Owen Examples

Code Examples & Integration Patterns

Explore practical examples of using the Owen Animation System in different frameworks and scenarios.

Framework Integration

React Integration

Complete React component with animation state management

View Example

Vue Integration

Vue 3 composition API with reactive animation controls

View Example

Node.js Server

Server-side animation processing and validation

View Example

Multi-Scheme Usage

Scheme Conversion

Converting animations between different naming schemes

View Example

Batch Processing

Processing multiple animations with automated conversion

View Example

Validation Pipeline

Complete validation workflow with error handling

View Example

Advanced Features

Custom Schemes

Creating and registering custom naming schemes

View Example

Performance Optimization

Optimizing animation loading and caching strategies

View Example

Testing Integration

Unit and integration testing for animation systems

View Example

React Integration Example

import React, { useEffect, useRef, useState } from 'react'
import { OwenAnimationContext } from '@kjanat/owen'

export function AnimatedCharacter({ characterModel, namingScheme = 'semantic' }) {
  const containerRef = useRef()
  const [animationContext, setAnimationContext] = useState(null)
  const [currentAnimation, setCurrentAnimation] = useState('idle')
  const [isPlaying, setIsPlaying] = useState(false)

  useEffect(() => {
    // Initialize Owen Animation Context
    const context = new OwenAnimationContext({
      namingScheme,
      autoConvert: true,
      container: containerRef.current
    })

    context.loadModel(characterModel).then(() => {
      setAnimationContext(context)
    })

    return () => context?.dispose()
  }, [characterModel, namingScheme])

  const playAnimation = async (animationName) => {
    if (!animationContext) return

    try {
      await animationContext.playAnimation(animationName)
      setCurrentAnimation(animationName)
      setIsPlaying(true)
    } catch (error) {
      console.error('Failed to play animation:', error)
    }
  }

  const stopAnimation = () => {
    animationContext?.stopAnimation()
    setIsPlaying(false)
  }

  return (
    <div className="animated-character">
      <div ref={containerRef} className="character-viewport" />

      <div className="animation-controls">
        <button onClick={() => playAnimation('walk_forward')}>
          Walk
        </button>
        <button onClick={() => playAnimation('character_run')}>
          Run
        </button>
        <button onClick={() => playAnimation('jump_high')}>
          Jump
        </button>
        <button onClick={stopAnimation}>
          Stop
        </button>
      </div>

      <div className="animation-info">
        <p>Current: {currentAnimation}</p>
        <p>Status: {isPlaying ? 'Playing' : 'Stopped'}</p>
        <p>Scheme: {namingScheme}</p>
      </div>
    </div>
  )
}

Animation Name Conversion

import { AnimationNameMapper } from '@kjanat/owen'

// Initialize the mapper
const mapper = new AnimationNameMapper()

// Single animation conversion
function convertAnimation(animationName, fromScheme, toScheme) {
  try {
    const converted = mapper.convert(animationName, toScheme, fromScheme)
    console.log(`${fromScheme}: ${animationName} → ${toScheme}: ${converted}`)
    return converted
  } catch (error) {
    console.error('Conversion failed:', error.message)
    return null
  }
}

// Batch conversion with validation
function convertAnimationBatch(animations, fromScheme, toScheme) {
  const results = {
    successful: [],
    failed: [],
    conflicts: []
  }

  animations.forEach(anim => {
    try {
      const converted = mapper.convert(anim, toScheme, fromScheme)

      // Check for conflicts
      if (results.successful.includes(converted)) {
        results.conflicts.push({
          original: anim,
          converted,
          conflict: 'Duplicate target name'
        })
      } else {
        results.successful.push({
          original: anim,
          converted,
          schemes: { from: fromScheme, to: toScheme }
        })
      }
    } catch (error) {
      results.failed.push({
        original: anim,
        error: error.message,
        suggestions: mapper.suggestCorrections(anim, fromScheme)
      })
    }
  })

  return results
}

// Example usage
const legacyAnimations = [
  'walk_forward', 'run_fast', 'jump_high',
  'attack_sword', 'defend_shield', 'idle_breathing'
]

const conversionResults = convertAnimationBatch(
  legacyAnimations,
  'legacy',
  'semantic'
)

console.log('Conversion Results:', conversionResults)

Batch Processing Pipeline

import { AnimationProcessor } from '@kjanat/owen'
import fs from 'fs/promises'
import path from 'path'

class AnimationBatchProcessor {
  constructor(options = {}) {
    this.processor = new AnimationProcessor(options)
    this.inputDir = options.inputDir || './assets/raw'
    this.outputDir = options.outputDir || './assets/processed'
    this.targetScheme = options.targetScheme || 'semantic'
  }

  async processDirectory() {
    console.log('Starting batch animation processing...')

    try {
      // Scan input directory
      const files = await this.scanAnimationFiles()
      console.log(`Found ${files.length} animation files`)

      // Process each file
      const results = await Promise.allSettled(
        files.map(file => this.processFile(file))
      )

      // Generate summary report
      const summary = this.generateSummary(results)
      await this.saveReport(summary)

      return summary
    } catch (error) {
      console.error('Batch processing failed:', error)
      throw error
    }
  }

  async scanAnimationFiles() {
    const files = []
    const entries = await fs.readdir(this.inputDir, { withFileTypes: true })

    for (const entry of entries) {
      if (entry.isFile() && /\.(gltf|glb|fbx)$/i.test(entry.name)) {
        files.push(path.join(this.inputDir, entry.name))
      }
    }

    return files
  }

  async processFile(inputFile) {
    const filename = path.basename(inputFile)
    console.log(`Processing: ${filename}`)

    try {
      // Load and analyze animation
      const animation = await this.processor.loadAnimation(inputFile)

      // Convert naming scheme
      const convertedName = this.processor.convertName(
        animation.name,
        this.targetScheme
      )

      // Apply optimizations
      const optimized = await this.processor.optimize(animation)

      // Save processed animation
      const outputFile = path.join(this.outputDir, `${convertedName}.gltf`)
      await this.processor.saveAnimation(optimized, outputFile)

      return {
        status: 'success',
        inputFile,
        outputFile,
        originalName: animation.name,
        convertedName,
        size: optimized.size,
        duration: optimized.duration
      }
    } catch (error) {
      return {
        status: 'error',
        inputFile,
        error: error.message
      }
    }
  }

  generateSummary(results) {
    const successful = results.filter(r => r.value?.status === 'success')
    const failed = results.filter(r => r.status === 'rejected' || r.value?.status === 'error')

    return {
      timestamp: new Date().toISOString(),
      total: results.length,
      successful: successful.length,
      failed: failed.length,
      successRate: (successful.length / results.length * 100).toFixed(2),
      details: {
        successful: successful.map(r => r.value),
        failed: failed.map(r => ({
          file: r.value?.inputFile || 'unknown',
          error: r.value?.error || r.reason
        }))
      }
    }
  }

  async saveReport(summary) {
    const reportPath = path.join(this.outputDir, 'processing-report.json')
    await fs.mkdir(path.dirname(reportPath), { recursive: true })
    await fs.writeFile(reportPath, JSON.stringify(summary, null, 2))
    console.log(`Report saved: ${reportPath}`)
  }
}

// Usage
const processor = new AnimationBatchProcessor({
  inputDir: './assets/blender-exports',
  outputDir: './assets/animations',
  targetScheme: 'semantic'
})

processor.processDirectory().then(summary => {
  console.log('Processing complete:', summary)
}).catch(error => {
  console.error('Processing failed:', error)
  process.exit(1)
})