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 = 14.0; /* maximum depth in the payload section */ 166 int_connector_depth = 11.5; /* 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 = 1; /* 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 = 2.0; /* the depth of the groove accommodating the front rim */ 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. In principle, the 287 interior of the case could be straight since the plastic guides of the 288 Plus 1 socket are outside the case. 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 353 /* Configured by SHORT_PCB. */ 354 pcb_back_support_top_bump_height = 3.8; 355 356 /* Move the PCB support towards the centre. */ 357 pcb_support_margin = 1.75; 358 pcb_support_offset_from_centre = edge_connector_cutout_back_width / 2 359 - pcb_support_margin; 360 361 /* 362 The PCB lugs protrude through the holes in the PCB. By extending both 363 the front and back lugs by the depth of the back support bump, they 364 overlap by that amount. The "mating" depth is defined as that amount 365 plus an additional amount for adhesion. 366 */ 367 368 pcb_lug_mating_depth = pcb_back_support_bump_depth + 1.0; 369 370 pcb_lug_depth = pcb_back_support_depth + 371 pcb_lug_mating_depth; 372 pcb_lug_inner_radius = 1.0; 373 pcb_lug_outer_radius = 5.5 / 2; 374 pcb_lug_offset_from_bottom = 14.35; 375 pcb_lug_offset_from_inside = 5.55; 376 377 /* 378 The cross detail on the back lug is intended to resist the front lug, 379 and thus starts at the point that the front lug ends. 380 */ 381 382 pcb_lug_cross_width = 6.7; 383 pcb_lug_cross_depth = pcb_back_support_depth + pcb_back_support_bump_depth - 384 pcb_lug_mating_depth; 385 pcb_lug_cross_height = 1.4; 386 387 pcb_front_lug_depth = pcb_front_support_depth + 388 pcb_lug_mating_depth; 389 pcb_front_lug_inner_radius = pcb_lug_outer_radius; 390 pcb_front_lug_outer_radius = pow( 391 pow(pcb_lug_cross_width / 2, 2) + 392 pow(pcb_lug_cross_height / 2, 2), 393 0.5); 394 395 wide_pcb_lug_offset_from_inside = 6.0; 396 wide_pcb_lug_offset_from_bottom = 6.0; 397 398 /* PCBs for checking. */ 399 400 pcb_width = 84.85; pcb_height = 62.5; pcb_depth = 1; 401 edge_connector_width = 56.5; edge_connector_height = 11.85; 402 403 /* 404 The rectangular part of the narrow left and right holes is smaller 405 than the "bump" in the case, but the circular parts make the overall 406 hole larger than the "bump". An extra depth is used for holes to avoid 407 surface definition problems. 408 */ 409 410 pcb_hole_margin = 0.55; 411 pcb_hole_width = 2.2; 412 pcb_hole_extra_depth = 0.1; 413 pcb_lug_hole_radius = 3.75; 414 415 pcb_left_hole_offset = pcb_back_support_left_bump_offset_from_bottom + pcb_hole_margin; 416 pcb_left_hole_height = pcb_back_support_left_bump_height - pcb_hole_margin * 2; 417 pcb_left_hole_offset_from_centre = -(pcb_support_offset_from_centre + 418 pcb_back_support_width / 2 - pcb_hole_width / 2); 419 420 pcb_right_hole_offset = pcb_back_support_right_bump_offset_from_bottom + pcb_hole_margin; 421 pcb_right_hole_height = pcb_back_support_right_bump_height - pcb_hole_margin * 2; 422 pcb_right_hole_offset_from_centre = -pcb_left_hole_offset_from_centre; 423 424 pcb_hole_depth = pcb_depth + 2 * pcb_hole_extra_depth; 425 pcb_hole_start_depth = edge_connector_cutout_back_depth + pcb_hole_extra_depth; 426 427 wide_pcb_width = 138.0; wide_pcb_height = pcb_height; wide_pcb_depth = pcb_depth; 428 wide_pcb_hole_depth = pcb_hole_depth; 429 wide_pcb_hole_start_depth = pcb_hole_start_depth; 430 431 wide_pcb_lug_hole_radius = 4.0; 432 433 /* Repeated constructs. */ 434 435 module pcb_support(xdir, bump_height, bump_offset) { 436 437 /* 438 Translate the support in the stated direction. 439 Since the support is already justified in the direction, the translation 440 moves the inner edge of the support to alignment with the end of the 441 edge connector cutout and then back by the PCB support margin. 442 */ 443 444 translate([xdir * 445 pcb_support_offset_from_centre, 446 edge_connector_cutout_back_depth, 447 int_payload_lower_extent]) 448 justify(pcb_back_support_width, 449 pcb_back_support_depth, 450 pcb_back_support_height, 451 xdir, 1, 1) 452 union() { 453 454 /* Underlying support strut. */ 455 456 cube([pcb_back_support_width, 457 pcb_back_support_depth, 458 pcb_back_support_height], center = true); 459 460 /* Middle bump. */ 461 462 cube_at(pcb_back_support_bump_width, 463 pcb_back_support_bump_depth, 464 bump_height, 465 0, -1, 1, 466 0, 467 -pcb_back_support_depth / 2, 468 -pcb_back_support_height / 2 + bump_offset); 469 470 /* Top bump. */ 471 472 if (SHORT_PCB) 473 cube_at(pcb_back_support_bump_width, 474 pcb_back_support_bump_depth, 475 pcb_back_support_top_bump_height, 476 0, -1, 1, 477 0, 478 -pcb_back_support_depth / 2, 479 pcb_back_support_height / 2 - 480 pcb_back_support_top_bump_height); 481 } 482 } 483 484 module pcb_lug(xdir) { 485 pcb_lug_explicit(xdir, int_connector_width / 2 - pcb_lug_offset_from_inside, 486 pcb_lug_offset_from_bottom); 487 } 488 489 module pcb_lug_wide(xdir) { 490 pcb_lug_explicit(xdir, int_payload_width / 2 - wide_pcb_lug_offset_from_inside, 491 wide_pcb_lug_offset_from_bottom); 492 pcb_lug_explicit(xdir, int_payload_width / 2 - wide_pcb_lug_offset_from_inside, 493 wide_pcb_height - edge_connector_height - wide_pcb_lug_offset_from_bottom); 494 } 495 496 module pcb_lug_explicit(xdir, pcb_lug_offset_from_centre, pcb_lug_offset_from_bottom) { 497 translate([payload_centre + xdir * pcb_lug_offset_from_centre, 498 back_depth, 499 int_payload_lower_extent + pcb_lug_offset_from_bottom 500 ]) 501 rotate([90, 0, 0]) 502 difference() { 503 504 /* Cylinder with cross. */ 505 506 union() { 507 cylinder(h=pcb_lug_depth, r=pcb_lug_outer_radius); 508 cube_at(pcb_lug_cross_width, 509 pcb_lug_cross_height, pcb_lug_cross_depth, 510 0, 0, 1, 511 0, 0, 0); 512 cube_at(pcb_lug_cross_height, 513 pcb_lug_cross_width, pcb_lug_cross_depth, 514 0, 0, 1, 515 0, 0, 0); 516 } 517 518 /* Hollowed out by a cylinder. */ 519 520 cylinder(h=pcb_lug_depth, r=pcb_lug_inner_radius); 521 522 /* Tapered off for easier connection. */ 523 524 translate([0, 0, pcb_lug_depth - pcb_lug_ro]) 525 fillet_torus(pcb_lug_outer_radius, pcb_lug_rr); 526 } 527 } 528 529 module pcb_front_lug(xdir) { 530 pcb_front_lug_explicit(xdir, int_connector_width / 2 - pcb_lug_offset_from_inside, 531 pcb_lug_offset_from_bottom); 532 } 533 534 module pcb_front_lug_wide(xdir) { 535 pcb_front_lug_explicit(xdir, int_payload_width / 2 - wide_pcb_lug_offset_from_inside, 536 wide_pcb_lug_offset_from_bottom); 537 pcb_front_lug_explicit(xdir, int_payload_width / 2 - wide_pcb_lug_offset_from_inside, 538 wide_pcb_height - edge_connector_height - wide_pcb_lug_offset_from_bottom); 539 } 540 541 module pcb_front_lug_explicit(xdir, pcb_lug_offset_from_centre, pcb_lug_offset_from_bottom) { 542 translate([payload_centre + xdir * pcb_lug_offset_from_centre, 543 -int_front_depth + pcb_front_lug_depth, 544 int_payload_lower_extent + pcb_lug_offset_from_bottom 545 ]) 546 rotate([90, 0, 0]) 547 difference() { 548 cylinder(h=pcb_front_lug_depth, 549 r=pcb_front_lug_outer_radius); 550 cylinder(h=pcb_front_lug_depth, 551 r=pcb_front_lug_inner_radius); 552 } 553 } 554 555 /* The actual shapes. */ 556 557 front_displacement = APART ? -connector_width * 0.6 : 0; 558 559 if (FRONT) 560 translate([front_displacement, 0, 0]) 561 difference() { 562 563 /* The cartridge surfaces. */ 564 565 union() { 566 567 /* Front portion. */ 568 569 if (FRONT_SURFACE) { 570 571 /* Surfaces surrounding the payload. */ 572 573 cube_at(payload_width, front, payload_height, 574 0, -1, 1, 575 payload_centre, -int_front_depth, int_payload_lower_extent); 576 577 cube_at(front_left, front_depth, payload_height, 578 -1, -1, 1, 579 int_payload_left_extent, 0, int_payload_lower_extent); 580 581 cube_at(front_right, front_depth, payload_height, 582 1, -1, 1, 583 int_payload_right_extent, 0, int_payload_lower_extent); 584 585 /* Surfaces surrounding the connector. */ 586 587 cube_at(connector_width, front, connector_height, 588 0, -1, -1, 589 0, -int_front_depth, int_payload_lower_extent); 590 591 cube_at(front_left, front_depth, connector_height, 592 -1, -1, -1, 593 -int_connector_width / 2, 0, int_payload_lower_extent); 594 595 cube_at(front_right, front_depth, connector_height, 596 1, -1, -1, 597 int_connector_width / 2, 0, int_payload_lower_extent); 598 599 /* Top surface for the front piece. */ 600 601 cube_at(payload_width, front_depth, top, 602 0, -1, 1, 603 payload_centre, 0, int_payload_upper_extent); 604 } 605 606 difference() { 607 608 /* Floor of cartridge. */ 609 610 cube_at(int_connector_width, int_front_depth, bottom, 611 0, -1, 1, 612 0, 0, lower_extent + int_connector_height); 613 614 /* Left cutout. */ 615 616 cube_at(edge_connector_cutout_front_width, 617 edge_connector_cutout_front_depth, 618 bottom, 619 1, -1, 1, 620 -int_connector_width / 2, 621 0, 622 lower_extent + int_connector_height); 623 624 /* Right cutout. */ 625 626 cube_at(edge_connector_cutout_front_width, 627 edge_connector_cutout_front_depth, 628 bottom, 629 -1, -1, 1, 630 int_connector_width / 2, 631 0, 632 lower_extent + int_connector_height); 633 } 634 635 /* Extended floor. */ 636 637 if (payload_width > connector_width) { 638 639 cube_at(payload_width - connector_width, front_depth, bottom, 640 1, -1, 1, 641 payload_left_extent, 0, lower_extent + int_connector_height); 642 } 643 644 /* PCB supports. */ 645 646 if (MODEL == ROM_CARTRIDGE) { 647 cube_at(pcb_front_support_width, 648 pcb_front_support_depth, 649 pcb_front_support_height, 650 -1, -1, 1, 651 -pcb_support_offset_from_centre, 652 0, 653 int_payload_lower_extent); 654 655 cube_at(pcb_front_support_width, 656 pcb_front_support_depth, 657 pcb_front_support_height, 658 1, -1, 1, 659 pcb_support_offset_from_centre, 660 0, 661 int_payload_lower_extent); 662 663 /* Circular "lugs" to hold PCBs in place. */ 664 665 pcb_front_lug(-1); 666 pcb_front_lug(1); 667 } 668 669 if (MODEL == WIDE_CARTRIDGE) { 670 671 pcb_front_lug_wide(-1); 672 pcb_front_lug_wide(1); 673 } 674 } 675 676 /* Label insets. */ 677 678 union() { 679 680 /* Front label. */ 681 682 if (FRONT_LABEL_INSET) 683 translate([front_label_left_extent, -front_depth, 684 lower_extent + front_label_offset_from_bottom]) 685 cube([front_label_width, front_label_depth, 686 front_label_height]); 687 688 /* Top label. See also the back piece. */ 689 690 if (TOP_LABEL_INSET) 691 translate([top_label_left_extent, 692 -front_depth + top_label_offset_from_front, 693 upper_extent - top_label_depth]) 694 cube([top_label_width, top_label_height, 695 top_label_depth]); 696 } 697 698 /* Inner front edge cavity. */ 699 700 translate([inner_front_edge_width / 2, -int_front_depth, lower_extent]) 701 rotate([0, -90, 0]) 702 linear_extrude(height = inner_front_edge_width) 703 polygon([ 704 [-extra, -inner_front_edge_depth], 705 [0, -inner_front_edge_depth], 706 [inner_front_edge_height, 0], 707 [-extra, 0], 708 ]); 709 710 /* Inner top cutout for the top and sides of the back portion. */ 711 712 cube_at(inner_top_front_cutout_width, 713 inner_top_front_cutout_depth, 714 inner_top_front_cutout_height, 715 1, -1, 1, 716 int_payload_left_extent, 0, int_payload_upper_extent); 717 718 cube_at(inner_payload_front_cutout_width, 719 inner_payload_front_cutout_depth, 720 inner_payload_front_cutout_height, 721 1, -1, 1, 722 int_payload_right_extent, 0, int_payload_lower_extent); 723 724 cube_at(inner_payload_front_cutout_width, 725 inner_payload_front_cutout_depth, 726 inner_payload_front_cutout_height, 727 -1, -1, 1, 728 int_payload_left_extent, 0, int_payload_lower_extent); 729 730 cube_at(inner_connector_front_cutout_width, 731 inner_connector_front_cutout_depth, 732 inner_connector_front_cutout_height, 733 1, -1, -1, 734 int_connector_width / 2, 0, int_payload_lower_extent); 735 736 cube_at(inner_connector_front_cutout_width, 737 inner_connector_front_cutout_depth, 738 inner_connector_front_cutout_height, 739 -1, -1, -1, 740 -int_connector_width / 2, 0, int_payload_lower_extent); 741 742 /* Fillets to round off the edges. */ 743 744 union() { 745 746 /* Top left and right rounding. */ 747 748 translate([payload_left_extent + ro, -front_depth / 2, upper_extent - ro]) 749 rotate([0, 0, 180]) 750 rotate([90, 0, 0]) 751 fillet(rr, front_depth); 752 translate([payload_right_extent - ro, -front_depth / 2, upper_extent - ro]) 753 rotate([90, 0, 0]) 754 fillet(rr, front_depth); 755 756 /* Top front rounding. */ 757 758 translate([payload_centre, -front_depth + ro, upper_extent - ro]) 759 rotate([0, 0, 180]) 760 rotate([0, -90, 0]) 761 fillet(rr, payload_width); 762 763 /* Edge rounding. */ 764 765 translate([payload_right_extent - ro, -front_depth + ro, int_payload_lower_extent - bottom - extra]) 766 rotate([0, 0, 270]) 767 fillet_justified(rr, payload_height + bottom + extra); 768 769 translate([payload_left_extent + ro, -front_depth + ro, int_payload_lower_extent - bottom - extra]) 770 rotate([0, 0, 180]) 771 fillet_justified(rr, payload_height + bottom + extra); 772 773 translate([connector_width / 2 - ro, -front_depth + ro, lower_extent]) 774 rotate([0, 0, 270]) 775 fillet_partitioned(rr, connector_height - bottom); 776 777 translate([-connector_width / 2 + ro, -front_depth + ro, lower_extent]) 778 rotate([0, 0, 180]) 779 fillet_partitioned(rr, connector_height - bottom); 780 } 781 } 782 783 /* 784 Place the back piece next to the front, with the internals facing out 785 the same way, if APART is defined. 786 */ 787 788 back_displacement = APART ? connector_width * 0.6 : 0; 789 back_rotation = APART ? 180 : 0; 790 791 if (BACK) 792 translate([back_displacement, 0, 0]) 793 rotate([0, 0, back_rotation]) { 794 difference() { 795 796 /* The cartridge surfaces. */ 797 798 union() { 799 800 /* Back portion. */ 801 802 if (BACK_SURFACE) { 803 804 /* Surfaces surrounding the payload. */ 805 806 /* Payload section of back surface. */ 807 808 cube_at(payload_width, payload_back, payload_height, 809 0, 1, 1, 810 payload_centre, int_payload_back_depth, int_payload_lower_extent); 811 812 cube_at(back_left, back_depth, payload_height, 813 -1, 1, 1, 814 int_payload_left_extent, 0, int_payload_lower_extent); 815 816 cube_at(back_right, back_depth, payload_height, 817 1, 1, 1, 818 int_payload_right_extent, 0, int_payload_lower_extent); 819 820 /* Surfaces surrounding the connector. */ 821 822 /* Connector section of back surface overlapping the floor. */ 823 824 cube_at(connector_width, connector_back, connector_height, 825 0, 1, -1, 826 0, int_connector_back_depth, int_payload_lower_extent); 827 828 cube_at(back_left, back_depth, connector_height, 829 -1, 1, -1, 830 -int_connector_width / 2, 0, int_payload_lower_extent); 831 832 cube_at(back_right, back_depth, connector_height, 833 1, 1, -1, 834 int_connector_width / 2, 0, int_payload_lower_extent); 835 836 /* Top of back piece. */ 837 838 cube_at(payload_width, back_depth, top, 839 0, 1, 1, 840 payload_centre, 0, int_payload_upper_extent); 841 842 /* 843 The extension of the back to connect with the front. Here, the inside 844 edges are rounded. The outside edges are rounded later. 845 846 An extra overlapping measure is employed so that the filleting is 847 continuous between the payload and connector portions. On the inside 848 edges, the connector filleting is extended upwards. 849 */ 850 851 /* Left side of payload. */ 852 853 difference() { 854 cube_at(back_left - groove_depth, groove_width_extra, inner_payload_front_cutout_height, 855 -1, 1, 1, 856 int_payload_left_extent, -groove_width_extra, int_payload_lower_extent); 857 translate([int_payload_left_extent - groove_ro, -groove_width_extra + groove_ro, 858 int_payload_lower_extent]) 859 rotate([0, 0, -90]) 860 fillet_justified(groove_rr, inner_payload_front_cutout_height); 861 } 862 863 /* Right side of payload. */ 864 865 difference() { 866 cube_at(back_right - groove_depth, groove_width_extra, inner_payload_front_cutout_height, 867 1, 1, 1, 868 int_payload_right_extent, -groove_width_extra, int_payload_lower_extent); 869 translate([int_payload_right_extent + groove_ro, -groove_width_extra + groove_ro, 870 int_payload_lower_extent]) 871 rotate([0, 0, 180]) 872 fillet_justified(groove_rr, inner_payload_front_cutout_height); 873 } 874 875 /* Left side of connector. */ 876 877 difference() { 878 cube_at(back_left - groove_depth, groove_width_extra, inner_connector_front_cutout_height, 879 -1, 1, -1, 880 -int_connector_width / 2, -groove_width_extra, int_payload_lower_extent); 881 translate([-int_connector_width / 2 - groove_ro, -groove_width_extra + groove_ro, 882 lower_extent]) 883 rotate([0, 0, -90]) 884 fillet_partitioned(groove_rr, inner_connector_front_cutout_height + extra); 885 } 886 887 /* Right side of connector. */ 888 889 difference() { 890 cube_at(back_right - groove_depth, groove_width_extra, inner_connector_front_cutout_height, 891 1, 1, -1, 892 int_connector_width / 2, -groove_width_extra, int_payload_lower_extent); 893 translate([int_connector_width / 2 + groove_ro, -groove_width_extra + groove_ro, 894 lower_extent]) 895 rotate([0, 0, 180]) 896 fillet_partitioned(groove_rr, inner_connector_front_cutout_height + extra); 897 } 898 899 /* Top side. */ 900 901 difference() { 902 cube_at(payload_width - groove_depth * 2, groove_width_extra, top - top_groove_depth, 903 0, 1, 1, 904 payload_centre, -groove_width_extra, int_payload_upper_extent); 905 translate([payload_centre, -groove_width_extra + groove_ro, 906 int_payload_upper_extent + groove_ro]) 907 rotate([0, 0, 180]) 908 rotate([0, 90, 0]) 909 fillet(groove_rr, payload_width - groove_depth * 2); 910 } 911 } 912 913 difference() { 914 915 /* Floor of cartridge. */ 916 917 cube_at(int_connector_width, int_connector_back_depth, bottom, 918 0, 1, 1, 919 0, 0, lower_extent + int_connector_height); 920 921 /* Edge connector cutout. */ 922 923 cube_at(edge_connector_cutout_back_width, 924 edge_connector_cutout_back_depth, 925 bottom, 926 0, 1, 1, 927 0, 0, lower_extent + int_connector_height); 928 } 929 930 /* Extended floor. */ 931 932 if (payload_width > connector_width) { 933 934 difference() { 935 936 cube_at(payload_width - connector_width, back_depth, bottom, 937 1, 1, 1, 938 payload_left_extent, 0, lower_extent + int_connector_height); 939 940 cube_at(payload_width - connector_width, edge_connector_cutout_front_depth, bottom, 941 1, 1, 1, 942 payload_left_extent, 0, lower_extent + int_connector_height); 943 } 944 } 945 946 /* PCB supports. */ 947 948 if (MODEL == ROM_CARTRIDGE) { 949 pcb_support(-1, pcb_back_support_left_bump_height, 950 pcb_back_support_left_bump_offset_from_bottom); 951 pcb_support(1, pcb_back_support_right_bump_height, 952 pcb_back_support_right_bump_offset_from_bottom); 953 954 /* Circular "lugs" to hold PCBs in place. */ 955 956 pcb_lug(-1); 957 pcb_lug(1); 958 } 959 960 if (MODEL == WIDE_CARTRIDGE) { 961 962 pcb_lug_wide(1); 963 pcb_lug_wide(-1); 964 } 965 } 966 967 /* Label insets. */ 968 969 union() { 970 971 /* Top label. See also the front piece. */ 972 973 if (TOP_LABEL_INSET) 974 translate([top_label_left_extent, 975 -front_depth + top_label_offset_from_front, 976 upper_extent - top_label_depth]) 977 cube([top_label_width, top_label_height, 978 top_label_depth]); 979 } 980 981 /* Top and side grooves, positioned in the back portion. */ 982 983 union() { 984 985 /* Left groove. */ 986 987 cube_at(groove_depth, groove_width_normal, height, 988 1, 1, 0, 989 payload_left_extent, 0, 0); 990 991 /* Right groove. */ 992 993 cube_at(groove_depth, groove_width_normal, height, 994 -1, 1, 0, 995 payload_right_extent, 0, 0); 996 997 /* Top grooves. */ 998 999 cube_at(payload_width, groove_width_normal, groove_depth, 1000 0, 1, -1, 1001 payload_centre, 0, upper_extent); 1002 1003 cube_at(payload_width, top_groove_width, top_groove_depth, 1004 0, 1, -1, 1005 payload_centre, -groove_width_extra, upper_extent); 1006 } 1007 1008 /* Back cavity. */ 1009 1010 if (BACK_CAVITY) 1011 intersection() { 1012 1013 /* From the bottom upwards. */ 1014 1015 translate([0, back_depth, lower_extent]) 1016 linear_extrude(height = back_cavity_height) 1017 translate([-int_connector_width / 2, 0, 0]) 1018 polygon([ 1019 [back_cavity_offset_from_inner_left, 0], 1020 [back_cavity_inner_offset_from_inner_left, 1021 -back_cavity_depth], 1022 [back_cavity_inner_offset_from_inner_left + 1023 back_cavity_inner_width, 1024 -back_cavity_depth], 1025 [back_cavity_offset_from_inner_left + 1026 back_cavity_width, 0] 1027 ]); 1028 1029 /* From left to right. */ 1030 1031 translate([back_cavity_width / 2, back_depth, lower_extent]) 1032 rotate([0, -90, 0]) 1033 linear_extrude(height = back_cavity_width) 1034 polygon([ 1035 [-extra, -back_cavity_depth], 1036 [back_cavity_inner_height, 1037 -back_cavity_depth], 1038 [back_cavity_height, 0], 1039 [-extra, 0] 1040 ]); 1041 } 1042 1043 /* Inner back cavities. */ 1044 1045 translate([0, int_connector_back_depth, lower_extent]) 1046 linear_extrude(height = int_connector_height) 1047 translate([-int_connector_width / 2, 0, 0]) 1048 polygon([ 1049 [0, 0], 1050 [inner_back_slope_max_offset, 0], 1051 [inner_back_slope_min_offset, 1052 inner_back_slope_depth], 1053 [0, inner_back_slope_depth] 1054 ]); 1055 1056 translate([0, int_connector_back_depth, lower_extent]) 1057 linear_extrude(height = int_connector_height) 1058 translate([int_connector_width / 2, 0, 0]) 1059 polygon([ 1060 [0, 0], 1061 [-inner_back_slope_max_offset, 0], 1062 [-inner_back_slope_min_offset, 1063 inner_back_slope_depth], 1064 [0, inner_back_slope_depth] 1065 ]); 1066 1067 /* Inner back edge cavity. */ 1068 1069 translate([inner_back_edge_width / 2, 1070 int_connector_back_depth + inner_back_edge_depth, lower_extent]) 1071 rotate([0, -90, 0]) 1072 linear_extrude(height = inner_back_edge_width) 1073 polygon([ 1074 [-extra, -inner_back_edge_depth], 1075 [inner_back_edge_height, -inner_back_edge_depth], 1076 [0, 0], 1077 [-extra, 0] 1078 ]); 1079 1080 /* Fillets to round off the edges. */ 1081 1082 union() { 1083 1084 /* Top left and right rounding. */ 1085 1086 translate([payload_left_extent + ro, back_depth / 2, upper_extent - ro]) 1087 rotate([0, 0, 180]) 1088 rotate([90, 0, 0]) 1089 fillet(rr, back_depth); 1090 translate([payload_right_extent - ro, back_depth / 2, upper_extent - ro]) 1091 rotate([90, 0, 0]) 1092 fillet(rr, back_depth); 1093 1094 /* Top back rounding. */ 1095 1096 translate([payload_centre, back_depth - ro, upper_extent - ro]) 1097 rotate([0, -90, 0]) 1098 fillet(rr, payload_width); 1099 1100 /* Outer edge rounding. */ 1101 1102 translate([payload_right_extent - ro, back_depth - ro, int_payload_lower_extent - bottom - extra]) 1103 fillet_justified(rr, payload_height + bottom + extra); 1104 1105 translate([payload_left_extent + ro, back_depth - ro, int_payload_lower_extent - bottom - extra]) 1106 rotate([0, 0, 90]) 1107 fillet_justified(rr, payload_height + bottom + extra); 1108 1109 translate([connector_width / 2 - ro, back_depth - ro, lower_extent]) 1110 fillet_partitioned(rr, connector_height - bottom); 1111 1112 translate([-connector_width / 2 + ro, back_depth - ro, lower_extent]) 1113 rotate([0, 0, 90]) 1114 fillet_partitioned(rr, connector_height - bottom); 1115 1116 /* 1117 Outer edge rounding of the back extension. This is done as a 1118 separate removal operation rather than occurring when creating the 1119 extension in order to ensure that the edges are actually rounded. 1120 1121 An extra overlapping measure is employed so that the filleting is 1122 continuous between the payload and connector portions. On the outside 1123 edges, the payload filleting is extended downwards. 1124 */ 1125 1126 translate([payload_left_extent + groove_depth + groove_ro, -groove_width_extra + groove_ro, 1127 int_payload_lower_extent - extra]) 1128 rotate([0, 0, 180]) 1129 fillet_justified(groove_rr, inner_payload_front_cutout_height + extra); 1130 1131 translate([payload_right_extent - groove_depth - groove_ro, -groove_width_extra + groove_ro, 1132 int_payload_lower_extent - extra]) 1133 rotate([0, 0, -90]) 1134 fillet_justified(groove_rr, inner_payload_front_cutout_height + extra); 1135 1136 /* Sides of connector. */ 1137 1138 translate([-connector_width / 2 + groove_depth + groove_ro, -groove_width_extra + groove_ro, 1139 lower_extent]) 1140 rotate([0, 0, 180]) 1141 fillet_partitioned(groove_rr, inner_connector_front_cutout_height); 1142 1143 translate([connector_width / 2 - groove_depth - groove_ro, -groove_width_extra + groove_ro, 1144 lower_extent]) 1145 rotate([0, 0, -90]) 1146 fillet_partitioned(groove_rr, inner_connector_front_cutout_height); 1147 1148 /* Top of payload. */ 1149 1150 translate([payload_centre, -groove_width_extra + groove_ro, 1151 int_payload_upper_extent + inner_top_front_cutout_height - groove_ro]) 1152 rotate([0, 0, 180]) 1153 rotate([0, -90, 0]) 1154 fillet(groove_rr, payload_width - groove_depth * 2); 1155 } 1156 } 1157 1158 /* PCBs for checking. */ 1159 1160 if (PCB) if (MODEL == ROM_CARTRIDGE) { 1161 translate([0, 0, int_payload_lower_extent]) 1162 difference() { 1163 1164 /* Mock PCB. */ 1165 1166 union() { 1167 cube_at(pcb_width, pcb_depth, 1168 pcb_height - edge_connector_height, 1169 0, -1, 1, 1170 0, edge_connector_cutout_back_depth, 0); 1171 cube_at(edge_connector_width, pcb_depth, 1172 edge_connector_height, 1173 0, -1, -1, 1174 0, edge_connector_cutout_back_depth, 0); 1175 } 1176 1177 /* Holes for mounting. */ 1178 1179 union() { 1180 cube_at(pcb_hole_width, pcb_hole_depth, pcb_left_hole_height, 1181 -1, -1, 1, 1182 pcb_left_hole_offset_from_centre, pcb_hole_start_depth, 1183 pcb_left_hole_offset); 1184 translate([pcb_left_hole_offset_from_centre - 1185 pcb_hole_width / 2, pcb_hole_start_depth, pcb_left_hole_offset]) 1186 rotate([90, 0, 0]) 1187 cylinder(h=pcb_hole_depth, r=pcb_hole_width / 2); 1188 translate([pcb_left_hole_offset_from_centre - 1189 pcb_hole_width / 2, pcb_hole_start_depth, 1190 pcb_left_hole_offset + pcb_left_hole_height]) 1191 rotate([90, 0, 0]) 1192 cylinder(h=pcb_hole_depth, r=pcb_hole_width / 2); 1193 } 1194 1195 union() { 1196 cube_at(pcb_hole_width, pcb_hole_depth, pcb_right_hole_height, 1197 1, -1, 1, 1198 pcb_right_hole_offset_from_centre, pcb_hole_start_depth, 1199 pcb_right_hole_offset); 1200 translate([pcb_right_hole_offset_from_centre + 1201 pcb_hole_width / 2, pcb_hole_start_depth, pcb_right_hole_offset]) 1202 rotate([90, 0, 0]) 1203 cylinder(h=pcb_hole_depth, r=pcb_hole_width / 2); 1204 translate([pcb_right_hole_offset_from_centre + 1205 pcb_hole_width / 2, pcb_hole_start_depth, 1206 pcb_right_hole_offset + pcb_right_hole_height]) 1207 rotate([90, 0, 0]) 1208 cylinder(h=pcb_hole_depth, r=pcb_hole_width / 2); 1209 } 1210 1211 /* Holes for lugs. */ 1212 1213 translate([ 1214 -int_connector_width / 2 + pcb_lug_offset_from_inside, 1215 pcb_hole_start_depth, pcb_lug_offset_from_bottom]) 1216 rotate([90, 0, 0]) 1217 cylinder(h=pcb_hole_depth, r=pcb_lug_hole_radius); 1218 1219 translate([ 1220 int_connector_width / 2 - pcb_lug_offset_from_inside, 1221 pcb_hole_start_depth, pcb_lug_offset_from_bottom]) 1222 rotate([90, 0, 0]) 1223 cylinder(h=pcb_hole_depth, r=pcb_lug_hole_radius); 1224 } 1225 } 1226 1227 if (PCB) if (MODEL == WIDE_CARTRIDGE) { 1228 translate([0, 0, int_payload_lower_extent]) 1229 difference() { 1230 1231 /* Mock PCB. */ 1232 1233 union() { 1234 cube_at(wide_pcb_width, wide_pcb_depth, 1235 wide_pcb_height - edge_connector_height, 1236 0, -1, 1, 1237 payload_centre, edge_connector_cutout_back_depth, 0); 1238 cube_at(edge_connector_width, wide_pcb_depth, 1239 edge_connector_height, 1240 0, -1, -1, 1241 0, edge_connector_cutout_back_depth, 0); 1242 } 1243 1244 /* Holes for lugs. */ 1245 1246 translate([ 1247 payload_centre + int_payload_width / 2 - wide_pcb_lug_offset_from_inside , 1248 wide_pcb_hole_start_depth, 1249 wide_pcb_height - edge_connector_height - wide_pcb_lug_offset_from_bottom]) 1250 rotate([90, 0, 0]) 1251 cylinder(h=wide_pcb_hole_depth, r=wide_pcb_lug_hole_radius); 1252 1253 translate([ 1254 payload_centre - int_payload_width / 2 + wide_pcb_lug_offset_from_inside , 1255 wide_pcb_hole_start_depth, 1256 wide_pcb_height - edge_connector_height - wide_pcb_lug_offset_from_bottom]) 1257 rotate([90, 0, 0]) 1258 cylinder(h=wide_pcb_hole_depth, r=wide_pcb_lug_hole_radius); 1259 1260 translate([ 1261 payload_centre + int_payload_width / 2 - wide_pcb_lug_offset_from_inside , 1262 wide_pcb_hole_start_depth, 1263 wide_pcb_lug_offset_from_bottom]) 1264 rotate([90, 0, 0]) 1265 cylinder(h=wide_pcb_hole_depth, r=wide_pcb_lug_hole_radius); 1266 1267 translate([ 1268 payload_centre - int_payload_width / 2 + wide_pcb_lug_offset_from_inside , 1269 wide_pcb_hole_start_depth, 1270 wide_pcb_lug_offset_from_bottom]) 1271 rotate([90, 0, 0]) 1272 cylinder(h=wide_pcb_hole_depth, r=wide_pcb_lug_hole_radius); 1273 } 1274 } 1275 } 1276 } 1277 1278 cartridge();