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