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