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