Implement multi-scheme animation name mapper for Owen Animation System
Some checks failed
CI/CD Pipeline / Test & Lint (16.x) (push) Has been cancelled
CI/CD Pipeline / Test & Lint (18.x) (push) Has been cancelled
CI/CD Pipeline / Test & Lint (20.x) (push) Has been cancelled
CI/CD Pipeline / Security Audit (push) Has been cancelled
CI/CD Pipeline / Release (push) Has been cancelled
Demo Deployment / Build Demo (push) Has been cancelled
Demo Deployment / Test Demo (push) Has been cancelled
Demo Deployment / Performance Audit (push) Has been cancelled
Demo Deployment / Deploy to Staging (push) Has been cancelled
Demo Deployment / Deploy to Production (push) Has been cancelled
Animation Processing Pipeline / Validate Animation Names (push) Has been cancelled
Animation Processing Pipeline / Process Blender Animation Assets (push) Has been cancelled
Multi-Scheme Testing / Validate Naming Schemes (artist) (push) Has been cancelled
Multi-Scheme Testing / Validate Naming Schemes (hierarchical) (push) Has been cancelled
Multi-Scheme Testing / Validate Naming Schemes (legacy) (push) Has been cancelled
Multi-Scheme Testing / Validate Naming Schemes (semantic) (push) Has been cancelled
Multi-Scheme Testing / Test Scheme Conversions (push) Has been cancelled
Multi-Scheme Testing / Validate Demo Functionality (push) Has been cancelled
Multi-Scheme Testing / Performance Benchmarks (push) Has been cancelled
Performance Testing / Animation Conversion Performance (100, artist) (push) Has been cancelled
Performance Testing / Animation Conversion Performance (100, hierarchical) (push) Has been cancelled
Performance Testing / Animation Conversion Performance (100, legacy) (push) Has been cancelled
Performance Testing / Animation Conversion Performance (100, semantic) (push) Has been cancelled
Performance Testing / Animation Conversion Performance (1000, artist) (push) Has been cancelled
Performance Testing / Animation Conversion Performance (1000, hierarchical) (push) Has been cancelled
Performance Testing / Animation Conversion Performance (1000, legacy) (push) Has been cancelled
Performance Testing / Animation Conversion Performance (1000, semantic) (push) Has been cancelled
Performance Testing / Animation Conversion Performance (5000, artist) (push) Has been cancelled
Performance Testing / Animation Conversion Performance (5000, hierarchical) (push) Has been cancelled
Performance Testing / Animation Conversion Performance (5000, legacy) (push) Has been cancelled
Performance Testing / Animation Conversion Performance (5000, semantic) (push) Has been cancelled
Performance Testing / Memory Usage Analysis (push) Has been cancelled
Performance Testing / Demo Performance Audit (push) Has been cancelled
Animation Processing Pipeline / Update Animation Documentation (push) Has been cancelled
Animation Processing Pipeline / Deploy Animation Demo (push) Has been cancelled
Performance Testing / Generate Performance Report (push) Has been cancelled

- Added AnimationNameMapper class to handle conversion between different animation naming schemes (legacy, artist, hierarchical, semantic).
- Included methods for initialization, pattern matching, conversion, and validation of animation names.
- Developed comprehensive unit tests for the animation name converter and demo pages using Playwright.
- Created a Vite configuration for the demo application, including asset handling and optimization settings.
- Enhanced the demo with features for batch conversion, performance metrics, and responsive design.
This commit is contained in:
2025-05-24 05:18:13 +02:00
parent d513e80c07
commit b447abee00
54 changed files with 14343 additions and 989 deletions

View File

@ -0,0 +1,361 @@
#!/usr/bin/env node
/**
* Check Naming Conflicts Script
* Analyzes animation names across all schemes to detect potential conflicts
*/
import fs from 'fs'
import path from 'path'
import { fileURLToPath, pathToFileURL } from 'url'
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
const PROJECT_ROOT = path.resolve(__dirname, '..')
const ANIMATION_MAPPER_PATH = path.join(PROJECT_ROOT, 'src', 'animation', 'AnimationNameMapper.js')
/**
* Check for naming conflicts across schemes
*/
async function checkNamingConflicts () {
try {
console.log('🔍 Checking for animation naming conflicts...')
// Import the AnimationNameMapper
const animationMapperUrl = pathToFileURL(ANIMATION_MAPPER_PATH)
const { AnimationNameMapper } = await import(animationMapperUrl)
const mapper = new AnimationNameMapper()
const conflicts = []
const warnings = []
const statistics = {
totalAnimations: 0,
duplicatesWithinScheme: 0,
crossSchemeConflicts: 0,
ambiguousNames: 0,
validationErrors: 0
}
const schemes = ['legacy', 'artist', 'hierarchical', 'semantic']
const allAnimationsByScheme = {}
// Collect all animations by scheme
schemes.forEach(scheme => {
allAnimationsByScheme[scheme] = mapper.getAllAnimationsByScheme(scheme)
statistics.totalAnimations += allAnimationsByScheme[scheme].length
})
console.log(`📊 Analyzing ${statistics.totalAnimations} total animations across ${schemes.length} schemes`)
// 1. Check for duplicates within each scheme
schemes.forEach(scheme => {
const animations = allAnimationsByScheme[scheme]
const seen = new Set()
const duplicates = []
animations.forEach(anim => {
if (seen.has(anim)) {
duplicates.push(anim)
statistics.duplicatesWithinScheme++
}
seen.add(anim)
})
if (duplicates.length > 0) {
conflicts.push({
type: 'duplicate_within_scheme',
scheme,
animations: duplicates,
severity: 'error',
message: `Duplicate animations found within ${scheme} scheme`
})
}
})
// 2. Check for cross-scheme conflicts (same name in different schemes with different meanings)
const nameToSchemes = {}
schemes.forEach(scheme => {
allAnimationsByScheme[scheme].forEach(anim => {
if (!nameToSchemes[anim]) {
nameToSchemes[anim] = []
}
nameToSchemes[anim].push(scheme)
})
})
Object.entries(nameToSchemes).forEach(([animName, animSchemes]) => {
if (animSchemes.length > 1) {
// Check if they map to the same semantic meaning
try {
const allSemantic = animSchemes.map(scheme => {
try {
return mapper.convert(animName, 'semantic')
} catch {
return null
}
}).filter(Boolean)
const uniqueSemantic = [...new Set(allSemantic)]
if (uniqueSemantic.length > 1) {
conflicts.push({
type: 'cross_scheme_conflict',
animationName: animName,
schemes: animSchemes,
semanticMappings: uniqueSemantic,
severity: 'error',
message: `Animation "${animName}" exists in multiple schemes but maps to different meanings`
})
statistics.crossSchemeConflicts++
}
} catch (error) {
warnings.push({
type: 'conversion_error',
animationName: animName,
schemes: animSchemes,
error: error.message,
severity: 'warning'
})
}
}
})
// 3. Check for ambiguous names (could be interpreted as multiple schemes)
const allAnimations = Object.values(allAnimationsByScheme).flat()
const uniqueAnimations = [...new Set(allAnimations)]
uniqueAnimations.forEach(anim => {
const detectedScheme = mapper.detectScheme(anim)
let possibleSchemes = 0
// Test if name could belong to other schemes
schemes.forEach(scheme => {
try {
const converted = mapper.convert(anim, scheme)
if (converted) possibleSchemes++
} catch {
// Can't convert to this scheme
}
})
if (possibleSchemes > 2) {
warnings.push({
type: 'ambiguous_name',
animationName: anim,
detectedScheme,
possibleSchemes,
severity: 'warning',
message: `Animation "${anim}" could be interpreted as belonging to multiple schemes`
})
statistics.ambiguousNames++
}
})
// 4. Validate all animations can be properly converted
uniqueAnimations.forEach(anim => {
schemes.forEach(targetScheme => {
try {
mapper.convert(anim, targetScheme)
} catch (error) {
if (!error.message.includes('not found in mapping')) {
warnings.push({
type: 'validation_error',
animationName: anim,
targetScheme,
error: error.message,
severity: 'warning'
})
statistics.validationErrors++
}
}
})
})
// 5. Check for naming convention violations
const conventionViolations = []
// Legacy should follow pattern: word_word_L/S
allAnimationsByScheme.legacy.forEach(anim => {
if (!/^[a-z]+(_[a-z]+)*_[LS]$/.test(anim)) {
conventionViolations.push({
type: 'convention_violation',
scheme: 'legacy',
animationName: anim,
expectedPattern: 'word_word_L/S',
severity: 'warning'
})
}
})
// Artist should follow pattern: Owen_PascalCase
allAnimationsByScheme.artist.forEach(anim => {
if (!/^Owen_[A-Z][a-zA-Z]*$/.test(anim)) {
conventionViolations.push({
type: 'convention_violation',
scheme: 'artist',
animationName: anim,
expectedPattern: 'Owen_PascalCase',
severity: 'warning'
})
}
})
// Hierarchical should follow pattern: owen.category.subcategory
allAnimationsByScheme.hierarchical.forEach(anim => {
if (!/^owen(\.[a-z]+)+$/.test(anim)) {
conventionViolations.push({
type: 'convention_violation',
scheme: 'hierarchical',
animationName: anim,
expectedPattern: 'owen.category.subcategory',
severity: 'warning'
})
}
})
// Semantic should follow pattern: OwenPascalCase
allAnimationsByScheme.semantic.forEach(anim => {
if (!/^Owen[A-Z][a-zA-Z]*$/.test(anim)) {
conventionViolations.push({
type: 'convention_violation',
scheme: 'semantic',
animationName: anim,
expectedPattern: 'OwenPascalCase',
severity: 'warning'
})
}
})
warnings.push(...conventionViolations)
// Generate report
const report = {
timestamp: new Date().toISOString(),
summary: {
status: conflicts.length === 0 ? 'PASS' : 'FAIL',
totalConflicts: conflicts.length,
totalWarnings: warnings.length,
statistics
},
conflicts,
warnings,
recommendations: generateRecommendations(conflicts, warnings),
schemes: Object.fromEntries(
schemes.map(scheme => [
scheme,
{
animationCount: allAnimationsByScheme[scheme].length,
sampleAnimations: allAnimationsByScheme[scheme].slice(0, 3)
}
])
)
}
// Save report
const reportsDir = path.join(PROJECT_ROOT, 'reports')
if (!fs.existsSync(reportsDir)) {
fs.mkdirSync(reportsDir, { recursive: true })
}
fs.writeFileSync(
path.join(reportsDir, 'naming-conflicts.json'),
JSON.stringify(report, null, 2),
'utf8'
)
// Print summary
console.log('\n📋 NAMING CONFLICT ANALYSIS SUMMARY')
console.log('='.repeat(50))
console.log(`Status: ${report.summary.status}`)
console.log(`Total Conflicts: ${conflicts.length}`)
console.log(`Total Warnings: ${warnings.length}`)
console.log(`Animations Analyzed: ${statistics.totalAnimations}`)
if (conflicts.length > 0) {
console.log('\n❌ CONFLICTS:')
conflicts.forEach((conflict, i) => {
console.log(`${i + 1}. ${conflict.type}: ${conflict.message}`)
})
}
if (warnings.length > 0 && warnings.length <= 10) {
console.log('\n⚠ WARNINGS:')
warnings.slice(0, 10).forEach((warning, i) => {
console.log(`${i + 1}. ${warning.type}: ${warning.message || warning.animationName}`)
})
if (warnings.length > 10) {
console.log(`... and ${warnings.length - 10} more warnings`)
}
}
console.log(`\n📁 Full report saved to: ${path.join(reportsDir, 'naming-conflicts.json')}`)
// Exit with error code if conflicts found
if (conflicts.length > 0) {
process.exit(1)
}
return report
} catch (error) {
console.error('❌ Error checking naming conflicts:', error.message)
process.exit(1)
}
}
/**
* Generate recommendations based on conflicts and warnings
*/
function generateRecommendations (conflicts, warnings) {
const recommendations = []
if (conflicts.some(c => c.type === 'duplicate_within_scheme')) {
recommendations.push({
type: 'fix_duplicates',
priority: 'high',
action: 'Remove duplicate animation names within each scheme',
description: 'Duplicate names can cause unpredictable behavior'
})
}
if (conflicts.some(c => c.type === 'cross_scheme_conflict')) {
recommendations.push({
type: 'resolve_cross_scheme',
priority: 'high',
action: 'Ensure names in different schemes map to the same semantic meaning',
description: 'Cross-scheme conflicts break the mapping system'
})
}
const conventionViolations = warnings.filter(w => w.type === 'convention_violation')
if (conventionViolations.length > 0) {
recommendations.push({
type: 'fix_conventions',
priority: 'medium',
action: `Fix ${conventionViolations.length} naming convention violations`,
description: 'Consistent naming conventions improve maintainability'
})
}
if (warnings.some(w => w.type === 'ambiguous_name')) {
recommendations.push({
type: 'clarify_ambiguous',
priority: 'low',
action: 'Review ambiguous animation names for clarity',
description: 'Ambiguous names can be confusing for developers'
})
}
return recommendations
}
// Run the script if called directly
if (process.argv[1] === __filename) {
checkNamingConflicts()
.then(report => {
console.log('✅ Naming conflict check complete!')
})
.catch(error => {
console.error('💥 Script failed:', error)
process.exit(1)
})
}