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 /* Extended floor. */ 528 529 if (payload_width > connector_width) { 530 531 cube_at(payload_width - connector_width, front_depth, bottom, 532 1, -1, 1, 533 payload_left_extent, 0, -height / 2 + int_connector_height); 534 } 535 536 /* PCB supports. */ 537 538 cube_at(pcb_front_support_width, 539 pcb_front_support_depth, 540 pcb_front_support_height, 541 -1, -1, 1, 542 -pcb_support_offset_from_centre, 543 0, 544 int_payload_lower_extent); 545 546 cube_at(pcb_front_support_width, 547 pcb_front_support_depth, 548 pcb_front_support_height, 549 1, -1, 1, 550 pcb_support_offset_from_centre, 551 0, 552 int_payload_lower_extent); 553 554 /* Circular "lugs" to hold PCBs in place. */ 555 556 pcb_front_lug(-1); 557 pcb_front_lug(1); 558 } 559 560 /* Label insets. */ 561 562 union() { 563 564 /* Front label. */ 565 566 if (FRONT_LABEL_INSET) 567 translate([front_label_left_extent, -front_depth, 568 front_label_offset_from_bottom - height / 2]) 569 cube([front_label_width, front_label_depth, 570 front_label_height]); 571 572 /* Top label. See also the back piece. */ 573 574 if (TOP_LABEL_INSET) 575 translate([top_label_left_extent, 576 -front_depth + top_label_offset_from_front, 577 height / 2 - top_label_depth]) 578 cube([top_label_width, top_label_height, 579 top_label_depth]); 580 } 581 582 /* Inner front edge cavity. */ 583 584 translate([inner_front_edge_width / 2, -int_front_depth, -height / 2]) 585 rotate([0, -90, 0]) 586 linear_extrude(height = inner_front_edge_width) 587 polygon([ 588 [-extra, -inner_front_edge_depth], 589 [0, -inner_front_edge_depth], 590 [inner_front_edge_height, 0], 591 [-extra, 0], 592 ]); 593 594 /* Inner top cutout for the top and sides of the back portion. */ 595 596 cube_at(inner_top_front_cutout_width, 597 inner_top_front_cutout_depth, 598 inner_top_front_cutout_height, 599 1, -1, 1, 600 int_payload_left_extent, 0, int_payload_upper_extent); 601 602 cube_at(inner_payload_front_cutout_width, 603 inner_payload_front_cutout_depth, 604 inner_payload_front_cutout_height, 605 1, -1, 1, 606 int_payload_right_extent, 0, int_payload_lower_extent); 607 608 cube_at(inner_payload_front_cutout_width, 609 inner_payload_front_cutout_depth, 610 inner_payload_front_cutout_height, 611 -1, -1, 1, 612 int_payload_left_extent, 0, int_payload_lower_extent); 613 614 cube_at(inner_connector_front_cutout_width, 615 inner_connector_front_cutout_depth, 616 inner_connector_front_cutout_height, 617 1, -1, -1, 618 int_connector_width / 2, 0, int_payload_lower_extent); 619 620 cube_at(inner_connector_front_cutout_width, 621 inner_connector_front_cutout_depth, 622 inner_connector_front_cutout_height, 623 -1, -1, -1, 624 -int_connector_width / 2, 0, int_payload_lower_extent); 625 626 /* Fillets to round off the edges. */ 627 628 union() { 629 630 /* Top left and right rounding. */ 631 632 translate([payload_left_extent + ro, -front_depth / 2, height / 2 - ro]) 633 rotate([0, 0, 180]) 634 rotate([90, 0, 0]) 635 fillet(rr, front_depth); 636 translate([payload_right_extent - ro, -front_depth / 2, height / 2 - ro]) 637 rotate([90, 0, 0]) 638 fillet(rr, front_depth); 639 640 /* Top front rounding. */ 641 642 translate([payload_centre, -front_depth + ro, height / 2 - ro]) 643 rotate([0, 0, 180]) 644 rotate([0, -90, 0]) 645 fillet(rr, payload_width); 646 647 /* Edge rounding. */ 648 649 translate([payload_right_extent - ro, -front_depth + ro, int_payload_lower_extent - bottom - extra]) 650 rotate([0, 0, 270]) 651 fillet_justified(rr, payload_height + bottom + extra); 652 653 translate([payload_left_extent + ro, -front_depth + ro, int_payload_lower_extent - bottom - extra]) 654 rotate([0, 0, 180]) 655 fillet_justified(rr, payload_height + bottom + extra); 656 657 translate([connector_width / 2 - ro, -front_depth + ro, -height / 2]) 658 rotate([0, 0, 270]) 659 fillet_partitioned(rr, connector_height - bottom); 660 661 translate([-connector_width / 2 + ro, -front_depth + ro, -height / 2]) 662 rotate([0, 0, 180]) 663 fillet_partitioned(rr, connector_height - bottom); 664 } 665 } 666 667 /* 668 Place the back piece next to the front, with the internals facing out 669 the same way, if APART is defined. 670 */ 671 672 back_displacement = APART ? connector_width * 0.6 : 0; 673 back_rotation = APART ? 180 : 0; 674 675 if (BACK) 676 translate([back_displacement, 0, 0]) 677 rotate([0, 0, back_rotation]) 678 difference() { 679 680 /* The cartridge surfaces. */ 681 682 union() { 683 684 /* Back portion. */ 685 686 if (BACK_SURFACE) { 687 688 /* Surfaces surrounding the payload. */ 689 690 /* Payload section of back surface. */ 691 692 cube_at(payload_width, payload_back, payload_height, 693 0, 1, 1, 694 payload_centre, int_payload_back_depth, int_payload_lower_extent); 695 696 cube_at(back_left, back_depth, payload_height, 697 -1, 1, 1, 698 int_payload_left_extent, 0, int_payload_lower_extent); 699 700 cube_at(back_right, back_depth, payload_height, 701 1, 1, 1, 702 int_payload_right_extent, 0, int_payload_lower_extent); 703 704 /* Surfaces surrounding the connector. */ 705 706 /* Connector section of back surface overlapping the floor. */ 707 708 cube_at(connector_width, connector_back, connector_height, 709 0, 1, -1, 710 0, int_connector_back_depth, int_payload_lower_extent); 711 712 cube_at(back_left, back_depth, connector_height, 713 -1, 1, -1, 714 -int_connector_width / 2, 0, int_payload_lower_extent); 715 716 cube_at(back_right, back_depth, connector_height, 717 1, 1, -1, 718 int_connector_width / 2, 0, int_payload_lower_extent); 719 720 /* Top of back piece. */ 721 722 cube_at(payload_width, back_depth, top, 723 0, 1, 1, 724 payload_centre, 0, int_payload_upper_extent); 725 726 /* 727 The extension of the back to connect with the front. Here, the inside 728 edges are rounded. The outside edges are rounded later. 729 730 An extra overlapping measure is employed so that the filleting is 731 continuous between the payload and connector portions. On the inside 732 edges, the connector filleting is extended upwards. 733 */ 734 735 /* Left side of payload. */ 736 737 difference() { 738 cube_at(back_left - groove_depth, groove_width_extra, inner_payload_front_cutout_height, 739 -1, 1, 1, 740 int_payload_left_extent, -groove_width_extra, int_payload_lower_extent); 741 translate([int_payload_left_extent - groove_ro, -groove_width_extra + groove_ro, 742 int_payload_lower_extent]) 743 rotate([0, 0, -90]) 744 fillet_justified(groove_rr, inner_payload_front_cutout_height); 745 } 746 747 /* Right side of payload. */ 748 749 difference() { 750 cube_at(back_right - groove_depth, groove_width_extra, inner_payload_front_cutout_height, 751 1, 1, 1, 752 int_payload_right_extent, -groove_width_extra, int_payload_lower_extent); 753 translate([int_payload_right_extent + groove_ro, -groove_width_extra + groove_ro, 754 int_payload_lower_extent]) 755 rotate([0, 0, 180]) 756 fillet_justified(groove_rr, inner_payload_front_cutout_height); 757 } 758 759 /* Left side of connector. */ 760 761 difference() { 762 cube_at(back_left - groove_depth, groove_width_extra, inner_connector_front_cutout_height, 763 -1, 1, -1, 764 -int_connector_width / 2, -groove_width_extra, int_payload_lower_extent); 765 translate([-int_connector_width / 2 - groove_ro, -groove_width_extra + groove_ro, 766 -height / 2]) 767 rotate([0, 0, -90]) 768 fillet_partitioned(groove_rr, inner_connector_front_cutout_height + extra); 769 } 770 771 /* Right side of connector. */ 772 773 difference() { 774 cube_at(back_right - groove_depth, groove_width_extra, inner_connector_front_cutout_height, 775 1, 1, -1, 776 int_connector_width / 2, -groove_width_extra, int_payload_lower_extent); 777 translate([int_connector_width / 2 + groove_ro, -groove_width_extra + groove_ro, 778 -height / 2]) 779 rotate([0, 0, 180]) 780 fillet_partitioned(groove_rr, inner_connector_front_cutout_height + extra); 781 } 782 783 /* Top side. */ 784 785 difference() { 786 cube_at(payload_width - groove_depth * 2, groove_width_extra, top - top_groove_depth, 787 0, 1, 1, 788 payload_centre, -groove_width_extra, int_payload_upper_extent); 789 translate([payload_centre, -groove_width_extra + groove_ro, 790 int_payload_upper_extent + groove_ro]) 791 rotate([0, 0, 180]) 792 rotate([0, 90, 0]) 793 fillet(groove_rr, payload_width - groove_depth * 2); 794 } 795 } 796 797 difference() { 798 799 /* Floor of cartridge. */ 800 801 cube_at(int_connector_width, int_connector_back_depth, bottom, 802 0, 1, 1, 803 0, 0, -height / 2 + int_connector_height); 804 805 /* Edge connector cutout. */ 806 807 cube_at(edge_connector_cutout_back_width, 808 edge_connector_cutout_back_depth, 809 bottom, 810 0, 1, 1, 811 0, 0, -height / 2 + int_connector_height); 812 } 813 814 /* Extended floor. */ 815 816 if (payload_width > connector_width) { 817 818 difference() { 819 820 cube_at(payload_width - connector_width, back_depth, bottom, 821 1, 1, 1, 822 payload_left_extent, 0, -height / 2 + int_connector_height); 823 824 cube_at(payload_width - connector_width, edge_connector_cutout_front_depth, bottom, 825 1, 1, 1, 826 payload_left_extent, 0, -height / 2 + int_connector_height); 827 } 828 } 829 830 /* PCB supports. */ 831 832 pcb_support(-1, pcb_back_support_left_bump_height, 833 pcb_back_support_left_bump_offset_from_bottom); 834 pcb_support(1, pcb_back_support_right_bump_height, 835 pcb_back_support_right_bump_offset_from_bottom); 836 837 /* Circular "lugs" to hold PCBs in place. */ 838 839 pcb_lug(-1); 840 pcb_lug(1); 841 } 842 843 /* Label insets. */ 844 845 union() { 846 847 /* Top label. See also the front piece. */ 848 849 if (TOP_LABEL_INSET) 850 translate([top_label_left_extent, 851 -front_depth + top_label_offset_from_front, 852 height / 2 - top_label_depth]) 853 cube([top_label_width, top_label_height, 854 top_label_depth]); 855 } 856 857 /* Top and side grooves, positioned in the back portion. */ 858 859 union() { 860 861 /* Left groove. */ 862 863 cube_at(groove_depth, groove_width_normal, height, 864 1, 1, 0, 865 payload_left_extent, 0, 0); 866 867 /* Right groove. */ 868 869 cube_at(groove_depth, groove_width_normal, height, 870 -1, 1, 0, 871 payload_right_extent, 0, 0); 872 873 /* Top grooves. */ 874 875 cube_at(payload_width, groove_width_normal, groove_depth, 876 0, 1, -1, 877 payload_centre, 0, height / 2); 878 879 cube_at(payload_width, top_groove_width, top_groove_depth, 880 0, 1, -1, 881 payload_centre, -groove_width_extra, height / 2); 882 } 883 884 /* Back cavity. */ 885 886 if (BACK_CAVITY) 887 intersection() { 888 889 /* From the bottom upwards. */ 890 891 translate([0, back_depth, -height / 2]) 892 linear_extrude(height = back_cavity_height) 893 translate([-int_connector_width / 2, 0, 0]) 894 polygon([ 895 [back_cavity_offset_from_inner_left, 0], 896 [back_cavity_inner_offset_from_inner_left, 897 -back_cavity_depth], 898 [back_cavity_inner_offset_from_inner_left + 899 back_cavity_inner_width, 900 -back_cavity_depth], 901 [back_cavity_offset_from_inner_left + 902 back_cavity_width, 0] 903 ]); 904 905 /* From left to right. */ 906 907 translate([back_cavity_width / 2, back_depth, -height / 2]) 908 rotate([0, -90, 0]) 909 linear_extrude(height = back_cavity_width) 910 polygon([ 911 [-extra, -back_cavity_depth], 912 [back_cavity_inner_height, 913 -back_cavity_depth], 914 [back_cavity_height, 0], 915 [-extra, 0] 916 ]); 917 } 918 919 /* Inner back cavities. */ 920 921 translate([0, int_connector_back_depth, -height / 2]) 922 linear_extrude(height = int_connector_height) 923 translate([-int_connector_width / 2, 0, 0]) 924 polygon([ 925 [0, 0], 926 [inner_back_slope_max_offset, 0], 927 [inner_back_slope_min_offset, 928 inner_back_slope_depth], 929 [0, inner_back_slope_depth] 930 ]); 931 932 translate([0, int_connector_back_depth, -height / 2]) 933 linear_extrude(height = int_connector_height) 934 translate([int_connector_width / 2, 0, 0]) 935 polygon([ 936 [0, 0], 937 [-inner_back_slope_max_offset, 0], 938 [-inner_back_slope_min_offset, 939 inner_back_slope_depth], 940 [0, inner_back_slope_depth] 941 ]); 942 943 /* Inner back edge cavity. */ 944 945 translate([inner_back_edge_width / 2, 946 int_connector_back_depth + inner_back_edge_depth, -height / 2]) 947 rotate([0, -90, 0]) 948 linear_extrude(height = inner_back_edge_width) 949 polygon([ 950 [-extra, -inner_back_edge_depth], 951 [inner_back_edge_height, -inner_back_edge_depth], 952 [0, 0], 953 [-extra, 0] 954 ]); 955 956 /* Fillets to round off the edges. */ 957 958 union() { 959 960 /* Top left and right rounding. */ 961 962 translate([payload_left_extent + ro, back_depth / 2, height / 2 - ro]) 963 rotate([0, 0, 180]) 964 rotate([90, 0, 0]) 965 fillet(rr, back_depth); 966 translate([payload_right_extent - ro, back_depth / 2, height / 2 - ro]) 967 rotate([90, 0, 0]) 968 fillet(rr, back_depth); 969 970 /* Top back rounding. */ 971 972 translate([payload_centre, back_depth - ro, height / 2 - ro]) 973 rotate([0, -90, 0]) 974 fillet(rr, payload_width); 975 976 /* Outer edge rounding. */ 977 978 translate([payload_right_extent - ro, back_depth - ro, int_payload_lower_extent - bottom - extra]) 979 fillet_justified(rr, payload_height + bottom + extra); 980 981 translate([payload_left_extent + ro, back_depth - ro, int_payload_lower_extent - bottom - extra]) 982 rotate([0, 0, 90]) 983 fillet_justified(rr, payload_height + bottom + extra); 984 985 translate([connector_width / 2 - ro, back_depth - ro, -height / 2]) 986 fillet_partitioned(rr, connector_height - bottom); 987 988 translate([-connector_width / 2 + ro, back_depth - ro, -height / 2]) 989 rotate([0, 0, 90]) 990 fillet_partitioned(rr, connector_height - bottom); 991 992 /* 993 Outer edge rounding of the back extension. This is done as a 994 separate removal operation rather than occurring when creating the 995 extension in order to ensure that the edges are actually rounded. 996 997 An extra overlapping measure is employed so that the filleting is 998 continuous between the payload and connector portions. On the outside 999 edges, the payload filleting is extended downwards. 1000 */ 1001 1002 translate([payload_left_extent + groove_depth + groove_ro, -groove_width_extra + groove_ro, 1003 int_payload_lower_extent - extra]) 1004 rotate([0, 0, 180]) 1005 fillet_justified(groove_rr, inner_payload_front_cutout_height + extra); 1006 1007 translate([payload_right_extent - groove_depth - groove_ro, -groove_width_extra + groove_ro, 1008 int_payload_lower_extent - extra]) 1009 rotate([0, 0, -90]) 1010 fillet_justified(groove_rr, inner_payload_front_cutout_height + extra); 1011 1012 /* Sides of connector. */ 1013 1014 translate([-connector_width / 2 + groove_depth + groove_ro, -groove_width_extra + groove_ro, 1015 -height / 2]) 1016 rotate([0, 0, 180]) 1017 fillet_partitioned(groove_rr, inner_connector_front_cutout_height); 1018 1019 translate([connector_width / 2 - groove_depth - groove_ro, -groove_width_extra + groove_ro, 1020 -height / 2]) 1021 rotate([0, 0, -90]) 1022 fillet_partitioned(groove_rr, inner_connector_front_cutout_height); 1023 1024 /* Top of payload. */ 1025 1026 translate([payload_centre, -groove_width_extra + groove_ro, 1027 int_payload_upper_extent + inner_top_front_cutout_height - groove_ro]) 1028 rotate([0, 0, 180]) 1029 rotate([0, -90, 0]) 1030 fillet(groove_rr, payload_width - groove_depth * 2); 1031 } 1032 } 1033 1034 /* PCB for checking. */ 1035 1036 pcb_width = 84.85; pcb_height = 62.5; pcb_depth = 1; 1037 edge_connector_width = 56.5; edge_connector_height = 11.85; 1038 pcb_position_y = int_payload_lower_extent; 1039 1040 pcb_left_hole_offset = pcb_back_support_left_bump_offset_from_bottom 1041 + 0.55; 1042 pcb_left_hole_width = 2.2; 1043 pcb_left_hole_height = pcb_back_support_left_bump_height - 0.55 * 2; 1044 pcb_left_hole_offset_from_centre = pcb_support_offset_from_centre + 1045 pcb_back_support_width / 2 - pcb_left_hole_width / 2; 1046 1047 pcb_right_hole_offset = pcb_back_support_right_bump_offset_from_bottom 1048 + 0.55; 1049 pcb_right_hole_width = pcb_left_hole_width; 1050 pcb_right_hole_height = pcb_back_support_right_bump_height - 0.55 * 2; 1051 pcb_right_hole_offset_from_centre = -pcb_left_hole_offset_from_centre; 1052 1053 pcb_hole_depth = pcb_depth + 0.2; 1054 pcb_hole_start_depth = -0.1; 1055 pcb_lug_hole_radius = 3.75; 1056 1057 if (PCB) 1058 translate([back_displacement, 0, 0]) 1059 translate([0, 0, pcb_position_y]) 1060 difference() { 1061 1062 /* Mock PCB. */ 1063 1064 union() { 1065 cube_at(pcb_width, pcb_depth, 1066 pcb_height - edge_connector_height, 1067 0, 1, 1, 1068 0, 0, 0); 1069 cube_at(edge_connector_width, pcb_depth, 1070 edge_connector_height, 1071 0, 1, -1, 1072 0, 0, 0); 1073 } 1074 1075 /* Holes for mounting. */ 1076 1077 union() { 1078 cube_at(pcb_left_hole_width, 1, pcb_left_hole_height, 1079 1, 1, 1, 1080 pcb_left_hole_offset_from_centre, 0, pcb_left_hole_offset); 1081 translate([pcb_left_hole_offset_from_centre + 1082 pcb_left_hole_width / 2, pcb_hole_start_depth, pcb_left_hole_offset]) 1083 rotate([-90, 0, 0]) 1084 cylinder(h=pcb_hole_depth, r=pcb_left_hole_width / 2); 1085 translate([pcb_left_hole_offset_from_centre + 1086 pcb_left_hole_width / 2, pcb_hole_start_depth, 1087 pcb_left_hole_offset + pcb_left_hole_height]) 1088 rotate([-90, 0, 0]) 1089 cylinder(h=pcb_hole_depth, r=pcb_left_hole_width / 2); 1090 } 1091 1092 union() { 1093 cube_at(pcb_right_hole_width, 1, pcb_right_hole_height, 1094 -1, 1, 1, 1095 pcb_right_hole_offset_from_centre, 0, pcb_right_hole_offset); 1096 translate([pcb_right_hole_offset_from_centre - 1097 pcb_right_hole_width / 2, pcb_hole_start_depth, pcb_right_hole_offset]) 1098 rotate([-90, 0, 0]) 1099 cylinder(h=pcb_hole_depth, r=pcb_right_hole_width / 2); 1100 translate([pcb_right_hole_offset_from_centre - 1101 pcb_right_hole_width / 2, pcb_hole_start_depth, 1102 pcb_right_hole_offset + pcb_right_hole_height]) 1103 rotate([-90, 0, 0]) 1104 cylinder(h=pcb_hole_depth, r=pcb_right_hole_width / 2); 1105 } 1106 1107 /* Holes for lugs. */ 1108 1109 translate([ 1110 -int_connector_width / 2 + pcb_lug_offset_from_inside, 1111 pcb_hole_start_depth, pcb_lug_offset_from_bottom]) 1112 rotate([-90, 0, 0]) 1113 cylinder(h=pcb_hole_depth, r=pcb_lug_hole_radius); 1114 1115 translate([ 1116 int_connector_width / 2 - pcb_lug_offset_from_inside, 1117 pcb_hole_start_depth, pcb_lug_offset_from_bottom]) 1118 rotate([-90, 0, 0]) 1119 cylinder(h=pcb_hole_depth, r=pcb_lug_hole_radius); 1120 } 1121 } 1122 1123 cartridge();