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
|
bw := float64(x-runStartX) * pixelToMM
|
||||||
bh := 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 {
|
if curIsInner {
|
||||||
addBoxAtZ(&encTris, bx, by, trayFloor+snapHeight, bw, bh, totalH-(trayFloor+snapHeight))
|
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 {
|
} else {
|
||||||
// Outer wall
|
if isSpringRelief {
|
||||||
addBoxAtZ(&encTris, bx, by, trayFloor, bw, bh, totalH-trayFloor)
|
// 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
|
runStartX = x
|
||||||
|
|
@ -535,13 +556,33 @@ func GenerateEnclosure(outlineImg image.Image, drillHoles []DrillHole, cfg Enclo
|
||||||
bw := float64(x-runStartX) * pixelToMM
|
bw := float64(x-runStartX) * pixelToMM
|
||||||
bh := 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 {
|
if curIsInner {
|
||||||
addBoxAtZ(&encTris, bx, by, trayFloor+snapHeight, bw, bh, totalH-(trayFloor+snapHeight))
|
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 {
|
} 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
|
runStartX = -1
|
||||||
}
|
}
|
||||||
|
|
@ -581,6 +622,7 @@ func GenerateEnclosure(outlineImg image.Image, drillHoles []DrillHole, cfg Enclo
|
||||||
|
|
||||||
sideNum := -1
|
sideNum := -1
|
||||||
minDist := math.MaxFloat64
|
minDist := math.MaxFloat64
|
||||||
|
var bestPosAlongSide float64
|
||||||
for _, bs := range boardSides {
|
for _, bs := range boardSides {
|
||||||
dx := bs.EndX - bs.StartX
|
dx := bs.EndX - bs.StartX
|
||||||
dy := bs.EndY - bs.StartY
|
dy := bs.EndY - bs.StartY
|
||||||
|
|
@ -596,6 +638,7 @@ func GenerateEnclosure(outlineImg image.Image, drillHoles []DrillHole, cfg Enclo
|
||||||
if dist < minDist {
|
if dist < minDist {
|
||||||
minDist = dist
|
minDist = dist
|
||||||
sideNum = bs.Num
|
sideNum = bs.Num
|
||||||
|
bestPosAlongSide = t * bs.Length
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -609,14 +652,18 @@ func GenerateEnclosure(outlineImg image.Image, drillHoles []DrillHole, cfg Enclo
|
||||||
if c.Side != sideNum {
|
if c.Side != sideNum {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Wall below cutout: from 0 to cutout.Y
|
|
||||||
if c.Y > 0.1 {
|
minZ, maxZ := cutoutZBounds(c, bestPosAlongSide)
|
||||||
addBoxAtZ(&cutoutEncTris, bx2, by2, 0, bw, bh, c.Y)
|
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
|
// Wall above cutout: from maxZ to totalH
|
||||||
cutTop := c.Y + c.Height
|
if maxZ < totalH-0.05 {
|
||||||
if cutTop < totalH-0.1 {
|
addBoxAtZ(&cutoutEncTris, bx2, by2, maxZ, bw, bh, totalH-maxZ)
|
||||||
addBoxAtZ(&cutoutEncTris, bx2, by2, cutTop, bw, bh, totalH-cutTop)
|
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
@ -736,11 +783,9 @@ func GenerateEnclosure(outlineImg image.Image, drillHoles []DrillHole, cfg Enclo
|
||||||
for y := 0; y < imgH; y++ {
|
for y := 0; y < imgH; y++ {
|
||||||
runStartX := -1
|
runStartX := -1
|
||||||
curIsWall := false
|
curIsWall := false
|
||||||
curIsBump := false
|
|
||||||
for x := 0; x <= imgW; x++ {
|
for x := 0; x <= imgW; x++ {
|
||||||
isTrayFloor := false
|
isTrayFloor := false
|
||||||
isTrayWall := false
|
isTrayWall := false
|
||||||
isTrayBump := false
|
|
||||||
if x < imgW {
|
if x < imgW {
|
||||||
idx := y*imgW + x
|
idx := y*imgW + x
|
||||||
if !pegMask[idx] {
|
if !pegMask[idx] {
|
||||||
|
|
@ -753,10 +798,6 @@ func GenerateEnclosure(outlineImg image.Image, drillHoles []DrillHole, cfg Enclo
|
||||||
if dist > clearanceDistPx && dist <= trayWallOuterPx && !boardMask[idx] {
|
if dist > clearanceDistPx && dist <= trayWallOuterPx && !boardMask[idx] {
|
||||||
isTrayWall = true
|
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 {
|
if runStartX == -1 {
|
||||||
runStartX = x
|
runStartX = x
|
||||||
curIsWall = isTrayWall
|
curIsWall = isTrayWall
|
||||||
curIsBump = isTrayBump
|
} else if isTrayWall != curIsWall {
|
||||||
} else if isTrayWall != curIsWall || isTrayBump != curIsBump {
|
|
||||||
bx := float64(runStartX) * pixelToMM
|
bx := float64(runStartX) * pixelToMM
|
||||||
by := float64(y) * pixelToMM
|
by := float64(y) * pixelToMM
|
||||||
bw := float64(x-runStartX) * pixelToMM
|
bw := float64(x-runStartX) * pixelToMM
|
||||||
bh := pixelToMM
|
bh := pixelToMM
|
||||||
|
|
||||||
addBoxAtZ(&trayTris, bx, by, 0, bw, bh, trayFloor)
|
addBoxAtZ(&trayTris, bx, by, 0, bw, bh, trayFloor)
|
||||||
if curIsWall {
|
|
||||||
addBoxAtZ(&trayTris, bx, by, trayFloor, bw, bh, snapHeight)
|
wallBase := trayFloor
|
||||||
} else if curIsBump {
|
wallH := snapHeight
|
||||||
// Adds a small 0.4mm bump on the outside of the wall
|
|
||||||
addBoxAtZ(&trayTris, bx, by, trayFloor+snapHeight-0.6, bw, bh, 0.4)
|
// 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
|
runStartX = x
|
||||||
curIsWall = isTrayWall
|
curIsWall = isTrayWall
|
||||||
curIsBump = isTrayBump
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if runStartX != -1 {
|
if runStartX != -1 {
|
||||||
|
|
@ -791,10 +884,64 @@ func GenerateEnclosure(outlineImg image.Image, drillHoles []DrillHole, cfg Enclo
|
||||||
bh := pixelToMM
|
bh := pixelToMM
|
||||||
|
|
||||||
addBoxAtZ(&trayTris, bx, by, 0, bw, bh, trayFloor)
|
addBoxAtZ(&trayTris, bx, by, 0, bw, bh, trayFloor)
|
||||||
if curIsWall {
|
|
||||||
addBoxAtZ(&trayTris, bx, by, trayFloor, bw, bh, snapHeight)
|
wallBase := trayFloor
|
||||||
} else if curIsBump {
|
wallH := snapHeight
|
||||||
addBoxAtZ(&trayTris, bx, by, trayFloor+snapHeight-0.6, bw, bh, 0.4)
|
|
||||||
|
// 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
|
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)
|
// Add Pry Clips to the Tray to sit under the Enclosure Pry Slots
|
||||||
fmt.Printf("Enclosure: %d triangles, Tray: %d triangles\n", len(encTris), len(trayTris))
|
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
|
_ = 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{
|
return &EnclosureResult{
|
||||||
EnclosureTriangles: encTris,
|
EnclosureTriangles: encTris,
|
||||||
TrayTriangles: trayTris,
|
TrayTriangles: trayTris,
|
||||||
|
|
@ -941,3 +1117,27 @@ func floodFillExterior(pixels []bool, w, h int) []bool {
|
||||||
|
|
||||||
return exterior
|
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{
|
session := &EnclosureSession{
|
||||||
Exports: r.Form["exports"],
|
Exports: r.Form["exports"],
|
||||||
OutlineGf: outlineGf,
|
OutlineGf: outlineGf,
|
||||||
|
|
@ -1000,10 +1001,10 @@ func enclosureUploadHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
BoardW: actualBoardW,
|
BoardW: actualBoardW,
|
||||||
BoardH: actualBoardH,
|
BoardH: actualBoardH,
|
||||||
TotalH: ecfg.WallHeight + ecfg.PCBThickness + 1.0,
|
TotalH: ecfg.WallHeight + ecfg.PCBThickness + 1.0,
|
||||||
MinBX: float64(minBX),
|
MinBX: float64(minBX)*pixelToMM + outlineBounds.MinX,
|
||||||
MaxBX: float64(maxBX),
|
MaxBX: float64(maxBX)*pixelToMM + outlineBounds.MinX,
|
||||||
BoardCenterY: boardCenterY,
|
BoardCenterY: outlineBounds.MaxY - boardCenterY*pixelToMM,
|
||||||
Sides: ExtractBoardSidesFromMask(boardMask, imgW, imgH, 25.4/ecfg.DPI, &outlineBounds),
|
Sides: ExtractBoardSidesFromMask(boardMask, imgW, imgH, pixelToMM, &outlineBounds),
|
||||||
}
|
}
|
||||||
sessionsMu.Lock()
|
sessionsMu.Lock()
|
||||||
sessions[uuid] = session
|
sessions[uuid] = session
|
||||||
|
|
|
||||||
59
scad.go
59
scad.go
|
|
@ -270,8 +270,8 @@ func WriteNativeSCAD(filename string, isTray bool, outlineVertices [][2]float64,
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cutouts are relative to board.
|
// Cutouts are relative to board. UI specifies c.Y from bottom, so c.Y adds to Z.
|
||||||
z := c.Height/2 + trayFloor + pcbT
|
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
|
w, d, h := c.Width, 20.0, c.Height // d is deep enough to cut through walls
|
||||||
|
|
||||||
dx := bs.EndX - bs.StartX
|
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
|
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")
|
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, " 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")
|
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 {
|
if isTray {
|
||||||
// --- TRAY ---
|
// --- TRAY ---
|
||||||
fmt.Fprintf(f, "// --- TRAY ---\n")
|
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+wt)
|
||||||
fmt.Fprintf(f, " offset(r=%f) board_polygon();\n", clearance)
|
fmt.Fprintf(f, " offset(r=%f) board_polygon();\n", clearance)
|
||||||
fmt.Fprintf(f, " }\n")
|
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, " }\n")
|
||||||
|
|
||||||
fmt.Fprintf(f, " // Subtract Lip Recess (for easy opening)\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
|
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, " 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, " side_cutouts();\n")
|
||||||
fmt.Fprintf(f, "}\n\n")
|
fmt.Fprintf(f, "}\n")
|
||||||
|
fmt.Fprintf(f, "pry_clips();\n\n")
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// --- ENCLOSURE ---
|
// --- 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, " // 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, " 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, " // Vertical relief slots for the tray clips to slide into\n")
|
||||||
fmt.Fprintf(f, " translate([0,0,%f]) linear_extrude(height=%f) difference() {\n", trayFloor+snapHeight-0.7, 0.6)
|
fmt.Fprintf(f, " clipZ = %f;\n", trayFloor+snapHeight)
|
||||||
fmt.Fprintf(f, " offset(r=%f) board_polygon();\n", clearance+wt+0.5)
|
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, " offset(r=%f) board_polygon();\n", clearance+wt)
|
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, " }\n")
|
|
||||||
fmt.Fprintf(f, " pry_slots();\n")
|
fmt.Fprintf(f, " pry_slots();\n")
|
||||||
fmt.Fprintf(f, " side_cutouts();\n")
|
fmt.Fprintf(f, " side_cutouts();\n")
|
||||||
fmt.Fprintf(f, "}\n")
|
fmt.Fprintf(f, "}\n")
|
||||||
fmt.Fprintf(f, "mounting_pegs(false);\n")
|
fmt.Fprintf(f, "mounting_pegs(false);\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(f, "}\n") // Close the top-level translate
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,8 @@
|
||||||
<span>Export</span>
|
<span>Export</span>
|
||||||
<div class="menu-dropdown export-options">
|
<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-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-svg" value="svg"> SVG (2D Vector)</label>
|
||||||
<label><input type="checkbox" name="export-png" value="png"> PNG (2D Raster)</label>
|
<label><input type="checkbox" name="export-png" value="png"> PNG (2D Raster)</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue