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