BigUI WIP: Star Factory

This commit is contained in:
firestarsdog
2026-06-30 23:41:23 -04:00
parent 1339ca02c4
commit ef5ece17eb
@@ -449,7 +449,7 @@ def init_list_panel(rect: rl.Rectangle, style: PanelStyle | None = None, metrics
return frame, scroll_rect, content_width
def draw_hud_background(rect: rl.Rectangle, accent: rl.Color, glow: float = 1.0) -> tuple[rl.Rectangle, rl.Color]:
def draw_hud_background(rect: rl.Rectangle, accent: rl.Color, glow: float = 1.0, *, radius_px: float = 100) -> tuple[rl.Rectangle, rl.Color]:
snapped = snap_rect(rect)
rx, ry, rw, rh = int(snapped.x), int(snapped.y), int(snapped.width), int(snapped.height)
face = rl.Rectangle(rx, ry, rw, rh)
@@ -460,16 +460,16 @@ def draw_hud_background(rect: rl.Rectangle, accent: rl.Color, glow: float = 1.0)
off = i * 2.5 * glow
gr = rl.Rectangle(rx - off, ry - off, rw + off * 2, rh + off * 2)
a = int(25 * (1.0 - i / 5) * glow)
draw_rounded_fill(gr, rl.Color(accent.r, accent.g, accent.b, max(0, min(255, a))), radius_px=100)
draw_rounded_fill(gr, rl.Color(accent.r, accent.g, accent.b, max(0, min(255, a))), radius_px=radius_px)
draw_rounded_fill(face, _HUD_BG_ON, radius_px=100)
draw_rounded_fill(face, _HUD_BG_ON, radius_px=radius_px)
bc = rl.Color(
max(0, min(255, int(off_border.r + (accent.r - off_border.r) * glow))),
max(0, min(255, int(off_border.g + (accent.g - off_border.g) * glow))),
max(0, min(255, int(off_border.b + (accent.b - off_border.b) * glow))),
255)
draw_rounded_stroke(face, bc, radius_px=100)
draw_rounded_stroke(face, bc, radius_px=radius_px)
return face, accent
@@ -1478,10 +1478,121 @@ def draw_standard_toggle_row(
)
_KNOB_ANIMATION_STATES: dict[str, float] = {}
_TOGGLE_CONSTELLATION_CACHE: dict[str, tuple[list[dict], list[tuple[int, int]]]] = {}
# Anchor-zone regions for tile (near-square) vs pill (wide/short)
_CONST_REGIONS_TILE = [
(0.18, 0.30, 0.18, 0.30), # top-left corner
(0.70, 0.82, 0.18, 0.30), # top-right corner
(0.18, 0.30, 0.70, 0.82), # bottom-left corner
(0.70, 0.82, 0.70, 0.82), # bottom-right corner
(0.38, 0.62, 0.18, 0.28), # top-center edge
(0.38, 0.62, 0.72, 0.82), # bottom-center edge
(0.18, 0.28, 0.38, 0.62), # left-center edge
(0.72, 0.82, 0.38, 0.62), # right-center edge
]
_CONST_REGIONS_PILL = [
(0.15, 0.30, 0.22, 0.42), # left cluster
(0.70, 0.85, 0.22, 0.42), # right cluster
(0.15, 0.30, 0.58, 0.78), # left cluster low
(0.70, 0.85, 0.58, 0.78), # right cluster low
(0.38, 0.62, 0.20, 0.35), # top-center
(0.38, 0.62, 0.65, 0.80), # bottom-center
(0.20, 0.35, 0.38, 0.62), # left-mid
(0.65, 0.80, 0.38, 0.62), # right-mid
]
def _build_constellation_nodes(
rng: random.Random,
num: int,
regions: list[tuple[float, float, float, float]],
*,
r_min: float,
r_max: float,
min_sep: float,
x_margin: float,
y_margin: float,
) -> tuple[list[dict], list[tuple[int, int]]]:
"""Shared constellation node generator used by both tiles and toggle pills."""
ax_min, ax_max, ay_min, ay_max = regions[rng.randint(0, len(regions) - 1)]
ax = ax_min + rng.random() * (ax_max - ax_min)
ay = ay_min + rng.random() * (ay_max - ay_min)
nodes: list[dict] = []
for _ in range(num):
for _ in range(20):
a = rng.random() * 2.0 * math.pi
r = r_min + rng.random() * r_max
x = max(x_margin, min(1.0 - x_margin, ax + r * math.cos(a)))
y = max(y_margin, min(1.0 - y_margin, ay + r * math.sin(a)))
if all(math.sqrt((x - n['x'])**2 + (y - n['y'])**2) >= min_sep for n in nodes):
nodes.append({'x': x, 'y': y})
break
else:
nodes.append({'x': x, 'y': y})
nodes.sort(key=lambda n: -(abs(n['x'] - 0.5) + abs(n['y'] - 0.5)))
for i, n in enumerate(nodes):
n['w'] = 0 if i == 0 else 1 if i == 1 else 2
vecs: list[tuple[int, int]] = [(0, j) for j in range(1, len(nodes))]
return nodes, vecs
def draw_constellation_nodes(
nodes: list[dict],
vecs: list[tuple[int, int]],
rect: rl.Rectangle,
accent: rl.Color,
glow: float,
*,
scale: float = 1.0,
) -> None:
"""Shared constellation renderer used by both tiles and toggle pills.
`scale` shrinks core/glow radii for smaller surfaces (e.g. 0.45 for pills).
"""
rx, ry, rw, rh = int(rect.x), int(rect.y), int(rect.width), int(rect.height)
va = int(10 + glow * 25)
if va > 2:
vc = rl.Color(accent.r, accent.g, accent.b, min(255, va))
for i, j in vecs:
rl.draw_line_ex(
rl.Vector2(int(rx + nodes[i]['x'] * rw), int(ry + nodes[i]['y'] * rh)),
rl.Vector2(int(rx + nodes[j]['x'] * rw), int(ry + nodes[j]['y'] * rh)),
1.0, vc,
)
for nd in nodes:
nx = int(rx + nd['x'] * rw)
ny = int(ry + nd['y'] * rh)
w = nd.get('w', 2)
if w == 0:
core_r, diff_r, col = 3.0 * scale, 12.0 * scale, _CONST_PRIMARY
elif w == 1:
core_r, diff_r, col = 2.0 * scale, 8.0 * scale, _CONST_SECONDARY
else:
core_r, diff_r, col = 1.2 * scale, 0.0, _CONST_TERTIARY
da = int(5 + glow * 20)
if diff_r > 0 and da > 2:
rl.draw_circle(nx, ny, max(1.0, diff_r), rl.Color(col.r, col.g, col.b, min(255, da)))
ca = int(130 + glow * 125)
rl.draw_circle(nx, ny, max(1.0, core_r), rl.Color(col.r, col.g, col.b, min(255, ca)))
def _get_or_create_toggle_constellation(seed_id: str) -> tuple[list[dict], list[tuple[int, int]]]:
if seed_id in _TOGGLE_CONSTELLATION_CACHE:
return _TOGGLE_CONSTELLATION_CACHE[seed_id]
rng = random.Random(seed_id)
nodes, vecs = _build_constellation_nodes(
rng, rng.randint(2, 4), _CONST_REGIONS_PILL,
r_min=0.06, r_max=0.22, min_sep=0.12, x_margin=0.10, y_margin=0.18,
)
_TOGGLE_CONSTELLATION_CACHE[seed_id] = (nodes, vecs)
return nodes, vecs
def draw_toggle_switch(
rect: rl.Rectangle,
enabled: bool,
*,
knob_progress: float | None = None,
is_enabled: bool = True,
track_color: rl.Color = AetherListColors.PRIMARY,
off_track_color: rl.Color = rl.Color(255, 255, 255, 24),
@@ -1490,15 +1601,51 @@ def draw_toggle_switch(
height: int = AETHER_LIST_METRICS.toggle_height,
right_inset: int = AETHER_LIST_METRICS.toggle_right_inset,
knob_offset: int = 20,
seed_id: str = "",
):
toggle_rect = rl.Rectangle(rect.x + rect.width - width - right_inset, rect.y + (rect.height - height) / 2, width, height)
track = track_color if enabled else off_track_color
if knob_progress is None:
knob_progress = 1.0 if enabled else 0.0
if not is_enabled:
track = with_alpha(mix_colors(off_track_color, track, 0.35), 42)
knob_color = with_alpha(knob_color, 132)
knob_x = toggle_rect.x + toggle_rect.width - knob_offset if enabled else toggle_rect.x + knob_offset
rl.draw_rectangle_rounded(toggle_rect, 1.0, 16, track)
rl.draw_circle(int(knob_x), int(toggle_rect.y + toggle_rect.height / 2), 16, knob_color)
knob_x = toggle_rect.x + knob_offset + knob_progress * (toggle_rect.width - 2 * knob_offset)
knob_y = toggle_rect.y + toggle_rect.height / 2
# Delegate to draw_hud_background — same layered bloom, fill, and lerped border as tiles
draw_hud_background(toggle_rect, track_color if is_enabled else with_alpha(track_color, 80), knob_progress, radius_px=TILE_RADIUS_PX)
if seed_id and enabled:
nodes, vecs = _get_or_create_toggle_constellation(seed_id)
glow = knob_progress
draw_constellation_nodes(nodes, vecs, toggle_rect, track_color, glow, scale=0.45)
# Gravity tethers during slide — drawn after stars so they appear under knob
if 0.0 < knob_progress < 1.0:
tether_alpha = int(60 * math.sin(knob_progress * math.pi))
tether_col = rl.Color(track_color.r, track_color.g, track_color.b, tether_alpha)
for node in nodes:
nx = toggle_rect.x + node['x'] * toggle_rect.width
ny = toggle_rect.y + node['y'] * toggle_rect.height
rl.draw_line_ex(rl.Vector2(nx, ny), rl.Vector2(knob_x, knob_y), 1.2, tether_col)
# Nearly-square slider thumb — physical button sliding across the starfield
knob_w = 30.0
knob_h = toggle_rect.height - 8.0 # 4px inset top + bottom = 34px
knob_roundness = 0.65 # ≈10px corner radius on 30px width — rect, not pill
knob_segments = 8
knob_rect = snap_rect(rl.Rectangle(
knob_x - knob_w / 2, knob_y - knob_h / 2, knob_w, knob_h
))
# Base fill
rl.draw_rectangle_rounded(knob_rect, knob_roundness, knob_segments, knob_color)
# Glass highlight — top ~40% of knob at low opacity, simulates light catching the face
highlight_rect = rl.Rectangle(knob_rect.x + 2, knob_rect.y + 2, knob_rect.width - 4, knob_rect.height * 0.40)
rl.draw_rectangle_rounded(highlight_rect, knob_roundness, knob_segments, with_alpha(rl.WHITE, 38))
# Thin border for depth
rl.draw_rectangle_rounded_lines_ex(knob_rect, knob_roundness, knob_segments, 1.0, with_alpha(rl.WHITE, 50))
def draw_action_pill(
@@ -1866,7 +2013,22 @@ def draw_settings_list_row(
)
if toggle_value is not None:
draw_toggle_switch(draw_rect, bool(toggle_value), is_enabled=enabled, track_color=style.accent)
target = 1.0 if toggle_value else 0.0
current_progress = _KNOB_ANIMATION_STATES.get(title, target)
dt = rl.get_frame_time()
current_progress += (target - current_progress) * 12.0 * dt
if abs(current_progress - target) < 0.001:
current_progress = target
_KNOB_ANIMATION_STATES[title] = current_progress
draw_toggle_switch(
draw_rect,
bool(toggle_value),
knob_progress=current_progress,
is_enabled=enabled,
track_color=style.accent,
seed_id=title,
)
return
if value:
@@ -3705,66 +3867,15 @@ class AetherTile(Widget):
return
rng = random.Random(self._constellation_seed())
num = _NODE_NUM_MIN + rng.randint(0, _NODE_NUM_MAX - _NODE_NUM_MIN)
regions = [
(0.18, 0.30, 0.18, 0.30), # top-left corner
(0.70, 0.82, 0.18, 0.30), # top-right corner
(0.18, 0.30, 0.70, 0.82), # bottom-left corner
(0.70, 0.82, 0.70, 0.82), # bottom-right corner
(0.38, 0.62, 0.18, 0.28), # top-center edge
(0.38, 0.62, 0.72, 0.82), # bottom-center edge
(0.18, 0.28, 0.38, 0.62), # left-center edge
(0.72, 0.82, 0.38, 0.62), # right-center edge
]
ax_min, ax_max, ay_min, ay_max = regions[rng.randint(0, 7)]
ax = ax_min + rng.random() * (ax_max - ax_min)
ay = ay_min + rng.random() * (ay_max - ay_min)
nodes = []
for _ in range(num):
for _ in range(20):
a = rng.random() * 2.0 * math.pi
r = 0.05 + rng.random() * 0.11
x = max(0.04, min(0.96, ax + r * math.cos(a)))
y = max(0.04, min(0.96, ay + r * math.sin(a)))
if all(math.sqrt((x - n['x'])**2 + (y - n['y'])**2) >= 0.07 for n in nodes):
nodes.append({'x': x, 'y': y})
break
else:
nodes.append({'x': x, 'y': y})
nodes.sort(key=lambda n: -(abs(n['x'] - 0.5) + abs(n['y'] - 0.5)))
for i, n in enumerate(nodes):
n['w'] = 0 if i == 0 else 1 if i == 1 else 2
vecs = [(0, j) for j in range(1, num)]
self._constellation_data = (nodes, vecs)
self._constellation_data = _build_constellation_nodes(
rng, num, _CONST_REGIONS_TILE,
r_min=0.05, r_max=0.11, min_sep=0.07, x_margin=0.04, y_margin=0.04,
)
def _draw_constellation(self, face: rl.Rectangle, accent: rl.Color, glow: float):
self._generate_and_cache_constellation()
nodes, vecs = self._constellation_data
rx, ry, rw, rh = int(face.x), int(face.y), int(face.width), int(face.height)
va = int(10 + glow * 25)
if va > 2:
vc = rl.Color(accent.r, accent.g, accent.b, min(255, va))
for i, j in vecs:
x1 = int(rx + nodes[i]['x'] * rw)
y1 = int(ry + nodes[i]['y'] * rh)
x2 = int(rx + nodes[j]['x'] * rw)
y2 = int(ry + nodes[j]['y'] * rh)
rl.draw_line_ex(rl.Vector2(x1, y1), rl.Vector2(x2, y2), 1.0, vc)
for nd in nodes:
nx = int(rx + nd['x'] * rw)
ny = int(ry + nd['y'] * rh)
if nd['w'] == 0:
core_r, diff_r, col = 3.0, 12.0, _CONST_PRIMARY
elif nd['w'] == 1:
core_r, diff_r, col = 2.0, 8.0, _CONST_SECONDARY
else:
core_r, diff_r, col = 1.2, 0.0, _CONST_TERTIARY
da = int(5 + glow * 20)
if diff_r > 0 and da > 2:
rl.draw_circle(nx, ny, int(diff_r), rl.Color(col.r, col.g, col.b, min(255, da)))
ca = int(130 + glow * 125)
rl.draw_circle(nx, ny, int(core_r), rl.Color(col.r, col.g, col.b, min(255, ca)))
draw_constellation_nodes(nodes, vecs, face, accent, glow, scale=1.0)
def _render_hud_background(self, rect: rl.Rectangle, accent: rl.Color, glow: float = 1.0) -> tuple[rl.Rectangle, rl.Color]:
sq = self._squish