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