feat: change color api to override current mode

This commit is contained in:
Oscar Lorentzon 2022-11-08 16:19:08 -08:00
parent 17446cf21e
commit bd10a1b620
12 changed files with 405 additions and 289 deletions

View File

@ -2,6 +2,7 @@
"extends": "../tsconfig.json",
"compilerOptions": {
"declaration": false,
"downlevelIteration": true,
"module": "commonjs",
"outDir": "../build/cjs/",
"sourceMap": false,

View File

@ -2409,7 +2409,6 @@ declare var CameraVisualizationMode: {|
+Cluster: 2, // 2
+ConnectedComponent: 3, // 3
+Sequence: 4, // 4
+Manual: 5, // 2
|};
declare var OriginalPositionMode: {|
@ -2422,7 +2421,6 @@ declare var PointVisualizationMode: {|
+Hidden: 0, // 0
+Original: 1, // 1
+Cluster: 2, // 2
+Manual: 3, // 2
|};
/**
@ -6607,24 +6605,13 @@ declare class SpatialComponent extends Component<SpatialConfiguration> {
constructor(name: string, container: Container, navigator: Navigator): this;
/**
* Configure the cluster color used when painting
* clusters manually.
*
* @description The configured color is applied when the
* following visualization modes are set respectively:
*
* {@link CameraVisualizationMode.Manual}
* {@link PointVisualizationMode.Manual}
*
* @param {string} clusterId - Id of the cluster to configure.
* @param {number} color - The color to paint the cluster with.
*
* @example
* ```js
* spatialComponent.configureClusterColor('my-cluster-id', 0x00ff00);
* ```
* Get the currently set camera frame override color, or null if
* no color is set.
* @param {string} clusterId - Id of the cluster.
* @returns {string | number | null} The current override color
* for the cluster.
*/
configureClusterColor(clusterId: string, color: number | string): void;
getCameraOverrideColor(clusterId: string): string | number | null;
/**
* Returns the image id of the camera frame closest to the current
@ -6648,6 +6635,44 @@ declare class SpatialComponent extends Component<SpatialConfiguration> {
* ```
*/
getFrameIdAt(pixelPoint: number[]): Promise<string>;
/**
* Get the currently set point override color, or null if
* no color is set.
* @param {string} clusterId - Id of the cluster.
* @returns {string | number | null} The current override color
* for the cluster.
*/
getPointOverrideColor(clusterId: string): string | number | null;
/**
* Override the camera color for a cluster.
* @description The configured color is applied for all visible
* visualization modes, overriding the color of the currently
* selected mode.
* @param {string} clusterId - Id of the cluster to configure.
* @param {number | string} color - The color to paint the cameras with.
* @example ```js
* spatialComponent.setCameraOverrideColor('my-cluster-id', 0x00ff00);
* ```
*/
setCameraOverrideColor(
clusterId: string,
color: number | string | null
): void;
/**
* Override the point color for a cluster.
* @description The configured color is applied for all visible
* visualization modes, overriding the color of the currently
* selected mode.
* @param {string} clusterId - Id of the cluster to configure.
* @param {number | string} color - The color to paint the points with.
* @example ```js
* spatialComponent.setPointOverrideColor('my-cluster-id', 0x00ff00);
* ```
*/
setPointOverrideColor(clusterId: string, color: number | string | null): void;
_activate(): void;
_deactivate(): void;
_getDefaultConfiguration(): SpatialConfiguration;

View File

@ -33,6 +33,7 @@
import {
CameraControls,
CameraVisualizationMode,
GraphDataProvider,
OriginalPositionMode,
PointVisualizationMode,
Viewer,
@ -43,34 +44,63 @@
let cameraControls = CameraControls.Earth;
let spatialActive = true;
const dataProvider = new GraphDataProvider({ accessToken });
const viewer = new Viewer({
accessToken,
cameraControls,
component: { cover: false, spatial: spatialActive },
container: "mly",
dataProvider,
});
viewer.moveTo(imageId).catch((error) => console.error(error));
const spatial = viewer.getComponent("spatial");
viewer.on("click", (event) => {
spatial
.getFrameIdAt(event.pixelPoint)
.then((id) => console.log("Clicked frame:", id));
});
function randomColor() {
return `hsl(${Math.floor(360 * Math.random())}, 100%, 75%)`;
}
let hoveredImageId = null;
let paintedClusterId = null;
viewer.on("mousemove", async (event) => {
function paintCluster(clusterId, color) {
spatial.setPointOverrideColor(clusterId, color);
spatial.setCameraOverrideColor(clusterId, color);
}
const colors = new Set();
viewer.on("image", (event) => {
const clusterId = event.image.clusterId;
if (colors.has(clusterId)) {
const imageId = await spatial.getFrameIdAt(event.pixelPoint);
if (imageId === hoveredImageId) {
return;
}
colors.add(clusterId);
spatial.configureClusterColor(clusterId, randomColor());
hoveredImageId = imageId;
if (paintedClusterId != null) {
paintCluster(paintedClusterId, null);
paintedClusterId = null;
}
if (hoveredImageId == null) {
return;
}
const images = await dataProvider.getImages([imageId]);
if (images.length !== 1) {
return;
}
const image = images[0];
if (image.node.id !== hoveredImageId) {
return;
}
const clusterId = image.node.cluster.id;
if (clusterId === paintedClusterId) {
return;
}
paintCluster(clusterId, 0xff00ff);
paintedClusterId = clusterId;
});
const s = Object.assign({}, spatial.defaultConfiguration, {
@ -84,14 +114,12 @@
const cluster = camMode.Cluster;
const connectedComponent = camMode.ConnectedComponent;
const sequence = camMode.Sequence;
const manual = camMode.Manual;
const camModeRotation = {};
camModeRotation[hidden] = homogeneous;
camModeRotation[homogeneous] = cluster;
camModeRotation[cluster] = connectedComponent;
camModeRotation[connectedComponent] = sequence;
camModeRotation[sequence] = manual;
camModeRotation[manual] = hidden;
camModeRotation[sequence] = hidden;
return function () {
s.cameraVisualizationMode =
@ -136,12 +164,10 @@
const hidden = PointVisualizationMode.Hidden;
const original = PointVisualizationMode.Original;
const cluster = PointVisualizationMode.Cluster;
const manual = PointVisualizationMode.Manual;
const pvmRotation = {};
pvmRotation[hidden] = original;
pvmRotation[original] = cluster;
pvmRotation[cluster] = manual;
pvmRotation[manual] = hidden;
pvmRotation[cluster] = hidden;
return function () {
s.pointVisualizationMode =
@ -172,12 +198,6 @@
};
})();
function resetManualColor() {
for (const clusterId of colors.keys()) {
spatial.configureClusterColor(clusterId, randomColor());
}
}
window.document.addEventListener("keydown", (e) => {
if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) {
return;
@ -224,9 +244,6 @@
case "f":
setFilter();
break;
case "m":
resetManualColor();
break;
default:
break;
}

View File

@ -5,10 +5,6 @@ export function isModeVisible(mode: CameraVisualizationMode): boolean {
return mode !== CameraVisualizationMode.Hidden;
}
export function isModeManual(mode: CameraVisualizationMode): boolean {
return mode === CameraVisualizationMode.Manual;
}
export function isOverviewState(state: State): boolean {
return state === State.Custom || state === State.Earth;
}

View File

@ -1,7 +1,7 @@
import { LngLatAlt } from "../../api/interfaces/LngLatAlt";
import { enuToGeodetic, geodeticToEnu } from "../../geo/GeoCoords";
export const SPATIAL_DEFAULT_MANUAL_COLOR = 0xFFFFFF;
export const SPATIAL_DEFAULT_COLOR = 0xFFFFFF;
export function resetEnu(reference: LngLatAlt, prevEnu: number[], prevReference: LngLatAlt): number[] {
const [prevX, prevY, prevZ] = prevEnu;

View File

@ -97,6 +97,19 @@ export class SpatialComponent extends Component<SpatialConfiguration> {
this._spatial = new Spatial();
}
/**
* Get the currently set camera frame override color, or null if
* no color is set.
*
* @param {string} clusterId - Id of the cluster.
*
* @returns {string | number | null} The current override color
* for the cluster.
*/
public getCameraOverrideColor(clusterId: string): string | number | null {
return this._scene.getCameraOverrideColor(clusterId);
}
/**
* Returns the image id of the camera frame closest to the current
* render camera position at the specified point.
@ -149,27 +162,58 @@ export class SpatialComponent extends Component<SpatialConfiguration> {
}
/**
* Configure the cluster color used when painting
* clusters manually.
* Get the currently set point override color, or null if
* no color is set.
*
* @description The configured color is applied when the
* following visualization modes are set respectively:
* @param {string} clusterId - Id of the cluster.
*
* {@link CameraVisualizationMode.Manual}
* {@link PointVisualizationMode.Manual}
* @returns {string | number | null} The current override color
* for the cluster.
*/
public getPointOverrideColor(clusterId: string): string | number | null {
return this._scene.getPointOverrideColor(clusterId);
}
/**
* Override the camera color for a cluster.
*
* @description The configured color is applied for all visible
* visualization modes, overriding the color of the currently
* selected mode.
*
* @param {string} clusterId - Id of the cluster to configure.
* @param {number} color - The color to paint the cluster with.
* @param {number | string} color - The color to paint the cameras with.
*
* @example
* ```js
* spatialComponent.configureClusterColor('my-cluster-id', 0x00ff00);
* spatialComponent.setCameraOverrideColor('my-cluster-id', 0x00ff00);
* ```
*/
public configureClusterColor(
public setCameraOverrideColor(
clusterId: string,
color: number | string): void {
this._scene.configureClusterColor(clusterId, color);
color: number | string | null): void {
this._scene.setCameraOverrideColor(clusterId, color);
}
/**
* Override the point color for a cluster.
*
* @description The configured color is applied for all visible
* visualization modes, overriding the color of the currently
* selected mode.
*
* @param {string} clusterId - Id of the cluster to configure.
* @param {number | string} color - The color to paint the points with.
*
* @example
* ```js
* spatialComponent.setPointOverrideColor('my-cluster-id', 0x00ff00);
* ```
*/
public setPointOverrideColor(
clusterId: string,
color: number | string | null): void {
this._scene.setPointOverrideColor(clusterId, color);
}
protected _activate(): void {

View File

@ -19,10 +19,10 @@ import { CellLine } from "./scene/CellLine";
import { SpatialIntersection } from "./scene/SpatialIntersection";
import { SpatialCell } from "./scene/SpatialCell";
import { SpatialAssets } from "./scene/SpatialAssets";
import { isModeManual, isModeVisible } from "./Modes";
import { isModeVisible } from "./Modes";
import { PointVisualizationMode } from "./enums/PointVisualizationMode";
import { LngLatAlt } from "../../api/interfaces/LngLatAlt";
import { resetEnu, SPATIAL_DEFAULT_MANUAL_COLOR } from "./SpatialCommon";
import { resetEnu, SPATIAL_DEFAULT_COLOR } from "./SpatialCommon";
const NO_CLUSTER_ID = "NO_CLUSTER_ID";
const NO_MERGE_ID = "NO_MERGE_ID";
@ -66,10 +66,11 @@ export class SpatialScene {
private _filter: FilterFunction;
private _imageCellMap: Map<string, string>;
private _clusterCellMap: Map<string, string[]>;
private _clusterCellMap: Map<string, Set<string>>;
private _colors: { hover: string, select: string; };
private _manualColors: Map<string, number | string>;
private _cameraOverrideColors: Map<string, number | string>;
private _pointOverrideColors: Map<string, number | string>;
constructor(
configuration: SpatialConfiguration,
@ -104,12 +105,16 @@ export class SpatialScene {
this._hoveredId = null;
this._selectedId = null;
this._colors = { hover: "#FF0000", select: "#FF8000" };
this._manualColors = new Map();
this._cameraOverrideColors = new Map();
this._pointOverrideColors = new Map();
this._filter = () => true;
}
public get needsRender(): boolean { return this._needsRender; }
public get needsRender(): boolean {
return this._needsRender;
}
public get intersection(): SpatialIntersection {
return this._intersection;
}
@ -125,26 +130,14 @@ export class SpatialScene {
const clusterId = reconstruction.id;
if (!this._manualColors.has(clusterId)) {
this._manualColors.set(clusterId, SPATIAL_DEFAULT_MANUAL_COLOR);
}
if (!(clusterId in this._clusters)) {
this._clusters[clusterId] = {
points: new Object3D(),
cellIds: [],
};
const visible = this._getClusterVisible(clusterId);
const color = this._getPointColor(clusterId);
const cluster = this._clusters[clusterId];
let color: number | string = null;
if (this._pointVisualizationMode === PointVisualizationMode.Cluster) {
color = this._assets.getColor(clusterId);
} else if (this._pointVisualizationMode === PointVisualizationMode.Manual) {
color = this._manualColors.get(clusterId);
}
const points = new ClusterPoints({
cluster: reconstruction,
color,
@ -152,6 +145,7 @@ export class SpatialScene {
scale: this._pointSize,
translation,
});
const visible = this._getClusterVisible(clusterId);
cluster.points.visible = visible;
cluster.points.add(points);
this._scene.add(cluster.points);
@ -195,16 +189,17 @@ export class SpatialScene {
}
const cell = this._images[cellId];
if (cell.hasImage(imageId)) { return; }
if (cell.hasImage(imageId)) {
return;
}
cell.addImage({ idMap, image: image });
let color: number | string = null;
if (this._pointVisualizationMode === PointVisualizationMode.Cluster) {
const colorId = cell.getColorId(imageId, this._cameraVisualizationMode);
color = this._assets.getColor(colorId);
} else if (this._pointVisualizationMode === PointVisualizationMode.Manual) {
color = this._manualColors.get(idMap.clusterId);
}
const colorId = cell.getColorId(imageId, this._cameraVisualizationMode);
let color: number | string =
this._cameraOverrideColors.has(idMap.clusterId) ?
this._pointOverrideColors.get(idMap.clusterId) :
this._assets.getColor(colorId);
const visible = this._filter(image);
cell.visualize({
id: imageId,
@ -218,9 +213,12 @@ export class SpatialScene {
});
if (!this._clusterCellMap.has(idMap.clusterId)) {
this._clusterCellMap.set(idMap.clusterId, []);
this._clusterCellMap.set(idMap.clusterId, new Set());
}
const clusterCells = this._clusterCellMap.get(idMap.clusterId);
if (!clusterCells.has(cellId)) {
clusterCells.add(cellId);
}
this._clusterCellMap.get(idMap.clusterId).push(cellId);
this._imageCellMap.set(imageId, cellId);
if (imageId === this._selectedId) {
@ -233,9 +231,6 @@ export class SpatialScene {
const clusterVisible = this._getClusterVisible(idMap.clusterId);
this._clusters[idMap.clusterId].points.visible = clusterVisible;
}
if (!this._manualColors.has(idMap.clusterId)) {
this._manualColors.set(idMap.clusterId, SPATIAL_DEFAULT_MANUAL_COLOR);
}
this._needsRender = true;
}
@ -254,39 +249,6 @@ export class SpatialScene {
this._needsRender = true;
}
public configureClusterColor(
clusterId: string,
color: number | string): void {
this._manualColors.set(clusterId, color);
if (!(clusterId in this._clusters)) {
return;
}
if (this._pointVisualizationMode === PointVisualizationMode.Manual) {
const cluster = this._clusters[clusterId];
cluster.points.visible = this._getClusterVisible(clusterId);
for (const points of cluster.points.children) {
(<ClusterPoints>points).setColor(color);
}
}
const mode = this._cameraVisualizationMode;
if (mode === CameraVisualizationMode.Manual) {
if (this._clusterCellMap.has(clusterId)) {
for (const cellId of this._clusterCellMap.get(clusterId)) {
const cell = this._images[cellId];
cell.applyColorMap(this._manualColors);
}
}
this._highlight(this._hoveredId, this._colors.hover, mode);
this._highlight(this._selectedId, this._colors.select, mode);
}
this._needsRender = true;
}
public deactivate(): void {
this._filter = () => true;
this._selectedId = null;
@ -295,15 +257,23 @@ export class SpatialScene {
this.uncache();
}
public hasCluster(clusterId: string, cellId: string): boolean {
return clusterId in this._clusters &&
this._clusters[clusterId].cellIds.indexOf(cellId) !== -1;
public getCameraOverrideColor(clusterId: string): string | number | null {
return this._cameraOverrideColors.get(clusterId);
}
public getPointOverrideColor(clusterId: string): string | number | null {
return this._pointOverrideColors.get(clusterId);
}
public hasCell(cellId: string): boolean {
return cellId in this._cells;
}
public hasCluster(clusterId: string, cellId: string): boolean {
return clusterId in this._clusters &&
this._clusters[clusterId].cellIds.indexOf(cellId) !== -1;
}
public hasImage(imageId: string, cellId: string): boolean {
return cellId in this._images &&
this._images[cellId].hasImage(imageId);
@ -354,8 +324,28 @@ export class SpatialScene {
}
}
public setCameraOverrideColor(
clusterId: string,
color: number | string | null): void {
if (color != null) {
this._cameraOverrideColors.set(clusterId, color);
} else {
this._cameraOverrideColors.delete(clusterId);
}
if (!this._clusterCellMap.has(clusterId)) {
return;
}
const cellIds = this._clusterCellMap.get(clusterId);
this._applyCameraColor([...cellIds.keys()]);
this._needsRender = true;
}
public setCameraSize(cameraSize: number): void {
if (Math.abs(cameraSize - this._cameraSize) < 1e-4) { return; }
if (Math.abs(cameraSize - this._cameraSize) < 1e-4) {
return;
}
const imageCells = this._images;
for (const cellId of Object.keys(imageCells)) {
@ -368,6 +358,34 @@ export class SpatialScene {
this._needsRender = true;
}
public setCameraVisualizationMode(mode: CameraVisualizationMode): void {
if (mode === this._cameraVisualizationMode) {
return;
}
this._cameraVisualizationMode = mode;
this._applyCameraColor(Object.keys(this._images));
this._needsRender = true;
}
public setCellVisibility(visible: boolean): void {
if (visible === this._cellsVisible) {
return;
}
for (const cellId in this._cells) {
if (!this._cells.hasOwnProperty(cellId)) {
continue;
}
this._cells[cellId].visible = visible;
}
this._cellsVisible = visible;
this._needsRender = true;
}
public setFilter(filter: FilterFunction): void {
this._filter = filter;
const clusterVisibles: { [key: string]: boolean; } = {};
@ -388,7 +406,10 @@ export class SpatialScene {
const pointsVisible =
this._pointVisualizationMode !== PointVisualizationMode.Hidden;
for (const clusterId in clusterVisibles) {
if (!clusterVisibles.hasOwnProperty(clusterId)) { continue; }
if (!clusterVisibles.hasOwnProperty(clusterId)) {
continue;
}
clusterVisibles[clusterId] &&= pointsVisible;
const visible = clusterVisibles[clusterId];
if (clusterId in this._clusters) {
@ -426,6 +447,20 @@ export class SpatialScene {
this._hoveredId = imageId;
}
public setPointOverrideColor(
clusterId: string,
color: number | string | null): void {
if (color != null) {
this._pointOverrideColors.set(clusterId, color);
} else {
this._pointOverrideColors.delete(clusterId);
}
this._applyPointColor(clusterId);
this._needsRender = true;
}
public setPointSize(pointSize: number): void {
if (Math.abs(pointSize - this._pointSize) < 1e-4) {
return;
@ -436,7 +471,6 @@ export class SpatialScene {
if (!clusters.hasOwnProperty(key)) {
continue;
}
for (const points of clusters[key].points.children) {
(<ClusterPoints>points).resize(pointSize);
}
@ -452,30 +486,22 @@ export class SpatialScene {
}
this._pointVisualizationMode = mode;
for (const clusterId in this._clusters) {
if (!this._clusters.hasOwnProperty(clusterId)) {
continue;
}
const cluster = this._clusters[clusterId];
cluster.points.visible = this._getClusterVisible(clusterId);
for (const points of cluster.points.children) {
let color: string | number = null;
if (mode === PointVisualizationMode.Cluster) {
color = this._assets.getColor(clusterId);
} else if (mode === PointVisualizationMode.Manual) {
color = this._manualColors.get(clusterId);
}
(<ClusterPoints>points).setColor(color);
}
this._applyPointColor(clusterId);
}
this._needsRender = true;
}
public setPositionMode(mode: OriginalPositionMode): void {
if (mode === this._positionMode) { return; }
if (mode === this._positionMode) {
return;
}
for (const cell of Object.values(this._images)) {
cell.applyPositionMode(mode);
}
@ -484,8 +510,9 @@ export class SpatialScene {
}
public setSelectedImage(id: string | null): void {
if (this._selectedId === id) { return; }
this._needsRender = true;
if (this._selectedId === id) {
return;
}
if (this._selectedId != null) {
this._resetCameraColor(this._selectedId);
@ -497,55 +524,6 @@ export class SpatialScene {
this._cameraVisualizationMode);
this._selectedId = id;
}
public setCellVisibility(visible: boolean): void {
if (visible === this._cellsVisible) {
return;
}
for (const cellId in this._cells) {
if (!this._cells.hasOwnProperty(cellId)) {
continue;
}
this._cells[cellId].visible = visible;
}
this._cellsVisible = visible;
this._needsRender = true;
}
public setCameraVisualizationMode(mode: CameraVisualizationMode): void {
if (mode === this._cameraVisualizationMode) { return; }
const visible = isModeVisible(mode);
if (isModeManual(mode)) {
const colors = this._manualColors;
for (const cell of Object.values(this._images)) {
cell.cameras.visible = visible;
cell.applyColorMap(colors);
}
} else {
const assets = this._assets;
for (const cell of Object.values(this._images)) {
cell.cameras.visible = visible;
const cameraMap = cell.getCamerasByMode(mode);
cameraMap.forEach(
(cameras, colorId) => {
const color = assets.getColor(colorId);
for (const camera of cameras) {
camera.setColor(color);
}
});
}
}
this._highlight(this._hoveredId, this._colors.hover, mode);
this._highlight(this._selectedId, this._colors.select, mode);
this._cameraVisualizationMode = mode;
this._needsRender = true;
}
@ -583,6 +561,52 @@ export class SpatialScene {
this._needsRender = true;
}
private _applyCameraColor(cellIds: string[]): void {
const mode = this._cameraVisualizationMode;
const visible = isModeVisible(mode);
const assets = this._assets;
const overrides = this._cameraOverrideColors;
const images = this._images;
for (const cellId of cellIds) {
if (!(cellId in images)) {
continue;
}
const cell = images[cellId];
cell.cameras.visible = visible;
const cameraMap = cell.getCamerasByMode(mode);
cameraMap.forEach(
(cameras, colorId) => {
let color: number | string = assets.getColor(colorId);
for (const camera of cameras) {
if (overrides.has(camera.clusterId)) {
camera.camera.setColor(overrides.get(camera.clusterId));
} else {
camera.camera.setColor(color);
}
}
});
}
this._highlight(this._hoveredId, this._colors.hover, mode);
this._highlight(this._selectedId, this._colors.select, mode);
}
private _applyPointColor(clusterId: string): void {
if (!(clusterId in this._clusters)) {
return;
}
const cluster = this._clusters[clusterId];
cluster.points.visible = this._getClusterVisible(clusterId);
const color = this._getPointColor(clusterId);
for (const points of cluster.points.children) {
(<ClusterPoints>points).setColor(color);
}
}
private _getClusterVisible(clusterId: string): boolean {
if (this._pointVisualizationMode === PointVisualizationMode.Hidden) {
return false;
@ -596,6 +620,19 @@ export class SpatialScene {
return visible;
}
private _disposeCell(cellId: string): void {
const cell = this._cells[cellId];
for (const line of cell.children.slice()) {
(<CellLine>line).dispose();
cell.remove(line);
}
this._scene.remove(cell);
delete this._cells[cellId];
}
private _disposePoints(cellId: string): void {
for (const clusterId of this._cellClusters[cellId].keys) {
if (!(clusterId in this._clusters)) {
@ -629,19 +666,6 @@ export class SpatialScene {
delete this._cellClusters[cellId];
}
private _disposeCell(cellId: string): void {
const cell = this._cells[cellId];
for (const line of cell.children.slice()) {
(<CellLine>line).dispose();
cell.remove(line);
}
this._scene.remove(cell);
delete this._cells[cellId];
}
private _getNear(cameraSize: number): number {
const near = RAY_NEAR_SCALE *
ORIGINAL_CAMERA_SIZE *
@ -650,24 +674,16 @@ export class SpatialScene {
return Math.max(0.01, near);
}
private _resetCameraColor(imageId: string): void {
const nceMap = this._imageCellMap;
if (imageId == null || !nceMap.has(imageId)) { return; }
const cellId = nceMap.get(imageId);
const cell = this._images[cellId];
const isManual = isModeManual(this._cameraVisualizationMode);
if (isManual) {
const clusterId = cell.getCluster(imageId);
const color = this._manualColors.get(clusterId) ??
SPATIAL_DEFAULT_MANUAL_COLOR;
cell.applyCameraColor(imageId, color);
} else {
const colorId = cell.getColorId(imageId, this._cameraVisualizationMode);
const color = this._assets.getColor(colorId);
cell.applyCameraColor(imageId, color);
private _getPointColor(clusterId: string): number | string | null {
let color: number | string = null;
if (this._pointVisualizationMode === PointVisualizationMode.Cluster) {
color = this._assets.getColor(clusterId);
}
if (this._pointOverrideColors.has(clusterId)) {
color = this._pointOverrideColors.get(clusterId);
}
return color;
}
private _highlight(
@ -675,10 +691,34 @@ export class SpatialScene {
color: string | number,
mode: CameraVisualizationMode): void {
const nceMap = this._imageCellMap;
if (imageId == null || !nceMap.has(imageId)) { return; }
if (imageId == null || !nceMap.has(imageId)) {
return;
}
const cellId = nceMap.get(imageId);
color = mode === CameraVisualizationMode.Homogeneous ?
color : SPATIAL_DEFAULT_MANUAL_COLOR;
const cell = this._images[cellId];
const clusterId = cell.getCluster(imageId);
const overridden = this._cameraOverrideColors.get(clusterId);
color = mode === CameraVisualizationMode.Homogeneous && !overridden ?
color : SPATIAL_DEFAULT_COLOR;
this._images[cellId].applyCameraColor(imageId, color);
}
private _resetCameraColor(imageId: string): void {
const nceMap = this._imageCellMap;
if (imageId == null || !nceMap.has(imageId)) { return; }
const cellId = nceMap.get(imageId);
const cell = this._images[cellId];
const colorId = cell.getColorId(imageId, this._cameraVisualizationMode);
let color: number | string = this._assets.getColor(colorId);
const clusterId = cell.getCluster(imageId);
if (this._cameraOverrideColors.has(clusterId)) {
color = this._cameraOverrideColors.get(clusterId);
}
cell.applyCameraColor(imageId, color);
}
}

View File

@ -26,14 +26,4 @@ export enum CameraVisualizationMode {
* their sequence.
*/
Sequence,
/**
* Manually paint the camera frustums of each cluster
* with a specific color.
*
* @description If no color has been specified for a
* visualized cluster, the cluster will be shown with
* a default color.
*/
Manual,
}

View File

@ -14,14 +14,4 @@ export enum PointVisualizationMode {
* cluster with the same random color.
*/
Cluster,
/**
* Manually paint the points of each cluster with a
* specific color.
*
* @description If no color has been specified for a
* visualized cluster, the cluster will be shown with
* a default color.
*/
Manual,
}

View File

@ -41,7 +41,7 @@ export class ClusterPoints extends Points {
this.material.dispose();
}
public setColor(color?: string | number): void {
public setColor(color: string | number | null): void {
this.material.vertexColors = color == null;
this.material.color = new Color(color);
this.material.needsUpdate = true;

View File

@ -1,7 +1,7 @@
import { CameraVisualizationMode } from "../enums/CameraVisualizationMode";
export class SpatialAssets {
private readonly _colors: Map<string, string>;
private readonly _colors: Map<string, number | string>;
constructor() {
this._colors = new Map();
@ -9,13 +9,15 @@ export class SpatialAssets {
this._colors.set(cvm[cvm.Homogeneous], "#FFFFFF");
}
public getColor(id: string): string {
public getColor(id: string): number | string {
const colors = this._colors;
if (!colors.has(id)) { colors.set(id, this._randomColor()); }
if (!colors.has(id)) {
colors.set(id, this._randomColor());
}
return colors.get(id);
}
private _randomColor(): string {
private _randomColor(): number | string {
return `hsl(${Math.floor(360 * Math.random())}, 100%, 60%)`;
}
}

View File

@ -15,9 +15,14 @@ import { isSpherical } from "../../../geo/Geo";
import { SphericalCameraFrame } from "./SphericalCameraFrame";
import { PerspectiveCameraFrame } from "./PerspectiveCameraFrame";
import { LngLatAlt } from "../../../api/interfaces/LngLatAlt";
import { resetEnu, SPATIAL_DEFAULT_MANUAL_COLOR } from "../SpatialCommon";
import { resetEnu, SPATIAL_DEFAULT_COLOR } from "../SpatialCommon";
type ColorIdCamerasMap = Map<string, CameraFrameBase[]>;
type IDCamera = {
camera: CameraFrameBase,
clusterId: string,
};
type ColorIdCamerasMap = Map<string, IDCamera[]>;
type ImageIdMap = {
ccId: string;
@ -41,6 +46,8 @@ type ImageVisualizationProps = {
positionMode: OriginalPositionMode;
};
const DEFAULT_ID = CameraVisualizationMode[CameraVisualizationMode.Homogeneous];
export class SpatialCell {
public readonly cameras: Object3D;
public readonly keys: string[];
@ -51,6 +58,7 @@ export class SpatialCell {
private readonly _cameraFrames: { [key: string]: CameraFrameBase; };
private readonly _clusters: ColorIdCamerasMap;
private readonly _connectedComponents: ColorIdCamerasMap;
private readonly _defaults: ColorIdCamerasMap;
private readonly _sequences: ColorIdCamerasMap;
private readonly _props: {
[id: string]: {
@ -73,9 +81,12 @@ export class SpatialCell {
this._positions = new Object3D();
this._cameraFrames = {};
this._clusters = new Map();
this._connectedComponents = new Map();
this._defaults = new Map();
this._sequences = new Map();
this._props = {};
this.clusterVisibles = {};
@ -94,15 +105,29 @@ export class SpatialCell {
public addImage(props: ImageProps): void {
const image = props.image;
const id = image.id;
if (this.hasImage(id)) { throw new Error(`Image exists ${id}`); }
if (this.hasImage(id)) {
throw new Error(`Image exists ${id}`);
}
const cId = props.idMap.clusterId;
if (!this._clusters.has(cId)) {
this._clusters.set(cId, []);
}
const ccId = props.idMap.ccId;
if (!(this._connectedComponents.has(ccId))) {
this._connectedComponents.set(ccId, []);
}
const cId = props.idMap.clusterId;
if (!this._clusters.has(cId)) { this._clusters.set(cId, []); }
if (!(this._defaults.has(DEFAULT_ID))) {
this._defaults.set(DEFAULT_ID, []);
}
const sId = props.idMap.sequenceId;
if (!this._sequences.has(sId)) { this._sequences.set(sId, []); }
if (!this._sequences.has(sId)) {
this._sequences.set(sId, []);
}
this._props[id] = {
image: image,
ids: { ccId, clusterId: cId, sequenceId: sId },
@ -114,19 +139,6 @@ export class SpatialCell {
this._cameraFrames[imageId].setColor(color);
}
public applyColorMap(colors: Map<string, number | string>): void {
const frames = this._cameraFrames;
const props = this._props;
for (const imageId in frames) {
if (!frames.hasOwnProperty(imageId)) { continue; }
const frame = frames[imageId];
const clusterId = props[imageId].ids.clusterId;
const color = colors.get(clusterId) ?? SPATIAL_DEFAULT_MANUAL_COLOR;
frame.setColor(color);
}
}
public applyCameraSize(size: number): void {
for (const camera of this.cameras.children) {
(<CameraFrameBase>camera).resize(size);
@ -170,20 +182,17 @@ export class SpatialCell {
this._intersection = null;
}
public getCamerasByMode(
mode: CameraVisualizationMode): ColorIdCamerasMap {
if (mode === CameraVisualizationMode.Cluster) {
return this._clusters;
} else if (mode === CameraVisualizationMode.ConnectedComponent) {
return this._connectedComponents;
} else if (mode === CameraVisualizationMode.Sequence) {
return this._sequences;
public getCamerasByMode(mode: CameraVisualizationMode): ColorIdCamerasMap {
switch (mode) {
case CameraVisualizationMode.Cluster:
return this._clusters;
case CameraVisualizationMode.ConnectedComponent:
return this._connectedComponents;
case CameraVisualizationMode.Sequence:
return this._sequences;
default:
return this._defaults;
}
const cvm = CameraVisualizationMode;
const defaultId = cvm[cvm.Homogeneous];
const cameras = <ColorIdCamerasMap>new Map();
cameras.set(defaultId, <CameraFrameBase[]>this.cameras.children);
return cameras;
}
public getColorId(imageId: string, mode: CameraVisualizationMode): string {
@ -197,7 +206,7 @@ export class SpatialCell {
case cvm.Sequence:
return props.ids.sequenceId;
default:
return cvm[cvm.Homogeneous];
return DEFAULT_ID;
}
}
@ -266,9 +275,11 @@ export class SpatialCell {
const ids = this._props[id].ids;
this.clusterVisibles[ids.clusterId] ||= visible;
this._connectedComponents.get(ids.ccId).push(camera);
this._clusters.get(ids.clusterId).push(camera);
this._sequences.get(ids.sequenceId).push(camera);
const idCamera = { camera, clusterId: ids.clusterId };
this._clusters.get(ids.clusterId).push(idCamera);
this._connectedComponents.get(ids.ccId).push(idCamera);
this._defaults.get(DEFAULT_ID).push(idCamera);
this._sequences.get(ids.sequenceId).push(idCamera);
const positionParameters: PositionLineParameters = {
material: this._positionMaterial,