Skip to content

Commit

Permalink
✨ feat: add ViewControls
Browse files Browse the repository at this point in the history
  • Loading branch information
xiangechen committed Jan 10, 2025
1 parent 7acca05 commit 12b7de5
Show file tree
Hide file tree
Showing 11 changed files with 187 additions and 34 deletions.
8 changes: 4 additions & 4 deletions packages/chili-core/src/visual/view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@ export interface IView extends IPropertyChanged, IDisposable {
up(): XYZ;
toImage(): string;
direction(): XYZ;
rotate(dx: number, dy: number): void;
zoomIn(): void;
zoomOut(): void;
rotate(dx: number, dy: number): Promise<void>;
zoomIn(): Promise<void>;
zoomOut(): Promise<void>;
rayAt(mx: number, my: number): Ray;
screenToWorld(mx: number, my: number): XYZ;
worldToScreen(point: XYZ): XY;
fitContent(): void;
fitContent(): Promise<void>;
resize(width: number, heigth: number): void;
setDom(element: HTMLElement): void;
close(): void;
Expand Down
32 changes: 21 additions & 11 deletions packages/chili-three/src/threeView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,10 @@ export class ThreeView extends Observable implements IView {

private createCamera() {
let camera: PerspectiveCamera | OrthographicCamera;
let aspect = this.width! / (this.height ?? 1);
let aspect = this.width! / this.height!;
if (Number.isNaN(aspect)) {
aspect = 1;
}
if (this.cameraType === CameraType.perspective) {
camera = new PerspectiveCamera(45, aspect, 1, 1e6);
} else {
Expand Down Expand Up @@ -243,6 +246,7 @@ export class ThreeView extends Observable implements IView {
}
this._controls = new CameraControls(this.camera, this.renderer.domElement);
this._controls.draggingSmoothTime = 0.06;
this._controls.smoothTime = 0.1;
this._controls.dollyToCursor = true;
this._controls.polarRotateSpeed = 0.8;
this._controls.azimuthRotateSpeed = 0.8;
Expand Down Expand Up @@ -312,6 +316,9 @@ export class ThreeView extends Observable implements IView {
}

resize(width: number, height: number) {
if (height < 0.00001) {
return;
}
this._renderer.setSize(width, height);
this._camera = this.createCamera();
if (this._controls) {
Expand All @@ -326,7 +333,7 @@ export class ThreeView extends Observable implements IView {
this.update();
}

fitContent(): void {
async fitContent() {
let box = new Box3();
let shapes = this.document.selection.getSelectedNodes().filter((x) => x instanceof VisualNode);
if (shapes.length === 0) {
Expand All @@ -339,7 +346,10 @@ export class ThreeView extends Observable implements IView {
}
let sphere = new Sphere();
box.getBoundingSphere(sphere);
this._controls?.fitToSphere(sphere, true);
if (sphere.radius < 1) {
sphere.radius = 1;
}
await this._controls?.fitToSphere(sphere, true);
}

get width() {
Expand All @@ -350,23 +360,23 @@ export class ThreeView extends Observable implements IView {
return this._dom?.clientHeight;
}

rotate(dx: number, dy: number): void {
this._controls?.rotate(dx, dy);
async rotate(dx: number, dy: number) {
await this._controls?.rotate(dx, dy);
}

zoomIn(): void {
async zoomIn() {
if (this.cameraType === CameraType.orthographic) {
this._controls?.zoom(this.camera.zoom * 0.3);
await this._controls?.zoom(this.camera.zoom * 0.3);
} else {
this._controls?.dolly(this.cameraPosition.distanceTo(this.cameraTarget) * 0.2);
await this._controls?.dolly(this.cameraPosition.distanceTo(this.cameraTarget) * 0.2);
}
}

zoomOut(): void {
async zoomOut() {
if (this.cameraType === CameraType.orthographic) {
this._controls?.zoom(-this.camera.zoom * 0.3);
await this._controls?.zoom(-this.camera.zoom * 0.3);
} else {
this._controls?.dolly(this.cameraPosition.distanceTo(this.cameraTarget) * -0.2);
await this._controls?.dolly(this.cameraPosition.distanceTo(this.cameraTarget) * -0.2);
}
}

Expand Down
14 changes: 8 additions & 6 deletions packages/chili-three/src/viewGizmo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export interface Axis {
size: number;
position: Vector3;
color: string[];
line?: number;
lineWidth?: number;
label?: string;
}

Expand Down Expand Up @@ -55,6 +55,8 @@ export class ViewGizmo extends HTMLElement {
this.style.right = "20px";
this.style.borderRadius = "100%";
this.style.cursor = "pointer";
this.style.userSelect = "none";
this.style.webkitUserSelect = "none";
}

private _initCanvas() {
Expand All @@ -67,15 +69,15 @@ export class ViewGizmo extends HTMLElement {
return canvas;
}

private _initAxes() {
private _initAxes(): Axis[] {
return [
{
axis: "x",
direction: new Vector3(1, 0, 0),
position: new Vector3(),
size: options.bubbleSizePrimary,
color: options.colors.x,
line: options.lineWidth,
lineWidth: options.lineWidth,
label: "X",
},
{
Expand All @@ -84,7 +86,7 @@ export class ViewGizmo extends HTMLElement {
position: new Vector3(),
size: options.bubbleSizePrimary,
color: options.colors.y,
line: options.lineWidth,
lineWidth: options.lineWidth,
label: "Y",
},
{
Expand All @@ -93,7 +95,7 @@ export class ViewGizmo extends HTMLElement {
position: new Vector3(),
size: options.bubbleSizePrimary,
color: options.colors.z,
line: options.lineWidth,
lineWidth: options.lineWidth,
label: "Z",
},
{
Expand Down Expand Up @@ -210,7 +212,7 @@ export class ViewGizmo extends HTMLElement {
for (let axis of axes) {
let color = this.getAxisColor(axis);
this.drawCircle(axis.position, axis.size, color);
this.drawLine(this._center, axis.position, color, axis.line);
this.drawLine(this._center, axis.position, color, axis.lineWidth);
this.drawLabel(axis);
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/chili-ui/src/home/home.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ export class Home extends HTMLElement {
"showPermanent",
async () => {
let document = await this.app.openDocument(item.id);
document?.application.activeView?.fitContent();
await document?.application.activeView?.fitContent();
},
"toast.excuting{0}",
I18n.translate("command.document.open"),
Expand Down
12 changes: 6 additions & 6 deletions packages/chili-ui/src/viewport/layoutViewport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export class LayoutViewport extends HTMLElement {
app.views.onCollectionChanged(this._handleViewCollectionChanged);
}

private _handleViewCollectionChanged = (args: CollectionChangedArgs) => {
private readonly _handleViewCollectionChanged = (args: CollectionChangedArgs) => {
if (args.action === CollectionAction.add) {
args.items.forEach((view) => {
this.createViewport(view);
Expand Down Expand Up @@ -62,7 +62,7 @@ export class LayoutViewport extends HTMLElement {
PubSub.default.remove("viewCursor", this._handleCursor);
}

private _handleCursor = (type: CursorType) => {
private readonly _handleCursor = (type: CursorType) => {
this.style.cursor = Cursor.get(type);
};

Expand All @@ -75,7 +75,7 @@ export class LayoutViewport extends HTMLElement {
return viewport;
}

private _handleActiveViewChanged = (view: IView | undefined) => {
private readonly _handleActiveViewChanged = (view: IView | undefined) => {
this._viewports.forEach((v) => {
if (v.view === view) {
v.classList.remove(style.hidden);
Expand All @@ -85,18 +85,18 @@ export class LayoutViewport extends HTMLElement {
});
};

private showSelectionControl = (controller: AsyncController) => {
private readonly showSelectionControl = (controller: AsyncController) => {
this._selectionController.setControl(controller);
this._selectionController.style.visibility = "visible";
this._selectionController.style.zIndex = "1000";
};

private clearSelectionControl = () => {
private readonly clearSelectionControl = () => {
this._selectionController.setControl(undefined);
this._selectionController.style.visibility = "hidden";
};

private _handleMaterialEdit = (
private readonly _handleMaterialEdit = (
document: IDocument,
editingMaterial: Material,
callback: (material: Material) => void,
Expand Down
52 changes: 52 additions & 0 deletions packages/chili-ui/src/viewport/viewport.module.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,55 @@
.root {
position: relative;
}

.viewControls {
position: absolute;
height: 100%;
top: 0;
right: 16px;
z-index: 999;
pointer-events: none;
padding-top: 160px;

svg {
border: none;
background: transparent;
width: 22px;
height: 22px;
padding: 8px;
border-radius: 7.5px;

&:hover {
background: rgba(255, 255, 255, 0.74);
}

&:active {
background: rgba(255, 255, 255, 0.96);
}
}

.actived {
background: rgba(255, 255, 255, 0.56);
}

.border {
border: 1px solid #8f8f8f;
background: rgba(204, 204, 204, 0.75);
border-radius: 10px;
margin: 1px 0px;
padding: 2px;
pointer-events: all;
display: flex;
flex-direction: column;
overflow: hidden;

div {
padding: 0;
margin: 0;
display: flex;
flex-direction: column;
overflow: hidden;
border-radius: 7.5px;
}
}
}
93 changes: 91 additions & 2 deletions packages/chili-ui/src/viewport/viewport.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
// Copyright 2022-2023 the Chili authors. All rights reserved. AGPL-3.0 license.

import { CameraType, IView } from "chili-core";
import { button, div, Flyout, localize } from "../components";
import { Binding, CameraType, IConverter, IView, Result } from "chili-core";
import { div, Flyout, svg } from "../components";
import style from "./viewport.module.css";

class CameraConverter implements IConverter<CameraType> {
constructor(readonly type: CameraType) {}

convert(value: CameraType): Result<string, string> {
if (value === this.type) {
return Result.ok(style.actived);
}
return Result.ok("");
}
}

export class Viewport extends HTMLElement {
private readonly _flyout: Flyout;
private readonly _eventCaches: [keyof HTMLElementEventMap, (e: any) => void][] = [];
Expand All @@ -13,6 +24,84 @@ export class Viewport extends HTMLElement {
this.className = style.root;
this.initEvent();
this._flyout = new Flyout();
this.render();
}

private render() {
this.append(
div(
{
className: style.viewControls,
onpointerdown(ev) {
ev.stopPropagation();
},
onclick: (e) => {
e.stopPropagation();
},
},
div(
{
className: style.border,
},
div(
{
className: new Binding(
this.view,
"cameraType",
new CameraConverter(CameraType.orthographic),
),
},
svg({
icon: "icon-orthographic",
onclick: (e) => {
e.stopPropagation();
this.view.cameraType = CameraType.orthographic;
},
}),
),
div(
{
className: new Binding(
this.view,
"cameraType",
new CameraConverter(CameraType.perspective),
),
},
svg({
icon: "icon-perspective",
onclick: (e) => {
e.stopPropagation();
this.view.cameraType = CameraType.perspective;
},
}),
),
),
div(
{
className: style.border,
},
svg({
icon: "icon-fitcontent",
onclick: async (e) => {
e.stopPropagation();
await this.view.fitContent();
},
}),
svg({
icon: "icon-zoomin",
onclick: () => {
this.view.zoomIn();
},
}),
svg({
icon: "icon-zoomout",
onclick: () => {
this.view.zoomOut();
},
}),
),
),
);
}

connectedCallback() {
Expand Down
2 changes: 1 addition & 1 deletion packages/chili/src/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ export class Application implements IApplication {
for (const file of opens) {
let json: Serialized = JSON.parse(await file.text());
await this.loadDocument(json);
this.activeView?.fitContent();
await this.activeView?.fitContent();
}
},
"toast.excuting{0}",
Expand Down
Loading

0 comments on commit 12b7de5

Please sign in to comment.