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