Skip to content

Commit

Permalink
Create a configurable axe
Browse files Browse the repository at this point in the history
  • Loading branch information
hbcarlos committed Jan 13, 2023
1 parent d642f18 commit 682c469
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 100 deletions.
66 changes: 12 additions & 54 deletions src/mainview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ 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 +20,7 @@ import {
import { v4 as uuid } from 'uuid';
import { JupyterCadModel } from './model';
import {
AxeHelper,
GridHelper,
IDict,
IDisplayShape,
Expand Down Expand Up @@ -80,7 +78,6 @@ export class MainView extends React.Component<IProps, IStates> {

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

this._resizeTimeout = null;

Expand Down Expand Up @@ -189,59 +186,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();
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 @@ -799,7 +750,6 @@ export class MainView extends React.Component<IProps, IStates> {
): void {
if (change.name === 'grid') {
this._gridHelper?.removeFromParent();
this._gridHelper = null;
const grid = change.newValue as GridHelper | undefined;

if (grid && grid.visible) {
Expand All @@ -808,13 +758,21 @@ export class MainView extends React.Component<IProps, IStates> {
grid.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);
}
}

if (change.name === 'axe') {
this._sceneAxe?.removeFromParent();
const axe = change.newValue as AxeHelper | undefined;

if (axe && axe.visible) {
this._sceneAxe = new THREE.AxesHelper(axe.size);
this._scene.add(this._sceneAxe);
}
}
}

private _handleThemeChange = (): void => {
Expand Down Expand Up @@ -916,7 +874,7 @@ export class MainView extends React.Component<IProps, IStates> {
private _geometry: THREE.BufferGeometry; // Threejs BufferGeometry
private _refLength: number | null = null; // Length of bounding box of current object
private _gridHelper: THREE.GridHelper | null = null; // 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
17 changes: 17 additions & 0 deletions src/schema/axe.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"type": "object",
"description": "Axe::Helper",
"title": "IAxe",
"required": ["Size", "Visible"],
"additionalProperties": false,
"properties": {
"Size": {
"type": "number",
"description": "Axe's size"
},
"Visible": {
"type": "boolean",
"description": "Whether the axe is visible or not"
}
}
}
150 changes: 104 additions & 46 deletions src/toolbar/gridtoolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,76 +2,134 @@ import { Button } from '@jupyterlab/ui-components';

import * as React from 'react';

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

interface IProps {
toolbarModel: ToolbarModel;
}

export class HelpersToolbarReact extends React.Component<IProps> {
private _schema: IDict;
interface IState {
Grid: IDict;
Axe: IDict;
}

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

constructor(props: IProps) {
super(props);
this._model = this.props.toolbarModel.jcadModel!;
this.state = this._createSchema();
}

async componentDidMount(): Promise<void> {
this._updateGrid();
this._model.viewChanged.connect(this._updateGrid, this);
componentDidMount(): void {
this._model.viewChanged.connect(this._updateSchema, this);
}

async componentWillUnmount(): Promise<void> {
this._model.viewChanged.disconnect(this._updateGrid, this);
componentWillUnmount(): void {
this._model.viewChanged.disconnect(this._updateSchema, this);
}

private _updateGrid(): void {
const grid = this._model.getView('grid') as GridHelper | undefined;
this._schema = {
title: 'Grid Helper',
shape: 'Grid::Helper',
schema: this.props.toolbarModel.formSchema['Grid::Helper'],
default: {
Name: 'Grid Helper',
Size: grid?.size ?? 40,
Divisions: grid?.divisions ?? 40,
Visible: grid?.visible ?? true
},
syncData: (props: IDict) => {
const { Size, Divisions, Visible } = props;
const grid: GridHelper = {
size: Size,
divisions: Divisions,
visible: Visible
};
this._model.setView('grid', grid);
}
private _createSchema(): IState {
const grid: GridHelper = {
size: 40,
divisions: 40,
visible: false
};
const axe: AxeHelper = {
size: 5,
visible: false
};
this._model.setView('grid', grid);
this._model.setView('axe', axe);

return {
Grid: {
title: 'Grid Helper',
shape: 'Grid::Helper',
schema: this.props.toolbarModel.formSchema['Grid::Helper'],
default: {
Name: 'Grid Helper',
Size: grid?.size ?? 40,
Divisions: grid?.divisions ?? 40,
Visible: grid?.visible ?? true
},
syncData: (props: IDict) => {
const { Size, Divisions, Visible } = props;
const grid: GridHelper = {
size: Size,
divisions: Divisions,
visible: Visible
};
this._model.setView('grid', grid);
}
},
Axe: {
title: 'Axe Helper',
shape: 'Axe::Helper',
schema: this.props.toolbarModel.formSchema['Axe::Helper'],
default: {
Name: 'Axe Helper',
Size: axe?.size ?? 5,
Visible: axe?.visible ?? true
},
syncData: (props: IDict) => {
const { Size, Visible } = props;
const axe: AxeHelper = {
size: Size,
visible: Visible
};
this._model.setView('axe', axe);
}
}
};
}

private _updateSchema(): void {
const grid = this._model.getView('grid') as GridHelper | undefined;
const axe = this._model.getView('axe') as AxeHelper | undefined;

const { Grid, Axe } = this.state;
Grid["default"] = {
Name: 'Grid Helper',
Size: grid?.size ?? 40,
Divisions: grid?.divisions ?? 40,
Visible: grid?.visible ?? true
};

Axe["default"] = {
Name: 'Axe Helper',
Size: axe?.size ?? 5,
Visible: axe?.visible ?? true
};

this.setState({ Grid, Axe });
}

render(): React.ReactNode {
return (
<div style={{ paddingLeft: '10px', display: 'flex' }}>
<Button
className={'jp-ToolbarButtonComponent'}
style={{ color: 'var(--jp-ui-font-color1)' }}
onClick={async () => {
const dialog = new FormDialog({
toolbarModel: this.props.toolbarModel,
title: this._schema.title,
sourceData: this._schema.default,
schema: this._schema.schema,
syncData: this._schema.syncData,
cancelButton: true
});
await dialog.launch();
}}
>
Grid
</Button>
{ 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>
);
}
Expand Down
8 changes: 8 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,14 @@ export type GridHelper = {
visible: boolean;
};

/**
* Axe's dimensions
*/
export type AxeHelper = {
size: number;
visible: boolean;
};

export interface IJcadObjectDocChange {
contextChange?: MapChange;
objectChange?: MapChange;
Expand Down

0 comments on commit 682c469

Please sign in to comment.