3D Workspace
Home
Assets
Affiliate Program
Sign up/Log in
?
Upgrade
DCC Bridge
3D Creation Made Simple
Text & Image to 3D Model in seconds
One-Click Texturing & Smart Detail Editing
Free Credits Monthly
Start Free
Anonymous1764971101
12-05 21:45
Model Name
white staircase 3d model
Tags
props
rendering
realistic
Prompt
# kallax_perfume_stand.py # CadQuery parametric model for a 4-tier perfume stand that fits IKEA Kallax, # with equal-width tiers, embedded LED channels, and rear cable trunk. # Exports STLs for each part. import cadquery as cq from pathlib import Path # -------------------- Parameters -------------------- # Kallax constraints footprint_x = 320.0 footprint_y = 320.0 overall_height = 240.0 # Tiers: all same width, stepped backward tier_count = 4 tier_width_usable = 300.0 # usable shelf width for bottles tier_margin = 10.0 # side margin beyond usable tier_total_width = tier_width_usable + 2 * tier_margin tier_depth = 80.0 # usable shelf depth per tier tier_thickness = 8.0 # slab thickness # Riser heights (half-step increase per tier: 40 → 60 → 80 → 100) riser_h = [40.0, 60.0, 80.0, 100.0] # Backward step offset per tier (front-to-back stagger) step_back = 28.0 # LED integration led_strip_w = 10.0 led_channel_w = 12.0 led_channel_d = 3.0 led_channel_offset_from_back = 18.0 # measured from rear edge of slab # Diffuser top skin over channel (thin region to scatter light) diffuser_skin_t = 2.0 diffuser_skin_pad = 3.0 # Light baffle behind channel baffle_t = 0.8 baffle_gap = 2.0 # Rear cable trunk trunk_w = 28.0 trunk_d = 40.0 trunk_wall = 3.0 trunk_pass_w = 6.0 trunk_pass_h = 8.0 trunk_pass_y_offset = 8.0 # position from tier rear edge # Base & feet base_t = 10.0 base_counterweight_cavity = True cavity_w = 140.0 cavity_d = 60.0 cavity_h = 8.0 pad_w = 16.0 pad_d = 16.0 pad_h = 4.0 # Side cheeks cheek_thickness = 6.0 cheek_depth = tier_depth + step_back * (tier_count - 1) + trunk_d + 10.0 cheek_height = overall_height front_edge_radius = 6.0 # R6 on front slab edge # Screw bosses (for M3 heat-set inserts) boss_d = 6.2 boss_h = 8.0 insert_d = 4.2 boss_offset_x = 20.0 # Tolerances clearance = 0.2 lip = 2.5 # tongue depth for joints # Positioning helpers front_offset_y = 20.0 # distance from front of base to first tier front # -------------------- Utility -------------------- def rounded_front_slab(width, depth, thickness, radius): """ Create a rectangular slab with a radiused front edge. """ slab = cq.Workplane("XY").box(width, depth, thickness) # Add front radius by cutting a quarter cylinder along the front edge # Simpler: chamfer-like visual via fillet on top front edge slab = slab.edges("|Z and <Y").fillet(radius) return slab def add_led_channel(slab, slab_w, slab_d, slab_t): """ Cut LED recess from the bottom, positioned near the back. Also add thin top skin region to encourage diffusion. """ # Channel rectangle channel_x = slab_w - led_channel_offset_from_back - led_channel_w channel_y = (slab_w - tier_width_usable) / 2.0 # center across usable width channel = ( cq.Workplane("XY") .translate((channel_x + led_channel_w / 2.0, channel_y + tier_width_usable / 2.0, -0.1)) .box(led_channel_w, tier_width_usable, led_channel_d + 0.2) ) # Subtract channel slab = slab.cut(channel) # Thin top skin region above channel to guide slicing skin_x = channel_x - diffuser_skin_pad skin_y = channel_y - diffuser_skin_pad skin = ( cq.Workplane("XY") .translate((skin_x + (led_channel_w + 2 * diffuser_skin_pad) / 2.0, skin_y + (tier_width_usable + 2 * diffuser_skin_pad) / 2.0, slab_t - diffuser_skin_t)) .box(led_channel_w + 2 * diffuser_skin_pad, tier_width_usable + 2 * diffuser_skin_pad, diffuser_skin_t) ) slab = slab.union(skin) # Baffle plate behind channel baffle_x = channel_x - baffle_gap - baffle_t baffle = ( cq.Workplane("XY") .translate((baffle_x + baffle_t / 2.0, channel_y + tier_width_usable / 2.0, 0)) .box(baffle_t, tier_width_usable, slab_t) ) slab = slab.union(baffle) return slab def add_rear_pass_through(slab, slab_w, slab_d, slab_t): """ Create pass-through at rear into trunk for wires. """ pass_x = slab_w - 3.0 pass_y = slab_d - trunk_pass_y_offset passthru = ( cq.Workplane("XY") .translate((pass_x + 2.0, pass_y + trunk_pass_w / 2.0, -0.1)) .box(4.0, trunk_pass_w, slab_t + 0.2) ) return slab.cut(passthru) def add_riser_block(slab, slab_w, slab_d, rh): """ Add a riser reinforcement under rear of slab. """ rb_y = slab_d - 18.0 rb = ( cq.Workplane("XY") .translate(((slab_w - tier_width_usable)/2.0 + tier_width_usable / 2.0, rb_y + 9.0, -rh)) .box(tier_width_usable, 18.0, rh) ) return slab.union(rb) def add_screw_bosses(slab, slab_w): """ Add two screw bosses under the slab (left/right). """ left_boss = ( cq.Workplane("XY") .translate((boss_offset_x, 18.0, -boss_h)) .cylinder(boss_h, boss_d/2.0) .cut(cq.Workplane("XY").translate((boss_offset_x, 18.0, -boss_h-0.1)).cylinder(boss_h+0.2, insert_d/2.0)) ) right_boss_x = slab_w - boss_offset_x right_boss = ( cq.Workplane("XY") .translate((right_boss_x, 18.0, -boss_h)) .cylinder(boss_h, boss_d/2.0) .cut(cq.Workplane("XY").translate((right_boss_x, 18.0, -boss_h-0.1)).cylinder(boss_h+0.2, insert_d/2.0)) ) return slab.union(left_boss).union(right_boss) # -------------------- Parts -------------------- def make_tier(n): # Tier indexing (1..4) rh = riser_h[n - 1] y_back_offset = step_back * (n - 1) slab_w = tier_total_width slab_d = tier_depth slab_t = tier_thickness # Base Z position for this tier z = base_t + sum(riser_h[:n - 1]) # Create slab slab = rounded_front_slab(slab_w, slab_d, slab_t, front_edge_radius) slab = add_led_channel(slab, slab_w, slab_d, slab_t) slab = add_rear_pass_through(slab, slab_w, slab_d, slab_t) slab = add_riser_block(slab, slab_w, slab_d, rh) slab = add_screw_bosses(slab, slab_w) # Place in assembly coordinates x = (footprint_x - slab_w) / 2.0 y = front_offset_y + y_back_offset return slab.translate((x, y, z)) def make_base(): base = cq.Workplane("XY").box(footprint_x, footprint_y, base_t) # Counterweight cavity if base_counterweight_cavity: cx = (footprint_x - cavity_w) / 2.0 cy = footprint_y - cavity_d - 10.0 cavity = ( cq.Workplane("XY") .translate((cx + cavity_w/2.0, cy + cavity_d/2.0, 1.0)) .box(cavity_w, cavity_d, cavity_h) ) base = base.cut(cavity) # Foot pads (3-point) pad1 = cq.Workplane("XY").translate((20 + pad_w/2.0, 20 + pad_d/2.0, -pad_h/2.0)).box(pad_w, pad_d, pad_h) pad2 = cq.Workplane("XY").translate((footprint_x - 20 - pad_w/2.0, 20 + pad_d/2.0, -pad_h/2.0)).box(pad_w, pad_d, pad_h) pad3 = cq.Workplane("XY").translate((20 + pad_w/2.0, footprint_y - 20 - pad_d/2.0, -pad_h/2.0)).box(pad_w, pad_d, pad_h) base = base.union(pad1).union(pad2).union(pad3) # Registration lips for cheeks left_lip = cq.Workplane("XY").translate(((footprint_x - tier_total_width)/2.0 - cheek_thickness + lip/2.0, front_offset_y + cheek_depth/2.0, base_t - lip/2.0)).box(lip, cheek_depth, lip) right_lip = cq.Workplane("XY").translate(((footprint_x + tier_total_width)/2.0 + lip/2.0, front_offset_y + cheek_depth/2.0, base_t - lip/2.0)).box(lip, cheek_depth, lip) base = base.union(left_lip).union(right_lip) return base def make_trunk(): x = (footprint_x - tier_total_width)/2.0 + tier_total_width y = front_offset_y + step_back*(tier_count-1) + tier_depth trunk_height = overall_height - base_t trunk = cq.Workplane("XY").translate((x + trunk_w/2.0, y + trunk_d/2.0, base_t + trunk_height/2.0)).box(trunk_w, trunk_d, trunk_height) # Hollow interior inner = cq.Workplane("XY").translate((x + trunk_w/2.0, y + trunk_d/2.0, base_t + trunk_height/2.0)).box(trunk_w - 2*trunk_wall, trunk_d - 2*trunk_wall, trunk_height - 2*trunk_wall) trunk = trunk.cut(inner) # Pass-throughs per tier for i in range(tier_count): zpos = base_t + sum(riser_h[:i]) + tier_thickness/2.0 passthru = cq.Workplane("XY").translate((x - 0.1 + trunk_wall/2.0, y + trunk_pass_y_offset + trunk_pass_w/2.0, zpos + trunk_pass_h/2.0)).box(trunk_wall+0.2, trunk_pass_w, trunk_pass_h) trunk = trunk.cut(passthru) # Connector bay at base rear conn = cq.Workplane("XY").translate((x + trunk_w - 12.0/2.0, y + trunk_d - 22.0/2.0, base_t - 0.1)).box(12.2, 22.2, trunk_wall+0.2) trunk = trunk.cut(conn) return trunk def make_cheek(left=True): x = (footprint_x - tier_total_width)/2.0 + (-cheek_thickness if left else tier_total_width) y = front_offset_y cheek = cq.Workplane("XY").translate((x + cheek_thickness/2.0, y + cheek_depth/2.0, overall_height/2.0)).box(cheek_thickness, cheek_depth, overall_height) # Groove for base registration groove = cq.Workplane("XY").translate((x + cheek_thickness/2.0, y + cheek_depth/2.0, base_t - (lip+0.2)/2.0)).box(cheek_thickness, cheek_depth, lip+0.2) cheek = cheek.cut(groove) # Light relief pockets for look relief = cq.Workplane("XY").translate((x + cheek_thickness/2.0, y + 6 + 12/2.0, 40 + 120/2.0)).box(cheek_thickness, 12, 120) cheek = cheek.cut(relief) return cheek # -------------------- Build parts -------------------- base = make_base() tiers = [make_tier(n) for n in range(1, tier_count+1)] trunk = make_trunk() cheekL = make_cheek(True) cheekR = make_cheek(False) # -------------------- Export STLs -------------------- outdir = Path(__file__).parent / "stl" outdir.mkdir(exist_ok=True) cq.exporters.export(base, outdir / "base.stl") for i, t in enumerate(tiers, start=1): cq.exporters.export(t, outdir / f"tier{i}.stl") cq.exporters.export(trunk, outdir / "trunk.stl") cq.exporters.export(cheekL, outdir / "cheek_left.stl") cq.exporters.export(cheekR, outdir / "cheek_right.stl") print(f"Exported STLs to: {outdir.resolve()}")
Detailed Info
Related Models
Enter invite code
Enter invite code to get credits!