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