import Reconciler from 'react-reconciler' ; import * as OGL from 'ogl' ; import { toPascalCase , applyProps , diffProps } from './utils' ; import { GL_ELEMENTS } from './constants' ; import { Catalogue , Instance , InstanceProps , RootState } from './types' ; const catalogue = { } ; /**
 * Extends the OGL namespace, accepting an object of keys pointing to external classes.
 */ export const extend = ( objects ) => Object . entries ( objects ) . forEach ( ( [ key , value ] ) => ( catalogue [ key ] = value ) ) ; /**
 * Creates an OGL element from a React node.
 */ export const createInstance = ( type , { object , args , ... props } , root ) => { const name = toPascalCase ( type ) ; const target = catalogue [ name ] || OGL [ name ] ; if ( type !== 'primitive' && ! target ) throw `${ type } is not a part of the OGL namespace! Did you forget to extend?` ; if ( type === 'primitive' && ! object ) throw `"object" must be set when using primitives.` ; if ( ! object && GL_ELEMENTS . some ( ( elem ) => Object . prototype . isPrototypeOf . call ( elem , target ) || elem === target ) ) { const isGL = ( arg ) => arg instanceof WebGL2RenderingContext || arg instanceof WebGLRenderingContext ; const gl = args ?. find ( ( arg ) => isGL ( arg ) ) ?? root . gl ; const attrs = args ?. find ( ( arg ) => ! isGL ( arg ) ) ?? { } ; const propAttrs = Object . entries ( props || { } ) . reduce ( ( acc , [ key , value ] ) => { if ( ! key . includes ( '-' ) ) acc [ key ] = value ; return acc ; } , attrs ) ; args = [ gl , propAttrs ] ; } const instance = object || ( Array . isArray ( args ) ? new target ( ... args ) : new target ( args ) ) ; if ( type === 'primitive' ) instance . isPrimitive = true ; if ( instance instanceof OGL . Geometry ) { props = { attach : 'geometry' , ... props } ; } else if ( instance instanceof OGL . Program ) { props = { attach : 'program' , ... props } ; } applyProps ( instance , props ) ; return instance ; } ; /**
 * Switches instance to a new one, moving over children.
 */ export const switchInstance = ( instance , type , props , root ) => { const newInstance = createInstance ( type , props , root ) ; if ( ! instance . isPrimitive && instance . children ) { instance . children . forEach ( ( child ) => appendChild ( newInstance , child ) ) ; instance . children = [ ] ; } removeChild ( instance . parent , instance ) ; appendChild ( instance . parent , newInstance ) ; return newInstance ; } ; /**
 * Adds elements to our scene and attaches children to their parents.
 */ export const appendChild = ( parentInstance , child ) => { if ( ! child ) return ; if ( child . attach ) { parentInstance [ child . attach ] = child ; } else { child . setParent ?. ( parentInstance ) ; } child . parent = parentInstance ; } ; /**
 * Removes elements from scene and disposes of them.
 */ export const removeChild = ( parentInstance , child ) => { if ( ! child ) return ; if ( ! child . attach && child . setParent ) { parentInstance ?. removeChild ?. ( child ) ; } } ; /**
 * Gets the root store and container from a target container and child instance.
 */ export const getContainer = ( container , child ) : { root : RootState ; container : Instance ; } => ( { root : container ?. scene ? container : container ?. rootNode ?? child . rootNode , container : container ?. scene || container , } ) ; /**
 * Inserts an instance between instances of a ReactNode.
 */ export const insertBefore = ( parentInstance , child , beforeChild ) => { if ( ! child ) return ; child . parent = parentInstance ; const index = parentInstance . children . indexOf ( beforeChild ) ; parentInstance . children = [ ... parentInstance . children . slice ( 0 , index ) , child , ... parentInstance . children . slice ( index ) ] ; } ; /**
 * Centralizes and handles mutations through an OGL scene-graph.
 */ export const reconciler = Reconciler ( { supportsMutation : true , isPrimaryRenderer : false , getPublicInstance : ( instance ) => instance , getRootHostContext : ( ) => null , getChildHostContext : ( parentHostContext ) => parentHostContext , createTextInstance : ( ) => console . warn ( 'Text is not allowed in the OGL scene-graph!' ) , prepareForCommit : ( ) => null , resetAfterCommit : ( ) => { } , shouldSetTextContent : ( ) => false , finalizeInitialChildren : ( ) => false , preparePortalMount : ( container ) => container , clearContainer : ( ) => false , createInstance , appendChild , appendInitialChild : appendChild , appendChildToContainer ( parentInstance , child ) { const { root , container } = getContainer ( parentInstance , child ) ; child . stateNode = root ; appendChild ( container , child ) ; } , removeChild , removeChildFromContainer ( parentInstance , child ) { const { container } = getContainer ( parentInstance , child ) ; removeChild ( container , child ) ; } , insertBefore , insertInContainerBefore ( parentInstance , child , beforeChild ) { const { container } = getContainer ( parentInstance , child ) ; insertBefore ( container , child , beforeChild ) ; } , prepareUpdate ( instance , type , oldProps , newProps ) { if ( instance . isPrimitive && newProps . object && newProps . object !== instance ) return [ true ] ; if ( type === 'program' ) { if ( oldProps . vertex !== newProps . vertex ) return [ true ] ; if ( oldProps . fragment !== newProps . fragment ) return [ true ] ; } if ( instance instanceof OGL . Geometry ) { const oldAttributes = Object . keys ( oldProps ) . filter ( ( key ) => ! key . startsWith ( 'attributes-' ) ) ; if ( oldAttributes . some ( ( key ) => oldProps [ key ] !== newProps [ key ] ) ) return [ true ] ; } if ( newProps . args ?. some ( ( value , index ) => value !== oldProps . args [ index ] ) ) return [ true ] ; const changedProps = diffProps ( instance , newProps , oldProps ) ; if ( Object . keys ( changedProps ) . length ) return [ false , changedProps ] ; return null ; } , commitUpdate ( instance , [ reconstruct , changedProps ] , type , oldProps , newProps , root ) { if ( reconstruct ) return switchInstance ( instance , type , newProps , root ) ; applyProps ( instance , changedProps ) ; } , hideInstance ( instance ) { instance . visible = false ; } , unhideInstance ( instance ) { instance . visible = true ; } , } ) ; reconciler . injectIntoDevTools ( { bundleType : process . env . NODE_ENV === 'production' ? 0 : 1 , rendererPackageName : 'react-ogl' , version : '0.1.0' , } ) ;