diff --git a/generate_scene.py b/generate_scene.py index 24fc3e9..8d08cd4 100644 --- a/generate_scene.py +++ b/generate_scene.py @@ -14,7 +14,13 @@ FIXED_Y = 2.5 STRAIGHT_BELT_ID = "3_38ygf" CURVED_BELT_ID = "1_ef28r" -SPUR_FORWARD_OFFSET = 0.75 # meters +# Spur / curve tuning +SPUR_TANGENT_NUDGE = 0.0 +MIN_CURVE_DEG = 1.0 + +# Geometry (must match asset) +CURVE_INNER_RADIUS = 1.815 +BELT_WIDTH = 1.524 # ----------------------- # HELPERS @@ -23,15 +29,11 @@ def transform_from_points(x1, y1, x2, y2): dx = x2 - x1 dy = y2 - y1 - # length in meters length = math.hypot(dx, dy) * SCALE - # POSITION mid_x = (x1 + x2) / 2 * SCALE - mid_z = -(y1 + y2) / 2 * SCALE # ✅ single Y→-Z mapping + mid_z = -(y1 + y2) / 2 * SCALE # AutoCAD Y → Godot -Z - # ROTATION (IMPORTANT) - # dy must be negated because Z is flipped rot_y = math.atan2(-dy, dx) return { @@ -40,7 +42,6 @@ def transform_from_points(x1, y1, x2, y2): "rot_y": rot_y, } - def transform3d(rot_y, x, y, z): c = math.cos(rot_y) s = math.sin(rot_y) @@ -55,20 +56,56 @@ def parse_key(key): p = key.split("_") return p[0], int(p[1]) +def unit_fwd(rot_y): + return math.cos(rot_y), math.sin(rot_y) + +def unit_right(rot_y): + return -math.sin(rot_y), math.cos(rot_y) + def end_point(conv): - dx = math.cos(conv["rot_y"]) - dz = math.sin(conv["rot_y"]) + dx, dz = unit_fwd(conv["rot_y"]) half = conv["length"] / 2 return ( conv["pos"][0] + dx * half, conv["pos"][2] + dz * half ) +def start_point(conv): + dx, dz = unit_fwd(conv["rot_y"]) + half = conv["length"] / 2 + return ( + conv["pos"][0] - dx * half, + conv["pos"][2] - dz * half + ) + +def add_straight_node(lines, name, rot_y, cx, cz, length): + t = transform3d(rot_y, cx, FIXED_Y, cz) + lines.append(f'[node name="{name}" parent="." instance=ExtResource("{STRAIGHT_BELT_ID}")]') + lines.append(f"transform = {t}") + lines.append("right_side_guards_enabled = false") + lines.append("left_side_guards_enabled = false") + lines.append("head_end_leg_enabled = false") + lines.append("tail_end_leg_enabled = false") + lines.append("enable_comms = true") + lines.append(f'speed_tag_name = "{name}_OIP"') + lines.append(f"size = Vector3({length:.6f}, 0.5, 1.524)") + lines.append("") + +def add_curved_node(lines, name, rot_y, px, pz, angle_deg): + t = transform3d(rot_y, px, FIXED_Y, pz) + lines.append(f'[node name="{name}" parent="." instance=ExtResource("{CURVED_BELT_ID}")]') + lines.append(f"transform = {t}") + lines.append(f"inner_radius = {CURVE_INNER_RADIUS}") + lines.append(f"conveyor_angle = {angle_deg:.3f}") + lines.append("enable_comms = true") + lines.append(f'speed_tag_name = "{name}_OIP"') + lines.append("") + # ----------------------- # READ CSV # ----------------------- straight = {} -spurs = [] +vfd_only = [] # included=0 candidates (some will become spurs, others will be chain-continued) with open(CSV_PATH, newline="") as f: reader = csv.DictReader(f) @@ -92,14 +129,16 @@ with open(CSV_PATH, newline="") as f: conv["name"] = key conv["prefix"] = prefix conv["sec"] = sec - straight[key] = conv + straight[key] = conv else: - spurs.append({ - "name": key, - "prefix": prefix, - "sec": sec - }) + vfd_only.append({"name": key, "prefix": prefix, "sec": sec}) + +# Precompute endpoints & directions for geometry straights +for c in straight.values(): + c["start"] = start_point(c) + c["end"] = end_point(c) + c["fwd"] = unit_fwd(c["rot_y"]) # ----------------------- # WRITE TSCN @@ -113,62 +152,157 @@ lines.append('') lines.append('[node name="GeneratedConveyors" type="Node3D"]') lines.append('') - # ----------------------- -# Straight conveyors +# 1) Geometry straight conveyors (included=1) # ----------------------- for c in straight.values(): - x, y, z = c["pos"] - t = transform3d(c["rot_y"], x, y, z) - - lines.append(f'[node name="{c["name"]}" parent="." instance=ExtResource("{STRAIGHT_BELT_ID}")]') - lines.append(f"transform = {t}") - lines.append("right_side_guards_enabled = false") - lines.append("left_side_guards_enabled = false") - lines.append("head_end_leg_enabled = false") - lines.append("tail_end_leg_enabled = false") - lines.append("enable_comms = true") - lines.append(f'speed_tag_name = "{c["name"]}_OIP"') - lines.append(f"size = Vector3({c['length']:.6f}, 0.5, 1.524)") - lines.append("") + cx, _, cz = c["pos"] + add_straight_node(lines, c["name"], c["rot_y"], cx, cz, c["length"]) # ----------------------- -# Spur conveyors +# 2) Spur conveyors (use vfd_only list, but mark those we actually place) # ----------------------- -for spur in spurs: +placed_spurs = set() + +for spur in vfd_only: prefix = spur["prefix"] sec = spur["sec"] prev_key = f"{prefix}_{sec-1}" next_key = f"{prefix}_{sec+1}" + # spur needs both neighbors with geometry (or already-added straights later, but at this stage it's fine) if prev_key not in straight or next_key not in straight: continue prev = straight[prev_key] nxt = straight[next_key] - px, pz = end_point(prev) + pfx, pfz = prev["fwd"] + nfx, nfz = nxt["fwd"] - fwd_x = math.cos(prev["rot_y"]) - fwd_z = math.sin(prev["rot_y"]) + cross = pfx * nfz - pfz * nfx + dot = pfx * nfx + pfz * nfz + delta = math.atan2(cross, dot) - mx = px + fwd_x * SPUR_FORWARD_OFFSET - mz = pz + fwd_z * SPUR_FORWARD_OFFSET + angle_deg = abs(delta) * 180.0 / math.pi + if angle_deg < MIN_CURVE_DEG: + continue - delta = nxt["rot_y"] - prev["rot_y"] - spur_angle_deg = abs(delta) * 180.0 / math.pi - if spur_angle_deg < 1.0: - spur_angle_deg = 30.0 + turn_sign = 1.0 if delta > 0 else -1.0 - t = transform3d(prev["rot_y"], mx, FIXED_Y, mz) + rx, rz = unit_right(prev["rot_y"]) + end_x, end_z = prev["end"] - lines.append(f'[node name="{spur["name"]}" parent="." instance=ExtResource("{CURVED_BELT_ID}")]') - lines.append(f"transform = {t}") - lines.append(f"conveyor_angle = {spur_angle_deg:.3f}") - lines.append("enable_comms = true") - lines.append(f'speed_tag_name = "{spur["name"]}_OIP"') - lines.append("") + # place along side edge then inward by inner radius (your current spur logic) + edge_x = end_x + rx * (BELT_WIDTH / 2) * turn_sign + edge_z = end_z + rz * (BELT_WIDTH / 2) * turn_sign + inward_x = -rx * turn_sign + inward_z = -rz * turn_sign + + place_x = edge_x + inward_x * CURVE_INNER_RADIUS + pfx * SPUR_TANGENT_NUDGE + place_z = edge_z + inward_z * CURVE_INNER_RADIUS + pfz * SPUR_TANGENT_NUDGE + + add_curved_node(lines, spur["name"], prev["rot_y"], place_x, place_z, angle_deg) + placed_spurs.add(spur["name"]) + +# ----------------------- +# 3) VFD-only straight conveyors (CHAIN CONTINUATION) +# Run AFTER spurs. Only place leftovers (not in placed_spurs). +# Also: after we place one, add it to "straight" so multiple missing sections can chain. +# ----------------------- +# Build quick lookup for vfd_only rows by key +vfd_by_key = {s["name"]: s for s in vfd_only} + +placed_chain = set() + +made_progress = True +while made_progress: + made_progress = False + + for spur in vfd_only: + name = spur["name"] + if name in placed_spurs or name in placed_chain: + continue + if name in straight: + continue + + prefix = spur["prefix"] + sec = spur["sec"] + prev_key = f"{prefix}_{sec-1}" + next_key = f"{prefix}_{sec+1}" + + # --- Case A: place AFTER previous (if previous exists, next does NOT exist/placed) --- + if prev_key in straight and next_key not in straight: + ref = straight[prev_key] + dx, dz = ref["fwd"] + length = ref["length"] + + # start at previous end, same direction, same length + sx, sz = ref["end"] + ex, ez = (sx + dx * length, sz + dz * length) + cx, cz = (sx + dx * (length / 2), sz + dz * (length / 2)) + + add_straight_node(lines, name, ref["rot_y"], cx, cz, length) + + # register as a new straight so the chain can continue + newc = { + "name": name, + "prefix": prefix, + "sec": sec, + "length": length, + "pos": (cx, FIXED_Y, cz), + "rot_y": ref["rot_y"], + } + newc["start"] = (sx, sz) + newc["end"] = (ex, ez) + newc["fwd"] = (dx, dz) + + straight[name] = newc + placed_chain.add(name) + made_progress = True + continue + + # --- Case B: place BEFORE next (if next exists, prev does NOT exist/placed) --- + if next_key in straight and prev_key not in straight: + ref = straight[next_key] + dx, dz = ref["fwd"] + length = ref["length"] + + # end at next start, same direction, same length (going backwards) + ex, ez = ref["start"] + sx, sz = (ex - dx * length, ez - dz * length) + cx, cz = (sx + dx * (length / 2), sz + dz * (length / 2)) + + add_straight_node(lines, name, ref["rot_y"], cx, cz, length) + + newc = { + "name": name, + "prefix": prefix, + "sec": sec, + "length": length, + "pos": (cx, FIXED_Y, cz), + "rot_y": ref["rot_y"], + } + newc["start"] = (sx, sz) + newc["end"] = (ex, ez) + newc["fwd"] = (dx, dz) + + straight[name] = newc + placed_chain.add(name) + made_progress = True + continue + +# ----------------------- +# WRITE FILE +# ----------------------- Path(OUT_TSCN).write_text("\n".join(lines), encoding="utf-8") -print(f"Generated {len(straight)} straight conveyors and {len(spurs)} spur candidates.") + +leftovers = [s["name"] for s in vfd_only if s["name"] not in placed_spurs and s["name"] not in placed_chain] +print( + f"Generated {len([k for k in straight.keys() if k not in vfd_by_key])} geometry straights, " + f"{len(placed_spurs)} spurs, " + f"{len(placed_chain)} chain-continued VFD-only straights. " + f"Leftovers not placed: {len(leftovers)}" +) diff --git a/generated_conveyors.tscn b/generated_conveyors.tscn index 4e69d3e..257369a 100644 --- a/generated_conveyors.tscn +++ b/generated_conveyors.tscn @@ -616,37 +616,103 @@ speed_tag_name = "UL6_9_OIP" size = Vector3(0.889000, 0.5, 1.524) [node name="UL2_6" parent="." instance=ExtResource("1_ef28r")] -transform = Transform3D(0.9999805530651744, 0, 0.006236464660995434, 0, 1, 0, -0.006236464660995434, 0, 0.9999805530651744, 47.04512188199888, 2.5, -50.257203945095746) +transform = Transform3D(0.9999805530651744, 0, 0.006236464660995434, 0, 1, 0, -0.006236464660995434, 0, 0.9999805530651744, 46.28856946991197, 2.5, -51.305506118977625) +inner_radius = 1.815 conveyor_angle = 32.079 enable_comms = true speed_tag_name = "UL2_6_OIP" [node name="UL3_4" parent="." instance=ExtResource("1_ef28r")] -transform = Transform3D(0.9999914228893805, 0, 0.0041417565925802435, 0, 1, 0, -0.0041417565925802435, 0, 0.9999914228893805, 51.99245130556704, 2.5, -53.09028764604443) +transform = Transform3D(0.9999914228893805, 0, 0.0041417565925802435, 0, 1, 0, -0.0041417565925802435, 0, 0.9999914228893805, 51.238096468708015, 2.5, -54.14017229690252) +inner_radius = 1.815 conveyor_angle = 31.158 enable_comms = true speed_tag_name = "UL3_4_OIP" [node name="PS2_4" parent="." instance=ExtResource("1_ef28r")] -transform = Transform3D(0.8622787824202933, 0, -0.5064339062382932, 0, 1, 0, 0.5064339062382932, 0, 0.8622787824202933, 91.80024031761522, 2.5, -49.40243484812128) +transform = Transform3D(0.8622787824202933, 0, -0.5064339062382932, 0, 1, 0, 0.5064339062382932, 0, 0.8622787824202933, 90.62025632753108, 2.5, -48.87428071991143) +inner_radius = 1.815 conveyor_angle = 30.029 enable_comms = true speed_tag_name = "PS2_4_OIP" [node name="PS1_3" parent="." instance=ExtResource("1_ef28r")] -transform = Transform3D(0.8675449395504647, 0, -0.49735880193315224, 0, 1, 0, 0.49735880193315224, 0, 0.8675449395504647, 84.66449507086286, 2.5, -42.29057085695013) +transform = Transform3D(0.8675449395504647, 0, -0.49735880193315224, 0, 1, 0, 0.49735880193315224, 0, 0.8675449395504647, 83.49011754776441, 2.5, -41.750065137053355) +inner_radius = 1.815 conveyor_angle = 30.015 enable_comms = true speed_tag_name = "PS1_3_OIP" [node name="UL5_6" parent="." instance=ExtResource("1_ef28r")] -transform = Transform3D(0.9988495791288503, 0, -0.047953292630627475, 0, 1, 0, 0.047953292630627475, 0, 0.9988495791288503, 46.19277797074663, 2.5, -62.962497831927024) +transform = Transform3D(0.9988495791288503, 0, -0.047953292630627475, 0, 1, 0, 0.047953292630627475, 0, 0.9988495791288503, 45.49413560354005, 2.5, -64.05025140822268) +inner_radius = 1.815 conveyor_angle = 28.769 enable_comms = true speed_tag_name = "UL5_6_OIP" [node name="UL6_4" parent="." instance=ExtResource("1_ef28r")] -transform = Transform3D(0.9996145027501965, 0, -0.027764111573710868, 0, 1, 0, 0.027764111573710868, 0, 0.9996145027501965, 46.75538510886264, 2.5, -67.31562141511971) +transform = Transform3D(0.9996145027501965, 0, -0.027764111573710868, 0, 1, 0, 0.027764111573710868, 0, 0.9996145027501965, 46.03490984128712, 2.5, -68.38903857019595) +inner_radius = 1.815 conveyor_angle = 29.004 enable_comms = true speed_tag_name = "UL6_4_OIP" + +[node name="PS1_1" parent="." instance=ExtResource("3_38ygf")] +transform = Transform3D(0.8675449395504647, 0, -0.49735880193315224, 0, 1, 0, 0.49735880193315224, 0, 0.8675449395504647, 79.231582202, 2.5, -45.4052302968) +right_side_guards_enabled = false +left_side_guards_enabled = false +head_end_leg_enabled = false +tail_end_leg_enabled = false +enable_comms = true +speed_tag_name = "PS1_1_OIP" +size = Vector3(3.674933, 0.5, 1.524) + +[node name="PS2_1" parent="." instance=ExtResource("3_38ygf")] +transform = Transform3D(0.8773678141674687, 0, -0.479818422596505, 0, 1, 0, 0.479818422596505, 0, 0.8773678141674687, 78.221077365, 2.5, -57.31528545810001) +right_side_guards_enabled = false +left_side_guards_enabled = false +head_end_leg_enabled = false +tail_end_leg_enabled = false +enable_comms = true +speed_tag_name = "PS2_1_OIP" +size = Vector3(1.173861, 0.5, 1.524) + +[node name="UL2_11" parent="." instance=ExtResource("3_38ygf")] +transform = Transform3D(0.8780689921649024, 0, -0.47853405834748336, 0, 1, 0, 0.47853405834748336, 0, 0.8780689921649024, 52.139953555800005, 2.5, -47.5879252075) +right_side_guards_enabled = false +left_side_guards_enabled = false +head_end_leg_enabled = false +tail_end_leg_enabled = false +enable_comms = true +speed_tag_name = "UL2_11_OIP" +size = Vector3(0.858309, 0.5, 1.524) + +[node name="UL3_10" parent="." instance=ExtResource("3_38ygf")] +transform = Transform3D(0.8768162540239772, 0, -0.4808255990266744, 0, 1, 0, 0.4808255990266744, 0, 0.8768162540239772, 61.577121956, 2.5, -47.5639441785) +right_side_guards_enabled = false +left_side_guards_enabled = false +head_end_leg_enabled = false +tail_end_leg_enabled = false +enable_comms = true +speed_tag_name = "UL3_10_OIP" +size = Vector3(0.891587, 0.5, 1.524) + +[node name="UL5_11" parent="." instance=ExtResource("3_38ygf")] +transform = Transform3D(0.8830309487153603, 0, -0.4693147596345664, 0, 1, 0, 0.4693147596345664, 0, 0.8830309487153603, 51.4080554895, 2.5, -60.115938721400006) +right_side_guards_enabled = false +left_side_guards_enabled = false +head_end_leg_enabled = false +tail_end_leg_enabled = false +enable_comms = true +speed_tag_name = "UL5_11_OIP" +size = Vector3(0.913773, 0.5, 1.524) + +[node name="UL6_10" parent="." instance=ExtResource("3_38ygf")] +transform = Transform3D(0.866025402838328, 0, -0.5000000016387117, 0, 1, 0, 0.5000000016387117, 0, 0.866025402838328, 59.208984464699995, 2.5, -60.16146606239999) +right_side_guards_enabled = false +left_side_guards_enabled = false +head_end_leg_enabled = false +tail_end_leg_enabled = false +enable_comms = true +speed_tag_name = "UL6_10_OIP" +size = Vector3(0.889000, 0.5, 1.524)