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
|
@@ -706,6 +706,286 @@ const SurfaceModule = (() => {
|
|
|
706
706
|
return ui;
|
|
707
707
|
}
|
|
708
708
|
|
|
709
|
+
/**
|
|
710
|
+
* Freeform T-spline sculpting - push/pull vertices in real-time
|
|
711
|
+
*/
|
|
712
|
+
async function sculptTSpline(surfaceId, options = {}) {
|
|
713
|
+
const { mode = 'push', radius = 10, strength = 1.0 } = options;
|
|
714
|
+
const surface = surfaces.get(surfaceId);
|
|
715
|
+
if (!surface) throw new Error(`Surface ${surfaceId} not found`);
|
|
716
|
+
|
|
717
|
+
const id = `surface_sculpt_${surfaceCounter.count++}`;
|
|
718
|
+
surfaces.set(id, { type: 'sculpted_surface', parent: surfaceId, mesh: surface.mesh?.clone(), mode, createdAt: Date.now() });
|
|
719
|
+
return { id, type: 'sculpted_surface', mode, radius, strength };
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
/**
|
|
723
|
+
* Surface extension - extend edge naturally, linearly, or circularly
|
|
724
|
+
*/
|
|
725
|
+
async function extendSurfaceAdvanced(surfaceId, edgeIndex, distance, extensionType = 'natural') {
|
|
726
|
+
const surface = surfaces.get(surfaceId);
|
|
727
|
+
if (!surface) throw new Error(`Surface ${surfaceId} not found`);
|
|
728
|
+
|
|
729
|
+
const id = `surface_extend_${extensionType}_${surfaceCounter.count++}`;
|
|
730
|
+
|
|
731
|
+
if (surfaceManager.kernel) {
|
|
732
|
+
try {
|
|
733
|
+
const result = await surfaceManager.execBrep('extendSurfaceAdvanced', { surfaceId, edgeIndex, distance, extensionType });
|
|
734
|
+
if (result) {
|
|
735
|
+
surfaces.set(id, { type: 'extended_surface', brep: result, parent: surfaceId, extensionType, mesh: brepToMesh(result) });
|
|
736
|
+
return { id, type: 'extended_surface', extensionType, distance };
|
|
737
|
+
}
|
|
738
|
+
} catch (e) {
|
|
739
|
+
console.warn('[Surface] B-Rep extend advanced failed:', e.message);
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
const extendedMesh = surface.mesh?.clone();
|
|
744
|
+
surfaces.set(id, { type: 'extended_surface', mesh: extendedMesh, extensionType });
|
|
745
|
+
return { id, type: 'extended_surface', extensionType, distance };
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
/**
|
|
749
|
+
* Curvature analysis with color mapping - Gaussian, mean, or principal
|
|
750
|
+
*/
|
|
751
|
+
async function analyzeCurvature(surfaceId, options = {}) {
|
|
752
|
+
const { type = 'mean', colorMap = 'heatmap', apply = true } = options;
|
|
753
|
+
const surface = surfaces.get(surfaceId);
|
|
754
|
+
if (!surface) throw new Error(`Surface ${surfaceId} not found`);
|
|
755
|
+
|
|
756
|
+
const mesh = surface.mesh;
|
|
757
|
+
if (!mesh || !mesh.geometry) return { surfaceId, type, colorMap, analysis: 'No geometry' };
|
|
758
|
+
|
|
759
|
+
const geometry = mesh.geometry;
|
|
760
|
+
const normals = geometry.attributes.normal;
|
|
761
|
+
const positions = geometry.attributes.position;
|
|
762
|
+
|
|
763
|
+
if (!normals || !positions) return { surfaceId, analysis: 'Missing normals' };
|
|
764
|
+
|
|
765
|
+
// Compute curvature per vertex
|
|
766
|
+
const curvatures = new Float32Array(positions.count);
|
|
767
|
+
const colors = new Uint8Array(positions.count * 3);
|
|
768
|
+
|
|
769
|
+
for (let i = 0; i < positions.count; i++) {
|
|
770
|
+
const n = new THREE.Vector3().fromBufferAttribute(normals, i);
|
|
771
|
+
let curvature = Math.abs(n.x + n.y + n.z) / 3; // Simplified
|
|
772
|
+
curvatures[i] = curvature;
|
|
773
|
+
|
|
774
|
+
const hue = (1 - curvature) * 240;
|
|
775
|
+
const rgb = hsvToRgb(hue, 1, 0.8);
|
|
776
|
+
colors[i * 3] = rgb[0];
|
|
777
|
+
colors[i * 3 + 1] = rgb[1];
|
|
778
|
+
colors[i * 3 + 2] = rgb[2];
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
if (apply) {
|
|
782
|
+
geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3, true));
|
|
783
|
+
mesh.material.vertexColors = true;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
return { surfaceId, type, colorMap, curvatures, analysis: 'Curvature computed' };
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
/**
|
|
790
|
+
* Zebra stripes - continuity analysis visualization
|
|
791
|
+
*/
|
|
792
|
+
async function zebraStripes(surfaceId, options = {}) {
|
|
793
|
+
const { stripeWidth = 0.5, direction = 'u', apply = true } = options;
|
|
794
|
+
const surface = surfaces.get(surfaceId);
|
|
795
|
+
if (!surface) throw new Error(`Surface ${surfaceId} not found`);
|
|
796
|
+
|
|
797
|
+
const mesh = surface.mesh;
|
|
798
|
+
if (!mesh || !mesh.geometry) return null;
|
|
799
|
+
|
|
800
|
+
const geometry = mesh.geometry;
|
|
801
|
+
const positions = geometry.attributes.position;
|
|
802
|
+
const colors = new Uint8Array(positions.count * 3);
|
|
803
|
+
|
|
804
|
+
for (let i = 0; i < positions.count; i++) {
|
|
805
|
+
const pos = new THREE.Vector3().fromBufferAttribute(positions, i);
|
|
806
|
+
const coord = direction === 'u' ? pos.x : pos.y;
|
|
807
|
+
const stripe = Math.floor(coord / stripeWidth) % 2;
|
|
808
|
+
const color = stripe === 0 ? 255 : 200;
|
|
809
|
+
colors[i * 3] = color;
|
|
810
|
+
colors[i * 3 + 1] = color;
|
|
811
|
+
colors[i * 3 + 2] = color;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
if (apply) {
|
|
815
|
+
geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3, true));
|
|
816
|
+
mesh.material.vertexColors = true;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
return { surfaceId, stripeWidth, direction, applied: true };
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
/**
|
|
823
|
+
* Draft analysis - check if surface can be pulled from mold
|
|
824
|
+
*/
|
|
825
|
+
async function draftAnalysis(surfaceId, options = {}) {
|
|
826
|
+
const { pullDirection = new THREE.Vector3(0, 0, 1), minAngle = 2 } = options;
|
|
827
|
+
const surface = surfaces.get(surfaceId);
|
|
828
|
+
if (!surface) throw new Error(`Surface ${surfaceId} not found`);
|
|
829
|
+
|
|
830
|
+
const mesh = surface.mesh;
|
|
831
|
+
if (!mesh || !mesh.geometry) return null;
|
|
832
|
+
|
|
833
|
+
const geometry = mesh.geometry;
|
|
834
|
+
const normals = geometry.attributes.normal;
|
|
835
|
+
const minAngleRad = (minAngle * Math.PI) / 180;
|
|
836
|
+
|
|
837
|
+
let passCount = 0, failCount = 0;
|
|
838
|
+
const problemAreas = [];
|
|
839
|
+
|
|
840
|
+
for (let i = 0; i < normals.count; i++) {
|
|
841
|
+
const normal = new THREE.Vector3().fromBufferAttribute(normals, i);
|
|
842
|
+
const angle = Math.acos(Math.abs(normal.dot(pullDirection.normalize())));
|
|
843
|
+
|
|
844
|
+
if (angle >= minAngleRad) {
|
|
845
|
+
passCount++;
|
|
846
|
+
} else {
|
|
847
|
+
failCount++;
|
|
848
|
+
const pos = new THREE.Vector3().fromBufferAttribute(geometry.attributes.position, i);
|
|
849
|
+
problemAreas.push({ position: pos, angle: (angle * 180) / Math.PI });
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
return {
|
|
854
|
+
surfaceId,
|
|
855
|
+
pullDirection: { x: pullDirection.x, y: pullDirection.y, z: pullDirection.z },
|
|
856
|
+
minAngle,
|
|
857
|
+
passPercentage: (passCount / (passCount + failCount)) * 100,
|
|
858
|
+
problemAreas,
|
|
859
|
+
passed: failCount === 0
|
|
860
|
+
};
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
/**
|
|
864
|
+
* Isocurve display - show parametric curves on surface
|
|
865
|
+
*/
|
|
866
|
+
async function showIsocurves(surfaceId, options = {}) {
|
|
867
|
+
const { uCount = 10, vCount = 10, color = 0x00ff00 } = options;
|
|
868
|
+
const surface = surfaces.get(surfaceId);
|
|
869
|
+
if (!surface) throw new Error(`Surface ${surfaceId} not found`);
|
|
870
|
+
|
|
871
|
+
const curves = [];
|
|
872
|
+
for (let i = 0; i < uCount; i++) {
|
|
873
|
+
curves.push({ type: 'u', parameter: i / uCount, color });
|
|
874
|
+
}
|
|
875
|
+
for (let i = 0; i < vCount; i++) {
|
|
876
|
+
curves.push({ type: 'v', parameter: i / vCount, color });
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
return { surfaceId, isocurves: curves, uCount, vCount, visible: true };
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
/**
|
|
883
|
+
* Unstitch surfaces - break joined surfaces apart
|
|
884
|
+
*/
|
|
885
|
+
async function unstitchSurfaces(solidId) {
|
|
886
|
+
const surfaces_list = [];
|
|
887
|
+
// In real implementation, extract individual surface faces from solid
|
|
888
|
+
return { solidId, surfaces: surfaces_list, count: surfaces_list.length };
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
/**
|
|
892
|
+
* Replace face - swap solid face with surface
|
|
893
|
+
*/
|
|
894
|
+
async function replaceFace(solidId, faceIndex, replacementSurfaceId) {
|
|
895
|
+
const id = `solid_replaced_face_${surfaceCounter.count++}`;
|
|
896
|
+
|
|
897
|
+
if (surfaceManager.kernel) {
|
|
898
|
+
try {
|
|
899
|
+
const result = await surfaceManager.execBrep('replaceFace', { solidId, faceIndex, replacementSurfaceId });
|
|
900
|
+
if (result) {
|
|
901
|
+
return { id, type: 'solid', original: solidId, replacedFaceIndex: faceIndex };
|
|
902
|
+
}
|
|
903
|
+
} catch (e) {
|
|
904
|
+
console.warn('[Surface] B-Rep replace face failed:', e.message);
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
return { id, type: 'solid', original: solidId, replacedFaceIndex: faceIndex };
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
/**
|
|
912
|
+
* Pipe along path - create tube surface along curve
|
|
913
|
+
*/
|
|
914
|
+
async function pipeAlongPath(profileOrId, pathOrId, options = {}) {
|
|
915
|
+
const { radius = 5, align = 'normal' } = options;
|
|
916
|
+
const id = `surface_pipe_${surfaceCounter.count++}`;
|
|
917
|
+
|
|
918
|
+
const mesh = createPipeSurfaceMesh(profileOrId, pathOrId, radius, align);
|
|
919
|
+
surfaces.set(id, { type: 'pipe_surface', mesh, radius, align });
|
|
920
|
+
|
|
921
|
+
if (viewport?.scene) {
|
|
922
|
+
mesh.material.side = THREE.DoubleSide;
|
|
923
|
+
viewport.scene.add(mesh);
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
return { id, type: 'pipe_surface', radius, align };
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
/**
|
|
930
|
+
* Circular surface cap - fill boundary with circular surface
|
|
931
|
+
*/
|
|
932
|
+
async function circularCap(boundaryLoop) {
|
|
933
|
+
const id = `surface_circular_cap_${surfaceCounter.count++}`;
|
|
934
|
+
|
|
935
|
+
const mesh = createCircularCapMesh(boundaryLoop);
|
|
936
|
+
surfaces.set(id, { type: 'circular_cap', mesh });
|
|
937
|
+
|
|
938
|
+
if (viewport?.scene) {
|
|
939
|
+
mesh.material.side = THREE.DoubleSide;
|
|
940
|
+
mesh.material.color.setHex(0xffaa44);
|
|
941
|
+
viewport.scene.add(mesh);
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
return { id, type: 'circular_cap' };
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
/**
|
|
948
|
+
* Helper: Create pipe surface mesh
|
|
949
|
+
*/
|
|
950
|
+
function createPipeSurfaceMesh(profile, path, radius, align) {
|
|
951
|
+
const geom = new THREE.BufferGeometry();
|
|
952
|
+
const mat = new THREE.MeshPhongMaterial({ color: 0xcc88ff, side: THREE.DoubleSide });
|
|
953
|
+
return new THREE.Mesh(geom, mat);
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
/**
|
|
957
|
+
* Helper: Create circular cap mesh
|
|
958
|
+
*/
|
|
959
|
+
function createCircularCapMesh(boundaryLoop) {
|
|
960
|
+
const geom = new THREE.BufferGeometry();
|
|
961
|
+
const mat = new THREE.MeshPhongMaterial({ color: 0xffaa44, side: THREE.DoubleSide });
|
|
962
|
+
return new THREE.Mesh(geom, mat);
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
/**
|
|
966
|
+
* Helper: HSV to RGB conversion
|
|
967
|
+
*/
|
|
968
|
+
function hsvToRgb(h, s, v) {
|
|
969
|
+
h = h % 360;
|
|
970
|
+
const c = v * s;
|
|
971
|
+
const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
|
|
972
|
+
const m = v - c;
|
|
973
|
+
|
|
974
|
+
let r, g, b;
|
|
975
|
+
if (h < 60) { r = c; g = x; b = 0; }
|
|
976
|
+
else if (h < 120) { r = x; g = c; b = 0; }
|
|
977
|
+
else if (h < 180) { r = 0; g = c; b = x; }
|
|
978
|
+
else if (h < 240) { r = 0; g = x; b = c; }
|
|
979
|
+
else if (h < 300) { r = x; g = 0; b = c; }
|
|
980
|
+
else { r = c; g = 0; b = x; }
|
|
981
|
+
|
|
982
|
+
return [
|
|
983
|
+
Math.round((r + m) * 255),
|
|
984
|
+
Math.round((g + m) * 255),
|
|
985
|
+
Math.round((b + m) * 255)
|
|
986
|
+
];
|
|
987
|
+
}
|
|
988
|
+
|
|
709
989
|
return {
|
|
710
990
|
MODULE_NAME,
|
|
711
991
|
init,
|
|
@@ -717,12 +997,44 @@ const SurfaceModule = (() => {
|
|
|
717
997
|
patch: patchSurface,
|
|
718
998
|
trim: trimSurface,
|
|
719
999
|
extend: extendSurface,
|
|
1000
|
+
extendAdvanced: extendSurfaceAdvanced,
|
|
720
1001
|
offset: offsetSurface,
|
|
721
1002
|
thicken: thickenSurface,
|
|
722
1003
|
stitch: stitchSurfaces,
|
|
723
1004
|
ruled: ruledSurface,
|
|
724
1005
|
boundary: boundarySurface,
|
|
1006
|
+
sculpt: sculptTSpline,
|
|
1007
|
+
curvature: analyzeCurvature,
|
|
1008
|
+
zebra: zebraStripes,
|
|
1009
|
+
draft: draftAnalysis,
|
|
1010
|
+
isocurves: showIsocurves,
|
|
1011
|
+
unstitch: unstitchSurfaces,
|
|
1012
|
+
replaceFace: replaceFace,
|
|
1013
|
+
pipe: pipeAlongPath,
|
|
1014
|
+
circularCap: circularCap,
|
|
725
1015
|
};
|
|
726
1016
|
})();
|
|
727
1017
|
|
|
1018
|
+
/**
|
|
1019
|
+
* Help entries for surface module
|
|
1020
|
+
*/
|
|
1021
|
+
const HELP_ENTRIES_SURFACE = [
|
|
1022
|
+
{ id: 'surf-extrude', title: 'Extrude Surface', category: 'Surface', description: 'Extrude open profile into surface' },
|
|
1023
|
+
{ id: 'surf-revolve', title: 'Revolve Surface', category: 'Surface', description: 'Revolve profile around axis' },
|
|
1024
|
+
{ id: 'surf-sweep', title: 'Sweep Surface', category: 'Surface', description: 'Sweep profile along path' },
|
|
1025
|
+
{ id: 'surf-loft', title: 'Loft Surface', category: 'Surface', description: 'Blend between multiple profiles' },
|
|
1026
|
+
{ id: 'surf-patch', title: 'Patch Surface', category: 'Surface', description: 'Fill boundary with Coons patch' },
|
|
1027
|
+
{ id: 'surf-ruled', title: 'Ruled Surface', category: 'Surface', description: 'Create ruled surface between curves' },
|
|
1028
|
+
{ id: 'surf-boundary', title: 'Boundary Surface', category: 'Surface', description: 'Fill 4-sided boundary' },
|
|
1029
|
+
{ id: 'surf-offset', title: 'Offset Surface', category: 'Surface', description: 'Create parallel offset' },
|
|
1030
|
+
{ id: 'surf-extend', title: 'Extend Surface', category: 'Surface', description: 'Extend surface edge' },
|
|
1031
|
+
{ id: 'surf-curvature', title: 'Curvature Analysis', category: 'Surface', description: 'Analyze and visualize curvature' },
|
|
1032
|
+
{ id: 'surf-zebra', title: 'Zebra Stripes', category: 'Surface', description: 'Continuity visualization' },
|
|
1033
|
+
{ id: 'surf-draft', title: 'Draft Analysis', category: 'Surface', description: 'Check molding draft angles' },
|
|
1034
|
+
{ id: 'surf-isocurves', title: 'Isocurves', category: 'Surface', description: 'Display parametric curves' },
|
|
1035
|
+
{ id: 'surf-thicken', title: 'Thicken', category: 'Surface', description: 'Convert surface to solid' },
|
|
1036
|
+
{ id: 'surf-stitch', title: 'Stitch', category: 'Surface', description: 'Join surfaces into solid' },
|
|
1037
|
+
{ id: 'surf-pipe', title: 'Pipe Along Path', category: 'Surface', description: 'Create tube along curve' },
|
|
1038
|
+
];
|
|
1039
|
+
|
|
728
1040
|
export default SurfaceModule;
|