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