Showing preview only (1,071K chars total). Download the full file or copy to clipboard to get everything.
Repository: Fennec-hub/three-viewport-gizmo
Branch: main
Commit: d1e74cb4f8e3
Files: 97
Total size: 1.0 MB
Directory structure:
gitextract_gbbdtq91/
├── .github/
│ └── workflows/
│ └── deploy.yml
├── .gitignore
├── .npmignore
├── LICENSE
├── dist/
│ ├── three-viewport-gizmo.d.ts
│ ├── three-viewport-gizmo.js
│ └── three-viewport-gizmo.umd.cjs
├── docs/
│ ├── .vitepress/
│ │ ├── components/
│ │ │ └── IframeContainer.vue
│ │ ├── config.ts
│ │ └── theme/
│ │ ├── icons/
│ │ │ ├── index.ts
│ │ │ └── three.icon.ts
│ │ ├── index.ts
│ │ └── style.css
│ ├── api.md
│ ├── custom.md
│ ├── examples/
│ │ ├── multiple-elements.md
│ │ ├── orbit-controls-events.md
│ │ ├── orbit-controls.md
│ │ ├── post-processing.md
│ │ ├── resizable-grid.md
│ │ ├── responsive.md
│ │ ├── standalone.md
│ │ ├── x-up.md
│ │ ├── yomotsu-camera-controls.md
│ │ └── z-up.md
│ ├── index.md
│ ├── public/
│ │ ├── assets/
│ │ │ ├── HDR/
│ │ │ │ └── studio_small_04_1k.hdr
│ │ │ ├── fonts/
│ │ │ │ └── Roboto_Regular.json
│ │ │ └── models/
│ │ │ └── three/
│ │ │ ├── license.txt
│ │ │ └── scene.gltf
│ │ └── samples/
│ │ ├── common/
│ │ │ └── threeModel.js
│ │ ├── config.html
│ │ ├── multiple-elements.html
│ │ ├── orbit-controls-events.html
│ │ ├── orbit-controls.html
│ │ ├── post-processing.html
│ │ ├── resizable-grid.html
│ │ ├── responsive.html
│ │ ├── standalone.html
│ │ ├── x-up.html
│ │ ├── yomotsu-camera-controls.html
│ │ └── z-up.html
│ └── quickstart.md
├── lib/
│ ├── ViewportGizmo.ts
│ ├── types.ts
│ └── utils/
│ ├── axesCorners.ts
│ ├── axesEdges.ts
│ ├── axesFaces.ts
│ ├── axesLines.ts
│ ├── axesMap.ts
│ ├── axesObjects.ts
│ ├── axisHover.ts
│ ├── constants.ts
│ ├── deepClone.ts
│ ├── getDomElement.ts
│ ├── gizmoBackground.ts
│ ├── gizmoDomElement.ts
│ ├── intersectedObjects.ts
│ ├── isClick.ts
│ ├── optionsFallback.ts
│ ├── roundedRectangleGeometry.ts
│ ├── updateAxis.ts
│ └── updateBackground.ts
├── live/
│ ├── index.html
│ ├── public/
│ │ ├── HDR/
│ │ │ └── studio_small_04_1k.hdr
│ │ ├── fonts/
│ │ │ └── Roboto_Regular.json
│ │ └── models/
│ │ ├── cad/
│ │ │ └── support_plate.gltf
│ │ └── three/
│ │ ├── license.txt
│ │ └── scene.gltf
│ ├── src/
│ │ ├── WebGPU.ts
│ │ ├── composer.ts
│ │ ├── configuration.ts
│ │ ├── constant.ts
│ │ ├── controls.ts
│ │ ├── controlsListeners.ts
│ │ ├── grid.ts
│ │ ├── gui.ts
│ │ ├── initScene.ts
│ │ ├── main.ts
│ │ ├── multiViewport.ts
│ │ ├── responsive.ts
│ │ ├── static.ts
│ │ ├── utils/
│ │ │ ├── ResizableGrid.ts
│ │ │ ├── get3DText.ts
│ │ │ ├── getSceneLights.ts
│ │ │ ├── loadEnvMap.ts
│ │ │ └── loadModel.ts
│ │ ├── vite-env.d.ts
│ │ ├── yomotsuCameraControls.ts
│ │ ├── z_up.ts
│ │ └── z_up_stand_alone.ts
│ └── style.css
├── package.json
├── readme.md
├── tsconfig.json
├── vite.config.ts
└── vite.live.config.ts
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/deploy.yml
================================================
# Sample workflow for building and deploying a VitePress site to GitHub Pages
#
name: Deploy VitePress site to Pages
on:
# Runs on pushes targeting the `main` branch. Change this to `master` if you're
# using the `master` branch as the default branch.
push:
branches: [main]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: pages
cancel-in-progress: false
jobs:
# Build job
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0 # Not needed if lastUpdated is not enabled
# - uses: pnpm/action-setup@v3 # Uncomment this block if you're using pnpm
# with:
# version: 9 # Not needed if you've set "packageManager" in package.json
# - uses: oven-sh/setup-bun@v1 # Uncomment this if you're using Bun
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm # or pnpm / yarn
- name: Setup Pages
uses: actions/configure-pages@v4
- name: Install dependencies
run: npm ci # or pnpm install / yarn install / bun install
- name: Build with VitePress
run: npm run docs:build # or pnpm docs:build / yarn docs:build / bun run docs:build
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: docs/.vitepress/dist
# Deployment job
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
needs: build
runs-on: ubuntu-latest
name: Deploy
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
================================================
FILE: .gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
node_modules
*.local
docs/.vitepress/dist
docs/.vitepress/cache
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
================================================
FILE: .npmignore
================================================
.vscode/*
/node_modules
/demo
/lib
/dist/demo
docs/.vitepress/dist
docs/.vitepress/cache
*.local
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2024 Fennec
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: dist/three-viewport-gizmo.d.ts
================================================
import { ColorRepresentation } from 'three';
import { DeepRequired } from 'utility-types';
import { Object3D } from 'three';
import { Object3DEventMap } from 'three';
import { OrbitControls } from 'three/examples/jsm/Addons.js';
import { OrthographicCamera } from 'three';
import { PerspectiveCamera } from 'three';
import { Vector3 } from 'three';
import { WebGLRenderer } from 'three';
import { WebGPURenderer } from 'three/webgpu';
/**
* Configuration options for individual gizmo axes.
* Each axis can be customized with its own appearance and behavior.
*/
export declare type GizmoAxisOptions = {
/** Whether to draw the axis. Default `true` */
enabled?: boolean;
/** Custom text label for the axis. If not specified, defaults to the axis name */
label?: string;
/** The axis opacity. Default `1` */
opacity?: number;
/** The scale multiplayer for the indicator size. Default `1` */
scale?: number;
/** Whether to draw the the axis line. */
line?: boolean;
/** The axis indicator background color. */
color?: ColorRepresentation;
/** Color for the axis text label */
labelColor?: ColorRepresentation;
/** The border around the axis indicator. */
border?: {
/** The border size around the axis indicator. */
size: number;
/** The border color around the axis indicator. */
color: ColorRepresentation;
};
/** The axis indicator hover options. */
hover?: {
/** The fill color on hover */
color?: ColorRepresentation;
/** The label text color when the axis is hovered. */
labelColor?: ColorRepresentation;
/** The opacity when the axis is hovered. Default `1` */
opacity?: number;
/** The indicator scale multiplayer when the axis is hovered Default `1` */
scale?: number;
/** The border around the axis indicator. */
border?: {
/** The border size around the axis indicator. */
size: number;
/** The border color around the axis indicator. */
color: ColorRepresentation;
};
};
};
/**
* Configuration options for the ViewportGizmo.
* All properties are optional and will fall back to default values if not specified.
*/
export declare type GizmoOptions = {
/** Parent element for the gizmo. Can be an HTMLElement or a CSS selector string */
container?: HTMLElement | string;
/** The Gizmo supports three types: `sphere`, `cube`, and `rounded-cube` configuration. Default `sphere` */
type?: "sphere" | "cube" | "rounded-cube";
/** Size of the gizmo widget in pixels. Default `128`*/
size?: number;
/**
* Represents the possible placement positions of the gizmo widget in the viewport.
* The position is determined by combining vertical alignment (top, center, bottom)
* with horizontal alignment (left, center, right).
*/
placement?: "top-left" | "top-center" | "top-right" | "center-left" | "center-center" | "center-right" | "bottom-left" | "bottom-center" | "bottom-right";
/** Offset of the gizmo from the container edges in pixels. */
offset?: {
/** Offset from the left edge in pixel. Default `10` */
left?: number;
/** Offset from the top edge in pixel. Default `10` */
top?: number;
/** Offset from the right edge in pixel. Default `10` */
right?: number;
/** Offset from the bottom edge in pixel. Default `10` */
bottom?: number;
};
/** Whether view changes should be animated. Default `true` */
animated?: boolean;
/** Animation speed multiplier. Higher values result in faster animations. Default `1` */
speed?: number;
/**
* The texture resolution. Higher values improve quality at the cost of performance.
* Default `64` for a `sphere` type, and `128` for the cube.
**/
resolution?: number;
/** The width of the axes lines material in pixels. LineMaterial2 */
lineWidth?: number;
/** HTML `id` attribute for the gizmo container */
id?: string;
/** HTML `class` attribute for the gizmo container */
className?: string;
/**
* Font configuration for axis labels.
* Follows standard CSS font properties.
*/
font?: {
/** Font family for axis labels */
family?: string;
/** Font weight for axis labels */
weight?: string | number;
/** Font size in pixels for axis labels */
size?: number;
};
/**
* The gizmo background configuration.
* The sphere provides visual context for the current orientation.
*/
background?: {
/** Whether to display the background sphere */
enabled?: boolean;
/** Color of the background sphere in normal state */
color?: ColorRepresentation;
/** Opacity of the background sphere in normal state (0-1) */
opacity?: number;
hover?: {
/** Color of the background sphere when hovered */
color?: ColorRepresentation;
/** Opacity of the background sphere when hovered (0-1) */
opacity?: number;
};
};
/**
* Configuration for the corner axes indicators of the gizmo.
* Corners provide additional visual cues for orientation, particularly useful in cube mode.
*/
corners?: {
/** Whether to display corner indicators. Default `true` */
enabled?: boolean;
/** Base color of the corner indicators in normal state */
color?: ColorRepresentation;
/** Opacity of corner indicators in normal state (0-1). Default `1` */
opacity?: number;
/** Scale multiplier for corner indicator size. Default `1` */
scale?: number;
/** Radius of the corner indicators in range [0, 1]. Controls the roundness.
* Default `1` for sphere type, `0.2` for the cube type.
*/
radius?: number;
/** Smoothness of the corner indicators. Higher values create smoother transitions. Default `18` */
smoothness?: number;
/** Corner indicator appearance when hovered */
hover?: {
/** Color of corner indicators when hovered */
color?: ColorRepresentation;
/** Opacity of corner indicators when hovered (0-1) */
opacity?: number;
/** Scale multiplier for corner indicators when hovered */
scale?: number;
};
};
/**
* Configuration for the edge indicators of the gizmo.
* Edges help define the boundaries between faces and improve spatial understanding.
*/
edges?: {
/** Whether to display edge indicators. Default `true` */
enabled?: boolean;
/** Base color of the edge indicators in normal state */
color?: ColorRepresentation;
/** Opacity of edge indicators in normal state (0-1). Default `1` */
opacity?: number;
/** Scale multiplier for edge indicator thickness. Default `1` */
scale?: number;
/** Radius of the edge indicators in pixels. Controls the roundness. Default `2` */
radius?: number;
/** Smoothness of the edge indicators (1-10). Higher values create smoother transitions. Default `8` */
smoothness?: number;
/** Edge indicator appearance when hovered */
hover?: {
/** Color of edge indicators when hovered */
color?: ColorRepresentation;
/** Opacity of edge indicators when hovered (0-1) */
opacity?: number;
/** Scale multiplier for edge indicators when hovered */
scale?: number;
};
};
/** The axes edge radius, applied to all axes. When `type: rounded-cube`, this will be the radius of the rounded edges and corners */
radius?: number;
/** The axes edge smoothness, applied to all axes */
smoothness?: number;
/**
* Configuration for positive `X` axis or the `Right` face.
* @see {@link GizmoAxisOptions}
*/
x?: GizmoAxisOptions;
/**
* Configuration for positive `Y` axis or the `Top` face.
* @see {@link GizmoAxisOptions}
*/
y?: GizmoAxisOptions;
/**
* Configuration for positive `Z` axis or the `Front` face.
* @see {@link GizmoAxisOptions}
*/
z?: GizmoAxisOptions;
/**
* Configuration for negative X axis or the `Left` face.
* @see {@link GizmoAxisOptions}
*/
nx?: GizmoAxisOptions;
/**
* Configuration for negative Y axis or the `Bottom` face.
* @see {@link GizmoAxisOptions}
*/
ny?: GizmoAxisOptions;
/**
* Configuration for negative Z axis or the `Back` face.
* @see {@link GizmoAxisOptions}
*/
nz?: GizmoAxisOptions;
/**
* @alias An alias for the negative `x` axis configuration `options.nx`,
* can be used with a cube configuration for more clarity.
*
* @see {@link GizmoAxisOptions}
*/
right?: GizmoAxisOptions;
/**
* @alias An alias for the `y` axis configuration `options.y`,
* can be used with a cube configuration for more clarity.
*
* @see {@link GizmoAxisOptions}
*/
top?: GizmoAxisOptions;
/**
* @alias An alias for the `z` axis configuration `options.z`,
* can be used with a cube configuration for more clarity.
*
* @see {@link GizmoAxisOptions}
*/
front?: GizmoAxisOptions;
/**
* @alias An alias for the `x` axis configuration `options.x`,
* can be used with a cube configuration for more clarity.
*
* @see {@link GizmoAxisOptions}
*/
left?: GizmoAxisOptions;
/**
* @alias An alias for the negative `y` axis configuration `options.ny`,
* can be used with a cube configuration for more clarity.
*
* @see {@link GizmoAxisOptions}
*/
bottom?: GizmoAxisOptions;
/**
* @alias An alias for the negative `z` axis configuration `options.nz`,
* can be used with a cube configuration for more clarity.
*
* @see {@link GizmoAxisOptions}
*/
back?: GizmoAxisOptions;
};
/** The {@link GizmoOptions } with all options set with their respective default and fallback */
declare type GizmoOptionsFallback = DeepRequired<GizmoOptions> & {
isSphere: boolean;
};
/**
* ViewportGizmo is a 3D camera orientation controller that provides a visual interface
* for changing the camera's viewing angle. It creates a widget that shows the current
* camera orientation and allows direct manipulation of the view through clicking or dragging.
*
* @fires ViewportGizmo#start - Fired when a view change interaction begins
* @fires ViewportGizmo#change - Fired during view changes
* @fires ViewportGizmo#end - Fired when a view change interaction ends
*
* @extends Object3D
*/
export declare class ViewportGizmo extends Object3D<ViewportGizmoEventMap> {
/** Whether the gizmo is currently active and responding to user input */
enabled: boolean;
/** The camera being controlled by this gizmo */
camera: OrthographicCamera | PerspectiveCamera;
/** The WebGLRenderer rendering the gizmo */
renderer: WebGLRenderer | WebGPURenderer;
/** The configuration options */
options: GizmoOptions;
/** The point around which the camera rotates */
target: Vector3;
/** Whether view changes should be animated */
animated: boolean;
/** The speed of view change animations. Higher values result in faster animations */
speed: number;
/**
* Indicates whether the gizmo is currently being animated or not,
* Useful when interacting with other camera controllers
*
* @readonly This value is set internally.
**/
animating: boolean;
private _options;
private _intersections;
private _background;
private _viewport;
private _originalViewport;
private _originalScissor;
private _scene;
private _camera;
private _container;
private _domElement;
private _domRect;
private _dragging;
private _distance;
private _clock;
private _targetQuaternion;
private _quaternionStart;
private _quaternionEnd;
private _pointerStart;
private _focus;
private _placement;
private _controls?;
private _controlsListeners?;
/**
* Creates a new ViewportGizmo instance.
*
* @param camera - The camera to be controlled by this gizmo
* @param renderer - The WebGL renderer used to render the scene
* @param options - {@link GizmoOptions}, Configuration options for the gizmo.
* @param options.container - Parent element for the gizmo. Can be an HTMLElement or a CSS selector string
* @param options.type - The gizmo configuration type. Either 'sphere' or 'cube', defaults to 'sphere'
* @param options.size - Size of the gizmo widget in pixels. Defaults to 128
* @param options.placement - Position of the gizmo in the viewport
* Options include:
* - `"top-left"`
* - `"top-center"`
* - `"top-right"`
* - `"center-left"`
* - `"center-center"`
* - `"center-right"`
* - `"bottom-left"`
* - `"bottom-center"`
* - `"bottom-right"`
* @param options.offset - Offset of the gizmo from container edges in pixels
* @param options.offset.left - Offset from the left edge
* @param options.offset.top - Offset from the top edge
* @param options.offset.right - Offset from the right edge
* @param options.offset.bottom - Offset from the bottom edge
* @param options.animated - Whether view changes should be animated. Defaults to true
* @param options.speed - Animation speed multiplier. Defaults to 1
* @param options.resolution - Texture resolution. Defaults to 64 for sphere, 128 for cube
* @param options.lineWidth - Width of the axes lines in pixels
* @param options.id - HTML `id` attribute for the gizmo container
* @param options.className - HTML `class` attribute for the gizmo container
* @param options.font - Font configuration for axis labels
* @param options.font.family - Font family for axis labels
* @param options.font.weight - Font weight for axis labels
* @param options.background - Configuration for the background sphere/cube
* @param options.background.enabled - Whether to display the background
* @param options.background.color - Color of the background in normal state
* @param options.background.opacity - Opacity of the background in normal state
* @param options.background.hover.color - Color of the background when hovered
* @param options.background.hover.opacity - Opacity of the background when hovered
* @param options.corners - Configuration for corner indicators
* @param options.corners.enabled - Whether to display corner indicators
* @param options.corners.color - Base color of corner indicators
* @param options.corners.opacity - Opacity of corner indicators
* @param options.corners.scale - Scale multiplier for corner indicators
* @param options.corners.radius - Radius of corner indicators
* @param options.corners.smoothness - Smoothness of corner indicators
* @param options.corners.hover.color - Color of corner indicators when hovered
* @param options.corners.hover.opacity - Opacity of corner indicators when hovered
* @param options.corners.hover.scale - Scale of corner indicators when hovered
* @param options.edges - Configuration for edge indicators
* @param options.edges.enabled - Whether to display edge indicators
* @param options.edges.color - Base color of edge indicators
* @param options.edges.opacity - Opacity of edge indicators
* @param options.edges.scale - Scale multiplier for edge indicators
* @param options.edges.radius - Radius of edge indicators
* @param options.edges.smoothness - Smoothness of edge indicators
* @param options.edges.hover.color - Color of edge indicators when hovered
* @param options.edges.hover.opacity - Opacity of edge indicators when hovered
* @param options.edges.hover.scale - Scale of edge indicators when hovered
* @param options.x - Configuration for positive X axis/face
* @param options.y - Configuration for positive Y axis/face
* @param options.z - Configuration for positive Z axis/face
* @param options.nx - Configuration for negative X axis/face
* @param options.ny - Configuration for negative Y axis/face
* @param options.nz - Configuration for negative Z axis/face
*
* @remarks Axis-specific configuration can also use alias names for cube mode:
* - `right` (same as `x`)
* - `left` (same as `nx`)
* - `top` (same as `y`)
* - `bottom` (same as `ny`)
* - `front` (same as `z`)
* - `back` (same as `nz`)
*
* For each axis/face configuration, the following options are available:
* @param options.AXIS.enabled - Whether to draw the axis
* @param options.AXIS.label - Custom text label for the axis
* @param options.AXIS.opacity - Axis opacity
* @param options.AXIS.scale - Scale multiplier for indicator size
* @param options.AXIS.line - Whether to draw the axis line
* @param options.AXIS.color - Axis indicator background color
* @param options.AXIS.labelColor - Axis label color
* @param options.AXIS.border.size - Border size around the axis indicator
* @param options.AXIS.border.color - Border color around the axis indicator
* @param options.AXIS.hover.color - Fill color on hover
* @param options.AXIS.hover.labelColor - Label text color on hover
* @param options.AXIS.hover.opacity - Opacity when hovered
* @param options.AXIS.hover.scale - Indicator scale when hovered
* @param options.AXIS.hover.border.size - Hover border size
* @param options.AXIS.hover.border.color - Hover border color
*/
constructor(camera: PerspectiveCamera | OrthographicCamera, renderer: WebGLRenderer | WebGPURenderer, options?: GizmoOptions);
/** Gets the current placement of the gizmo relative to its container. */
get placement(): GizmoOptionsFallback["placement"];
/**
* Sets and update the placement of the gizmo relative to its container.
*
* @param placement - The new placement position
*/
set placement(placement: GizmoOptionsFallback["placement"]);
/**
* Regenerates the gizmo with the new options.
*
* @remarks
* - Not recommended for use in real-time rendering or animation loops
* - Provides a way to completely rebuild the gizmo with new options
* - Can be computationally expensive, so use sparingly
*/
set(options?: GizmoOptions): this;
/**
* Renders the gizmo to the screen.
* This method handles viewport and scissor management to ensure the gizmo
* renders correctly without affecting the main scene rendering.
*
* @returns The gizmo instance for method chaining
*/
render(): this;
/**
* Updates the gizmo's DOM-related properties based on its current position
* and size in the document.
*
* @returns The gizmo instance for method chaining
*/
domUpdate(): this;
/**
* Updates the gizmo's orientation to match the current camera orientation.
*
* @returns The gizmo instance for method chaining
*/
cameraUpdate(): this;
/**
* Performs a complete update of the gizmo, including both DOM and camera-related updates.
*
* @param controls - Internal. Set to `false` if the update event comes from the attached controls.
*
* @returns The gizmo instance for method chaining
*/
update(controls?: boolean): this;
/**
* Connects OrbitControls with the gizmo, handling interaction states and updates.
* Automatically detaches any previously attached controls.
*
* @param controls - The scene's {@link https://threejs.org/docs/#examples/en/controls/OrbitControls OrbitControls}
*/
attachControls(controls: OrbitControls): this;
/** Removes all control event listeners and references. Safe to call multiple times. */
detachControls(): this | undefined;
/** Cleans up all resources including geometries, materials, textures, and event listeners. */
dispose(): void;
/**
* Updates the gizmo's orientation either based on the camera or internal state.
*
* @private
* @param fromCamera - Whether to update based on camera orientation (true) or internal state (false)
*/
private _updateOrientation;
/**
* Handles the animation of camera position and orientation changes.
*
* @private
*/
private _animate;
/**
* Sets the camera orientation to look at the target from a specific axis.
*
* @private
* @param position - The axis point position
*/
private _setOrientation;
/**
* Handles the pointer down event for starting drag operations.
*
* @private
* @param e - The pointer event
*/
private _onPointerDown;
/**
* Converts the input-coordinates from the standard Y-axis up to what is set in Object3D.DEFAULT_UP.
*
* @private
* @param target - The target Vector3 to be converted
* @param isSpherical - Whether or not the coordinates are for a sphere
* @returns The converted coordinates
*/
private coordinateConversion;
/**
* Handles pointer move events for hover effects and drag operations.
*
* @private
* @param e - The pointer event
*/
private _onPointerMove;
/**
* Handles pointer leave events to reset hover states.
*
* @private
*/
private _onPointerLeave;
/**
* Handles click events for axis selection.
*
* @private
* @param e - The pointer event
*/
private _handleClick;
/**
* Handles hover effects for interactive elements.
*
* @private
* @param e - The pointer event
*/
private _handleHover;
}
/**
* Map of custom events emitted by the ViewportGizmo.
* Extends Three.js Object3D events with gizmo-specific interaction events.
* @extends Object3DEventMap
*/
export declare interface ViewportGizmoEventMap extends Object3DEventMap {
/**
* Fired when a view change interaction begins.
* This can be triggered by:
* - Starting a drag operation
* - Clicking an axis to start a view transition
*/
start: {};
/**
* Fired when a view change interaction completes.
* This can be triggered by:
* - Releasing a drag operation
* - Completing a view transition animation
*/
end: {};
/**
* Fired during view changes.
* This can be triggered by:
* - Active drag operations updating the view
* - View transition animations in progress
* - Any other camera orientation updates
*/
change: {};
}
export { }
================================================
FILE: dist/three-viewport-gizmo.js
================================================
var ce = Object.defineProperty;
var le = (s, e, t) => e in s ? ce(s, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : s[e] = t;
var _ = (s, e, t) => le(s, typeof e != "symbol" ? e + "" : e, t);
import { Vector3 as M, Vector2 as K, Raycaster as de, Object3D as I, Color as Xt, CanvasTexture as ue, RepeatWrapping as Ot, SRGBColorSpace as he, BufferGeometry as $t, BufferAttribute as at, Sprite as xt, SpriteMaterial as Et, Mesh as X, MeshBasicMaterial as gt, SphereGeometry as Qt, CylinderGeometry as fe, BackSide as pe, InstancedBufferGeometry as me, Float32BufferAttribute as Bt, InstancedInterleavedBuffer as bt, InterleavedBufferAttribute as Z, WireframeGeometry as ge, Box3 as At, Sphere as Yt, ShaderMaterial as ye, ShaderLib as ct, UniformsUtils as Jt, UniformsLib as lt, Vector4 as Q, Line3 as _e, Matrix4 as Kt, MathUtils as ve, Quaternion as J, Clock as be, Scene as we, OrthographicCamera as Se, PerspectiveCamera as xe, Spherical as Ee } from "three";
const te = (s, e) => {
const [t, n] = e.split("-");
return Object.assign(s.style, {
left: n === "left" ? "0" : n === "center" ? "50%" : "",
right: n === "right" ? "0" : "",
top: t === "top" ? "0" : t === "bottom" ? "" : "50%",
bottom: t === "bottom" ? "0" : "",
transform: `${n === "center" ? "translateX(-50%)" : ""} ${t === "center" ? "translateY(-50%)" : ""}`
}), e;
}, Ae = ({
placement: s,
size: e,
offset: t,
id: n,
className: i
}) => {
const o = document.createElement("div"), { top: a, left: c, right: l, bottom: u } = t;
return Object.assign(o.style, {
id: n,
position: "absolute",
zIndex: "1000",
height: `${e}px`,
width: `${e}px`,
margin: `${a}px ${l}px ${u}px ${c}px`,
borderRadius: "100%"
}), te(o, s), n && (o.id = n), i && (o.className = i), o;
}, Me = (s) => {
const e = typeof s == "string" ? document.querySelector(s) : s;
if (!e) throw Error("Invalid DOM element");
return e;
};
function wt(s, e, t) {
return Math.max(e, Math.min(t, s));
}
const Ue = [
["x", 0, 3],
["y", 1, 4],
["z", 2, 5]
], Dt = /* @__PURE__ */ new M();
function Pt({ isSphere: s }, e, t) {
s && (Dt.set(0, 0, 1).applyQuaternion(t.quaternion), Ue.forEach(([n, i, o]) => {
const a = Dt[n];
let c = e[i], l = c.userData.opacity;
c.material.opacity = wt(a >= 0 ? l : l / 2, 0, 1), c = e[o], l = c.userData.opacity, c.material.opacity = wt(a >= 0 ? l / 2 : l, 0, 1);
}));
}
const ze = (s, e, t = 10) => Math.abs(s.clientX - e.x) < t && Math.abs(s.clientY - e.y) < t, Rt = /* @__PURE__ */ new de(), Gt = /* @__PURE__ */ new K(), Ft = (s, e, t, n) => {
Gt.set(
(s.clientX - e.left) / e.width * 2 - 1,
-((s.clientY - e.top) / e.height) * 2 + 1
), Rt.setFromCamera(Gt, t);
const i = Rt.intersectObjects(
n,
!1
);
if (i.length > 0) {
i.sort((u, r) => u.distance - r.distance);
const a = 0.2, c = i[0].distance, l = i.filter(
(u) => u.distance <= c + a
);
l.length > 1 && (l.sort((u, r) => (r.object.userData.intersectionOrder || 0) - (u.object.userData.intersectionOrder || 0)), i.splice(0, l.length, ...l));
}
const o = i.length ? i[0] : null;
return !o || !o.object.visible ? null : o;
}, et = 1e-6, Te = 2 * Math.PI, ee = ["x", "y", "z"], $ = [...ee, "nx", "ny", "nz"], Le = ["x", "z", "y", "nx", "nz", "ny"], Ce = ["z", "x", "y", "nz", "nx", "ny"], dt = "Right", ut = "Top", ht = "Front", ft = "Left", pt = "Bottom", mt = "Back", Oe = [
dt,
ut,
ht,
ft,
pt,
mt
].map((s) => s.toLocaleLowerCase()), ne = 1.3, It = (s, e = !0) => {
const { material: t, userData: n } = s, { color: i, opacity: o } = e ? n.hover : n;
t.color.set(i), t.opacity = o;
}, j = (s) => JSON.parse(JSON.stringify(s)), Be = {
yUp: {
x: dt,
y: ut,
z: ht,
nx: ft,
ny: pt,
nz: mt
},
zUp: {
x: dt,
y: mt,
z: ut,
nx: ft,
ny: ht,
nz: pt
},
xUp: {
x: ut,
y: ht,
z: dt,
nx: pt,
ny: mt,
nz: ft
}
}, De = (s) => {
const e = s.type || "sphere", t = e === "sphere", n = e === "rounded-cube", i = s.resolution || t ? 64 : 128, o = I.DEFAULT_UP, a = o.z === 1, c = o.x === 1, u = Be[a ? "zUp" : c ? "xUp" : "yUp"], { container: r } = s;
s.container = void 0, s = JSON.parse(JSON.stringify(s)), s.container = r;
const h = a ? Le : c ? Ce : $;
Oe.forEach((p, w) => {
s[p] && (s[h[w]] = s[p]);
});
const d = {
enabled: !0,
color: 16777215,
opacity: 1,
scale: 0.7,
labelColor: 2236962,
line: !1,
border: {
size: 0,
color: 14540253
},
hover: {
color: t ? 16777215 : 9688043,
labelColor: 2236962,
opacity: 1,
scale: 0.7,
border: {
size: 0,
color: 14540253
}
}
}, f = {
line: !1,
scale: t ? 0.45 : 0.7,
hover: {
scale: t ? 0.5 : 0.7
}
}, v = {
type: e,
container: document.body,
size: 128,
placement: "top-right",
resolution: i,
lineWidth: 4,
radius: t ? 1 : n ? 0.3 : 0.2,
smoothness: 18,
animated: !0,
speed: 1,
background: {
enabled: !0,
color: t ? 16777215 : 14739180,
opacity: t ? 0 : 1,
hover: {
color: t ? 16777215 : 14739180,
opacity: t ? 0.2 : 1
}
},
font: {
family: "sans-serif",
weight: 900
},
offset: {
top: 10,
left: 10,
bottom: 10,
right: 10
},
corners: {
enabled: !t,
color: t ? 15915362 : 16777215,
opacity: 1,
scale: t ? 0.15 : 0.2,
radius: 1,
smoothness: 18,
hover: {
color: t ? 16777215 : 9688043,
opacity: 1,
scale: t ? 0.2 : 0.225
}
},
edges: {
enabled: !t,
color: t ? 15915362 : n ? 15658734 : 16777215,
opacity: t ? 1 : 0,
radius: t ? 1 : 0.125,
smoothness: 18,
scale: t ? 0.15 : 1,
hover: {
color: t ? 16777215 : 9688043,
opacity: 1,
scale: t ? 0.2 : 1
}
},
x: {
...j(d),
...t ? { label: "X", color: 16725587, line: !0 } : { label: u.x }
},
y: {
...j(d),
...t ? { label: "Y", color: 9100032, line: !0 } : { label: u.y }
},
z: {
...j(d),
...t ? { label: "Z", color: 2920447, line: !0 } : { label: u.z }
},
nx: {
...j(f),
label: t ? "" : u.nx
},
ny: {
...j(f),
label: t ? "" : u.ny
},
nz: {
...j(f),
label: t ? "" : u.nz
}
};
if (St(s, v), n) {
const p = s;
p.edges.radius = p.radius, p.edges.scale = 1, p.edges.opacity = 1, p.edges.hover.scale = 1, p.edges.hover.opacity = 1, p.corners.radius = p.radius, p.corners.scale = 1, p.corners.opacity = 1, p.corners.hover.scale = 1, p.corners.hover.opacity = 1, p.radius = 0, $.forEach((w) => {
p[w].scale = 1, p[w].opacity = 1, p[w].hover.scale = 1, p[w].hover.opacity = 1;
});
}
return ee.forEach(
(p) => St(
s[`n${p}`],
j(s[p])
)
), { ...s, isSphere: t };
};
function St(s, ...e) {
if (s instanceof HTMLElement || typeof s != "object" || s === null)
return s;
for (const t of e)
for (const n in t)
n !== "container" && n in t && (s[n] === void 0 ? s[n] = t[n] : typeof t[n] == "object" && !Array.isArray(t[n]) && (s[n] = St(
s[n] || {},
t[n]
)));
return s;
}
const Pe = (s, e = 2) => {
const t = new Xt(), n = e * 2, { isSphere: i, resolution: o, radius: a, font: c, corners: l, edges: u } = s, r = $.map((m) => ({ ...s[m], radius: a }));
i && l.enabled && r.push(l), i && u.enabled && r.push(u);
const h = document.createElement("canvas"), d = h.getContext("2d");
h.width = o * 2 + n * 2, h.height = o * r.length + n * r.length;
const [f, v] = C(r, o, c);
r.forEach(
({
radius: m,
label: S,
color: H,
labelColor: x,
border: E,
hover: {
color: k,
labelColor: O,
border: B
}
}, D) => {
const F = o * D + D * n + e;
y(
e,
F,
e,
o,
m,
S,
E,
H,
x
), y(
o + e * 3,
F,
e,
o,
m,
S,
B ?? E,
k ?? H,
O ?? x
);
}
);
const p = r.length, w = e / (o * 2), A = e / (o * 6), L = 1 / p, b = new ue(h);
return b.repeat.set(0.5 - 2 * w, L - 2 * A), b.offset.set(w, 1 - A), Object.assign(b, {
colorSpace: he,
wrapS: Ot,
wrapT: Ot,
userData: {
offsetX: w,
offsetY: A,
cellHeight: L
}
}), b;
function y(m, S, H, x, E, k, O, B, D) {
if (E = E * (x / 2), B != null && B !== "" && (F(), d.fillStyle = t.set(B).getStyle(), d.fill()), O && O.size) {
const N = O.size * x / 2;
m += N, S += N, x -= O.size * x, E = Math.max(0, E - N), F(), d.strokeStyle = t.set(O.color).getStyle(), d.lineWidth = O.size * x, d.stroke();
}
k && g(
d,
m + x / 2,
S + (x + H) / 2,
k,
t.set(D).getStyle()
);
function F() {
d.beginPath(), d.moveTo(m + E, S), d.lineTo(m + x - E, S), d.arcTo(m + x, S, m + x, S + E, E), d.lineTo(m + x, S + x - E), d.arcTo(m + x, S + x, m + x - E, S + x, E), d.lineTo(m + E, S + x), d.arcTo(m, S + x, m, S + x - E, E), d.lineTo(m, S + E), d.arcTo(m, S, m + E, S, E), d.closePath();
}
}
function C(m, S, H) {
const E = [...m].sort((tt, ae) => {
var Lt, Ct;
return (((Lt = tt.label) == null ? void 0 : Lt.length) || 0) - (((Ct = ae.label) == null ? void 0 : Ct.length) || 0);
}).pop().label, { family: k, weight: O } = H, B = i ? Math.sqrt(Math.pow(S * 0.7, 2) / 2) : S;
let D = B;
s.font.size > 0 && (D = s.font.size);
let F = 0, N = 0;
do {
d.font = `${O} ${D}px ${k}`;
const tt = d.measureText(E);
F = tt.width, N = tt.fontBoundingBoxDescent, D--;
} while (F > B && D > 0);
const Tt = B / N, oe = Math.min(B / F, Tt), re = Math.floor(D * oe);
return [`${O} ${re}px ${k}`, Tt];
}
function g(m, S, H, x, E) {
m.font = f, m.textAlign = "center", m.textBaseline = "middle", m.fillStyle = E, m.fillText(x, S, H + (i ? v : 0));
}
}, Re = (s, e) => s.offset.x = (e ? 0.5 : 0) + s.userData.offsetX, Mt = (s, e) => {
const {
offset: t,
userData: { offsetY: n, cellHeight: i }
} = s;
t.y = 1 - (e + 1) * i + n;
};
function Ut(s, e, t = 2, n = 2) {
const i = t / 2 - s, o = n / 2 - s, a = s / t, c = (t - s) / t, l = s / n, u = (n - s) / n, r = [i, o, 0, -i, o, 0, -i, -o, 0, i, -o, 0], h = [c, u, a, u, a, l, c, l], d = [
3 * (e + 1) + 3,
3 * (e + 1) + 4,
e + 4,
e + 5,
2 * (e + 1) + 4,
2,
1,
2 * (e + 1) + 3,
3,
4 * (e + 1) + 3,
4,
0
], f = [0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, 8, 9, 10, 8, 10, 11].map(
(g) => d[g]
);
let v, p, w, A, L, b, y, C;
for (let g = 0; g < 4; g++) {
A = g < 1 || g > 2 ? i : -i, L = g < 2 ? o : -o, b = g < 1 || g > 2 ? c : a, y = g < 2 ? u : l;
for (let m = 0; m <= e; m++)
v = Math.PI / 2 * (g + m / e), p = Math.cos(v), w = Math.sin(v), r.push(A + s * p, L + s * w, 0), h.push(b + a * p, y + l * w), m < e && (C = (e + 1) * g + m + 4, f.push(g, C, C + 1));
}
return new $t().setIndex(new at(new Uint32Array(f), 1)).setAttribute(
"position",
new at(new Float32Array(r), 3)
).setAttribute("uv", new at(new Float32Array(h), 2));
}
const Ge = (s, e) => {
const t = new M(), { isSphere: n, radius: i, smoothness: o, type: a } = s, l = a === "rounded-cube" ? 2 - s.edges.radius * 2 : 2, u = Ut(i, o, l, l);
return $.map((r, h) => {
const d = h < 3, f = $[h], v = h ? e.clone() : e;
Mt(v, h);
const { enabled: p, scale: w, opacity: A, hover: L } = s[f], b = {
map: v,
opacity: A,
transparent: !0
}, y = n ? new xt(new Et(b)) : new X(u, new gt(b)), C = d ? f : f[1];
if (y.position[C] = (d ? 1 : -1) * (n ? ne : 1), !n) {
y.lookAt(t.copy(y.position).multiplyScalar(1.7));
const g = I.DEFAULT_UP.z === 1, m = I.DEFAULT_UP.x === 1;
(g || m) && (f === "z" && g || f === "x" && m ? y.rotateZ(-Math.PI / 2) : (f === "nz" && g || f === "nx" && m) && y.rotateZ(Math.PI / 2));
}
return y.scale.setScalar(w), y.renderOrder = 1, y.visible = p, y.userData = {
scale: w,
opacity: A,
hover: L
}, y;
});
}, Fe = (s, e) => {
const { isSphere: t, corners: n, type: i } = s, o = i === "rounded-cube";
if (!n.enabled) return [];
const { color: a, opacity: c, scale: l, radius: u, smoothness: r, hover: h } = n, d = t ? null : o ? new Qt(u, r * 2, r) : Ut(u, r), f = {
transparent: !0,
opacity: c
}, v = o ? 1 - u : 0.85, p = [
1,
1,
1,
-1,
1,
1,
1,
-1,
1,
-1,
-1,
1,
1,
1,
-1,
-1,
1,
-1,
1,
-1,
-1,
-1,
-1,
-1
].map((A) => A * v), w = new M();
return Array(p.length / 3).fill(0).map((A, L) => {
if (t) {
const C = e.clone();
Mt(C, 6), f.map = C;
} else
f.color = a;
const b = t ? new xt(new Et(f)) : new X(d, new gt(f)), y = L * 3;
return b.position.set(p[y], p[y + 1], p[y + 2]), t && b.position.normalize().multiplyScalar(1.7), b.scale.setScalar(l), b.lookAt(w.copy(b.position).multiplyScalar(2)), b.renderOrder = 1, b.userData = {
color: a,
opacity: c,
scale: l,
hover: h,
intersectionOrder: 1
}, b;
});
}, Ie = (s, e, t) => {
const { isSphere: n, edges: i, type: o } = s, a = o === "rounded-cube";
if (!i.enabled) return [];
const { color: c, opacity: l, scale: u, hover: r, radius: h, smoothness: d } = i, f = a ? 2 - h * 2 : 1.2, v = n ? null : a ? new fe(h, h, f, d * 4) : Ut(h, d, f, 0.25), p = {
transparent: !0,
opacity: l
}, w = a ? 1 - h : 0.925, A = [
0,
1,
1,
0,
-1,
1,
1,
0,
1,
-1,
0,
1,
0,
1,
-1,
0,
-1,
-1,
1,
0,
-1,
-1,
0,
-1,
1,
1,
0,
1,
-1,
0,
-1,
1,
0,
-1,
-1,
0
].map((y) => y * w), L = new M(), b = new M(0, 1, 0);
return Array(A.length / 3).fill(0).map((y, C) => {
if (n) {
const S = e.clone();
Mt(S, t), p.map = S;
} else
p.color = c;
const g = n ? new xt(new Et(p)) : new X(v, new gt(p)), m = C * 3;
return g.position.set(A[m], A[m + 1], A[m + 2]), n && g.position.normalize().multiplyScalar(1.7), g.scale.setScalar(u), g.up.copy(b), g.lookAt(L.copy(g.position).multiplyScalar(2)), a ? (!n && !g.position.z && (g.rotation.z = Math.PI), !n && !g.position.x && (g.rotation.x = 0), !n && !g.position.x && (g.rotation.z = Math.PI / 2)) : !n && !g.position.y && (g.rotation.z = Math.PI / 2), g.renderOrder = 1, g.userData = {
color: c,
opacity: l,
scale: u,
hover: r
}, g;
});
};
function He(s, e = !1) {
const t = s[0].index !== null, n = new Set(Object.keys(s[0].attributes)), i = new Set(Object.keys(s[0].morphAttributes)), o = {}, a = {}, c = s[0].morphTargetsRelative, l = new $t();
let u = 0;
for (let r = 0; r < s.length; ++r) {
const h = s[r];
let d = 0;
if (t !== (h.index !== null))
return console.error("THREE.BufferGeometryUtils: .mergeGeometries() failed with geometry at index " + r + ". All geometries must have compatible attributes; make sure index attribute exists among all geometries, or in none of them."), null;
for (const f in h.attributes) {
if (!n.has(f))
return console.error("THREE.BufferGeometryUtils: .mergeGeometries() failed with geometry at index " + r + '. All geometries must have compatible attributes; make sure "' + f + '" attribute exists among all geometries, or in none of them.'), null;
o[f] === void 0 && (o[f] = []), o[f].push(h.attributes[f]), d++;
}
if (d !== n.size)
return console.error("THREE.BufferGeometryUtils: .mergeGeometries() failed with geometry at index " + r + ". Make sure all geometries have the same number of attributes."), null;
if (c !== h.morphTargetsRelative)
return console.error("THREE.BufferGeometryUtils: .mergeGeometries() failed with geometry at index " + r + ". .morphTargetsRelative must be consistent throughout all geometries."), null;
for (const f in h.morphAttributes) {
if (!i.has(f))
return console.error("THREE.BufferGeometryUtils: .mergeGeometries() failed with geometry at index " + r + ". .morphAttributes must be consistent throughout all geometries."), null;
a[f] === void 0 && (a[f] = []), a[f].push(h.morphAttributes[f]);
}
if (e) {
let f;
if (t)
f = h.index.count;
else if (h.attributes.position !== void 0)
f = h.attributes.position.count;
else
return console.error("THREE.BufferGeometryUtils: .mergeGeometries() failed with geometry at index " + r + ". The geometry must have either an index or a position attribute"), null;
l.addGroup(u, f, r), u += f;
}
}
if (t) {
let r = 0;
const h = [];
for (let d = 0; d < s.length; ++d) {
const f = s[d].index;
for (let v = 0; v < f.count; ++v)
h.push(f.getX(v) + r);
r += s[d].attributes.position.count;
}
l.setIndex(h);
}
for (const r in o) {
const h = Ht(o[r]);
if (!h)
return console.error("THREE.BufferGeometryUtils: .mergeGeometries() failed while trying to merge the " + r + " attribute."), null;
l.setAttribute(r, h);
}
for (const r in a) {
const h = a[r][0].length;
if (h === 0) break;
l.morphAttributes = l.morphAttributes || {}, l.morphAttributes[r] = [];
for (let d = 0; d < h; ++d) {
const f = [];
for (let p = 0; p < a[r].length; ++p)
f.push(a[r][p][d]);
const v = Ht(f);
if (!v)
return console.error("THREE.BufferGeometryUtils: .mergeGeometries() failed while trying to merge the " + r + " morphAttribute."), null;
l.morphAttributes[r].push(v);
}
}
return l;
}
function Ht(s) {
let e, t, n, i = -1, o = 0;
for (let u = 0; u < s.length; ++u) {
const r = s[u];
if (e === void 0 && (e = r.array.constructor), e !== r.array.constructor)
return console.error("THREE.BufferGeometryUtils: .mergeAttributes() failed. BufferAttribute.array must be of consistent array types across matching attributes."), null;
if (t === void 0 && (t = r.itemSize), t !== r.itemSize)
return console.error("THREE.BufferGeometryUtils: .mergeAttributes() failed. BufferAttribute.itemSize must be consistent across matching attributes."), null;
if (n === void 0 && (n = r.normalized), n !== r.normalized)
return console.error("THREE.BufferGeometryUtils: .mergeAttributes() failed. BufferAttribute.normalized must be consistent across matching attributes."), null;
if (i === -1 && (i = r.gpuType), i !== r.gpuType)
return console.error("THREE.BufferGeometryUtils: .mergeAttributes() failed. BufferAttribute.gpuType must be consistent across matching attributes."), null;
o += r.count * t;
}
const a = new e(o), c = new at(a, t, n);
let l = 0;
for (let u = 0; u < s.length; ++u) {
const r = s[u];
if (r.isInterleavedBufferAttribute) {
const h = l / t;
for (let d = 0, f = r.count; d < f; d++)
for (let v = 0; v < t; v++) {
const p = r.getComponent(d, v);
c.setComponent(d + h, v, p);
}
} else
a.set(r.array, l);
l += r.count * t;
}
return i !== void 0 && (c.gpuType = i), c;
}
const ke = (s, e) => {
const {
isSphere: t,
background: { enabled: n, color: i, opacity: o, hover: a }
} = e;
let c;
const l = new gt({
color: i,
side: pe,
opacity: o,
transparent: !0,
depthWrite: !1
});
if (!n) return null;
if (t)
c = new X(
new Qt(1.8, 64, 64),
l
);
else {
let u;
s.forEach((r) => {
const h = r.scale.x;
r.scale.setScalar(0.9), r.updateMatrix();
const d = r.geometry.clone();
d.applyMatrix4(r.matrix), u = u ? He([u, d]) : d, r.scale.setScalar(h);
}), c = new X(u, l);
}
return c.userData = {
color: i,
opacity: o,
hover: a
}, c;
}, kt = new At(), nt = new M();
class ie extends me {
constructor() {
super(), this.isLineSegmentsGeometry = !0, this.type = "LineSegmentsGeometry";
const e = [-1, 2, 0, 1, 2, 0, -1, 1, 0, 1, 1, 0, -1, 0, 0, 1, 0, 0, -1, -1, 0, 1, -1, 0], t = [-1, 2, 1, 2, -1, 1, 1, 1, -1, -1, 1, -1, -1, -2, 1, -2], n = [0, 2, 1, 2, 3, 1, 2, 4, 3, 4, 5, 3, 4, 6, 5, 6, 7, 5];
this.setIndex(n), this.setAttribute("position", new Bt(e, 3)), this.setAttribute("uv", new Bt(t, 2));
}
applyMatrix4(e) {
const t = this.attributes.instanceStart, n = this.attributes.instanceEnd;
return t !== void 0 && (t.applyMatrix4(e), n.applyMatrix4(e), t.needsUpdate = !0), this.boundingBox !== null && this.computeBoundingBox(), this.boundingSphere !== null && this.computeBoundingSphere(), this;
}
setPositions(e) {
let t;
e instanceof Float32Array ? t = e : Array.isArray(e) && (t = new Float32Array(e));
const n = new bt(t, 6, 1);
return this.setAttribute("instanceStart", new Z(n, 3, 0)), this.setAttribute("instanceEnd", new Z(n, 3, 3)), this.instanceCount = this.attributes.instanceStart.count, this.computeBoundingBox(), this.computeBoundingSphere(), this;
}
setColors(e) {
let t;
e instanceof Float32Array ? t = e : Array.isArray(e) && (t = new Float32Array(e));
const n = new bt(t, 6, 1);
return this.setAttribute("instanceColorStart", new Z(n, 3, 0)), this.setAttribute("instanceColorEnd", new Z(n, 3, 3)), this;
}
fromWireframeGeometry(e) {
return this.setPositions(e.attributes.position.array), this;
}
fromEdgesGeometry(e) {
return this.setPositions(e.attributes.position.array), this;
}
fromMesh(e) {
return this.fromWireframeGeometry(new ge(e.geometry)), this;
}
fromLineSegments(e) {
const t = e.geometry;
return this.setPositions(t.attributes.position.array), this;
}
computeBoundingBox() {
this.boundingBox === null && (this.boundingBox = new At());
const e = this.attributes.instanceStart, t = this.attributes.instanceEnd;
e !== void 0 && t !== void 0 && (this.boundingBox.setFromBufferAttribute(e), kt.setFromBufferAttribute(t), this.boundingBox.union(kt));
}
computeBoundingSphere() {
this.boundingSphere === null && (this.boundingSphere = new Yt()), this.boundingBox === null && this.computeBoundingBox();
const e = this.attributes.instanceStart, t = this.attributes.instanceEnd;
if (e !== void 0 && t !== void 0) {
const n = this.boundingSphere.center;
this.boundingBox.getCenter(n);
let i = 0;
for (let o = 0, a = e.count; o < a; o++)
nt.fromBufferAttribute(e, o), i = Math.max(i, n.distanceToSquared(nt)), nt.fromBufferAttribute(t, o), i = Math.max(i, n.distanceToSquared(nt));
this.boundingSphere.radius = Math.sqrt(i), isNaN(this.boundingSphere.radius) && console.error("THREE.LineSegmentsGeometry.computeBoundingSphere(): Computed radius is NaN. The instanced position data is likely to have NaN values.", this);
}
}
toJSON() {
}
applyMatrix(e) {
return console.warn("THREE.LineSegmentsGeometry: applyMatrix() has been renamed to applyMatrix4()."), this.applyMatrix4(e);
}
}
lt.line = {
worldUnits: { value: 1 },
linewidth: { value: 1 },
resolution: { value: new K(1, 1) },
dashOffset: { value: 0 },
dashScale: { value: 1 },
dashSize: { value: 1 },
gapSize: { value: 1 }
// todo FIX - maybe change to totalSize
};
ct.line = {
uniforms: Jt.merge([
lt.common,
lt.fog,
lt.line
]),
vertexShader: (
/* glsl */
`
#include <common>
#include <color_pars_vertex>
#include <fog_pars_vertex>
#include <logdepthbuf_pars_vertex>
#include <clipping_planes_pars_vertex>
uniform float linewidth;
uniform vec2 resolution;
attribute vec3 instanceStart;
attribute vec3 instanceEnd;
attribute vec3 instanceColorStart;
attribute vec3 instanceColorEnd;
#ifdef WORLD_UNITS
varying vec4 worldPos;
varying vec3 worldStart;
varying vec3 worldEnd;
#ifdef USE_DASH
varying vec2 vUv;
#endif
#else
varying vec2 vUv;
#endif
#ifdef USE_DASH
uniform float dashScale;
attribute float instanceDistanceStart;
attribute float instanceDistanceEnd;
varying float vLineDistance;
#endif
void trimSegment( const in vec4 start, inout vec4 end ) {
// trim end segment so it terminates between the camera plane and the near plane
// conservative estimate of the near plane
float a = projectionMatrix[ 2 ][ 2 ]; // 3nd entry in 3th column
float b = projectionMatrix[ 3 ][ 2 ]; // 3nd entry in 4th column
float nearEstimate = - 0.5 * b / a;
float alpha = ( nearEstimate - start.z ) / ( end.z - start.z );
end.xyz = mix( start.xyz, end.xyz, alpha );
}
void main() {
#ifdef USE_COLOR
vColor.xyz = ( position.y < 0.5 ) ? instanceColorStart : instanceColorEnd;
#endif
#ifdef USE_DASH
vLineDistance = ( position.y < 0.5 ) ? dashScale * instanceDistanceStart : dashScale * instanceDistanceEnd;
vUv = uv;
#endif
float aspect = resolution.x / resolution.y;
// camera space
vec4 start = modelViewMatrix * vec4( instanceStart, 1.0 );
vec4 end = modelViewMatrix * vec4( instanceEnd, 1.0 );
#ifdef WORLD_UNITS
worldStart = start.xyz;
worldEnd = end.xyz;
#else
vUv = uv;
#endif
// special case for perspective projection, and segments that terminate either in, or behind, the camera plane
// clearly the gpu firmware has a way of addressing this issue when projecting into ndc space
// but we need to perform ndc-space calculations in the shader, so we must address this issue directly
// perhaps there is a more elegant solution -- WestLangley
bool perspective = ( projectionMatrix[ 2 ][ 3 ] == - 1.0 ); // 4th entry in the 3rd column
if ( perspective ) {
if ( start.z < 0.0 && end.z >= 0.0 ) {
trimSegment( start, end );
} else if ( end.z < 0.0 && start.z >= 0.0 ) {
trimSegment( end, start );
}
}
// clip space
vec4 clipStart = projectionMatrix * start;
vec4 clipEnd = projectionMatrix * end;
// ndc space
vec3 ndcStart = clipStart.xyz / clipStart.w;
vec3 ndcEnd = clipEnd.xyz / clipEnd.w;
// direction
vec2 dir = ndcEnd.xy - ndcStart.xy;
// account for clip-space aspect ratio
dir.x *= aspect;
dir = normalize( dir );
#ifdef WORLD_UNITS
vec3 worldDir = normalize( end.xyz - start.xyz );
vec3 tmpFwd = normalize( mix( start.xyz, end.xyz, 0.5 ) );
vec3 worldUp = normalize( cross( worldDir, tmpFwd ) );
vec3 worldFwd = cross( worldDir, worldUp );
worldPos = position.y < 0.5 ? start: end;
// height offset
float hw = linewidth * 0.5;
worldPos.xyz += position.x < 0.0 ? hw * worldUp : - hw * worldUp;
// don't extend the line if we're rendering dashes because we
// won't be rendering the endcaps
#ifndef USE_DASH
// cap extension
worldPos.xyz += position.y < 0.5 ? - hw * worldDir : hw * worldDir;
// add width to the box
worldPos.xyz += worldFwd * hw;
// endcaps
if ( position.y > 1.0 || position.y < 0.0 ) {
worldPos.xyz -= worldFwd * 2.0 * hw;
}
#endif
// project the worldpos
vec4 clip = projectionMatrix * worldPos;
// shift the depth of the projected points so the line
// segments overlap neatly
vec3 clipPose = ( position.y < 0.5 ) ? ndcStart : ndcEnd;
clip.z = clipPose.z * clip.w;
#else
vec2 offset = vec2( dir.y, - dir.x );
// undo aspect ratio adjustment
dir.x /= aspect;
offset.x /= aspect;
// sign flip
if ( position.x < 0.0 ) offset *= - 1.0;
// endcaps
if ( position.y < 0.0 ) {
offset += - dir;
} else if ( position.y > 1.0 ) {
offset += dir;
}
// adjust for linewidth
offset *= linewidth;
// adjust for clip-space to screen-space conversion // maybe resolution should be based on viewport ...
offset /= resolution.y;
// select end
vec4 clip = ( position.y < 0.5 ) ? clipStart : clipEnd;
// back to clip space
offset *= clip.w;
clip.xy += offset;
#endif
gl_Position = clip;
vec4 mvPosition = ( position.y < 0.5 ) ? start : end; // this is an approximation
#include <logdepthbuf_vertex>
#include <clipping_planes_vertex>
#include <fog_vertex>
}
`
),
fragmentShader: (
/* glsl */
`
uniform vec3 diffuse;
uniform float opacity;
uniform float linewidth;
#ifdef USE_DASH
uniform float dashOffset;
uniform float dashSize;
uniform float gapSize;
#endif
varying float vLineDistance;
#ifdef WORLD_UNITS
varying vec4 worldPos;
varying vec3 worldStart;
varying vec3 worldEnd;
#ifdef USE_DASH
varying vec2 vUv;
#endif
#else
varying vec2 vUv;
#endif
#include <common>
#include <color_pars_fragment>
#include <fog_pars_fragment>
#include <logdepthbuf_pars_fragment>
#include <clipping_planes_pars_fragment>
vec2 closestLineToLine(vec3 p1, vec3 p2, vec3 p3, vec3 p4) {
float mua;
float mub;
vec3 p13 = p1 - p3;
vec3 p43 = p4 - p3;
vec3 p21 = p2 - p1;
float d1343 = dot( p13, p43 );
float d4321 = dot( p43, p21 );
float d1321 = dot( p13, p21 );
float d4343 = dot( p43, p43 );
float d2121 = dot( p21, p21 );
float denom = d2121 * d4343 - d4321 * d4321;
float numer = d1343 * d4321 - d1321 * d4343;
mua = numer / denom;
mua = clamp( mua, 0.0, 1.0 );
mub = ( d1343 + d4321 * ( mua ) ) / d4343;
mub = clamp( mub, 0.0, 1.0 );
return vec2( mua, mub );
}
void main() {
#include <clipping_planes_fragment>
#ifdef USE_DASH
if ( vUv.y < - 1.0 || vUv.y > 1.0 ) discard; // discard endcaps
if ( mod( vLineDistance + dashOffset, dashSize + gapSize ) > dashSize ) discard; // todo - FIX
#endif
float alpha = opacity;
#ifdef WORLD_UNITS
// Find the closest points on the view ray and the line segment
vec3 rayEnd = normalize( worldPos.xyz ) * 1e5;
vec3 lineDir = worldEnd - worldStart;
vec2 params = closestLineToLine( worldStart, worldEnd, vec3( 0.0, 0.0, 0.0 ), rayEnd );
vec3 p1 = worldStart + lineDir * params.x;
vec3 p2 = rayEnd * params.y;
vec3 delta = p1 - p2;
float len = length( delta );
float norm = len / linewidth;
#ifndef USE_DASH
#ifdef USE_ALPHA_TO_COVERAGE
float dnorm = fwidth( norm );
alpha = 1.0 - smoothstep( 0.5 - dnorm, 0.5 + dnorm, norm );
#else
if ( norm > 0.5 ) {
discard;
}
#endif
#endif
#else
#ifdef USE_ALPHA_TO_COVERAGE
// artifacts appear on some hardware if a derivative is taken within a conditional
float a = vUv.x;
float b = ( vUv.y > 0.0 ) ? vUv.y - 1.0 : vUv.y + 1.0;
float len2 = a * a + b * b;
float dlen = fwidth( len2 );
if ( abs( vUv.y ) > 1.0 ) {
alpha = 1.0 - smoothstep( 1.0 - dlen, 1.0 + dlen, len2 );
}
#else
if ( abs( vUv.y ) > 1.0 ) {
float a = vUv.x;
float b = ( vUv.y > 0.0 ) ? vUv.y - 1.0 : vUv.y + 1.0;
float len2 = a * a + b * b;
if ( len2 > 1.0 ) discard;
}
#endif
#endif
vec4 diffuseColor = vec4( diffuse, alpha );
#include <logdepthbuf_fragment>
#include <color_fragment>
gl_FragColor = vec4( diffuseColor.rgb, alpha );
#include <tonemapping_fragment>
#include <colorspace_fragment>
#include <fog_fragment>
#include <premultiplied_alpha_fragment>
}
`
)
};
class zt extends ye {
constructor(e) {
super({
type: "LineMaterial",
uniforms: Jt.clone(ct.line.uniforms),
vertexShader: ct.line.vertexShader,
fragmentShader: ct.line.fragmentShader,
clipping: !0
// required for clipping support
}), this.isLineMaterial = !0, this.setValues(e);
}
get color() {
return this.uniforms.diffuse.value;
}
set color(e) {
this.uniforms.diffuse.value = e;
}
get worldUnits() {
return "WORLD_UNITS" in this.defines;
}
set worldUnits(e) {
e === !0 ? this.defines.WORLD_UNITS = "" : delete this.defines.WORLD_UNITS;
}
get linewidth() {
return this.uniforms.linewidth.value;
}
set linewidth(e) {
this.uniforms.linewidth && (this.uniforms.linewidth.value = e);
}
get dashed() {
return "USE_DASH" in this.defines;
}
set dashed(e) {
e === !0 !== this.dashed && (this.needsUpdate = !0), e === !0 ? this.defines.USE_DASH = "" : delete this.defines.USE_DASH;
}
get dashScale() {
return this.uniforms.dashScale.value;
}
set dashScale(e) {
this.uniforms.dashScale.value = e;
}
get dashSize() {
return this.uniforms.dashSize.value;
}
set dashSize(e) {
this.uniforms.dashSize.value = e;
}
get dashOffset() {
return this.uniforms.dashOffset.value;
}
set dashOffset(e) {
this.uniforms.dashOffset.value = e;
}
get gapSize() {
return this.uniforms.gapSize.value;
}
set gapSize(e) {
this.uniforms.gapSize.value = e;
}
get opacity() {
return this.uniforms.opacity.value;
}
set opacity(e) {
this.uniforms && (this.uniforms.opacity.value = e);
}
get resolution() {
return this.uniforms.resolution.value;
}
set resolution(e) {
this.uniforms.resolution.value.copy(e);
}
get alphaToCoverage() {
return "USE_ALPHA_TO_COVERAGE" in this.defines;
}
set alphaToCoverage(e) {
this.defines && (e === !0 !== this.alphaToCoverage && (this.needsUpdate = !0), e === !0 ? this.defines.USE_ALPHA_TO_COVERAGE = "" : delete this.defines.USE_ALPHA_TO_COVERAGE);
}
}
const yt = new Q(), jt = new M(), Wt = new M(), U = new Q(), z = new Q(), P = new Q(), _t = new M(), vt = new Kt(), T = new _e(), qt = new M(), it = new At(), st = new Yt(), R = new Q();
let G, q;
function Nt(s, e, t) {
return R.set(0, 0, -e, 1).applyMatrix4(s.projectionMatrix), R.multiplyScalar(1 / R.w), R.x = q / t.width, R.y = q / t.height, R.applyMatrix4(s.projectionMatrixInverse), R.multiplyScalar(1 / R.w), Math.abs(Math.max(R.x, R.y));
}
function je(s, e) {
const t = s.matrixWorld, n = s.geometry, i = n.attributes.instanceStart, o = n.attributes.instanceEnd, a = Math.min(n.instanceCount, i.count);
for (let c = 0, l = a; c < l; c++) {
T.start.fromBufferAttribute(i, c), T.end.fromBufferAttribute(o, c), T.applyMatrix4(t);
const u = new M(), r = new M();
G.distanceSqToSegment(T.start, T.end, r, u), r.distanceTo(u) < q * 0.5 && e.push({
point: r,
pointOnLine: u,
distance: G.origin.distanceTo(r),
object: s,
face: null,
faceIndex: c,
uv: null,
uv1: null
});
}
}
function We(s, e, t) {
const n = e.projectionMatrix, o = s.material.resolution, a = s.matrixWorld, c = s.geometry, l = c.attributes.instanceStart, u = c.attributes.instanceEnd, r = Math.min(c.instanceCount, l.count), h = -e.near;
G.at(1, P), P.w = 1, P.applyMatrix4(e.matrixWorldInverse), P.applyMatrix4(n), P.multiplyScalar(1 / P.w), P.x *= o.x / 2, P.y *= o.y / 2, P.z = 0, _t.copy(P), vt.multiplyMatrices(e.matrixWorldInverse, a);
for (let d = 0, f = r; d < f; d++) {
if (U.fromBufferAttribute(l, d), z.fromBufferAttribute(u, d), U.w = 1, z.w = 1, U.applyMatrix4(vt), z.applyMatrix4(vt), U.z > h && z.z > h)
continue;
if (U.z > h) {
const b = U.z - z.z, y = (U.z - h) / b;
U.lerp(z, y);
} else if (z.z > h) {
const b = z.z - U.z, y = (z.z - h) / b;
z.lerp(U, y);
}
U.applyMatrix4(n), z.applyMatrix4(n), U.multiplyScalar(1 / U.w), z.multiplyScalar(1 / z.w), U.x *= o.x / 2, U.y *= o.y / 2, z.x *= o.x / 2, z.y *= o.y / 2, T.start.copy(U), T.start.z = 0, T.end.copy(z), T.end.z = 0;
const p = T.closestPointToPointParameter(_t, !0);
T.at(p, qt);
const w = ve.lerp(U.z, z.z, p), A = w >= -1 && w <= 1, L = _t.distanceTo(qt) < q * 0.5;
if (A && L) {
T.start.fromBufferAttribute(l, d), T.end.fromBufferAttribute(u, d), T.start.applyMatrix4(a), T.end.applyMatrix4(a);
const b = new M(), y = new M();
G.distanceSqToSegment(T.start, T.end, y, b), t.push({
point: y,
pointOnLine: b,
distance: G.origin.distanceTo(y),
object: s,
face: null,
faceIndex: d,
uv: null,
uv1: null
});
}
}
}
class qe extends X {
constructor(e = new ie(), t = new zt({ color: Math.random() * 16777215 })) {
super(e, t), this.isLineSegments2 = !0, this.type = "LineSegments2";
}
// for backwards-compatibility, but could be a method of LineSegmentsGeometry...
computeLineDistances() {
const e = this.geometry, t = e.attributes.instanceStart, n = e.attributes.instanceEnd, i = new Float32Array(2 * t.count);
for (let a = 0, c = 0, l = t.count; a < l; a++, c += 2)
jt.fromBufferAttribute(t, a), Wt.fromBufferAttribute(n, a), i[c] = c === 0 ? 0 : i[c - 1], i[c + 1] = i[c] + jt.distanceTo(Wt);
const o = new bt(i, 2, 1);
return e.setAttribute("instanceDistanceStart", new Z(o, 1, 0)), e.setAttribute("instanceDistanceEnd", new Z(o, 1, 1)), this;
}
raycast(e, t) {
const n = this.material.worldUnits, i = e.camera;
i === null && !n && console.error('LineSegments2: "Raycaster.camera" needs to be set in order to raycast against LineSegments2 while worldUnits is set to false.');
const o = e.params.Line2 !== void 0 && e.params.Line2.threshold || 0;
G = e.ray;
const a = this.matrixWorld, c = this.geometry, l = this.material;
q = l.linewidth + o, c.boundingSphere === null && c.computeBoundingSphere(), st.copy(c.boundingSphere).applyMatrix4(a);
let u;
if (n)
u = q * 0.5;
else {
const h = Math.max(i.near, st.distanceToPoint(G.origin));
u = Nt(i, h, l.resolution);
}
if (st.radius += u, G.intersectsSphere(st) === !1)
return;
c.boundingBox === null && c.computeBoundingBox(), it.copy(c.boundingBox).applyMatrix4(a);
let r;
if (n)
r = q * 0.5;
else {
const h = Math.max(i.near, it.distanceToPoint(G.origin));
r = Nt(i, h, l.resolution);
}
it.expandByScalar(r), G.intersectsBox(it) !== !1 && (n ? je(this, t) : We(this, i, t));
}
onBeforeRender(e) {
const t = this.material.uniforms;
t && t.resolution && (e.getViewport(yt), this.material.uniforms.resolution.value.set(yt.z, yt.w));
}
}
class se extends ie {
constructor() {
super(), this.isLineGeometry = !0, this.type = "LineGeometry";
}
setPositions(e) {
const t = e.length - 3, n = new Float32Array(2 * t);
for (let i = 0; i < t; i += 3)
n[2 * i] = e[i], n[2 * i + 1] = e[i + 1], n[2 * i + 2] = e[i + 2], n[2 * i + 3] = e[i + 3], n[2 * i + 4] = e[i + 4], n[2 * i + 5] = e[i + 5];
return super.setPositions(n), this;
}
setColors(e) {
const t = e.length - 3, n = new Float32Array(2 * t);
for (let i = 0; i < t; i += 3)
n[2 * i] = e[i], n[2 * i + 1] = e[i + 1], n[2 * i + 2] = e[i + 2], n[2 * i + 3] = e[i + 3], n[2 * i + 4] = e[i + 4], n[2 * i + 5] = e[i + 5];
return super.setColors(n), this;
}
setFromPoints(e) {
const t = e.length - 1, n = new Float32Array(6 * t);
for (let i = 0; i < t; i++)
n[6 * i] = e[i].x, n[6 * i + 1] = e[i].y, n[6 * i + 2] = e[i].z || 0, n[6 * i + 3] = e[i + 1].x, n[6 * i + 4] = e[i + 1].y, n[6 * i + 5] = e[i + 1].z || 0;
return super.setPositions(n), this;
}
fromLine(e) {
const t = e.geometry;
return this.setPositions(t.attributes.position.array), this;
}
}
class Ne extends qe {
constructor(e = new se(), t = new zt({ color: Math.random() * 16777215 })) {
super(e, t), this.isLine2 = !0, this.type = "Line2";
}
}
const Ve = (s) => {
const e = new Xt(), t = [], n = [], { isSphere: i } = s;
if ($.forEach((c, l) => {
const { enabled: u, line: r, scale: h, color: d } = s[c];
if (!u || !r) return;
const f = l < 3 ? 1 : -1, p = (i ? ne - h / 2 : 0.975) * f;
t.push(
c.includes("x") ? p : 0,
c.includes("y") ? p : 0,
c.includes("z") ? p : 0,
0,
0,
0
);
const w = e.set(d).toArray();
n.push(...w, ...w);
}), !t.length) return null;
const o = new se().setPositions(t).setColors(n), a = new zt({
linewidth: s.lineWidth,
vertexColors: !0,
resolution: new K(window.innerWidth, window.innerHeight)
});
return new Ne(o, a).computeLineDistances();
}, Ze = (s) => {
const { corners: e, edges: t } = s, n = [], i = Pe(s), o = Ge(s, i);
n.push(...o), e.enabled && n.push(...Fe(s, i)), t.enabled && n.push(...Ie(s, i, e.enabled ? 7 : 6));
const a = ke(o, s), c = Ve(s);
return [n, a, c];
}, Y = (s, e = !0) => {
const { material: t, userData: n } = s, { opacity: i, color: o, scale: a } = e ? n.hover : n;
s.scale.setScalar(a), t.opacity = i, t.map ? Re(t.map, e) : t.color.set(o);
}, V = /* @__PURE__ */ new Kt(), Vt = /* @__PURE__ */ new Ee(), Xe = /* @__PURE__ */ new K(), W = /* @__PURE__ */ new M(), Zt = /* @__PURE__ */ new Q(), ot = /* @__PURE__ */ new J().setFromAxisAngle(new M(0, 0, 1), Math.PI / 2), rt = /* @__PURE__ */ new J().setFromAxisAngle(new M(0, 0, 1), -Math.PI / 2);
class Ye extends I {
/**
* Creates a new ViewportGizmo instance.
*
* @param camera - The camera to be controlled by this gizmo
* @param renderer - The WebGL renderer used to render the scene
* @param options - {@link GizmoOptions}, Configuration options for the gizmo.
* @param options.container - Parent element for the gizmo. Can be an HTMLElement or a CSS selector string
* @param options.type - The gizmo configuration type. Either 'sphere' or 'cube', defaults to 'sphere'
* @param options.size - Size of the gizmo widget in pixels. Defaults to 128
* @param options.placement - Position of the gizmo in the viewport
* Options include:
* - `"top-left"`
* - `"top-center"`
* - `"top-right"`
* - `"center-left"`
* - `"center-center"`
* - `"center-right"`
* - `"bottom-left"`
* - `"bottom-center"`
* - `"bottom-right"`
* @param options.offset - Offset of the gizmo from container edges in pixels
* @param options.offset.left - Offset from the left edge
* @param options.offset.top - Offset from the top edge
* @param options.offset.right - Offset from the right edge
* @param options.offset.bottom - Offset from the bottom edge
* @param options.animated - Whether view changes should be animated. Defaults to true
* @param options.speed - Animation speed multiplier. Defaults to 1
* @param options.resolution - Texture resolution. Defaults to 64 for sphere, 128 for cube
* @param options.lineWidth - Width of the axes lines in pixels
* @param options.id - HTML `id` attribute for the gizmo container
* @param options.className - HTML `class` attribute for the gizmo container
* @param options.font - Font configuration for axis labels
* @param options.font.family - Font family for axis labels
* @param options.font.weight - Font weight for axis labels
* @param options.background - Configuration for the background sphere/cube
* @param options.background.enabled - Whether to display the background
* @param options.background.color - Color of the background in normal state
* @param options.background.opacity - Opacity of the background in normal state
* @param options.background.hover.color - Color of the background when hovered
* @param options.background.hover.opacity - Opacity of the background when hovered
* @param options.corners - Configuration for corner indicators
* @param options.corners.enabled - Whether to display corner indicators
* @param options.corners.color - Base color of corner indicators
* @param options.corners.opacity - Opacity of corner indicators
* @param options.corners.scale - Scale multiplier for corner indicators
* @param options.corners.radius - Radius of corner indicators
* @param options.corners.smoothness - Smoothness of corner indicators
* @param options.corners.hover.color - Color of corner indicators when hovered
* @param options.corners.hover.opacity - Opacity of corner indicators when hovered
* @param options.corners.hover.scale - Scale of corner indicators when hovered
* @param options.edges - Configuration for edge indicators
* @param options.edges.enabled - Whether to display edge indicators
* @param options.edges.color - Base color of edge indicators
* @param options.edges.opacity - Opacity of edge indicators
* @param options.edges.scale - Scale multiplier for edge indicators
* @param options.edges.radius - Radius of edge indicators
* @param options.edges.smoothness - Smoothness of edge indicators
* @param options.edges.hover.color - Color of edge indicators when hovered
* @param options.edges.hover.opacity - Opacity of edge indicators when hovered
* @param options.edges.hover.scale - Scale of edge indicators when hovered
* @param options.x - Configuration for positive X axis/face
* @param options.y - Configuration for positive Y axis/face
* @param options.z - Configuration for positive Z axis/face
* @param options.nx - Configuration for negative X axis/face
* @param options.ny - Configuration for negative Y axis/face
* @param options.nz - Configuration for negative Z axis/face
*
* @remarks Axis-specific configuration can also use alias names for cube mode:
* - `right` (same as `x`)
* - `left` (same as `nx`)
* - `top` (same as `y`)
* - `bottom` (same as `ny`)
* - `front` (same as `z`)
* - `back` (same as `nz`)
*
* For each axis/face configuration, the following options are available:
* @param options.AXIS.enabled - Whether to draw the axis
* @param options.AXIS.label - Custom text label for the axis
* @param options.AXIS.opacity - Axis opacity
* @param options.AXIS.scale - Scale multiplier for indicator size
* @param options.AXIS.line - Whether to draw the axis line
* @param options.AXIS.color - Axis indicator background color
* @param options.AXIS.labelColor - Axis label color
* @param options.AXIS.border.size - Border size around the axis indicator
* @param options.AXIS.border.color - Border color around the axis indicator
* @param options.AXIS.hover.color - Fill color on hover
* @param options.AXIS.hover.labelColor - Label text color on hover
* @param options.AXIS.hover.opacity - Opacity when hovered
* @param options.AXIS.hover.scale - Indicator scale when hovered
* @param options.AXIS.hover.border.size - Hover border size
* @param options.AXIS.hover.border.color - Hover border color
*/
constructor(t, n, i = {}) {
super();
/** Whether the gizmo is currently active and responding to user input */
_(this, "enabled", !0);
/** The camera being controlled by this gizmo */
_(this, "camera");
/** The WebGLRenderer rendering the gizmo */
_(this, "renderer");
/** The configuration options */
_(this, "options");
/** The point around which the camera rotates */
_(this, "target", new M());
/** Whether view changes should be animated */
_(this, "animated", !0);
/** The speed of view change animations. Higher values result in faster animations */
_(this, "speed", 1);
/**
* Indicates whether the gizmo is currently being animated or not,
* Useful when interacting with other camera controllers
*
* @readonly This value is set internally.
**/
_(this, "animating", !1);
_(this, "_options");
_(this, "_intersections");
_(this, "_background", null);
_(this, "_viewport", [0, 0, 0, 0]);
_(this, "_originalViewport", [0, 0, 0, 0]);
_(this, "_originalScissor", [0, 0, 0, 0]);
_(this, "_scene");
_(this, "_camera");
_(this, "_container");
_(this, "_domElement");
_(this, "_domRect");
_(this, "_dragging", !1);
_(this, "_distance", 0);
_(this, "_clock", new be());
_(this, "_targetQuaternion", new J());
_(this, "_quaternionStart", new J());
_(this, "_quaternionEnd", new J());
_(this, "_pointerStart", new K());
_(this, "_focus", null);
_(this, "_placement");
_(this, "_controls");
_(this, "_controlsListeners");
this.camera = t, this.renderer = n, this._scene = new we().add(this), this.set(i);
}
/** Gets the current placement of the gizmo relative to its container. */
get placement() {
return this._placement;
}
/**
* Sets and update the placement of the gizmo relative to its container.
*
* @param placement - The new placement position
*/
set placement(t) {
this._placement = te(this._domElement, t), this.domUpdate();
}
/**
* Regenerates the gizmo with the new options.
*
* @remarks
* - Not recommended for use in real-time rendering or animation loops
* - Provides a way to completely rebuild the gizmo with new options
* - Can be computationally expensive, so use sparingly
*/
set(t = {}) {
this.dispose(), this.options = t, this._options = De(t), this._camera = this._options.isSphere ? new Se(-1.8, 1.8, 1.8, -1.8, 5, 10) : new xe(26, 1, 5, 10), this._camera.position.set(0, 0, 7);
const [n, i, o] = Ze(this._options);
i && this.add(i), o && this.add(o), this.add(...n), this._background = i, this._intersections = n;
const { container: a, animated: c, speed: l } = this._options;
return this.animated = c, this.speed = l, this._container = a ? Me(a) : document.body, this._domElement = Ae(this._options), this._domElement.onpointerdown = (u) => this._onPointerDown(u), this._domElement.onpointermove = (u) => this._onPointerMove(u), this._domElement.onpointerleave = () => this._onPointerLeave(), this._container.appendChild(this._domElement), this._controls && this.attachControls(this._controls), this.update(), this;
}
/**
* Renders the gizmo to the screen.
* This method handles viewport and scissor management to ensure the gizmo
* renders correctly without affecting the main scene rendering.
*
* @returns The gizmo instance for method chaining
*/
render() {
this.animating && this._animate();
const { renderer: t, _viewport: n } = this, i = t.getScissorTest(), o = t.autoClear;
return t.autoClear = !1, t.setViewport(...n), i && t.setScissor(...n), t.clear(!1, !0, !1), t.render(this._scene, this._camera), t.setViewport(...this._originalViewport), i && t.setScissor(...this._originalScissor), t.autoClear = o, this;
}
/**
* Updates the gizmo's DOM-related properties based on its current position
* and size in the document.
*
* @returns The gizmo instance for method chaining
*/
domUpdate() {
this._domRect = this._domElement.getBoundingClientRect();
const t = this.renderer, n = this._domRect, i = t.domElement.getBoundingClientRect();
return this._viewport.splice(
0,
4,
n.left - i.left,
t.domElement.clientHeight - (n.top - i.top + n.height),
n.width,
n.height
), t.getViewport(Zt).toArray(this._originalViewport), t.getScissorTest() && t.getScissor(Zt).toArray(this._originalScissor), this;
}
/**
* Updates the gizmo's orientation to match the current camera orientation.
*
* @returns The gizmo instance for method chaining
*/
cameraUpdate() {
return this._updateOrientation(), this;
}
/**
* Performs a complete update of the gizmo, including both DOM and camera-related updates.
*
* @param controls - Internal. Set to `false` if the update event comes from the attached controls.
*
* @returns The gizmo instance for method chaining
*/
update(t = !0) {
return t && this._controls && this._controls.update(), this.domUpdate().cameraUpdate();
}
/**
* Connects OrbitControls with the gizmo, handling interaction states and updates.
* Automatically detaches any previously attached controls.
*
* @param controls - The scene's {@link https://threejs.org/docs/#examples/en/controls/OrbitControls OrbitControls}
*/
attachControls(t) {
return this.detachControls(), this.target = t.target, this._controlsListeners = {
start: () => t.enabled = !1,
end: () => t.enabled = !0,
change: () => this.update(!1)
}, this.addEventListener("start", this._controlsListeners.start), this.addEventListener("end", this._controlsListeners.end), t.addEventListener("change", this._controlsListeners.change), this._controls = t, this;
}
/** Removes all control event listeners and references. Safe to call multiple times. */
detachControls() {
if (!(!this._controlsListeners || !this._controls))
return this.target = new M().copy(this._controls.target), this.removeEventListener("start", this._controlsListeners.start), this.removeEventListener("end", this._controlsListeners.end), this._controls.removeEventListener(
"change",
this._controlsListeners.change
), this._controlsListeners = void 0, this._controls = void 0, this;
}
/** Cleans up all resources including geometries, materials, textures, and event listeners. */
dispose() {
var t;
this.detachControls(), this.children.forEach((n) => {
var o, a, c, l;
this.remove(n);
const i = n;
(o = i.material) == null || o.dispose(), (c = (a = i.material) == null ? void 0 : a.map) == null || c.dispose(), (l = i.geometry) == null || l.dispose();
}), (t = this._domElement) == null || t.remove();
}
/**
* Updates the gizmo's orientation either based on the camera or internal state.
*
* @private
* @param fromCamera - Whether to update based on camera orientation (true) or internal state (false)
*/
_updateOrientation(t = !0) {
t && (this.quaternion.copy(this.camera.quaternion).invert(), this.updateMatrixWorld()), Pt(this._options, this._intersections, this.camera);
}
/**
* Handles the animation of camera position and orientation changes.
*
* @private
*/
_animate() {
const { position: t, quaternion: n } = this.camera;
if (t.set(0, 0, 1), !this.animated) {
t.applyQuaternion(this._quaternionEnd).multiplyScalar(this._distance).add(this.target), n.copy(this._targetQuaternion), this._updateOrientation(), this.animating = !1, this.dispatchEvent({ type: "change" }), this.dispatchEvent({ type: "end" });
return;
}
this._controls && (this._controls.enabled = !1);
const o = this._clock.getDelta() * Te * this.speed;
if (this._quaternionStart.rotateTowards(this._quaternionEnd, o), t.applyQuaternion(this._quaternionStart).multiplyScalar(this._distance).add(this.target), n.rotateTowards(this._targetQuaternion, o), this._updateOrientation(), requestAnimationFrame(() => this.dispatchEvent({ type: "change" })), this._quaternionStart.angleTo(this._quaternionEnd) < et) {
if (this._controls) {
const a = this.camera.position.clone().sub(this.target).normalize(), c = I.DEFAULT_UP.z === 1 && Math.abs(a.z) > 0.99, l = I.DEFAULT_UP.x === 1 && Math.abs(a.x) > 0.99;
c ? this.camera.position.set(0, -1e-6, this.camera.position.z) : l && this.camera.position.set(this.camera.position.x, et, 0), this._controls.update(), this._controls.enabled = !0;
}
this.animating = !1, this.dispatchEvent({ type: "end" });
}
}
/**
* Sets the camera orientation to look at the target from a specific axis.
*
* @private
* @param position - The axis point position
*/
_setOrientation(t) {
const n = this.camera, i = this.target;
if (W.copy(t).multiplyScalar(this._distance), V.setPosition(W).lookAt(W, this.position, this.up), this._targetQuaternion.setFromRotationMatrix(V), W.add(i), V.lookAt(W, i, this.up), this._quaternionEnd.setFromRotationMatrix(V), V.setPosition(n.position).lookAt(n.position, i, this.up), this._quaternionStart.setFromRotationMatrix(V), I.DEFAULT_UP.z === 1 && Math.abs(t.z) > 0.99) {
const o = Math.sign(t.z);
this._targetQuaternion.multiply(o === 1 ? rt : ot), this._quaternionEnd.multiply(o === 1 ? rt : ot);
} else if (I.DEFAULT_UP.x === 1 && Math.abs(t.x) > 0.99) {
const o = Math.sign(t.x);
this._targetQuaternion.multiply(o === 1 ? rt : ot), this._quaternionEnd.multiply(o === 1 ? rt : ot);
}
this.animating = !0, this._clock.start(), this.dispatchEvent({ type: "start" });
}
/**
* Handles the pointer down event for starting drag operations.
*
* @private
* @param e - The pointer event
*/
_onPointerDown(t) {
if (!this.enabled) return;
const n = (u) => {
if (!this._dragging) {
if (ze(u, this._pointerStart)) return;
this._dragging = !0;
}
const r = Xe.set(u.clientX, u.clientY).sub(this._pointerStart).multiplyScalar(1 / this._domRect.width * Math.PI), h = this.coordinateConversion(
W.subVectors(this.camera.position, this.target)
), d = Vt.setFromVector3(h);
d.theta = c - r.x, d.phi = wt(
l - r.y,
et,
Math.PI - et
), this.coordinateConversion(
this.camera.position.setFromSpherical(d),
!0
).add(this.target), this.camera.lookAt(this.target), this.quaternion.copy(this.camera.quaternion).invert(), this._updateOrientation(!1), this.dispatchEvent({ type: "change" });
}, i = () => {
if (document.removeEventListener("pointermove", n, !1), document.removeEventListener("pointerup", i, !1), !this._dragging) return this._handleClick(t);
this._focus && (Y(this._focus, !1), this._focus = null), this._dragging = !1, this.dispatchEvent({ type: "end" });
};
if (this.animating) return;
t.preventDefault(), this._pointerStart.set(t.clientX, t.clientY);
const o = this.coordinateConversion(
W.subVectors(this.camera.position, this.target)
), a = Vt.setFromVector3(o), c = a.theta, l = a.phi;
this._distance = a.radius, document.addEventListener("pointermove", n, !1), document.addEventListener("pointerup", i, !1), this.dispatchEvent({ type: "start" });
}
/**
* Converts the input-coordinates from the standard Y-axis up to what is set in Object3D.DEFAULT_UP.
*
* @private
* @param target - The target Vector3 to be converted
* @param isSpherical - Whether or not the coordinates are for a sphere
* @returns The converted coordinates
*/
coordinateConversion(t, n = !1) {
const { x: i, y: o, z: a } = t, c = I.DEFAULT_UP;
return c.x === 1 ? n ? t.set(o, a, i) : t.set(a, i, o) : c.z === 1 ? n ? t.set(a, i, o) : t.set(o, a, i) : t;
}
/**
* Handles pointer move events for hover effects and drag operations.
*
* @private
* @param e - The pointer event
*/
_onPointerMove(t) {
!this.enabled || this._dragging || (this._background && It(this._background, !0), this._handleHover(t));
}
/**
* Handles pointer leave events to reset hover states.
*
* @private
*/
_onPointerLeave() {
!this.enabled || this._dragging || (this._background && It(this._background, !1), this._focus && Y(this._focus, !1), this._domElement.style.cursor = "");
}
/**
* Handles click events for axis selection.
*
* @private
* @param e - The pointer event
*/
_handleClick(t) {
const n = Ft(
t,
this._domRect,
this._camera,
this._intersections
);
this._focus && (Y(this._focus, !1), this._focus = null), n && (this._setOrientation(n.object.position), this.dispatchEvent({ type: "change" }));
}
/**
* Handles hover effects for interactive elements.
*
* @private
* @param e - The pointer event
*/
_handleHover(t) {
const n = Ft(
t,
this._domRect,
this._camera,
this._intersections
), i = (n == null ? void 0 : n.object) || null;
this._focus !== i && (this._domElement.style.cursor = i ? "pointer" : "", this._focus && Y(this._focus, !1), (this._focus = i) ? Y(i, !0) : Pt(this._options, this._intersections, this.camera));
}
}
export {
Ye as ViewportGizmo
};
//# sourceMappingURL=three-viewport-gizmo.js.map
================================================
FILE: dist/three-viewport-gizmo.umd.cjs
================================================
(function(L,o){typeof exports=="object"&&typeof module<"u"?o(exports,require("three")):typeof define=="function"&&define.amd?define(["exports","three"],o):(L=typeof globalThis<"u"?globalThis:L||self,o(L.ThreeViewportGizmo={},L.THREE))})(this,function(L,o){"use strict";var fe=Object.defineProperty;var pe=(L,o,j)=>o in L?fe(L,o,{enumerable:!0,configurable:!0,writable:!0,value:j}):L[o]=j;var v=(L,o,j)=>pe(L,typeof o!="symbol"?o+"":o,j);const j=(s,e)=>{const[t,n]=e.split("-");return Object.assign(s.style,{left:n==="left"?"0":n==="center"?"50%":"",right:n==="right"?"0":"",top:t==="top"?"0":t==="bottom"?"":"50%",bottom:t==="bottom"?"0":"",transform:`${n==="center"?"translateX(-50%)":""} ${t==="center"?"translateY(-50%)":""}`}),e},Gt=({placement:s,size:e,offset:t,id:n,className:i})=>{const r=document.createElement("div"),{top:c,left:l,right:d,bottom:f}=t;return Object.assign(r.style,{id:n,position:"absolute",zIndex:"1000",height:`${e}px`,width:`${e}px`,margin:`${c}px ${d}px ${f}px ${l}px`,borderRadius:"100%"}),j(r,s),n&&(r.id=n),i&&(r.className=i),r},Ft=s=>{const e=typeof s=="string"?document.querySelector(s):s;if(!e)throw Error("Invalid DOM element");return e};function lt(s,e,t){return Math.max(e,Math.min(t,s))}const Vt=[["x",0,3],["y",1,4],["z",2,5]],yt=new o.Vector3;function _t({isSphere:s},e,t){s&&(yt.set(0,0,1).applyQuaternion(t.quaternion),Vt.forEach(([n,i,r])=>{const c=yt[n];let l=e[i],d=l.userData.opacity;l.material.opacity=lt(c>=0?d:d/2,0,1),l=e[r],d=l.userData.opacity,l.material.opacity=lt(c>=0?d/2:d,0,1)}))}const jt=(s,e,t=10)=>Math.abs(s.clientX-e.x)<t&&Math.abs(s.clientY-e.y)<t,vt=new o.Raycaster,bt=new o.Vector2,wt=(s,e,t,n)=>{bt.set((s.clientX-e.left)/e.width*2-1,-((s.clientY-e.top)/e.height)*2+1),vt.setFromCamera(bt,t);const i=vt.intersectObjects(n,!1);if(i.length>0){i.sort((f,a)=>f.distance-a.distance);const c=.2,l=i[0].distance,d=i.filter(f=>f.distance<=l+c);d.length>1&&(d.sort((f,a)=>(a.object.userData.intersectionOrder||0)-(f.object.userData.intersectionOrder||0)),i.splice(0,d.length,...d))}const r=i.length?i[0]:null;return!r||!r.object.visible?null:r},$=1e-6,Ht=2*Math.PI,St=["x","y","z"],N=[...St,"nx","ny","nz"],kt=["x","z","y","nx","nz","ny"],Wt=["z","x","y","nz","nx","ny"],Y="Right",J="Top",K="Front",tt="Left",et="Bottom",nt="Back",qt=[Y,J,K,tt,et,nt].map(s=>s.toLocaleLowerCase()),xt=1.3,Et=(s,e=!0)=>{const{material:t,userData:n}=s,{color:i,opacity:r}=e?n.hover:n;t.color.set(i),t.opacity=r},H=s=>JSON.parse(JSON.stringify(s)),Nt={yUp:{x:Y,y:J,z:K,nx:tt,ny:et,nz:nt},zUp:{x:Y,y:nt,z:J,nx:tt,ny:K,nz:et},xUp:{x:J,y:K,z:Y,nx:et,ny:nt,nz:tt}},Zt=s=>{const e=s.type||"sphere",t=e==="sphere",n=e==="rounded-cube",i=s.resolution||t?64:128,r=o.Object3D.DEFAULT_UP,c=r.z===1,l=r.x===1,f=Nt[c?"zUp":l?"xUp":"yUp"],{container:a}=s;s.container=void 0,s=JSON.parse(JSON.stringify(s)),s.container=a;const p=c?kt:l?Wt:N;qt.forEach((m,S)=>{s[m]&&(s[p[S]]=s[m])});const u={enabled:!0,color:16777215,opacity:1,scale:.7,labelColor:2236962,line:!1,border:{size:0,color:14540253},hover:{color:t?16777215:9688043,labelColor:2236962,opacity:1,scale:.7,border:{size:0,color:14540253}}},h={line:!1,scale:t?.45:.7,hover:{scale:t?.5:.7}},b={type:e,container:document.body,size:128,placement:"top-right",resolution:i,lineWidth:4,radius:t?1:n?.3:.2,smoothness:18,animated:!0,speed:1,background:{enabled:!0,color:t?16777215:14739180,opacity:t?0:1,hover:{color:t?16777215:14739180,opacity:t?.2:1}},font:{family:"sans-serif",weight:900},offset:{top:10,left:10,bottom:10,right:10},corners:{enabled:!t,color:t?15915362:16777215,opacity:1,scale:t?.15:.2,radius:1,smoothness:18,hover:{color:t?16777215:9688043,opacity:1,scale:t?.2:.225}},edges:{enabled:!t,color:t?15915362:n?15658734:16777215,opacity:t?1:0,radius:t?1:.125,smoothness:18,scale:t?.15:1,hover:{color:t?16777215:9688043,opacity:1,scale:t?.2:1}},x:{...H(u),...t?{label:"X",color:16725587,line:!0}:{label:f.x}},y:{...H(u),...t?{label:"Y",color:9100032,line:!0}:{label:f.y}},z:{...H(u),...t?{label:"Z",color:2920447,line:!0}:{label:f.z}},nx:{...H(h),label:t?"":f.nx},ny:{...H(h),label:t?"":f.ny},nz:{...H(h),label:t?"":f.nz}};if(dt(s,b),n){const m=s;m.edges.radius=m.radius,m.edges.scale=1,m.edges.opacity=1,m.edges.hover.scale=1,m.edges.hover.opacity=1,m.corners.radius=m.radius,m.corners.scale=1,m.corners.opacity=1,m.corners.hover.scale=1,m.corners.hover.opacity=1,m.radius=0,N.forEach(S=>{m[S].scale=1,m[S].opacity=1,m[S].hover.scale=1,m[S].hover.opacity=1})}return St.forEach(m=>dt(s[`n${m}`],H(s[m]))),{...s,isSphere:t}};function dt(s,...e){if(s instanceof HTMLElement||typeof s!="object"||s===null)return s;for(const t of e)for(const n in t)n!=="container"&&n in t&&(s[n]===void 0?s[n]=t[n]:typeof t[n]=="object"&&!Array.isArray(t[n])&&(s[n]=dt(s[n]||{},t[n])));return s}const Xt=(s,e=2)=>{const t=new o.Color,n=e*2,{isSphere:i,resolution:r,radius:c,font:l,corners:d,edges:f}=s,a=N.map(g=>({...s[g],radius:c}));i&&d.enabled&&a.push(d),i&&f.enabled&&a.push(f);const p=document.createElement("canvas"),u=p.getContext("2d");p.width=r*2+n*2,p.height=r*a.length+n*a.length;const[h,b]=O(a,r,l);a.forEach(({radius:g,label:x,color:V,labelColor:E,border:A,hover:{color:q,labelColor:C,border:I}},G)=>{const F=r*G+G*n+e;_(e,F,e,r,g,x,A,V,E),_(r+e*3,F,e,r,g,x,I??A,q??V,C??E)});const m=a.length,S=e/(r*2),M=e/(r*6),B=1/m,w=new o.CanvasTexture(p);return w.repeat.set(.5-2*S,B-2*M),w.offset.set(S,1-M),Object.assign(w,{colorSpace:o.SRGBColorSpace,wrapS:o.RepeatWrapping,wrapT:o.RepeatWrapping,userData:{offsetX:S,offsetY:M,cellHeight:B}}),w;function _(g,x,V,E,A,q,C,I,G){if(A=A*(E/2),I!=null&&I!==""&&(F(),u.fillStyle=t.set(I).getStyle(),u.fill()),C&&C.size){const X=C.size*E/2;g+=X,x+=X,E-=C.size*E,A=Math.max(0,A-X),F(),u.strokeStyle=t.set(C.color).getStyle(),u.lineWidth=C.size*E,u.stroke()}q&&y(u,g+E/2,x+(E+V)/2,q,t.set(G).getStyle());function F(){u.beginPath(),u.moveTo(g+A,x),u.lineTo(g+E-A,x),u.arcTo(g+E,x,g+E,x+A,A),u.lineTo(g+E,x+E-A),u.arcTo(g+E,x+E,g+E-A,x+E,A),u.lineTo(g+A,x+E),u.arcTo(g,x+E,g,x+E-A,A),u.lineTo(g,x+A),u.arcTo(g,x,g+A,x,A),u.closePath()}}function O(g,x,V){const A=[...g].sort((ct,ue)=>{var Rt,It;return(((Rt=ct.label)==null?void 0:Rt.length)||0)-(((It=ue.label)==null?void 0:It.length)||0)}).pop().label,{family:q,weight:C}=V,I=i?Math.sqrt(Math.pow(x*.7,2)/2):x;let G=I;s.font.size>0&&(G=s.font.size);let F=0,X=0;do{u.font=`${C} ${G}px ${q}`;const ct=u.measureText(A);F=ct.width,X=ct.fontBoundingBoxDescent,G--}while(F>I&&G>0);const Pt=I/X,le=Math.min(I/F,Pt),de=Math.floor(G*le);return[`${C} ${de}px ${q}`,Pt]}function y(g,x,V,E,A){g.font=h,g.textAlign="center",g.textBaseline="middle",g.fillStyle=A,g.fillText(E,x,V+(i?b:0))}},Qt=(s,e)=>s.offset.x=(e?.5:0)+s.userData.offsetX,ut=(s,e)=>{const{offset:t,userData:{offsetY:n,cellHeight:i}}=s;t.y=1-(e+1)*i+n};function ft(s,e,t=2,n=2){const i=t/2-s,r=n/2-s,c=s/t,l=(t-s)/t,d=s/n,f=(n-s)/n,a=[i,r,0,-i,r,0,-i,-r,0,i,-r,0],p=[l,f,c,f,c,d,l,d],u=[3*(e+1)+3,3*(e+1)+4,e+4,e+5,2*(e+1)+4,2,1,2*(e+1)+3,3,4*(e+1)+3,4,0],h=[0,1,2,0,2,3,4,5,6,4,6,7,8,9,10,8,10,11].map(y=>u[y]);let b,m,S,M,B,w,_,O;for(let y=0;y<4;y++){M=y<1||y>2?i:-i,B=y<2?r:-r,w=y<1||y>2?l:c,_=y<2?f:d;for(let g=0;g<=e;g++)b=Math.PI/2*(y+g/e),m=Math.cos(b),S=Math.sin(b),a.push(M+s*m,B+s*S,0),p.push(w+c*m,_+d*S),g<e&&(O=(e+1)*y+g+4,h.push(y,O,O+1))}return new o.BufferGeometry().setIndex(new o.BufferAttribute(new Uint32Array(h),1)).setAttribute("position",new o.BufferAttribute(new Float32Array(a),3)).setAttribute("uv",new o.BufferAttribute(new Float32Array(p),2))}const $t=(s,e)=>{const t=new o.Vector3,{isSphere:n,radius:i,smoothness:r,type:c}=s,d=c==="rounded-cube"?2-s.edges.radius*2:2,f=ft(i,r,d,d);return N.map((a,p)=>{const u=p<3,h=N[p],b=p?e.clone():e;ut(b,p);const{enabled:m,scale:S,opacity:M,hover:B}=s[h],w={map:b,opacity:M,transparent:!0},_=n?new o.Sprite(new o.SpriteMaterial(w)):new o.Mesh(f,new o.MeshBasicMaterial(w)),O=u?h:h[1];if(_.position[O]=(u?1:-1)*(n?xt:1),!n){_.lookAt(t.copy(_.position).multiplyScalar(1.7));const y=o.Object3D.DEFAULT_UP.z===1,g=o.Object3D.DEFAULT_UP.x===1;(y||g)&&(h==="z"&&y||h==="x"&&g?_.rotateZ(-Math.PI/2):(h==="nz"&&y||h==="nx"&&g)&&_.rotateZ(Math.PI/2))}return _.scale.setScalar(S),_.renderOrder=1,_.visible=m,_.userData={scale:S,opacity:M,hover:B},_})},Yt=(s,e)=>{const{isSphere:t,corners:n,type:i}=s,r=i==="rounded-cube";if(!n.enabled)return[];const{color:c,opacity:l,scale:d,radius:f,smoothness:a,hover:p}=n,u=t?null:r?new o.SphereGeometry(f,a*2,a):ft(f,a),h={transparent:!0,opacity:l},b=r?1-f:.85,m=[1,1,1,-1,1,1,1,-1,1,-1,-1,1,1,1,-1,-1,1,-1,1,-1,-1,-1,-1,-1].map(M=>M*b),S=new o.Vector3;return Array(m.length/3).fill(0).map((M,B)=>{if(t){const O=e.clone();ut(O,6),h.map=O}else h.color=c;const w=t?new o.Sprite(new o.SpriteMaterial(h)):new o.Mesh(u,new o.MeshBasicMaterial(h)),_=B*3;return w.position.set(m[_],m[_+1],m[_+2]),t&&w.position.normalize().multiplyScalar(1.7),w.scale.setScalar(d),w.lookAt(S.copy(w.position).multiplyScalar(2)),w.renderOrder=1,w.userData={color:c,opacity:l,scale:d,hover:p,intersectionOrder:1},w})},Jt=(s,e,t)=>{const{isSphere:n,edges:i,type:r}=s,c=r==="rounded-cube";if(!i.enabled)return[];const{color:l,opacity:d,scale:f,hover:a,radius:p,smoothness:u}=i,h=c?2-p*2:1.2,b=n?null:c?new o.CylinderGeometry(p,p,h,u*4):ft(p,u,h,.25),m={transparent:!0,opacity:d},S=c?1-p:.925,M=[0,1,1,0,-1,1,1,0,1,-1,0,1,0,1,-1,0,-1,-1,1,0,-1,-1,0,-1,1,1,0,1,-1,0,-1,1,0,-1,-1,0].map(_=>_*S),B=new o.Vector3,w=new o.Vector3(0,1,0);return Array(M.length/3).fill(0).map((_,O)=>{if(n){const x=e.clone();ut(x,t),m.map=x}else m.color=l;const y=n?new o.Sprite(new o.SpriteMaterial(m)):new o.Mesh(b,new o.MeshBasicMaterial(m)),g=O*3;return y.position.set(M[g],M[g+1],M[g+2]),n&&y.position.normalize().multiplyScalar(1.7),y.scale.setScalar(f),y.up.copy(w),y.lookAt(B.copy(y.position).multiplyScalar(2)),c?(!n&&!y.position.z&&(y.rotation.z=Math.PI),!n&&!y.position.x&&(y.rotation.x=0),!n&&!y.position.x&&(y.rotation.z=Math.PI/2)):!n&&!y.position.y&&(y.rotation.z=Math.PI/2),y.renderOrder=1,y.userData={color:l,opacity:d,scale:f,hover:a},y})};function Kt(s,e=!1){const t=s[0].index!==null,n=new Set(Object.keys(s[0].attributes)),i=new Set(Object.keys(s[0].morphAttributes)),r={},c={},l=s[0].morphTargetsRelative,d=new o.BufferGeometry;let f=0;for(let a=0;a<s.length;++a){const p=s[a];let u=0;if(t!==(p.index!==null))return console.error("THREE.BufferGeometryUtils: .mergeGeometries() failed with geometry at index "+a+". All geometries must have compatible attributes; make sure index attribute exists among all geometries, or in none of them."),null;for(const h in p.attributes){if(!n.has(h))return console.error("THREE.BufferGeometryUtils: .mergeGeometries() failed with geometry at index "+a+'. All geometries must have compatible attributes; make sure "'+h+'" attribute exists among all geometries, or in none of them.'),null;r[h]===void 0&&(r[h]=[]),r[h].push(p.attributes[h]),u++}if(u!==n.size)return console.error("THREE.BufferGeometryUtils: .mergeGeometries() failed with geometry at index "+a+". Make sure all geometries have the same number of attributes."),null;if(l!==p.morphTargetsRelative)return console.error("THREE.BufferGeometryUtils: .mergeGeometries() failed with geometry at index "+a+". .morphTargetsRelative must be consistent throughout all geometries."),null;for(const h in p.morphAttributes){if(!i.has(h))return console.error("THREE.BufferGeometryUtils: .mergeGeometries() failed with geometry at index "+a+". .morphAttributes must be consistent throughout all geometries."),null;c[h]===void 0&&(c[h]=[]),c[h].push(p.morphAttributes[h])}if(e){let h;if(t)h=p.index.count;else if(p.attributes.position!==void 0)h=p.attributes.position.count;else return console.error("THREE.BufferGeometryUtils: .mergeGeometries() failed with geometry at index "+a+". The geometry must have either an index or a position attribute"),null;d.addGroup(f,h,a),f+=h}}if(t){let a=0;const p=[];for(let u=0;u<s.length;++u){const h=s[u].index;for(let b=0;b<h.count;++b)p.push(h.getX(b)+a);a+=s[u].attributes.position.count}d.setIndex(p)}for(const a in r){const p=At(r[a]);if(!p)return console.error("THREE.BufferGeometryUtils: .mergeGeometries() failed while trying to merge the "+a+" attribute."),null;d.setAttribute(a,p)}for(const a in c){const p=c[a][0].length;if(p===0)break;d.morphAttributes=d.morphAttributes||{},d.morphAttributes[a]=[];for(let u=0;u<p;++u){const h=[];for(let m=0;m<c[a].length;++m)h.push(c[a][m][u]);const b=At(h);if(!b)return console.error("THREE.BufferGeometryUtils: .mergeGeometries() failed while trying to merge the "+a+" morphAttribute."),null;d.morphAttributes[a].push(b)}}return d}function At(s){let e,t,n,i=-1,r=0;for(let f=0;f<s.length;++f){const a=s[f];if(e===void 0&&(e=a.array.constructor),e!==a.array.constructor)return console.error("THREE.BufferGeometryUtils: .mergeAttributes() failed. BufferAttribute.array must be of consistent array types across matching attributes."),null;if(t===void 0&&(t=a.itemSize),t!==a.itemSize)return console.error("THREE.BufferGeometryUtils: .mergeAttributes() failed. BufferAttribute.itemSize must be consistent across matching attributes."),null;if(n===void 0&&(n=a.normalized),n!==a.normalized)return console.error("THREE.BufferGeometryUtils: .mergeAttributes() failed. BufferAttribute.normalized must be consistent across matching attributes."),null;if(i===-1&&(i=a.gpuType),i!==a.gpuType)return console.error("THREE.BufferGeometryUtils: .mergeAttributes() failed. BufferAttribute.gpuType must be consistent across matching attributes."),null;r+=a.count*t}const c=new e(r),l=new o.BufferAttribute(c,t,n);let d=0;for(let f=0;f<s.length;++f){const a=s[f];if(a.isInterleavedBufferAttribute){const p=d/t;for(let u=0,h=a.count;u<h;u++)for(let b=0;b<t;b++){const m=a.getComponent(u,b);l.setComponent(u+p,b,m)}}else c.set(a.array,d);d+=a.count*t}return i!==void 0&&(l.gpuType=i),l}const te=(s,e)=>{const{isSphere:t,background:{enabled:n,color:i,opacity:r,hover:c}}=e;let l;const d=new o.MeshBasicMaterial({color:i,side:o.BackSide,opacity:r,transparent:!0,depthWrite:!1});if(!n)return null;if(t)l=new o.Mesh(new o.SphereGeometry(1.8,64,64),d);else{let f;s.forEach(a=>{const p=a.scale.x;a.scale.setScalar(.9),a.updateMatrix();const u=a.geometry.clone();u.applyMatrix4(a.matrix),f=f?Kt([f,u]):u,a.scale.setScalar(p)}),l=new o.Mesh(f,d)}return l.userData={color:i,opacity:r,hover:c},l},Mt=new o.Box3,it=new o.Vector3;class Ut extends o.InstancedBufferGeometry{constructor(){super(),this.isLineSegmentsGeometry=!0,this.type="LineSegmentsGeometry";const e=[-1,2,0,1,2,0,-1,1,0,1,1,0,-1,0,0,1,0,0,-1,-1,0,1,-1,0],t=[-1,2,1,2,-1,1,1,1,-1,-1,1,-1,-1,-2,1,-2],n=[0,2,1,2,3,1,2,4,3,4,5,3,4,6,5,6,7,5];this.setIndex(n),this.setAttribute("position",new o.Float32BufferAttribute(e,3)),this.setAttribute("uv",new o.Float32BufferAttribute(t,2))}applyMatrix4(e){const t=this.attributes.instanceStart,n=this.attributes.instanceEnd;return t!==void 0&&(t.applyMatrix4(e),n.applyMatrix4(e),t.needsUpdate=!0),this.boundingBox!==null&&this.computeBoundingBox(),this.boundingSphere!==null&&this.computeBoundingSphere(),this}setPositions(e){let t;e instanceof Float32Array?t=e:Array.isArray(e)&&(t=new Float32Array(e));const n=new o.InstancedInterleavedBuffer(t,6,1);return this.setAttribute("instanceStart",new o.InterleavedBufferAttribute(n,3,0)),this.setAttribute("instanceEnd",new o.InterleavedBufferAttribute(n,3,3)),this.instanceCount=this.attributes.instanceStart.count,this.computeBoundingBox(),this.computeBoundingSphere(),this}setColors(e){let t;e instanceof Float32Array?t=e:Array.isArray(e)&&(t=new Float32Array(e));const n=new o.InstancedInterleavedBuffer(t,6,1);return this.setAttribute("instanceColorStart",new o.InterleavedBufferAttribute(n,3,0)),this.setAttribute("instanceColorEnd",new o.InterleavedBufferAttribute(n,3,3)),this}fromWireframeGeometry(e){return this.setPositions(e.attributes.position.array),this}fromEdgesGeometry(e){return this.setPositions(e.attributes.position.array),this}fromMesh(e){return this.fromWireframeGeometry(new o.WireframeGeometry(e.geometry)),this}fromLineSegments(e){const t=e.geometry;return this.setPositions(t.attributes.position.array),this}computeBoundingBox(){this.boundingBox===null&&(this.boundingBox=new o.Box3);const e=this.attributes.instanceStart,t=this.attributes.instanceEnd;e!==void 0&&t!==void 0&&(this.boundingBox.setFromBufferAttribute(e),Mt.setFromBufferAttribute(t),this.boundingBox.union(Mt))}computeBoundingSphere(){this.boundingSphere===null&&(this.boundingSphere=new o.Sphere),this.boundingBox===null&&this.computeBoundingBox();const e=this.attributes.instanceStart,t=this.attributes.instanceEnd;if(e!==void 0&&t!==void 0){const n=this.boundingSphere.center;this.boundingBox.getCenter(n);let i=0;for(let r=0,c=e.count;r<c;r++)it.fromBufferAttribute(e,r),i=Math.max(i,n.distanceToSquared(it)),it.fromBufferAttribute(t,r),i=Math.max(i,n.distanceToSquared(it));this.boundingSphere.radius=Math.sqrt(i),isNaN(this.boundingSphere.radius)&&console.error("THREE.LineSegmentsGeometry.computeBoundingSphere(): Computed radius is NaN. The instanced position data is likely to have NaN values.",this)}}toJSON(){}applyMatrix(e){return console.warn("THREE.LineSegmentsGeometry: applyMatrix() has been renamed to applyMatrix4()."),this.applyMatrix4(e)}}o.UniformsLib.line={worldUnits:{value:1},linewidth:{value:1},resolution:{value:new o.Vector2(1,1)},dashOffset:{value:0},dashScale:{value:1},dashSize:{value:1},gapSize:{value:1}},o.ShaderLib.line={uniforms:o.UniformsUtils.merge([o.UniformsLib.common,o.UniformsLib.fog,o.UniformsLib.line]),vertexShader:`
#include <common>
#include <color_pars_vertex>
#include <fog_pars_vertex>
#include <logdepthbuf_pars_vertex>
#include <clipping_planes_pars_vertex>
uniform float linewidth;
uniform vec2 resolution;
attribute vec3 instanceStart;
attribute vec3 instanceEnd;
attribute vec3 instanceColorStart;
attribute vec3 instanceColorEnd;
#ifdef WORLD_UNITS
varying vec4 worldPos;
varying vec3 worldStart;
varying vec3 worldEnd;
#ifdef USE_DASH
varying vec2 vUv;
#endif
#else
varying vec2 vUv;
#endif
#ifdef USE_DASH
uniform float dashScale;
attribute float instanceDistanceStart;
attribute float instanceDistanceEnd;
varying float vLineDistance;
#endif
void trimSegment( const in vec4 start, inout vec4 end ) {
// trim end segment so it terminates between the camera plane and the near plane
// conservative estimate of the near plane
float a = projectionMatrix[ 2 ][ 2 ]; // 3nd entry in 3th column
float b = projectionMatrix[ 3 ][ 2 ]; // 3nd entry in 4th column
float nearEstimate = - 0.5 * b / a;
float alpha = ( nearEstimate - start.z ) / ( end.z - start.z );
end.xyz = mix( start.xyz, end.xyz, alpha );
}
void main() {
#ifdef USE_COLOR
vColor.xyz = ( position.y < 0.5 ) ? instanceColorStart : instanceColorEnd;
#endif
#ifdef USE_DASH
vLineDistance = ( position.y < 0.5 ) ? dashScale * instanceDistanceStart : dashScale * instanceDistanceEnd;
vUv = uv;
#endif
float aspect = resolution.x / resolution.y;
// camera space
vec4 start = modelViewMatrix * vec4( instanceStart, 1.0 );
vec4 end = modelViewMatrix * vec4( instanceEnd, 1.0 );
#ifdef WORLD_UNITS
worldStart = start.xyz;
worldEnd = end.xyz;
#else
vUv = uv;
#endif
// special case for perspective projection, and segments that terminate either in, or behind, the camera plane
// clearly the gpu firmware has a way of addressing this issue when projecting into ndc space
// but we need to perform ndc-space calculations in the shader, so we must address this issue directly
// perhaps there is a more elegant solution -- WestLangley
bool perspective = ( projectionMatrix[ 2 ][ 3 ] == - 1.0 ); // 4th entry in the 3rd column
if ( perspective ) {
if ( start.z < 0.0 && end.z >= 0.0 ) {
trimSegment( start, end );
} else if ( end.z < 0.0 && start.z >= 0.0 ) {
trimSegment( end, start );
}
}
// clip space
vec4 clipStart = projectionMatrix * start;
vec4 clipEnd = projectionMatrix * end;
// ndc space
vec3 ndcStart = clipStart.xyz / clipStart.w;
vec3 ndcEnd = clipEnd.xyz / clipEnd.w;
// direction
vec2 dir = ndcEnd.xy - ndcStart.xy;
// account for clip-space aspect ratio
dir.x *= aspect;
dir = normalize( dir );
#ifdef WORLD_UNITS
vec3 worldDir = normalize( end.xyz - start.xyz );
vec3 tmpFwd = normalize( mix( start.xyz, end.xyz, 0.5 ) );
vec3 worldUp = normalize( cross( worldDir, tmpFwd ) );
vec3 worldFwd = cross( worldDir, worldUp );
worldPos = position.y < 0.5 ? start: end;
// height offset
float hw = linewidth * 0.5;
worldPos.xyz += position.x < 0.0 ? hw * worldUp : - hw * worldUp;
// don't extend the line if we're rendering dashes because we
// won't be rendering the endcaps
#ifndef USE_DASH
// cap extension
worldPos.xyz += position.y < 0.5 ? - hw * worldDir : hw * worldDir;
// add width to the box
worldPos.xyz += worldFwd * hw;
// endcaps
if ( position.y > 1.0 || position.y < 0.0 ) {
worldPos.xyz -= worldFwd * 2.0 * hw;
}
#endif
// project the worldpos
vec4 clip = projectionMatrix * worldPos;
// shift the depth of the projected points so the line
// segments overlap neatly
vec3 clipPose = ( position.y < 0.5 ) ? ndcStart : ndcEnd;
clip.z = clipPose.z * clip.w;
#else
vec2 offset = vec2( dir.y, - dir.x );
// undo aspect ratio adjustment
dir.x /= aspect;
offset.x /= aspect;
// sign flip
if ( position.x < 0.0 ) offset *= - 1.0;
// endcaps
if ( position.y < 0.0 ) {
offset += - dir;
} else if ( position.y > 1.0 ) {
offset += dir;
}
// adjust for linewidth
offset *= linewidth;
// adjust for clip-space to screen-space conversion // maybe resolution should be based on viewport ...
offset /= resolution.y;
// select end
vec4 clip = ( position.y < 0.5 ) ? clipStart : clipEnd;
// back to clip space
offset *= clip.w;
clip.xy += offset;
#endif
gl_Position = clip;
vec4 mvPosition = ( position.y < 0.5 ) ? start : end; // this is an approximation
#include <logdepthbuf_vertex>
#include <clipping_planes_vertex>
#include <fog_vertex>
}
`,fragmentShader:`
uniform vec3 diffuse;
uniform float opacity;
uniform float linewidth;
#ifdef USE_DASH
uniform float dashOffset;
uniform float dashSize;
uniform float gapSize;
#endif
varying float vLineDistance;
#ifdef WORLD_UNITS
varying vec4 worldPos;
varying vec3 worldStart;
varying vec3 worldEnd;
#ifdef USE_DASH
varying vec2 vUv;
#endif
#else
varying vec2 vUv;
#endif
#include <common>
#include <color_pars_fragment>
#include <fog_pars_fragment>
#include <logdepthbuf_pars_fragment>
#include <clipping_planes_pars_fragment>
vec2 closestLineToLine(vec3 p1, vec3 p2, vec3 p3, vec3 p4) {
float mua;
float mub;
vec3 p13 = p1 - p3;
vec3 p43 = p4 - p3;
vec3 p21 = p2 - p1;
float d1343 = dot( p13, p43 );
float d4321 = dot( p43, p21 );
float d1321 = dot( p13, p21 );
float d4343 = dot( p43, p43 );
float d2121 = dot( p21, p21 );
float denom = d2121 * d4343 - d4321 * d4321;
float numer = d1343 * d4321 - d1321 * d4343;
mua = numer / denom;
mua = clamp( mua, 0.0, 1.0 );
mub = ( d1343 + d4321 * ( mua ) ) / d4343;
mub = clamp( mub, 0.0, 1.0 );
return vec2( mua, mub );
}
void main() {
#include <clipping_planes_fragment>
#ifdef USE_DASH
if ( vUv.y < - 1.0 || vUv.y > 1.0 ) discard; // discard endcaps
if ( mod( vLineDistance + dashOffset, dashSize + gapSize ) > dashSize ) discard; // todo - FIX
#endif
float alpha = opacity;
#ifdef WORLD_UNITS
// Find the closest points on the view ray and the line segment
vec3 rayEnd = normalize( worldPos.xyz ) * 1e5;
vec3 lineDir = worldEnd - worldStart;
vec2 params = closestLineToLine( worldStart, worldEnd, vec3( 0.0, 0.0, 0.0 ), rayEnd );
vec3 p1 = worldStart + lineDir * params.x;
vec3 p2 = rayEnd * params.y;
vec3 delta = p1 - p2;
float len = length( delta );
float norm = len / linewidth;
#ifndef USE_DASH
#ifdef USE_ALPHA_TO_COVERAGE
float dnorm = fwidth( norm );
alpha = 1.0 - smoothstep( 0.5 - dnorm, 0.5 + dnorm, norm );
#else
if ( norm > 0.5 ) {
discard;
}
#endif
#endif
#else
#ifdef USE_ALPHA_TO_COVERAGE
// artifacts appear on some hardware if a derivative is taken within a conditional
float a = vUv.x;
float b = ( vUv.y > 0.0 ) ? vUv.y - 1.0 : vUv.y + 1.0;
float len2 = a * a + b * b;
float dlen = fwidth( len2 );
if ( abs( vUv.y ) > 1.0 ) {
alpha = 1.0 - smoothstep( 1.0 - dlen, 1.0 + dlen, len2 );
}
#else
if ( abs( vUv.y ) > 1.0 ) {
float a = vUv.x;
float b = ( vUv.y > 0.0 ) ? vUv.y - 1.0 : vUv.y + 1.0;
float len2 = a * a + b * b;
if ( len2 > 1.0 ) discard;
}
#endif
#endif
vec4 diffuseColor = vec4( diffuse, alpha );
#include <logdepthbuf_fragment>
#include <color_fragment>
gl_FragColor = vec4( diffuseColor.rgb, alpha );
#include <tonemapping_fragment>
#include <colorspace_fragment>
#include <fog_fragment>
#include <premultiplied_alpha_fragment>
}
`};class pt extends o.ShaderMaterial{constructor(e){super({type:"LineMaterial",uniforms:o.UniformsUtils.clone(o.ShaderLib.line.uniforms),vertexShader:o.ShaderLib.line.vertexShader,fragmentShader:o.ShaderLib.line.fragmentShader,clipping:!0}),this.isLineMaterial=!0,this.setValues(e)}get color(){return this.uniforms.diffuse.value}set color(e){this.uniforms.diffuse.value=e}get worldUnits(){return"WORLD_UNITS"in this.defines}set worldUnits(e){e===!0?this.defines.WORLD_UNITS="":delete this.defines.WORLD_UNITS}get linewidth(){return this.uniforms.linewidth.value}set linewidth(e){this.uniforms.linewidth&&(this.uniforms.linewidth.value=e)}get dashed(){return"USE_DASH"in this.defines}set dashed(e){e===!0!==this.dashed&&(this.needsUpdate=!0),e===!0?this.defines.USE_DASH="":delete this.defines.USE_DASH}get dashScale(){return this.uniforms.dashScale.value}set dashScale(e){this.uniforms.dashScale.value=e}get dashSize(){return this.uniforms.dashSize.value}set dashSize(e){this.uniforms.dashSize.value=e}get dashOffset(){return this.uniforms.dashOffset.value}set dashOffset(e){this.uniforms.dashOffset.value=e}get gapSize(){return this.uniforms.gapSize.value}set gapSize(e){this.uniforms.gapSize.value=e}get opacity(){return this.uniforms.opacity.value}set opacity(e){this.uniforms&&(this.uniforms.opacity.value=e)}get resolution(){return this.uniforms.resolution.value}set resolution(e){this.uniforms.resolution.value.copy(e)}get alphaToCoverage(){return"USE_ALPHA_TO_COVERAGE"in this.defines}set alphaToCoverage(e){this.defines&&(e===!0!==this.alphaToCoverage&&(this.needsUpdate=!0),e===!0?this.defines.USE_ALPHA_TO_COVERAGE="":delete this.defines.USE_ALPHA_TO_COVERAGE)}}const ht=new o.Vector4,zt=new o.Vector3,Tt=new o.Vector3,U=new o.Vector4,z=new o.Vector4,D=new o.Vector4,mt=new o.Vector3,gt=new o.Matrix4,T=new o.Line3,Lt=new o.Vector3,ot=new o.Box3,st=new o.Sphere,P=new o.Vector4;let R,k;function Bt(s,e,t){return P.set(0,0,-e,1).applyMatrix4(s.projectionMatrix),P.multiplyScalar(1/P.w),P.x=k/t.width,P.y=k/t.height,P.applyMatrix4(s.projectionMatrixInverse),P.multiplyScalar(1/P.w),Math.abs(Math.max(P.x,P.y))}function ee(s,e){const t=s.matrixWorld,n=s.geometry,i=n.attributes.instanceStart,r=n.attributes.instanceEnd,c=Math.min(n.instanceCount,i.count);for(let l=0,d=c;l<d;l++){T.start.fromBufferAttribute(i,l),T.end.fromBufferAttribute(r,l),T.applyMatrix4(t);const f=new o.Vector3,a=new o.Vector3;R.distanceSqToSegment(T.start,T.end,a,f),a.distanceTo(f)<k*.5&&e.push({point:a,pointOnLine:f,distance:R.origin.distanceTo(a),object:s,face:null,faceIndex:l,uv:null,uv1:null})}}function ne(s,e,t){const n=e.projectionMatrix,r=s.material.resolution,c=s.matrixWorld,l=s.geometry,d=l.attributes.instanceStart,f=l.attributes.instanceEnd,a=Math.min(l.instanceCount,d.count),p=-e.near;R.at(1,D),D.w=1,D.applyMatrix4(e.matrixWorldInverse),D.applyMatrix4(n),D.multiplyScalar(1/D.w),D.x*=r.x/2,D.y*=r.y/2,D.z=0,mt.copy(D),gt.multiplyMatrices(e.matrixWorldInverse,c);for(let u=0,h=a;u<h;u++){if(U.fromBufferAttribute(d,u),z.fromBufferAttribute(f,u),U.w=1,z.w=1,U.applyMatrix4(gt),z.applyMatrix4(gt),U.z>p&&z.z>p)continue;if(U.z>p){const w=U.z-z.z,_=(U.z-p)/w;U.lerp(z,_)}else if(z.z>p){const w=z.z-U.z,_=(z.z-p)/w;z.lerp(U,_)}U.applyMatrix4(n),z.applyMatrix4(n),U.multiplyScalar(1/U.w),z.multiplyScalar(1/z.w),U.x*=r.x/2,U.y*=r.y/2,z.x*=r.x/2,z.y*=r.y/2,T.start.copy(U),T.start.z=0,T.end.copy(z),T.end.z=0;const m=T.closestPointToPointParameter(mt,!0);T.at(m,Lt);const S=o.MathUtils.lerp(U.z,z.z,m),M=S>=-1&&S<=1,B=mt.distanceTo(Lt)<k*.5;if(M&&B){T.start.fromBufferAttribute(d,u),T.end.fromBufferAttribute(f,u),T.start.applyMatrix4(c),T.end.applyMatrix4(c);const w=new o.Vector3,_=new o.Vector3;R.distanceSqToSegment(T.start,T.end,_,w),t.push({point:_,pointOnLine:w,distance:R.origin.distanceTo(_),object:s,face:null,faceIndex:u,uv:null,uv1:null})}}}class ie extends o.Mesh{constructor(e=new Ut,t=new pt({color:Math.random()*16777215})){super(e,t),this.isLineSegments2=!0,this.type="LineSegments2"}computeLineDistances(){const e=this.geometry,t=e.attributes.instanceStart,n=e.attributes.instanceEnd,i=new Float32Array(2*t.count);for(let c=0,l=0,d=t.count;c<d;c++,l+=2)zt.fromBufferAttribute(t,c),Tt.fromBufferAttribute(n,c),i[l]=l===0?0:i[l-1],i[l+1]=i[l]+zt.distanceTo(Tt);const r=new o.InstancedInterleavedBuffer(i,2,1);return e.setAttribute("instanceDistanceStart",new o.InterleavedBufferAttribute(r,1,0)),e.setAttribute("instanceDistanceEnd",new o.InterleavedBufferAttribute(r,1,1)),this}raycast(e,t){const n=this.material.worldUnits,i=e.camera;i===null&&!n&&console.error('LineSegments2: "Raycaster.camera" needs to be set in order to raycast against LineSegments2 while worldUnits is set to false.');const r=e.params.Line2!==void 0&&e.params.Line2.threshold||0;R=e.ray;const c=this.matrixWorld,l=this.geometry,d=this.material;k=d.linewidth+r,l.boundingSphere===null&&l.computeBoundingSphere(),st.copy(l.boundingSphere).applyMatrix4(c);let f;if(n)f=k*.5;else{const p=Math.max(i.near,st.distanceToPoint(R.origin));f=Bt(i,p,d.resolution)}if(st.radius+=f,R.intersectsSphere(st)===!1)return;l.boundingBox===null&&l.computeBoundingBox(),ot.copy(l.boundingBox).applyMatrix4(c);let a;if(n)a=k*.5;else{const p=Math.max(i.near,ot.distanceToPoint(R.origin));a=Bt(i,p,d.resolution)}ot.expandByScalar(a),R.intersectsBox(ot)!==!1&&(n?ee(this,t):ne(this,i,t))}onBeforeRender(e){const t=this.material.uniforms;t&&t.resolution&&(e.getViewport(ht),this.material.uniforms.resolution.value.set(ht.z,ht.w))}}class Ot extends Ut{constructor(){super(),this.isLineGeometry=!0,this.type="LineGeometry"}setPositions(e){const t=e.length-3,n=new Float32Array(2*t);for(let i=0;i<t;i+=3)n[2*i]=e[i],n[2*i+1]=e[i+1],n[2*i+2]=e[i+2],n[2*i+3]=e[i+3],n[2*i+4]=e[i+4],n[2*i+5]=e[i+5];return super.setPositions(n),this}setColors(e){const t=e.length-3,n=new Float32Array(2*t);for(let i=0;i<t;i+=3)n[2*i]=e[i],n[2*i+1]=e[i+1],n[2*i+2]=e[i+2],n[2*i+3]=e[i+3],n[2*i+4]=e[i+4],n[2*i+5]=e[i+5];return super.setColors(n),this}setFromPoints(e){const t=e.length-1,n=new Float32Array(6*t);for(let i=0;i<t;i++)n[6*i]=e[i].x,n[6*i+1]=e[i].y,n[6*i+2]=e[i].z||0,n[6*i+3]=e[i+1].x,n[6*i+4]=e[i+1].y,n[6*i+5]=e[i+1].z||0;return super.setPositions(n),this}fromLine(e){const t=e.geometry;return this.setPositions(t.attributes.position.array),this}}class oe extends ie{constructor(e=new Ot,t=new pt({color:Math.random()*16777215})){super(e,t),this.isLine2=!0,this.type="Line2"}}const se=s=>{const e=new o.Color,t=[],n=[],{isSphere:i}=s;if(N.forEach((l,d)=>{const{enabled:f,line:a,scale:p,color:u}=s[l];if(!f||!a)return;const h=d<3?1:-1,m=(i?xt-p/2:.975)*h;t.push(l.includes("x")?m:0,l.includes("y")?m:0,l.includes("z")?m:0,0,0,0);const S=e.set(u).toArray();n.push(...S,...S)}),!t.length)return null;const r=new Ot().setPositions(t).setColors(n),c=new pt({linewidth:s.lineWidth,vertexColors:!0,resolution:new o.Vector2(window.innerWidth,window.innerHeight)});return new oe(r,c).computeLineDistances()},re=s=>{const{corners:e,edges:t}=s,n=[],i=Xt(s),r=$t(s,i);n.push(...r),e.enabled&&n.push(...Yt(s,i)),t.enabled&&n.push(...Jt(s,i,e.enabled?7:6));const c=te(r,s),l=se(s);return[n,c,l]},Q=(s,e=!0)=>{const{material:t,userData:n}=s,{opacity:i,color:r,scale:c}=e?n.hover:n;s.scale.setScalar(c),t.opacity=i,t.map?Qt(t.map,e):t.color.set(r)},Z=new o.Matrix4,Ct=new o.Spherical,ae=new o.Vector2,W=new o.Vector3,Dt=new o.Vector4,rt=new o.Quaternion().setFromAxisAngle(new o.Vector3(0,0,1),Math.PI/2),at=new o.Quaternion().setFromAxisAngle(new o.Vector3(0,0,1),-Math.PI/2);class ce extends o.Object3D{constructor(t,n,i={}){super();v(this,"enabled",!0);v(this,"camera");v(this,"renderer");v(this,"options");v(this,"target",new o.Vector3);v(this,"animated",!0);v(this,"speed",1);v(this,"animating",!1);v(this,"_options");v(this,"_intersections");v(this,"_background",null);v(this,"_viewport",[0,0,0,0]);v(this,"_originalViewport",[0,0,0,0]);v(this,"_originalScissor",[0,0,0,0]);v(this,"_scene");v(this,"_camera");v(this,"_container");v(this,"_domElement");v(this,"_domRect");v(this,"_dragging",!1);v(this,"_distance",0);v(this,"_clock",new o.Clock);v(this,"_targetQuaternion",new o.Quaternion);v(this,"_quaternionStart",new o.Quaternion);v(this,"_quaternionEnd",new o.Quaternion);v(this,"_pointerStart",new o.Vector2);v(this,"_focus",null);v(this,"_placement");v(this,"_controls");v(this,"_controlsListeners");this.camera=t,this.renderer=n,this._scene=new o.Scene().add(this),this.set(i)}get placement(){return this._placement}set placement(t){this._placement=j(this._domElement,t),this.domUpdate()}set(t={}){this.dispose(),this.options=t,this._options=Zt(t),this._camera=this._options.isSphere?new o.OrthographicCamera(-1.8,1.8,1.8,-1.8,5,10):new o.PerspectiveCamera(26,1,5,10),this._camera.position.set(0,0,7);const[n,i,r]=re(this._options);i&&this.add(i),r&&this.add(r),this.add(...n),this._background=i,this._intersections=n;const{container:c,animated:l,speed:d}=this._options;return this.animated=l,this.speed=d,this._container=c?Ft(c):document.body,this._domElement=Gt(this._options),this._domElement.onpointerdown=f=>this._onPointerDown(f),this._domElement.onpointermove=f=>this._onPointerMove(f),this._domElement.onpointerleave=()=>this._onPointerLeave(),this._container.appendChild(this._domElement),this._controls&&this.attachControls(this._controls),this.update(),this}render(){this.animating&&this._animate();const{renderer:t,_viewport:n}=this,i=t.getScissorTest(),r=t.autoClear;return t.autoClear=!1,t.setViewport(...n),i&&t.setScissor(...n),t.clear(!1,!0,!1),t.render(this._scene,this._camera),t.setViewport(...this._originalViewport),i&&t.setScissor(...this._originalScissor),t.autoClear=r,this}domUpdate(){this._domRect=this._domElement.getBoundingClientRect();const t=this.renderer,n=this._domRect,i=t.domElement.getBoundingClientRect();return this._viewport.splice(0,4,n.left-i.left,t.domElement.clientHeight-(n.top-i.top+n.height),n.width,n.height),t.getViewport(Dt).toArray(this._originalViewport),t.getScissorTest()&&t.getScissor(Dt).toArray(this._originalScissor),this}cameraUpdate(){return this._updateOrientation(),this}update(t=!0){return t&&this._controls&&this._controls.update(),this.domUpdate().cameraUpdate()}attachControls(t){return this.detachControls(),this.target=t.target,this._controlsListeners={start:()=>t.enabled=!1,end:()=>t.enabled=!0,change:()=>this.update(!1)},this.addEventListener("start",this._controlsListeners.start),this.addEventListener("end",this._controlsListeners.end),t.addEventListener("change",this._controlsListeners.change),this._controls=t,this}detachControls(){if(!(!this._controlsListeners||!this._controls))return this.target=new o.Vector3().copy(this._controls.target),this.removeEventListener("start",this._controlsListeners.start),this.removeEventListener("end",this._controlsListeners.end),this._controls.removeEventListener("change",this._controlsListeners.change),this._controlsListeners=void 0,this._controls=void 0,this}dispose(){var t;this.detachControls(),this.children.forEach(n=>{var r,c,l,d;this.remove(n);const i=n;(r=i.material)==null||r.dispose(),(l=(c=i.material)==null?void 0:c.map)==null||l.dispose(),(d=i.geometry)==null||d.dispose()}),(t=this._domElement)==null||t.remove()}_updateOrientation(t=!0){t&&(this.quaternion.copy(this.camera.quaternion).invert(),this.updateMatrixWorld()),_t(this._options,this._intersections,this.camera)}_animate(){const{position:t,quaternion:n}=this.camera;if(t.set(0,0,1),!this.animated){t.applyQuaternion(this._quaternionEnd).multiplyScalar(this._distance).add(this.target),n.copy(this._targetQuaternion),this._updateOrientation(),this.animating=!1,this.dispatchEvent({type:"change"}),this.dispatchEvent({type:"end"});return}this._controls&&(this._controls.enabled=!1);const r=this._clock.getDelta()*Ht*this.speed;if(this._quaternionStart.rotateTowards(this._quaternionEnd,r),t.applyQuaternion(this._quaternionStart).multiplyScalar(this._distance).add(this.target),n.rotateTowards(this._targetQuaternion,r),this._updateOrientation(),requestAnimationFrame(()=>this.dispatchEvent({type:"change"})),this._quaternionStart.angleTo(this._quaternionEnd)<$){if(this._controls){const c=this.camera.position.clone().sub(this.target).normalize(),l=o.Object3D.DEFAULT_UP.z===1&&Math.abs(c.z)>.99,d=o.Object3D.DEFAULT_UP.x===1&&Math.abs(c.x)>.99;l?this.camera.position.set(0,-1e-6,this.camera.position.z):d&&this.camera.position.set(this.camera.position.x,$,0),this._controls.update(),this._controls.enabled=!0}this.animating=!1,this.dispatchEvent({type:"end"})}}_setOrientation(t){const n=this.camera,i=this.target;if(W.copy(t).multiplyScalar(this._distance),Z.setPosition(W).lookAt(W,this.position,this.up),this._targetQuaternion.setFromRotationMatrix(Z),W.add(i),Z.lookAt(W,i,this.up),this._quaternionEnd.setFromRotationMatrix(Z),Z.setPosition(n.position).lookAt(n.position,i,this.up),this._quaternionStart.setFromRotationMatrix(Z),o.Object3D.DEFAULT_UP.z===1&&Math.abs(t.z)>.99){const r=Math.sign(t.z);this._targetQuaternion.multiply(r===1?at:rt),this._quaternionEnd.multiply(r===1?at:rt)}else if(o.Object3D.DEFAULT_UP.x===1&&Math.abs(t.x)>.99){const r=Math.sign(t.x);this._targetQuaternion.multiply(r===1?at:rt),this._quaternionEnd.multiply(r===1?at:rt)}this.animating=!0,this._clock.start(),this.dispatchEvent({type:"start"})}_onPointerDown(t){if(!this.enabled)return;const n=f=>{if(!this._dragging){if(jt(f,this._pointerStart))return;this._dragging=!0}const a=ae.set(f.clientX,f.clientY).sub(this._pointerStart).multiplyScalar(1/this._domRect.width*Math.PI),p=this.coordinateConversion(W.subVectors(this.camera.position,this.target)),u=Ct.setFromVector3(p);u.theta=l-a.x,u.phi=lt(d-a.y,$,Math.PI-$),this.coordinateConversion(this.camera.position.setFromSpherical(u),!0).add(this.target),this.camera.lookAt(this.target),this.quaternion.copy(this.camera.quaternion).invert(),this._updateOrientation(!1),this.dispatchEvent({type:"change"})},i=()=>{if(document.removeEventListener("pointermove",n,!1),document.removeEventListener("pointerup",i,!1),!this._dragging)return this._handleClick(t);this._focus&&(Q(this._focus,!1),this._focus=null),this._dragging=!1,this.dispatchEvent({type:"end"})};if(this.animating)return;t.preventDefault(),this._pointerStart.set(t.clientX,t.clientY);const r=this.coordinateConversion(W.subVectors(this.camera.position,this.target)),c=Ct.setFromVector3(r),l=c.theta,d=c.phi;this._distance=c.radius,document.addEventListener("pointermove",n,!1),document.addEventListener("pointerup",i,!1),this.dispatchEvent({type:"start"})}coordinateConversion(t,n=!1){const{x:i,y:r,z:c}=t,l=o.Object3D.DEFAULT_UP;return l.x===1?n?t.set(r,c,i):t.set(c,i,r):l.z===1?n?t.set(c,i,r):t.set(r,c,i):t}_onPointerMove(t){!this.enabled||this._dragging||(this._background&&Et(this._background,!0),this._handleHover(t))}_onPointerLeave(){!this.enabled||this._dragging||(this._background&&Et(this._background,!1),this._focus&&Q(this._focus,!1),this._domElement.style.cursor="")}_handleClick(t){const n=wt(t,this._domRect,this._camera,this._intersections);this._focus&&(Q(this._focus,!1),this._focus=null),n&&(this._setOrientation(n.object.position),this.dispatchEvent({type:"change"}))}_handleHover(t){const n=wt(t,this._domRect,this._camera,this._intersections),i=(n==null?void 0:n.object)||null;this._focus!==i&&(this._domElement.style.cursor=i?"pointer":"",this._focus&&Q(this._focus,!1),(this._focus=i)?Q(i,!0):_t(this._options,this._intersections,this.camera))}}L.ViewportGizmo=ce,Object.defineProperty(L,Symbol.toStringTag,{value:"Module"})});
//# sourceMappingURL=three-viewport-gizmo.umd.cjs.map
================================================
FILE: docs/.vitepress/components/IframeContainer.vue
================================================
<template>
<ClientOnly>
<div class="iframe-wrapper">
<div class="controls">
<a :href="fullUrl" target="_blank" title="Open in New Tab">
<v-icon name="fa-external-link-alt" />
</a>
<a :href="sourceURL" target="_blank" title="Source code">
<v-icon name="fa-code" />
</a>
<button @click="toggleFullScreen" :title="isFullScreen ? 'Exit Full Screen' : 'Full Screen'">
<v-icon name="fa-expand" />
</button>
</div>
<div class="responsive-container" :style="{ paddingBottom: aspectRatioPadding }">
<iframe ref="iframeRef" :src="fullUrl" :class="{ 'full-screen': isFullScreen }" loading="lazy"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen></iframe>
</div>
</div>
</ClientOnly>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted, computed } from 'vue'
const { url, aspectRatio = '16/9' } = defineProps<{
url: string,
aspectRatio?: string
}>()
const iframeRef = ref<HTMLIFrameElement>();
const isFullScreen = ref(false)
const sourceURL = computed(() => `https://github.com/Fennec-hub/three-viewport-gizmo/blob/main/docs/public/samples/${url}`)
const fullUrl = computed(() => `${window.location.origin}/three-viewport-gizmo/samples/${url}`);
// Convert aspect ratio to a percentage for padding-bottom
const aspectRatioPadding = computed(() => {
const [width, height] = aspectRatio.split('/').map(Number);
return `${(height / width) * 100}%`;
});
// Handle fullscreen changes
const onFullScreenChange = () => {
isFullScreen.value = !!document.fullscreenElement
}
onMounted(() => {
document.addEventListener('fullscreenchange', onFullScreenChange)
})
onUnmounted(() => {
document.removeEventListener('fullscreenchange', onFullScreenChange)
})
// Toggle fullscreen
const toggleFullScreen = async () => {
if (!document.fullscreenElement) {
try {
await iframeRef.value!.requestFullscreen()
} catch (err) {
console.error('Error attempting to enable fullscreen:', err)
}
} else {
if (document.exitFullscreen) {
document.exitFullscreen()
}
}
}
</script>
<style scoped lang="postcss">
.iframe-wrapper {
position: relative;
width: 100%;
height: 100%;
margin: 2em 0;
&:hover {
& .controls {
display: flex;
}
}
}
.responsive-container {
position: relative;
width: 100%;
height: 0;
overflow: hidden;
background: transparent;
}
iframe {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
border: 0;
background: transparent;
&.full-screen {
position: fixed;
inset: 0;
width: 100vw;
height: 100vh;
z-index: 9999;
}
}
.controls {
position: absolute;
inset-block-end: 0.5em;
inset-inline-end: 0.5em;
display: none;
gap: 1em;
z-index: 10;
border-radius: 4px;
box-shadow: 0 2px 4px rgb(0 0 0 / 0.1);
backdrop-filter: blur(4px);
& button,
& a {
background: #2229;
border: 0;
border-radius: 4px;
padding: 4px 8px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
color: #aaa;
text-decoration: none;
transition: all 0.2s ease;
&:hover {
background: #222;
transform: translateY(-1px);
box-shadow: 0 2px 4px rgb(0 0 0 / 0.1);
}
}
}
</style>
================================================
FILE: docs/.vitepress/config.ts
================================================
import { defineConfig } from "vitepress";
const examples = (type: "sphere" | "cube" | "rounded-cube") => [
{
text: "Orbit controls",
link: `examples/orbit-controls?type=${type}`,
},
{
text: "OrbitControls Events",
link: `examples/orbit-controls-events?type=${type}`,
},
{ text: "Standalone", link: `examples/standalone?type=${type}` },
{ text: "Post processing", link: `examples/post-processing?type=${type}` },
{
text: "Yomotsu camera controls",
link: `examples/yomotsu-camera-controls?type=${type}`,
},
{ text: "Responsive", link: `examples/responsive?type=${type}` },
{ text: "Resizable grid", link: `examples/resizable-grid?type=${type}` },
{
text: "Multiple elements",
link: `examples/multiple-elements?type=${type}`,
},
{
text: "Z-up coordinate system",
link: `examples/z-up?type=${type}`,
},
{
text: "X-up coordinate system",
link: `examples/x-up?type=${type}`,
},
];
// https://vitepress.dev/reference/site-config
export default defineConfig({
base: "/three-viewport-gizmo/",
head: [["link", { rel: "icon", href: "./assets/three-viewport-gizmo.svg" }]],
title: "Three Viewport Gizmo",
description: "A three.js customizable 3D view helper for any camera setup",
appearance: "force-dark",
themeConfig: {
// https://vitepress.dev/reference/default-theme-config
logo: "./assets/three-viewport-gizmo.svg",
nav: [
{ text: "API", link: "/api" },
{
text: "Examples",
items: [
{ text: "Sphere", link: "examples/orbit-controls?type=sphere" },
{ text: "Cube", link: "examples/orbit-controls?type=cube" },
{ text: "Rounded Cube", link: "examples/orbit-controls?type=rounded-cube" },
],
},
{
text: "Customize Your Gizmo",
link: "/custom",
target: "_blank",
},
],
sidebar: [
{ text: "Quickstart", link: "/quickstart" },
{ text: "API", link: "/api" },
{
text: "Examples",
items: [
{ text: "Sphere", items: examples("sphere"), collapsed: false },
{ text: "Cube", items: examples("cube"), collapsed: false },
{ text: "Rounded Cube", items: examples("rounded-cube"), collapsed: false },
],
},
],
socialLinks: [
{
icon: "github",
link: "https://github.com/fennec-hub/three-viewport-gizmo",
},
{
icon: "x",
link: "https://x.com/_Fennec_Xyz",
},
],
},
vite: {
build: {
commonjsOptions: {
include: [/oh-vue-icons/],
},
},
optimizeDeps: {
include: ["oh-vue-icons"],
},
ssr: {
noExternal: ["oh-vue-icons"],
},
},
});
================================================
FILE: docs/.vitepress/theme/icons/index.ts
================================================
import { OhVueIcon, addIcons } from "oh-vue-icons";
import { FaExpand, FaExternalLinkAlt, FaCode } from "oh-vue-icons/icons";
//import { threeIcon } from "./three.icon";
addIcons(FaExpand, FaExternalLinkAlt, FaCode);
export { OhVueIcon };
================================================
FILE: docs/.vitepress/theme/icons/three.icon.ts
================================================
export const threeIcon = {
name: "three",
width: 226.77,
height: 226.77,
raw: `<g transform="translate(8.964 4.2527)" fill="none" fill-rule="evenodd" stroke="#fff" stroke-linecap="butt" stroke-linejoin="round" stroke-width="4">
<path d="m63.02 200.61-43.213-174.94 173.23 49.874z"/>
<path d="m106.39 50.612 21.591 87.496-86.567-24.945z"/>
<path d="m84.91 125.03-10.724-43.465 43.008 12.346z"/>
<path d="m63.458 38.153 10.724 43.465-43.008-12.346z"/>
<path d="m149.47 62.93 10.724 43.465-43.008-12.346z"/>
<path d="m84.915 125.06 10.724 43.465-43.008-12.346z"/>
</g>`,
};
================================================
FILE: docs/.vitepress/theme/index.ts
================================================
// .vitepress/theme/index.js
import type { Theme } from "vitepress";
import DefaultTheme from "vitepress/theme";
import IframeContainer from "../components/IframeContainer.vue";
import "./style.css";
import { OhVueIcon } from "./icons";
export default {
extends: DefaultTheme,
enhanceApp({ app }) {
app.component("IframeContainer", IframeContainer);
app.component("v-icon", OhVueIcon);
},
} satisfies Theme;
================================================
FILE: docs/.vitepress/theme/style.css
================================================
:root {
/* Indigo to more red tones */
--vp-c-indigo-1: #fb8982 !important;
--vp-c-indigo-2: #e25656 !important;
--vp-c-indigo-3: #cc4343 !important;
--vp-c-indigo-soft: rgba(230, 66, 66, 0.16) !important;
/* Purple to orange tones */
--vp-c-purple-1: #fb8982 !important;
--vp-c-purple-2: #e25656 !important;
--vp-c-purple-3: #cc4343 !important;
--vp-c-purple-soft: rgba(230, 66, 66, 0.16) !important;
}
.VPNavBarMenu a:nth-of-type(2) {
position: relative;
}
.VPNavBarMenu a:nth-of-type(2).active {
display: none;
}
.VPNavBarMenu a:nth-of-type(2) span {
position: relative;
font-weight: 900 !important;
}
.VPNavBarMenu a:nth-of-type(2):before {
content: "";
position: absolute;
top: 20%;
left: 0;
width: 100%;
height: 60%;
background: var(--vp-c-brand-3);
border-radius: 2em;
}
svg {
width: 1em;
height: 1em;
}
.VPHero .container {
align-items: center;
}
.VPHero .image {
aspect-ratio: 1;
}
================================================
FILE: docs/api.md
================================================
# ViewportGizmo
A 3D camera orientation controller that provides a visual interface for manipulating the camera's viewing angle. The widget displays the current camera orientation and allows users to adjust the view directly by clicking or dragging.
## Constructor
`constructor( camera: PerspectiveCamera | OrthographicCamera, renderer: WebGLRenderer, options?: GizmoOptions )`
- **`camera`** — `PerspectiveCamera | OrthographicCamera`
The camera to be controlled by the gizmo.
- **`renderer`** — `WebGLRenderer`
The renderer used to render the scene.
- **`options`** — [GizmoOptions](#gizmooptions) (optional)
Configuration options for the gizmo.
## Events
- **`start`** — Triggered when a view change interaction begins.
Occurs when:
- Dragging starts
- Clicking an axis initiates a view transition
- **`change`** — Triggered during view changes.
Occurs when:
- Dragging updates the view
- View transition animations are in progress
- Any camera orientation updates happen
- **`end`** — Triggered when a view change interaction ends.
Occurs when:
- Dragging stops
- View transition animation completes
## Properties
- **`enabled`** — `boolean`
Specifies if the gizmo is active and responsive to user input.
- **`camera`** — `OrthographicCamera | PerspectiveCamera`
The camera controlled by this gizmo.
- **`target`** — `Vector3`
The point around which the camera rotates.
- **`placement`** — `boolean`
Sets and update the placement of the gizmo relative to its container.
- **`animated`** — `boolean`
Specifies if view changes should be animated.
- **`speed`** — `number`
Controls animation speed, where higher values result in faster animations.
- **`animating`** — `boolean` (readonly)
`True` if the gizmo is currently animating view changes.
## Methods
- **`set`** — `( options?: GizmoOptions ): ViewportGizmo`
Regenerates the gizmo with the new options.
::: warning
- Not recommended for use in real-time rendering or animation loops
- Provides a way to completely rebuild the gizmo with new options
- Can be computationally expensive, so use sparingly
:::
- **`render`** — `(): ViewportGizmo`
Renders the gizmo within the viewport.
- **`domUpdate`** — `(): ViewportGizmo`
Updates the gizmo's DOM properties based on position and size in the document.
- **`cameraUpdate`** — `(): ViewportGizmo`
Updates the gizmo's orientation to match the current camera orientation.
- **`update`** — `( controls?: boolean ): ViewportGizmo`
Performs a full update of the gizmo, including DOM and camera-related updates.
- **`attachControls`** — `( controls: OrbitControls ): void`
Links `OrbitControls` with the gizmo, managing interaction states and updates.
- **`detachControls`** — `(): void`
Removes all event listeners and detaches controls if previously attached.
- **`dispose`** — `(): void`
Cleans up all resources, including geometries, materials, textures, and event listeners.
---
## Interfaces
### GizmoOptions
```typescript
type GizmoOptions = {
container?: HTMLElement | string;
type?: "sphere" | "cube" | "rounded-cube";
size?: number;
placement?:
| "top-left"
| "top-center"
| "top-right"
| "center-left"
| "center-center"
| "center-right"
| "bottom-left"
| "bottom-center"
| "bottom-right";
offset?: {
left?: number;
top?: number;
right?: number;
bottom?: number;
};
animated?: boolean;
speed?: number;
resolution?: number;
lineWidth?: number;
id?: string;
className?: string;
font?: {
family?: string;
weight?: string | number;
};
background?: {
enabled?: boolean;
color?: ColorRepresentation;
opacity?: number;
hover?: {
color?: ColorRepresentation;
opacity?: number;
};
};
corners?: {
enabled?: boolean;
color?: ColorRepresentation;
opacity?: number;
scale?: number;
radius?: number;
smoothness?: number;
hover?: {
color?: ColorRepresentation;
opacity?: number;
scale?: number;
};
};
edges?: {
enabled?: boolean;
color?: ColorRepresentation;
opacity?: number;
scale?: number;
radius?: number;
smoothness?: number;
hover?: {
color?: ColorRepresentation;
opacity?: number;
scale?: number;
};
};
radius?: number;
smoothness?: number;
x?: GizmoAxisOptions;
y?: GizmoAxisOptions;
z?: GizmoAxisOptions;
nx?: GizmoAxisOptions;
ny?: GizmoAxisOptions;
nz?: GizmoAxisOptions;
right?: GizmoAxisOptions;
top?: GizmoAxisOptions;
front?: GizmoAxisOptions;
left?: GizmoAxisOptions;
bottom?: GizmoAxisOptions;
back?: GizmoAxisOptions;
};
type GizmoAxisOptions = {
enabled?: boolean;
label?: string;
opacity?: number;
scale?: number;
line?: boolean;
color?: ColorRepresentation;
labelColor?: ColorRepresentation;
border?: {
size: number;
color: ColorRepresentation;
};
hover?: {
color?: ColorRepresentation;
labelColor?: ColorRepresentation;
opacity?: number;
scale?: number;
border?: {
size: number;
color: ColorRepresentation;
};
};
};
```
Defines comprehensive configuration options for the `ViewportGizmo`. Each option customizes the appearance or behavior of the gizmo in the viewport.
- **`container`** — `HTMLElement | string`
Specifies the parent container for the gizmo. Can be an HTML element or a CSS selector string.
- **`type`** — `"sphere" | "cube" | "rounded-cube"`
Determines the gizmo configuration type. Defaults to `"sphere"`.
- **`size`** — `number`
Sets the size of the gizmo widget in pixels. Defaults to `128`.
- **`placement`** — `GizmoDomPlacement`
Determines the position of the gizmo in the viewport. Valid values include:
- `"top-left"`
- `"top-center"`
- `"top-right"`
- `"center-left"`
- `"center-center"`
- `"center-right"`
- `"bottom-left"`
- `"bottom-center"`
- `"bottom-right"`
- **`offset`** — `object`
Configures offset from container edges in pixels.
- **`left`** — `number`
Offset from the left edge.
- **`top`** — `number`
Offset from the top edge.
- **`right`** — `number`
Offset from the right edge.
- **`bottom`** — `number`
Offset from the bottom edge.
- **`animated`** — `boolean`
Enables or disables animations for view changes. Defaults to `true`.
- **`speed`** — `number`
Adjusts the animation speed for view transitions. Defaults to `1`.
- **`resolution`** — `number`
Adjusts the texture resolution. Defaults to `64` for sphere, `128` for cube.
- **`lineWidth`** — `number`
Sets the width of the axes lines in pixels.
- **`id`** — `string`
Sets the HTML `id` attribute for the gizmo container element.
- **`className`** — `string`
Sets the HTML `class` attribute for the gizmo container element.
- **`font`** — `object`
Configuration for the font used in axis labels.
- **`family`** — `string`
- Specifies the font family for the axis labels.
- **`weight`** — `string | number`
- Sets the font weight for axis labels.
- **`radius`** — `number`
Sets the axes edge radius. When using `rounded-cube` type, this will be the radius of the rounded edges and corners. Defaults to `1` for `sphere` and `0.2` for `rounded-cube`.
- **`background`** — `object`
Configures the background sphere/cube.
- **`enabled`** — `boolean`
Enables or disables the background.
- **`color`** — `ColorRepresentation`
Sets the background color in normal state.
- **`opacity`** — `number`
Sets the background opacity in normal state.
- **`hover`** — `object`
- **`color`** — `ColorRepresentation`
Sets the background color when hovered.
- **`opacity`** — `number`
Sets the background opacity when hovered.
- **`corners`** — `object`
Configures corner indicators.
- **`enabled`** — `boolean`
Enables or disables corner indicators.
- **`color`** — `ColorRepresentation`
Sets the base color of corner indicators.
- **`opacity`** — `number`
Sets the opacity of corner indicators.
- **`scale`** — `number`
Sets the scale multiplier for corner indicators.
- **`radius`** — `number`
Sets the radius of corner indicators.
- **`smoothness`** — `number`
Controls the smoothness of corner indicators.
- **`hover`** — `object`
- **`color`** — `ColorRepresentation`
Sets the color of corner indicators when hovered.
- **`opacity`** — `number`
Sets the opacity of corner indicators when hovered.
- **`scale`** — `number`
Sets the scale of corner indicators when hovered.
- **`edges`** — `object`
Configures edge indicators.
- **`enabled`** — `boolean`
- Enables or disables edge indicators.
- **`color`** — `ColorRepresentation`
- Sets the base color of edge indicators.
- **`opacity`** — `number`
- Sets the opacity of edge indicators.
- **`scale`** — `number`
- Sets the scale multiplier for edge indicators.
- **`radius`** — `number`
- Sets the radius of edge indicators.
- **`smoothness`** — `number`
- Controls the smoothness of edge indicators.
- **`hover`** — `object`
- **`color`** — `ColorRepresentation`
- Sets the color of edge indicators when hovered.
- **`opacity`** — `number`
- Sets the opacity of edge indicators when hovered.
- **`scale`** — `number`
- Sets the scale of edge indicators when hovered.
- **Axis Configurations**
- Configurations for `x`, `y`, `z`, `nx`, `ny`, `nz` axes
- Alias names for cube mode: `right`, `left`, `top`, `bottom`, `front`, `back`
Each axis configuration supports:
- **`enabled`** — `boolean`
Toggles the axis visibility.
- **`label`** — `string`
Custom text label for the axis.
- **`opacity`** — `number`
Sets the axis opacity.
- **`scale`** — `number`
Sets the scale multiplier for the indicator size.
- **`line`** — `boolean`
Toggles the axis line visibility.
- **`color`** — `ColorRepresentation`
Sets the axis indicator background color.
- **`labelColor`** — `ColorRepresentation`
Sets the axis label color.
- **`border`** — `object`
- **`size`** — `number`
Sets the border size around the axis indicator.
- **`color`** — `ColorRepresentation`
Sets the border color around the axis indicator.
- **`hover`** — `object`
- **`color`** — `ColorRepresentation`
Sets the fill color on hover.
- **`labelColor`** — `ColorRepresentation`
Sets the label text color on hover.
- **`opacity`** — `number`
Sets the opacity when hovered.
- **`scale`** — `number`
Sets the indicator scale when hovered.
- **`border`** — `object`
- **`size`** — `number`
Sets the hover border size.
- **`color`** — `ColorRepresentation`
Sets the hover border color.
---
### ViewportGizmoEventMap
Defines custom events emitted by `ViewportGizmo`. This extends `Object3DEventMap` with additional events for gizmo interactions.
- **`start`** — Triggered when a view change interaction begins.
- **`change`** — Triggered during view changes.
- **`end`** — Triggered when a view change interaction ends.
================================================
FILE: docs/custom.md
================================================
---
layout: home
---
<script setup lang="ts">
import { ref, computed } from "vue";
const type = ref<"cube" | "sphere" | "rounded-cube">("sphere");
const label = computed(() => type.value === "sphere" ? "⬜ Cube" : type.value === "rounded-cube" ? "⬜ Rounded Cube" : "⬤ Sphere");
const switchType = () => type.value = type.value === "sphere" ? "cube" : type.value === "cube" ? "rounded-cube" : "sphere";
</script>
<button class="VPLink link" @click="switchType">Switch to type: <span>{{ label }}</span></button>
<IframeContainer :url="`config.html?type=${type}`" />
<style scoped>
button {
display: flex;
margin: auto;
padding: 0 1em;
background: var(--vp-c-brand-3);
border-radius: 1em;
}
span {
padding: 0 0 0 0.5em;
font-weight: 900;
}
</style>
================================================
FILE: docs/examples/multiple-elements.md
================================================
<script setup lang="ts">
const type = new URLSearchParams(window.location.search).get("type") || "sphere";
</script>
# Multiple Elements
<IframeContainer :url="`multiple-elements.html?type=${type}`" />
This example demonstrates using ViewportGizmo with Three.js multi-element rendering using scissor rendering. Each viewport is defined by scissor areas, and the gizmo is set up to track camera orientation in each section independently. This setup allows flexible multi-view configurations while maintaining orientation cues across different viewports.
### Source
[...samples/multiple-elements.html](https://github.com/Fennec-hub/three-viewport-gizmo/blob/main/docs/public/samples/multiple-elements.html?type="")
================================================
FILE: docs/examples/orbit-controls-events.md
================================================
<script setup lang="ts">
const type = new URLSearchParams(window.location.search).get("type") || "sphere";
</script>
# OrbitControls Events
<IframeContainer :url="`orbit-controls-events.html?type=${type}`" />
This example demonstrates using event listeners for finer control over ViewportGizmo integration with OrbitControls. The gizmo and controls share a target, and event listeners toggle control states and update the gizmo when the controls change, ensuring smooth interaction.
<div v-if="type === 'cube'">
```js
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import { ViewportGizmo } from "three-viewport-gizmo";
//... Scene's initialization
// Initialize The Gizmo
const gizmo = new ViewportGizmo(camera, renderer);
const controls = new OrbitControls(camera, renderer.domElement);
// The Gizmo and the OrbitControls must share the same target
gizmo.target = controls.target.set(0, 3, 0);
camera.lookAt(controls.target);
// Set the events listeners
gizmo.addEventListener("start", () => (controls.enabled = false));
gizmo.addEventListener("end", () => (controls.enabled = true));
controls.addEventListener("change", () => gizmo.update(false));
// Render
function animation(time) {
//... Scene's animations and render
gizmo.render();
}
// Resize
window.onresize = () => {
//... Scene's resize logic
controls.update();
};
```
</div>
<div v-else-if="type === `cube`">
```js
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import { ViewportGizmo } from "three-viewport-gizmo";
//... Scene's initialization
// Initialize The Gizmo
const gizmo = new ViewportGizmo(camera, renderer);
const controls = new OrbitControls(camera, renderer.domElement, {
type: "cube",
});
// The Gizmo and the OrbitControls must share the same target
gizmo.target = controls.target.set(0, 3, 0);
camera.lookAt(controls.target);
// Set the events listeners
gizmo.addEventListener("start", () => (controls.enabled = false));
gizmo.addEventListener("end", () => (controls.enabled = true));
controls.addEventListener("change", () => gizmo.update(false));
// Render
function animation(time) {
//... Scene's animations and render
gizmo.render();
}
// Resize
window.onresize = () => {
//... Scene's resize logic
controls.update();
};
```
</div>
<div v-else-if="type === `rounded-cube`">
```js
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import { ViewportGizmo } from "three-viewport-gizmo";
//... Scene's initialization
// Initialize The Gizmo
const gizmo = new ViewportGizmo(camera, renderer);
const controls = new OrbitControls(camera, renderer.domElement, {
type: "rounded-cube",
});
// The Gizmo and the OrbitControls must share the same target
gizmo.target = controls.target.set(0, 3, 0);
camera.lookAt(controls.target);
// Set the events listeners
gizmo.addEventListener("start", () => (controls.enabled = false));
gizmo.addEventListener("end", () => (controls.enabled = true));
controls.addEventListener("change", () => gizmo.update(false));
// Render
function animation(time) {
//... Scene's animations and render
gizmo.render();
}
// Resize
window.onresize = () => {
//... Scene's resize logic
controls.update();
};
```
</div>
### Source
[...samples/orbit-controls-events.html](https://github.com/Fennec-hub/three-viewport-gizmo/blob/main/docs/public/samples/orbit-controls-events.html)
================================================
FILE: docs/examples/orbit-controls.md
================================================
<script setup lang="ts">
const type = new URLSearchParams(window.location.search).get("type") || "sphere";
</script>
# OrbitControls
<IframeContainer :url="`orbit-controls.html?type=${type}`" />
This example shows how to integrate ViewportGizmo with OrbitControls in a Three.js scene. The gizmo provides an interactive orientation guide linked to the camera, helping with navigation. It updates automatically with window resizing to keep the view responsive.
<div v-if="type === `sphere`">
```js {9,10,16,23}
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import { ViewportGizmo } from "three-viewport-gizmo";
//... Initialize your Scene
const controls = new OrbitControls(camera, renderer.domElement);
const gizmo = new ViewportGizmo(camera, renderer);
gizmo.attachControls(controls);
// Render
function animation(time) {
//... Scene's animations and render
gizmo.render();
}
// Resize
window.onresize = () => {
//... Scene's resize logic
gizmo.update();
};
```
</div>
<div v-else-if="type === `cube`">
```js {9,10,16,23}
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import { ViewportGizmo } from "three-viewport-gizmo";
//... Initialize your Scene
const controls = new OrbitControls(camera, renderer.domElement);
const gizmo = new ViewportGizmo(camera, renderer, { type: "cube" });
gizmo.attachControls(controls);
// Render
function animation(time) {
//... Scene's animations and render
gizmo.render();
}
// Resize
window.onresize = () => {
//... Scene's resize logic
gizmo.update();
};
```
</div>
<div v-else-if="type === `rounded-cube`">
```js {9,10,16,23}
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import { ViewportGizmo } from "three-viewport-gizmo";
//... Initialize your Scene
const controls = new OrbitControls(camera, renderer.domElement);
const gizmo = new ViewportGizmo(camera, renderer, { type: "rounded-cube" });
gizmo.attachControls(controls);
// Render
function animation(time) {
//... Scene's animations and render
gizmo.render();
}
// Resize
window.onresize = () => {
//... Scene's resize logic
gizmo.update();
};
```
</div>
### Source
[...samples/orbit-controls.html](https://github.com/Fennec-hub/three-viewport-gizmo/blob/main/docs/public/samples/orbit-controls.html)
================================================
FILE: docs/examples/post-processing.md
================================================
<script setup lang="ts">
const type = new URLSearchParams(window.location.search).get("type") || "sphere";
</script>
# Post processing
<IframeContainer :url="`post-processing.html?type=${type}`" />
This example integrates ViewportGizmo with Three.js post-processing effects. The gizmo operates alongside visual effects, providing an orientation guide without interfering with post-processing layers. Resize handling keeps the gizmo and effects responsive to screen size changes.
### Source
[...samples/post-processing.html](https://github.com/Fennec-hub/three-viewport-gizmo/blob/main/docs/public/samples/post-processing.html)
================================================
FILE: docs/examples/resizable-grid.md
================================================
<script setup lang="ts">
const type = new URLSearchParams(window.location.search).get("type") || "sphere";
</script>
# Resizable Grid
<IframeContainer :url="`resizable-grid.html?type=${type}`" />
This example shows ViewportGizmo integrated with a resizable grid, simulating its use in a mockup editor. The gizmo provides orientation feedback for camera movements within a grid-based layout, allowing users to navigate and align objects within a design space. The grid and gizmo adjust responsively as the editor is resized, ensuring smooth, intuitive interaction.
### Source
[...samples/resizable-grid.html](https://github.com/Fennec-hub/three-viewport-gizmo/blob/main/docs/public/samples/resizable-grid.html)
================================================
FILE: docs/examples/responsive.md
================================================
<script setup lang="ts">
const type = new URLSearchParams(window.location.search).get("type") || "sphere";
</script>
# Responsive
<IframeContainer :url="`responsive.html?type=${type}`" />
This example illustrates ViewportGizmo in a CSS-responsive design setup. The gizmo adapts to different screen sizes, maintaining its functionality and orientation cues across various devices. CSS media queries and flexible layouts ensure that the gizmo remains usable and visually appealing in responsive web environments, enhancing the user experience.
---
First, assign a class name or an ID to the gizmo instance for styling purposes:
<div v-if="type === `sphere`">
```javascript
const gizmo = new ViewportGizmo(camera, renderer, {
className: "responsive-gizmo",
});
```
</div>
<div v-else-if="type === `cube`">
```javascript
const gizmo = new ViewportGizmo(camera, renderer, {
type: "cube",
className: "responsive-gizmo",
});
```
</div>
<div v-else-if="type === `rounded-cube`">
```javascript
const gizmo = new ViewportGizmo(camera, renderer, {
type: "rounded-cube",
className: "responsive-gizmo",
});
```
</div>
Next, define your CSS styles with responsiveness in mind, just as you would for any HTML element.
::: warning `!important`
Be sure to use the `!important` flag to override the default styles applied to the gizmo.
:::
```css
.responsive-gizmo {
width: auto !important;
height: 50% !important;
aspect-ratio: 1;
}
```
This setup ensures that the gizmo scales appropriately within your responsive design, enhancing usability across different screen sizes.
### Source
[...samples/responsive.html](https://github.com/Fennec-hub/three-viewport-gizmo/blob/main/docs/public/samples/responsive.html)
================================================
FILE: docs/examples/standalone.md
================================================
<script setup lang="ts">
const type = new URLSearchParams(window.location.search).get("type") || "sphere";
</script>
# Standalone
<IframeContainer :url="`standalone.html?type=${type}`" />
This example shows ViewportGizmo used as a standalone component without OrbitControls. The gizmo provides an independent orientation guide, allowing direct manipulation of the camera's view without additional control layers. Resize handling ensures the gizmo remains responsive across different screen sizes.
### Source
[...samples/standalone.html](https://github.com/Fennec-hub/three-viewport-gizmo/blob/main/docs/public/samples/standalone.html)
================================================
FILE: docs/examples/x-up.md
================================================
<script setup lang="ts">
const type = new URLSearchParams(window.location.search).get("type") || "sphere";
</script>
# X-up Coordinate System
<IframeContainer :url="`x-up.html?type=${type}`" />
This example demonstrates how to use ViewportGizmo in a Three.js scene with an X-up coordinate system. This coordinate system is used in specific engineering applications, some physics engines, and certain legacy CAD workflows where the X-axis represents the vertical direction.
The Gizmo is designed to work out of the box with the X-up, right-handed coordinate system without requiring additional configuration.
The default directions are as follows:
- Up: `X+`
- Right: `Z+`
- Forward: `Y-`
### Source
[...samples/x-up.html](https://github.com/Fennec-hub/three-viewport-gizmo/blob/main/docs/public/samples/x-up.html)
================================================
FILE: docs/examples/yomotsu-camera-controls.md
================================================
<script setup lang="ts">
const type = new URLSearchParams(window.location.search).get("type") || "sphere";
</script>
# Yomotsu Camera Controls
<IframeContainer :url="`yomotsu-camera-controls.html?type=${type}`" />
This example demonstrates integrating ViewportGizmo with [Yomotsu camera controls](https://github.com/yomotsu/camera-controls), showing how to link the gizmo with advanced or
custom control setups. The gizmo is configured to follow the camera's movements, allowing precise orientation feedback while preserving custom control behaviors. Resize handling ensures the gizmo and controls remain responsive.
### Source
[...samples/yomotsu-camera-controls.html](https://github.com/Fennec-hub/three-viewport-gizmo/blob/main/docs/public/samples/yomotsu-camera-controls.html)
================================================
FILE: docs/examples/z-up.md
================================================
<script setup lang="ts">
const type = new URLSearchParams(window.location.search).get("type") || "sphere";
</script>
# Z-up Coordinate System
<IframeContainer :url="`z-up.html?type=${type}`" />
This example demonstrates how to use ViewportGizmo in a Three.js scene with a Z-up coordinate system. This coordinate system is commonly used in CAD applications, where the Z-axis represents the vertical direction.
The Gizmo is designed to work out of the box with the Z-up, right-handed coordinate system without requiring additional configuration.
The default directions are as follows:
- Up: `Z+`
- Right: `X+`
- Forward: `Y+`
### Source
[...samples/z-up.html](https://github.com/Fennec-hub/three-viewport-gizmo/blob/main/docs/public/samples/z-up.html)
================================================
FILE: docs/index.md
================================================
---
layout: home
hero:
name: Three Viewport Gizmo
text: 3D View Camera Helper for Three.js
tagline: Lightweight, Customizable & Responsive
image:
src: ./assets/three-viewport-gizmo.png
alt: Three Viewport Gizmo Preview
actions:
- theme: brand
text: Quickstart
link: /quickstart
- theme: alt
text: Examples
link: /examples/orbit-controls
- theme: alt
text: View on GitHub
link: https://github.com/fennec-hub/three-viewport-gizmo
features:
- icon: { src: "./assets/icons/camera.svg", alt: "camera" }
title: Universal Camera Support
details: Seamlessly integrates with any camera setup, whether standalone or with OrbitControls.
- icon: { src: "./assets/icons/palette.svg", alt: "palette" }
title: Fully Customizable
details: From position and size to axis colors and fonts. Adapt the gizmo to match your project's aesthetic perfectly.
- icon: { src: "./assets/icons/responsive.svg", alt: "responsive" }
title: Responsive Design
details: Automatically adapts to viewport changes and different screen sizes for both desktop and mobile applications.
- icon: { src: "./assets/icons/render.svg", alt: "render" }
title: Advanced Rendering Support
details: Compatible with post-processing effects, multiple viewports, and scissor rendering.
head:
- - meta
- name: description
content: Three Viewport Gizmo - A customizable 3D orientation helper for Three.js cameras
- - meta
- name: keywords
content: three.js, webgl, 3d, viewport, gizmo, camera, controls
footer: MIT Licensed | Copyright © 2024 Fennec
---
<script setup lang="ts">
import { onMounted } from 'vue';
onMounted(() => {
const heroImage = document.querySelector('.VPHero .image-container')!;
const container = heroImage.parentElement
heroImage.remove();
const iframe = document.createElement('iframe')
iframe.src = `${window.location.origin}/three-viewport-gizmo/samples/responsive.html`;
iframe.style.width = '100%'
iframe.style.height = '100%'
iframe.frameBorder = '0'
container.appendChild(iframe);
})
</script>
================================================
FILE: docs/public/assets/fonts/Roboto_Regular.json
================================================
{"glyphs":{"0":{"ha":780,"x_min":78,"x_max":701,"o":"m 701 421 q 626 94 701 201 q 391 -14 551 -14 q 157 91 233 -14 q 78 404 81 196 l 78 572 q 153 895 78 789 q 389 1001 229 1001 q 624 899 549 1001 q 701 584 699 797 l 701 421 m 576 593 q 531 825 576 752 q 389 898 486 898 q 249 825 293 898 q 203 602 205 753 l 203 401 q 250 165 203 241 q 391 89 296 89 q 529 161 484 89 q 576 387 574 233 l 576 593 z "},"1":{"ha":780,"x_min":115,"x_max":494,"o":"m 494 0 l 368 0 l 368 836 l 115 743 l 115 857 l 475 992 l 494 992 l 494 0 z "},"2":{"ha":780,"x_min":63,"x_max":729,"o":"m 729 0 l 82 0 l 82 90 l 424 470 q 529 610 500 556 q 557 722 557 664 q 511 848 557 799 q 386 898 464 898 q 240 845 292 898 q 189 696 189 791 l 63 696 q 151 917 63 833 q 386 1001 239 1001 q 604 929 524 1001 q 684 736 684 857 q 498 389 684 591 l 233 102 l 729 102 l 729 0 z "},"3":{"ha":780,"x_min":64,"x_max":690,"o":"m 264 555 l 359 555 q 498 602 448 556 q 549 724 549 647 q 376 898 549 898 q 247 851 295 898 q 198 728 198 805 l 73 728 q 158 923 73 846 q 376 1001 244 1001 q 595 927 516 1001 q 675 722 675 853 q 633 597 675 657 q 519 507 591 536 q 645 421 601 481 q 690 275 690 361 q 603 64 690 142 q 377 -14 516 -14 q 151 62 238 -14 q 64 260 64 137 l 190 260 q 241 136 190 182 q 377 89 292 89 q 516 136 468 89 q 564 273 564 184 q 511 405 564 359 q 359 452 458 451 l 264 452 l 264 555 z "},"4":{"ha":780,"x_min":36,"x_max":749,"o":"m 612 332 l 749 332 l 749 229 l 612 229 l 612 0 l 486 0 l 486 229 l 36 229 l 36 303 l 478 987 l 612 987 l 612 332 m 178 332 l 486 332 l 486 816 l 471 789 l 178 332 z "},"5":{"ha":780,"x_min":104,"x_max":725,"o":"m 140 495 l 190 987 l 696 987 l 696 871 l 296 871 l 267 602 q 431 645 339 645 q 646 556 566 645 q 725 315 725 467 q 643 74 725 162 q 412 -14 560 -14 q 199 59 281 -14 q 104 260 116 132 l 223 260 q 283 132 235 175 q 412 89 332 89 q 550 149 500 89 q 600 313 600 208 q 546 472 600 412 q 402 532 492 532 q 273 496 320 532 l 240 469 l 140 495 z "},"6":{"ha":780,"x_min":90,"x_max":713,"o":"m 574 988 l 574 882 l 551 882 q 318 795 405 879 q 218 558 231 711 q 431 648 296 648 q 636 557 559 648 q 713 322 713 466 q 630 78 713 170 q 408 -14 547 -14 q 178 95 266 -14 q 90 376 90 204 l 90 424 q 206 841 90 696 q 552 988 322 985 l 574 988 m 410 543 q 291 505 345 543 q 216 408 237 466 l 216 361 q 271 165 216 239 q 408 90 326 90 q 541 153 492 90 q 589 316 589 215 q 540 480 589 418 q 410 543 491 543 z "},"7":{"ha":780,"x_min":52,"x_max":720,"o":"m 720 917 l 311 0 l 179 0 l 587 884 l 52 884 l 52 987 l 720 987 l 720 917 z "},"8":{"ha":780,"x_min":76,"x_max":704,"o":"m 681 730 q 642 598 681 656 q 536 508 603 541 q 659 411 614 475 q 704 267 704 347 q 617 62 704 138 q 390 -14 531 -14 q 162 63 248 -14 q 76 267 76 139 q 120 411 76 347 q 241 509 163 475 q 137 599 175 541 q 99 730 99 656 q 179 928 99 854 q 390 1001 259 1001 q 601 928 520 1001 q 681 730 681 854 m 578 269 q 526 404 578 352 q 389 456 473 456 q 253 405 304 456 q 201 269 201 353 q 251 137 201 185 q 390 89 301 89 q 528 137 478 89 q 578 269 578 186 m 390 898 q 270 852 316 898 q 224 728 224 806 q 270 606 224 652 q 390 559 315 559 q 510 606 465 559 q 555 728 555 652 q 509 850 555 803 q 390 898 462 898 z "},"9":{"ha":780,"x_min":68,"x_max":689,"o":"m 563 434 q 469 359 524 387 q 349 330 414 330 q 200 372 264 330 q 102 491 137 414 q 68 659 68 567 q 105 838 68 758 q 212 959 143 917 q 374 1001 281 1001 q 605 891 520 1001 q 689 593 689 782 l 689 556 q 575 135 689 268 q 231 -1 461 3 l 207 -1 l 207 105 l 233 105 q 472 186 389 108 q 563 434 555 264 m 370 434 q 486 473 433 434 q 564 568 539 511 l 564 618 q 510 819 564 742 q 374 897 456 897 q 241 833 292 897 q 191 666 191 770 q 240 499 191 565 q 370 434 288 434 z "},"\u0000":{"ha":0,"x_min":0,"x_max":0,"o":""},"\u0002":{"ha":0,"x_min":0,"x_max":0,"o":""},"\r":{"ha":344,"x_min":0,"x_max":0,"o":""}," ":{"ha":344,"x_min":0,"x_max":0,"o":""},"!":{"ha":357,"x_min":109,"x_max":257,"o":"m 235 279 l 122 279 l 113 987 l 245 987 l 235 279 m 109 63 q 127 114 109 94 q 182 135 146 135 q 238 114 219 135 q 257 63 257 94 q 238 13 257 33 q 182 -7 219 -7 q 127 13 146 -7 q 109 63 109 33 z "},"\"":{"ha":444,"x_min":92,"x_max":371,"o":"m 188 949 l 168 707 l 92 707 l 93 1042 l 188 1042 l 188 949 m 371 949 l 351 707 l 275 707 l 276 1042 l 371 1042 l 371 949 z "},"#":{"ha":855,"x_min":81,"x_max":838,"o":"m 519 278 l 340 278 l 286 0 l 189 0 l 243 278 l 81 278 l 81 371 l 260 371 l 307 611 l 132 611 l 132 705 l 326 705 l 381 987 l 478 987 l 422 705 l 602 705 l 657 987 l 755 987 l 699 705 l 838 705 l 838 611 l 681 611 l 634 371 l 787 371 l 787 278 l 616 278 l 562 0 l 465 0 l 519 278 m 357 371 l 536 371 l 583 611 l 404 611 l 357 371 z "},"$":{"ha":780,"x_min":75,"x_max":706,"o":"m 581 254 q 537 359 581 317 q 389 437 493 402 q 179 552 245 481 q 113 732 113 623 q 177 914 113 843 q 355 999 242 985 l 355 1147 l 456 1147 l 456 998 q 634 903 570 983 q 697 684 697 823 l 572 684 q 527 836 572 779 q 404 892 481 892 q 281 850 323 892 q 238 734 238 808 q 283 624 238 665 q 432 549 328 583 q 593 475 535 515 q 679 381 651 435 q 706 256 706 328 q 637 71 706 141 q 444 -12 568 1 l 444 -141 l 344 -141 l 344 -12 q 146 78 218 0 q 75 291 75 157 l 200 291 q 250 146 200 197 q 390 95 299 95 q 530 138 479 95 q 581 254 581 180 z "},"%":{"ha":1017,"x_min":71,"x_max":957,"o":"m 71 798 q 128 944 71 886 q 273 1002 184 1002 q 419 944 363 1002 q 475 793 475 886 l 475 745 q 419 599 475 656 q 275 543 362 543 q 129 599 186 543 q 71 750 71 656 l 71 798 m 165 745 q 195 658 165 692 q 275 624 224 624 q 352 657 323 624 q 382 748 382 690 l 382 798 q 353 885 382 850 q 273 920 323 920 q 194 885 223 920 q 165 795 165 850 l 165 745 m 552 242 q 609 388 552 331 q 754 446 665 446 q 900 389 843 446 q 957 237 957 332 l 957 189 q 900 43 957 100 q 755 -14 844 -14 q 610 42 667 -14 q 552 193 552 99 l 552 242 m 646 189 q 675 101 646 136 q 755 67 705 67 q 833 101 804 67 q 863 192 863 134 l 863 242 q 833 330 863 296 q 754 363 804 363 q 676 330 706 363 q 646 239 646 296 l 646 189 m 303 75 l 232 119 l 714 891 l 785 846 l 303 75 z "},"&":{"ha":863,"x_min":68,"x_max":859,"o":"m 68 265 q 108 396 68 336 q 260 535 148 456 q 172 664 194 615 q 149 761 149 713 q 216 937 149 873 q 396 1001 282 1001 q 564 942 498 1001 q 631 792 631 882 q 601 682 631 732 q 495 576 570 631 l 422 522 l 642 260 q 688 456 688 348 l 802 456 q 718 169 802 283 l 859 0 l 709 0 l 643 78 q 525 10 593 33 q 388 -14 458 -14 q 156 63 243 -14 q 68 265 68 140 m 388 89 q 570 165 488 89 l 330 453 l 307 437 q 194 265 194 353 q 246 137 194 185 q 388 89 298 89 m 275 765 q 355 602 275 700 l 435 659 q 498 721 481 691 q 515 792 515 750 q 481 868 515 838 q 395 898 447 898 q 307 860 340 898 q 275 765 275 823 z "},"'":{"ha":242,"x_min":70,"x_max":172,"o":"m 172 966 l 157 717 l 70 717 l 71 1042 l 172 1042 l 172 966 z "},"(":{"ha":475,"x_min":90,"x_max":448,"o":"m 90 401 q 131 695 90 554 q 254 951 172 836 q 422 1114 335 1067 l 448 1031 q 286 800 349 956 q 216 450 222 644 l 216 393 q 311 -62 216 131 q 448 -242 369 -177 l 422 -319 q 250 -151 332 -269 q 90 401 90 80 z "},")":{"ha":483,"x_min":26,"x_max":385,"o":"m 385 394 q 345 104 385 243 q 223 -152 305 -35 q 52 -319 142 -269 l 26 -242 q 194 6 130 -162 q 258 380 257 175 l 258 402 q 229 667 258 545 q 145 886 199 789 q 26 1038 92 983 l 52 1114 q 223 949 142 1065 q 344 693 304 833 q 385 394 385 553 z "},"*":{"ha":598,"x_min":19,"x_max":578,"o":"m 224 667 l 19 728 l 50 830 l 255 754 l 249 987 l 353 987 l 346 751 l 547 825 l 578 722 l 370 661 l 505 477 l 420 413 l 294 608 l 172 418 l 87 479 l 224 667 z "},"+":{"ha":787,"x_min":53,"x_max":730,"o":"m 454 530 l 730 530 l 730 411 l 454 411 l 454 99 l 328 99 l 328 411 l 53 411 l 53 530 l 328 530 l 328 818 l 454 818 l 454 530 z "},",":{"ha":273,"x_min":20,"x_max":209,"o":"m 91 -197 l 20 -148 q 86 35 83 -59 l 86 149 l 209 149 l 209 50 q 175 -87 209 -18 q 91 -197 142 -155 z "},"-":{"ha":383,"x_min":25,"x_max":356,"o":"m 356 368 l 25 368 l 25 471 l 356 471 l 356 368 z "},".":{"ha":366,"x_min":98,"x_max":254,"o":"m 98 66 q 117 120 98 98 q 175 142 136 142 q 234 120 214 142 q 254 66 254 98 q 234 14 254 35 q 175 -7 214 -7 q 117 14 136 -7 q 98 66 98 35 z "},"/":{"ha":572,"x_min":12,"x_max":532,"o":"m 120 -85 l 12 -85 l 425 987 l 532 987 l 120 -85 z "},":":{"ha":336,"x_min":91,"x_max":248,"o":"m 91 66 q 110 120 91 98 q 168 142 130 142 q 227 120 207 142 q 247 66 247 98 q 227 14 247 35 q 168 -7 207 -7 q 110 14 130 -7 q 91 66 91 35 m 92 665 q 111 719 92 697 q 169 741 130 741 q 228 719 208 741 q 248 665 248 697 q 228 612 248 633 q 169 591 208 591 q 111 612 130 591 q 92 665 92 633 z "},";":{"ha":294,"x_min":28,"x_max":231,"o":"m 75 665 q 95 719 75 697 q 153 741 114 741 q 211 719 191 741 q 231 665 231 697 q 211 612 231 633 q 153 591 191 591 q 95 612 114 591 q 75 665 75 633 m 99 -197 l 28 -148 q 94 35 92 -59 l 94 149 l 217 149 l 217 50 q 183 -87 217 -18 q 99 -197 150 -155 z "},"<":{"ha":706,"x_min":49,"x_max":604,"o":"m 179 437 l 604 265 l 604 132 l 49 389 l 49 488 l 604 745 l 604 612 l 179 437 z "},"=":{"ha":762,"x_min":103,"x_max":669,"o":"m 669 552 l 103 552 l 103 661 l 669 661 l 669 552 m 669 271 l 103 271 l 103 379 l 669 379 l 669 271 z "},">":{"ha":726,"x_min":91,"x_max":670,"o":"m 539 441 l 91 616 l 91 745 l 670 489 l 670 390 l 91 133 l 91 263 l 539 441 z "},"?":{"ha":656,"x_min":51,"x_max":601,"o":"m 242 278 q 260 406 243 359 q 330 509 277 452 l 418 601 q 475 739 475 665 q 438 851 475 810 q 330 891 401 891 q 218 854 260 891 q 176 756 176 818 l 51 756 q 129 933 52 866 q 330 1001 206 1001 q 529 932 458 1001 q 601 743 601 863 q 491 509 601 625 l 417 436 q 368 278 368 381 l 242 278 m 237 63 q 255 114 237 94 q 311 135 274 135 q 366 114 347 135 q 385 63 385 94 q 366 13 385 33 q 311 -7 347 -7 q 255 13 274 -7 q 237 63 237 33 z "},"@":{"ha":1247,"x_min":72,"x_max":1187,"o":"m 1179 340 q 1097 81 1171 176 q 901 -14 1024 -14 q 739 100 774 -14 q 655 15 702 43 q 557 -14 608 -14 q 412 65 461 -14 q 375 283 362 144 q 426 483 385 395 q 532 621 467 570 q 668 671 597 671 q 766 656 723 671 q 864 599 810 642 l 829 223 q 916 66 816 66 q 1040 142 992 66 q 1091 340 1087 218 q 993 743 1104 604 q 656 881 882 881 q 414 813 519 881 q 247 618 308 746 q 178 324 186 491 q 219 32 170 156 q 368 -157 268 -92 q 610 -222 468 -222 q 732 -208 671 -222 q 834 -169 793 -193 l 859 -247 q 748 -290 817 -273 q 607 -307 678 -307 q 315 -231 437 -307 q 133 -11 193 -155 q 80 324 72 132 q 163 660 88 511 q 368 889 239 808 q 659 970 496 970 q 948 895 827 970 q 1128 675 1069 819 q 1179 340 1187 532 m 483 283 q 501 135 473 186 q 588 83 528 83 q 666 118 629 83 q 728 217 704 153 l 729 223 l 760 564 q 679 584 722 584 q 548 503 600 584 q 483 283 496 422 z "},"A":{"ha":906,"x_min":19,"x_max":888,"o":"m 660 258 l 246 258 l 153 0 l 19 0 l 396 987 l 510 987 l 888 0 l 754 0 l 660 258 m 286 365 l 621 365 l 453 827 l 286 365 z "},"B":{"ha":865,"x_min":115,"x_max":787,"o":"m 115 0 l 115 987 l 437 987 q 679 921 598 987 q 760 724 760 854 q 721 602 760 655 q 614 519 682 549 q 740 434 694 497 q 787 285 787 372 q 701 76 787 152 q 457 0 614 0 l 115 0 m 245 462 l 245 106 l 460 106 q 603 154 551 106 q 656 283 656 201 q 462 462 656 462 l 245 462 m 245 566 l 441 566 q 578 609 527 566 q 629 725 629 652 q 582 843 629 806 q 437 880 534 880 l 245 880 l 245 566 z "},"C":{"ha":904,"x_min":81,"x_max":841,"o":"m 841 313 q 725 72 823 157 q 467 -14 628 -14 q 186 112 292 -14 q 81 448 81 237 l 81 543 q 130 785 81 680 q 269 945 179 889 q 478 1001 359 1001 q 730 913 635 1001 q 841 670 825 825 l 710 670 q 636 841 693 788 q 478 894 580 894 q 282 802 353 894 q 212 539 212 709 l 212 444 q 279 188 212 283 q 467 93 346 93 q 633 142 575 93 q 710 313 691 191 l 841 313 z "},"D":{"ha":911,"x_min":115,"x_max":829,"o":"m 115 0 l 115 987 l 393 987 q 621 930 522 987 q 774 768 720 873 q 829 527 828 663 l 829 464 q 775 219 829 324 q 621 58 721 114 q 388 0 521 1 l 115 0 m 245 880 l 245 106 l 382 106 q 616 200 532 106 q 700 467 700 294 l 700 524 q 621 786 700 692 q 397 880 542 879 l 245 880 z "},"E":{"ha":789,"x_min":115,"x_max":742,"o":"m 673 456 l 245 456 l 245 106 l 742 106 l 742 0 l 115 0 l 115 987 l 735 987 l 735 880 l 245 880 l 245 563 l 673 563 l 673 456 z "},"F":{"ha":768,"x_min":115,"x_max":726,"o":"m 659 436 l 245 436 l 245 0 l 115 0 l 115 987 l 726 987 l 726 880 l 245 880 l 245 543 l 659 543 l 659 436 z "},"G":{"ha":946,"x_min":83,"x_max":844,"o":"m 844 130 q 704 22 793 58 q 494 -14 614 -14 q 280 43 374 -14 q 135 204 186 100 q 83 446 84 309 l 83 532 q 187 878 83 755 q 479 1001 291 1001 q 728 922 634 1001 q 844 698 823 843 l 713 698 q 480 894 677 894 q 282 802 349 894 q 214 536 214 710 l 214 455 q 290 191 214 289 q 495 93 366 93 q 623 109 568 93 q 714 164 678 125 l 714 386 l 486 386 l 486 492 l 844 492 l 844 130 z "},"H":{"ha":990,"x_min":115,"x_max":873,"o":"m 873 0 l 743 0 l 743 456 l 245 456 l 245 0 l 115 0 l 115 987 l 245 987 l 245 563 l 743 563 l 743 987 l 873 987 l 873 0 z "},"I":{"ha":378,"x_min":124,"x_max":254,"o":"m 254 0 l 124 0 l 124 987 l 254 987 l 254 0 z "},"J":{"ha":766,"x_min":36,"x_max":659,"o":"m 528 987 l 659 987 l 659 288 q 574 66 659 146 q 347 -14 489 -14 q 118 62 200 -14 q 36 273 36 137 l 166 273 q 213 140 166 188 q 347 93 259 93 q 478 144 428 93 q 528 286 528 195 l 528 987 z "},"K":{"ha":871,"x_min":115,"x_max":871,"o":"m 366 459 l 245 334 l 245 0 l 115 0 l 115 987 l 245 987 l 245 499 l 684 987 l 841 987 l 452 551 l 871 0 l 715 0 l 366 459 z "},"L":{"ha":747,"x_min":115,"x_max":713,"o":"m 245 106 l 713 106 l 713 0 l 115 0 l 115 987 l 245 987 l 245 106 z "},"M":{"ha":1213,"x_min":115,"x_max":1097,"o":"m 283 987 l 606 182 l 928 987 l 1097 987 l 1097 0 l 967 0 l 967 385 l 979 800 l 655 0 l 555 0 l 232 798 l 245 385 l 245 0 l 115 0 l 115 987 l 283 987 z "},"N":{"ha":990,"x_min":115,"x_max":873,"o":"m 873 0 l 743 0 l 245 761 l 245 0 l 115 0 l 115 987 l 245 987 l 744 223 l 744 987 l 873 987 l 873 0 z "},"O":{"ha":955,"x_min":80,"x_max":874,"o":"m 874 462 q 825 209 874 317 q 687 43 777 100 q 478 -14 597 -14 q 271 44 361 -14 q 131 207 181 101 q 80 453 81 313 l 80 524 q 130 776 80 667 q 270 943 179 885 q 477 1001 360 1001 q 686 944 595 1001 q 825 778 777 886 q 874 524 874 669 l 874 462 m 745 526 q 674 795 745 701 q 477 889 604 889 q 282 795 353 889 q 210 534 212 701 l 210 462 q 281 194 210 292 q 478 97 353 97 q 673 189 604 97 q 745 452 743 281 l 745 526 z "},"P":{"ha":876,"x_min":115,"x_max":825,"o":"m 245 387 l 245 0 l 115 0 l 115 987 l 479 987 q 733 905 641 987 q 825 686 825 822 q 735 464 825 542 q 477 387 645 387 l 245 387 m 245 493 l 479 493 q 639 542 583 493 q 694 684 694 591 q 639 825 694 772 q 486 880 583 878 l 245 880 l 245 493 z "},"Q":{"ha":955,"x_min":74,"x_max":872,"o":"m 869 462 q 823 215 869 319 q 696 54 778 112 l 872 -85 l 783 -167 l 575 -1 q 472 -14 526 -14 q 265 44 355 -14 q 125 207 175 101 q 74 453 75 313 l 74 524 q 123 776 74 667 q 263 943 173 885 q 471 1001 354 1001 q 681 943 590 1001 q 820 778 771 886 q 869 525 869 669 l 869 462 m 739 526 q 669 794 739 700 q 471 889 600 889 q 277 795 348 889 q 204 534 206 701 l 204 462 q 275 195 204 292 q 472 97 346 97 q 667 189 598 97 q 739 452 736 281 l 739 526 z "},"R":{"ha":855,"x_min":114,"x_max":831,"o":"m 477 399 l 245 399 l 245 0 l 114 0 l 114 987 l 441 987 q 697 911 608 987 q 787 690 787 836 q 738 530 787 598 q 599 427 688 461 l 831 8 l 831 0 l 691 0 l 477 399 m 245 506 l 445 506 q 599 556 542 506 q 656 690 656 606 q 602 831 656 782 q 444 880 547 880 l 245 880 l 245 506 z "},"S":{"ha":824,"x_min":54,"x_max":772,"o":"m 406 440 q 162 558 238 488 q 85 732 85 629 q 179 925 85 848 q 421 1001 272 1001 q 603 962 523 1001 q 726 853 682 922 q 770 702 770 784 l 639 702 q 582 843 639 791 q 421 894 525 894 q 271 851 325 894 q 217 734 217 809 q 268 632 217 673 q 443 555 319 590 q 635 479 566 521 q 738 382 705 437 q 772 251 772 326 q 678 59 772 131 q 428 -14 585 -14 q 238 25 326 -14 q 102 132 150 64 q 54 286 54 200 l 185 286 q 251 145 185 197 q 428 93 317 93 q 586 135 531 93 q 641 250 641 177 q 590 362 641 322 q 406 440 539 401 z "},"T":{"ha":829,"x_min":33,"x_max":797,"o":"m 797 880 l 479 880 l 479 0 l 350 0 l 350 880 l 33 880 l 33 987 l 797 987 l 797 880 z "},"U":{"ha":901,"x_min":95,"x_max":810,"o":"m 810 987 l 810 316 q 722 87 809 176 q 486 -12 635 -1 l 451 -14 q 193 74 289 -14 q 95 315 96 161 l 95 987 l 224 987 l 224 319 q 283 152 224 212 q 451 93 342 93 q 620 152 562 93 q 679 318 679 211 l 679 987 l 810 987 z "},"V":{"ha":884,"x_min":19,"x_max":866,"o":"m 441 173 l 724 987 l 866 987 l 500 0 l 385 0 l 19 987 l 161 987 l 441 173 z "},"W":{"ha":1232,"x_min":41,"x_max":1202,"o":"m 328 311 l 347 181 l 374 298 l 570 987 l 680 987 l 870 298 l 897 179 l 918 312 l 1072 987 l 1202 987 l 963 0 l 844 0 l 641 720 l 625 795 l 610 720 l 399 0 l 280 0 l 41 987 l 172 987 l 328 311 z "},"X":{"ha":871,"x_min":39,"x_max":834,"o":"m 437 609 l 673 987 l 827 987 l 515 498 l 834 0 l 680 0 l 437 385 l 193 0 l 39 0 l 359 498 l 46 987 l 199 987 l 437 609 z "},"Y":{"ha":834,"x_min":10,"x_max":821,"o":"m 416 492 l 673 987 l 821 987 l 481 368 l 481 0 l 351 0 l 351 368 l 10 987 l 159 987 l 416 492 z "},"Z":{"ha":831,"x_min":58,"x_max":777,"o":"m 212 106 l 777 106 l 777 0 l 58 0 l 58 98 l 600 880 l 67 880 l 67 987 l 755 987 l 755 892 l 212 106 z "},"[":{"ha":368,"x_min":99,"x_max":355,"o":"m 355 1025 l 225 1025 l 225 -109 l 355 -109 l 355 -212 l 99 -212 l 99 1128 l 355 1128 l 355 1025 z "},"\\":{"ha":570,"x_min":27,"x_max":559,"o":"m 27 987 l 146 987 l 559 -85 l 439 -85 l 27 987 z "},"]":{"ha":368,"x_min":6,"x_max":262,"o":"m 6 1128 l 262 1128 l 262 -212 l 6 -212 l 6 -109 l 137 -109 l 137 1025 l 6 1025 l 6 1128 z "},"^":{"ha":581,"x_min":43,"x_max":534,"o":"m 289 821 l 160 494 l 43 494 l 246 987 l 332 987 l 534 494 l 418 494 l 289 821 z "},"_":{"ha":627,"x_min":3,"x_max":624,"o":"m 624 -102 l 3 -102 l 3 0 l 624 0 l 624 -102 z "},"`":{"ha":429,"x_min":39,"x_max":321,"o":"m 321 842 l 214 842 l 39 1042 l 190 1042 l 321 842 z "},"a":{"ha":755,"x_min":74,"x_max":680,"o":"m 548 0 q 530 77 537 22 q 321 -14 443 -14 q 143 48 213 -14 q 74 203 74 109 q 161 381 74 318 q 406 445 248 445 l 528 445 l 528 503 q 489 607 528 568 q 373 646 450 646 q 260 612 306 646 q 215 530 215 578 l 89 530 q 128 636 89 585 q 234 718 167 688 q 380 747 300 747 q 579 684 507 747 q 654 509 651 621 l 654 172 q 680 11 654 71 l 680 0 l 548 0 m 340 96 q 452 126 399 96 q 528 205 505 157 l 528 356 l 430 356 q 199 221 199 356 q 239 129 199 162 q 340 96 278 96 z "},"b":{"ha":779,"x_min":95,"x_max":716,"o":"m 716 359 q 639 89 716 191 q 431 -14 562 -14 q 216 85 292 -14 l 210 0 l 95 0 l 95 1042 l 220 1042 l 220 653 q 430 747 296 747 q 640 646 564 747 q 716 370 716 545 l 716 359 m 591 373 q 541 571 591 501 q 399 641 492 641 q 220 526 275 641 l 220 208 q 400 93 278 93 q 541 163 490 93 q 591 373 591 233 z "},"c":{"ha":727,"x_min":62,"x_max":681,"o":"m 389 89 q 507 130 456 89 q 562 231 557 170 l 681 231 q 637 111 677 168 q 531 20 597 54 q 389 -14 464 -14 q 151 86 239 -14 q 62 360 62 186 l 62 381 q 102 572 62 488 q 215 701 141 655 q 389 747 288 747 q 594 673 512 747 q 681 481 675 600 l 562 481 q 508 598 557 553 q 389 644 460 644 q 240 575 293 644 q 188 376 188 507 l 188 353 q 240 157 188 226 q 389 89 292 89 z "},"d":{"ha":783,"x_min":64,"x_max":684,"o":"m 64 373 q 144 645 64 542 q 354 747 224 747 q 558 659 483 747 l 558 1042 l 684 1042 l 684 0 l 568 0 l 562 79 q 353 -14 487 -14 q 145 91 225 -14 q 64 363 64 195 l 64 373 m 190 359 q 241 163 190 234 q 384 93 293 93 q 558 200 503 93 l 558 537 q 385 641 502 641 q 241 570 293 641 q 190 359 190 498 z "},"e":{"ha":736,"x_min":63,"x_max":686,"o":"m 399 -14 q 157 84 250 -14 q 63 347 63 182 l 63 370 q 105 565 63 479 q 221 699 146 650 q 384 747 296 747 q 606 653 527 747 q 686 383 686 559 l 686 331 l 189 331 q 252 156 191 222 q 406 89 313 89 q 519 116 473 89 q 600 188 565 143 l 676 128 q 399 -14 584 -14 m 384 644 q 256 589 308 644 q 193 434 205 534 l 560 434 l 560 444 q 509 592 555 539 q 384 644 463 644 z "},"f":{"ha":482,"x_min":41,"x_max":484,"o":"m 157 0 l 157 637 l 41 637 l 41 734 l 157 734 l 157 809 q 220 991 157 927 q 398 1056 283 1056 q 484 1044 441 1056 l 477 943 q 410 949 446 949 q 315 913 349 949 q 282 811 282 878 l 282 734 l 439 734 l 439 637 l 282 637 l 282 0 l 157 0 z "},"g":{"ha":779,"x_min":65,"x_max":685,"o":"m 65 373 q 144 646 65 545 q 355 747 224 747 q 564 652 489 747 l 570 734 l 685 734 l 685 18 q 601 -207 685 -125 q 374 -289 516 -289 q 218 -255 294 -289 q 102 -162 142 -221 l 168 -87 q 365 -186 248 -186 q 508 -135 456 -186 q 559 10 559 -83 l 559 73 q 353 -14 484 -14 q 145 90 224 -14 q 65 373 65 194 m 191 359 q 242 164 191 235 q 385 93 293 93 q 559 201 503 93 l 559 536 q 386 641 501 641 q 243 570 294 641 q 191 359 191 498 z "},"h":{"ha":765,"x_min":95,"x_max":672,"o":"m 220 645 q 437 747 304 747 q 672 485 670 747 l 672 0 l 547 0 l 547 486 q 510 603 546 565 q 399 641 475 641 q 292 608 338 641 q 220 523 246 576 l 220 0 l 95 0 l 95 1042 l 220 1042 l 220 645 z "},"i":{"ha":337,"x_min":96,"x_max":244,"o":"m 231 0 l 106 0 l 106 734 l 231 734 l 231 0 m 96 928 q 114 980 96 959 q 170 1001 133 1001 q 225 980 206 1001 q 244 928 244 959 q 225 878 244 898 q 170 857 206 857 q 114 878 133 857 q 96 928 96 898 z "},"j":{"ha":332,"x_min":-44,"x_max":234,"o":"m 224 734 l 224 -85 q 33 -296 224 -296 q -44 -284 -9 -296 l -44 -184 q 13 -189 -22 -189 q 77 -166 55 -189 q 99 -87 99 -144 l 99 734 l 224 734 m 86 928 q 105 980 86 958 q 159 1001 123 1001 q 215 980 196 1001 q 234 928 234 959 q 215 878 234 898 q 159 857 196 857 q 104 878 123 857 q 86 928 86 898 z "},"k":{"ha":704,"x_min":96,"x_max":703,"o":"m 300 340 l 221 258 l 221 0 l 96 0 l 96 1042 l 221 1042 l 221 412 l 288 492 l 517 734 l 669 734 l 384 427 l 703 0 l 555 0 l 300 340 z "},"l":{"ha":337,"x_min":106,"x_max":231,"o":"m 231 0 l 106 0 l 106 1042 l 231 1042 l 231 0 z "},"m":{"ha":1217,"x_min":94,"x_max":1123,"o":"m 213 734 l 216 652 q 434 747 297 747 q 644 629 588 747 q 739 715 680 682 q 878 747 798 747 q 1123 492 1119 747 l 1123 0 l 998 0 l 998 484 q 962 602 998 563 q 841 641 926 641 q 725 599 771 641 q 671 487 679 557 l 671 0 l 545 0 l 545 481 q 389 641 545 641 q 220 536 265 641 l 220 0 l 94 0 l 94 734 l 213 734 z "},"n":{"ha":766,"x_min":95,"x_max":672,"o":"m 214 734 l 218 642 q 437 747 302 747 q 672 485 670 747 l 672 0 l 547 0 l 547 486 q 510 603 546 565 q 399 641 475 641 q 292 608 338 641 q 220 523 246 576 l 220 0 l 95 0 l 95 734 l 214 734 z "},"o":{"ha":792,"x_min":62,"x_max":730,"o":"m 62 374 q 104 568 62 481 q 222 701 146 654 q 395 747 298 747 q 637 644 545 747 q 730 368 730 540 l 730 359 q 689 166 730 252 q 571 34 648 81 q 396 -14 495 -14 q 154 90 247 -14 q 62 365 62 194 l 62 374 m 188 359 q 244 163 188 237 q 396 89 301 89 q 548 164 492 89 q 604 374 604 239 q 547 569 604 494 q 395 644 490 644 q 245 570 302 644 q 188 359 188 496 z "},"p":{"ha":779,"x_min":95,"x_max":715,"o":"m 715 359 q 638 89 715 191 q 431 -14 562 -14 q 220 71 297 -14 l 220 -282 l 95 -282 l 95 734 l 210 734 l 216 652 q 429 747 292 747 q 638 648 561 747 q 715 370 715 548 l 715 359 m 589 373 q 536 569 589 497 q 391 641 484 641 q 220 540 277 641 l 220 189 q 393 89 277 89 q 536 160 483 89 q 589 373 589 232 z "},"q":{"ha":789,"x_min":64,"x_max":683,"o":"m 64 373 q 144 647 64 546 q 357 747 223 747 q 562 660 487 747 l 568 734 l 683 734 l 683 -282 l 557 -282 l 557 68 q 355 -14 481 -14 q 143 90 222 -14 q 64 364 64 193 l 64 373 m 190 359 q 243 161 190 233 q 387 89 296 89 q 557 188 498 89 l 557 547 q 388 644 498 644 q 243 572 297 644 q 190 359 190 500 z "},"r":{"ha":470,"x_min":95,"x_max":450,"o":"m 450 621 q 388 626 421 626 q 220 521 264 626 l 220 0 l 95 0 l 95 734 l 217 734 l 219 649 q 394 747 281 747 q 450 738 431 747 l 450 621 z "},"s":{"ha":716,"x_min":64,"x_max":648,"o":"m 522 195 q 484 274 522 245 q 350 322 446 302 q 199 371 255 342 q 116 439 143 399 q 90 532 90 478 q 166 685 90 623 q 361 747 242 747 q 563 683 486 747 q 641 518 641 618 l 515 518 q 471 607 515 570 q 361 644 427 644 q 254 614 292 644 q 215 536 215 585 q 251 468 215 491 q 381 424 287 445 q 533 374 475 403 q 620 303 591 345 q 648 203 648 262 q 569 46 648 105 q 365 -14 490 -14 q 209 18 277 -14 q 103 105 141 49 q 64 226 64 161 l 190 226 q 240 126 193 163 q 365 89 288 89 q 479 118 436 89 q 522 195 522 146 z "},"t":{"ha":454,"x_min":6,"x_max":406,"o":"m 265 911 l 265 734 l 402 734 l 402 637 l 265 637 l 265 182 q 283 116 265 138 q 346 94 302 94 q 406 102 368 94 l 406 0 q 309 -14 356 -14 q 182 37 225 -14 q 140 182 140 88 l 140 637 l 6 637 l 6 734 l 140 734 l 140 911 l 265 911 z "},"u":{"ha":766,"x_min":92,"x_max":670,"o":"m 548 73 q 333 -14 475 -14 q 154 55 216 -14 q 92 256 93 123 l 92 734 l 218 734 l 218 260 q 353 93 218 93 q 545 200 497 93 l 545 734 l 670 734 l 670 0 l 551 0 l 548 73 z "},"v":{"ha":673,"x_min":22,"x_max":647,"o":"m 337 170 l 519 734 l 647 734 l 384 0 l 288 0 l 22 734 l 151 734 l 337 170 z "},"w":{"ha":1044,"x_min":29,"x_max":1011,"o":"m 745 173 l 886 734 l 1011 734 l 798 0 l 696 0 l 517 556 l 344 0 l 242 0 l 29 734 l 154 734 l 298 184 l 469 734 l 570 734 l 745 173 z "},"x":{"ha":688,"x_min":28,"x_max":658,"o":"m 341 466 l 504 734 l 650 734 l 410 371 l 658 0 l 513 0 l 343 275 l 174 0 l 28 0 l 275 371 l 35 734 l 180 734 l 341 466 z "},"y":{"ha":657,"x_min":15,"x_max":640,"o":"m 335 184 l 506 734 l 640 734 l 345 -113 q 127 -296 277 -296 l 104 -294 l 57 -286 l 57 -184 l 91 -186 q 190 -161 155 -186 q 249 -66 226 -135 l 277 8 l 15 734 l 152 734 l 335 184 z "},"z":{"ha":688,"x_min":60,"x_max":642,"o":"m 213 102 l 642 102 l 642 0 l 60 0 l 60 92 l 465 630 l 66 630 l 66 734 l 622 734 l 622 645 l 213 102 z "},"{":{"ha":470,"x_min":43,"x_max":454,"o":"m 429 -248 q 248 -137 309 -214 q 187 68 187 -59 l 187 203 q 43 368 187 368 l 43 467 q 187 631 187 467 l 187 772 q 248 972 189 896 q 429 1083 307 1048 l 454 1005 q 313 768 313 960 l 313 631 q 199 417 313 477 q 313 201 313 356 l 313 61 q 454 -170 315 -125 l 429 -248 z "},"|":{"ha":338,"x_min":119,"x_max":220,"o":"m 220 -183 l 119 -183 l 119 987 l 220 987 l 220 -183 z "},"}":{"ha":470,"x_min":13,"x_max":425,"o":"m 13 -170 q 155 54 151 -126 l 155 203 q 278 417 155 360 q 155 631 155 473 l 155 768 q 14 1005 155 960 l 39 1083 q 220 973 159 1049 q 281 771 280 897 l 281 629 q 425 467 281 467 l 425 368 q 281 203 281 368 l 281 66 q 219 -138 281 -61 q 39 -248 158 -214 l 13 -170 z "},"~":{"ha":945,"x_min":89,"x_max":857,"o":"m 857 527 q 793 346 857 420 q 637 273 730 273 q 545 291 588 273 q 444 359 501 309 q 361 421 387 409 q 308 433 336 433 q 227 397 255 433 q 198 297 198 361 l 89 296 q 151 474 89 404 q 308 544 214 544 q 407 522 359 544 q 514 446 454 500 q 617 385 574 391 l 637 383 q 721 422 688 383 q 753 526 753 462 l 857 527 z "},"¡":{"ha":338,"x_min":94,"x_max":243,"o":"m 115 464 l 229 464 l 238 -244 l 106 -244 l 115 464 m 243 675 q 224 624 243 645 q 169 603 205 603 q 113 624 132 603 q 94 675 94 645 q 113 726 94 706 q 169 747 132 747 q 224 726 205 747 q 243 675 243 706 z "},"¢":{"ha":760,"x_min":71,"x_max":690,"o":"m 397 89 q 515 130 465 89 q 571 231 566 171 l 690 231 q 618 78 686 146 q 454 -8 551 9 l 454 -166 l 328 -166 l 328 -7 q 139 112 207 16 q 71 357 71 208 l 71 381 q 140 622 71 525 q 328 741 208 718 l 328 894 l 454 894 l 454 743 q 621 655 555 727 q 690 481 686 583 l 571 481 q 517 598 566 553 q 397 644 469 644 q 249 575 302 644 q 197 376 197 507 l 197 353 q 249 157 197 226 q 397 89 302 89 z "},"£":{"ha":807,"x_min":62,"x_max":765,"o":"m 304 422 l 310 273 q 268 106 310 168 l 765 106 l 764 0 l 64 0 l 64 106 l 117 106 q 161 157 144 113 q 179 267 178 201 l 179 272 l 174 422 l 62 422 l 62 528 l 170 528 l 164 705 q 247 920 164 840 q 466 1001 330 1001 q 670 929 595 1001 q 745 737 745 857 l 616 737 q 573 852 616 810 q 454 894 530 894 q 339 842 383 894 q 295 705 295 789 l 301 528 l 517 528 l 517 422 l 304 422 z "},"¤":{"ha":990,"x_min":71,"x_max":930,"o":"m 748 76 q 498 -14 640 -14 q 250 75 358 -14 l 159 -18 l 71 74 l 165 170 q 95 412 95 275 q 171 663 95 552 l 71 765 l 159 857 l 259 755 q 498 837 366 837 q 739 755 631 837 l 840 858 l 930 765 l 827 661 q 902 412 902 550 q 833 172 902 279 l 930 74 l 840 -18 l 748 76 m 211 412 q 250 257 211 329 q 355 144 289 185 q 498 102 422 102 q 642 144 575 102 q 746 257 708 186 q 785 412 785 329 q 746 567 785 495 q 641 679 708 638 q 498 720 574 720 q 355 680 422 720 q 250 567 289 639 q 211 412 211 495 z "},"¥":{"ha":729,"x_min":10,"x_max":719,"o":"m 366 538 l 574 987 l 719 987 l 470 499 l 647 499 l 647 414 l 427 414 l 427 302 l 647 302 l 647 218 l 427 218 l 427 0 l 296 0 l 296 218 l 81 218 l 81 302 l 296 302 l 296 414 l 81 414 l 81 499 l 259 499 l 10 987 l 157 987 l 366 538 z "},"¦":{"ha":333,"x_min":100,"x_max":226,"o":"m 100 -183 l 100 353 l 226 353 l 226 -183 l 100 -183 m 226 473 l 100 473 l 100 987 l 226 987 l 226 473 z "},"§":{"ha":852,"x_min":61,"x_max":777,"o":"m 777 292 q 650 106 777 164 q 722 27 697 73 q 746 -87 746 -19 q 658 -268 746 -201 q 415 -336 570 -336 q 271 -317 339 -336 q 155 -259 203 -298 q 61 -43 61 -182 l 187 -42 q 248 -181 187 -130 q 415 -233 309 -233 q 564 -193 507 -233 q 620 -88 620 -153 q 572 8 620 -28 q 382 85 524 43 q 193 156 258 118 q 97 246 128 193 q 65 374 65 298 q 189 559 65 500 q 120 639 144 593 q 96 753 96 685 q 186 933 96 865 q 427 1001 277 1001 q 673 924 585 1001 q 761 709 761 848 l 635 709 q 578 846 635 793 q 427 899 522 899 q 276 860 331 899 q 222 754 222 821 q 241 680 222 707 q 306 631 260 653 q 449 582 351 609 q 603 530 546 555 q 699 473 661 506 q 757 396 737 440 q 777 292 777 353 m 408 469 q 296 503 347 485 q 217 456 242 490 q 191 375 191 422 q 210 300 191 328 q 273 251 228 273 q 414 201 318 229 q 541 161 511 173 q 622 209 593 175 q 650 290 650 243 q 603 387 650 350 q 408 469 556 424 z "},"¨":{"ha":581,"x_min":68,"x_max":509,"o":"m 68 930 q 87 981 68 960 q 142 1002 106 1002 q 198 981 179 1002 q 217 930 217 960 q 198 879 217 899 q 142 859 179 859 q 87 879 106 859 q 68 930 68 899 m 360 928 q 379 980 360 959 q 434 1001 397 1001 q 490 980 471 1001 q 509 928 509 959 q 490 878 509 898 q 434 857 471 857 q 379 878 397 857 q 360 928 360 898 z "},"©":{"ha":1091,"x_min":62,"x_max":1024,"o":"m 759 405 q 700 247 759 301 q 534 192 642 192 q 364 263 428 192 q 300 458 300 335 l 300 533 q 365 724 300 652 q 534 795 429 795 q 701 740 643 795 q 760 583 760 684 l 661 583 q 628 679 661 650 q 534 708 596 708 q 434 661 471 708 q 397 531 397 614 l 397 454 q 434 326 397 373 q 534 279 471 279 q 628 308 597 279 q 660 405 660 336 l 759 405 m 140 494 q 194 278 140 378 q 341 123 248 179 q 543 66 435 66 q 745 123 652 66 q 892 278 838 179 q 946 494 946 378 q 892 708 946 610 q 746 863 839 806 q 543 920 652 920 q 341 864 435 920 q 194 709 248 808 q 140 494 140 610 m 62 494 q 125 749 62 631 q 301 933 189 866 q 543 1001 413 1001 q 785 933 673 1001 q 961 749 897 866 q 1024 494 1024 631 q 963 244 1024 361 q 790 57 902 127 q 543 -14 677 -14 q 297 56 410 -14 q 123 243 185 126 q 62 494 62 359 z "},"ª":{"ha":621,"x_min":100,"x_max":531,"o":"m 419 478 q 407 527 411 501 q 269 469 355 469 q 144 511 188 469 q 100 623 100 553 q 156 739 100 698 q 330 779 213 779 l 403 779 l 403 814 q 319 906 403 906 q 245 888 272 906 q 218 835 218 869 l 109 843 q 168 957 109 913 q 319 1001 226 1001 q 461 952 409 1001 q 513 813 513 904 l 513 599 q 531 478 513 533 l 419 478 m 295 562 q 354 575 324 562 q 403 607 384 589 l 403 703 l 327 703 q 240 681 271 703 q 210 625 210 660 q 295 562 210 562 z "},"«":{"ha":652,"x_min":69,"x_max":589,"o":"m 190 372 l 365 102 l 269 102 l 69 366 l 69 378 l 269 642 l 365 642 l 190 372 m 414 372 l 589 102 l 493 102 l 293 366 l 293 378 l 493 642 l 589 642 l 414 372 z "},"¬":{"ha":769,"x_min":86,"x_max":650,"o":"m 650 254 l 524 254 l 524 433 l 86 433 l 86 543 l 650 543 l 650 254 z "},"®":{"ha":1092,"x_min":61,"x_max":1023,"o":"m 61 494 q 124 749 61 631 q 300 933 188 866 q 543 1001 412 1001 q 785 933 673 1001 q 960 749 897 866 q 1023 494 1023 631 q 962 244 1023 361 q 789 57 901 127 q 543 -14 677 -14 q 297 56 409 -14 q 123 243 184 126 q 61 494 61 359 m 139 494 q 193 278 139 378 q 341 123 248 179 q 543 66 434 66 q 745 124 652 66 q 892 279 839 181 q 945 494 945 378 q 892 708 945 610 q 745 863 839 806 q 543 920 652 920 q 340 864 434 920 q 193 709 247 808 q 139 494 139 610 m 443 444 l 443 214 l 347 214 l 347 790 l 534 790 q 695 746 638 790 q 753 616 753 701 q 666 489 753 533 q 749 351 749 455 l 749 309 q 761 225 749 251 l 761 214 l 663 214 q 653 301 653 239 q 651 376 653 364 q 562 444 640 441 l 443 444 m 443 530 l 549 530 q 627 554 597 532 q 657 613 657 576 q 631 683 657 663 q 536 704 604 704 l 443 704 l 443 530 z "},"¯":{"ha":636,"x_min":96,"x_max":552,"o":"m 552 883 l 96 883 l 96 980 l 552 980 l 552 883 z "},"°":{"ha":519,"x_min":88,"x_max":431,"o":"m 88 825 q 139 948 88 895 q 261 1001 189 1001 q 381 948 332 1001 q 431 825 431 896 q 381 702 431 753 q 261 651 332 651 q 139 702 190 651 q 88 825 88 753 m 261 738 q 323 762 298 738 q 348 825 348 786 q 323 889 348 864 q 261 915 298 915 q 198 888 224 915 q 173 825 173 861 q 198 763 173 788 q 261 738 224 738 z "},"±":{"ha":742,"x_min":66,"x_max":687,"o":"m 440 579 l 687 579 l 687 477 l 440 477 l 440 196 l 327 19
gitextract_gbbdtq91/ ├── .github/ │ └── workflows/ │ └── deploy.yml ├── .gitignore ├── .npmignore ├── LICENSE ├── dist/ │ ├── three-viewport-gizmo.d.ts │ ├── three-viewport-gizmo.js │ └── three-viewport-gizmo.umd.cjs ├── docs/ │ ├── .vitepress/ │ │ ├── components/ │ │ │ └── IframeContainer.vue │ │ ├── config.ts │ │ └── theme/ │ │ ├── icons/ │ │ │ ├── index.ts │ │ │ └── three.icon.ts │ │ ├── index.ts │ │ └── style.css │ ├── api.md │ ├── custom.md │ ├── examples/ │ │ ├── multiple-elements.md │ │ ├── orbit-controls-events.md │ │ ├── orbit-controls.md │ │ ├── post-processing.md │ │ ├── resizable-grid.md │ │ ├── responsive.md │ │ ├── standalone.md │ │ ├── x-up.md │ │ ├── yomotsu-camera-controls.md │ │ └── z-up.md │ ├── index.md │ ├── public/ │ │ ├── assets/ │ │ │ ├── HDR/ │ │ │ │ └── studio_small_04_1k.hdr │ │ │ ├── fonts/ │ │ │ │ └── Roboto_Regular.json │ │ │ └── models/ │ │ │ └── three/ │ │ │ ├── license.txt │ │ │ └── scene.gltf │ │ └── samples/ │ │ ├── common/ │ │ │ └── threeModel.js │ │ ├── config.html │ │ ├── multiple-elements.html │ │ ├── orbit-controls-events.html │ │ ├── orbit-controls.html │ │ ├── post-processing.html │ │ ├── resizable-grid.html │ │ ├── responsive.html │ │ ├── standalone.html │ │ ├── x-up.html │ │ ├── yomotsu-camera-controls.html │ │ └── z-up.html │ └── quickstart.md ├── lib/ │ ├── ViewportGizmo.ts │ ├── types.ts │ └── utils/ │ ├── axesCorners.ts │ ├── axesEdges.ts │ ├── axesFaces.ts │ ├── axesLines.ts │ ├── axesMap.ts │ ├── axesObjects.ts │ ├── axisHover.ts │ ├── constants.ts │ ├── deepClone.ts │ ├── getDomElement.ts │ ├── gizmoBackground.ts │ ├── gizmoDomElement.ts │ ├── intersectedObjects.ts │ ├── isClick.ts │ ├── optionsFallback.ts │ ├── roundedRectangleGeometry.ts │ ├── updateAxis.ts │ └── updateBackground.ts ├── live/ │ ├── index.html │ ├── public/ │ │ ├── HDR/ │ │ │ └── studio_small_04_1k.hdr │ │ ├── fonts/ │ │ │ └── Roboto_Regular.json │ │ └── models/ │ │ ├── cad/ │ │ │ └── support_plate.gltf │ │ └── three/ │ │ ├── license.txt │ │ └── scene.gltf │ ├── src/ │ │ ├── WebGPU.ts │ │ ├── composer.ts │ │ ├── configuration.ts │ │ ├── constant.ts │ │ ├── controls.ts │ │ ├── controlsListeners.ts │ │ ├── grid.ts │ │ ├── gui.ts │ │ ├── initScene.ts │ │ ├── main.ts │ │ ├── multiViewport.ts │ │ ├── responsive.ts │ │ ├── static.ts │ │ ├── utils/ │ │ │ ├── ResizableGrid.ts │ │ │ ├── get3DText.ts │ │ │ ├── getSceneLights.ts │ │ │ ├── loadEnvMap.ts │ │ │ └── loadModel.ts │ │ ├── vite-env.d.ts │ │ ├── yomotsuCameraControls.ts │ │ ├── z_up.ts │ │ └── z_up_stand_alone.ts │ └── style.css ├── package.json ├── readme.md ├── tsconfig.json ├── vite.config.ts └── vite.live.config.ts
SYMBOL INDEX (245 symbols across 23 files)
FILE: dist/three-viewport-gizmo.d.ts
type GizmoAxisOptions (line 16) | type GizmoAxisOptions = {
type GizmoOptions (line 62) | type GizmoOptions = {
type GizmoOptionsFallback (line 266) | type GizmoOptionsFallback = DeepRequired<GizmoOptions> & {
class ViewportGizmo (line 281) | class ViewportGizmo extends Object3D<ViewportGizmoEventMap> {
type ViewportGizmoEventMap (line 545) | interface ViewportGizmoEventMap extends Object3DEventMap {
FILE: dist/three-viewport-gizmo.js
function wt (line 36) | function wt(s, e, t) {
function Pt (line 44) | function Pt({ isSphere: s }, e, t) {
function St (line 232) | function St(s, ...e) {
function y (line 297) | function y(m, S, H, x, E, k, O, B, D) {
function C (line 313) | function C(m, S, H) {
function g (line 329) | function g(m, S, H, x, E) {
function Ut (line 339) | function Ut(s, e, t = 2, n = 2) {
function He (line 494) | function He(s, e = !1) {
function Ht (line 560) | function Ht(s) {
class ie (line 625) | class ie extends me {
method constructor (line 626) | constructor() {
method applyMatrix4 (line 631) | applyMatrix4(e) {
method setPositions (line 635) | setPositions(e) {
method setColors (line 641) | setColors(e) {
method fromWireframeGeometry (line 647) | fromWireframeGeometry(e) {
method fromEdgesGeometry (line 650) | fromEdgesGeometry(e) {
method fromMesh (line 653) | fromMesh(e) {
method fromLineSegments (line 656) | fromLineSegments(e) {
method computeBoundingBox (line 660) | computeBoundingBox() {
method computeBoundingSphere (line 665) | computeBoundingSphere() {
method toJSON (line 677) | toJSON() {
method applyMatrix (line 679) | applyMatrix(e) {
class zt (line 1078) | class zt extends ye {
method constructor (line 1079) | constructor(e) {
method color (line 1089) | get color() {
method color (line 1092) | set color(e) {
method worldUnits (line 1095) | get worldUnits() {
method worldUnits (line 1098) | set worldUnits(e) {
method linewidth (line 1101) | get linewidth() {
method linewidth (line 1104) | set linewidth(e) {
method dashed (line 1107) | get dashed() {
method dashed (line 1110) | set dashed(e) {
method dashScale (line 1113) | get dashScale() {
method dashScale (line 1116) | set dashScale(e) {
method dashSize (line 1119) | get dashSize() {
method dashSize (line 1122) | set dashSize(e) {
method dashOffset (line 1125) | get dashOffset() {
method dashOffset (line 1128) | set dashOffset(e) {
method gapSize (line 1131) | get gapSize() {
method gapSize (line 1134) | set gapSize(e) {
method opacity (line 1137) | get opacity() {
method opacity (line 1140) | set opacity(e) {
method resolution (line 1143) | get resolution() {
method resolution (line 1146) | set resolution(e) {
method alphaToCoverage (line 1149) | get alphaToCoverage() {
method alphaToCoverage (line 1152) | set alphaToCoverage(e) {
function Nt (line 1158) | function Nt(s, e, t) {
function je (line 1161) | function je(s, e) {
function We (line 1178) | function We(s, e, t) {
class qe (line 1211) | class qe extends X {
method constructor (line 1212) | constructor(e = new ie(), t = new zt({ color: Math.random() * 16777215...
method computeLineDistances (line 1216) | computeLineDistances() {
method raycast (line 1223) | raycast(e, t) {
method onBeforeRender (line 1249) | onBeforeRender(e) {
class se (line 1254) | class se extends ie {
method constructor (line 1255) | constructor() {
method setPositions (line 1258) | setPositions(e) {
method setColors (line 1264) | setColors(e) {
method setFromPoints (line 1270) | setFromPoints(e) {
method fromLine (line 1276) | fromLine(e) {
class Ne (line 1281) | class Ne extends qe {
method constructor (line 1282) | constructor(e = new se(), t = new zt({ color: Math.random() * 16777215...
class Ye (line 1318) | class Ye extends I {
method constructor (line 1411) | constructor(t, n, i = {}) {
method placement (line 1459) | get placement() {
method placement (line 1467) | set placement(t) {
method set (line 1478) | set(t = {}) {
method render (line 1492) | render() {
method domUpdate (line 1503) | domUpdate() {
method cameraUpdate (line 1520) | cameraUpdate() {
method update (line 1530) | update(t = !0) {
method attachControls (line 1539) | attachControls(t) {
method detachControls (line 1547) | detachControls() {
method dispose (line 1555) | dispose() {
method _updateOrientation (line 1570) | _updateOrientation(t = !0) {
method _animate (line 1578) | _animate() {
method _setOrientation (line 1600) | _setOrientation(t) {
method _onPointerDown (line 1617) | _onPointerDown(t) {
method coordinateConversion (line 1654) | coordinateConversion(t, n = !1) {
method _onPointerMove (line 1664) | _onPointerMove(t) {
method _onPointerLeave (line 1672) | _onPointerLeave() {
method _handleClick (line 1681) | _handleClick(t) {
method _handleHover (line 1696) | _handleHover(t) {
FILE: dist/three-viewport-gizmo.umd.cjs
function lt (line 1) | function lt(s,e,t){return Math.max(e,Math.min(t,s))}
function _t (line 1) | function _t({isSphere:s},e,t){s&&(yt.set(0,0,1).applyQuaternion(t.quater...
function dt (line 1) | function dt(s,...e){if(s instanceof HTMLElement||typeof s!="object"||s==...
function _ (line 1) | function _(g,x,V,E,A,q,C,I,G){if(A=A*(E/2),I!=null&&I!==""&&(F(),u.fillS...
function O (line 1) | function O(g,x,V){const A=[...g].sort((ct,ue)=>{var Rt,It;return(((Rt=ct...
function y (line 1) | function y(g,x,V,E,A){g.font=h,g.textAlign="center",g.textBaseline="midd...
function ft (line 1) | function ft(s,e,t=2,n=2){const i=t/2-s,r=n/2-s,c=s/t,l=(t-s)/t,d=s/n,f=(...
function Kt (line 1) | function Kt(s,e=!1){const t=s[0].index!==null,n=new Set(Object.keys(s[0]...
function At (line 1) | function At(s){let e,t,n,i=-1,r=0;for(let f=0;f<s.length;++f){const a=s[...
class Ut (line 1) | class Ut extends o.InstancedBufferGeometry{constructor(){super(),this.is...
method constructor (line 1) | constructor(){super(),this.isLineSegmentsGeometry=!0,this.type="LineSe...
method applyMatrix4 (line 1) | applyMatrix4(e){const t=this.attributes.instanceStart,n=this.attribute...
method setPositions (line 1) | setPositions(e){let t;e instanceof Float32Array?t=e:Array.isArray(e)&&...
method setColors (line 1) | setColors(e){let t;e instanceof Float32Array?t=e:Array.isArray(e)&&(t=...
method fromWireframeGeometry (line 1) | fromWireframeGeometry(e){return this.setPositions(e.attributes.positio...
method fromEdgesGeometry (line 1) | fromEdgesGeometry(e){return this.setPositions(e.attributes.position.ar...
method fromMesh (line 1) | fromMesh(e){return this.fromWireframeGeometry(new o.WireframeGeometry(...
method fromLineSegments (line 1) | fromLineSegments(e){const t=e.geometry;return this.setPositions(t.attr...
method computeBoundingBox (line 1) | computeBoundingBox(){this.boundingBox===null&&(this.boundingBox=new o....
method computeBoundingSphere (line 1) | computeBoundingSphere(){this.boundingSphere===null&&(this.boundingSphe...
method toJSON (line 1) | toJSON(){}
method applyMatrix (line 1) | applyMatrix(e){return console.warn("THREE.LineSegmentsGeometry: applyM...
class pt (line 371) | class pt extends o.ShaderMaterial{constructor(e){super({type:"LineMateri...
method constructor (line 371) | constructor(e){super({type:"LineMaterial",uniforms:o.UniformsUtils.clo...
method color (line 371) | get color(){return this.uniforms.diffuse.value}
method color (line 371) | set color(e){this.uniforms.diffuse.value=e}
method worldUnits (line 371) | get worldUnits(){return"WORLD_UNITS"in this.defines}
method worldUnits (line 371) | set worldUnits(e){e===!0?this.defines.WORLD_UNITS="":delete this.defin...
method linewidth (line 371) | get linewidth(){return this.uniforms.linewidth.value}
method linewidth (line 371) | set linewidth(e){this.uniforms.linewidth&&(this.uniforms.linewidth.val...
method dashed (line 371) | get dashed(){return"USE_DASH"in this.defines}
method dashed (line 371) | set dashed(e){e===!0!==this.dashed&&(this.needsUpdate=!0),e===!0?this....
method dashScale (line 371) | get dashScale(){return this.uniforms.dashScale.value}
method dashScale (line 371) | set dashScale(e){this.uniforms.dashScale.value=e}
method dashSize (line 371) | get dashSize(){return this.uniforms.dashSize.value}
method dashSize (line 371) | set dashSize(e){this.uniforms.dashSize.value=e}
method dashOffset (line 371) | get dashOffset(){return this.uniforms.dashOffset.value}
method dashOffset (line 371) | set dashOffset(e){this.uniforms.dashOffset.value=e}
method gapSize (line 371) | get gapSize(){return this.uniforms.gapSize.value}
method gapSize (line 371) | set gapSize(e){this.uniforms.gapSize.value=e}
method opacity (line 371) | get opacity(){return this.uniforms.opacity.value}
method opacity (line 371) | set opacity(e){this.uniforms&&(this.uniforms.opacity.value=e)}
method resolution (line 371) | get resolution(){return this.uniforms.resolution.value}
method resolution (line 371) | set resolution(e){this.uniforms.resolution.value.copy(e)}
method alphaToCoverage (line 371) | get alphaToCoverage(){return"USE_ALPHA_TO_COVERAGE"in this.defines}
method alphaToCoverage (line 371) | set alphaToCoverage(e){this.defines&&(e===!0!==this.alphaToCoverage&&(...
function Bt (line 371) | function Bt(s,e,t){return P.set(0,0,-e,1).applyMatrix4(s.projectionMatri...
function ee (line 371) | function ee(s,e){const t=s.matrixWorld,n=s.geometry,i=n.attributes.insta...
function ne (line 371) | function ne(s,e,t){const n=e.projectionMatrix,r=s.material.resolution,c=...
class ie (line 371) | class ie extends o.Mesh{constructor(e=new Ut,t=new pt({color:Math.random...
method constructor (line 371) | constructor(e=new Ut,t=new pt({color:Math.random()*16777215})){super(e...
method computeLineDistances (line 371) | computeLineDistances(){const e=this.geometry,t=e.attributes.instanceSt...
method raycast (line 371) | raycast(e,t){const n=this.material.worldUnits,i=e.camera;i===null&&!n&...
method onBeforeRender (line 371) | onBeforeRender(e){const t=this.material.uniforms;t&&t.resolution&&(e.g...
class Ot (line 371) | class Ot extends Ut{constructor(){super(),this.isLineGeometry=!0,this.ty...
method constructor (line 371) | constructor(){super(),this.isLineGeometry=!0,this.type="LineGeometry"}
method setPositions (line 371) | setPositions(e){const t=e.length-3,n=new Float32Array(2*t);for(let i=0...
method setColors (line 371) | setColors(e){const t=e.length-3,n=new Float32Array(2*t);for(let i=0;i<...
method setFromPoints (line 371) | setFromPoints(e){const t=e.length-1,n=new Float32Array(6*t);for(let i=...
method fromLine (line 371) | fromLine(e){const t=e.geometry;return this.setPositions(t.attributes.p...
class oe (line 371) | class oe extends ie{constructor(e=new Ot,t=new pt({color:Math.random()*1...
method constructor (line 371) | constructor(e=new Ot,t=new pt({color:Math.random()*16777215})){super(e...
class ce (line 371) | class ce extends o.Object3D{constructor(t,n,i={}){super();v(this,"enable...
method constructor (line 371) | constructor(t,n,i={}){super();v(this,"enabled",!0);v(this,"camera");v(...
method placement (line 371) | get placement(){return this._placement}
method placement (line 371) | set placement(t){this._placement=j(this._domElement,t),this.domUpdate()}
method set (line 371) | set(t={}){this.dispose(),this.options=t,this._options=Zt(t),this._came...
method render (line 371) | render(){this.animating&&this._animate();const{renderer:t,_viewport:n}...
method domUpdate (line 371) | domUpdate(){this._domRect=this._domElement.getBoundingClientRect();con...
method cameraUpdate (line 371) | cameraUpdate(){return this._updateOrientation(),this}
method update (line 371) | update(t=!0){return t&&this._controls&&this._controls.update(),this.do...
method attachControls (line 371) | attachControls(t){return this.detachControls(),this.target=t.target,th...
method detachControls (line 371) | detachControls(){if(!(!this._controlsListeners||!this._controls))retur...
method dispose (line 371) | dispose(){var t;this.detachControls(),this.children.forEach(n=>{var r,...
method _updateOrientation (line 371) | _updateOrientation(t=!0){t&&(this.quaternion.copy(this.camera.quaterni...
method _animate (line 371) | _animate(){const{position:t,quaternion:n}=this.camera;if(t.set(0,0,1),...
method _setOrientation (line 371) | _setOrientation(t){const n=this.camera,i=this.target;if(W.copy(t).mult...
method _onPointerDown (line 371) | _onPointerDown(t){if(!this.enabled)return;const n=f=>{if(!this._draggi...
method coordinateConversion (line 371) | coordinateConversion(t,n=!1){const{x:i,y:r,z:c}=t,l=o.Object3D.DEFAULT...
method _onPointerMove (line 371) | _onPointerMove(t){!this.enabled||this._dragging||(this._background&&Et...
method _onPointerLeave (line 371) | _onPointerLeave(){!this.enabled||this._dragging||(this._background&&Et...
method _handleClick (line 371) | _handleClick(t){const n=wt(t,this._domRect,this._camera,this._intersec...
method _handleHover (line 371) | _handleHover(t){const n=wt(t,this._domRect,this._camera,this._intersec...
FILE: docs/.vitepress/theme/index.ts
method enhanceApp (line 10) | enhanceApp({ app }) {
FILE: docs/public/samples/common/threeModel.js
constant ROOT (line 22) | const ROOT = "../../three-viewport-gizmo/assets/";
function threeModelAnimation (line 151) | function threeModelAnimation() {
FILE: lib/ViewportGizmo.ts
class ViewportGizmo (line 63) | class ViewportGizmo extends Object3D<ViewportGizmoEventMap> {
method constructor (line 212) | constructor(
method placement (line 226) | get placement(): GizmoOptionsFallback["placement"] {
method placement (line 235) | set placement(placement: GizmoOptionsFallback["placement"]) {
method set (line 248) | set(options: GizmoOptions = {}) {
method render (line 299) | render() {
method domUpdate (line 328) | domUpdate() {
method cameraUpdate (line 357) | cameraUpdate() {
method update (line 369) | update(controls: boolean = true) {
method attachControls (line 380) | attachControls(controls: OrbitControls) {
method detachControls (line 401) | detachControls() {
method dispose (line 421) | dispose() {
method _updateOrientation (line 441) | private _updateOrientation(fromCamera: boolean = true) {
method _animate (line 455) | private _animate() {
method _setOrientation (line 526) | private _setOrientation(position: Vector3) {
method _onPointerDown (line 568) | private _onPointerDown(e: PointerEvent) {
method coordinateConversion (line 651) | private coordinateConversion(target: Vector3, isSpherical = false) {
method _onPointerMove (line 671) | private _onPointerMove(e: PointerEvent) {
method _onPointerLeave (line 684) | private _onPointerLeave() {
method _handleClick (line 699) | private _handleClick(e: PointerEvent) {
method _handleHover (line 725) | private _handleHover(e: PointerEvent) {
FILE: lib/types.ts
type GizmoOptions (line 15) | type GizmoOptions = {
type GizmoAxisOptions (line 281) | type GizmoAxisOptions = {
type ViewportGizmoEventMap (line 340) | interface ViewportGizmoEventMap extends Object3DEventMap {
type GizmoOptionsFallback (line 368) | type GizmoOptionsFallback = DeepRequired<GizmoOptions> & {
type GizmoAxisObject (line 373) | type GizmoAxisObject = Mesh<BufferGeometry, MeshBasicMaterial> | Sprite;
type GizmoViewportArray (line 376) | type GizmoViewportArray = [
FILE: lib/utils/axesMap.ts
function drawAxis (line 104) | function drawAxis(
function getFontStyle (line 160) | function getFontStyle(
function drawText (line 197) | function drawText(
FILE: lib/utils/constants.ts
constant GIZMO_EPSILON (line 1) | const GIZMO_EPSILON = 1e-6;
constant GIZMO_TURN_RATE (line 2) | const GIZMO_TURN_RATE = 2 * Math.PI;
constant GIZMO_MAIN_AXES (line 3) | const GIZMO_MAIN_AXES = ["x", "y", "z"] as const;
constant GIZMO_AXES (line 4) | const GIZMO_AXES = [...GIZMO_MAIN_AXES, "nx", "ny", "nz"] as const;
constant GIZMO_AXES_Z_UP (line 5) | const GIZMO_AXES_Z_UP = ["x", "z", "y", "nx", "nz", "ny"] as const;
constant GIZMO_AXES_X_UP (line 6) | const GIZMO_AXES_X_UP = ["z", "x", "y", "nz", "nx", "ny"] as const;
constant GIZMO_FACE_RIGHT (line 7) | const GIZMO_FACE_RIGHT = "Right";
constant GIZMO_FACE_TOP (line 8) | const GIZMO_FACE_TOP = "Top";
constant GIZMO_FACE_FRONT (line 9) | const GIZMO_FACE_FRONT = "Front";
constant GIZMO_FACE_LEFT (line 10) | const GIZMO_FACE_LEFT = "Left";
constant GIZMO_FACE_BOTTOM (line 11) | const GIZMO_FACE_BOTTOM = "Bottom";
constant GIZMO_FACE_BACK (line 12) | const GIZMO_FACE_BACK = "Back";
constant GIZMO_FACES (line 13) | const GIZMO_FACES = [
constant GIZMO_SPHERE_AXES_DISTANCE (line 21) | const GIZMO_SPHERE_AXES_DISTANCE = 1.3;
FILE: lib/utils/optionsFallback.ts
constant FACE_LABELS_FROM_UP_DIRECTION (line 23) | const FACE_LABELS_FROM_UP_DIRECTION = {
function assignNestedDefaults (line 234) | function assignNestedDefaults<T>(target: T, ...defaultObjects: T[]) {
FILE: lib/utils/roundedRectangleGeometry.ts
function roundedRectangleGeometry (line 13) | function roundedRectangleGeometry(
FILE: lib/utils/updateAxis.ts
function updateAxis (line 16) | function updateAxis(
FILE: live/src/WebGPU.ts
function animation (line 41) | function animation() {
FILE: live/src/composer.ts
function initSceneWithComposer (line 30) | function initSceneWithComposer(
FILE: live/src/configuration.ts
function configuration (line 20) | function configuration() {
function updateViewportGizmo (line 64) | function updateViewportGizmo(
FILE: live/src/grid.ts
function grid (line 26) | function grid() {
FILE: live/src/gui.ts
function copyOptions (line 116) | function copyOptions() {
FILE: live/src/initScene.ts
function initScene (line 29) | function initScene(
FILE: live/src/multiViewport.ts
function multiViewport (line 25) | function multiViewport() {
function createMultiViewportElements (line 108) | function createMultiViewportElements(
FILE: live/src/responsive.ts
function responsive (line 9) | function responsive() {
FILE: live/src/static.ts
function staticRenderScene (line 3) | function staticRenderScene() {
FILE: live/src/utils/ResizableGrid.ts
type GridOptions (line 1) | interface GridOptions {
type DragState (line 7) | interface DragState {
class ResizableGrid (line 16) | class ResizableGrid {
method constructor (line 29) | constructor(
method createHandle (line 39) | private createHandle(isHorizontal: boolean): HTMLElement {
method createCell (line 56) | private createCell(
method createColumn (line 69) | private createColumn(id: string, isMiddle: boolean = false): HTMLEleme...
method startColumnResize (line 76) | private startColumnResize(e: MouseEvent): void {
method startRowResize (line 97) | private startRowResize(e: MouseEvent): void {
method handleResize (line 121) | private handleResize(dragState: DragState, isHorizontal: boolean): void {
method init (line 163) | private init(canvas: HTMLCanvasElement): void {
FILE: live/src/z_up_stand_alone.ts
function animation (line 66) | function animation() {
Condensed preview — 97 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,099K chars).
[
{
"path": ".github/workflows/deploy.yml",
"chars": 2101,
"preview": "# Sample workflow for building and deploying a VitePress site to GitHub Pages\n#\nname: Deploy VitePress site to Pages\n\non"
},
{
"path": ".gitignore",
"chars": 217,
"preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\n\nnode_modules\n*.local\ndocs/.vitepress/dist\ndocs/.vitepress/cache\n\n# Editor directories "
},
{
"path": ".npmignore",
"chars": 96,
"preview": ".vscode/*\n/node_modules\n/demo\n/lib\n/dist/demo\ndocs/.vitepress/dist\ndocs/.vitepress/cache\n*.local"
},
{
"path": "LICENSE",
"chars": 1063,
"preview": "MIT License\n\nCopyright (c) 2024 Fennec\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof "
},
{
"path": "dist/three-viewport-gizmo.d.ts",
"chars": 23628,
"preview": "import { ColorRepresentation } from 'three';\r\nimport { DeepRequired } from 'utility-types';\r\nimport { Object3D } from 't"
},
{
"path": "dist/three-viewport-gizmo.js",
"chars": 60102,
"preview": "var ce = Object.defineProperty;\nvar le = (s, e, t) => e in s ? ce(s, e, { enumerable: !0, configurable: !0, writable: !0"
},
{
"path": "dist/three-viewport-gizmo.umd.cjs",
"chars": 41020,
"preview": "(function(L,o){typeof exports==\"object\"&&typeof module<\"u\"?o(exports,require(\"three\")):typeof define==\"function\"&&define"
},
{
"path": "docs/.vitepress/components/IframeContainer.vue",
"chars": 3414,
"preview": "<template>\n <ClientOnly>\n <div class=\"iframe-wrapper\">\n <div class=\"controls\">\n <a :href=\"fullUrl\" targe"
},
{
"path": "docs/.vitepress/config.ts",
"chars": 2733,
"preview": "import { defineConfig } from \"vitepress\";\n\nconst examples = (type: \"sphere\" | \"cube\" | \"rounded-cube\") => [\n {\n text"
},
{
"path": "docs/.vitepress/theme/icons/index.ts",
"chars": 242,
"preview": "import { OhVueIcon, addIcons } from \"oh-vue-icons\";\nimport { FaExpand, FaExternalLinkAlt, FaCode } from \"oh-vue-icons/ic"
},
{
"path": "docs/.vitepress/theme/icons/three.icon.ts",
"chars": 592,
"preview": "export const threeIcon = {\n name: \"three\",\n width: 226.77,\n height: 226.77,\n raw: `<g transform=\"translate(8.964 4.2"
},
{
"path": "docs/.vitepress/theme/index.ts",
"chars": 423,
"preview": "// .vitepress/theme/index.js\nimport type { Theme } from \"vitepress\";\nimport DefaultTheme from \"vitepress/theme\";\nimport "
},
{
"path": "docs/.vitepress/theme/style.css",
"chars": 952,
"preview": ":root {\n /* Indigo to more red tones */\n --vp-c-indigo-1: #fb8982 !important;\n --vp-c-indigo-2: #e25656 !important;\n "
},
{
"path": "docs/api.md",
"chars": 11293,
"preview": "# ViewportGizmo\n\nA 3D camera orientation controller that provides a visual interface for manipulating the camera's viewi"
},
{
"path": "docs/custom.md",
"chars": 789,
"preview": "---\nlayout: home\n---\n\n<script setup lang=\"ts\">\n\nimport { ref, computed } from \"vue\";\n\nconst type = ref<\"cube\" | \"sphere\""
},
{
"path": "docs/examples/multiple-elements.md",
"chars": 718,
"preview": "<script setup lang=\"ts\">\nconst type = new URLSearchParams(window.location.search).get(\"type\") || \"sphere\";\n</script>\n\n# "
},
{
"path": "docs/examples/orbit-controls-events.md",
"chars": 3509,
"preview": "<script setup lang=\"ts\">\nconst type = new URLSearchParams(window.location.search).get(\"type\") || \"sphere\";\n</script>\n\n# "
},
{
"path": "docs/examples/orbit-controls.md",
"chars": 2424,
"preview": "<script setup lang=\"ts\">\nconst type = new URLSearchParams(window.location.search).get(\"type\") || \"sphere\";\n</script>\n\n# "
},
{
"path": "docs/examples/post-processing.md",
"chars": 632,
"preview": "<script setup lang=\"ts\">\nconst type = new URLSearchParams(window.location.search).get(\"type\") || \"sphere\";\n</script>\n\n# "
},
{
"path": "docs/examples/resizable-grid.md",
"chars": 715,
"preview": "<script setup lang=\"ts\">\nconst type = new URLSearchParams(window.location.search).get(\"type\") || \"sphere\";\n</script>\n\n# "
},
{
"path": "docs/examples/responsive.md",
"chars": 1731,
"preview": "<script setup lang=\"ts\">\nconst type = new URLSearchParams(window.location.search).get(\"type\") || \"sphere\";\n</script>\n\n# "
},
{
"path": "docs/examples/standalone.md",
"chars": 640,
"preview": "<script setup lang=\"ts\">\nconst type = new URLSearchParams(window.location.search).get(\"type\") || \"sphere\";\n</script>\n\n# "
},
{
"path": "docs/examples/x-up.md",
"chars": 821,
"preview": "<script setup lang=\"ts\">\nconst type = new URLSearchParams(window.location.search).get(\"type\") || \"sphere\";\n</script>\n\n# "
},
{
"path": "docs/examples/yomotsu-camera-controls.md",
"chars": 786,
"preview": "<script setup lang=\"ts\">\nconst type = new URLSearchParams(window.location.search).get(\"type\") || \"sphere\";\n</script>\n\n# "
},
{
"path": "docs/examples/z-up.md",
"chars": 757,
"preview": "<script setup lang=\"ts\">\nconst type = new URLSearchParams(window.location.search).get(\"type\") || \"sphere\";\n</script>\n\n# "
},
{
"path": "docs/index.md",
"chars": 2131,
"preview": "---\nlayout: home\n\nhero:\n name: Three Viewport Gizmo\n text: 3D View Camera Helper for Three.js\n tagline: Lightweight, "
},
{
"path": "docs/public/assets/fonts/Roboto_Regular.json",
"chars": 338727,
"preview": "{\"glyphs\":{\"0\":{\"ha\":780,\"x_min\":78,\"x_max\":701,\"o\":\"m 701 421 q 626 94 701 201 q 391 -14 551 -14 q 157 91 233 -14 q 78 "
},
{
"path": "docs/public/assets/models/three/license.txt",
"chars": 648,
"preview": "Model Information:\n* title:\tThree.js\n* source:\thttps://sketchfab.com/3d-models/threejs-60320862bf904b7ab0e032c27daf7c7c\n"
},
{
"path": "docs/public/assets/models/three/scene.gltf",
"chars": 3539,
"preview": "{\n \"accessors\": [\n {\n \"bufferView\": 2,\n \"componentType\": 5126,\n \"count\": 398,\n \"max\": [\n "
},
{
"path": "docs/public/samples/common/threeModel.js",
"chars": 4842,
"preview": "import {\n Group,\n Mesh,\n PMREMGenerator,\n AmbientLight,\n PointLight,\n Clock,\n MeshPhysicalMaterial,\n DoubleSide,"
},
{
"path": "docs/public/samples/config.html",
"chars": 7261,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-w"
},
{
"path": "docs/public/samples/multiple-elements.html",
"chars": 8243,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-w"
},
{
"path": "docs/public/samples/orbit-controls-events.html",
"chars": 4583,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-w"
},
{
"path": "docs/public/samples/orbit-controls.html",
"chars": 4290,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-w"
},
{
"path": "docs/public/samples/post-processing.html",
"chars": 4943,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-w"
},
{
"path": "docs/public/samples/resizable-grid.html",
"chars": 10820,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-w"
},
{
"path": "docs/public/samples/responsive.html",
"chars": 10066,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-w"
},
{
"path": "docs/public/samples/standalone.html",
"chars": 4043,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-w"
},
{
"path": "docs/public/samples/x-up.html",
"chars": 4738,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-w"
},
{
"path": "docs/public/samples/yomotsu-camera-controls.html",
"chars": 4950,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-w"
},
{
"path": "docs/public/samples/z-up.html",
"chars": 4675,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-w"
},
{
"path": "docs/quickstart.md",
"chars": 2233,
"preview": "# Quick Start\n\n## Try it Online\n\nYou can try ViewportGizmo directly in your browser on [jsFiddle](https://jsfiddle.net/o"
},
{
"path": "lib/ViewportGizmo.ts",
"chars": 25420,
"preview": "import {\n Camera,\n Clock,\n Matrix4,\n Mesh,\n MeshBasicMaterial,\n Object3D,\n OrthographicCamera,\n PerspectiveCamer"
},
{
"path": "lib/types.ts",
"chars": 10557,
"preview": "import { DeepRequired } from \"utility-types\";\nimport {\n BufferGeometry,\n ColorRepresentation,\n Mesh,\n MeshBasicMater"
},
{
"path": "lib/utils/axesCorners.ts",
"chars": 2090,
"preview": "import { GizmoAxisObject, GizmoOptionsFallback } from \"@lib/types\";\nimport {\n CanvasTexture,\n Mesh,\n MeshBasicMateria"
},
{
"path": "lib/utils/axesEdges.ts",
"chars": 2609,
"preview": "import { GizmoOptionsFallback } from \"@lib/types\";\nimport {\n CanvasTexture,\n Mesh,\n MeshBasicMaterial,\n MeshBasicMat"
},
{
"path": "lib/utils/axesFaces.ts",
"chars": 2525,
"preview": "import { GizmoAxisObject, GizmoOptionsFallback } from \"@lib/types\";\nimport { roundedRectangleGeometry } from \"./roundedR"
},
{
"path": "lib/utils/axesLines.ts",
"chars": 1490,
"preview": "import { Color, Vector2 } from \"three\";\nimport { Line2 } from \"three/addons/lines/Line2.js\";\nimport { LineGeometry } fro"
},
{
"path": "lib/utils/axesMap.ts",
"chars": 5665,
"preview": "import {\n CanvasTexture,\n Color,\n ColorRepresentation,\n RepeatWrapping,\n SRGBColorSpace,\n Texture,\n} from \"three\";"
},
{
"path": "lib/utils/axesObjects.ts",
"chars": 858,
"preview": "import { GizmoOptionsFallback, GizmoAxisObject } from \"../types\";\nimport { axesMap } from \"./axesMap\";\nimport { axesFace"
},
{
"path": "lib/utils/axisHover.ts",
"chars": 448,
"preview": "import { GizmoAxisObject } from \"@lib/types\";\nimport { setMapHoverOffset } from \"./axesMap\";\n\nexport const axisHover = ("
},
{
"path": "lib/utils/constants.ts",
"chars": 837,
"preview": "export const GIZMO_EPSILON = 1e-6;\nexport const GIZMO_TURN_RATE = 2 * Math.PI;\nexport const GIZMO_MAIN_AXES = [\"x\", \"y\","
},
{
"path": "lib/utils/deepClone.ts",
"chars": 73,
"preview": "export const deepClone = <T>(obj: T) => JSON.parse(JSON.stringify(obj));\n"
},
{
"path": "lib/utils/getDomElement.ts",
"chars": 290,
"preview": "export const getDomElement = (\n domElement: string | HTMLElement\n): HTMLElement => {\n const element =\n typeof domEl"
},
{
"path": "lib/utils/gizmoBackground.ts",
"chars": 1403,
"preview": "import { GizmoAxisObject, GizmoOptionsFallback } from \"@lib/types\";\nimport {\n BackSide,\n BufferGeometry,\n Mesh,\n Mes"
},
{
"path": "lib/utils/gizmoDomElement.ts",
"chars": 1132,
"preview": "import { GizmoOptionsFallback } from \"../types\";\n\nexport const setDomPlacement = (\n domElement: HTMLElement,\n placemen"
},
{
"path": "lib/utils/intersectedObjects.ts",
"chars": 2069,
"preview": "import { GizmoAxisObject } from \"@lib/types\";\nimport {\n Vector2,\n Raycaster,\n type Object3D,\n type Camera,\n Interse"
},
{
"path": "lib/utils/isClick.ts",
"chars": 236,
"preview": "import { Vector2 } from \"three\";\n\nexport const isClick = (\n e: PointerEvent,\n startCoords: Vector2,\n threshold: numbe"
},
{
"path": "lib/utils/optionsFallback.ts",
"chars": 6561,
"preview": "import {\n GizmoAxisOptions,\n GizmoOptions,\n GizmoOptionsFallback,\n} from \"@lib/types\";\nimport {\n GIZMO_MAIN_AXES,\n "
},
{
"path": "lib/utils/roundedRectangleGeometry.ts",
"chars": 2131,
"preview": "import { BufferGeometry, BufferAttribute } from \"three\";\n\n/**\n * Generate a Rounded Rectangle geometry\n *\n * @param radi"
},
{
"path": "lib/utils/updateAxis.ts",
"chars": 975,
"preview": "import { GizmoAxisObject, GizmoOptionsFallback } from \"@lib/types\";\nimport { Camera, Vector3 } from \"three\";\nimport { cl"
},
{
"path": "lib/utils/updateBackground.ts",
"chars": 362,
"preview": "import { BufferGeometry, Mesh, MeshBasicMaterial } from \"three\";\n\nexport const updateBackground = (\n background: Mesh<B"
},
{
"path": "live/index.html",
"chars": 686,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <link rel=\"icon\" type=\"image/svg+xml\" href=\"/"
},
{
"path": "live/public/fonts/Roboto_Regular.json",
"chars": 338727,
"preview": "{\"glyphs\":{\"0\":{\"ha\":780,\"x_min\":78,\"x_max\":701,\"o\":\"m 701 421 q 626 94 701 201 q 391 -14 551 -14 q 157 91 233 -14 q 78 "
},
{
"path": "live/public/models/cad/support_plate.gltf",
"chars": 1972,
"preview": "{\r\n \"asset\": {\r\n \"version\": \"2.0\"\r\n },\r\n \"scene\": 0,\r\n \"scenes\": [\r\n {\r\n \"nodes\": [0]\r\n }\r\n ],\r\n \"no"
},
{
"path": "live/public/models/three/license.txt",
"chars": 648,
"preview": "Model Information:\n* title:\tThree.js\n* source:\thttps://sketchfab.com/3d-models/threejs-60320862bf904b7ab0e032c27daf7c7c\n"
},
{
"path": "live/public/models/three/scene.gltf",
"chars": 3539,
"preview": "{\n \"accessors\": [\n {\n \"bufferView\": 2,\n \"componentType\": 5126,\n \"count\": 398,\n \"max\": [\n "
},
{
"path": "live/src/WebGPU.ts",
"chars": 1434,
"preview": "import * as THREE from \"three/webgpu\";\nimport { OrbitControls } from \"three/addons/controls/OrbitControls.js\";\nimport { "
},
{
"path": "live/src/composer.ts",
"chars": 4737,
"preview": "import {\n CineonToneMapping,\n Clock,\n ConeGeometry,\n DoubleSide,\n Mesh,\n MeshPhysicalMaterial,\n Object3D,\n Octah"
},
{
"path": "live/src/configuration.ts",
"chars": 2036,
"preview": "import {\n Color,\n ColorRepresentation,\n GridHelper,\n PerspectiveCamera,\n Scene,\n WebGLRenderer,\n} from \"three\";\nim"
},
{
"path": "live/src/constant.ts",
"chars": 521,
"preview": "import { GizmoOptions } from \"@lib/types\";\n\nconst colors = {\n color: 0x333333,\n labelColor: 0xdddddd,\n hover: {\n c"
},
{
"path": "live/src/controls.ts",
"chars": 875,
"preview": "import { OrbitControls } from \"three/examples/jsm/Addons.js\";\nimport { initScene } from \"./initScene\";\nimport { Object3D"
},
{
"path": "live/src/controlsListeners.ts",
"chars": 873,
"preview": "import { OrbitControls } from \"three/examples/jsm/Addons.js\";\nimport { initScene } from \"./initScene\";\nimport { Object3D"
},
{
"path": "live/src/grid.ts",
"chars": 4354,
"preview": "import {\n CineonToneMapping,\n ConeGeometry,\n DoubleSide,\n Mesh,\n MeshPhysicalMaterial,\n OctahedronGeometry,\n Pers"
},
{
"path": "live/src/gui.ts",
"chars": 4066,
"preview": "import { updateViewportGizmo } from \"./configuration\";\n\nimport { GUI } from \"three/addons/libs/lil-gui.module.min.js\";\ni"
},
{
"path": "live/src/initScene.ts",
"chars": 4840,
"preview": "import {\n CineonToneMapping,\n Clock,\n Color,\n ConeGeometry,\n DoubleSide,\n GridHelper,\n Group,\n Mesh,\n MeshPhysi"
},
{
"path": "live/src/main.ts",
"chars": 1201,
"preview": "import { orbitOrTrackballControls } from \"./controls\";\nimport { initScene } from \"./initScene\";\nimport { yomotsuCameraCo"
},
{
"path": "live/src/multiViewport.ts",
"chars": 4590,
"preview": "import { GizmoOptionsFallback } from \"@lib/types\";\nimport { ViewportGizmo } from \"@lib/ViewportGizmo\";\nimport {\n BoxGeo"
},
{
"path": "live/src/responsive.ts",
"chars": 4833,
"preview": "import { PerspectiveCamera, Scene, WebGLRenderer } from \"three\";\nimport {\n GizmoAxisOptions,\n GizmoOptions,\n Viewport"
},
{
"path": "live/src/static.ts",
"chars": 97,
"preview": "import { initScene } from \"./initScene\";\n\nexport function staticRenderScene() {\n initScene();\n}\n"
},
{
"path": "live/src/utils/ResizableGrid.ts",
"chars": 5963,
"preview": "interface GridOptions {\n rows?: number;\n initialColumnWidths?: number[];\n initialRowHeights?: number[];\n}\n\ninterface "
},
{
"path": "live/src/utils/get3DText.ts",
"chars": 761,
"preview": "import { Material, Mesh } from \"three\";\nimport { TextGeometry } from \"three/examples/jsm/geometries/TextGeometry.js\";\nim"
},
{
"path": "live/src/utils/getSceneLights.ts",
"chars": 626,
"preview": "import { AmbientLight, Group, PointLight, Scene } from \"three\";\n\nexport const setSceneLights = (group: Group | Scene) =>"
},
{
"path": "live/src/utils/loadEnvMap.ts",
"chars": 951,
"preview": "import { PMREMGenerator, Scene, ToneMapping, WebGLRenderer } from \"three\";\nimport { RGBELoader } from \"three/examples/js"
},
{
"path": "live/src/utils/loadModel.ts",
"chars": 308,
"preview": "import { GLTFLoader } from \"three/examples/jsm/loaders/GLTFLoader.js\";\n\nexport const loadModel = async (path: string) =>"
},
{
"path": "live/src/vite-env.d.ts",
"chars": 38,
"preview": "/// <reference types=\"vite/client\" />\n"
},
{
"path": "live/src/yomotsuCameraControls.ts",
"chars": 1503,
"preview": "import { ViewportGizmo } from \"@lib/ViewportGizmo\";\nimport CameraControls from \"camera-controls\";\n\nimport {\n Vector2,\n "
},
{
"path": "live/src/z_up.ts",
"chars": 972,
"preview": "import { OrbitControls } from \"three/examples/jsm/Addons.js\";\nimport { initScene } from \"./initScene\";\nimport { AxesHelp"
},
{
"path": "live/src/z_up_stand_alone.ts",
"chars": 2277,
"preview": "import * as THREE from \"three\";\nimport { OrbitControls } from \"three/addons/controls/OrbitControls.js\";\nimport { Viewpor"
},
{
"path": "live/style.css",
"chars": 1680,
"preview": "* {\n box-sizing: border-box;\n}\n\nbody {\n margin: 0;\n overflow: hidden;\n font-family: Arial, Helvetica, sans-serif;\n "
},
{
"path": "package.json",
"chars": 1842,
"preview": "{\n \"name\": \"three-viewport-gizmo\",\n \"description\": \"Three Viewport Gizmo is a highly customizable standalone interacti"
},
{
"path": "readme.md",
"chars": 2414,
"preview": "<h1 align=\"center\">Three Viewport Gizmo</h1>\n\n<p align=\"center\">\n <a href=\"https://fennec-hub.github.io/three-viewport-"
},
{
"path": "tsconfig.json",
"chars": 823,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"ES2020\",\n \"useDefineForClassFields\": true,\n \"module\": \"ESNext\",\n \"lib\":"
},
{
"path": "vite.config.ts",
"chars": 695,
"preview": "import { resolve } from \"path\";\nimport { defineConfig } from \"vite\";\nimport dts from \"vite-plugin-dts\";\n\nexport default "
},
{
"path": "vite.live.config.ts",
"chars": 355,
"preview": "import { resolve } from \"path\";\nimport { defineConfig } from \"vite\";\n\nexport default defineConfig({\n base: \"/three-view"
}
]
// ... and 2 more files (download for full content)
About this extraction
This page contains the full source code of the Fennec-hub/three-viewport-gizmo GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 97 files (1.0 MB), approximately 450.5k tokens, and a symbol index with 245 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.