1 /* 2 Copyright (C) 2014, 2015, 2016 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 /* Model configurations. Do not edit! */ 23 24 ROM_CARTRIDGE = 0; 25 WIDE_CARTRIDGE = 1; 26 27 /* Set this to the desired model type. */ 28 29 MODEL = ROM_CARTRIDGE; 30 31 /* Plug and socket options. Do not edit! */ 32 33 BACK_PLUG = 0; 34 BACK_SOCKET = 1; 35 36 /* Set this to the desired lug type. */ 37 38 BACK_LUG = BACK_PLUG; 39 40 /* 41 Configure the generated shapes. 42 For the absolute simplest shape (for basic 3D printers)... 43 44 BACK_CONNECTOR_SECTION = 0; 45 FRONT_LABEL_INSET = 0; TOP_LABEL_INSET = 0; 46 GROOVE = 0; ROUND_EDGES = 0; ROUND_CONNECTING_EDGES = 0; 47 48 To retain the back connecting piece instead, change the above to... 49 50 BACK_CONNECTOR_SECTION = 1; 51 52 And set the following options... 53 54 EXTERNAL_BACK_CAVITY = 0; 55 EXTERNAL_BACK_CUTOUT = 1; 56 57 Leave all options enabled for more capable 3D printers. 58 */ 59 60 /* The back part of the connecting section of the cartridge. */ 61 BACK_CONNECTOR_SECTION = 1; 62 63 /* A necessary exterior cavity to let the cartridge fit inside the slot. */ 64 EXTERNAL_BACK_CAVITY = 1; 65 66 /* An alternative cutout to let the cartridge fit inside the slot. */ 67 EXTERNAL_BACK_CUTOUT = 0; 68 69 /* Optional insets for labels. */ 70 FRONT_LABEL_INSET = 1; 71 TOP_LABEL_INSET = 1; 72 73 /* Optional groove between the pieces to match the Electron. */ 74 GROOVE = 1; 75 76 /* Optional rounding of the external edges. */ 77 ROUND_EDGES = 1; 78 79 /* Recommended rounding of connection edges. */ 80 ROUND_CONNECTING_EDGES = 1; 81 82 /* Set this where a shorter PCB is to be used. */ 83 84 SHORT_PCB = 0; 85 86 /* 87 Options for checking. Some useful combinations... 88 89 Check feature alignment: APART = 0; BACK_SURFACE = 0; FRONT_SURFACE = 0; 90 Check PCB alignment: PCB = 1; BACK_SURFACE = 0; 91 */ 92 93 BACK = 1; BACK_SURFACE = 1; 94 FRONT = 1; FRONT_SURFACE = 1; 95 TOP_SURFACE = 1; 96 97 /* For printing: APART = 1; PCB = 0; */ 98 99 APART = 1; 100 PCB = 0; 101 102 /* To check overlaps: INTERSECT = 1; CLOSED = 1; */ 103 104 INTERSECT = 0; 105 CLOSED = 0; 106 107 /* Separation when not apart and not closed. */ 108 109 SEPARATION = 10; 110 111 /* To save time (before printing): FILLET = 0; */ 112 113 FILLET = 1; 114 115 /* 116 Rounding/fillet radius and additional margin of subtracted 117 material. The additional margin helps avoid geometry problems. 118 */ 119 120 rr = 2; 121 ro = rr; 122 extra = 0.1; 123 124 groove_rr = 0.2; 125 groove_ro = groove_rr; 126 127 pcb_lug_rr = 0.5; 128 pcb_lug_ro = pcb_lug_rr; 129 130 /* A fillet performs rounding using a quarter segment of a cylinder. */ 131 132 module fillet(r, h) { 133 if (FILLET) 134 translate([0, 0, -h/2]) 135 difference() { 136 cube([r + extra, r + extra, h + extra]); 137 cylinder(r = r, h = h); 138 } 139 } 140 141 /* A fillet justified using the axes. */ 142 143 module fillet_justified(r, h) { 144 if (FILLET) 145 difference() { 146 cube([r + extra, r + extra, h + extra]); 147 cylinder(r = r, h = h); 148 } 149 } 150 151 /* A justified fillet with the extra material below the y-axis. */ 152 153 module fillet_partitioned(r, h) { 154 if (FILLET) 155 difference() { 156 translate([0, 0, -extra]) 157 cube([r + extra, r + extra, h + extra]); 158 cylinder(r = r, h = h); 159 } 160 } 161 162 module fillet_torus(radius, rounding) { 163 if (FILLET) 164 difference() { 165 cube_at((radius + extra) * 2, (radius + extra) * 2, rounding + extra, 166 0, 0, 1, 167 0, 0, 0); 168 union() { 169 rotate_extrude(convexity = 10) 170 translate([radius - rounding, 0, 0]) 171 circle(r = rounding); 172 cylinder(r = radius - rounding, h = rounding); 173 } 174 } 175 } 176 177 /* 178 Justify an object of the given dimensions, according to the given 179 factors (where 1 indicates moving the object to the positive side of an 180 axis, and -1 indicates moving it to the negative side of an axis). 181 182 NOTE: child should eventually be replaced by children. 183 */ 184 module justify(width, depth, height, wdir, ddir, hdir) { 185 translate([ 186 wdir * width / 2, 187 ddir * depth / 2, 188 hdir * height / 2]) 189 child(); 190 } 191 192 /* 193 Make a cuboid of the given dimensions, justifying it according to the given 194 factors, and moving it to the specified coordinates. 195 196 NOTE: Usage of justify within this module will not work due to recursion 197 NOTE: limitations in openscad, potentially removed in more recent 198 NOTE: releases. Thus, the justify transform is merged in here. 199 */ 200 module cube_at(width, depth, height, wdir, ddir, hdir, x, y, z) { 201 translate([ 202 x + wdir * width / 2, 203 y + ddir * depth / 2, 204 z + hdir * height / 2]) 205 cube([width, depth, height], center = true); 206 } 207 208 /* Model widths. */ 209 210 ROM_CARTRIDGE_int_payload_width = 86.0; 211 WIDE_CARTRIDGE_int_payload_width = 140.0; 212 213 /* Internal dimensions. */ 214 215 /* internal width in the payload section */ 216 int_payload_width = 217 MODEL == ROM_CARTRIDGE ? ROM_CARTRIDGE_int_payload_width : 218 MODEL == WIDE_CARTRIDGE ? WIDE_CARTRIDGE_int_payload_width : 219 ROM_CARTRIDGE_int_payload_width; 220 221 int_connector_width = 86.0; /* limited to the Plus 1 socket dimensions */ 222 int_payload_depth = 14.0; /* maximum depth in the payload section */ 223 int_connector_depth = 11.5; /* maximum depth in the connector section */ 224 int_payload_height = 51.5; /* space between the top and the floor */ 225 int_connector_height = 13.5; /* vertical offset of bottom/floor of payload area */ 226 227 /* Side thicknesses. */ 228 229 front = 2; 230 payload_back = 1; /* in the payload area the thickness is reduced */ 231 connector_back = 3.5; /* the back cavity requires a thicker back wall */ 232 top = 3; 233 side = 2; /* increased from 1.5 for 3D printing reliability */ 234 bottom = 1; /* thickness of floor of payload area */ 235 236 /* How much extra depth the back provides for mating with the front. */ 237 238 groove_width_extra = 2.0; /* the depth of the groove accommodating the front rim */ 239 front_back_overlap = 1.0; 240 groove_width_overlap = front_back_overlap + groove_width_extra; 241 242 /* 243 Division of pieces into front and back, defining the extents. The back 244 depth is extended by the front/back overlap, and thus the internal back 245 depth calculated here is greater than the actual depth that the back 246 portion can hold. 247 */ 248 249 int_front_depth = 4.5; 250 front_depth = int_front_depth + front; 251 int_payload_back_depth = int_payload_depth - int_front_depth + front_back_overlap; 252 int_connector_back_depth = int_connector_depth - int_front_depth + front_back_overlap; 253 back_depth = int_payload_back_depth + payload_back; 254 255 /* Cartridge dimensions. */ 256 257 payload_width = int_payload_width + side + side; 258 payload_height = top + int_payload_height; 259 260 connector_width = int_connector_width + side + side; 261 connector_height = bottom + int_connector_height; 262 263 height = payload_height + bottom + int_connector_height; 264 depth = int_payload_depth + front + payload_back; 265 266 upper_extent = height / 2; 267 lower_extent = -upper_extent; 268 int_payload_upper_extent = upper_extent - top; 269 int_payload_lower_extent = lower_extent + int_connector_height + bottom; 270 271 /* Connector extents. */ 272 273 int_connector_right_extent = int_connector_width / 2; 274 int_connector_left_extent = -int_connector_right_extent; 275 connector_left_extent = int_connector_left_extent - side; 276 connector_right_extent = int_connector_right_extent + side; 277 278 /* Where the payload is wider than the connector, the payload expands to the left. */ 279 280 int_payload_right_extent = int_connector_right_extent; 281 int_payload_left_extent = int_payload_right_extent - int_payload_width; 282 payload_left_extent = int_payload_left_extent - side; 283 payload_right_extent = int_payload_right_extent + side; 284 payload_centre = (payload_left_extent + payload_right_extent) / 2; 285 286 /* Cartridge surfaces. */ 287 288 front_side = side; 289 front_left = front_side; 290 front_right = front_side; 291 back_side = side; 292 back_left = back_side; 293 back_right = back_side; 294 295 /* Label details. */ 296 297 front_label_width = payload_width - side - side - 3.0; 298 front_label_height = 46.0; 299 front_label_depth = 1.0; 300 front_label_offset_from_bottom = 19.5; 301 front_label_centre = payload_centre; 302 front_label_left_extent = front_label_centre - front_label_width / 2; 303 304 top_label_width = front_label_width; 305 top_label_height = 11.5; /* the height as seen from above */ 306 top_label_depth = front_label_depth; 307 top_label_offset_from_front = 2.5; 308 top_label_centre = payload_centre; 309 top_label_left_extent = top_label_centre - front_label_width / 2; 310 311 /* 312 The groove around the sides and top. This is extended to allow the 313 pieces to fit together, and this extension is generated regardless of 314 whether the visible groove is enabled or not. 315 */ 316 317 groove_width_exposed = GROOVE ? 1.5 : 0; 318 groove_width_normal = groove_width_exposed + front_back_overlap; 319 groove_depth = 1.0; /* how deep the groove goes into each side */ 320 321 /* Additional cutting to mate the back and front. */ 322 323 top_groove_width = groove_width_overlap; 324 top_groove_depth = 2.0; 325 326 /* 327 Space for the inner edge of the back inside the front. 328 Offsets are measured from the outside surfaces. 329 */ 330 331 inner_top_front_cutout_width = int_payload_width; 332 inner_top_front_cutout_depth = top_groove_width; 333 inner_top_front_cutout_height = top - top_groove_depth; 334 335 inner_payload_front_cutout_height = payload_height - top_groove_depth; 336 inner_payload_front_cutout_width = front_side - groove_depth; 337 inner_payload_front_cutout_depth = groove_width_overlap; 338 339 inner_connector_front_cutout_height = connector_height; 340 inner_connector_front_cutout_width = front_side - groove_depth; 341 inner_connector_front_cutout_depth = groove_width_overlap; 342 343 /* 344 The back cavity is the indented part at the bottom of the back of the 345 cartridge. It appears to be mechanically necessary, making sure that 346 cartridges cannot be plugged in the wrong way round. 347 */ 348 349 back_cavity_width = 68.0; 350 back_cavity_inner_width = 65.0; 351 back_cavity_offset_from_inner_left = 9.0; 352 back_cavity_inner_offset_from_inner_left = 10.5; 353 back_cavity_height = 13.5; 354 back_cavity_inner_height = 12.0; 355 back_cavity_depth = 1.5; 356 357 /* 358 The effect of the cavity on the inside of the case. In principle, the 359 interior of the case could be straight since the plastic guides of the 360 Plus 1 socket are outside the case. 361 */ 362 363 inner_back_slope_depth = 2.5; 364 inner_back_slope_width = inner_back_slope_depth; 365 inner_back_slope_max_offset = 10.5; 366 inner_back_slope_min_offset = inner_back_slope_max_offset - inner_back_slope_width; 367 368 /* The tapering off of the inner back edge. */ 369 370 inner_back_edge_width = 69.0; 371 inner_back_edge_height = 3.0; 372 inner_back_edge_depth = 1.5; 373 374 /* The tapering off of the inner front edge. */ 375 376 inner_front_edge_width = connector_width - front_side * 2; 377 inner_front_edge_height = 3.0; 378 inner_front_edge_depth = 1.5; 379 380 /* 381 The cutout in the floor of the back of the cartridge that accommodates 382 the edge connector. 383 */ 384 385 edge_connector_cutout_back_depth = 3.0; 386 edge_connector_cutout_back_width = 57.5; 387 388 /* 389 The cutouts in the floor of the front of the cartridge that produce a 390 kind of tab that guides the edge connector into place in the back cutout. 391 */ 392 393 edge_connector_cutout_front_depth = front_back_overlap; 394 edge_connector_cutout_front_width = (int_connector_width - edge_connector_cutout_back_width) / 2; 395 396 /* 397 Edge connectors are themselves 0.05" or approximately 1.27mm in 398 thickness according to the Acorn Electron Cartridge Interface Specification 399 (Acorn Support Application Group Note 014). 400 */ 401 402 /* Extra internal features. Depths include front and back surfaces. */ 403 404 pcb_back_support_width = 1.2; 405 pcb_back_support_depth = back_depth - 406 edge_connector_cutout_back_depth; 407 pcb_back_support_height = height - int_connector_height - top - bottom; 408 409 pcb_front_support_width = pcb_back_support_width; 410 pcb_front_support_depth = int_front_depth; 411 pcb_front_support_height = pcb_back_support_height; 412 413 /* 414 Features measured from the Stardot Dual ROM Adaptor cartridge board 415 dimensions diagram. Offsets are from inside the bottom "floor". 416 */ 417 418 pcb_back_support_bump_width = pcb_front_support_width; 419 pcb_back_support_bump_depth = 1.5; 420 pcb_back_support_left_bump_height = 13.2; 421 pcb_back_support_right_bump_height = 10.7; 422 pcb_back_support_left_bump_offset_from_bottom = 15.1; 423 pcb_back_support_right_bump_offset_from_bottom = 17.6; 424 pcb_back_support_break_from_bottom = 35.3; /* 36.45 from the above diagram */ 425 pcb_back_support_break_height = 13; /* 11.55 from the above diagram */ 426 427 /* Configured by SHORT_PCB. */ 428 pcb_back_support_top_bump_height = int_payload_height 429 - pcb_back_support_break_from_bottom - pcb_back_support_break_height; 430 431 /* Move the PCB support towards the centre. */ 432 pcb_support_margin = 1.75; 433 pcb_support_offset_from_centre = edge_connector_cutout_back_width / 2 434 - pcb_support_margin; 435 436 /* 437 The PCB lugs protrude through the holes in the PCB. By extending both 438 the front and back lugs by the depth of the back support bump, they 439 overlap by that amount. The "mating" depth is defined as that amount 440 plus an additional amount for adhesion. 441 */ 442 443 pcb_lug_mating_depth = pcb_back_support_bump_depth + 1.0; 444 445 pcb_back_lug_depth = pcb_back_support_depth + 446 pcb_lug_mating_depth; 447 pcb_back_lug_inner_radius = 1.0; 448 pcb_back_lug_outer_radius = 5.5 / 2; 449 450 /* 451 The cross detail on the back lug is intended to resist the front lug, 452 and thus starts at the point that the front lug ends. 453 */ 454 455 pcb_lug_cross_width = 6.7; 456 pcb_lug_cross_depth = pcb_back_support_depth + pcb_back_support_bump_depth - 457 pcb_lug_mating_depth; 458 pcb_lug_cross_height = 1.4; 459 460 /* 461 The front depth is calculated similarly to the back depth, with the 462 outer radius of the socket corresponding to the dimensions of the cross 463 detail. The inner radius of the front's socket is the same as the outer 464 radius of the back's plug, with friction holding the two together. 465 */ 466 467 pcb_front_lug_depth = pcb_front_support_depth + 468 pcb_lug_mating_depth; 469 pcb_front_lug_inner_radius = pcb_back_lug_outer_radius; 470 pcb_front_lug_outer_radius = pow( 471 pow(pcb_lug_cross_width / 2, 2) + 472 pow(pcb_lug_cross_height / 2, 2), 473 0.5); 474 475 /* 476 With BACK_LUG as BACK_SOCKET, no overlap is desired, since both 477 front and back provide sockets. The depth is accounted for as follows: 478 479 int_payload_back_depth + payload_back == back_depth 480 + int_front_depth - pcb_front_lug_depth == back lug space in the front 481 - front_back_overlap == correction for overlap 482 483 int_front_depth - pcb_front_lug_depth 484 == pcb_front_support_depth - pcb_front_support_depth - pcb_lug_mating_depth 485 == -pcb_lug_mating_depth 486 487 Note that the lug starts at the outer back surface, which is why the 488 payload_back quantity is included. 489 */ 490 491 pcb_back_socket_lug_depth = back_depth - front_back_overlap - pcb_lug_mating_depth; 492 493 /* Both front and back lugs need to be positioned in the same place. */ 494 495 pcb_lug_offset_from_bottom = 14.35; 496 pcb_lug_offset_from_inside = 5.55; 497 498 wide_pcb_lug_offset_from_inside = 6.0; 499 wide_pcb_lug_offset_from_bottom = 6.0; 500 501 /* PCBs for checking. */ 502 503 pcb_width = 84.85; pcb_height = 62.5; pcb_depth = 1; 504 edge_connector_width = 56.5; edge_connector_height = 11.85; 505 506 /* 507 The rectangular part of the narrow left and right holes is smaller 508 than the "bump" in the case, but the circular parts make the overall 509 hole larger than the "bump". An extra depth is used for holes to avoid 510 surface definition problems. 511 */ 512 513 pcb_hole_margin = 0.55; 514 pcb_hole_width = 2.2; 515 pcb_hole_extra_depth = 0.1; 516 pcb_lug_hole_radius = 3.75; 517 518 pcb_left_hole_offset = pcb_back_support_left_bump_offset_from_bottom + pcb_hole_margin; 519 pcb_left_hole_height = pcb_back_support_left_bump_height - pcb_hole_margin * 2; 520 pcb_left_hole_offset_from_centre = -(pcb_support_offset_from_centre + 521 pcb_back_support_width / 2 - pcb_hole_width / 2); 522 523 pcb_right_hole_offset = pcb_back_support_right_bump_offset_from_bottom + pcb_hole_margin; 524 pcb_right_hole_height = pcb_back_support_right_bump_height - pcb_hole_margin * 2; 525 pcb_right_hole_offset_from_centre = -pcb_left_hole_offset_from_centre; 526 527 pcb_hole_depth = pcb_depth + 2 * pcb_hole_extra_depth; 528 pcb_hole_start_depth = edge_connector_cutout_back_depth + pcb_hole_extra_depth; 529 530 wide_pcb_width = 138.0; wide_pcb_height = pcb_height; wide_pcb_depth = pcb_depth; 531 wide_pcb_hole_depth = pcb_hole_depth; 532 wide_pcb_hole_start_depth = pcb_hole_start_depth; 533 534 wide_pcb_lug_hole_radius = 4.0; 535 536 /* An example feature for use with PCB testing. */ 537 538 feature_width = 15.0; feature_depth = 7.0; feature_height = 40.0; 539 feature_height_offset = 10.0; 540 541 /* Repeated constructs. */ 542 543 module pcb_support(xdir, bump_height, bump_offset, break_offset, break_height) { 544 545 /* 546 Translate the support in the stated direction. 547 Since the support is already justified in the direction, the translation 548 moves the inner edge of the support to alignment with the end of the 549 edge connector cutout and then back by the PCB support margin. 550 */ 551 552 translate([xdir * 553 pcb_support_offset_from_centre, 554 edge_connector_cutout_back_depth, 555 int_payload_lower_extent]) { 556 557 /* 558 Convert the co-ordinates from being centred on the origin to being 559 "justified" to have either positive or negative x values, and 560 having positive y and z values. 561 */ 562 563 justify(pcb_back_support_width, 564 pcb_back_support_depth, 565 pcb_back_support_height, 566 xdir, 1, 1) { 567 568 union() { 569 570 /* Underlying support strut. */ 571 572 difference() { 573 cube([pcb_back_support_width, 574 pcb_back_support_depth, 575 pcb_back_support_height], center = true); 576 cube_at(pcb_back_support_width, 577 pcb_back_support_depth, 578 break_height, 579 0, 1, 1, 580 0, 581 -pcb_back_support_depth / 2, 582 -pcb_back_support_height / 2 + break_offset); 583 } 584 585 /* Middle bump. */ 586 587 cube_at(pcb_back_support_bump_width, 588 pcb_back_support_bump_depth, 589 bump_height, 590 0, -1, 1, 591 0, 592 -pcb_back_support_depth / 2, 593 -pcb_back_support_height / 2 + bump_offset); 594 595 /* Top bump. */ 596 597 if (SHORT_PCB) 598 cube_at(pcb_back_support_bump_width, 599 pcb_back_support_bump_depth, 600 pcb_back_support_top_bump_height, 601 0, -1, 1, 602 0, 603 -pcb_back_support_depth / 2, 604 pcb_back_support_height / 2 - 605 pcb_back_support_top_bump_height); 606 } 607 } 608 } 609 } 610 611 /* Choose the kind of "lug" in the back piece. */ 612 613 module pcb_lug(xdir) { 614 if (BACK_LUG == BACK_PLUG) { 615 pcb_back_plug_lug(xdir); 616 } else if (BACK_LUG == BACK_SOCKET) { 617 pcb_back_socket_lug(xdir); 618 } 619 } 620 621 module pcb_lug_wide(xdir) { 622 if (BACK_LUG == BACK_PLUG) { 623 pcb_back_plug_lug_wide(xdir); 624 } else if (BACK_LUG == BACK_SOCKET) { 625 pcb_back_socket_lug_wide(xdir); 626 } 627 } 628 629 /* The conventional back "lug" which is actually a plug. */ 630 631 module pcb_back_plug_lug(xdir) { 632 pcb_back_plug_lug_explicit(xdir, int_connector_width / 2 - pcb_lug_offset_from_inside, 633 pcb_lug_offset_from_bottom); 634 } 635 636 module pcb_back_plug_lug_wide(xdir) { 637 pcb_back_plug_lug_explicit(xdir, int_payload_width / 2 - wide_pcb_lug_offset_from_inside, 638 wide_pcb_lug_offset_from_bottom); 639 pcb_back_plug_lug_explicit(xdir, int_payload_width / 2 - wide_pcb_lug_offset_from_inside, 640 wide_pcb_height - edge_connector_height - wide_pcb_lug_offset_from_bottom); 641 } 642 643 module pcb_back_plug_lug_explicit(xdir, offset_from_centre, offset_from_bottom) { 644 translate([payload_centre + xdir * offset_from_centre, 645 back_depth, 646 int_payload_lower_extent + offset_from_bottom 647 ]) 648 rotate([90, 0, 0]) 649 difference() { 650 651 /* Cylinder with cross. */ 652 653 union() { 654 cylinder(h=pcb_back_lug_depth, r=pcb_back_lug_outer_radius); 655 cube_at(pcb_lug_cross_width, 656 pcb_lug_cross_height, pcb_lug_cross_depth, 657 0, 0, 1, 658 0, 0, 0); 659 cube_at(pcb_lug_cross_height, 660 pcb_lug_cross_width, pcb_lug_cross_depth, 661 0, 0, 1, 662 0, 0, 0); 663 } 664 665 /* Hollowed out by a cylinder. */ 666 667 cylinder(h=pcb_back_lug_depth, r=pcb_back_lug_inner_radius); 668 669 /* Tapered off for easier connection. */ 670 671 if (ROUND_CONNECTING_EDGES) 672 translate([0, 0, pcb_back_lug_depth - pcb_lug_ro]) 673 fillet_torus(pcb_back_lug_outer_radius, pcb_lug_rr); 674 } 675 } 676 677 /* The conventional front "lug" which is actually a socket. */ 678 679 module pcb_front_lug(xdir) { 680 pcb_socket_lug_explicit(xdir, int_connector_width / 2 - pcb_lug_offset_from_inside, 681 -int_front_depth + pcb_front_lug_depth, 682 pcb_lug_offset_from_bottom, 683 pcb_front_lug_depth, pcb_front_lug_outer_radius, pcb_front_lug_inner_radius); 684 } 685 686 module pcb_front_lug_wide(xdir) { 687 pcb_socket_lug_explicit(xdir, int_payload_width / 2 - wide_pcb_lug_offset_from_inside, 688 -int_front_depth + pcb_front_lug_depth, 689 wide_pcb_lug_offset_from_bottom, 690 pcb_front_lug_depth, pcb_front_lug_outer_radius, pcb_front_lug_inner_radius); 691 pcb_socket_lug_explicit(xdir, int_payload_width / 2 - wide_pcb_lug_offset_from_inside, 692 -int_front_depth + pcb_front_lug_depth, 693 wide_pcb_height - edge_connector_height - wide_pcb_lug_offset_from_bottom, 694 pcb_front_lug_depth, pcb_front_lug_outer_radius, pcb_front_lug_inner_radius); 695 } 696 697 /* 698 The alternative back "lug" which is actually a socket as well, meaning 699 that a cylinder runs from the front surface to the back, with the two 700 sections meeting in the middle. 701 */ 702 703 module pcb_back_socket_lug(xdir) { 704 pcb_socket_lug_explicit(xdir, int_connector_width / 2 - pcb_lug_offset_from_inside, 705 back_depth, 706 pcb_lug_offset_from_bottom, 707 pcb_back_socket_lug_depth, pcb_front_lug_outer_radius, pcb_front_lug_inner_radius); 708 } 709 710 module pcb_back_socket_lug_wide(xdir) { 711 pcb_socket_lug_explicit(xdir, int_payload_width / 2 - wide_pcb_lug_offset_from_inside, 712 back_depth, 713 wide_pcb_lug_offset_from_bottom, 714 pcb_back_socket_lug_depth, pcb_front_lug_outer_radius, pcb_front_lug_inner_radius); 715 pcb_socket_lug_explicit(xdir, int_payload_width / 2 - wide_pcb_lug_offset_from_inside, 716 back_depth, 717 wide_pcb_height - edge_connector_height - wide_pcb_lug_offset_from_bottom, 718 pcb_back_socket_lug_depth, pcb_front_lug_outer_radius, pcb_front_lug_inner_radius); 719 } 720 721 /* The common implementation of the cylinder socket. */ 722 723 module pcb_socket_lug_explicit(xdir, offset_from_centre, depth_offset, offset_from_bottom, 724 lug_depth, lug_outer_radius, lug_inner_radius) { 725 726 translate([payload_centre + xdir * offset_from_centre, 727 depth_offset, 728 int_payload_lower_extent + offset_from_bottom 729 ]) 730 rotate([90, 0, 0]) 731 difference() { 732 cylinder(h=lug_depth, 733 r=lug_outer_radius); 734 cylinder(h=lug_depth, 735 r=lug_inner_radius); 736 } 737 } 738 739 /* The holes in the front surface when socket lugs are used. */ 740 741 module pcb_front_socket_hole(xdir) { 742 pcb_socket_hole_explicit(xdir, int_connector_width / 2 - pcb_lug_offset_from_inside, 743 -int_front_depth + extra, 744 pcb_lug_offset_from_bottom, 745 front + extra * 2, pcb_front_lug_inner_radius); 746 } 747 748 module pcb_front_socket_hole_wide(xdir) { 749 pcb_socket_hole_explicit(xdir, int_payload_width / 2 - wide_pcb_lug_offset_from_inside, 750 -int_front_depth + extra, 751 wide_pcb_lug_offset_from_bottom, 752 front + extra * 2, pcb_front_lug_inner_radius); 753 pcb_socket_hole_explicit(xdir, int_payload_width / 2 - wide_pcb_lug_offset_from_inside, 754 -int_front_depth + extra, 755 wide_pcb_height - edge_connector_height - wide_pcb_lug_offset_from_bottom, 756 front + extra * 2, pcb_front_lug_inner_radius); 757 } 758 759 /* The holes in the back surface when socket lugs are used. */ 760 761 module pcb_back_socket_hole(xdir) { 762 pcb_socket_hole_explicit(xdir, int_connector_width / 2 - pcb_lug_offset_from_inside, 763 back_depth + extra, 764 pcb_lug_offset_from_bottom, 765 payload_back + extra * 2, pcb_front_lug_inner_radius); 766 } 767 768 module pcb_back_socket_hole_wide(xdir) { 769 pcb_socket_hole_explicit(xdir, int_payload_width / 2 - wide_pcb_lug_offset_from_inside, 770 back_depth + extra, 771 wide_pcb_lug_offset_from_bottom, 772 payload_back + extra * 2, pcb_front_lug_inner_radius); 773 pcb_socket_hole_explicit(xdir, int_payload_width / 2 - wide_pcb_lug_offset_from_inside, 774 back_depth + extra, 775 wide_pcb_height - edge_connector_height - wide_pcb_lug_offset_from_bottom, 776 payload_back + extra * 2, pcb_front_lug_inner_radius); 777 } 778 779 /* 780 The common implementation of the circular hole used when both pieces 781 employ the socket lug. 782 */ 783 784 module pcb_socket_hole_explicit(xdir, offset_from_centre, depth_offset, offset_from_bottom, 785 hole_depth, hole_radius) { 786 787 translate([payload_centre + xdir * offset_from_centre, 788 depth_offset, 789 int_payload_lower_extent + offset_from_bottom 790 ]) 791 rotate([90, 0, 0]) 792 cylinder(h=hole_depth, r=hole_radius); 793 } 794 795 /* The actual shapes. */ 796 797 module front_piece(front_displacement, front_displacement_together) { 798 if (FRONT) 799 translate([front_displacement, front_displacement_together, 0]) 800 difference() { 801 802 /* The cartridge surfaces. */ 803 804 union() { 805 806 /* Front portion. */ 807 808 if (FRONT_SURFACE) { 809 810 /* Surfaces surrounding the payload. */ 811 812 cube_at(payload_width, front, payload_height, 813 0, -1, 1, 814 payload_centre, -int_front_depth, int_payload_lower_extent); 815 816 cube_at(front_left, front_depth, payload_height, 817 -1, -1, 1, 818 int_payload_left_extent, 0, int_payload_lower_extent); 819 820 cube_at(front_right, front_depth, payload_height, 821 1, -1, 1, 822 int_payload_right_extent, 0, int_payload_lower_extent); 823 824 /* Surfaces surrounding the connector. */ 825 826 cube_at(connector_width, front, connector_height, 827 0, -1, -1, 828 0, -int_front_depth, int_payload_lower_extent); 829 830 cube_at(front_left, front_depth, connector_height, 831 -1, -1, -1, 832 int_connector_left_extent, 0, int_payload_lower_extent); 833 834 cube_at(front_right, front_depth, connector_height, 835 1, -1, -1, 836 int_connector_right_extent, 0, int_payload_lower_extent); 837 838 /* Top surface for the front piece. */ 839 840 if (TOP_SURFACE) 841 cube_at(payload_width, front_depth, top, 842 0, -1, 1, 843 payload_centre, 0, int_payload_upper_extent); 844 } 845 846 difference() { 847 848 /* Floor of cartridge. */ 849 850 if (payload_width > connector_width) { 851 cube_at(payload_width, front_depth, bottom, 852 1, -1, 1, 853 payload_left_extent, 0, lower_extent + int_connector_height); 854 } else { 855 cube_at(int_connector_width, int_front_depth, bottom, 856 0, -1, 1, 857 0, 0, lower_extent + int_connector_height); 858 } 859 860 /* Left cutout. */ 861 862 cube_at(edge_connector_cutout_front_width, 863 edge_connector_cutout_front_depth, 864 bottom, 865 1, -1, 1, 866 int_connector_left_extent, 867 0, 868 lower_extent + int_connector_height); 869 870 /* Right cutout. */ 871 872 cube_at(edge_connector_cutout_front_width, 873 edge_connector_cutout_front_depth, 874 bottom, 875 -1, -1, 1, 876 int_connector_right_extent, 877 0, 878 lower_extent + int_connector_height); 879 } 880 881 /* PCB supports. */ 882 883 if (MODEL == ROM_CARTRIDGE) { 884 cube_at(pcb_front_support_width, 885 pcb_front_support_depth, 886 pcb_front_support_height, 887 -1, -1, 1, 888 -pcb_support_offset_from_centre, 889 0, 890 int_payload_lower_extent); 891 892 cube_at(pcb_front_support_width, 893 pcb_front_support_depth, 894 pcb_front_support_height, 895 1, -1, 1, 896 pcb_support_offset_from_centre, 897 0, 898 int_payload_lower_extent); 899 900 /* Circular "lugs" to hold PCBs in place. */ 901 902 pcb_front_lug(-1); 903 pcb_front_lug(1); 904 } 905 906 if (MODEL == WIDE_CARTRIDGE) { 907 908 pcb_front_lug_wide(-1); 909 pcb_front_lug_wide(1); 910 } 911 } 912 913 /* Label insets. */ 914 915 union() { 916 917 /* Front label. */ 918 919 if (FRONT_LABEL_INSET) 920 translate([front_label_left_extent, -front_depth, 921 lower_extent + front_label_offset_from_bottom]) 922 cube([front_label_width, front_label_depth, 923 front_label_height]); 924 925 /* Top label. See also the back piece. */ 926 927 if (TOP_LABEL_INSET) 928 translate([top_label_left_extent, 929 -front_depth + top_label_offset_from_front, 930 upper_extent - top_label_depth]) 931 cube([top_label_width, top_label_height, 932 top_label_depth]); 933 } 934 935 /* Front socket holes depending on back lug choices. */ 936 937 if (BACK_LUG == BACK_SOCKET) { 938 939 if (MODEL == ROM_CARTRIDGE) { 940 pcb_front_socket_hole(-1); 941 pcb_front_socket_hole(1); 942 } 943 944 if (MODEL == WIDE_CARTRIDGE) { 945 pcb_front_socket_hole_wide(-1); 946 pcb_front_socket_hole_wide(1); 947 } 948 } 949 950 /* Inner front edge cavity. */ 951 952 translate([inner_front_edge_width / 2, -int_front_depth, lower_extent]) 953 rotate([0, -90, 0]) 954 linear_extrude(height = inner_front_edge_width) 955 polygon([ 956 [-extra, -inner_front_edge_depth], 957 [0, -inner_front_edge_depth], 958 [inner_front_edge_height, 0], 959 [-extra, 0], 960 ]); 961 962 /* Inner grooves for the top and sides of the back portion. */ 963 964 cube_at(inner_top_front_cutout_width, 965 inner_top_front_cutout_depth, 966 inner_top_front_cutout_height, 967 1, -1, 1, 968 int_payload_left_extent, 0, int_payload_upper_extent); 969 970 cube_at(inner_payload_front_cutout_width, 971 inner_payload_front_cutout_depth, 972 inner_payload_front_cutout_height, 973 1, -1, 1, 974 int_payload_right_extent, 0, int_payload_lower_extent); 975 976 cube_at(inner_payload_front_cutout_width, 977 inner_payload_front_cutout_depth, 978 inner_payload_front_cutout_height, 979 -1, -1, 1, 980 int_payload_left_extent, 0, int_payload_lower_extent); 981 982 /* Cutout to accept the back connector sides (or the floor of the back piece). */ 983 984 cube_at(inner_connector_front_cutout_width, 985 BACK_CONNECTOR_SECTION ? inner_connector_front_cutout_depth : edge_connector_cutout_front_depth, 986 BACK_CONNECTOR_SECTION ? inner_connector_front_cutout_height : bottom, 987 1, -1, -1, 988 int_connector_right_extent, 0, int_payload_lower_extent); 989 990 cube_at(inner_connector_front_cutout_width, 991 BACK_CONNECTOR_SECTION ? inner_connector_front_cutout_depth : edge_connector_cutout_front_depth, 992 BACK_CONNECTOR_SECTION ? inner_connector_front_cutout_height : bottom, 993 -1, -1, -1, 994 int_connector_left_extent, 0, int_payload_lower_extent); 995 996 /* Fillets to round off the edges. */ 997 998 union() { 999 if (ROUND_EDGES) { 1000 1001 /* Top left and right rounding. */ 1002 1003 translate([payload_left_extent + ro, -front_depth / 2, upper_extent - ro]) 1004 rotate([0, 0, 180]) 1005 rotate([90, 0, 0]) 1006 fillet(rr, front_depth); 1007 translate([payload_right_extent - ro, -front_depth / 2, upper_extent - ro]) 1008 rotate([90, 0, 0]) 1009 fillet(rr, front_depth); 1010 1011 /* Top front rounding. */ 1012 1013 translate([payload_centre, -front_depth + ro, upper_extent - ro]) 1014 rotate([0, 0, 180]) 1015 rotate([0, -90, 0]) 1016 fillet(rr, payload_width); 1017 1018 /* Edge rounding. */ 1019 1020 translate([payload_right_extent - ro, -front_depth + ro, int_payload_lower_extent - bottom - extra]) 1021 rotate([0, 0, 270]) 1022 fillet_justified(rr, payload_height + bottom + extra); 1023 1024 translate([payload_left_extent + ro, -front_depth + ro, int_payload_lower_extent - bottom - extra]) 1025 rotate([0, 0, 180]) 1026 fillet_justified(rr, payload_height + bottom + extra); 1027 1028 translate([connector_right_extent - ro, -front_depth + ro, lower_extent]) 1029 rotate([0, 0, 270]) 1030 fillet_partitioned(rr, connector_height - bottom); 1031 1032 translate([connector_left_extent + ro, -front_depth + ro, lower_extent]) 1033 rotate([0, 0, 180]) 1034 fillet_partitioned(rr, connector_height - bottom); 1035 } 1036 } 1037 } 1038 } 1039 1040 module back_piece(back_displacement, back_rotation) { 1041 if (BACK) 1042 translate([back_displacement, 0, 0]) 1043 rotate([0, 0, back_rotation]) { 1044 difference() { 1045 1046 /* The cartridge surfaces. */ 1047 1048 union() { 1049 1050 /* Back portion. */ 1051 1052 if (BACK_SURFACE) { 1053 1054 /* Surfaces surrounding the payload. */ 1055 1056 /* Payload section of back surface. */ 1057 1058 cube_at(payload_width, payload_back, payload_height, 1059 0, 1, 1, 1060 payload_centre, int_payload_back_depth, int_payload_lower_extent); 1061 1062 cube_at(back_left, back_depth, payload_height, 1063 -1, 1, 1, 1064 int_payload_left_extent, 0, int_payload_lower_extent); 1065 1066 cube_at(back_right, back_depth, payload_height, 1067 1, 1, 1, 1068 int_payload_right_extent, 0, int_payload_lower_extent); 1069 1070 /* Surfaces surrounding the connector. */ 1071 1072 /* Connector section of back surface overlapping the floor. */ 1073 1074 if (BACK_CONNECTOR_SECTION) { 1075 cube_at(connector_width, connector_back, connector_height, 1076 0, 1, -1, 1077 0, int_connector_back_depth, int_payload_lower_extent); 1078 1079 cube_at(back_left, back_depth, connector_height, 1080 -1, 1, -1, 1081 int_connector_left_extent, 0, int_payload_lower_extent); 1082 1083 cube_at(back_right, back_depth, connector_height, 1084 1, 1, -1, 1085 int_connector_right_extent, 0, int_payload_lower_extent); 1086 } 1087 1088 /* Top of back piece. */ 1089 1090 if (TOP_SURFACE) 1091 cube_at(payload_width, back_depth, top, 1092 0, 1, 1, 1093 payload_centre, 0, int_payload_upper_extent); 1094 1095 /* 1096 The extension of the back to connect with the front. Here, the inside 1097 edges are rounded. The outside edges are rounded later. 1098 1099 An extra overlapping measure is employed so that the filleting is 1100 continuous between the payload and connector portions. On the inside 1101 edges, the connector filleting is extended upwards. 1102 */ 1103 1104 /* Left side of payload. */ 1105 1106 difference() { 1107 cube_at(back_left - groove_depth, groove_width_extra, inner_payload_front_cutout_height, 1108 -1, 1, 1, 1109 int_payload_left_extent, -groove_width_extra, int_payload_lower_extent); 1110 if (ROUND_CONNECTING_EDGES) 1111 translate([int_payload_left_extent - groove_ro, -groove_width_extra + groove_ro, 1112 int_payload_lower_extent]) 1113 rotate([0, 0, -90]) 1114 fillet_justified(groove_rr, inner_payload_front_cutout_height); 1115 } 1116 1117 /* Right side of payload. */ 1118 1119 difference() { 1120 cube_at(back_right - groove_depth, groove_width_extra, inner_payload_front_cutout_height, 1121 1, 1, 1, 1122 int_payload_right_extent, -groove_width_extra, int_payload_lower_extent); 1123 if (ROUND_CONNECTING_EDGES) 1124 translate([int_payload_right_extent + groove_ro, -groove_width_extra + groove_ro, 1125 int_payload_lower_extent]) 1126 rotate([0, 0, 180]) 1127 fillet_justified(groove_rr, inner_payload_front_cutout_height); 1128 } 1129 1130 if (BACK_CONNECTOR_SECTION) { 1131 1132 /* Left side of connector (or floor extension). */ 1133 1134 difference() { 1135 cube_at(back_left - groove_depth, groove_width_extra, 1136 inner_connector_front_cutout_height, 1137 -1, 1, -1, 1138 int_connector_left_extent, -groove_width_extra, int_payload_lower_extent); 1139 if (ROUND_CONNECTING_EDGES) 1140 translate([int_connector_left_extent - groove_ro, 1141 -groove_width_extra + groove_ro, 1142 lower_extent]) 1143 rotate([0, 0, -90]) 1144 fillet_partitioned(groove_rr, 1145 inner_connector_front_cutout_height + extra); 1146 } 1147 1148 /* Right side of connector (or floor extension). */ 1149 1150 difference() { 1151 cube_at(back_right - groove_depth, groove_width_extra, 1152 inner_connector_front_cutout_height, 1153 1, 1, -1, 1154 int_connector_right_extent, -groove_width_extra, int_payload_lower_extent); 1155 if (ROUND_CONNECTING_EDGES) 1156 translate([int_connector_right_extent + groove_ro, 1157 -groove_width_extra + groove_ro, 1158 lower_extent]) 1159 rotate([0, 0, 180]) 1160 fillet_partitioned(groove_rr, 1161 inner_connector_front_cutout_height + extra); 1162 } 1163 } 1164 1165 /* Top side. */ 1166 1167 difference() { 1168 cube_at(payload_width - groove_depth * 2, groove_width_extra, top - top_groove_depth, 1169 0, 1, 1, 1170 payload_centre, -groove_width_extra, int_payload_upper_extent); 1171 if (ROUND_CONNECTING_EDGES) 1172 translate([payload_centre, -groove_width_extra + groove_ro, 1173 int_payload_upper_extent + groove_ro]) 1174 rotate([0, 0, 180]) 1175 rotate([0, 90, 0]) 1176 fillet(groove_rr, payload_width - groove_depth * 2); 1177 } 1178 } 1179 1180 difference() { 1181 1182 /* Floor of cartridge. */ 1183 1184 if (payload_width > connector_width) { 1185 1186 difference() { 1187 cube_at(payload_width, back_depth, bottom, 1188 1, 1, 1, 1189 payload_left_extent, 0, lower_extent + int_connector_height); 1190 1191 /* Cut out the floor to provide a tab. */ 1192 1193 cube_at(payload_width - connector_width + groove_depth, 1194 edge_connector_cutout_front_depth, bottom, 1195 1, 1, 1, 1196 payload_left_extent, 0, lower_extent + int_connector_height); 1197 } 1198 1199 } else { 1200 cube_at(connector_width, back_depth, bottom, 1201 0, 1, 1, 1202 0, 0, lower_extent + int_connector_height); 1203 } 1204 1205 /* Edge connector cutout. */ 1206 1207 cube_at(edge_connector_cutout_back_width, 1208 edge_connector_cutout_back_depth, 1209 bottom, 1210 0, 1, 1, 1211 0, 0, lower_extent + int_connector_height); 1212 } 1213 1214 /* PCB supports. */ 1215 1216 if (MODEL == ROM_CARTRIDGE) { 1217 pcb_support(-1, pcb_back_support_left_bump_height, 1218 pcb_back_support_left_bump_offset_from_bottom, 1219 pcb_back_support_break_from_bottom, 1220 pcb_back_support_break_height); 1221 pcb_support(1, pcb_back_support_right_bump_height, 1222 pcb_back_support_right_bump_offset_from_bottom, 1223 pcb_back_support_break_from_bottom, 1224 pcb_back_support_break_height); 1225 1226 /* Circular "lugs" to hold PCBs in place. */ 1227 1228 pcb_lug(-1); 1229 pcb_lug(1); 1230 } 1231 1232 if (MODEL == WIDE_CARTRIDGE) { 1233 1234 pcb_lug_wide(1); 1235 pcb_lug_wide(-1); 1236 } 1237 } 1238 1239 /* Label insets. */ 1240 1241 union() { 1242 1243 /* Top label. See also the front piece. */ 1244 1245 if (TOP_LABEL_INSET) 1246 translate([top_label_left_extent, 1247 -front_depth + top_label_offset_from_front, 1248 upper_extent - top_label_depth]) 1249 cube([top_label_width, top_label_height, 1250 top_label_depth]); 1251 } 1252 1253 /* Top and side grooves, positioned in the back portion. */ 1254 1255 union() { 1256 1257 /* 1258 Grooves are separated to permit a discontinuity with the 1259 wide cartridge and for control over the back section. 1260 */ 1261 1262 cube_at(groove_depth, groove_width_normal, payload_height + bottom, 1263 1, 1, -1, 1264 payload_left_extent, 0, upper_extent); 1265 1266 if (BACK_CONNECTOR_SECTION) 1267 cube_at(groove_depth, groove_width_normal, connector_height, 1268 1, 1, 1, 1269 connector_left_extent, 0, lower_extent); 1270 1271 /* Right groove. */ 1272 1273 cube_at(groove_depth, groove_width_normal, height, 1274 -1, 1, 0, 1275 payload_right_extent, 0, 0); 1276 1277 if (BACK_CONNECTOR_SECTION) 1278 cube_at(groove_depth, groove_width_normal, connector_height, 1279 -1, 1, 1, 1280 connector_right_extent, 0, lower_extent); 1281 1282 /* Top grooves. */ 1283 1284 cube_at(payload_width, groove_width_normal, groove_depth, 1285 0, 1, -1, 1286 payload_centre, 0, upper_extent); 1287 1288 cube_at(payload_width, top_groove_width, top_groove_depth, 1289 0, 1, -1, 1290 payload_centre, -groove_width_extra, upper_extent); 1291 } 1292 1293 /* Back socket holes. */ 1294 1295 if (BACK_LUG == BACK_SOCKET) { 1296 1297 if (MODEL == ROM_CARTRIDGE) { 1298 pcb_back_socket_hole(-1); 1299 pcb_back_socket_hole(1); 1300 } 1301 1302 if (MODEL == WIDE_CARTRIDGE) { 1303 pcb_back_socket_hole_wide(-1); 1304 pcb_back_socket_hole_wide(1); 1305 } 1306 } 1307 1308 /* The connector section at the base of the back. */ 1309 1310 if (BACK_CONNECTOR_SECTION) { 1311 1312 /* Provide an external cavity. */ 1313 1314 if (EXTERNAL_BACK_CAVITY) 1315 intersection() { 1316 1317 /* From the bottom upwards. */ 1318 1319 translate([0, back_depth, lower_extent]) 1320 linear_extrude(height = back_cavity_height) 1321 translate([int_connector_left_extent, 0, 0]) 1322 polygon([ 1323 [back_cavity_offset_from_inner_left, 0], 1324 [back_cavity_inner_offset_from_inner_left, 1325 -back_cavity_depth], 1326 [back_cavity_inner_offset_from_inner_left + 1327 back_cavity_inner_width, 1328 -back_cavity_depth], 1329 [back_cavity_offset_from_inner_left + 1330 back_cavity_width, 0] 1331 ]); 1332 1333 /* From left to right. */ 1334 1335 translate([back_cavity_width / 2, back_depth, lower_extent]) 1336 rotate([0, -90, 0]) 1337 linear_extrude(height = back_cavity_width) 1338 polygon([ 1339 [-extra, -back_cavity_depth], 1340 [back_cavity_inner_height, 1341 -back_cavity_depth], 1342 [back_cavity_height, 0], 1343 [-extra, 0] 1344 ]); 1345 } 1346 1347 /* Or remove the back section entirely. */ 1348 1349 else if (EXTERNAL_BACK_CUTOUT) 1350 translate([0, back_depth, lower_extent]) 1351 linear_extrude(height = back_cavity_height) 1352 translate([int_connector_left_extent, 0, 0]) 1353 polygon([ 1354 [back_cavity_offset_from_inner_left, 0], 1355 [inner_back_slope_min_offset, 1356 -connector_back + inner_back_slope_depth], 1357 [inner_back_slope_min_offset, -connector_back], 1358 [int_connector_width - inner_back_slope_min_offset, 1359 -connector_back], 1360 [int_connector_width - inner_back_slope_min_offset, 1361 -connector_back + inner_back_slope_depth], 1362 [back_cavity_offset_from_inner_left + 1363 back_cavity_width, 0] 1364 ]); 1365 1366 /* Inner back cavities making the back thinner at the sides. */ 1367 1368 translate([0, int_connector_back_depth, lower_extent]) 1369 linear_extrude(height = int_connector_height) 1370 translate([int_connector_left_extent, 0, 0]) 1371 polygon([ 1372 [0, 0], 1373 [inner_back_slope_max_offset, 0], 1374 [inner_back_slope_min_offset, 1375 inner_back_slope_depth], 1376 [0, inner_back_slope_depth] 1377 ]); 1378 1379 translate([0, int_connector_back_depth, lower_extent]) 1380 linear_extrude(height = int_connector_height) 1381 translate([int_connector_right_extent, 0, 0]) 1382 polygon([ 1383 [0, 0], 1384 [-inner_back_slope_max_offset, 0], 1385 [-inner_back_slope_min_offset, 1386 inner_back_slope_depth], 1387 [0, inner_back_slope_depth] 1388 ]); 1389 1390 /* Inner back edge cavity, tapering the bottom edge. */ 1391 1392 translate([inner_back_edge_width / 2, 1393 int_connector_back_depth + inner_back_edge_depth, lower_extent]) 1394 rotate([0, -90, 0]) 1395 linear_extrude(height = inner_back_edge_width) 1396 polygon([ 1397 [-extra, -inner_back_edge_depth], 1398 [inner_back_edge_height, -inner_back_edge_depth], 1399 [0, 0], 1400 [-extra, 0] 1401 ]); 1402 } 1403 1404 /* Fillets to round off the edges. */ 1405 1406 union() { 1407 if (ROUND_EDGES) { 1408 1409 /* Top left and right rounding. */ 1410 1411 translate([payload_left_extent + ro, back_depth / 2, upper_extent - ro]) 1412 rotate([0, 0, 180]) 1413 rotate([90, 0, 0]) 1414 fillet(rr, back_depth); 1415 translate([payload_right_extent - ro, back_depth / 2, upper_extent - ro]) 1416 rotate([90, 0, 0]) 1417 fillet(rr, back_depth); 1418 1419 /* Top back rounding. */ 1420 1421 translate([payload_centre, back_depth - ro, upper_extent - ro]) 1422 rotate([0, -90, 0]) 1423 fillet(rr, payload_width); 1424 1425 /* Outer edge rounding. */ 1426 1427 translate([payload_right_extent - ro, back_depth - ro, int_payload_lower_extent - bottom - extra]) 1428 fillet_justified(rr, payload_height + bottom + extra); 1429 1430 translate([payload_left_extent + ro, back_depth - ro, int_payload_lower_extent - bottom - extra]) 1431 rotate([0, 0, 90]) 1432 fillet_justified(rr, payload_height + bottom + extra); 1433 1434 if (BACK_CONNECTOR_SECTION) { 1435 translate([connector_right_extent - ro, back_depth - ro, lower_extent]) 1436 fillet_partitioned(rr, connector_height - bottom); 1437 1438 translate([connector_left_extent + ro, back_depth - ro, lower_extent]) 1439 rotate([0, 0, 90]) 1440 fillet_partitioned(rr, connector_height - bottom); 1441 } 1442 } 1443 1444 if (ROUND_CONNECTING_EDGES) { 1445 1446 /* 1447 Outer edge rounding of the back extension. This is done as a 1448 separate removal operation rather than occurring when creating the 1449 extension in order to ensure that the edges are actually rounded. 1450 1451 An extra overlapping measure is employed so that the filleting is 1452 continuous between the payload and connector portions. On the outside 1453 edges, the payload filleting is extended downwards. 1454 */ 1455 1456 translate([payload_left_extent + groove_depth + groove_ro, -groove_width_extra + groove_ro, 1457 int_payload_lower_extent - extra]) 1458 rotate([0, 0, 180]) 1459 fillet_justified(groove_rr, inner_payload_front_cutout_height + extra); 1460 1461 translate([payload_right_extent - groove_depth - groove_ro, -groove_width_extra + groove_ro, 1462 int_payload_lower_extent - extra]) 1463 rotate([0, 0, -90]) 1464 fillet_justified(groove_rr, inner_payload_front_cutout_height + extra); 1465 1466 /* Sides of connector. */ 1467 1468 translate([connector_left_extent + groove_depth + groove_ro, -groove_width_extra + groove_ro, 1469 lower_extent]) 1470 rotate([0, 0, 180]) 1471 fillet_partitioned(groove_rr, inner_connector_front_cutout_height); 1472 1473 translate([connector_right_extent - groove_depth - groove_ro, -groove_width_extra + groove_ro, 1474 lower_extent]) 1475 rotate([0, 0, -90]) 1476 fillet_partitioned(groove_rr, inner_connector_front_cutout_height); 1477 1478 /* Top of payload. */ 1479 1480 translate([payload_centre, -groove_width_extra + groove_ro, 1481 int_payload_upper_extent + inner_top_front_cutout_height - groove_ro]) 1482 rotate([0, 0, 180]) 1483 rotate([0, -90, 0]) 1484 fillet(groove_rr, payload_width - groove_depth * 2); 1485 } 1486 } 1487 } 1488 1489 /* PCBs for checking. */ 1490 1491 if (PCB) if (MODEL == ROM_CARTRIDGE) { 1492 translate([0, 0, int_payload_lower_extent]) 1493 difference() { 1494 1495 /* Mock PCB. */ 1496 1497 union() { 1498 cube_at(pcb_width, pcb_depth, 1499 pcb_height - edge_connector_height, 1500 0, -1, 1, 1501 0, edge_connector_cutout_back_depth, 0); 1502 cube_at(edge_connector_width, pcb_depth, 1503 edge_connector_height, 1504 0, -1, -1, 1505 0, edge_connector_cutout_back_depth, 0); 1506 1507 /* Feature. */ 1508 1509 cube_at(feature_width, feature_depth, feature_height, 1510 0, 1, 1, 1511 0, edge_connector_cutout_back_depth, feature_height_offset); 1512 } 1513 1514 /* Holes for mounting. */ 1515 1516 union() { 1517 cube_at(pcb_hole_width, pcb_hole_depth, pcb_left_hole_height, 1518 -1, -1, 1, 1519 pcb_left_hole_offset_from_centre, pcb_hole_start_depth, 1520 pcb_left_hole_offset); 1521 translate([pcb_left_hole_offset_from_centre - 1522 pcb_hole_width / 2, pcb_hole_start_depth, pcb_left_hole_offset]) 1523 rotate([90, 0, 0]) 1524 cylinder(h=pcb_hole_depth, r=pcb_hole_width / 2); 1525 translate([pcb_left_hole_offset_from_centre - 1526 pcb_hole_width / 2, pcb_hole_start_depth, 1527 pcb_left_hole_offset + pcb_left_hole_height]) 1528 rotate([90, 0, 0]) 1529 cylinder(h=pcb_hole_depth, r=pcb_hole_width / 2); 1530 } 1531 1532 union() { 1533 cube_at(pcb_hole_width, pcb_hole_depth, pcb_right_hole_height, 1534 1, -1, 1, 1535 pcb_right_hole_offset_from_centre, pcb_hole_start_depth, 1536 pcb_right_hole_offset); 1537 translate([pcb_right_hole_offset_from_centre + 1538 pcb_hole_width / 2, pcb_hole_start_depth, pcb_right_hole_offset]) 1539 rotate([90, 0, 0]) 1540 cylinder(h=pcb_hole_depth, r=pcb_hole_width / 2); 1541 translate([pcb_right_hole_offset_from_centre + 1542 pcb_hole_width / 2, pcb_hole_start_depth, 1543 pcb_right_hole_offset + pcb_right_hole_height]) 1544 rotate([90, 0, 0]) 1545 cylinder(h=pcb_hole_depth, r=pcb_hole_width / 2); 1546 } 1547 1548 /* Holes for lugs. */ 1549 1550 translate([ 1551 int_connector_left_extent + pcb_lug_offset_from_inside, 1552 pcb_hole_start_depth, pcb_lug_offset_from_bottom]) 1553 rotate([90, 0, 0]) 1554 cylinder(h=pcb_hole_depth, r=pcb_lug_hole_radius); 1555 1556 translate([ 1557 int_connector_right_extent - pcb_lug_offset_from_inside, 1558 pcb_hole_start_depth, pcb_lug_offset_from_bottom]) 1559 rotate([90, 0, 0]) 1560 cylinder(h=pcb_hole_depth, r=pcb_lug_hole_radius); 1561 } 1562 } 1563 1564 if (PCB) if (MODEL == WIDE_CARTRIDGE) { 1565 translate([0, 0, int_payload_lower_extent]) 1566 difference() { 1567 1568 /* Mock PCB. */ 1569 1570 union() { 1571 cube_at(wide_pcb_width, wide_pcb_depth, 1572 wide_pcb_height - edge_connector_height, 1573 0, -1, 1, 1574 payload_centre, edge_connector_cutout_back_depth, 0); 1575 cube_at(edge_connector_width, wide_pcb_depth, 1576 edge_connector_height, 1577 0, -1, -1, 1578 0, edge_connector_cutout_back_depth, 0); 1579 1580 /* Feature. */ 1581 1582 cube_at(feature_width, feature_depth, feature_height, 1583 0, 1, 1, 1584 0, edge_connector_cutout_back_depth, feature_height_offset); 1585 } 1586 1587 /* Holes for lugs. */ 1588 1589 translate([ 1590 payload_centre + int_payload_width / 2 - wide_pcb_lug_offset_from_inside , 1591 wide_pcb_hole_start_depth, 1592 wide_pcb_height - edge_connector_height - wide_pcb_lug_offset_from_bottom]) 1593 rotate([90, 0, 0]) 1594 cylinder(h=wide_pcb_hole_depth, r=wide_pcb_lug_hole_radius); 1595 1596 translate([ 1597 payload_centre - int_payload_width / 2 + wide_pcb_lug_offset_from_inside , 1598 wide_pcb_hole_start_depth, 1599 wide_pcb_height - edge_connector_height - wide_pcb_lug_offset_from_bottom]) 1600 rotate([90, 0, 0]) 1601 cylinder(h=wide_pcb_hole_depth, r=wide_pcb_lug_hole_radius); 1602 1603 translate([ 1604 payload_centre + int_payload_width / 2 - wide_pcb_lug_offset_from_inside , 1605 wide_pcb_hole_start_depth, 1606 wide_pcb_lug_offset_from_bottom]) 1607 rotate([90, 0, 0]) 1608 cylinder(h=wide_pcb_hole_depth, r=wide_pcb_lug_hole_radius); 1609 1610 translate([ 1611 payload_centre - int_payload_width / 2 + wide_pcb_lug_offset_from_inside , 1612 wide_pcb_hole_start_depth, 1613 wide_pcb_lug_offset_from_bottom]) 1614 rotate([90, 0, 0]) 1615 cylinder(h=wide_pcb_hole_depth, r=wide_pcb_lug_hole_radius); 1616 } 1617 } 1618 } 1619 } 1620 1621 /* 1622 Place the back piece next to the front, with the internals facing out 1623 the same way, if APART is defined. 1624 */ 1625 1626 front_displacement_together = CLOSED ? front_back_overlap : APART ? 0 : front_back_overlap - SEPARATION; 1627 front_displacement = APART ? -connector_width * 0.6 : 0; 1628 back_displacement = APART ? connector_width * 0.6 : 0; 1629 back_rotation = APART ? 180 : 0; 1630 1631 if (INTERSECT) { 1632 intersection() { 1633 front_piece(0, front_displacement_together); 1634 back_piece(0, 0); 1635 } 1636 } else { 1637 front_piece(front_displacement, front_displacement_together); 1638 back_piece(back_displacement, back_rotation); 1639 } 1640 } 1641 1642 cartridge();