Skip to content

Commit

Permalink
Merge pull request #94 from hbcarlos/grid
Browse files Browse the repository at this point in the history
Toolbar for configuring the grid and axes
  • Loading branch information
hbcarlos authored Jan 13, 2023
2 parents aa8ca1a + a5d9d27 commit 323d3ec
Show file tree
Hide file tree
Showing 21 changed files with 206 additions and 85 deletions.
2 changes: 1 addition & 1 deletion binder/environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ channels:
dependencies:
# runtime dependencies
- python >=3.8,<3.9.0a0
- jupyterlab >=4.0.0a30,<5.0.0a0
- jupyterlab >=4.0.0a32,<5.0.0a0
# extra pins for solveability
- notebook-shim >=0.2.0
- jupyterlab_server >=2.16.1
Expand Down
9 changes: 3 additions & 6 deletions src/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,12 @@ export class JupyterCadWidgetFactory extends ABCWidgetFactory<
protected createNewWidget(
context: DocumentRegistry.IContext<JupyterCadModel>
): JupyterCadWidget {
const toolbarModel = new ToolbarModel({ context });
const content = new JupyterCadPanel(context);
const toolbarModel = new ToolbarModel({ panel: content, context });
const toolbar = new ToolbarWidget({
model: toolbarModel,
commands: this._commands
});
return new JupyterCadWidget({
context,
content: new JupyterCadPanel(context),
toolbar
});
return new JupyterCadWidget({ context, content, toolbar });
}
}
89 changes: 22 additions & 67 deletions src/mainview.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
import { DocumentRegistry } from '@jupyterlab/docregistry';
import { IObservableMap, ObservableMap } from '@jupyterlab/observables';
import { User } from '@jupyterlab/services';

import { MapChange } from '@jupyter/ydoc';

import { CommandRegistry } from '@lumino/commands';
import { JSONValue } from '@lumino/coreutils';
import { ContextMenu } from '@lumino/widgets';

import * as React from 'react';
import * as Color from 'd3-color';
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { Line2 } from 'three/examples/jsm/lines/Line2.js';
import { LineGeometry } from 'three/examples/jsm/lines/LineGeometry.js';
import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial.js';

import {
computeBoundsTree,
Expand All @@ -23,6 +22,7 @@ import {
import { v4 as uuid } from 'uuid';
import { JupyterCadModel } from './model';
import {
AxeHelper,
IDict,
IDisplayShape,
IJupyterCadClientState,
Expand All @@ -43,13 +43,12 @@ THREE.Mesh.prototype.raycast = acceleratedRaycast;

const DARK_BG_COLOR = 'linear-gradient(rgb(0, 0, 42), rgb(82, 87, 110))';
const LIGHT_BG_COLOR = 'radial-gradient(#efeded, #8f9091)';
const DARK_GRID_COLOR = 0x4f6882;
const LIGHT_GRID_COLOR = 0x888888;

const DEFAULT_MESH_COLOR = new THREE.Color('#434442');
const SELECTED_MESH_COLOR = new THREE.Color('#AB5118');

interface IProps {
view: ObservableMap<JSONValue>;
context: DocumentRegistry.IContext<JupyterCadModel>;
}

Expand Down Expand Up @@ -77,9 +76,9 @@ export class MainView extends React.Component<IProps, IStates> {

this._geometry = new THREE.BufferGeometry();
this._geometry.setDrawRange(0, 3 * 10000);
this._sceneAxe = [];

this._resizeTimeout = null;
this.props.view.changed.connect(this._onViewChanged, this);

const lightTheme =
document.body.getAttribute('data-jp-theme-light') === 'true';
Expand Down Expand Up @@ -136,6 +135,7 @@ export class MainView extends React.Component<IProps, IStates> {
componentWillUnmount(): void {
window.cancelAnimationFrame(this._requestID);
window.removeEventListener('resize', this._handleWindowResize);
this.props.view.changed.disconnect(this._onViewChanged, this);
this._controls.dispose();
this._postMessage({
action: WorkerAction.CLOSE_FILE,
Expand Down Expand Up @@ -184,72 +184,13 @@ export class MainView extends React.Component<IProps, IStates> {
});
};

addSceneAxe = (dir: THREE.Vector3, color: number): void => {
const origin = new THREE.Vector3(0, 0, 0);
const length = 20;
const arrowHelperX = new THREE.ArrowHelper(
dir,
origin,
length,
color,
0.4,
0.2
);
this._scene.add(arrowHelperX);
const positions = [
origin.x,
origin.y,
origin.z,
length * dir.x,
length * dir.y,
length * dir.z
];

const lineColor = new THREE.Color(color);
const colors = [
lineColor.r,
lineColor.g,
lineColor.b,
lineColor.r,
lineColor.g,
lineColor.b
];
const geo = new LineGeometry();
geo.setPositions(positions);
geo.setColors(colors);
const matLine = new LineMaterial({
linewidth: 1.5, // in pixels
vertexColors: true
});
matLine.resolution.set(800, 600);
const line = new Line2(geo, matLine);
this._sceneAxe.push(arrowHelperX, line);
this._scene.add(line);
};

sceneSetup = (): void => {
if (this.divRef.current !== null) {
this._camera = new THREE.PerspectiveCamera(90, 2, 0.1, 1000);
this._camera.position.set(8, 8, 8);
this._camera.up.set(0, 0, 1);

this._scene = new THREE.Scene();
const size = 40;
const divisions = 40;
this._gridHelper = new THREE.GridHelper(
size,
divisions,
this.state.lightTheme ? LIGHT_GRID_COLOR : DARK_GRID_COLOR,
this.state.lightTheme ? LIGHT_GRID_COLOR : DARK_GRID_COLOR
// 0x888888,
// 0x888888
);
this._gridHelper.geometry.rotateX(Math.PI / 2);

this._scene.add(this._gridHelper);
this.addSceneAxe(new THREE.Vector3(1, 0, 0), 0x00ff00);
this.addSceneAxe(new THREE.Vector3(0, 1, 0), 0xff0000);
this.addSceneAxe(new THREE.Vector3(0, 0, 1), 0xffff00);

const lights: Array<any> = [];
lights[0] = new THREE.AmbientLight(0x404040); // soft white light
Expand Down Expand Up @@ -801,6 +742,21 @@ export class MainView extends React.Component<IProps, IStates> {
}
}

private _onViewChanged(
sender: ObservableMap<JSONValue>,
change: IObservableMap.IChangedArgs<JSONValue>
): void {
if (change.key === 'axes') {
this._sceneAxe?.removeFromParent();
const axe = change.newValue as AxeHelper | undefined;

if (change.type !== 'remove' && axe && axe.visible) {
this._sceneAxe = new THREE.AxesHelper(axe.size);
this._scene.add(this._sceneAxe);
}
}
}

private _handleThemeChange = (): void => {
const lightTheme =
document.body.getAttribute('data-jp-theme-light') === 'true';
Expand Down Expand Up @@ -899,8 +855,7 @@ export class MainView extends React.Component<IProps, IStates> {
private _requestID: any = null; // ID of window.requestAnimationFrame
private _geometry: THREE.BufferGeometry; // Threejs BufferGeometry
private _refLength: number | null = null; // Length of bounding box of current object
private _gridHelper: THREE.GridHelper; // Threejs grid
private _sceneAxe: (THREE.ArrowHelper | Line2)[]; // Array of X, Y and Z axe
private _sceneAxe: THREE.Object3D | null; // Array of X, Y and Z axe
private _controls: OrbitControls; // Threejs control
private _resizeTimeout: any;
private _collaboratorPointers: IDict<THREE.Mesh>;
Expand Down
3 changes: 1 addition & 2 deletions src/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ export class JupyterCadModel implements IJupyterCadModel {
}

fromString(data: string): void {
console.debug('fromString:', data);
const jsonData: IJCadContent = JSON.parse(data);
const ajv = new Ajv();
const validate = ajv.compile(jcadSchema);
Expand Down Expand Up @@ -209,6 +208,7 @@ export class JupyterCadModel implements IJupyterCadModel {
private _dirty = false;
private _readOnly = false;
private _isDisposed = false;

private _contentChanged = new Signal<this, void>(this);
private _stateChanged = new Signal<this, IChangedArgs<any>>(this);
private _themeChanged = new Signal<this, IChangedArgs<any>>(this);
Expand All @@ -226,7 +226,6 @@ export class JupyterCadDoc
{
constructor() {
super();

this._options = this.ydoc.getMap<any>('options');
this._objects = this.ydoc.getArray<Y.Map<any>>('objects');
this._metadata = this.ydoc.getMap<string>('metadata');
Expand Down
121 changes: 121 additions & 0 deletions src/toolbar/helpertoolbar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { Button } from '@jupyterlab/ui-components';

import * as React from 'react';

import { AxeHelper, IDict } from '../types';
import { JupyterCadPanel } from '../widget';
import { FormDialog } from './formdialog';
import { ToolbarModel } from './model';

interface IProps {
toolbarModel: ToolbarModel;
}

interface IState {
Axes: IDict;
}

const FORM_SCHEMA = {
type: 'object',
required: ['Size', 'Visible'],
additionalProperties: false,
properties: {
Size: {
type: 'number',
description: 'Size of the axes'
},
Visible: {
type: 'boolean',
description: 'Whether the axes are visible or not'
}
}
};

export class HelpersToolbarReact extends React.Component<IProps, IState> {
private _panel: JupyterCadPanel;

constructor(props: IProps) {
super(props);
this._panel = this.props.toolbarModel.panel;
this.state = this._createSchema();
}

componentDidMount(): void {
this._panel.viewChanged.connect(this._updateSchema, this);
}

componentWillUnmount(): void {
this._panel.viewChanged.disconnect(this._updateSchema, this);
}

private _createSchema(): IState {
let axes = this._panel.getAxes();

if (!axes) {
axes = {
size: 5,
visible: false
};
this._panel.setAxes(axes);
}

return {
Axes: {
title: 'Axes Helper',
shape: 'Axe::Helper',
schema: FORM_SCHEMA,
default: {
Size: axes?.size ?? 5,
Visible: axes?.visible ?? true
},
syncData: (props: IDict) => {
const { Size, Visible } = props;
const axe: AxeHelper = {
size: Size,
visible: Visible
};
this._panel.setAxes(axe);
}
}
};
}

private _updateSchema(): void {
const axe = this._panel.getAxes();
const { Axes } = this.state;
Axes['default'] = {
Size: axe?.size ?? 5,
Visible: axe?.visible ?? true
};

this.setState({ Axes });
}

render(): React.ReactNode {
return (
<div style={{ paddingLeft: '10px', display: 'flex' }}>
{Object.entries(this.state).map(([key, value]) => {
return (
<Button
className={'jp-ToolbarButtonComponent'}
style={{ color: 'var(--jp-ui-font-color1)' }}
onClick={async () => {
const dialog = new FormDialog({
toolbarModel: this.props.toolbarModel,
title: value.title,
sourceData: value.default,
schema: value.schema,
syncData: value.syncData,
cancelButton: true
});
await dialog.launch();
}}
>
{key}
</Button>
);
})}
</div>
);
}
}
9 changes: 9 additions & 0 deletions src/toolbar/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import formSchema from '../_interface/forms.json';
import { IJupyterCadDoc } from '../types';
import { JupyterCadModel } from './../model';
import { User } from '@jupyterlab/services';
import { JupyterCadPanel } from '../widget';

export interface IUserData {
userId: number;
Expand All @@ -14,13 +15,19 @@ export interface IUserData {

export class ToolbarModel {
constructor(options: ToolbarModel.IOptions) {
this._panel = options.panel;
this._context = options.context;
this._context.ready.then(() => {
this._filePath = this._context.path;
});

this._prepareSchema();
}

get panel(): JupyterCadPanel {
return this._panel;
}

get sharedModel(): IJupyterCadDoc | undefined {
return this._sharedModel;
}
Expand Down Expand Up @@ -108,6 +115,7 @@ export class ToolbarModel {
});
}

private _panel: JupyterCadPanel;
private _context: DocumentRegistry.IContext<JupyterCadModel>;
private _sharedModel?: IJupyterCadDoc;
private _formSchema = JSON.parse(JSON.stringify(formSchema));
Expand All @@ -118,6 +126,7 @@ export class ToolbarModel {

export namespace ToolbarModel {
export interface IOptions {
panel: JupyterCadPanel;
context: DocumentRegistry.IContext<JupyterCadModel>;
}
}
Loading

0 comments on commit 323d3ec

Please sign in to comment.