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