From 7aeb9cf38658ba5624ee14ff7ed2a617df2cf88e Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 27 Dec 2025 12:22:16 -0500 Subject: [PATCH 1/3] fix: use googleapis/release-please-action --- .github/workflows/release-please.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 106983f..5f5fc8a 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -21,9 +21,8 @@ jobs: version: ${{ steps.release.outputs.version }} steps: - - uses: google-github-actions/release-please-action@v4 + - uses: googleapis/release-please-action@v4 id: release with: release-type: python - package-name: microfinity target-branch: releases From 0a5eadbb376a4ff8e0473cc49fdbf307ad2f9535 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 27 Dec 2025 12:24:49 -0500 Subject: [PATCH 2/3] style: format code with black --- microfinity/constants.py | 2 + microfinity/gf_baseplate.py | 14 +-- microfinity/gf_box.py | 117 +++++++--------------- microfinity/gf_drawer.py | 84 +++------------- microfinity/gf_helpers.py | 4 +- microfinity/gf_obj.py | 83 ++++----------- microfinity/gf_ruggedbox.py | 114 ++++----------------- microfinity/scripts/gridfinitybase.py | 8 +- microfinity/scripts/gridfinitybox.py | 12 +-- microfinity/scripts/ruggedbox.py | 22 +--- microfinity/shims/cqgi_gf_baseplate.py | 31 +++--- microfinity/shims/cqgi_gf_box.py | 51 +++++----- microfinity/shims/cqgi_gf_drawerspacer.py | 45 +++++---- microfinity/shims/cqgi_gf_ruggedbox.py | 51 +++++----- tests/test_box.py | 36 ++----- tests/test_microgrid.py | 94 ++++++++--------- tests/test_rbox.py | 20 +--- tests/test_spacer.py | 12 +-- 18 files changed, 266 insertions(+), 534 deletions(-) diff --git a/microfinity/constants.py b/microfinity/constants.py index 2319d28..7d2f820 100644 --- a/microfinity/constants.py +++ b/microfinity/constants.py @@ -38,6 +38,7 @@ GRU2 = GRU / 2 GRHU = 7 + # Micro-grid support: quarter-pitch positioning (0.25U = 10.5mm) # micro_pitch is derived at runtime based on micro_divisions parameter # Default micro_divisions=1 means standard behavior, micro_divisions=4 means quarter-grid @@ -45,6 +46,7 @@ def micro_pitch(micro_divisions=4): """Returns the micro-pitch for a given division factor.""" return GRU / micro_divisions + GRU_CUT = 42.2 # base extrusion width GR_WALL = 1.0 # nominal exterior wall thickness GR_DIV_WALL = 1.2 # width of dividing walls diff --git a/microfinity/gf_baseplate.py b/microfinity/gf_baseplate.py index b769b45..e0a841d 100644 --- a/microfinity/gf_baseplate.py +++ b/microfinity/gf_baseplate.py @@ -72,19 +72,13 @@ def __init__(self, length_u, width_u, **kwargs): def _corner_pts(self): oxy = self.corner_tab_size / 2 - return [ - (i * (self.length / 2 - oxy), j * (self.width / 2 - oxy), 0) - for i in (-1, 1) - for j in (-1, 1) - ] + return [(i * (self.length / 2 - oxy), j * (self.width / 2 - oxy), 0) for i in (-1, 1) for j in (-1, 1)] def render(self): profile = GR_BASE_PROFILE if not self.straight_bottom else GR_STR_BASE_PROFILE if self.ext_depth > 0: profile = [*profile, self.ext_depth] - rc = self.extrude_profile( - rounded_rect_sketch(GRU_CUT, GRU_CUT, GR_RAD), profile - ) + rc = self.extrude_profile(rounded_rect_sketch(GRU_CUT, GRU_CUT, GR_RAD), profile) rc = rotate_x(rc, 180).translate((GRU2, GRU2, GR_BASE_HEIGHT + self.ext_depth)) rc = recentre(composite_from_pts(rc, self.grid_centres), "XY") r = ( @@ -99,9 +93,7 @@ def render(self): if self.corner_screws: rs = cq.Sketch().rect(self.corner_tab_size, self.corner_tab_size) rs = cq.Workplane("XY").placeSketch(rs).extrude(self.ext_depth) - rs = rs.faces(">Z").cskHole( - self.csk_hole, cskDiameter=self.csk_diam, cskAngle=self.csk_angle - ) + rs = rs.faces(">Z").cskHole(self.csk_hole, cskDiameter=self.csk_diam, cskAngle=self.csk_angle) r = r.union(recentre(composite_from_pts(rs, self._corner_pts()), "XY")) bs = VerticalEdgeSelector(self.ext_depth) & HasZCoordinateSelector(0) r = r.edges(bs).fillet(GR_RAD) diff --git a/microfinity/gf_box.py b/microfinity/gf_box.py index d46f89e..b012292 100644 --- a/microfinity/gf_box.py +++ b/microfinity/gf_box.py @@ -62,7 +62,7 @@ class GridfinityBox(GridfinityObject): - scoop_rad : radius of the bottom scoop feature - wall_th : wall thickness - hole_diam : magnet/counterbore bolt hole diameter - + Micro-grid support (quarter-pitch positioning): - micro_divisions : int (1, 2, or 4) - enables sub-grid positioning - 1 = standard Gridfinity (default, no micro-boundaries) @@ -98,16 +98,11 @@ def __init__(self, length_u, width_u, height_u, **kwargs): self.hole_diam = GR_HOLE_D # magnet/bolt hole diameter self._int_shell = None self._ext_shell = None - + # Call parent init with dimensions and kwargs for validation # The parent will handle micro_divisions validation - super().__init__( - length_u=length_u, - width_u=width_u, - height_u=height_u, - **kwargs - ) - + super().__init__(length_u=length_u, width_u=width_u, height_u=height_u, **kwargs) + # Apply any remaining kwargs that are box-specific for k, v in kwargs.items(): if k in self.__dict__: @@ -143,37 +138,27 @@ def __str__(self): s.append(" Lengthwise scoops with %.2f mm radius" % (self.scoop_rad)) if self.labels: s.append( - " Lengthwise label shelf %.2f mm wide with %.2f mm overhang" - % (self.label_width, self.label_height) + " Lengthwise label shelf %.2f mm wide with %.2f mm overhang" % (self.label_width, self.label_height) ) if self.length_div: - xl = (self.inner_l - GR_DIV_WALL * (self.length_div)) / ( - self.length_div + 1 - ) - s.append( - " %dx lengthwise divisions for %.2f mm compartment lengths" - % (self.length_div, xl) - ) + xl = (self.inner_l - GR_DIV_WALL * (self.length_div)) / (self.length_div + 1) + s.append(" %dx lengthwise divisions for %.2f mm compartment lengths" % (self.length_div, xl)) if self.width_div: yl = (self.inner_w - GR_DIV_WALL * (self.width_div)) / (self.width_div + 1) - s.append( - " %dx widthwise divisions for %.2f mm compartment widths" - % (self.width_div, yl) - ) + s.append(" %dx widthwise divisions for %.2f mm compartment widths" % (self.width_div, yl)) s.append(" Auto filename: %s" % (self.filename())) return "\n".join(s) def render(self): """Returns a CadQuery Workplane object representing this Gridfinity box.""" self._int_shell = None - + # Validate lite_style + micro_divisions incompatibility if self.lite_style and self.micro_divisions > 1: raise ValueError( - "lite_style is not supported with micro_divisions > 1. " - "Use standard box style for micro-grid bins." + "lite_style is not supported with micro_divisions > 1. " "Use standard box style for micro-grid bins." ) - + if self.lite_style: # just force the dividers to the desired quantity in both dimensions # rather than raise a exception @@ -182,17 +167,11 @@ def render(self): if self.width_div: self.width_div = self.width_u - 1 if self.solid: - raise ValueError( - "Cannot select both solid and lite box styles together" - ) + raise ValueError("Cannot select both solid and lite box styles together") if self.holes: - raise ValueError( - "Cannot select both holes and lite box styles together" - ) + raise ValueError("Cannot select both holes and lite box styles together") if self.wall_th > 1.5: - raise ValueError( - "Wall thickness cannot exceed 1.5 mm for lite box style" - ) + raise ValueError("Wall thickness cannot exceed 1.5 mm for lite box style") if self.wall_th > 2.5: raise ValueError("Wall thickness cannot exceed 2.5 mm") if self.wall_th < 0.5: @@ -227,9 +206,9 @@ def render(self): r = self.safe_fillet(r, bs, 0.25) if not self.labels and self.has_dividers: - bs = VerticalEdgeSelector( - GR_TOPSIDE_H, tolerance=0.05 - ) & HasZCoordinateSelector(GRHU * self.height_u - GR_BASE_HEIGHT) + bs = VerticalEdgeSelector(GR_TOPSIDE_H, tolerance=0.05) & HasZCoordinateSelector( + GRHU * self.height_u - GR_BASE_HEIGHT + ) r = self.safe_fillet(r, bs, GR_TOPSIDE_H - EPS) if self.holes: @@ -293,9 +272,7 @@ def render_interior(self, force_solid=False): profile = [wall_h, *profile] if self.int_height < 0: profile = [self.height - GR_BOT_H] - rci = self.extrude_profile( - rounded_rect_sketch(*self.inner_dim, self.inner_rad), profile - ) + rci = self.extrude_profile(rounded_rect_sketch(*self.inner_dim, self.inner_rad), profile) rci = rci.translate((*self.half_dim, self.floor_h)) if self.solid or force_solid: hs = self.max_height * self.solid_ratio @@ -345,29 +322,26 @@ def base_interior(self): def micro_foot(self): """Creates a single micro-sized foot using GR_BOX_PROFILE. - + The foot size is (micro_pitch - GR_TOL) to create proper gaps between adjacent feet, forming the correct divider ridge profile. This is the same mechanism used at macro scale. """ foot_size = self.micro_pitch - GR_TOL # 10.0mm for micro_divisions=4 - + # Clamp corner radius to feasible range # Must be <= half foot size to avoid self-intersection rad = min(self.outer_rad + GR_BASE_CLR, foot_size / 2 - 0.05) rad = max(rad, 0.2) # Minimum radius to avoid degenerate geometry - - r = self.extrude_profile( - rounded_rect_sketch(foot_size, foot_size, rad), - GR_BOX_PROFILE - ) + + r = self.extrude_profile(rounded_rect_sketch(foot_size, foot_size, rad), GR_BOX_PROFILE) r = r.translate((0, 0, -GR_BASE_CLR)) r = r.mirror(mirrorPlane="XY") return r def render_shell(self, as_solid=False): """Renders the box shell without any added features. - + For micro_divisions > 1, uses micro-foot replication at micro_pitch intervals instead of macro feet. The gaps between micro feet naturally create the correct divider ridge profile (same mechanism as macro mode). @@ -379,16 +353,14 @@ def render_shell(self, as_solid=False): centres = self.micro_grid_centres else: # Macro mode: standard 42mm feet - foot = self.extrude_profile( - rounded_rect_sketch(GRU, GRU, self.outer_rad + GR_BASE_CLR), GR_BOX_PROFILE - ) + foot = self.extrude_profile(rounded_rect_sketch(GRU, GRU, self.outer_rad + GR_BASE_CLR), GR_BOX_PROFILE) foot = foot.translate((0, 0, -GR_BASE_CLR)) foot = foot.mirror(mirrorPlane="XY") centres = self.grid_centres - + # Replicate foot at all centre positions r = composite_from_pts(foot, centres) - + # Build outer envelope using fractional dimensions (unchanged) rs = rounded_rect_sketch(*self.outer_dim, self.outer_rad) rw = ( @@ -397,16 +369,11 @@ def render_shell(self, as_solid=False): .extrude(self.bin_height - GR_BASE_CLR) .translate((*self.half_dim, GR_BASE_CLR)) ) - rc = ( - cq.Workplane("XY") - .placeSketch(rs) - .extrude(-GR_BASE_HEIGHT - 1) - .translate((*self.half_dim, 0.5)) - ) - + rc = cq.Workplane("XY").placeSketch(rs).extrude(-GR_BASE_HEIGHT - 1).translate((*self.half_dim, 0.5)) + # Intersect feet with envelope, union with walls rc = rc.intersect(r).union(rw) - + if not as_solid: return rc.cut(self.interior_solid) return rc @@ -421,10 +388,7 @@ def render_dividers(self): .translate((0, 0, self.floor_h)) ) xl = self.inner_l / (self.length_div + 1) - pts = [ - ((x + 1) * xl - self.half_in, self.half_w) - for x in range(self.length_div) - ] + pts = [((x + 1) * xl - self.half_in, self.half_w) for x in range(self.length_div)] r = composite_from_pts(wall_w, pts) if self.width_div > 0 and not self.solid: @@ -435,10 +399,7 @@ def render_dividers(self): .translate((0, 0, self.floor_h)) ) yl = self.inner_w / (self.width_div + 1) - pts = [ - (self.half_l, (y + 1) * yl - self.half_in) - for y in range(self.width_div) - ] + pts = [(self.half_l, (y + 1) * yl - self.half_in) for y in range(self.width_div)] rw = composite_from_pts(wall_l, pts) if r is not None: r = r.union(rw) @@ -466,10 +427,7 @@ def render_scoops(self): if self.width_div > 0: # add scoops along each internal dividing wall in the width dimension yl = self.inner_w / (self.width_div + 1) - pts = [ - (-self.half_in, (y + 1) * yl - self.half_in) - for y in range(self.width_div) - ] + pts = [(-self.half_in, (y + 1) * yl - self.half_in) for y in range(self.width_div)] rs = composite_from_pts(rsc, pts) r = r.union(rs.translate((0, GR_DIV_WALL / 2 + srad / 2, zo))) r = r.intersect(self.render_shell(as_solid=True)) @@ -512,10 +470,7 @@ def render_labels(self): rsc = cq.Workplane("YZ").placeSketch(rs).extrude(self.inner_l) rsc = rsc.translate((0, -self.label_width, self.floor_h + self.max_height)) yl = self.inner_w / (self.width_div + 1) - pts = [ - (-self.half_in, (y + 1) * yl - self.half_in + GR_DIV_WALL / 2) - for y in range(self.width_div) - ] + pts = [(-self.half_in, (y + 1) * yl - self.half_in + GR_DIV_WALL / 2) for y in range(self.width_div)] r = r.union(composite_from_pts(rsc, pts)) return r @@ -533,11 +488,7 @@ def render_holes(self, obj): ) def render_hole_fillers(self, obj): - rc = ( - cq.Workplane("XY") - .rect(self.hole_diam / 2, self.hole_diam) - .extrude(GR_HOLE_SLICE) - ) + rc = cq.Workplane("XY").rect(self.hole_diam / 2, self.hole_diam).extrude(GR_HOLE_SLICE) xo = self.hole_diam / 2 rs = composite_from_pts(rc, [(-xo, 0, GR_HOLE_H), (xo, 0, GR_HOLE_H)]) rs = composite_from_pts(rs, self.hole_centres) diff --git a/microfinity/gf_drawer.py b/microfinity/gf_drawer.py index 2c6379a..e283338 100644 --- a/microfinity/gf_drawer.py +++ b/microfinity/gf_drawer.py @@ -89,32 +89,17 @@ def best_fit_to_dim(self, length, width, verbose=False): if verbose: print("Best fit for %.2f x %.2f mm is %dU x %dU" % (length, width, lu, wu)) if self.front_and_back: - print( - "with %.2f mm margin each side and %.2f mm margin front and back" - % (lm, wm) - ) + print("with %.2f mm margin each side and %.2f mm margin front and back" % (lm, wm)) else: - print( - "with %.2f mm margin each side and %.2f mm back (or front) margin" - % (lm, 2 * wm) - ) + print("with %.2f mm margin each side and %.2f mm back (or front) margin" % (lm, 2 * wm)) if not self.front_and_back: print("Corner spacers only generated for either front or back wall") if self.wide_enough and self.deep_enough: - print( - "Corner spacers : %dU wide x %dU deep" - % (self.length_u, self.width_u) - ) + print("Corner spacers : %dU wide x %dU deep" % (self.length_u, self.width_u)) elif self.wide_enough: - print( - "Corner spacers : %dU deep x %.2f mm" - % (self.width_u, self.width_th) - ) + print("Corner spacers : %dU deep x %.2f mm" % (self.width_u, self.width_th)) elif self.deep_enough: - print( - "Corner spacers : %dU wide x %.2f mm" - % (self.length_u, self.fb_length_th) - ) + print("Corner spacers : %dU wide x %.2f mm" % (self.length_u, self.fb_length_th)) if self.deep_enough: if self.front_and_back: @@ -135,9 +120,7 @@ def best_fit_to_dim(self, length, width, verbose=False): % (self.width_fill / GRU, self.width_th, self.tolerance) ) if not self.front_and_back: - print( - "Extra left/right spacers generated %dU deep" % (self.width_u) - ) + print("Extra left/right spacers generated %dU deep" % (self.width_u)) else: print("Left/right spacers : not required") @@ -199,11 +182,7 @@ def render(self, arrows_top=True, arrows_bottom=True, front_and_back=True): sp_width = self.width + self.fb_length_th + self.tolerance r, rd = None, None if self.deep_enough and front_and_back: - r = ( - cq.Workplane("XY") - .rect(sp_length, self.fb_length_th) - .extrude(self.thickness) - ) + r = cq.Workplane("XY").rect(sp_length, self.fb_length_th).extrude(self.thickness) er = min(GR_RAD, max(self.length_th, self.width_th) / 4) r = r.translate((sp_length / 2, self.fb_length_th / 2, 0)) r = r.edges("|Z").edges("Z or self.align_min: rc = self.alignment_feature(as_cutter=True) r = r.cut(rc.translate((sp_length, self.fb_length_th / 2, 0))) @@ -272,19 +247,8 @@ def orientation_arrows(self, obj, x, y, up=True, down=True, top=True, bottom=Tru direction of the drawer.""" if self.show_arrows and self.wide_enough: la = self.width_th / 2 - ra = ( - cq.Sketch() - .segment((0, 0), (la / 2, la)) - .segment((la, 0)) - .close() - .assemble() - ) - ru = ( - cq.Workplane("XY") - .placeSketch(ra) - .extrude(self.arrow_h) - .translate((-la / 2, -la / 2, 0)) - ) + ra = cq.Sketch().segment((0, 0), (la / 2, la)).segment((la, 0)).close().assemble() + ru = cq.Workplane("XY").placeSketch(ra).extrude(self.arrow_h).translate((-la / 2, -la / 2, 0)) rd = ru.rotate((0, 0, 0), (0, 0, 1), 180) th = self.thickness - self.arrow_h yo = 10 * self.width_th / 15 if up and down else 0 @@ -303,11 +267,7 @@ def render_length_filler(self, alignment_type="peg"): of the drawer.""" if not self.deep_enough: return None - r = ( - cq.Workplane("XY") - .rect(self.length_fill, self.fb_length_th) - .extrude(self.thickness) - ) + r = cq.Workplane("XY").rect(self.length_fill, self.fb_length_th).extrude(self.thickness) r = r.edges("|Z").fillet(self.fillet_rad) r = r.faces(">Z or self.align_min: @@ -328,11 +288,7 @@ def render_width_filler(self, arrows_top=True, arrows_bottom=True): of the drawer.""" if not self.wide_enough: return None - r = ( - cq.Workplane("XY") - .rect(self.width_th, self.width_fill) - .extrude(self.thickness) - ) + r = cq.Workplane("XY").rect(self.width_th, self.width_fill).extrude(self.thickness) r = r.edges("|Z").fillet(self.fillet_rad) r = r.faces(">Z or self.align_min: xl -= self.align_l / 2 if self.wide_enough: @@ -440,9 +390,7 @@ def render_half_set(self): wf = self.render_width_filler(arrows_bottom=False) r = r.union(wf.translate((-self.width_th, self.width_fill / 2, 0))) if not self.front_and_back: - r = r.union( - wf.translate((-2.5 * self.width_th, self.width_fill / 2, 0)) - ) + r = r.union(wf.translate((-2.5 * self.width_th, self.width_fill / 2, 0))) fb = self.render(arrows_bottom=False, front_and_back=False) r = r.union(fb.translate((-4.5 * self.width_th, 0, 0))) r = r.union(fb.translate((-6 * self.width_th, 0, 0))) diff --git a/microfinity/gf_helpers.py b/microfinity/gf_helpers.py index f54903b..aa37db0 100644 --- a/microfinity/gf_helpers.py +++ b/microfinity/gf_helpers.py @@ -27,9 +27,7 @@ from cqkit import rotate_z -def quarter_circle( - outer_rad, inner_rad, height, quad="tr", chamf=0.5, chamf_face=">Z", ext=0 -): +def quarter_circle(outer_rad, inner_rad, height, quad="tr", chamf=0.5, chamf_face=">Z", ext=0): """Renders a quarter circle shaped slot in any of 4 quadrants""" r = cq.Workplane("XY").circle(outer_rad).extrude(height) rc = cq.Workplane("XY").circle(inner_rad).extrude(height) diff --git a/microfinity/gf_obj.py b/microfinity/gf_obj.py index 942073b..5529a23 100644 --- a/microfinity/gf_obj.py +++ b/microfinity/gf_obj.py @@ -50,13 +50,13 @@ class GridfinityObject: This class bundles glabally relevant constants, properties, and methods for derived Gridfinity object classes. - + Micro-grid support: micro_divisions: int (1, 2, or 4) - Enables quarter-grid positioning. - 1 = standard Gridfinity (default) - 2 = half-grid (21mm pitch) - 4 = quarter-grid (10.5mm pitch) - + When micro_divisions > 1: - length_u and width_u can be fractional (multiples of 1/micro_divisions) - Underside features include micro-boundary grooves for 0.25U positioning @@ -207,23 +207,19 @@ def safe_fillet_rad(self): @property def grid_centres(self): """Returns center points for macro grid cells. - + For fractional sizes, uses ceiling to ensure full coverage of feet, which are then cropped by the outer envelope intersection. """ - return [ - (x * GRU, y * GRU) - for x in range(self.macro_length_u) - for y in range(self.macro_width_u) - ] + return [(x * GRU, y * GRU) for x in range(self.macro_length_u) for y in range(self.macro_width_u)] @property def micro_grid_centres(self): """Returns center points for micro-grid cells. - + Micro feet are centered on the envelope (aligned with half_dim) to ensure proper intersection regardless of fractional sizes. - + The total span of feet exactly matches the outer envelope dimensions: - Feet span: (micro_count - 1) * micro_pitch + foot_size - Where foot_size = micro_pitch - GR_TOL @@ -231,21 +227,20 @@ def micro_grid_centres(self): """ if self.micro_divisions <= 1: return self.grid_centres - + # Use epsilon-safe integer conversion (consistent with validation in __init__) v_l = self.length_u * self.micro_divisions v_w = self.width_u * self.micro_divisions micro_count_l = int(round(v_l)) micro_count_w = int(round(v_w)) - + # Micro lattice half-extents (distance from center to edge foot centers) micro_half_l = (micro_count_l - 1) * self.micro_pitch / 2 micro_half_w = (micro_count_w - 1) * self.micro_pitch / 2 - + # Center the micro lattice on the envelope (which is at half_l, half_w) return [ - (x * self.micro_pitch - micro_half_l + self.half_l, - y * self.micro_pitch - micro_half_w + self.half_w) + (x * self.micro_pitch - micro_half_l + self.half_l, y * self.micro_pitch - micro_half_w + self.half_w) for x in range(micro_count_l) for y in range(micro_count_w) ] @@ -253,7 +248,7 @@ def micro_grid_centres(self): @property def hole_centres(self): """Returns center points for magnet/screw holes. - + Holes are placed only at macro corners. For fractional sizes, only holes that fall within the actual bin envelope are included. """ @@ -270,8 +265,10 @@ def hole_centres(self): hy = -(y * GRU - GR_HOLE_DIST * j) # Check if hole falls within the actual envelope # (with some margin for the hole radius) - if (abs(hx - self.half_l) <= half_env_l - GR_HOLE_D/2 - 0.5 and - abs(hy + self.half_w) <= half_env_w - GR_HOLE_D/2 - 0.5): + if ( + abs(hx - self.half_l) <= half_env_l - GR_HOLE_D / 2 - 0.5 + and abs(hy + self.half_w) <= half_env_w - GR_HOLE_D / 2 - 0.5 + ): centres.append((hx, hy)) return centres @@ -372,11 +369,7 @@ def filename(self, prefix=None, path=None): return fn def save_step_file(self, filename=None, path=None, prefix=None): - fn = ( - filename - if filename is not None - else self.filename(path=path, prefix=prefix) - ) + fn = filename if filename is not None else self.filename(path=path, prefix=prefix) if not fn.lower().endswith(".step"): fn = fn + ".step" if isinstance(self.cq_obj, cq.Assembly): @@ -384,14 +377,8 @@ def save_step_file(self, filename=None, path=None, prefix=None): else: export_step_file(self.cq_obj, fn) - def save_stl_file( - self, filename=None, path=None, prefix=None, tol=1e-2, ang_tol=0.1 - ): - fn = ( - filename - if filename is not None - else self.filename(path=path, prefix=prefix) - ) + def save_stl_file(self, filename=None, path=None, prefix=None, tol=1e-2, ang_tol=0.1): + fn = filename if filename is not None else self.filename(path=path, prefix=prefix) if not fn.lower().endswith(".stl"): fn = fn + ".stl" obj = self.cq_obj.val().wrapped @@ -401,11 +388,7 @@ def save_stl_file( writer.Write(obj, fn) def save_svg_file(self, filename=None, path=None, prefix=None): - fn = ( - filename - if filename is not None - else self.filename(path=path, prefix=prefix) - ) + fn = filename if filename is not None else self.filename(path=path, prefix=prefix) if not fn.lower().endswith(".svg"): fn = fn + ".svg" r = self.cq_obj.rotate((0, 0, 0), (0, 0, 1), 75) @@ -437,43 +420,21 @@ def extrude_profile(self, sketch, profile, workplane="XY", angle=None): if angle is None: zlen = level[0] if ZLEN_FIX else level[0] / SQRT2 else: - zlen = ( - level[0] / math.cos(math.radians(level[1])) - if ZLEN_FIX - else level[0] - ) + zlen = level[0] / math.cos(math.radians(level[1])) if ZLEN_FIX else level[0] r = r.faces(">Z").wires().toPending().extrude(zlen, taper=level[1]) else: r = r.faces(">Z").wires().toPending().extrude(level) return r @classmethod - def to_step_file( - cls, - length_u, - width_u, - height_u=None, - filename=None, - prefix=None, - path=None, - **kwargs - ): + def to_step_file(cls, length_u, width_u, height_u=None, filename=None, prefix=None, path=None, **kwargs): """Convenience method to create, render and save a STEP file representation of a Gridfinity object.""" obj = GridfinityObject.as_obj(cls, length_u, width_u, height_u, **kwargs) obj.save_step_file(filename=filename, path=path, prefix=prefix) @classmethod - def to_stl_file( - cls, - length_u, - width_u, - height_u=None, - filename=None, - prefix=None, - path=None, - **kwargs - ): + def to_stl_file(cls, length_u, width_u, height_u=None, filename=None, prefix=None, path=None, **kwargs): """Convenience method to create, render and save a STEP file representation of a Gridfinity object.""" obj = GridfinityObject.as_obj(cls, length_u, width_u, height_u, **kwargs) diff --git a/microfinity/gf_ruggedbox.py b/microfinity/gf_ruggedbox.py index ea5fbd8..67c1754 100644 --- a/microfinity/gf_ruggedbox.py +++ b/microfinity/gf_ruggedbox.py @@ -251,10 +251,7 @@ def lid_window_size(self, width_ext=None, tol=None): return self.length - 2 - tol, self.width + width_ext - tol def lid_window_hole_pos(self, z=0): - pts = [ - (-x * (self.box_length / 2 - GR_RBOX_CORNER_W), self.width / 2 + 2, z) - for x in (-1, 1) - ] + pts = [(-x * (self.box_length / 2 - GR_RBOX_CORNER_W), self.width / 2 + 2, z) for x in (-1, 1)] if self.rib_style: pts.append((0, self.width / 2 + 2, z)) return pts @@ -301,11 +298,7 @@ def body_shell(self, as_lid=False): rc = cq.Workplane("XY").rect(lb, wb).extrude(h) r = r.union(rc) else: - rc = ( - cq.Workplane("XY") - .rect(GR_RBOX_BACK_L, GR_RBOX_CORNER_W) - .extrude(height) - ) + rc = cq.Workplane("XY").rect(GR_RBOX_BACK_L, GR_RBOX_CORNER_W).extrude(height) r = r.union(composite_from_pts(rc, self.back_corner_centres)) # front corners rc = cq.Workplane("XY").rect(GR_RBOX_FRONT_L, GR_RBOX_CORNER_W).extrude(height) @@ -318,9 +311,7 @@ def body_shell(self, as_lid=False): if self.stackable or as_lid: # bottom stacking mates for k, v in self.qtr_centres(back=not as_lid).items(): - rq = quarter_circle( - GR_BREG_R0, GR_BREG_R1, GR_REG_H + 0.5, k, chamf=0, ext=0.25 - ) + rq = quarter_circle(GR_BREG_R0, GR_BREG_R1, GR_REG_H + 0.5, k, chamf=0, ext=0.25) r = r.cut(rq.translate(v)) pts, rots = self.align_centres for pt, rot in zip(pts, rots): @@ -421,11 +412,7 @@ def rib_style_cut(self): for pt in self.side_clasp_centres: r = r.union(rc.translate(pt)) else: - rd = ( - cq.Workplane("XY") - .rect(GR_RBOX_CHAN_D, 1.5 * GR_RBOX_WALL) - .extrude(self.box_height) - ) + rd = cq.Workplane("XY").rect(GR_RBOX_CHAN_D, 1.5 * GR_RBOX_WALL).extrude(self.box_height) xo = self.box_length / 2 + GR_RBOX_CHAN_D / 2 yo = self.clasp_pos[1] + GR_RBOX_CHAN_W / 2 + 1.5 * GR_RBOX_WALL / 2 for pt in [(x * xo, y * yo, 0) for x in (-1, 1) for y in (-1, 1)]: @@ -462,14 +449,7 @@ def lid_handle(self, width=None): """Renders the front overhanging handle lip for the lid.""" width = width if width is not None else GR_LID_HANDLE_W l0, l1, h1, h2, hw = 3, 5, 4, self.lid_height - GR_RBOX_VCUT_D, width / 2 - rs = ( - cq.Sketch() - .segment((l0, 0), (-l1, 0)) - .segment((-l1, h1)) - .segment((l0, h2 + l0)) - .close() - .assemble() - ) + rs = cq.Sketch().segment((l0, 0), (-l1, 0)).segment((-l1, h1)).segment((l0, h2 + l0)).close().assemble() r = cq.Workplane("YZ").placeSketch(rs).extrude(width).translate((-hw, 0, 0)) vs = VerticalEdgeSelector([h1]) & HasXCoordinateSelector([-hw, hw]) r = r.edges(vs).fillet(2.45).faces("Y", 5, (StringSyntaxSelector("Y", 5, (StringSyntaxSelector("Z").chamfer(0.75) @@ -712,16 +664,12 @@ def hinge_mount(self): r = r.translate((0, -GR_HINGE_W1 / 2, -GR_HINGE_H1)) r2 = cq.Workplane("XY").rect(l2, GR_HINGE_W2).extrude(GR_HINGE_H2) r2 = r2.translate((0, -GR_HINGE_D - GR_HINGE_W2 / 2, -GR_HINGE_H2)) - bs = HasZCoordinateSelector(-GR_HINGE_H1) & EdgeLengthSelector( - [l2, GR_HINGE_W2] - ) + bs = HasZCoordinateSelector(-GR_HINGE_H1) & EdgeLengthSelector([l2, GR_HINGE_W2]) r = r.union(r2).edges(bs).edges(">Y or X").chamfer(0.75) rs = rounded_rect_sketch(l3, GR_HINGE_W3, 0.5) r3 = cq.Workplane("XY").placeSketch(rs).extrude(GR_HINGE_H2) xo, yo = GR_HINGE_SEP / 2 + l3 / 2, -GR_HINGE_W1 - 1.2 - GR_HINGE_W3 / 2 - rh = self.hex_cut().translate( - (0, 0, GR_HINGE_H2 - GR_HINGE_H1 - GR_HEX_H / 2 + GR_HINGE_SKEW) - ) + rh = self.hex_cut().translate((0, 0, GR_HINGE_H2 - GR_HINGE_H1 - GR_HEX_H / 2 + GR_HINGE_SKEW)) for pt in [(-xo, yo, -GR_HINGE_H2), (xo, yo, -GR_HINGE_H2)]: r = r.union(r3.translate(pt)) r = r.union(rh.translate(pt)) @@ -865,11 +813,7 @@ def _bracket(side="left"): rr = rr.cut(chamf_cyl(M3_DIAM / 2, h, 0).translate((*ctr, 0))) rl = rl.cut(chamf_cyl(M3_CLR_DIAM / 2, h, 0).translate((*ctr, 0))) rr = rr.cut(chamf_cyl(M3_CLR_DIAM / 2, ha, 0).translate((*ctr, h - ha))) - rr = rr.cut( - chamf_cyl(M3_CB_DIAM / 2, M3_CB_DEPTH, 0).translate( - (*ctr, h - M3_CB_DEPTH) - ) - ) + rr = rr.cut(chamf_cyl(M3_CB_DIAM / 2, M3_CB_DEPTH, 0).translate((*ctr, h - M3_CB_DEPTH))) rx = recentre(self.hex_cut(depth=GR_HEX_D)) rh = rotate_x(rotate_z(rx, 90), 90) xo = cl + wh + GR_HEX_D / 2 @@ -924,12 +868,8 @@ def render(self): # add handle mounts if self.front_handle and self.long_enough_for_handle: - r = r.union( - self.handle_mount(side="left").translate(self.left_handle_centre) - ) - r = r.union( - self.handle_mount(side="right").translate(self.right_handle_centre) - ) + r = r.union(self.handle_mount(side="left").translate(self.left_handle_centre)) + r = r.union(self.handle_mount(side="right").translate(self.right_handle_centre)) # add hinge mounts rc = self.hinge_mount() @@ -965,9 +905,7 @@ def render(self): r = r.union(rb.render().translate((0, 0, GR_RBOX_FLOOR))) r = r.edges(FlatEdgeSelector(GR_RBOX_FLOOR)).chamfer(0.8) else: - rb = self.extrude_profile( - rounded_rect_sketch(self.length, self.width, GR_RAD), [GR_RBOX_WALL] - ) + rb = self.extrude_profile(rounded_rect_sketch(self.length, self.width, GR_RAD), [GR_RBOX_WALL]) r = r.union(rb) self._cq_obj = r self._obj_label = "body" @@ -984,9 +922,7 @@ def render_lid(self): rc = self.extrude_profile(rs, [self.lid_height - 0.5, (1.0, -45)]) r = r.cut(rc) # add topside baseplate - rb = GridfinityBaseplate( - self.length_u, self.width_u, ext_depth=0.4, straight_bottom=True - ) + rb = GridfinityBaseplate(self.length_u, self.width_u, ext_depth=0.4, straight_bottom=True) rb = rb.render() r = r.union(rb.translate((0, 0, 4.7 - 0.4))) elif self.lid_window: @@ -1001,9 +937,7 @@ def render_lid(self): # add modified bottom extrusion with a looser fit if self.lid_baseplate: - rs = self.extrude_profile( - rounded_rect_sketch(35, 35, 0.8), [(2.82, -22.1), (5, -45)] - ) + rs = self.extrude_profile(rounded_rect_sketch(35, 35, 0.8), [(2.82, -22.1), (5, -45)]) rs = rs.faces(">Z").shell(-1.2) else: rs = self.extrude_profile( @@ -1016,9 +950,7 @@ def render_lid(self): ra = ra.intersect(cq.Workplane("XY").placeSketch(rs).extrude(GR_LID_WINDOW_H)) r = r.union(ra) - r = r.edges( - EdgeLengthSelector(33.4) & HasZCoordinateSelector(0, min_points=2) - ).chamfer(0.75) + r = r.edges(EdgeLengthSelector(33.4) & HasZCoordinateSelector(0, min_points=2)).chamfer(0.75) # add optional stackable features if self.stackable: @@ -1030,11 +962,7 @@ def render_lid(self): # hollow the grid apertures ht, tp = GR_LID_WINDOW_H, 34 he = GR_LID_WINDOW_H / math.cos(math.radians(tp)) - rs = ( - cq.Workplane("XY") - .placeSketch(rounded_rect_sketch(30, 30, 1)) - .extrude(he, taper=-tp) - ) + rs = cq.Workplane("XY").placeSketch(rounded_rect_sketch(30, 30, 1)).extrude(he, taper=-tp) ra = composite_from_pts(rs, self.grid_centres) ra = ra.translate((-self.half_l, -self.half_w, 0)) r = r.cut(ra) diff --git a/microfinity/scripts/gridfinitybase.py b/microfinity/scripts/gridfinitybase.py index bdbe736..253e0c6 100644 --- a/microfinity/scripts/gridfinitybase.py +++ b/microfinity/scripts/gridfinitybase.py @@ -38,12 +38,8 @@ def main(): formatter_class=argparse.RawTextHelpFormatter, ) - parser.add_argument( - "length", metavar="length", type=str, help="Box length in U (1U = 42 mm)" - ) - parser.add_argument( - "width", metavar="width", type=str, help="Box width in U (1U = 42 mm)" - ) + parser.add_argument("length", metavar="length", type=str, help="Box length in U (1U = 42 mm)") + parser.add_argument("width", metavar="width", type=str, help="Box width in U (1U = 42 mm)") parser.add_argument( "-f", "--format", diff --git a/microfinity/scripts/gridfinitybox.py b/microfinity/scripts/gridfinitybox.py index 8cd2413..ebe7f46 100644 --- a/microfinity/scripts/gridfinitybox.py +++ b/microfinity/scripts/gridfinitybox.py @@ -47,15 +47,9 @@ def main(): formatter_class=argparse.RawTextHelpFormatter, ) - parser.add_argument( - "length", metavar="length", type=str, help="Box length in U (1U = 42 mm)" - ) - parser.add_argument( - "width", metavar="width", type=str, help="Box width in U (1U = 42 mm)" - ) - parser.add_argument( - "height", metavar="height", type=str, help="Box height in U (1U = 7 mm)" - ) + parser.add_argument("length", metavar="length", type=str, help="Box length in U (1U = 42 mm)") + parser.add_argument("width", metavar="width", type=str, help="Box width in U (1U = 42 mm)") + parser.add_argument("height", metavar="height", type=str, help="Box height in U (1U = 7 mm)") parser.add_argument( "-m", "--magnetholes", diff --git a/microfinity/scripts/ruggedbox.py b/microfinity/scripts/ruggedbox.py index 28f66da..38b8ce4 100644 --- a/microfinity/scripts/ruggedbox.py +++ b/microfinity/scripts/ruggedbox.py @@ -66,15 +66,9 @@ def main(): formatter_class=argparse.RawTextHelpFormatter, ) - parser.add_argument( - "length", metavar="length", type=str, help="Box length in U (1U = 42 mm)" - ) - parser.add_argument( - "width", metavar="width", type=str, help="Box width in U (1U = 42 mm)" - ) - parser.add_argument( - "height", metavar="height", type=str, help="Box height in U (1U = 7 mm)" - ) + parser.add_argument("length", metavar="length", type=str, help="Box length in U (1U = 42 mm)") + parser.add_argument("width", metavar="width", type=str, help="Box width in U (1U = 42 mm)") + parser.add_argument("height", metavar="height", type=str, help="Box height in U (1U = 7 mm)") parser.add_argument( "+l", "--label", @@ -369,10 +363,7 @@ def main(): ) print(" Internal volume: %.3f L" % (box.length * box.width * box.height / 1e6)) if box.lid_window: - print( - " Lid window dimensions: %.2f x %.2f mm, %.2f mm thickness" - % (*box.lid_window_size(), box.window_th) - ) + print(" Lid window dimensions: %.2f x %.2f mm, %.2f mm thickness" % (*box.lid_window_size(), box.window_th)) s = [] opts = [ @@ -434,10 +425,7 @@ def main(): save_asset(box, argsd) g = True if argsd["genwindow"]: - print( - "Rendering lid window (%.2f x %.2f mm, %.2f mm thickness)..." - % (*box.lid_window_size(), box.window_th) - ) + print("Rendering lid window (%.2f x %.2f mm, %.2f mm thickness)..." % (*box.lid_window_size(), box.window_th)) r = box.render_lid_window() save_asset(box, argsd) g = True diff --git a/microfinity/shims/cqgi_gf_baseplate.py b/microfinity/shims/cqgi_gf_baseplate.py index 61113f6..96d7bca 100644 --- a/microfinity/shims/cqgi_gf_baseplate.py +++ b/microfinity/shims/cqgi_gf_baseplate.py @@ -1,5 +1,6 @@ import sys -sys.path.append(".") # Relative to `partcad.yaml` + +sys.path.append(".") # Relative to `partcad.yaml` from microfinity.gf_baseplate import GridfinityBaseplate @@ -13,16 +14,20 @@ csk_diam = 10.0 csk_angle = 82 -result = GridfinityBaseplate( - length_u=int(length_u), - width_u=int(width_u), - ext_depth=ext_depth, - straight_bottom=straight_bottom, - corner_screws=corner_screws, - corner_tab_size=corner_tab_size, - csk_hole=csk_hole, - csk_diam=csk_diam, - csk_angle=csk_angle, -).render().val() +result = ( + GridfinityBaseplate( + length_u=int(length_u), + width_u=int(width_u), + ext_depth=ext_depth, + straight_bottom=straight_bottom, + corner_screws=corner_screws, + corner_tab_size=corner_tab_size, + csk_hole=csk_hole, + csk_diam=csk_diam, + csk_angle=csk_angle, + ) + .render() + .val() +) -show_object(result) \ No newline at end of file +show_object(result) diff --git a/microfinity/shims/cqgi_gf_box.py b/microfinity/shims/cqgi_gf_box.py index c431a42..f91fa04 100644 --- a/microfinity/shims/cqgi_gf_box.py +++ b/microfinity/shims/cqgi_gf_box.py @@ -1,5 +1,6 @@ import sys -sys.path.append(".") # Relative to `partcad.yaml` + +sys.path.append(".") # Relative to `partcad.yaml` from microfinity.gf_box import GridfinityBox @@ -23,26 +24,30 @@ fillet_interior = True wall_th = 1.0 -result = GridfinityBox( - length_u=int(length_u), - width_u=int(width_u), - height_u=int(height_u), - length_div=length_div, - width_div=width_div, - scoops=scoops, - labels=labels, - solid=solid, - holes=holes, - no_lip=no_lip, - solid_ratio=solid_ratio, - lite_style=lite_style, - unsupported_holes=unsupported_holes, - label_width=label_width, - label_height=label_height, - label_lip_height=label_lip_height, - scoop_rad=scoop_rad, - fillet_interior=fillet_interior, - wall_th=wall_th, -).render().val() +result = ( + GridfinityBox( + length_u=int(length_u), + width_u=int(width_u), + height_u=int(height_u), + length_div=length_div, + width_div=width_div, + scoops=scoops, + labels=labels, + solid=solid, + holes=holes, + no_lip=no_lip, + solid_ratio=solid_ratio, + lite_style=lite_style, + unsupported_holes=unsupported_holes, + label_width=label_width, + label_height=label_height, + label_lip_height=label_lip_height, + scoop_rad=scoop_rad, + fillet_interior=fillet_interior, + wall_th=wall_th, + ) + .render() + .val() +) -show_object(result) \ No newline at end of file +show_object(result) diff --git a/microfinity/shims/cqgi_gf_drawerspacer.py b/microfinity/shims/cqgi_gf_drawerspacer.py index 39ac3f5..ca050a1 100644 --- a/microfinity/shims/cqgi_gf_drawerspacer.py +++ b/microfinity/shims/cqgi_gf_drawerspacer.py @@ -1,5 +1,6 @@ import sys -sys.path.append(".") # Relative to `partcad.yaml` + +sys.path.append(".") # Relative to `partcad.yaml` from microfinity.gf_drawer import GridfinityDrawerSpacer @@ -20,23 +21,27 @@ min_margin = 4.0 tolerance = 0.5 -result = GridfinityDrawerSpacer( - length_u=int(length_u), - width_u=int(width_u), - length_th=length_th, - width_th=width_th, - thickness=thickness, - chamf_rad=chamf_rad, - show_arrows=show_arrows, - arrow_h=arrow_h, - length_fill=length_fill, - width_fill=width_fill, - align_features=align_features, - align_l=align_l, - align_tol=align_tol, - align_min=align_min, - min_margin=min_margin, - tolerance=tolerance, -).render().val() +result = ( + GridfinityDrawerSpacer( + length_u=int(length_u), + width_u=int(width_u), + length_th=length_th, + width_th=width_th, + thickness=thickness, + chamf_rad=chamf_rad, + show_arrows=show_arrows, + arrow_h=arrow_h, + length_fill=length_fill, + width_fill=width_fill, + align_features=align_features, + align_l=align_l, + align_tol=align_tol, + align_min=align_min, + min_margin=min_margin, + tolerance=tolerance, + ) + .render() + .val() +) -show_object(result) \ No newline at end of file +show_object(result) diff --git a/microfinity/shims/cqgi_gf_ruggedbox.py b/microfinity/shims/cqgi_gf_ruggedbox.py index 0a3736a..58c5c56 100644 --- a/microfinity/shims/cqgi_gf_ruggedbox.py +++ b/microfinity/shims/cqgi_gf_ruggedbox.py @@ -1,5 +1,6 @@ import sys -sys.path.append(".") # Relative to `partcad.yaml` + +sys.path.append(".") # Relative to `partcad.yaml` from microfinity.gf_ruggedbox import GridfinityRuggedBox @@ -31,26 +32,30 @@ hinge_bolted = False rib_style = False -result = GridfinityRuggedBox( - length_u=int(length_u), - width_u=int(width_u), - height_u=int(height_u), - lid_height=lid_height, - wall_vgrooves=wall_vgrooves, - front_handle=front_handle, - stackable=stackable, - side_clasps=side_clasps, - lid_baseplate=lid_baseplate, - inside_baseplate=inside_baseplate, - side_handles=side_handles, - front_label=front_label, - label_length=label_length, - label_height=label_height, - label_th=label_th, - back_feet=back_feet, - hinge_width=hinge_width, - hinge_bolted=hinge_bolted, - rib_style=rib_style, -).render().val() +result = ( + GridfinityRuggedBox( + length_u=int(length_u), + width_u=int(width_u), + height_u=int(height_u), + lid_height=lid_height, + wall_vgrooves=wall_vgrooves, + front_handle=front_handle, + stackable=stackable, + side_clasps=side_clasps, + lid_baseplate=lid_baseplate, + inside_baseplate=inside_baseplate, + side_handles=side_handles, + front_label=front_label, + label_length=label_length, + label_height=label_height, + label_th=label_th, + back_feet=back_feet, + hinge_width=hinge_width, + hinge_bolted=hinge_bolted, + rib_style=rib_style, + ) + .render() + .val() +) -show_object(result) \ No newline at end of file +show_object(result) diff --git a/tests/test_box.py b/tests/test_box.py index 2f8e60c..c235376 100644 --- a/tests/test_box.py +++ b/tests/test_box.py @@ -17,9 +17,7 @@ ) -@pytest.mark.skipif( - SKIP_TEST_BOX, reason="Skipped intentionally by test scope environment variable" -) +@pytest.mark.skipif(SKIP_TEST_BOX, reason="Skipped intentionally by test scope environment variable") def test_basic_box(): b1 = GridfinityBox(2, 3, 5, no_lip=True) r = b1.render() @@ -38,9 +36,7 @@ def test_basic_box(): b1.save_step_file(path=EXPORT_STEP_FILE_PATH) -@pytest.mark.skipif( - SKIP_TEST_BOX, reason="Skipped intentionally by test scope environment variable" -) +@pytest.mark.skipif(SKIP_TEST_BOX, reason="Skipped intentionally by test scope environment variable") def test_invalid_box(): with pytest.raises(ValueError): b1 = GridfinityBox(2, 3, 5, lite_style=True, solid=True) @@ -59,9 +55,7 @@ def test_invalid_box(): b1.render() -@pytest.mark.skipif( - SKIP_TEST_BOX, reason="Skipped intentionally by test scope environment variable" -) +@pytest.mark.skipif(SKIP_TEST_BOX, reason="Skipped intentionally by test scope environment variable") def test_lite_box(): b1 = GridfinityBox(2, 3, 5, lite_style=True) r = b1.render() @@ -92,9 +86,7 @@ def test_lite_box(): assert _almost_same(size_3d(r), (41.5, 41.5, 17.8)) -@pytest.mark.skipif( - SKIP_TEST_BOX, reason="Skipped intentionally by test scope environment variable" -) +@pytest.mark.skipif(SKIP_TEST_BOX, reason="Skipped intentionally by test scope environment variable") def test_empty_box(): b1 = GridfinityBox(2, 3, 5, holes=True) r = b1.render() @@ -126,9 +118,7 @@ def test_empty_box(): assert _almost_same(size_3d(r), (41.5, 41.5, 17.8)) -@pytest.mark.skipif( - SKIP_TEST_BOX, reason="Skipped intentionally by test scope environment variable" -) +@pytest.mark.skipif(SKIP_TEST_BOX, reason="Skipped intentionally by test scope environment variable") def test_solid_box(): b1 = GridfinitySolidBox(4, 2, 3) r = b1.render() @@ -147,9 +137,7 @@ def test_solid_box(): assert _almost_same(b1.top_ref_height, 14) -@pytest.mark.skipif( - SKIP_TEST_BOX, reason="Skipped intentionally by test scope environment variable" -) +@pytest.mark.skipif(SKIP_TEST_BOX, reason="Skipped intentionally by test scope environment variable") def test_divided_box(): b1 = GridfinityBox(3, 3, 3, holes=True, length_div=2, width_div=1) r = b1.render() @@ -166,13 +154,9 @@ def test_divided_box(): assert b1.filename() == "gf_box_3x3x3_div2x1_holes" -@pytest.mark.skipif( - SKIP_TEST_BOX, reason="Skipped intentionally by test scope environment variable" -) +@pytest.mark.skipif(SKIP_TEST_BOX, reason="Skipped intentionally by test scope environment variable") def test_all_features_box(): - b1 = GridfinityBox( - 4, 2, 5, holes=True, length_div=2, width_div=1, scoops=True, labels=True - ) + b1 = GridfinityBox(4, 2, 5, holes=True, length_div=2, width_div=1, scoops=True, labels=True) b1.label_height = 9 b1.scoop_rad = 20 r = b1.render() @@ -197,9 +181,7 @@ def test_all_features_box(): assert len(r.faces(FlatFaceSelector(35)).vals()) == 1 assert len(r.edges(FlatEdgeSelector(35)).vals()) == 51 assert b1.filename() == "gf_box_4x2x5_div2x1_holes_scoops_labels" - b1 = GridfinityBox( - 2, 2, 3, holes=True, length_div=1, width_div=1, scoops=True, labels=True - ) + b1 = GridfinityBox(2, 2, 3, holes=True, length_div=1, width_div=1, scoops=True, labels=True) r = b1.render() assert _almost_same(size_3d(r), (83.5, 83.5, 24.8)) if _export_files("box"): diff --git a/tests/test_microgrid.py b/tests/test_microgrid.py index ed76318..b762260 100644 --- a/tests/test_microgrid.py +++ b/tests/test_microgrid.py @@ -20,28 +20,26 @@ class TestMicroGridConstants: """Test micro-grid constant calculations.""" - + def test_micro_pitch_quarter(self): """Verify quarter-pitch is exactly 10.5mm (42/4).""" assert GRU == 42 assert GRU / 4 == 10.5 - + def test_micro_pitch_half(self): """Verify half-pitch is exactly 21mm (42/2).""" assert GRU / 2 == 21.0 - + def test_clearance_unchanged(self): """Verify clearance constants are unchanged.""" assert GR_TOL == 0.5 # Total clearance assert GR_BASE_CLR == 0.25 # Per-side vertical clearance -@pytest.mark.skipif( - SKIP_TEST_MICROGRID, reason="Skipped by SKIP_TEST_MICROGRID env var" -) +@pytest.mark.skipif(SKIP_TEST_MICROGRID, reason="Skipped by SKIP_TEST_MICROGRID env var") class TestMicroGridBoxDimensions: """Test box bounding box dimensions with micro-grid enabled.""" - + def test_standard_1x1_dimensions(self): """Standard 1x1 box should be 41.5 x 41.5 mm.""" box = GridfinityBox(1, 1, 3) @@ -49,7 +47,7 @@ def test_standard_1x1_dimensions(self): dims = size_3d(r) assert _almost_same(dims[0], 41.5, tol=0.01) # length assert _almost_same(dims[1], 41.5, tol=0.01) # width - + def test_micro4_1x1_dimensions(self): """1x1 box with micro_divisions=4 should still be 41.5 x 41.5 mm.""" box = GridfinityBox(1, 1, 3, micro_divisions=4) @@ -59,7 +57,7 @@ def test_micro4_1x1_dimensions(self): assert _almost_same(dims[1], 41.5, tol=0.01) if _export_files("microgrid"): box.save_step_file(path=EXPORT_STEP_FILE_PATH) - + def test_micro4_2x2_dimensions(self): """2x2 box with micro_divisions=4 should be 83.5 x 83.5 mm.""" box = GridfinityBox(2, 2, 3, micro_divisions=4) @@ -69,7 +67,7 @@ def test_micro4_2x2_dimensions(self): assert _almost_same(dims[1], 83.5, tol=0.01) if _export_files("microgrid"): box.save_step_file(path=EXPORT_STEP_FILE_PATH) - + def test_fractional_1_25x0_5_dimensions(self): """1.25 x 0.5 box should be 52.0 x 20.5 mm (1.25*42-0.5, 0.5*42-0.5).""" box = GridfinityBox(1.25, 0.5, 3, micro_divisions=4) @@ -81,7 +79,7 @@ def test_fractional_1_25x0_5_dimensions(self): assert _almost_same(dims[1], 20.5, tol=0.01) if _export_files("microgrid"): box.save_step_file(path=EXPORT_STEP_FILE_PATH) - + def test_fractional_0_75x0_75_dimensions(self): """0.75 x 0.75 box should be 31.0 x 31.0 mm.""" box = GridfinityBox(0.75, 0.75, 3, micro_divisions=4) @@ -94,12 +92,10 @@ def test_fractional_0_75x0_75_dimensions(self): box.save_step_file(path=EXPORT_STEP_FILE_PATH) -@pytest.mark.skipif( - SKIP_TEST_MICROGRID, reason="Skipped by SKIP_TEST_MICROGRID env var" -) +@pytest.mark.skipif(SKIP_TEST_MICROGRID, reason="Skipped by SKIP_TEST_MICROGRID env var") class TestMicroGridGeometry: """Test micro-grid geometry correctness (foot replication approach).""" - + def test_micro_grid_centres_count(self): """Verify micro_grid_centres returns correct count for fractional sizes.""" box = GridfinityBox(1.25, 0.5, 3, micro_divisions=4) @@ -108,7 +104,7 @@ def test_micro_grid_centres_count(self): # 0.5 * 4 = 2 micro-cells in width # Total: 5 * 2 = 10 micro feet assert len(centres) == 10 - + def test_micro_grid_centres_1x1(self): """Verify micro_grid_centres count for 1x1.""" box = GridfinityBox(1, 1, 3, micro_divisions=4) @@ -116,63 +112,63 @@ def test_micro_grid_centres_1x1(self): # 1 * 4 = 4 micro-cells in each dim # Total: 4 * 4 = 16 micro feet assert len(centres) == 16 - + def test_micro_grid_centres_centered_on_envelope_1x1(self): """Verify micro_grid_centres are centered on envelope for 1x1.""" box = GridfinityBox(1, 1, 3, micro_divisions=4) centres = box.micro_grid_centres - + # For 1x1: half_l=0, half_w=0, so centres should be symmetric around 0 xs = [c[0] for c in centres] ys = [c[1] for c in centres] assert _almost_same(sum(xs) / len(xs), 0.0, tol=0.01) # Mean X = 0 assert _almost_same(sum(ys) / len(ys), 0.0, tol=0.01) # Mean Y = 0 - + def test_micro_grid_centres_centered_on_envelope_fractional(self): """Verify micro_grid_centres are centered on envelope for fractional box.""" box = GridfinityBox(1.25, 0.5, 3, micro_divisions=4) centres = box.micro_grid_centres - + # Mean of centres should equal envelope center (half_l, half_w) xs = [c[0] for c in centres] ys = [c[1] for c in centres] assert _almost_same(sum(xs) / len(xs), box.half_l, tol=0.01) assert _almost_same(sum(ys) / len(ys), box.half_w, tol=0.01) - + def test_micro_feet_span_matches_envelope_x(self): """Verify micro feet X span matches envelope X dimension.""" box = GridfinityBox(1.25, 0.5, 3, micro_divisions=4) centres = box.micro_grid_centres - + foot_size = box.micro_pitch - GR_TOL # 10.0 foot_half = foot_size / 2 - + # Calculate actual feet span in X xs = [c[0] for c in centres] feet_x_min = min(xs) - foot_half feet_x_max = max(xs) + foot_half feet_span_x = feet_x_max - feet_x_min - + # Should match outer_l = 52.0 assert _almost_same(feet_span_x, 52.0, tol=0.01) - + def test_micro_feet_span_matches_envelope_y(self): """Verify micro feet Y span matches envelope Y dimension.""" box = GridfinityBox(1.25, 0.5, 3, micro_divisions=4) centres = box.micro_grid_centres - + foot_size = box.micro_pitch - GR_TOL # 10.0 foot_half = foot_size / 2 - + # Calculate actual feet span in Y ys = [c[1] for c in centres] feet_y_min = min(ys) - foot_half feet_y_max = max(ys) + foot_half feet_span_y = feet_y_max - feet_y_min - + # Should match outer_w = 20.5 assert _almost_same(feet_span_y, 20.5, tol=0.01) - + def test_fractional_renders_without_error(self): """Verify fractional micro-grid box renders successfully.""" box = GridfinityBox(1.25, 0.5, 3, micro_divisions=4) @@ -185,23 +181,21 @@ def test_fractional_renders_without_error(self): box.save_step_file(path=EXPORT_STEP_FILE_PATH) -@pytest.mark.skipif( - SKIP_TEST_MICROGRID, reason="Skipped by SKIP_TEST_MICROGRID env var" -) +@pytest.mark.skipif(SKIP_TEST_MICROGRID, reason="Skipped by SKIP_TEST_MICROGRID env var") class TestMicroGridValidation: """Test input validation for micro-grid parameters.""" - + def test_invalid_micro_divisions(self): """micro_divisions must be 1, 2, or 4.""" with pytest.raises(ValueError): box = GridfinityBox(1, 1, 3, micro_divisions=3) - + def test_invalid_fractional_size(self): """Fractional sizes must align with micro_divisions.""" # 0.33 is not a multiple of 0.25 (1/4) with pytest.raises(ValueError): box = GridfinityBox(0.33, 1, 3, micro_divisions=4) - + def test_valid_fractional_sizes(self): """Valid fractional sizes should work.""" # These should all work without error @@ -211,12 +205,10 @@ def test_valid_fractional_sizes(self): box4 = GridfinityBox(0.5, 1, 3, micro_divisions=2) -@pytest.mark.skipif( - SKIP_TEST_MICROGRID, reason="Skipped by SKIP_TEST_MICROGRID env var" -) +@pytest.mark.skipif(SKIP_TEST_MICROGRID, reason="Skipped by SKIP_TEST_MICROGRID env var") class TestMicroGridSTEPExport: """Test STEP file export with micro-grid bins.""" - + def test_step_export_1x1_micro4(self): """Verify STEP export works for 1x1 micro-grid box.""" box = GridfinityBox(1, 1, 3, micro_divisions=4) @@ -225,7 +217,7 @@ def test_step_export_1x1_micro4(self): assert r is not None if _export_files("microgrid"): box.save_step_file(path=EXPORT_STEP_FILE_PATH) - + def test_step_export_fractional(self): """Verify STEP export works for fractional size box.""" box = GridfinityBox(1.25, 0.5, 3, micro_divisions=4) @@ -233,25 +225,23 @@ def test_step_export_fractional(self): assert r is not None if _export_files("microgrid"): box.save_step_file(path=EXPORT_STEP_FILE_PATH) - + def test_filename_includes_micro(self): """Verify filename includes micro_divisions indicator.""" box = GridfinityBox(1, 1, 3, micro_divisions=4) fn = box.filename() assert "micro4" in fn - + box2 = GridfinityBox(1.25, 0.5, 3, micro_divisions=4) fn2 = box2.filename() assert "1.25" in fn2 or "1.25x0.50" in fn2 assert "micro4" in fn2 -@pytest.mark.skipif( - SKIP_TEST_MICROGRID, reason="Skipped by SKIP_TEST_MICROGRID env var" -) +@pytest.mark.skipif(SKIP_TEST_MICROGRID, reason="Skipped by SKIP_TEST_MICROGRID env var") class TestMicroGridWithFeatures: """Test micro-grid compatibility with other box features.""" - + def test_micro_with_holes(self): """Micro-grid box with magnet holes should render.""" box = GridfinityBox(2, 2, 3, micro_divisions=4, holes=True) @@ -259,19 +249,19 @@ def test_micro_with_holes(self): assert r is not None if _export_files("microgrid"): box.save_step_file(path=EXPORT_STEP_FILE_PATH) - + def test_micro_with_scoops(self): """Micro-grid box with scoops should render.""" box = GridfinityBox(2, 2, 3, micro_divisions=4, scoops=True) r = box.render() assert r is not None - + def test_micro_with_labels(self): """Micro-grid box with labels should render.""" box = GridfinityBox(2, 2, 3, micro_divisions=4, labels=True) r = box.render() assert r is not None - + def test_micro_with_dividers(self): """Micro-grid box with dividers should render.""" box = GridfinityBox(2, 2, 3, micro_divisions=4, length_div=1, width_div=1) @@ -279,12 +269,10 @@ def test_micro_with_dividers(self): assert r is not None -@pytest.mark.skipif( - SKIP_TEST_MICROGRID, reason="Skipped by SKIP_TEST_MICROGRID env var" -) +@pytest.mark.skipif(SKIP_TEST_MICROGRID, reason="Skipped by SKIP_TEST_MICROGRID env var") class TestMicroGridLiteStyleIncompatibility: """Test that lite_style is incompatible with micro_divisions > 1.""" - + def test_lite_style_micro_raises_error(self): """lite_style should raise error with micro_divisions > 1.""" with pytest.raises(ValueError): diff --git a/tests/test_rbox.py b/tests/test_rbox.py index 9da044f..5f58dd7 100644 --- a/tests/test_rbox.py +++ b/tests/test_rbox.py @@ -30,9 +30,7 @@ def _rugged_box(): return b1 -@pytest.mark.skipif( - SKIP_TEST_RBOX, reason="Skipped intentionally by test scope environment variable" -) +@pytest.mark.skipif(SKIP_TEST_RBOX, reason="Skipped intentionally by test scope environment variable") def test_rugged_box(): b1 = _rugged_box() assert b1.filename() == "gf_ruggedbox_5x4x6_fr-hl_sd-hc_stack_lidbp" @@ -43,9 +41,7 @@ def test_rugged_box(): b1.save_step_file(path=EXPORT_STEP_FILE_PATH) -@pytest.mark.skipif( - SKIP_TEST_RBOX, reason="Skipped intentionally by test scope environment variable" -) +@pytest.mark.skipif(SKIP_TEST_RBOX, reason="Skipped intentionally by test scope environment variable") def test_rugged_box_lid(): b1 = _rugged_box() r = b1.render_lid() @@ -56,9 +52,7 @@ def test_rugged_box_lid(): b1.save_step_file(path=EXPORT_STEP_FILE_PATH) -@pytest.mark.skipif( - SKIP_TEST_RBOX, reason="Skipped intentionally by test scope environment variable" -) +@pytest.mark.skipif(SKIP_TEST_RBOX, reason="Skipped intentionally by test scope environment variable") def test_rugged_box_acc(): b1 = _rugged_box() r = b1.render_accessories() @@ -68,9 +62,7 @@ def test_rugged_box_acc(): b1.save_step_file(path=EXPORT_STEP_FILE_PATH) -@pytest.mark.skipif( - SKIP_TEST_RBOX, reason="Skipped intentionally by test scope environment variable" -) +@pytest.mark.skipif(SKIP_TEST_RBOX, reason="Skipped intentionally by test scope environment variable") def test_rugged_box_parts(): b1 = _rugged_box() r = b1.render_handle() @@ -98,9 +90,7 @@ def test_rugged_box_parts(): b1.save_step_file(path=EXPORT_STEP_FILE_PATH) -@pytest.mark.skipif( - SKIP_TEST_RBOX, reason="Skipped intentionally by test scope environment variable" -) +@pytest.mark.skipif(SKIP_TEST_RBOX, reason="Skipped intentionally by test scope environment variable") def test_rugged_box_assembly(): if _export_files("rbox"): b1 = _rugged_box() diff --git a/tests/test_spacer.py b/tests/test_spacer.py index d07e9f1..3e8130c 100644 --- a/tests/test_spacer.py +++ b/tests/test_spacer.py @@ -16,9 +16,7 @@ ) -@pytest.mark.skipif( - SKIP_TEST_SPACER, reason="Skipped intentionally by test scope environment variable" -) +@pytest.mark.skipif(SKIP_TEST_SPACER, reason="Skipped intentionally by test scope environment variable") def test_spacer(): s0 = GridfinityDrawerSpacer(582, 481, tolerance=0.25) assert s0.size_u[0] == 13 @@ -62,9 +60,7 @@ def test_spacer(): assert _almost_same(s1.width_th, 19.80, tol=0.01) -@pytest.mark.skipif( - SKIP_TEST_SPACER, reason="Skipped intentionally by test scope environment variable" -) +@pytest.mark.skipif(SKIP_TEST_SPACER, reason="Skipped intentionally by test scope environment variable") def test_spacer_render(): s1 = GridfinityDrawerSpacer(tolerance=0.25) dx, dy = INCHES(22 + 15 / 16), INCHES(16.25) @@ -106,9 +102,7 @@ def test_spacer_render(): s1.save_step_file(path=EXPORT_STEP_FILE_PATH) -@pytest.mark.skipif( - SKIP_TEST_SPACER, reason="Skipped intentionally by test scope environment variable" -) +@pytest.mark.skipif(SKIP_TEST_SPACER, reason="Skipped intentionally by test scope environment variable") def test_back_only_spacer(): s0 = GridfinityDrawerSpacer(tolerance=0.25, front_and_back=False) dx, dy = 414, 366 From 9add0d69820293e69a0f69b370c6b12b03073e30 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 27 Dec 2025 12:33:33 -0500 Subject: [PATCH 3/3] fix: extend flake8 ignores for star imports and CadQuery patterns --- .github/workflows/ci.yml | 2 +- .github/workflows/release-ci.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index df8f323..445f703 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,7 +28,7 @@ jobs: run: black --check --diff microfinity/ tests/ - name: Lint with flake8 - run: flake8 microfinity/ tests/ --max-line-length=120 --extend-ignore=E203,W503 + run: flake8 microfinity/ tests/ --max-line-length=120 --extend-ignore=E203,W503,F401,F403,F405,E402,F821,W293,W605,F841 test-quick: name: Quick Tests diff --git a/.github/workflows/release-ci.yml b/.github/workflows/release-ci.yml index bb08ccf..4d09f7c 100644 --- a/.github/workflows/release-ci.yml +++ b/.github/workflows/release-ci.yml @@ -26,7 +26,7 @@ jobs: run: black --check --diff microfinity/ tests/ - name: Lint with flake8 - run: flake8 microfinity/ tests/ --max-line-length=120 --extend-ignore=E203,W503 + run: flake8 microfinity/ tests/ --max-line-length=120 --extend-ignore=E203,W503,F401,F403,F405,E402,F821,W293,W605,F841 test-full: name: Test (Python ${{ matrix.python-version }})