1 /* 2 Copyright (C) 2014 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 /* Configure the generated shapes. */ 23 24 BACK_CAVITY = 1; 25 FRONT_LABEL_INSET = 1; 26 TOP_LABEL_INSET = 1; 27 GROOVE = 1; 28 29 /* 30 Options for checking. Some useful combinations... 31 32 Check feature alignment: APART = 0; BACK_SURFACE = 0; FRONT_SURFACE = 0; 33 Check PCB alignment: PCB = 1; BACK_SURFACE = 0; 34 */ 35 36 BACK = 1; BACK_SURFACE = 1; 37 FRONT = 1; FRONT_SURFACE = 1; 38 39 /* For printing: APART = 1; PCB = 0; */ 40 41 APART = 1; 42 PCB = 0; 43 44 /* 45 Rounding/fillet radius and additional margin of subtracted 46 material. The additional margin helps avoid geometry problems. 47 */ 48 49 rr = 2; 50 ro = rr; 51 extra = 0.1; 52 53 groove_rr = 0.5; 54 groove_ro = groove_rr; 55 56 module fillet(r, h) { 57 translate([0, 0, -h/2]) 58 difference() { 59 cube([r + extra, r + extra, h + extra]); 60 cylinder(r = r, h = h); 61 } 62 } 63 64 module fillet_justified(r, h) { 65 difference() { 66 cube([r + extra, r + extra, h + extra]); 67 cylinder(r = r, h = h); 68 } 69 } 70 71 /* 72 Justify an object of the given dimensions, according to the given 73 factors (where 1 indicates moving the object to the positive side of an 74 axis, and -1 indicates moving it to the negative side of an axis). 75 76 NOTE: child should eventually be replaced by children. 77 */ 78 module justify(width, depth, height, wdir, ddir, hdir) { 79 translate([ 80 wdir * width / 2, 81 ddir * depth / 2, 82 hdir * height / 2]) 83 child(); 84 } 85 86 /* 87 Make a cuboid of the given dimensions, justifying it according to the given 88 factors, and moving it to the specified coordinates. 89 90 NOTE: Usage of justify within this module will not work due to recursion 91 NOTE: limitations in openscad, potentially removed in more recent 92 NOTE: releases. Thus, the justify transform is merged in here. 93 */ 94 module cube_at(width, depth, height, wdir, ddir, hdir, x, y, z) { 95 translate([ 96 x + wdir * width / 2, 97 y + ddir * depth / 2, 98 z + hdir * height / 2]) 99 cube([width, depth, height], center = true); 100 } 101 102 /* Internal dimensions. */ 103 104 int_width = 86.0; 105 int_depth = 11.0; 106 int_payload_height = 50.8; /* space between the top and the floor */ 107 int_connector_height = 13.5; /* vertical offset of bottom/floor of payload area */ 108 109 /* Side thicknesses. */ 110 111 front = 2; 112 back = 3.5; 113 top = 3; 114 side = 2; /* increased from 1.5 for 3D printing reliability */ 115 bottom = 1; /* thickness of floor of payload area */ 116 117 /* How much extra depth the back provides for mating with the front. */ 118 119 groove_width_extra = 1.0; 120 front_back_overlap = 1.0; 121 groove_width_overlap = front_back_overlap + groove_width_extra; 122 123 /* Division of pieces into front and back, defining the extents. */ 124 125 int_front_depth = 4.5; 126 front_depth = int_front_depth + front; 127 int_back_depth = int_depth - int_front_depth + front_back_overlap; 128 back_depth = int_back_depth + back; 129 130 /* Cartridge dimensions. */ 131 132 width = int_width + side + side; 133 height = top + int_payload_height + bottom + int_connector_height; 134 depth = int_depth + front + back; 135 int_payload_upper_extent = height / 2 - top; 136 int_payload_lower_extent = -height / 2 + int_connector_height + bottom; 137 138 /* Cartridge surfaces. */ 139 140 front_side = side; 141 front_left = front_side; 142 front_right = front_side; 143 back_side = side; 144 back_left = back_side; 145 back_right = back_side; 146 147 /* Label details. */ 148 149 front_label_width = 83.0; 150 front_label_height = 46.0; 151 front_label_depth = 1.0; 152 front_label_offset_from_bottom = 19.5; 153 154 top_label_width = front_label_width; 155 top_label_height = 11.5; /* the height as seen from above */ 156 top_label_depth = front_label_depth; 157 top_label_offset_from_front = 2.5; 158 159 /* 160 The groove around the sides and top. This is extended to allow the 161 pieces to fit together, and this extension is generated regardless of 162 whether the visible groove is enabled or not. 163 */ 164 165 groove_width_exposed = GROOVE ? 1.5 : 0; 166 groove_width_normal = groove_width_exposed + front_back_overlap; 167 groove_depth = 1.0; /* how deep the groove goes into each side */ 168 169 /* Additional cutting to mate the back and front. */ 170 171 top_groove_width = groove_width_overlap; 172 top_groove_depth = 2.0; 173 174 /* 175 Space for the inner edge of the back inside the front. 176 Offsets are measured from the outside surfaces. 177 */ 178 179 inner_top_front_cutout_width = int_width; 180 inner_top_front_cutout_depth = top_groove_width; 181 inner_top_front_cutout_height = top - top_groove_depth; 182 183 inner_side_front_cutout_height = height - top_groove_depth; 184 inner_side_front_cutout_width = front_side - groove_depth; 185 inner_side_front_cutout_depth = groove_width_overlap; 186 187 /* 188 The back cavity is the indented part at the bottom of the back of the 189 cartridge. It appears to be mechanically superfluous, just making the 190 shape of the cartridge look a bit fancier. 191 */ 192 193 back_cavity_width = 68.0; 194 back_cavity_inner_width = 65.0; 195 back_cavity_offset_from_inner_left = 9.0; 196 back_cavity_inner_offset_from_inner_left = 10.5; 197 back_cavity_height = 13.5; 198 back_cavity_inner_height = 12.0; 199 back_cavity_depth = 1.5; 200 201 /* 202 The effect of the cavity on the inside of the case. The most important 203 measurement is the maximum offset since it defines the width of the 204 internal space that should accommodate the plastic guides of the Plus 1 205 socket. 206 */ 207 208 inner_back_slope_depth = 2.5; 209 inner_back_slope_width = inner_back_slope_depth; 210 inner_back_slope_max_offset = 10.5; 211 inner_back_slope_min_offset = inner_back_slope_max_offset - inner_back_slope_width; 212 213 /* The tapering off of the inner back edge. */ 214 215 inner_back_edge_width = 69.0; 216 inner_back_edge_height = 3.0; 217 inner_back_edge_depth = 1.5; 218 219 /* The tapering off of the inner front edge. */ 220 221 inner_front_edge_width = width - front_side * 2; 222 inner_front_edge_height = 3.0; 223 inner_front_edge_depth = 1.5; 224 225 /* 226 The cutouts in the floor of the front of the cartridge that produce a 227 kind of tab that guides the edge connector into place in the back cutout. 228 */ 229 230 edge_connector_cutout_front_offset = 1.0; 231 edge_connector_cutout_front_depth = front_back_overlap; 232 edge_connector_cutout_front_width = 15.0; 233 234 /* 235 The cutout in the floor of the back of the cartridge that accommodates 236 the edge connector. 237 */ 238 239 edge_connector_cutout_back_depth = 3.0; 240 edge_connector_cutout_back_width = 57.5; 241 242 /* 243 Edge connectors are themselves 0.05" or approximately 1.27mm in 244 thickness according to the Acorn Electron Cartridge Interface Specification 245 (Acorn Support Application Group Note 014). 246 */ 247 248 /* Extra internal features. Depths include front and back surfaces. */ 249 250 pcb_back_support_width = 1.2; 251 pcb_back_support_depth = back_depth - 252 edge_connector_cutout_back_depth; 253 pcb_back_support_height = height - int_connector_height - top - bottom; 254 255 pcb_front_support_width = 1.2; 256 pcb_front_support_depth = int_front_depth; 257 pcb_front_support_height = pcb_back_support_height; 258 259 /* 260 Features measured from the Stardot Dual ROM Adaptor cartridge board 261 dimensions diagram. Offsets are from inside the bottom "floor". 262 */ 263 264 pcb_back_support_bump_width = pcb_front_support_width; 265 pcb_back_support_bump_depth = 1.5; 266 pcb_back_support_left_bump_height = 13.2; 267 pcb_back_support_right_bump_height = 10.7; 268 pcb_back_support_left_bump_offset_from_bottom = 15.1; 269 pcb_back_support_right_bump_offset_from_bottom = 17.6; 270 pcb_back_support_top_bump_height = 3.8; 271 272 /* Move the PCB support towards the centre. */ 273 pcb_support_margin = 1.75; 274 pcb_support_offset_from_centre = edge_connector_cutout_back_width / 2 275 - pcb_support_margin; 276 277 pcb_lug_depth = pcb_back_support_depth + 278 pcb_back_support_bump_depth; 279 pcb_lug_inner_radius = 1.0; 280 pcb_lug_outer_radius = 5.5 / 2; 281 pcb_lug_offset_from_bottom = 14.35; 282 pcb_lug_offset_from_inside = 5.55; 283 284 pcb_lug_cross_width = 6.7; 285 pcb_lug_cross_depth = pcb_back_support_depth; 286 pcb_lug_cross_height = 1.4; 287 288 pcb_front_lug_depth = pcb_back_support_bump_depth + 289 pcb_front_support_depth; 290 pcb_front_lug_inner_radius = pcb_lug_outer_radius; 291 pcb_front_lug_outer_radius = pow( 292 pow(pcb_lug_cross_width / 2, 2) + 293 pow(pcb_lug_cross_height / 2, 2), 294 0.5); 295 296 /* Repeated constructs. */ 297 298 module pcb_support(xdir, bump_height, bump_offset) { 299 300 /* 301 Translate the support in the stated direction. 302 Since the support is already justified in the direction, the translation 303 moves the inner edge of the support to alignment with the end of the 304 edge connector cutout and then back by the PCB support margin. 305 */ 306 307 translate([xdir * 308 pcb_support_offset_from_centre, 309 edge_connector_cutout_back_depth, 310 int_payload_lower_extent]) 311 justify(pcb_back_support_width, 312 pcb_back_support_depth, 313 pcb_back_support_height, 314 xdir, 1, 1) 315 union() { 316 317 /* Underlying support strut. */ 318 319 cube([pcb_back_support_width, 320 pcb_back_support_depth, 321 pcb_back_support_height], center = true); 322 323 /* Middle bump. */ 324 325 cube_at(pcb_back_support_bump_width, 326 pcb_back_support_bump_depth, 327 bump_height, 328 0, -1, 1, 329 0, 330 -pcb_back_support_depth / 2, 331 -pcb_back_support_height / 2 + bump_offset); 332 333 /* Top bump. */ 334 335 cube_at(pcb_back_support_bump_width, 336 pcb_back_support_bump_depth, 337 pcb_back_support_top_bump_height, 338 0, -1, 1, 339 0, 340 -pcb_back_support_depth / 2, 341 pcb_back_support_height / 2 - 342 pcb_back_support_top_bump_height); 343 } 344 } 345 346 module pcb_lug(xdir) { 347 translate([xdir * 348 (int_width / 2 - pcb_lug_offset_from_inside), 349 back_depth, 350 int_payload_lower_extent + pcb_lug_offset_from_bottom 351 ]) 352 rotate([90, 0, 0]) 353 difference() { 354 union() { 355 cylinder(h=pcb_lug_depth, r=pcb_lug_outer_radius); 356 cube_at(pcb_lug_cross_width, 357 pcb_lug_cross_height, pcb_lug_cross_depth, 358 0, 0, 1, 359 0, 0, 0); 360 cube_at(pcb_lug_cross_height, 361 pcb_lug_cross_width, pcb_lug_cross_depth, 362 0, 0, 1, 363 0, 0, 0); 364 } 365 cylinder(h=pcb_lug_depth, r=pcb_lug_inner_radius); 366 } 367 } 368 369 module pcb_front_lug(xdir) { 370 translate([xdir * 371 (int_width / 2 - pcb_lug_offset_from_inside), 372 -int_front_depth + pcb_front_lug_depth, 373 int_payload_lower_extent + pcb_lug_offset_from_bottom 374 ]) 375 rotate([90, 0, 0]) 376 difference() { 377 cylinder(h=pcb_front_lug_depth, 378 r=pcb_front_lug_outer_radius); 379 cylinder(h=pcb_front_lug_depth, 380 r=pcb_front_lug_inner_radius); 381 } 382 } 383 384 /* The actual shapes. */ 385 386 front_displacement = APART ? -width * 0.6 : 0; 387 388 if (FRONT) 389 translate([front_displacement, 0, 0]) 390 difference() { 391 392 /* The cartridge surfaces. */ 393 394 union() { 395 396 /* Front portion. */ 397 398 if (FRONT_SURFACE) { 399 cube_at(width, front, height, 400 0, -1, 0, 401 0, -int_front_depth, 0); 402 cube_at(front_left, front_depth, height, 403 -1, -1, 0, 404 -int_width / 2, 0, 0); 405 cube_at(front_right, front_depth, height, 406 1, -1, 0, 407 int_width / 2, 0, 0); 408 cube_at(width, front_depth, top, 409 0, -1, 1, 410 0, 0, int_payload_upper_extent); 411 } 412 413 difference() { 414 415 /* Floor of cartridge. */ 416 417 cube_at(int_width, int_front_depth, bottom, 418 0, -1, 1, 419 0, 0, -height / 2 + int_connector_height); 420 421 /* Left cutout. */ 422 423 cube_at(edge_connector_cutout_front_width, 424 edge_connector_cutout_front_depth, 425 bottom, 426 1, -1, 1, 427 -width / 2 + edge_connector_cutout_front_offset, 428 0, 429 -height / 2 + int_connector_height); 430 431 /* Right cutout. */ 432 433 cube_at(edge_connector_cutout_front_width, 434 edge_connector_cutout_front_depth, 435 bottom, 436 -1, -1, 1, 437 width / 2 - edge_connector_cutout_front_offset, 438 0, 439 -height / 2 + int_connector_height); 440 } 441 442 /* PCB supports. */ 443 444 cube_at(pcb_front_support_width, 445 pcb_front_support_depth, 446 pcb_front_support_height, 447 -1, -1, 1, 448 -pcb_support_offset_from_centre, 449 0, 450 int_payload_lower_extent); 451 452 cube_at(pcb_front_support_width, 453 pcb_front_support_depth, 454 pcb_front_support_height, 455 1, -1, 1, 456 pcb_support_offset_from_centre, 457 0, 458 int_payload_lower_extent); 459 460 /* Circular "lugs" to hold PCBs in place. */ 461 462 pcb_front_lug(-1); 463 pcb_front_lug(1); 464 } 465 466 /* Label insets. */ 467 468 union() { 469 470 /* Front label. */ 471 472 if (FRONT_LABEL_INSET) 473 translate([-front_label_width / 2, -front_depth, 474 front_label_offset_from_bottom - height / 2]) 475 cube([front_label_width, front_label_depth, 476 front_label_height]); 477 478 /* Top label. See also the back piece. */ 479 480 if (TOP_LABEL_INSET) 481 translate([-top_label_width / 2, 482 -front_depth + top_label_offset_from_front, 483 height / 2 - top_label_depth]) 484 cube([top_label_width, top_label_height, 485 top_label_depth]); 486 } 487 488 /* Inner front edge cavity. */ 489 490 translate([inner_front_edge_width / 2, -int_front_depth, -height / 2]) 491 rotate([0, -90, 0]) 492 linear_extrude(height = inner_front_edge_width) 493 polygon([ 494 [-extra, -inner_front_edge_depth], 495 [0, -inner_front_edge_depth], 496 [inner_front_edge_height, 0], 497 [-extra, 0], 498 ]); 499 500 /* Inner top cutout for the top and sides of the back portion. */ 501 502 cube_at(inner_top_front_cutout_width, 503 inner_top_front_cutout_depth, 504 inner_top_front_cutout_height, 505 0, -1, 1, 506 0, 0, int_payload_upper_extent); 507 508 cube_at(inner_side_front_cutout_width, 509 inner_side_front_cutout_depth, 510 inner_side_front_cutout_height, 511 1, -1, 0, 512 int_width / 2, 0, -inner_top_front_cutout_height); 513 514 cube_at(inner_side_front_cutout_width, 515 inner_side_front_cutout_depth, 516 inner_side_front_cutout_height, 517 -1, -1, 0, 518 -int_width / 2, 0, -inner_top_front_cutout_height); 519 520 /* Fillets to round off the edges. */ 521 522 union() { 523 524 /* Top left and right rounding. */ 525 526 translate([-width / 2 + ro, -front_depth / 2, height / 2 - ro]) 527 rotate([0, 0, 180]) 528 rotate([90, 0, 0]) 529 fillet(rr, front_depth); 530 translate([width / 2 - ro, -front_depth / 2, height / 2 - ro]) 531 rotate([90, 0, 0]) 532 fillet(rr, front_depth); 533 534 /* Top front rounding. */ 535 536 translate([0, -front_depth + ro, height / 2 - ro]) 537 rotate([0, 0, 180]) 538 rotate([0, -90, 0]) 539 fillet(rr, width); 540 541 /* Edge rounding. */ 542 543 translate([-width / 2 + ro, -front_depth + ro, 0]) 544 rotate([0, 0, 180]) 545 fillet(rr, height); 546 translate([width / 2 - ro, -front_depth + ro, 0]) 547 rotate([0, 0, 270]) 548 fillet(rr, height); 549 } 550 } 551 552 /* 553 Place the back piece next to the front, with the internals facing out 554 the same way, if APART is defined. 555 */ 556 557 back_displacement = APART ? width * 0.6 : 0; 558 back_rotation = APART ? 180 : 0; 559 560 if (BACK) 561 translate([back_displacement, 0, 0]) 562 rotate([0, 0, back_rotation]) 563 difference() { 564 565 /* The cartridge surfaces. */ 566 567 union() { 568 569 /* Back portion. */ 570 571 if (BACK_SURFACE) { 572 cube_at(width, back, height, 573 0, 1, 0, 574 0, int_back_depth, 0); 575 576 cube_at(back_left, back_depth, height, 577 -1, 1, 0, 578 -int_width / 2, 0, 0); 579 580 cube_at(back_right, back_depth, height, 581 1, 1, 0, 582 int_width / 2, 0, 0); 583 584 cube_at(width, back_depth, top, 585 0, 1, 1, 586 0, 0, int_payload_upper_extent); 587 588 /* The extension of the back to connect with the front. */ 589 590 difference() { 591 cube_at(back_left - groove_depth, groove_width_extra, inner_side_front_cutout_height, 592 -1, 1, 1, 593 -int_width / 2, -groove_width_extra, -height / 2); 594 translate([-int_width / 2 - groove_ro, -groove_width_extra + groove_ro, 595 -height / 2]) 596 rotate([0, 0, -90]) 597 fillet_justified(groove_rr, inner_side_front_cutout_height); 598 } 599 600 difference() { 601 cube_at(back_right - groove_depth, groove_width_extra, inner_side_front_cutout_height, 602 1, 1, 1, 603 int_width / 2, -groove_width_extra, -height / 2); 604 translate([int_width / 2 + groove_ro, -groove_width_extra + groove_ro, 605 -height / 2]) 606 rotate([0, 0, 180]) 607 fillet_justified(groove_rr, inner_side_front_cutout_height); 608 } 609 610 difference() { 611 cube_at(width - groove_depth * 2, groove_width_extra, top - top_groove_depth, 612 0, 1, 1, 613 0, -groove_width_extra, int_payload_upper_extent); 614 translate([0, -groove_width_extra + groove_ro, 615 int_payload_upper_extent + groove_ro]) 616 rotate([0, 0, 180]) 617 rotate([0, 90, 0]) 618 fillet(groove_rr, width - groove_depth * 2); 619 } 620 } 621 622 difference() { 623 624 /* Floor of cartridge. */ 625 626 cube_at(int_width, int_back_depth, bottom, 627 0, 1, 1, 628 0, 0, -height / 2 + int_connector_height); 629 630 /* Edge connector cutout. */ 631 632 cube_at(edge_connector_cutout_back_width, 633 edge_connector_cutout_back_depth, 634 bottom, 635 0, 1, 1, 636 0, 0, -height / 2 + int_connector_height); 637 } 638 639 /* PCB supports. */ 640 641 pcb_support(-1, pcb_back_support_left_bump_height, 642 pcb_back_support_left_bump_offset_from_bottom); 643 pcb_support(1, pcb_back_support_right_bump_height, 644 pcb_back_support_right_bump_offset_from_bottom); 645 646 /* Circular "lugs" to hold PCBs in place. */ 647 648 pcb_lug(-1); 649 pcb_lug(1); 650 } 651 652 /* Label insets. */ 653 654 union() { 655 656 /* Top label. See also the front piece. */ 657 658 if (TOP_LABEL_INSET) 659 translate([-top_label_width / 2, 660 -front_depth + top_label_offset_from_front, 661 height / 2 - top_label_depth]) 662 cube([top_label_width, top_label_height, 663 top_label_depth]); 664 } 665 666 /* Top and side grooves, positioned in the back portion. */ 667 668 union() { 669 670 /* Left groove. */ 671 672 cube_at(groove_depth, groove_width_normal, height, 673 1, 1, 0, 674 -width / 2, 0, 0); 675 676 /* Right groove. */ 677 678 cube_at(groove_depth, groove_width_normal, height, 679 -1, 1, 0, 680 width / 2, 0, 0); 681 682 /* Top grooves. */ 683 684 cube_at(width, groove_width_normal, groove_depth, 685 0, 1, -1, 686 0, 0, height / 2); 687 688 cube_at(width, top_groove_width, top_groove_depth, 689 0, 1, -1, 690 0, -groove_width_extra, height / 2); 691 } 692 693 /* Back cavity. */ 694 695 if (BACK_CAVITY) 696 intersection() { 697 698 /* From the bottom upwards. */ 699 700 translate([0, back_depth, -height / 2]) 701 linear_extrude(height = back_cavity_height) 702 translate([-int_width / 2, 0, 0]) 703 polygon([ 704 [back_cavity_offset_from_inner_left, 0], 705 [back_cavity_inner_offset_from_inner_left, 706 -back_cavity_depth], 707 [back_cavity_inner_offset_from_inner_left + 708 back_cavity_inner_width, 709 -back_cavity_depth], 710 [back_cavity_offset_from_inner_left + 711 back_cavity_width, 0] 712 ]); 713 714 /* From left to right. */ 715 716 translate([back_cavity_width / 2, back_depth, -height / 2]) 717 rotate([0, -90, 0]) 718 linear_extrude(height = back_cavity_width) 719 polygon([ 720 [-extra, -back_cavity_depth], 721 [back_cavity_inner_height, 722 -back_cavity_depth], 723 [back_cavity_height, 0], 724 [-extra, 0] 725 ]); 726 } 727 728 /* Inner back cavities. */ 729 730 translate([0, int_back_depth, -height / 2]) 731 linear_extrude(height = int_connector_height) 732 translate([-int_width / 2, 0, 0]) 733 polygon([ 734 [0, 0], 735 [inner_back_slope_max_offset, 0], 736 [inner_back_slope_min_offset, 737 inner_back_slope_depth], 738 [0, inner_back_slope_depth] 739 ]); 740 741 translate([0, int_back_depth, -height / 2]) 742 linear_extrude(height = int_connector_height) 743 translate([int_width / 2, 0, 0]) 744 polygon([ 745 [0, 0], 746 [-inner_back_slope_max_offset, 0], 747 [-inner_back_slope_min_offset, 748 inner_back_slope_depth], 749 [0, inner_back_slope_depth] 750 ]); 751 752 /* Inner back edge cavity. */ 753 754 translate([inner_back_edge_width / 2, 755 int_back_depth + inner_back_edge_depth, -height / 2]) 756 rotate([0, -90, 0]) 757 linear_extrude(height = inner_back_edge_width) 758 polygon([ 759 [-extra, -inner_back_edge_depth], 760 [inner_back_edge_height, -inner_back_edge_depth], 761 [0, 0], 762 [-extra, 0] 763 ]); 764 765 /* Fillets to round off the edges. */ 766 767 union() { 768 769 /* Top left and right rounding. */ 770 771 translate([-width / 2 + ro, back_depth / 2, height / 2 - ro]) 772 rotate([0, 0, 180]) 773 rotate([90, 0, 0]) 774 fillet(rr, back_depth); 775 translate([width / 2 - ro, back_depth / 2, height / 2 - ro]) 776 rotate([90, 0, 0]) 777 fillet(rr, back_depth); 778 779 /* Top back rounding. */ 780 781 translate([0, back_depth - ro, height / 2 - ro]) 782 rotate([0, -90, 0]) 783 fillet(rr, width); 784 785 /* Edge rounding. */ 786 787 translate([width / 2 - ro, back_depth - ro, 0]) 788 fillet(rr, height); 789 translate([-width / 2 + ro, back_depth - ro, 0]) 790 rotate([0, 0, 90]) 791 fillet(rr, height); 792 793 /* Outer edge rounding of the back extension. */ 794 795 translate([-width / 2 + groove_depth + groove_ro, -groove_width_extra + groove_ro, 796 -height / 2]) 797 rotate([0, 0, 180]) 798 fillet_justified(groove_rr, inner_side_front_cutout_height); 799 800 translate([width / 2 - groove_depth - groove_ro, -groove_width_extra + groove_ro, 801 -height / 2]) 802 rotate([0, 0, -90]) 803 fillet_justified(groove_rr, inner_side_front_cutout_height); 804 805 translate([0, -groove_width_extra + groove_ro, 806 int_payload_upper_extent + inner_top_front_cutout_height - groove_ro]) 807 rotate([0, 0, 180]) 808 rotate([0, -90, 0]) 809 fillet(groove_rr, width - groove_depth * 2); 810 } 811 } 812 813 /* PCB for checking. */ 814 815 pcb_width = 84.85; pcb_height = 62.5; pcb_depth = 1; 816 edge_connector_width = 56.5; edge_connector_height = 11.85; 817 pcb_position_y = int_payload_lower_extent; 818 819 pcb_left_hole_offset = pcb_back_support_left_bump_offset_from_bottom 820 + 0.55; 821 pcb_left_hole_width = 2.2; 822 pcb_left_hole_height = pcb_back_support_left_bump_height - 0.55 * 2; 823 pcb_left_hole_offset_from_centre = pcb_support_offset_from_centre + 824 pcb_back_support_width / 2 - pcb_left_hole_width / 2; 825 826 pcb_right_hole_offset = pcb_back_support_right_bump_offset_from_bottom 827 + 0.55; 828 pcb_right_hole_width = pcb_left_hole_width; 829 pcb_right_hole_height = pcb_back_support_right_bump_height - 0.55 * 2; 830 pcb_right_hole_offset_from_centre = -pcb_left_hole_offset_from_centre; 831 832 pcb_hole_depth = pcb_depth + 0.2; 833 pcb_hole_start_depth = -0.1; 834 pcb_lug_hole_radius = 3.75; 835 836 if (PCB) 837 translate([back_displacement, 0, 0]) 838 translate([0, 0, pcb_position_y]) 839 difference() { 840 841 /* Mock PCB. */ 842 843 union() { 844 cube_at(pcb_width, pcb_depth, 845 pcb_height - edge_connector_height, 846 0, 1, 1, 847 0, 0, 0); 848 cube_at(edge_connector_width, pcb_depth, 849 edge_connector_height, 850 0, 1, -1, 851 0, 0, 0); 852 } 853 854 /* Holes for mounting. */ 855 856 union() { 857 cube_at(pcb_left_hole_width, 1, pcb_left_hole_height, 858 1, 1, 1, 859 pcb_left_hole_offset_from_centre, 0, pcb_left_hole_offset); 860 translate([pcb_left_hole_offset_from_centre + 861 pcb_left_hole_width / 2, pcb_hole_start_depth, pcb_left_hole_offset]) 862 rotate([-90, 0, 0]) 863 cylinder(h=pcb_hole_depth, r=pcb_left_hole_width / 2); 864 translate([pcb_left_hole_offset_from_centre + 865 pcb_left_hole_width / 2, pcb_hole_start_depth, 866 pcb_left_hole_offset + pcb_left_hole_height]) 867 rotate([-90, 0, 0]) 868 cylinder(h=pcb_hole_depth, r=pcb_left_hole_width / 2); 869 } 870 871 union() { 872 cube_at(pcb_right_hole_width, 1, pcb_right_hole_height, 873 -1, 1, 1, 874 pcb_right_hole_offset_from_centre, 0, pcb_right_hole_offset); 875 translate([pcb_right_hole_offset_from_centre - 876 pcb_right_hole_width / 2, pcb_hole_start_depth, pcb_right_hole_offset]) 877 rotate([-90, 0, 0]) 878 cylinder(h=pcb_hole_depth, r=pcb_right_hole_width / 2); 879 translate([pcb_right_hole_offset_from_centre - 880 pcb_right_hole_width / 2, pcb_hole_start_depth, 881 pcb_right_hole_offset + pcb_right_hole_height]) 882 rotate([-90, 0, 0]) 883 cylinder(h=pcb_hole_depth, r=pcb_right_hole_width / 2); 884 } 885 886 /* Holes for lugs. */ 887 888 translate([ 889 -int_width / 2 + pcb_lug_offset_from_inside, 890 pcb_hole_start_depth, pcb_lug_offset_from_bottom]) 891 rotate([-90, 0, 0]) 892 cylinder(h=pcb_hole_depth, r=pcb_lug_hole_radius); 893 894 translate([ 895 int_width / 2 - pcb_lug_offset_from_inside, 896 pcb_hole_start_depth, pcb_lug_offset_from_bottom]) 897 rotate([-90, 0, 0]) 898 cylinder(h=pcb_hole_depth, r=pcb_lug_hole_radius); 899 } 900 } 901 902 cartridge();