Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ Here is a summary of the steps to cut a new release:
- If the repo generates PyPI release(s), create a scoped PyPI [token](https://packaging.python.org/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/#saving-credentials-on-github). We recommend using a scoped token for security reasons.

- You can store the token as `PYPI_TOKEN` in your fork's `Secrets`.

- Advanced usage: if you are releasing multiple repos, you can create a secret named `PYPI_TOKEN_MAP` instead of `PYPI_TOKEN` that is formatted as follows:

```text
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@
"three": "^0.162.0",
"ts-xor": "^1.3.0",
"unique-names-generator": "^4.7.1",
"use-file-picker": "^2.1.2"
"use-file-picker": "^2.1.2",
"zustand": "^4.5.2"
},
"devDependencies": {
"@jupyterlab/builder": "^4.0.0",
Expand Down
128 changes: 45 additions & 83 deletions src/arCube.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import {
selectAppData,
selectIsSomeoneScreenSharing
} from '@100mslive/react-sdk';
import { selectIsSomeoneScreenSharing } from '@100mslive/react-sdk';
//@ts-expect-error AR.js doesn't have type definitions
import * as THREEx from '@ar-js-org/ar.js/three.js/build/ar-threex.js';
import { IThemeManager } from '@jupyterlab/apputils';
Expand All @@ -10,9 +7,9 @@ import { ISignal, Signal } from '@lumino/signaling';
import * as THREE from 'three';
import { RoundedBoxGeometry } from 'three/examples/jsm/geometries/RoundedBoxGeometry';
import { GLTF, GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { APP_DATA } from './constants';
import { hmsActions, hmsStore } from './hms';
import { hmsStore } from './hms';
import { IModelRegistryData } from './registry';
import { useCubeStore } from './store';

const FIRST_SCENE = 0;
const SECOND_SCENE = 1;
Expand All @@ -29,19 +26,9 @@ class ArCube {
* Construct a new JupyterLab-Gather widget.
*/
constructor() {
this.secondSceneSignal = new Signal(this);
this.scaleSignal = new Signal(this);
this.bgCubeCenter = new THREE.Vector3();
this.initialize();

// this.animate();
// window.addEventListener('markerFound', () => {
// console.log('Marker found');
// });

// window.addEventListener('markerLost', () => {
// console.log('Marker lost');
// });
}

modelInScene: string[];
Expand All @@ -66,59 +53,45 @@ class ArCube {
resolve: any;
deltaTime: number;
totalTime: number;
okToLoadModel: boolean;
renderTarget: THREE.WebGLRenderTarget;
sceneGroups: THREE.Group[];
isSecondScene: boolean;
bgCubeBoundingBox: THREE.Box3;
readonly existingWebcam: HTMLVideoElement | null;
readonly newWebcam: HTMLVideoElement | undefined;
readonly secondSceneSignal: Signal<this, boolean>;
readonly scaleSignal: Signal<this, IScaleSignal>;
bgCubeCenter: THREE.Vector3;
arjsVid: HTMLElement | null;
// sceneGroup: THREE.Group;
// sceneGroupArray: THREE.Group[];
// edgeGroup: THREE.Group;
// gltfModel: THREE.Group;
// observer: IntersectionObserver;
// readonly markerControls: any;
// readonly ambientLight: THREE.AmbientLight;
// readonly rotationArray: THREE.Vector3[];
// readonly markerRoot: THREE.Group;
// readonly markerGroup: THREE.Group;
// readonly pointLight: THREE.PointLight;
// readonly loader: THREE.TextureLoader;
// readonly stageMesh: THREE.MeshBasicMaterial;
// readonly stage: THREE.Mesh;
// readonly edgeGeometry: THREE.CylinderGeometry;
// readonly edgeCenters: THREE.Vector3[];
// readonly edgeRotations: THREE.Vector3[];
// readonly animationRequestId: number | undefined;
// readonly now: number;
// readonly then: number;
// readonly elapsed: number;
// readonly fpsInterval: number;
// readonly webcamFromArjs: HTMLElement | null;
// model: IModelRegistryData;
videoDeviceIdUnsub: () => void;
isSecondSceneUnsub: () => void;
themeChangedSignal: ISignal<
IThemeManager,
IChangedArgs<string, string | null>
>;
> | null;

initialize() {
this.sceneGroups = [];
this.modelInScene = new Array(2);
this.scenesWithModel = {};
hmsStore.subscribe(
this.setupSource.bind(this),
selectAppData(APP_DATA.videoDeviceId)

this.videoDeviceIdUnsub = useCubeStore.subscribe(
state => state.videoDeviceId,
videoDeviceId => {
console.log('dev - videoDeviceId', videoDeviceId);
this.setupSource();
}
);

this.themeChangedSignal = hmsStore.getState(
selectAppData(APP_DATA.themeChanged)
this.isSecondSceneUnsub = useCubeStore.subscribe(
state => state.isSecondScene,
isSecondScene => (this.isSecondScene = isSecondScene)
);
this.themeChangedSignal.connect(this.handleThemeChange.bind(this));

this.themeChangedSignal = useCubeStore.getState().themeChangedSignal;

this.themeChangedSignal
? this.themeChangedSignal.connect(this.handleThemeChange.bind(this))
: console.log('Theme change signal not found');

this.setupThreeStuff();

Expand All @@ -131,10 +104,21 @@ class ArCube {
this.setupScene(FIRST_SCENE);
}

cleanUp() {
this.videoDeviceIdUnsub();
this.isSecondSceneUnsub();

useCubeStore.setState({
canLoadModel: true,
modelInScene: [],
scenesWithModel: {},
isSecondScene: false
});
}

setupThreeStuff() {
console.log('setting up three stuff');

this.okToLoadModel = true;
this.scene = new THREE.Scene();

// promise to track if AR.js has loaded the webcam
Expand Down Expand Up @@ -199,13 +183,11 @@ class ArCube {
this.clock = new THREE.Clock();
this.deltaTime = 0;
this.totalTime = 0;

hmsActions.setAppData(APP_DATA.renderer, this.renderer);
}

setupSource() {
console.log('setting up source');
const deviceId = hmsStore.getState(selectAppData(APP_DATA.videoDeviceId));
const deviceId = useCubeStore.getState().videoDeviceId;

this.arToolkitSource = new THREEx.ArToolkitSource({
sourceType: 'webcam',
Expand All @@ -228,22 +210,6 @@ class ArCube {
return cubeColorValue;
}

// updateSource() {
// const deviceId = hmsStore.getState(selectAppData('videoDeviceId'));

// this.arToolkitSource = new THREEx.ArToolkitSource({
// sourceType: 'webcam',
// deviceId
// });

// this.arjsVid = document.getElementById('arjs-video');

// if (this.arjsVid) {
// this.arjsVid.remove();
// }
// this.arToolkitSource.init();
// }

setupContext() {
console.log('setting up context');

Expand Down Expand Up @@ -390,10 +356,9 @@ class ArCube {
// }

// load model
this.okToLoadModel = false;
hmsActions.setAppData(APP_DATA.canLoadModel, false);
useCubeStore.setState({ canLoadModel: false });

if ('url' in model) {
if ('url' in model!) {
this.gltfLoader.load(
model.url,
gltf => {
Expand All @@ -406,7 +371,7 @@ class ArCube {
console.log('Error loading model url', error);
}
);
} else if ('gltf' in model) {
} else if ('gltf' in model!) {
// const data = JSON.stringify(model.gltf);
const data = model.gltf;
this.gltfLoader.parse(
Expand Down Expand Up @@ -462,7 +427,6 @@ class ArCube {

// add model to scene
this.sceneGroups[sceneNumber].add(gltfModel);
this.okToLoadModel = true;

// Track which scenes a model is loaded in
// This is mostly to reflect changes to a model in JupyterCAD if it's loaded in multiple scenes
Expand All @@ -484,10 +448,11 @@ class ArCube {
// Track which model is loaded in which scene
// This is to get model names on the scale sliders
this.modelInScene[sceneNumber] = modelName;
useCubeStore.setState({ modelInScene: this.modelInScene });

// update app data state
hmsActions.setAppData(APP_DATA.loadedModels, updatedScenesWithModel);
hmsActions.setAppData(APP_DATA.canLoadModel, true);
useCubeStore.setState({ canLoadModel: true });
useCubeStore.setState({ scenesWithModel: updatedScenesWithModel });

// Send scale value to right sidebar
this.scaleSignal.emit({ sceneNumber, scale: minRatio });
Expand Down Expand Up @@ -522,15 +487,14 @@ class ArCube {
}

findModelByName(name: string) {
const modelRegistry = hmsStore.getState(
selectAppData(APP_DATA.modelRegistry)
);
const modelRegistry = useCubeStore.getState().modelRegistry;
return modelRegistry.find(
(model: IModelRegistryData) => model.name === name
);
}

changeModelInScene(sceneNumber: number, modelName: string) {
console.log('dev - change model in scene', sceneNumber, modelName);
// update tracking stuff
const modelNameToRemove = this.modelInScene[sceneNumber];
const updatedModels = { ...this.scenesWithModel };
Expand Down Expand Up @@ -558,14 +522,12 @@ class ArCube {

enableSecondScene() {
console.log('enabling second');
this.isSecondScene = true;
this.setupScene(SECOND_SCENE);
this.secondSceneSignal.emit(true);
useCubeStore.setState({ isSecondScene: true });
}

disableSecondScene() {
console.log('disabling second');
this.isSecondScene = false;
//TODO this won't work with more than two scenes but it's fine for now
this.sceneGroups.pop();

Expand All @@ -575,7 +537,7 @@ class ArCube {
}
});

this.secondSceneSignal.emit(false);
useCubeStore.setState({ isSecondScene: false });
}

resizeCanvasToDisplaySize() {
Expand Down
7 changes: 4 additions & 3 deletions src/arCubePlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ import {
} from '@100mslive/hms-video-store';
import * as THREE from 'three';
import ArCube from './arCube';
import { APP_DATA } from './constants';
import { hmsActions } from './hms';
import { useCubeStore } from './store';

class ArCubePlugin implements HMSVideoPlugin {
input: HTMLCanvasElement | null;
Expand Down Expand Up @@ -147,7 +146,7 @@ class ArCubePlugin implements HMSVideoPlugin {

async init() {
this.arCube = new ArCube();
hmsActions.setAppData(APP_DATA.arCube, this.arCube);
useCubeStore.setState({ arCube: this.arCube });

this.arCube.animate();
}
Expand All @@ -161,6 +160,8 @@ class ArCubePlugin implements HMSVideoPlugin {
}

stop() {
useCubeStore.setState({ arCube: null });

// Remove video element added by AR.js
const video = document.getElementById('arjs-video') as HTMLVideoElement;

Expand Down
10 changes: 6 additions & 4 deletions src/components/MainDisplay.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {
selectAppData,
selectIsConnectedToRoom,
selectSessionStore,
useHMSActions,
Expand All @@ -8,11 +7,12 @@ import {
import { IStateDB } from '@jupyterlab/statedb';
import React, { useEffect } from 'react';

import { APP_DATA, SESSION_STORE } from '../constants';
import { SESSION_STORE } from '../constants';
import GridView from '../layouts/GridView';
import JoinFormView from '../layouts/JoinFormView';
import PresenterView from '../layouts/PresenterView';
import PreviewView from '../layouts/PreviewView';
import { useCubeStore } from '../store';
import ControlBar from './ControlBar';

interface IMainDisplayProps {
Expand All @@ -22,14 +22,16 @@ interface IMainDisplayProps {
export const MainDisplay = ({ state }: IMainDisplayProps) => {
const hmsActions = useHMSActions();
const isConnected = useHMSStore(selectIsConnectedToRoom);
const isConnecting = useHMSStore(selectAppData(APP_DATA.isConnecting));
const isPresenting = useHMSStore(
selectSessionStore(SESSION_STORE.isPresenting)
);

const isConnecting = useCubeStore.use.isConnecting();
const updateIsConnecting = useCubeStore.use.updateIsConnecting();

useEffect(() => {
if (isConnected) {
hmsActions.setAppData(APP_DATA.isConnecting, false);
updateIsConnecting(false);
}
}, [isConnected]);

Expand Down
4 changes: 4 additions & 0 deletions src/components/buttons/PluginButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import React, { useEffect, useState } from 'react';
import ArCubePlugin from '../../arCubePlugin';
import { SESSION_STORE } from '../../constants';
import { useCubeStore } from '../../store';

const PluginButton = () => {
const hmsActions = useHMSActions();
Expand All @@ -31,8 +32,10 @@
);

const [isDisabled, setIsDisabled] = useState(false);
const [prevRole, setPrevRole] = useState<HMSRole>();

Check warning on line 35 in src/components/buttons/PluginButton.tsx

View workflow job for this annotation

GitHub Actions / build

'prevRole' is assigned a value but never used

const arCube = useCubeStore.use.arCube();

useEffect(() => {
hmsActions.sessionStore.observe(SESSION_STORE.isPresenting);
hmsActions.sessionStore.observe(SESSION_STORE.presenterId);
Expand Down Expand Up @@ -62,12 +65,13 @@
togglePresenterRole(prevRole?.name);
hmsActions.sessionStore.set(SESSION_STORE.isPresenting, false);
hmsActions.sessionStore.set(SESSION_STORE.presenterId, '');
arCube?.cleanUp();

await hmsActions.removePluginFromVideoTrack(arPlugin);
}
};

const togglePresenterRole = (roleName?: string) => {

Check warning on line 74 in src/components/buttons/PluginButton.tsx

View workflow job for this annotation

GitHub Actions / build

'togglePresenterRole' is assigned a value but never used
if (localPeer && roleName) {
hmsActions.changeRoleOfPeer(localPeer.id, roleName, true);
} else {
Expand Down
1 change: 1 addition & 0 deletions src/components/modals/AddNewFileModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
uniqueNamesGenerator
} from 'unique-names-generator';
import { useFilePicker } from 'use-file-picker';
//@ts-expect-error no types
import { FileTypeValidator } from 'use-file-picker/validators';
import { IModelRegistryData } from '../../registry';
import Modal from './Modal';
Expand Down
Loading
Loading