bugfixes
This commit is contained in:
parent
cf424d4611
commit
66ea1db755
Binary file not shown.
274
enclosure.go
274
enclosure.go
|
|
@ -513,15 +513,36 @@ func GenerateEnclosure(outlineImg image.Image, drillHoles []DrillHole, cfg Enclo
|
|||
bw := float64(x-runStartX) * pixelToMM
|
||||
bh := pixelToMM
|
||||
|
||||
isSpringRelief := false
|
||||
if !curIsInner && bx >= float64(minBX)*pixelToMM-clearance-wt-1.0 && bx <= float64(maxBX)*pixelToMM+clearance+wt+1.0 {
|
||||
// Check if the current pixel run constitutes either the left or right clip relief
|
||||
pryWMM := 8.0
|
||||
by_center := float64(int(boardCenterY/float64(boardCount))) * pixelToMM
|
||||
|
||||
leftClipX := float64(minBX)*pixelToMM - clearance - wt
|
||||
rightClipX := float64(maxBX)*pixelToMM + clearance + wt
|
||||
|
||||
if by >= by_center-pryWMM/2.0-0.5 && by <= by_center+pryWMM/2.0+0.5 {
|
||||
if math.Abs(bx-leftClipX) <= 1.5 || math.Abs(bx-rightClipX) <= 1.5 {
|
||||
isSpringRelief = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if curIsInner {
|
||||
addBoxAtZ(&encTris, bx, by, trayFloor+snapHeight, bw, bh, totalH-(trayFloor+snapHeight))
|
||||
} else if curIsSnap {
|
||||
// Snap groove: remove material from (trayFloor+snapHeight-0.7) to (trayFloor+snapHeight-0.1)
|
||||
addBoxAtZ(&encTris, bx, by, trayFloor, bw, bh, snapHeight-0.7)
|
||||
addBoxAtZ(&encTris, bx, by, trayFloor+snapHeight-0.1, bw, bh, totalH-(trayFloor+snapHeight-0.1))
|
||||
} else {
|
||||
// Outer wall
|
||||
addBoxAtZ(&encTris, bx, by, trayFloor, bw, bh, totalH-trayFloor)
|
||||
if isSpringRelief {
|
||||
// For relief wall cut, omit the bottom solid wall material from the tray floor
|
||||
addBoxAtZ(&encTris, bx, by, trayFloor+snapHeight+1.0, bw, bh, totalH-(trayFloor+snapHeight+1.0))
|
||||
} else if curIsSnap {
|
||||
// Snap groove: remove material from (trayFloor+snapHeight-0.7) to (trayFloor+snapHeight-0.1)
|
||||
addBoxAtZ(&encTris, bx, by, trayFloor, bw, bh, snapHeight-0.7)
|
||||
addBoxAtZ(&encTris, bx, by, trayFloor+snapHeight-0.1, bw, bh, totalH-(trayFloor+snapHeight-0.1))
|
||||
} else {
|
||||
// Outer wall
|
||||
addBoxAtZ(&encTris, bx, by, trayFloor, bw, bh, totalH-trayFloor)
|
||||
}
|
||||
}
|
||||
|
||||
runStartX = x
|
||||
|
|
@ -535,13 +556,33 @@ func GenerateEnclosure(outlineImg image.Image, drillHoles []DrillHole, cfg Enclo
|
|||
bw := float64(x-runStartX) * pixelToMM
|
||||
bh := pixelToMM
|
||||
|
||||
isSpringRelief := false
|
||||
if !curIsInner && bx >= float64(minBX)*pixelToMM-clearance-wt-1.0 && bx <= float64(maxBX)*pixelToMM+clearance+wt+1.0 {
|
||||
pryWMM := 8.0
|
||||
by_center := float64(int(boardCenterY/float64(boardCount))) * pixelToMM
|
||||
|
||||
leftClipX := float64(minBX)*pixelToMM - clearance - wt
|
||||
rightClipX := float64(maxBX)*pixelToMM + clearance + wt
|
||||
|
||||
if by >= by_center-pryWMM/2.0-0.5 && by <= by_center+pryWMM/2.0+0.5 {
|
||||
if math.Abs(bx-leftClipX) <= 1.5 || math.Abs(bx-rightClipX) <= 1.5 {
|
||||
isSpringRelief = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if curIsInner {
|
||||
addBoxAtZ(&encTris, bx, by, trayFloor+snapHeight, bw, bh, totalH-(trayFloor+snapHeight))
|
||||
} else if curIsSnap {
|
||||
addBoxAtZ(&encTris, bx, by, trayFloor, bw, bh, snapHeight-0.7)
|
||||
addBoxAtZ(&encTris, bx, by, trayFloor+snapHeight-0.1, bw, bh, totalH-(trayFloor+snapHeight-0.1))
|
||||
} else {
|
||||
addBoxAtZ(&encTris, bx, by, trayFloor, bw, bh, totalH-trayFloor)
|
||||
if isSpringRelief {
|
||||
// For relief wall cut, omit the bottom solid wall material from the tray floor
|
||||
addBoxAtZ(&encTris, bx, by, trayFloor+snapHeight+1.0, bw, bh, totalH-(trayFloor+snapHeight+1.0))
|
||||
} else if curIsSnap {
|
||||
addBoxAtZ(&encTris, bx, by, trayFloor, bw, bh, snapHeight-0.7)
|
||||
addBoxAtZ(&encTris, bx, by, trayFloor+snapHeight-0.1, bw, bh, totalH-(trayFloor+snapHeight-0.1))
|
||||
} else {
|
||||
addBoxAtZ(&encTris, bx, by, trayFloor, bw, bh, totalH-trayFloor)
|
||||
}
|
||||
}
|
||||
runStartX = -1
|
||||
}
|
||||
|
|
@ -581,6 +622,7 @@ func GenerateEnclosure(outlineImg image.Image, drillHoles []DrillHole, cfg Enclo
|
|||
|
||||
sideNum := -1
|
||||
minDist := math.MaxFloat64
|
||||
var bestPosAlongSide float64
|
||||
for _, bs := range boardSides {
|
||||
dx := bs.EndX - bs.StartX
|
||||
dy := bs.EndY - bs.StartY
|
||||
|
|
@ -596,6 +638,7 @@ func GenerateEnclosure(outlineImg image.Image, drillHoles []DrillHole, cfg Enclo
|
|||
if dist < minDist {
|
||||
minDist = dist
|
||||
sideNum = bs.Num
|
||||
bestPosAlongSide = t * bs.Length
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -609,14 +652,18 @@ func GenerateEnclosure(outlineImg image.Image, drillHoles []DrillHole, cfg Enclo
|
|||
if c.Side != sideNum {
|
||||
continue
|
||||
}
|
||||
// Wall below cutout: from 0 to cutout.Y
|
||||
if c.Y > 0.1 {
|
||||
addBoxAtZ(&cutoutEncTris, bx2, by2, 0, bw, bh, c.Y)
|
||||
|
||||
minZ, maxZ := cutoutZBounds(c, bestPosAlongSide)
|
||||
minZ += trayFloor + pcbT
|
||||
maxZ += trayFloor + pcbT
|
||||
|
||||
// Wall below cutout: from 0 to minZ
|
||||
if minZ > 0.05 {
|
||||
addBoxAtZ(&cutoutEncTris, bx2, by2, 0, bw, bh, minZ)
|
||||
}
|
||||
// Wall above cutout: from cutout.Y+cutout.H to totalH
|
||||
cutTop := c.Y + c.Height
|
||||
if cutTop < totalH-0.1 {
|
||||
addBoxAtZ(&cutoutEncTris, bx2, by2, cutTop, bw, bh, totalH-cutTop)
|
||||
// Wall above cutout: from maxZ to totalH
|
||||
if maxZ < totalH-0.05 {
|
||||
addBoxAtZ(&cutoutEncTris, bx2, by2, maxZ, bw, bh, totalH-maxZ)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
|
@ -736,11 +783,9 @@ func GenerateEnclosure(outlineImg image.Image, drillHoles []DrillHole, cfg Enclo
|
|||
for y := 0; y < imgH; y++ {
|
||||
runStartX := -1
|
||||
curIsWall := false
|
||||
curIsBump := false
|
||||
for x := 0; x <= imgW; x++ {
|
||||
isTrayFloor := false
|
||||
isTrayWall := false
|
||||
isTrayBump := false
|
||||
if x < imgW {
|
||||
idx := y*imgW + x
|
||||
if !pegMask[idx] {
|
||||
|
|
@ -753,10 +798,6 @@ func GenerateEnclosure(outlineImg image.Image, drillHoles []DrillHole, cfg Enclo
|
|||
if dist > clearanceDistPx && dist <= trayWallOuterPx && !boardMask[idx] {
|
||||
isTrayWall = true
|
||||
}
|
||||
// Tray Bumps sit on the outside of the Tray Wall
|
||||
if dist > trayWallOuterPx && dist <= trayWallOuterPx+snapDepthPx && !boardMask[idx] {
|
||||
isTrayBump = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -764,24 +805,76 @@ func GenerateEnclosure(outlineImg image.Image, drillHoles []DrillHole, cfg Enclo
|
|||
if runStartX == -1 {
|
||||
runStartX = x
|
||||
curIsWall = isTrayWall
|
||||
curIsBump = isTrayBump
|
||||
} else if isTrayWall != curIsWall || isTrayBump != curIsBump {
|
||||
} else if isTrayWall != curIsWall {
|
||||
bx := float64(runStartX) * pixelToMM
|
||||
by := float64(y) * pixelToMM
|
||||
bw := float64(x-runStartX) * pixelToMM
|
||||
bh := pixelToMM
|
||||
|
||||
addBoxAtZ(&trayTris, bx, by, 0, bw, bh, trayFloor)
|
||||
if curIsWall {
|
||||
addBoxAtZ(&trayTris, bx, by, trayFloor, bw, bh, snapHeight)
|
||||
} else if curIsBump {
|
||||
// Adds a small 0.4mm bump on the outside of the wall
|
||||
addBoxAtZ(&trayTris, bx, by, trayFloor+snapHeight-0.6, bw, bh, 0.4)
|
||||
|
||||
wallBase := trayFloor
|
||||
wallH := snapHeight
|
||||
|
||||
// Evaluate cutout limits if this pixel run falls into a cutout mask
|
||||
isCutout := false
|
||||
for testX := runStartX; testX < x; testX++ {
|
||||
if wallCutoutMask[y*imgW+testX] {
|
||||
isCutout = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if isCutout && len(sideCutouts) > 0 {
|
||||
midX := (runStartX + x) / 2
|
||||
bxMid := float64(midX)*pixelToMM + cfg.OutlineBounds.MinX
|
||||
byMid := cfg.OutlineBounds.MaxY - float64(y)*pixelToMM
|
||||
|
||||
sideNum := -1
|
||||
minDist := math.MaxFloat64
|
||||
var bestPosAlongSide float64
|
||||
for _, bs := range boardSides {
|
||||
dx := bs.EndX - bs.StartX
|
||||
dy := bs.EndY - bs.StartY
|
||||
lenSq := dx*dx + dy*dy
|
||||
if lenSq == 0 {
|
||||
continue
|
||||
}
|
||||
t := ((bxMid-bs.StartX)*dx + (byMid-bs.StartY)*dy) / lenSq
|
||||
tClamp := math.Max(0, math.Min(1, t))
|
||||
projX := bs.StartX + tClamp*dx
|
||||
projY := bs.StartY + tClamp*dy
|
||||
dist := math.Sqrt((bxMid-projX)*(bxMid-projX) + (byMid-projY)*(byMid-projY))
|
||||
if dist < minDist {
|
||||
minDist = dist
|
||||
sideNum = bs.Num
|
||||
bestPosAlongSide = t * bs.Length
|
||||
}
|
||||
}
|
||||
|
||||
for _, c := range sideCutouts {
|
||||
if c.Side == sideNum {
|
||||
minZ, _ := cutoutZBounds(c, bestPosAlongSide)
|
||||
minZ += trayFloor + pcbT
|
||||
|
||||
// Tray wall goes up to trayFloor + snapHeight. If minZ is lower, truncate it.
|
||||
if minZ < trayFloor+wallH {
|
||||
wallH = minZ - trayFloor
|
||||
if wallH < 0 {
|
||||
wallH = 0
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if curIsWall && wallH > 0.05 {
|
||||
addBoxAtZ(&trayTris, bx, by, wallBase, bw, bh, wallH)
|
||||
}
|
||||
|
||||
runStartX = x
|
||||
curIsWall = isTrayWall
|
||||
curIsBump = isTrayBump
|
||||
}
|
||||
} else {
|
||||
if runStartX != -1 {
|
||||
|
|
@ -791,10 +884,64 @@ func GenerateEnclosure(outlineImg image.Image, drillHoles []DrillHole, cfg Enclo
|
|||
bh := pixelToMM
|
||||
|
||||
addBoxAtZ(&trayTris, bx, by, 0, bw, bh, trayFloor)
|
||||
if curIsWall {
|
||||
addBoxAtZ(&trayTris, bx, by, trayFloor, bw, bh, snapHeight)
|
||||
} else if curIsBump {
|
||||
addBoxAtZ(&trayTris, bx, by, trayFloor+snapHeight-0.6, bw, bh, 0.4)
|
||||
|
||||
wallBase := trayFloor
|
||||
wallH := snapHeight
|
||||
|
||||
// Evaluate cutout limits if this pixel run falls into a cutout mask
|
||||
isCutout := false
|
||||
for testX := runStartX; testX < x; testX++ {
|
||||
if wallCutoutMask[y*imgW+testX] {
|
||||
isCutout = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if isCutout && len(sideCutouts) > 0 {
|
||||
midX := (runStartX + x) / 2
|
||||
bxMid := float64(midX)*pixelToMM + cfg.OutlineBounds.MinX
|
||||
byMid := cfg.OutlineBounds.MaxY - float64(y)*pixelToMM
|
||||
|
||||
sideNum := -1
|
||||
minDist := math.MaxFloat64
|
||||
var bestPosAlongSide float64
|
||||
for _, bs := range boardSides {
|
||||
dx := bs.EndX - bs.StartX
|
||||
dy := bs.EndY - bs.StartY
|
||||
lenSq := dx*dx + dy*dy
|
||||
if lenSq == 0 {
|
||||
continue
|
||||
}
|
||||
t := ((bxMid-bs.StartX)*dx + (byMid-bs.StartY)*dy) / lenSq
|
||||
tClamp := math.Max(0, math.Min(1, t))
|
||||
projX := bs.StartX + tClamp*dx
|
||||
projY := bs.StartY + tClamp*dy
|
||||
dist := math.Sqrt((bxMid-projX)*(bxMid-projX) + (byMid-projY)*(byMid-projY))
|
||||
if dist < minDist {
|
||||
minDist = dist
|
||||
sideNum = bs.Num
|
||||
bestPosAlongSide = t * bs.Length
|
||||
}
|
||||
}
|
||||
|
||||
for _, c := range sideCutouts {
|
||||
if c.Side == sideNum {
|
||||
minZ, _ := cutoutZBounds(c, bestPosAlongSide)
|
||||
minZ += trayFloor + pcbT
|
||||
|
||||
if minZ < trayFloor+wallH {
|
||||
wallH = minZ - trayFloor
|
||||
if wallH < 0 {
|
||||
wallH = 0
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if curIsWall && wallH > 0.05 {
|
||||
addBoxAtZ(&trayTris, bx, by, wallBase, bw, bh, wallH)
|
||||
}
|
||||
runStartX = -1
|
||||
}
|
||||
|
|
@ -802,11 +949,40 @@ func GenerateEnclosure(outlineImg image.Image, drillHoles []DrillHole, cfg Enclo
|
|||
}
|
||||
}
|
||||
|
||||
// (Old PCB support rim, snap bump, embossed lip, and removal tab loops have been permanently removed because the Tray geometry forms a flush fitting bottom shoe-box lid interface)
|
||||
fmt.Printf("Enclosure: %d triangles, Tray: %d triangles\n", len(encTris), len(trayTris))
|
||||
// Add Pry Clips to the Tray to sit under the Enclosure Pry Slots
|
||||
if boardCount > 0 {
|
||||
pryWMM := 8.0
|
||||
pryDMM := 1.0
|
||||
clipH := 0.8
|
||||
|
||||
leftX := float64(minBX)*pixelToMM - clearance - wt
|
||||
rightX := float64(maxBX)*pixelToMM + clearance + wt
|
||||
by_center := float64(int(boardCenterY/float64(boardCount))) * pixelToMM
|
||||
|
||||
// Z coordinates: trayFloor + snapHeight - clipH ensures the clip finishes flush with the top of the tray wall
|
||||
addBoxAtZ(&trayTris, leftX-pryDMM, by_center-pryWMM/2.0, trayFloor+snapHeight-clipH, pryDMM, pryWMM, clipH)
|
||||
addBoxAtZ(&trayTris, rightX, by_center-pryWMM/2.0, trayFloor+snapHeight-clipH, pryDMM, pryWMM, clipH)
|
||||
}
|
||||
|
||||
_ = math.Pi // keep math import for Phase 2 cylindrical pegs
|
||||
|
||||
// Shift meshes to origin so the exported STL is centered
|
||||
offsetX := float64(imgW) * pixelToMM / 2.0
|
||||
offsetY := float64(imgH) * pixelToMM / 2.0
|
||||
|
||||
for i := range encTris {
|
||||
for j := 0; j < 3; j++ {
|
||||
encTris[i][j].X -= offsetX
|
||||
encTris[i][j].Y -= offsetY
|
||||
}
|
||||
}
|
||||
for i := range trayTris {
|
||||
for j := 0; j < 3; j++ {
|
||||
trayTris[i][j].X -= offsetX
|
||||
trayTris[i][j].Y -= offsetY
|
||||
}
|
||||
}
|
||||
|
||||
return &EnclosureResult{
|
||||
EnclosureTriangles: encTris,
|
||||
TrayTriangles: trayTris,
|
||||
|
|
@ -941,3 +1117,27 @@ func floodFillExterior(pixels []bool, w, h int) []bool {
|
|||
|
||||
return exterior
|
||||
}
|
||||
|
||||
// cutoutZBounds calculates the accurate Z bounds taking into account corner radii
|
||||
func cutoutZBounds(c SideCutout, posAlongSide float64) (float64, float64) {
|
||||
minZ := c.Y
|
||||
maxZ := c.Y + c.Height
|
||||
|
||||
if c.CornerRadius > 0 {
|
||||
r := c.CornerRadius
|
||||
localX := posAlongSide - c.X
|
||||
|
||||
if localX < r {
|
||||
dx := r - localX
|
||||
dy := r - math.Sqrt(math.Max(0, r*r-dx*dx))
|
||||
minZ += dy
|
||||
maxZ -= dy
|
||||
} else if localX > c.Width-r {
|
||||
dx := localX - (c.Width - r)
|
||||
dy := r - math.Sqrt(math.Max(0, r*r-dx*dx))
|
||||
minZ += dy
|
||||
maxZ -= dy
|
||||
}
|
||||
}
|
||||
return minZ, maxZ
|
||||
}
|
||||
|
|
|
|||
9
main.go
9
main.go
|
|
@ -988,6 +988,7 @@ func enclosureUploadHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
pixelToMM := 25.4 / ecfg.DPI
|
||||
session := &EnclosureSession{
|
||||
Exports: r.Form["exports"],
|
||||
OutlineGf: outlineGf,
|
||||
|
|
@ -1000,10 +1001,10 @@ func enclosureUploadHandler(w http.ResponseWriter, r *http.Request) {
|
|||
BoardW: actualBoardW,
|
||||
BoardH: actualBoardH,
|
||||
TotalH: ecfg.WallHeight + ecfg.PCBThickness + 1.0,
|
||||
MinBX: float64(minBX),
|
||||
MaxBX: float64(maxBX),
|
||||
BoardCenterY: boardCenterY,
|
||||
Sides: ExtractBoardSidesFromMask(boardMask, imgW, imgH, 25.4/ecfg.DPI, &outlineBounds),
|
||||
MinBX: float64(minBX)*pixelToMM + outlineBounds.MinX,
|
||||
MaxBX: float64(maxBX)*pixelToMM + outlineBounds.MinX,
|
||||
BoardCenterY: outlineBounds.MaxY - boardCenterY*pixelToMM,
|
||||
Sides: ExtractBoardSidesFromMask(boardMask, imgW, imgH, pixelToMM, &outlineBounds),
|
||||
}
|
||||
sessionsMu.Lock()
|
||||
sessions[uuid] = session
|
||||
|
|
|
|||
59
scad.go
59
scad.go
|
|
@ -270,8 +270,8 @@ func WriteNativeSCAD(filename string, isTray bool, outlineVertices [][2]float64,
|
|||
continue
|
||||
}
|
||||
|
||||
// Cutouts are relative to board.
|
||||
z := c.Height/2 + trayFloor + pcbT
|
||||
// Cutouts are relative to board. UI specifies c.Y from bottom, so c.Y adds to Z.
|
||||
z := c.Height/2 + trayFloor + pcbT + c.Y
|
||||
w, d, h := c.Width, 20.0, c.Height // d is deep enough to cut through walls
|
||||
|
||||
dx := bs.EndX - bs.StartX
|
||||
|
|
@ -287,7 +287,19 @@ func WriteNativeSCAD(filename string, isTray bool, outlineVertices [][2]float64,
|
|||
|
||||
rotDeg := (bs.Angle * 180.0 / math.Pi) - 90.0
|
||||
|
||||
fmt.Fprintf(f, " translate([%f, %f, %f]) rotate([0, 0, %f]) cube([%f, %f, %f], center=true);\n", midX, midY, z, rotDeg, w, d, h)
|
||||
if c.CornerRadius > 0 {
|
||||
r := c.CornerRadius
|
||||
fmt.Fprintf(f, " translate([%f, %f, %f]) rotate([0, 0, %f]) {\n", midX, midY, z, rotDeg)
|
||||
fmt.Fprintf(f, " hull() {\n")
|
||||
fmt.Fprintf(f, " translate([%f, 0, %f]) rotate([90, 0, 0]) cylinder(r=%f, h=%f, center=true);\n", w/2-r, h/2-r, r, d)
|
||||
fmt.Fprintf(f, " translate([%f, 0, %f]) rotate([90, 0, 0]) cylinder(r=%f, h=%f, center=true);\n", -(w/2 - r), h/2-r, r, d)
|
||||
fmt.Fprintf(f, " translate([%f, 0, %f]) rotate([90, 0, 0]) cylinder(r=%f, h=%f, center=true);\n", w/2-r, -(h/2 - r), r, d)
|
||||
fmt.Fprintf(f, " translate([%f, 0, %f]) rotate([90, 0, 0]) cylinder(r=%f, h=%f, center=true);\n", -(w/2 - r), -(h/2 - r), r, d)
|
||||
fmt.Fprintf(f, " }\n")
|
||||
fmt.Fprintf(f, " }\n")
|
||||
} else {
|
||||
fmt.Fprintf(f, " translate([%f, %f, %f]) rotate([0, 0, %f]) cube([%f, %f, %f], center=true);\n", midX, midY, z, rotDeg, w, d, h)
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(f, "}\n\n")
|
||||
|
||||
|
|
@ -299,6 +311,18 @@ func WriteNativeSCAD(filename string, isTray bool, outlineVertices [][2]float64,
|
|||
fmt.Fprintf(f, " translate([%f, %f, 0]) cube([%f, %f, %f], center=true);\n", maxBX+clearance+wt-pryD/2, boardCenterY, pryD*2, pryW, snapHeight*3)
|
||||
fmt.Fprintf(f, "}\n\n")
|
||||
|
||||
// Print Pry Clips Module
|
||||
fmt.Fprintf(f, "module pry_clips() {\n")
|
||||
clipH := 0.8
|
||||
clipZ := trayFloor + snapHeight - clipH/2.0
|
||||
fmt.Fprintf(f, " translate([%f, %f, %f]) cube([%f, %f, %f], center=true);\n", minBX-clearance-wt-0.5, boardCenterY, clipZ, 1.0, pryW, clipH)
|
||||
fmt.Fprintf(f, " translate([%f, %f, %f]) cube([%f, %f, %f], center=true);\n", maxBX+clearance+wt+0.5, boardCenterY, clipZ, 1.0, pryW, clipH)
|
||||
fmt.Fprintf(f, "}\n\n")
|
||||
|
||||
centerX := cfg.OutlineBounds.MinX + (cfg.OutlineBounds.MaxX-cfg.OutlineBounds.MinX)/2.0
|
||||
centerY := cfg.OutlineBounds.MinY + (cfg.OutlineBounds.MaxY-cfg.OutlineBounds.MinY)/2.0
|
||||
fmt.Fprintf(f, "translate([%f, %f, 0]) {\n", -centerX, -centerY)
|
||||
|
||||
if isTray {
|
||||
// --- TRAY ---
|
||||
fmt.Fprintf(f, "// --- TRAY ---\n")
|
||||
|
|
@ -311,19 +335,6 @@ func WriteNativeSCAD(filename string, isTray bool, outlineVertices [][2]float64,
|
|||
fmt.Fprintf(f, " offset(r=%f) board_polygon();\n", clearance+wt)
|
||||
fmt.Fprintf(f, " offset(r=%f) board_polygon();\n", clearance)
|
||||
fmt.Fprintf(f, " }\n")
|
||||
fmt.Fprintf(f, " // Snap Bumps (on outside of tray wall)\n")
|
||||
fmt.Fprintf(f, " translate([0,0,%f]) linear_extrude(height=%f) difference() {\n", trayFloor+snapHeight-0.6, 0.4)
|
||||
fmt.Fprintf(f, " offset(r=%f) board_polygon();\n", clearance+wt+0.4)
|
||||
fmt.Fprintf(f, " offset(r=%f) board_polygon();\n", clearance+wt)
|
||||
fmt.Fprintf(f, " }\n")
|
||||
fmt.Fprintf(f, " // Mounting Pegs\n")
|
||||
for _, hole := range holes {
|
||||
if hole.Type != DrillTypeMounting {
|
||||
continue
|
||||
}
|
||||
pegRadius := (hole.Diameter / 2.0) - 0.15
|
||||
fmt.Fprintf(f, " translate([%f,%f,0]) cylinder(h=%f, r=%f, $fn=32);\n", hole.X, hole.Y, totalH-lidThick, pegRadius)
|
||||
}
|
||||
fmt.Fprintf(f, " }\n")
|
||||
|
||||
fmt.Fprintf(f, " // Subtract Lip Recess (for easy opening)\n")
|
||||
|
|
@ -340,9 +351,9 @@ func WriteNativeSCAD(filename string, isTray bool, outlineVertices [][2]float64,
|
|||
socketRadius := (hole.Diameter / 2.0) + 0.1
|
||||
fmt.Fprintf(f, " translate([%f,%f,-1]) cylinder(h=%f, r=%f, $fn=32);\n", hole.X, hole.Y, trayFloor+2, socketRadius)
|
||||
}
|
||||
fmt.Fprintf(f, " pry_slots();\n")
|
||||
fmt.Fprintf(f, " side_cutouts();\n")
|
||||
fmt.Fprintf(f, "}\n\n")
|
||||
fmt.Fprintf(f, "}\n")
|
||||
fmt.Fprintf(f, "pry_clips();\n\n")
|
||||
|
||||
} else {
|
||||
// --- ENCLOSURE ---
|
||||
|
|
@ -357,16 +368,18 @@ func WriteNativeSCAD(filename string, isTray bool, outlineVertices [][2]float64,
|
|||
fmt.Fprintf(f, " // Subtract Tray Recess (Accommodates Tray Wall)\n")
|
||||
fmt.Fprintf(f, " translate([0,0,-1]) linear_extrude(height=%f) offset(r=%f) board_polygon();\n", trayFloor+snapHeight+0.2, clearance+wt+0.15)
|
||||
|
||||
fmt.Fprintf(f, " // Subtract Snap Groove\n")
|
||||
fmt.Fprintf(f, " translate([0,0,%f]) linear_extrude(height=%f) difference() {\n", trayFloor+snapHeight-0.7, 0.6)
|
||||
fmt.Fprintf(f, " offset(r=%f) board_polygon();\n", clearance+wt+0.5)
|
||||
fmt.Fprintf(f, " offset(r=%f) board_polygon();\n", clearance+wt)
|
||||
fmt.Fprintf(f, " }\n")
|
||||
fmt.Fprintf(f, " // Vertical relief slots for the tray clips to slide into\n")
|
||||
fmt.Fprintf(f, " clipZ = %f;\n", trayFloor+snapHeight)
|
||||
fmt.Fprintf(f, " translate([%f, %f, trayFloor - 1]) cube([1.5, %f, clipZ+1], center=true);\n", minBX-clearance-wt-0.25, boardCenterY, pryW+1.0)
|
||||
fmt.Fprintf(f, " translate([%f, %f, trayFloor - 1]) cube([1.5, %f, clipZ+1], center=true);\n", maxBX+clearance+wt+0.25, boardCenterY, pryW+1.0)
|
||||
|
||||
fmt.Fprintf(f, " pry_slots();\n")
|
||||
fmt.Fprintf(f, " side_cutouts();\n")
|
||||
fmt.Fprintf(f, "}\n")
|
||||
fmt.Fprintf(f, "mounting_pegs(false);\n")
|
||||
}
|
||||
|
||||
fmt.Fprintf(f, "}\n") // Close the top-level translate
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,8 @@
|
|||
<span>Export</span>
|
||||
<div class="menu-dropdown export-options">
|
||||
<label><input type="checkbox" name="export-stl" value="stl" checked> STL (3D Mesh)</label>
|
||||
<label><input type="checkbox" name="export-scad" value="scad"> SCAD (Native OpenSCAD)</label>
|
||||
<label><input type="checkbox" name="export-scad" value="scad" checked> SCAD (Native
|
||||
OpenSCAD)</label>
|
||||
<label><input type="checkbox" name="export-svg" value="svg"> SVG (2D Vector)</label>
|
||||
<label><input type="checkbox" name="export-png" value="png"> PNG (2D Raster)</label>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in New Issue