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