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