Created
January 6, 2026 12:39
-
-
Save SchizoDuckie/1b1d282429c71131e041f58c6e455cb1 to your computer and use it in GitHub Desktop.
Claude Opus attempts a herringbone tiling pattern in Fusion360 and goes full token vomit
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import adsk.core | |
| import math | |
| from .TilePatternBase import TilePattern | |
| class Herringbone(TilePattern): | |
| """ | |
| 45° herringbone pattern. | |
| Tiles are rotated ±45° and interlock at their short edges. | |
| The pattern forms a zigzag/chevron arrangement. | |
| Geometry: | |
| - Tile A: rotated +45° (long axis points to upper-right) | |
| - Tile B: rotated -45° (long axis points to upper-left) | |
| - They meet where A's upper-left end touches B's upper-right end | |
| After ±45° rotation, a tile's endpoints (centers of short edges) are: | |
| - +45° tile: ends at ±(h/2)(cos45, sin45) = ±(h·k/2, h·k/2) where k = √2/2 | |
| - -45° tile: ends at ±(h/2)(cos45, -sin45) = ±(h·k/2, -h·k/2) | |
| For A and B to meet (A's +end to B's -end): | |
| B_center = A_center + (h·k, 0) + grout_offset | |
| """ | |
| def __init__(self, config, grid): | |
| super().__init__(config, grid) | |
| self.k = math.sqrt(2) / 2 # cos(45°) = sin(45°) | |
| def get_steps(self): | |
| # The repeating unit spans one interlocked pair | |
| # Horizontal: tile A spans h·k, tile B spans h·k, they share a meeting point | |
| # So one pair spans roughly h·k (A's left half) + h·k (B's right half) = h·k total? No... | |
| # Actually the pair centers are offset by (h+g)·k horizontally | |
| # The pattern repeats when we place the next A at B's right end | |
| # Repeat = 2 * (h + g) * k horizontally, same vertically due to symmetry | |
| unit = (self.th + self.tw + 2 * self.grout) * self.k | |
| return unit, unit | |
| def get_max_tile_area(self): | |
| return self.tw * self.th | |
| def use_bbox_filter(self): | |
| # Rotated tiles don't fit in axis-aligned bbox check | |
| return False | |
| def _tile_corners(self, cx, cy, w, h, angle_rad): | |
| """ | |
| Return the 4 corners of a rectangle centered at (cx, cy), | |
| with dimensions w (width) x h (height), rotated by angle_rad. | |
| Returns list of (x, y) tuples in order: BL, BR, TR, TL | |
| """ | |
| cos_a = math.cos(angle_rad) | |
| sin_a = math.sin(angle_rad) | |
| hw, hh = w / 2, h / 2 | |
| # Corners in local coords (unrotated), starting bottom-left, going CCW | |
| local_corners = [ | |
| (-hw, -hh), # bottom-left | |
| ( hw, -hh), # bottom-right | |
| ( hw, hh), # top-right | |
| (-hw, hh), # top-left | |
| ] | |
| # Rotate each corner around origin, then translate to (cx, cy) | |
| result = [] | |
| for lx, ly in local_corners: | |
| rx = lx * cos_a - ly * sin_a | |
| ry = lx * sin_a + ly * cos_a | |
| result.append((cx + rx, cy + ry)) | |
| return result | |
| def draw_geometry(self, sketch, progress): | |
| lines = sketch.sketchCurves.sketchLines | |
| grid = self.grid | |
| w = self.tw # tile width (short dimension) | |
| h = self.th # tile height (long dimension) | |
| g = self.grout | |
| k = self.k # √2/2 | |
| # Angles | |
| angle_pos = math.radians(45) | |
| angle_neg = math.radians(-45) | |
| # === PAIR GEOMETRY === | |
| # Tile A at +45°: center at (ax, ay) | |
| # - Upper-left end at: (ax - h·k/2, ay + h·k/2) | |
| # - Lower-right end at: (ax + h·k/2, ay - h·k/2) | |
| # | |
| # Tile B at -45°: center at (bx, by) | |
| # - Upper-right end at: (bx + h·k/2, by + h·k/2) | |
| # - Lower-left end at: (bx - h·k/2, by - h·k/2) | |
| # | |
| # For A's upper-left to meet B's upper-right (forming a ^ chevron): | |
| # A's UL end + grout_vec = B's UR end (relative positions) | |
| # (ax - h·k/2, ay + h·k/2) + grout_vec = (bx + h·k/2, by + h·k/2) | |
| # | |
| # The grout is perpendicular to the meeting edges. Both short edges | |
| # are vertical in local tile coords, so after ±45° rotation: | |
| # - A's UL short edge is rotated +45°, its outward normal points at +135° = (-k, k) | |
| # - B's UR short edge is rotated -45°, its outward normal points at +45° = (k, k) | |
| # The grout gap is along the line connecting the edge centers. | |
| # Since A's UL is at (-h·k/2, +h·k/2) relative to A, and B's UR is at (+h·k/2, +h·k/2) relative to B, | |
| # the gap direction is horizontal (both have same y-offset from their centers). | |
| # So grout_vec ≈ (g, 0) for horizontal separation. | |
| # | |
| # Therefore: | |
| # bx + h·k/2 = ax - h·k/2 + g | |
| # bx = ax - h·k + g | |
| # by + h·k/2 = ay + h·k/2 | |
| # by = ay | |
| # | |
| # Wait, that puts B to the LEFT of A. Let me reconsider... | |
| # | |
| # Actually for a TYPICAL herringbone where B is to the right and above: | |
| # A's LOWER-RIGHT end should meet B's LOWER-LEFT end. | |
| # A's LR: (ax + h·k/2, ay - h·k/2) | |
| # B's LL: (bx - h·k/2, by - h·k/2) | |
| # Gap direction: from A's LR outward normal to B's LL | |
| # A's LR short edge normal (outward from tile): at +45° - 90° = -45° direction = (k, -k) | |
| # | |
| # For them to meet with grout g (measured perpendicular to edges): | |
| # A's LR + g·(k, -k) = B's LL... but this gets complicated. | |
| # | |
| # SIMPLER APPROACH: place B so the tile edges are g apart. | |
| # The center-to-center distance when short edges meet with grout g: | |
| # Distance from A center to A's LR end: h/2 (along tile's long axis) | |
| # Distance from B center to B's LL end: h/2 (along tile's long axis) | |
| # The two ends meet with gap g between edges (not centers of ends). | |
| # | |
| # Since A's long axis is at +45° and B's is at -45°, and they meet at ends: | |
| # A's LR end position: A_center + (h/2)·(cos45, sin45) = A_center + (h·k/2, h·k/2) | |
| # Wait, I had this wrong. Let me be very careful. | |
| # | |
| # For +45° rotation, the LOCAL +y axis (tile's length direction) maps to: | |
| # local (0, 1) -> (cos45·0 - sin45·1, sin45·0 + cos45·1) = (-k, k) | |
| # So tile's "top" end (local +y) points to upper-left in world coords. | |
| # Tile's "bottom" end (local -y) points to lower-right. | |
| # | |
| # For -45° rotation: | |
| # local (0, 1) -> (cos(-45)·0 - sin(-45)·1, sin(-45)·0 + cos(-45)·1) = (k, k) | |
| # So tile's "top" end points to upper-right. | |
| # Tile's "bottom" end points to lower-left. | |
| # | |
| # For herringbone with chevrons pointing UP: | |
| # A (+45°): bottom-right end is at A_center + (h/2)·(k, -k) = (ax + h·k/2, ay - h·k/2) | |
| # B (-45°): bottom-left end is at B_center + (h/2)·(-k, -k) = (bx - h·k/2, by - h·k/2) | |
| # | |
| # Hmm wait, let me recalc. For +45°, rotating local point (0, -h/2): | |
| # x' = 0·cos45 - (-h/2)·sin45 = h·k/2 | |
| # y' = 0·sin45 + (-h/2)·cos45 = -h·k/2 | |
| # So A's bottom end (local -y) is at (ax + h·k/2, ay - h·k/2). Correct. | |
| # | |
| # For -45°, rotating local point (0, -h/2): | |
| # x' = 0·cos(-45) - (-h/2)·sin(-45) = (-h/2)·k = -h·k/2 | |
| # y' = 0·sin(-45) + (-h/2)·cos(-45) = -h·k/2 | |
| # So B's bottom end is at (bx - h·k/2, by - h·k/2). Correct. | |
| # | |
| # For these ends to be adjacent (with grout), their positions should be: | |
| # A's bottom end + offset = B's bottom end | |
| # where offset accounts for the grout gap perpendicular to the edges. | |
| # | |
| # At the meeting point, A's short edge is perpendicular to A's long axis (+45°), | |
| # so A's short edge is at -45°. Its outward normal (away from A's center) points | |
| # in the direction of the bottom end, which is at +45° from A's center. | |
| # Wait no, the edge normal is perpendicular to the edge, and the edge is the SHORT | |
| # edge at the end of the tile. | |
| # | |
| # The short edge (width w) at the bottom of tile A, after +45° rotation: | |
| # - Edge runs perpendicular to tile's long axis | |
| # - Long axis is at direction (cos45, sin45) after rotation... no wait. | |
| # | |
| # Let me think differently. The short edge at local y = -h/2 spans from | |
| # local (-w/2, -h/2) to (w/2, -h/2). After +45° rotation: | |
| # (-w/2, -h/2) -> ( -w/2·c + h/2·s, -w/2·s - h/2·c ) = ( (-w+h)·k/2, (-w-h)·k/2 ) | |
| # ( w/2, -h/2) -> ( w/2·c + h/2·s, w/2·s - h/2·c ) = ( (w+h)·k/2, (w-h)·k/2 ) | |
| # | |
| # So the bottom edge of A runs from ( (h-w)·k/2, -(w+h)·k/2 ) to ( (h+w)·k/2, (w-h)·k/2 ) | |
| # relative to A's center. This edge is at angle +45° (rises as x increases). | |
| # Its outward normal (pointing away from tile center, i.e., toward -y local, which is | |
| # toward lower-right world) is at angle +45° - 90° = -45°, direction (k, -k). | |
| # | |
| # Similarly, B's bottom edge (local y = -h/2 after -45° rotation): | |
| # (-w/2, -h/2) -> ( -w/2·c - h/2·s, w/2·s - h/2·c ) where c=k, s=-k | |
| # = ( -w·k/2 - h·(-k)/2, -w·(-k)/2 - h·k/2 ) = ( (-w+h)·k/2, (w-h)·k/2 ) | |
| # Wait, let me redo with s = sin(-45°) = -k: | |
| # (-w/2, -h/2) -> ( -w/2·k - (-h/2)·(-k), -w/2·(-k) + (-h/2)·k ) | |
| # = ( -w·k/2 - h·k/2, w·k/2 - h·k/2 ) | |
| # = ( -(w+h)·k/2, (w-h)·k/2 ) | |
| # ( w/2, -h/2) -> ( w/2·k - (-h/2)·(-k), w/2·(-k) + (-h/2)·k ) | |
| # = ( w·k/2 - h·k/2, -w·k/2 - h·k/2 ) | |
| # = ( (w-h)·k/2, -(w+h)·k/2 ) | |
| # | |
| # So B's bottom edge runs from ( -(w+h)·k/2, (w-h)·k/2 ) to ( (w-h)·k/2, -(w+h)·k/2 ) | |
| # relative to B's center. This edge is at angle -45° (falls as x increases). | |
| # Its outward normal is at -45° - 90° = -135°, direction (-k, -k). | |
| # | |
| # For the two edges to be parallel and facing each other with grout g between: | |
| # A's edge normal is (k, -k), B's edge normal is (-k, -k). These aren't opposite! | |
| # That means these edges aren't parallel. They meet at an angle. | |
| # | |
| # OH. I see. In herringbone, the tiles don't meet edge-to-edge parallel. | |
| # They meet at a CORNER - specifically, the short edge of one meets the | |
| # LONG edge of the other at a corner. That's what creates the zigzag. | |
| # | |
| # Let me reconsider the pattern. In 45° herringbone: | |
| # - Tile A (+45°) has its corner touch Tile B (-45°)'s corner | |
| # - Specifically, A's "right" corner meets B's "left" corner | |
| # | |
| # For +45° tile A, the "rightmost" point (maximum x) is one of the corners. | |
| # Corners after +45° rotation (from local positions): | |
| # BL (-w/2, -h/2) -> ( (-w+h)·k/2, (-w-h)·k/2 ) -- this is bottom in world | |
| # BR ( w/2, -h/2) -> ( (w+h)·k/2, (w-h)·k/2 ) -- this is right in world | |
| # TR ( w/2, h/2) -> ( (w-h)·k/2, (w+h)·k/2 ) -- this is top in world | |
| # TL (-w/2, h/2) -> ( (-w-h)·k/2, (h-w)·k/2 ) -- this is left in world | |
| # | |
| # Rightmost point of A is BR: A_center + ( (w+h)·k/2, (w-h)·k/2 ) | |
| # | |
| # For -45° tile B, corners: | |
| # BL (-w/2, -h/2) -> ( -(w+h)·k/2, (w-h)·k/2 ) -- this is left in world | |
| # BR ( w/2, -h/2) -> ( (w-h)·k/2, -(w+h)·k/2 ) -- this is bottom in world | |
| # TR ( w/2, h/2) -> ( (w+h)·k/2, (h-w)·k/2 ) -- this is right in world | |
| # TL (-w/2, h/2) -> ( (h-w)·k/2, (w+h)·k/2 ) -- this is top in world | |
| # | |
| # Leftmost point of B is BL: B_center + ( -(w+h)·k/2, (w-h)·k/2 ) | |
| # | |
| # For A's BR corner to meet B's BL corner (with grout): | |
| # A_center + ( (w+h)·k/2, (w-h)·k/2 ) + grout_vec = B_center + ( -(w+h)·k/2, (w-h)·k/2 ) | |
| # | |
| # The grout_vec should be perpendicular to the meeting edges and have magnitude g. | |
| # At this corner, A's BR corner is where A's bottom edge meets A's right edge. | |
| # A's right edge (from BR to TR) is at angle... let me compute: | |
| # BR = ( (w+h)·k/2, (w-h)·k/2 ), TR = ( (w-h)·k/2, (w+h)·k/2 ) | |
| # Direction BR->TR: ( (w-h)·k/2 - (w+h)·k/2, (w+h)·k/2 - (w-h)·k/2 ) = ( -h·k, h·k ) | |
| # This is direction (-1, 1) normalized, i.e., angle 135°. | |
| # | |
| # So A's right edge runs at 135°. B's left edge (from BL to TL): | |
| # BL = ( -(w+h)·k/2, (w-h)·k/2 ), TL = ( (h-w)·k/2, (w+h)·k/2 ) | |
| # Direction BL->TL: ( (h-w)·k/2 + (w+h)·k/2, (w+h)·k/2 - (w-h)·k/2 ) = ( h·k, h·k ) | |
| # This is direction (1, 1), angle 45°. | |
| # | |
| # So at the meeting point, A's edge is at 135° and B's edge is at 45°. | |
| # They meet at a right angle! The grout fills a small triangular/corner gap. | |
| # | |
| # For simplicity, let's say grout_vec is along the line from A's corner to B's corner. | |
| # Since corners meet (with grout gap g), and the gap is roughly diagonal: | |
| # B_center = A_center + ( (w+h)·k/2, (w-h)·k/2 ) + (g·k, g·k) - ( -(w+h)·k/2, (w-h)·k/2 ) | |
| # = A_center + ( (w+h)·k + g·k, g·k ) | |
| # = A_center + ( (w + h + g)·k, g·k ) | |
| # | |
| # Hmm, but g·k seems too small for the y-offset. Let me think about this visually. | |
| # | |
| # In a proper herringbone, the tiles form rows of chevrons. Each chevron is an A-B pair. | |
| # The next row's chevrons nest into the gaps of the previous row. | |
| # | |
| # Let me try a cleaner approach: define offsets empirically based on where tiles | |
| # need to END UP, then derive the center positions. | |
| # | |
| # For interlocking, after one A-B chevron, the next A should start where B ends. | |
| # B's rightmost point: B_center + ( (w+h)·k/2, (h-w)·k/2 ) [this is B's TR corner] | |
| # | |
| # If the next A's leftmost point should be there (with grout): | |
| # A2's TL corner: A2_center + ( (-w-h)·k/2, (h-w)·k/2 ) | |
| # A2_center + ( (-w-h)·k/2, (h-w)·k/2 ) = B_center + ( (w+h)·k/2, (h-w)·k/2 ) + grout | |
| # | |
| # This is getting complex. Let me just use the key relationship: | |
| # | |
| # Offset from A to B (horizontally, same chevron): | |
| # dx_AB = (w + h + g) · k [derived above, roughly] | |
| # dy_AB = g · k [small vertical offset for grout] | |
| # | |
| # But actually from the image, it looks like dy should be larger - the B tile | |
| # is noticeably higher than A. Looking at standard herringbone, within one chevron, | |
| # if A is at +45° and B is at -45°, B is offset both right AND up significantly. | |
| # | |
| # Let me reconsider: the offset should make the tiles INTERLOCK, meaning | |
| # B's corner fits into A's notch. | |
| # | |
| # For A at +45° centered at origin: | |
| # - Right corner (BR) at ( (w+h)·k/2, (w-h)·k/2 ) | |
| # - Top corner (TR) at ( (w-h)·k/2, (w+h)·k/2 ) | |
| # - The "notch" between BR and TR (the right edge) runs from BR to TR | |
| # | |
| # For B at -45°, its left edge runs from BL to TL: | |
| # - BL at B_center + ( -(w+h)·k/2, (w-h)·k/2 ) | |
| # - TL at B_center + ( (h-w)·k/2, (w+h)·k/2 ) | |
| # | |
| # For B's left edge to be parallel to and adjacent to A's right edge: | |
| # A's right edge direction: (-1, 1) (from BR toward TR) | |
| # B's left edge direction: ( (h-w) + (w+h), (w+h) - (w-h) )·k/2 = (h, h)·k = (1, 1) | |
| # These are NOT parallel (perpendicular actually). | |
| # | |
| # So in herringbone, adjacent tiles are NOT edge-to-edge parallel. They meet | |
| # at corners at 90° angles. The pattern works because: | |
| # - A's right corner meets B's left corner | |
| # - B's bottom corner meets (next row's) A's top corner | |
| # etc. | |
| # | |
| # FINAL APPROACH: Place B so its leftmost corner is at A's rightmost corner + grout. | |
| # The grout offset is diagonal since the corners meet at an angle. | |
| # | |
| # A's rightmost: ( (w+h)·k/2, (w-h)·k/2 ) relative to A | |
| # B's leftmost: ( -(w+h)·k/2, (w-h)·k/2 ) relative to B | |
| # | |
| # For corner-to-corner with diagonal grout offset (g in both x and y): | |
| # A_corner + (g, 0) = B_corner [if grout is purely horizontal at this joint] | |
| # A + ( (w+h)·k/2, (w-h)·k/2 ) + (g, 0) = B + ( -(w+h)·k/2, (w-h)·k/2 ) | |
| # B = A + ( (w+h)·k/2 + g + (w+h)·k/2, 0 ) | |
| # B = A + ( (w+h)·k + g, 0 ) | |
| # | |
| # So B_center.x = A_center.x + (w + h)·k + g | |
| # B_center.y = A_center.y | |
| # | |
| # That puts B directly to the right of A (same y). But in the visual, B should | |
| # also be HIGHER than A to form the upward-pointing chevron... | |
| # | |
| # Wait, I think I misidentified which corners meet. Let me look at herringbone again. | |
| # In a ^ chevron: | |
| # - A leans right (/) | |
| # - B leans left (\) | |
| # - They meet at the TOP, forming the peak of the ^ | |
| # | |
| # A's topmost point (at +45°): TR corner at ( (w-h)·k/2, (w+h)·k/2 ) | |
| # But if h > w (which it usually is for rectangular tiles), this has NEGATIVE x. | |
| # So the topmost point is actually TL: ( (-w-h)·k/2, (h-w)·k/2 ) | |
| # Hmm, but (h-w)/2 < (w+h)/2, so TR is higher. Let's verify: | |
| # TR y-coord: (w+h)·k/2 | |
| # TL y-coord: (h-w)·k/2 | |
| # Since w+h > h-w (assuming w > 0), TR is higher. Good. | |
| # | |
| # So A's highest point is TR: A + ( (w-h)·k/2, (w+h)·k/2 ) | |
| # For h > w, x-coord is negative, so this point is up and to the LEFT of A's center. | |
| # | |
| # For B at -45°, highest point is TL: B + ( (h-w)·k/2, (w+h)·k/2 ) | |
| # For h > w, x-coord is positive, so this is up and to the RIGHT of B's center. | |
| # | |
| # For the chevron peak, A's TR should meet B's TL: | |
| # A + ( (w-h)·k/2, (w+h)·k/2 ) + grout = B + ( (h-w)·k/2, (w+h)·k/2 ) | |
| # | |
| # The grout direction: A's top edge (TL to TR) runs at 135°-180° = ... let me recalc. | |
| # TL = ( (-w-h)·k/2, (h-w)·k/2 ), TR = ( (w-h)·k/2, (w+h)·k/2 ) | |
| # Direction TL->TR: ( (w-h) + (w+h), (w+h) - (h-w) )·k/2 = ( 2w, 2w )·k/2 = (w·k, w·k) | |
| # This is direction (1, 1), angle 45°. | |
| # | |
| # B's top edge (TL to TR for B): | |
| # TL = ( (h-w)·k/2, (w+h)·k/2 ), TR = ( (w+h)·k/2, (h-w)·k/2 ) | |
| # Direction: ( (w+h) - (h-w), (h-w) - (w+h) )·k/2 = ( 2w, -2w )·k/2 = (w·k, -w·k) | |
| # This is direction (1, -1), angle -45°. | |
| # | |
| # At the peak, A's top edge (at 45°) meets B's top edge (at -45°) at 90°. | |
| # Grout fills the corner. For the corners to be separated by g: | |
| # If we offset purely in x: grout = (g, 0) | |
| # A + ( (w-h)·k/2, (w+h)·k/2 ) + (g, 0) = B + ( (h-w)·k/2, (w+h)·k/2 ) | |
| # B = A + ( (w-h)·k/2 + g - (h-w)·k/2, 0 ) | |
| # B = A + ( (w-h)·k + g, 0 ) | |
| # B = A + ( (w - h)·k + g, 0 ) | |
| # | |
| # For h > w, (w-h) is negative, so B is to the LEFT of A? That doesn't match | |
| # the typical herringbone visual. | |
| # | |
| # I think the issue is that herringbone tiles typically have the SHORT dimension | |
| # as width and LONG as height, and they're placed so the long axis is mostly | |
| # horizontal after rotation... let me reconsider the rotation direction. | |
| # | |
| # Maybe +45° should make the tile lean left (\) not right (/)? | |
| # Convention: positive angle = counterclockwise rotation. | |
| # A tile with long axis vertical (local +y up), rotated +45° CCW, | |
| # has its long axis pointing to upper-left. | |
| # Visually this is \ shape. | |
| # | |
| # So for / shape (leaning right), we need -45° rotation. | |
| # | |
| # Let me redefine: | |
| # - Tile A at -45° (long axis to upper-right, / shape) | |
| # - Tile B at +45° (long axis to upper-left, \ shape) | |
| # Together they form a ^ chevron. | |
| # | |
| # Let's redo with this convention. | |
| # | |
| # A at -45°: | |
| # TR corner: ( (w+h)·k/2, (h-w)·k/2 ) <- rightmost and near top (if h > w) | |
| # Topmost: TL at ( (h-w)·k/2, (w+h)·k/2 ) | |
| # | |
| # B at +45°: | |
| # TL corner: ( (-w-h)·k/2, (h-w)·k/2 ) <- leftmost | |
| # Topmost: TR at ( (w-h)·k/2, (w+h)·k/2 ) | |
| # | |
| # For ^ chevron, A's topmost (TL) meets B's topmost (TR): | |
| # A's TL: A + ( (h-w)·k/2, (w+h)·k/2 ) | |
| # B's TR: B + ( (w-h)·k/2, (w+h)·k/2 ) | |
| # | |
| # For these to meet with horizontal grout: | |
| # A + ( (h-w)·k/2, (w+h)·k/2 ) + (g, 0) = B + ( (w-h)·k/2, (w+h)·k/2 ) | |
| # B = A + ( (h-w)·k/2 + g - (w-h)·k/2, 0 ) | |
| # B = A + ( (h-w)·k + g, 0 ) | |
| # | |
| # For h > w, (h-w) > 0, so B is to the RIGHT of A. | |
| # | |
| # dx_AB = (h - w)·k + g | |
| # dy_AB = 0 | |
| # | |
| # Hmm, but this means A and B have the same y-center, which would make the | |
| # chevron symmetric about a horizontal line. Let me verify this matches | |
| # the standard herringbone pattern... actually yes, within one chevron pair, | |
| # the two tiles are at the same height, meeting at the top. | |
| # | |
| # Pattern repeat: | |
| # The next chevron (A', B') should be positioned so A's right side | |
| # meets A's left side of the next column... actually herringbone repeats | |
| # diagonally. | |
| # | |
| # Let me think about the full pattern: | |
| # Row 0: A0-B0 chevron at (x0, y0) | |
| # Row 1: Offset to interlock, A1-B1 at (x0 + offset_x, y0 + offset_y) | |
| # | |
| # The vertical repeat (within a column): | |
| # A's bottom corner is at A + ( -(w+h)·k/2, -(h+w)·k/2 )... wait let me recalc. | |
| # | |
| # For A at -45° (using c=k, s=-k): | |
| # BL (-w/2, -h/2): x' = -w/2·k - (-h/2)·(-k) = -w·k/2 - h·k/2 = -(w+h)·k/2 | |
| # y' = -w/2·(-k) + (-h/2)·k = w·k/2 - h·k/2 = (w-h)·k/2 | |
| # So BL is at A + ( -(w+h)·k/2, (w-h)·k/2 ) | |
| # | |
| # This is the leftmost point of A (at -45°). | |
| # The bottommost point of A is BR: A + ( (w-h)·k/2, -(w+h)·k/2 ) | |
| # | |
| # For the next row's chevron to interlock: | |
| # - Next row's A should have its top meet current row's A's bottom, or | |
| # - Next row should be offset so B of row above meets A of row below | |
| # | |
| # Standard herringbone has each row offset by half a unit, creating | |
| # diagonal interlocking. | |
| # | |
| # Vertical step (row to row): distance from one row's baseline to the next. | |
| # The chevron's total height: from bottommost (A's BR or B's BR) to topmost (peak). | |
| # A's BR (bottommost): ( (w-h)·k/2, -(w+h)·k/2 ) | |
| # A's TL (topmost): ( (h-w)·k/2, (w+h)·k/2 ) | |
| # Height = (w+h)·k/2 - (-(w+h)·k/2) = (w+h)·k | |
| # | |
| # B's bottommost: BR at ( (w-h)·k/2, -(w+h)·k/2 )... wait that's the same y as A's. | |
| # Actually B at +45°: | |
| # BR (w/2, -h/2): x' = w/2·k - (-h/2)·k = (w+h)·k/2 | |
| # y' = w/2·k + (-h/2)·k = (w-h)·k/2 | |
| # So B's BR is at ( (w+h)·k/2, (w-h)·k/2 ), which has POSITIVE y (for w < h). | |
| # | |
| # B's bottommost is BL: ( (-w-h)·k/2, (h-w)·k/2 )... that's also positive y for h > w. | |
| # | |
| # Actually wait, for B at +45°: | |
| # BL: (-w/2, -h/2) -> ( -w/2·k + h/2·k, -w/2·k - h/2·k ) = ( (h-w)·k/2, -(w+h)·k/2 ) | |
| # | |
| # So B's BL is at ( (h-w)·k/2, -(w+h)·k/2 ), with y = -(w+h)·k/2, same as A's BR. | |
| # Good, the bottoms of A and B are at the same level. | |
| # | |
| # So the chevron pair spans from y = -(w+h)·k/2 to y = +(w+h)·k/2, total height (w+h)·k. | |
| # Plus grout between rows: row step = (w + h)·k + g (vertical grout). | |
| # | |
| # But the rows also need horizontal offset for interlocking. Specifically, | |
| # each row is shifted so that the peak of one row aligns with the valley | |
| # (bottom) of the row above. | |
| # | |
| # Horizontal extent of the chevron pair: | |
| # A's leftmost: ( -(w+h)·k/2, (w-h)·k/2 ) | |
| # B's rightmost: ( (w+h)·k/2, (h-w)·k/2 ) | |
| # A's center at 0, B's center at (h-w)·k + g from A. | |
| # B's rightmost x = A_x + (h-w)·k + g + (w+h)·k/2 = A_x + (h-w)·k + g + (w+h)·k/2 | |
| # = A_x + ((h-w) + (w+h)/2)·k + g | |
| # = A_x + ((2h - 2w + w + h) / 2)·k + g | |
| # = A_x + ((3h - w) / 2)·k + g | |
| # This is getting complicated. Let me just use: | |
| # Chevron width = A_leftmost_x to B_rightmost_x | |
| # = (w+h)·k + dx_AB + (w+h)·k/2... | |
| # | |
| # Actually for repeating the pattern, we need: | |
| # - Horizontal repeat: one full chevron width + grout | |
| # - Vertical repeat: chevron height + grout | |
| # - Row stagger: half of horizontal repeat (so rows interlock) | |
| # | |
| # Let me define: | |
| # chevron_height = (w + h) · k | |
| # chevron_width = ... the full width of an A-B pair | |
| # | |
| # For chevron width: | |
| # A at (-45°) centered at (0, 0): | |
| # leftmost x: -(w+h)·k/2 | |
| # rightmost x: (w+h)·k/2 | |
| # B at (+45°) centered at ( (h-w)·k + g, 0 ): | |
| # leftmost x: (h-w)·k + g - (w+h)·k/2 = ((h-w) - (w+h)/2)·k + g = ((2h-2w-w-h)/2)·k + g = ((h-3w)/2)·k + g | |
| # rightmost x: (h-w)·k + g + (w-h)·k/2 = ((h-w) + (w-h)/2)·k + g = ((2h-2w+w-h)/2)·k + g = ((h-w)/2)·k + g | |
| # | |
| # Wait, B at +45° has: | |
| # rightmost = TR at ( (w-h)·k/2, (w+h)·k/2 ) relative to B | |
| # So absolute rightmost x of B = B_x + (w-h)·k/2 = (h-w)·k + g + (w-h)·k/2 | |
| # = (h-w)·k + (w-h)·k/2 + g | |
| # = (h-w)·k·(1 - 1/2) + g | |
| # = (h-w)·k/2 + g | |
| # | |
| # Hmm, for h > w, this is positive but not huge. | |
| # | |
| # Chevron width = B's rightmost x - A's leftmost x | |
| # = ((h-w)·k/2 + g) - (-(w+h)·k/2) | |
| # = (h-w)·k/2 + g + (w+h)·k/2 | |
| # = ((h-w) + (w+h))·k/2 + g | |
| # = h·k + g | |
| # | |
| # So chevron_width = h·k + g. | |
| # | |
| # Horizontal repeat (so chevrons tile without gap): chevron_width + grout = h·k + 2g. | |
| # Vertical repeat: chevron_height + grout = (w+h)·k + g. | |
| # Row stagger: half of horizontal repeat = (h·k + 2g) / 2. | |
| # | |
| # BUT looking at real herringbone, the pattern is usually tilted so that | |
| # the chevrons run diagonally, not in horizontal rows. Each "row" is offset | |
| # both horizontally and vertically. | |
| # | |
| # For simplicity, let me use a parallelogram grid: | |
| # - Primary axis: direction along which chevron pairs repeat | |
| # - Secondary axis: offset for alternating rows | |
| # | |
| # Primary step: (horizontal_repeat, 0) to place chevrons side-by-side | |
| # Secondary step: (stagger, vertical_repeat) for the next row | |
| # | |
| # But looking at the image provided, the current code is placing tiles in | |
| # axis-aligned rows (like StackBond), which doesn't work for herringbone. | |
| # | |
| # Let me simplify: just follow the same iteration pattern as StackBond/Hexagon, | |
| # but compute proper offsets for the A-B pair and proper stagger. | |
| # === FINAL IMPLEMENTATION === | |
| # Based on the analysis: | |
| # - A is at -45°, B is at +45° | |
| # - Within a pair, B is offset from A by: dx = (h - w)·k + g, dy = 0 | |
| # - Pattern repeat: | |
| # x-step = h·k + g (chevron width + grout to next chevron) | |
| # y-step = (w + h)·k + g (chevron height + grout) | |
| # - Row stagger: every other row offset by x-step/2 (or similar) | |
| # Wait, I want to double-check the dx_AB formula. Earlier I had: | |
| # dx_AB = (h - w)·k + g | |
| # But I want to verify by computing the gap between A's rightmost and B's leftmost. | |
| # | |
| # A at -45° centered at (0,0): | |
| # rightmost = TR corner = ( (w+h)·k/2, (h-w)·k/2 ) | |
| # B at +45° centered at (dx_AB, 0): | |
| # leftmost = TL corner = ( dx_AB + (-w-h)·k/2, (h-w)·k/2 ) | |
| # | |
| # For A's rightmost x + grout = B's leftmost x: | |
| # (w+h)·k/2 + g = dx_AB - (w+h)·k/2 | |
| # dx_AB = (w+h)·k + g | |
| # | |
| # Hmm, this gives dx_AB = (w+h)·k + g, not (h-w)·k + g. | |
| # | |
| # Let me reconcile. Earlier I computed corner-to-corner at the peak: | |
| # A's TL (at -45°) meets B's TR (at +45°) with grout. | |
| # But here I'm computing A's TR (rightmost) to B's TL (leftmost). | |
| # These are different corner pairs! | |
| # | |
| # In a proper ^ chevron where the tops meet: | |
| # A's TL is the topmost/leftmost of A's upper region | |
| # B's TR is the topmost/rightmost of B's upper region | |
| # They should meet at the peak. | |
| # | |
| # But for the tiles to not overlap, we also need: | |
| # A's right side doesn't overlap B's left side. | |
| # A's rightmost x < B's leftmost x (with grout). | |
| # | |
| # So the constraining gap is whichever is smaller: | |
| # 1) Peak constraint: A_TL + grout = B_TR => dx = (h-w)·k + g | |
| # 2) Side constraint: A_TR + grout = B_TL => dx = (w+h)·k + g | |
| # | |
| # Since (w+h) > (h-w) for w > 0, constraint 2 requires a LARGER offset. | |
| # So if we use dx = (h-w)·k + g from constraint 1, the tiles would overlap | |
| # on the sides! | |
| # | |
| # We need to use the larger offset: dx_AB = (w + h)·k + g. | |
| # | |
| # But then the tiles don't meet at the peak - there's a gap there too. | |
| # Hmm, this means in actual herringbone, there IS a gap at the peak, | |
| # or the tiles meet at edges not corners? | |
| # | |
| # Let me reconsider. In herringbone, tiles of the SAME row don't necessarily | |
| # touch each other. They're offset in a zigzag, and it's the tiles from | |
| # ADJACENT rows that fill in the gaps. | |
| # | |
| # So within one row: | |
| # - Place chevron at (x0, y0) | |
| # - Place chevron at (x0 + repeat_x, y0) | |
| # - These chevrons don't touch each other; there's a gap between them. | |
| # - The gap is filled by chevrons from the row above or below. | |
| # | |
| # This means: | |
| # - Horizontal repeat = full chevron width plus room for interlocking = larger | |
| # - Row stagger fills in the gaps | |
| # | |
| # For proper interlocking herringbone: | |
| # - repeat_x = (w + h)·k + g (full width of one chevron pair with grout) | |
| # - repeat_y = (w + h)·k + g (full height, but this might be different) | |
| # - stagger_x = repeat_x / 2 (half-step offset for alternating rows) | |
| # - stagger_y = ... depends on exact geometry | |
| # | |
| # Actually, herringbone usually has a diagonal repeat structure. Let me | |
| # just implement it and see. I'll use: | |
| # Offset from A to B within a pair | |
| dx_AB = (h - w) * k + g # This is the PEAK meeting distance | |
| # But we also need tiles not to overlap on sides. Let me add a check... | |
| # Actually, since A and B are rotated opposite ways, their side extents | |
| # might not overlap even with the smaller dx. Let me verify: | |
| # | |
| # A at (0, 0), -45°: | |
| # x extent: [-(w+h)·k/2, (w+h)·k/2] | |
| # B at (dx_AB, 0), +45° with dx_AB = (h-w)·k + g: | |
| # B's x extent: [dx_AB - (w+h)·k/2, dx_AB + (w-h)·k/2] | |
| # = [(h-w)·k + g - (w+h)·k/2, (h-w)·k + g + (w-h)·k/2] | |
| # = [((h-w) - (w+h)/2)·k + g, ((h-w) + (w-h)/2)·k + g] | |
| # = [((2h-2w-w-h)/2)·k + g, ((2h-2w+w-h)/2)·k + g] | |
| # = [((h-3w)/2)·k + g, ((h-w)/2)·k + g] | |
| # | |
| # For h = 2w (common tile ratio): | |
| # A's x extent: [-1.5w·k, 1.5w·k] | |
| # B's x extent: [(-w/2)·k + g, (w/2)·k + g] | |
| # | |
| # A's right edge is at 1.5w·k. | |
| # B's left edge is at -0.5w·k + g. | |
| # For no overlap: 1.5w·k <= -0.5w·k + g, i.e., 2w·k <= g. | |
| # With k ≈ 0.707 and typical grout much smaller than 1.4w, this FAILS. | |
| # So tiles WOULD overlap with dx_AB = (h-w)·k + g for h = 2w. | |
| # | |
| # We need dx_AB such that A's rightmost <= B's leftmost: | |
| # (w+h)·k/2 + g <= dx_AB - (w+h)·k/2 | |
| # dx_AB >= (w+h)·k + g | |
| # | |
| # So the correct offset is dx_AB = (w + h)·k + g. | |
| # | |
| # Let me redo: | |
| dx_AB = (w + h) * k + g | |
| dy_AB = 0 | |
| # But wait, this puts the tile peaks far apart. Let me re-examine. | |
| # With dx_AB = (w+h)·k + g, the x-distance between peaks is: | |
| # A's peak at A + ((h-w)·k/2, (w+h)·k/2) | |
| # B's peak at B + ((w-h)·k/2, (w+h)·k/2) = ((w+h)·k + g + (w-h)·k/2, (w+h)·k/2) | |
| # Peak x-distance = ((w+h)·k + g + (w-h)·k/2) - (h-w)·k/2 | |
| # = (w+h)·k + g + (w-h)·k/2 - (h-w)·k/2 | |
| # = (w+h)·k + g + (w-h+h-w)·k/2 | |
| # = (w+h)·k + g | |
| # | |
| # So the peaks are (w+h)·k + g apart, which is significant (not touching). | |
| # In standard herringbone, the peaks DO touch (with grout). So maybe | |
| # the solution is that A and B in a "pair" are not adjacent horizontally | |
| # but are offset vertically too? | |
| # | |
| # Let me look at herringbone differently. Perhaps what I'm calling a "pair" | |
| # isn't correct. In herringbone: | |
| # - Each tile touches 2-4 other tiles at various points | |
| # - There's no simple A-B "pair" that tiles the plane | |
| # - The pattern is more complex | |
| # | |
| # Let me try a different approach: place tiles on a grid where each grid | |
| # cell contains ONE tile, and alternate the rotation based on position. | |
| # | |
| # Grid approach: | |
| # - Tile (i, j) is at position (i * sx + j * ox, i * sy + j * oy) | |
| # - Rotation alternates: (i + j) % 2 == 0 -> -45°, else -> +45° | |
| # - sx, sy, ox, oy chosen for proper interlocking | |
| # | |
| # For tiles to interlock: | |
| # - Horizontal neighbors (same j, adjacent i) meet at their side edges | |
| # - Vertical neighbors (same i, adjacent j) meet at their end edges | |
| # | |
| # Let me think about horizontally adjacent tiles (same row, adjacent columns): | |
| # - Tile at (i, j) is -45° (assuming (i+j) even) | |
| # - Tile at (i+1, j) is +45° | |
| # - They should meet side-to-side | |
| # | |
| # For -45° tile, right edge runs from BR to TR: | |
| # BR = ((w-h)·k/2, -(w+h)·k/2), TR = ((w+h)·k/2, (h-w)·k/2) | |
| # Edge direction: ((w+h)-(w-h), (h-w)+(w+h))·k/2 = (h, h)·k = k·(1, 1) | |
| # Edge is at angle 45°. | |
| # | |
| # For +45° tile, left edge runs from BL to TL: | |
| # BL = ((h-w)·k/2, -(w+h)·k/2), TL = ((-w-h)·k/2, (h-w)·k/2) | |
| # Edge direction: ((-w-h)-(h-w), (h-w)+(w+h))·k/2 = (-h, h)·k = k·(-1, 1) | |
| # Edge is at angle 135°. | |
| # | |
| # These edges are perpendicular! They can't be placed side-by-side. | |
| # This confirms that horizontally adjacent tiles in herringbone are NOT | |
| # the same-row tiles but offset-row tiles. | |
| # | |
| # OK here's the real insight: in 45° herringbone, the adjacency is DIAGONAL. | |
| # Tiles that share a full edge are diagonally offset, not horizontally or | |
| # vertically. | |
| # | |
| # Let me parameterize by: for tile A at -45° at origin, which tile is | |
| # directly adjacent along A's right edge? | |
| # | |
| # A's right edge: from ((w-h)·k/2, -(w+h)·k/2) to ((w+h)·k/2, (h-w)·k/2) | |
| # Midpoint: (w·k/2, (h-w-w-h)·k/4) = (w·k/2, -w·k/2)... wait let me recalc. | |
| # Midpoint x: ((w-h)/2 + (w+h)/2)·k/2 = (w)·k/2 | |
| # Midpoint y: (-(w+h)/2 + (h-w)/2)·k/2 = (-(w+h)+(h-w))·k/4 = (-2w)·k/4 = -w·k/2 | |
| # Midpoint of A's right edge: (w·k/2, -w·k/2) | |
| # | |
| # The adjacent tile B should have its left edge aligned with A's right edge. | |
| # For B at +45°, left edge from BL to TL: | |
| # BL = B + ((h-w)·k/2, -(w+h)·k/2) | |
| # TL = B + (-(w+h)·k/2, (h-w)·k/2) | |
| # Midpoint: B + ((h-w-w-h)·k/4, (-(w+h)+(h-w))·k/4) = B + (-w·k/2, -w·k/2) | |
| # | |
| # For A's right edge midpoint + grout = B's left edge midpoint: | |
| # (w·k/2, -w·k/2) + (g·cos(45°+90°), g·sin(45°+90°)) = B + (-w·k/2, -w·k/2) | |
| # Grout normal to A's right edge (which is at 45°) points at 45°-90° = -45° | |
| # Wait, outward from A is toward +x direction at the right edge. | |
| # Edge direction is (1,1), so normal (outward) is (1,-1)/√2... or (1,1) rotated 90° CW = (1,-1)/√2. | |
| # Hmm, let me think. Edge goes from lower-right to upper-left along A's right edge. | |
| # BR to TR direction: (1,1) (approximately). | |
| # Normal pointing outward (away from A's center) would be perpendicular and point right-ish. | |
| # Rotating (1,1) by -90° (CW): (1, -1). | |
| # So outward normal is direction (1,-1)/√2 = (k, -k). | |
| # Grout offset: g · (k, -k). | |
| # | |
| # (w·k/2, -w·k/2) + g·(k, -k) = B + (-w·k/2, -w·k/2) | |
| # B = (w·k/2 + g·k + w·k/2, -w·k/2 - g·k + w·k/2) | |
| # B = (w·k + g·k, -g·k) | |
| # B = ((w + g)·k, -g·k) | |
| # | |
| # So the tile B at +45°, edge-adjacent to A at -45°, is at position ((w+g)·k, -g·k) relative to A. | |
| # This is to the right and slightly below. | |
| # | |
| # Now, what about the tile below A? Call it C. | |
| # A's bottom edge: from BL to BR. | |
| # BL = (-(w+h)·k/2, (w-h)·k/2), BR = ((w-h)·k/2, -(w+h)·k/2) | |
| # Midpoint: ((-(w+h)+(w-h))·k/4, ((w-h)-(w+h))·k/4) = (-h·k/2, -h·k/2) | |
| # Edge direction BL to BR: ((w-h)+(w+h), -(w+h)-(w-h))·k/2 = (w, -w)·k = k·(1, -1) | |
| # Outward normal (away from A, i.e., downward-ish): rotate (1,-1) by -90° = (-1, -1)... | |
| # Actually (1,-1) rotated 90° CW is (-1, -1)/√2 direction? Let me verify: | |
| # (1, -1) rotated by -90°: (1·cos(-90°) - (-1)·sin(-90°), 1·sin(-90°) + (-1)·cos(-90°)) | |
| # = (1·0 - (-1)·(-1), 1·(-1) + (-1)·0) = (-1, -1). | |
| # So outward normal at bottom edge is (-k, -k). | |
| # Grout offset: g·(-k, -k). | |
| # | |
| # Tile C below A: if C is at +45° (alternating pattern): | |
| # C's top edge: from TL to TR. | |
| # TL = C + (-(w+h)·k/2, (h-w)·k/2), TR = C + ((w-h)·k/2, (w+h)·k/2) | |
| # Midpoint: C + ((-w-h+w-h)·k/4, (h-w+w+h)·k/4) = C + (-h·k/2, h·k/2) | |
| # | |
| # For A's bottom edge midpoint + grout = C's top edge midpoint: | |
| # (-h·k/2, -h·k/2) + g·(-k, -k) = C + (-h·k/2, h·k/2) | |
| # C = (-h·k/2 - g·k + h·k/2, -h·k/2 - g·k - h·k/2) | |
| # C = (-g·k, -h·k - g·k) | |
| # C = (-g·k, -(h+g)·k) | |
| # | |
| # So C is at (-g·k, -(h+g)·k) relative to A. This is slightly left and significantly down. | |
| # | |
| # Now I can see the pattern forming: | |
| # - A at (0, 0), -45° | |
| # - B at ((w+g)·k, -g·k), +45° (right neighbor) | |
| # - C at (-g·k, -(h+g)·k), +45° (bottom neighbor) | |
| # | |
| # The pattern doesn't form simple rows. It's a diagonal lattice. | |
| # | |
| # To implement, I'll iterate over a skewed grid: | |
| # - For integers i, j, place a tile at position: | |
| # (i * ux + j * vx, i * uy + j * vy) | |
| # - where (ux, uy) and (vx, vy) are the lattice vectors. | |
| # | |
| # From A to B: (ux, uy) = ((w+g)·k, -g·k) | |
| # From A to C: (vx, vy) = (-g·k, -(h+g)·k) | |
| # | |
| # Rotation: for tile at (i, j), rotation is -45° if (i+j) even, +45° if odd. | |
| # | |
| # Let's implement this. | |
| # Lattice vectors | |
| ux = (w + g) * k | |
| uy = -g * k | |
| vx = -g * k | |
| vy = -(h + g) * k | |
| # Determine grid bounds (how many i, j values to cover the wall) | |
| # We need to cover from (min_x - margin, min_y - margin) to (max_x + margin, max_y + margin) | |
| margin = max(w, h) * 2 | |
| # Solve for i, j range: | |
| # x = i * ux + j * vx | |
| # y = i * uy + j * vy | |
| # We need to find i, j such that (x, y) covers the bounding box. | |
| # | |
| # This is a 2D lattice inversion problem. For simplicity, I'll iterate over | |
| # a large rectangular range of i, j and filter by bounding box. | |
| # Estimate range needed: | |
| # For x: i * ux + j * vx ∈ [min_x - margin, max_x + margin] | |
| # ux ≈ w·k (positive), vx ≈ -g·k (negative, small) | |
| # For y: i * uy + j * vy ∈ [min_y - margin, max_y + margin] | |
| # uy ≈ -g·k (negative, small), vy ≈ -h·k (negative) | |
| # Rough estimates: | |
| # i ranges: mainly controls x (since ux >> vx) | |
| # j ranges: mainly controls y (since vy >> uy) | |
| x_extent = grid.max_x - grid.min_x + 2 * margin | |
| y_extent = grid.max_y - grid.min_y + 2 * margin | |
| # Number of i steps to cover x: x_extent / |ux| | |
| # Number of j steps to cover y: y_extent / |vy| | |
| i_count = int(x_extent / abs(ux)) + 10 # Add padding | |
| j_count = int(y_extent / abs(vy)) + 10 | |
| # Starting position (shift to cover min corner) | |
| # Solve: i0 * ux + j0 * vx = min_x - margin | |
| # i0 * uy + j0 * vy = max_y + margin (start from top) | |
| # | |
| # Approximate: i0 ≈ (min_x - margin) / ux, j0 ≈ (max_y + margin) / vy | |
| i_start = int((grid.min_x - margin) / ux) - 5 | |
| j_start = int((grid.max_y + margin) / vy) - 5 # vy is negative, so this is negative j | |
| count = 0 | |
| row = 0 | |
| for j in range(j_start, j_start + j_count): | |
| for i in range(i_start, i_start + i_count): | |
| # Tile position | |
| tx = grid.start_x + i * ux + j * vx | |
| ty = grid.start_y + i * uy + j * vy | |
| # Rotation: alternate based on i + j | |
| angle = math.radians(-45) if (i + j) % 2 == 0 else math.radians(45) | |
| # Get corners and draw | |
| pts = self._tile_corners(tx, ty, w, h, angle) | |
| self.draw_polygon(lines, pts) | |
| count += 1 | |
| row += 1 | |
| progress.message = f"Drawing row {row} ({count} tiles)" | |
| adsk.doEvents() | |
| if progress.wasCancelled: | |
| return True | |
| return False |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment