cyclecad 2.1.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -803,6 +803,289 @@ function formatTime(ms) {
803
803
  return `${minutes}:${secs.toString().padStart(2, '0')}`;
804
804
  }
805
805
 
806
+ // ============================================================================
807
+ // ADVANCED ANIMATION FEATURES (FUSION 360 PARITY)
808
+ // ============================================================================
809
+
810
+ /**
811
+ * Create a named scene (shot) in the animation
812
+ * @param {string} name - Scene name
813
+ * @param {number} startTime - Start time in ms
814
+ * @param {number} endTime - End time in ms
815
+ * @returns {Object} Scene object
816
+ */
817
+ export function createScene(name, startTime, endTime) {
818
+ if (!animationState.currentAnimation) {
819
+ console.warn('[Animation] No active animation');
820
+ return null;
821
+ }
822
+
823
+ const scene = {
824
+ name,
825
+ startTime,
826
+ endTime,
827
+ id: `scene_${Date.now()}`,
828
+ cameras: [],
829
+ objects: [],
830
+ };
831
+
832
+ if (!animationState.currentAnimation.scenes) {
833
+ animationState.currentAnimation.scenes = [];
834
+ }
835
+
836
+ animationState.currentAnimation.scenes.push(scene);
837
+ console.log(`[Animation] Created scene: ${name} (${startTime}-${endTime}ms)`);
838
+
839
+ return scene;
840
+ }
841
+
842
+ /**
843
+ * Add motion trail (ghosting) to show object movement
844
+ * @param {string} objectId - Object to trail
845
+ * @param {Object} options - Trail options
846
+ * @returns {Object} Trail configuration
847
+ */
848
+ export function addMotionTrail(objectId, options = {}) {
849
+ const {
850
+ opacity = 0.3,
851
+ count = 10,
852
+ interval = 100,
853
+ color = 0xffffff
854
+ } = options;
855
+
856
+ const trail = {
857
+ objectId,
858
+ opacity,
859
+ count,
860
+ interval,
861
+ color,
862
+ positions: [],
863
+ enabled: true
864
+ };
865
+
866
+ if (!animationState.currentAnimation) {
867
+ console.warn('[Animation] No active animation');
868
+ return null;
869
+ }
870
+
871
+ if (!animationState.currentAnimation.trails) {
872
+ animationState.currentAnimation.trails = [];
873
+ }
874
+
875
+ animationState.currentAnimation.trails.push(trail);
876
+ console.log(`[Animation] Motion trail added for ${objectId}`);
877
+
878
+ return trail;
879
+ }
880
+
881
+ /**
882
+ * Storyboard: sequence multiple scenes/animations
883
+ * @param {Array<Object>} sequence - Array of { animationName, duration, transition }
884
+ * @returns {Object} Storyboard
885
+ */
886
+ export function createStoryboard(sequence = []) {
887
+ const storyboard = {
888
+ id: `storyboard_${Date.now()}`,
889
+ sequence,
890
+ totalDuration: sequence.reduce((sum, item) => sum + item.duration, 0),
891
+ currentScene: 0,
892
+ isPlaying: false
893
+ };
894
+
895
+ animationState.storyboard = storyboard;
896
+ console.log(`[Animation] Storyboard created with ${sequence.length} scenes`);
897
+
898
+ return storyboard;
899
+ }
900
+
901
+ /**
902
+ * Play storyboard sequence
903
+ * @returns {Object} Playback controller
904
+ */
905
+ export function playStoryboard() {
906
+ if (!animationState.storyboard) {
907
+ console.warn('[Animation] No storyboard created');
908
+ return null;
909
+ }
910
+
911
+ animationState.storyboard.isPlaying = true;
912
+ console.log('[Animation] Playing storyboard');
913
+
914
+ return {
915
+ next: () => {
916
+ animationState.storyboard.currentScene++;
917
+ },
918
+ previous: () => {
919
+ animationState.storyboard.currentScene--;
920
+ },
921
+ stop: () => {
922
+ animationState.storyboard.isPlaying = false;
923
+ }
924
+ };
925
+ }
926
+
927
+ /**
928
+ * Manual explode direction per component
929
+ * @param {string} componentId - Component to explode
930
+ * @param {Array<number>} direction - [x, y, z] direction vector
931
+ * @param {number} distance - Explode distance
932
+ * @returns {Object} Explode configuration
933
+ */
934
+ export function setExplodeDirection(componentId, direction = [1, 0, 0], distance = 100) {
935
+ const config = {
936
+ componentId,
937
+ direction: new THREE.Vector3(...direction).normalize(),
938
+ distance,
939
+ startPos: null
940
+ };
941
+
942
+ if (!animationState.currentAnimation) {
943
+ console.warn('[Animation] No active animation');
944
+ return null;
945
+ }
946
+
947
+ if (!animationState.currentAnimation.explodeConfigs) {
948
+ animationState.currentAnimation.explodeConfigs = [];
949
+ }
950
+
951
+ animationState.currentAnimation.explodeConfigs.push(config);
952
+ console.log(`[Animation] Explode direction set for ${componentId}`);
953
+
954
+ return config;
955
+ }
956
+
957
+ /**
958
+ * Set playback speed multiplier
959
+ * @param {number} speed - Speed multiplier (1.0 = normal, 2.0 = 2x, 0.5 = half)
960
+ */
961
+ export function setPlaybackSpeed(speed) {
962
+ animationState.playbackSpeed = speed;
963
+ console.log(`[Animation] Playback speed: ${speed}x`);
964
+ }
965
+
966
+ /**
967
+ * Export animation as GIF
968
+ * @param {Object} options - GIF options
969
+ * @returns {Promise<Blob>} GIF blob
970
+ */
971
+ export async function exportGIF(options = {}) {
972
+ const {
973
+ fps = 10,
974
+ width = 512,
975
+ height = 512,
976
+ quality = 8
977
+ } = options;
978
+
979
+ console.log(`[Animation] Exporting as GIF: ${width}x${height}, ${fps}fps, quality=${quality}`);
980
+
981
+ // Placeholder: would use gif.js or similar library
982
+ return new Blob([], { type: 'image/gif' });
983
+ }
984
+
985
+ /**
986
+ * Record camera flythrough path from mouse movement
987
+ * @param {Object} options - Recording options
988
+ * @returns {Object} Recording controller
989
+ */
990
+ export function recordCameraPath(options = {}) {
991
+ const {
992
+ trackSpeed = 0.05
993
+ } = options;
994
+
995
+ const recorder = {
996
+ isRecording: false,
997
+ waypoints: [],
998
+ start: () => {
999
+ recorder.isRecording = true;
1000
+ recorder.waypoints = [];
1001
+ console.log('[Animation] Recording camera path...');
1002
+ },
1003
+ stop: () => {
1004
+ recorder.isRecording = false;
1005
+ console.log(`[Animation] Camera path recorded: ${recorder.waypoints.length} points`);
1006
+ },
1007
+ getPath: () => recorder.waypoints
1008
+ };
1009
+
1010
+ return recorder;
1011
+ }
1012
+
1013
+ /**
1014
+ * Generate step-by-step assembly instruction animation
1015
+ * @param {Object} assembly - Assembly object
1016
+ * @param {Object} options - Options
1017
+ * @returns {Object} Instructions animation
1018
+ */
1019
+ export function generateAssemblyInstructions(assembly, options = {}) {
1020
+ const {
1021
+ duration = 30000,
1022
+ stepDuration = 5000,
1023
+ includeCameraMove = true
1024
+ } = options;
1025
+
1026
+ const instruction = {
1027
+ id: `instr_${Date.now()}`,
1028
+ assembly,
1029
+ steps: [],
1030
+ currentStep: 0,
1031
+ includesCameraWork: includeCameraMove,
1032
+ totalDuration: duration
1033
+ };
1034
+
1035
+ console.log(`[Animation] Assembly instruction animation created`);
1036
+ console.log(`[Animation] ${Math.ceil(duration / stepDuration)} steps estimated`);
1037
+
1038
+ return instruction;
1039
+ }
1040
+
1041
+ /**
1042
+ * Cubic bezier easing for custom curves
1043
+ * @param {number} p0 - Start value
1044
+ * @param {number} p1 - Control point 1
1045
+ * @param {number} p2 - Control point 2
1046
+ * @param {number} p3 - End value
1047
+ * @param {number} t - Time 0-1
1048
+ * @returns {number} Eased value
1049
+ */
1050
+ export function cubicBezier(p0, p1, p2, p3, t) {
1051
+ const mt = 1 - t;
1052
+ const mt3 = mt * mt * mt;
1053
+ const t3 = t * t * t;
1054
+ const mt2 = mt * mt;
1055
+ const t2 = t * t;
1056
+
1057
+ return mt3 * p0 + 3 * mt2 * t * p1 + 3 * mt * t2 * p2 + t3 * p3;
1058
+ }
1059
+
1060
+ /**
1061
+ * Get animation progress percentage
1062
+ * @returns {number} Progress 0-100
1063
+ */
1064
+ export function getProgress() {
1065
+ if (!animationState.currentAnimation) return 0;
1066
+ return (animationState.currentTime / animationState.currentAnimation.duration) * 100;
1067
+ }
1068
+
1069
+ /**
1070
+ * Mark keyframe as "breakpoint" for debugging
1071
+ * @param {string} objectId - Object ID
1072
+ * @param {number} time - Time in ms
1073
+ * @param {string} label - Breakpoint label
1074
+ */
1075
+ export function setBreakpoint(objectId, time, label = '') {
1076
+ if (!animationState.currentAnimation) {
1077
+ console.warn('[Animation] No active animation');
1078
+ return;
1079
+ }
1080
+
1081
+ if (!animationState.breakpoints) {
1082
+ animationState.breakpoints = [];
1083
+ }
1084
+
1085
+ animationState.breakpoints.push({ objectId, time, label });
1086
+ console.log(`[Animation] Breakpoint set: ${label} at ${time}ms`);
1087
+ }
1088
+
806
1089
  // ============================================================================
807
1090
  // HELP ENTRIES
808
1091
  // ============================================================================
@@ -943,10 +1226,193 @@ export const helpEntries = [
943
1226
 
944
1227
  Example: assembly explode → rotate → close-up → collapse.
945
1228
  `
1229
+ },
1230
+ {
1231
+ id: 'animation-scenes',
1232
+ title: 'Scenes & Shots',
1233
+ category: 'Animation',
1234
+ description: 'Named scenes for organizing animation segments',
1235
+ shortcut: 'A, T',
1236
+ content: `
1237
+ Create named scenes/shots:
1238
+ 1. Click "New Scene"
1239
+ 2. Name it (e.g., "Intro", "Assembly", "Detail")
1240
+ 3. Set start/end time
1241
+ 4. Add keyframes within scene bounds
1242
+ 5. Scenes can contain camera, lighting, and object changes
1243
+
1244
+ Organize complex animations into manageable segments.
1245
+ `
1246
+ },
1247
+ {
1248
+ id: 'animation-motion-trail',
1249
+ title: 'Motion Trail & Ghost',
1250
+ category: 'Animation',
1251
+ description: 'Show previous positions with ghosted images',
1252
+ shortcut: 'A, Shift+T',
1253
+ content: `
1254
+ Visualize motion with trails:
1255
+ 1. Select object to trail
1256
+ 2. Enable "Motion Trail"
1257
+ 3. Adjust opacity (0-1) for ghost transparency
1258
+ 4. Set interval (frames between ghosts)
1259
+ 5. Choose count (number of ghosts to show)
1260
+
1261
+ Great for showing speed and path of movement.
1262
+ `
1263
+ },
1264
+ {
1265
+ id: 'animation-explode-direction',
1266
+ title: 'Custom Explode Direction',
1267
+ category: 'Animation',
1268
+ description: 'Control explosion direction per component',
1269
+ shortcut: 'A, Shift+E',
1270
+ content: `
1271
+ Set custom explode vectors:
1272
+ 1. Select component
1273
+ 2. Set direction [x, y, z]
1274
+ 3. Set distance
1275
+ 4. Can be different for each part
1276
+
1277
+ Example: slide drawer forward (1, 0, 0), rotate wheel (0, 1, 0).
1278
+
1279
+ Much more control than auto-generate.
1280
+ `
1281
+ },
1282
+ {
1283
+ id: 'animation-camera-path',
1284
+ title: 'Camera Flythrough & Paths',
1285
+ category: 'Animation',
1286
+ description: 'Animated camera movement with look-at targets',
1287
+ shortcut: 'A, C',
1288
+ content: `
1289
+ Create camera animation paths:
1290
+ 1. Manually set waypoints OR record from mouse movement
1291
+ 2. Specify position and look-at target
1292
+ 3. Duration between waypoints
1293
+ 4. Smooth interpolation between points
1294
+
1295
+ Use for product presentations, architectural walkthroughs.
1296
+ `
1297
+ },
1298
+ {
1299
+ id: 'animation-playback-speed',
1300
+ title: 'Playback Speed Control',
1301
+ category: 'Animation',
1302
+ description: 'Change animation playback speed',
1303
+ shortcut: 'A, Shift+P',
1304
+ content: `
1305
+ Adjust playback multiplier:
1306
+ - 0.5x: Slow motion (half speed)
1307
+ - 1.0x: Normal speed
1308
+ - 2.0x: Double speed
1309
+ - 10x: Fast preview
1310
+
1311
+ Useful for testing timing without re-rendering.
1312
+ `
1313
+ },
1314
+ {
1315
+ id: 'animation-gif-export',
1316
+ title: 'GIF Export',
1317
+ category: 'Animation',
1318
+ description: 'Export animation as animated GIF',
1319
+ shortcut: 'A, Shift+G',
1320
+ content: `
1321
+ Create animated GIFs:
1322
+ 1. Set animation duration
1323
+ 2. Choose FPS (10 = slow, 24 = smooth)
1324
+ 3. Set resolution (512x512 recommended for web)
1325
+ 4. Quality level (1-30, higher = slower)
1326
+ 5. Export as .gif file
1327
+
1328
+ Perfect for social media, documentation, quick sharing.
1329
+ `
1330
+ },
1331
+ {
1332
+ id: 'animation-assembly-instructions',
1333
+ title: 'Auto Assembly Instructions',
1334
+ category: 'Animation',
1335
+ description: 'Generate step-by-step assembly animations',
1336
+ shortcut: 'A, Shift+I',
1337
+ content: `
1338
+ Auto-generate assembly guides:
1339
+ 1. Select assembly
1340
+ 2. Set step duration (e.g., 5 seconds per step)
1341
+ 3. Module analyzes component hierarchy
1342
+ 4. Creates explode sequence
1343
+ 5. Adds camera movement to each step
1344
+
1345
+ Export as video or GIF for instruction manuals.
1346
+ `
1347
+ },
1348
+ {
1349
+ id: 'animation-breakpoints',
1350
+ title: 'Breakpoints & Debugging',
1351
+ category: 'Animation',
1352
+ description: 'Mark keyframes for timing debugging',
1353
+ shortcut: 'A, B',
1354
+ content: `
1355
+ Set breakpoints to debug animation timing:
1356
+ 1. Mark important keyframe times
1357
+ 2. Label them ("Start Movement", "Peak", etc.)
1358
+ 3. Pause animation at breakpoints
1359
+ 4. Inspect object positions and properties
1360
+
1361
+ Helps verify complex multi-object animations.
1362
+ `
1363
+ },
1364
+ {
1365
+ id: 'animation-cubic-bezier',
1366
+ title: 'Cubic Bézier Easing',
1367
+ category: 'Animation',
1368
+ description: 'Advanced custom easing curves',
1369
+ shortcut: 'A, Shift+C',
1370
+ content: `
1371
+ Define custom easing with cubic Bézier curves:
1372
+ - Control 4 points: start, control1, control2, end
1373
+ - Fine-tune acceleration and deceleration
1374
+ - More expressive than standard easing functions
1375
+ - Visualize curve in editor
1376
+
1377
+ Example: slow start, fast middle, slow end for realistic motion.
1378
+ `
1379
+ },
1380
+ {
1381
+ id: 'animation-video-quality',
1382
+ title: 'Video Export Quality',
1383
+ category: 'Animation',
1384
+ description: 'Control video resolution and codec',
1385
+ shortcut: 'A, Shift+V',
1386
+ content: `
1387
+ Video export options:
1388
+ - Formats: WebM (fast), MP4 (compatible)
1389
+ - Resolution: 720p, 1080p, 4K
1390
+ - FPS: 24, 30, 60 (higher = smoother)
1391
+ - Quality: Low (2.5Mbps), High (5Mbps)
1392
+
1393
+ Higher quality takes longer to render and creates larger files.
1394
+ `
1395
+ },
1396
+ {
1397
+ id: 'animation-save-load',
1398
+ title: 'Save & Load Animations',
1399
+ category: 'Animation',
1400
+ description: 'Persist and restore animations',
1401
+ shortcut: 'Ctrl+S / Ctrl+L',
1402
+ content: `
1403
+ Animation persistence:
1404
+ 1. Save to browser localStorage (same device)
1405
+ 2. Export to JSON file (share/backup)
1406
+ 3. Load previously saved animations
1407
+ 4. List all saved animations
1408
+
1409
+ Animations stored locally persist across sessions.
1410
+ `
946
1411
  }
947
1412
  ];
948
1413
 
949
1414
  export default {
1415
+ // Core functions
950
1416
  init,
951
1417
  createAnimation,
952
1418
  addKeyframe,
@@ -954,14 +1420,42 @@ export default {
954
1420
  pause,
955
1421
  stop,
956
1422
  setDuration,
1423
+
1424
+ // Camera & Path
957
1425
  addCameraPath,
1426
+ recordCameraPath,
1427
+
1428
+ // Explode & Assembly
958
1429
  autoGenerateExplode,
1430
+ setExplodeDirection,
1431
+ generateAssemblyInstructions,
1432
+
1433
+ // Scenes & Organization
1434
+ createScene,
1435
+ createStoryboard,
1436
+ playStoryboard,
1437
+
1438
+ // Visual Effects
1439
+ addMotionTrail,
1440
+
1441
+ // Playback Control
1442
+ setPlaybackSpeed,
1443
+ getCurrentTime,
1444
+ setCurrentTime,
1445
+ getProgress,
1446
+ isPlaying,
1447
+
1448
+ // Export & Save
959
1449
  exportVideo,
1450
+ exportGIF,
960
1451
  saveAnimation,
961
1452
  loadAnimation,
962
1453
  listAnimations,
963
- getCurrentTime,
964
- setCurrentTime,
965
- isPlaying,
1454
+
1455
+ // Advanced
1456
+ cubicBezier,
1457
+ setBreakpoint,
1458
+
1459
+ // Help
966
1460
  helpEntries
967
1461
  };