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 = 50.8; /* 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 = 1.2; 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 425 /* Configured by SHORT_PCB. */ 426 pcb_back_support_top_bump_height = 3.8; 427 428 /* Move the PCB support towards the centre. */ 429 pcb_support_margin = 1.75; 430 pcb_support_offset_from_centre = edge_connector_cutout_back_width / 2 431 - pcb_support_margin; 432 433 /* 434 The PCB lugs protrude through the holes in the PCB. By extending both 435 the front and back lugs by the depth of the back support bump, they 436 overlap by that amount. The "mating" depth is defined as that amount 437 plus an additional amount for adhesion. 438 */ 439 440 pcb_lug_mating_depth = pcb_back_support_bump_depth + 1.0; 441 442 pcb_back_lug_depth = pcb_back_support_depth + 443 pcb_lug_mating_depth; 444 pcb_back_lug_inner_radius = 1.0; 445 pcb_back_lug_outer_radius = 5.5 / 2; 446 447 /* 448 The cross detail on the back lug is intended to resist the front lug, 449 and thus starts at the point that the front lug ends. 450 */ 451 452 pcb_lug_cross_width = 6.7; 453 pcb_lug_cross_depth = pcb_back_support_depth + pcb_back_support_bump_depth - 454 pcb_lug_mating_depth; 455 pcb_lug_cross_height = 1.4; 456 457 /* 458 The front depth is calculated similarly to the back depth, with the 459 outer radius of the socket corresponding to the dimensions of the cross 460 detail. The inner radius of the front's socket is the same as the outer 461 radius of the back's plug, with friction holding the two together. 462 */ 463 464 pcb_front_lug_depth = pcb_front_support_depth + 465 pcb_lug_mating_depth; 466 pcb_front_lug_inner_radius = pcb_back_lug_outer_radius; 467 pcb_front_lug_outer_radius = pow( 468 pow(pcb_lug_cross_width / 2, 2) + 469 pow(pcb_lug_cross_height / 2, 2), 470 0.5); 471 472 /* 473 With BACK_LUG as BACK_SOCKET, no overlap is desired, since both 474 front and back provide sockets. The depth is accounted for as follows: 475 476 int_payload_back_depth + payload_back == back_depth 477 + int_front_depth - pcb_front_lug_depth == back lug space in the front 478 - front_back_overlap == correction for overlap 479 480 int_front_depth - pcb_front_lug_depth 481 == pcb_front_support_depth - pcb_front_support_depth - pcb_lug_mating_depth 482 == -pcb_lug_mating_depth 483 484 Note that the lug starts at the outer back surface, which is why the 485 payload_back quantity is included. 486 */ 487 488 pcb_back_socket_lug_depth = back_depth - front_back_overlap - pcb_lug_mating_depth; 489 490 /* Both front and back lugs need to be positioned in the same place. */ 491 492 pcb_lug_offset_from_bottom = 14.35; 493 pcb_lug_offset_from_inside = 5.55; 494 495 wide_pcb_lug_offset_from_inside = 6.0; 496 wide_pcb_lug_offset_from_bottom = 6.0; 497 498 /* PCBs for checking. */ 499 500 pcb_width = 84.85; pcb_height = 62.5; pcb_depth = 1; 501 edge_connector_width = 56.5; edge_connector_height = 11.85; 502 503 /* 504 The rectangular part of the narrow left and right holes is smaller 505 than the "bump" in the case, but the circular parts make the overall 506 hole larger than the "bump". An extra depth is used for holes to avoid 507 surface definition problems. 508 */ 509 510 pcb_hole_margin = 0.55; 511 pcb_hole_width = 2.2; 512 pcb_hole_extra_depth = 0.1; 513 pcb_lug_hole_radius = 3.75; 514 515 pcb_left_hole_offset = pcb_back_support_left_bump_offset_from_bottom + pcb_hole_margin; 516 pcb_left_hole_height = pcb_back_support_left_bump_height - pcb_hole_margin * 2; 517 pcb_left_hole_offset_from_centre = -(pcb_support_offset_from_centre + 518 pcb_back_support_width / 2 - pcb_hole_width / 2); 519 520 pcb_right_hole_offset = pcb_back_support_right_bump_offset_from_bottom + pcb_hole_margin; 521 pcb_right_hole_height = pcb_back_support_right_bump_height - pcb_hole_margin * 2; 522 pcb_right_hole_offset_from_centre = -pcb_left_hole_offset_from_centre; 523 524 pcb_hole_depth = pcb_depth + 2 * pcb_hole_extra_depth; 525 pcb_hole_start_depth = edge_connector_cutout_back_depth + pcb_hole_extra_depth; 526 527 wide_pcb_width = 138.0; wide_pcb_height = pcb_height; wide_pcb_depth = pcb_depth; 528 wide_pcb_hole_depth = pcb_hole_depth; 529 wide_pcb_hole_start_depth = pcb_hole_start_depth; 530 531 wide_pcb_lug_hole_radius = 4.0; 532 533 /* An example feature for use with PCB testing. */ 534 535 feature_width = 15.0; feature_depth = 7.0; feature_height = 40.0; 536 feature_height_offset = 10.0; 537 538 /* Repeated constructs. */ 539 540 module pcb_support(xdir, bump_height, bump_offset) { 541 542 /* 543 Translate the support in the stated direction. 544 Since the support is already justified in the direction, the translation 545 moves the inner edge of the support to alignment with the end of the 546 edge connector cutout and then back by the PCB support margin. 547 */ 548 549 translate([xdir * 550 pcb_support_offset_from_centre, 551 edge_connector_cutout_back_depth, 552 int_payload_lower_extent]) 553 justify(pcb_back_support_width, 554 pcb_back_support_depth, 555 pcb_back_support_height, 556 xdir, 1, 1) 557 union() { 558 559 /* Underlying support strut. */ 560 561 cube([pcb_back_support_width, 562 pcb_back_support_depth, 563 pcb_back_support_height], center = true); 564 565 /* Middle bump. */ 566 567 cube_at(pcb_back_support_bump_width, 568 pcb_back_support_bump_depth, 569 bump_height, 570 0, -1, 1, 571 0, 572 -pcb_back_support_depth / 2, 573 -pcb_back_support_height / 2 + bump_offset); 574 575 /* Top bump. */ 576 577 if (SHORT_PCB) 578 cube_at(pcb_back_support_bump_width, 579 pcb_back_support_bump_depth, 580 pcb_back_support_top_bump_height, 581 0, -1, 1, 582 0, 583 -pcb_back_support_depth / 2, 584 pcb_back_support_height / 2 - 585 pcb_back_support_top_bump_height); 586 } 587 } 588 589 /* Choose the kind of "lug" in the back piece. */ 590 591 module pcb_lug(xdir) { 592 if (BACK_LUG == BACK_PLUG) { 593 pcb_back_plug_lug(xdir); 594 } else if (BACK_LUG == BACK_SOCKET) { 595 pcb_back_socket_lug(xdir); 596 } 597 } 598 599 module pcb_lug_wide(xdir) { 600 if (BACK_LUG == BACK_PLUG) { 601 pcb_back_plug_lug_wide(xdir); 602 } else if (BACK_LUG == BACK_SOCKET) { 603 pcb_back_socket_lug_wide(xdir); 604 } 605 } 606 607 /* The conventional back "lug" which is actually a plug. */ 608 609 module pcb_back_plug_lug(xdir) { 610 pcb_back_plug_lug_explicit(xdir, int_connector_width / 2 - pcb_lug_offset_from_inside, 611 pcb_lug_offset_from_bottom); 612 } 613 614 module pcb_back_plug_lug_wide(xdir) { 615 pcb_back_plug_lug_explicit(xdir, int_payload_width / 2 - wide_pcb_lug_offset_from_inside, 616 wide_pcb_lug_offset_from_bottom); 617 pcb_back_plug_lug_explicit(xdir, int_payload_width / 2 - wide_pcb_lug_offset_from_inside, 618 wide_pcb_height - edge_connector_height - wide_pcb_lug_offset_from_bottom); 619 } 620 621 module pcb_back_plug_lug_explicit(xdir, offset_from_centre, offset_from_bottom) { 622 translate([payload_centre + xdir * offset_from_centre, 623 back_depth, 624 int_payload_lower_extent + offset_from_bottom 625 ]) 626 rotate([90, 0, 0]) 627 difference() { 628 629 /* Cylinder with cross. */ 630 631 union() { 632 cylinder(h=pcb_back_lug_depth, r=pcb_back_lug_outer_radius); 633 cube_at(pcb_lug_cross_width, 634 pcb_lug_cross_height, pcb_lug_cross_depth, 635 0, 0, 1, 636 0, 0, 0); 637 cube_at(pcb_lug_cross_height, 638 pcb_lug_cross_width, pcb_lug_cross_depth, 639 0, 0, 1, 640 0, 0, 0); 641 } 642 643 /* Hollowed out by a cylinder. */ 644 645 cylinder(h=pcb_back_lug_depth, r=pcb_back_lug_inner_radius); 646 647 /* Tapered off for easier connection. */ 648 649 if (ROUND_CONNECTING_EDGES) 650 translate([0, 0, pcb_back_lug_depth - pcb_lug_ro]) 651 fillet_torus(pcb_back_lug_outer_radius, pcb_lug_rr); 652 } 653 } 654 655 /* The conventional front "lug" which is actually a socket. */ 656 657 module pcb_front_lug(xdir) { 658 pcb_socket_lug_explicit(xdir, int_connector_width / 2 - pcb_lug_offset_from_inside, 659 -int_front_depth + pcb_front_lug_depth, 660 pcb_lug_offset_from_bottom, 661 pcb_front_lug_depth, pcb_front_lug_outer_radius, pcb_front_lug_inner_radius); 662 } 663 664 module pcb_front_lug_wide(xdir) { 665 pcb_socket_lug_explicit(xdir, int_payload_width / 2 - wide_pcb_lug_offset_from_inside, 666 -int_front_depth + pcb_front_lug_depth, 667 wide_pcb_lug_offset_from_bottom, 668 pcb_front_lug_depth, pcb_front_lug_outer_radius, pcb_front_lug_inner_radius); 669 pcb_socket_lug_explicit(xdir, int_payload_width / 2 - wide_pcb_lug_offset_from_inside, 670 -int_front_depth + pcb_front_lug_depth, 671 wide_pcb_height - edge_connector_height - wide_pcb_lug_offset_from_bottom, 672 pcb_front_lug_depth, pcb_front_lug_outer_radius, pcb_front_lug_inner_radius); 673 } 674 675 /* 676 The alternative back "lug" which is actually a socket as well, meaning 677 that a cylinder runs from the front surface to the back, with the two 678 sections meeting in the middle. 679 */ 680 681 module pcb_back_socket_lug(xdir) { 682 pcb_socket_lug_explicit(xdir, int_connector_width / 2 - pcb_lug_offset_from_inside, 683 back_depth, 684 pcb_lug_offset_from_bottom, 685 pcb_back_socket_lug_depth, pcb_front_lug_outer_radius, pcb_front_lug_inner_radius); 686 } 687 688 module pcb_back_socket_lug_wide(xdir) { 689 pcb_socket_lug_explicit(xdir, int_payload_width / 2 - wide_pcb_lug_offset_from_inside, 690 back_depth, 691 wide_pcb_lug_offset_from_bottom, 692 pcb_back_socket_lug_depth, pcb_front_lug_outer_radius, pcb_front_lug_inner_radius); 693 pcb_socket_lug_explicit(xdir, int_payload_width / 2 - wide_pcb_lug_offset_from_inside, 694 back_depth, 695 wide_pcb_height - edge_connector_height - wide_pcb_lug_offset_from_bottom, 696 pcb_back_socket_lug_depth, pcb_front_lug_outer_radius, pcb_front_lug_inner_radius); 697 } 698 699 /* The common implementation of the cylinder socket. */ 700 701 module pcb_socket_lug_explicit(xdir, offset_from_centre, depth_offset, offset_from_bottom, 702 lug_depth, lug_outer_radius, lug_inner_radius) { 703 704 translate([payload_centre + xdir * offset_from_centre, 705 depth_offset, 706 int_payload_lower_extent + offset_from_bottom 707 ]) 708 rotate([90, 0, 0]) 709 difference() { 710 cylinder(h=lug_depth, 711 r=lug_outer_radius); 712 cylinder(h=lug_depth, 713 r=lug_inner_radius); 714 } 715 } 716 717 /* The holes in the front surface when socket lugs are used. */ 718 719 module pcb_front_socket_hole(xdir) { 720 pcb_socket_hole_explicit(xdir, int_connector_width / 2 - pcb_lug_offset_from_inside, 721 -int_front_depth + extra, 722 pcb_lug_offset_from_bottom, 723 front + extra * 2, pcb_front_lug_inner_radius); 724 } 725 726 module pcb_front_socket_hole_wide(xdir) { 727 pcb_socket_hole_explicit(xdir, int_payload_width / 2 - wide_pcb_lug_offset_from_inside, 728 -int_front_depth + extra, 729 wide_pcb_lug_offset_from_bottom, 730 front + extra * 2, pcb_front_lug_inner_radius); 731 pcb_socket_hole_explicit(xdir, int_payload_width / 2 - wide_pcb_lug_offset_from_inside, 732 -int_front_depth + extra, 733 wide_pcb_height - edge_connector_height - wide_pcb_lug_offset_from_bottom, 734 front + extra * 2, pcb_front_lug_inner_radius); 735 } 736 737 /* The holes in the back surface when socket lugs are used. */ 738 739 module pcb_back_socket_hole(xdir) { 740 pcb_socket_hole_explicit(xdir, int_connector_width / 2 - pcb_lug_offset_from_inside, 741 back_depth + extra, 742 pcb_lug_offset_from_bottom, 743 payload_back + extra * 2, pcb_front_lug_inner_radius); 744 } 745 746 module pcb_back_socket_hole_wide(xdir) { 747 pcb_socket_hole_explicit(xdir, int_payload_width / 2 - wide_pcb_lug_offset_from_inside, 748 back_depth + extra, 749 wide_pcb_lug_offset_from_bottom, 750 payload_back + extra * 2, pcb_front_lug_inner_radius); 751 pcb_socket_hole_explicit(xdir, int_payload_width / 2 - wide_pcb_lug_offset_from_inside, 752 back_depth + extra, 753 wide_pcb_height - edge_connector_height - wide_pcb_lug_offset_from_bottom, 754 payload_back + extra * 2, pcb_front_lug_inner_radius); 755 } 756 757 /* 758 The common implementation of the circular hole used when both pieces 759 employ the socket lug. 760 */ 761 762 module pcb_socket_hole_explicit(xdir, offset_from_centre, depth_offset, offset_from_bottom, 763 hole_depth, hole_radius) { 764 765 translate([payload_centre + xdir * offset_from_centre, 766 depth_offset, 767 int_payload_lower_extent + offset_from_bottom 768 ]) 769 rotate([90, 0, 0]) 770 cylinder(h=hole_depth, r=hole_radius); 771 } 772 773 /* The actual shapes. */ 774 775 module front_piece(front_displacement, front_displacement_together) { 776 if (FRONT) 777 translate([front_displacement, front_displacement_together, 0]) 778 difference() { 779 780 /* The cartridge surfaces. */ 781 782 union() { 783 784 /* Front portion. */ 785 786 if (FRONT_SURFACE) { 787 788 /* Surfaces surrounding the payload. */ 789 790 cube_at(payload_width, front, payload_height, 791 0, -1, 1, 792 payload_centre, -int_front_depth, int_payload_lower_extent); 793 794 cube_at(front_left, front_depth, payload_height, 795 -1, -1, 1, 796 int_payload_left_extent, 0, int_payload_lower_extent); 797 798 cube_at(front_right, front_depth, payload_height, 799 1, -1, 1, 800 int_payload_right_extent, 0, int_payload_lower_extent); 801 802 /* Surfaces surrounding the connector. */ 803 804 cube_at(connector_width, front, connector_height, 805 0, -1, -1, 806 0, -int_front_depth, int_payload_lower_extent); 807 808 cube_at(front_left, front_depth, connector_height, 809 -1, -1, -1, 810 int_connector_left_extent, 0, int_payload_lower_extent); 811 812 cube_at(front_right, front_depth, connector_height, 813 1, -1, -1, 814 int_connector_right_extent, 0, int_payload_lower_extent); 815 816 /* Top surface for the front piece. */ 817 818 if (TOP_SURFACE) 819 cube_at(payload_width, front_depth, top, 820 0, -1, 1, 821 payload_centre, 0, int_payload_upper_extent); 822 } 823 824 difference() { 825 826 /* Floor of cartridge. */ 827 828 if (payload_width > connector_width) { 829 cube_at(payload_width, front_depth, bottom, 830 1, -1, 1, 831 payload_left_extent, 0, lower_extent + int_connector_height); 832 } else { 833 cube_at(int_connector_width, int_front_depth, bottom, 834 0, -1, 1, 835 0, 0, lower_extent + int_connector_height); 836 } 837 838 /* Left cutout. */ 839 840 cube_at(edge_connector_cutout_front_width, 841 edge_connector_cutout_front_depth, 842 bottom, 843 1, -1, 1, 844 int_connector_left_extent, 845 0, 846 lower_extent + int_connector_height); 847 848 /* Right cutout. */ 849 850 cube_at(edge_connector_cutout_front_width, 851 edge_connector_cutout_front_depth, 852 bottom, 853 -1, -1, 1, 854 int_connector_right_extent, 855 0, 856 lower_extent + int_connector_height); 857 } 858 859 /* PCB supports. */ 860 861 if (MODEL == ROM_CARTRIDGE) { 862 cube_at(pcb_front_support_width, 863 pcb_front_support_depth, 864 pcb_front_support_height, 865 -1, -1, 1, 866 -pcb_support_offset_from_centre, 867 0, 868 int_payload_lower_extent); 869 870 cube_at(pcb_front_support_width, 871 pcb_front_support_depth, 872 pcb_front_support_height, 873 1, -1, 1, 874 pcb_support_offset_from_centre, 875 0, 876 int_payload_lower_extent); 877 878 /* Circular "lugs" to hold PCBs in place. */ 879 880 pcb_front_lug(-1); 881 pcb_front_lug(1); 882 } 883 884 if (MODEL == WIDE_CARTRIDGE) { 885 886 pcb_front_lug_wide(-1); 887 pcb_front_lug_wide(1); 888 } 889 } 890 891 /* Label insets. */ 892 893 union() { 894 895 /* Front label. */ 896 897 if (FRONT_LABEL_INSET) 898 translate([front_label_left_extent, -front_depth, 899 lower_extent + front_label_offset_from_bottom]) 900 cube([front_label_width, front_label_depth, 901 front_label_height]); 902 903 /* Top label. See also the back piece. */ 904 905 if (TOP_LABEL_INSET) 906 translate([top_label_left_extent, 907 -front_depth + top_label_offset_from_front, 908 upper_extent - top_label_depth]) 909 cube([top_label_width, top_label_height, 910 top_label_depth]); 911 } 912 913 /* Front socket holes depending on back lug choices. */ 914 915 if (BACK_LUG == BACK_SOCKET) { 916 917 if (MODEL == ROM_CARTRIDGE) { 918 pcb_front_socket_hole(-1); 919 pcb_front_socket_hole(1); 920 } 921 922 if (MODEL == WIDE_CARTRIDGE) { 923 pcb_front_socket_hole_wide(-1); 924 pcb_front_socket_hole_wide(1); 925 } 926 } 927 928 /* Inner front edge cavity. */ 929 930 translate([inner_front_edge_width / 2, -int_front_depth, lower_extent]) 931 rotate([0, -90, 0]) 932 linear_extrude(height = inner_front_edge_width) 933 polygon([ 934 [-extra, -inner_front_edge_depth], 935 [0, -inner_front_edge_depth], 936 [inner_front_edge_height, 0], 937 [-extra, 0], 938 ]); 939 940 /* Inner grooves for the top and sides of the back portion. */ 941 942 cube_at(inner_top_front_cutout_width, 943 inner_top_front_cutout_depth, 944 inner_top_front_cutout_height, 945 1, -1, 1, 946 int_payload_left_extent, 0, int_payload_upper_extent); 947 948 cube_at(inner_payload_front_cutout_width, 949 inner_payload_front_cutout_depth, 950 inner_payload_front_cutout_height, 951 1, -1, 1, 952 int_payload_right_extent, 0, int_payload_lower_extent); 953 954 cube_at(inner_payload_front_cutout_width, 955 inner_payload_front_cutout_depth, 956 inner_payload_front_cutout_height, 957 -1, -1, 1, 958 int_payload_left_extent, 0, int_payload_lower_extent); 959 960 /* Cutout to accept the back connector sides (or the floor of the back piece). */ 961 962 cube_at(inner_connector_front_cutout_width, 963 BACK_CONNECTOR_SECTION ? inner_connector_front_cutout_depth : edge_connector_cutout_front_depth, 964 BACK_CONNECTOR_SECTION ? inner_connector_front_cutout_height : bottom, 965 1, -1, -1, 966 int_connector_right_extent, 0, int_payload_lower_extent); 967 968 cube_at(inner_connector_front_cutout_width, 969 BACK_CONNECTOR_SECTION ? inner_connector_front_cutout_depth : edge_connector_cutout_front_depth, 970 BACK_CONNECTOR_SECTION ? inner_connector_front_cutout_height : bottom, 971 -1, -1, -1, 972 int_connector_left_extent, 0, int_payload_lower_extent); 973 974 /* Fillets to round off the edges. */ 975 976 union() { 977 if (ROUND_EDGES) { 978 979 /* Top left and right rounding. */ 980 981 translate([payload_left_extent + ro, -front_depth / 2, upper_extent - ro]) 982 rotate([0, 0, 180]) 983 rotate([90, 0, 0]) 984 fillet(rr, front_depth); 985 translate([payload_right_extent - ro, -front_depth / 2, upper_extent - ro]) 986 rotate([90, 0, 0]) 987 fillet(rr, front_depth); 988 989 /* Top front rounding. */ 990 991 translate([payload_centre, -front_depth + ro, upper_extent - ro]) 992 rotate([0, 0, 180]) 993 rotate([0, -90, 0]) 994 fillet(rr, payload_width); 995 996 /* Edge rounding. */ 997 998 translate([payload_right_extent - ro, -front_depth + ro, int_payload_lower_extent - bottom - extra]) 999 rotate([0, 0, 270]) 1000 fillet_justified(rr, payload_height + bottom + extra); 1001 1002 translate([payload_left_extent + ro, -front_depth + ro, int_payload_lower_extent - bottom - extra]) 1003 rotate([0, 0, 180]) 1004 fillet_justified(rr, payload_height + bottom + extra); 1005 1006 translate([connector_right_extent - ro, -front_depth + ro, lower_extent]) 1007 rotate([0, 0, 270]) 1008 fillet_partitioned(rr, connector_height - bottom); 1009 1010 translate([connector_left_extent + ro, -front_depth + ro, lower_extent]) 1011 rotate([0, 0, 180]) 1012 fillet_partitioned(rr, connector_height - bottom); 1013 } 1014 } 1015 } 1016 } 1017 1018 module back_piece(back_displacement, back_rotation) { 1019 if (BACK) 1020 translate([back_displacement, 0, 0]) 1021 rotate([0, 0, back_rotation]) { 1022 difference() { 1023 1024 /* The cartridge surfaces. */ 1025 1026 union() { 1027 1028 /* Back portion. */ 1029 1030 if (BACK_SURFACE) { 1031 1032 /* Surfaces surrounding the payload. */ 1033 1034 /* Payload section of back surface. */ 1035 1036 cube_at(payload_width, payload_back, payload_height, 1037 0, 1, 1, 1038 payload_centre, int_payload_back_depth, int_payload_lower_extent); 1039 1040 cube_at(back_left, back_depth, payload_height, 1041 -1, 1, 1, 1042 int_payload_left_extent, 0, int_payload_lower_extent); 1043 1044 cube_at(back_right, back_depth, payload_height, 1045 1, 1, 1, 1046 int_payload_right_extent, 0, int_payload_lower_extent); 1047 1048 /* Surfaces surrounding the connector. */ 1049 1050 /* Connector section of back surface overlapping the floor. */ 1051 1052 if (BACK_CONNECTOR_SECTION) { 1053 cube_at(connector_width, connector_back, connector_height, 1054 0, 1, -1, 1055 0, int_connector_back_depth, int_payload_lower_extent); 1056 1057 cube_at(back_left, back_depth, connector_height, 1058 -1, 1, -1, 1059 int_connector_left_extent, 0, int_payload_lower_extent); 1060 1061 cube_at(back_right, back_depth, connector_height, 1062 1, 1, -1, 1063 int_connector_right_extent, 0, int_payload_lower_extent); 1064 } 1065 1066 /* Top of back piece. */ 1067 1068 if (TOP_SURFACE) 1069 cube_at(payload_width, back_depth, top, 1070 0, 1, 1, 1071 payload_centre, 0, int_payload_upper_extent); 1072 1073 /* 1074 The extension of the back to connect with the front. Here, the inside 1075 edges are rounded. The outside edges are rounded later. 1076 1077 An extra overlapping measure is employed so that the filleting is 1078 continuous between the payload and connector portions. On the inside 1079 edges, the connector filleting is extended upwards. 1080 */ 1081 1082 /* Left side of payload. */ 1083 1084 difference() { 1085 cube_at(back_left - groove_depth, groove_width_extra, inner_payload_front_cutout_height, 1086 -1, 1, 1, 1087 int_payload_left_extent, -groove_width_extra, int_payload_lower_extent); 1088 if (ROUND_CONNECTING_EDGES) 1089 translate([int_payload_left_extent - groove_ro, -groove_width_extra + groove_ro, 1090 int_payload_lower_extent]) 1091 rotate([0, 0, -90]) 1092 fillet_justified(groove_rr, inner_payload_front_cutout_height); 1093 } 1094 1095 /* Right side of payload. */ 1096 1097 difference() { 1098 cube_at(back_right - groove_depth, groove_width_extra, inner_payload_front_cutout_height, 1099 1, 1, 1, 1100 int_payload_right_extent, -groove_width_extra, int_payload_lower_extent); 1101 if (ROUND_CONNECTING_EDGES) 1102 translate([int_payload_right_extent + groove_ro, -groove_width_extra + groove_ro, 1103 int_payload_lower_extent]) 1104 rotate([0, 0, 180]) 1105 fillet_justified(groove_rr, inner_payload_front_cutout_height); 1106 } 1107 1108 if (BACK_CONNECTOR_SECTION) { 1109 1110 /* Left side of connector (or floor extension). */ 1111 1112 difference() { 1113 cube_at(back_left - groove_depth, groove_width_extra, 1114 inner_connector_front_cutout_height, 1115 -1, 1, -1, 1116 int_connector_left_extent, -groove_width_extra, int_payload_lower_extent); 1117 if (ROUND_CONNECTING_EDGES) 1118 translate([int_connector_left_extent - groove_ro, 1119 -groove_width_extra + groove_ro, 1120 lower_extent]) 1121 rotate([0, 0, -90]) 1122 fillet_partitioned(groove_rr, 1123 inner_connector_front_cutout_height + extra); 1124 } 1125 1126 /* Right side of connector (or floor extension). */ 1127 1128 difference() { 1129 cube_at(back_right - groove_depth, groove_width_extra, 1130 inner_connector_front_cutout_height, 1131 1, 1, -1, 1132 int_connector_right_extent, -groove_width_extra, int_payload_lower_extent); 1133 if (ROUND_CONNECTING_EDGES) 1134 translate([int_connector_right_extent + groove_ro, 1135 -groove_width_extra + groove_ro, 1136 lower_extent]) 1137 rotate([0, 0, 180]) 1138 fillet_partitioned(groove_rr, 1139 inner_connector_front_cutout_height + extra); 1140 } 1141 } 1142 1143 /* Top side. */ 1144 1145 difference() { 1146 cube_at(payload_width - groove_depth * 2, groove_width_extra, top - top_groove_depth, 1147 0, 1, 1, 1148 payload_centre, -groove_width_extra, int_payload_upper_extent); 1149 if (ROUND_CONNECTING_EDGES) 1150 translate([payload_centre, -groove_width_extra + groove_ro, 1151 int_payload_upper_extent + groove_ro]) 1152 rotate([0, 0, 180]) 1153 rotate([0, 90, 0]) 1154 fillet(groove_rr, payload_width - groove_depth * 2); 1155 } 1156 } 1157 1158 difference() { 1159 1160 /* Floor of cartridge. */ 1161 1162 if (payload_width > connector_width) { 1163 1164 difference() { 1165 cube_at(payload_width, back_depth, bottom, 1166 1, 1, 1, 1167 payload_left_extent, 0, lower_extent + int_connector_height); 1168 1169 /* Cut out the floor to provide a tab. */ 1170 1171 cube_at(payload_width - connector_width + groove_depth, 1172 edge_connector_cutout_front_depth, bottom, 1173 1, 1, 1, 1174 payload_left_extent, 0, lower_extent + int_connector_height); 1175 } 1176 1177 } else { 1178 cube_at(connector_width, back_depth, bottom, 1179 0, 1, 1, 1180 0, 0, lower_extent + int_connector_height); 1181 } 1182 1183 /* Edge connector cutout. */ 1184 1185 cube_at(edge_connector_cutout_back_width, 1186 edge_connector_cutout_back_depth, 1187 bottom, 1188 0, 1, 1, 1189 0, 0, lower_extent + int_connector_height); 1190 } 1191 1192 /* PCB supports. */ 1193 1194 if (MODEL == ROM_CARTRIDGE) { 1195 pcb_support(-1, pcb_back_support_left_bump_height, 1196 pcb_back_support_left_bump_offset_from_bottom); 1197 pcb_support(1, pcb_back_support_right_bump_height, 1198 pcb_back_support_right_bump_offset_from_bottom); 1199 1200 /* Circular "lugs" to hold PCBs in place. */ 1201 1202 pcb_lug(-1); 1203 pcb_lug(1); 1204 } 1205 1206 if (MODEL == WIDE_CARTRIDGE) { 1207 1208 pcb_lug_wide(1); 1209 pcb_lug_wide(-1); 1210 } 1211 } 1212 1213 /* Label insets. */ 1214 1215 union() { 1216 1217 /* Top label. See also the front piece. */ 1218 1219 if (TOP_LABEL_INSET) 1220 translate([top_label_left_extent, 1221 -front_depth + top_label_offset_from_front, 1222 upper_extent - top_label_depth]) 1223 cube([top_label_width, top_label_height, 1224 top_label_depth]); 1225 } 1226 1227 /* Top and side grooves, positioned in the back portion. */ 1228 1229 union() { 1230 1231 /* 1232 Grooves are separated to permit a discontinuity with the 1233 wide cartridge and for control over the back section. 1234 */ 1235 1236 cube_at(groove_depth, groove_width_normal, payload_height + bottom, 1237 1, 1, -1, 1238 payload_left_extent, 0, upper_extent); 1239 1240 if (BACK_CONNECTOR_SECTION) 1241 cube_at(groove_depth, groove_width_normal, connector_height, 1242 1, 1, 1, 1243 connector_left_extent, 0, lower_extent); 1244 1245 /* Right groove. */ 1246 1247 cube_at(groove_depth, groove_width_normal, height, 1248 -1, 1, 0, 1249 payload_right_extent, 0, 0); 1250 1251 if (BACK_CONNECTOR_SECTION) 1252 cube_at(groove_depth, groove_width_normal, connector_height, 1253 -1, 1, 1, 1254 connector_right_extent, 0, lower_extent); 1255 1256 /* Top grooves. */ 1257 1258 cube_at(payload_width, groove_width_normal, groove_depth, 1259 0, 1, -1, 1260 payload_centre, 0, upper_extent); 1261 1262 cube_at(payload_width, top_groove_width, top_groove_depth, 1263 0, 1, -1, 1264 payload_centre, -groove_width_extra, upper_extent); 1265 } 1266 1267 /* Back socket holes. */ 1268 1269 if (BACK_LUG == BACK_SOCKET) { 1270 1271 if (MODEL == ROM_CARTRIDGE) { 1272 pcb_back_socket_hole(-1); 1273 pcb_back_socket_hole(1); 1274 } 1275 1276 if (MODEL == WIDE_CARTRIDGE) { 1277 pcb_back_socket_hole_wide(-1); 1278 pcb_back_socket_hole_wide(1); 1279 } 1280 } 1281 1282 /* The connector section at the base of the back. */ 1283 1284 if (BACK_CONNECTOR_SECTION) { 1285 1286 /* Provide an external cavity. */ 1287 1288 if (EXTERNAL_BACK_CAVITY) 1289 intersection() { 1290 1291 /* From the bottom upwards. */ 1292 1293 translate([0, back_depth, lower_extent]) 1294 linear_extrude(height = back_cavity_height) 1295 translate([int_connector_left_extent, 0, 0]) 1296 polygon([ 1297 [back_cavity_offset_from_inner_left, 0], 1298 [back_cavity_inner_offset_from_inner_left, 1299 -back_cavity_depth], 1300 [back_cavity_inner_offset_from_inner_left + 1301 back_cavity_inner_width, 1302 -back_cavity_depth], 1303 [back_cavity_offset_from_inner_left + 1304 back_cavity_width, 0] 1305 ]); 1306 1307 /* From left to right. */ 1308 1309 translate([back_cavity_width / 2, back_depth, lower_extent]) 1310 rotate([0, -90, 0]) 1311 linear_extrude(height = back_cavity_width) 1312 polygon([ 1313 [-extra, -back_cavity_depth], 1314 [back_cavity_inner_height, 1315 -back_cavity_depth], 1316 [back_cavity_height, 0], 1317 [-extra, 0] 1318 ]); 1319 } 1320 1321 /* Or remove the back section entirely. */ 1322 1323 else if (EXTERNAL_BACK_CUTOUT) 1324 translate([0, back_depth, lower_extent]) 1325 linear_extrude(height = back_cavity_height) 1326 translate([int_connector_left_extent, 0, 0]) 1327 polygon([ 1328 [back_cavity_offset_from_inner_left, 0], 1329 [inner_back_slope_min_offset, 1330 -connector_back + inner_back_slope_depth], 1331 [inner_back_slope_min_offset, -connector_back], 1332 [int_connector_width - inner_back_slope_min_offset, 1333 -connector_back], 1334 [int_connector_width - inner_back_slope_min_offset, 1335 -connector_back + inner_back_slope_depth], 1336 [back_cavity_offset_from_inner_left + 1337 back_cavity_width, 0] 1338 ]); 1339 1340 /* Inner back cavities making the back thinner at the sides. */ 1341 1342 translate([0, int_connector_back_depth, lower_extent]) 1343 linear_extrude(height = int_connector_height) 1344 translate([int_connector_left_extent, 0, 0]) 1345 polygon([ 1346 [0, 0], 1347 [inner_back_slope_max_offset, 0], 1348 [inner_back_slope_min_offset, 1349 inner_back_slope_depth], 1350 [0, inner_back_slope_depth] 1351 ]); 1352 1353 translate([0, int_connector_back_depth, lower_extent]) 1354 linear_extrude(height = int_connector_height) 1355 translate([int_connector_right_extent, 0, 0]) 1356 polygon([ 1357 [0, 0], 1358 [-inner_back_slope_max_offset, 0], 1359 [-inner_back_slope_min_offset, 1360 inner_back_slope_depth], 1361 [0, inner_back_slope_depth] 1362 ]); 1363 1364 /* Inner back edge cavity, tapering the bottom edge. */ 1365 1366 translate([inner_back_edge_width / 2, 1367 int_connector_back_depth + inner_back_edge_depth, lower_extent]) 1368 rotate([0, -90, 0]) 1369 linear_extrude(height = inner_back_edge_width) 1370 polygon([ 1371 [-extra, -inner_back_edge_depth], 1372 [inner_back_edge_height, -inner_back_edge_depth], 1373 [0, 0], 1374 [-extra, 0] 1375 ]); 1376 } 1377 1378 /* Fillets to round off the edges. */ 1379 1380 union() { 1381 if (ROUND_EDGES) { 1382 1383 /* Top left and right rounding. */ 1384 1385 translate([payload_left_extent + ro, back_depth / 2, upper_extent - ro]) 1386 rotate([0, 0, 180]) 1387 rotate([90, 0, 0]) 1388 fillet(rr, back_depth); 1389 translate([payload_right_extent - ro, back_depth / 2, upper_extent - ro]) 1390 rotate([90, 0, 0]) 1391 fillet(rr, back_depth); 1392 1393 /* Top back rounding. */ 1394 1395 translate([payload_centre, back_depth - ro, upper_extent - ro]) 1396 rotate([0, -90, 0]) 1397 fillet(rr, payload_width); 1398 1399 /* Outer edge rounding. */ 1400 1401 translate([payload_right_extent - ro, back_depth - ro, int_payload_lower_extent - bottom - extra]) 1402 fillet_justified(rr, payload_height + bottom + extra); 1403 1404 translate([payload_left_extent + ro, back_depth - ro, int_payload_lower_extent - bottom - extra]) 1405 rotate([0, 0, 90]) 1406 fillet_justified(rr, payload_height + bottom + extra); 1407 1408 if (BACK_CONNECTOR_SECTION) { 1409 translate([connector_right_extent - ro, back_depth - ro, lower_extent]) 1410 fillet_partitioned(rr, connector_height - bottom); 1411 1412 translate([connector_left_extent + ro, back_depth - ro, lower_extent]) 1413 rotate([0, 0, 90]) 1414 fillet_partitioned(rr, connector_height - bottom); 1415 } 1416 } 1417 1418 if (ROUND_CONNECTING_EDGES) { 1419 1420 /* 1421 Outer edge rounding of the back extension. This is done as a 1422 separate removal operation rather than occurring when creating the 1423 extension in order to ensure that the edges are actually rounded. 1424 1425 An extra overlapping measure is employed so that the filleting is 1426 continuous between the payload and connector portions. On the outside 1427 edges, the payload filleting is extended downwards. 1428 */ 1429 1430 translate([payload_left_extent + groove_depth + groove_ro, -groove_width_extra + groove_ro, 1431 int_payload_lower_extent - extra]) 1432 rotate([0, 0, 180]) 1433 fillet_justified(groove_rr, inner_payload_front_cutout_height + extra); 1434 1435 translate([payload_right_extent - groove_depth - groove_ro, -groove_width_extra + groove_ro, 1436 int_payload_lower_extent - extra]) 1437 rotate([0, 0, -90]) 1438 fillet_justified(groove_rr, inner_payload_front_cutout_height + extra); 1439 1440 /* Sides of connector. */ 1441 1442 translate([connector_left_extent + groove_depth + groove_ro, -groove_width_extra + groove_ro, 1443 lower_extent]) 1444 rotate([0, 0, 180]) 1445 fillet_partitioned(groove_rr, inner_connector_front_cutout_height); 1446 1447 translate([connector_right_extent - groove_depth - groove_ro, -groove_width_extra + groove_ro, 1448 lower_extent]) 1449 rotate([0, 0, -90]) 1450 fillet_partitioned(groove_rr, inner_connector_front_cutout_height); 1451 1452 /* Top of payload. */ 1453 1454 translate([payload_centre, -groove_width_extra + groove_ro, 1455 int_payload_upper_extent + inner_top_front_cutout_height - groove_ro]) 1456 rotate([0, 0, 180]) 1457 rotate([0, -90, 0]) 1458 fillet(groove_rr, payload_width - groove_depth * 2); 1459 } 1460 } 1461 } 1462 1463 /* PCBs for checking. */ 1464 1465 if (PCB) if (MODEL == ROM_CARTRIDGE) { 1466 translate([0, 0, int_payload_lower_extent]) 1467 difference() { 1468 1469 /* Mock PCB. */ 1470 1471 union() { 1472 cube_at(pcb_width, pcb_depth, 1473 pcb_height - edge_connector_height, 1474 0, -1, 1, 1475 0, edge_connector_cutout_back_depth, 0); 1476 cube_at(edge_connector_width, pcb_depth, 1477 edge_connector_height, 1478 0, -1, -1, 1479 0, edge_connector_cutout_back_depth, 0); 1480 1481 /* Feature. */ 1482 1483 cube_at(feature_width, feature_depth, feature_height, 1484 0, 1, 1, 1485 0, edge_connector_cutout_back_depth, feature_height_offset); 1486 } 1487 1488 /* Holes for mounting. */ 1489 1490 union() { 1491 cube_at(pcb_hole_width, pcb_hole_depth, pcb_left_hole_height, 1492 -1, -1, 1, 1493 pcb_left_hole_offset_from_centre, pcb_hole_start_depth, 1494 pcb_left_hole_offset); 1495 translate([pcb_left_hole_offset_from_centre - 1496 pcb_hole_width / 2, pcb_hole_start_depth, pcb_left_hole_offset]) 1497 rotate([90, 0, 0]) 1498 cylinder(h=pcb_hole_depth, r=pcb_hole_width / 2); 1499 translate([pcb_left_hole_offset_from_centre - 1500 pcb_hole_width / 2, pcb_hole_start_depth, 1501 pcb_left_hole_offset + pcb_left_hole_height]) 1502 rotate([90, 0, 0]) 1503 cylinder(h=pcb_hole_depth, r=pcb_hole_width / 2); 1504 } 1505 1506 union() { 1507 cube_at(pcb_hole_width, pcb_hole_depth, pcb_right_hole_height, 1508 1, -1, 1, 1509 pcb_right_hole_offset_from_centre, pcb_hole_start_depth, 1510 pcb_right_hole_offset); 1511 translate([pcb_right_hole_offset_from_centre + 1512 pcb_hole_width / 2, pcb_hole_start_depth, pcb_right_hole_offset]) 1513 rotate([90, 0, 0]) 1514 cylinder(h=pcb_hole_depth, r=pcb_hole_width / 2); 1515 translate([pcb_right_hole_offset_from_centre + 1516 pcb_hole_width / 2, pcb_hole_start_depth, 1517 pcb_right_hole_offset + pcb_right_hole_height]) 1518 rotate([90, 0, 0]) 1519 cylinder(h=pcb_hole_depth, r=pcb_hole_width / 2); 1520 } 1521 1522 /* Holes for lugs. */ 1523 1524 translate([ 1525 int_connector_left_extent + pcb_lug_offset_from_inside, 1526 pcb_hole_start_depth, pcb_lug_offset_from_bottom]) 1527 rotate([90, 0, 0]) 1528 cylinder(h=pcb_hole_depth, r=pcb_lug_hole_radius); 1529 1530 translate([ 1531 int_connector_right_extent - pcb_lug_offset_from_inside, 1532 pcb_hole_start_depth, pcb_lug_offset_from_bottom]) 1533 rotate([90, 0, 0]) 1534 cylinder(h=pcb_hole_depth, r=pcb_lug_hole_radius); 1535 } 1536 } 1537 1538 if (PCB) if (MODEL == WIDE_CARTRIDGE) { 1539 translate([0, 0, int_payload_lower_extent]) 1540 difference() { 1541 1542 /* Mock PCB. */ 1543 1544 union() { 1545 cube_at(wide_pcb_width, wide_pcb_depth, 1546 wide_pcb_height - edge_connector_height, 1547 0, -1, 1, 1548 payload_centre, edge_connector_cutout_back_depth, 0); 1549 cube_at(edge_connector_width, wide_pcb_depth, 1550 edge_connector_height, 1551 0, -1, -1, 1552 0, edge_connector_cutout_back_depth, 0); 1553 1554 /* Feature. */ 1555 1556 cube_at(feature_width, feature_depth, feature_height, 1557 0, 1, 1, 1558 0, edge_connector_cutout_back_depth, feature_height_offset); 1559 } 1560 1561 /* Holes for lugs. */ 1562 1563 translate([ 1564 payload_centre + int_payload_width / 2 - wide_pcb_lug_offset_from_inside , 1565 wide_pcb_hole_start_depth, 1566 wide_pcb_height - edge_connector_height - wide_pcb_lug_offset_from_bottom]) 1567 rotate([90, 0, 0]) 1568 cylinder(h=wide_pcb_hole_depth, r=wide_pcb_lug_hole_radius); 1569 1570 translate([ 1571 payload_centre - int_payload_width / 2 + wide_pcb_lug_offset_from_inside , 1572 wide_pcb_hole_start_depth, 1573 wide_pcb_height - edge_connector_height - wide_pcb_lug_offset_from_bottom]) 1574 rotate([90, 0, 0]) 1575 cylinder(h=wide_pcb_hole_depth, r=wide_pcb_lug_hole_radius); 1576 1577 translate([ 1578 payload_centre + int_payload_width / 2 - wide_pcb_lug_offset_from_inside , 1579 wide_pcb_hole_start_depth, 1580 wide_pcb_lug_offset_from_bottom]) 1581 rotate([90, 0, 0]) 1582 cylinder(h=wide_pcb_hole_depth, r=wide_pcb_lug_hole_radius); 1583 1584 translate([ 1585 payload_centre - int_payload_width / 2 + wide_pcb_lug_offset_from_inside , 1586 wide_pcb_hole_start_depth, 1587 wide_pcb_lug_offset_from_bottom]) 1588 rotate([90, 0, 0]) 1589 cylinder(h=wide_pcb_hole_depth, r=wide_pcb_lug_hole_radius); 1590 } 1591 } 1592 } 1593 } 1594 1595 /* 1596 Place the back piece next to the front, with the internals facing out 1597 the same way, if APART is defined. 1598 */ 1599 1600 front_displacement_together = CLOSED ? front_back_overlap : APART ? 0 : front_back_overlap - SEPARATION; 1601 front_displacement = APART ? -connector_width * 0.6 : 0; 1602 back_displacement = APART ? connector_width * 0.6 : 0; 1603 back_rotation = APART ? 180 : 0; 1604 1605 if (INTERSECT) { 1606 intersection() { 1607 front_piece(0, front_displacement_together); 1608 back_piece(0, 0); 1609 } 1610 } else { 1611 front_piece(front_displacement, front_displacement_together); 1612 back_piece(back_displacement, back_rotation); 1613 } 1614 } 1615 1616 cartridge();