diff --git a/tensorflow_similarity/visualization/projector_v2/.gitignore b/tensorflow_similarity/visualization/projector_v2/.gitignore deleted file mode 100644 index 8901c5b4..00000000 --- a/tensorflow_similarity/visualization/projector_v2/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -bin -node_modules diff --git a/tensorflow_similarity/visualization/projector_v2/README.md b/tensorflow_similarity/visualization/projector_v2/README.md deleted file mode 100644 index 8e324175..00000000 --- a/tensorflow_similarity/visualization/projector_v2/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# Experimental projector v2 - -This is an experimental embedding projector that is custom written for more UI affordances. - -## Instructions - -Under construction. - -To build the frontend bundle, do: -```bash -# Install dependencies -$ yarn -# Build bundle using esbuild -$ yarn dev -# Type check using tsc -$ yarn typecheck -# Use prettier to autoformat TypeScript -$ yarn fix -``` diff --git a/tensorflow_similarity/visualization/projector_v2/index.ts b/tensorflow_similarity/visualization/projector_v2/index.ts deleted file mode 100644 index 39c295d2..00000000 --- a/tensorflow_similarity/visualization/projector_v2/index.ts +++ /dev/null @@ -1,52 +0,0 @@ -/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -==============================================================================*/ - -/** - * Entry point for rendering the embedding projector. - */ -import {GlobalMessenger, getCellMessenger} from './lib/ipc'; -import {createElement, updateStyles} from './lib/renderer'; -import {Header} from './views/header'; -import {Projector} from './views/projector'; - -declare namespace globalThis { - let messenger: GlobalMessenger|undefined; - let bootstrap: ((domId: string) => void|undefined); -} - -function bootstrap(domId: string) { - const messenger = getCellMessenger(); - const main = updateStyles( - createElement('div', null, [ - new Header(messenger).getDomElement(), - new Projector(messenger).getDomElement(), - ]), - { - display: 'grid', - height: '100%', - width: '100%', - } - ); - - const bootstrapableDom = document.getElementById(domId); - - if (!bootstrapableDom) { - throw new Error("Expected to be bootstrapped with a container created by the widget."); - } - bootstrapableDom.appendChild(main); -} - -globalThis.messenger = globalThis.messenger || new GlobalMessenger(); -globalThis.bootstrap = globalThis.bootstrap || bootstrap; diff --git a/tensorflow_similarity/visualization/projector_v2/lib/ipc.ts b/tensorflow_similarity/visualization/projector_v2/lib/ipc.ts deleted file mode 100644 index 94c568bf..00000000 --- a/tensorflow_similarity/visualization/projector_v2/lib/ipc.ts +++ /dev/null @@ -1,157 +0,0 @@ -/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -==============================================================================*/ - -import {InboundMessageType, Message, OutboundMessageType} from './types'; - -let _cellMessenger: MessengerImpl; - -export class GlobalMessenger { - private tx?: (cellId: string, eventType: string) => void; - private rx?: (varNames: string[]) => Promise; - private messengers = new Map(); - - // Colab will listen to the event from the `dom` to receive a message from TS. - initForColab(dom: HTMLElement) { - this.tx = (outputCellId: string, eventType: string) => { - dom.dispatchEvent(new CustomEvent(eventType)); - }; - this.rx = async (dataGlobalVarNames: string[]) => { - const data = []; - const anyThis = globalThis as any; - for (const varWithUrl of dataGlobalVarNames) { - const url = anyThis[varWithUrl]; - const response = await fetch(url); - const obj = await response.json(); - data.push(...obj); - - URL.revokeObjectURL(url); - delete anyThis[varWithUrl]; - } - return data; - }; - } - - initForIPython(portNumber: number) { - if (this.tx && this.rx) return; - - this.tx = (outputCellId: string, eventType: string) => { - fetch(`${location.protocol}//${location.hostname}:${portNumber}`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - mode: 'cors', - body: JSON.stringify({eventType, cellId: outputCellId}), - }); - }; - this.rx = async (dataGlobalVarNames) => { - const data = []; - const anyThis = globalThis as any; - for (const varWithData of dataGlobalVarNames) { - if (Array.isArray(anyThis[varWithData])) { - data.push(...anyThis[varWithData]); - } - - delete anyThis[varWithData]; - } - return data; - }; - } - - createMessengerForOutputcell(id: string) { - _cellMessenger = new MessengerImpl((type: OutboundMessageType) => - this.sendMessage(id, type) - ); - this.messengers.set(id, _cellMessenger); - } - - private sendMessage(outputCellId: string, type: OutboundMessageType) { - if (!this.tx) { - throw new Error( - 'Cannot send a message before Notebook has bootstrapped the module' - ); - } - this.tx(outputCellId, type); - } - - async onMessageFromPython( - cellId: string, - type: InboundMessageType, - dataGlobalVarNames: string[], - otherPayload: any - ) { - if (!this.rx) { - throw new Error(''); - } - const mainPayload = (await this.rx(dataGlobalVarNames)) ?? []; - - let message = null; - switch (type) { - case InboundMessageType.UPDATE: { - message = { - type, - mainPayload, - auxPayload: otherPayload, - }; - break; - } - case InboundMessageType.METADATA: { - message = { - type, - mainPayload, - auxPayload: otherPayload, - }; - break; - } - default: - throw new RangeError(`Unknown message type: ${type}`); - } - - if (message) { - this.messengers.get(cellId).invokeCallbacks(message); - } - } -} - -export interface Messenger { - sendMessage(type: OutboundMessageType): void; - - addOnMessage(cb: (message: Message) => void): void; -} - -class MessengerImpl implements Messenger { - private readonly callbacks: Array<(message: Message) => void> = []; - constructor( - private readonly sendMessageImpl: (type: OutboundMessageType) => void - ) {} - - sendMessage(type: OutboundMessageType) { - this.sendMessageImpl(type); - } - - addOnMessage(cb: (message: Message) => void) { - this.callbacks.push(cb); - } - - invokeCallbacks(message: Message) { - for (const callback of this.callbacks) { - callback(message); - } - } -} - -export function getCellMessenger() { - return _cellMessenger; -} diff --git a/tensorflow_similarity/visualization/projector_v2/lib/renderer.ts b/tensorflow_similarity/visualization/projector_v2/lib/renderer.ts deleted file mode 100644 index 7427c4ad..00000000 --- a/tensorflow_similarity/visualization/projector_v2/lib/renderer.ts +++ /dev/null @@ -1,87 +0,0 @@ -/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -==============================================================================*/ - -interface Listeners { - onPassiveClick: (event: MouseEvent) => void; - onPassiveMouseMove: (event: MouseEvent) => void; - onChange: (event: Event) => void; -} - -/** - * Possible props to the createElement. - */ -export type Props = Exclude< - { - [prop: string]: string | Function; - }, - keyof Listeners -> & - Listeners; - -/** - * Basic element renderer that mimics API of React without JSX. - */ -export function createElement( - tagName: string, - props: Partial | null = null, - children: Array = [] -) { - const element = document.createElement(tagName); - - if (props !== null) { - for (const [key, value] of Object.entries(props)) { - switch (key) { - case 'onPassiveClick': - element.addEventListener('click', props.onPassiveClick!, { - passive: true, - }); - break; - case 'onPassiveMouseMove': - element.addEventListener('mousemove', props.onPassiveMouseMove!, { - passive: true, - }); - break; - case 'onChange': - element.addEventListener('change', props.onChange!); - break; - default: - if (typeof value === 'string') { - element.setAttribute(key, value); - } else { - throw new RangeError(`Callback for ${key} is not implemented yet.`); - } - } - } - } - - for (const child of children) { - const el = - child instanceof HTMLElement ? child : document.createTextNode(child); - element.appendChild(el); - } - - return element; -} - -/** - * Applies style object onto the passed `element`. - */ -export function updateStyles( - element: HTMLElement, - styleObject: Partial -): HTMLElement { - Object.assign(element.style, styleObject); - return element; -} diff --git a/tensorflow_similarity/visualization/projector_v2/lib/types.ts b/tensorflow_similarity/visualization/projector_v2/lib/types.ts deleted file mode 100644 index 4286acd6..00000000 --- a/tensorflow_similarity/visualization/projector_v2/lib/types.ts +++ /dev/null @@ -1,58 +0,0 @@ -/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -==============================================================================*/ - -export enum OutboundMessageType { - STOP = 'stop', -} - -export enum InboundMessageType { - UPDATE = 'update', - METADATA = 'meta', -} - -interface UpdatePayload { - step: number; - maxStep: number; -} - -interface MetaPayload { - algo: string; -} - -export interface UpdateMessage { - type: InboundMessageType.UPDATE; - mainPayload: Point[]; - auxPayload: UpdatePayload; -} - -export interface MetadataMessage { - type: InboundMessageType.METADATA; - mainPayload: Metadata[]; - auxPayload: MetaPayload; -} - -export type Message = UpdateMessage | MetadataMessage; - -// [x, y] or [x, y, z]. -export type Point = [number, number] | [number, number, number]; - -// Metadata for each embedding point. It is static across dim reduction step. -export interface Metadata { - label: string; - // Data URI. - imageLabel?: string; - color: number; -} - diff --git a/tensorflow_similarity/visualization/projector_v2/package.json b/tensorflow_similarity/visualization/projector_v2/package.json deleted file mode 100644 index 75175894..00000000 --- a/tensorflow_similarity/visualization/projector_v2/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "scripts": { - "dev": "esbuild index.ts --bundle --watch --sourcemap=inline --outfile=bin/index.js", - "typecheck": "tsc --noEmit", - "release": "esbuild index.ts --minify --bundle --outfile=bin/index.js", - "fix": "prettier --write **/*.{js,json}" - }, - "devDependencies": { - "@types/three": "^0.131.0", - "esbuild": "^0.12.26", - "prettier": "2.4.0", - "tslib": "^2.3.1", - "typescript": "^4.4.3" - }, - "dependencies": { - "three": "^0.137.0" - }, - "license": "MIT" -} diff --git a/tensorflow_similarity/visualization/projector_v2/tsconfig.json b/tensorflow_similarity/visualization/projector_v2/tsconfig.json deleted file mode 100644 index 956f8e0f..00000000 --- a/tensorflow_similarity/visualization/projector_v2/tsconfig.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "compilerOptions": { - "downlevelIteration": true, - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "importHelpers": true, - "inlineSourceMap": true, - "moduleResolution": "node", - "noFallthroughCasesInSwitch": true, - "noImplicitReturns": true, - "strict": true, - "lib": ["dom", "es2020"] - } -} diff --git a/tensorflow_similarity/visualization/projector_v2/views/header.ts b/tensorflow_similarity/visualization/projector_v2/views/header.ts deleted file mode 100644 index b9a3d4d6..00000000 --- a/tensorflow_similarity/visualization/projector_v2/views/header.ts +++ /dev/null @@ -1,111 +0,0 @@ -/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -==============================================================================*/ - -import {Messenger} from '../lib/ipc'; -import {createElement, updateStyles} from '../lib/renderer'; -import {InboundMessageType, Message, OutboundMessageType} from '../lib/types'; - -export class Header { - private stepTime: number = Date.now(); - private readonly numberFormatter = new Intl.NumberFormat(); - private readonly rtFormatter = new (Intl as any).RelativeTimeFormat(); - private readonly step = createElement('div', null, ['Unknown / Unknown']); - private readonly runningTime = updateStyles(createElement('div', null), { - fontSize: '0.85em', - }); - - private timeoutId: number = -1; - private readonly container = updateStyles( - createElement('div', null, [ - this.step, - this.runningTime, - // spacer. - createElement('div'), - createElement( - 'button', - { - onPassiveClick: () => { - this.messenger.sendMessage(OutboundMessageType.STOP); - }, - }, - ['Stop'] - ), - ]), - { - alignItems: 'center', - backgroundColor: '#333e', - color: '#fff', - display: 'grid', - gridTemplateColumns: 'auto auto auto 1fr auto', - columnGap: '10px', - height: '30px', - padding: '0 10px', - } - ); - - constructor(private readonly messenger: Messenger) { - messenger.addOnMessage((message: Message) => { - switch (message.type) { - case InboundMessageType.UPDATE: { - this.updateSteps(message.auxPayload.step, message.auxPayload.maxStep); - break; - } - } - }); - this.updateRunningTime(); - } - - private updateRunningTime() { - this.timeoutId = setTimeout(() => { - let diff = (Date.now() - this.stepTime) / 1000; - let unit = 'second'; - if (diff > 60) { - diff /= 60; - unit = 'minute'; - } - if (diff > 60) { - diff /= 60; - unit = 'hour'; - } - if (diff > 24) { - diff /= 24; - unit = 'day'; - } - this.runningTime.textContent = this.rtFormatter.format( - -Math.round(diff), - unit - ); - this.updateRunningTime(); - }, 500); - } - - getDomElement(): HTMLElement { - return this.container; - } - - private updateSteps(stepNumber: number, maxSteps: number): void { - const formattedStepNumber = this.numberFormatter.format(stepNumber); - const formattedMaxSteps = this.numberFormatter.format(maxSteps); - this.step.textContent = `${formattedStepNumber} / ${formattedMaxSteps}`; - - if (stepNumber >= maxSteps) { - clearTimeout(this.timeoutId); - this.runningTime.textContent = 'Finished'; - } else { - this.stepTime = Date.now(); - this.runningTime.textContent = 'moments ago'; - } - } -} diff --git a/tensorflow_similarity/visualization/projector_v2/views/projector.ts b/tensorflow_similarity/visualization/projector_v2/views/projector.ts deleted file mode 100644 index f5de2941..00000000 --- a/tensorflow_similarity/visualization/projector_v2/views/projector.ts +++ /dev/null @@ -1,304 +0,0 @@ -/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -==============================================================================*/ - -/** - * Renders embedding visualization in a 3d scene. - */ - -import * as THREE from 'three'; -import {OrbitControls} from 'three/examples/jsm/controls/OrbitControls'; - -import {Messenger} from '../lib/ipc'; -import {createElement, updateStyles} from '../lib/renderer'; -import {InboundMessageType, Message, Metadata, Point} from '../lib/types'; -import {SettingsPanel} from './settings'; - -const HOVERED_COLOR = new THREE.Color(0xff0000); -const DEFAULT_OPACITY = 0.6; -const anyWindow = window; - -export class Projector { - private readonly settings = new SettingsPanel(); - private readonly renderer = new THREE.WebGLRenderer(); - private readonly scene = new THREE.Scene(); - private readonly pointGroup = new THREE.Group(); - private camera?: THREE.Camera; - private control?: OrbitControls; - private readonly raycaster = new THREE.Raycaster(); - - private metadata: Metadata[] = []; - private readonly objectToIndex = new WeakMap(); - private readonly container: HTMLElement; - private readonly labelImg = createElement('div'); - private readonly labelText = createElement('div'); - private readonly labelContainer = createElement('div', null, [ - this.labelText, - this.labelImg, - ]); - private containerRect?: DOMRect; - - private hoveredMesh: THREE.Mesh | null = null; - - constructor(messenger: Messenger) { - messenger.addOnMessage((message: Message) => { - switch (message.type) { - case InboundMessageType.UPDATE: { - const updateMessage = message; - this.updatePoints(updateMessage.mainPayload); - break; - } - case InboundMessageType.METADATA: { - const metadataMessage = message; - this.setMetadata(metadataMessage.mainPayload); - break; - } - } - }); - - const {canvas, container} = this.setupDom(); - this.container = container; - - this.renderer.setClearColor(0xffffff, 1); - this.renderer.setPixelRatio(window.devicePixelRatio); - - this.scene.add(this.pointGroup); - this.scene.add(new THREE.AxesHelper(Number.MAX_SAFE_INTEGER)); - - this.settings.addSettingChangeListener((prev, next) => { - if (!prev || prev.threeDimensions !== next.threeDimensions) { - if (this.camera) { - this.scene.remove(this.camera); - } - - const {width, height} = container.getBoundingClientRect(); - if (next.threeDimensions) { - this.camera = new THREE.PerspectiveCamera( - 55, - width / height, - 1, - 20000 - ); - this.camera.position.set(30, 30, 100); - } else { - this.camera = new THREE.OrthographicCamera( - -width / 2, - width / 2, - height / 2, - -height / 2, - -Number.MAX_SAFE_INTEGER, - Number.MAX_SAFE_INTEGER - ); - } - this.scene.add(this.camera); - - if (!this.control) { - this.control = new OrbitControls(this.camera, canvas); - } else { - this.control.object = this.camera; - } - } - }); - - anyWindow.requestIdleCallback(() => this.animate()); - } - - private setupDom(): { - canvas: HTMLCanvasElement; - container: HTMLElement; - } { - const canvas = this.renderer.domElement; - updateStyles(this.labelContainer, { - position: 'absolute', - pointerEvents: 'none', - willChange: 'top, left', - } as Partial); - updateStyles(this.labelImg, { - width: '300px', - height: '200px', - backgroundSize: 'contain', - backgroundRepeat: 'no-repeat', - }); - - const container = createElement('div', null, [ - this.renderer.domElement, - this.labelContainer, - this.settings.getDomElement(), - ]); - updateStyles(container, { - height: '100%', - overflow: 'hidden', - position: 'relative', - width: '100%', - }); - - const resizeObserver = new anyWindow.ResizeObserver(() => { - this.containerRect = container.getBoundingClientRect(); - const {width, height} = this.containerRect; - this.renderer.setSize(width, height); - - if (this.camera instanceof THREE.PerspectiveCamera) { - this.camera.aspect = width / height; - this.camera.updateProjectionMatrix(); - this.renderer.setSize(width, height); - } - }); - resizeObserver.observe(container); - - canvas.addEventListener('mousemove', (event) => this.onMouseMove(event), { - passive: true, - }); - - return {canvas, container}; - } - - getDomElement(): HTMLElement { - return this.container; - } - - private updatePoints(points: Point[]): void { - const existingChildren = [...this.pointGroup.children] as THREE.Mesh[]; - - existingChildren - .slice(points.length) - .forEach((obj) => this.pointGroup.remove(obj)); - - for (const [index, point] of points.entries()) { - let mesh = existingChildren[index]; - const x = point[0]; - const y = point[1]; - const z = point[2] ?? 0; - const meta = this.metadata[index]; - - if (mesh) { - mesh.position.set(x, y, z); - } else { - const geom = new THREE.SphereBufferGeometry(1, 32, 32); - const material = new THREE.MeshBasicMaterial({transparent: true}); - mesh = new THREE.Mesh(geom, material); - mesh.position.set(x, y, z); - this.updateColor( - mesh, - new THREE.Color(meta?.color ?? '#f00'), - DEFAULT_OPACITY - ); - this.pointGroup.add(mesh); - } - - this.objectToIndex.set(mesh, index); - } - } - - private setMetadata(metadata: Metadata[]) { - this.metadata = metadata; - } - - private animate() { - requestAnimationFrame(() => this.animate()); - if (!this.camera || !this.control) return; - this.control.update(); - this.updateLabel(); - this.renderer.render(this.scene, this.camera); - } - - private updateColor( - mesh: THREE.Mesh, - color: THREE.Color, - opacity: number - ): void { - const material = mesh.material; - if (material instanceof THREE.MeshBasicMaterial) { - material.color = color; - material.opacity = opacity; - material.needsUpdate = true; - } - } - - private updateLabel() { - const pointIndex = this.hoveredMesh - ? this.objectToIndex.get(this.hoveredMesh) ?? -1 - : null; - if ( - !this.containerRect || - !this.hoveredMesh || - pointIndex == null || - !this.camera - ) { - updateStyles(this.labelContainer, {display: 'none'}); - return; - } - - const metadata = this.metadata[pointIndex] || {label: 'Unknown'}; - const target = this.hoveredMesh.position.clone(); - target.project(this.camera); - const {width, height} = this.containerRect; - const x = (target.x * width) / 2 + width / 2; - const y = -((target.y * height) / 2) + height / 2; - this.labelText.textContent = metadata.label; - - if (metadata.imageLabel) { - updateStyles(this.labelImg, { - backgroundImage: `url(${metadata.imageLabel})`, - }); - } - - updateStyles(this.labelContainer, { - display: 'block', - fontSize: '1.5em', - left: String(x + 5) + 'px', - top: String(y + 5) + 'px', - }); - } - - private onMouseMove(event: MouseEvent) { - // Raycaster expects the mouse to be a number between -1 and 1. - if (!this.containerRect || !this.camera) return; - const clientXInDom = event.clientX - this.containerRect.x; - const clientYInDom = event.clientY - this.containerRect.y; - const mouse = new THREE.Vector2( - (clientXInDom / this.containerRect.width) * 2 - 1, - -(clientYInDom / this.containerRect.height) * 2 + 1 - ); - - this.raycaster.setFromCamera(mouse, this.camera); - - // Intersections pick up things like AxesHelper. - const intersects = this.raycaster.intersectObjects( - this.pointGroup.children - ); - const firstMesh = intersects.find( - (intersection) => intersection.object instanceof THREE.Mesh - ); - - if (this.hoveredMesh === firstMesh?.object) return; - if (this.hoveredMesh) { - const pointIndex = this.objectToIndex.get(this.hoveredMesh) ?? -1; - const metadata = this.metadata[pointIndex]; - if (metadata) { - this.updateColor( - this.hoveredMesh, - new THREE.Color(metadata.color ?? '#f00'), - DEFAULT_OPACITY - ); - } - } - - if (firstMesh) { - this.hoveredMesh = firstMesh.object as THREE.Mesh; - this.updateColor(this.hoveredMesh, HOVERED_COLOR, 1); - } else { - this.hoveredMesh = null; - } - } -} diff --git a/tensorflow_similarity/visualization/projector_v2/views/settings.ts b/tensorflow_similarity/visualization/projector_v2/views/settings.ts deleted file mode 100644 index c83a170e..00000000 --- a/tensorflow_similarity/visualization/projector_v2/views/settings.ts +++ /dev/null @@ -1,94 +0,0 @@ -/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -==============================================================================*/ - -import {createElement, updateStyles} from '../lib/renderer'; - -export interface Settings { - threeDimensions: boolean; -} - -const DEFAULT_SETTINGS: Settings = { - threeDimensions: true, -}; - -export class SettingsPanel { - private readonly settings = DEFAULT_SETTINGS; - private readonly listeners = new Set< - (prev: Settings | null, next: Settings) => void - >(); - - private readonly toggleThreeDimensions = createElement('input', { - id: 'three-dim', - type: 'checkbox', - checked: this.settings.threeDimensions ? 'true' : 'false', - onChange: (event) => this.onDimensionChange(event), - }); - - private readonly container = updateStyles( - createElement('div', null, [ - updateStyles( - createElement('ul', null, [ - updateStyles( - createElement('li', null, [ - this.toggleThreeDimensions, - createElement('label', {for: 'three-dim'}, ['3D']), - ]), - { - display: 'flex', - padding: '3px 5px', - } - ), - ]), - { - listStyleType: 'none', - margin: '0', - padding: '0', - } - ), - ]), - { - backgroundColor: '#333e', - color: '#fff', - minWidth: '100px', - position: 'absolute', - right: '0', - top: '0', - } - ); - - addSettingChangeListener( - cb: (prev: Settings | null, next: Settings) => void - ) { - this.listeners.add(cb); - setTimeout(() => cb(null, this.settings), 0); - } - - getDomElement(): HTMLElement { - return this.container; - } - - private onDimensionChange(event: Event): void { - const checkbox = event.target as HTMLInputElement; - this.setSettingsAndNotify({threeDimensions: checkbox.checked}); - } - - private setSettingsAndNotify(settings: Partial) { - const prevSetting = {...this.settings}; - Object.assign(this.settings, settings); - for (const cb of this.listeners) { - cb(prevSetting, this.settings); - } - } -} diff --git a/tensorflow_similarity/visualization/projector_v2/yarn.lock b/tensorflow_similarity/visualization/projector_v2/yarn.lock deleted file mode 100644 index 1fb1a8e9..00000000 --- a/tensorflow_similarity/visualization/projector_v2/yarn.lock +++ /dev/null @@ -1,33 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@types/three@^0.131.0": - version "0.131.0" - resolved "https://registry.yarnpkg.com/@types/three/-/three-0.131.0.tgz#98f59887b05f71e665cf2fbdf04e0e4ceb9f89b6" - integrity sha512-4VCtsDi6mIId96GcGKG91e2Y6VwU2T0u/YB7vCFJh1kXik93arxn7l9tVZHo1LXOtgCJJDdC+e1fwf2Vu/4ySw== - -esbuild@^0.12.26: - version "0.12.26" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.12.26.tgz#35f2d58ac3fa4629df24aa4d6fd72feb5522e94b" - integrity sha512-YmTkhPKjvTJ+G5e96NyhGf69bP+hzO0DscqaVJTi5GM34uaD4Ecj7omu5lJO+NrxCUBRhy2chONLK1h/2LwoXA== - -prettier@2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.4.0.tgz#85bdfe0f70c3e777cf13a4ffff39713ca6f64cba" - integrity sha512-DsEPLY1dE5HF3BxCRBmD4uYZ+5DCbvatnolqTqcxEgKVZnL2kUfyu7b8pPQ5+hTBkdhU9SLUmK0/pHb07RE4WQ== - -three@^0.137.0: - version "0.137.0" - resolved "https://registry.yarnpkg.com/three/-/three-0.137.0.tgz#0ebd6ba66637a332c31f234bcdd35aeec071a6e3" - integrity sha512-rzSDhia6cU35UCy6y+zEEws6vSgytfHqFMSaBvUcySgzwvDO6vETyswtSNi/+aVqJw8WLMwyT1mlQQ1T/dxxOA== - -tslib@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" - integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== - -typescript@^4.4.3: - version "4.4.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.3.tgz#bdc5407caa2b109efd4f82fe130656f977a29324" - integrity sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA==