@@ -935,3 +935,118 @@ describe 'Exposure Detection - Cavity and Hole Detection', ->
935935 # 2. Each covered region
936936 # Expect multiple skin sections.
937937 expect (skinSectionCount).toBeGreaterThan (3 )
938+
939+ test ' should detect fully covered region when smaller region is from BELOW (inverted pyramid)' , ->
940+
941+ # Regression test for inverted pyramid case.
942+ # When the shape is inverted (small base, large top), the fully covered region
943+ # comes from below the transition layer, not above.
944+ # On transition layers, the center area (covered by the small slab below) should
945+ # be detected as "fully covered" and get a skin wall + regular infill,
946+ # while the outer ring should get O-shaped skin infill only.
947+ cubeSize = 10
948+
949+ # Inverted: small base slab (3x3) at bottom, large top slab (5x5) on top.
950+ baseSlab = new THREE.BoxGeometry (3 * cubeSize, 3 * cubeSize, cubeSize)
951+ baseSlabMesh = new THREE.Mesh (baseSlab, new THREE.MeshBasicMaterial ())
952+ baseSlabMesh .position .set (0 , 0 , 0 )
953+ baseSlabMesh .updateMatrixWorld ()
954+
955+ topSlab = new THREE.BoxGeometry (5 * cubeSize, 5 * cubeSize, cubeSize)
956+ topSlabMesh = new THREE.Mesh (topSlab, new THREE.MeshBasicMaterial ())
957+ topSlabMesh .position .set (0 , 0 , cubeSize)
958+ topSlabMesh .updateMatrixWorld ()
959+
960+ invertedMesh = await Polytree .unite (baseSlabMesh, topSlabMesh)
961+
962+ finalMesh = new THREE.Mesh (invertedMesh .geometry , invertedMesh .material )
963+ finalMesh .position .set (0 , 0 , 0 )
964+ finalMesh .updateMatrixWorld ()
965+
966+ # Configure slicer with exposure detection enabled.
967+ slicer .setLayerHeight (0.2 )
968+ slicer .setShellSkinThickness (0.8 ) # 4 skin layers.
969+ slicer .setShellWallThickness (0.8 )
970+ slicer .setInfillDensity (20 )
971+ slicer .setInfillPattern (' grid' )
972+ slicer .setVerbose (true )
973+ slicer .setAutohome (false )
974+ slicer .setExposureDetection (true )
975+
976+ result = slicer .slice (finalMesh)
977+
978+ # The base slab is 10mm tall = 50 layers (1-50).
979+ # The top slab is 10mm tall = 50 layers (51-100).
980+ # First 4 layers of top slab (51-54) should have adaptive skin.
981+ # Center 3x3 area (covered by small slab below) should NOT have skin infill.
982+ # Outer ring (exposed area) should have skin infill.
983+ # Build plate center = 110x110 (220x220 bed).
984+ # 3x3 slab covers X=[95,125], Y=[95,125].
985+ # 5x5 slab covers X=[85,135], Y=[85,135].
986+ lines = result .split (' \n ' )
987+ layer51Started = false
988+ layer52Started = false
989+ layer51SkinInfillLines = []
990+ layer51FillLines = []
991+ inFillSection = false
992+
993+ for line in lines
994+
995+ if line .includes (' LAYER: 51 of' )
996+ layer51Started = true
997+ inFillSection = false
998+ else if line .includes (' LAYER: 52 of' )
999+ layer52Started = true
1000+ break
1001+ else if layer51Started
1002+ if line .includes (' TYPE: FILL' )
1003+ inFillSection = true
1004+ else if line .includes (' TYPE:' )
1005+ inFillSection = false
1006+
1007+ if line .includes (' Moving to skin infill line' )
1008+ layer51SkinInfillLines .push (line)
1009+ else if inFillSection and line .includes (' G1' ) and line .includes (' E' )
1010+ layer51FillLines .push (line)
1011+
1012+ # Verify that layer 51 has skin infill (outer ring exposed area).
1013+ expect (layer51SkinInfillLines .length ).toBeGreaterThan (0 )
1014+
1015+ # Verify NO skin infill lines are in the fully covered center area (95-125).
1016+ # The 3x3 slab below covers X=[95,125] and Y=[95,125].
1017+ # Skin infill should only be in the exposed outer ring, not the center.
1018+ centerSkinInfillCount = 0
1019+
1020+ for line in layer51SkinInfillLines
1021+
1022+ xMatch = line .match (/ X([\d. ] + )/ )
1023+ yMatch = line .match (/ Y([\d. ] + )/ )
1024+
1025+ if xMatch and yMatch
1026+
1027+ xCoord = parseFloat (xMatch[1 ])
1028+ yCoord = parseFloat (yMatch[1 ])
1029+
1030+ if xCoord > 95 and xCoord < 125 and yCoord > 95 and yCoord < 125
1031+ centerSkinInfillCount += 1
1032+
1033+ expect (centerSkinInfillCount).toBe (0 )
1034+
1035+ # Verify that regular infill IS generated in the covered center area.
1036+ # This ensures structural support in fully covered regions.
1037+ centerFillCount = 0
1038+
1039+ for line in layer51FillLines
1040+
1041+ xMatch = line .match (/ X([\d. ] + )/ )
1042+ yMatch = line .match (/ Y([\d. ] + )/ )
1043+
1044+ if xMatch and yMatch
1045+
1046+ xCoord = parseFloat (xMatch[1 ])
1047+ yCoord = parseFloat (yMatch[1 ])
1048+
1049+ if xCoord > 95 and xCoord < 125 and yCoord > 95 and yCoord < 125
1050+ centerFillCount += 1
1051+
1052+ expect (centerFillCount).toBeGreaterThan (0 )
0 commit comments