1 /* 2 Copyright (C) 2014, 2015, 2016 Paul Boddie 3 4 This program is free software; you can redistribute it and/or modify it under 5 the terms of the GNU General Public License as published by the Free Software 6 Foundation; either version 3 of the License, or (at your option) any later 7 version. 8 9 This program is distributed in the hope that it will be useful, but WITHOUT 10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 12 more details. 13 14 You should have received a copy of the GNU General Public License along with 15 this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 module cartridge() 19 { 20 $fn = 50; 21 22 /* Model configurations. Do not edit! */ 23 24 ROM_CARTRIDGE = 0; 25 WIDE_CARTRIDGE = 1; 26 27 /* 28 Configure the generated shapes. 29 For the absolute simplest shape (for basic 3D printers)... 30 31 BACK_CAVITY = 0; FRONT_LABEL_INSET = 0; TOP_LABEL_INSET = 0; 32 GROOVE = 0; ROUND_EDGES = 0; ROUND_CONNECTING_EDGES = 0 33 34 Leave all options enabled for more capable 3D printers. 35 */ 36 37 /* A cosmetic cut-out to match the necessary interior cavity. */ 38 BACK_CAVITY = 1; 39 40 /* Optional insets for labels. */ 41 FRONT_LABEL_INSET = 1; 42 TOP_LABEL_INSET = 1; 43 44 /* Optional groove between the pieces to match the Electron. */ 45 GROOVE = 1; 46 47 /* Optional rounding of the external edges. */ 48 ROUND_EDGES = 1; 49 50 /* Recommended rounding of connection edges. */ 51 ROUND_CONNECTING_EDGES = 1; 52 53 /* Set this to the desired model type. */ 54 55 MODEL = ROM_CARTRIDGE; 56 57 /* Set this where a shorter PCB is to be used. */ 58 59 SHORT_PCB = 0; 60 61 /* 62 Options for checking. Some useful combinations... 63 64 Check feature alignment: APART = 0; BACK_SURFACE = 0; FRONT_SURFACE = 0; 65 Check PCB alignment: PCB = 1; BACK_SURFACE = 0; 66 */ 67 68 BACK = 1; BACK_SURFACE = 1; 69 FRONT = 1; FRONT_SURFACE = 1; 70 TOP_SURFACE = 1; 71 72 /* For printing: APART = 1; PCB = 0; */ 73 74 APART = 1; 75 PCB = 0; 76 77 /* To save time (before printing): FILLET = 0; */ 78 79 FILLET = 1; 80 81 /* 82 Rounding/fillet radius and additional margin of subtracted 83 material. The additional margin helps avoid geometry problems. 84 */ 85 86 rr = 2; 87 ro = rr; 88 extra = 0.1; 89 90 groove_rr = 0.2; 91 groove_ro = groove_rr; 92 93 pcb_lug_rr = 0.5; 94 pcb_lug_ro = pcb_lug_rr; 95 96 /* A fillet performs rounding using a quarter segment of a cylinder. */ 97 98 module fillet(r, h) { 99 if (FILLET) 100 translate([0, 0, -h/2]) 101 difference() { 102 cube([r + extra, r + extra, h + extra]); 103 cylinder(r = r, h = h); 104 } 105 } 106 107 /* A fillet justified using the axes. */ 108 109 module fillet_justified(r, h) { 110 if (FILLET) 111 difference() { 112 cube([r + extra, r + extra, h + extra]); 113 cylinder(r = r, h = h); 114 } 115 } 116 117 /* A justified fillet with the extra material below the y-axis. */ 118 119 module fillet_partitioned(r, h) { 120 if (FILLET) 121 difference() { 122 translate([0, 0, -extra]) 123 cube([r + extra, r + extra, h + extra]); 124 cylinder(r = r, h = h); 125 } 126 } 127 128 module fillet_torus(radius, rounding) { 129 if (FILLET) 130 difference() { 131 cube_at((radius + extra) * 2, (radius + extra) * 2, rounding + extra, 132 0, 0, 1, 133 0, 0, 0); 134 union() { 135 rotate_extrude(convexity = 10) 136 translate([radius - rounding, 0, 0]) 137 circle(r = rounding); 138 cylinder(r = radius - rounding, h = rounding); 139 } 140 } 141 } 142 143 /* 144 Justify an object of the given dimensions, according to the given 145 factors (where 1 indicates moving the object to the positive side of an 146 axis, and -1 indicates moving it to the negative side of an axis). 147 148 NOTE: child should eventually be replaced by children. 149 */ 150 module justify(width, depth, height, wdir, ddir, hdir) { 151 translate([ 152 wdir * width / 2, 153 ddir * depth / 2, 154 hdir * height / 2]) 155 child(); 156 } 157 158 /* 159 Make a cuboid of the given dimensions, justifying it according to the given 160 factors, and moving it to the specified coordinates. 161 162 NOTE: Usage of justify within this module will not work due to recursion 163 NOTE: limitations in openscad, potentially removed in more recent 164 NOTE: releases. Thus, the justify transform is merged in here. 165 */ 166 module cube_at(width, depth, height, wdir, ddir, hdir, x, y, z) { 167 translate([ 168 x + wdir * width / 2, 169 y + ddir * depth / 2, 170 z + hdir * height / 2]) 171 cube([width, depth, height], center = true); 172 } 173 174 /* Model widths. */ 175 176 ROM_CARTRIDGE_int_payload_width = 86.0; 177 WIDE_CARTRIDGE_int_payload_width = 140.0; 178 179 /* Internal dimensions. */ 180 181 /* internal width in the payload section */ 182 int_payload_width = 183 MODEL == ROM_CARTRIDGE ? ROM_CARTRIDGE_int_payload_width : 184 MODEL == WIDE_CARTRIDGE ? WIDE_CARTRIDGE_int_payload_width : 185 ROM_CARTRIDGE_int_payload_width; 186 187 int_connector_width = 86.0; /* limited to the Plus 1 socket dimensions */ 188 int_payload_depth = 14.0; /* maximum depth in the payload section */ 189 int_connector_depth = 11.5; /* maximum depth in the connector section */ 190 int_payload_height = 50.8; /* space between the top and the floor */ 191 int_connector_height = 13.5; /* vertical offset of bottom/floor of payload area */ 192 193 /* Side thicknesses. */ 194 195 front = 2; 196 payload_back = 1; /* in the payload area the thickness is reduced */ 197 connector_back = 3.5; /* the back cavity requires a thicker back wall */ 198 top = 3; 199 side = 2; /* increased from 1.5 for 3D printing reliability */ 200 bottom = 1; /* thickness of floor of payload area */ 201 202 /* How much extra depth the back provides for mating with the front. */ 203 204 groove_width_extra = 2.0; /* the depth of the groove accommodating the front rim */ 205 front_back_overlap = 1.0; 206 groove_width_overlap = front_back_overlap + groove_width_extra; 207 208 /* Division of pieces into front and back, defining the extents. */ 209 210 int_front_depth = 4.5; 211 front_depth = int_front_depth + front; 212 int_payload_back_depth = int_payload_depth - int_front_depth + front_back_overlap; 213 int_connector_back_depth = int_connector_depth - int_front_depth + front_back_overlap; 214 back_depth = int_payload_back_depth + payload_back; 215 216 /* Cartridge dimensions. */ 217 218 payload_width = int_payload_width + side + side; 219 connector_width = int_connector_width + side + side; 220 payload_height = top + int_payload_height; 221 connector_height = bottom + int_connector_height; 222 height = payload_height + bottom + int_connector_height; 223 depth = int_payload_depth + front + payload_back; 224 upper_extent = height / 2; 225 lower_extent = -upper_extent; 226 int_payload_upper_extent = upper_extent - top; 227 int_payload_lower_extent = lower_extent + int_connector_height + bottom; 228 229 /* Where the payload is wider than the connector, the payload expands to the left. */ 230 231 int_payload_right_extent = int_connector_width / 2; 232 int_payload_left_extent = int_payload_right_extent - int_payload_width; 233 payload_left_extent = int_payload_left_extent - side; 234 payload_right_extent = int_payload_right_extent + side; 235 payload_centre = (payload_left_extent + payload_right_extent) / 2; 236 237 /* Cartridge surfaces. */ 238 239 front_side = side; 240 front_left = front_side; 241 front_right = front_side; 242 back_side = side; 243 back_left = back_side; 244 back_right = back_side; 245 246 /* Label details. */ 247 248 front_label_width = payload_width - side - side - 3.0; 249 front_label_height = 46.0; 250 front_label_depth = 1.0; 251 front_label_offset_from_bottom = 19.5; 252 front_label_centre = payload_centre; 253 front_label_left_extent = front_label_centre - front_label_width / 2; 254 255 top_label_width = front_label_width; 256 top_label_height = 11.5; /* the height as seen from above */ 257 top_label_depth = front_label_depth; 258 top_label_offset_from_front = 2.5; 259 top_label_centre = payload_centre; 260 top_label_left_extent = top_label_centre - front_label_width / 2; 261 262 /* 263 The groove around the sides and top. This is extended to allow the 264 pieces to fit together, and this extension is generated regardless of 265 whether the visible groove is enabled or not. 266 */ 267 268 groove_width_exposed = GROOVE ? 1.5 : 0; 269 groove_width_normal = groove_width_exposed + front_back_overlap; 270 groove_depth = 1.0; /* how deep the groove goes into each side */ 271 272 /* Additional cutting to mate the back and front. */ 273 274 top_groove_width = groove_width_overlap; 275 top_groove_depth = 2.0; 276 277 /* 278 Space for the inner edge of the back inside the front. 279 Offsets are measured from the outside surfaces. 280 */ 281 282 inner_top_front_cutout_width = int_payload_width; 283 inner_top_front_cutout_depth = top_groove_width; 284 inner_top_front_cutout_height = top - top_groove_depth; 285 286 inner_payload_front_cutout_height = payload_height - top_groove_depth; 287 inner_payload_front_cutout_width = front_side - groove_depth; 288 inner_payload_front_cutout_depth = groove_width_overlap; 289 290 inner_connector_front_cutout_height = connector_height; 291 inner_connector_front_cutout_width = front_side - groove_depth; 292 inner_connector_front_cutout_depth = groove_width_overlap; 293 294 /* 295 The back cavity is the indented part at the bottom of the back of the 296 cartridge. It appears to be mechanically necessary, making sure that 297 cartridges cannot be plugged in the wrong way round. 298 */ 299 300 back_cavity_width = 68.0; 301 back_cavity_inner_width = 65.0; 302 back_cavity_offset_from_inner_left = 9.0; 303 back_cavity_inner_offset_from_inner_left = 10.5; 304 back_cavity_height = 13.5; 305 back_cavity_inner_height = 12.0; 306 back_cavity_depth = 1.5; 307 308 /* 309 The effect of the cavity on the inside of the case. In principle, the 310 interior of the case could be straight since the plastic guides of the 311 Plus 1 socket are outside the case. 312 */ 313 314 inner_back_slope_depth = 2.5; 315 inner_back_slope_width = inner_back_slope_depth; 316 inner_back_slope_max_offset = 10.5; 317 inner_back_slope_min_offset = inner_back_slope_max_offset - inner_back_slope_width; 318 319 /* The tapering off of the inner back edge. */ 320 321 inner_back_edge_width = 69.0; 322 inner_back_edge_height = 3.0; 323 inner_back_edge_depth = 1.5; 324 325 /* The tapering off of the inner front edge. */ 326 327 inner_front_edge_width = connector_width - front_side * 2; 328 inner_front_edge_height = 3.0; 329 inner_front_edge_depth = 1.5; 330 331 /* 332 The cutout in the floor of the back of the cartridge that accommodates 333 the edge connector. 334 */ 335 336 edge_connector_cutout_back_depth = 3.0; 337 edge_connector_cutout_back_width = 57.5; 338 339 /* 340 The cutouts in the floor of the front of the cartridge that produce a 341 kind of tab that guides the edge connector into place in the back cutout. 342 */ 343 344 edge_connector_cutout_front_depth = front_back_overlap; 345 edge_connector_cutout_front_width = (int_connector_width - edge_connector_cutout_back_width) / 2; 346 347 /* 348 Edge connectors are themselves 0.05" or approximately 1.27mm in 349 thickness according to the Acorn Electron Cartridge Interface Specification 350 (Acorn Support Application Group Note 014). 351 */ 352 353 /* Extra internal features. Depths include front and back surfaces. */ 354 355 pcb_back_support_width = 1.2; 356 pcb_back_support_depth = back_depth - 357 edge_connector_cutout_back_depth; 358 pcb_back_support_height = height - int_connector_height - top - bottom; 359 360 pcb_front_support_width = 1.2; 361 pcb_front_support_depth = int_front_depth; 362 pcb_front_support_height = pcb_back_support_height; 363 364 /* 365 Features measured from the Stardot Dual ROM Adaptor cartridge board 366 dimensions diagram. Offsets are from inside the bottom "floor". 367 */ 368 369 pcb_back_support_bump_width = pcb_front_support_width; 370 pcb_back_support_bump_depth = 1.5; 371 pcb_back_support_left_bump_height = 13.2; 372 pcb_back_support_right_bump_height = 10.7; 373 pcb_back_support_left_bump_offset_from_bottom = 15.1; 374 pcb_back_support_right_bump_offset_from_bottom = 17.6; 375 376 /* Configured by SHORT_PCB. */ 377 pcb_back_support_top_bump_height = 3.8; 378 379 /* Move the PCB support towards the centre. */ 380 pcb_support_margin = 1.75; 381 pcb_support_offset_from_centre = edge_connector_cutout_back_width / 2 382 - pcb_support_margin; 383 384 /* 385 The PCB lugs protrude through the holes in the PCB. By extending both 386 the front and back lugs by the depth of the back support bump, they 387 overlap by that amount. The "mating" depth is defined as that amount 388 plus an additional amount for adhesion. 389 */ 390 391 pcb_lug_mating_depth = pcb_back_support_bump_depth + 1.0; 392 393 pcb_lug_depth = pcb_back_support_depth + 394 pcb_lug_mating_depth; 395 pcb_lug_inner_radius = 1.0; 396 pcb_lug_outer_radius = 5.5 / 2; 397 pcb_lug_offset_from_bottom = 14.35; 398 pcb_lug_offset_from_inside = 5.55; 399 400 /* 401 The cross detail on the back lug is intended to resist the front lug, 402 and thus starts at the point that the front lug ends. 403 */ 404 405 pcb_lug_cross_width = 6.7; 406 pcb_lug_cross_depth = pcb_back_support_depth + pcb_back_support_bump_depth - 407 pcb_lug_mating_depth; 408 pcb_lug_cross_height = 1.4; 409 410 pcb_front_lug_depth = pcb_front_support_depth + 411 pcb_lug_mating_depth; 412 pcb_front_lug_inner_radius = pcb_lug_outer_radius; 413 pcb_front_lug_outer_radius = pow( 414 pow(pcb_lug_cross_width / 2, 2) + 415 pow(pcb_lug_cross_height / 2, 2), 416 0.5); 417 418 wide_pcb_lug_offset_from_inside = 6.0; 419 wide_pcb_lug_offset_from_bottom = 6.0; 420 421 /* PCBs for checking. */ 422 423 pcb_width = 84.85; pcb_height = 62.5; pcb_depth = 1; 424 edge_connector_width = 56.5; edge_connector_height = 11.85; 425 426 /* 427 The rectangular part of the narrow left and right holes is smaller 428 than the "bump" in the case, but the circular parts make the overall 429 hole larger than the "bump". An extra depth is used for holes to avoid 430 surface definition problems. 431 */ 432 433 pcb_hole_margin = 0.55; 434 pcb_hole_width = 2.2; 435 pcb_hole_extra_depth = 0.1; 436 pcb_lug_hole_radius = 3.75; 437 438 pcb_left_hole_offset = pcb_back_support_left_bump_offset_from_bottom + pcb_hole_margin; 439 pcb_left_hole_height = pcb_back_support_left_bump_height - pcb_hole_margin * 2; 440 pcb_left_hole_offset_from_centre = -(pcb_support_offset_from_centre + 441 pcb_back_support_width / 2 - pcb_hole_width / 2); 442 443 pcb_right_hole_offset = pcb_back_support_right_bump_offset_from_bottom + pcb_hole_margin; 444 pcb_right_hole_height = pcb_back_support_right_bump_height - pcb_hole_margin * 2; 445 pcb_right_hole_offset_from_centre = -pcb_left_hole_offset_from_centre; 446 447 pcb_hole_depth = pcb_depth + 2 * pcb_hole_extra_depth; 448 pcb_hole_start_depth = edge_connector_cutout_back_depth + pcb_hole_extra_depth; 449 450 wide_pcb_width = 138.0; wide_pcb_height = pcb_height; wide_pcb_depth = pcb_depth; 451 wide_pcb_hole_depth = pcb_hole_depth; 452 wide_pcb_hole_start_depth = pcb_hole_start_depth; 453 454 wide_pcb_lug_hole_radius = 4.0; 455 456 /* An example feature for use with PCB testing. */ 457 458 feature_width = 15.0; feature_depth = 7.0; feature_height = 40.0; 459 feature_height_offset = 10.0; 460 461 /* Repeated constructs. */ 462 463 module pcb_support(xdir, bump_height, bump_offset) { 464 465 /* 466 Translate the support in the stated direction. 467 Since the support is already justified in the direction, the translation 468 moves the inner edge of the support to alignment with the end of the 469 edge connector cutout and then back by the PCB support margin. 470 */ 471 472 translate([xdir * 473 pcb_support_offset_from_centre, 474 edge_connector_cutout_back_depth, 475 int_payload_lower_extent]) 476 justify(pcb_back_support_width, 477 pcb_back_support_depth, 478 pcb_back_support_height, 479 xdir, 1, 1) 480 union() { 481 482 /* Underlying support strut. */ 483 484 cube([pcb_back_support_width, 485 pcb_back_support_depth, 486 pcb_back_support_height], center = true); 487 488 /* Middle bump. */ 489 490 cube_at(pcb_back_support_bump_width, 491 pcb_back_support_bump_depth, 492 bump_height, 493 0, -1, 1, 494 0, 495 -pcb_back_support_depth / 2, 496 -pcb_back_support_height / 2 + bump_offset); 497 498 /* Top bump. */ 499 500 if (SHORT_PCB) 501 cube_at(pcb_back_support_bump_width, 502 pcb_back_support_bump_depth, 503 pcb_back_support_top_bump_height, 504 0, -1, 1, 505 0, 506 -pcb_back_support_depth / 2, 507 pcb_back_support_height / 2 - 508 pcb_back_support_top_bump_height); 509 } 510 } 511 512 module pcb_lug(xdir) { 513 pcb_lug_explicit(xdir, int_connector_width / 2 - pcb_lug_offset_from_inside, 514 pcb_lug_offset_from_bottom); 515 } 516 517 module pcb_lug_wide(xdir) { 518 pcb_lug_explicit(xdir, int_payload_width / 2 - wide_pcb_lug_offset_from_inside, 519 wide_pcb_lug_offset_from_bottom); 520 pcb_lug_explicit(xdir, int_payload_width / 2 - wide_pcb_lug_offset_from_inside, 521 wide_pcb_height - edge_connector_height - wide_pcb_lug_offset_from_bottom); 522 } 523 524 module pcb_lug_explicit(xdir, pcb_lug_offset_from_centre, pcb_lug_offset_from_bottom) { 525 translate([payload_centre + xdir * pcb_lug_offset_from_centre, 526 back_depth, 527 int_payload_lower_extent + pcb_lug_offset_from_bottom 528 ]) 529 rotate([90, 0, 0]) 530 difference() { 531 532 /* Cylinder with cross. */ 533 534 union() { 535 cylinder(h=pcb_lug_depth, r=pcb_lug_outer_radius); 536 cube_at(pcb_lug_cross_width, 537 pcb_lug_cross_height, pcb_lug_cross_depth, 538 0, 0, 1, 539 0, 0, 0); 540 cube_at(pcb_lug_cross_height, 541 pcb_lug_cross_width, pcb_lug_cross_depth, 542 0, 0, 1, 543 0, 0, 0); 544 } 545 546 /* Hollowed out by a cylinder. */ 547 548 cylinder(h=pcb_lug_depth, r=pcb_lug_inner_radius); 549 550 /* Tapered off for easier connection. */ 551 552 if (ROUND_CONNECTING_EDGES) 553 translate([0, 0, pcb_lug_depth - pcb_lug_ro]) 554 fillet_torus(pcb_lug_outer_radius, pcb_lug_rr); 555 } 556 } 557 558 module pcb_front_lug(xdir) { 559 pcb_front_lug_explicit(xdir, int_connector_width / 2 - pcb_lug_offset_from_inside, 560 pcb_lug_offset_from_bottom); 561 } 562 563 module pcb_front_lug_wide(xdir) { 564 pcb_front_lug_explicit(xdir, int_payload_width / 2 - wide_pcb_lug_offset_from_inside, 565 wide_pcb_lug_offset_from_bottom); 566 pcb_front_lug_explicit(xdir, int_payload_width / 2 - wide_pcb_lug_offset_from_inside, 567 wide_pcb_height - edge_connector_height - wide_pcb_lug_offset_from_bottom); 568 } 569 570 module pcb_front_lug_explicit(xdir, pcb_lug_offset_from_centre, pcb_lug_offset_from_bottom) { 571 translate([payload_centre + xdir * pcb_lug_offset_from_centre, 572 -int_front_depth + pcb_front_lug_depth, 573 int_payload_lower_extent + pcb_lug_offset_from_bottom 574 ]) 575 rotate([90, 0, 0]) 576 difference() { 577 cylinder(h=pcb_front_lug_depth, 578 r=pcb_front_lug_outer_radius); 579 cylinder(h=pcb_front_lug_depth, 580 r=pcb_front_lug_inner_radius); 581 } 582 } 583 584 /* The actual shapes. */ 585 586 front_displacement = APART ? -connector_width * 0.6 : 0; 587 588 if (FRONT) 589 translate([front_displacement, 0, 0]) 590 difference() { 591 592 /* The cartridge surfaces. */ 593 594 union() { 595 596 /* Front portion. */ 597 598 if (FRONT_SURFACE) { 599 600 /* Surfaces surrounding the payload. */ 601 602 cube_at(payload_width, front, payload_height, 603 0, -1, 1, 604 payload_centre, -int_front_depth, int_payload_lower_extent); 605 606 cube_at(front_left, front_depth, payload_height, 607 -1, -1, 1, 608 int_payload_left_extent, 0, int_payload_lower_extent); 609 610 cube_at(front_right, front_depth, payload_height, 611 1, -1, 1, 612 int_payload_right_extent, 0, int_payload_lower_extent); 613 614 /* Surfaces surrounding the connector. */ 615 616 cube_at(connector_width, front, connector_height, 617 0, -1, -1, 618 0, -int_front_depth, int_payload_lower_extent); 619 620 cube_at(front_left, front_depth, connector_height, 621 -1, -1, -1, 622 -int_connector_width / 2, 0, int_payload_lower_extent); 623 624 cube_at(front_right, front_depth, connector_height, 625 1, -1, -1, 626 int_connector_width / 2, 0, int_payload_lower_extent); 627 628 /* Top surface for the front piece. */ 629 630 if (TOP_SURFACE) 631 cube_at(payload_width, front_depth, top, 632 0, -1, 1, 633 payload_centre, 0, int_payload_upper_extent); 634 } 635 636 difference() { 637 638 /* Floor of cartridge. */ 639 640 cube_at(int_connector_width, int_front_depth, bottom, 641 0, -1, 1, 642 0, 0, lower_extent + int_connector_height); 643 644 /* Left cutout. */ 645 646 cube_at(edge_connector_cutout_front_width, 647 edge_connector_cutout_front_depth, 648 bottom, 649 1, -1, 1, 650 -int_connector_width / 2, 651 0, 652 lower_extent + int_connector_height); 653 654 /* Right cutout. */ 655 656 cube_at(edge_connector_cutout_front_width, 657 edge_connector_cutout_front_depth, 658 bottom, 659 -1, -1, 1, 660 int_connector_width / 2, 661 0, 662 lower_extent + int_connector_height); 663 } 664 665 /* Extended floor. */ 666 667 if (payload_width > connector_width) { 668 669 cube_at(payload_width - connector_width, front_depth, bottom, 670 1, -1, 1, 671 payload_left_extent, 0, lower_extent + int_connector_height); 672 } 673 674 /* PCB supports. */ 675 676 if (MODEL == ROM_CARTRIDGE) { 677 cube_at(pcb_front_support_width, 678 pcb_front_support_depth, 679 pcb_front_support_height, 680 -1, -1, 1, 681 -pcb_support_offset_from_centre, 682 0, 683 int_payload_lower_extent); 684 685 cube_at(pcb_front_support_width, 686 pcb_front_support_depth, 687 pcb_front_support_height, 688 1, -1, 1, 689 pcb_support_offset_from_centre, 690 0, 691 int_payload_lower_extent); 692 693 /* Circular "lugs" to hold PCBs in place. */ 694 695 pcb_front_lug(-1); 696 pcb_front_lug(1); 697 } 698 699 if (MODEL == WIDE_CARTRIDGE) { 700 701 pcb_front_lug_wide(-1); 702 pcb_front_lug_wide(1); 703 } 704 } 705 706 /* Label insets. */ 707 708 union() { 709 710 /* Front label. */ 711 712 if (FRONT_LABEL_INSET) 713 translate([front_label_left_extent, -front_depth, 714 lower_extent + front_label_offset_from_bottom]) 715 cube([front_label_width, front_label_depth, 716 front_label_height]); 717 718 /* Top label. See also the back piece. */ 719 720 if (TOP_LABEL_INSET) 721 translate([top_label_left_extent, 722 -front_depth + top_label_offset_from_front, 723 upper_extent - top_label_depth]) 724 cube([top_label_width, top_label_height, 725 top_label_depth]); 726 } 727 728 /* Inner front edge cavity. */ 729 730 translate([inner_front_edge_width / 2, -int_front_depth, lower_extent]) 731 rotate([0, -90, 0]) 732 linear_extrude(height = inner_front_edge_width) 733 polygon([ 734 [-extra, -inner_front_edge_depth], 735 [0, -inner_front_edge_depth], 736 [inner_front_edge_height, 0], 737 [-extra, 0], 738 ]); 739 740 /* Inner top cutout for the top and sides of the back portion. */ 741 742 cube_at(inner_top_front_cutout_width, 743 inner_top_front_cutout_depth, 744 inner_top_front_cutout_height, 745 1, -1, 1, 746 int_payload_left_extent, 0, int_payload_upper_extent); 747 748 cube_at(inner_payload_front_cutout_width, 749 inner_payload_front_cutout_depth, 750 inner_payload_front_cutout_height, 751 1, -1, 1, 752 int_payload_right_extent, 0, int_payload_lower_extent); 753 754 cube_at(inner_payload_front_cutout_width, 755 inner_payload_front_cutout_depth, 756 inner_payload_front_cutout_height, 757 -1, -1, 1, 758 int_payload_left_extent, 0, int_payload_lower_extent); 759 760 cube_at(inner_connector_front_cutout_width, 761 inner_connector_front_cutout_depth, 762 inner_connector_front_cutout_height, 763 1, -1, -1, 764 int_connector_width / 2, 0, int_payload_lower_extent); 765 766 cube_at(inner_connector_front_cutout_width, 767 inner_connector_front_cutout_depth, 768 inner_connector_front_cutout_height, 769 -1, -1, -1, 770 -int_connector_width / 2, 0, int_payload_lower_extent); 771 772 /* Fillets to round off the edges. */ 773 774 union() { 775 if (ROUND_EDGES) { 776 777 /* Top left and right rounding. */ 778 779 translate([payload_left_extent + ro, -front_depth / 2, upper_extent - ro]) 780 rotate([0, 0, 180]) 781 rotate([90, 0, 0]) 782 fillet(rr, front_depth); 783 translate([payload_right_extent - ro, -front_depth / 2, upper_extent - ro]) 784 rotate([90, 0, 0]) 785 fillet(rr, front_depth); 786 787 /* Top front rounding. */ 788 789 translate([payload_centre, -front_depth + ro, upper_extent - ro]) 790 rotate([0, 0, 180]) 791 rotate([0, -90, 0]) 792 fillet(rr, payload_width); 793 794 /* Edge rounding. */ 795 796 translate([payload_right_extent - ro, -front_depth + ro, int_payload_lower_extent - bottom - extra]) 797 rotate([0, 0, 270]) 798 fillet_justified(rr, payload_height + bottom + extra); 799 800 translate([payload_left_extent + ro, -front_depth + ro, int_payload_lower_extent - bottom - extra]) 801 rotate([0, 0, 180]) 802 fillet_justified(rr, payload_height + bottom + extra); 803 804 translate([connector_width / 2 - ro, -front_depth + ro, lower_extent]) 805 rotate([0, 0, 270]) 806 fillet_partitioned(rr, connector_height - bottom); 807 808 translate([-connector_width / 2 + ro, -front_depth + ro, lower_extent]) 809 rotate([0, 0, 180]) 810 fillet_partitioned(rr, connector_height - bottom); 811 } 812 } 813 } 814 815 /* 816 Place the back piece next to the front, with the internals facing out 817 the same way, if APART is defined. 818 */ 819 820 back_displacement = APART ? connector_width * 0.6 : 0; 821 back_rotation = APART ? 180 : 0; 822 823 if (BACK) 824 translate([back_displacement, 0, 0]) 825 rotate([0, 0, back_rotation]) { 826 difference() { 827 828 /* The cartridge surfaces. */ 829 830 union() { 831 832 /* Back portion. */ 833 834 if (BACK_SURFACE) { 835 836 /* Surfaces surrounding the payload. */ 837 838 /* Payload section of back surface. */ 839 840 cube_at(payload_width, payload_back, payload_height, 841 0, 1, 1, 842 payload_centre, int_payload_back_depth, int_payload_lower_extent); 843 844 cube_at(back_left, back_depth, payload_height, 845 -1, 1, 1, 846 int_payload_left_extent, 0, int_payload_lower_extent); 847 848 cube_at(back_right, back_depth, payload_height, 849 1, 1, 1, 850 int_payload_right_extent, 0, int_payload_lower_extent); 851 852 /* Surfaces surrounding the connector. */ 853 854 /* Connector section of back surface overlapping the floor. */ 855 856 cube_at(connector_width, connector_back, connector_height, 857 0, 1, -1, 858 0, int_connector_back_depth, int_payload_lower_extent); 859 860 cube_at(back_left, back_depth, connector_height, 861 -1, 1, -1, 862 -int_connector_width / 2, 0, int_payload_lower_extent); 863 864 cube_at(back_right, back_depth, connector_height, 865 1, 1, -1, 866 int_connector_width / 2, 0, int_payload_lower_extent); 867 868 /* Top of back piece. */ 869 870 if (TOP_SURFACE) 871 cube_at(payload_width, back_depth, top, 872 0, 1, 1, 873 payload_centre, 0, int_payload_upper_extent); 874 875 /* 876 The extension of the back to connect with the front. Here, the inside 877 edges are rounded. The outside edges are rounded later. 878 879 An extra overlapping measure is employed so that the filleting is 880 continuous between the payload and connector portions. On the inside 881 edges, the connector filleting is extended upwards. 882 */ 883 884 /* Left side of payload. */ 885 886 difference() { 887 cube_at(back_left - groove_depth, groove_width_extra, inner_payload_front_cutout_height, 888 -1, 1, 1, 889 int_payload_left_extent, -groove_width_extra, int_payload_lower_extent); 890 if (ROUND_CONNECTING_EDGES) 891 translate([int_payload_left_extent - groove_ro, -groove_width_extra + groove_ro, 892 int_payload_lower_extent]) 893 rotate([0, 0, -90]) 894 fillet_justified(groove_rr, inner_payload_front_cutout_height); 895 } 896 897 /* Right side of payload. */ 898 899 difference() { 900 cube_at(back_right - groove_depth, groove_width_extra, inner_payload_front_cutout_height, 901 1, 1, 1, 902 int_payload_right_extent, -groove_width_extra, int_payload_lower_extent); 903 if (ROUND_CONNECTING_EDGES) 904 translate([int_payload_right_extent + groove_ro, -groove_width_extra + groove_ro, 905 int_payload_lower_extent]) 906 rotate([0, 0, 180]) 907 fillet_justified(groove_rr, inner_payload_front_cutout_height); 908 } 909 910 /* Left side of connector. */ 911 912 difference() { 913 cube_at(back_left - groove_depth, groove_width_extra, inner_connector_front_cutout_height, 914 -1, 1, -1, 915 -int_connector_width / 2, -groove_width_extra, int_payload_lower_extent); 916 if (ROUND_CONNECTING_EDGES) 917 translate([-int_connector_width / 2 - groove_ro, -groove_width_extra + groove_ro, 918 lower_extent]) 919 rotate([0, 0, -90]) 920 fillet_partitioned(groove_rr, inner_connector_front_cutout_height + extra); 921 } 922 923 /* Right side of connector. */ 924 925 difference() { 926 cube_at(back_right - groove_depth, groove_width_extra, inner_connector_front_cutout_height, 927 1, 1, -1, 928 int_connector_width / 2, -groove_width_extra, int_payload_lower_extent); 929 if (ROUND_CONNECTING_EDGES) 930 translate([int_connector_width / 2 + groove_ro, -groove_width_extra + groove_ro, 931 lower_extent]) 932 rotate([0, 0, 180]) 933 fillet_partitioned(groove_rr, inner_connector_front_cutout_height + extra); 934 } 935 936 /* Top side. */ 937 938 difference() { 939 cube_at(payload_width - groove_depth * 2, groove_width_extra, top - top_groove_depth, 940 0, 1, 1, 941 payload_centre, -groove_width_extra, int_payload_upper_extent); 942 if (ROUND_CONNECTING_EDGES) 943 translate([payload_centre, -groove_width_extra + groove_ro, 944 int_payload_upper_extent + groove_ro]) 945 rotate([0, 0, 180]) 946 rotate([0, 90, 0]) 947 fillet(groove_rr, payload_width - groove_depth * 2); 948 } 949 } 950 951 difference() { 952 953 /* Floor of cartridge. */ 954 955 cube_at(int_connector_width, int_connector_back_depth, bottom, 956 0, 1, 1, 957 0, 0, lower_extent + int_connector_height); 958 959 /* Edge connector cutout. */ 960 961 cube_at(edge_connector_cutout_back_width, 962 edge_connector_cutout_back_depth, 963 bottom, 964 0, 1, 1, 965 0, 0, lower_extent + int_connector_height); 966 } 967 968 /* Extended floor. */ 969 970 if (payload_width > connector_width) { 971 972 difference() { 973 974 cube_at(payload_width - connector_width, back_depth, bottom, 975 1, 1, 1, 976 payload_left_extent, 0, lower_extent + int_connector_height); 977 978 cube_at(payload_width - connector_width, edge_connector_cutout_front_depth, bottom, 979 1, 1, 1, 980 payload_left_extent, 0, lower_extent + int_connector_height); 981 } 982 } 983 984 /* PCB supports. */ 985 986 if (MODEL == ROM_CARTRIDGE) { 987 pcb_support(-1, pcb_back_support_left_bump_height, 988 pcb_back_support_left_bump_offset_from_bottom); 989 pcb_support(1, pcb_back_support_right_bump_height, 990 pcb_back_support_right_bump_offset_from_bottom); 991 992 /* Circular "lugs" to hold PCBs in place. */ 993 994 pcb_lug(-1); 995 pcb_lug(1); 996 } 997 998 if (MODEL == WIDE_CARTRIDGE) { 999 1000 pcb_lug_wide(1); 1001 pcb_lug_wide(-1); 1002 } 1003 } 1004 1005 /* Label insets. */ 1006 1007 union() { 1008 1009 /* Top label. See also the front piece. */ 1010 1011 if (TOP_LABEL_INSET) 1012 translate([top_label_left_extent, 1013 -front_depth + top_label_offset_from_front, 1014 upper_extent - top_label_depth]) 1015 cube([top_label_width, top_label_height, 1016 top_label_depth]); 1017 } 1018 1019 /* Top and side grooves, positioned in the back portion. */ 1020 1021 union() { 1022 1023 /* Left groove. */ 1024 1025 cube_at(groove_depth, groove_width_normal, height, 1026 1, 1, 0, 1027 payload_left_extent, 0, 0); 1028 1029 /* Right groove. */ 1030 1031 cube_at(groove_depth, groove_width_normal, height, 1032 -1, 1, 0, 1033 payload_right_extent, 0, 0); 1034 1035 /* Top grooves. */ 1036 1037 cube_at(payload_width, groove_width_normal, groove_depth, 1038 0, 1, -1, 1039 payload_centre, 0, upper_extent); 1040 1041 cube_at(payload_width, top_groove_width, top_groove_depth, 1042 0, 1, -1, 1043 payload_centre, -groove_width_extra, upper_extent); 1044 } 1045 1046 /* Back cavity. */ 1047 1048 if (BACK_CAVITY) 1049 intersection() { 1050 1051 /* From the bottom upwards. */ 1052 1053 translate([0, back_depth, lower_extent]) 1054 linear_extrude(height = back_cavity_height) 1055 translate([-int_connector_width / 2, 0, 0]) 1056 polygon([ 1057 [back_cavity_offset_from_inner_left, 0], 1058 [back_cavity_inner_offset_from_inner_left, 1059 -back_cavity_depth], 1060 [back_cavity_inner_offset_from_inner_left + 1061 back_cavity_inner_width, 1062 -back_cavity_depth], 1063 [back_cavity_offset_from_inner_left + 1064 back_cavity_width, 0] 1065 ]); 1066 1067 /* From left to right. */ 1068 1069 translate([back_cavity_width / 2, back_depth, lower_extent]) 1070 rotate([0, -90, 0]) 1071 linear_extrude(height = back_cavity_width) 1072 polygon([ 1073 [-extra, -back_cavity_depth], 1074 [back_cavity_inner_height, 1075 -back_cavity_depth], 1076 [back_cavity_height, 0], 1077 [-extra, 0] 1078 ]); 1079 } 1080 1081 /* Inner back cavities. */ 1082 1083 translate([0, int_connector_back_depth, lower_extent]) 1084 linear_extrude(height = int_connector_height) 1085 translate([-int_connector_width / 2, 0, 0]) 1086 polygon([ 1087 [0, 0], 1088 [inner_back_slope_max_offset, 0], 1089 [inner_back_slope_min_offset, 1090 inner_back_slope_depth], 1091 [0, inner_back_slope_depth] 1092 ]); 1093 1094 translate([0, int_connector_back_depth, lower_extent]) 1095 linear_extrude(height = int_connector_height) 1096 translate([int_connector_width / 2, 0, 0]) 1097 polygon([ 1098 [0, 0], 1099 [-inner_back_slope_max_offset, 0], 1100 [-inner_back_slope_min_offset, 1101 inner_back_slope_depth], 1102 [0, inner_back_slope_depth] 1103 ]); 1104 1105 /* Inner back edge cavity. */ 1106 1107 translate([inner_back_edge_width / 2, 1108 int_connector_back_depth + inner_back_edge_depth, lower_extent]) 1109 rotate([0, -90, 0]) 1110 linear_extrude(height = inner_back_edge_width) 1111 polygon([ 1112 [-extra, -inner_back_edge_depth], 1113 [inner_back_edge_height, -inner_back_edge_depth], 1114 [0, 0], 1115 [-extra, 0] 1116 ]); 1117 1118 /* Fillets to round off the edges. */ 1119 1120 union() { 1121 if (ROUND_EDGES) { 1122 1123 /* Top left and right rounding. */ 1124 1125 translate([payload_left_extent + ro, back_depth / 2, upper_extent - ro]) 1126 rotate([0, 0, 180]) 1127 rotate([90, 0, 0]) 1128 fillet(rr, back_depth); 1129 translate([payload_right_extent - ro, back_depth / 2, upper_extent - ro]) 1130 rotate([90, 0, 0]) 1131 fillet(rr, back_depth); 1132 1133 /* Top back rounding. */ 1134 1135 translate([payload_centre, back_depth - ro, upper_extent - ro]) 1136 rotate([0, -90, 0]) 1137 fillet(rr, payload_width); 1138 1139 /* Outer edge rounding. */ 1140 1141 translate([payload_right_extent - ro, back_depth - ro, int_payload_lower_extent - bottom - extra]) 1142 fillet_justified(rr, payload_height + bottom + extra); 1143 1144 translate([payload_left_extent + ro, back_depth - ro, int_payload_lower_extent - bottom - extra]) 1145 rotate([0, 0, 90]) 1146 fillet_justified(rr, payload_height + bottom + extra); 1147 1148 translate([connector_width / 2 - ro, back_depth - ro, lower_extent]) 1149 fillet_partitioned(rr, connector_height - bottom); 1150 1151 translate([-connector_width / 2 + ro, back_depth - ro, lower_extent]) 1152 rotate([0, 0, 90]) 1153 fillet_partitioned(rr, connector_height - bottom); 1154 } 1155 1156 if (ROUND_CONNECTING_EDGES) { 1157 1158 /* 1159 Outer edge rounding of the back extension. This is done as a 1160 separate removal operation rather than occurring when creating the 1161 extension in order to ensure that the edges are actually rounded. 1162 1163 An extra overlapping measure is employed so that the filleting is 1164 continuous between the payload and connector portions. On the outside 1165 edges, the payload filleting is extended downwards. 1166 */ 1167 1168 translate([payload_left_extent + groove_depth + groove_ro, -groove_width_extra + groove_ro, 1169 int_payload_lower_extent - extra]) 1170 rotate([0, 0, 180]) 1171 fillet_justified(groove_rr, inner_payload_front_cutout_height + extra); 1172 1173 translate([payload_right_extent - groove_depth - groove_ro, -groove_width_extra + groove_ro, 1174 int_payload_lower_extent - extra]) 1175 rotate([0, 0, -90]) 1176 fillet_justified(groove_rr, inner_payload_front_cutout_height + extra); 1177 1178 /* Sides of connector. */ 1179 1180 translate([-connector_width / 2 + groove_depth + groove_ro, -groove_width_extra + groove_ro, 1181 lower_extent]) 1182 rotate([0, 0, 180]) 1183 fillet_partitioned(groove_rr, inner_connector_front_cutout_height); 1184 1185 translate([connector_width / 2 - groove_depth - groove_ro, -groove_width_extra + groove_ro, 1186 lower_extent]) 1187 rotate([0, 0, -90]) 1188 fillet_partitioned(groove_rr, inner_connector_front_cutout_height); 1189 1190 /* Top of payload. */ 1191 1192 translate([payload_centre, -groove_width_extra + groove_ro, 1193 int_payload_upper_extent + inner_top_front_cutout_height - groove_ro]) 1194 rotate([0, 0, 180]) 1195 rotate([0, -90, 0]) 1196 fillet(groove_rr, payload_width - groove_depth * 2); 1197 } 1198 } 1199 } 1200 1201 /* PCBs for checking. */ 1202 1203 if (PCB) if (MODEL == ROM_CARTRIDGE) { 1204 translate([0, 0, int_payload_lower_extent]) 1205 difference() { 1206 1207 /* Mock PCB. */ 1208 1209 union() { 1210 cube_at(pcb_width, pcb_depth, 1211 pcb_height - edge_connector_height, 1212 0, -1, 1, 1213 0, edge_connector_cutout_back_depth, 0); 1214 cube_at(edge_connector_width, pcb_depth, 1215 edge_connector_height, 1216 0, -1, -1, 1217 0, edge_connector_cutout_back_depth, 0); 1218 1219 /* Feature. */ 1220 1221 cube_at(feature_width, feature_depth, feature_height, 1222 0, 1, 1, 1223 0, edge_connector_cutout_back_depth, feature_height_offset); 1224 } 1225 1226 /* Holes for mounting. */ 1227 1228 union() { 1229 cube_at(pcb_hole_width, pcb_hole_depth, pcb_left_hole_height, 1230 -1, -1, 1, 1231 pcb_left_hole_offset_from_centre, pcb_hole_start_depth, 1232 pcb_left_hole_offset); 1233 translate([pcb_left_hole_offset_from_centre - 1234 pcb_hole_width / 2, pcb_hole_start_depth, pcb_left_hole_offset]) 1235 rotate([90, 0, 0]) 1236 cylinder(h=pcb_hole_depth, r=pcb_hole_width / 2); 1237 translate([pcb_left_hole_offset_from_centre - 1238 pcb_hole_width / 2, pcb_hole_start_depth, 1239 pcb_left_hole_offset + pcb_left_hole_height]) 1240 rotate([90, 0, 0]) 1241 cylinder(h=pcb_hole_depth, r=pcb_hole_width / 2); 1242 } 1243 1244 union() { 1245 cube_at(pcb_hole_width, pcb_hole_depth, pcb_right_hole_height, 1246 1, -1, 1, 1247 pcb_right_hole_offset_from_centre, pcb_hole_start_depth, 1248 pcb_right_hole_offset); 1249 translate([pcb_right_hole_offset_from_centre + 1250 pcb_hole_width / 2, pcb_hole_start_depth, pcb_right_hole_offset]) 1251 rotate([90, 0, 0]) 1252 cylinder(h=pcb_hole_depth, r=pcb_hole_width / 2); 1253 translate([pcb_right_hole_offset_from_centre + 1254 pcb_hole_width / 2, pcb_hole_start_depth, 1255 pcb_right_hole_offset + pcb_right_hole_height]) 1256 rotate([90, 0, 0]) 1257 cylinder(h=pcb_hole_depth, r=pcb_hole_width / 2); 1258 } 1259 1260 /* Holes for lugs. */ 1261 1262 translate([ 1263 -int_connector_width / 2 + pcb_lug_offset_from_inside, 1264 pcb_hole_start_depth, pcb_lug_offset_from_bottom]) 1265 rotate([90, 0, 0]) 1266 cylinder(h=pcb_hole_depth, r=pcb_lug_hole_radius); 1267 1268 translate([ 1269 int_connector_width / 2 - pcb_lug_offset_from_inside, 1270 pcb_hole_start_depth, pcb_lug_offset_from_bottom]) 1271 rotate([90, 0, 0]) 1272 cylinder(h=pcb_hole_depth, r=pcb_lug_hole_radius); 1273 } 1274 } 1275 1276 if (PCB) if (MODEL == WIDE_CARTRIDGE) { 1277 translate([0, 0, int_payload_lower_extent]) 1278 difference() { 1279 1280 /* Mock PCB. */ 1281 1282 union() { 1283 cube_at(wide_pcb_width, wide_pcb_depth, 1284 wide_pcb_height - edge_connector_height, 1285 0, -1, 1, 1286 payload_centre, edge_connector_cutout_back_depth, 0); 1287 cube_at(edge_connector_width, wide_pcb_depth, 1288 edge_connector_height, 1289 0, -1, -1, 1290 0, edge_connector_cutout_back_depth, 0); 1291 1292 /* Feature. */ 1293 1294 cube_at(feature_width, feature_depth, feature_height, 1295 0, 1, 1, 1296 0, edge_connector_cutout_back_depth, feature_height_offset); 1297 } 1298 1299 /* Holes for lugs. */ 1300 1301 translate([ 1302 payload_centre + int_payload_width / 2 - wide_pcb_lug_offset_from_inside , 1303 wide_pcb_hole_start_depth, 1304 wide_pcb_height - edge_connector_height - wide_pcb_lug_offset_from_bottom]) 1305 rotate([90, 0, 0]) 1306 cylinder(h=wide_pcb_hole_depth, r=wide_pcb_lug_hole_radius); 1307 1308 translate([ 1309 payload_centre - int_payload_width / 2 + wide_pcb_lug_offset_from_inside , 1310 wide_pcb_hole_start_depth, 1311 wide_pcb_height - edge_connector_height - wide_pcb_lug_offset_from_bottom]) 1312 rotate([90, 0, 0]) 1313 cylinder(h=wide_pcb_hole_depth, r=wide_pcb_lug_hole_radius); 1314 1315 translate([ 1316 payload_centre + int_payload_width / 2 - wide_pcb_lug_offset_from_inside , 1317 wide_pcb_hole_start_depth, 1318 wide_pcb_lug_offset_from_bottom]) 1319 rotate([90, 0, 0]) 1320 cylinder(h=wide_pcb_hole_depth, r=wide_pcb_lug_hole_radius); 1321 1322 translate([ 1323 payload_centre - int_payload_width / 2 + wide_pcb_lug_offset_from_inside , 1324 wide_pcb_hole_start_depth, 1325 wide_pcb_lug_offset_from_bottom]) 1326 rotate([90, 0, 0]) 1327 cylinder(h=wide_pcb_hole_depth, r=wide_pcb_lug_hole_radius); 1328 } 1329 } 1330 } 1331 } 1332 1333 cartridge();