Browse Source

refactor: update utility imports in composables and core files (#973)

- Changed import statements in various files to directly import specific utility functions from './utils/is' instead of importing the entire module.
- This improves clarity and reduces potential namespace conflicts, ensuring that only the necessary functions are imported for use.
- Updated related logic in composables and core operations to utilize the new import structure for better maintainability.
Alvaro Saburido 1 month ago
parent
commit
c3c2ac1191

+ 1 - 1
src/composables/useCamera/index.ts

@@ -4,7 +4,7 @@ import type { TresContext } from '../useTresContextProvider'
 
 import { Camera, PerspectiveCamera } from 'three'
 import { computed, onUnmounted, ref, watchEffect } from 'vue'
-import { camera as isCamera } from '../../utils/is'
+import { isCamera } from '../../utils/is'
 
 export const useCamera = ({ sizes }: Pick<TresContext, 'sizes'> & { scene: TresScene }) => {
   // the computed does not trigger, when for example the camera position changes

+ 1 - 1
src/composables/useTexture/index.ts

@@ -1,6 +1,6 @@
 import type { LoadingManager, Texture } from 'three'
 import { TextureLoader } from 'three'
-import { isArray } from '../../utils'
+import { isArray } from '../../utils/is'
 
 export interface PBRMaterialOptions {
   /**

+ 5 - 5
src/composables/useTresEventManager/index.ts

@@ -3,8 +3,8 @@ import type { Object3D, Object3DEventMap, Scene } from 'three'
 import type { TresContext } from '../useTresContextProvider'
 import { shallowRef } from 'vue'
 import { hyphenate } from '../../utils'
-import * as is from '../../utils/is'
 import { useRaycaster } from '../useRaycaster'
+import { isObject3D, isTresObject } from '../../utils/is'
 
 export interface TresEventManager {
   /**
@@ -183,13 +183,13 @@ export function useTresEventManager(
   })
 
   function registerObject(maybeTresObject: unknown) {
-    if (is.tresObject(maybeTresObject) && is.object3D(maybeTresObject)) {
+    if (isTresObject(maybeTresObject) && isObject3D(maybeTresObject)) {
       objectsWithEvents.value.push(maybeTresObject as TresInstance)
     }
   }
 
   function deregisterObject(maybeTresObject: unknown) {
-    if (is.tresObject(maybeTresObject) && is.object3D(maybeTresObject)) {
+    if (isTresObject(maybeTresObject) && isObject3D(maybeTresObject)) {
       const index = objectsWithEvents.value.indexOf(maybeTresObject as TresInstance)
       if (index > -1) {
         objectsWithEvents.value.splice(index, 1)
@@ -198,13 +198,13 @@ export function useTresEventManager(
   }
 
   function registerPointerMissedObject(maybeTresObject: unknown) {
-    if (is.tresObject(maybeTresObject) && is.object3D(maybeTresObject) && maybeTresObject.onPointerMissed) {
+    if (isTresObject(maybeTresObject) && isObject3D(maybeTresObject) && maybeTresObject.onPointerMissed) {
       pointerMissedObjects.push(maybeTresObject)
     }
   }
 
   function deregisterPointerMissedObject(maybeTresObject: unknown) {
-    if (is.tresObject(maybeTresObject) && is.object3D(maybeTresObject)) {
+    if (isTresObject(maybeTresObject) && isObject3D(maybeTresObject)) {
       const index = pointerMissedObjects.indexOf(maybeTresObject)
       if (index > -1) {
         pointerMissedObjects.splice(index, 1)

+ 14 - 14
src/core/nodeOps.ts

@@ -4,7 +4,7 @@ import { BufferAttribute, Object3D } from 'three'
 import { isRef, type RendererOptions } from 'vue'
 import { attach, deepArrayEqual, doRemoveDeregister, doRemoveDetach, invalidateInstance, isHTMLTag, kebabToCamel, noop, prepareTresInstance, setPrimitiveObject, unboxTresPrimitive } from '../utils'
 import { logError } from '../utils/logger'
-import * as is from '../utils/is'
+import { isArray, isFunction, isObject, isObject3D, isScene, isUndefined } from '../utils/is'
 import { createRetargetingProxy } from '../utils/primitive/createRetargetingProxy'
 import { catalogue } from './catalogue'
 
@@ -40,7 +40,7 @@ export const nodeOps: (context: TresContext) => RendererOptions<TresObject, Tres
     let obj: TresObject | null
 
     if (tag === 'primitive') {
-      if (!is.obj(props.object) || isRef(props.object)) {
+      if (!isObject(props.object) || isRef(props.object)) {
         logError(
           'Tres primitives need an \'object\' prop, whose value is an object or shallowRef<object>',
         )
@@ -121,7 +121,7 @@ export const nodeOps: (context: TresContext) => RendererOptions<TresObject, Tres
     if (childInstance.__tres.attach) {
       attach(parentInstance, childInstance, childInstance.__tres.attach)
     }
-    else if (is.object3D(child) && is.object3D(parentInstance)) {
+    else if (isObject3D(child) && isObject3D(parentInstance)) {
       parentInstance.add(child)
       child.dispatchEvent({ type: 'added' })
     }
@@ -153,9 +153,9 @@ export const nodeOps: (context: TresContext) => RendererOptions<TresObject, Tres
 
     // NOTE: Derive `dispose` value for this `remove` call and
     // recursive remove calls.
-    dispose = is.und(dispose) ? 'default' : dispose
+    dispose = isUndefined(dispose) ? 'default' : dispose
     const userDispose = node.__tres?.dispose
-    if (!is.und(userDispose)) {
+    if (!isUndefined(userDispose)) {
       if (userDispose === null) {
         // NOTE: Treat as `false` to act like R3F
         dispose = false
@@ -209,11 +209,11 @@ export const nodeOps: (context: TresContext) => RendererOptions<TresObject, Tres
     doRemoveDeregister(node, context)
 
     // NOTE: 4) Dispose `node`
-    if (shouldDispose && !is.scene(node)) {
-      if (is.fun(dispose)) {
+    if (shouldDispose && !isScene(node)) {
+      if (isFunction(dispose)) {
         dispose(node as TresInstance)
       }
-      else if (is.fun(node.dispose)) {
+      else if (isFunction(node.dispose)) {
         try {
           node.dispose()
         }
@@ -265,7 +265,7 @@ export const nodeOps: (context: TresContext) => RendererOptions<TresObject, Tres
       return
     }
 
-    if (is.object3D(node) && key === 'blocks-pointer-events') {
+    if (isObject3D(node) && key === 'blocks-pointer-events') {
       if (nextValue || nextValue === '') { node[key] = nextValue }
       else { delete node[key] }
       return
@@ -319,23 +319,23 @@ export const nodeOps: (context: TresContext) => RendererOptions<TresObject, Tres
     let value = nextValue
     if (value === '') { value = true }
     // Set prop, prefer atomic methods if applicable
-    if (is.fun(target)) {
+    if (isFunction(target)) {
       // don't call pointer event callback functions
 
       if (!supportedPointerEvents.includes(prop)) {
-        if (is.arr(value)) { node[finalKey](...value) }
+        if (isArray(value)) { node[finalKey](...value) }
         else { node[finalKey](value) }
       }
       // NOTE: Set on* callbacks
       // Issue: https://github.com/Tresjs/tres/issues/360
-      if (finalKey.startsWith('on') && is.fun(value)) {
+      if (finalKey.startsWith('on') && isFunction(value)) {
         root[finalKey] = value
       }
       return
     }
-    if (!target?.set && !is.fun(target)) { root[finalKey] = value }
+    if (!target?.set && !isFunction(target)) { root[finalKey] = value }
     else if (target.constructor === value.constructor && target?.copy) { target?.copy(value) }
-    else if (is.arr(value)) { target.set(...value) }
+    else if (isArray(value)) { target.set(...value) }
     else if (!target.isColor && target.setScalar) { target.setScalar(value) }
     else { target.set(value) }
 

+ 3 - 2
src/devtools/plugin.ts

@@ -9,10 +9,11 @@ import {
 import { Color, type Mesh } from 'three'
 import { reactive } from 'vue'
 import { createHighlightMesh, editSceneObject } from '../utils'
-import * as is from '../utils/is'
 import { bytesToKB, calculateMemoryUsage } from '../utils/perf'
 import { toastMessage } from './utils'
 
+import { isLight } from '../utils/is'
+
 export interface Tags {
   label: string
   textColor: number
@@ -52,7 +53,7 @@ const createNode = (object: TresObject): SceneGraphObject => {
   }
 
   if (object.type.includes('Light')) {
-    if (is.light(object)) {
+    if (isLight(object)) {
       node.tags.push({
         label: `${object.intensity}`,
         textColor: 0x9499A6,

+ 11 - 15
src/utils/index.ts

@@ -4,7 +4,7 @@ import type { Material, Mesh, Object3D, Texture } from 'three'
 import type { TresContext } from '../composables/useTresContextProvider'
 import { DoubleSide, MathUtils, MeshBasicMaterial, Scene, Vector3 } from 'three'
 import { HightlightMesh } from '../devtools/highlight'
-import * as is from './is'
+import { isFunction, isNumber, isString, isTresPrimitive, isUndefined } from './is'
 
 export * from './logger'
 export function toSetMethodName(key: string) {
@@ -163,11 +163,6 @@ export function deepArrayEqual(arr1: any[], arr2: any[]): boolean {
   return true
 }
 
-/**
- * TypeSafe version of Array.isArray
- */
-export const isArray = Array.isArray as (a: any) => a is any[] | readonly any[]
-
 export function editSceneObject(scene: Scene, objectUuid: string, propertyPath: string[], value: any): void {
   // Function to recursively find the object by UUID
   const findObjectByUuid = (node: Object3D): Object3D | undefined => {
@@ -361,7 +356,7 @@ function joinAsCamelCase(...strings: string[]): string {
 const INDEX_REGEX = /-\d+$/
 
 export function attach(parent: TresInstance, child: TresInstance, type: AttachType) {
-  if (is.str(type)) {
+  if (isString(type)) {
     // NOTE: If attaching into an array (foo-0), create one
     if (INDEX_REGEX.test(type)) {
       const typeWithoutTrailingIndex = type.replace(INDEX_REGEX, '')
@@ -374,7 +369,7 @@ export function attach(parent: TresInstance, child: TresInstance, type: AttachTy
         const previousAttach = target[key]
         const augmentedArray: any[] & { __tresDetach?: () => void } = []
         augmentedArray.__tresDetach = () => {
-          if (augmentedArray.every(v => is.und(v))) {
+          if (augmentedArray.every(v => isUndefined(v))) {
             target[key] = previousAttach
           }
         }
@@ -392,7 +387,7 @@ export function attach(parent: TresInstance, child: TresInstance, type: AttachTy
 }
 
 export function detach(parent: any, child: TresInstance, type: AttachType) {
-  if (is.str(type)) {
+  if (isString(type)) {
     const { target, key } = resolve(parent, type)
     const previous = child.__tres.previousAttach
     // When the previous value was undefined, it means the value was never set to begin with
@@ -456,15 +451,15 @@ export function noop(fn: string): any {
 export function setPixelRatio(renderer: { setPixelRatio?: (dpr: number) => void, getPixelRatio?: () => number }, systemDpr: number, userDpr?: number | [number, number]) {
   // NOTE: Optional `setPixelRatio` allows this function to accept
   // THREE renderers like SVGRenderer.
-  if (!is.fun(renderer.setPixelRatio)) { return }
+  if (!isFunction(renderer.setPixelRatio)) { return }
 
   let newDpr = 0
 
-  if (userDpr && is.arr(userDpr) && userDpr.length >= 2) {
+  if (userDpr && Array.isArray(userDpr) && userDpr.length >= 2) {
     const [min, max] = userDpr
     newDpr = MathUtils.clamp(systemDpr, min, max)
   }
-  else if (is.num(userDpr)) { newDpr = userDpr }
+  else if (isNumber(userDpr)) { newDpr = userDpr }
   else { newDpr = systemDpr }
 
   // NOTE: Don't call `setPixelRatio` unless both:
@@ -525,7 +520,7 @@ export function setPrimitiveObject(
 }
 
 export function unboxTresPrimitive<T>(maybePrimitive: T): T | TresInstance {
-  if (is.tresPrimitive(maybePrimitive)) {
+  if (isTresPrimitive(maybePrimitive)) {
     // NOTE:
     // `primitive` has-a THREE object. Multiple `primitive`s can have
     // the same THREE object. We want to allow the same THREE object
@@ -533,8 +528,9 @@ export function unboxTresPrimitive<T>(maybePrimitive: T): T | TresInstance {
     // that, e.g., materials and geometries.
     // But __tres (`LocalState`) only allows for a single parent.
     // So: copy `__tres` to the object when unboxing.
-    maybePrimitive.object.__tres = maybePrimitive.__tres
-    return maybePrimitive.object
+    const primitive = maybePrimitive as unknown as TresPrimitive
+    primitive.object.__tres = primitive.__tres
+    return primitive.object
   }
   else {
     return maybePrimitive

+ 16 - 17
src/utils/is.test.ts

@@ -1,6 +1,6 @@
 import type { Camera, Light, Material, Object3D } from 'three'
 import { AmbientLight, BufferGeometry, DirectionalLight, Fog, Group, Mesh, MeshBasicMaterial, MeshNormalMaterial, OrthographicCamera, PerspectiveCamera, PointLight, Scene } from 'three'
-import * as is from './is'
+import { isArray, isBoolean, isBufferGeometry, isCamera, isFog, isFunction, isLight, isMaterial, isNumber, isObject, isObject3D, isScene, isString, isTresObject, isUndefined } from './is'
 
 const NUMBERS: Record<string, number> = {
   '0': 0,
@@ -89,27 +89,26 @@ const BUFFER_GEOMETRIES: Record<string, BufferGeometry> = {
 
 const OBJECTS = Object.assign({}, { '{}': {}, '{ a: "a" }': { a: 'a' } }, FOGS, MATERIALS, OBJECT3DS, BUFFER_GEOMETRIES)
 
-// NOTE: See definition in is.ts
 const TRES_OBJECTS = Object.assign({}, MATERIALS, OBJECT3DS, BUFFER_GEOMETRIES, FOGS)
 
 const ALL = Object.assign({}, NUMBERS, BOOLEANS, STRINGS, NULL, UNDEFINED, ARRAYS, FUNCTIONS, OBJECTS)
 
 describe('is', () => {
-  describe('is.und(a: any)', () => { test(is.und, UNDEFINED) })
-  describe('is.arr(a: any)', () => { test(is.arr, ARRAYS) })
-  describe('is.num(a: any)', () => { test(is.num, NUMBERS) })
-  describe('is.str(a: any)', () => { test(is.str, STRINGS) })
-  describe('is.bool(a: any)', () => { test(is.bool, BOOLEANS) })
-  describe('is.fun(a: any)', () => { test(is.fun, FUNCTIONS) })
-  describe('is.obj(a: any)', () => { test(is.obj, OBJECTS) })
-  describe('is.object3D(a: any)', () => { test(is.object3D, OBJECT3DS) })
-  describe('is.camera(a: any)', () => { test(is.camera, CAMERAS) })
-  describe('is.bufferGeometry(a: any)', () => { test(is.bufferGeometry, BUFFER_GEOMETRIES) })
-  describe('is.material(a: any)', () => { test(is.material, MATERIALS) })
-  describe('is.light(a: any)', () => { test(is.light, LIGHTS) })
-  describe('is.fog(a: any)', () => { test(is.fog, FOGS) })
-  describe('is.scene(a: any)', () => { test(is.scene, SCENES) })
-  describe('is.tresObject(a: any)', () => { test(is.tresObject, TRES_OBJECTS) })
+  describe('isUndefined(a: any)', () => { test(isUndefined, UNDEFINED) })
+  describe('isArray(a: any)', () => { test(isArray, ARRAYS) })
+  describe('isNumber(a: any)', () => { test(isNumber, NUMBERS) })
+  describe('isString(a: any)', () => { test(isString, STRINGS) })
+  describe('isBoolean(a: any)', () => { test(isBoolean, BOOLEANS) })
+  describe('isFunction(a: any)', () => { test(isFunction, FUNCTIONS) })
+  describe('isObject(a: any)', () => { test(isObject, OBJECTS) })
+  describe('isObject3D(a: any)', () => { test(isObject3D, OBJECT3DS) })
+  describe('isCamera(a: any)', () => { test(isCamera, CAMERAS) })
+  describe('isBufferGeometry(a: any)', () => { test(isBufferGeometry, BUFFER_GEOMETRIES) })
+  describe('isMaterial(a: any)', () => { test(isMaterial, MATERIALS) })
+  describe('isLight(a: any)', () => { test(isLight, LIGHTS) })
+  describe('isFog(a: any)', () => { test(isFog, FOGS) })
+  describe('isScene(a: any)', () => { test(isScene, SCENES) })
+  describe('isTresObject(a: any)', () => { test(isTresObject, TRES_OBJECTS) })
 })
 
 /**

+ 265 - 32
src/utils/is.ts

@@ -1,68 +1,301 @@
 import type { TresObject, TresPrimitive } from 'src/types'
 import type { BufferGeometry, Camera, Fog, Light, Material, Object3D, Scene } from 'three'
 
-export function und(u: unknown): u is undefined {
-  return typeof u === 'undefined'
+/**
+ * Type guard to check if a value is undefined
+ * @param value - The value to check
+ * @returns True if the value is undefined, false otherwise
+ * @example
+ * ```ts
+ * const value = undefined
+ * if (isUndefined(value)) {
+ *   // TypeScript knows value is undefined here
+ * }
+ * ```
+ */
+export function isUndefined(value: unknown): value is undefined {
+  return typeof value === 'undefined'
 }
 
-export function arr(u: unknown): u is Array<unknown> {
-  return Array.isArray(u)
+/**
+ * Type guard to check if a value is an array
+ * @param value - The value to check
+ * @returns True if the value is an array, false otherwise
+ * @example
+ * ```ts
+ * const value = [1, 2, 3]
+ * if (isArray(value)) {
+ *   // TypeScript knows value is Array<unknown> here
+ *   value.length // OK
+ *   value.map(x => x) // OK
+ * }
+ * ```
+ */
+export function isArray(value: unknown): value is Array<unknown> {
+  return Array.isArray(value)
 }
 
-export function num(u: unknown): u is number {
-  return typeof u === 'number'
+/**
+ * Type guard to check if a value is a number
+ * @param value - The value to check
+ * @returns True if the value is a number (including NaN and Infinity), false otherwise
+ * @example
+ * ```ts
+ * const value = 42
+ * if (isNumber(value)) {
+ *   // TypeScript knows value is number here
+ *   value.toFixed(2) // OK
+ *   value * 2 // OK
+ * }
+ * ```
+ */
+export function isNumber(value: unknown): value is number {
+  return typeof value === 'number'
 }
 
-export function str(u: unknown): u is string {
-  return typeof u === 'string'
+/**
+ * Type guard to check if a value is a string
+ * @param value - The value to check
+ * @returns True if the value is a string, false otherwise
+ * @example
+ * ```ts
+ * const value = "hello"
+ * if (isString(value)) {
+ *   // TypeScript knows value is string here
+ *   value.length // OK
+ *   value.toUpperCase() // OK
+ * }
+ * ```
+ */
+export function isString(value: unknown): value is string {
+  return typeof value === 'string'
 }
 
-export function bool(u: unknown): u is boolean {
-  return u === true || u === false
+/**
+ * Type guard to check if a value is a boolean
+ * @param value - The value to check
+ * @returns True if the value is strictly true or false, false otherwise
+ * @example
+ * ```ts
+ * const value = true
+ * if (isBoolean(value)) {
+ *   // TypeScript knows value is boolean here
+ *   !value // OK
+ *   value && true // OK
+ * }
+ * ```
+ */
+export function isBoolean(value: unknown): value is boolean {
+  return value === true || value === false
 }
 
-export function fun(u: unknown): u is (...args: any[]) => any {
-  return typeof u === 'function'
+/**
+ * Type guard to check if a value is a function
+ * @param value - The value to check
+ * @returns True if the value is a callable function, false otherwise
+ * @example
+ * ```ts
+ * const value = () => {}
+ * if (isFunction(value)) {
+ *   // TypeScript knows value is (...args: any[]) => any here
+ *   value() // OK
+ *   value.call(null) // OK
+ * }
+ * ```
+ */
+export function isFunction(value: unknown): value is (...args: any[]) => any {
+  return typeof value === 'function'
 }
 
-export function obj(u: unknown): u is Record<string | number | symbol, unknown> {
-  return u === Object(u) && !arr(u) && !fun(u)
+/**
+ * Type guard to check if a value is a plain object
+ * @param value - The value to check
+ * @returns True if the value is a plain object (not null, array, or function), false otherwise
+ * @example
+ * ```ts
+ * const value = { key: 'value' }
+ * if (isObject(value)) {
+ *   // TypeScript knows value is Record<string | number | symbol, unknown> here
+ *   Object.keys(value) // OK
+ *   value.key // OK
+ * }
+ * ```
+ */
+export function isObject(value: unknown): value is Record<string | number | symbol, unknown> {
+  return value === Object(value) && !isArray(value) && !isFunction(value)
 }
 
-export function object3D(u: unknown): u is Object3D {
-  return obj(u) && !!(u.isObject3D)
+/**
+ * Type guard to check if a value is a Three.js Object3D
+ * @param value - The value to check
+ * @returns True if the value is a Three.js Object3D instance, false otherwise
+ * @example
+ * ```ts
+ * const value = new THREE.Object3D()
+ * if (isObject3D(value)) {
+ *   // TypeScript knows value is Object3D here
+ *   value.position // OK
+ *   value.rotation // OK
+ *   value.scale // OK
+ * }
+ * ```
+ */
+export function isObject3D(value: unknown): value is Object3D {
+  return isObject(value) && !!(value.isObject3D)
 }
 
-export function camera(u: unknown): u is Camera {
-  return obj(u) && !!(u.isCamera)
+/**
+ * Type guard to check if a value is a Three.js Camera
+ * @param value - The value to check
+ * @returns True if the value is a Three.js Camera instance, false otherwise
+ * @example
+ * ```ts
+ * const value = new THREE.PerspectiveCamera()
+ * if (isCamera(value)) {
+ *   // TypeScript knows value is Camera here
+ *   value.fov // OK
+ *   value.near // OK
+ *   value.far // OK
+ * }
+ * ```
+ */
+export function isCamera(value: unknown): value is Camera {
+  return isObject(value) && !!(value.isCamera)
 }
 
-export function bufferGeometry(u: unknown): u is BufferGeometry {
-  return obj(u) && !!(u.isBufferGeometry)
+/**
+ * Type guard to check if a value is a Three.js BufferGeometry
+ * @param value - The value to check
+ * @returns True if the value is a Three.js BufferGeometry instance, false otherwise
+ * @example
+ * ```ts
+ * const value = new THREE.BufferGeometry()
+ * if (isBufferGeometry(value)) {
+ *   // TypeScript knows value is BufferGeometry here
+ *   value.attributes // OK
+ *   value.index // OK
+ *   value.computeVertexNormals() // OK
+ * }
+ * ```
+ */
+export function isBufferGeometry(value: unknown): value is BufferGeometry {
+  return isObject(value) && !!(value.isBufferGeometry)
 }
 
-export function material(u: unknown): u is Material {
-  return obj(u) && !!(u.isMaterial)
+/**
+ * Type guard to check if a value is a Three.js Material
+ * @param value - The value to check
+ * @returns True if the value is a Three.js Material instance, false otherwise
+ * @example
+ * ```ts
+ * const value = new THREE.MeshStandardMaterial()
+ * if (isMaterial(value)) {
+ *   // TypeScript knows value is Material here
+ *   value.color // OK
+ *   value.metalness // OK
+ *   value.roughness // OK
+ * }
+ * ```
+ */
+export function isMaterial(value: unknown): value is Material {
+  return isObject(value) && !!(value.isMaterial)
 }
 
-export function light(u: unknown): u is Light {
-  return obj(u) && !!(u.isLight)
+/**
+ * Type guard to check if a value is a Three.js Light
+ * @param value - The value to check
+ * @returns True if the value is a Three.js Light instance, false otherwise
+ * @example
+ * ```ts
+ * const value = new THREE.DirectionalLight()
+ * if (isLight(value)) {
+ *   // TypeScript knows value is Light here
+ *   value.intensity // OK
+ *   value.color // OK
+ *   value.position // OK
+ * }
+ * ```
+ */
+export function isLight(value: unknown): value is Light {
+  return isObject(value) && !!(value.isLight)
 }
 
-export function fog(u: unknown): u is Fog {
-  return obj(u) && !!(u.isFog)
+/**
+ * Type guard to check if a value is a Three.js Fog
+ * @param value - The value to check
+ * @returns True if the value is a Three.js Fog instance, false otherwise
+ * @example
+ * ```ts
+ * const value = new THREE.Fog(0x000000, 1, 1000)
+ * if (isFog(value)) {
+ *   // TypeScript knows value is Fog here
+ *   value.color // OK
+ *   value.near // OK
+ *   value.far // OK
+ * }
+ * ```
+ */
+export function isFog(value: unknown): value is Fog {
+  return isObject(value) && !!(value.isFog)
 }
 
-export function scene(u: unknown): u is Scene {
-  return obj(u) && !!(u.isScene)
+/**
+ * Type guard to check if a value is a Three.js Scene
+ * @param value - The value to check
+ * @returns True if the value is a Three.js Scene instance, false otherwise
+ * @example
+ * ```ts
+ * const value = new THREE.Scene()
+ * if (isScene(value)) {
+ *   // TypeScript knows value is Scene here
+ *   value.children // OK
+ *   value.add(new THREE.Object3D()) // OK
+ *   value.remove(new THREE.Object3D()) // OK
+ * }
+ * ```
+ */
+export function isScene(value: unknown): value is Scene {
+  return isObject(value) && !!(value.isScene)
 }
 
-export function tresObject(u: unknown): u is TresObject {
+/**
+ * Type guard to check if a value is a TresObject
+ * @param value - The value to check
+ * @returns True if the value is a TresObject (Object3D | BufferGeometry | Material | Fog), false otherwise
+ * @example
+ * ```ts
+ * const value = new THREE.Mesh()
+ * if (isTresObject(value)) {
+ *   // TypeScript knows value is TresObject here
+ *   // You can use common properties and methods shared by all TresObjects
+ * }
+ * ```
+ * @remarks
+ * TresObject is a union type that represents the core Three.js objects that can be used in TresJS.
+ * This includes Object3D, BufferGeometry, Material, and Fog instances.
+ */
+export function isTresObject(value: unknown): value is TresObject {
   // NOTE: TresObject is currently defined as
   // TresObject3D | THREE.BufferGeometry | THREE.Material | THREE.Fog
-  return object3D(u) || bufferGeometry(u) || material(u) || fog(u)
+  return isObject3D(value) || isBufferGeometry(value) || isMaterial(value) || isFog(value)
 }
 
-export function tresPrimitive(u: unknown): u is TresPrimitive {
-  return obj(u) && !!(u.isPrimitive)
+/**
+ * Type guard to check if a value is a TresPrimitive
+ * @param value - The value to check
+ * @returns True if the value is a TresPrimitive instance, false otherwise
+ * @example
+ * ```ts
+ * const value = { isPrimitive: true }
+ * if (isTresPrimitive(value)) {
+ *   // TypeScript knows value is TresPrimitive here
+ *   // You can use properties and methods specific to TresPrimitives
+ * }
+ * ```
+ * @remarks
+ * TresPrimitive is a special type in TresJS that represents primitive objects
+ * that can be used directly in the scene without needing to be wrapped in a Three.js object.
+ */
+export function isTresPrimitive(value: unknown): value is TresPrimitive {
+  return isObject(value) && !!(value.isPrimitive)
 }