AcornElectron

cartridge.scad

80:6a90399652a5
2017-03-31 Paul Boddie Added options to configure the back socket arrangement, any holes in the back piece for securing the pieces, and for adjustment of the hole radius.
     1 /*     2 Copyright (C) 2014, 2015, 2016, 2017 Paul Boddie <paul@boddie.org.uk>     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 	/* Model configurations. Do not edit! */    23     24 	ROM_CARTRIDGE = 0;    25 	WIDE_CARTRIDGE = 1;    26     27 	/* Set this to the desired model type. */    28     29 	MODEL = ROM_CARTRIDGE;    30     31 	/* Plug and socket options. Do not edit! */    32     33 	BACK_PLUG = 0;    34 	BACK_SOCKET = 1;    35     36 	/*    37 	Set this to the desired lug type.    38 	The back plug is a normal plug that connects with the front socket.    39 	The back socket is the same kind of tube as the front socket, permitting    40 	the insertion of screws or nuts through the case to hold both pieces    41 	together.    42 	*/    43     44 	BACK_LUG = BACK_PLUG;    45     46 	/*    47 	With the back socket chosen, a through-hole can be requested so that    48 	a nut and bolt arrangement can be used.    49 	Otherwise, a hole will only exist to screw things through the front of    50 	the cartridge into the front and back sockets.    51 	*/    52     53 	BACK_SOCKET_HOLE = 0;    54     55 	/*    56 	Set the plug and (partial) through-hole radius.    57 	Adjust this for the screws or bolts to be used when using a back socket.    58 	*/    59     60 	BACK_PLUG_RADIUS = 5.5 / 2;    61     62 	/*    63 	Configure the generated shapes.    64 	For the absolute simplest shape (for basic 3D printers)...    65     66 	BACK_CONNECTOR_SECTION = 0;    67 	FRONT_LABEL_INSET = 0; TOP_LABEL_INSET = 0;    68 	GROOVE = 0; ROUND_EDGES = 0; ROUND_CONNECTING_EDGES = 0;    69     70 	To retain the back connecting piece instead, change the above to...    71     72 	BACK_CONNECTOR_SECTION = 1;    73     74 	And set the following options...    75     76 	EXTERNAL_BACK_CAVITY = 0;    77 	EXTERNAL_BACK_CUTOUT = 1;    78     79 	Leave all options enabled for more capable 3D printers.    80 	*/    81     82 	/* The back part of the connecting section of the cartridge. */    83 	BACK_CONNECTOR_SECTION = 1;    84     85 	/* A necessary exterior cavity to let the cartridge fit inside the slot. */    86 	EXTERNAL_BACK_CAVITY = 1;    87     88 	/* An alternative cutout to let the cartridge fit inside the slot. */    89 	EXTERNAL_BACK_CUTOUT = 0;    90     91 	/* Optional insets for labels. */    92 	FRONT_LABEL_INSET = 1;    93 	TOP_LABEL_INSET = 1;    94     95 	/* Optional groove between the pieces to match the Electron. */    96 	GROOVE = 1;    97     98 	/* Optional rounding of the external edges. */    99 	ROUND_EDGES = 1;   100    101 	/* Recommended rounding of connection edges. */   102 	ROUND_CONNECTING_EDGES = 1;   103    104 	/* Set this where a shorter PCB is to be used. */   105    106 	SHORT_PCB = 0;   107    108 	/*   109 	Options for checking. Some useful combinations...   110    111 	Check feature alignment: APART = 0; BACK_SURFACE = 0; FRONT_SURFACE = 0;   112 	Check PCB alignment:     PCB = 1; BACK_SURFACE = 0;   113 	*/   114    115 	BACK = 1; BACK_SURFACE = 1;   116 	FRONT = 1; FRONT_SURFACE = 1;   117 	TOP_SURFACE = 1;   118    119 	/* For printing: APART = 1; PCB = 0; */   120    121 	APART = 1;   122 	PCB = 0;   123    124 	/* To check overlaps: INTERSECT = 1; CLOSED = 1; */   125    126 	INTERSECT = 0;   127 	CLOSED = 0;   128    129 	/* Separation when not apart and not closed. */   130    131 	SEPARATION = 10;   132    133 	/* To save time (before printing): FILLET = 0; */   134    135 	FILLET = 1;   136    137 	/*   138 	Rounding/fillet radius and additional margin of subtracted   139 	material. The additional margin helps avoid geometry problems.   140 	*/   141    142 	rr = 2;   143 	ro = rr;   144 	extra = 0.1;   145    146 	groove_rr = 0.2;   147 	groove_ro = groove_rr;   148    149 	pcb_lug_rr = 0.5;   150 	pcb_lug_ro = pcb_lug_rr;   151    152 	/* A fillet performs rounding using a quarter segment of a cylinder. */   153    154 	module fillet(r, h) {   155 		if (FILLET)   156 			translate([0, 0, -h/2])   157 				difference() {   158 					cube([r + extra, r + extra, h + extra]);   159 					cylinder(r = r, h = h);   160 				}   161 	}   162    163 	/* A fillet justified using the axes. */   164    165 	module fillet_justified(r, h) {   166 		if (FILLET)   167 			difference() {   168 				cube([r + extra, r + extra, h + extra]);   169 				cylinder(r = r, h = h);   170 			}   171 	}   172    173 	/* A justified fillet with the extra material below the y-axis. */   174    175 	module fillet_partitioned(r, h) {   176 		if (FILLET)   177 			difference() {   178 				translate([0, 0, -extra])   179 					cube([r + extra, r + extra, h + extra]);   180 				cylinder(r = r, h = h);   181 			}   182 	}   183    184 	module fillet_torus(radius, rounding) {   185 		if (FILLET)   186 			difference() {   187 				cube_at((radius + extra) * 2, (radius + extra) * 2, rounding + extra,   188 					0, 0, 1,   189 					0, 0, 0);   190 				union() {   191 					rotate_extrude(convexity = 10)   192 						translate([radius - rounding, 0, 0])   193 							circle(r = rounding);   194 					cylinder(r = radius - rounding, h = rounding);   195 				}   196 			}   197 	}   198    199 	/*   200 	Justify an object of the given dimensions, according to the given   201 	factors (where 1 indicates moving the object to the positive side of an   202 	axis, and -1 indicates moving it to the negative side of an axis).   203    204 	NOTE: child should eventually be replaced by children.   205 	*/   206 	module justify(width, depth, height, wdir, ddir, hdir) {   207 		translate([   208 			wdir * width / 2,   209 			ddir * depth / 2,   210 			hdir * height / 2])   211 			child();   212 	}   213    214 	/*   215 	Make a cuboid of the given dimensions, justifying it according to the given   216 	factors, and moving it to the specified coordinates.   217    218 	NOTE: Usage of justify within this module will not work due to recursion   219 	NOTE: limitations in openscad, potentially removed in more recent   220 	NOTE: releases. Thus, the justify transform is merged in here.   221 	*/   222 	module cube_at(width, depth, height, wdir, ddir, hdir, x, y, z) {   223 		translate([   224 			x + wdir * width / 2,   225 			y + ddir * depth / 2,   226 			z + hdir * height / 2])   227 			cube([width, depth, height], center = true);   228 	}   229    230 	/* Model widths. */   231    232 	ROM_CARTRIDGE_int_payload_width = 86.0;   233 	WIDE_CARTRIDGE_int_payload_width = 140.0;   234    235 	/* Internal dimensions. */   236    237 	/* internal width in the payload section */   238 	int_payload_width =   239 		MODEL == ROM_CARTRIDGE ? ROM_CARTRIDGE_int_payload_width :   240 		MODEL == WIDE_CARTRIDGE ? WIDE_CARTRIDGE_int_payload_width :   241 			ROM_CARTRIDGE_int_payload_width;   242    243 	int_connector_width = 86.0;	/* limited to the Plus 1 socket dimensions */   244 	int_payload_depth = 14.0;	/* maximum depth in the payload section */   245 	int_connector_depth = 11.5;	/* maximum depth in the connector section */   246 	int_payload_height = 51.5;	/* space between the top and the floor */   247 	int_connector_height = 13.5; 	/* vertical offset of bottom/floor of payload area */   248    249 	/* Side thicknesses. */   250    251 	front = 2;   252 	payload_back = 1;		/* in the payload area the thickness is reduced */   253 	connector_back = 3.5;		/* the back cavity requires a thicker back wall */   254 	top = 3;   255 	side = 2; 			/* increased from 1.5 for 3D printing reliability */   256 	bottom = 1;			/* thickness of floor of payload area */   257    258 	/* How much extra depth the back provides for mating with the front. */   259    260 	groove_width_extra = 2.0;	/* the depth of the groove accommodating the front rim */   261 	front_back_overlap = 1.0;   262 	groove_width_overlap = front_back_overlap + groove_width_extra;   263    264 	/*   265 	Division of pieces into front and back, defining the extents. The back   266 	depth is extended by the front/back overlap, and thus the internal back   267 	depth calculated here is greater than the actual depth that the back   268 	portion can hold.   269 	*/   270    271 	int_front_depth = 4.5;   272 	front_depth = int_front_depth + front;   273 	int_payload_back_depth = int_payload_depth - int_front_depth + front_back_overlap;   274 	int_connector_back_depth = int_connector_depth - int_front_depth + front_back_overlap;   275 	back_depth = int_payload_back_depth + payload_back;   276    277 	/* Cartridge dimensions. */   278    279 	payload_width = int_payload_width + side + side;   280 	payload_height = top + int_payload_height;   281    282 	connector_width = int_connector_width + side + side;   283 	connector_height = bottom + int_connector_height;   284    285 	height = payload_height + bottom + int_connector_height;   286 	depth = int_payload_depth + front + payload_back;   287    288 	upper_extent = height / 2;   289 	lower_extent = -upper_extent;   290 	int_payload_upper_extent = upper_extent - top;   291 	int_payload_lower_extent = lower_extent + int_connector_height + bottom;   292    293 	/* Connector extents. */   294    295 	int_connector_right_extent = int_connector_width / 2;   296 	int_connector_left_extent = -int_connector_right_extent;   297 	connector_left_extent = int_connector_left_extent - side;   298 	connector_right_extent = int_connector_right_extent + side;   299    300 	/* Where the payload is wider than the connector, the payload expands to the left. */   301    302 	int_payload_right_extent = int_connector_right_extent;   303 	int_payload_left_extent = int_payload_right_extent - int_payload_width;   304 	payload_left_extent = int_payload_left_extent - side;   305 	payload_right_extent = int_payload_right_extent + side;   306 	payload_centre = (payload_left_extent + payload_right_extent) / 2;   307    308 	/* Cartridge surfaces. */   309    310 	front_side = side;   311 	front_left = front_side;   312 	front_right = front_side;   313 	back_side = side;   314 	back_left = back_side;   315 	back_right = back_side;   316    317 	/* Label details. */   318    319 	front_label_width = payload_width - side - side - 3.0;   320 	front_label_height = 46.0;   321 	front_label_depth = 1.0;   322 	front_label_offset_from_bottom = 19.5;   323 	front_label_centre = payload_centre;   324 	front_label_left_extent = front_label_centre - front_label_width / 2;   325    326 	top_label_width = front_label_width;   327 	top_label_height = 11.5; /* the height as seen from above */   328 	top_label_depth = front_label_depth;   329 	top_label_offset_from_front = 2.5;   330 	top_label_centre = payload_centre;   331 	top_label_left_extent = top_label_centre - front_label_width / 2;   332    333 	/*   334 	The groove around the sides and top. This is extended to allow the   335 	pieces to fit together, and this extension is generated regardless of   336 	whether the visible groove is enabled or not.   337 	*/   338    339 	groove_width_exposed = GROOVE ? 1.5 : 0;   340 	groove_width_normal = groove_width_exposed + front_back_overlap;   341 	groove_depth = 1.0; /* how deep the groove goes into each side */   342    343 	/* Additional cutting to mate the back and front. */   344    345 	top_groove_width = groove_width_overlap;   346 	top_groove_depth = 2.0;   347    348 	/*   349 	Space for the inner edge of the back inside the front.   350 	Offsets are measured from the outside surfaces.   351 	*/   352    353 	inner_top_front_cutout_width = int_payload_width;   354 	inner_top_front_cutout_depth = top_groove_width;   355 	inner_top_front_cutout_height = top - top_groove_depth;   356    357 	inner_payload_front_cutout_height = payload_height - top_groove_depth;   358 	inner_payload_front_cutout_width = front_side - groove_depth;   359 	inner_payload_front_cutout_depth = groove_width_overlap;   360    361 	inner_connector_front_cutout_height = connector_height;   362 	inner_connector_front_cutout_width = front_side - groove_depth;   363 	inner_connector_front_cutout_depth = groove_width_overlap;   364    365 	/*   366 	The back cavity is the indented part at the bottom of the back of the   367 	cartridge. It appears to be mechanically necessary, making sure that   368 	cartridges cannot be plugged in the wrong way round.   369 	*/   370    371 	back_cavity_width = 68.0;   372 	back_cavity_inner_width = 65.0;   373 	back_cavity_offset_from_inner_left = 9.0;   374 	back_cavity_inner_offset_from_inner_left = 10.5;   375 	back_cavity_height = 13.5;   376 	back_cavity_inner_height = 12.0;   377 	back_cavity_depth = 1.5;   378    379 	/*   380 	The effect of the cavity on the inside of the case. In principle, the   381 	interior of the case could be straight since the plastic guides of the   382 	Plus 1 socket are outside the case.   383 	*/   384    385 	inner_back_slope_depth = 2.5;   386 	inner_back_slope_width = inner_back_slope_depth;   387 	inner_back_slope_max_offset = 10.5;   388 	inner_back_slope_min_offset = inner_back_slope_max_offset - inner_back_slope_width;   389    390 	/* The tapering off of the inner back edge. */   391    392 	inner_back_edge_width = 69.0;   393 	inner_back_edge_height = 3.0;   394 	inner_back_edge_depth = 1.5;   395    396 	/* The tapering off of the inner front edge. */   397    398 	inner_front_edge_width = connector_width - front_side * 2;   399 	inner_front_edge_height = 3.0;   400 	inner_front_edge_depth = 1.5;   401    402 	/*   403 	The cutout in the floor of the back of the cartridge that accommodates   404 	the edge connector.   405 	*/   406    407 	edge_connector_cutout_back_depth = 3.0;   408 	edge_connector_cutout_back_width = 57.5;   409    410 	/*   411 	The cutouts in the floor of the front of the cartridge that produce a   412 	kind of tab that guides the edge connector into place in the back cutout.   413 	*/   414    415 	edge_connector_cutout_front_depth = front_back_overlap;   416 	edge_connector_cutout_front_width = (int_connector_width - edge_connector_cutout_back_width) / 2;   417    418 	/*   419 	Edge connectors are themselves 0.05" or approximately 1.27mm in   420 	thickness according to the Acorn Electron Cartridge Interface Specification   421 	(Acorn Support Application Group Note 014).   422 	*/   423    424 	/* Extra internal features. Depths include front and back surfaces. */   425    426 	pcb_back_support_width = 1.2;   427 	pcb_back_support_depth = back_depth -   428 		edge_connector_cutout_back_depth;   429 	pcb_back_support_height = height - int_connector_height - top - bottom;   430    431 	pcb_front_support_width = pcb_back_support_width;   432 	pcb_front_support_depth = int_front_depth;   433 	pcb_front_support_height = pcb_back_support_height;   434    435 	/*   436 	Features measured from the Stardot Dual ROM Adaptor cartridge board   437 	dimensions diagram. Offsets are from inside the bottom "floor".   438 	*/   439    440 	pcb_back_support_bump_width = pcb_front_support_width;   441 	pcb_back_support_bump_depth = 1.5;   442 	pcb_back_support_left_bump_height = 13.2;   443 	pcb_back_support_right_bump_height = 10.7;   444 	pcb_back_support_left_bump_offset_from_bottom = 15.1;   445 	pcb_back_support_right_bump_offset_from_bottom = 17.6;   446 	pcb_back_support_break_from_bottom = 35.3;	/* 36.45 from the above diagram */   447 	pcb_back_support_break_height = 13;		/* 11.55 from the above diagram */   448    449 	/* Configured by SHORT_PCB. */   450 	pcb_back_support_top_bump_height = int_payload_height   451 		- pcb_back_support_break_from_bottom - pcb_back_support_break_height;   452    453 	/* Move the PCB support towards the centre. */   454 	pcb_support_margin = 1.75;   455 	pcb_support_offset_from_centre = edge_connector_cutout_back_width / 2   456 		- pcb_support_margin;   457    458 	/*   459 	The PCB lugs protrude through the holes in the PCB. By extending both   460 	the front and back lugs by the depth of the back support bump, they   461 	overlap by that amount. The "mating" depth is defined as that amount   462 	plus an additional amount for adhesion.   463 	*/   464    465 	pcb_lug_mating_depth = pcb_back_support_bump_depth + 1.0;   466    467 	pcb_back_lug_depth = pcb_back_support_depth +   468 		pcb_lug_mating_depth;   469 	pcb_back_lug_inner_radius = 1.0;   470 	pcb_back_lug_outer_radius = BACK_PLUG_RADIUS;   471    472 	/*   473 	The cross detail on the back lug is intended to resist the front lug,   474 	and thus starts at the point that the front lug ends.   475 	*/   476    477 	pcb_lug_cross_width = 6.7;   478 	pcb_lug_cross_depth = pcb_back_support_depth + pcb_back_support_bump_depth -   479 		pcb_lug_mating_depth;   480 	pcb_lug_cross_height = 1.4;   481    482 	/*   483 	The front depth is calculated similarly to the back depth, with the   484 	outer radius of the socket corresponding to the dimensions of the cross   485 	detail. The inner radius of the front's socket is the same as the outer   486 	radius of the back's plug, with friction holding the two together.   487 	*/   488    489 	pcb_front_lug_depth = pcb_front_support_depth +   490 		pcb_lug_mating_depth;   491 	pcb_front_lug_inner_radius = pcb_back_lug_outer_radius;   492 	pcb_front_lug_outer_radius = pow(   493 		pow(pcb_lug_cross_width / 2, 2) +   494 		pow(pcb_lug_cross_height / 2, 2),   495 		0.5);   496    497 	/*   498 	With BACK_LUG as BACK_SOCKET, no overlap is desired, since both   499 	front and back provide sockets. The depth is accounted for as follows:   500    501 	int_payload_back_depth + payload_back	== back_depth   502 	+ int_front_depth - pcb_front_lug_depth	== back lug space in the front   503 	- front_back_overlap			== correction for overlap   504    505 	int_front_depth - pcb_front_lug_depth   506 	== pcb_front_support_depth - pcb_front_support_depth - pcb_lug_mating_depth   507 	== -pcb_lug_mating_depth   508    509 	Note that the lug starts at the outer back surface, which is why the   510 	payload_back quantity is included.   511 	*/   512    513 	pcb_back_socket_lug_depth = back_depth - front_back_overlap - pcb_lug_mating_depth;   514    515 	/* Both front and back lugs need to be positioned in the same place. */   516    517 	pcb_lug_offset_from_bottom = 14.35;   518 	pcb_lug_offset_from_inside = 5.55;   519    520 	wide_pcb_lug_offset_from_inside = 6.0;   521 	wide_pcb_lug_offset_from_bottom = 6.0;   522    523 	/* PCBs for checking. */   524     525 	pcb_width = 84.85; pcb_height = 62.5; pcb_depth = 1;   526 	edge_connector_width = 56.5; edge_connector_height = 11.85;   527    528 	/*   529 	The rectangular part of the narrow left and right holes is smaller   530 	than the "bump" in the case, but the circular parts make the overall   531 	hole larger than the "bump". An extra depth is used for holes to avoid   532 	surface definition problems.   533 	*/   534    535 	pcb_hole_margin = 0.55;   536 	pcb_hole_width = 2.2;   537 	pcb_hole_extra_depth = 0.1;   538 	pcb_lug_hole_radius = 3.75;   539    540 	pcb_left_hole_offset = pcb_back_support_left_bump_offset_from_bottom + pcb_hole_margin;   541 	pcb_left_hole_height = pcb_back_support_left_bump_height - pcb_hole_margin * 2;   542 	pcb_left_hole_offset_from_centre = -(pcb_support_offset_from_centre +   543 		pcb_back_support_width / 2 - pcb_hole_width / 2);   544    545 	pcb_right_hole_offset = pcb_back_support_right_bump_offset_from_bottom + pcb_hole_margin;   546 	pcb_right_hole_height = pcb_back_support_right_bump_height - pcb_hole_margin * 2;   547 	pcb_right_hole_offset_from_centre = -pcb_left_hole_offset_from_centre;   548    549 	pcb_hole_depth = pcb_depth + 2 * pcb_hole_extra_depth;   550 	pcb_hole_start_depth = edge_connector_cutout_back_depth + pcb_hole_extra_depth;   551    552 	wide_pcb_width = 138.0; wide_pcb_height = pcb_height; wide_pcb_depth = pcb_depth;   553 	wide_pcb_hole_depth = pcb_hole_depth;   554 	wide_pcb_hole_start_depth = pcb_hole_start_depth;   555    556 	wide_pcb_lug_hole_radius = 4.0;   557    558 	/* An example feature for use with PCB testing. */   559    560 	feature_width = 15.0; feature_depth = 7.0; feature_height = 40.0;   561 	feature_height_offset = 10.0;   562    563 	/* Repeated constructs. */   564    565 	module pcb_support(xdir, bump_height, bump_offset, break_offset, break_height) {   566    567 		/*   568 		Translate the support in the stated direction.   569 		Since the support is already justified in the direction, the translation   570 		moves the inner edge of the support to alignment with the end of the   571 		edge connector cutout and then back by the PCB support margin.   572 		*/   573    574 		translate([xdir *   575 			pcb_support_offset_from_centre,   576 			edge_connector_cutout_back_depth,   577 			int_payload_lower_extent]) {   578    579 			/*   580 			Convert the co-ordinates from being centred on the origin to being   581 			"justified" to have either positive or negative x values, and   582 			having positive y and z values.   583 			*/   584    585 			justify(pcb_back_support_width,   586 				pcb_back_support_depth,   587 				pcb_back_support_height,   588 				xdir, 1, 1) {   589    590 				union() {   591    592 					/* Underlying support strut. */   593    594 					difference() {   595 						cube([pcb_back_support_width,   596 							pcb_back_support_depth,   597 							pcb_back_support_height], center = true);   598 						cube_at(pcb_back_support_width,   599 							pcb_back_support_depth,   600 							break_height,   601 							0, 1, 1,   602 							0,   603 							-pcb_back_support_depth / 2,   604 							-pcb_back_support_height / 2 + break_offset);   605 					}   606    607 					/* Middle bump. */   608    609 					cube_at(pcb_back_support_bump_width,   610 						pcb_back_support_bump_depth,   611 						bump_height,   612 						0, -1, 1,   613 						0,   614 						-pcb_back_support_depth / 2,   615 						-pcb_back_support_height / 2 + bump_offset);   616    617 					/* Top bump. */   618    619 					if (SHORT_PCB)   620 						cube_at(pcb_back_support_bump_width,   621 							pcb_back_support_bump_depth,   622 							pcb_back_support_top_bump_height,   623 							0, -1, 1,   624 							0,   625 							-pcb_back_support_depth / 2,   626 							pcb_back_support_height / 2 -   627 								pcb_back_support_top_bump_height);   628 				}   629 			}   630 		}   631 	}   632    633 	/* Choose the kind of "lug" in the back piece. */   634    635 	module pcb_lug(xdir) {   636 		if (BACK_LUG == BACK_PLUG) {   637 			pcb_back_plug_lug(xdir);   638 		} else if (BACK_LUG == BACK_SOCKET) {   639 			pcb_back_socket_lug(xdir);   640 		}   641 	}   642    643 	module pcb_lug_wide(xdir) {   644 		if (BACK_LUG == BACK_PLUG) {   645 			pcb_back_plug_lug_wide(xdir);   646 		} else if (BACK_LUG == BACK_SOCKET) {   647 			pcb_back_socket_lug_wide(xdir);   648 		}   649 	}   650    651 	/* The conventional back "lug" which is actually a plug. */   652    653 	module pcb_back_plug_lug(xdir) {   654 		pcb_back_plug_lug_explicit(xdir, int_connector_width / 2 - pcb_lug_offset_from_inside,   655 			pcb_lug_offset_from_bottom);   656 	}   657    658 	module pcb_back_plug_lug_wide(xdir) {   659 		pcb_back_plug_lug_explicit(xdir, int_payload_width / 2 - wide_pcb_lug_offset_from_inside,   660 			wide_pcb_lug_offset_from_bottom);   661 		pcb_back_plug_lug_explicit(xdir, int_payload_width / 2 - wide_pcb_lug_offset_from_inside,   662 			wide_pcb_height - edge_connector_height - wide_pcb_lug_offset_from_bottom);   663 	}   664    665 	module pcb_back_plug_lug_explicit(xdir, offset_from_centre, offset_from_bottom) {   666 		translate([payload_centre + xdir * offset_from_centre,   667 			back_depth,   668 			int_payload_lower_extent + offset_from_bottom   669 			])   670 			rotate([90, 0, 0])   671 				difference() {   672    673 					/* Cylinder with cross. */   674    675 					union() {   676 						cylinder(h=pcb_back_lug_depth, r=pcb_back_lug_outer_radius);   677 						cube_at(pcb_lug_cross_width,   678 							pcb_lug_cross_height, pcb_lug_cross_depth,   679 							0, 0, 1,   680 							0, 0, 0);   681 						cube_at(pcb_lug_cross_height,   682 							pcb_lug_cross_width, pcb_lug_cross_depth,   683 							0, 0, 1,   684 							0, 0, 0);   685 					}   686    687 					/* Hollowed out by a cylinder. */   688    689 					cylinder(h=pcb_back_lug_depth, r=pcb_back_lug_inner_radius);   690    691 					/* Tapered off for easier connection. */   692    693 					if (ROUND_CONNECTING_EDGES)   694 						translate([0, 0, pcb_back_lug_depth - pcb_lug_ro])   695 							fillet_torus(pcb_back_lug_outer_radius, pcb_lug_rr);   696 				}   697 	}   698    699 	/* The conventional front "lug" which is actually a socket. */   700    701 	module pcb_front_lug(xdir) {   702 		pcb_socket_lug_explicit(xdir, int_connector_width / 2 - pcb_lug_offset_from_inside,   703 			-int_front_depth + pcb_front_lug_depth,   704 			pcb_lug_offset_from_bottom,   705 			pcb_front_lug_depth, pcb_front_lug_outer_radius, pcb_front_lug_inner_radius);   706 	}   707    708 	module pcb_front_lug_wide(xdir) {   709 		pcb_socket_lug_explicit(xdir, int_payload_width / 2 - wide_pcb_lug_offset_from_inside,   710 			-int_front_depth + pcb_front_lug_depth,   711 			wide_pcb_lug_offset_from_bottom,   712 			pcb_front_lug_depth, pcb_front_lug_outer_radius, pcb_front_lug_inner_radius);   713 		pcb_socket_lug_explicit(xdir, int_payload_width / 2 - wide_pcb_lug_offset_from_inside,   714 			-int_front_depth + pcb_front_lug_depth,   715 			wide_pcb_height - edge_connector_height - wide_pcb_lug_offset_from_bottom,   716 			pcb_front_lug_depth, pcb_front_lug_outer_radius, pcb_front_lug_inner_radius);   717 	}   718    719 	/*   720 	The alternative back "lug" which is actually a socket as well, meaning   721 	that a cylinder runs from the front surface to the back, with the two   722 	sections meeting in the middle.   723 	*/   724    725 	module pcb_back_socket_lug(xdir) {   726 		pcb_socket_lug_explicit(xdir, int_connector_width / 2 - pcb_lug_offset_from_inside,   727 			back_depth,   728 			pcb_lug_offset_from_bottom,   729 			pcb_back_socket_lug_depth, pcb_front_lug_outer_radius, pcb_front_lug_inner_radius);   730 	}   731    732 	module pcb_back_socket_lug_wide(xdir) {   733 		pcb_socket_lug_explicit(xdir, int_payload_width / 2 - wide_pcb_lug_offset_from_inside,   734 			back_depth,   735 			wide_pcb_lug_offset_from_bottom,   736 			pcb_back_socket_lug_depth, pcb_front_lug_outer_radius, pcb_front_lug_inner_radius);   737 		pcb_socket_lug_explicit(xdir, int_payload_width / 2 - wide_pcb_lug_offset_from_inside,   738 			back_depth,   739 			wide_pcb_height - edge_connector_height - wide_pcb_lug_offset_from_bottom,   740 			pcb_back_socket_lug_depth, pcb_front_lug_outer_radius, pcb_front_lug_inner_radius);   741 	}   742    743 	/* The common implementation of the cylinder socket. */   744    745 	module pcb_socket_lug_explicit(xdir, offset_from_centre, depth_offset, offset_from_bottom,   746 		lug_depth, lug_outer_radius, lug_inner_radius) {   747    748 		translate([payload_centre + xdir * offset_from_centre,   749 			depth_offset,   750 			int_payload_lower_extent + offset_from_bottom   751 			])   752 			rotate([90, 0, 0])   753 				difference() {   754 					cylinder(h=lug_depth,   755 						r=lug_outer_radius);   756 					cylinder(h=lug_depth,   757 						r=lug_inner_radius);   758 				}   759 	}   760    761 	/* The holes in the front surface when socket lugs are used. */   762    763 	module pcb_front_socket_hole(xdir) {   764 		pcb_socket_hole_explicit(xdir, int_connector_width / 2 - pcb_lug_offset_from_inside,   765 			-int_front_depth + extra,   766 			pcb_lug_offset_from_bottom,   767 			front + extra * 2, pcb_front_lug_inner_radius);   768 	}   769    770 	module pcb_front_socket_hole_wide(xdir) {   771 		pcb_socket_hole_explicit(xdir, int_payload_width / 2 - wide_pcb_lug_offset_from_inside,   772 			-int_front_depth + extra,   773 			wide_pcb_lug_offset_from_bottom,   774 			front + extra * 2, pcb_front_lug_inner_radius);   775 		pcb_socket_hole_explicit(xdir, int_payload_width / 2 - wide_pcb_lug_offset_from_inside,   776 			-int_front_depth + extra,   777 			wide_pcb_height - edge_connector_height - wide_pcb_lug_offset_from_bottom,   778 			front + extra * 2, pcb_front_lug_inner_radius);   779 	}   780    781 	/* The holes in the back surface when socket lugs are used. */   782    783 	module pcb_back_socket_hole(xdir) {   784 		pcb_socket_hole_explicit(xdir, int_connector_width / 2 - pcb_lug_offset_from_inside,   785 			back_depth + extra,   786 			pcb_lug_offset_from_bottom,   787 			payload_back + extra * 2, pcb_front_lug_inner_radius);   788 	}   789    790 	module pcb_back_socket_hole_wide(xdir) {   791 		pcb_socket_hole_explicit(xdir, int_payload_width / 2 - wide_pcb_lug_offset_from_inside,   792 			back_depth + extra,   793 			wide_pcb_lug_offset_from_bottom,   794 			payload_back + extra * 2, pcb_front_lug_inner_radius);   795 		pcb_socket_hole_explicit(xdir, int_payload_width / 2 - wide_pcb_lug_offset_from_inside,   796 			back_depth + extra,   797 			wide_pcb_height - edge_connector_height - wide_pcb_lug_offset_from_bottom,   798 			payload_back + extra * 2, pcb_front_lug_inner_radius);   799 	}   800    801 	/*   802 	The common implementation of the circular hole used when both pieces   803 	employ the socket lug.   804 	*/   805    806 	module pcb_socket_hole_explicit(xdir, offset_from_centre, depth_offset, offset_from_bottom,   807 		hole_depth, hole_radius) {   808    809 		translate([payload_centre + xdir * offset_from_centre,   810 			depth_offset,   811 			int_payload_lower_extent + offset_from_bottom   812 			])   813 			rotate([90, 0, 0])   814 				cylinder(h=hole_depth, r=hole_radius);   815 	}   816    817 	/* The actual shapes. */   818    819 	module front_piece(front_displacement, front_displacement_together) {   820 		if (FRONT)   821 			translate([front_displacement, front_displacement_together, 0])   822 			difference() {   823    824 				/* The cartridge surfaces. */   825    826 				union() {   827    828 					/* Front portion. */   829    830 					if (FRONT_SURFACE) {   831    832 						/* Surfaces surrounding the payload. */   833    834 						cube_at(payload_width, front, payload_height,   835 							0, -1, 1,   836 							payload_centre, -int_front_depth, int_payload_lower_extent);   837    838 						cube_at(front_left, front_depth, payload_height,   839 							-1, -1, 1,   840 							int_payload_left_extent, 0, int_payload_lower_extent);   841    842 						cube_at(front_right, front_depth, payload_height,   843 							1, -1, 1,   844 							int_payload_right_extent, 0, int_payload_lower_extent);   845    846 						/* Surfaces surrounding the connector. */   847    848 						cube_at(connector_width, front, connector_height,   849 							0, -1, -1,   850 							0, -int_front_depth, int_payload_lower_extent);   851    852 						cube_at(front_left, front_depth, connector_height,   853 							-1, -1, -1,   854 							int_connector_left_extent, 0, int_payload_lower_extent);   855    856 						cube_at(front_right, front_depth, connector_height,   857 							1, -1, -1,   858 							int_connector_right_extent, 0, int_payload_lower_extent);   859    860 						/* Top surface for the front piece. */   861    862 						if (TOP_SURFACE)   863 							cube_at(payload_width, front_depth, top,   864 								0, -1, 1,   865 								payload_centre, 0, int_payload_upper_extent);   866 					}   867    868 					difference() {   869    870 						/* Floor of cartridge. */   871    872 						if (payload_width > connector_width) {   873 							cube_at(payload_width, front_depth, bottom,   874 								1, -1, 1,   875 								payload_left_extent, 0, lower_extent + int_connector_height);   876 						} else {   877 							cube_at(int_connector_width, int_front_depth, bottom,   878 								0, -1, 1,   879 								0, 0, lower_extent + int_connector_height);   880 						}   881    882 						/* Left cutout. */   883    884 						cube_at(edge_connector_cutout_front_width,   885 							edge_connector_cutout_front_depth,   886 							bottom,   887 							1, -1, 1,   888 							int_connector_left_extent,   889 							0,   890 							lower_extent + int_connector_height);   891    892 						/* Right cutout. */   893    894 						cube_at(edge_connector_cutout_front_width,   895 							edge_connector_cutout_front_depth,   896 							bottom,   897 							-1, -1, 1,   898 							int_connector_right_extent,   899 							0,   900 							lower_extent + int_connector_height);   901 					}   902    903 					/* PCB supports. */   904    905 					if (MODEL == ROM_CARTRIDGE) {   906 						cube_at(pcb_front_support_width,   907 								pcb_front_support_depth,   908 								pcb_front_support_height,   909 								-1, -1, 1,   910 								-pcb_support_offset_from_centre,   911 								0,   912 								int_payload_lower_extent);   913    914 						cube_at(pcb_front_support_width,   915 								pcb_front_support_depth,   916 								pcb_front_support_height,   917 								1, -1, 1,   918 								pcb_support_offset_from_centre,   919 								0,   920 								int_payload_lower_extent);   921    922 						/* Circular "lugs" to hold PCBs in place. */   923    924 						pcb_front_lug(-1);   925 						pcb_front_lug(1);   926 					}   927    928 					if (MODEL == WIDE_CARTRIDGE) {   929    930 						pcb_front_lug_wide(-1);   931 						pcb_front_lug_wide(1);   932 					}   933 				}   934    935 				/* Label insets. */   936    937 				union() {   938    939 					/* Front label. */   940    941 					if (FRONT_LABEL_INSET)   942 						translate([front_label_left_extent, -front_depth,   943 							lower_extent + front_label_offset_from_bottom])   944 							cube([front_label_width, front_label_depth,   945 								front_label_height]);   946    947 					/* Top label. See also the back piece. */   948    949 					if (TOP_LABEL_INSET)   950 						translate([top_label_left_extent,   951 							-front_depth + top_label_offset_from_front,   952 							upper_extent - top_label_depth])   953 							cube([top_label_width, top_label_height,   954 								top_label_depth]);   955 				}   956    957 				/* Front socket holes depending on back lug choices. */   958    959 				if (BACK_LUG == BACK_SOCKET) {   960    961 					if (MODEL == ROM_CARTRIDGE) {   962 						pcb_front_socket_hole(-1);   963 						pcb_front_socket_hole(1);   964 					}   965    966 					if (MODEL == WIDE_CARTRIDGE) {   967 						pcb_front_socket_hole_wide(-1);   968 						pcb_front_socket_hole_wide(1);   969 					}   970 				}   971    972 				/* Inner front edge cavity. */   973    974 				translate([inner_front_edge_width / 2, -int_front_depth, lower_extent])   975 					rotate([0, -90, 0])   976 						linear_extrude(height = inner_front_edge_width)   977 							polygon([   978 								[-extra, -inner_front_edge_depth],   979 								[0, -inner_front_edge_depth],   980 								[inner_front_edge_height, 0],   981 								[-extra, 0],   982 								]);   983    984 				/* Inner grooves for the top and sides of the back portion. */   985    986 				cube_at(inner_top_front_cutout_width,   987 					inner_top_front_cutout_depth,   988 					inner_top_front_cutout_height,   989 					1, -1, 1,   990 					int_payload_left_extent, 0, int_payload_upper_extent);   991    992 				cube_at(inner_payload_front_cutout_width,   993 					inner_payload_front_cutout_depth,   994 					inner_payload_front_cutout_height,   995 					1, -1, 1,   996 					int_payload_right_extent, 0, int_payload_lower_extent);   997    998 				cube_at(inner_payload_front_cutout_width,   999 					inner_payload_front_cutout_depth,  1000 					inner_payload_front_cutout_height,  1001 					-1, -1, 1,  1002 					int_payload_left_extent, 0, int_payload_lower_extent);  1003   1004 				/* Cutout to accept the back connector sides (or the floor of the back piece). */  1005   1006 				cube_at(inner_connector_front_cutout_width,  1007 					BACK_CONNECTOR_SECTION ? inner_connector_front_cutout_depth : edge_connector_cutout_front_depth,  1008 					BACK_CONNECTOR_SECTION ? inner_connector_front_cutout_height : bottom,  1009 					1, -1, -1,  1010 					int_connector_right_extent, 0, int_payload_lower_extent);  1011   1012 				cube_at(inner_connector_front_cutout_width,  1013 					BACK_CONNECTOR_SECTION ? inner_connector_front_cutout_depth : edge_connector_cutout_front_depth,  1014 					BACK_CONNECTOR_SECTION ? inner_connector_front_cutout_height : bottom,  1015 					-1, -1, -1,  1016 					int_connector_left_extent, 0, int_payload_lower_extent);  1017   1018 				/* Fillets to round off the edges. */  1019   1020 				union() {  1021 					if (ROUND_EDGES) {  1022   1023 						/* Top left and right rounding. */  1024 					  1025 						translate([payload_left_extent + ro, -front_depth / 2, upper_extent - ro])  1026 							rotate([0, 0, 180])  1027 								rotate([90, 0, 0])  1028 									fillet(rr, front_depth);  1029 						translate([payload_right_extent - ro, -front_depth / 2, upper_extent - ro])  1030 							rotate([90, 0, 0])  1031 								fillet(rr, front_depth);  1032   1033 						/* Top front rounding. */  1034 					  1035 						translate([payload_centre, -front_depth + ro, upper_extent - ro])  1036 							rotate([0, 0, 180])  1037 								rotate([0, -90, 0])  1038 									fillet(rr, payload_width);  1039   1040 						/* Edge rounding. */  1041 					  1042 						translate([payload_right_extent - ro, -front_depth + ro, int_payload_lower_extent - bottom - extra])  1043 							rotate([0, 0, 270])  1044 								fillet_justified(rr, payload_height + bottom + extra);  1045   1046 						translate([payload_left_extent + ro, -front_depth + ro, int_payload_lower_extent - bottom - extra])  1047 							rotate([0, 0, 180])  1048 								fillet_justified(rr, payload_height + bottom + extra);  1049   1050 						translate([connector_right_extent - ro, -front_depth + ro, lower_extent])  1051 							rotate([0, 0, 270])  1052 								fillet_partitioned(rr, connector_height - bottom);  1053   1054 						translate([connector_left_extent + ro, -front_depth + ro, lower_extent])  1055 							rotate([0, 0, 180])  1056 								fillet_partitioned(rr, connector_height - bottom);  1057 					}  1058 				}  1059 			}  1060 	}  1061   1062 	module back_piece(back_displacement, back_rotation) {  1063 		if (BACK)  1064 			translate([back_displacement, 0, 0])  1065 			rotate([0, 0, back_rotation]) {  1066 				difference() {  1067   1068 					/* The cartridge surfaces. */  1069   1070 					union() {  1071   1072 						/* Back portion. */  1073   1074 						if (BACK_SURFACE) {  1075   1076 							/* Surfaces surrounding the payload. */  1077   1078 							/* Payload section of back surface. */  1079   1080 							cube_at(payload_width, payload_back, payload_height,  1081 								0, 1, 1,  1082 								payload_centre, int_payload_back_depth, int_payload_lower_extent);  1083   1084 							cube_at(back_left, back_depth, payload_height,  1085 								-1, 1, 1,  1086 								int_payload_left_extent, 0, int_payload_lower_extent);  1087   1088 							cube_at(back_right, back_depth, payload_height,  1089 								1, 1, 1,  1090 								int_payload_right_extent, 0, int_payload_lower_extent);  1091   1092 							/* Surfaces surrounding the connector. */  1093   1094 							/* Connector section of back surface overlapping the floor. */  1095   1096 							if (BACK_CONNECTOR_SECTION) {  1097 								cube_at(connector_width, connector_back, connector_height,  1098 									0, 1, -1,  1099 									0, int_connector_back_depth, int_payload_lower_extent);  1100   1101 								cube_at(back_left, back_depth, connector_height,  1102 									-1, 1, -1,  1103 									int_connector_left_extent, 0, int_payload_lower_extent);  1104   1105 								cube_at(back_right, back_depth, connector_height,  1106 									1, 1, -1,  1107 									int_connector_right_extent, 0, int_payload_lower_extent);  1108 							}  1109   1110 							/* Top of back piece. */  1111   1112 							if (TOP_SURFACE)  1113 								cube_at(payload_width, back_depth, top,  1114 									0, 1, 1,  1115 									payload_centre, 0, int_payload_upper_extent);  1116   1117 							/*  1118 							The extension of the back to connect with the front. Here, the inside  1119 							edges are rounded. The outside edges are rounded later.  1120   1121 							An extra overlapping measure is employed so that the filleting is  1122 							continuous between the payload and connector portions. On the inside  1123 							edges, the connector filleting is extended upwards.  1124 							*/  1125   1126 							/* Left side of payload. */  1127   1128 							difference() {  1129 								cube_at(back_left - groove_depth, groove_width_extra, inner_payload_front_cutout_height,  1130 									-1, 1, 1,  1131 									int_payload_left_extent, -groove_width_extra, int_payload_lower_extent);  1132 								if (ROUND_CONNECTING_EDGES)  1133 									translate([int_payload_left_extent - groove_ro, -groove_width_extra + groove_ro,  1134 										int_payload_lower_extent])  1135 										rotate([0, 0, -90])  1136 											fillet_justified(groove_rr, inner_payload_front_cutout_height);  1137 							}  1138   1139 							/* Right side of payload. */  1140   1141 							difference() {  1142 								cube_at(back_right - groove_depth, groove_width_extra, inner_payload_front_cutout_height,  1143 									1, 1, 1,  1144 									int_payload_right_extent, -groove_width_extra, int_payload_lower_extent);  1145 								if (ROUND_CONNECTING_EDGES)  1146 									translate([int_payload_right_extent + groove_ro, -groove_width_extra + groove_ro,  1147 										int_payload_lower_extent])  1148 										rotate([0, 0, 180])  1149 											fillet_justified(groove_rr, inner_payload_front_cutout_height);  1150 							}  1151   1152 							if (BACK_CONNECTOR_SECTION) {  1153   1154 								/* Left side of connector (or floor extension). */  1155   1156 								difference() {  1157 									cube_at(back_left - groove_depth, groove_width_extra,  1158 										inner_connector_front_cutout_height,  1159 										-1, 1, -1,  1160 										int_connector_left_extent, -groove_width_extra, int_payload_lower_extent);  1161 									if (ROUND_CONNECTING_EDGES)  1162 										translate([int_connector_left_extent - groove_ro,  1163 											-groove_width_extra + groove_ro,  1164 											lower_extent])  1165 											rotate([0, 0, -90])  1166 												fillet_partitioned(groove_rr,  1167 													inner_connector_front_cutout_height + extra);  1168 								}  1169   1170 								/* Right side of connector (or floor extension). */  1171   1172 								difference() {  1173 									cube_at(back_right - groove_depth, groove_width_extra,  1174 										inner_connector_front_cutout_height,  1175 										1, 1, -1,  1176 										int_connector_right_extent, -groove_width_extra, int_payload_lower_extent);  1177 									if (ROUND_CONNECTING_EDGES)  1178 										translate([int_connector_right_extent + groove_ro,  1179 											-groove_width_extra + groove_ro,  1180 											lower_extent])  1181 											rotate([0, 0, 180])  1182 												fillet_partitioned(groove_rr,  1183 													inner_connector_front_cutout_height + extra);  1184 								}  1185 							}  1186   1187 							/* Top side. */  1188   1189 							difference() {  1190 								cube_at(payload_width - groove_depth * 2, groove_width_extra, top - top_groove_depth,  1191 									0, 1, 1,  1192 									payload_centre, -groove_width_extra, int_payload_upper_extent);  1193 								if (ROUND_CONNECTING_EDGES)  1194 									translate([payload_centre, -groove_width_extra + groove_ro,  1195 										int_payload_upper_extent + groove_ro])  1196 										rotate([0, 0, 180])  1197 											rotate([0, 90, 0])  1198 												fillet(groove_rr, payload_width - groove_depth * 2);  1199 							}  1200 						}  1201   1202 						difference() {  1203   1204 							/* Floor of cartridge. */  1205   1206 							if (payload_width > connector_width) {  1207   1208 								difference() {  1209 									cube_at(payload_width, back_depth, bottom,  1210 										1, 1, 1,  1211 										payload_left_extent, 0, lower_extent + int_connector_height);  1212   1213 									/* Cut out the floor to provide a tab. */  1214   1215 									cube_at(payload_width - connector_width + groove_depth,  1216 										edge_connector_cutout_front_depth, bottom,  1217 										1, 1, 1,  1218 										payload_left_extent, 0, lower_extent + int_connector_height);  1219 								}  1220   1221 							} else {  1222 								cube_at(connector_width, back_depth, bottom,  1223 									0, 1, 1,  1224 									0, 0, lower_extent + int_connector_height);  1225 							}  1226   1227 							/* Edge connector cutout. */  1228   1229 							cube_at(edge_connector_cutout_back_width,  1230 								edge_connector_cutout_back_depth,  1231 								bottom,  1232 								0, 1, 1,  1233 								0, 0, lower_extent + int_connector_height);  1234 						}  1235   1236 						/* PCB supports. */  1237   1238 						if (MODEL == ROM_CARTRIDGE) {  1239 							pcb_support(-1, pcb_back_support_left_bump_height,  1240 								pcb_back_support_left_bump_offset_from_bottom,  1241 								pcb_back_support_break_from_bottom,  1242 								pcb_back_support_break_height);  1243 							pcb_support(1, pcb_back_support_right_bump_height,  1244 								pcb_back_support_right_bump_offset_from_bottom,  1245 								pcb_back_support_break_from_bottom,  1246 								pcb_back_support_break_height);  1247   1248 							/* Circular "lugs" to hold PCBs in place. */  1249   1250 							pcb_lug(-1);  1251 							pcb_lug(1);  1252 						}  1253   1254 						if (MODEL == WIDE_CARTRIDGE) {  1255   1256 							pcb_lug_wide(1);  1257 							pcb_lug_wide(-1);  1258 						}  1259 					}  1260   1261 					/* Label insets. */  1262   1263 					union() {  1264   1265 						/* Top label. See also the front piece. */  1266   1267 						if (TOP_LABEL_INSET)  1268 							translate([top_label_left_extent,  1269 								-front_depth + top_label_offset_from_front,  1270 								upper_extent - top_label_depth])  1271 								cube([top_label_width, top_label_height,  1272 									top_label_depth]);  1273 					}  1274   1275 					/* Top and side grooves, positioned in the back portion. */  1276   1277 					union() {  1278   1279 						/*  1280 						Grooves are separated to permit a discontinuity with the  1281 						wide cartridge and for control over the back section.  1282 						*/  1283   1284 						cube_at(groove_depth, groove_width_normal, payload_height + bottom,  1285 							1, 1, -1,  1286 							payload_left_extent, 0, upper_extent);  1287   1288 						if (BACK_CONNECTOR_SECTION)  1289 							cube_at(groove_depth, groove_width_normal, connector_height,  1290 								1, 1, 1,  1291 								connector_left_extent, 0, lower_extent);  1292   1293 						/* Right groove. */  1294   1295 						cube_at(groove_depth, groove_width_normal, height,  1296 							-1, 1, 0,  1297 							payload_right_extent, 0, 0);  1298   1299 						if (BACK_CONNECTOR_SECTION)  1300 							cube_at(groove_depth, groove_width_normal, connector_height,  1301 								-1, 1, 1,  1302 								connector_right_extent, 0, lower_extent);  1303   1304 						/* Top grooves. */  1305   1306 						cube_at(payload_width, groove_width_normal, groove_depth,  1307 							0, 1, -1,  1308 							payload_centre, 0, upper_extent);  1309   1310 						cube_at(payload_width, top_groove_width, top_groove_depth,  1311 							0, 1, -1,  1312 							payload_centre, -groove_width_extra, upper_extent);  1313 					}  1314   1315 					/* Back socket holes. */  1316   1317 					if ((BACK_LUG == BACK_SOCKET) && (BACK_SOCKET_HOLE == 1)) {  1318   1319 						if (MODEL == ROM_CARTRIDGE) {  1320 							pcb_back_socket_hole(-1);  1321 							pcb_back_socket_hole(1);  1322 						}  1323   1324 						if (MODEL == WIDE_CARTRIDGE) {  1325 							pcb_back_socket_hole_wide(-1);  1326 							pcb_back_socket_hole_wide(1);  1327 						}  1328 					}  1329   1330 					/* The connector section at the base of the back. */  1331   1332 					if (BACK_CONNECTOR_SECTION) {  1333   1334 						/* Provide an external cavity. */  1335   1336 						if (EXTERNAL_BACK_CAVITY)  1337 							intersection() {  1338   1339 								/* From the bottom upwards. */  1340   1341 								translate([0, back_depth, lower_extent])  1342 									linear_extrude(height = back_cavity_height)  1343 										translate([int_connector_left_extent, 0, 0])  1344 											polygon([  1345 												[back_cavity_offset_from_inner_left, 0],  1346 												[back_cavity_inner_offset_from_inner_left,  1347 													-back_cavity_depth],  1348 												[back_cavity_inner_offset_from_inner_left +  1349 													back_cavity_inner_width,  1350 													-back_cavity_depth],  1351 												[back_cavity_offset_from_inner_left +  1352 													back_cavity_width, 0]  1353 												]);  1354   1355 								/* From left to right. */  1356   1357 								translate([back_cavity_width / 2, back_depth, lower_extent])  1358 									rotate([0, -90, 0])  1359 										linear_extrude(height = back_cavity_width)  1360 											polygon([  1361 												[-extra, -back_cavity_depth],  1362 												[back_cavity_inner_height,  1363 													-back_cavity_depth],  1364 												[back_cavity_height, 0],  1365 												[-extra, 0]  1366 												]);  1367 							}  1368   1369 						/* Or remove the back section entirely. */  1370   1371 						else if (EXTERNAL_BACK_CUTOUT)  1372 							translate([0, back_depth, lower_extent])  1373 								linear_extrude(height = back_cavity_height)  1374 									translate([int_connector_left_extent, 0, 0])  1375 										polygon([  1376 											[back_cavity_offset_from_inner_left, 0],  1377 											[inner_back_slope_min_offset,  1378 												-connector_back + inner_back_slope_depth],  1379 											[inner_back_slope_min_offset, -connector_back],  1380 											[int_connector_width - inner_back_slope_min_offset,  1381 												-connector_back],  1382 											[int_connector_width - inner_back_slope_min_offset,  1383 												-connector_back + inner_back_slope_depth],  1384 											[back_cavity_offset_from_inner_left +  1385 												back_cavity_width, 0]  1386 											]);  1387   1388 						/* Inner back cavities making the back thinner at the sides. */  1389   1390 						translate([0, int_connector_back_depth, lower_extent])  1391 							linear_extrude(height = int_connector_height)  1392 								translate([int_connector_left_extent, 0, 0])  1393 									polygon([  1394 										[0, 0],  1395 										[inner_back_slope_max_offset, 0],  1396 										[inner_back_slope_min_offset,  1397 											inner_back_slope_depth],  1398 										[0, inner_back_slope_depth]  1399 										]);  1400   1401 						translate([0, int_connector_back_depth, lower_extent])  1402 							linear_extrude(height = int_connector_height)  1403 								translate([int_connector_right_extent, 0, 0])  1404 									polygon([  1405 										[0, 0],  1406 										[-inner_back_slope_max_offset, 0],  1407 										[-inner_back_slope_min_offset,  1408 											inner_back_slope_depth],  1409 										[0, inner_back_slope_depth]  1410 										]);  1411   1412 						/* Inner back edge cavity, tapering the bottom edge. */  1413   1414 						translate([inner_back_edge_width / 2,  1415 							int_connector_back_depth + inner_back_edge_depth, lower_extent])  1416 							rotate([0, -90, 0])  1417 								linear_extrude(height = inner_back_edge_width)  1418 									polygon([  1419 										[-extra, -inner_back_edge_depth],  1420 										[inner_back_edge_height, -inner_back_edge_depth],  1421 										[0, 0],  1422 										[-extra, 0]  1423 										]);  1424 					}  1425   1426 					/* Fillets to round off the edges. */  1427   1428 					union() {  1429 						if (ROUND_EDGES) {  1430   1431 							/* Top left and right rounding. */  1432 						  1433 							translate([payload_left_extent + ro, back_depth / 2, upper_extent - ro])  1434 								rotate([0, 0, 180])  1435 									rotate([90, 0, 0])  1436 										fillet(rr, back_depth);  1437 							translate([payload_right_extent - ro, back_depth / 2, upper_extent - ro])  1438 								rotate([90, 0, 0])  1439 									fillet(rr, back_depth);  1440 						  1441 							/* Top back rounding. */  1442 						  1443 							translate([payload_centre, back_depth - ro, upper_extent - ro])  1444 								rotate([0, -90, 0])  1445 									fillet(rr, payload_width);  1446   1447 							/* Outer edge rounding. */  1448 						  1449 							translate([payload_right_extent - ro, back_depth - ro, int_payload_lower_extent - bottom - extra])  1450 								fillet_justified(rr, payload_height + bottom + extra);  1451   1452 							translate([payload_left_extent + ro, back_depth - ro, int_payload_lower_extent - bottom - extra])  1453 								rotate([0, 0, 90])  1454 									fillet_justified(rr, payload_height + bottom + extra);  1455   1456 							if (BACK_CONNECTOR_SECTION) {  1457 								translate([connector_right_extent - ro, back_depth - ro, lower_extent])  1458 									fillet_partitioned(rr, connector_height - bottom);  1459   1460 								translate([connector_left_extent + ro, back_depth - ro, lower_extent])  1461 									rotate([0, 0, 90])  1462 										fillet_partitioned(rr, connector_height - bottom);  1463 							}  1464 						}  1465   1466 						if (ROUND_CONNECTING_EDGES) {  1467   1468 							/*  1469 							Outer edge rounding of the back extension. This is done as a  1470 							separate removal operation rather than occurring when creating the  1471 							extension in order to ensure that the edges are actually rounded.  1472   1473 							An extra overlapping measure is employed so that the filleting is  1474 							continuous between the payload and connector portions. On the outside  1475 							edges, the payload filleting is extended downwards.  1476 							*/  1477   1478 							translate([payload_left_extent + groove_depth + groove_ro, -groove_width_extra + groove_ro,  1479 								int_payload_lower_extent - extra])  1480 								rotate([0, 0, 180])  1481 									fillet_justified(groove_rr, inner_payload_front_cutout_height + extra);  1482   1483 							translate([payload_right_extent - groove_depth - groove_ro, -groove_width_extra + groove_ro,  1484 								int_payload_lower_extent - extra])  1485 								rotate([0, 0, -90])  1486 									fillet_justified(groove_rr, inner_payload_front_cutout_height + extra);  1487   1488 							/* Sides of connector. */  1489   1490 							translate([connector_left_extent + groove_depth + groove_ro, -groove_width_extra + groove_ro,  1491 								lower_extent])  1492 								rotate([0, 0, 180])  1493 									fillet_partitioned(groove_rr, inner_connector_front_cutout_height);  1494   1495 							translate([connector_right_extent - groove_depth - groove_ro, -groove_width_extra + groove_ro,  1496 								lower_extent])  1497 								rotate([0, 0, -90])  1498 									fillet_partitioned(groove_rr, inner_connector_front_cutout_height);  1499   1500 							/* Top of payload. */  1501   1502 							translate([payload_centre, -groove_width_extra + groove_ro,  1503 								int_payload_upper_extent + inner_top_front_cutout_height - groove_ro])  1504 								rotate([0, 0, 180])  1505 									rotate([0, -90, 0])  1506 										fillet(groove_rr, payload_width - groove_depth * 2);  1507 						}  1508 					}  1509 				}  1510   1511 				/* PCBs for checking. */  1512 			   1513 				if (PCB) if (MODEL == ROM_CARTRIDGE) {  1514 					translate([0, 0, int_payload_lower_extent])  1515 						difference() {  1516   1517 							/* Mock PCB. */  1518   1519 							union() {  1520 								cube_at(pcb_width, pcb_depth,  1521 									pcb_height - edge_connector_height,  1522 									0, -1, 1,  1523 									0, edge_connector_cutout_back_depth, 0);  1524 								cube_at(edge_connector_width, pcb_depth,  1525 									edge_connector_height,  1526 									0, -1, -1,  1527 									0, edge_connector_cutout_back_depth, 0);  1528   1529 								/* Feature. */  1530   1531 								cube_at(feature_width, feature_depth, feature_height,  1532 									0, 1, 1,  1533 									0, edge_connector_cutout_back_depth, feature_height_offset);  1534 							}  1535   1536 							/* Holes for mounting. */  1537   1538 							union() {  1539 								cube_at(pcb_hole_width, pcb_hole_depth, pcb_left_hole_height,  1540 									-1, -1, 1,  1541 									pcb_left_hole_offset_from_centre, pcb_hole_start_depth,  1542 										pcb_left_hole_offset);  1543 								translate([pcb_left_hole_offset_from_centre -  1544 									pcb_hole_width / 2, pcb_hole_start_depth, pcb_left_hole_offset])  1545 									rotate([90, 0, 0])  1546 										cylinder(h=pcb_hole_depth, r=pcb_hole_width / 2);  1547 								translate([pcb_left_hole_offset_from_centre -  1548 									pcb_hole_width / 2, pcb_hole_start_depth,  1549 									pcb_left_hole_offset + pcb_left_hole_height])  1550 									rotate([90, 0, 0])  1551 										cylinder(h=pcb_hole_depth, r=pcb_hole_width / 2);  1552 							}  1553   1554 							union() {  1555 								cube_at(pcb_hole_width, pcb_hole_depth, pcb_right_hole_height,  1556 									1, -1, 1,  1557 									pcb_right_hole_offset_from_centre, pcb_hole_start_depth,  1558 										pcb_right_hole_offset);  1559 								translate([pcb_right_hole_offset_from_centre +  1560 									pcb_hole_width / 2, pcb_hole_start_depth, pcb_right_hole_offset])  1561 									rotate([90, 0, 0])  1562 										cylinder(h=pcb_hole_depth, r=pcb_hole_width / 2);  1563 								translate([pcb_right_hole_offset_from_centre +  1564 									pcb_hole_width / 2, pcb_hole_start_depth,  1565 									pcb_right_hole_offset + pcb_right_hole_height])  1566 									rotate([90, 0, 0])  1567 										cylinder(h=pcb_hole_depth, r=pcb_hole_width / 2);  1568 							}  1569   1570 							/* Holes for lugs. */  1571   1572 							translate([  1573 								int_connector_left_extent + pcb_lug_offset_from_inside,  1574 								pcb_hole_start_depth, pcb_lug_offset_from_bottom])  1575 								rotate([90, 0, 0])  1576 									cylinder(h=pcb_hole_depth, r=pcb_lug_hole_radius);  1577   1578 							translate([  1579 								int_connector_right_extent - pcb_lug_offset_from_inside,  1580 								pcb_hole_start_depth, pcb_lug_offset_from_bottom])  1581 								rotate([90, 0, 0])  1582 									cylinder(h=pcb_hole_depth, r=pcb_lug_hole_radius);  1583 						}  1584 				}  1585   1586 				if (PCB) if (MODEL == WIDE_CARTRIDGE) {  1587 					translate([0, 0, int_payload_lower_extent])  1588 						difference() {  1589   1590 							/* Mock PCB. */  1591   1592 							union() {  1593 								cube_at(wide_pcb_width, wide_pcb_depth,  1594 									wide_pcb_height - edge_connector_height,  1595 									0, -1, 1,  1596 									payload_centre, edge_connector_cutout_back_depth, 0);  1597 								cube_at(edge_connector_width, wide_pcb_depth,  1598 									edge_connector_height,  1599 									0, -1, -1,  1600 									0, edge_connector_cutout_back_depth, 0);  1601   1602 								/* Feature. */  1603   1604 								cube_at(feature_width, feature_depth, feature_height,  1605 									0, 1, 1,  1606 									0, edge_connector_cutout_back_depth, feature_height_offset);  1607 							}  1608   1609 							/* Holes for lugs. */  1610   1611 							translate([  1612 								payload_centre + int_payload_width / 2 - wide_pcb_lug_offset_from_inside ,  1613 								wide_pcb_hole_start_depth,  1614 								wide_pcb_height - edge_connector_height - wide_pcb_lug_offset_from_bottom])  1615 								rotate([90, 0, 0])  1616 									cylinder(h=wide_pcb_hole_depth, r=wide_pcb_lug_hole_radius);  1617   1618 							translate([  1619 								payload_centre - int_payload_width / 2 + wide_pcb_lug_offset_from_inside ,  1620 								wide_pcb_hole_start_depth,  1621 								wide_pcb_height - edge_connector_height - wide_pcb_lug_offset_from_bottom])  1622 								rotate([90, 0, 0])  1623 									cylinder(h=wide_pcb_hole_depth, r=wide_pcb_lug_hole_radius);  1624   1625 							translate([  1626 								payload_centre + int_payload_width / 2 - wide_pcb_lug_offset_from_inside ,  1627 								wide_pcb_hole_start_depth,  1628 								wide_pcb_lug_offset_from_bottom])  1629 								rotate([90, 0, 0])  1630 									cylinder(h=wide_pcb_hole_depth, r=wide_pcb_lug_hole_radius);  1631   1632 							translate([  1633 								payload_centre - int_payload_width / 2 + wide_pcb_lug_offset_from_inside ,  1634 								wide_pcb_hole_start_depth,  1635 								wide_pcb_lug_offset_from_bottom])  1636 								rotate([90, 0, 0])  1637 									cylinder(h=wide_pcb_hole_depth, r=wide_pcb_lug_hole_radius);  1638 						}  1639 				}  1640 			}  1641 	}  1642   1643 	/*  1644 	Place the back piece next to the front, with the internals facing out  1645 	the same way, if APART is defined.  1646 	*/  1647   1648 	front_displacement_together = CLOSED ? front_back_overlap : APART ? 0 : front_back_overlap - SEPARATION;  1649 	front_displacement = APART ? -connector_width * 0.6 : 0;  1650 	back_displacement = APART ? connector_width * 0.6 : 0;  1651 	back_rotation = APART ? 180 : 0;  1652   1653 	if (INTERSECT) {  1654 		intersection() {  1655 			front_piece(0, front_displacement_together);  1656 			back_piece(0, 0);  1657 		}  1658 	} else {  1659 		front_piece(front_displacement, front_displacement_together);  1660 		back_piece(back_displacement, back_rotation);  1661 	}  1662 }  1663   1664 cartridge();