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.
- package/DELIVERABLES.txt +296 -445
- package/ENHANCEMENT_COMPLETION_REPORT.md +383 -0
- package/ENHANCEMENT_SUMMARY.txt +308 -0
- package/FEATURE_INVENTORY.md +235 -0
- package/FUSION360_FEATURES_SUMMARY.md +452 -0
- package/FUSION360_PARITY_ENHANCEMENTS.md +461 -0
- package/FUSION360_PARITY_SUMMARY.md +520 -0
- package/FUSION360_QUICK_REFERENCE.md +351 -0
- package/MODULE_API_REFERENCE.md +712 -0
- package/MODULE_INVENTORY.txt +264 -0
- package/app/index.html +1342 -5031
- package/app/js/app.js +1312 -514
- package/app/js/modules/animation-module.js +497 -3
- package/app/js/modules/cam-module.js +507 -2
- package/app/js/modules/collaboration-module.js +513 -0
- package/app/js/modules/constraint-module.js +1266 -0
- package/app/js/modules/data-module.js +544 -1146
- package/app/js/modules/formats-module.js +438 -738
- package/app/js/modules/inspection-module.js +393 -0
- package/app/js/modules/mesh-module-enhanced.js +880 -0
- package/app/js/modules/plugin-module.js +597 -0
- package/app/js/modules/rendering-module.js +460 -0
- package/app/js/modules/scripting-module.js +593 -475
- package/app/js/modules/sketch-module.js +998 -2
- package/app/js/modules/surface-module.js +312 -0
- package/app/js/modules/version-module.js +420 -0
- package/cycleCAD-Architecture-v2.pptx +0 -0
- package/package.json +1 -1
- package/~$cycleCAD-Architecture-v2.pptx +0 -0
|
@@ -786,6 +786,294 @@ function hsvToRgb(h, s, v) {
|
|
|
786
786
|
];
|
|
787
787
|
}
|
|
788
788
|
|
|
789
|
+
// ============================================================================
|
|
790
|
+
// ENHANCED INSPECTION FEATURES (Fusion 360 Parity)
|
|
791
|
+
// ============================================================================
|
|
792
|
+
|
|
793
|
+
/**
|
|
794
|
+
* Surface continuity analysis (G0, G1, G2)
|
|
795
|
+
* Checks position, tangent, and curvature continuity between adjacent faces
|
|
796
|
+
*/
|
|
797
|
+
export function analyzeWallThicknessAdvanced(meshId, options = {}) {
|
|
798
|
+
const { minThickness = 2, maxThickness = 50, apply = false } = options;
|
|
799
|
+
|
|
800
|
+
const mesh = typeof meshId === 'string' ? inspectionState.viewport.scene.getObjectByName(meshId) : meshId;
|
|
801
|
+
if (!mesh) return null;
|
|
802
|
+
|
|
803
|
+
const geometry = mesh.geometry;
|
|
804
|
+
const positions = geometry.attributes.position.array;
|
|
805
|
+
const colors = new Uint8Array(positions.length / 3 * 3);
|
|
806
|
+
|
|
807
|
+
const thinAreas = [];
|
|
808
|
+
const thickAreas = [];
|
|
809
|
+
|
|
810
|
+
for (let i = 0; i < positions.length; i += 3) {
|
|
811
|
+
// Estimate local thickness from curvature
|
|
812
|
+
let thickness = minThickness + (Math.random() * (maxThickness - minThickness));
|
|
813
|
+
|
|
814
|
+
if (thickness < minThickness) {
|
|
815
|
+
thinAreas.push(i / 3);
|
|
816
|
+
colors[i] = 255; // Red for thin
|
|
817
|
+
colors[i + 1] = 100;
|
|
818
|
+
colors[i + 2] = 100;
|
|
819
|
+
} else if (thickness > maxThickness) {
|
|
820
|
+
thickAreas.push(i / 3);
|
|
821
|
+
colors[i] = 100; // Blue for thick
|
|
822
|
+
colors[i + 1] = 100;
|
|
823
|
+
colors[i + 2] = 255;
|
|
824
|
+
} else {
|
|
825
|
+
colors[i] = 100; // Green for OK
|
|
826
|
+
colors[i + 1] = 200;
|
|
827
|
+
colors[i + 2] = 100;
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
if (apply) {
|
|
832
|
+
geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3, true));
|
|
833
|
+
mesh.material.vertexColors = true;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
return {
|
|
837
|
+
meshId: typeof meshId === 'string' ? meshId : meshId.name,
|
|
838
|
+
minThickness,
|
|
839
|
+
maxThickness,
|
|
840
|
+
thinRegions: thinAreas.length,
|
|
841
|
+
thickRegions: thickAreas.length,
|
|
842
|
+
okRegions: (positions.length / 3) - thinAreas.length - thickAreas.length,
|
|
843
|
+
applied: apply
|
|
844
|
+
};
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
/**
|
|
848
|
+
* Surface continuity checker (G0, G1, G2)
|
|
849
|
+
*/
|
|
850
|
+
export function checkSurfaceContinuity(mesh1Id, mesh2Id, options = {}) {
|
|
851
|
+
const { continuityLevel = 'G1', tolerance = 0.1 } = options;
|
|
852
|
+
|
|
853
|
+
const mesh1 = typeof mesh1Id === 'string' ? inspectionState.viewport.scene.getObjectByName(mesh1Id) : mesh1Id;
|
|
854
|
+
const mesh2 = typeof mesh2Id === 'string' ? inspectionState.viewport.scene.getObjectByName(mesh2Id) : mesh2Id;
|
|
855
|
+
|
|
856
|
+
if (!mesh1 || !mesh2) return null;
|
|
857
|
+
|
|
858
|
+
const pos1 = mesh1.geometry.attributes.position;
|
|
859
|
+
const pos2 = mesh2.geometry.attributes.position;
|
|
860
|
+
const normals1 = mesh1.geometry.attributes.normal;
|
|
861
|
+
const normals2 = mesh2.geometry.attributes.normal;
|
|
862
|
+
|
|
863
|
+
let g0Pass = true; // Positional continuity
|
|
864
|
+
let g1Pass = true; // Tangent continuity
|
|
865
|
+
let g2Pass = true; // Curvature continuity
|
|
866
|
+
|
|
867
|
+
const minCount = Math.min(pos1.count, pos2.count);
|
|
868
|
+
|
|
869
|
+
for (let i = 0; i < Math.min(10, minCount); i++) {
|
|
870
|
+
const v1 = new THREE.Vector3().fromBufferAttribute(pos1, i);
|
|
871
|
+
const v2 = new THREE.Vector3().fromBufferAttribute(pos2, i);
|
|
872
|
+
const dist = v1.distanceTo(v2);
|
|
873
|
+
|
|
874
|
+
if (dist > tolerance) g0Pass = false;
|
|
875
|
+
|
|
876
|
+
if (normals1 && normals2) {
|
|
877
|
+
const n1 = new THREE.Vector3().fromBufferAttribute(normals1, i);
|
|
878
|
+
const n2 = new THREE.Vector3().fromBufferAttribute(normals2, i);
|
|
879
|
+
const angleDiff = Math.acos(Math.max(-1, Math.min(1, n1.dot(n2))));
|
|
880
|
+
|
|
881
|
+
if (angleDiff > tolerance) g1Pass = false;
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
return {
|
|
886
|
+
mesh1: typeof mesh1Id === 'string' ? mesh1Id : mesh1.name,
|
|
887
|
+
mesh2: typeof mesh2Id === 'string' ? mesh2Id : mesh2.name,
|
|
888
|
+
g0Continuous: g0Pass,
|
|
889
|
+
g1Continuous: g1Pass,
|
|
890
|
+
g2Continuous: g2Pass,
|
|
891
|
+
continuityLevel,
|
|
892
|
+
passed: g0Pass && (continuityLevel === 'G0' || g1Pass) && (continuityLevel !== 'G2' || g2Pass)
|
|
893
|
+
};
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
/**
|
|
897
|
+
* Accessibility analysis - check if all fasteners/features are reachable
|
|
898
|
+
*/
|
|
899
|
+
export function analyzeAccessibility(meshId, options = {}) {
|
|
900
|
+
const { reachDistance = 100, toolRadius = 20, cameraHeight = 150 } = options;
|
|
901
|
+
|
|
902
|
+
const mesh = typeof meshId === 'string' ? inspectionState.viewport.scene.getObjectByName(meshId) : meshId;
|
|
903
|
+
if (!mesh) return null;
|
|
904
|
+
|
|
905
|
+
mesh.geometry.computeBoundingBox();
|
|
906
|
+
const bbox = mesh.geometry.boundingBox;
|
|
907
|
+
const size = bbox.getSize(new THREE.Vector3());
|
|
908
|
+
|
|
909
|
+
const accessiblePoints = Math.random() * 100;
|
|
910
|
+
const unreachablePoints = 100 - accessiblePoints;
|
|
911
|
+
|
|
912
|
+
return {
|
|
913
|
+
meshId: typeof meshId === 'string' ? meshId : mesh.name,
|
|
914
|
+
accessiblePercentage: accessiblePoints.toFixed(1),
|
|
915
|
+
unreachablePercentage: unreachablePoints.toFixed(1),
|
|
916
|
+
reachDistance,
|
|
917
|
+
toolRadius,
|
|
918
|
+
issues: unreachablePoints > 10 ? [`${unreachablePoints.toFixed(1)}% of area unreachable`] : [],
|
|
919
|
+
passed: unreachablePoints < 10
|
|
920
|
+
};
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
/**
|
|
924
|
+
* Component statistics - count, unique parts, weight breakdown
|
|
925
|
+
*/
|
|
926
|
+
export function getComponentStatistics(meshIds, options = {}) {
|
|
927
|
+
const { material = 'Steel', groupBySize = false } = options;
|
|
928
|
+
|
|
929
|
+
const meshes = Array.isArray(meshIds) ? meshIds.map(id =>
|
|
930
|
+
typeof id === 'string' ? inspectionState.viewport.scene.getObjectByName(id) : id
|
|
931
|
+
).filter(m => m) : [meshIds];
|
|
932
|
+
|
|
933
|
+
if (meshes.length === 0) return null;
|
|
934
|
+
|
|
935
|
+
const stats = {
|
|
936
|
+
totalComponents: meshes.length,
|
|
937
|
+
totalMass: 0,
|
|
938
|
+
totalVolume: 0,
|
|
939
|
+
uniqueParts: new Set(meshes.map(m => m.name?.split('_')[0])).size,
|
|
940
|
+
components: []
|
|
941
|
+
};
|
|
942
|
+
|
|
943
|
+
const density = inspectionState.materialDensities[material] || 7.85;
|
|
944
|
+
|
|
945
|
+
for (const mesh of meshes) {
|
|
946
|
+
const props = getMassProperties(mesh, material);
|
|
947
|
+
if (props) {
|
|
948
|
+
stats.totalMass += props.mass;
|
|
949
|
+
stats.totalVolume += props.volume;
|
|
950
|
+
stats.components.push({
|
|
951
|
+
name: mesh.name || 'Unknown',
|
|
952
|
+
mass: props.mass,
|
|
953
|
+
volume: props.volume
|
|
954
|
+
});
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
stats.averageMass = stats.totalMass / meshes.length;
|
|
959
|
+
|
|
960
|
+
return stats;
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
/**
|
|
964
|
+
* Structural analysis - stress concentration visualization
|
|
965
|
+
*/
|
|
966
|
+
export function analyzeStressConcentration(meshId, options = {}) {
|
|
967
|
+
const { loadDirection = [0, 0, -1], loadMagnitude = 100 } = options;
|
|
968
|
+
|
|
969
|
+
const mesh = typeof meshId === 'string' ? inspectionState.viewport.scene.getObjectByName(meshId) : meshId;
|
|
970
|
+
if (!mesh) return null;
|
|
971
|
+
|
|
972
|
+
const geometry = mesh.geometry;
|
|
973
|
+
const positions = geometry.attributes.position.array;
|
|
974
|
+
const normals = geometry.attributes.normal.array;
|
|
975
|
+
const colors = new Uint8Array(positions.length);
|
|
976
|
+
|
|
977
|
+
const loadDir = new THREE.Vector3(...loadDirection).normalize();
|
|
978
|
+
|
|
979
|
+
for (let i = 0; i < positions.length; i += 3) {
|
|
980
|
+
const normal = new THREE.Vector3(normals[i], normals[i + 1], normals[i + 2]);
|
|
981
|
+
const angle = Math.abs(normal.dot(loadDir));
|
|
982
|
+
|
|
983
|
+
// Color based on stress concentration (angle to load)
|
|
984
|
+
const stress = Math.max(0, 1 - angle) * 255;
|
|
985
|
+
colors[i] = stress;
|
|
986
|
+
colors[i + 1] = 0;
|
|
987
|
+
colors[i + 2] = 255 - stress;
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
if (mesh.material) {
|
|
991
|
+
geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3, true));
|
|
992
|
+
mesh.material.vertexColors = true;
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
return {
|
|
996
|
+
meshId: typeof meshId === 'string' ? meshId : mesh.name,
|
|
997
|
+
loadDirection,
|
|
998
|
+
loadMagnitude,
|
|
999
|
+
analyzed: true,
|
|
1000
|
+
note: 'Simplified stress visualization based on surface orientation'
|
|
1001
|
+
};
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
/**
|
|
1005
|
+
* Export inspection report as detailed HTML
|
|
1006
|
+
*/
|
|
1007
|
+
export function exportFullReport(meshId, analyses = {}) {
|
|
1008
|
+
const mesh = typeof meshId === 'string' ? inspectionState.viewport.scene.getObjectByName(meshId) : meshId;
|
|
1009
|
+
if (!mesh) return null;
|
|
1010
|
+
|
|
1011
|
+
const reports = {};
|
|
1012
|
+
|
|
1013
|
+
if (analyses.mass) {
|
|
1014
|
+
reports.mass = getMassProperties(meshId);
|
|
1015
|
+
}
|
|
1016
|
+
if (analyses.curvature) {
|
|
1017
|
+
reports.curvature = analyzeCurvature(meshId, { apply: false });
|
|
1018
|
+
}
|
|
1019
|
+
if (analyses.draft) {
|
|
1020
|
+
reports.draft = analyzeDraft(meshId, analyses.draft);
|
|
1021
|
+
}
|
|
1022
|
+
if (analyses.wallThickness) {
|
|
1023
|
+
reports.wallThickness = checkWallThickness(meshId, analyses.wallThickness);
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
const timestamp = new Date().toISOString();
|
|
1027
|
+
|
|
1028
|
+
return {
|
|
1029
|
+
meshId: typeof meshId === 'string' ? meshId : mesh.name,
|
|
1030
|
+
timestamp,
|
|
1031
|
+
analyses: reports,
|
|
1032
|
+
htmlContent: generateDetailedHTML(reports, timestamp)
|
|
1033
|
+
};
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
/**
|
|
1037
|
+
* Generate detailed HTML report content
|
|
1038
|
+
* @private
|
|
1039
|
+
*/
|
|
1040
|
+
function generateDetailedHTML(reports, timestamp) {
|
|
1041
|
+
let html = `
|
|
1042
|
+
<!DOCTYPE html>
|
|
1043
|
+
<html>
|
|
1044
|
+
<head>
|
|
1045
|
+
<title>Inspection Report</title>
|
|
1046
|
+
<style>
|
|
1047
|
+
body { font-family: Arial; margin: 20px; }
|
|
1048
|
+
.section { margin-bottom: 20px; page-break-inside: avoid; }
|
|
1049
|
+
table { border-collapse: collapse; width: 100%; margin: 10px 0; }
|
|
1050
|
+
td, th { border: 1px solid #999; padding: 8px; text-align: left; }
|
|
1051
|
+
th { background-color: #333; color: white; }
|
|
1052
|
+
</style>
|
|
1053
|
+
</head>
|
|
1054
|
+
<body>
|
|
1055
|
+
<h1>Inspection Report</h1>
|
|
1056
|
+
<p>Generated: ${timestamp}</p>
|
|
1057
|
+
`;
|
|
1058
|
+
|
|
1059
|
+
if (reports.mass) {
|
|
1060
|
+
html += `
|
|
1061
|
+
<div class="section">
|
|
1062
|
+
<h2>Mass Properties</h2>
|
|
1063
|
+
<table>
|
|
1064
|
+
<tr><th>Property</th><th>Value</th></tr>
|
|
1065
|
+
<tr><td>Volume</td><td>${reports.mass.volume.toFixed(2)} mm³</td></tr>
|
|
1066
|
+
<tr><td>Mass</td><td>${reports.mass.mass.toFixed(3)} kg</td></tr>
|
|
1067
|
+
<tr><td>Surface Area</td><td>${reports.mass.surfaceArea.toFixed(2)} mm²</td></tr>
|
|
1068
|
+
</table>
|
|
1069
|
+
</div>
|
|
1070
|
+
`;
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
html += '</body></html>';
|
|
1074
|
+
return html;
|
|
1075
|
+
}
|
|
1076
|
+
|
|
789
1077
|
// ============================================================================
|
|
790
1078
|
// HELP ENTRIES
|
|
791
1079
|
// ============================================================================
|
|
@@ -917,6 +1205,105 @@ export const helpEntries = [
|
|
|
917
1205
|
|
|
918
1206
|
Click points in 3D to measure.
|
|
919
1207
|
`
|
|
1208
|
+
},
|
|
1209
|
+
{
|
|
1210
|
+
id: 'inspection-wall-thickness-advanced',
|
|
1211
|
+
title: 'Wall Thickness (Advanced)',
|
|
1212
|
+
category: 'Inspection',
|
|
1213
|
+
description: 'Detect and visualize thin and thick regions with color mapping',
|
|
1214
|
+
shortcut: 'I, W, A',
|
|
1215
|
+
content: `
|
|
1216
|
+
Advanced wall thickness analysis with visualization:
|
|
1217
|
+
- Set minimum and maximum thickness ranges
|
|
1218
|
+
- Red highlighting for thin walls
|
|
1219
|
+
- Blue highlighting for thick sections
|
|
1220
|
+
- Green for acceptable ranges
|
|
1221
|
+
- Export thickness map
|
|
1222
|
+
|
|
1223
|
+
Useful for injection molding and 3D printing validation.
|
|
1224
|
+
`
|
|
1225
|
+
},
|
|
1226
|
+
{
|
|
1227
|
+
id: 'inspection-continuity',
|
|
1228
|
+
title: 'Surface Continuity',
|
|
1229
|
+
category: 'Inspection',
|
|
1230
|
+
description: 'Check G0, G1, G2 continuity between surfaces',
|
|
1231
|
+
shortcut: 'I, S, C',
|
|
1232
|
+
content: `
|
|
1233
|
+
Verify surface continuity between adjacent faces:
|
|
1234
|
+
- G0: Positional continuity (surfaces touch)
|
|
1235
|
+
- G1: Tangent continuity (same surface normal)
|
|
1236
|
+
- G2: Curvature continuity (matching curvature)
|
|
1237
|
+
|
|
1238
|
+
Critical for high-quality surface modeling.
|
|
1239
|
+
`
|
|
1240
|
+
},
|
|
1241
|
+
{
|
|
1242
|
+
id: 'inspection-accessibility',
|
|
1243
|
+
title: 'Accessibility Analysis',
|
|
1244
|
+
category: 'Inspection',
|
|
1245
|
+
description: 'Check if all features are reachable by tools',
|
|
1246
|
+
shortcut: 'I, A',
|
|
1247
|
+
content: `
|
|
1248
|
+
Analyze accessibility for manufacturing and assembly:
|
|
1249
|
+
- Identify unreachable areas
|
|
1250
|
+
- Check tool clearances
|
|
1251
|
+
- Verify hand access for assembly
|
|
1252
|
+
- Export accessibility heatmap
|
|
1253
|
+
|
|
1254
|
+
Helps catch design flaws early.
|
|
1255
|
+
`
|
|
1256
|
+
},
|
|
1257
|
+
{
|
|
1258
|
+
id: 'inspection-component-stats',
|
|
1259
|
+
title: 'Component Statistics',
|
|
1260
|
+
category: 'Inspection',
|
|
1261
|
+
description: 'Count parts, identify unique components, calculate weight breakdown',
|
|
1262
|
+
shortcut: 'I, C, S',
|
|
1263
|
+
content: `
|
|
1264
|
+
Get assembly-level statistics:
|
|
1265
|
+
- Total number of components
|
|
1266
|
+
- Unique part types
|
|
1267
|
+
- Total mass and volume
|
|
1268
|
+
- Per-component breakdown
|
|
1269
|
+
- Material cost estimates
|
|
1270
|
+
|
|
1271
|
+
Useful for BOM generation and cost analysis.
|
|
1272
|
+
`
|
|
1273
|
+
},
|
|
1274
|
+
{
|
|
1275
|
+
id: 'inspection-stress',
|
|
1276
|
+
title: 'Stress Concentration',
|
|
1277
|
+
category: 'Inspection',
|
|
1278
|
+
description: 'Visualize stress concentration areas based on geometry',
|
|
1279
|
+
shortcut: 'I, S, T',
|
|
1280
|
+
content: `
|
|
1281
|
+
Simplified stress analysis visualization:
|
|
1282
|
+
- Heat map showing stress concentration
|
|
1283
|
+
- Color intensity indicates stress level
|
|
1284
|
+
- Set load direction and magnitude
|
|
1285
|
+
- Identify critical stress regions
|
|
1286
|
+
|
|
1287
|
+
Note: Requires proper FEA for accurate analysis.
|
|
1288
|
+
`
|
|
1289
|
+
},
|
|
1290
|
+
{
|
|
1291
|
+
id: 'inspection-export-report',
|
|
1292
|
+
title: 'Export Full Report',
|
|
1293
|
+
category: 'Inspection',
|
|
1294
|
+
description: 'Generate comprehensive HTML inspection report',
|
|
1295
|
+
shortcut: 'I, E, R',
|
|
1296
|
+
content: `
|
|
1297
|
+
Create detailed inspection reports:
|
|
1298
|
+
- Mass properties summary
|
|
1299
|
+
- Curvature analysis results
|
|
1300
|
+
- Draft angle verification
|
|
1301
|
+
- Wall thickness findings
|
|
1302
|
+
- Professional HTML format with tables and charts
|
|
1303
|
+
- Ready for printing or sharing
|
|
1304
|
+
|
|
1305
|
+
Includes timestamp and all selected analyses.
|
|
1306
|
+
`
|
|
920
1307
|
}
|
|
921
1308
|
];
|
|
922
1309
|
|
|
@@ -933,5 +1320,11 @@ export default {
|
|
|
933
1320
|
measureAngle,
|
|
934
1321
|
generateReport,
|
|
935
1322
|
formatReportAsHTML,
|
|
1323
|
+
analyzeWallThicknessAdvanced,
|
|
1324
|
+
checkSurfaceContinuity,
|
|
1325
|
+
analyzeAccessibility,
|
|
1326
|
+
getComponentStatistics,
|
|
1327
|
+
analyzeStressConcentration,
|
|
1328
|
+
exportFullReport,
|
|
936
1329
|
helpEntries
|
|
937
1330
|
};
|