diff --git a/declarations/mapillary.js.flow b/declarations/mapillary.js.flow index 549a687f..451043b8 100644 --- a/declarations/mapillary.js.flow +++ b/declarations/mapillary.js.flow @@ -230,23 +230,6 @@ export interface LngLatAlt extends LngLat { */ alt: number; } - -/** - * Contract describing a reconstruction point. - */ -export interface PointContract { - /** - * RGB color vector of the point, normalized to floats - * on the interval [0, 1]; - */ - color: number[]; - - /** - * Coordinates in metric scale in topocentric ENU - * reference frame with respect to a geo reference. - */ - coordinates: number[]; -} /** * Contract describing cluster reconstruction data. */ @@ -257,12 +240,32 @@ export interface ClusterContract { id: string; /** - * The points of the reconstruction. + * The IDs of the points. + * @description The order of the IDs correspond with the order + * of the color and coordinate arrays. */ - points: { - [pointId: string]: PointContract, - ... - }; + pointIds: string[]; + + /** + * The colors of the reconstruction. + * @description The colors are represented as RGB values + * normalized to floats on the interval [0, 1]. + * + * Colors are ordered according to the point IDs in + * a flattened array. + */ + colors: number[]; + + /** + * The points of the reconstruction. + * @description The points are represented in local topocentric + * ENU coordinates in metric scale relative to the cluster + * reference longitude, latitude, altitude. + * + * Coordinates are ordered according to the point IDs in + * a flattened array. + */ + coordinates: number[]; /** * The reference longitude, latitude, altitude of diff --git a/src/api/contracts/ClusterContract.ts b/src/api/contracts/ClusterContract.ts index 5e690ac2..b821c33f 100644 --- a/src/api/contracts/ClusterContract.ts +++ b/src/api/contracts/ClusterContract.ts @@ -1,5 +1,4 @@ import { LngLatAlt } from "../interfaces/LngLatAlt"; -import { PointContract } from "./PointContract"; /** * Contract describing cluster reconstruction data. @@ -10,14 +9,36 @@ export interface ClusterContract { */ id: string; + /** + * The IDs of the points. + * + * @description The order of the IDs correspond with the order + * of the color and coordinate arrays. + */ + pointIds: string[]; + + /** + * The colors of the reconstruction. + * + * @description The colors are represented as RGB values + * normalized to floats on the interval [0, 1]. + * + * Colors are ordered according to the point IDs in + * a flattened array. + */ + colors: number[]; + /** * The points of the reconstruction. * * @description The points are represented in local topocentric - * ENU coordinates relative to the cluster reference longitude, - * latitude, altitude. + * ENU coordinates in metric scale relative to the cluster + * reference longitude, latitude, altitude. + * + * Coordinates are ordered according to the point IDs in + * a flattened array. */ - points: { [pointId: string]: PointContract; }; + coordinates: number[]; /** * The reference longitude, latitude, altitude of diff --git a/src/api/contracts/PointContract.ts b/src/api/contracts/PointContract.ts deleted file mode 100644 index 846477be..00000000 --- a/src/api/contracts/PointContract.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Contract describing a reconstruction point. - */ -export interface PointContract { - /** - * RGB color vector of the point, normalized to floats - * on the interval [0, 1]; - */ - color: number[]; - - /** - * Coordinates in metric scale in topocentric ENU - * reference frame with respect to a geo reference. - */ - coordinates: number[]; -} diff --git a/src/api/provider/GraphConverter.ts b/src/api/provider/GraphConverter.ts index 2d1b62bf..7a005434 100644 --- a/src/api/provider/GraphConverter.ts +++ b/src/api/provider/GraphConverter.ts @@ -38,16 +38,26 @@ export class GraphConverter { : ClusterContract { const id: string = null; + const colors: number[] = []; + const coordinates: number[] = []; + const pointIds: string[] = []; + const points = source.points; - const normalize = 1 / 255; + const normalizer = 1 / 255; for (const pointId in points) { if (!points.hasOwnProperty(pointId)) { continue; } - const color = points[pointId].color; - color[0] *= normalize; - color[1] *= normalize; - color[2] *= normalize; + + pointIds.push(pointId); + const point = points[pointId]; + + const color = point.color; + colors.push(color[0] * normalizer); + colors.push(color[1] * normalizer); + colors.push(color[2] * normalizer); + + coordinates.push(...point.coordinates); } const lla = source.reference_lla; @@ -57,8 +67,10 @@ export class GraphConverter { lng: lla.longitude, }; return { + colors, + coordinates, id, - points, + pointIds, reference, }; } diff --git a/src/component/spatial/scene/ClusterPoints.ts b/src/component/spatial/scene/ClusterPoints.ts index b73a2326..0dc3a8f3 100644 --- a/src/component/spatial/scene/ClusterPoints.ts +++ b/src/component/spatial/scene/ClusterPoints.ts @@ -53,30 +53,16 @@ export class ClusterPoints extends Points { } private _makeAttributes(cluster: ClusterContract): void { - const positions: number[] = []; - const colors: number[] = []; - - const points = cluster.points; - for (const pointId in points) { - if (!points.hasOwnProperty(pointId)) { - continue; - } - - const point = points[pointId]; - positions.push(...point.coordinates); - - const color = point.color; - colors.push(color[0]); - colors.push(color[1]); - colors.push(color[2]); - } - const geometry = this.geometry; + geometry.setAttribute( "position", - new BufferAttribute(new Float32Array(positions), 3)); + new BufferAttribute( + new Float32Array(cluster.coordinates), 3)); + + const colorSize = cluster.colors.length / cluster.pointIds.length; geometry.setAttribute( "color", - new BufferAttribute(new Float32Array(colors), 3)); + new BufferAttribute(new Float32Array(cluster.colors), colorSize)); } } diff --git a/src/external/api.ts b/src/external/api.ts index d9a64c1e..d32a2ef6 100644 --- a/src/external/api.ts +++ b/src/external/api.ts @@ -48,7 +48,6 @@ export { ImageTilesContract } from "../api/contracts/ImageTilesContract"; export { ImageTilesRequestContract } from "../api/contracts/ImageTilesRequestContract"; export { MeshContract } from "../api/contracts/MeshContract"; -export { PointContract } from "../api/contracts/PointContract"; export { SequenceContract } from "../api/contracts/SequenceContract"; export { SpatialImagesContract } from "../api/contracts/SpatialImagesContract"; diff --git a/test/api/provider/GraphConverter.test.ts b/test/api/provider/GraphConverter.test.ts index ecbfad17..1405c442 100644 --- a/test/api/provider/GraphConverter.test.ts +++ b/test/api/provider/GraphConverter.test.ts @@ -72,14 +72,20 @@ describe("GraphConverter.clusterReconstruction", () => { expect(cluster).toBeDefined(); // Points - expect(Object.keys(cluster.points).length).toBe(1); - expect(pointId in cluster.points).toBe(true); - expect(cluster.points[pointId]).toBeDefined(); - expect(cluster.points[pointId].color[0]).toBeCloseTo(color[0] / 255); - expect(cluster.points[pointId].color[1]).toBeCloseTo(color[1] / 255); - expect(cluster.points[pointId].color[2]).toBeCloseTo(color[2] / 255); - expect(cluster.points[pointId].coordinates) - .toEqual(point.coordinates); + expect(Object.keys(cluster.pointIds).length).toBe(1); + + const index = cluster.pointIds.indexOf(pointId); + expect(index).toBeGreaterThanOrEqual(0); + + const pointIndex = 3 * index; + + expect(cluster.colors[pointIndex + 0]).toBeCloseTo(color[0] / 255); + expect(cluster.colors[pointIndex + 1]).toBeCloseTo(color[1] / 255); + expect(cluster.colors[pointIndex + 2]).toBeCloseTo(color[2] / 255); + + expect(cluster.coordinates[pointIndex + 0]).toBeCloseTo(coordinates[0]); + expect(cluster.coordinates[pointIndex + 1]).toBeCloseTo(coordinates[1]); + expect(cluster.coordinates[pointIndex + 2]).toBeCloseTo(coordinates[2]); // Reference expect(cluster.reference).toBeDefined(); diff --git a/test/api/provider/GraphDataProvider.test.ts b/test/api/provider/GraphDataProvider.test.ts index a3fd7935..bd073c5f 100644 --- a/test/api/provider/GraphDataProvider.test.ts +++ b/test/api/provider/GraphDataProvider.test.ts @@ -52,7 +52,7 @@ describe("GraphDataProvider.ctor", () => { describe("GraphDataProvider.getCluster", () => { it("should return cluster reconstruction on successful load", (done) => { const compressed = [{ - points: {}, + pointIds: [], reference_lla: { altitude: 1, latitude: 2, longitude: 3 }, }]; const fetchMock = (fetchArrayBuffer) @@ -65,7 +65,7 @@ describe("GraphDataProvider.getCluster", () => { provider.getCluster("url") .then( (r: ClusterContract): void => { - expect(r.points).toEqual({}); + expect(r.pointIds.length).toBe(0); expect(r.reference.alt).toBe(1); expect(r.reference.lat).toBe(2); expect(r.reference.lng).toBe(3); diff --git a/test/component/spatial/SpatialCache.test.ts b/test/component/spatial/SpatialCache.test.ts index 8c8ff0eb..d09ffde2 100644 --- a/test/component/spatial/SpatialCache.test.ts +++ b/test/component/spatial/SpatialCache.test.ts @@ -387,8 +387,10 @@ describe("SpatialCache.updateCell$", () => { describe("SpatialCache.updateReconstructions$", () => { const createCluster = (key: string): ClusterContract => { return { + colors: [], + coordinates: [], id: key, - points: {}, + pointIds: [], reference: { lat: 0, lng: 0, alt: 0 }, }; };