Laporkan

Ripple Lamp - tetesan air kayu yang dipotong laser

Instruksi
disc.xs
disc
.xs
Skenario aplikasi
Pemrosesan laser
Mesin & modul
xTool M2
10W
Bahan yang digunakan
kayu lapis
Strip LED
gift
6
520

Informasi

      Cakram kayu lapis 300 mm dipotong dengan satu slot spiral yang berkelanjutan, sehingga dapat digantung
lebih dari dua belas tulang rusuk berbentuk dan mengambil bentuk riak yang menyebar dari
jatuh jatuh. Strip LED di dalam cincin dasar menerangi dari tepi.

Profil tidak digambar dengan tangan. Ini adalah fungsi Bessel, yang merupakan
solusi nyata untuk drop landing di membran lingkaran, jadi jarak tempuh
Cincin dan cara mereka memudar keluar berasal dari fisika.

Masalah menarik di sini adalah bahwa cakram datar secara matematis tidak dapat menjadi
permukaan bergelombang tanpa mengubah kelilingnya sendiri. 
Slot konsentris tidak bisa melakukannya, karena cincin tertutup tidak bisa
Ubah kelilingnya. Spiral bisa, karena ia berakhir di atas dirinya sendiri seperti
pegas jam. Itulah sebabnya polanya terlihat seperti sekarang.

Potong menggunakan laser dioda 10 W dari kayu lapis poplar 3,5 mm. Semua geometri adalah
dihasilkan oleh 4 skrip Python pendek sehingga Anda bisa mengubah diameternya,
Amplitudo atau slot pitch dan regenerasi file.

Saya mempostingnya di Facebook: 

https://www.facebook.com/groups/lasermaterials/permalink/1338253281623420/

Saya mempostingnya di Reddit: 

https://www.reddit.com/r/xToolOfficial/s/fd0xtk4Syw

____
Proyek ini adalah desain saya, dibangun melalui beberapa iterasi pengujian dan perakitan. Kemiripan dengan lampu atau proyek lain yang mungkin Anda lihat hanyalah kebetulan, saya sudah mencari sebelum mulai dan tidak menemukan yang serupa, tapi jika Anda tahu karya sebelumnya seperti ini, saya benar-benar ingin melihatnya.
    

Instruksi

1

Langkah1: Dapatkan materi + tautan media sosial

- Triplek 3,5 mm, kira-kira 300 x 900 mm 
- strip LED COB 24 V, putih hangat, sekitar 900 mm
- lem kayu
- amplas halus, grit

 240 Saya mempostingnya di facebook: 
 
2

Langkah2: Ukur kayu lapis Anda

Jangan lewatkan ini. Lembaran saya dijual dalam ukuran 4 mm dan kaliper menunjukkan 3,5. Lebih parah lagi,
bahkan tidak konsisten: saya mendapatkan variasi sekitar 0,3 mm antara sudut-sudut
lembaran yang sama.
 
Hub pertama yang saya potong menggunakan jarak 0,15 mm. Delapan tulang rusuk masuk dengan baik dan empat tulang rusuk
terakhir hancur saat mencoba memaksanya. Slot di file sekarang
0,5 mm lebih besar, terdengar berantakan tapi sebenarnya berhasil setelah Anda menambahkan
variasi sheet ke laser char.
3

Langkah3: Potong sisir uji terlebih dahulu - OPSIONAL

Lakukan langkah ini jika Anda ingin meregenerasi SVG dengan sangat presisi:
 
hub.py juga menghasilkan sisir kecil dengan enam slot, dari 3,7 hingga 4,2 mm dalam
langkah 0,1 mm. Potong hanya itu, lalu coba masukkan sepotong kayu lapis ke
setiap slot.

Anda ingin yang limbahnya meluncur masuk dengan tekanan ibu jari ringan dan
tidak jatuh sendiri. Nomor itu masuk ke hub. Dua menit di sini
menyelamatkan bagian yang hancur.
4

Langkah4: Jalankan skrip - OPSIONAL

Lakukan langkah ini jika Anda ingin membuat ulang SVG dengan sangat presisi:

python3 disc.py

python3 rib.py

python3 hub.py 

python3 ring.py
 
Masing-masing mencetak ringkasan sebelum menulis SVG-nya, sehingga Anda bisa memeriksa
angka secara mental tanpa membuka apa pun. Cakram seharusnya melaporkan sekitar 15 putaran dan
pita sepanjang 6,6 m.
5

Langkah5: Atur potongan

Potong kotak kecil pola spiral pada potongan sampingan sebelum memutuskan.
Pengaturan saya pada dioda 10 W dengan kain poplar 3,5 mm adalah 100% daya, 4 mm/s,
1 pass, air assist aktif.
6

Langkah6: Potong semuanya

Ripple Lamp - tetesan air kayu yang dipotong laser instruction: step 6 - Potong semuanya
Pratinjau
Ripple Lamp - tetesan air kayu yang dipotong laser instruction: step 6 - Potong semuanya
Pratinjau
Ripple Lamp - tetesan air kayu yang dipotong laser instruction: step 6 - Potong semuanya
Pratinjau
Ripple Lamp - tetesan air kayu yang dipotong laser instruction: step 6 - Potong semuanya
Pratinjau
- Cakram: 1
- Tulang rusuk: 12
- Hub: 2 (mereka direkatkan bersama)
- Cincin: 3 strip dan 3 pelat sambungan
7

Langkah7: Pahami apa tulang rusuknya sebelum Anda menempelkan apa pun

Ripple Lamp - tetesan air kayu yang dipotong laser instruction: step 7 - Pahami apa tulang rusuknya sebelum Anda menempelkan apa pun
Pratinjau
Ripple Lamp - tetesan air kayu yang dipotong laser instruction: step 7 - Pahami apa tulang rusuknya sebelum Anda menempelkan apa pun
Pratinjau
Tulang rusuk bukanlah pelat datar yang kamu tumpuk. Mereka berdiri tegak, seperti
rangka di dalam lambung kapal, dan bentuk tepi atasnya yang menahan
ombak. Cakram itu tersebar di seluruh dua belas sekaligus.

 

Tingginya mencapai 12,4 mm di ujung dalam, hingga 22,6 mm di puncak pertama,
turun ke 16,3 mm di palung, dan kembali ke 21,2 mm di puncak kedua.
Kurva itu adalah profil Bessel yang dipotong pada kayu.
8

Langkah8: Bangun hub

Ripple Lamp - tetesan air kayu yang dipotong laser instruction: step 8 - Bangun hub
Pratinjau
Ripple Lamp - tetesan air kayu yang dipotong laser instruction: step 8 - Bangun hub
Pratinjau
Lem dua cakram hub secara hadapan dengan slot yang sejajar. Cara yang dapat diandalkan:
tumpuk, dorong dua rib yang terpotong melalui slot yang berlawanan untuk memaksa penyelarasan,
lalu lem dan biarkan kering seperti itu.
Menggandakannya memberikan cengkeraman 7 mm pada setiap tulang rusuk, bukan 3,5 mm.
Lalu masukkan tulang rusuk.
9

Langkah9: Bebaskan cakram

Ripple Lamp - tetesan air kayu yang dipotong laser instruction: step 9 - Bebaskan cakram
Pratinjau
Ada 14 tab kecil 0,6 mm di sepanjang satu radius yang menahan spiral dalam
register. Tanpa itu, semuanya akan terurai begitu Anda mengangkatnya dari
tempat tidur.
 
Letakkan cakram rata dan potong setiap cakram dengan pisau tajam. Semua.

 

Sekarang pita sepanjang 6,6 m hanya dipasang di tengah dan pinggiran. Bawa rata
di atas kartu. Jangan ambil dari tepi.
10

Langkah10: Bentuk dan lem

Ripple Lamp - tetesan air kayu yang dipotong laser instruction: step 10 - Bentuk dan lem
Pratinjau
Letakkan cakram di atas laba-laba, pusatkan ke tengah, lalu lem ke luar sambil berjalan.

 

Beratkan saja, gravitasi saja tidak cukup untuk membuat pita itu berputar di tulang rusuk.
11

Langkah11: Cincin dasar dan pencahayaan

Cakram berakhir menjadi 293 mm lebar setelah melengkung, bukan 300, karena material bergerak mengikuti gelombang, bukan lurus. Cincin itu disesuaikan dengan ukuran itu.

Lem cincin ke bagian bawah rim cakram dan beri berat.

Kemudian tempelkan strip COB di sisi dalam, 8 hingga 10 mm di bawah tepi atas, mengarah ke dalam.

12

Langkah12: KODE PYTHON

"""
Ripple lamp - rib generator.


 

One rib. I cut 12 of these and stand them on edge in the hub like the
spokes of a wheel. The wavy top edge is what the disc lies on, the straight
bottom edge sits flat on the table, that's what sets the height, not the hub.
"""


 

import math


 

# ---- same wave as the disc, don't touch these separately ----
R = 150.0
A0 = 9.0
KZ = 13.3237
K = KZ / R


 

RIB_R0 = 12.0 # inner end, any closer and 12 ribs collide at the centre
CLEAR = 10.0 # air under the lowest point of the wave, room for the hub
MARGIN = 10.0 # pull the outer end back so the rib can't poke past the disc


 

OUT = "rib.svg"



 

def bessel(n, x, steps=1500):
total = 0.0
for i in range(steps + 1):
t = math.pi * i / steps
w = 1.0 if 0 < i < steps else 0.5
total += w * math.cos(n * t - x * math.sin(t))
return total / steps



 

N = 30000
STEP = R / N
HEIGHT = [-A0 * bessel(0, K * STEP * i) for i in range(N + 1)]
RIM = HEIGHT[N]
HEIGHT = [h - RIM for h in HEIGHT] # measure everything from the rim



 

def height(r):
return HEIGHT[max(0, min(N, int(round(r / STEP))))]



 

# ---- how far the disc actually reaches once it's curved ----
# This caught me out. The flat disc is 300 mm but the material has to travel
# along the wave, not straight, so the finished thing comes out smaller.
# The ribs live in the finished shape, so they use this radius, not 150.
walk = 0.0
R_PROJ = R
for i in range(1, N + 1):
walk += math.hypot(STEP, HEIGHT[i] - HEIGHT[i - 1])
if walk >= R:
R_PROJ = STEP * i
break


 

RIB_R1 = R_PROJ - MARGIN
BASE = min(HEIGHT) - CLEAR
RIB_L = RIB_R1 - RIB_R0


 

# --- draw it: wavy top left to right, then straight back along the bottom ---
X0, Y0 = 5.0, 5.0 + (max(HEIGHT) - BASE)
pts = []
steps = 400
for i in range(steps + 1):
r = RIB_R0 + RIB_L * i / steps
pts.append((X0 + (r - RIB_R0), Y0 - (height(r) - BASE)))
d = "M " + " L ".join(f"{p[0]:.3f} {p[1]:.3f}" for p in pts)
d += f" L {X0 + RIB_L:.3f} {Y0:.3f} L {X0:.3f} {Y0:.3f} Z"


 

W, H = RIB_L + 10, Y0 + 5
svg = [f'<svg xmlns="http://www.w3.org/2000/svg" width="{W:.0f}mm" height="{H:.0f}mm" '
f'viewBox="0 0 {W:.0f} {H:.0f}">',
'<g fill="none" stroke="#000000" stroke-width="0.1">',
f'<path d="{d}"/>', '</g></svg>']
open(OUT, "w").write("\n".join(svg))


 

edge = 0.0
for i in range(1, len(pts)):
edge += math.hypot(pts[i][0] - pts[i - 1][0], pts[i][1] - pts[i - 1][1])


 

print(f"{OUT} written - cut 12 of these")
print(f"flat disc is {2 * R:.0f} mm, curved it ends up {2 * R_PROJ:.0f} mm")
print(f"rib runs r={RIB_R0} to r={RIB_R1:.1f}, so {RIB_L:.1f} mm long")
print(f"baseline sits {-BASE:.2f} mm below the disc rim")
print()
print(" r height above baseline")
for rr in (12, 25, 43, 60, 79, 100, 114, 130, RIB_R1):
print(f"{rr:6.1f} {height(rr) - BASE:9.2f} mm")
print()
print(f"tallest {max(height(RIB_R0 + RIB_L * i / 400) - BASE for i in range(401)):.1f} mm, "
f"shortest {min(height(RIB_R0 + RIB_L * i / 400) - BASE for i in range(401)):.1f} mm")
print(f"one rib is {(edge + RIB_L + 2 * (height(RIB_R0) - BASE)) / 1000:.2f} m of cutting, "
f"x12 = {12 * (edge + RIB_L + 2 * (height(RIB_R0) - BASE)) / 1000:.2f} m")
print(f"spacing at the rim {2 * math.pi * RIB_R1 / 12:.0f} mm")
 
"""
Ripple lamp - hub generator.


 

The hub is the little disc that holds the 12 ribs at 30 degrees apart.
Slots are open at the rim so a rib slides in sideways along the table.
They can't drop in from above, a rib is 131 mm long and the slot is 26.


 

I cut this twice and glue the two together, 7 mm of grip instead of 3.5
keeps the ribs from wobbling.
"""


 

import math


 

# ---- the important bit ----
# My sheet was sold as 4 mm and calipers say 3.5. It also isn't even across
# the sheet, I get about +/- 0.3 mm from corner to corner. First time round I
# used 3.65 mm slots and 4 of the 12 ribs wouldn't go in, so now I measure the
# THICKEST spot I can find and add clearance on top of that.
T_MEASURED = 3.5 # thickest reading I got with the calipers
CLEARANCE = 0.5 # covers sheet variation plus the charring from the laser
SLOT_W = T_MEASURED + CLEARANCE


 

HUB_R = 38.0 # hub radius
SLOT_R0 = 12.0 # slot bottom, this is what sets where the rib stops
N_RIB = 12


 

# Small comb I cut first to check the fit before committing to the hub.
# Each tooth gap is 0.1 mm wider than the last.
MAKE_TEST_COMB = True
COMB_START = 3.7
COMB_STEP = 0.1
COMB_N = 6


 

OUT = "hub.svg"


 

paths = []
HX = HY = HUB_R + 5.0


 

# ---- hub outline, one closed path ----
# Going round: arc along the rim, dive in to the slot bottom, round the end,
# back out, arc to the next slot.
hw = SLOT_W / 2
t_out = math.sqrt(HUB_R ** 2 - hw ** 2) # where the slot side meets the rim
d = []
first = None
for k in range(N_RIB):
a = k * 2 * math.pi / N_RIB
ca, sa = math.cos(a), math.sin(a)


 

def pt(t, off):
return (HX + t * ca - off * sa, HY + t * sa + off * ca)


 

out_m, in_m = pt(t_out, -hw), pt(SLOT_R0, -hw)
in_p, out_p = pt(SLOT_R0, hw), pt(t_out, hw)
if k == 0:
first = out_m
d.append(f"M {out_m[0]:.3f} {out_m[1]:.3f}")
else:
d.append(f"A {HUB_R:.3f} {HUB_R:.3f} 0 0 1 {out_m[0]:.3f} {out_m[1]:.3f}")
d.append(f"L {in_m[0]:.3f} {in_m[1]:.3f}")
d.append(f"A {hw:.3f} {hw:.3f} 0 0 1 {in_p[0]:.3f} {in_p[1]:.3f}")
d.append(f"L {out_p[0]:.3f} {out_p[1]:.3f}")
d.append(f"A {HUB_R:.3f} {HUB_R:.3f} 0 0 1 {first[0]:.3f} {first[1]:.3f} Z")
paths.append(" ".join(d))


 

cut = 2 * math.pi * HUB_R + N_RIB * (2 * (t_out - SLOT_R0) + math.pi * hw)


 

# ---- fit test comb ----
W = 2 * HUB_R + 10
H = 2 * HUB_R + 10
if MAKE_TEST_COMB:
cw, ch, tooth = 10.0, 22.0, 6.0
x0, y0 = 5.0, H + 8.0
total_w = COMB_N * (cw + COMB_START) + 10
paths.append(f"M {x0:.3f} {y0:.3f} L {x0 + total_w:.3f} {y0:.3f} "
f"L {x0 + total_w:.3f} {y0 + ch:.3f} L {x0:.3f} {y0 + ch:.3f} Z")
x = x0 + 5.0
for i in range(COMB_N):
w = COMB_START + i * COMB_STEP
paths.append(f"M {x:.3f} {y0:.3f} L {x + w:.3f} {y0:.3f} "
f"L {x + w:.3f} {y0 + tooth + 6:.3f} L {x:.3f} {y0 + tooth + 6:.3f} Z")
cut += 2 * (w + tooth + 6)
x += w + cw
H = y0 + ch + 5
W = max(W, x0 + total_w + 5)


 

svg = [f'<svg xmlns="http://www.w3.org/2000/svg" width="{W:.0f}mm" height="{H:.0f}mm" '
f'viewBox="0 0 {W:.0f} {H:.0f}">', '<g fill="none" stroke="#000000" stroke-width="0.1">']
svg += [f'<path d="{p}"/>' for p in paths]
svg.append('</g></svg>')
open(OUT, "w").write("\n".join(svg))


 

print(f"{OUT} written")
print(f"hub {2 * HUB_R:.0f} mm across, {N_RIB} open slots")
print(f"slot width {SLOT_W:.2f} mm for {T_MEASURED} mm plywood "
f"({CLEARANCE} mm clearance)")
print(f"slot depth {HUB_R - SLOT_R0:.0f} mm, tooth {2 * math.pi * HUB_R / N_RIB - SLOT_W:.1f} mm wide")
print(f"solid middle {2 * SLOT_R0:.0f} mm across")
if MAKE_TEST_COMB:
widths = ", ".join(f"{COMB_START + i * COMB_STEP:.1f}" for i in range(COMB_N))
print(f"test comb slots: {widths} mm")
print(f"cut length {cut / 1000:.2f} m - cut TWO of the hub")
print(f"sheet {W:.0f} x {H:.0f} mm")


 


 

"""
Ripple lamp - top disc generator.


 

Cuts a 300 mm disc with one long variable-pitch spiral slot in it.
Once it comes off the laser the whole thing is basically one 6.6 m
ribbon held at the hub and at the rim, so it can drape over the ribs.
"""


 

import math


 

# ---- disc ----
R = 150.0 # disc radius, so 300 mm across
R_START = 15.0 # spiral starts here, everything inside stays solid
R_END = 138.5 # spiral stops here, the rest is my glue land for the cylinder
P0 = 6.0 # radial pitch near the centre
P1 = 11.0 # radial pitch out at the rim
SLOT_W = 2.0 # slot width, this is also my closing budget when it bends
T = 3.5 # actual measured thickness of my plywood


 

# ---- wave shape ---
# I'm using the J0 Bessel function instead of a cosine because that's the real
# shape a drop makes on a round membrane, and the peak comes out gentler.
A0 = 9.0 # amplitude
KZ = 13.3237 # 3rd max of J0 -> dip in the middle, dip at the rim
K = KZ / R


 

# --- transport tabs ----
# Without these the spiral unwinds the second I lift it off the bed.
# They all sit on one radius so I can snap them with a single pass of a blade.
TAB_ANGLE = 0.0
TAB_W = 0.6


 

OUT = "disc.svg"



 

def bessel(n, x, steps=1500):
"""J_n via the integral form. No scipy needed."""
total = 0.0
for i in range(steps + 1):
t = math.pi * i / steps
w = 1.0 if 0 < i < steps else 0.5
total += w * math.cos(n * t - x * math.sin(t))
return total / steps



 

# Precompute the profile once, it's slow otherwise.
N = 20000
STEP = R / N
HEIGHT = [-A0 * bessel(0, K * STEP * i) for i in range(N + 1)]



 

def height(r):
return HEIGHT[max(0, min(N, int(r / STEP)))]



 

def curvature(r, h=1.0):
return (height(r + h) - 2 * height(r) + height(r - h)) / h ** 2



 

def pitch(r):
"""Tighter pitch in the middle where the wood has to work hardest."""
u = (r - R_START) / (R_END - R_START)
return P0 + (P1 - P0) * max(0.0, min(1.0, u))



 

# ---- walk the spiral ----
CX = CY = 155.0
polar = []
theta, r = 0.0, R_START
d_theta = 0.0008
while r < R_END:
polar.append((theta, r))
r += pitch(r) / (2 * math.pi) * d_theta
theta += d_theta
polar.append((theta, R_END))
TURNS = theta / (2 * math.pi)


 

points = [(CX + rr * math.cos(th), CY + rr * math.sin(th)) for th, rr in polar]


 

# running length along the centreline, I need it to place the tabs
length = [0.0]
for i in range(1, len(points)):
length.append(length[-1] + math.hypot(points[i][0] - points[i - 1][0],
points[i][1] - points[i - 1][1]))
RIBBON = length[-1]


 

# every time the spiral crosses the tab radius, leave a bridge
gaps = []
for i in range(1, len(polar)):
a_prev = (polar[i - 1][0] - TAB_ANGLE) % (2 * math.pi)
a_now = (polar[i][0] - TAB_ANGLE) % (2 * math.pi)
if a_now < a_prev and 15 < length[i] < RIBBON - 15:
gaps.append((length[i] - TAB_W / 2, length[i] + TAB_W / 2))



 

def slot_outline(seg):
"""Offset a run of centreline points both ways and cap the ends round."""
hw = SLOT_W / 2
left, right = [], []
n = len(seg)
for i in range(n):
a, b = max(0, i - 1), min(n - 1, i + 1)
dx, dy = seg[b][0] - seg[a][0], seg[b][1] - seg[a][1]
m = math.hypot(dx, dy)
if m == 0:
continue
nx, ny = -dy / m, dx / m
left.append((seg[i][0] + hw * nx, seg[i][1] + hw * ny))
right.append((seg[i][0] - hw * nx, seg[i][1] - hw * ny))
d = "M " + " L ".join(f"{p[0]:.3f} {p[1]:.3f}" for p in left)
d += f" A {hw:.3f} {hw:.3f} 0 0 1 {right[-1][0]:.3f} {right[-1][1]:.3f}"
d += " L " + " L ".join(f"{p[0]:.3f} {p[1]:.3f}" for p in reversed(right[:-1]))
d += f" A {hw:.3f} {hw:.3f} 0 0 1 {left[0][0]:.3f} {left[0][1]:.3f} Z"
return d



 

paths = [f"M {CX - R:.3f} {CY:.3f} A {R:.3f} {R:.3f} 0 1 1 {CX + R:.3f} {CY:.3f} "
f"A {R:.3f} {R:.3f} 0 1 1 {CX - R:.3f} {CY:.3f} Z"]
cut = 2 * math.pi * R


 

# break the spiral into runs, skipping the tab positions
run = []
for i, p in enumerate(points):
in_gap = any(g0 <= length[i] <= g1 for g0, g1 in gaps)
if in_gap:
if len(run) > 3:
paths.append(slot_outline(run))
cut += 2 * (length[i] - length[i - len(run)]) + math.pi * SLOT_W
run = []
else:
run.append(p)
if len(run) > 3:
paths.append(slot_outline(run))
cut += 2 * (length[-1] - length[-len(run)]) + math.pi * SLOT_W


 

SIZE = 310
svg = [f'<svg xmlns="http://www.w3.org/2000/svg" width="{SIZE}mm" height="{SIZE}mm" '
f'viewBox="0 0 {SIZE} {SIZE}">', '<g fill="none" stroke="#000000" stroke-width="0.1">']
svg += [f'<path d="{d}"/>' for d in paths]
svg.append('</g></svg>')
open(OUT, "w").write("\n".join(svg))


 

# ---- sanity check before I waste a sheet ----
print(f"{OUT} written")
print(f"single spiral, {TURNS:.1f} turns, pitch {P0} -> {P1} mm")
print(f"ribbon length {RIBBON / 1000:.2f} m")
print(f"{len(gaps)} transport tabs, {TAB_W} mm each")
print(f"solid centre {2 * (R_START - SLOT_W / 2):.0f} mm across, "
f"solid rim {R - R_END - SLOT_W / 2:.1f} mm wide")
print()
print(" r pitch height slot closes ribbon w/t")
for rr in (15, 25, 43, 60, 79, 100, 114, 130, 150):
c = abs(curvature(rr))
p = pitch(rr)
print(f"{rr:5.0f} {p:6.1f} {height(rr) - height(R):+8.2f} "
f"{T * c * p:9.2f} mm {(p - SLOT_W) / T:9.2f}")
print()
print(f"wave height {max(HEIGHT) - min(HEIGHT):.1f} mm")
print(f"total cut {cut / 1000:.1f} m")


 


 

"""
Ripple lamp - base ring generator.


 

A flat strip with a living hinge cut into it, so it rolls up into the ring the
disc sits on. The LED strip goes on the inside face near the top and fires
inward across the underside of the waves.


 

The strip is 875 mm around, which doesn't fit my sheet, so it comes out in
three pieces joined with splice plates on the inside.
"""


 

import math


 

# ---- geometry that has to agree with the disc ----
R = 150.0 # flat disc radius
A0 = 9.0
KZ = 13.3237
K = KZ / R
T = 3.5 # measured plywood thickness


 

RING_OUT_R = 141.0 # outside radius of the ring
HEIGHT_MM = 45.0 # how tall the ring is
N_SEG = 3 # pieces, each has to fit my sheet


 

# ---- living hinge ----
HINGE_PITCH = 8.0 # spacing within one row
MARGIN = 3.0 # keep slits off the top and bottom edges
OVERLAP = 3.0 # how much the two rows overlap in the middle
END_SOLID = 15.0 # uncut zone at each end, that's the glue land


 

SPLICE_W, SPLICE_H = 30.0, 39.0


 

OUT = "ring.svg"



 

def bessel(n, x, steps=1500):
total = 0.0
for i in range(steps + 1):
t = math.pi * i / steps
w = 1.0 if 0 < i < steps else 0.5
total += w * math.cos(n * t - x * math.sin(t))
return total / steps



 

# Work out how big the disc really ends up and where its solid rim lands,
# because the ring has to glue onto that band and nowhere else.
N = 30000
STEP = R / N
H = [-A0 * bessel(0, K * STEP * i) for i in range(N + 1)]
RIM = H[N]
H = [h - RIM for h in H]


 

walk = [0.0]
for i in range(1, N + 1):
walk.append(walk[-1] + math.hypot(STEP, H[i] - H[i - 1]))



 

def projected(flat_r):
"""Where a point that sits at flat_r on the flat disc ends up once curved."""
for i in range(N + 1):
if walk[i] >= flat_r:
return STEP * i
return R



 

R_PROJ = projected(R) # finished disc radius
RIM_INNER = projected(139.5) # inner edge of the solid rim, curved
RING_IN_R = RING_OUT_R - T
MID_R = RING_OUT_R - T / 2
LENGTH = 2 * math.pi * MID_R # measured on the middle of the wall
SEG_L = LENGTH / N_SEG


 

paths = []
cut = 0.0



 

def rect(x, y, w, h):
return (f"M {x:.3f} {y:.3f} L {x + w:.3f} {y:.3f} "
f"L {x + w:.3f} {y + h:.3f} L {x:.3f} {y + h:.3f} Z")



 

# ---- the three strips ----
# Two rows of slits, offset by half a pitch, overlapping in the middle. The
# overlap is what actually does the bending - the wood twists there.
row_a = (MARGIN, (HEIGHT_MM + OVERLAP) / 2)
row_b = ((HEIGHT_MM - OVERLAP) / 2, HEIGHT_MM - MARGIN)


 

for seg in range(N_SEG):
x0, y0 = 3.0, 3.0 + seg * (HEIGHT_MM + 8)
paths.append(rect(x0, y0, SEG_L, HEIGHT_MM))
cut += 2 * (SEG_L + HEIGHT_MM)
x = x0 + END_SOLID
row = 0
while x < x0 + SEG_L - END_SOLID:
a, b = row_a if row % 2 == 0 else row_b
paths.append(f"M {x:.3f} {y0 + a:.3f} L {x:.3f} {y0 + b:.3f}")
cut += b - a
x += HINGE_PITCH / 2
row += 1


 

# ---- splice plates, glued on the inside over each butt joint ----
sy = 3.0 + N_SEG * (HEIGHT_MM + 8) + 5
for k in range(N_SEG):
paths.append(rect(3.0 + k * (SPLICE_W + 6), sy, SPLICE_W, SPLICE_H))
cut += 2 * (SPLICE_W + SPLICE_H)


 

W = max(3.0 + SEG_L + 3, 3.0 + N_SEG * (SPLICE_W + 6) + 3)
HT = sy + SPLICE_H + 3
svg = [f'<svg xmlns="http://www.w3.org/2000/svg" width="{W:.0f}mm" height="{HT:.0f}mm" '
f'viewBox="0 0 {W:.0f} {HT:.0f}">',
'<g fill="none" stroke="#000000" stroke-width="0.1">']
svg += [f'<path d="{p}"/>' for p in paths]
svg.append('</g></svg>')
open(OUT, "w").write("\n".join(svg))


 

print(f"{OUT} written")
print(f"finished disc {2 * R_PROJ:.0f} mm across")
print(f"ring {2 * RING_OUT_R:.0f} mm outside, {2 * RING_IN_R:.0f} mm inside")
print(f"overhang {R_PROJ - RING_OUT_R:.1f} mm all round")
print(f"strip {LENGTH:.0f} mm long -> {N_SEG} pieces of {SEG_L:.1f} mm")
print()
print("checks:")
print(f" disc solid rim sits between r={RIM_INNER:.1f} and r={R_PROJ:.1f} once curved")
print(f" ring wall sits between r={RING_IN_R:.1f} and r={RING_OUT_R:.1f}")
ok = RIM_INNER <= RING_IN_R and RING_OUT_R <= R_PROJ
print(f" wall lands on the glue band: {'yes' if ok else 'NO'}")
print(f" ribs must stop before r={RING_IN_R - 1:.1f} - set MARGIN in rib.py to 10")
print(f" bend radius {MID_R:.1f} mm, strain without the hinge would be "
f"{T / 2 / MID_R * 100:.2f}%")
print(f" LED strip runs about {2 * math.pi * RING_IN_R:.0f} mm around the inside")
print()
print(f"cut length {cut / 1000:.2f} m")
print(f"sheet {W:.0f} x {HT:.0f} mm")


 


 

Komentar

Remix

Anda mungkin juga menyukai