锥桶自定义更新

This commit is contained in:
cyy_mac
2026-06-16 22:45:15 +08:00
parent 0b41844e72
commit cf94fc566b
6 changed files with 201 additions and 26 deletions

113
cone_editor.py Normal file
View File

@@ -0,0 +1,113 @@
"""Visual cone placement editor. Click on the 5m×5m map to place cones."""
import json
import os
import tkinter as tk
from PIL import Image, ImageTk
MAP_FILE = "智能车地图源文件.avif"
OUTPUT = "cones.json"
MAP_SIZE_M = 5.0 # meters
CANVAS_W = 800
CANVAS_H = 800
class ConeEditor:
def __init__(self, root):
self.root = root
root.title("锥桶编辑器 — 左键放置 / 右键删除 / 滚轮保存")
self.cones = [] # list of {"x": world_x, "y": world_y}
# Load map image
img = Image.open(MAP_FILE)
self.img_w, self.img_h = img.size
img = img.resize((CANVAS_W, CANVAS_H), Image.LANCZOS)
self.photo = ImageTk.PhotoImage(img)
# Canvas
self.canvas = tk.Canvas(root, width=CANVAS_W, height=CANVAS_H, cursor="crosshair")
self.canvas.pack()
self.canvas.create_image(0, 0, anchor=tk.NW, image=self.photo)
# Info label
self.info = tk.Label(root, text="左键放置 | 右键删除最近 | 鼠标查看坐标")
self.info.pack()
# Buttons
btn_frame = tk.Frame(root)
btn_frame.pack(pady=5)
tk.Button(btn_frame, text="保存 (cones.json)", command=self.save).pack(side=tk.LEFT, padx=5)
tk.Button(btn_frame, text="清除全部", command=self.clear).pack(side=tk.LEFT, padx=5)
tk.Button(btn_frame, text="加载已有", command=self.load_existing).pack(side=tk.LEFT, padx=5)
tk.Label(btn_frame, text=f"地图: {MAP_SIZE_M}m×{MAP_SIZE_M}m | (-2.5,-2.5) → (2.5,2.5)").pack(side=tk.LEFT, padx=10)
# Bindings
self.canvas.bind("<Button-1>", self.on_click)
self.canvas.bind("<Button-3>", self.on_right_click)
self.canvas.bind("<Motion>", self.on_move)
# Load existing if available
self.load_existing()
def px_to_world(self, px, py):
x = px / CANVAS_W * MAP_SIZE_M - MAP_SIZE_M / 2
y = MAP_SIZE_M / 2 - py / CANVAS_H * MAP_SIZE_M
return x, y
def world_to_px(self, wx, wy):
px = (wx + MAP_SIZE_M / 2) / MAP_SIZE_M * CANVAS_W
py = (MAP_SIZE_M / 2 - wy) / MAP_SIZE_M * CANVAS_H
return px, py
def on_move(self, event):
wx, wy = self.px_to_world(event.x, event.y)
self.info.config(text=f"坐标: ({wx:.3f}, {wy:.3f}) | 锥桶数量: {len(self.cones)}")
def on_click(self, event):
wx, wy = self.px_to_world(event.x, event.y)
# Clamp to map bounds
half = MAP_SIZE_M / 2
wx = max(-half, min(half, wx))
wy = max(-half, min(half, wy))
self.cones.append({"x": round(wx, 3), "y": round(wy, 3)})
self.redraw()
def on_right_click(self, event):
if not self.cones:
return
wx, wy = self.px_to_world(event.x, event.y)
# Remove nearest cone
idx = min(range(len(self.cones)), key=lambda i: (self.cones[i]["x"] - wx) ** 2 + (self.cones[i]["y"] - wy) ** 2)
self.cones.pop(idx)
self.redraw()
def redraw(self):
self.canvas.delete("cone")
r = 5
for c in self.cones:
px, py = self.world_to_px(c["x"], c["y"])
self.canvas.create_oval(px - r, py - r, px + r, py + r, fill="blue", outline="white", width=2, tags="cone")
def save(self):
with open(OUTPUT, "w") as f:
json.dump(self.cones, f, indent=2)
print(f"已保存 {len(self.cones)} 个锥桶到 {OUTPUT}")
self.info.config(text=f"已保存 {len(self.cones)} 个锥桶")
def clear(self):
self.cones.clear()
self.redraw()
print("已清除所有锥桶")
def load_existing(self):
if os.path.exists(OUTPUT):
with open(OUTPUT) as f:
self.cones = json.load(f)
self.redraw()
print(f"已加载 {len(self.cones)} 个锥桶")
if __name__ == "__main__":
root = tk.Tk()
app = ConeEditor(root)
root.mainloop()