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