Surn commited on
Commit
8a7b065
·
1 Parent(s): ae12d34

HURA 0.2.0

Browse files
Files changed (3) hide show
  1. README.md +2 -0
  2. app.py +48 -18
  3. utils/hex_hura.py +359 -43
README.md CHANGED
@@ -48,6 +48,7 @@ Welcome to HexaGrid Creator, the ultimate tool for transforming your images into
48
  - **Pre-rendered Maps:** Access a library of ready-to-use hexagon maps for quick customization.
49
  - **Add Margins:** Add customizable margins around your images for a polished print-ready look.
50
  - **Sketch Pad Integration:** Directly sketch on images to modify or replace them before further processing.
 
51
 
52
 
53
  ### Why You'll Love It
@@ -68,6 +69,7 @@ Welcome to HexaGrid Creator, the ultimate tool for transforming your images into
68
  - **TRELLIS Depth & 3D Model Generation:** Create detailed depth maps and 3D models, complete with GLB and Gaussian file extraction.
69
  - **Add Margins:** Fine-tune image margins for a professional finish.
70
  - **Sketch Pad Integration:** Use the built-in sketch pad to edit images on the fly before processing.
 
71
 
72
  Join the hive and start creating with HexaGrid Creator today!
73
 
 
48
  - **Pre-rendered Maps:** Access a library of ready-to-use hexagon maps for quick customization.
49
  - **Add Margins:** Add customizable margins around your images for a polished print-ready look.
50
  - **Sketch Pad Integration:** Directly sketch on images to modify or replace them before further processing.
51
+ - **HURA:** Generate HURA (Hexagonal Uniformly Redundant Arrays) patterns for sketches and masks
52
 
53
 
54
  ### Why You'll Love It
 
69
  - **TRELLIS Depth & 3D Model Generation:** Create detailed depth maps and 3D models, complete with GLB and Gaussian file extraction.
70
  - **Add Margins:** Fine-tune image margins for a professional finish.
71
  - **Sketch Pad Integration:** Use the built-in sketch pad to edit images on the fly before processing.
72
+ - **HURA:** Generate HURA (Hexagonal Uniformly Redundant Arrays) patterns for sketches and masks
73
 
74
  Join the hive and start creating with HexaGrid Creator today!
75
 
app.py CHANGED
@@ -1,4 +1,4 @@
1
- from ast import Str
2
  from tokenize import String
3
  import gradio as gr
4
  from numba.core.types import string
@@ -25,6 +25,7 @@ from trellis.pipelines import TrellisImageTo3DPipeline
25
  from trellis.representations import Gaussian, MeshExtractResult
26
  from trellis.utils import render_utils, postprocessing_utils
27
  from pathlib import Path
 
28
 
29
  import logging
30
  #logging.getLogger("transformers.modeling_utils").setLevel(logging.ERROR)
@@ -746,6 +747,7 @@ def on_input_image_change(image_path):
746
  return None, gr.update()
747
  img, img_path = convert_to_rgba_png(image_path)
748
  width, height = img.size
 
749
  return [img_path, gr.update(width=width, height=height)]
750
 
751
  def update_sketch_dimensions(input_image, sketch_image):
@@ -761,18 +763,21 @@ def update_sketch_dimensions(input_image, sketch_image):
761
  return [sk_img, gr.update()]
762
 
763
 
764
- def composite_with_control_sync(input_image, sketch_image, slider_value):
 
765
  # Load the images using open_image() if they are provided as file paths.
766
- in_img = open_image(input_image) if isinstance(input_image, str) else input_image
767
- sk_img_path, _ = get_image_from_dict(sketch_image)
768
- sk_img = open_image(sk_img_path)
 
 
769
 
770
- # Resize sketch image if dimensions don't match input image.
771
- if in_img.size != sk_img.size:
772
- sk_img = sk_img.resize(in_img.size, Image.LANCZOS)
773
 
774
  # Now composite using the original alpha_composite_with_control function.
775
- result_img = alpha_composite_with_control(in_img, sk_img, slider_value)
776
  return result_img
777
 
778
  def replace_with_sketch_image(sketch_image, replace_current_lut_example_img: bool = False):
@@ -1168,14 +1173,16 @@ with gr.Blocks(css_paths="style_20250314.css", title=title, theme='Surn/beeuty',
1168
  HexaGrid Creator is a web-based application that allows you to apply a hexagon grid overlay to any image. You can customize the size, color, and opacity of the hexagons, as well as the background and border colors. The result is a visually striking image that looks like it was made from hexagonal tiles!
1169
 
1170
  ### What Can You Do?
1171
- - **Generate Hexagon Grids:** Create beautiful hexagon grid overlays on any image with fully customizable parameters. (also square and triangles)
1172
- - **AI-Powered Image Generation:** Use advanced AI models to generate images based on your prompts and apply hexagon grids to them.
1173
- - **Color Exclusion:** Select and exclude specific colors from your hexagon grid for a cleaner and more refined look.
1174
- - **Interactive Customization:** Adjust hexagon size, border size, rotation, background color, and more in real-time.
1175
- - **Depth and 3D Model Generation:** Generate depth maps and 3D models from your images for enhanced visualization.
1176
- - **Image Filter [Look-Up Table (LUT)] Application:** Apply filters (LUTs) to your images for color grading and enhancement.
1177
- - **Pre-rendered Maps:** Access a library of pre-rendered hexagon maps for quick and easy customization.
1178
- - **Add Margins:** Add customizable margins around your images for a polished finish.
 
 
1179
 
1180
  ### Why You'll Love It
1181
  - **Fun and Easy to Use:** With an intuitive interface and real-time previews, creating hexagon grids has never been this fun!
@@ -1194,6 +1201,8 @@ with gr.Blocks(css_paths="style_20250314.css", title=title, theme='Surn/beeuty',
1194
  - **Image Filter [Look-Up Table (LUT)] Application:** Apply filters (LUTs) to your images for color grading and enhancement.
1195
  - **Depth and 3D Model Generation:** Create depth maps and 3D models from your images for enhanced visualization.
1196
  - **Add Margins:** Customize margins around your images for a polished finish.
 
 
1197
 
1198
  Join the hive and start creating with HexaGrid Creator today!
1199
 
@@ -1300,11 +1309,32 @@ with gr.Blocks(css_paths="style_20250314.css", title=title, theme='Surn/beeuty',
1300
  composite_button = gr.Button("Composite to Input Image", elem_classes="solid")
1301
  composite_sketch_button = gr.Button("Composite to Sketh", elem_classes="solid")
1302
  with gr.Accordion("Blur", open = False):
1303
- with gr.Row():
1304
  blur_amount = gr.Slider(label="Blur Amount", minimum=0, maximum=100, value=5, interactive=True)
1305
  with gr.Row():
1306
  blur_button = gr.Button("Blur Input Image", elem_classes="solid")
1307
  blur_sketch_button = gr.Button("Blur Sketch", elem_classes="solid")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1308
  with gr.Tabs(selected="hex_gen") as input_tabs:
1309
  with gr.Tab("HexaGrid Generation", id="hex_gen") as hexa_gen_tab:
1310
  with gr.Column(elem_classes="outline-important"):
 
1
+ #from ast import Constant
2
  from tokenize import String
3
  import gradio as gr
4
  from numba.core.types import string
 
25
  from trellis.representations import Gaussian, MeshExtractResult
26
  from trellis.utils import render_utils, postprocessing_utils
27
  from pathlib import Path
28
+ import utils.hex_hura as hex_hura
29
 
30
  import logging
31
  #logging.getLogger("transformers.modeling_utils").setLevel(logging.ERROR)
 
747
  return None, gr.update()
748
  img, img_path = convert_to_rgba_png(image_path)
749
  width, height = img.size
750
+ hex_components["set_height_width_hura_image"](width, height)
751
  return [img_path, gr.update(width=width, height=height)]
752
 
753
  def update_sketch_dimensions(input_image, sketch_image):
 
763
  return [sk_img, gr.update()]
764
 
765
 
766
+ def composite_with_control_sync(input_image, new_image, slider_value):
767
+
768
  # Load the images using open_image() if they are provided as file paths.
769
+ new_img_path, _ = get_image_from_dict(new_image)
770
+ if input_image is None:
771
+ return new_img_path
772
+ in_img = open_image(input_image) if isinstance(input_image, (dict,str)) else input_image
773
+ new_img = open_image(new_img_path)
774
 
775
+ # Resize new image if dimensions don't match input image.
776
+ if in_img.size != new_img.size:
777
+ new_img = new_img.resize(in_img.size, Image.LANCZOS)
778
 
779
  # Now composite using the original alpha_composite_with_control function.
780
+ result_img = alpha_composite_with_control(in_img, new_img, slider_value)
781
  return result_img
782
 
783
  def replace_with_sketch_image(sketch_image, replace_current_lut_example_img: bool = False):
 
1173
  HexaGrid Creator is a web-based application that allows you to apply a hexagon grid overlay to any image. You can customize the size, color, and opacity of the hexagons, as well as the background and border colors. The result is a visually striking image that looks like it was made from hexagonal tiles!
1174
 
1175
  ### What Can You Do?
1176
+ - **Generate Hexagon Grids:** Create stunning hexagon, square, or triangle grid overlays with fully customizable parameters.
1177
+ - **AI-Powered Image Generation:** Use advanced AI models and LoRA weights to generate images from your prompts and apply unique grid overlays.
1178
+ - **Color Exclusion:** Pick and exclude specific colors from your hexagon grid for improved clarity.
1179
+ - **Interactive Customization:** Adjust grid size, border size, rotation, background color, and more—all in real-time.
1180
+ - **Depth & 3D Model Generation:** Generate depth maps and interactive 3D models (with GLB and Gaussian extraction) for enhanced visualization.
1181
+ - **Image Filter [LUT] Application:** Apply advanced color grading filters with live previews using LUT files.
1182
+ - **Pre-rendered Maps:** Access a library of ready-to-use hexagon maps for quick customization.
1183
+ - **Add Margins:** Add customizable margins around your images for a polished print-ready look.
1184
+ - **Sketch Pad Integration:** Directly sketch on images to modify or replace them before further processing.
1185
+ - **HURA:** Generate HURA (Hexagonal Uniformly Redundant Arrays) patterns for sketches and masks
1186
 
1187
  ### Why You'll Love It
1188
  - **Fun and Easy to Use:** With an intuitive interface and real-time previews, creating hexagon grids has never been this fun!
 
1201
  - **Image Filter [Look-Up Table (LUT)] Application:** Apply filters (LUTs) to your images for color grading and enhancement.
1202
  - **Depth and 3D Model Generation:** Create depth maps and 3D models from your images for enhanced visualization.
1203
  - **Add Margins:** Customize margins around your images for a polished finish.
1204
+ - **Sketch Pad Integration:** Use the built-in sketch pad to edit images on the fly before processing.
1205
+ - **HURA:** Generate HURA (Hexagonal Uniformly Redundant Arrays) patterns for sketches and masks
1206
 
1207
  Join the hive and start creating with HexaGrid Creator today!
1208
 
 
1309
  composite_button = gr.Button("Composite to Input Image", elem_classes="solid")
1310
  composite_sketch_button = gr.Button("Composite to Sketh", elem_classes="solid")
1311
  with gr.Accordion("Blur", open = False):
1312
+ with gr.Row():
1313
  blur_amount = gr.Slider(label="Blur Amount", minimum=0, maximum=100, value=5, interactive=True)
1314
  with gr.Row():
1315
  blur_button = gr.Button("Blur Input Image", elem_classes="solid")
1316
  blur_sketch_button = gr.Button("Blur Sketch", elem_classes="solid")
1317
+ with gr.Accordion("Generate Hex Hura Background", open = False):
1318
+ hex_components = hex_hura.render()
1319
+ with gr.Row():
1320
+ hura_alpha_composite_slider = gr.Slider(0,100,50,0.5, label="HURA Transparancy", elem_id="hura_alpha_composite_slider", interactive=True)
1321
+ with gr.Row():
1322
+ hura_button = gr.Button("Composite Input Image", elem_classes="solid")
1323
+ hura_sketch_button = gr.Button("Composite Sketh Image", elem_classes="solid")
1324
+
1325
+ hura_sketch_button.click(
1326
+ fn=composite_with_control_sync,
1327
+ inputs=[sketch_image, hex_components["target_image"], hura_alpha_composite_slider],
1328
+ outputs=[sketch_image],
1329
+ scroll_to_output=True
1330
+ )
1331
+ hura_button.click(
1332
+ fn=composite_with_control_sync,
1333
+ inputs=[input_image, hex_components["target_image"], hura_alpha_composite_slider],
1334
+ outputs=[input_image],
1335
+ scroll_to_output=True
1336
+ )
1337
+
1338
  with gr.Tabs(selected="hex_gen") as input_tabs:
1339
  with gr.Tab("HexaGrid Generation", id="hex_gen") as hexa_gen_tab:
1340
  with gr.Column(elem_classes="outline-important"):
utils/hex_hura.py CHANGED
@@ -1,12 +1,109 @@
 
 
 
1
  from PIL import Image
2
  import math
 
 
3
 
4
- v = 139
5
- r = 5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
 
7
- # Define the smoothstep function for vignette effect
8
  def smoothstep(edge0, edge1, x):
9
- """Smoothly interpolate between edge0 and edge1 based on x."""
 
 
 
10
  if edge0 == edge1:
11
  return 0.0 if x < edge0 else 1.0
12
  t = min(max((x - edge0) / (edge1 - edge0), 0.0), 1.0)
@@ -15,16 +112,24 @@ def smoothstep(edge0, edge1, x):
15
  # Define the hexagon function to compute coordinates
16
  def hexagon(p):
17
  """
18
- Compute hexagon coordinates and distances for a given point p.
19
- Returns (h_x, h_y, e, f) where h_x and h_y are integer coordinates.
 
 
 
 
 
 
 
 
20
  """
21
  # Transform to hexagonal coordinate system
22
- q = (p[0] * 2.0 * 0.5773503, p[1] + p[0] * 0.5773503)
23
  pi = (math.floor(q[0]), math.floor(q[1]))
24
  pf = (q[0] - pi[0], q[1] - pi[1])
25
- v = (pi[0] + pi[1]) % 3.0
26
- ca = 1.0 if v >= 1.0 else 0.0
27
- cb = 1.0 if v >= 2.0 else 0.0
28
  ma = (1.0 if pf[1] >= pf[0] else 0.0, 1.0 if pf[0] >= pf[1] else 0.0)
29
  temp = (
30
  1.0 - pf[1] + ca * (pf[0] + pf[1] - 1.0) + cb * (pf[1] - 2.0 * pf[0]),
@@ -38,38 +143,41 @@ def hexagon(p):
38
  h_xy = (pi[0] + ca - cb * ma[0], pi[1] + ca - cb * ma[1])
39
  return (h_xy[0], h_xy[1], e, f)
40
 
41
- def ura(p, r=1.0, v=10.0):
 
42
  """
43
- Generate a grayscale value based on hexagonal coordinates, emulating the HLSL URA function.
44
-
45
- Parameters:
46
- p (tuple): A 2-tuple (x, y) of coordinates.
47
- r (float): Multiplier for p[0]. Default is 1.0.
48
- v (float): Modulus value controlling the pattern frequency. Default is 10.0.
49
 
 
 
 
50
  Returns:
51
- float: 1.0 if no pattern match is found, otherwise 0.0.
52
- """
53
- import math
54
- l = math.fmod(p[1] + r * p[0], v)
 
 
 
 
 
 
 
55
  rz = 1.0
56
- for i in range(1, int(v/2)):
57
  if math.isclose(math.fmod(i * i, v), l, abs_tol=1e-6):
58
  rz = 0.0
59
  break
60
  return rz
61
 
62
- # Define the color palette
63
- default_colors = [
64
- (255, 0, 0), # Red
65
- (0, 255, 0), # Green
66
- (0, 0, 255) # Blue
67
- ]
68
-
69
  # Generate the image with colorful_hexagonal pattern
70
- def generate_image_color(width, height, colors=default_colors):
71
  """Generate an RGB image with a colorful hexagonal pattern."""
72
  img = Image.new('RGB', (width, height))
 
 
 
 
73
  aspect = width / height
74
  for j in range(height):
75
  for i in range(width):
@@ -81,14 +189,15 @@ def generate_image_color(width, height, colors=default_colors):
81
  p_y = q_y * 2.0 - 1.0
82
  p = (p_x, p_y)
83
  # Scale coordinates for pattern frequency
84
- h = hexagon((p[0] * 21.0, p[1] * 21.0))
85
  h_xy = (int(h[0]), int(h[1]))
86
  # Assign color based on hexagon coordinates
87
- color_index = (h_xy[0] + h_xy[1]) % len(colors)
 
88
  col = colors[color_index]
89
  # Apply vignette effect
90
  q = (q_x * 2.0 - 1.0, q_y * 2.0 - 1.0)
91
- vignette = smoothstep(1.01, 0.97, max(abs(q[0]), abs(q[1])))
92
  col = tuple(int(c * vignette) for c in col)
93
  # Set the pixel color
94
  img.putpixel((i, j), col)
@@ -104,7 +213,7 @@ def generate_image_grayscale(width, height):
104
  p_x = (q_x * 2.0 - 1.0) * aspect
105
  p_y = q_y * 2.0 - 1.0
106
  p = (p_x, p_y)
107
- h = hexagon((p[0] * 21.0, p[1] * 21.0))
108
  rz = ura(h[:2])
109
  smooth = smoothstep(-0.2, 0.13, h[2])
110
  if rz > 0.5:
@@ -112,16 +221,223 @@ def generate_image_grayscale(width, height):
112
  else:
113
  col = 1.0 - smooth
114
  q = (q_x * 2.0 - 1.0, q_y * 2.0 - 1.0)
115
- vignette = smoothstep(1.01, 0.97, max(abs(q[0]), abs(q[1])))
116
  col *= vignette
117
- color = int(col * 255)
118
  img.putpixel((i, j), (color, color, color))
119
  return img
120
 
121
- # Example usage
122
- #if __name__ == "__main__":
123
- # # Set image dimensions
124
- # width, height = 800, 600
125
- # img = generate_image(width, height)
126
- # img.save('colorful_hexagon_pattern.png')
127
- # print("Image saved as 'colorful_hexagon_pattern.png'")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # HURA (Hexagonal Uniformly Redundant Arrays) are used for aperture masks and imaging, and encoding.
2
+ # check it out https://ntrs.nasa.gov/citations/19850026627
3
+ # by Surn (Charles Fettinger) 4/5/2025
4
  from PIL import Image
5
  import math
6
+ import gradio as gr
7
+ from tempfile import NamedTemporaryFile
8
 
9
+ from transformers.models.deprecated.vit_hybrid import image_processing_vit_hybrid
10
+ import utils.constants as constants
11
+ import utils.color_utils as color_utils
12
+
13
+
14
+ class HuraConfig:
15
+ """Configuration for Hexagonal Uniformly Redundant Array pattern generation."""
16
+
17
+ def __init__(self):
18
+ # Core parameters
19
+ self.v = 139 # Prime number parameter (affects pattern complexity)
20
+ self.r = 42 # Pattern frequency parameter
21
+ self.version = "0.2.0"
22
+
23
+ # Pattern generation constants
24
+ self.hex_ratio = 0.5773503 # sqrt(3)/3
25
+ self.pattern_scale = 21.0 # Controls pattern frequency
26
+ self.vignette_inner = 0.97
27
+ self.vignette_outer = 1.01
28
+
29
+ # Colors
30
+ self.default_colors = [
31
+ (255, 0, 0), # Red
32
+ (0, 255, 0), # Green
33
+ (0, 0, 255) # Blue
34
+ ]
35
+
36
+ # Prime number calculation
37
+ self.prime_range_start = 1
38
+ self.prime_range_end = 5001
39
+ self._primes_cache = None # Will be lazily loaded
40
+
41
+ def get_v(self):
42
+ """Get the current V parameter value."""
43
+ return self.v
44
+
45
+ def set_v(self, value):
46
+ """Set the V parameter value."""
47
+ if not isinstance(value, (int, float)) or value < 1:
48
+ raise ValueError(f"V value must be a positive float, got {value}")
49
+ self.v = value
50
+
51
+ def get_r(self):
52
+ """Get the current R parameter value."""
53
+ return self.r
54
+
55
+ def set_r(self, value):
56
+ """Set the R parameter value."""
57
+ if not isinstance(value, (int, float)) or value < 1:
58
+ raise ValueError(f"R value must be a positive float, got {value}")
59
+ self.r = value
60
+
61
+ def get_primes(self):
62
+ """Get or calculate the list of primes in the configured range."""
63
+ if self._primes_cache is None:
64
+ self._primes_cache = get_primes_in_range(self.prime_range_start, self.prime_range_end)
65
+ return self._primes_cache
66
+
67
+ def find_nearest_prime(self, value):
68
+ """Find the nearest prime number to the given value."""
69
+ return min(self.get_primes(), key=lambda x: abs(x - value))
70
+
71
+ def reset_colors(self):
72
+ """Reset default colors to original values."""
73
+ self.default_colors = [
74
+ (255, 0, 0), # Red
75
+ (0, 255, 0), # Green
76
+ (0, 0, 255) # Blue
77
+ ]
78
+ return self.default_colors
79
+
80
+ # Initialize the HuraConfig instance
81
+ config = HuraConfig()
82
+
83
+ # For backwards compatibility - consider deprecating these
84
+ __version__ = config.version
85
+ _V = config.v
86
+ _R = config.r
87
+
88
+ def get_v():
89
+ return config.get_v()
90
+
91
+ def set_v(val):
92
+ config.set_v(val)
93
+
94
+ def get_r():
95
+ return config.get_r()
96
+
97
+ def set_r(val):
98
+ config.set_r(val)
99
+
100
+ state_colors = []
101
 
 
102
  def smoothstep(edge0, edge1, x):
103
+ """
104
+ Smoothstep function for vignette effect.
105
+ Smoothly interpolate between edge0 and edge1 based on x.
106
+ """
107
  if edge0 == edge1:
108
  return 0.0 if x < edge0 else 1.0
109
  t = min(max((x - edge0) / (edge1 - edge0), 0.0), 1.0)
 
112
  # Define the hexagon function to compute coordinates
113
  def hexagon(p):
114
  """
115
+ Compute hexagon coordinates and metrics for point p.
116
+
117
+ Args:
118
+ p (tuple): Normalized point (x,y) in [-aspect,aspect] � [-1,1] range
119
+
120
+ Returns:
121
+ tuple: (hex_x, hex_y, edge_distance, center_distance)
122
+ - hex_x, hex_y: Integer coordinates of the hexagon cell
123
+ - edge_distance: Distance to nearest edge (0-1)
124
+ - center_distance: Distance to cell center (0-1)
125
  """
126
  # Transform to hexagonal coordinate system
127
+ q = (p[0] * 2.0 * config.hex_ratio, p[1] + p[0] * config.hex_ratio)
128
  pi = (math.floor(q[0]), math.floor(q[1]))
129
  pf = (q[0] - pi[0], q[1] - pi[1])
130
+ mod_val = (pi[0] + pi[1]) % 3.0 # renamed from v
131
+ ca = 1.0 if mod_val >= 1.0 else 0.0
132
+ cb = 1.0 if mod_val >= 2.0 else 0.0
133
  ma = (1.0 if pf[1] >= pf[0] else 0.0, 1.0 if pf[0] >= pf[1] else 0.0)
134
  temp = (
135
  1.0 - pf[1] + ca * (pf[0] + pf[1] - 1.0) + cb * (pf[1] - 2.0 * pf[0]),
 
143
  h_xy = (pi[0] + ca - cb * ma[0], pi[1] + ca - cb * ma[1])
144
  return (h_xy[0], h_xy[1], e, f)
145
 
146
+ # important note: this is not a true hexagonal pattern, but a hexagonal grid
147
+ def ura(p):
148
  """
149
+ Generate binary pattern value based on Uniformly Redundant Array algorithm.
 
 
 
 
 
150
 
151
+ Args:
152
+ p (tuple): Hexagon coordinates (x,y)
153
+
154
  Returns:
155
+ float: 1.0 for pattern, 0.0 for background
156
+
157
+ future consideration.. add animation
158
+ #ifdef INCREMENT_R
159
+ float l = mod(p.y + floor(time*1.5)*p.x, v);
160
+ #else
161
+ float l = mod(p.y + r*p.x, v);
162
+ """
163
+ r = get_r()
164
+ v = get_v()
165
+ l = math.fmod(abs(p[1]) + r * abs(p[0]), v)
166
  rz = 1.0
167
+ for i in range(1, int(v/2) + 1):
168
  if math.isclose(math.fmod(i * i, v), l, abs_tol=1e-6):
169
  rz = 0.0
170
  break
171
  return rz
172
 
 
 
 
 
 
 
 
173
  # Generate the image with colorful_hexagonal pattern
174
+ def generate_image_color(width, height, colors=None):
175
  """Generate an RGB image with a colorful hexagonal pattern."""
176
  img = Image.new('RGB', (width, height))
177
+ if colors is None or colors == []:
178
+ colors = config.default_colors
179
+ r = config.get_r()
180
+ v = config.get_v()
181
  aspect = width / height
182
  for j in range(height):
183
  for i in range(width):
 
189
  p_y = q_y * 2.0 - 1.0
190
  p = (p_x, p_y)
191
  # Scale coordinates for pattern frequency
192
+ h = hexagon((p[0] * config.pattern_scale, p[1] * config.pattern_scale))
193
  h_xy = (int(h[0]), int(h[1]))
194
  # Assign color based on hexagon coordinates
195
+ rz = math.fmod(abs(h_xy[0]) + r * abs(h_xy[1]),v)
196
+ color_index = int(rz % len(colors))
197
  col = colors[color_index]
198
  # Apply vignette effect
199
  q = (q_x * 2.0 - 1.0, q_y * 2.0 - 1.0)
200
+ vignette = smoothstep(config.vignette_outer, config.vignette_inner, max(abs(q[0]), abs(q[1])))
201
  col = tuple(int(c * vignette) for c in col)
202
  # Set the pixel color
203
  img.putpixel((i, j), col)
 
213
  p_x = (q_x * 2.0 - 1.0) * aspect
214
  p_y = q_y * 2.0 - 1.0
215
  p = (p_x, p_y)
216
+ h = hexagon((p[0] * config.pattern_scale, p[1] * config.pattern_scale))
217
  rz = ura(h[:2])
218
  smooth = smoothstep(-0.2, 0.13, h[2])
219
  if rz > 0.5:
 
221
  else:
222
  col = 1.0 - smooth
223
  q = (q_x * 2.0 - 1.0, q_y * 2.0 - 1.0)
224
+ vignette = smoothstep(config.vignette_outer, config.vignette_inner, max(abs(q[0]), abs(q[1])))
225
  col *= vignette
226
+ color = int(abs(col) * 255)
227
  img.putpixel((i, j), (color, color, color))
228
  return img
229
 
230
+ def get_primes_in_range(start: int, end: int) -> list:
231
+ """
232
+ Return a list of prime numbers between start and end (inclusive).
233
+
234
+ Uses the Sieve of Eratosthenes for efficiency.
235
+
236
+ Parameters:
237
+ start (int): The starting number of the range.
238
+ end (int): The ending number of the range.
239
+
240
+ Returns:
241
+ list: A list of prime numbers between start and end.
242
+ """
243
+ if end < 2:
244
+ return []
245
+ sieve = [True] * (end + 1)
246
+ sieve[0] = sieve[1] = False
247
+ for i in range(2, int(end ** 0.5) + 1):
248
+ if sieve[i]:
249
+ for j in range(i * i, end + 1, i):
250
+ sieve[j] = False
251
+ return [i for i in range(start, end + 1) if sieve[i]]
252
+
253
+ def find_nearest_prime(value):
254
+ """Find the closest prime number to the given value."""
255
+ return config.find_nearest_prime(value)
256
+
257
+ def generate_pattern_background(pattern_type="color", width=1024, height=768, v_value=_V, r_value=_R, colors=None):
258
+ # Generate a hexagonal pattern image with the given parameters.
259
+ # Do not pass gr.State values here
260
+ # Set the parameters
261
+ set_v(v_value)
262
+ set_r(r_value)
263
+ print(f"Generating pattern with V: {v_value}, R: {r_value}, Colors: {colors}")
264
+ color_count = 3
265
+
266
+ if pattern_type == "color":
267
+ if colors is None:
268
+ img = generate_image_color(width, height)
269
+ else:
270
+ img = generate_image_color(width, height, colors)
271
+ color_count = len(colors)
272
+ else: # grayscale
273
+ img = generate_image_grayscale(width, height)
274
+ color_count = 1
275
+
276
+ # Save to temporary file and return path
277
+ with NamedTemporaryFile(delete=False,prefix=f"hura_{str(color_count)}_v{str(v_value)}_r{str(r_value)}_", suffix=".png") as tmp:
278
+ img.save(tmp.name, format="PNG")
279
+ constants.temp_files.append(tmp.name)
280
+ return tmp.name
281
+
282
+
283
+ def create_color_swatch_html(colors):
284
+ """Create HTML for displaying color swatches"""
285
+ swatches = ''.join(
286
+ f'<div style="width: 50px; height: 50px; background-color: rgb{c}; '
287
+ f'border: 1px solid #ccc;"></div>'
288
+ for c in colors
289
+ )
290
+ return f'<div style="display: flex; gap: 10px;">{swatches}</div>'
291
+
292
+ def _add_color(color, color_list):
293
+ if color is None:
294
+ return color_list, color_list, ""
295
+
296
+ # Convert hex color to RGB
297
+ rgb_color = color_utils.hex_to_rgb(color)
298
+ color_list = color_list + [rgb_color]
299
+
300
+ # Create HTML to display color swatches
301
+ html = create_color_swatch_html(color_list)
302
+ return color_list, html
303
+
304
+ def _init_colors():
305
+ """Initialize the color swatches HTML display based on config colors"""
306
+ updated_list = list(config.default_colors)
307
+ # Rebuild the HTML swatches from the updated list
308
+ html = create_color_swatch_html(updated_list)
309
+ return html
310
+
311
+ def reset_colors():
312
+ """Reset the color list to the default colors."""
313
+ colors = config.reset_colors()
314
+ html = create_color_swatch_html(colors)
315
+ return colors, html
316
+
317
+ def _generate_pattern_from_state(pt, width, height, v_val, r_val, colors_list):
318
+ # colors_list is automatically the raw value from the gr.State input
319
+ return generate_pattern_background(pt, width, height, v_val, r_val, colors_list)
320
+
321
+ def render() -> dict:
322
+ """
323
+ Renders a colorful or grayscale hexagonal pattern creation interface
324
+
325
+ Returns:
326
+ dict: A dictionary containing:
327
+ - target_image (gr.Image): The generated pattern image component
328
+ - run_generate_hex_pattern (function): Function to generate a pattern with given dimensions
329
+ - set_height_width_hura_image (function): Function to update the slider values
330
+ - width_slider (gr.Slider): The width slider component
331
+ - height_slider (gr.Slider): The height slider component
332
+ """
333
+
334
+ # Initialize state
335
+ global state_colors
336
+ state_colors = gr.State(config.default_colors)
337
+ init_colors_html = _init_colors()
338
+
339
+
340
+ target_image = gr.Image(label="Generated Pattern", type="filepath")
341
+ with gr.Row():
342
+ pattern_type = gr.Radio(
343
+ label="Pattern Type",
344
+ choices=["color", "grayscale"],
345
+ value="grayscale",
346
+ type="value"
347
+ )
348
+ with gr.Column():
349
+ with gr.Row():
350
+ width_slider = gr.Slider(minimum=256, maximum=2560, value=1024, label="Width", step=8)
351
+ height_slider = gr.Slider(minimum=256, maximum=2560, value=768, label="Height", step=8)
352
+ v_value_slider = gr.Slider(minimum=config.prime_range_start, maximum=config.prime_range_end, value=config.v, label="V Value (Prime Number)", step=1)
353
+ r_value_slider = gr.Slider(minimum=1, maximum=100, value=config.r, label="R Value")
354
+ show_borders_chbox = gr.Checkbox(label="Show Borders", value=True)
355
+
356
+ with gr.Row(visible=False) as color_row:
357
+ color_picker = gr.ColorPicker(label="Pick a Color")
358
+ add_button = gr.Button("Add Color")
359
+ with gr.Column():
360
+ color_display = gr.HTML(label="Color Swatches", value=init_colors_html)
361
+ with gr.Row():
362
+ delete_colors_button = gr.Button("Delete Colors")
363
+ reset_colors_button = gr.Button("Reset Colors")
364
+ with gr.Row():
365
+ generate_button = gr.Button("Generate Pattern")
366
+
367
+
368
+ def run_generate_hex_pattern(width: int, height: int) -> str:
369
+ """
370
+ Generate a colored hexagonal pattern image with the given width and height.
371
+ Uses default V and R values and the default color palette.
372
+
373
+ Returns:
374
+ str: The filepath of the generated image.
375
+ """
376
+ global state_colors
377
+ width_slider.value=width
378
+ height_slider.value=height
379
+ gr.update()
380
+ # Use the current _V, _R, and default_colors
381
+ filepath = generate_pattern_background(
382
+ pattern_type="color",
383
+ width=width,
384
+ height=height,
385
+ v_value=get_v(),
386
+ r_value=get_r(),
387
+ colors=state_colors.value
388
+ )
389
+ return filepath
390
+
391
+ def set_height_width_hura_image(width, height) -> None:
392
+ """
393
+ Set the height and width of the image.
394
+ """
395
+ width_slider.value=width
396
+ height_slider.value=height
397
+ gr.update()
398
+
399
+
400
+ pattern_type.change(
401
+ fn=lambda x: gr.update(visible=(x == "color")),
402
+ inputs=pattern_type,
403
+ outputs=color_row
404
+ )
405
+ add_button.click(
406
+ fn=_add_color,
407
+ inputs=[color_picker, state_colors],
408
+ outputs=[state_colors, color_display]
409
+ )
410
+ delete_colors_button.click(
411
+ fn=lambda x: ([], "<div>Add Colors</div>"),
412
+ inputs=[],
413
+ outputs=[state_colors, color_display]
414
+ )
415
+ reset_colors_button.click(
416
+ fn=reset_colors,
417
+ inputs=[],
418
+ outputs=[state_colors,color_display]
419
+ )
420
+ generate_button.click(
421
+ fn=_generate_pattern_from_state,
422
+ inputs=[pattern_type, width_slider, height_slider, v_value_slider, r_value_slider, state_colors],
423
+ outputs=target_image
424
+ )
425
+
426
+ v_value_slider.input(
427
+ lambda x: config.find_nearest_prime(x),
428
+ inputs=v_value_slider,
429
+ outputs=v_value_slider
430
+ )
431
+ v_value_slider.change(
432
+ lambda x: config.find_nearest_prime(x),
433
+ inputs=v_value_slider,
434
+ outputs=v_value_slider
435
+ )
436
+
437
+ return {
438
+ "target_image": target_image,
439
+ "run_generate_hex_pattern": run_generate_hex_pattern,
440
+ "set_height_width_hura_image": set_height_width_hura_image,
441
+ "width_slider": width_slider,
442
+ "height_slider": height_slider
443
+ }