diff --git a/@flywave/flywave-draco/src/draco-format.d.ts b/@flywave/flywave-draco/src/draco-format.d.ts new file mode 100644 index 0000000..7be5add --- /dev/null +++ b/@flywave/flywave-draco/src/draco-format.d.ts @@ -0,0 +1,12 @@ +/** + * Draco3D compressed geometries + */ +export declare const DracoFormat: { + readonly name: "Draco"; + readonly id: "draco"; + readonly module: "draco"; + readonly extensions: readonly ["drc"]; + readonly mimeTypes: readonly ["application/octet-stream"]; + readonly binary: true; + readonly tests: readonly ["DRACO"]; +}; diff --git a/@flywave/flywave-draco/src/draco-format.js b/@flywave/flywave-draco/src/draco-format.js new file mode 100644 index 0000000..fc2e419 --- /dev/null +++ b/@flywave/flywave-draco/src/draco-format.js @@ -0,0 +1,14 @@ +/* Copyright (C) 2025 flywave.gl contributors */ +/** + * Draco3D compressed geometries + */ +export const DracoFormat = { + name: "Draco", + id: "draco", + module: "draco", + extensions: ["drc"], + mimeTypes: ["application/octet-stream"], + binary: true, + tests: ["DRACO"] +}; +//# sourceMappingURL=draco-format.js.map \ No newline at end of file diff --git a/@flywave/flywave-draco/src/draco-loader.d.ts b/@flywave/flywave-draco/src/draco-loader.d.ts new file mode 100644 index 0000000..1a5904c --- /dev/null +++ b/@flywave/flywave-draco/src/draco-loader.d.ts @@ -0,0 +1,65 @@ +import type { DracoParseOptions } from "./loader/draco-parser"; +import type { DracoMesh } from "./loader/draco-types"; +export interface DracoLoaderOptions { + headers?: Record; + draco?: DracoParseOptions & { + /** @deprecated WASM decoding is faster but JS is more backwards compatible */ + decoderType?: "wasm" | "js"; + /** @deprecated Specify where to load the Draco decoder library */ + libraryPath?: string; + /** Override the URL to the worker bundle (by default loads from unpkg.com) */ + workerUrl?: string; + }; +} +/** + * Worker loader for Draco3D compressed geometries + */ +export declare const DracoWorkerLoader: { + readonly dataType: DracoMesh; + readonly batchType: never; + readonly name: "Draco"; + readonly id: "draco"; + readonly module: "draco"; + readonly version: any; + readonly worker: true; + readonly extensions: readonly ["drc"]; + readonly mimeTypes: readonly ["application/octet-stream"]; + readonly binary: true; + readonly tests: readonly ["DRACO"]; + readonly options: { + readonly draco: { + readonly decoderType: "wasm" | "js"; + readonly libraryPath: "libs/"; + readonly extraAttributes: {}; + readonly attributeNameEntry: any; + }; + }; +}; +/** + * Loader for Draco3D compressed geometries + */ +export declare const DracoLoader: { + readonly parse: typeof parse; + readonly dataType: DracoMesh; + readonly batchType: never; + readonly name: "Draco"; + readonly id: "draco"; + readonly module: "draco"; + readonly version: any; + readonly worker: true; + readonly extensions: readonly ["drc"]; + readonly mimeTypes: readonly ["application/octet-stream"]; + readonly binary: true; + readonly tests: readonly ["DRACO"]; + readonly options: { + readonly draco: { + readonly decoderType: "wasm" | "js"; + readonly libraryPath: "libs/"; + readonly extraAttributes: {}; + readonly attributeNameEntry: any; + }; + }; +}; +declare function parse(arrayBuffer: ArrayBuffer, options?: DracoLoaderOptions, context?: any): Promise; +export declare function loadDraco(url: string, loader: typeof DracoLoader, options?: DracoLoaderOptions, context?: any): Promise; +export {}; diff --git a/@flywave/flywave-draco/src/draco-loader.js b/@flywave/flywave-draco/src/draco-loader.js new file mode 100644 index 0000000..0393bca --- /dev/null +++ b/@flywave/flywave-draco/src/draco-loader.js @@ -0,0 +1,69 @@ +/* Copyright (C) 2025 flywave.gl contributors */ +import { loadDracoDecoderModule } from "./loader/draco-module-loader"; +import DracoParser from "./loader/draco-parser"; +import { VERSION } from "./loader/utils/version"; +/** + * Worker loader for Draco3D compressed geometries + */ +export const DracoWorkerLoader = { + dataType: null, + batchType: null, + name: "Draco", + id: "draco", + module: "draco", + // shapes: ['mesh'], + version: VERSION, + worker: true, + extensions: ["drc"], + mimeTypes: ["application/octet-stream"], + binary: true, + tests: ["DRACO"], + options: { + draco: { + decoderType: typeof WebAssembly === "object" ? "wasm" : "js", // 'js' for IE11 + libraryPath: "libs/", + extraAttributes: {}, + attributeNameEntry: undefined + } + } +}; +/** + * Loader for Draco3D compressed geometries + */ +export const DracoLoader = { + ...DracoWorkerLoader, + parse +}; +async function parse(arrayBuffer, options, context) { + const { draco } = await loadDracoDecoderModule(options); + const dracoParser = new DracoParser(draco); + try { + return dracoParser.parseSync(arrayBuffer, options?.draco); + } + finally { + dracoParser.destroy(); + } +} +export async function loadDraco(url, loader, options, context) { + const fetchFn = context?.fetch || globalThis.fetch; + if (!fetchFn) { + throw new Error("Fetch function is required to load subtree"); + } + try { + // 1. Get subtree file + const response = await fetchFn(url, { + headers: options.headers + }); + if (!response.ok) { + throw new Error(`Failed to fetch subtree: ${response.status} ${response.statusText}`); + } + // 2. Read binary data + const arrayBuffer = await response.arrayBuffer(); + // 3. Parse data using loader + return await loader.parse(arrayBuffer, options, context); + } + catch (error) { + throw new Error(`Subtree loading failed: ${error.message}`); + } +} +//# sourceMappingURL=draco-loader.js.map \ No newline at end of file diff --git a/@flywave/flywave-draco/src/draco-writer.d.ts b/@flywave/flywave-draco/src/draco-writer.d.ts new file mode 100644 index 0000000..66ba2ca --- /dev/null +++ b/@flywave/flywave-draco/src/draco-writer.d.ts @@ -0,0 +1,46 @@ +import type { DracoBuildOptions } from "./loader/draco-builder"; +import type { DracoMesh } from "./loader/draco-types"; +/** Writer Options for draco */ +export interface DracoWriterOptions { + draco?: DracoBuildOptions & { + method?: "MESH_EDGEBREAKER_ENCODING" | "MESH_SEQUENTIAL_ENCODING"; + speed?: [number, number]; + quantization?: Record; + attributeNameEntry?: string; + }; +} +/** + * Browser worker doesn't work because of issue during "draco_encoder.js" loading. + * Refused to execute script from 'https://raw.githubusercontent.com/google/draco/1.4.1/javascript/draco_encoder.js' because its MIME type ('') is not executable. + */ +export declare const DracoWriterWorker: { + id: string; + name: string; + module: string; + version: any; + worker: boolean; + options: { + draco: {}; + source: any; + }; +}; +/** + * Exporter for Draco3D compressed geometries + */ +export declare const DracoWriter: { + readonly name: "DRACO"; + readonly id: "draco"; + readonly module: "draco"; + readonly version: any; + readonly extensions: readonly ["drc"]; + readonly mimeTypes: readonly ["application/octet-stream"]; + readonly options: { + readonly draco: { + pointcloud: boolean; + attributeNameEntry: string; + }; + }; + readonly encode: typeof encode; +}; +declare function encode(data: DracoMesh, options?: DracoWriterOptions): Promise; +export {}; diff --git a/@flywave/flywave-draco/src/draco-writer.js b/@flywave/flywave-draco/src/draco-writer.js new file mode 100644 index 0000000..799fba3 --- /dev/null +++ b/@flywave/flywave-draco/src/draco-writer.js @@ -0,0 +1,56 @@ +/* Copyright (C) 2025 flywave.gl contributors */ +import DRACOBuilder from "./loader/draco-builder"; +import { loadDracoEncoderModule } from "./loader/draco-module-loader"; +import { VERSION } from "./loader/utils/version"; +const DEFAULT_DRACO_WRITER_OPTIONS = { + pointcloud: false, // Set to true if pointcloud (mode: 0, no indices) + attributeNameEntry: "name" + // Draco Compression Parameters + // method: 'MESH_EDGEBREAKER_ENCODING', // Use draco defaults + // speed: [5, 5], // Use draco defaults + // quantization: { // Use draco defaults + // POSITION: 10 + // } +}; +/** + * Browser worker doesn't work because of issue during "draco_encoder.js" loading. + * Refused to execute script from 'https://raw.githubusercontent.com/google/draco/1.4.1/javascript/draco_encoder.js' because its MIME type ('') is not executable. + */ +export const DracoWriterWorker = { + id: "draco-writer", + name: "Draco compressed geometry writer", + module: "draco", + version: VERSION, + worker: true, + options: { + draco: {}, + source: null + } +}; +/** + * Exporter for Draco3D compressed geometries + */ +export const DracoWriter = { + name: "DRACO", + id: "draco", + module: "draco", + version: VERSION, + extensions: ["drc"], + mimeTypes: ["application/octet-stream"], + options: { + draco: DEFAULT_DRACO_WRITER_OPTIONS + }, + encode +}; +async function encode(data, options = {}) { + // Dynamically load draco + const { draco } = await loadDracoEncoderModule(options); + const dracoBuilder = new DRACOBuilder(draco); + try { + return dracoBuilder.encodeSync(data, options.draco); + } + finally { + dracoBuilder.destroy(); + } +} +//# sourceMappingURL=draco-writer.js.map \ No newline at end of file diff --git a/@flywave/flywave-draco/src/draco3d/draco3d-types.d.ts b/@flywave/flywave-draco/src/draco3d/draco3d-types.d.ts new file mode 100644 index 0000000..352580d --- /dev/null +++ b/@flywave/flywave-draco/src/draco3d/draco3d-types.d.ts @@ -0,0 +1,294 @@ +/** Draco3D untyped memory pointer */ +type VoidPtr = any; +/** Draco3D geometry attribute type */ +export declare enum draco_GeometryAttribute_Type { + "draco_GeometryAttribute::INVALID" = 0, + "draco_GeometryAttribute::POSITION" = 1, + "draco_GeometryAttribute::NORMAL" = 2, + "draco_GeometryAttribute::COLOR" = 3, + "draco_GeometryAttribute::TEX_COORD" = 4, + "draco_GeometryAttribute::GENERIC" = 5 +} +/** Draco3D encoded geometry type */ +export declare enum draco_EncodedGeometryType { + "draco::INVALID_GEOMETRY_TYPE" = 0, + "draco::POINT_CLOUD" = 1, + "draco::TRIANGULAR_MESH" = 2 +} +/** Draco3D data type */ +export declare enum draco_DataType { + "draco::DT_INVALID" = 0, + "draco::DT_INT8" = 1, + "draco::DT_UINT8" = 2, + "draco::DT_INT16" = 3, + "draco::DT_UINT16" = 4, + "draco::DT_INT32" = 5, + "draco::DT_UINT32" = 6, + "draco::DT_INT64" = 7, + "draco::DT_UINT64" = 8, + "draco::DT_FLOAT32" = 9, + "draco::DT_FLOAT64" = 10, + "draco::DT_BOOL" = 11, + "draco::DT_TYPES_COUNT" = 12 +} +/** Draco3D status code */ +export declare enum draco_StatusCode { + "draco_Status::OK" = 0, + "draco_Status::DRACO_ERROR" = 1, + "draco_Status::IO_ERROR" = 2, + "draco_Status::INVALID_PARAMETER" = 3, + "draco_Status::UNSUPPORTED_VERSION" = 4, + "draco_Status::UNKNOWN_VERSION" = 5 +} +/** Draco3D decoder buffer allocated on emscripten heap */ +export declare class DecoderBuffer { + constructor(); + Init(data: Int8Array, data_size: number): void; +} +/** Draco3D attribute transform data */ +export declare class AttributeTransformData { + constructor(); + transform_type(): number; +} +/** Draco3D geometry attribute */ +export declare class GeometryAttribute { + constructor(); +} +/** Draco3D point attribute */ +export declare class PointAttribute { + ptr: VoidPtr; + constructor(); + size(): number; + GetAttributeTransformData(): AttributeTransformData; + attribute_type(): number; + data_type(): number; + num_components(): number; + normalized(): boolean; + byte_stride(): number; + byte_offset(): number; + unique_id(): number; +} +/** Draco3D attribute transform */ +export declare class AttributeQuantizationTransform { + constructor(); + InitFromAttribute(att: PointAttribute): boolean; + quantization_bits(): number; + min_value(axis: number): number; + range(): number; +} +/** Draco3D attribute transform */ +export declare class AttributeOctahedronTransform { + constructor(); + InitFromAttribute(att: PointAttribute): boolean; + quantization_bits(): number; +} +/** Draco3D point cloud */ +export declare class PointCloud { + ptr: VoidPtr; + constructor(); + num_attributes(): number; + num_points(): number; +} +/** Draco3D mesh */ +export declare class Mesh extends PointCloud { + constructor(); + num_faces(): number; +} +/** Draco3D metadata */ +export declare class Metadata { + ptr: VoidPtr; + constructor(); +} +/** Draco3D status */ +export declare class Status { + constructor(); + code(): draco_StatusCode; + ok(): boolean; + error_msg(): string; +} +/** Draco3D Float32Array allocated on the emscripten heap */ +export declare class DracoFloat32Array { + constructor(); + GetValue(index: number): number; + size(): number; +} +/** Draco3D Int8Array allocated on the emscripten heap */ +export declare class DracoInt8Array { + constructor(); + GetValue(index: number): number; + size(): number; +} +/** Draco3D Uint8Array allocated on the emscripten heap */ +export declare class DracoUInt8Array { + GetValue(index: number): number; + size(): number; +} +/** Draco3D Int16Array allocated on the emscripten heap */ +export declare class DracoInt16Array { + constructor(); + GetValue(index: number): number; + size(): number; +} +/** Draco3D Uint16Array allocated on the emscripten heap */ +export declare class DracoUInt16Array { + constructor(); + GetValue(index: number): number; + size(): number; +} +/** Draco3D Int32Array allocated on the emscripten heap */ +export declare class DracoInt32Array { + constructor(); + GetValue(index: number): number; + size(): number; +} +/** Draco3D Uint32Array allocated on the emscripten heap */ +export declare class DracoUInt32Array { + constructor(); + GetValue(index: number): number; + size(): number; +} +/** Draco3D metadata querier */ +export declare class MetadataQuerier { + constructor(); + HasEntry(metadata: Metadata, entry_name: string): string; + GetIntEntry(metadata: Metadata, entry_name: string): any; + GetIntEntryArray(metadata: Metadata, entry_name: string, out_values: DracoInt32Array): any; + GetDoubleEntry(metadata: Metadata, entry_name: string): number; + GetStringEntry(metadata: Metadata, entry_name: string): string; + NumEntries(metadata: Metadata): number; + GetEntryName(metadata: Metadata, entry_id: number): string; +} +/** + * Draco3D Decoder class + */ +export declare class Decoder { + constructor(); + GetEncodedGeometryType(in_buffer: DecoderBuffer): draco_EncodedGeometryType; + DecodeBufferToPointCloud(in_buffer: DecoderBuffer, out_point_cloud: PointCloud): Status; + DecodeBufferToMesh(in_buffer: DecoderBuffer, out_mesh: Mesh): Status; + GetAttributeId(pc: PointCloud, type: draco_GeometryAttribute_Type): number; + GetAttributeIdByName(pc: PointCloud, name: string): number; + GetAttributeIdByMetadataEntry(pc: PointCloud, name: string, value: string): number; + GetAttribute(pc: PointCloud, att_id: number): PointAttribute; + GetAttributeByUniqueId(pc: PointCloud, unique_id: number): PointAttribute; + GetMetadata(pc: PointCloud): Metadata; + GetAttributeMetadata(pc: PointCloud, att_id: number): Metadata; + GetFaceFromMesh(m: Mesh, face_id: number, out_values: DracoInt32Array): boolean; + GetTriangleStripsFromMesh(m: Mesh, strip_values: DracoInt32Array): any; + GetTrianglesUInt16Array(m: Mesh, out_size: number, out_values: VoidPtr): boolean; + GetTrianglesUInt32Array(m: Mesh, out_size: number, out_values: VoidPtr): boolean; + GetAttributeFloat(pa: PointAttribute, att_index: number, out_values: DracoFloat32Array): boolean; + GetAttributeFloatForAllPoints(pc: PointCloud, pa: PointAttribute, out_values: DracoFloat32Array): boolean; + GetAttributeIntForAllPoints(pc: PointCloud, pa: PointAttribute, out_values: DracoInt32Array): boolean; + GetAttributeInt8ForAllPoints(pc: PointCloud, pa: PointAttribute, out_values: DracoInt8Array): boolean; + GetAttributeUInt8ForAllPoints(pc: PointCloud, pa: PointAttribute, out_values: DracoUInt8Array): boolean; + GetAttributeInt16ForAllPoints(pc: PointCloud, pa: PointAttribute, out_values: DracoInt16Array): boolean; + GetAttributeUInt16ForAllPoints(pc: PointCloud, pa: PointAttribute, out_values: DracoUInt16Array): boolean; + GetAttributeInt32ForAllPoints(pc: PointCloud, pa: PointAttribute, out_values: DracoInt32Array): boolean; + GetAttributeUInt32ForAllPoints(pc: PointCloud, pa: PointAttribute, out_values: DracoUInt32Array): boolean; + GetAttributeDataArrayForAllPoints(pc: PointCloud, pa: PointAttribute, data_type: draco_DataType, out_size: number, out_values: VoidPtr): boolean; + SkipAttributeTransform(att_type: draco_GeometryAttribute_Type): void; +} +/** Draco3D metadata builder */ +export declare class MetadataBuilder { + constructor(); + AddStringEntry(metadata: Metadata, entry_name: string, entry_value: string): any; + AddIntEntry(metadata: Metadata, entry_name: string, entry_value: number): any; + AddDoubleEntry(metadata: Metadata, entry_name: string, entry_value: number): any; + AddIntEntryArray(metadata: Metadata, entry_name: string, entry_value: Int32Array, num_values: number): any; +} +/** Draco3D point cloud builder */ +export declare class PointCloudBuilder { + constructor(); + PointCloudBuilder(): void; + AddFloatAttribute(pc: PointCloud, type: draco_GeometryAttribute_Type, num_vertices: number, num_components: number, att_values: Float32Array): any; + AddInt8Attribute(pc: PointCloud, type: draco_GeometryAttribute_Type, num_vertices: number, num_components: number, att_values: Int8Array): any; + AddUInt8Attribute(pc: PointCloud, type: draco_GeometryAttribute_Type, num_vertices: number, num_components: number, att_values: Uint8Array): any; + AddInt16Attribute(pc: PointCloud, type: draco_GeometryAttribute_Type, num_vertices: number, num_components: number, att_values: Int16Array): any; + AddUInt16Attribute(pc: PointCloud, type: draco_GeometryAttribute_Type, num_vertices: number, num_components: number, att_values: Uint16Array): any; + AddInt32Attribute(pc: PointCloud, type: draco_GeometryAttribute_Type, num_vertices: number, num_components: number, att_values: Int32Array): any; + AddUInt32Attribute(pc: PointCloud, type: draco_GeometryAttribute_Type, num_vertices: number, num_components: number, att_values: Uint32Array): any; + AddMetadata(pc: PointCloud, metadata: Metadata): boolean; + SetMetadataForAttribute(pc: PointCloud, attribute_id: number, metadata: Metadata): any; +} +/** Draco3D mesh builder */ +export declare class MeshBuilder extends PointCloudBuilder { + constructor(); + AddFacesToMesh(mesh: Mesh, num_faces: number, faces: number[]): boolean; +} +/** Draco3D encoder */ +export declare class Encoder { + constructor(); + Encoder(): void; + SetEncodingMethod(method: number): void; + SetAttributeQuantization(type: draco_GeometryAttribute_Type, quantization_bits: number): any; + SetAttributeExplicitQuantization(type: draco_GeometryAttribute_Type, quantization_bits: number, num_components: number, origin: number[], range: number): any; + SetSpeedOptions(encoding_speed: number, decoding_speed: number): void; + SetTrackEncodedProperties(flag: boolean): void; + EncodeMeshToDracoBuffer(mesh: Mesh, encoded_data: DracoInt8Array): any; + EncodePointCloudToDracoBuffer(pc: PointCloud, deduplicate_values: boolean, encoded_data: DracoInt8Array): any; + GetNumberOfEncodedPoints(): number; + GetNumberOfEncodedFaces(): number; +} +/** Draco3D expert encoder */ +export declare class ExpertEncoder { + constructor(); + ExpertEncoder(pc: PointCloud): void; + SetEncodingMethod(method: number): void; + SetAttributeQuantization(att_id: number, quantization_bits: number): any; + SetAttributeExplicitQuantization(att_id: number, quantization_bits: number, num_components: number, origin: number[], range: number): any; + SetSpeedOptions(encoding_speed: number, decoding_speed: number): void; + SetTrackEncodedProperties(flag: boolean): void; + EncodeToDracoBuffer(deduplicate_values: boolean, encoded_data: DracoInt8Array): any; + GetNumberOfEncodedPoints(): number; + GetNumberOfEncodedFaces(): number; +} +/** Draco3D module interface */ +export interface Draco3D { + readonly INVALID_GEOMETRY_TYPE: draco_EncodedGeometryType; + readonly POINT_CLOUD: draco_EncodedGeometryType; + readonly TRIANGULAR_MESH: draco_EncodedGeometryType; + readonly INVALID: draco_GeometryAttribute_Type; + readonly POSITION: draco_GeometryAttribute_Type; + readonly NORMAL: draco_GeometryAttribute_Type; + readonly COLOR: draco_GeometryAttribute_Type; + readonly TEX_COORD: draco_GeometryAttribute_Type; + readonly GENERIC: draco_GeometryAttribute_Type; + readonly DT_INVALID: draco_DataType; + readonly DT_INT8: draco_DataType; + readonly DT_UINT8: draco_DataType; + readonly DT_INT16: draco_DataType; + readonly DT_UINT16: draco_DataType; + readonly DT_INT32: draco_DataType; + readonly DT_UINT32: draco_DataType; + readonly DT_INT64: draco_DataType; + readonly DT_UINT64: draco_DataType; + readonly DT_FLOAT32: draco_DataType; + readonly DT_FLOAT64: draco_DataType; + readonly DT_BOOL: draco_DataType; + readonly DT_TYPES_COUNT: draco_DataType; + readonly Mesh: typeof Mesh; + readonly PointCloud: typeof PointCloud; + readonly Metadata: typeof Metadata; + readonly Encoder: typeof Encoder; + readonly MeshBuilder: typeof MeshBuilder; + readonly MetadataBuilder: typeof MetadataBuilder; + readonly MetadataQuerier: typeof MetadataQuerier; + readonly Decoder: typeof Decoder; + readonly DecoderBuffer: typeof DecoderBuffer; + readonly DracoFloat32Array: typeof DracoFloat32Array; + readonly DracoInt8Array: typeof DracoInt8Array; + readonly DracoUInt8Array: typeof DracoUInt8Array; + readonly DracoInt16Array: typeof DracoInt16Array; + readonly DracoUInt16Array: typeof DracoUInt16Array; + readonly DracoInt32Array: typeof DracoInt32Array; + readonly DracoUInt32Array: typeof DracoUInt32Array; + readonly AttributeQuantizationTransform: typeof AttributeQuantizationTransform; + destroy(resource: any): void; + _malloc(byteLength: number): number; + _free(ptr: number): void; + HEAPF32: { + buffer: ArrayBuffer; + }; +} +export {}; diff --git a/@flywave/flywave-draco/src/draco3d/draco3d-types.js b/@flywave/flywave-draco/src/draco3d/draco3d-types.js new file mode 100644 index 0000000..19a3273 --- /dev/null +++ b/@flywave/flywave-draco/src/draco3d/draco3d-types.js @@ -0,0 +1,47 @@ +/* Copyright (C) 2025 flywave.gl contributors */ +// DRACO WEB DECODER IDL +/** Draco3D geometry attribute type */ +export var draco_GeometryAttribute_Type; +(function (draco_GeometryAttribute_Type) { + draco_GeometryAttribute_Type[draco_GeometryAttribute_Type["draco_GeometryAttribute::INVALID"] = 0] = "draco_GeometryAttribute::INVALID"; + draco_GeometryAttribute_Type[draco_GeometryAttribute_Type["draco_GeometryAttribute::POSITION"] = 1] = "draco_GeometryAttribute::POSITION"; + draco_GeometryAttribute_Type[draco_GeometryAttribute_Type["draco_GeometryAttribute::NORMAL"] = 2] = "draco_GeometryAttribute::NORMAL"; + draco_GeometryAttribute_Type[draco_GeometryAttribute_Type["draco_GeometryAttribute::COLOR"] = 3] = "draco_GeometryAttribute::COLOR"; + draco_GeometryAttribute_Type[draco_GeometryAttribute_Type["draco_GeometryAttribute::TEX_COORD"] = 4] = "draco_GeometryAttribute::TEX_COORD"; + draco_GeometryAttribute_Type[draco_GeometryAttribute_Type["draco_GeometryAttribute::GENERIC"] = 5] = "draco_GeometryAttribute::GENERIC"; +})(draco_GeometryAttribute_Type || (draco_GeometryAttribute_Type = {})); +/** Draco3D encoded geometry type */ +export var draco_EncodedGeometryType; +(function (draco_EncodedGeometryType) { + draco_EncodedGeometryType[draco_EncodedGeometryType["draco::INVALID_GEOMETRY_TYPE"] = 0] = "draco::INVALID_GEOMETRY_TYPE"; + draco_EncodedGeometryType[draco_EncodedGeometryType["draco::POINT_CLOUD"] = 1] = "draco::POINT_CLOUD"; + draco_EncodedGeometryType[draco_EncodedGeometryType["draco::TRIANGULAR_MESH"] = 2] = "draco::TRIANGULAR_MESH"; +})(draco_EncodedGeometryType || (draco_EncodedGeometryType = {})); +/** Draco3D data type */ +export var draco_DataType; +(function (draco_DataType) { + draco_DataType[draco_DataType["draco::DT_INVALID"] = 0] = "draco::DT_INVALID"; + draco_DataType[draco_DataType["draco::DT_INT8"] = 1] = "draco::DT_INT8"; + draco_DataType[draco_DataType["draco::DT_UINT8"] = 2] = "draco::DT_UINT8"; + draco_DataType[draco_DataType["draco::DT_INT16"] = 3] = "draco::DT_INT16"; + draco_DataType[draco_DataType["draco::DT_UINT16"] = 4] = "draco::DT_UINT16"; + draco_DataType[draco_DataType["draco::DT_INT32"] = 5] = "draco::DT_INT32"; + draco_DataType[draco_DataType["draco::DT_UINT32"] = 6] = "draco::DT_UINT32"; + draco_DataType[draco_DataType["draco::DT_INT64"] = 7] = "draco::DT_INT64"; + draco_DataType[draco_DataType["draco::DT_UINT64"] = 8] = "draco::DT_UINT64"; + draco_DataType[draco_DataType["draco::DT_FLOAT32"] = 9] = "draco::DT_FLOAT32"; + draco_DataType[draco_DataType["draco::DT_FLOAT64"] = 10] = "draco::DT_FLOAT64"; + draco_DataType[draco_DataType["draco::DT_BOOL"] = 11] = "draco::DT_BOOL"; + draco_DataType[draco_DataType["draco::DT_TYPES_COUNT"] = 12] = "draco::DT_TYPES_COUNT"; +})(draco_DataType || (draco_DataType = {})); +/** Draco3D status code */ +export var draco_StatusCode; +(function (draco_StatusCode) { + draco_StatusCode[draco_StatusCode["draco_Status::OK"] = 0] = "draco_Status::OK"; + draco_StatusCode[draco_StatusCode["draco_Status::DRACO_ERROR"] = 1] = "draco_Status::DRACO_ERROR"; + draco_StatusCode[draco_StatusCode["draco_Status::IO_ERROR"] = 2] = "draco_Status::IO_ERROR"; + draco_StatusCode[draco_StatusCode["draco_Status::INVALID_PARAMETER"] = 3] = "draco_Status::INVALID_PARAMETER"; + draco_StatusCode[draco_StatusCode["draco_Status::UNSUPPORTED_VERSION"] = 4] = "draco_Status::UNSUPPORTED_VERSION"; + draco_StatusCode[draco_StatusCode["draco_Status::UNKNOWN_VERSION"] = 5] = "draco_Status::UNKNOWN_VERSION"; +})(draco_StatusCode || (draco_StatusCode = {})); +//# sourceMappingURL=draco3d-types.js.map \ No newline at end of file diff --git a/@flywave/flywave-draco/src/index.d.ts b/@flywave/flywave-draco/src/index.d.ts new file mode 100644 index 0000000..e1cf7f3 --- /dev/null +++ b/@flywave/flywave-draco/src/index.d.ts @@ -0,0 +1,6 @@ +export { DRACO_EXTERNAL_LIBRARIES, DRACO_EXTERNAL_LIBRARY_URLS } from "./loader/draco-module-loader"; +export type { DracoMesh, DracoLoaderData } from "./loader/draco-types"; +export type { DracoWriterOptions } from "./draco-writer"; +export { DracoWriterWorker, DracoWriter } from "./draco-writer"; +export type { DracoLoaderOptions, loadDraco } from "./draco-loader"; +export { DracoWorkerLoader, DracoLoader } from "./draco-loader"; diff --git a/@flywave/flywave-draco/src/index.js b/@flywave/flywave-draco/src/index.js new file mode 100644 index 0000000..9fdb5ad --- /dev/null +++ b/@flywave/flywave-draco/src/index.js @@ -0,0 +1,6 @@ +/* Copyright (C) 2025 flywave.gl contributors */ +// Module constants +export { DRACO_EXTERNAL_LIBRARIES, DRACO_EXTERNAL_LIBRARY_URLS } from "./loader/draco-module-loader"; +export { DracoWriterWorker, DracoWriter } from "./draco-writer"; +export { DracoWorkerLoader, DracoLoader } from "./draco-loader"; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/@flywave/flywave-draco/src/libs/draco_encoder.d.ts b/@flywave/flywave-draco/src/libs/draco_encoder.d.ts new file mode 100644 index 0000000..1c63c04 --- /dev/null +++ b/@flywave/flywave-draco/src/libs/draco_encoder.d.ts @@ -0,0 +1,5 @@ +export = DracoEncoderModule; +declare function DracoEncoderModule(DracoEncoderModule: any, ...args: any[]): any; +declare namespace DracoEncoderModule { + export { DracoEncoderModule }; +} diff --git a/@flywave/flywave-draco/src/libs/draco_wasm_wrapper.d.ts b/@flywave/flywave-draco/src/libs/draco_wasm_wrapper.d.ts new file mode 100644 index 0000000..e973d4a --- /dev/null +++ b/@flywave/flywave-draco/src/libs/draco_wasm_wrapper.d.ts @@ -0,0 +1,5 @@ +export = DracoDecoderModule; +declare function DracoDecoderModule(n: any): any; +declare namespace DracoDecoderModule { + export { DracoDecoderModule }; +} diff --git a/@flywave/flywave-draco/src/loader/draco-builder.d.ts b/@flywave/flywave-draco/src/loader/draco-builder.d.ts new file mode 100644 index 0000000..baad793 --- /dev/null +++ b/@flywave/flywave-draco/src/loader/draco-builder.d.ts @@ -0,0 +1,93 @@ +import type { TypedArray } from "three"; +import * as THREE from "three"; +import type { draco_GeometryAttribute_Type, Draco3D, Encoder, Mesh, MeshBuilder, Metadata, MetadataBuilder, PointCloud } from "../draco3d/draco3d-types"; +import type { DracoMesh } from "./draco-types"; +export interface DracoBuildOptions { + pointcloud?: boolean; + metadata?: { + [key: string]: string; + }; + attributesMetadata?: {}; + log?: any; + speed?: [number, number]; + method?: string; + quantization?: { + [attributeName: string]: number; + }; +} +export default class DracoBuilder { + draco: Draco3D; + dracoEncoder: Encoder; + dracoMeshBuilder: MeshBuilder; + dracoMetadataBuilder: MetadataBuilder; + log: any; + constructor(draco: Draco3D); + destroy(): void; + destroyEncodedObject(object: any): void; + /** + * Encode mesh or point cloud + * @param mesh =({}) + * @param options + */ + encodeSync(mesh: DracoMesh, options?: DracoBuildOptions): ArrayBuffer; + _getAttributesFromMesh(mesh: DracoMesh): { + [key: string]: THREE.BufferAttribute; + }; + _encodePointCloud(pointcloud: DracoMesh, options: DracoBuildOptions): ArrayBuffer; + _encodeMesh(mesh: DracoMesh, options: DracoBuildOptions): ArrayBuffer; + /** + * Set encoding options. + * @param {{speed?: any; method?: any; quantization?: any;}} options + */ + _setOptions(options: DracoBuildOptions): void; + /** + * @param {Mesh} dracoMesh + * @param {object} attributes + * @returns {Mesh} + */ + _createDracoMesh(dracoMesh: Mesh, attributes: any, options: DracoBuildOptions): Mesh; + /** + * @param {} dracoPointCloud + * @param {object} attributes + */ + _createDracoPointCloud(dracoPointCloud: PointCloud, attributes: object, options: DracoBuildOptions): PointCloud; + /** + * @param mesh + * @param attributeName + * @param attribute + * @param vertexCount + */ + _addAttributeToMesh(mesh: PointCloud, attributeName: string, attribute: TypedArray, vertexCount: number): number; + /** + * DRACO can compress attributes of know type better + * TODO - expose an attribute type map? + * @param attributeName + */ + _getDracoAttributeType(attributeName: string): draco_GeometryAttribute_Type | "indices"; + _getPositionAttribute(attributes: any): any; + /** + * Add metadata for the geometry. + * @param dracoGeometry - WASM Draco Object + * @param metadata + */ + _addGeometryMetadata(dracoGeometry: PointCloud, metadata: { + [key: string]: string; + }): void; + /** + * Add metadata for an attribute to geometry. + * @param dracoGeometry - WASM Draco Object + * @param uniqueAttributeId + * @param metadata + */ + _addAttributeMetadata(dracoGeometry: PointCloud, uniqueAttributeId: number, metadata: Map | { + [key: string]: string; + }): void; + /** + * Add contents of object or map to a WASM Draco Metadata Object + * @param dracoMetadata - WASM Draco Object + * @param metadata + */ + _populateDracoMetadata(dracoMetadata: Metadata, metadata: Map | { + [key: string]: string; + }): void; +} diff --git a/@flywave/flywave-draco/src/loader/draco-builder.js b/@flywave/flywave-draco/src/loader/draco-builder.js new file mode 100644 index 0000000..bc642d3 --- /dev/null +++ b/@flywave/flywave-draco/src/loader/draco-builder.js @@ -0,0 +1,352 @@ +/* Copyright (C) 2025 flywave.gl contributors */ +import * as THREE from "three"; +// Native Draco attribute names to GLTF attribute names. +const GLTF_TO_DRACO_ATTRIBUTE_NAME_MAP = { + POSITION: "POSITION", + NORMAL: "NORMAL", + COLOR_0: "COLOR", + TEXCOORD_0: "TEX_COORD" +}; +const noop = () => { }; +export default class DracoBuilder { + // draco - the draco decoder, either import `draco3d` or load dynamically + constructor(draco) { + this.draco = draco; + this.dracoEncoder = new this.draco.Encoder(); + this.dracoMeshBuilder = new this.draco.MeshBuilder(); + this.dracoMetadataBuilder = new this.draco.MetadataBuilder(); + } + destroy() { + this.destroyEncodedObject(this.dracoMeshBuilder); + this.destroyEncodedObject(this.dracoEncoder); + this.destroyEncodedObject(this.dracoMetadataBuilder); + // @ts-ignore + this.dracoMeshBuilder = null; + // @ts-ignore + this.dracoEncoder = null; + // @ts-ignore + this.draco = null; + } + // TBD - when does this need to be called? + destroyEncodedObject(object) { + if (object) { + this.draco.destroy(object); + } + } + /** + * Encode mesh or point cloud + * @param mesh =({}) + * @param options + */ + encodeSync(mesh, options = {}) { + this.log = noop; // TODO + this._setOptions(options); + return options.pointcloud + ? this._encodePointCloud(mesh, options) + : this._encodeMesh(mesh, options); + } + // PRIVATE + _getAttributesFromMesh(mesh) { + // Extract attributes from Three.js geometry + const attributes = {}; + // Iterate through all geometry attributes + mesh.geometry.attributes = mesh.geometry.attributes || {}; + for (const [name, attribute] of Object.entries(mesh.geometry.attributes)) { + if (attribute instanceof THREE.BufferAttribute) { + attributes[name] = attribute; + } + } + // Add index data + if (mesh.geometry.index) { + attributes.indices = new THREE.BufferAttribute(mesh.geometry.index.array, 1 // The itemSize of the index is fixed to 1 + ); + } + return attributes; + } + _encodePointCloud(pointcloud, options) { + const dracoPointCloud = new this.draco.PointCloud(); + if (options.metadata) { + this._addGeometryMetadata(dracoPointCloud, options.metadata); + } + const attributes = this._getAttributesFromMesh(pointcloud); + // Build a `DracoPointCloud` from the input data + this._createDracoPointCloud(dracoPointCloud, attributes, options); + const dracoData = new this.draco.DracoInt8Array(); + try { + const encodedLen = this.dracoEncoder.EncodePointCloudToDracoBuffer(dracoPointCloud, false, dracoData); + if (!(encodedLen > 0)) { + throw new Error("Draco encoding failed."); + } + this.log(`DRACO encoded ${dracoPointCloud.num_points()} points + with ${dracoPointCloud.num_attributes()} attributes into ${encodedLen} bytes`); + return dracoInt8ArrayToArrayBuffer(dracoData); + } + finally { + this.destroyEncodedObject(dracoData); + this.destroyEncodedObject(dracoPointCloud); + } + } + _encodeMesh(mesh, options) { + const dracoMesh = new this.draco.Mesh(); + if (options.metadata) { + this._addGeometryMetadata(dracoMesh, options.metadata); + } + const attributes = this._getAttributesFromMesh(mesh); + // Build a `DracoMesh` from the input data + this._createDracoMesh(dracoMesh, attributes, options); + const dracoData = new this.draco.DracoInt8Array(); + try { + const encodedLen = this.dracoEncoder.EncodeMeshToDracoBuffer(dracoMesh, dracoData); + if (encodedLen <= 0) { + throw new Error("Draco encoding failed."); + } + this.log(`DRACO encoded ${dracoMesh.num_points()} points + with ${dracoMesh.num_attributes()} attributes into ${encodedLen} bytes`); + return dracoInt8ArrayToArrayBuffer(dracoData); + } + finally { + this.destroyEncodedObject(dracoData); + this.destroyEncodedObject(dracoMesh); + } + } + /** + * Set encoding options. + * @param {{speed?: any; method?: any; quantization?: any;}} options + */ + _setOptions(options) { + if ("speed" in options) { + // @ts-ignore + this.dracoEncoder.SetSpeedOptions(...options.speed); + } + if ("method" in options) { + const dracoMethod = this.draco[options.method || "MESH_SEQUENTIAL_ENCODING"]; + // assert(dracoMethod) + this.dracoEncoder.SetEncodingMethod(dracoMethod); + } + if ("quantization" in options) { + for (const attribute in options.quantization) { + const bits = options.quantization[attribute]; + const dracoPosition = this.draco[attribute]; + this.dracoEncoder.SetAttributeQuantization(dracoPosition, bits); + } + } + } + /** + * @param {Mesh} dracoMesh + * @param {object} attributes + * @returns {Mesh} + */ + _createDracoMesh(dracoMesh, attributes, options) { + const optionalMetadata = options.attributesMetadata || {}; + try { + const positions = this._getPositionAttribute(attributes); + if (!positions) { + throw new Error("positions"); + } + const vertexCount = positions.length / 3; + for (let attributeName in attributes) { + const attribute = attributes[attributeName]; + attributeName = GLTF_TO_DRACO_ATTRIBUTE_NAME_MAP[attributeName] || attributeName; + const uniqueId = this._addAttributeToMesh(dracoMesh, attributeName, attribute, vertexCount); + if (uniqueId !== -1) { + this._addAttributeMetadata(dracoMesh, uniqueId, { + name: attributeName, + ...(optionalMetadata[attributeName] || {}) + }); + } + } + } + catch (error) { + this.destroyEncodedObject(dracoMesh); + throw error; + } + return dracoMesh; + } + /** + * @param {} dracoPointCloud + * @param {object} attributes + */ + _createDracoPointCloud(dracoPointCloud, attributes, options) { + const optionalMetadata = options.attributesMetadata || {}; + try { + const positions = this._getPositionAttribute(attributes); + if (!positions) { + throw new Error("positions"); + } + const vertexCount = positions.length / 3; + for (let attributeName in attributes) { + const attribute = attributes[attributeName]; + attributeName = GLTF_TO_DRACO_ATTRIBUTE_NAME_MAP[attributeName] || attributeName; + const uniqueId = this._addAttributeToMesh(dracoPointCloud, attributeName, attribute, vertexCount); + if (uniqueId !== -1) { + this._addAttributeMetadata(dracoPointCloud, uniqueId, { + name: attributeName, + ...(optionalMetadata[attributeName] || {}) + }); + } + } + } + catch (error) { + this.destroyEncodedObject(dracoPointCloud); + throw error; + } + return dracoPointCloud; + } + /** + * @param mesh + * @param attributeName + * @param attribute + * @param vertexCount + */ + _addAttributeToMesh(mesh, attributeName, attribute, vertexCount) { + if (!ArrayBuffer.isView(attribute)) { + return -1; + } + const type = this._getDracoAttributeType(attributeName); + // @ts-ignore TODO/fix types + const size = attribute.length / vertexCount; + if (type === "indices") { + // @ts-ignore TODO/fix types + const numFaces = attribute.length / 3; + this.log(`Adding attribute ${attributeName}, size ${numFaces}`); + // @ts-ignore assumes mesh is a Mesh, not a point cloud + this.dracoMeshBuilder.AddFacesToMesh(mesh, numFaces, attribute); + return -1; + } + this.log(`Adding attribute ${attributeName}, size ${size}`); + const builder = this.dracoMeshBuilder; + const { buffer } = attribute; + switch (attribute.constructor) { + case Int8Array: + return builder.AddInt8Attribute(mesh, type, vertexCount, size, new Int8Array(buffer)); + case Int16Array: + return builder.AddInt16Attribute(mesh, type, vertexCount, size, new Int16Array(buffer)); + case Int32Array: + return builder.AddInt32Attribute(mesh, type, vertexCount, size, new Int32Array(buffer)); + case Uint8Array: + case Uint8ClampedArray: + return builder.AddUInt8Attribute(mesh, type, vertexCount, size, new Uint8Array(buffer)); + case Uint16Array: + return builder.AddUInt16Attribute(mesh, type, vertexCount, size, new Uint16Array(buffer)); + case Uint32Array: + return builder.AddUInt32Attribute(mesh, type, vertexCount, size, new Uint32Array(buffer)); + case Float32Array: + return builder.AddFloatAttribute(mesh, type, vertexCount, size, new Float32Array(buffer)); + default: + // eslint-disable-next-line no-console + console.warn("Unsupported attribute type", attribute); + return -1; + } + // case Float64Array: + // Add attribute does not seem to be exposed + // return builder.AddAttribute(mesh, type, vertexCount, size, new Float32Array(buffer)); + } + /** + * DRACO can compress attributes of know type better + * TODO - expose an attribute type map? + * @param attributeName + */ + _getDracoAttributeType(attributeName) { + switch (attributeName.toLowerCase()) { + case "indices": + return "indices"; + case "position": + case "positions": + case "vertices": + return this.draco.POSITION; + case "normal": + case "normals": + return this.draco.NORMAL; + case "color": + case "colors": + return this.draco.COLOR; + case "texcoord": + case "texcoords": + return this.draco.TEX_COORD; + default: + return this.draco.GENERIC; + } + } + _getPositionAttribute(attributes) { + for (const attributeName in attributes) { + const attribute = attributes[attributeName]; + const dracoType = this._getDracoAttributeType(attributeName); + if (dracoType === this.draco.POSITION) { + return attribute; + } + } + return null; + } + /** + * Add metadata for the geometry. + * @param dracoGeometry - WASM Draco Object + * @param metadata + */ + _addGeometryMetadata(dracoGeometry, metadata) { + const dracoMetadata = new this.draco.Metadata(); + this._populateDracoMetadata(dracoMetadata, metadata); + this.dracoMeshBuilder.AddMetadata(dracoGeometry, dracoMetadata); + } + /** + * Add metadata for an attribute to geometry. + * @param dracoGeometry - WASM Draco Object + * @param uniqueAttributeId + * @param metadata + */ + _addAttributeMetadata(dracoGeometry, uniqueAttributeId, metadata) { + // Note: Draco JS IDL doesn't seem to expose draco.AttributeMetadata, however it seems to + // create such objects automatically from draco.Metadata object. + const dracoAttributeMetadata = new this.draco.Metadata(); + this._populateDracoMetadata(dracoAttributeMetadata, metadata); + // Draco3d doc note: Directly add attribute metadata to geometry. + // You can do this without explicitly adding |GeometryMetadata| to mesh. + this.dracoMeshBuilder.SetMetadataForAttribute(dracoGeometry, uniqueAttributeId, dracoAttributeMetadata); + } + /** + * Add contents of object or map to a WASM Draco Metadata Object + * @param dracoMetadata - WASM Draco Object + * @param metadata + */ + _populateDracoMetadata(dracoMetadata, metadata) { + for (const [key, value] of getEntries(metadata)) { + switch (typeof value) { + case "number": + if (Math.trunc(value) === value) { + this.dracoMetadataBuilder.AddIntEntry(dracoMetadata, key, value); + } + else { + this.dracoMetadataBuilder.AddDoubleEntry(dracoMetadata, key, value); + } + break; + case "object": + if (value instanceof Int32Array) { + this.dracoMetadataBuilder.AddIntEntryArray(dracoMetadata, key, value, value.length); + } + break; + case "string": + default: + this.dracoMetadataBuilder.AddStringEntry(dracoMetadata, key, value); + } + } + } +} +// HELPER FUNCTIONS +/** + * Copy encoded data to buffer + * @param dracoData + */ +function dracoInt8ArrayToArrayBuffer(dracoData) { + const byteLength = dracoData.size(); + const outputBuffer = new ArrayBuffer(byteLength); + const outputData = new Int8Array(outputBuffer); + for (let i = 0; i < byteLength; ++i) { + outputData[i] = dracoData.GetValue(i); + } + return outputBuffer; +} +/** Enable iteration over either an object or a map */ +function getEntries(container) { + const hasEntriesFunc = container.entries && !container.hasOwnProperty("entries"); + return hasEntriesFunc ? container.entries() : Object.entries(container); +} +//# sourceMappingURL=draco-builder.js.map \ No newline at end of file diff --git a/@flywave/flywave-draco/src/loader/draco-module-loader.d.ts b/@flywave/flywave-draco/src/loader/draco-module-loader.d.ts new file mode 100644 index 0000000..1261985 --- /dev/null +++ b/@flywave/flywave-draco/src/loader/draco-module-loader.d.ts @@ -0,0 +1,18 @@ +export declare const DRACO_EXTERNAL_LIBRARIES: { + /** The primary Draco3D encoder, javascript wrapper part */ + DECODER: string; + /** The primary draco decoder, compiled web assembly part */ + DECODER_WASM: string; + /** Fallback decoder for non-webassebly environments. Very big bundle, lower performance */ + FALLBACK_DECODER: string; + /** Draco encoder */ + ENCODER: string; +}; +export declare const DRACO_EXTERNAL_LIBRARY_URLS: { + [DRACO_EXTERNAL_LIBRARIES.DECODER]: string; + [DRACO_EXTERNAL_LIBRARIES.DECODER_WASM]: string; + [DRACO_EXTERNAL_LIBRARIES.FALLBACK_DECODER]: string; + [DRACO_EXTERNAL_LIBRARIES.ENCODER]: string; +}; +export declare function loadDracoDecoderModule(options: any): Promise; +export declare function loadDracoEncoderModule(options: any): Promise; diff --git a/@flywave/flywave-draco/src/loader/draco-module-loader.js b/@flywave/flywave-draco/src/loader/draco-module-loader.js new file mode 100644 index 0000000..6b7f791 --- /dev/null +++ b/@flywave/flywave-draco/src/loader/draco-module-loader.js @@ -0,0 +1,106 @@ +/* Copyright (C) 2025 flywave.gl contributors */ +// Dynamic DRACO module loading inspired by THREE.DRACOLoader +// https://github.com/mrdoob/three.js/blob/398c4f39ebdb8b23eefd4a7a5ec49ec0c96c7462/examples/jsm/loaders/DRACOLoader.js +// by Don McCurdy / https://www.donmccurdy.com / MIT license +import { loadLibrary } from "@flywave/flywave-utils"; +const DRACO_DECODER_VERSION = "1.5.6"; +const DRACO_ENCODER_VERSION = "1.4.1"; +const STATIC_DECODER_URL = `https://www.gstatic.com/draco/versioned/decoders/${DRACO_DECODER_VERSION}`; +export const DRACO_EXTERNAL_LIBRARIES = { + /** The primary Draco3D encoder, javascript wrapper part */ + DECODER: "draco_wasm_wrapper.js", + /** The primary draco decoder, compiled web assembly part */ + DECODER_WASM: "draco_decoder.wasm", + /** Fallback decoder for non-webassebly environments. Very big bundle, lower performance */ + FALLBACK_DECODER: "draco_decoder.js", + /** Draco encoder */ + ENCODER: "draco_encoder.js" +}; +export const DRACO_EXTERNAL_LIBRARY_URLS = { + [DRACO_EXTERNAL_LIBRARIES.DECODER]: `${STATIC_DECODER_URL}/${DRACO_EXTERNAL_LIBRARIES.DECODER}`, + [DRACO_EXTERNAL_LIBRARIES.DECODER_WASM]: `${STATIC_DECODER_URL}/${DRACO_EXTERNAL_LIBRARIES.DECODER_WASM}`, + [DRACO_EXTERNAL_LIBRARIES.FALLBACK_DECODER]: `${STATIC_DECODER_URL}/${DRACO_EXTERNAL_LIBRARIES.FALLBACK_DECODER}`, + [DRACO_EXTERNAL_LIBRARIES.ENCODER]: `https://raw.githubusercontent.com/google/draco/${DRACO_ENCODER_VERSION}/javascript/${DRACO_EXTERNAL_LIBRARIES.ENCODER}` +}; +let loadDecoderPromise; +let loadEncoderPromise; +export async function loadDracoDecoderModule(options) { + const modules = options.modules || {}; + // Check if a bundled draco3d library has been supplied by application + if (modules.draco3d) { + loadDecoderPromise || (loadDecoderPromise = modules.draco3d.createDecoderModule({}).then(draco => { + return { draco }; + })); + } + else { + // If not, dynamically load the WASM script from our CDN + loadDecoderPromise || (loadDecoderPromise = loadDracoDecoder(options)); + } + return loadDecoderPromise; +} +export async function loadDracoEncoderModule(options) { + const modules = options.modules || {}; + // Check if a bundled draco3d library has been supplied by application + if (modules.draco3d) { + loadEncoderPromise || (loadEncoderPromise = modules.draco3d.createEncoderModule({}).then(draco => { + return { draco }; + })); + } + else { + // If not, dynamically load the WASM script from our CDN + loadEncoderPromise || (loadEncoderPromise = loadDracoEncoder(options)); + } + return loadEncoderPromise; +} +// DRACO DECODER LOADING +async function loadDracoDecoder(options) { + let DracoDecoderModule; + let wasmBinary; + const DRACO_EXTERNAL_LIBRARY_URLS_ = options.draco?.libraryPath + ? { + [DRACO_EXTERNAL_LIBRARIES.DECODER]: `${options.draco.libraryPath}/${DRACO_EXTERNAL_LIBRARIES.DECODER}`, + [DRACO_EXTERNAL_LIBRARIES.DECODER_WASM]: `${options.draco.libraryPath}/${DRACO_EXTERNAL_LIBRARIES.DECODER_WASM}`, + [DRACO_EXTERNAL_LIBRARIES.FALLBACK_DECODER]: `${options.draco.libraryPath}/${DRACO_EXTERNAL_LIBRARIES.FALLBACK_DECODER}`, + [DRACO_EXTERNAL_LIBRARIES.ENCODER]: `${options.draco.libraryPath}/${DRACO_EXTERNAL_LIBRARIES.ENCODER}` + } + : DRACO_EXTERNAL_LIBRARY_URLS; + switch (options.draco && options.draco.decoderType) { + case "js": + DracoDecoderModule = await loadLibrary(DRACO_EXTERNAL_LIBRARY_URLS_[DRACO_EXTERNAL_LIBRARIES.FALLBACK_DECODER], "draco", options, DRACO_EXTERNAL_LIBRARIES.FALLBACK_DECODER); + break; + case "wasm": + default: + [DracoDecoderModule, wasmBinary] = await Promise.all([ + await loadLibrary(DRACO_EXTERNAL_LIBRARY_URLS_[DRACO_EXTERNAL_LIBRARIES.DECODER], "draco", options, DRACO_EXTERNAL_LIBRARIES.DECODER), + await loadLibrary(DRACO_EXTERNAL_LIBRARY_URLS_[DRACO_EXTERNAL_LIBRARIES.DECODER_WASM], "draco", options, DRACO_EXTERNAL_LIBRARIES.DECODER_WASM) + ]); + } + // Depends on how import happened... + // @ts-ignore + DracoDecoderModule = DracoDecoderModule || globalThis.DracoDecoderModule; + return await initializeDracoDecoder(DracoDecoderModule, wasmBinary); +} +function initializeDracoDecoder(DracoDecoderModule, wasmBinary) { + const options = {}; + if (wasmBinary) { + options.wasmBinary = wasmBinary; + } + return new Promise(resolve => { + DracoDecoderModule({ + ...options, + onModuleLoaded: draco => resolve({ draco }) // Module is Promise-like. Wrap in object to avoid loop. + }); + }); +} +// ENCODER +async function loadDracoEncoder(options) { + let DracoEncoderModule = await loadLibrary(DRACO_EXTERNAL_LIBRARY_URLS[DRACO_EXTERNAL_LIBRARIES.ENCODER], "draco", options, DRACO_EXTERNAL_LIBRARIES.ENCODER); + // @ts-ignore + DracoEncoderModule = DracoEncoderModule || globalThis.DracoEncoderModule; + return await new Promise(resolve => { + DracoEncoderModule({ + onModuleLoaded: draco => resolve({ draco }) // Module is Promise-like. Wrap in object to avoid loop. + }); + }); +} +//# sourceMappingURL=draco-module-loader.js.map \ No newline at end of file diff --git a/@flywave/flywave-draco/src/loader/draco-parser.d.ts b/@flywave/flywave-draco/src/loader/draco-parser.d.ts new file mode 100644 index 0000000..f77b6d4 --- /dev/null +++ b/@flywave/flywave-draco/src/loader/draco-parser.d.ts @@ -0,0 +1,137 @@ +import * as THREE from "three"; +import type { Decoder, Draco3D, Mesh, Metadata, MetadataQuerier, PointAttribute, PointCloud } from "../draco3d/draco3d-types"; +import type { DracoAttribute, DracoLoaderData, DracoMesh, DracoMetadataEntry, DracoOctahedronTransform, DracoQuantizationTransform } from "./draco-types"; +/** Options to control draco parsing */ +export interface DracoParseOptions { + /** How triangle indices should be generated (mesh only) */ + topology?: "triangle-list" | "triangle-strip"; + /** Specify which attribute metadata entry stores the attribute name */ + attributeNameEntry?: string; + /** Names and ids of extra attributes to include in the output */ + extraAttributes?: { + [uniqueId: string]: number; + }; + /** Skip transforms specific quantized attributes */ + quantizedAttributes?: Array<"POSITION" | "NORMAL" | "COLOR" | "TEX_COORD" | "GENERIC">; + /** Skip transforms specific octahedron encoded attributes */ + octahedronAttributes?: Array<"POSITION" | "NORMAL" | "COLOR" | "TEX_COORD" | "GENERIC">; + /** Attribute name mapping */ + attributeMap?: { + [key: string]: string; + }; +} +export default class DracoParser { + draco: Draco3D; + decoder: Decoder; + metadataQuerier: MetadataQuerier; + constructor(draco: Draco3D); + /** + * Destroy draco resources + */ + destroy(): void; + /** + * NOTE: caller must call `destroyGeometry` on the return value after using it + * @param arrayBuffer + * @param options + */ + parseSync(arrayBuffer: ArrayBuffer, options?: DracoParseOptions): DracoMesh; + /** + * Extract + * @param dracoGeometry + * @param geometry_type + * @param options + * @returns + */ + _getDracoLoaderData(dracoGeometry: Mesh | PointCloud, geometry_type: any, options: DracoParseOptions): DracoLoaderData; + /** + * Extract all draco provided information and metadata for each attribute + * @param dracoGeometry + * @param options + * @returns + */ + _getDracoAttributes(dracoGeometry: Mesh | PointCloud, options: DracoParseOptions): { + [unique_id: number]: DracoAttribute; + }; + /** + * Get standard loaders.gl mesh category data + * Extracts the geometry from draco + * @param dracoGeometry + * @param options + */ + _getMeshData(dracoGeometry: Mesh | PointCloud, loaderData: DracoLoaderData, options: DracoParseOptions): THREE.BufferGeometry; + private _getMeshAttributes; + /** + * For meshes, we need indices to define the faces. + * @param dracoGeometry + */ + _getTriangleListIndices(dracoGeometry: Mesh): Uint32Array; + /** + * For meshes, we need indices to define the faces. + * @param dracoGeometry + */ + _getTriangleStripIndices(dracoGeometry: Mesh): Int32Array; + /** + * + * @param dracoGeometry + * @param dracoAttribute + * @param attributeName + */ + _getAttributeValues(dracoGeometry: Mesh | PointCloud, attribute: DracoAttribute): { + value: THREE.TypedArray; + size: number; + } | null; + /** + * DRACO does not store attribute names - We need to deduce an attribute name + * for each attribute + _getAttributeNames( + dracoGeometry: Mesh | PointCloud, + options: DracoParseOptions + ): {[unique_id: number]: string} { + const attributeNames: {[unique_id: number]: string} = {}; + for (let attributeId = 0; attributeId < dracoGeometry.num_attributes(); attributeId++) { + const dracoAttribute = this.decoder.GetAttribute(dracoGeometry, attributeId); + const attributeName = this._deduceAttributeName(dracoAttribute, options); + attributeNames[attributeName] = attributeName; + } + return attributeNames; + } + */ + /** + * Deduce an attribute name. + * @note DRACO does not save attribute names, just general type (POSITION, COLOR) + * to help optimize compression. We generate GLTF compatible names for the Draco-recognized + * types + * @param attributeData + */ + _deduceAttributeName(attribute: DracoAttribute, options: DracoParseOptions): string; + /** Get top level metadata */ + _getTopLevelMetadata(dracoGeometry: Mesh | PointCloud): { + [entry: string]: DracoMetadataEntry; + }; + /** Get per attribute metadata */ + _getAttributeMetadata(dracoGeometry: Mesh | PointCloud, attributeId: number): { + [entry: string]: DracoMetadataEntry; + }; + /** + * Extract metadata field values + * @param dracoMetadata + * @returns + */ + _getDracoMetadata(dracoMetadata: Metadata): { + [entry: string]: DracoMetadataEntry; + }; + /** + * Extracts possible values for one metadata entry by name + * @param dracoMetadata + * @param entryName + */ + _getDracoMetadataField(dracoMetadata: Metadata, entryName: string): DracoMetadataEntry; + /** Skip transforms for specific attribute types */ + _disableAttributeTransforms(options: DracoParseOptions): void; + /** + * Extract (and apply?) Position Transform + * @todo not used + */ + _getQuantizationTransform(dracoAttribute: PointAttribute, options: DracoParseOptions): DracoQuantizationTransform | null; + _getOctahedronTransform(dracoAttribute: PointAttribute, options: DracoParseOptions): DracoOctahedronTransform | null; +} diff --git a/@flywave/flywave-draco/src/loader/draco-parser.js b/@flywave/flywave-draco/src/loader/draco-parser.js new file mode 100644 index 0000000..92d9e25 --- /dev/null +++ b/@flywave/flywave-draco/src/loader/draco-parser.js @@ -0,0 +1,471 @@ +/* Copyright (C) 2025 flywave.gl contributors */ +import * as THREE from "three"; +import { getDracoSchema } from "./utils/get-draco-schema"; +// @ts-ignore +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const GEOMETRY_TYPE = { + TRIANGULAR_MESH: 0, + POINT_CLOUD: 1 +}; +// Native Draco attribute names to GLTF attribute names. +const DRACO_TO_GLTF_ATTRIBUTE_NAME_MAP = { + POSITION: "POSITION", + NORMAL: "NORMAL", + COLOR: "COLOR_0", + TEX_COORD: "TEXCOORD_0" +}; +const DRACO_DATA_TYPE_TO_TYPED_ARRAY_MAP = { + 1: Int8Array, + 2: Uint8Array, + 3: Int16Array, + 4: Uint16Array, + 5: Int32Array, + 6: Uint32Array, + // 7: BigInt64Array, + // 8: BigUint64Array, + 9: Float32Array + // 10: Float64Array + // 11: BOOL - What array type do we use for this? +}; +const INDEX_ITEM_SIZE = 4; +export default class DracoParser { + // draco - the draco decoder, either import `draco3d` or load dynamically + constructor(draco) { + this.draco = draco; + this.decoder = new this.draco.Decoder(); + this.metadataQuerier = new this.draco.MetadataQuerier(); + } + /** + * Destroy draco resources + */ + destroy() { + this.draco.destroy(this.decoder); + this.draco.destroy(this.metadataQuerier); + } + /** + * NOTE: caller must call `destroyGeometry` on the return value after using it + * @param arrayBuffer + * @param options + */ + parseSync(arrayBuffer, options = {}) { + const buffer = new this.draco.DecoderBuffer(); + buffer.Init(new Int8Array(arrayBuffer), arrayBuffer.byteLength); + this._disableAttributeTransforms(options); + const geometry_type = this.decoder.GetEncodedGeometryType(buffer); + const dracoGeometry = geometry_type === this.draco.TRIANGULAR_MESH + ? new this.draco.Mesh() + : new this.draco.PointCloud(); + try { + let dracoStatus; + switch (geometry_type) { + case this.draco.TRIANGULAR_MESH: + dracoStatus = this.decoder.DecodeBufferToMesh(buffer, dracoGeometry); + break; + case this.draco.POINT_CLOUD: + dracoStatus = this.decoder.DecodeBufferToPointCloud(buffer, dracoGeometry); + break; + default: + throw new Error("DRACO: Unknown geometry type."); + } + if (!dracoStatus.ok() || !dracoGeometry.ptr) { + const message = `DRACO decompression failed: ${dracoStatus.error_msg()}`; + // console.error(message); + throw new Error(message); + } + const loaderData = this._getDracoLoaderData(dracoGeometry, geometry_type, options); + const geometry = this._getMeshData(dracoGeometry, loaderData, options); + const boundingBox = geometry.boundingBox; + // Pass additional attribute mapping options + const schema = getDracoSchema(geometry, loaderData); + const data = { + loader: "draco", + loaderData, + header: { + vertexCount: dracoGeometry.num_points(), + boundingBox + }, + geometry, + schema + }; + return data; + } + finally { + this.draco.destroy(buffer); + if (dracoGeometry) { + this.draco.destroy(dracoGeometry); + } + } + } + // Draco specific "loader data" + /** + * Extract + * @param dracoGeometry + * @param geometry_type + * @param options + * @returns + */ + _getDracoLoaderData(dracoGeometry, geometry_type, options) { + const metadata = this._getTopLevelMetadata(dracoGeometry); + const attributes = this._getDracoAttributes(dracoGeometry, options); + return { + geometry_type, + num_attributes: dracoGeometry.num_attributes(), + num_points: dracoGeometry.num_points(), + num_faces: dracoGeometry instanceof this.draco.Mesh ? dracoGeometry.num_faces() : 0, + metadata, + attributes + }; + } + /** + * Extract all draco provided information and metadata for each attribute + * @param dracoGeometry + * @param options + * @returns + */ + _getDracoAttributes(dracoGeometry, options) { + const dracoAttributes = {}; + for (let attributeId = 0; attributeId < dracoGeometry.num_attributes(); attributeId++) { + // Note: Draco docs do not seem clear on `GetAttribute` ids just being a zero-based index, + // but it does seems to work this way + const dracoAttribute = this.decoder.GetAttribute(dracoGeometry, attributeId); + const metadata = this._getAttributeMetadata(dracoGeometry, attributeId); + dracoAttributes[dracoAttribute.unique_id()] = { + unique_id: dracoAttribute.unique_id(), + attribute_type: dracoAttribute.attribute_type(), + data_type: dracoAttribute.data_type(), + num_components: dracoAttribute.num_components(), + byte_offset: dracoAttribute.byte_offset(), + byte_stride: dracoAttribute.byte_stride(), + normalized: dracoAttribute.normalized(), + attribute_index: attributeId, + metadata + }; + // Add transformation parameters for any attributes app wants untransformed + const quantization = this._getQuantizationTransform(dracoAttribute, options); + if (quantization) { + dracoAttributes[dracoAttribute.unique_id()].quantization_transform = quantization; + } + const octahedron = this._getOctahedronTransform(dracoAttribute, options); + if (octahedron) { + dracoAttributes[dracoAttribute.unique_id()].octahedron_transform = octahedron; + } + } + return dracoAttributes; + } + /** + * Get standard loaders.gl mesh category data + * Extracts the geometry from draco + * @param dracoGeometry + * @param options + */ + _getMeshData(dracoGeometry, loaderData, options) { + const geometry = new THREE.BufferGeometry(); + const attributes = this._getMeshAttributes(loaderData, dracoGeometry, options); + // Set geometry attributes + for (const [name, attribute] of Object.entries(attributes)) { + geometry.setAttribute(name.toLowerCase(), attribute); + } + // Process mesh indices + if (dracoGeometry instanceof this.draco.Mesh) { + let indices; + switch (options.topology) { + case "triangle-strip": + indices = new THREE.BufferAttribute(this._getTriangleStripIndices(dracoGeometry), 1); + geometry.setIndex(indices); + geometry.setDrawRange(0, indices.count); + break; + case "triangle-list": + default: + indices = new THREE.BufferAttribute(this._getTriangleListIndices(dracoGeometry), 1); + geometry.setIndex(indices); + break; + } + } + // Automatically calculate normals and bounding box + geometry.computeVertexNormals(); + geometry.computeBoundingBox(); + return geometry; + } + _getMeshAttributes(loaderData, dracoGeometry, options) { + const attributes = {}; + for (const loaderAttribute of Object.values(loaderData.attributes)) { + const attributeName = this._deduceAttributeName(loaderAttribute, options); + const values = this._getAttributeValues(dracoGeometry, loaderAttribute); + if (values) { + // Convert attribute names to Three.js specification + const threeName = DRACO_TO_GLTF_ATTRIBUTE_NAME_MAP[attributeName]?.toLowerCase() || + attributeName.toLowerCase(); + attributes[threeName] = new THREE.BufferAttribute(values.value, values.size); + } + } + return attributes; + } + // MESH INDICES EXTRACTION + /** + * For meshes, we need indices to define the faces. + * @param dracoGeometry + */ + _getTriangleListIndices(dracoGeometry) { + // Example on how to retrieve mesh and attributes. + const numFaces = dracoGeometry.num_faces(); + const numIndices = numFaces * 3; + const byteLength = numIndices * INDEX_ITEM_SIZE; + const ptr = this.draco._malloc(byteLength); + try { + this.decoder.GetTrianglesUInt32Array(dracoGeometry, byteLength, ptr); + return new Uint32Array(this.draco.HEAPF32.buffer, ptr, numIndices).slice(); + } + finally { + this.draco._free(ptr); + } + } + /** + * For meshes, we need indices to define the faces. + * @param dracoGeometry + */ + _getTriangleStripIndices(dracoGeometry) { + const dracoArray = new this.draco.DracoInt32Array(); + try { + /* const numStrips = */ this.decoder.GetTriangleStripsFromMesh(dracoGeometry, dracoArray); + return getUint32Array(dracoArray); + } + finally { + this.draco.destroy(dracoArray); + } + } + /** + * + * @param dracoGeometry + * @param dracoAttribute + * @param attributeName + */ + _getAttributeValues(dracoGeometry, attribute) { + const TypedArrayCtor = DRACO_DATA_TYPE_TO_TYPED_ARRAY_MAP[attribute.data_type]; + if (!TypedArrayCtor) { + // eslint-disable-next-line no-console + console.warn(`DRACO: Unsupported attribute type ${attribute.data_type}`); + return null; + } + const numComponents = attribute.num_components; + const numPoints = dracoGeometry.num_points(); + const numValues = numPoints * numComponents; + const byteLength = numValues * TypedArrayCtor.BYTES_PER_ELEMENT; + const dataType = getDracoDataType(this.draco, TypedArrayCtor); + let value; + const ptr = this.draco._malloc(byteLength); + try { + const dracoAttribute = this.decoder.GetAttribute(dracoGeometry, attribute.attribute_index); + this.decoder.GetAttributeDataArrayForAllPoints(dracoGeometry, dracoAttribute, dataType, byteLength, ptr); + value = new TypedArrayCtor(this.draco.HEAPF32.buffer, ptr, numValues).slice(); + } + finally { + this.draco._free(ptr); + } + return { value, size: numComponents }; + } + // Attribute names + /** + * DRACO does not store attribute names - We need to deduce an attribute name + * for each attribute + _getAttributeNames( + dracoGeometry: Mesh | PointCloud, + options: DracoParseOptions + ): {[unique_id: number]: string} { + const attributeNames: {[unique_id: number]: string} = {}; + for (let attributeId = 0; attributeId < dracoGeometry.num_attributes(); attributeId++) { + const dracoAttribute = this.decoder.GetAttribute(dracoGeometry, attributeId); + const attributeName = this._deduceAttributeName(dracoAttribute, options); + attributeNames[attributeName] = attributeName; + } + return attributeNames; + } + */ + /** + * Deduce an attribute name. + * @note DRACO does not save attribute names, just general type (POSITION, COLOR) + * to help optimize compression. We generate GLTF compatible names for the Draco-recognized + * types + * @param attributeData + */ + _deduceAttributeName(attribute, options) { + // Deduce name based on application provided map + const uniqueId = attribute.unique_id; + for (const [attributeName, attributeUniqueId] of Object.entries(options.extraAttributes || {})) { + if (attributeUniqueId === uniqueId) { + return attributeName; + } + } + // Deduce name based on attribute type + const thisAttributeType = attribute.attribute_type; + for (const dracoAttributeConstant in DRACO_TO_GLTF_ATTRIBUTE_NAME_MAP) { + const attributeType = this.draco[dracoAttributeConstant]; + if (attributeType === thisAttributeType) { + // TODO - Return unique names if there multiple attributes per type + // (e.g. multiple TEX_COORDS or COLORS) + return DRACO_TO_GLTF_ATTRIBUTE_NAME_MAP[dracoAttributeConstant]; + } + } + // Look up in metadata + // TODO - shouldn't this have priority? + const entryName = options.attributeNameEntry || "name"; + if (attribute.metadata[entryName]) { + return attribute.metadata[entryName].string; + } + // Attribute of "GENERIC" type, we need to assign some name + return `CUSTOM_ATTRIBUTE_${uniqueId}`; + } + // METADATA EXTRACTION + /** Get top level metadata */ + _getTopLevelMetadata(dracoGeometry) { + const dracoMetadata = this.decoder.GetMetadata(dracoGeometry); + return this._getDracoMetadata(dracoMetadata); + } + /** Get per attribute metadata */ + _getAttributeMetadata(dracoGeometry, attributeId) { + const dracoMetadata = this.decoder.GetAttributeMetadata(dracoGeometry, attributeId); + return this._getDracoMetadata(dracoMetadata); + } + /** + * Extract metadata field values + * @param dracoMetadata + * @returns + */ + _getDracoMetadata(dracoMetadata) { + // The not so wonderful world of undocumented Draco APIs :( + if (!dracoMetadata || !dracoMetadata.ptr) { + return {}; + } + const result = {}; + const numEntries = this.metadataQuerier.NumEntries(dracoMetadata); + for (let entryIndex = 0; entryIndex < numEntries; entryIndex++) { + const entryName = this.metadataQuerier.GetEntryName(dracoMetadata, entryIndex); + result[entryName] = this._getDracoMetadataField(dracoMetadata, entryName); + } + return result; + } + /** + * Extracts possible values for one metadata entry by name + * @param dracoMetadata + * @param entryName + */ + _getDracoMetadataField(dracoMetadata, entryName) { + const dracoArray = new this.draco.DracoInt32Array(); + try { + // Draco metadata fields can hold int32 arrays + this.metadataQuerier.GetIntEntryArray(dracoMetadata, entryName, dracoArray); + const intArray = getInt32Array(dracoArray); + return { + int: this.metadataQuerier.GetIntEntry(dracoMetadata, entryName), + string: this.metadataQuerier.GetStringEntry(dracoMetadata, entryName), + double: this.metadataQuerier.GetDoubleEntry(dracoMetadata, entryName), + intArray + }; + } + finally { + this.draco.destroy(dracoArray); + } + } + // QUANTIZED ATTRIBUTE SUPPORT (NO DECOMPRESSION) + /** Skip transforms for specific attribute types */ + _disableAttributeTransforms(options) { + const { quantizedAttributes = [], octahedronAttributes = [] } = options; + const skipAttributes = [...quantizedAttributes, ...octahedronAttributes]; + for (const dracoAttributeName of skipAttributes) { + this.decoder.SkipAttributeTransform(this.draco[dracoAttributeName]); + } + } + /** + * Extract (and apply?) Position Transform + * @todo not used + */ + _getQuantizationTransform(dracoAttribute, options) { + const { quantizedAttributes = [] } = options; + const attribute_type = dracoAttribute.attribute_type(); + const skip = quantizedAttributes.map(type => this.decoder[type]).includes(attribute_type); + if (skip) { + const transform = new this.draco.AttributeQuantizationTransform(); + try { + if (transform.InitFromAttribute(dracoAttribute)) { + return { + quantization_bits: transform.quantization_bits(), + range: transform.range(), + min_values: new Float32Array([1, 2, 3]).map(i => transform.min_value(i)) + }; + } + } + finally { + this.draco.destroy(transform); + } + } + return null; + } + _getOctahedronTransform(dracoAttribute, options) { + const { octahedronAttributes = [] } = options; + const attribute_type = dracoAttribute.attribute_type(); + const octahedron = octahedronAttributes + .map(type => this.decoder[type]) + .includes(attribute_type); + if (octahedron) { + const transform = new this.draco.AttributeQuantizationTransform(); + try { + if (transform.InitFromAttribute(dracoAttribute)) { + return { + quantization_bits: transform.quantization_bits() + }; + } + } + finally { + this.draco.destroy(transform); + } + } + return null; + } +} +/** + * Get draco specific data type by TypedArray constructor type + * @param attributeType + * @returns draco specific data type + */ +function getDracoDataType(draco, attributeType) { + switch (attributeType) { + case Float32Array: + return draco.DT_FLOAT32; + case Int8Array: + return draco.DT_INT8; + case Int16Array: + return draco.DT_INT16; + case Int32Array: + return draco.DT_INT32; + case Uint8Array: + return draco.DT_UINT8; + case Uint16Array: + return draco.DT_UINT16; + case Uint32Array: + return draco.DT_UINT32; + default: + return draco.DT_INVALID; + } +} +/** + * Copy a Draco int32 array into a JS typed array + */ +function getInt32Array(dracoArray) { + const numValues = dracoArray.size(); + const intArray = new Int32Array(numValues); + for (let i = 0; i < numValues; i++) { + intArray[i] = dracoArray.GetValue(i); + } + return intArray; +} +/** + * Copy a Draco int32 array into a JS typed array + */ +function getUint32Array(dracoArray) { + const numValues = dracoArray.size(); + const intArray = new Int32Array(numValues); + for (let i = 0; i < numValues; i++) { + intArray[i] = dracoArray.GetValue(i); + } + return intArray; +} +//# sourceMappingURL=draco-parser.js.map \ No newline at end of file diff --git a/@flywave/flywave-draco/src/loader/draco-types.d.ts b/@flywave/flywave-draco/src/loader/draco-types.d.ts new file mode 100644 index 0000000..c11125f --- /dev/null +++ b/@flywave/flywave-draco/src/loader/draco-types.d.ts @@ -0,0 +1,70 @@ +import * as THREE from "three"; +export interface DracoMetadataEntry { + int: number; + string: string; + double: number; + intArray: Int32Array; +} +/** For attributes that have not been fully decompressed */ +export interface DracoQuantizationTransform { + quantization_bits?: number; + range?: number; + min_values?: Float32Array; +} +/** For attributes that have not been fully decompressed */ +export interface DracoOctahedronTransform { + quantization_bits?: number; +} +/** Draco attribute fields */ +export interface DracoAttribute { + unique_id: number; + num_components: number; + attribute_type: number; + data_type: number; + byte_offset: number; + byte_stride: number; + normalized: boolean; + name?: string; + quantization_transform?: DracoQuantizationTransform; + octahedron_transform?: DracoOctahedronTransform; + metadata: { + [key: string]: DracoMetadataEntry; + }; + attribute_index: number; +} +/** + * Draco format specific data + * The `data.loaderData` field will have this layout when `data.loader === 'draco'`. + * @todo Metadata should also be available in normalized form in a standard `Schema`. + */ +export interface DracoLoaderData { + geometry_type: number; + num_attributes: number; + num_points: number; + num_faces: number; + metadata: { + [entry: string]: DracoMetadataEntry; + }; + attributes: { + [unique_id: number]: DracoAttribute; + }; +} +/** + * Mesh with Draco specific data + */ +export interface DracoMesh { + loader: "draco"; + loaderData: DracoLoaderData; + geometry: THREE.BufferGeometry; + header: { + vertexCount: number; + boundingBox: THREE.Box3; + }; + schema: { + attributes: { + [name: string]: THREE.BufferAttribute; + }; + index?: THREE.BufferAttribute; + metadata: Record; + }; +} diff --git a/@flywave/flywave-draco/src/loader/draco-types.js b/@flywave/flywave-draco/src/loader/draco-types.js new file mode 100644 index 0000000..c01294e --- /dev/null +++ b/@flywave/flywave-draco/src/loader/draco-types.js @@ -0,0 +1,3 @@ +/* Copyright (C) 2025 flywave.gl contributors */ +export {}; +//# sourceMappingURL=draco-types.js.map \ No newline at end of file diff --git a/@flywave/flywave-draco/src/loader/utils/get-draco-schema.d.ts b/@flywave/flywave-draco/src/loader/utils/get-draco-schema.d.ts new file mode 100644 index 0000000..4783ad9 --- /dev/null +++ b/@flywave/flywave-draco/src/loader/utils/get-draco-schema.d.ts @@ -0,0 +1,11 @@ +import * as THREE from "three"; +/** Extract an arrow-like schema from a Draco mesh */ +interface DracoSchema { + attributes: { + [name: string]: THREE.BufferAttribute; + }; + index?: THREE.BufferAttribute; + metadata: Record; +} +export declare function getDracoSchema(geometry: THREE.BufferGeometry, loaderData: any): DracoSchema; +export {}; diff --git a/@flywave/flywave-draco/src/loader/utils/get-draco-schema.js b/@flywave/flywave-draco/src/loader/utils/get-draco-schema.js new file mode 100644 index 0000000..62454c5 --- /dev/null +++ b/@flywave/flywave-draco/src/loader/utils/get-draco-schema.js @@ -0,0 +1,38 @@ +/* Copyright (C) 2025 flywave.gl contributors */ +import * as THREE from "three"; +export function getDracoSchema(geometry, loaderData) { + const schema = { + attributes: {}, + metadata: makeMetadata(loaderData.metadata) + }; + // Process vertex attributes + geometry.attributes = geometry.attributes || {}; + for (const [name, attribute] of Object.entries(geometry.attributes)) { + if (attribute instanceof THREE.BufferAttribute) { + schema.attributes[name] = createThreeAttribute(name, attribute, loaderData.attributes[name]); + } + } + // Process indices + if (geometry.index) { + schema.index = createThreeIndex(geometry.index); + } + return schema; +} +function createThreeAttribute(name, attribute, loaderData) { + return new THREE.BufferAttribute(attribute.array, attribute.itemSize, attribute.normalized).setUsage(THREE.StaticDrawUsage); +} +function createThreeIndex(index) { + return new THREE.BufferAttribute(index.array, 1 // The itemSize of the index is fixed to 1 + ).setUsage(THREE.StaticDrawUsage); +} +function makeMetadata(metadata) { + const result = {}; + for (const key in metadata) { + result[key] = { + value: metadata[key].value, + type: typeof metadata[key].value + }; + } + return result; +} +//# sourceMappingURL=get-draco-schema.js.map \ No newline at end of file diff --git a/@flywave/flywave-draco/src/loader/utils/version.d.ts b/@flywave/flywave-draco/src/loader/utils/version.d.ts new file mode 100644 index 0000000..7594755 --- /dev/null +++ b/@flywave/flywave-draco/src/loader/utils/version.d.ts @@ -0,0 +1 @@ +export declare const VERSION: any; diff --git a/@flywave/flywave-draco/src/loader/utils/version.js b/@flywave/flywave-draco/src/loader/utils/version.js new file mode 100644 index 0000000..990f2c8 --- /dev/null +++ b/@flywave/flywave-draco/src/loader/utils/version.js @@ -0,0 +1,6 @@ +/* Copyright (C) 2025 flywave.gl contributors */ +// Version constant cannot be imported, it needs to correspond to the build version of **this** module. +// __VERSION__ is injected by babel-plugin-version-inline +// @ts-ignore TS2304: Cannot find name '__VERSION__'. +export const VERSION = typeof __VERSION__ !== "undefined" ? __VERSION__ : "latest"; +//# sourceMappingURL=version.js.map \ No newline at end of file diff --git a/@flywave/flywave-examples/codebrowser.d.ts b/@flywave/flywave-examples/codebrowser.d.ts new file mode 100644 index 0000000..4c883b5 --- /dev/null +++ b/@flywave/flywave-examples/codebrowser.d.ts @@ -0,0 +1 @@ +import "style-loader!css-loader!highlight.js/styles/default.css"; diff --git a/@flywave/flywave-examples/codebrowser.js.map b/@flywave/flywave-examples/codebrowser.js.map new file mode 100644 index 0000000..cde0968 --- /dev/null +++ b/@flywave/flywave-examples/codebrowser.js.map @@ -0,0 +1 @@ +{"version":3,"file":"codebrowser.js","sourceRoot":"","sources":["codebrowser.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAEhD,OAAO,yDAAyD,CAAC;AAEjE,OAAO,KAAK,IAAI,MAAM,cAAc,CAAC;AAErC,CAAC,GAAG,EAAE;IACF,IAAI,QAAQ,CAAC,MAAM,KAAK,SAAS,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChE,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;IAC3C,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7D,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC9B,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;IAChD,CAAC;IAED,MAAM,QAAQ,GAAG,gCAAgC,CAAC;IAElD,KAAK,CAAC,GAAG,EAAE,EAAE,WAAW,EAAE,aAAa,EAAE,CAAC;SACrC,IAAI,CAAC,QAAQ,CAAC,EAAE;QACb,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;QACzC,CAAC;QACD,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC;IAC3B,CAAC,CAAC;SACD,IAAI,CAAC,QAAQ,CAAC,EAAE;QACb,MAAM,WAAW,GAAG,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;QACpD,IAAI,CAAC,WAAW,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;QACjD,CAAC;QAED,MAAM,OAAO,GAAG,GAAG,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;QACvD,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC;QAC/D,MAAM,mBAAmB,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;QAErE,IAAI,eAAe,GAAG,mBAAmB,CAAC,KAAK,CAAC;QAChD,eAAe,GAAG,eAAe,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE;YAClE,OAAO,GAAG,EAAE,YAAY,QAAQ,CAAC,QAAQ,QAAQ,kBAAkB,CAC/D,OAAO,GAAG,GAAG,GAAG,EAAE,GAAG,KAAK,CAC7B,KAAK,EAAE,OAAO,EAAE,EAAE,CAAC;QACxB,CAAC,CAAC,CAAC;QAEH,WAAW,CAAC,SAAS,GAAG,eAAe,CAAC;IAC5C,CAAC,CAAC,CAAC;AACX,CAAC,CAAC,EAAE,CAAC"} \ No newline at end of file diff --git a/@flywave/flywave-examples/decoder/decoder.d.ts b/@flywave/flywave-examples/decoder/decoder.d.ts new file mode 100644 index 0000000..1170bfd --- /dev/null +++ b/@flywave/flywave-examples/decoder/decoder.d.ts @@ -0,0 +1 @@ +import "@flywave/flywave.gl/DecoderBundleMain"; diff --git a/@flywave/flywave-examples/decoder/decoder.js.map b/@flywave/flywave-examples/decoder/decoder.js.map new file mode 100644 index 0000000..411788f --- /dev/null +++ b/@flywave/flywave-examples/decoder/decoder.js.map @@ -0,0 +1 @@ +{"version":3,"file":"decoder.js","sourceRoot":"","sources":["decoder.ts"],"names":[],"mappings":"AACA,OAAQ,uCAAuC,CAAC"} \ No newline at end of file diff --git a/@flywave/flywave-examples/example-browser.d.ts b/@flywave/flywave-examples/example-browser.d.ts new file mode 100644 index 0000000..4585ca7 --- /dev/null +++ b/@flywave/flywave-examples/example-browser.d.ts @@ -0,0 +1,11 @@ +type ExampleDefinitions = Record; +/** + * Example browser HTML / DOM app. + */ +declare function exampleBrowser(exampleDefinitions: ExampleDefinitions): void; +/** + * Maps pageUrl to srcUrl. + * + * Loaded using + + + + + + + `; + // 清空容器并添加 iframe + const container = containerRef.current; + while (container.firstChild) { + container.removeChild(container.firstChild); + } + container.appendChild(iframe); + // 写入 iframe 内容 + const iframeDoc = iframe.contentDocument || iframe.contentWindow?.document; + if (iframeDoc) { + iframeDoc.open(); + iframeDoc.write(iframeContent); + iframeDoc.close(); + } + // 清理函数 + return () => { + if (container.firstChild === iframe) { + container.removeChild(iframe); + } + }; + }, []); + return
; +}; +export default FlywaveGlobe; +//# sourceMappingURL=FlywaveGlobe.js.map \ No newline at end of file diff --git a/docs/src/components/FlywaveGlobe.js.map b/docs/src/components/FlywaveGlobe.js.map new file mode 100644 index 0000000..15b5d31 --- /dev/null +++ b/docs/src/components/FlywaveGlobe.js.map @@ -0,0 +1 @@ +{"version":3,"file":"FlywaveGlobe.js","sourceRoot":"","sources":["FlywaveGlobe.tsx"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/docs/src/examples/index.d.ts b/docs/src/examples/index.d.ts new file mode 100644 index 0000000..80f0137 --- /dev/null +++ b/docs/src/examples/index.d.ts @@ -0,0 +1,2 @@ +import type { Example } from "../pages/example-detail"; +export declare const EXAMPLES_CONFIG: Example[]; diff --git a/docs/src/examples/index.js b/docs/src/examples/index.js new file mode 100644 index 0000000..755ed20 --- /dev/null +++ b/docs/src/examples/index.js @@ -0,0 +1,88 @@ +/* Copyright (C) 2025 flywave.gl contributors */ +import { EXAMPLE_CATEGORIES } from "@flywave/flywave-examples/src/example-categories"; +// 动态导入所有示例配置 +const examples = []; +// 通过模块导入方式动态获取示例列表和配置 +// 仅扫描@flywave/flywave-examples/src目录下的示例文件夹 +const exampleContext = require.context("@flywave/flywave-examples/src", true, /config\.ts$/); +// 用于跟踪已添加的示例ID,防止重复 +const addedExampleIds = new Set(); +// 获取当前语言环境 +const getCurrentLocale = () => { + if (typeof window !== 'undefined') { + // 尝试从 URL 获取语言参数 + const urlParams = new URLSearchParams(window.location.search); + const lang = urlParams.get('locale'); + if (lang && (lang === 'zh' || lang === 'en')) + return lang; + // 尝试从 localStorage 获取 + const storedLocale = localStorage.getItem('locale'); + if (storedLocale && (storedLocale === 'zh' || storedLocale === 'en')) + return storedLocale; + // 尝试从 Docusaurus 的语言路径获取 + if (window.location.pathname.startsWith('/zh/')) { + return 'zh'; + } + // 检查 HTML 根元素的语言属性 + const htmlLang = document.documentElement.lang; + if (htmlLang && (htmlLang.includes('zh') || htmlLang.includes('en'))) { + return htmlLang.includes('zh') ? 'zh' : 'en'; + } + } + return 'en'; // 默认语言 +}; +// 动态导入每个示例的配置和代码 +exampleContext.keys().forEach((configKey) => { + try { + // 确保只处理@flywave/flywave-examples/src目录下的config.ts文件 + if (configKey.startsWith("./") && configKey.includes("/config.ts")) { + // 从路径中提取示例名称 + const examplePath = configKey.replace("./", "").replace("/config.ts", ""); + const pathParts = examplePath.split("/"); + const exampleId = pathParts[0]; + // 检查示例是否已经添加过,防止重复 + if (exampleId && exampleId !== "src" && !addedExampleIds.has(exampleId)) { + // 导入配置文件 + const config = exampleContext(configKey).default; + // 获取编译后的示例代码作为原始字符串资源 + let code = ""; + try { + const jsModule = require(`!!raw-loader!@flywave/flywave-examples/src/${exampleId}/index.js`); + code = jsModule.default || jsModule; + } + catch (codeError) { + console.warn(`无法获取示例代码: ${exampleId}`, codeError); + } + // 根据当前语言环境决定使用哪种语言的标题和描述 + const currentLocale = getCurrentLocale(); + const title = currentLocale === 'zh' && config.titleZh ? config.titleZh : config.title; + const description = currentLocale === 'zh' && config.descriptionZh ? config.descriptionZh : config.description; + // 根据分类code获取分类名称(根据当前语言环境) + const categoryInfo = EXAMPLE_CATEGORIES.find(cat => cat.code === config.code); + let categoryName = config.code || "未分类"; + if (categoryInfo) { + categoryName = currentLocale === 'zh' && categoryInfo.nameZh ? categoryInfo.nameZh : categoryInfo.name; + } + examples.push({ + id: exampleId, + title: title, + description: description, + category: categoryName, // 使用映射后的分类名称 + categoryCode: config.code, // 保存分类code用于排序 + order: config.order || 0, // 保存排序字段 + code: code, + language: "javascript", + image: config.thumbnail || "" + }); + // 记录已添加的示例ID + addedExampleIds.add(exampleId); + } + } + } + catch (error) { + console.error(`加载示例配置失败: ${configKey}`, error); + } +}); +// 导出示例配置 +export const EXAMPLES_CONFIG = examples; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/docs/src/examples/index.js.map b/docs/src/examples/index.js.map new file mode 100644 index 0000000..6fc66cb --- /dev/null +++ b/docs/src/examples/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/docs/src/pages/example-detail.d.ts b/docs/src/pages/example-detail.d.ts new file mode 100644 index 0000000..9066452 --- /dev/null +++ b/docs/src/pages/example-detail.d.ts @@ -0,0 +1,13 @@ +import React from "react"; +export interface Example { + id: string; + title: string; + description: string; + category: string; + categoryCode?: string; + order?: number; + code: string; + language: string; + image?: string; +} +export default function ExampleDetail(): React.JSX.Element; diff --git a/docs/src/pages/example-detail.js b/docs/src/pages/example-detail.js new file mode 100644 index 0000000..16690bb --- /dev/null +++ b/docs/src/pages/example-detail.js @@ -0,0 +1,534 @@ +/* Copyright (C) 2025 flywave.gl contributors */ +import React, { useState, useRef, useEffect, useCallback, useReducer } from "react"; +import { useLocation } from "@docusaurus/router"; +import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; +import Layout from "@theme/Layout"; +import { Editor } from "@monaco-editor/react"; +import styles from "./example-detail.module.css"; +import { EXAMPLES_CONFIG } from "../examples"; +import { EXAMPLE_CATEGORIES } from "@flywave/flywave-examples/src/example-categories"; +import { CESIUM_ION_TOKEN } from "@flywave/flywave-examples/src/token-config"; +// 使用 useReducer 来强制更新组件 +const updateReducer = (state) => state + 1; +export default function ExampleDetail() { + const location = useLocation(); + const { siteConfig } = useDocusaurusContext(); + const previewRef = useRef(null); + const [exampleId, setExampleId] = useState("hello-world"); + const [code, setCode] = useState(""); + const [isRunning, setIsRunning] = useState(false); + const [editorWidth, setEditorWidth] = useState(50); + const [isResizing, setIsResizing] = useState(false); + const [isDarkTheme, setIsDarkTheme] = useState(true); + const [copyrightText, setCopyrightText] = useState(""); + const [isLoading, setIsLoading] = useState(true); + const [, forceUpdate] = useReducer(updateReducer, 0); // 用于强制更新 + // 获取当前示例对象 + const currentExample = EXAMPLES_CONFIG.find(ex => ex.id === exampleId) || EXAMPLES_CONFIG[0]; + // 初始化版权信息 + useEffect(() => { + const config = siteConfig; + if (config?.customFields?.copyright) { + setCopyrightText(config.customFields.copyright); + } + else if (config?.themeConfig?.footer?.copyright) { + setCopyrightText(config.themeConfig.footer.copyright); + } + else { + setCopyrightText(`Copyright © ${new Date().getFullYear()} flywave.gl. Built with Docusaurus.`); + } + }, [siteConfig]); + // 初始化和处理URL参数 + useEffect(() => { + const initExample = () => { + if (EXAMPLES_CONFIG.length === 0) { + console.error("示例配置为空"); + setIsLoading(false); + return; + } + // 支持两种URL参数格式:查询参数 ?id=... 或路径参数 /example-detail/... + // 首先尝试从查询参数获取 + const urlParams = new URLSearchParams(location.search); + let urlExampleId = urlParams.get("id"); + // 如果查询参数中没有找到id,尝试从路径中解析(处理类似 /example-detail/xxx 的格式) + if (!urlExampleId) { + const pathSegments = location.pathname.split('/'); + // 查找可能包含id的路径段 + for (let i = 0; i < pathSegments.length; i++) { + if (pathSegments[i] === 'example-detail' && i + 1 < pathSegments.length) { + const potentialId = pathSegments[i + 1]; + // 检查是否是有效的示例ID(存在于EXAMPLES_CONFIG中) + if (EXAMPLES_CONFIG.some(ex => ex.id === potentialId)) { + urlExampleId = potentialId; + break; + } + } + } + } + // 如果仍然没有找到,使用默认值 + if (!urlExampleId) { + urlExampleId = "hello-world"; + } + // 确保示例存在 + const targetExample = EXAMPLES_CONFIG.find(ex => ex.id === urlExampleId) || EXAMPLES_CONFIG[0]; + // 设置示例ID和代码 + setExampleId(targetExample.id); + setCode(targetExample.code); + setIsLoading(false); + // 立即运行示例,使用目标示例的代码,避免依赖可能未更新的状态 + setTimeout(() => { + if (previewRef.current) { + runCodeWithContent(targetExample.code); + } + }, 50); + }; + // 确保 EXAMPLES_CONFIG 已加载 + if (EXAMPLES_CONFIG.length > 0) { + initExample(); + } + else { + // 如果 EXAMPLES_CONFIG 还没加载,等待一会儿再试 + const timer = setTimeout(initExample, 100); + return () => clearTimeout(timer); + } + }, [location.pathname, location.search, EXAMPLES_CONFIG.length]); // 监听 EXAMPLES_CONFIG 长度变化 + // 监听浏览器的 popstate 事件,处理前进后退和语言切换 + useEffect(() => { + const handlePopState = () => { + // URL发生变化时重新解析参数 + const urlParams = new URLSearchParams(location.search); + let urlExampleId = urlParams.get("id"); + if (!urlExampleId) { + const pathSegments = location.pathname.split('/'); + for (let i = 0; i < pathSegments.length; i++) { + if (pathSegments[i] === 'example-detail' && i + 1 < pathSegments.length) { + const potentialId = pathSegments[i + 1]; + if (EXAMPLES_CONFIG.some(ex => ex.id === potentialId)) { + urlExampleId = potentialId; + break; + } + } + } + } + if (!urlExampleId) { + urlExampleId = "hello-world"; + } + const targetExample = EXAMPLES_CONFIG.find(ex => ex.id === urlExampleId) || EXAMPLES_CONFIG[0]; + if (targetExample.id !== exampleId) { + setExampleId(targetExample.id); + setCode(targetExample.code); + setTimeout(() => { + if (previewRef.current) { + runCodeWithContent(targetExample.code); + } + }, 0); + } + }; + window.addEventListener('popstate', handlePopState); + return () => { + window.removeEventListener('popstate', handlePopState); + }; + }, [location.pathname, location.search, EXAMPLES_CONFIG, exampleId]); + // 监听主题变化 + useEffect(() => { + const handleThemeChange = () => { + const isDark = document.documentElement.getAttribute("data-theme") === "dark"; + setIsDarkTheme(isDark); + }; + handleThemeChange(); + const observer = new MutationObserver(handleThemeChange); + observer.observe(document.documentElement, { + attributes: true, + attributeFilter: ["data-theme"] + }); + return () => observer.disconnect(); + }, []); + // 处理拖拽事件 + useEffect(() => { + const handleMouseMove = (e) => { + if (!isResizing) + return; + const container = document.querySelector(`.${styles.panelsContainer}`); + if (!container) + return; + const containerRect = container.getBoundingClientRect(); + const relativeX = e.clientX - containerRect.left; + const percentage = Math.min(Math.max((relativeX / containerRect.width) * 100, 20), 80); + setEditorWidth(percentage); + }; + const handleMouseUp = () => setIsResizing(false); + if (isResizing) { + document.addEventListener("mousemove", handleMouseMove); + document.addEventListener("mouseup", handleMouseUp); + document.body.style.cursor = "col-resize"; + document.body.style.userSelect = "none"; + } + return () => { + document.removeEventListener("mousemove", handleMouseMove); + document.removeEventListener("mouseup", handleMouseUp); + document.body.style.cursor = ""; + document.body.style.userSelect = ""; + }; + }, [isResizing]); + const handleRunCode = useCallback(() => { + runCodeWithContent(code); + }, [code]); + const handleResetCode = () => { + setCode(currentExample.code); + }; + const handleFullscreen = () => { + if (previewRef.current) { + const iframe = previewRef.current.querySelector("iframe"); + if (iframe) { + iframe.requestFullscreen?.().catch(err => { + console.error("全屏模式错误:", err); + }); + } + } + }; + const handleBackToHome = () => { + // 获取当前语言前缀 + const currentLangPrefix = window.location.pathname.startsWith('/zh/') ? '/zh' : + window.location.pathname.startsWith('/en/') ? '/en' : ''; + // 返回示例页面,保持语言前缀,确保路径格式正确 + // 修复:移除多余的斜杠,统一使用不带结尾斜杠的格式 + const homeUrl = currentLangPrefix ? `${currentLangPrefix}/examples` : '/examples'; + window.location.href = homeUrl; + }; + const handleExampleSelect = (selectedExampleId) => { + // 确保示例存在 + const selectedExample = EXAMPLES_CONFIG.find(ex => ex.id === selectedExampleId); + if (!selectedExample) { + console.error(`找不到示例: ${selectedExampleId}`); + return; + } + // 同时更新ID和代码,确保同步 + setExampleId(selectedExample.id); + setCode(selectedExample.code); + // 更新URL参数 + const newUrl = new URL(window.location.href); + newUrl.searchParams.set('id', selectedExample.id); + window.history.replaceState({}, '', newUrl.toString()); + // 立即运行新代码 + setTimeout(() => { + if (previewRef.current) { + runCodeWithContent(selectedExample.code); + } + }, 0); + }; + // 新增一个函数,直接使用代码内容运行,避免依赖状态 + const runCodeWithContent = (codeContent) => { + if (!previewRef.current || !codeContent) + return; + setIsRunning(true); + // 清除之前的预览内容 + previewRef.current.innerHTML = '
正在加载示例...
'; + // 创建 iframe 来运行代码 + const iframe = document.createElement("iframe"); + iframe.style.width = "100%"; + iframe.style.height = "100%"; + iframe.style.border = "none"; + iframe.setAttribute("title", "示例预览"); + // 添加加载超时处理 + const timeoutId = setTimeout(() => { + if (previewRef.current) { + previewRef.current.innerHTML = '
加载超时,请检查网络连接或重试
'; + setIsRunning(false); + } + }, 15000); + // 等待 iframe 加载完成 + iframe.onload = () => { + clearTimeout(timeoutId); + try { + const iframeDoc = iframe.contentDocument || iframe.contentWindow?.document; + if (!iframeDoc) { + throw new Error("无法访问 iframe 文档"); + } + const isDarkTheme = document.documentElement.getAttribute("data-theme") === "dark"; + iframeDoc.open(); + iframeDoc.write(` + + + + + + + + + + + + + `); + iframeDoc.close(); + } + catch (error) { + clearTimeout(timeoutId); + if (previewRef.current) { + previewRef.current.innerHTML = `
运行错误: ${error.message}
`; + } + setIsRunning(false); + } + finally { + setIsRunning(false); + } + }; + // iframe 加载错误处理 + iframe.onerror = () => { + clearTimeout(timeoutId); + if (previewRef.current) { + previewRef.current.innerHTML = '
iframe 加载失败,请检查网络连接或重试
'; + } + setIsRunning(false); + }; + // 将 iframe 添加到预览区域 + previewRef.current.innerHTML = ""; + previewRef.current.appendChild(iframe); + }; + // 按类别分组示例 + const examplesByCategory = {}; + EXAMPLES_CONFIG.forEach(example => { + const categoryKey = example.categoryCode || example.category; + if (!examplesByCategory[categoryKey]) { + examplesByCategory[categoryKey] = []; + } + examplesByCategory[categoryKey].push(example); + }); + // 对每个分类中的示例按order字段排序 + Object.keys(examplesByCategory).forEach(categoryKey => { + examplesByCategory[categoryKey].sort((a, b) => { + if (a.order !== undefined && b.order !== undefined) { + return a.order - b.order; + } + if (a.order !== undefined) { + return -1; + } + if (b.order !== undefined) { + return 1; + } + return a.title.localeCompare(b.title); + }); + }); + // 获取当前语言环境 + const currentLocale = typeof window !== 'undefined' ? + (window.location.pathname.startsWith('/zh/') || document.documentElement.lang.includes('zh') ? 'zh' : 'en') : 'en'; + // 根据EXAMPLE_CATEGORIES定义的顺序对分类进行排序 + const sortedCategories = [...EXAMPLE_CATEGORIES]; + const categoriesWithExamples = sortedCategories.filter(category => examplesByCategory[category.code] && examplesByCategory[category.code].length > 0); + const definedCategoryCodes = new Set(EXAMPLE_CATEGORIES.map(cat => cat.code)); + const undefinedCategories = Object.keys(examplesByCategory).filter(categoryCode => !definedCategoryCodes.has(categoryCode)); + if (isLoading || EXAMPLES_CONFIG.length === 0) { + //@ts-ignore + return ( +
+
+
加载中...
+
+
+
); + } + return ( + //@ts-ignore + +
+
+
+
+

+ {currentLocale === 'zh' ? '示例列表' : 'Example List'} +

+
+ +
+
+
+ {/* 显示已定义的分类 */} + {categoriesWithExamples.map(category => { + const examples = examplesByCategory[category.code] || []; + return (
+
+ {currentLocale === 'zh' && category.nameZh ? category.nameZh : category.name} +
+ {examples.map(ex => (
handleExampleSelect(ex.id)}> +
+ {ex.image ? ({ex.title}) : (
+ {ex.title} +
)} +
+
+
+ {ex.title} +
+
+ {ex.description} +
+
+
))} +
); + })} + + {/* 显示未定义的分类 */} + {undefinedCategories.map(categoryCode => { + const examples = examplesByCategory[categoryCode] || []; + const categoryName = examples.length > 0 ? examples[0].category : categoryCode; + return (
+
{categoryName}
+ {examples.map(ex => (
handleExampleSelect(ex.id)}> +
+ {ex.image ? ({ex.title}) : (
+ {ex.title} +
)} +
+
+
+ {ex.title} +
+
+ {ex.description} +
+
+
))} +
); + })} +
+
+
+
+
+

{currentExample.title}

+
+ + + +
+
+
+
+
+
+ {currentLocale === 'zh' ? "代码编辑器" : "Code Editor"} +
+
+ setCode(value || "")} theme={isDarkTheme ? "vs-dark" : "light"} options={{ + minimap: { enabled: false }, + scrollBeyondLastLine: false, + automaticLayout: true, + fontSize: 13, + lineNumbers: "on", + roundedSelection: false, + smoothScrolling: true, + wordWrap: "on", + folding: true, + renderLineHighlight: "all", + scrollbar: { + vertical: "auto", + horizontal: "auto" + } + }}/> +
+
setIsResizing(true)}/> +
+
+
+ {currentLocale === 'zh' ? "实时预览" : "Live Preview"} +
+
+
+
+

{currentLocale === 'zh' ? "实时预览区域" : "Live Preview Area"}

+

{currentLocale === 'zh' ? '点击"运行代码"按钮查看效果' : 'Click "Run Code" button to see the result'}

+

{currentLocale === 'zh' ? `当前示例: ${currentExample.title}` : `Current Example: ${currentExample.title}`}

+
+
+
+
+
+
+
+ {/* 动态读取配置的底部版权信息 */} +
+
{copyrightText}
+
+
+ ); +} +//# sourceMappingURL=example-detail.js.map \ No newline at end of file diff --git a/docs/src/pages/example-detail.js.map b/docs/src/pages/example-detail.js.map new file mode 100644 index 0000000..977d3c8 --- /dev/null +++ b/docs/src/pages/example-detail.js.map @@ -0,0 +1 @@ +{"version":3,"file":"example-detail.js","sourceRoot":"","sources":["example-detail.tsx"],"names":[],"mappings":"AAAA,gDAAgD;AAEhD,OAAO,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AACpF,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,oBAAoB,MAAM,kCAAkC,CAAC;AACpE,OAAO,MAAM,MAAM,eAAe,CAAC;AACnC,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAE9C,OAAO,MAAM,MAAM,6BAA6B,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,kDAAkD,CAAC;AACtF,OAAO,EAAE,gBAAgB,EAAE,MAAM,4CAA4C,CAAC;AA6B9E,wBAAwB;AACxB,MAAM,aAAa,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,KAAK,GAAG,CAAC,CAAC;AAEnD,MAAM,CAAC,OAAO,UAAU,aAAa;IACjC,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IAC/B,MAAM,EAAE,UAAU,EAAE,GAAG,oBAAoB,EAAE,CAAC;IAC9C,MAAM,UAAU,GAAG,MAAM,CAAiB,IAAI,CAAC,CAAC;IAChD,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAS,aAAa,CAAC,CAAC;IAClE,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAS,EAAE,CAAC,CAAC;IAC7C,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAU,KAAK,CAAC,CAAC;IAC3D,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAS,EAAE,CAAC,CAAC;IAC3D,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,QAAQ,CAAU,KAAK,CAAC,CAAC;IAC7D,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAU,IAAI,CAAC,CAAC;IAC9D,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC,GAAG,QAAQ,CAAS,EAAE,CAAC,CAAC;IAC/D,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAU,IAAI,CAAC,CAAC;IAC1D,MAAM,CAAC,EAAE,WAAW,CAAC,GAAG,UAAU,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS;IAE/D,WAAW;IACX,MAAM,cAAc,GAAG,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,SAAS,CAAC,IAAI,eAAe,CAAC,CAAC,CAAC,CAAC;IAE7F,UAAU;IACV,SAAS,CAAC,GAAG,EAAE;QACX,MAAM,MAAM,GAAG,UAAwB,CAAC;QACxC,IAAI,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,CAAC;YAClC,gBAAgB,CAAC,MAAM,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC;QACpD,CAAC;aAAM,IAAI,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;YAChD,gBAAgB,CAAC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC1D,CAAC;aAAM,CAAC;YACJ,gBAAgB,CACZ,eAAe,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,qCAAqC,CAC/E,CAAC;QACN,CAAC;IACL,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC;IAEjB,cAAc;IACd,SAAS,CAAC,GAAG,EAAE;QACX,MAAM,WAAW,GAAG,GAAG,EAAE;YACrB,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC/B,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;gBACxB,YAAY,CAAC,KAAK,CAAC,CAAC;gBACpB,OAAO;YACX,CAAC;YAED,qDAAqD;YACrD,cAAc;YACd,MAAM,SAAS,GAAG,IAAI,eAAe,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YACvD,IAAI,YAAY,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAEvC,uDAAuD;YACvD,IAAI,CAAC,YAAY,EAAE,CAAC;gBAChB,MAAM,YAAY,GAAG,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gBAClD,eAAe;gBACf,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,YAAY,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;oBAC3C,IAAI,YAAY,CAAC,CAAC,CAAC,KAAK,gBAAgB,IAAI,CAAC,GAAG,CAAC,GAAG,YAAY,CAAC,MAAM,EAAE,CAAC;wBACtE,MAAM,WAAW,GAAG,YAAY,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;wBACxC,oCAAoC;wBACpC,IAAI,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,WAAW,CAAC,EAAE,CAAC;4BACpD,YAAY,GAAG,WAAW,CAAC;4BAC3B,MAAM;wBACV,CAAC;oBACL,CAAC;gBACL,CAAC;YACL,CAAC;YAED,iBAAiB;YACjB,IAAI,CAAC,YAAY,EAAE,CAAC;gBAChB,YAAY,GAAG,aAAa,CAAC;YACjC,CAAC;YAED,SAAS;YACT,MAAM,aAAa,GAAG,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,YAAY,CAAC,IAAI,eAAe,CAAC,CAAC,CAAC,CAAC;YAE/F,YAAY;YACZ,YAAY,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;YAC/B,OAAO,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;YAE5B,YAAY,CAAC,KAAK,CAAC,CAAC;YAEpB,gCAAgC;YAChC,UAAU,CAAC,GAAG,EAAE;gBACZ,IAAI,UAAU,CAAC,OAAO,EAAE,CAAC;oBACrB,kBAAkB,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;gBAC3C,CAAC;YACL,CAAC,EAAE,EAAE,CAAC,CAAC;QACX,CAAC,CAAC;QAEF,yBAAyB;QACzB,IAAI,eAAe,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC7B,WAAW,EAAE,CAAC;QAClB,CAAC;aAAM,CAAC;YACJ,kCAAkC;YAClC,MAAM,KAAK,GAAG,UAAU,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;YAC3C,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;QACrC,CAAC;IACL,CAAC,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,0BAA0B;IAE5F,iCAAiC;IACjC,SAAS,CAAC,GAAG,EAAE;QACX,MAAM,cAAc,GAAG,GAAG,EAAE;YACxB,iBAAiB;YACjB,MAAM,SAAS,GAAG,IAAI,eAAe,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YACvD,IAAI,YAAY,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAEvC,IAAI,CAAC,YAAY,EAAE,CAAC;gBAChB,MAAM,YAAY,GAAG,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gBAClD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,YAAY,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;oBAC3C,IAAI,YAAY,CAAC,CAAC,CAAC,KAAK,gBAAgB,IAAI,CAAC,GAAG,CAAC,GAAG,YAAY,CAAC,MAAM,EAAE,CAAC;wBACtE,MAAM,WAAW,GAAG,YAAY,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;wBACxC,IAAI,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,WAAW,CAAC,EAAE,CAAC;4BACpD,YAAY,GAAG,WAAW,CAAC;4BAC3B,MAAM;wBACV,CAAC;oBACL,CAAC;gBACL,CAAC;YACL,CAAC;YAED,IAAI,CAAC,YAAY,EAAE,CAAC;gBAChB,YAAY,GAAG,aAAa,CAAC;YACjC,CAAC;YAED,MAAM,aAAa,GAAG,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,YAAY,CAAC,IAAI,eAAe,CAAC,CAAC,CAAC,CAAC;YAC/F,IAAI,aAAa,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;gBACjC,YAAY,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;gBAC/B,OAAO,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;gBAE5B,UAAU,CAAC,GAAG,EAAE;oBACZ,IAAI,UAAU,CAAC,OAAO,EAAE,CAAC;wBACrB,kBAAkB,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;oBAC3C,CAAC;gBACL,CAAC,EAAE,CAAC,CAAC,CAAC;YACV,CAAC;QACL,CAAC,CAAC;QAEF,MAAM,CAAC,gBAAgB,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;QACpD,OAAO,GAAG,EAAE;YACR,MAAM,CAAC,mBAAmB,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;QAC3D,CAAC,CAAC;IACN,CAAC,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,EAAE,eAAe,EAAE,SAAS,CAAC,CAAC,CAAC;IAErE,SAAS;IACT,SAAS,CAAC,GAAG,EAAE;QACX,MAAM,iBAAiB,GAAG,GAAG,EAAE;YAC3B,MAAM,MAAM,GAAG,QAAQ,CAAC,eAAe,CAAC,YAAY,CAAC,YAAY,CAAC,KAAK,MAAM,CAAC;YAC9E,cAAc,CAAC,MAAM,CAAC,CAAC;QAC3B,CAAC,CAAC;QAEF,iBAAiB,EAAE,CAAC;QAEpB,MAAM,QAAQ,GAAG,IAAI,gBAAgB,CAAC,iBAAiB,CAAC,CAAC;QACzD,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,eAAe,EAAE;YACvC,UAAU,EAAE,IAAI;YAChB,eAAe,EAAE,CAAC,YAAY,CAAC;SAClC,CAAC,CAAC;QAEH,OAAO,GAAG,EAAE,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC;IACvC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,SAAS;IACT,SAAS,CAAC,GAAG,EAAE;QACX,MAAM,eAAe,GAAG,CAAC,CAAa,EAAE,EAAE;YACtC,IAAI,CAAC,UAAU;gBAAE,OAAO;YACxB,MAAM,SAAS,GAAG,QAAQ,CAAC,aAAa,CAAC,IAAI,MAAM,CAAC,eAAe,EAAE,CAAC,CAAC;YACvE,IAAI,CAAC,SAAS;gBAAE,OAAO;YACvB,MAAM,aAAa,GAAG,SAAS,CAAC,qBAAqB,EAAE,CAAC;YACxD,MAAM,SAAS,GAAG,CAAC,CAAC,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC;YACjD,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,SAAS,GAAG,aAAa,CAAC,KAAK,CAAC,GAAG,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;YACvF,cAAc,CAAC,UAAU,CAAC,CAAC;QAC/B,CAAC,CAAC;QAEF,MAAM,aAAa,GAAG,GAAG,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAEjD,IAAI,UAAU,EAAE,CAAC;YACb,QAAQ,CAAC,gBAAgB,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC;YACxD,QAAQ,CAAC,gBAAgB,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;YACpD,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,YAAY,CAAC;YAC1C,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,MAAM,CAAC;QAC5C,CAAC;QAED,OAAO,GAAG,EAAE;YACR,QAAQ,CAAC,mBAAmB,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC;YAC3D,QAAQ,CAAC,mBAAmB,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;YACvD,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,EAAE,CAAC;YAChC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,EAAE,CAAC;QACxC,CAAC,CAAC;IACN,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC;IAEjB,MAAM,aAAa,GAAG,WAAW,CAAC,GAAG,EAAE;QACnC,kBAAkB,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAEX,MAAM,eAAe,GAAG,GAAG,EAAE;QACzB,OAAO,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC,CAAC;IAEF,MAAM,gBAAgB,GAAG,GAAG,EAAE;QAC1B,IAAI,UAAU,CAAC,OAAO,EAAE,CAAC;YACrB,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;YAC1D,IAAI,MAAM,EAAE,CAAC;gBACT,MAAM,CAAC,iBAAiB,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE;oBACrC,OAAO,CAAC,KAAK,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;gBAClC,CAAC,CAAC,CAAC;YACP,CAAC;QACL,CAAC;IACL,CAAC,CAAC;IAEF,MAAM,gBAAgB,GAAG,GAAG,EAAE;QAC1B,WAAW;QACX,MAAM,iBAAiB,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;YACrD,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QAEnF,yBAAyB;QACzB,2BAA2B;QAC3B,MAAM,OAAO,GAAG,iBAAiB,CAAC,CAAC,CAAC,GAAG,iBAAiB,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC;QAClF,MAAM,CAAC,QAAQ,CAAC,IAAI,GAAG,OAAO,CAAC;IACnC,CAAC,CAAC;IAEF,MAAM,mBAAmB,GAAG,CAAC,iBAAyB,EAAE,EAAE;QACtD,SAAS;QACT,MAAM,eAAe,GAAG,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,iBAAiB,CAAC,CAAC;QAChF,IAAI,CAAC,eAAe,EAAE,CAAC;YACnB,OAAO,CAAC,KAAK,CAAC,UAAU,iBAAiB,EAAE,CAAC,CAAC;YAC7C,OAAO;QACX,CAAC;QAED,iBAAiB;QACjB,YAAY,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC;QACjC,OAAO,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QAE9B,UAAU;QACV,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC7C,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,EAAE,eAAe,CAAC,EAAE,CAAC,CAAC;QAClD,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;QAEvD,UAAU;QACV,UAAU,CAAC,GAAG,EAAE;YACZ,IAAI,UAAU,CAAC,OAAO,EAAE,CAAC;gBACrB,kBAAkB,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;YAC7C,CAAC;QACL,CAAC,EAAE,CAAC,CAAC,CAAC;IACV,CAAC,CAAC;IAEF,2BAA2B;IAC3B,MAAM,kBAAkB,GAAG,CAAC,WAAmB,EAAE,EAAE;QAC/C,IAAI,CAAC,UAAU,CAAC,OAAO,IAAI,CAAC,WAAW;YAAE,OAAO;QAEhD,YAAY,CAAC,IAAI,CAAC,CAAC;QAEnB,YAAY;QACZ,UAAU,CAAC,OAAO,CAAC,SAAS,GAAG,sCAAsC,CAAC;QAEtE,kBAAkB;QAClB,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QAChD,MAAM,CAAC,KAAK,CAAC,KAAK,GAAG,MAAM,CAAC;QAC5B,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC;QAC7B,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC;QAC7B,MAAM,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAErC,WAAW;QACX,MAAM,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE;YAC9B,IAAI,UAAU,CAAC,OAAO,EAAE,CAAC;gBACrB,UAAU,CAAC,OAAO,CAAC,SAAS,GAAG,0CAA0C,CAAC;gBAC1E,YAAY,CAAC,KAAK,CAAC,CAAC;YACxB,CAAC;QACL,CAAC,EAAE,KAAK,CAAC,CAAC;QAEV,iBAAiB;QACjB,MAAM,CAAC,MAAM,GAAG,GAAG,EAAE;YACjB,YAAY,CAAC,SAAS,CAAC,CAAC;YACxB,IAAI,CAAC;gBACD,MAAM,SAAS,GAAG,MAAM,CAAC,eAAe,IAAI,MAAM,CAAC,aAAa,EAAE,QAAQ,CAAC;gBAC3E,IAAI,CAAC,SAAS,EAAE,CAAC;oBACb,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC;gBACtC,CAAC;gBAED,MAAM,WAAW,GAAG,QAAQ,CAAC,eAAe,CAAC,YAAY,CAAC,YAAY,CAAC,KAAK,MAAM,CAAC;gBAEnF,SAAS,CAAC,IAAI,EAAE,CAAC;gBACjB,SAAS,CAAC,KAAK,CAAC;;;;;;;sCAOM,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;gCACzC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;;mCAEhC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4CAkE1B,gBAAgB;;iBAE3C,WAAW;;;;SAInB,CAAC,CAAC;gBACK,SAAS,CAAC,KAAK,EAAE,CAAC;YACtB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACb,YAAY,CAAC,SAAS,CAAC,CAAC;gBACxB,IAAI,UAAU,CAAC,OAAO,EAAE,CAAC;oBACrB,UAAU,CAAC,OAAO,CAAC,SAAS,GAAG,4BAA4B,KAAK,CAAC,OAAO,QAAQ,CAAC;gBACrF,CAAC;gBACD,YAAY,CAAC,KAAK,CAAC,CAAC;YACxB,CAAC;oBAAS,CAAC;gBACP,YAAY,CAAC,KAAK,CAAC,CAAC;YACxB,CAAC;QACL,CAAC,CAAC;QAEF,gBAAgB;QAChB,MAAM,CAAC,OAAO,GAAG,GAAG,EAAE;YAClB,YAAY,CAAC,SAAS,CAAC,CAAC;YACxB,IAAI,UAAU,CAAC,OAAO,EAAE,CAAC;gBACrB,UAAU,CAAC,OAAO,CAAC,SAAS,GAAG,iDAAiD,CAAC;YACrF,CAAC;YACD,YAAY,CAAC,KAAK,CAAC,CAAC;QACxB,CAAC,CAAC;QAEF,mBAAmB;QACnB,UAAU,CAAC,OAAO,CAAC,SAAS,GAAG,EAAE,CAAC;QAClC,UAAU,CAAC,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IAC3C,CAAC,CAAC;IAEF,UAAU;IACV,MAAM,kBAAkB,GAAiC,EAAE,CAAC;IAC5D,eAAe,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE;QAC9B,MAAM,WAAW,GAAG,OAAO,CAAC,YAAY,IAAI,OAAO,CAAC,QAAQ,CAAC;QAC7D,IAAI,CAAC,kBAAkB,CAAC,WAAW,CAAC,EAAE,CAAC;YACnC,kBAAkB,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC;QACzC,CAAC;QACD,kBAAkB,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,sBAAsB;IACtB,MAAM,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE;QAClD,kBAAkB,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;YAC1C,IAAI,CAAC,CAAC,KAAK,KAAK,SAAS,IAAI,CAAC,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;gBACjD,OAAO,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC;YAC7B,CAAC;YACD,IAAI,CAAC,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;gBACxB,OAAO,CAAC,CAAC,CAAC;YACd,CAAC;YACD,IAAI,CAAC,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;gBACxB,OAAO,CAAC,CAAC;YACb,CAAC;YACD,OAAO,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAC1C,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;IAEH,WAAW;IACX,MAAM,aAAa,GAAG,OAAO,MAAM,KAAK,WAAW,CAAC,CAAC;QACnD,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,QAAQ,CAAC,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAErH,mCAAmC;IACnC,MAAM,gBAAgB,GAAG,CAAC,GAAG,kBAAkB,CAAC,CAAC;IACjD,MAAM,sBAAsB,GAAG,gBAAgB,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAC9D,kBAAkB,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,kBAAkB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CACpF,CAAC;IAEF,MAAM,oBAAoB,GAAG,IAAI,GAAG,CAAC,kBAAkB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;IAC9E,MAAM,mBAAmB,GAAG,MAAM,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC,MAAM,CAC9D,YAAY,CAAC,EAAE,CAAC,CAAC,oBAAoB,CAAC,GAAG,CAAC,YAAY,CAAC,CAC1D,CAAC;IAEF,IAAI,SAAS,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5C,YAAY;QACZ,OAAO,CAAC,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAC/D;gBAAA,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAC/B;oBAAA,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,gBAAgB,CAAC,CACpC;wBAAA,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,MAAM,EAAE,GAAG,CACtD;oBAAA,EAAE,GAAG,CACT;gBAAA,EAAE,GAAG,CACT;YAAA,EAAE,MAAM,CAAC,CACZ,CAAC;IACN,CAAC;IAED,OAAO;IACH,YAAY;IACZ,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAC3D;YAAA,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAC/B;gBAAA,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAC1C;oBAAA,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAC3B;wBAAA,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CACjC;4BAAA,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,CAC/B;gCAAA,CAAC,aAAa,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,cAAc,CACrD;4BAAA,EAAE,EAAE,CACJ;4BAAA,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC,CAClC;gCAAA,CAAC,MAAM,CACH,SAAS,CAAC,CAAC,MAAM,CAAC,iBAAiB,CAAC,CACpC,OAAO,CAAC,CAAC,gBAAgB,CAAC,CAE1B;sCAAE,CAAC,aAAa,KAAK,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,kBAAkB,CAC7D;gCAAA,EAAE,MAAM,CACZ;4BAAA,EAAE,GAAG,CACT;wBAAA,EAAE,GAAG,CACL;wBAAA,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAC/B;4BAAA,CAAC,cAAc,CACf;4BAAA,CAAC,sBAAsB,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE;YACnC,MAAM,QAAQ,GAAG,kBAAkB,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YACzD,OAAO,CACH,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,eAAe,CAAC,CACvD;wCAAA,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CACjC;4CAAA,CAAC,aAAa,KAAK,IAAI,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAChF;wCAAA,EAAE,GAAG,CACL;wCAAA,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,CAChB,CAAC,GAAG,CACA,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CACX,SAAS,CAAC,CAAC,GAAG,MAAM,CAAC,WAAW,IAC5B,EAAE,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAC1C,EAAE,CAAC,CACH,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC,mBAAmB,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAE1C;gDAAA,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,CAChC;oDAAA,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CACR,CAAC,GAAG,CACA,GAAG,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CACd,GAAG,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CACd,SAAS,CAAC,CAAC,MAAM,CAAC,gBAAgB,CAAC,EACrC,CACL,CAAC,CAAC,CAAC,CACA,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,gBAAgB,CAAC,CACpC;4DAAA,CAAC,EAAE,CAAC,KAAK,CACb;wDAAA,EAAE,GAAG,CAAC,CACT,CACL;gDAAA,EAAE,GAAG,CACL;gDAAA,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC,CAClC;oDAAA,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,CAChC;wDAAA,CAAC,EAAE,CAAC,KAAK,CACb;oDAAA,EAAE,GAAG,CACL;oDAAA,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,kBAAkB,CAAC,CACtC;wDAAA,CAAC,EAAE,CAAC,WAAW,CACnB;oDAAA,EAAE,GAAG,CACT;gDAAA,EAAE,GAAG,CACT;4CAAA,EAAE,GAAG,CAAC,CACT,CAAC,CACN;oCAAA,EAAE,GAAG,CAAC,CACT,CAAC;QACN,CAAC,CAAC,CAEF;;4BAAA,CAAC,cAAc,CACf;4BAAA,CAAC,mBAAmB,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE;YACpC,MAAM,QAAQ,GAAG,kBAAkB,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC;YACxD,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC;YAC/E,OAAO,CACH,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,YAAY,CAAC,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,eAAe,CAAC,CACtD;wCAAA,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,YAAY,CAAC,EAAE,GAAG,CACzD;wCAAA,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,CAChB,CAAC,GAAG,CACA,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CACX,SAAS,CAAC,CAAC,GAAG,MAAM,CAAC,WAAW,IAC5B,EAAE,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAC1C,EAAE,CAAC,CACH,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC,mBAAmB,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAE1C;gDAAA,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,CAChC;oDAAA,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CACR,CAAC,GAAG,CACA,GAAG,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CACd,GAAG,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CACd,SAAS,CAAC,CAAC,MAAM,CAAC,gBAAgB,CAAC,EACrC,CACL,CAAC,CAAC,CAAC,CACA,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,gBAAgB,CAAC,CACpC;4DAAA,CAAC,EAAE,CAAC,KAAK,CACb;wDAAA,EAAE,GAAG,CAAC,CACT,CACL;gDAAA,EAAE,GAAG,CACL;gDAAA,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC,CAClC;oDAAA,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,CAChC;wDAAA,CAAC,EAAE,CAAC,KAAK,CACb;oDAAA,EAAE,GAAG,CACL;oDAAA,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,kBAAkB,CAAC,CACtC;wDAAA,CAAC,EAAE,CAAC,WAAW,CACnB;oDAAA,EAAE,GAAG,CACT;gDAAA,EAAE,GAAG,CACT;4CAAA,EAAE,GAAG,CAAC,CACT,CAAC,CACN;oCAAA,EAAE,GAAG,CAAC,CACT,CAAC;QACN,CAAC,CAAC,CACN;wBAAA,EAAE,GAAG,CACT;oBAAA,EAAE,GAAG,CACL;oBAAA,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAC/B;wBAAA,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAC1B;4BAAA,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CACjC;gCAAA,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,EAAE,CAC7D;gCAAA,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CACjC;oCAAA,CAAC,MAAM,CACH,SAAS,CAAC,CAAC,GAAG,MAAM,CAAC,YAAY,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC,CACtD,OAAO,CAAC,CAAC,aAAa,CAAC,CACvB,QAAQ,CAAC,CAAC,SAAS,CAAC,CAEpB;wCAAA,CAAC,SAAS,CAAC,CAAC;YACR,CAAC,aAAa,KAAK,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC;YACpD,CAAC,aAAa,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CACtD;oCAAA,EAAE,MAAM,CACR;oCAAA,CAAC,MAAM,CACH,SAAS,CAAC,CAAC,GAAG,MAAM,CAAC,YAAY,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC,CACxD,OAAO,CAAC,CAAC,eAAe,CAAC,CACzB,KAAK,CAAC,CAAC,aAAa,KAAK,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,6BAA6B,CAAC,CAE5E;wCAAA,CAAC,aAAa,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,YAAY,CACnD;oCAAA,EAAE,MAAM,CACR;oCAAA,CAAC,MAAM,CACH,SAAS,CAAC,CAAC,GAAG,MAAM,CAAC,YAAY,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC,CACxD,OAAO,CAAC,CAAC,gBAAgB,CAAC,CAE1B;wCAAA,CAAC,aAAa,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,oBAAoB,CAC3D;oCAAA,EAAE,MAAM,CACZ;gCAAA,EAAE,GAAG,CACT;4BAAA,EAAE,GAAG,CACT;wBAAA,EAAE,GAAG,CACL;wBAAA,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,eAAe,CAAC,CACnC;4BAAA,CAAC,GAAG,CACA,SAAS,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAC9B,KAAK,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,WAAW,GAAG,EAAE,CAAC,CAEpC;gCAAA,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAC/B;oCAAA,CAAC,IAAI,CAAC,CAAC,aAAa,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,IAAI,CAClE;gCAAA,EAAE,GAAG,CACL;gCAAA,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CACjC;oCAAA,CAAC,MAAM,CACH,QAAQ,CAAC,CAAC,cAAc,CAAC,QAAQ,CAAC,CAClC,KAAK,CAAC,CAAC,IAAI,CAAC,CACZ,QAAQ,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CACxC,KAAK,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CACzC,OAAO,CAAC,CAAC;YACL,OAAO,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE;YAC3B,oBAAoB,EAAE,KAAK;YAC3B,eAAe,EAAE,IAAI;YACrB,QAAQ,EAAE,EAAE;YACZ,WAAW,EAAE,IAAI;YACjB,gBAAgB,EAAE,KAAK;YACvB,eAAe,EAAE,IAAI;YACrB,QAAQ,EAAE,IAAI;YACd,OAAO,EAAE,IAAI;YACb,mBAAmB,EAAE,KAAK;YAC1B,SAAS,EAAE;gBACP,QAAQ,EAAE,MAAM;gBAChB,UAAU,EAAE,MAAM;aACrB;SACJ,CAAC,EAEV;gCAAA,EAAE,GAAG,CACL;gCAAA,CAAC,GAAG,CACA,SAAS,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAC1B,WAAW,CAAC,CAAC,GAAG,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,EAE/C;4BAAA,EAAE,GAAG,CACL;4BAAA,CAAC,GAAG,CACA,SAAS,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,CAC/B,KAAK,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,GAAG,GAAG,WAAW,GAAG,EAAE,CAAC,CAE1C;gCAAA,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAC/B;oCAAA,CAAC,IAAI,CAAC,CAAC,aAAa,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,cAAc,CAAC,EAAE,IAAI,CAClE;gCAAA,EAAE,GAAG,CACL;gCAAA,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC,CAClC;oCAAA,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC,CACnD;wCAAA,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,kBAAkB,CAAC,CACtC;4CAAA,CAAC,CAAC,CAAC,CAAC,aAAa,KAAK,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,mBAAmB,CAAC,EAAE,CAAC,CAC/D;4CAAA,CAAC,CAAC,CAAC,CAAC,aAAa,KAAK,IAAI,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,2CAA2C,CAAC,EAAE,CAAC,CAC/F;4CAAA,CAAC,CAAC,CAAC,CAAC,aAAa,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,cAAc,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,oBAAoB,cAAc,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,CACjH;wCAAA,EAAE,GAAG,CACT;oCAAA,EAAE,GAAG,CACT;gCAAA,EAAE,GAAG,CACT;4BAAA,EAAE,GAAG,CACT;wBAAA,EAAE,GAAG,CACT;oBAAA,EAAE,GAAG,CACT;gBAAA,EAAE,GAAG,CACL;gBAAA,CAAC,mBAAmB,CACpB;gBAAA,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,mBAAmB,CAAC,CACvC;oBAAA,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,GAAG,CAChE;gBAAA,EAAE,GAAG,CACT;YAAA,EAAE,GAAG,CACT;QAAA,EAAE,MAAM,CAAC,CACZ,CAAC;AACN,CAAC"} \ No newline at end of file diff --git a/docs/src/pages/examples.d.ts b/docs/src/pages/examples.d.ts new file mode 100644 index 0000000..2582b56 --- /dev/null +++ b/docs/src/pages/examples.d.ts @@ -0,0 +1,2 @@ +import type { ReactNode } from "react"; +export default function Examples(): ReactNode; diff --git a/docs/src/pages/examples.js b/docs/src/pages/examples.js new file mode 100644 index 0000000..3633ea0 --- /dev/null +++ b/docs/src/pages/examples.js @@ -0,0 +1,133 @@ +/* Copyright (C) 2025 flywave.gl contributors */ +import React from "react"; +import Link from "@docusaurus/Link"; +import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; +import Layout from "@theme/Layout"; +import Translate from '@docusaurus/Translate'; +const Heading = require("@theme/Heading").default; +import styles from "./examples.module.css"; +import { EXAMPLES_CONFIG } from "../examples"; +import { EXAMPLE_CATEGORIES } from "@flywave/flywave-examples/src/example-categories"; +function ExamplesHeader() { + const { siteConfig } = useDocusaurusContext(); + return (
+
+ + + Examples + + +

+ + Learn various features of flywave.gl through practical examples + +

+
+
); +} +function getExampleLink(exampleId) { + // 获取当前语言前缀 + const currentLangPrefix = typeof window !== 'undefined' + ? (window.location.pathname.startsWith('/zh/') ? '/zh' : + window.location.pathname.startsWith('/en/') ? '/en' : '') + : ''; + // 跳转到示例详情页,保持语言前缀,确保路径格式正确 + // 修复:移除多余的斜杠,统一使用不带结尾斜杠的格式 + return currentLangPrefix ? `${currentLangPrefix}/example-detail?id=${exampleId}` : `/example-detail?id=${exampleId}`; +} +function ExampleCard({ example }) { + return (
+
+ {example.image ? ({example.title}) : (
{example.title}
)} +
+
+

{example.title}

+

{example.description}

+

{example.category}

+ + + View Details + + +
+
); +} +export default function Examples() { + // 按类别分组示例 + const examplesByCategory = {}; + EXAMPLES_CONFIG.forEach(example => { + // 使用categoryCode作为键进行分组,如果没有则使用category + const categoryKey = example.categoryCode || example.category; + if (!examplesByCategory[categoryKey]) { + examplesByCategory[categoryKey] = []; + } + examplesByCategory[categoryKey].push(example); + }); + // 对每个分类中的示例按order字段排序 + Object.keys(examplesByCategory).forEach(categoryKey => { + examplesByCategory[categoryKey].sort((a, b) => { + // 如果都有order字段,按order排序 + if (a.order !== undefined && b.order !== undefined) { + return a.order - b.order; + } + // 如果只有a有order字段,a排在前面 + if (a.order !== undefined) { + return -1; + } + // 如果只有b有order字段,b排在前面 + if (b.order !== undefined) { + return 1; + } + // 如果都没有order字段,按标题排序 + return a.title.localeCompare(b.title); + }); + }); + // 根据EXAMPLE_CATEGORIES定义的顺序对分类进行排序 + const sortedCategories = [...EXAMPLE_CATEGORIES]; + // 获取当前语言环境 + const currentLocale = typeof window !== 'undefined' ? + (window.location.pathname.startsWith('/zh/') || document.documentElement.lang.includes('zh') ? 'zh' : 'en') : 'en'; + // 过滤出有示例的分类 + const categoriesWithExamples = sortedCategories.filter(category => examplesByCategory[category.code] && examplesByCategory[category.code].length > 0); + // 添加没有在EXAMPLE_CATEGORIES中定义但有示例的分类 + const definedCategoryCodes = new Set(EXAMPLE_CATEGORIES.map(cat => cat.code)); + const undefinedCategories = Object.keys(examplesByCategory).filter(categoryCode => !definedCategoryCodes.has(categoryCode)); + return ( + +
+
+ {/* 显示已定义的分类 */} + {categoriesWithExamples.map(category => { + const examples = examplesByCategory[category.code] || []; + return (
+ + {currentLocale === 'zh' && category.nameZh ? category.nameZh : category.name} + +
+ {examples.map(example => (
+ +
))} +
+
); + })} + + {/* 显示未定义的分类 */} + {undefinedCategories.map(categoryCode => { + const examples = examplesByCategory[categoryCode] || []; + const categoryName = examples.length > 0 ? examples[0].category : categoryCode; + return (
+ + {categoryName} + +
+ {examples.map(example => (
+ +
))} +
+
); + })} +
+
+
); +} +//# sourceMappingURL=examples.js.map \ No newline at end of file diff --git a/docs/src/pages/examples.js.map b/docs/src/pages/examples.js.map new file mode 100644 index 0000000..2c08223 --- /dev/null +++ b/docs/src/pages/examples.js.map @@ -0,0 +1 @@ +{"version":3,"file":"examples.js","sourceRoot":"","sources":["examples.tsx"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/docs/src/pages/index.d.ts b/docs/src/pages/index.d.ts new file mode 100644 index 0000000..5869616 --- /dev/null +++ b/docs/src/pages/index.d.ts @@ -0,0 +1,26 @@ +import type { ReactNode } from "react"; +export declare const GlobeIcon: ({ size, color }: { + size?: number; + color?: string; +}) => import("react").JSX.Element; +export declare const GamepadIcon: ({ size, color }: { + size?: number; + color?: string; +}) => import("react").JSX.Element; +export declare const LinkIcon: ({ size, color }: { + size?: number; + color?: string; +}) => import("react").JSX.Element; +export declare const TextIcon: ({ size, color }: { + size?: number; + color?: string; +}) => import("react").JSX.Element; +export declare const ShieldIcon: ({ size, color }: { + size?: number; + color?: string; +}) => import("react").JSX.Element; +export declare const CubeIcon: ({ size, color }: { + size?: number; + color?: string; +}) => import("react").JSX.Element; +export default function Home(): ReactNode; diff --git a/docs/src/pages/index.js b/docs/src/pages/index.js new file mode 100644 index 0000000..fa76d7b --- /dev/null +++ b/docs/src/pages/index.js @@ -0,0 +1,144 @@ +/* Copyright (C) 2025 flywave.gl contributors */ +import Link from "@docusaurus/Link"; +import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; +import Layout from "@theme/Layout"; +import Translate from '@docusaurus/Translate'; +const Heading = require("@theme/Heading").default; +import styles from "./index.module.css"; +//@ts-ignore +import FlywaveGlobe from "@site/src/components/FlywaveGlobe"; +// src/components/icons/index.js +export const GlobeIcon = ({ size = 24, color = "currentColor" }) => ( + + + + ); +export const GamepadIcon = ({ size = 24, color = "currentColor" }) => ( + + + + ); +export const LinkIcon = ({ size = 24, color = "currentColor" }) => ( + + + ); +export const TextIcon = ({ size = 24, color = "currentColor" }) => ( + + ); +export const ShieldIcon = ({ size = 24, color = "currentColor" }) => ( + + ); +export const CubeIcon = ({ size = 24, color = "currentColor" }) => ( + + + + ); +function HomepageHeader() { + const { siteConfig } = useDocusaurusContext(); + return (
+
+
+
+ {siteConfig.title} +

{siteConfig.tagline}

+
+

+ + 一个基于 TypeScript 构建的开源 3D 地图渲染引擎,利用 WebGL 和 Three.js + 实现高性能、可扩展且模块化的地图可视化解决方案。 + +

+
+
+ + + 入门指南 - 5分钟 ⏱️ + + + + + 查看示例 + + +
+
+
+ +
+
+
+
); +} +function FeatureCard({ icon, title, description }) { + return (
+
{icon}
+

{title}

+

{description}

+
); +} +function FeaturesSection() { + return (
+
+
+
+ FEATURES +
+

+ + Flywave.gl 提供全面的 3D 地图渲染能力,专为高性能可视化设计 + +

+
+
+ 多源数据支持} description={全面支持 3D Tiles、地形数据(DEM、Quantized Mesh等)、矢量瓦片等多种数据格式,构建丰富的三维地理信息可视化。}/> + 流畅交互操作} description={优化的地图漫游体验,支持平滑的缩放、旋转和倾斜操作,提供直观自然的3D地图交互体验。}/> + Three.js 深度集成} description={与 Three.js 无缝集成,充分利用 WebGL 的强大功能,便于复用 Three.js 生态资源和扩展自定义功能。}/> + 高级文字系统} description={支持多语言的高质量文本渲染系统,包含中文在内的多种语言显示,以及灵活的标注功能。}/> + TypeScript 优势} description={采用 TypeScript 构建,提供完整的类型定义和智能提示,增强代码可维护性,模块化架构支持按需加载。}/> + 模块化架构} description={基于模块化设计,支持灵活的功能组合和扩展,便于构建高度定制化的3D地图应用。}/> +
+
+
); +} +function QuickStartSection() { + return (
+
+ + + 快速开始 + + +

+ + 要开始使用 flywave.gl,您可以查看我们的示例教程,了解如何创建第一个地图应用。 + +

+
+ + + 查看示例 + + + + + 教程概览 + + +
+
+
); +} +export default function Home() { + return ( + +
+
+ +
+
+ +
+
+
); +} +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/docs/src/pages/index.js.map b/docs/src/pages/index.js.map new file mode 100644 index 0000000..63e36ae --- /dev/null +++ b/docs/src/pages/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["index.tsx"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/docs/test-server.d.ts b/docs/test-server.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/docs/test-server.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/karma.conf.d.ts b/karma.conf.d.ts new file mode 100644 index 0000000..d623d21 --- /dev/null +++ b/karma.conf.d.ts @@ -0,0 +1,2 @@ +declare function _exports(config: any): void; +export = _exports; diff --git a/scripts/apidocs.d.ts b/scripts/apidocs.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/scripts/apidocs.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/scripts/apidocs.js b/scripts/apidocs.js new file mode 100644 index 0000000..6886233 --- /dev/null +++ b/scripts/apidocs.js @@ -0,0 +1,133 @@ +/* Copyright (C) 2025 flywave.gl contributors */ +/* eslint-disable no-console */ +import { Extractor, ExtractorConfig } from "@microsoft/api-extractor"; +import { execSync } from "child_process"; +import * as fs from "fs"; +import * as path from "path"; +import { createRequire } from "module"; +const require = createRequire(import.meta.url); +/** + * 修复可能导致 acorn 解析错误的内容 + * 基于搜索到的资料:TypeDoc 注释中的花括号需要转义 + */ +function fixAcornConflicts(content) { + return (content + // 转义花括号以避免 acorn 解析错误 + .replace(/{/g, "{") + .replace(/}/g, "}") + .replace(//g, "<T>") + .replace("**Returns:**", "**Returns:**\n\n```typescript\n")); +} +/** + * 处理生成的 Markdown 文件以修复 acorn 解析错误 + */ +function processGeneratedMarkdownFiles() { + const apiDocsPath = path.resolve("docs/docs/api"); + if (!fs.existsSync(apiDocsPath)) { + console.log("API docs directory not found, skipping acorn fixes"); + return; + } + const files = fs.readdirSync(apiDocsPath); + let fixedFiles = 0; + files.forEach(file => { + if (file.endsWith(".md")) { + const filePath = path.join(apiDocsPath, file); + let content = fs.readFileSync(filePath, "utf8"); + // 使用统一的函数处理所有非TypeScript代码块 + content = fixAcornConflicts(content); + fs.writeFileSync(filePath, content); + fixedFiles++; + } + }); + console.log("✅ Fixed acorn conflicts in " + fixedFiles + " markdown files"); +} +async function main() { + const reportFolder = path.resolve("input"); + const reportTempFolder = path.resolve("temp"); + fs.mkdirSync(reportFolder, { recursive: true }); + fs.mkdirSync(reportTempFolder, { recursive: true }); + // 定义所有要处理的包 + const packages = [ + "@flywave/flywave-mapview", + "@flywave/flywave-features-datasource", + "@flywave/flywave-geojson-datasource", + "@flywave/flywave-webtile-datasource", + "@flywave/flywave-terrain-datasource", + "@flywave/flywave-vectortile-datasource", + "@flywave/flywave-3dtile-datasource", + "@flywave/flywave-draw-controls", + "@flywave/flywave-map-controls", + "@flywave/flywave-inspector" + ]; + // 先构建所有包 + for (const packageName of packages) { + const packageJson = require(`${packageName}/package.json`); + const config = ExtractorConfig.prepare({ + packageJson, + packageJsonFullPath: path.resolve(`${packageName}/package.json`), + configObjectFullPath: path.resolve(`${packageName}`), + configObject: { + projectFolder: path.resolve(packageName), + mainEntryPointFilePath: path.resolve(`${packageName}/lib/src/index.d.ts`), + compiler: { + tsconfigFilePath: path.resolve(`${packageName}/tsconfig.json`), + overrideTsconfig: { + include: ["lib/**/*.d.ts"], + exclude: [ + "src/**/*", + "node_modules/**/*", + "dist/**/*", + "**/*.test.ts", + "**/*.spec.ts", + "**/test/**/*" + ] + } + }, + docModel: { + enabled: true, + apiJsonFilePath: `${reportFolder}/.api.json` + }, + apiReport: { + enabled: true, + reportFolder, + reportTempFolder, + reportFileName: ".api.md" + } + } + }); + const result = Extractor.invoke(config, { + localBuild: true, + messageCallback: message => { + let loc = ""; + if (message.sourceFilePath !== undefined) { + loc += `${message.sourceFilePath}:`; + if (message.sourceFileLine !== undefined) { + loc += `${message.sourceFileLine}:`; + if (message.sourceFileColumn !== undefined) { + loc += `${message.sourceFileColumn}:`; + } + } + loc += " "; + } + console.warn(`${loc}(${message.category}) ${message.text} (${message.messageId})`); + } + }); + if (!result.succeeded) { + throw new Error(`failed to extract api when processing '${packageName}'`); + } + } + // 生成文档 + console.log(execSync("pnpm exec api-documenter markdown --output-folder docs/docs/api ").toString()); + // 修复 acorn 解析错误 + console.log("🔧 Fixing acorn parsing conflicts..."); + processGeneratedMarkdownFiles(); + // 删除临时文件夹 + if (fs.existsSync(reportFolder)) { + fs.rmSync(reportFolder, { recursive: true, force: true }); + } + if (fs.existsSync(reportTempFolder)) { + fs.rmSync(reportTempFolder, { recursive: true, force: true }); + } +} +main().catch(console.error); +//# sourceMappingURL=apidocs.js.map \ No newline at end of file diff --git a/scripts/apidocs.js.map b/scripts/apidocs.js.map new file mode 100644 index 0000000..2ebce88 --- /dev/null +++ b/scripts/apidocs.js.map @@ -0,0 +1 @@ +{"version":3,"file":"apidocs.js","sourceRoot":"","sources":["apidocs.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/scripts/checkLicenses.d.ts b/scripts/checkLicenses.d.ts new file mode 100644 index 0000000..5650444 --- /dev/null +++ b/scripts/checkLicenses.d.ts @@ -0,0 +1,13 @@ +/** + * Checks whether specified source files contain license matching the specified RegExp. + * @param sourceFiles The source files to check + * @param licenseRegEx RegExp to match the license. + * IMPORTANT, RegExp match should match the whole license and in the match groups returns the + * following: + * - match[1] - copyright start year + * - match[2] - copyright end year (if specified) + * @param callback The callback function to execute upon completion. Array of errors strings is passed back, + * in case not matching file is found. + * @param [fix=false] Flag indicating whether correct licenses should be automatically fixed. Default is false. + */ +export declare function checkLicenses(sourceFiles: string[], licenseRegEx: RegExp, callback: (errors: string[]) => void, fix?: boolean): void; diff --git a/scripts/checkLicenses.js b/scripts/checkLicenses.js new file mode 100644 index 0000000..5b3f60d --- /dev/null +++ b/scripts/checkLicenses.js @@ -0,0 +1,101 @@ +/* Copyright (C) 2025 flywave.gl contributors */ +import { exec } from "child_process"; +import * as fs from "fs"; +/** + * To determine the first and last commit year of the file and check against the year(s) in the file's copyright: + * @param sourceFile Path of the source file + * @param match License match result + * @param callback Callback to call once check is completed. Error message should be passed back + */ +function checkYear(sourceFile, [start, end], callback) { + const currentYear = String(new Date().getFullYear()); + exec([ + // Added + `git --no-pager log --follow --diff-filter=A -n 1 --format=%ad --date=format:%Y -- ${sourceFile}`, + // Last modified + `git --no-pager log --follow --diff-filter=MRCA -n 1 --format=%ad --date=format:%Y -- ${sourceFile}` + ].join(" && "), (error, stdOut, stdErr) => { + const copyrightStart = start; + const hasRange = end !== undefined; + const copyrightEnd = hasRange ? end : start; + const found = `${copyrightStart}${hasRange ? "-" + copyrightEnd : ""}`; + if (error) { + callback(error.message); + } + else if (stdErr) { + callback(stdErr.toString()); + } + else if (stdOut.toString() === "") { + // The file is not in Git repository yet, we can't detect the years. + // Please commit first and re-run. + callback(); + } + else if (!/^\d{4}\n\d{4}\n$/.test(stdOut.toString())) { + callback(`Can't determine first/last year of Git commit for file ${sourceFile}`); + } + else { + const [firstCommit, lastCommit] = stdOut.toString().split("\n"); + if (copyrightStart !== firstCommit || + copyrightEnd !== lastCommit || + (hasRange && copyrightStart === copyrightEnd)) { + // Since a new commit is needed to fix it, the new commit must contain the current year: + const expected = `${firstCommit}${firstCommit !== currentYear ? "-" + currentYear : ""}`; + if (found !== expected) { + callback(`${sourceFile} expected: ${expected}, found: ${found}`); + } + else { + callback(); + } + } + else { + callback(); + } + } + }); +} +/** + * Checks whether specified source files contain license matching the specified RegExp. + * @param sourceFiles The source files to check + * @param licenseRegEx RegExp to match the license. + * IMPORTANT, RegExp match should match the whole license and in the match groups returns the + * following: + * - match[1] - copyright start year + * - match[2] - copyright end year (if specified) + * @param callback The callback function to execute upon completion. Array of errors strings is passed back, + * in case not matching file is found. + * @param [fix=false] Flag indicating whether correct licenses should be automatically fixed. Default is false. + */ +export function checkLicenses(sourceFiles, licenseRegEx, callback, fix = false) { + const total = sourceFiles.length; + const errors = []; + let current = 0; + function checkIfDone() { + if (++current >= total) { + callback(errors); + } + } + sourceFiles.forEach(sourceFile => { + const content = fs.readFileSync(sourceFile, { encoding: "utf8" }); + const match = licenseRegEx.exec(content); + if (match === null) { + errors.push(`${sourceFile} has no valid copyright notice`); + checkIfDone(); + } + else { + checkYear(sourceFile, [match[1], match[2]], error => { + if (error) { + // Let's fix it in case of wrong year(s) found and AUTO_FIX is activated: + const errorMatch = /expected: (.+), found: (.+)/.exec(error); + if (fix && errorMatch !== null) { + fs.writeFileSync(sourceFile, content.replace(licenseRegEx, match[0].replace(errorMatch[2], errorMatch[1])), { encoding: "utf8" }); + } + else { + errors.push(error); + } + } + checkIfDone(); + }); + } + }); +} +//# sourceMappingURL=checkLicenses.js.map \ No newline at end of file diff --git a/scripts/checkLicenses.js.map b/scripts/checkLicenses.js.map new file mode 100644 index 0000000..840e60e --- /dev/null +++ b/scripts/checkLicenses.js.map @@ -0,0 +1 @@ +{"version":3,"file":"checkLicenses.js","sourceRoot":"","sources":["checkLicenses.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/scripts/manage-devdeps.d.ts b/scripts/manage-devdeps.d.ts new file mode 100644 index 0000000..b798801 --- /dev/null +++ b/scripts/manage-devdeps.d.ts @@ -0,0 +1,2 @@ +#!/usr/bin/env node +export {}; diff --git a/scripts/prepare_doc_deploy.d.ts b/scripts/prepare_doc_deploy.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/scripts/prepare_doc_deploy.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/scripts/prepare_doc_deploy.js b/scripts/prepare_doc_deploy.js new file mode 100644 index 0000000..59b52b2 --- /dev/null +++ b/scripts/prepare_doc_deploy.js @@ -0,0 +1,55 @@ +/* Copyright (C) 2025 flywave.gl contributors */ +import { execSync } from "child_process"; +import { writeFileSync } from "fs"; +import { copySync, ensureDirSync, removeSync } from "fs-extra"; +import { glob } from "glob"; +import { gt } from "semver"; +const fetch = require("node-fetch"); +//This script prepares the documentation and flywave.gl website to be deployed to S3 +// Precondition: documentation ready on /dist folder +// including docs and examples (e.g. after pnpm run build && pnpm run typedoc) +// See: https://docs.github.com/en/actions/reference/environment-variables +const branch = process.env.GITHUB_REF; +const commitHash = execSync("git rev-parse --short HEAD").toString().trimRight(); +// We store releases using the short commit hash +const isReleaseBranch = branch?.includes("release"); +const refName = isReleaseBranch ? commitHash : "master"; +// create the following directory structure +// dist +// ├──s3_deploy (to be deployed to s3) +// │ ├── [ master | {githash} ] (folder with docs and examples) +// │ ├── index.html (and assets for minisite) +// │ ├── releases.json (list all releases in order) +const targetFolder = `dist/s3_deploy/`; +removeSync(targetFolder); +ensureDirSync(targetFolder); +copySync("www/dist/", `${targetFolder}/`); +copySync("dist/doc/", `${targetFolder}/docs/${refName}/doc`); +copySync("dist/doc-snippets/", `${targetFolder}/docs/${refName}/doc-snippets/`); +copySync("dist/examples/", `${targetFolder}/docs/${refName}/examples/`); +if (isReleaseBranch) { + const now = new Date(); + // WARNING, dates are 0 indexed, hence +1 + const dateString = `${now.getDate()}-${now.getMonth() + 1}-${now.getFullYear()}`; + const allPackages = glob.sync("@flywave/*/package.json"); + const newRelease = { + date: dateString, + hash: commitHash, + version: "" + }; + for (const testPackage of allPackages) { + const mapviewPackage = require(testPackage); + if (newRelease.version === "" || gt(mapviewPackage.version, newRelease.version)) { + newRelease.version = mapviewPackage.version; + } + } + fetch("https://www.flywave.net/releases.json") + .then((res) => { + return res.json(); + }) + .then((releases) => { + const newReleases = [newRelease, ...releases]; + writeFileSync(`${targetFolder}/releases.json`, JSON.stringify(newReleases, undefined, 2)); + }); +} +//# sourceMappingURL=prepare_doc_deploy.js.map \ No newline at end of file diff --git a/scripts/prepare_doc_deploy.js.map b/scripts/prepare_doc_deploy.js.map new file mode 100644 index 0000000..dbccf6d --- /dev/null +++ b/scripts/prepare_doc_deploy.js.map @@ -0,0 +1 @@ +{"version":3,"file":"prepare_doc_deploy.js","sourceRoot":"","sources":["prepare_doc_deploy.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAEhD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AACnC,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAC/D,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE5B,MAAM,KAAK,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;AAEpC,oFAAoF;AACpF,oDAAoD;AACpD,8EAA8E;AAE9E,0EAA0E;AAC1E,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC;AACtC,MAAM,UAAU,GAAG,QAAQ,CAAC,4BAA4B,CAAC,CAAC,QAAQ,EAAE,CAAC,SAAS,EAAE,CAAC;AACjF,gDAAgD;AAChD,MAAM,eAAe,GAAG,MAAM,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC;AACpD,MAAM,OAAO,GAAG,eAAe,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC;AAExD,2CAA2C;AAC3C,OAAO;AACP,sCAAsC;AACtC,iEAAiE;AACjE,+CAA+C;AAC/C,qDAAqD;AAErD,MAAM,YAAY,GAAG,iBAAiB,CAAC;AAEvC,UAAU,CAAC,YAAY,CAAC,CAAC;AACzB,aAAa,CAAC,YAAY,CAAC,CAAC;AAC5B,QAAQ,CAAC,WAAW,EAAE,GAAG,YAAY,GAAG,CAAC,CAAC;AAC1C,QAAQ,CAAC,WAAW,EAAE,GAAG,YAAY,SAAS,OAAO,MAAM,CAAC,CAAC;AAC7D,QAAQ,CAAC,oBAAoB,EAAE,GAAG,YAAY,SAAS,OAAO,gBAAgB,CAAC,CAAC;AAChF,QAAQ,CAAC,gBAAgB,EAAE,GAAG,YAAY,SAAS,OAAO,YAAY,CAAC,CAAC;AAoBxE,IAAI,eAAe,EAAE,CAAC;IAClB,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IACvB,yCAAyC;IACzC,MAAM,UAAU,GAAG,GAAG,GAAG,CAAC,OAAO,EAAE,IAAI,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,IAAI,GAAG,CAAC,WAAW,EAAE,EAAE,CAAC;IACjF,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;IACzD,MAAM,UAAU,GAAY;QACxB,IAAI,EAAE,UAAU;QAChB,IAAI,EAAE,UAAU;QAChB,OAAO,EAAE,EAAE;KACd,CAAC;IACF,KAAK,MAAM,WAAW,IAAI,WAAW,EAAE,CAAC;QACpC,MAAM,cAAc,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;QAC5C,IAAI,UAAU,CAAC,OAAO,KAAK,EAAE,IAAI,EAAE,CAAC,cAAc,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YAC9E,UAAU,CAAC,OAAO,GAAG,cAAc,CAAC,OAAO,CAAC;QAChD,CAAC;IACL,CAAC;IAED,KAAK,CAAC,uCAAuC,CAAC;SACzC,IAAI,CAAC,CAAC,GAAa,EAAE,EAAE;QACpB,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC;IACtB,CAAC,CAAC;SACD,IAAI,CAAC,CAAC,QAAmB,EAAE,EAAE;QAC1B,MAAM,WAAW,GAAG,CAAC,UAAU,EAAE,GAAG,QAAQ,CAAC,CAAC;QAC9C,aAAa,CACT,GAAG,YAAY,gBAAgB,EAC/B,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,SAAS,EAAE,CAAC,CAAC,CAC5C,CAAC;IACN,CAAC,CAAC,CAAC;AACX,CAAC"} \ No newline at end of file diff --git a/test/ImportTest.d.ts b/test/ImportTest.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/test/ImportTest.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/test/ImportTest.js b/test/ImportTest.js new file mode 100644 index 0000000..64f0814 --- /dev/null +++ b/test/ImportTest.js @@ -0,0 +1,186 @@ +/* Copyright (C) 2025 flywave.gl contributors */ +/* + * Copyright (C) 2018-2021 HERE Europe B.V. + * Licensed under Apache 2.0, see full license in LICENSE + * SPDX-License-Identifier: Apache-2.0 + */ +import { assert } from "chai"; +import * as fs from "fs"; +import * as glob from "glob"; +import * as path from "path"; +// these dependencies are ok to include in files using node.js +const nodeDependencyWhitelist = { + http: true, + https: true, + util: true, + url: true, + fs: true, + path: true +}; +function checkImports() { + // first, read all package.json files and remember the dependencies + const packageDependencies = {}; + const packageConfigs = {}; + const packageJsonFiles = [...glob.sync(__dirname + "/../@flywave/*/package.json")]; + for (const packageJsonFile of packageJsonFiles) { + const moduleName = "@flywave/" + packageJsonFile.split(path.sep).slice(-2, -1)[0]; + packageConfigs[moduleName] = JSON.parse(fs.readFileSync(packageJsonFile, "utf-8")); + } + // recursively gets all direct and indirect dependencies + function getDependencies(moduleName, deps) { + if (deps[moduleName]) { + return; + } + deps[moduleName] = true; + const config = packageConfigs[moduleName]; + if (config) { + for (const dep in config.dependencies) { + getDependencies(dep, deps); + } + for (const dep in config.peerDependencies) { + getDependencies(dep, deps); + } + } + } + for (const moduleName in packageConfigs) { + const config = packageConfigs[moduleName]; + const dependencies = {}; + for (const dep in config.dependencies) { + getDependencies(dep, dependencies); + } + for (const dep in config.devDependencies) { + getDependencies(dep, dependencies); + } + for (const dep in config.peerDependencies) { + getDependencies(dep, dependencies); + } + packageDependencies[moduleName] = dependencies; + } + // now, iterate all typescript source files + const sourceFiles = glob + .sync(__dirname + "/../@flywave/**/*.ts") + .filter(sourcePath => !sourcePath.includes("node_modules") && !sourcePath.includes("generator-flywave.gl")); + // regular expression catching all imported modules + const importRE = /(.*)?import\s*{[\s\S]*?}\s*from\s+["'](.*)["']/gi; + const environmentRE = /@flywave:check-imports:environment:(.*)/gi; + const errors = new Array(); + for (const sourceFile of sourceFiles) { + const contents = fs.readFileSync(sourceFile, "utf-8"); + let env = "browser"; + const relativePath = path.relative(__dirname + "/../@flywave", sourceFile); + const moduleName = "@flywave/" + relativePath.split(path.sep)[0]; + const modulePath = path.resolve(__dirname + "/../", moduleName); + const environmentMatch = environmentRE.exec(contents); + if (environmentMatch) { + env = environmentMatch[1]; + if (env !== "browser" && env !== "node") { + errors.push(`Error: ${relativePath} unknown '@flywave:environment:' type: ${env}. Supported types: 'node', 'browser'`); + env = "browser"; + } + } + // iterate through all matched imported modules + let matches; + while ((matches = importRE.exec(contents)) != null) { + const beginningOfLine = matches[1] || ""; + // If the line is a comment, ignore it + if (beginningOfLine.includes("//") || + beginningOfLine.includes("/*") || + beginningOfLine.includes("*")) { + continue; + } + const importedModule = matches[2]; + // 1) Make sure we don't self-import + if (importedModule === moduleName) { + errors.push(`Error: ${relativePath} contains wrong module import "${importedModule}", change it to a relative import`); + } + // 2) Make sure we don't include unknown dependencies + const localModule = importedModule.startsWith("."); + const allowedNodeModule = env === "node" && nodeDependencyWhitelist[importedModule]; + if (!localModule && !allowedNodeModule) { + const importedModuleName = importedModule.startsWith("@") + ? importedModule.split("/").slice(0, 2).join("/") + : importedModule.split("/")[0]; + if (packageDependencies[moduleName] !== undefined && + packageDependencies[moduleName][importedModuleName] === undefined) { + errors.push(`Error: unknown module ${importedModuleName} used in ${moduleName}: ` + + `${relativePath}`); + } + } + // 3) Make sure we don't include other @flywave modules via relative paths + if (importedModule.startsWith("..")) { + const resolvedImportedModule = path.resolve(path.dirname(sourceFile), importedModule); + if (!path.dirname(resolvedImportedModule).startsWith(modulePath)) { + errors.push(`Error: the module ${importedModule} is imported via a relative path in ${sourceFile}`); + } + } + } + } + // 4) Look for circular dependencies (Tarjan's SCC algorithm) + function detectCircularDependencies() { + let index = 0; + const stack = new Array(); + const modules = new Map(); + for (const moduleName in packageConfigs) { + const dependencies = Object.keys(packageDependencies[moduleName]).filter(dependencyName => dependencyName.startsWith("@flywave/") && dependencyName !== moduleName); + const module = { + name: moduleName, + index: undefined, + lowLink: undefined, + onStack: false, + dependencies + }; + modules.set(moduleName, module); + } + modules.forEach(function (module) { + if (module.index === undefined) { + strongConnect(module); + } + }); + function strongConnect(module) { + module.index = index; + module.lowLink = index; + index++; + stack.push(module); + module.onStack = true; + for (let i = 0; i < module.dependencies.length; i++) { + const successorName = module.dependencies[i]; + const successor = modules.get(successorName); + if (successor === undefined) { + continue; + } + if (successor.index === undefined) { + strongConnect(successor); + module.lowLink = Math.min(module.lowLink, successor.lowLink); + } + else if (stack.includes(successor)) { + module.lowLink = Math.min(module.lowLink, successor.index); + } + } + if (module.lowLink === module.index) { + // module is a root node + let circularDependency = false; + let errorMessage = `Loop detected: ${module.name}`; + let dependency = stack.pop(); + while (dependency !== undefined && dependency.name !== module.name) { + module.onStack = false; + errorMessage += ` -> ${dependency.name}`; + dependency = stack.pop(); + circularDependency = true; + } + if (circularDependency) { + errorMessage += ` -> ${module.name}`; + errors.push(errorMessage); + } + } + } + } + detectCircularDependencies(); + return errors; +} +describe("ImportCheck", function () { + it("Uses correct imports", function () { + const errors = checkImports(); + assert.deepEqual(errors, []); + }); +}); +//# sourceMappingURL=ImportTest.js.map \ No newline at end of file diff --git a/test/ImportTest.js.map b/test/ImportTest.js.map new file mode 100644 index 0000000..1a2182e --- /dev/null +++ b/test/ImportTest.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ImportTest.js","sourceRoot":"","sources":["ImportTest.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/test/LicenseHeaderTest.d.ts b/test/LicenseHeaderTest.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/test/LicenseHeaderTest.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/test/LicenseHeaderTest.js b/test/LicenseHeaderTest.js new file mode 100644 index 0000000..e77e6ae --- /dev/null +++ b/test/LicenseHeaderTest.js @@ -0,0 +1,26 @@ +/* Copyright (C) 2025 flywave.gl contributors */ +import { assert } from "chai"; +import * as glob from "glob"; +import * as path from "path"; +import { checkLicenses } from "../scripts/checkLicenses"; +const APACHE_LICENSE = /\/\*\n \* Copyright \(C\) (\d{4})(?:-(\d{4}))? HERE Europe B\.V\.\n \* Licensed under Apache 2\.0\, see full license in LICENSE\n \* SPDX\-License\-Identifier\: Apache\-2\.0\n \*\//; +// To fix wrong year(s) of the copyright notice on a local machine automatically: +const AUTO_FIX = process.argv.slice(2).includes("--fix"); +describe("LicenseHeaderCheck", function () { + it("Contains correct license header", function (done) { + // In some cases (coverage run on CI) --no-timeouts flag is not passed, + // so we have to set it here specifically. + this.timeout(180000); // 3 mins + const sourceFiles = glob + .sync(path.join(__dirname, "..", "**/*.ts")) + .filter(file => !file.includes("/node_modules/")) + .filter(file => !file.includes("/dist/")) + .filter(file => !file.endsWith(".d.ts")) + .sort(); + checkLicenses(sourceFiles, APACHE_LICENSE, errors => { + assert.deepEqual(errors, []); + done(); + }, AUTO_FIX); + }); +}); +//# sourceMappingURL=LicenseHeaderTest.js.map \ No newline at end of file diff --git a/test/LicenseHeaderTest.js.map b/test/LicenseHeaderTest.js.map new file mode 100644 index 0000000..9e2e9f9 --- /dev/null +++ b/test/LicenseHeaderTest.js.map @@ -0,0 +1 @@ +{"version":3,"file":"LicenseHeaderTest.js","sourceRoot":"","sources":["LicenseHeaderTest.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAEhD,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAE7B,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAEzD,MAAM,cAAc,GAChB,sLAAsL,CAAC;AAC3L,iFAAiF;AACjF,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;AAEzD,QAAQ,CAAC,oBAAoB,EAAE;IAC3B,EAAE,CAAC,iCAAiC,EAAE,UAAU,IAAI;QAChD,uEAAuE;QACvE,0CAA0C;QAC1C,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;QAC/B,MAAM,WAAW,GAAG,IAAI;aACnB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;aAC3C,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC;aAChD,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;aACxC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;aACvC,IAAI,EAAE,CAAC;QAEZ,aAAa,CACT,WAAW,EACX,cAAc,EACd,MAAM,CAAC,EAAE;YACL,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;YAC7B,IAAI,EAAE,CAAC;QACX,CAAC,EACD,QAAQ,CACX,CAAC;IACN,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"} \ No newline at end of file diff --git a/test/performance/LinesPerformanceTest.d.ts b/test/performance/LinesPerformanceTest.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/test/performance/LinesPerformanceTest.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/test/performance/LinesPerformanceTest.js b/test/performance/LinesPerformanceTest.js new file mode 100644 index 0000000..8aa0652 --- /dev/null +++ b/test/performance/LinesPerformanceTest.js @@ -0,0 +1,43 @@ +/* Copyright (C) 2025 flywave.gl contributors */ +import { mercatorProjection } from "@flywave/flywave-geoutils"; +import { createLineGeometry } from "@flywave/flywave-lines"; +import { measureThroughputSync } from "@flywave/flywave-test-utils/lib/ProfileHelper"; +import * as THREE from "three"; +if (typeof window === "undefined") { + const perfHooks = require("perf_hooks"); + global.performance = perfHooks.performance; + global.PerformanceObserver = perfHooks.PerformanceObserver; + global.PerformanceEntry = perfHooks.PerformanceEntry; +} +describe(`lines`, function () { + this.timeout(0); + const center = new THREE.Vector3(); + const tests = [ + { segments: 2 }, + { segments: 4 }, + { segments: 16 }, + { segments: 64 }, + { segments: 256 } + ]; + before(function () { + this.timeout(0); + tests.forEach(test => { + const segments = test.segments; + test.points = []; + const radius = 100; + for (let i = 0; i < segments; i++) { + const angle = (i * 360) / segments; + test.points.push(Math.cos(THREE.MathUtils.degToRad(angle) * radius), Math.cos(THREE.MathUtils.degToRad(angle) * radius), 0); + } + }); + }); + tests.forEach(test => { + it(`createLineGeometry segments=${test.segments}`, async function () { + this.timeout(0); + await measureThroughputSync(`createLineGeometry segments=${test.segments}`, 1000, function () { + createLineGeometry(center, test.points, mercatorProjection); + }); + }); + }); +}); +//# sourceMappingURL=LinesPerformanceTest.js.map \ No newline at end of file diff --git a/test/performance/LinesPerformanceTest.js.map b/test/performance/LinesPerformanceTest.js.map new file mode 100644 index 0000000..f293fb5 --- /dev/null +++ b/test/performance/LinesPerformanceTest.js.map @@ -0,0 +1 @@ +{"version":3,"file":"LinesPerformanceTest.js","sourceRoot":"","sources":["LinesPerformanceTest.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAEhD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,qBAAqB,EAAE,MAAM,+CAA+C,CAAC;AACtF,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;IAChC,MAAM,SAAS,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IAEvC,MAAc,CAAC,WAAW,GAAG,SAAS,CAAC,WAAW,CAAC;IACnD,MAAc,CAAC,mBAAmB,GAAG,SAAS,CAAC,mBAAmB,CAAC;IACnE,MAAc,CAAC,gBAAgB,GAAG,SAAS,CAAC,gBAAgB,CAAC;AAClE,CAAC;AAED,QAAQ,CAAC,OAAO,EAAE;IACd,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAChB,MAAM,MAAM,GAAG,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;IAEnC,MAAM,KAAK,GAAmD;QAC1D,EAAE,QAAQ,EAAE,CAAC,EAAE;QACf,EAAE,QAAQ,EAAE,CAAC,EAAE;QACf,EAAE,QAAQ,EAAE,EAAE,EAAE;QAChB,EAAE,QAAQ,EAAE,EAAE,EAAE;QAChB,EAAE,QAAQ,EAAE,GAAG,EAAE;KACpB,CAAC;IAEF,MAAM,CAAC;QACH,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAChB,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;YACjB,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;YAC/B,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;YACjB,MAAM,MAAM,GAAG,GAAG,CAAC;YACnB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,EAAE,CAAC,EAAE,EAAE,CAAC;gBAChC,MAAM,KAAK,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,QAAQ,CAAC;gBACnC,IAAI,CAAC,MAAM,CAAC,IAAI,CACZ,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,EAClD,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,EAClD,CAAC,CACJ,CAAC;YACN,CAAC;QACL,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;IAEH,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;QACjB,EAAE,CAAC,+BAA+B,IAAI,CAAC,QAAQ,EAAE,EAAE,KAAK;YACpD,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YAChB,MAAM,qBAAqB,CACvB,+BAA+B,IAAI,CAAC,QAAQ,EAAE,EAC9C,IAAI,EACJ;gBACI,kBAAkB,CAAC,MAAM,EAAE,IAAI,CAAC,MAAO,EAAE,kBAAkB,CAAC,CAAC;YACjE,CAAC,CACJ,CAAC;QACN,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"} \ No newline at end of file diff --git a/test/performance/OmvDecoderPerformanceTest.d.ts b/test/performance/OmvDecoderPerformanceTest.d.ts new file mode 100644 index 0000000..18d30b4 --- /dev/null +++ b/test/performance/OmvDecoderPerformanceTest.d.ts @@ -0,0 +1,33 @@ +import { type Theme } from "@flywave/flywave-datasource-protocol"; +import { type OmvRestClientParameters } from "@flywave/flywave-vectortile-datasource"; +export interface OMVDecoderPerformanceTestOptions { + /** + * + */ + repeats?: number; + /** + * Theme url or object. + * + * Will be resolved using [[ThemeLoader.load]]. + */ + theme: Theme | string; + /** + * Styleset name, defaults to `tilezen`. + */ + styleSetName?: string; + /** + * Morton codes of tiles. + */ + tiles: number[]; + /** + * Requires settings for [[OmvRestClient]] to download tiles. + */ + omvRestClientOptions: OmvRestClientParameters; +} +/** + * Create tests that downloads some OMV tiles from real datasource, then decodes them using + * particular style. + * + * @see OMVDecoderPerformanceTestOptions + */ +export declare function createOMVDecoderPerformanceTest(name: string, options: OMVDecoderPerformanceTestOptions): void; diff --git a/test/performance/OmvDecoderPerformanceTest.js b/test/performance/OmvDecoderPerformanceTest.js new file mode 100644 index 0000000..0309d49 --- /dev/null +++ b/test/performance/OmvDecoderPerformanceTest.js @@ -0,0 +1,165 @@ +/* Copyright (C) 2025 flywave.gl contributors */ +import { MapEnv, StyleSetEvaluator } from "@flywave/flywave-datasource-protocol/index-decoder"; +import { apikey } from "@flywave/flywave-examples/config"; +import { mercatorProjection, sphereProjection, TileKey, webMercatorProjection } from "@flywave/flywave-geoutils"; +import { ThemeLoader } from "@flywave/flywave-mapview"; +import { getTestResourceUrl } from "@flywave/flywave-test-utils/index.node"; +import { measurePerformanceSync } from "@flywave/flywave-test-utils/lib/ProfileHelper"; +import { APIFormat, AuthenticationMethod, OmvRestClient } from "@flywave/flywave-vectortile-datasource"; +import { OmvDataAdapter } from "@flywave/flywave-vectortile-datasource/lib/adapters/omv/OmvDataAdapter"; +import { DecodeInfo } from "@flywave/flywave-vectortile-datasource/lib/DecodeInfo"; +import { VectorTileDataProcessor } from "@flywave/flywave-vectortile-datasource/lib/VectorTileDecoder"; +import { assert } from "chai"; +if (typeof window === "undefined") { + const perfHooks = require("perf_hooks"); + global.performance = perfHooks.performance; + global.PerformanceObserver = perfHooks.PerformanceObserver; + global.PerformanceEntry = perfHooks.PerformanceEntry; +} +/** + * Create tests that downloads some OMV tiles from real datasource, then decodes them using + * particular style. + * + * @see OMVDecoderPerformanceTestOptions + */ +export function createOMVDecoderPerformanceTest(name, options) { + const repeats = options.repeats ?? 10; + const styleSetName = options.styleSetName ?? "tilezen"; + describe(`OMVDecoderPerformanceTest - ${name}`, function () { + this.timeout(0); + let omvTiles; + let theme; + before(async function () { + this.timeout(10000); + const omvDataProvider = new OmvRestClient(options.omvRestClientOptions); + await omvDataProvider.connect(); + assert(omvDataProvider.ready()); + omvTiles = await Promise.all(options.tiles.map(async (mortonCode) => { + const tileKey = TileKey.fromMortonCode(mortonCode); + const tile = await omvDataProvider.getTile(tileKey); + assert(tile instanceof ArrayBuffer); + return [tileKey, tile]; + })); + theme = await ThemeLoader.load(options.theme); + assert.isObject(theme.styles); + assert.isArray(theme.styles[styleSetName]); + }); + it(`measure feature matching time`, async () => { + const counterName = `OMVDecoderPerformanceTest-${name} styleMatchOnly`; + this.timeout(0); + const styleSetEvaluator = new StyleSetEvaluator({ + styleSet: theme.styles[styleSetName], + definitions: theme.definitions + }); + const geometryProcessor = { + processPointFeature(layerName, tileExtents, geometry, properties) { + const env = new MapEnv(properties); + styleSetEvaluator.getMatchingTechniques(env, layerName, "point"); + }, + processLineFeature(layerName, tileExtents, geometry, properties) { + const env = new MapEnv(properties); + styleSetEvaluator.getMatchingTechniques(env, layerName, "line"); + }, + processPolygonFeature(layerName, tileExtents, geometry, properties) { + const env = new MapEnv(properties); + styleSetEvaluator.getMatchingTechniques(env, layerName, "polygon"); + } + }; + await measurePerformanceSync(counterName, repeats, function () { + for (const [tileKey, tileData] of omvTiles) { + const decoder = new OmvDataAdapter(); + const decodeInfo = new DecodeInfo(mercatorProjection, tileKey, 0); + decoder.process(tileData, decodeInfo, geometryProcessor); + } + }); + }); + it(`measure decode time - webMercator`, async () => { + const counterName = `OMVDecoderPerformanceTest-${name} webMercator`; + this.timeout(0); + const projection = webMercatorProjection; + const styleSetEvaluator = new StyleSetEvaluator({ + styleSet: theme.styles[styleSetName], + definitions: theme.definitions + }); + await measurePerformanceSync(counterName, repeats, function () { + for (const [tileKey, tileData] of omvTiles) { + const decoder = new VectorTileDataProcessor(tileKey, projection, styleSetEvaluator, new OmvDataAdapter()); + decoder.getDecodedTile(tileData); + } + }); + }); + it(`measure decode time - sphereProjection`, async () => { + this.timeout(0); + const counterName = `OMVDecoderPerformanceTest-${name} sphere`; + const projection = sphereProjection; + const styleSetEvaluator = new StyleSetEvaluator({ + styleSet: theme.styles[styleSetName], + definitions: theme.definitions + }); + await measurePerformanceSync(counterName, repeats, function () { + for (const [tileKey, tileData] of omvTiles) { + const decoder = new VectorTileDataProcessor(tileKey, projection, styleSetEvaluator, new OmvDataAdapter()); + decoder.getDecodedTile(tileData); + } + }); + }); + }); +} +const BERLIN_CENTER_TILES = [371506851, 371506850, 371506849, 371506848]; +createOMVDecoderPerformanceTest("theme=berlin tiles=4 region=berlin data=herebase", { + theme: getTestResourceUrl("@flywave/flywave-map-theme", "resources/tilezen_base.json"), + tiles: BERLIN_CENTER_TILES, + omvRestClientOptions: { + baseUrl: "https://vector.hereapi.com/v2/vectortiles/base/mc", + apiFormat: APIFormat.XYZOMV, + authenticationCode: apikey, + authenticationMethod: { + method: AuthenticationMethod.QueryString, + name: "apikey" + } + } +}); +createOMVDecoderPerformanceTest("theme=berlin tiles=4 region=berlin data=osmbase", { + theme: getTestResourceUrl("@flywave/flywave-map-theme", "resources/tilezen_base.json"), + tiles: BERLIN_CENTER_TILES, + omvRestClientOptions: { + baseUrl: "https://vector.hereapi.com/v2/vectortiles/base/mc", + apiFormat: APIFormat.XYZOMV, + authenticationCode: apikey, + authenticationMethod: { + method: AuthenticationMethod.QueryString, + name: "apikey" + } + } +}); +const NEW_YORK_TILES = [ + 327439127, 327439124, 327439125, 327439168, 327439170, + 327438781, 327438783, 327438826, 327438782, 327438824 +]; +createOMVDecoderPerformanceTest("theme=berlin tiles=10 region=ny data=herebase", { + theme: getTestResourceUrl("@flywave/flywave-map-theme", "resources/tilezen_base.json"), + tiles: NEW_YORK_TILES, + omvRestClientOptions: { + baseUrl: "https://vector.hereapi.com/v2/vectortiles/base/mc", + apiFormat: APIFormat.XYZOMV, + authenticationCode: apikey, + authenticationMethod: { + method: AuthenticationMethod.QueryString, + name: "apikey" + } + } +}); +createOMVDecoderPerformanceTest("theme=berlin tiles=10 region=ny data=osmbase", { + theme: getTestResourceUrl("@flywave/flywave-map-theme", "resources/tilezen_base.json"), + tiles: NEW_YORK_TILES, + omvRestClientOptions: { + baseUrl: "https://vector.hereapi.com/v2/vectortiles/base/mc", + apiFormat: APIFormat.XYZOMV, + authenticationCode: apikey, + authenticationMethod: { + method: AuthenticationMethod.QueryString, + name: "apikey" + } + } +}); +//# sourceMappingURL=OmvDecoderPerformanceTest.js.map \ No newline at end of file diff --git a/test/performance/OmvDecoderPerformanceTest.js.map b/test/performance/OmvDecoderPerformanceTest.js.map new file mode 100644 index 0000000..e49ac18 --- /dev/null +++ b/test/performance/OmvDecoderPerformanceTest.js.map @@ -0,0 +1 @@ +{"version":3,"file":"OmvDecoderPerformanceTest.js","sourceRoot":"","sources":["OmvDecoderPerformanceTest.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/test/rendering/ClipPlanesRenderingTest.d.ts b/test/rendering/ClipPlanesRenderingTest.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/test/rendering/ClipPlanesRenderingTest.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/test/rendering/ClipPlanesRenderingTest.js b/test/rendering/ClipPlanesRenderingTest.js new file mode 100644 index 0000000..05f3695 --- /dev/null +++ b/test/rendering/ClipPlanesRenderingTest.js @@ -0,0 +1,216 @@ +/* Copyright (C) 2025 flywave.gl contributors */ +import { mercatorProjection, sphereProjection } from "@flywave/flywave-geoutils"; +import { CameraUtils } from "@flywave/flywave-mapview"; +import * as THREE from "three"; +import { GeoJsonTest } from "./utils/GeoJsonTest"; +import { ThemeBuilder } from "./utils/ThemeBuilder"; +const style = [ + { + when: "$geometryType == 'polygon'", + technique: "fill", + attr: { + color: ["get", "color"], + opacity: 1.0, + minZoomLevel: ["get", "minZoom"], + maxZoomLevel: ["get", "maxZoom"] + } + } +]; +const theme = { clearColor: "black", lights: ThemeBuilder.lights, styles: { geojson: style } }; +const seaworld = { + type: "FeatureCollection", + features: [ + { + type: "Feature", + properties: { color: "#436981" }, + geometry: { + type: "Polygon", + coordinates: [ + [ + [-180, -90], + [180, -90], + [180, 90], + [-180, 90], + [-180, -90] + ] + ] + } + }, + // Crosshair at camera target [0,0] for high zoom levels. + { + type: "Feature", + properties: { color: "red", minZoom: 6 }, + geometry: { + type: "Polygon", + coordinates: [ + [ + [-0.002, -0.02], + [0.002, -0.02], + [0.002, 0.02], + [-0.002, 0.02], + [-0.002, -0.02] + ] + ] + } + }, + { + type: "Feature", + properties: { color: "red", minZoom: 6 }, + geometry: { + type: "Polygon", + coordinates: [ + [ + [-0.02, -0.002], + [0.02, -0.002], + [0.02, 0.002], + [-0.02, 0.002], + [-0.02, -0.002] + ] + ] + } + }, + // Crosshair at camera target [0,0] for low zoom levels. + { + type: "Feature", + properties: { color: "red", maxZoom: 6 }, + geometry: { + type: "Polygon", + coordinates: [ + [ + [-0.1, -1], + [0.1, -1], + [0.1, 1], + [-0.1, 1], + [-0.1, -1] + ] + ] + } + }, + { + type: "Feature", + properties: { color: "red", maxZoom: 6 }, + geometry: { + type: "Polygon", + coordinates: [ + [ + [-1, -0.1], + [1, -0.1], + [1, 0.1], + [-1, 0.1], + [-1, -0.1] + ] + ] + } + } + ] +}; +describe("ClipPlanes rendering test", function () { + this.timeout(5000); + let geoJsonTest; + beforeEach(function () { + geoJsonTest = new GeoJsonTest(); + }); + afterEach(() => { + geoJsonTest.dispose(); + }); + const tests = [ + { + projection: mercatorProjection, + zoomLevel: 11, + tilt: 65 + }, + { + projection: mercatorProjection, + zoomLevel: 11, + tilt: 65, + principalPointNDC: [0, -0.5] + }, + { + projection: mercatorProjection, + zoomLevel: 11, + tilt: 70 + }, + { + projection: mercatorProjection, + zoomLevel: 11, + tilt: 70, + principalPointNDC: [0, 0.5] + }, + { + projection: sphereProjection, + zoomLevel: 11, + tilt: 65 + }, + { + projection: sphereProjection, + zoomLevel: 11, + tilt: 65, + principalPointNDC: [0, -0.5] + }, + { + projection: sphereProjection, + zoomLevel: 11, + tilt: 70 + }, + { + projection: sphereProjection, + zoomLevel: 11, + tilt: 70, + principalPointNDC: [0, 0.5] + }, + { + projection: sphereProjection, + zoomLevel: 4, + tilt: 10 + }, + { + projection: sphereProjection, + zoomLevel: 4, + tilt: 10, + principalPointNDC: [0, -0.9] + }, + { + projection: sphereProjection, + zoomLevel: 4, + principalPointNDC: [-0.7, 0] + }, + { + projection: sphereProjection, + zoomLevel: 4, + principalPointNDC: [0.7, 0] + }, + { + projection: sphereProjection, + zoomLevel: 3, + principalPointNDC: [0, 0.9] + } + ]; + for (const test of tests) { + const projection = test.projection; + const zoomLevel = test.zoomLevel; + const tilt = test.tilt ?? 0; + const principalPoint = test.principalPointNDC ?? [0, 0]; + const projName = projection === mercatorProjection ? "mercator" : "sphere"; + const testName = `${projName}, zl${zoomLevel}, tilt ${tilt}, ppal point ${principalPoint}`; + // For image name, replace test name spaces and commas with dashes and make lower case. + const testImageName = "clip-planes-" + testName.replace(/[,\s]+/g, "-").toLowerCase(); + it(`${testName}`, async function () { + await geoJsonTest.run({ + mochaTest: this, + testImageName, + theme, + geoJson: seaworld, + lookAt: { + zoomLevel, + target: [0, 0], + tilt + }, + projection, + beforeFinishCallback: mapView => { + CameraUtils.setPrincipalPoint(mapView.camera, new THREE.Vector2().fromArray(principalPoint)); + } + }); + }); + } +}); +//# sourceMappingURL=ClipPlanesRenderingTest.js.map \ No newline at end of file diff --git a/test/rendering/ClipPlanesRenderingTest.js.map b/test/rendering/ClipPlanesRenderingTest.js.map new file mode 100644 index 0000000..2b31cca --- /dev/null +++ b/test/rendering/ClipPlanesRenderingTest.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ClipPlanesRenderingTest.js","sourceRoot":"","sources":["ClipPlanesRenderingTest.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/test/rendering/GeoJsonDataRendering.d.ts b/test/rendering/GeoJsonDataRendering.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/test/rendering/GeoJsonDataRendering.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/test/rendering/GeoJsonDataRendering.js b/test/rendering/GeoJsonDataRendering.js new file mode 100644 index 0000000..f99eb94 --- /dev/null +++ b/test/rendering/GeoJsonDataRendering.js @@ -0,0 +1,928 @@ +/* Copyright (C) 2025 flywave.gl contributors */ +import { clipLineString } from "@flywave/flywave-geometry/src/ClipLineString"; +import { wrapLineString } from "@flywave/flywave-geometry/src/WrapLineString"; +import { wrapPolygon } from "@flywave/flywave-geometry/src/WrapPolygon"; +import { EarthConstants, GeoBox, GeoCoordinates, webMercatorProjection, webMercatorTilingScheme } from "@flywave/flywave-geoutils"; +import { DataProvider } from "@flywave/flywave-mapview-decoder"; +import * as turf from "@turf/turf"; +import { Vector3 } from "three"; +import * as polygon_crossing_antimeridian from "../resources/polygon_crossing_antimeridian.json"; +import { GeoJsonStore } from "./utils/GeoJsonStore"; +import { GeoJsonTest } from "./utils/GeoJsonTest"; +import { ThemeBuilder } from "./utils/ThemeBuilder"; +// Mocha discourages using arrow functions, see https://mochajs.org/#arrow-functions +describe("MapView + OmvDataSource + GeoJsonDataProvider rendering test", function () { + const geoJsonTest = new GeoJsonTest(); + // get the default lighting set up.fterEach(() => geoJsonTest.dispose()); + const lights = ThemeBuilder.lights; + afterEach(() => { + geoJsonTest.dispose(); + }); + it("renders flat polygon using fill technique", async function () { + this.timeout(5000); + const greenStyle = [ + { + when: "$geometryType == 'polygon'", + technique: "fill", + attr: { + color: "#008000", + opacity: 0.5 + } + } + ]; + await geoJsonTest.run({ + mochaTest: this, + testImageName: "geojson-polygon-fill", + theme: { lights, styles: { geojson: greenStyle } }, + geoJson: "../dist/resources/basic_polygon.json" + }); + }); + it("renders flat polygon using extruded-polygon technique", async function () { + this.timeout(5000); + const greenStyle = [ + { + when: "$geometryType == 'polygon'", + technique: "extruded-polygon", + attr: { + height: 0, + color: "#008000", + opacity: 0.9, + lineWidth: 1, + lineColor: "#172023", + lineColorMix: 0.6, + metalness: 0.5, + roughness: 0.5 + } + } + ]; + await geoJsonTest.run({ + mochaTest: this, + testImageName: "geojson-extruded-polygon-flat", + theme: { + lights, + styles: { geojson: greenStyle } + }, + geoJson: "../dist/resources/basic_polygon.json", + lookAt: { + tilt: 45, + heading: 30 + } + }); + }); + it("renders japanese flag with enabled as dynamic expression", async function () { + this.timeout(50000); + const greenStyle = [ + { + when: ["==", ["geometry-type"], "Point"], + technique: "circles", + renderOrder: 10000, + // select the color based on the the value of the dynamic property `correct`. + color: "#BC002D", + // This causes the bug FLYWAVE-12247 + enabled: ["get", "enabled", ["dynamic-properties"]], + size: ["get", "size", ["dynamic-properties"]] + } + ]; + await geoJsonTest.run({ + mochaTest: this, + testImageName: "geojson-point-enabled-as-dynamic-expression", + theme: { lights, styles: { geojson: greenStyle } }, + geoJson: "../dist/resources/basic_polygon.json", + size: 150 + }); + }); + it("renders extruded polygons with height", async function () { + this.timeout(5000); + const ourStyle = [ + { + when: ["==", ["geometry-type"], "Polygon"], + technique: "extruded-polygon", + attr: { + vertexColors: false, + lineWidth: 1, + lineColor: "#172023", + lineColorMix: 0.6, + metalness: 0.5, + roughness: 0.5 + } + } + ]; + await geoJsonTest.run({ + mochaTest: this, + testImageName: "geojson-extruded-polygon-with-height", + theme: { lights, styles: { geojson: ourStyle } }, + geoJson: "../dist/resources/basic_polygon.json", + lookAt: { + tilt: 45, + heading: 30 + } + }); + }); + it("renders extruded polygons with height and color", async function () { + this.timeout(5000); + const ourStyle = [ + { + when: ["==", ["geometry-type"], "Polygon"], + technique: "extruded-polygon", + attr: { + vertexColors: true, + color: ["string", ["get", "color"], "#5050f0"], + lineWidth: 1, + lineColor: "#172023", + lineColorMix: 0.6, + metalness: 0.5, + roughness: 0.5 + } + } + ]; + await geoJsonTest.run({ + mochaTest: this, + testImageName: "geojson-extruded-polygon-with-height-color", + theme: { lights, styles: { geojson: ourStyle } }, + geoJson: "../dist/resources/basic_polygon.json", + lookAt: { + tilt: 45, + heading: 30 + } + }); + }); + it("renders extruded polygons with height and color - no batching", async function () { + this.timeout(5000); + const ourStyle = [ + { + when: ["==", ["geometry-type"], "Polygon"], + technique: "extruded-polygon", + attr: { + color: ["string", ["get", "color"], "#5050f0"], + lineWidth: 1, + lineColor: "#172023", + lineColorMix: 0.6, + metalness: 0.5, + roughness: 0.5 + } + } + ]; + await geoJsonTest.run({ + mochaTest: this, + testImageName: "geojson-extruded-polygon-with-height-color-no-batching", + theme: { lights, styles: { geojson: ourStyle } }, + geoJson: "../dist/resources/basic_polygon.json", + lookAt: { + tilt: 45, + heading: 30 + } + }); + }); + it("renders extruded polygons with height, without outline", async function () { + this.timeout(5000); + const ourStyle = [ + { + when: ["==", ["geometry-type"], "Polygon"], + technique: "extruded-polygon", + attr: { + vertexColors: false, + lineWidth: 0, + lineColor: "#172023", + lineColorMix: 0.6, + metalness: 0.5, + roughness: 0.5 + } + } + ]; + await geoJsonTest.run({ + mochaTest: this, + testImageName: "geojson-extruded-polygon-with-height-no-outline", + theme: { lights, styles: { geojson: ourStyle } }, + geoJson: "../dist/resources/basic_polygon.json", + lookAt: { + tilt: 45, + heading: 30 + } + }); + }); + it("renders extruded polygons with height, with lineWidth Expression," + " resulting in 0", async function () { + this.timeout(5000); + const ourStyle = [ + { + when: ["==", ["geometry-type"], "Polygon"], + technique: "extruded-polygon", + attr: { + vertexColors: false, + lineWidth: { + interpolation: "Discrete", + zoomLevels: [9, 12], + values: [0.0, 1.0] + }, + lineColor: "#172023", + lineColorMix: 0.6, + metalness: 0.5, + roughness: 0.5 + } + } + ]; + await geoJsonTest.run({ + mochaTest: this, + testImageName: "geojson-extruded-polygon-linewidth-expression-to-zero", + theme: { lights, styles: { geojson: ourStyle } }, + geoJson: "../dist/resources/basic_polygon.json", + lookAt: { + tilt: 45, + heading: 30, + zoomLevel: 9 + } + }); + }); + it("renders extruded polygons with height, with lineWidth Expression," + " resulting in 1", async function () { + this.timeout(5000); + const ourStyle = [ + { + when: ["==", ["geometry-type"], "Polygon"], + technique: "extruded-polygon", + attr: { + vertexColors: false, + lineWidth: { + interpolation: "Discrete", + zoomLevels: [9, 12], + values: [1.0, 0.0] + }, + lineColor: "#172023", + lineColorMix: 0.6, + metalness: 0.5, + roughness: 0.5 + } + } + ]; + await geoJsonTest.run({ + mochaTest: this, + testImageName: "geojson-extruded-polygon-linewidth-expression-to-one", + theme: { lights, styles: { geojson: ourStyle } }, + geoJson: "../dist/resources/basic_polygon.json", + lookAt: { + tilt: 45, + heading: 30, + zoomLevel: 9 + } + }); + }); + it("renders multi line strings", async function () { + this.timeout(5000); + const ourStyle = [ + { + when: ["==", ["geometry-type"], "LineString"], + technique: "solid-line", + color: "red", + lineWidth: "4px" + } + ]; + await geoJsonTest.run({ + mochaTest: this, + testImageName: "geojson-multilinestring", + theme: { lights, styles: { geojson: ourStyle } }, + geoJson: "../dist/resources/MatchedLinksShape.json", + lookAt: { + target: [-0.13603, 51.508], + zoomLevel: 15 + } + }); + }); + it("renders geometry touching the tile border", async function () { + this.timeout(5000); + const geoJson = { + type: "FeatureCollection", + features: [ + { + type: "Feature", + properties: { + "fill-color": "rgb(255,255,0)", + "stroke-color": "#000" + }, + geometry: { + type: "Polygon", + coordinates: [ + [ + [0, 51.5], + [0.01, 51.5], + [0, 51.49], + [0, 51.5] + ] + ] + } + } + ] + }; + const ourStyle = [ + { + when: ["==", ["geometry-type"], "Polygon"], + technique: "fill", + color: ["get", "fill-color"], + lineColor: ["get", "stroke-color"], + lineWidth: 1 + } + ]; + await geoJsonTest.run({ + mochaTest: this, + testImageName: "geojson-stroke-polygons-at-tile-border", + theme: { lights, styles: { geojson: ourStyle } }, + geoJson, + lookAt: { + target: [0, 51.5], + zoomLevel: 13.4 + } + }); + }); + it("renders geometry crossing the tile border", async function () { + this.timeout(5000); + const geoJson = { + type: "FeatureCollection", + features: [ + { + type: "Feature", + properties: { + "fill-color": "rgb(255,255,0)", + "stroke-color": "#000" + }, + geometry: { + type: "Polygon", + coordinates: [ + [ + [-0.01, 51.5], + [0.01, 51.5], + [0, 51.49], + [-0.01, 51.5] + ] + ] + } + } + ] + }; + const ourStyle = [ + { + when: ["==", ["geometry-type"], "Polygon"], + technique: "fill", + color: ["get", "fill-color"], + lineColor: ["get", "stroke-color"], + lineWidth: 1 + } + ]; + await geoJsonTest.run({ + mochaTest: this, + testImageName: "geojson-stroke-polygons-crossing-tile-border", + theme: { lights, styles: { geojson: ourStyle } }, + geoJson, + lookAt: { + target: [0, 51.5], + zoomLevel: 13.4 + } + }); + }); + it("renders geometries touching and crossing tile bounds", async function () { + this.timeout(5000); + const target = GeoCoordinates.fromGeoPoint([0, 51.5]); + const tileKey = webMercatorTilingScheme.getTileKey(target, 13); + const geoBox = webMercatorTilingScheme.getGeoBox(tileKey); + const offset = 0.002; + const geoJson = { + type: "FeatureCollection", + features: [ + { + type: "Feature", + properties: { + "fill-color": "rgb(255,255,0)", + "stroke-color": "#000", + "sort-rank": 1 + }, + geometry: { + type: "Polygon", + coordinates: [ + [ + [geoBox.center.longitude, geoBox.north + offset], + [geoBox.east + offset, geoBox.center.latitude], + [geoBox.center.longitude, geoBox.south - offset], + [geoBox.west - offset, geoBox.center.latitude], + [geoBox.center.longitude, geoBox.north + offset] + ] + ] + } + }, + { + type: "Feature", + properties: { + "fill-color": "rgba(0, 0, 0, 0.1)", + "stroke-color": "#000", + "sort-rank": 0 + }, + geometry: { + type: "Polygon", + coordinates: [ + [ + [geoBox.west, geoBox.north], + [geoBox.east, geoBox.north], + [geoBox.east, geoBox.south], + [geoBox.west, geoBox.south], + [geoBox.west, geoBox.north] + ] + ] + } + } + ] + }; + const ourStyle = [ + { + when: ["==", ["geometry-type"], "Polygon"], + technique: "fill", + color: ["get", "fill-color"], + lineColor: ["get", "stroke-color"], + lineWidth: 1, + renderOrder: ["get", "sort-rank"] + } + ]; + await geoJsonTest.run({ + mochaTest: this, + testImageName: "geojson-stroke-polygons-touching-and-crossing-tile-border", + theme: { lights, styles: { geojson: ourStyle } }, + geoJson, + lookAt: { + zoomLevel: 13.01, + target: geoBox.center + } + }); + }); + describe("wrap polygon crossing antimeridian", async function () { + const ourStyle = [ + { + when: ["all", ["==", ["geometry-type"], "Polygon"], ["!has", "fragment"]], + technique: "solid-line", + color: "rgba(255,0,0,1)", + lineWidth: "2px" + }, + { + when: [ + "all", + ["==", ["geometry-type"], "Polygon"], + ["==", ["get", "fragment"], "left"] + ], + technique: "fill", + color: "rgba(255,0,0,0.5)", + lineColor: "rgb(0,0,0)", + lineWidth: 1, + renderOrder: 1 + }, + { + when: [ + "all", + ["==", ["geometry-type"], "Polygon"], + ["==", ["get", "fragment"], "middle"] + ], + technique: "fill", + color: "rgba(0,255,0,0.5)", + lineColor: "rgb(0,0,0)", + lineWidth: 1, + renderOrder: 0 + }, + { + when: [ + "all", + ["==", ["geometry-type"], "Polygon"], + ["==", ["get", "fragment"], "right"] + ], + technique: "fill", + color: "rgba(0,0,255,0.5)", + lineColor: "rgb(0,0,0)", + lineWidth: 1, + renderOrder: 1 + }, + { + when: ["==", ["geometry-type"], "LineString"], + technique: "solid-line", + lineWidth: "2px", + color: "rgb(0,0,0)" + } + ]; + // Convert the coordinates of the polygon from GeoJson to GeoCoordinates. + const coordinates = polygon_crossing_antimeridian.features[0].geometry.coordinates[0].map(([lng, lat]) => GeoCoordinates.fromGeoPoint([lng, lat])); + // Split the polygon in 3 parts. + const wrappedPolygon = wrapPolygon(coordinates); + // Extracts a part (left, middle or right) from the wrapped polygon + // and convert it to a GeoJson feature. + const toGeoPolygonFeature = (part) => { + const coordinates = wrappedPolygon[part]; + // Converts an Array of GeoCoordinates to GeoJSON coordinates. + const toGeoJsonCoordinates = (coordinates) => [ + (coordinates ?? []).map(({ lat, lng }) => [lng, lat]) + ]; + return { + type: "Feature", + properties: { + fragment: part + }, + geometry: { + type: "Polygon", + coordinates: toGeoJsonCoordinates(coordinates) + } + }; + }; + const separator = { + type: "Feature", + properties: {}, + geometry: { + type: "LineString", + coordinates: [ + [180, 90], + [180, -90] + ] + } + }; + const lookAt = { + target: [-160, 40], + distance: EarthConstants.EQUATORIAL_CIRCUMFERENCE * 2.5 + }; + it(`polygon to wrap`, async function () { + this.timeout(5000); + await geoJsonTest.run({ + lookAt, + mochaTest: this, + testImageName: `geojson-wrap-polygon-crossing-antimeridian`, + theme: { lights, styles: { geojson: ourStyle } }, + geoJson: polygon_crossing_antimeridian + }); + }); + it(`wrapped polygon - merged`, async function () { + this.timeout(5000); + await geoJsonTest.run({ + lookAt, + mochaTest: this, + testImageName: `geojson-wrap-polygon-crossing-antimeridian-merged`, + theme: { lights, styles: { geojson: ourStyle } }, + geoJson: { + type: "FeatureCollection", + features: [ + toGeoPolygonFeature("left"), + toGeoPolygonFeature("middle"), + toGeoPolygonFeature("right"), + separator + ] + } + }); + }); + for (const part in wrappedPolygon) { + if (!wrappedPolygon.hasOwnProperty(part)) { + continue; + } + const feature = toGeoPolygonFeature(part); + it(`wrapped polygon - ${part}`, async function () { + this.timeout(5000); + await geoJsonTest.run({ + lookAt, + mochaTest: this, + testImageName: `geojson-wrap-polygon-crossing-antimeridian-${part}`, + theme: { lights, styles: { geojson: ourStyle } }, + geoJson: { + type: "FeatureCollection", + features: [feature, separator] + } + }); + }); + } + }); + describe("wrap linestring crossing antimeridian", async function () { + const ourStyle = [ + { + when: ["all", ["==", ["geometry-type"], "Polygon"], ["!has", "fragment"]], + technique: "solid-line", + color: "rgba(255,0,0,1)", + lineWidth: "2px" + }, + { + when: [ + "all", + ["==", ["geometry-type"], "LineString"], + ["==", ["get", "fragment"], "left"] + ], + technique: "solid-line", + color: "rgba(255,0,0,1)", + lineWidth: "2px", + caps: "Round", + renderOrder: 1 + }, + { + when: [ + "all", + ["==", ["geometry-type"], "LineString"], + ["==", ["get", "fragment"], "middle"] + ], + technique: "solid-line", + color: "rgba(0,255,0,1)", + lineWidth: "2px", + renderOrder: 0 + }, + { + when: [ + "all", + ["==", ["geometry-type"], "LineString"], + ["==", ["get", "fragment"], "right"] + ], + technique: "solid-line", + color: "rgba(0,0,255,1)", + lineWidth: "2px", + renderOrder: 1 + }, + { + when: ["all", ["==", ["geometry-type"], "LineString"], ["!has", "fragment"]], + technique: "solid-line", + lineWidth: "2px", + color: "rgb(0,0,0)" + } + ]; + // Convert the coordinates of the polygon from GeoJson to GeoCoordinates. + const coordinates = polygon_crossing_antimeridian.features[0].geometry.coordinates[0].map(([lng, lat]) => GeoCoordinates.fromGeoPoint([lng, lat])); + // Split the polygon in 3 parts. + const wrappedLineString = wrapLineString(coordinates); + // Extracts a part (left, middle or right) from the wrapped polygon + // and convert it to a GeoJson feature. + const toGeoJsonMultiLineString = (part) => { + const coordinates = wrappedLineString[part]; + // Converts an Array of GeoCoordinates to GeoJSON coordinates. + const toGeoJsonCoordinates = (coordinates) => { + coordinates = coordinates ?? []; + return coordinates.map(line => { + return line.map(({ lat, lng }) => [lng, lat]); + }); + }; + return { + type: "Feature", + properties: { + fragment: part + }, + geometry: { + type: "MultiLineString", + coordinates: toGeoJsonCoordinates(coordinates) + } + }; + }; + const separator = { + type: "Feature", + properties: {}, + geometry: { + type: "LineString", + coordinates: [ + [180, 90], + [180, -90] + ] + } + }; + const lookAt = { + target: [-160, 40], + distance: EarthConstants.EQUATORIAL_CIRCUMFERENCE * 2.5 + }; + it(`linestring to wrap`, async function () { + this.timeout(5000); + await geoJsonTest.run({ + lookAt, + mochaTest: this, + testImageName: `geojson-wrap-linestring-crossing-antimeridian`, + theme: { lights, styles: { geojson: ourStyle } }, + geoJson: polygon_crossing_antimeridian + }); + }); + it(`wrapped linestring - merged`, async function () { + this.timeout(5000); + await geoJsonTest.run({ + lookAt, + mochaTest: this, + testImageName: `geojson-wrap-linestring-crossing-antimeridian-merged`, + theme: { lights, styles: { geojson: ourStyle } }, + geoJson: { + type: "FeatureCollection", + features: [ + toGeoJsonMultiLineString("left"), + toGeoJsonMultiLineString("middle"), + toGeoJsonMultiLineString("right"), + separator + ] + } + }); + }); + for (const part in wrappedLineString) { + if (!wrappedLineString.hasOwnProperty(part)) { + continue; + } + const feature = toGeoJsonMultiLineString(part); + it(`wrapped linestring - ${part}`, async function () { + this.timeout(5000); + await geoJsonTest.run({ + lookAt, + mochaTest: this, + testImageName: `geojson-wrap-linestring-crossing-antimeridian-${part}`, + theme: { lights, styles: { geojson: ourStyle } }, + geoJson: { + type: "FeatureCollection", + features: [feature, separator] + } + }); + }); + } + }); + describe("clip lines against bounds", async function () { + it("clip long line string", async function () { + const bounds = [ + [-2.8125, -15.28418511407642], + [76.9921875, -15.28418511407642], + [76.9921875, 54.77534585936447], + [-2.8125, 54.77534585936447], + [-2.8125, -15.28418511407642] + ]; + const geoBox = new GeoBox(GeoCoordinates.fromGeoPoint(bounds[0]), GeoCoordinates.fromGeoPoint(bounds[0])); + bounds.forEach(geoPoint => { + geoBox.growToContain(GeoCoordinates.fromGeoPoint(geoPoint)); + }); + const { west, south, east, north } = geoBox; + const { min, max } = webMercatorTilingScheme.projection.projectBox(geoBox); + const inputLineString = [ + [-28.125, 33.43144133557529], + [-24.609375, 49.38237278700955], + [-9.140625, 39.36827914916014], + [21.4453125, 44.08758502824516], + [25.6640625, 61.60639637138628], + [45, 45.82879925192134], + [56.6015625, 62.2679226294176], + [61.87499999999999, 43.32517767999296], + [88.24218749999999, 39.639537564366684], + [84.72656249999999, 14.26438308756265], + [53.78906249999999, 13.923403897723347], + [33.3984375, 27.994401411046148], + [52.734375, 35.17380831799959], + [58.71093750000001, 28.613459424004414], + [61.52343749999999, 35.460669951495305], + [38.67187499999999, 39.639537564366684], + [16.875, 32.24997445586331], + [12.65625, 14.604847155053898], + [-14.0625, -4.214943141390639], + [41.1328125, -26.11598592533351] + ]; + const points = inputLineString.map(geoPos => { + const { x, y } = webMercatorProjection.projectPoint(GeoCoordinates.fromGeoPoint(geoPos)); + return new Vector3(x, y, 0); + }); + const lineStrings = clipLineString(points, min.x, min.y, max.x, max.y); + const coordinates = lineStrings.map(lineString => lineString.map(({ x, y }) => webMercatorProjection.unprojectPoint(new Vector3(x, y, 0)).toGeoPoint())); + const geoJson = { + type: "FeatureCollection", + features: [ + { + type: "Feature", + properties: {}, + geometry: { + type: "Polygon", + coordinates: [ + [ + [west, south], + [east, south], + [east, north], + [west, north], + [west, south] + ] + ] + } + }, + { + type: "Feature", + properties: { + color: "#00ff00" + }, + geometry: { + type: "LineString", + coordinates: inputLineString + } + }, + { + type: "Feature", + properties: { + color: "#ff0000" + }, + geometry: { + type: "MultiLineString", + coordinates + } + } + ] + }; + const ourStyle = [ + { + when: ["==", ["geometry-type"], "Polygon"], + technique: "fill", + color: "rgba(100,100,100,0.5)", + lineWidth: 2 + }, + { + when: ["==", ["geometry-type"], "LineString"], + technique: "solid-line", + metricUnit: "Pixel", + color: ["get", "color"], + lineWidth: 2 + } + ]; + await geoJsonTest.run({ + mochaTest: this, + testImageName: "geojson-clip-line-against-tile-border", + theme: { lights, styles: { geojson: ourStyle } }, + geoJson: geoJson, + lookAt: { + zoomLevel: 2, + target: geoBox.center + } + }); + }); + }); + describe("Tiled GeoJson", async function () { + it("Circle with an hole with same winding as the outer ring", async function () { + const dataProvider = new (class extends DataProvider { + ready() { + return true; + } + async getTile(tileKey) { + const geoBox = webMercatorTilingScheme.getGeoBox(tileKey); + const { longitudeSpan, center } = geoBox; + const [cx, cy] = center.toGeoPoint(); + const circle = turf.circle([cx, cy], longitudeSpan * 0.1, { + units: "degrees" + }); + const innerCircle = turf.circle([cx, cy], longitudeSpan * 0.04, { + units: "degrees" + }); + const ring = turf.polygon([ + ...circle.geometry.coordinates, + ...innerCircle.geometry.coordinates + ]); + return turf.featureCollection([ring]); + } + async connect() { } + dispose() { } + })(); + await geoJsonTest.run({ + dataProvider, + mochaTest: this, + testImageName: "geojson-polygon-holes-windings", + theme: { + styles: [ + { + styleSet: "geojson", + when: ["==", ["geometry-type"], "Polygon"], + technique: "fill", + color: "red", + lineColor: "#000", + lineWidth: 1 + } + ] + }, + lookAt: { + zoomLevel: 14.0, + target: webMercatorTilingScheme.getGeoBox(webMercatorTilingScheme.getTileKey(GeoCoordinates.fromGeoPoint([-90, -42.52556]), 13)).center + } + }); + }); + }); + describe("Polygons", async function () { + it("Triangle with a hole rendered at zoom level 2", async function () { + const theme = { + lights, + styles: [ + { + styleSet: "geojson", + when: ["==", ["geometry-type"], "Polygon"], + technique: "fill", + color: "rgba(100,100,100,0.5)", + lineWidth: 2 + } + ] + }; + const polygon = turf.polygon([ + [ + [0, 0, 0], + [170, 35, 0], + [170, -35, 0], + [0, 0, 0] + ], + [ + [0, 0, 0], + [170, 15, 0], + [170, -15, 0], + [0, 0, 0] + ] + ]); + const [lng, lat] = turf.center(polygon).geometry.coordinates; + const geoJsonStore = new GeoJsonStore(); + geoJsonStore.features.insert(polygon); + await geoJsonTest.run({ + mochaTest: this, + testImageName: "geojson-triangle-with-hole-at-zoom-level-2", + dataProvider: geoJsonStore, + theme, + lookAt: { + zoomLevel: 2, + target: [lng, lat] + } + }); + }); + }); +}); +//# sourceMappingURL=GeoJsonDataRendering.js.map \ No newline at end of file diff --git a/test/rendering/GeoJsonDataRendering.js.map b/test/rendering/GeoJsonDataRendering.js.map new file mode 100644 index 0000000..874037d --- /dev/null +++ b/test/rendering/GeoJsonDataRendering.js.map @@ -0,0 +1 @@ +{"version":3,"file":"GeoJsonDataRendering.js","sourceRoot":"","sources":["GeoJsonDataRendering.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/test/rendering/GeoJsonFeaturesRendering.d.ts b/test/rendering/GeoJsonFeaturesRendering.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/test/rendering/GeoJsonFeaturesRendering.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/test/rendering/GeoJsonFeaturesRendering.js b/test/rendering/GeoJsonFeaturesRendering.js new file mode 100644 index 0000000..c5c0f06 --- /dev/null +++ b/test/rendering/GeoJsonFeaturesRendering.js @@ -0,0 +1,290 @@ +/* Copyright (C) 2025 flywave.gl contributors */ +import * as turf from "@turf/turf"; +import { assert } from "chai"; +import * as THREE from "three"; +import { GeoJsonStore } from "./utils/GeoJsonStore"; +import { GeoJsonTest } from "./utils/GeoJsonTest"; +import { ThemeBuilder } from "./utils/ThemeBuilder"; +const strokePolygonLayer = (params = {}) => { + return { + id: "stroke-polygon", + styleSet: "geojson", + technique: "solid-line", + color: "orange", + lineWidth: ["world-ppi-scale", 20], + secondaryColor: "magenta", + secondaryWidth: ["world-ppi-scale", 35], + ...params + }; +}; +describe("GeoJson features", function () { + const geoJsonTest = new GeoJsonTest(); + const lights = ThemeBuilder.lights; + afterEach(() => { + geoJsonTest.dispose(); + }); + it("Stroke Circle", async function () { + const pos = [13.2, 53.2]; + const circle = turf.circle(pos, 600, { + units: "meters" + }); + const geoJson = turf.featureCollection([circle]); + await geoJsonTest.run({ + testImageName: "geojson-stroke-circle", + mochaTest: this, + geoJson: geoJson, + lookAt: { + target: pos, + zoomLevel: 14 + }, + theme: { + lights, + styles: [ + strokePolygonLayer({ + lineWidth: ["world-ppi-scale", 50], + secondaryWidth: ["world-ppi-scale", 80] + }) + ] + } + }); + }); + it("Extrude Circle", async function () { + const pos = [13.2, 53.2]; + const circle = turf.circle(pos, 600, { + units: "meters" + }); + const geoJson = turf.featureCollection([circle]); + await geoJsonTest.run({ + testImageName: "geojson-extruded-circle", + mochaTest: this, + geoJson: geoJson, + lookAt: { + target: pos, + zoomLevel: 14, + tilt: 60 + }, + theme: { + lights, + styles: [ + { + styleSet: "geojson", + technique: "extruded-polygon", + color: "red", + constantHeight: true, + height: 700 + } + ] + } + }); + }); + describe("Stroke Arcs", async function () { + const lineCaps = ["None", "Round", "Square", "TriangleIn", "TriangleOut"]; + lineCaps.forEach(caps => { + it(`Stroke Arc using from 0 to 300 using '${caps}' caps`, async function () { + const center = [13.2, 53.2]; + const arc = turf.lineArc(center, 5, 0, 300); + const geoJson = turf.featureCollection([arc]); + await geoJsonTest.run({ + mochaTest: this, + geoJson: geoJson, + testImageName: `stroke-arc-with-caps-${caps}`, + lookAt: { + target: center, + zoomLevel: 10 + }, + theme: { + lights, + styles: [strokePolygonLayer({ caps })] + } + }); + }); + }); + }); + describe("Stroke Circles created using lineArc", async function () { + const lineCaps = ["None", "Round", "Square", "TriangleIn", "TriangleOut"]; + lineCaps.forEach(caps => { + it(`Stroke Arc from 0 to 360 using '${caps}' caps`, async function () { + const center = [13.2, 53.2]; + const arc = turf.lineArc(center, 5, 0, 360); + const geoJson = turf.featureCollection([arc]); + await geoJsonTest.run({ + mochaTest: this, + geoJson: geoJson, + testImageName: `stroke-circles-creates-from-arcs-${caps}`, + lookAt: { + target: center, + zoomLevel: 10 + }, + theme: { + lights, + styles: [strokePolygonLayer({ caps })] + } + }); + }); + }); + }); + describe("extruded-polygon technique", async function () { + // Helper function to test the winding of the rings. + const isClockWise = (ring) => THREE.ShapeUtils.isClockWise(ring.map(p => new THREE.Vector2().fromArray(p))); + // the theme used by this test suite. + const theme = { + lights, + styles: [ + { + styleSet: "geojson", + technique: "extruded-polygon", + color: "#0335a2", + lineWidth: 1, + lineColor: "red", + constantHeight: true, + height: 300 + } + ] + }; + // the polygon feature + const polygon = turf.polygon([ + [ + [0.00671649362467299, 51.49353450058163, 0], + [0.006598810705050831, 51.48737650885326, 0], + [0.015169103358567556, 51.48731300784492, 0], + [0.015286786278189713, 51.49347100814989, 0], + [0.006716493624672999, 51.49353450058163, 0], + [0.00671649362467299, 51.49353450058163, 0] + ] + ]); + // the center geo location of the polygon + const [longitude, latitude] = turf.center(polygon).geometry.coordinates; + // the default camera setup used by this test suite. + const lookAt = { + target: [longitude, latitude], + zoomLevel: 15, + tilt: 40, + heading: 45 + }; + it("Clockwise polygon touching tile border", async function () { + const dataProvider = new GeoJsonStore(); + dataProvider.features.insert(polygon); + await geoJsonTest.run({ + mochaTest: this, + dataProvider, + testImageName: `geojson-extruded-polygon-touching-tile-bounds`, + lookAt, + theme + }); + }); + it("Clockwise polygon with a hole", async function () { + const dataProvider = new GeoJsonStore(); + const innerPolygon = turf.transformScale(polygon, 0.8, { origin: "center" }); + const polygonWithHole = turf.difference(polygon, innerPolygon); + dataProvider.features.insert(polygonWithHole); + assert.strictEqual(polygonWithHole?.geometry?.coordinates.length, 2); + const outerRing = polygonWithHole.geometry.coordinates[0]; + const innerRing = polygonWithHole.geometry.coordinates[1]; + // test that the winding of the inner ring is opposite to the winding + // of the outer ring. + assert.notStrictEqual(isClockWise(outerRing), isClockWise(innerRing)); + await geoJsonTest.run({ + mochaTest: this, + dataProvider, + testImageName: `geojson-extruded-polygon-cw-with-hole`, + lookAt, + theme + }); + }); + it("Clockwise polygon with a hole wrong winding", async function () { + const dataProvider = new GeoJsonStore(); + const innerPolygon = turf.transformScale(polygon, 0.8, { origin: "center" }); + const polygonWithHole = turf.difference(polygon, innerPolygon); + dataProvider.features.insert(polygonWithHole); + assert.strictEqual(polygonWithHole?.geometry?.coordinates.length, 2); + const outerRing = polygonWithHole.geometry.coordinates[0]; + const innerRing = polygonWithHole.geometry.coordinates[1]; + // reverse the winding of the inner ring so to have the same winding + // of the outer ring + innerRing.reverse(); + // verify that the winding of the rings is the same + assert.strictEqual(isClockWise(outerRing), isClockWise(innerRing)); + await geoJsonTest.run({ + mochaTest: this, + dataProvider, + testImageName: `geojson-extruded-polygon-cw-wrong-winding-of-inner-ring`, + lookAt, + theme + }); + }); + it("Clockwise multi-polygon with a hole wrong winding", async function () { + const dataProvider = new GeoJsonStore(); + const scaledPolygon = turf.transformScale(polygon, 0.5, { origin: "center" }); + const innerPolygon = turf.transformScale(scaledPolygon, 0.8, { origin: "center" }); + const polygonWithHole = turf.difference(scaledPolygon, innerPolygon); + const otherPolygonWithHole = turf.clone(polygonWithHole); + turf.transformTranslate(polygonWithHole, 200, 90, { units: "meters", mutate: true }); + turf.transformTranslate(otherPolygonWithHole, 200, -90, { + units: "meters", + mutate: true + }); + const multi = turf.union(polygonWithHole, otherPolygonWithHole); + assert.strictEqual(multi.geometry?.type, "MultiPolygon"); + dataProvider.features.insert(multi); + await geoJsonTest.run({ + mochaTest: this, + dataProvider, + testImageName: `geojson-extruded-multi-polygon-cw-wrong-winding-of-inner-ring`, + lookAt, + theme + }); + }); + }); + describe("fill technqiue", async function () { + it("Stroked enclosed polygons", async function () { + const box1 = turf.bboxPolygon([-0.1, -0.1, 0.1, 0.1]); + box1.properties = { + "fill-color": "rgba(0, 255, 0, 0.5)", + "render-order": 2, + "stroke-color": "rgba(0, 255, 255, 0.5)", + "stroke-size": 40, + "stroke-render-order": 2.5 + }; + const box2 = turf.bboxPolygon([-0.4, -0.4, 0.4, 0.4]); + box2.properties = { + "fill-color": "rgba(255, 0, 0, 0.5)", + "render-order": 1, + "stroke-color": "rgba(0, 0, 255, 0.5)", + "stroke-size": 40, + "stroke-render-order": 1.5 + }; + const dataProvider = new GeoJsonStore(); + dataProvider.features.insert(box1); + dataProvider.features.insert(box2); + await geoJsonTest.run({ + mochaTest: this, + dataProvider, + testImageName: `geojson-stroked-enclosed-polygons`, + lookAt: { + target: [0, 0], + zoomLevel: 8.8 + }, + theme: { + lights: ThemeBuilder.lights, + styles: [ + { + styleSet: "geojson", + technique: "fill", + color: ["get", "fill-color"], + renderOrder: ["get", "render-order"] + }, + { + styleSet: "geojson", + technique: "solid-line", + color: ["get", "stroke-color"], + lineWidth: ["world-ppi-scale", ["get", "stroke-size"]], + renderOrder: ["get", "stroke-render-order"], + clipping: true + } + ] + } + }); + }); + }); +}); +//# sourceMappingURL=GeoJsonFeaturesRendering.js.map \ No newline at end of file diff --git a/test/rendering/GeoJsonFeaturesRendering.js.map b/test/rendering/GeoJsonFeaturesRendering.js.map new file mode 100644 index 0000000..237a2a6 --- /dev/null +++ b/test/rendering/GeoJsonFeaturesRendering.js.map @@ -0,0 +1 @@ +{"version":3,"file":"GeoJsonFeaturesRendering.js","sourceRoot":"","sources":["GeoJsonFeaturesRendering.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/test/rendering/ScreenSpaceRenderingTests.d.ts b/test/rendering/ScreenSpaceRenderingTests.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/test/rendering/ScreenSpaceRenderingTests.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/test/rendering/ScreenSpaceRenderingTests.js b/test/rendering/ScreenSpaceRenderingTests.js new file mode 100644 index 0000000..81281e3 --- /dev/null +++ b/test/rendering/ScreenSpaceRenderingTests.js @@ -0,0 +1,373 @@ +/* Copyright (C) 2025 flywave.gl contributors */ +import { EarthConstants, sphereProjection } from "@flywave/flywave-geoutils"; +import { GeoJsonTest } from "./utils/GeoJsonTest"; +import { ThemeBuilder } from "./utils/ThemeBuilder"; +// Mocha discourages using arrow functions, see https://mochajs.org/#arrow-functions +describe("ScreenSpaceRendering Test", function () { + const geoJsonTest = new GeoJsonTest(); + it("renders icon without text, if fontcatalog not found ", async function () { + this.timeout(5000); + await geoJsonTest.run({ + mochaTest: this, + testImageName: "geojson-no-text-icon", + theme: new ThemeBuilder().withInvalidFontCatalog().withMarkerStyle().build(), + geoJson: { + type: "FeatureCollection", + features: [ + { + type: "Feature", + properties: { text: "Marker" }, + geometry: { type: "Point", coordinates: [14.6, 53.3] } + } + ] + }, + lookAt: { tilt: 80, zoomLevel: 19 }, + tileGeoJson: false + }); + }); + it("renders interleaved icon and text", async function () { + this.timeout(5000); + await geoJsonTest.run({ + mochaTest: this, + testImageName: "geojson-interleaved-icon-text", + theme: new ThemeBuilder().withFontCatalog().withMarkerStyle().build(), + geoJson: { + type: "FeatureCollection", + features: [ + { + type: "Feature", + properties: { + text: "Marker0", + color: "red", + imageTexture: "red-icon", + renderOrder: 0 + }, + geometry: { type: "Point", coordinates: [14.6, 53.3] } + }, + { + type: "Feature", + properties: { + text: "Marker1", + color: "green", + imageTexture: "green-icon", + renderOrder: 1 + }, + geometry: { type: "Point", coordinates: [14.6, 53.32] } + }, + { + type: "Feature", + properties: { + text: "Marker2", + color: "blue", + imageTexture: "white-icon", + renderOrder: 2 + }, + geometry: { type: "Point", coordinates: [14.6, 53.28] } + } + ] + }, + lookAt: { tilt: 0, zoomLevel: 10 }, + tileGeoJson: false + }); + }); + it("renders icons and text using double precision coordinates", async function () { + this.timeout(5000); + // FLYWAVE-14465: Text and cross icon must be centered on green square. + const squaresStyle = { + when: "$geometryType == 'point'", + technique: "squares", + color: "green", + size: 17, + styleSet: "geojson" + }; + await geoJsonTest.run({ + mochaTest: this, + testImageName: "geojson-precision-icon-text", + theme: new ThemeBuilder() + .withFontCatalog() + .withMarkerStyle() + .withStyle(squaresStyle) + .build(), + geoJson: { + type: "FeatureCollection", + features: [ + { + type: "Feature", + properties: { + text: "o", + color: "white", + imageTexture: "plus-icon", + iconYOffset: 0, + size: 18 + }, + geometry: { + type: "MultiPoint", + coordinates: [ + [14.60015, 53.30007], + [14.60015, 53.29993], + [14.59985, 53.29993], + [14.59985, 53.30007] + ] + } + } + ] + }, + lookAt: { tilt: 0, zoomLevel: 20 }, + tileGeoJson: false + }); + }); + it("renders point using marker technique, with theme set to datasource", async function () { + this.timeout(5000); + await geoJsonTest.run({ + mochaTest: this, + testImageName: "geojson-point-after-theme-reset-in-datasource", + theme: new ThemeBuilder().build(), + geoJson: { + type: "FeatureCollection", + features: [ + { + type: "Feature", + properties: { text: "Marker", renderOrder: 1 }, + geometry: { type: "Point", coordinates: [14.6, 53.3] } + } + ] + }, + lookAt: { tilt: 0, zoomLevel: 10 }, + tileGeoJson: false, + beforeFinishCallback: async (mapView) => { + await mapView + .getDataSourceByName("geojson") + ?.setTheme({ styles: [ThemeBuilder.markerStyle] }); + } + }); + }); + it("renders point using marker technique, after fontcatalog added in set theme", async function () { + this.timeout(5000); + await geoJsonTest.run({ + mochaTest: this, + testImageName: "geojson-point-after-font-added", + theme: new ThemeBuilder().withMarkerStyle().build(), + geoJson: { + type: "FeatureCollection", + features: [ + { + type: "Feature", + properties: { text: "Marker", renderOrder: 1 }, + geometry: { type: "Point", coordinates: [14.6, 53.3] } + } + ] + }, + lookAt: { tilt: 0, zoomLevel: 10 }, + tileGeoJson: false, + beforeFinishCallback: async (mapView) => { + await mapView + .getDataSourceByName("geojson") + ?.setTheme(new ThemeBuilder(true).withMarkerStyle().build()); + await mapView.setTheme(new ThemeBuilder().withFontCatalog().build()); + } + }); + }); + it("renders point using marker technique, after theme reset", async function () { + this.timeout(5000); + await geoJsonTest.run({ + mochaTest: this, + testImageName: "geojson-point-after-theme-reset", + theme: new ThemeBuilder().withMarkerStyle().withFontCatalog().build(), + geoJson: { + type: "FeatureCollection", + features: [ + { + type: "Feature", + properties: { text: "Marker", renderOrder: 1, color: "blue" }, + geometry: { type: "Point", coordinates: [14.6, 53.3] } + } + ] + }, + lookAt: { tilt: 0, zoomLevel: 10 }, + tileGeoJson: false, + beforeFinishCallback: async (mapView) => { + await mapView.setTheme(new ThemeBuilder().withMarkerStyle().withFontCatalog().build()); + } + }); + }); + it("removes marker, after datasource is removed", async function () { + this.timeout(5000); + await geoJsonTest.run({ + mochaTest: this, + testImageName: "geojson-after-datasource-removed", + theme: new ThemeBuilder().withMarkerStyle().withFontCatalog().build(), + geoJson: { + type: "FeatureCollection", + features: [ + { + type: "Feature", + properties: { text: "Marker", renderOrder: 1, color: "blue" }, + geometry: { type: "Point", coordinates: [14.6, 53.3] } + } + ] + }, + lookAt: { tilt: 0, zoomLevel: 10 }, + tileGeoJson: false, + beforeFinishCallback: async (mapView) => { + await mapView.removeDataSource(mapView.getDataSourceByName("geojson")); + } + }); + }); + it("renders elevated point using marker technique", async function () { + this.timeout(5000); + await geoJsonTest.run({ + mochaTest: this, + testImageName: "geojson-elevated-point", + theme: new ThemeBuilder().withMarkerStyle().withFontCatalog().build(), + geoJson: { + type: "FeatureCollection", + features: [ + { + type: "Feature", + properties: { text: "Marker" }, + geometry: { type: "Point", coordinates: [14.6, 53.3, 15] } + } + ] + }, + lookAt: { tilt: 80, zoomLevel: 19 }, + tileGeoJson: false + }); + }); + it("renders a point which is elevated above MAX_BUILDING_HEIGHT", async function () { + this.timeout(5000); + await geoJsonTest.run({ + mochaTest: this, + testImageName: "geojson-elevated-point-above-max-building-height", + theme: new ThemeBuilder().withMarkerStyle().withFontCatalog().build(), + geoJson: { + type: "FeatureCollection", + features: [ + { + type: "Feature", + properties: { text: "Marker" }, + geometry: { + type: "Point", + coordinates: [14.6, 53.3, 2 * EarthConstants.MAX_BUILDING_HEIGHT] + } + } + ] + }, + lookAt: { zoomLevel: 14 }, + tileGeoJson: false + }); + }); + it("renders point markers using dataSourceOrder", async function () { + this.timeout(5000); + const markerStyle = { + when: ["==", ["geometry-type"], "Point"], + technique: "labeled-icon", + imageTexture: ["get", "icon"], + text: ["get", "text"], + size: 15, + iconMayOverlap: true, + iconReserveSpace: false, + textMayOverlap: true, + textReserveSpace: false, + color: "black", + renderOrder: ["get", "renderOrder"], + styleSet: "geojson" + }; + await geoJsonTest.run({ + mochaTest: this, + testImageName: "markers-with-different-dataSourceOrders", + theme: new ThemeBuilder().withStyle(markerStyle).withFontCatalog().build(), + geoJson: { + type: "FeatureCollection", + features: [ + { + type: "Feature", + properties: { text: "1", icon: "red-icon", renderOrder: 1 }, + geometry: { type: "Point", coordinates: [14.6, 53.3] } + }, + { + type: "Feature", + properties: { text: "2", icon: "red-icon", renderOrder: 2 }, + geometry: { type: "Point", coordinates: [14.65, 53.33] } + } + ] + }, + extraDataSource: { + geoJson: { + type: "FeatureCollection", + features: [ + { + type: "Feature", + properties: { text: "3", icon: "green-icon", renderOrder: 0 }, + geometry: { type: "Point", coordinates: [14.68, 53.26] } + } + ] + }, + dataSourceOrder: 1 + } + }); + }); + it("renders marker correctly on zoomed out globe", async function () { + const geoJson = { + type: "FeatureCollection", + features: [ + { + type: "Feature", + properties: { color: "#436981" }, + geometry: { + type: "Polygon", + coordinates: [ + [ + [-180, -90], + [180, -90], + [180, 90], + [-180, 90], + [-180, -90] + ] + ] + } + }, + { + type: "Feature", + geometry: { type: "Point", coordinates: [0, 0] } + } + ] + }; + await geoJsonTest.run({ + mochaTest: this, + testImageName: "marker-on-zoomed-out-globe", + theme: new ThemeBuilder().withMarkerStyle().withFontCatalog().build(), + geoJson, + lookAt: { + zoomLevel: 3, + target: [0, 0] + }, + projection: sphereProjection + }); + }); + it("renders icon after image is loaded", async function () { + this.timeout(5000); + await geoJsonTest.run({ + mochaTest: this, + testImageName: "geojson-icon-after-image-loaded", + theme: new ThemeBuilder().withFontCatalog().withMarkerStyle().build(), + geoJson: { + type: "FeatureCollection", + features: [ + { + type: "Feature", + properties: { + text: "Marker0", + color: "red", + imageTexture: "icon-to-load", + renderOrder: 0 + }, + geometry: { type: "Point", coordinates: [14.6, 53.3] } + } + ] + }, + lookAt: { tilt: 0, zoomLevel: 10 }, + tileGeoJson: false + }); + }); +}); +//# sourceMappingURL=ScreenSpaceRenderingTests.js.map \ No newline at end of file diff --git a/test/rendering/ScreenSpaceRenderingTests.js.map b/test/rendering/ScreenSpaceRenderingTests.js.map new file mode 100644 index 0000000..da675f4 --- /dev/null +++ b/test/rendering/ScreenSpaceRenderingTests.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ScreenSpaceRenderingTests.js","sourceRoot":"","sources":["ScreenSpaceRenderingTests.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/test/rendering/StylingTest.d.ts b/test/rendering/StylingTest.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/test/rendering/StylingTest.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/test/rendering/StylingTest.js b/test/rendering/StylingTest.js new file mode 100644 index 0000000..320bd18 --- /dev/null +++ b/test/rendering/StylingTest.js @@ -0,0 +1,948 @@ +/* Copyright (C) 2025 flywave.gl contributors */ +// Mocha discourages using arrow functions, see https://mochajs.org/#arrow-functions +import { TextureCoordinateType } from "@flywave/flywave-datasource-protocol"; +import { FeaturesDataSource } from "@flywave/flywave-features-datasource"; +import { OrientedBox3, ProjectionType } from "@flywave/flywave-geoutils"; +import { MapView, MapViewEventNames } from "@flywave/flywave-mapview"; +import { GeoJsonTiler } from "@flywave/flywave-mapview-decoder/index-worker"; +import { getPlatform, RenderingTestHelper, waitForEvent } from "@flywave/flywave-test-utils"; +import { getReferenceImageUrl } from "@flywave/flywave-test-utils/lib/rendering/ReferenceImageLocator"; +import { getOptionValue } from "@flywave/flywave-utils"; +import { VectorTileDecoder } from "@flywave/flywave-vectortile-datasource/index-worker"; +import { assert } from "chai"; +import * as THREE from "three"; +function baseRenderingTest(name, options, testFun, cleanupFun) { + const commonTestOptions = { module: "flywave.gl" }; + const imageUrl = getReferenceImageUrl({ ...commonTestOptions, platform: getPlatform(), name }); + RenderingTestHelper.cachedLoadImageData(imageUrl).catch(_ => { + // We can ignore error here, as _if_ this file was really needed, then test + // will try to resolve this promise and report failure in test context. + }); + it(name, async function () { + this.timeout(100000); + let canvas; + // TODO: remove `module` name from RenderingTestHalper API + try { + const ibct = new RenderingTestHelper(this, commonTestOptions); + canvas = document.createElement("canvas"); + canvas.width = options.width ?? options.height ?? 100; + canvas.height = options.height ?? options.width ?? 100; + await testFun(canvas); + await ibct.assertCanvasMatchesReference(canvas, name, options); + } + finally { + if (cleanupFun !== undefined) { + cleanupFun(); + } + if (canvas !== undefined) { + canvas.width = 0; + canvas.height = 0; + canvas = undefined; + } + } + }); +} +function mapViewFitGeoBox(mapView, geoBox, margin = 0.1) { + if (mapView.projection.type !== ProjectionType.Planar) { + throw new Error("mapViewFitGeoBox doesn't support non-planar projections"); + } + const boundingBox = new THREE.Box3(); + const tmpVec3 = new THREE.Vector3(); + mapView.projection.projectBox(geoBox, boundingBox); + const size = boundingBox.getSize(tmpVec3); + const viewSize = Math.max(size.x, size.y); + const fov = mapView.camera.fov; + const height = (viewSize / 2) * (1 / Math.tan(THREE.MathUtils.degToRad(fov / 2))); + const distance = height * (1 + margin); + boundingBox.getCenter(tmpVec3); + const target = mapView.projection.unprojectPoint(tmpVec3); + return { + target, + distance, + tilt: 0, + heading: 0 + }; +} +function mapViewFeaturesRenderingTest(name, options, testFun) { + let mapView; + baseRenderingTest(name, options, async function (canvas) { + //document.body.appendChild(canvas); + mapView = new MapView({ + canvas, + theme: options.theme ?? {}, + preserveDrawingBuffer: true, + disableFading: true, + pixelRatio: 1 + }); + mapView.animatedExtrusionHandler.enabled = false; + const dataSource = new FeaturesDataSource({ + styleSetName: "geojson", + geojson: options.geoJson, + decoder: new VectorTileDecoder(), + tiler: new GeoJsonTiler(), + gatherFeatureAttributes: true + }); + options.dynamicProperties?.forEach(dynamicProperty => { + mapView?.setDynamicProperty(dynamicProperty.name, dynamicProperty.value); + }); + mapView.addDataSource(dataSource); + const geoBox = dataSource.getGeoBox(); + assert.isDefined(geoBox); + const defaultLookAt = mapViewFitGeoBox(mapView, geoBox, getOptionValue(options.margin, 0.15)); + const lookAt = { ...defaultLookAt, ...options.lookAt }; + mapView.lookAt(lookAt); + if (options.grid !== undefined) { + const gridDivisions = 4; + const gridSize = options.grid * gridDivisions; + const position = geoBox.center; + const box = new OrientedBox3(); + mapView.projection.localTangentSpace(position, box); + const grid = new THREE.GridHelper(gridSize, gridDivisions, 0xff0000, 0x00ff00); + grid.geometry.rotateX(Math.PI / 2); + grid.anchor = position; + grid.setRotationFromMatrix(box.getRotationMatrix()); + mapView.mapAnchors.add(grid); + } + mapView.update(); + if (testFun !== undefined) { + await testFun(mapView, dataSource); + } + else { + await waitForEvent(mapView, MapViewEventNames.FrameComplete); + } + // Don't dispose mapView yet b/c the context will be lost since three.js 0.115 + // when WebGLRenderer.dispose is called (i.e. actual IBCT image will be empty). + }, () => { + if (mapView !== undefined) { + mapView.dispose(); + mapView = undefined; + } + }); +} +describe("MapView Styling Test", function () { + const referenceBackground = { + // background polygon, taking about half of view + type: "Feature", + geometry: { + type: "Polygon", + coordinates: [ + [ + [0.004, 0.004], + [-0.0, 0.004], + [-0.0, -0.004], + [0.004, -0.004], + [0.004, 0.004] + ] + ] + }, + properties: { + kind: "background" + } + }; + const referenceBackroundStyle = { + when: "$geometryType == 'polygon' && kind == 'background'", + technique: "fill", + final: true, + attr: { + color: "#22f" + } + }; + const themeTextSettings = { + fontCatalogs: [ + { + name: "fira", + url: "../dist/resources/fonts/Default_FontCatalog.json" + } + ], + images: { + "my-marker-icon": { + url: "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDIyLjEuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHdpZHRoPSI0OHB4IiBoZWlnaHQ9IjQ4cHgiIHZlcnNpb249IjEuMSIgaWQ9Imx1aS1pY29uLWRlc3RpbmF0aW9ucGluLW9uZGFyay1zb2xpZC1sYXJnZSIKCSB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4PSIwcHgiIHk9IjBweCIgdmlld0JveD0iMCAwIDQ4IDQ4IgoJIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDQ4IDQ4IiB4bWw6c3BhY2U9InByZXNlcnZlIj4KPGc+Cgk8ZyBpZD0ibHVpLWljb24tZGVzdGluYXRpb25waW4tb25kYXJrLXNvbGlkLWxhcmdlLWJvdW5kaW5nLWJveCIgb3BhY2l0eT0iMCI+CgkJPHBhdGggZmlsbD0iI2ZmZmZmZiIgZD0iTTQ3LDF2NDZIMVYxSDQ3IE00OCwwSDB2NDhoNDhWMEw0OCwweiIvPgoJPC9nPgoJPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGZpbGw9IiNmZmZmZmYiIGQ9Ik0yNCwyQzEzLjg3MDgsMiw1LjY2NjcsMTAuMTU4NCw1LjY2NjcsMjAuMjIzMwoJCWMwLDUuMDMyNSwyLjA1MzMsOS41ODg0LDUuMzcxNywxMi44ODgzTDI0LDQ2bDEyLjk2MTctMTIuODg4M2MzLjMxODMtMy4zLDUuMzcxNy03Ljg1NTgsNS4zNzE3LTEyLjg4ODMKCQlDNDIuMzMzMywxMC4xNTg0LDM0LjEyOTIsMiwyNCwyeiBNMjQsMjVjLTIuNzY1LDAtNS0yLjIzNS01LTVzMi4yMzUtNSw1LTVzNSwyLjIzNSw1LDVTMjYuNzY1LDI1LDI0LDI1eiIvPgo8L2c+Cjwvc3ZnPgo=", + preload: true + } + }, + imageTextures: [ + { + name: "my-marker-icon", + image: "my-marker-icon" + } + ] + }; + describe("point features", function () { + const points = [ + { + type: "Feature", + geometry: { + type: "Point", + coordinates: [0.003, 0.001] + }, + properties: { + name: "aBcD" + } + }, + { + type: "Feature", + geometry: { + type: "Point", + coordinates: [-0.003, 0.001] + }, + properties: { + name: "aBcD" + } + }, + { + type: "Feature", + geometry: { + type: "Point", + coordinates: [0.0, -0.002] + }, + properties: { + name: "aBcD" + } + } + ]; + function makePointTextTestCases(testCases, options) { + for (const testCase in testCases) { + const attr = testCases[testCase]; + mapViewFeaturesRenderingTest(`solid-line-styling-${testCase}`, { + geoJson: { + type: "FeatureCollection", + features: [ + // tested horizontal line + ...points, + referenceBackground + ] + }, + theme: { + ...themeTextSettings, + styles: { + geojson: [ + referenceBackroundStyle, + { + when: "$geometryType == 'point'", + technique: "text", + attr + } + ] + } + }, + ...options + }); + } + } + function makePointPoiTestCases(testCases, options) { + for (const testCase in testCases) { + const attr = testCases[testCase]; + mapViewFeaturesRenderingTest(`poi-styling-${testCase}`, { + geoJson: { + type: "FeatureCollection", + features: [ + // tested horizontal line + ...points, + referenceBackground + ] + }, + theme: { + ...themeTextSettings, + clearColor: "#a0a0a0", + styles: { + geojson: [ + referenceBackroundStyle, + { + when: "$geometryType == 'point'", + technique: "labeled-icon", + attr + } + ] + } + }, + ...options + }); + } + } + function makePointTestCases(testCases, options) { + for (const testCase in testCases) { + const attr = testCases[testCase]; + mapViewFeaturesRenderingTest(`solid-line-styling-${testCase}`, { + geoJson: { + type: "FeatureCollection", + features: [ + // tested horizontal line + ...points, + referenceBackground + ] + }, + theme: { + ...themeTextSettings, + styles: { + geojson: [ + referenceBackroundStyle, + { + when: "$geometryType == 'point'", + technique: "circles", + attr + } + ] + } + }, + ...options + }); + } + } + describe("text", function () { + makePointTextTestCases({ + "point-text-basic": { color: "#f0f", size: 16 }, + "point-text-rgba": { color: "#f0f9", size: 16 }, + "point-text-bg-rgba": { + color: "#f0f", + backgroundSize: 6, + backgroundColor: "#0008", + size: 16 + } + }, { + margin: 0.5 + }); + }); + describe("poi", function () { + makePointPoiTestCases({ + "poi-basic-icon-only": { + imageTexture: "my-marker-icon", + size: 10, + screenHeight: 20, + screenWidth: 20, + text: "", + iconBrightness: 0.7 + }, + "poi-basic-text-only": { + color: "#fe09", + size: 16, + screenHeight: 20, + screenWidth: 20 + }, + "poi-basic-both": { + color: "#fe0", + size: 16, + imageTexture: "my-marker-icon", + screenHeight: 20, + screenWidth: 20, + iconYOffset: 16, + iconColor: "#e22" + } + }, { + margin: 0.1 + }); + }); + describe("circles", function () { + makePointTestCases({ + "point-circles-basic": { color: "#ca6", size: 10 }, + "point-circles-rgba": { color: "#ca69", size: 10 }, + "point-circles-rgb-opacity": { color: "#ca6", opacity: 0.5, size: 10 }, + "point-circles-rgba-opacity": { color: "#ca69", opacity: 0.5, size: 10 } + }, { + margin: 0.5 + }); + }); + }); + describe("line features", function () { + const straightLine = { + type: "Feature", + geometry: { + type: "LineString", + coordinates: [ + [0.004, 0.001], + [-0.004, 0.001] + ] + } + }; + const shortLine = { + type: "Feature", + geometry: { + type: "LineString", + coordinates: [ + [0.004, 0.001], + [0.0, 0.001] + ] + } + }; + const diagonalLine = { + type: "Feature", + geometry: { + type: "LineString", + coordinates: [ + [0.004, -0.004], + [-0.004, 0.004] + ] + } + }; + function makeLineTestCase(testCases, lineGeometry = straightLine) { + for (const testCase in testCases) { + for (const clipping of [true, false]) { + const postfix = clipping === true ? "" : "-no-clipping"; + const attr = JSON.parse(JSON.stringify(testCases[testCase])); + attr.clipping = clipping; + mapViewFeaturesRenderingTest(`solid-line-styling-${testCase}${postfix}`, { + geoJson: { + type: "FeatureCollection", + features: [lineGeometry, referenceBackground] + }, + theme: { + styles: { + geojson: [ + referenceBackroundStyle, + { + when: "$geometryType == 'line'", + technique: "solid-line", + attr + } + ] + } + } + // grid: 100 + }); + } + } + } + describe("solid-line technique", function () { + describe("basic", function () { + makeLineTestCase({ + "basic-100m": { lineWidth: 100, color: "#0b97c4" }, + "basic-dash-100m": { + lineWidth: 100, + color: "#0b97c4", + dashSize: 100, + gapSize: 100 + }, + "basic-dash-10px-world-ppi-scale": { + lineWidth: ["world-ppi-scale", 10], + color: "#0b97c4", + dashSize: ["world-ppi-scale", 10], + gapSize: ["world-ppi-scale", 10] + }, + "basic-dash-10px-string": { + lineWidth: "10px", + color: "#0b97c4", + dashSize: "10px", + gapSize: "10px" + }, + "basic-100m-rgba": { lineWidth: 100, color: "#0b97c470" }, + "basic-100m-rgba-square": { + lineWidth: 100, + color: "#0b97c470", + caps: "Square" + }, + "basic-100m-rgba-triangle-out": { + lineWidth: 100, + color: "#0b97c470", + caps: "TriangleIn" + }, + "basic-100m-rgba-trianglein": { + lineWidth: 100, + color: "#0b97c470", + caps: "TriangleOut" + }, + "basic-100m-rgba-none": { lineWidth: 100, color: "#0b97c470", caps: "None" }, + "basic-10px-rgba": { lineWidth: "10px", color: "#0b97c470" } + }); + // Short line that ends on tile border + makeLineTestCase({ + "short-100m": { lineWidth: 100, color: "#0b97c4" }, + "short-100m-rgba": { lineWidth: 100, color: "#0b97c470" }, + "short-100m-rgba-square": { + lineWidth: 100, + color: "#0b97c470", + caps: "Square" + }, + "short-100m-rgba-triangle-out": { + lineWidth: 100, + color: "#0b97c470", + caps: "TriangleIn" + }, + "short-100m-rgba-trianglein": { + lineWidth: 100, + color: "#0b97c470", + caps: "TriangleOut" + }, + "short-100m-rgba-none": { + lineWidth: 100, + color: "#0b97c470", + caps: "None" + }, + "short-10px-rgba": { lineWidth: "10px", color: "#0b97c470" } + }, shortLine); + // Diagonal lines are buggy at the moment + makeLineTestCase({ + "diagonal-100m": { lineWidth: 100, color: "#0b97c4" }, + "diagonal-dash-100m": { + lineWidth: 100, + color: "#0b97c4", + dashSize: 80, + gapSize: 80 + }, + "diagonal-100m-rgba": { lineWidth: 100, color: "#0b97c470" }, + "diagonal-100m-rgba-square": { + lineWidth: 100, + color: "#0b97c470", + caps: "Square" + }, + "diagonal-100m-rgba-triangle-out": { + lineWidth: 100, + color: "#0b97c470", + caps: "TriangleIn" + }, + "diagonal-100m-rgba-trianglein": { + lineWidth: 100, + color: "#0b97c470", + caps: "TriangleOut" + }, + "diagonal-100m-rgba-none": { + lineWidth: 100, + color: "#0b97c470", + caps: "None" + }, + "diagonal-10px-rgba": { lineWidth: "10px", color: "#0b97c470" } + }, diagonalLine); + }); + describe("with outline", function () { + makeLineTestCase({ + "outline-10px-2px": { + // BUGGY ? + lineWidth: "10px", + color: "#0b97c4", + outlineWidth: "2px", + outlineColor: "#7f7" + }, + "outline-10px-2px-rgba": { + lineWidth: "10px", + color: "#0b97c470", + outlineWidth: "2px", + outlineColor: "#7f7" + } + }); + // Short line that end on tile border + makeLineTestCase({ + "short-outline-10px-2px": { + // BUGGY ? + lineWidth: "10px", + color: "#0b97c4", + outlineWidth: "2px", + outlineColor: "#7f7" + }, + "short-outline-10px-2px-rgba": { + lineWidth: "10px", + color: "#0b97c470", + outlineWidth: "2px", + outlineColor: "#7f7" + } + }, shortLine); + // Diagonal lines are buggy at the moment + makeLineTestCase({ + "diagonal-outline-10px-2px": { + lineWidth: "10px", + color: "#0b97c4", + outlineWidth: "2px", + outlineColor: "#7f7" + }, + "diagonal-outline-10px-2px-rgba": { + lineWidth: "10px", + color: "#0b97c470", + outlineWidth: "2px", + outlineColor: "#7f7" + } + }, diagonalLine); + }); + }); + describe("text from lines", function () { + mapViewFeaturesRenderingTest(`line-text-basic`, { + width: 200, + height: 200, + geoJson: { + type: "FeatureCollection", + features: [ + // tested horizontal line + straightLine, + referenceBackground + ] + }, + theme: { + ...themeTextSettings, + styles: { + geojson: [ + referenceBackroundStyle, + { + when: "$geometryType == 'line'", + technique: "solid-line", + attr: { + color: "#E3D49A", + outlineColor: "#3A4C69", + lineWidth: 40, + outlineWidth: 10, + clipping: true + } + }, + { + when: "$geometryType == 'line'", + technique: "text", + attr: { + text: "Test", + color: "#2f3", + backgroundColor: "#cfe", + size: 20, + backgroundSize: 5, + fontStyle: "Bold", + vAlignment: "Above" + } + } + ] + } + } + }); + }); + }); + describe("polygon features", function () { + const lights = [ + { + type: "ambient", + color: "#FFFFFF", + name: "ambientLight", + intensity: 0.5 + }, + { + type: "directional", + color: "#FFFFFF", + name: "light1", + intensity: 0.5, + direction: { + x: -5, + y: -2, + z: 10 + } + } + ]; + const rectangle1 = { + // sample rectangular polygon + type: "Feature", + geometry: { + type: "Polygon", + coordinates: [ + [ + [0.001, 0.001], + [-0.004, 0.001], + [-0.004, -0.002], + [0.001, -0.002], + [0.001, 0.001] + ] + ] + }, + properties: { + name: "Awesome Building", + kind: "building", + height: 200, + bbox: [-0.004, -0.002, 0.001, 0.001] + } + }; + const rectangle2 = { + type: "Feature", + geometry: { + type: "Polygon", + coordinates: [ + [ + [0.001, 0.002], + [0.004, 0.002], + [0.004, -0.002], + [0.001, -0.002] + ] + ] + }, + properties: { + name: "Not So Awesome Building", + kind: "building", + height: 200 + } + }; + function makePolygonTestCases(technique, testCases, options) { + let extraFeatures = []; + if (options && options.geoJson) { + extraFeatures = + options.geoJson.type === "FeatureCollection" ? options.geoJson.features : []; + options = { ...options }; + delete options.geoJson; + } + for (const testCase in testCases) { + const attr = testCases[testCase]; + mapViewFeaturesRenderingTest(`polygon-styling-${testCase}`, { + geoJson: { + type: "FeatureCollection", + features: [ + // tested horizontal line + rectangle1, + rectangle2, + referenceBackground, + ...extraFeatures + ] + }, + theme: { + lights, + styles: { + geojson: [ + referenceBackroundStyle, + { + when: "$geometryType == 'polygon'", + technique: technique, + attr: attr + } + ] + } + }, + ...options + }); + } + } + describe("fill technique", function () { + describe("no outline", function () { + makePolygonTestCases("fill", { + fill: { color: "#0b97c4" }, + "fill-rgba": { color: "#0b97c470" } + }); + }); + describe("with outline", function () { + makePolygonTestCases("fill", { + // all tests are buggy ? because all outlines have 1px width + "fill-outline-200m": { color: "#0b97c4", lineColor: "#7f7", lineWidth: 200 }, + "fill-rgba-outline-200m": { + color: "#0b97c470", + lineColor: "#7f7", + lineWidth: 200 + }, + "fill-rgba-outline-rgba-200m": { + color: "#0b97c470", + lineColor: "#7f77", + lineWidth: 200 + }, + "fill-outline-disabled": { + color: "#0b97c4", + lineColor: "#7f7", + lineWidth: 200, + enabled: false + }, + "fill-outline-partially-disabled": { + color: "#0b97c4", + lineColor: "#7f7", + lineWidth: 200, + enabled: ["match", ["get", "name"], "Awesome Building", true, false] + } + // TODO: not supported by typings + // "rect-rgba-outline-rgba-5px": { + // color: "#0b97c470", + // lineColor: "#7f77", + // lineWidth: "5px" + // } + }); + }); + describe("textured", function () { + const textureUrl = "../dist/resources/radar.png"; + makePolygonTestCases("fill", { + "fill-texture-feature-space": { + color: "#ffffff", + map: textureUrl, + mapProperties: { flipY: true, minFilter: "linear" }, + transparent: true, + textureCoordinateType: TextureCoordinateType.FeatureSpace, + enabled: ["match", ["get", "name"], "Awesome Building", true, false] + }, + "fill-texture-dynamic": { + color: "#ffffff", + map: ["get", "dynamic-texture", ["dynamic-properties"]], + mapProperties: { flipY: true, minFilter: "linear" }, + transparent: true, + textureCoordinateType: TextureCoordinateType.FeatureSpace, + enabled: ["match", ["get", "name"], "Awesome Building", true, false] + } + }, { + dynamicProperties: [{ name: "dynamic-texture", value: textureUrl }] + }); + }); + }); + describe("standard technique", function () { + mapViewFeaturesRenderingTest(`polygon-dynamic-color`, { + geoJson: { + type: "FeatureCollection", + features: [rectangle1, rectangle2, referenceBackground] + }, + theme: { + lights, + styles: { + geojson: [ + referenceBackroundStyle, + { + when: "$geometryType == 'polygon'", + technique: "standard", + attr: { + color: ["get", "dynamic-color", ["dynamic-properties"]] + } + } + ] + } + }, + dynamicProperties: [{ name: "dynamic-color", value: "green" }] + }); + mapViewFeaturesRenderingTest(`polygon-standard-texture`, { + geoJson: { + type: "FeatureCollection", + features: [rectangle1, rectangle2, referenceBackground] + }, + theme: { + lights, + styles: { + geojson: [ + referenceBackroundStyle, + { + when: "$geometryType == 'polygon'", + technique: "standard", + attr: { + color: "#ffffff", + map: "../dist/resources/wests_textures/paving.png", + mapProperties: { + repeatU: 10, + repeatV: 10, + wrapS: "repeat", + wrapT: "repeat" + }, + textureCoordinateType: TextureCoordinateType.TileSpace + } + } + ] + } + } + }, async () => { + // we have no API to know when texture is already loaded + await new Promise(resolve => setTimeout(resolve, 500)); + }); + mapViewFeaturesRenderingTest(`polygon-standard-texture-transparent`, { + geoJson: { + type: "FeatureCollection", + features: [rectangle1, referenceBackground] + }, + theme: { + lights, + styles: { + geojson: [ + referenceBackroundStyle, + { + when: "$geometryType == 'polygon'", + technique: "standard", + attr: { + color: "#ffffff", + opacity: 0.5, + map: "../dist/resources/wests_textures/paving.png", + mapProperties: { + repeatU: 10, + repeatV: 10, + wrapS: "repeat", + wrapT: "repeat" + }, + textureCoordinateType: TextureCoordinateType.TileSpace + } + } + ] + } + } + }, async () => { + // we have no API to know when texture is already loaded + await new Promise(resolve => setTimeout(resolve, 500)); + }); + }); + describe("extruded-polygon technique", function () { + const tower = { + // sample polygon, that is smaller and higher than previous one + type: "Feature", + geometry: { + type: "Polygon", + coordinates: [ + [ + [0.002, 0.001], + [-0.002, 0.001], + [-0.002, -0.001], + [0.002, -0.001], + [0.002, 0.001] + ] + ] + }, + properties: { + kind: "tower", + height: 400 + } + }; + const viewOptions = { + margin: 0.3, + lookAt: { + tilt: 35, + heading: 30 + } + }; + describe("flat", function () { + makePolygonTestCases("extruded-polygon", { + "extruded-polygon-flat": { color: "#0b97c4", height: 0 }, + "extruded-polygon-flat-rgba": { color: "#0b97c470", height: 0 }, + "extruded-polygon-flat-rgba-outline": { + color: "#0b97c470", + height: 0, + lineWidth: 1, + lineColor: "#aaa" + } + }, viewOptions); + }); + describe("3d", function () { + makePolygonTestCases("extruded-polygon", { + "extruded-polygon-3d": { color: "#0b97c4" }, + "extruded-polygon-3d-rgba": { + color: "#0b97c480" + }, + "extruded-polygon-3d-rgba-disabled": { + color: "#0b97c480", + enabled: false + }, + "extruded-polygon-3d-rgba-outline": { + color: "#0b97c480", + lineWidth: 1, + lineColorMix: 0, + lineColor: "#7f7" + }, + "extruded-polygon-3d-rgba-outline-disabled": { + color: "#0b97c480", + lineWidth: 1, + lineColorMix: 0, + lineColor: "#7f7", + enabled: false + }, + "extruded-polygon-3d-rgba-outline-partialy-disabled": { + color: "#0b97c480", + lineWidth: 1, + lineColorMix: 0, + lineColor: "#7f7", + enabled: ["match", ["get", "name"], "Awesome Building", true, false] + } + }, viewOptions); + }); + describe("3d overlapping", function () { + makePolygonTestCases("extruded-polygon", { + "extruded-polygon-3d-overlap": { color: "#0b97c4" }, + "extruded-polygon-3d-overlap-rgba": { + color: "#0b97c480" + }, + "extruded-polygon-3d-overlap-rgba-outline": { + color: "#0b97c480", + lineWidth: 1, + lineColorMix: 0, + lineColor: "#7f7" + } + }, { + geoJson: { + type: "FeatureCollection", + features: [tower] + }, + ...viewOptions + }); + }); + }); + }); +}); +//# sourceMappingURL=StylingTest.js.map \ No newline at end of file diff --git a/test/rendering/StylingTest.js.map b/test/rendering/StylingTest.js.map new file mode 100644 index 0000000..db99073 --- /dev/null +++ b/test/rendering/StylingTest.js.map @@ -0,0 +1 @@ +{"version":3,"file":"StylingTest.js","sourceRoot":"","sources":["StylingTest.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/test/rendering/WebTileDataRendering.d.ts b/test/rendering/WebTileDataRendering.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/test/rendering/WebTileDataRendering.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/test/rendering/WebTileDataRendering.js b/test/rendering/WebTileDataRendering.js new file mode 100644 index 0000000..eaf6abf --- /dev/null +++ b/test/rendering/WebTileDataRendering.js @@ -0,0 +1,288 @@ +/* Copyright (C) 2025 flywave.gl contributors */ +import { sphereProjection } from "@flywave/flywave-geoutils"; +import { MapView, MapViewEventNames, TextureLoader } from "@flywave/flywave-mapview"; +import { RenderingTestHelper, waitForEvent } from "@flywave/flywave-test-utils"; +import { WebTileDataSource } from "@flywave/flywave-webtile-datasource"; +// Mocha discourages using arrow functions, see https://mochajs.org/#arrow-functions +describe("MapView + WebTileData rendering test", function () { + let mapView; + afterEach(() => { + if (mapView !== undefined) { + mapView.dispose(); + } + }); + async function webTileTest(options) { + const ibct = new RenderingTestHelper(options.mochaTest, { module: "mapview" }); + const canvas = document.createElement("canvas"); + canvas.width = 400; + canvas.height = 300; + mapView = new MapView({ + canvas, + theme: { clearColor: options.clearColor }, + preserveDrawingBuffer: true, + pixelRatio: 1, + projection: options.projection + }); + const defaultLookAt = { + target: { lat: 53.3, lng: 14.6 }, + zoomLevel: 2, + tilt: 0, + heading: 0 + }; + const lookAt = { ...defaultLookAt, ...options.lookAt }; + mapView.lookAt(lookAt); + // Shutdown errors cause by firefox bug + mapView.renderer.getContext().getShaderInfoLog = (x) => { + return ""; + }; + const webTileDataSource = new WebTileDataSource(Object.assign({ + dataProvider: { + getTexture: options.getTexture + }, + name: "webtile" + }, options.webTileOptions)); + mapView.addDataSource(webTileDataSource); + if (options.runBeforeFinish !== undefined) { + await options.runBeforeFinish(); + } + await waitForEvent(mapView, MapViewEventNames.FrameComplete); + await ibct.assertCanvasMatchesReference(canvas, options.testImageName); + } + it("renders webtile from loaded texture png", async function () { + this.timeout(5000); + await webTileTest({ + mochaTest: this, + testImageName: "webtile-clover", + getTexture: (tile) => { + return Promise.all([ + new TextureLoader().load("../dist/resources/wests_textures/clover.png"), + [] + ]); + } + }); + }); + it("renders webtile from loaded texture png on sphere", async function () { + this.timeout(5000); + await webTileTest({ + mochaTest: this, + testImageName: "webtile-clover-sphere", + projection: sphereProjection, + clearColor: "#ff0000", + getTexture: (tile) => { + return Promise.all([ + new TextureLoader().load("../dist/resources/wests_textures/clover.png"), + [] + ]); + } + }); + }); + it("renders webtile from loaded texture with opacity", async function () { + this.timeout(5000); + await webTileTest({ + mochaTest: this, + testImageName: "webtile-opacity", + getTexture: (tile) => { + return Promise.all([ + new TextureLoader().load("../dist/resources/wests_textures/clover.png"), + [] + ]); + }, + webTileOptions: { renderingOptions: { opacity: 0.5 } } + }); + }); + it("renders webtile from loaded texture with opacity on sphere", async function () { + this.timeout(5000); + await webTileTest({ + mochaTest: this, + testImageName: "webtile-opacity-sphere", + projection: sphereProjection, + getTexture: (tile) => { + return Promise.all([ + new TextureLoader().load("../dist/resources/wests_textures/clover.png"), + [] + ]); + }, + webTileOptions: { renderingOptions: { opacity: 0.5 } } + }); + }); + it("renders webtile from loaded texture png with alpha", async function () { + this.timeout(5000); + await webTileTest({ + mochaTest: this, + testImageName: "webtile-questionmark", + getTexture: (tile) => { + return Promise.all([ + new TextureLoader().load("../dist/resources/replacementCharacter.png"), + [] + ]); + } + }); + }); + it("renders webtile from loaded texture png with alpha, with minDataLevel", async function () { + this.timeout(5000); + await webTileTest({ + mochaTest: this, + testImageName: "webtile-min-data-level", + getTexture: (tile) => { + return Promise.all([ + new TextureLoader().load("../dist/resources/replacementCharacter.png"), + [] + ]); + }, + webTileOptions: { minDataLevel: 3 } + }); + }); + it("renders webtile from loaded texture png with alpha, with minDisplayLevel", async function () { + this.timeout(5000); + await webTileTest({ + mochaTest: this, + testImageName: "webtile-min-display-level", + getTexture: (tile) => { + return Promise.all([ + new TextureLoader().load("../dist/resources/replacementCharacter.png"), + [] + ]); + }, + webTileOptions: { minDisplayLevel: 3 } + }); + }); + it("renders 3 layered webTileDataSources with renderOrder", async function () { + this.timeout(5000); + const runBeforeFinish = async function () { + const webTileDataSource = new WebTileDataSource({ + dataSourceOrder: 2000, + renderingOptions: { + transparent: true + }, + minDataLevel: 3, + dataProvider: { + getTexture: (tile) => { + return Promise.all([ + new TextureLoader().load("../dist/resources/replacementCharacter.png"), + [] + ]); + } + }, + name: "webtile-transparent" + }); + await mapView.addDataSource(webTileDataSource); + const webTileDataSourcePavement = new WebTileDataSource({ + dataSourceOrder: 0, + dataProvider: { + getTexture: (tile) => { + return Promise.all([ + new TextureLoader().load("../dist/resources/wests_textures/paving.png"), + [] + ]); + } + }, + name: "webtile-paving" + }); + await mapView.addDataSource(webTileDataSourcePavement); + }; + await webTileTest({ + mochaTest: this, + testImageName: "webtile-layered-order", + getTexture: (tile) => { + return Promise.all([ + new TextureLoader().load("../dist/resources/wests_textures/clover.png"), + [] + ]); + }, + webTileOptions: { + renderingOptions: { + opacity: 0.5 + }, + dataSourceOrder: 1000, + name: "webtile-clover" + }, + runBeforeFinish + }); + }); + it("renders 3 layered webTileDataSources with opaque on top renderOrder", async function () { + this.timeout(5000); + const runBeforeFinish = async function () { + const webTileDataSource = new WebTileDataSource({ + renderingOptions: { + renderOrder: 3, + transparent: true + }, + minDataLevel: 3, + dataProvider: { + getTexture: (tile) => { + return Promise.all([ + new TextureLoader().load("../dist/resources/replacementCharacter.png"), + [] + ]); + } + }, + name: "webtile-transparent" + }); + await mapView.addDataSource(webTileDataSource); + const webTileDataSourcePavement = new WebTileDataSource({ + renderingOptions: { + renderOrder: 2 + }, + dataProvider: { + getTexture: (tile) => { + return Promise.all([ + new TextureLoader().load("../dist/resources/wests_textures/paving.png"), + [] + ]); + } + }, + name: "webtile-paving" + }); + await mapView.addDataSource(webTileDataSourcePavement); + }; + await webTileTest({ + mochaTest: this, + testImageName: "webtile-layered-render-order-opaque-top", + getTexture: (tile) => { + return Promise.all([ + new TextureLoader().load("../dist/resources/wests_textures/clover.png"), + [] + ]); + }, + webTileOptions: { + renderingOptions: { + opacity: 0.5, + renderOrder: 1 + }, + name: "webtile-clover" + }, + runBeforeFinish + }); + }); + it("renders webtiles on antimeridian without cracks for planar projection", async function () { + this.timeout(5000); + await webTileTest({ + mochaTest: this, + testImageName: "webtile-antimeridan-planar", + getTexture: (tile) => { + return Promise.all([new TextureLoader().load("../dist/resources/sea.png"), []]); + }, + lookAt: { + target: { lat: 64, lng: 180 }, + zoomLevel: 20 + } + }); + }); + it("renders webtiles on antimeridian without cracks for sphere projection", async function () { + // To be fixed. + this.timeout(5000); + await webTileTest({ + mochaTest: this, + testImageName: "webtile-antimeridan-sphere", + getTexture: (tile) => { + return Promise.all([new TextureLoader().load("../dist/resources/sea.png"), []]); + }, + lookAt: { + target: { lat: 64, lng: 180 }, + zoomLevel: 20 + }, + projection: sphereProjection + }); + }); +}); +//# sourceMappingURL=WebTileDataRendering.js.map \ No newline at end of file diff --git a/test/rendering/WebTileDataRendering.js.map b/test/rendering/WebTileDataRendering.js.map new file mode 100644 index 0000000..a691f4c --- /dev/null +++ b/test/rendering/WebTileDataRendering.js.map @@ -0,0 +1 @@ +{"version":3,"file":"WebTileDataRendering.js","sourceRoot":"","sources":["WebTileDataRendering.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/test/rendering/utils/GeoJsonStore.d.ts b/test/rendering/utils/GeoJsonStore.d.ts new file mode 100644 index 0000000..a3bc443 --- /dev/null +++ b/test/rendering/utils/GeoJsonStore.d.ts @@ -0,0 +1,34 @@ +import { type TileKey } from "@flywave/flywave-geoutils"; +import { DataProvider } from "@flywave/flywave-mapview-decoder"; +/** + * A simple {@link @flywave/flywave-mapview-decoder/DataProvider} that organizes GeoJson features using an rtree. + * + * The `GeoJsonStore` can be used as a {@link @flywave/flywave-mapview-decoder/DataProvider} + * of {@link @flywave/flywave-vectortile-datasource/VectorTileDataSource}s. + * + * @example + * ```typescript + * const geoJsonStore = new GeoJsonStore(); + * + * const dataSource = new VectorTileDataSource({ + * dataProvider: geoJsonStore, + * // ... + * }); + * + * geoJsonStore.features.insert(polygonFeature); + * geoJsonStore.features.insert(lineStringFeature); + * // ... + * ``` + */ +export declare class GeoJsonStore extends DataProvider { + /** + * The set of GeoJson features organized by this this `GeoJsonStore`. + */ + readonly features: import("geojson-rbush").RBush; + ready(): boolean; + getTile(tileKey: TileKey): Promise>; + protected connect(): Promise; + protected dispose(): void; +} diff --git a/test/rendering/utils/GeoJsonStore.js b/test/rendering/utils/GeoJsonStore.js new file mode 100644 index 0000000..817fa75 --- /dev/null +++ b/test/rendering/utils/GeoJsonStore.js @@ -0,0 +1,44 @@ +/* Copyright (C) 2025 flywave.gl contributors */ +import { webMercatorTilingScheme } from "@flywave/flywave-geoutils"; +import { DataProvider } from "@flywave/flywave-mapview-decoder"; +import RBush from "geojson-rbush"; +/** + * A simple {@link @flywave/flywave-mapview-decoder/DataProvider} that organizes GeoJson features using an rtree. + * + * The `GeoJsonStore` can be used as a {@link @flywave/flywave-mapview-decoder/DataProvider} + * of {@link @flywave/flywave-vectortile-datasource/VectorTileDataSource}s. + * + * @example + * ```typescript + * const geoJsonStore = new GeoJsonStore(); + * + * const dataSource = new VectorTileDataSource({ + * dataProvider: geoJsonStore, + * // ... + * }); + * + * geoJsonStore.features.insert(polygonFeature); + * geoJsonStore.features.insert(lineStringFeature); + * // ... + * ``` + */ +export class GeoJsonStore extends DataProvider { + constructor() { + super(...arguments); + /** + * The set of GeoJson features organized by this this `GeoJsonStore`. + */ + this.features = RBush(); + } + ready() { + return true; + } + async getTile(tileKey) { + const { west, south, east, north } = webMercatorTilingScheme.getGeoBox(tileKey); + const features = this.features.search([west, south, east, north]); + return features; + } + async connect() { } + dispose() { } +} +//# sourceMappingURL=GeoJsonStore.js.map \ No newline at end of file diff --git a/test/rendering/utils/GeoJsonStore.js.map b/test/rendering/utils/GeoJsonStore.js.map new file mode 100644 index 0000000..6050483 --- /dev/null +++ b/test/rendering/utils/GeoJsonStore.js.map @@ -0,0 +1 @@ +{"version":3,"file":"GeoJsonStore.js","sourceRoot":"","sources":["GeoJsonStore.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAEhD,OAAO,EAAgB,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAClF,OAAO,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAChE,OAAO,KAAK,MAAM,eAAe,CAAC;AAElC;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,OAAO,YAAa,SAAQ,YAAY;IAA9C;;QACI;;WAEG;QACM,aAAQ,GAAG,KAAK,EAAE,CAAC;IAehC,CAAC;IAbG,KAAK;QACD,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,OAAgB;QAC1B,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,uBAAuB,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QAChF,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;QAClE,OAAO,QAAQ,CAAC;IACpB,CAAC;IAES,KAAK,CAAC,OAAO,KAAmB,CAAC;IAEjC,OAAO,KAAU,CAAC;CAC/B"} \ No newline at end of file diff --git a/test/rendering/utils/GeoJsonTest.d.ts b/test/rendering/utils/GeoJsonTest.d.ts new file mode 100644 index 0000000..3469e74 --- /dev/null +++ b/test/rendering/utils/GeoJsonTest.d.ts @@ -0,0 +1,28 @@ +import { type FlatTheme, type GeoJson, type Theme } from "@flywave/flywave-datasource-protocol"; +import { type Projection } from "@flywave/flywave-geoutils"; +import { type LookAtParams, MapView } from "@flywave/flywave-mapview"; +import { type DataProvider } from "@flywave/flywave-mapview-decoder"; +export interface GeoJsonDataSourceTestOptions { + geoJson?: string | GeoJson; + tileGeoJson?: boolean; + dataProvider?: DataProvider; + dataSourceOrder?: number; +} +export interface GeoJsonTestOptions extends GeoJsonDataSourceTestOptions { + mochaTest: Mocha.Context; + testImageName: string; + theme: Theme | FlatTheme; + geoJson?: string | GeoJson; + lookAt?: Partial; + tileGeoJson?: boolean; + dataProvider?: DataProvider; + extraDataSource?: GeoJsonDataSourceTestOptions; + beforeFinishCallback?: (mapView: MapView) => void; + size?: number; + projection?: Projection; +} +export declare class GeoJsonTest { + mapView: MapView; + dispose(): void; + run(options: GeoJsonTestOptions): Promise; +} diff --git a/test/rendering/utils/GeoJsonTest.js b/test/rendering/utils/GeoJsonTest.js new file mode 100644 index 0000000..a81353f --- /dev/null +++ b/test/rendering/utils/GeoJsonTest.js @@ -0,0 +1,75 @@ +/* Copyright (C) 2025 flywave.gl contributors */ +import { GeoJsonDataProvider } from "@flywave/flywave-geojson-datasource/src"; +import { MapView, MapViewEventNames } from "@flywave/flywave-mapview"; +import { GeoJsonTiler } from "@flywave/flywave-mapview-decoder/index-worker"; +import { RenderingTestHelper, waitForEvent } from "@flywave/flywave-test-utils"; +import { VectorTileDataSource } from "@flywave/flywave-vectortile-datasource"; +import { VectorTileDecoder } from "@flywave/flywave-vectortile-datasource/index-worker"; +import * as sinon from "sinon"; +function createDataSource(name, options) { + const tiler = new GeoJsonTiler(); + if (options.tileGeoJson === false) { + sinon.stub(tiler, "getTile").resolves(options.geoJson); + } + return new VectorTileDataSource({ + decoder: new VectorTileDecoder(), + dataProvider: options.dataProvider ?? + new GeoJsonDataProvider("geojson", typeof options.geoJson === "string" + ? new URL(options.geoJson, window.location.href) + : options.geoJson, { tiler }), + name, + styleSetName: "geojson", + dataSourceOrder: options.dataSourceOrder + }); +} +export class GeoJsonTest { + dispose() { + this.mapView?.dispose(); + } + async run(options) { + const ibct = new RenderingTestHelper(options.mochaTest, { module: "mapview" }); + const canvas = document.createElement("canvas"); + canvas.width = 400; + canvas.height = 300; + this.mapView = new MapView({ + canvas, + theme: options.theme, + preserveDrawingBuffer: true, + pixelRatio: 1, + disableFading: true, + projection: options.projection + }); + this.mapView.animatedExtrusionHandler.enabled = false; + const defaultLookAt = { + target: { lat: 53.3, lng: 14.6 }, + distance: 200000, + tilt: 0, + heading: 0 + }; + const lookAt = { ...defaultLookAt, ...options.lookAt }; + this.mapView.lookAt(lookAt); + // Shutdown errors cause by firefox bug + this.mapView.renderer.getContext().getShaderInfoLog = (x) => { + return ""; + }; + const tiler = new GeoJsonTiler(); + if (options.tileGeoJson === false) { + sinon.stub(tiler, "getTile").resolves(options.geoJson); + } + const dataSource = createDataSource("geojson", options); + this.mapView.setDynamicProperty("enabled", true); + if (options.size) { + this.mapView.setDynamicProperty("size", options.size); + } + await this.mapView.addDataSource(dataSource); + if (options.extraDataSource) { + await this.mapView.addDataSource(createDataSource("geojson2", options.extraDataSource)); + } + if (options.beforeFinishCallback) { + await options.beforeFinishCallback?.(this.mapView); + } + await waitForEvent(this.mapView, MapViewEventNames.FrameComplete); + await ibct.assertCanvasMatchesReference(canvas, options.testImageName); + } +} +//# sourceMappingURL=GeoJsonTest.js.map \ No newline at end of file diff --git a/test/rendering/utils/GeoJsonTest.js.map b/test/rendering/utils/GeoJsonTest.js.map new file mode 100644 index 0000000..7949e64 --- /dev/null +++ b/test/rendering/utils/GeoJsonTest.js.map @@ -0,0 +1 @@ +{"version":3,"file":"GeoJsonTest.js","sourceRoot":"","sources":["GeoJsonTest.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/test/rendering/utils/ThemeBuilder.d.ts b/test/rendering/utils/ThemeBuilder.d.ts new file mode 100644 index 0000000..f29bab1 --- /dev/null +++ b/test/rendering/utils/ThemeBuilder.d.ts @@ -0,0 +1,45 @@ +import { type FlatTheme, type ImageTexture, type Light, type Style } from "@flywave/flywave-datasource-protocol"; +/** + * Utility Class for Rendering IBCT Tests + */ +export declare class ThemeBuilder { + private readonly m_useEmptyTheme; + static readonly imageTextures: ImageTexture[]; + static readonly lights: Light[]; + static readonly markerStyle: Style; + static readonly images: { + "icon-to-load": { + preload: boolean; + url: any; + }; + "red-icon": { + url: string; + preload: boolean; + }; + "green-icon": { + url: string; + preload: boolean; + }; + "white-icon": { + url: string; + preload: boolean; + }; + "plus-icon": { + url: string; + preload: boolean; + }; + }; + private readonly m_baseTheme; + private m_theme; + /** + * + * @param m_useEmptyTheme - If `true` initializes with an empty Theme, @default `false` + * The default uses some basic theme settings for initialization. + */ + constructor(m_useEmptyTheme?: boolean); + build(): FlatTheme; + withFontCatalog(): this; + withInvalidFontCatalog(): this; + withMarkerStyle(): this; + withStyle(style: Style): this; +} diff --git a/test/rendering/utils/ThemeBuilder.js b/test/rendering/utils/ThemeBuilder.js new file mode 100644 index 0000000..67a373b --- /dev/null +++ b/test/rendering/utils/ThemeBuilder.js @@ -0,0 +1,142 @@ +/* Copyright (C) 2025 flywave.gl contributors */ +import { getAppBaseUrl, resolveReferenceUri } from "@flywave/flywave-utils"; +// Mocha discourages using arrow functions, see https://mochajs.org/#arrow-functions +/** + * Utility Class for Rendering IBCT Tests + */ +export class ThemeBuilder { + /** + * + * @param m_useEmptyTheme - If `true` initializes with an empty Theme, @default `false` + * The default uses some basic theme settings for initialization. + */ + constructor(m_useEmptyTheme = false) { + this.m_useEmptyTheme = m_useEmptyTheme; + this.m_baseTheme = { + lights: ThemeBuilder.lights, + sky: { + type: "gradient", + topColor: "#161719", + bottomColor: "#262829", + groundColor: "#262829" + }, + clearColor: "#4A4D4E", + images: ThemeBuilder.images, + imageTextures: ThemeBuilder.imageTextures, + styles: [] + }; + this.m_theme = { styles: [] }; + if (!this.m_useEmptyTheme) { + this.m_theme = this.m_baseTheme; + } + } + build() { + return this.m_theme; + } + withFontCatalog() { + this.m_theme = { + ...this.m_theme, + fontCatalogs: [ + { + name: "fira", + url: "../dist/resources/fonts/Default_FontCatalog.json" + } + ] + }; + return this; + } + withInvalidFontCatalog() { + this.m_theme = { + ...this.m_theme, + fontCatalogs: [ + { + name: "invalid", + url: "invalid.json" + } + ] + }; + return this; + } + withMarkerStyle() { + return this.withStyle(ThemeBuilder.markerStyle); + } + withStyle(style) { + this.m_theme.styles.push(style); + return this; + } +} +ThemeBuilder.imageTextures = [ + { + name: "white-icon", + image: "white-icon" + }, + { + name: "red-icon", + image: "red-icon" + }, + { + name: "green-icon", + image: "green-icon" + }, + { + name: "plus-icon", + image: "plus-icon" + } +]; +ThemeBuilder.lights = [ + { + type: "ambient", + color: "#FFFFFF", + name: "ambientLight", + intensity: 0.9 + }, + { + type: "directional", + color: "#FFFFFF", + name: "light1", + intensity: 0.8, + direction: { + x: 1, + y: 5, + z: 0.5 + } + } +]; +ThemeBuilder.markerStyle = { + id: "baseOrder", + when: ["==", ["geometry-type"], "Point"], + technique: "labeled-icon", + // imageTexture: "white-icon", + size: ["number", ["get", "size"], 15], + iconYOffset: ["number", ["get", "iconYOffset"], 30], + text: ["get", "text"], + color: ["get", "color"], + imageTexture: ["string", ["get", "imageTexture"], "white-icon"], + renderOrder: ["get", "renderOrder"], + textReserveSpace: false, + iconReserveSpace: false, + styleSet: "geojson" +}; +ThemeBuilder.images = { + "icon-to-load": { + preload: false, + url: resolveReferenceUri(getAppBaseUrl(), "../dist/resources/icon-128x128.png") + }, + "red-icon": { + url: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzOCIgaGVpZ2h0PSI0NyIgdmlld0JveD0iMCAwIDM4IDQ3Ij48ZyBmaWxsPSJub25lIj48cGF0aCBmaWxsPSIjMEYxNjIxIiBmaWxsLW9wYWNpdHk9Ii40IiBkPSJNMTUgNDZjMCAuMzE3IDEuNzkuNTc0IDQgLjU3NHM0LS4yNTcgNC0uNTc0YzAtLjMxNy0xLjc5LS41NzQtNC0uNTc0cy00IC4yNTctNCAuNTc0eiI+PC9wYXRoPjxwYXRoIGZpbGw9IiNiNjAxMDEiIGQ9Ik0zMy4yNSAzMS42NTJBMTkuMDE1IDE5LjAxNSAwIDAgMCAzOCAxOS4wNkMzOCA4LjU0OSAyOS40NzggMCAxOSAwUzAgOC41NSAwIDE5LjA1OWMwIDQuODIzIDEuNzk1IDkuMjMzIDQuNzUgMTIuNTkzTDE4Ljk3NSA0NiAzMy4yNSAzMS42NTJ6Ij48L3BhdGg+PHBhdGggZmlsbD0iIzZBNkQ3NCIgZmlsbC1vcGFjaXR5PSIuNSIgZD0iTTI2Ljg2MiAzNy41bDQuNzE0LTQuNzdjMy44MjItMy41NzYgNS45MjQtOC40MTEgNS45MjQtMTMuNjJDMzcuNSA4Ljg0NyAyOS4yLjUgMTkgLjVTLjUgOC44NDguNSAxOS4xMWMwIDUuMjA5IDIuMTAyIDEwLjA0NCA1LjkxOSAxMy42MTRsNC43MTkgNC43NzZoMTUuNzI0ek0xOSAwYzEwLjQ5MyAwIDE5IDguNTI1IDE5IDE5LjA0MSAwIDUuNTA3LTIuMzQ4IDEwLjQ1NC02LjA3OSAxMy45MzJMMTkgNDYgNi4wNzkgMzIuOTczQzIuMzQ4IDI5LjQ5NSAwIDI0LjU0OCAwIDE5LjA0IDAgOC41MjUgOC41MDcgMCAxOSAweiI+PC9wYXRoPjwvZz48L3N2Zz4K", + preload: true + }, + "green-icon": { + url: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzOCIgaGVpZ2h0PSI0NyIgdmlld0JveD0iMCAwIDM4IDQ3Ij48ZyBmaWxsPSJub25lIj48cGF0aCBmaWxsPSIjMEYxNjIxIiBmaWxsLW9wYWNpdHk9Ii40IiBkPSJNMTUgNDZjMCAuMzE3IDEuNzkuNTc0IDQgLjU3NHM0LS4yNTcgNC0uNTc0YzAtLjMxNy0xLjc5LS41NzQtNC0uNTc0cy00IC4yNTctNCAuNTc0eiI+PC9wYXRoPjxwYXRoIGZpbGw9IiMwNGI2MDEiIGQ9Ik0zMy4yNSAzMS42NTJBMTkuMDE1IDE5LjAxNSAwIDAgMCAzOCAxOS4wNkMzOCA4LjU0OSAyOS40NzggMCAxOSAwUzAgOC41NSAwIDE5LjA1OWMwIDQuODIzIDEuNzk1IDkuMjMzIDQuNzUgMTIuNTkzTDE4Ljk3NSA0NiAzMy4yNSAzMS42NTJ6Ij48L3BhdGg+PHBhdGggZmlsbD0iIzZBNkQ3NCIgZmlsbC1vcGFjaXR5PSIuNSIgZD0iTTI2Ljg2MiAzNy41bDQuNzE0LTQuNzdjMy44MjItMy41NzYgNS45MjQtOC40MTEgNS45MjQtMTMuNjJDMzcuNSA4Ljg0NyAyOS4yLjUgMTkgLjVTLjUgOC44NDguNSAxOS4xMWMwIDUuMjA5IDIuMTAyIDEwLjA0NCA1LjkxOSAxMy42MTRsNC43MTkgNC43NzZoMTUuNzI0ek0xOSAwYzEwLjQ5MyAwIDE5IDguNTI1IDE5IDE5LjA0MSAwIDUuNTA3LTIuMzQ4IDEwLjQ1NC02LjA3OSAxMy45MzJMMTkgNDYgNi4wNzkgMzIuOTczQzIuMzQ4IDI5LjQ5NSAwIDI0LjU0OCAwIDE5LjA0IDAgOC41MjUgOC41MDcgMCAxOSAweiI+PC9wYXRoPjwvZz48L3N2Zz4K", + preload: true + }, + "white-icon": { + url: "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDIyLjEuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHdpZHRoPSI0OHB4IiBoZWlnaHQ9IjQ4cHgiIHZlcnNpb249IjEuMSIgaWQ9Imx1aS1pY29uLWRlc3RpbmF0aW9ucGluLW9uZGFyay1zb2xpZC1sYXJnZSIKCSB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4PSIwcHgiIHk9IjBweCIgdmlld0JveD0iMCAwIDQ4IDQ4IgoJIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDQ4IDQ4IiB4bWw6c3BhY2U9InByZXNlcnZlIj4KPGc+Cgk8ZyBpZD0ibHVpLWljb24tZGVzdGluYXRpb25waW4tb25kYXJrLXNvbGlkLWxhcmdlLWJvdW5kaW5nLWJveCIgb3BhY2l0eT0iMCI+CgkJPHBhdGggZmlsbD0iI2ZmZmZmZiIgZD0iTTQ3LDF2NDZIMVYxSDQ3IE00OCwwSDB2NDhoNDhWMEw0OCwweiIvPgoJPC9nPgoJPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGZpbGw9IiNmZmZmZmYiIGQ9Ik0yNCwyQzEzLjg3MDgsMiw1LjY2NjcsMTAuMTU4NCw1LjY2NjcsMjAuMjIzMwoJCWMwLDUuMDMyNSwyLjA1MzMsOS41ODg0LDUuMzcxNywxMi44ODgzTDI0LDQ2bDEyLjk2MTctMTIuODg4M2MzLjMxODMtMy4zLDUuMzcxNy03Ljg1NTgsNS4zNzE3LTEyLjg4ODMKCQlDNDIuMzMzMywxMC4xNTg0LDM0LjEyOTIsMiwyNCwyeiBNMjQsMjVjLTIuNzY1LDAtNS0yLjIzNS01LTVzMi4yMzUtNSw1LTVzNSwyLjIzNSw1LDVTMjYuNzY1LDI1LDI0LDI1eiIvPgo8L2c+Cjwvc3ZnPgo=", + preload: true + }, + "plus-icon": { + url: "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iMTIwIgogICBpZD0ic3ZnMjQ1NCIKICAgc29kaXBvZGk6dmVyc2lvbj0iMC4zMiIKICAgaW5rc2NhcGU6dmVyc2lvbj0iMC40NiIKICAgdmVyc2lvbj0iMS4wIgogICBzb2RpcG9kaTpkb2NuYW1lPSJQbHVzIHN5bWJvbC5zdmciCiAgIGlua3NjYXBlOm91dHB1dF9leHRlbnNpb249Im9yZy5pbmtzY2FwZS5vdXRwdXQuc3ZnLmlua3NjYXBlIj4KICA8ZGVmcwogICAgIGlkPSJkZWZzMjQ1NiI+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9Ijc0NC4wOTQ0OCA6IDUyNi4xODEwOSA6IDEiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMzcyLjA0NzI0IDogMzUwLjc4NzM5IDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI0NjIiIC8+CiAgPC9kZWZzPgogIDxzb2RpcG9kaTpuYW1lZHZpZXcKICAgICBpZD0iYmFzZSIKICAgICBwYWdlY29sb3I9IiNmZmZmZmYiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgYm9yZGVyb3BhY2l0eT0iMS4wIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiCiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIKICAgICBpbmtzY2FwZTp6b29tPSIwLjc5NTQ5NTEzIgogICAgIGlua3NjYXBlOmN4PSIxOTQuNTEzNjEiCiAgICAgaW5rc2NhcGU6Y3k9IjI2Ny43ODMzOSIKICAgICBpbmtzY2FwZTpkb2N1bWVudC11bml0cz0icHgiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ibGF5ZXIxIgogICAgIHNob3dncmlkPSJmYWxzZSIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjE0NDAiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iODgxIgogICAgIGlua3NjYXBlOndpbmRvdy14PSItNCIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iLTQiIC8+CiAgPGcKICAgICBpbmtzY2FwZTpsYWJlbD0iTGl2ZWxsbyAxIgogICAgIGlua3NjYXBlOmdyb3VwbW9kZT0ibGF5ZXIiCiAgICAgaWQ9ImxheWVyMSIKICAgICB0cmFuc2Zvcm09InNjYWxlKDAuMikiPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmb250LXNpemU6NDMxLjkyNDIyNDg1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZpbGw6I2ZmMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjE7Zm9udC1mYW1pbHk6Qml0c3RyZWFtIFZlcmEgU2FucyIKICAgICAgIGQ9Ik0gMjQxLjk5MjE5LDQwNy45MTAxNiBMIDI0MS45OTIxOSwzMTAuNDQ5MjIgTCAxNDQuNzI2NTYsMzEwLjQ0OTIyIEwgMTQ0LjcyNjU2LDI5NC40MzM1OSBMIDI0MS45OTIxOSwyOTQuNDMzNTkgTCAyNDEuOTkyMTksMTk3LjM2MzI4IEwgMjU3LjYxNzE5LDE5Ny4zNjMyOCBMIDI1Ny42MTcxOSwyOTQuNDMzNTkgTCAzNTUuMjczNDQsMjk0LjQzMzU5IEwgMzU1LjI3MzQ0LDMxMC40NDkyMiBMIDI1Ny42MTcxOSwzMTAuNDQ5MjIgTCAyNTcuNjE3MTksNDA3LjkxMDE2IEwgMjQxLjk5MjE5LDQwNy45MTAxNiB6IgogICAgICAgaWQ9InRleHQyNDY0IiAvPgogIDwvZz4KPC9zdmc+Cg==", + preload: true + } +}; +//# sourceMappingURL=ThemeBuilder.js.map \ No newline at end of file diff --git a/test/rendering/utils/ThemeBuilder.js.map b/test/rendering/utils/ThemeBuilder.js.map new file mode 100644 index 0000000..ba8796b --- /dev/null +++ b/test/rendering/utils/ThemeBuilder.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ThemeBuilder.js","sourceRoot":"","sources":["ThemeBuilder.ts"],"names":[],"mappings":"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"} \ No newline at end of file