Files
StandardSence/.cursor/skills/cyclegui-app-development/references/workspace-interaction.md
T
zhaowei.huangandCursor 158d770c54 docs: 添加 cyclegui-app-development 项目技能
将 CycleGUI 开发技能(含 Duplicated id 防碰撞规范)纳入 .cursor/skills,并调整 gitignore 仅放行 skills 目录可提交。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-30 22:38:18 +08:00

4.6 KiB
Raw Blame History

Workspace 交互、对象选择和坐标拾取

namePattern 语法

所有 namePattern 使用 glob 语义,不是 regex

selectOp.SetObjectSelectable("UISite-*");  // 命中 UISite-3
selectOp.SetObjectSelectable("UISite-.*"); // 错:这不是 regex

需要正则时,在 C# 侧用 Regex 先筛出具体名字,再逐个传具体名字给 CycleGUI API。

对象选择

画布或 Workspace 中的业务对象需要点击、选择、框选或刷选时,优先把它们绘制成 CycleGUI 原生可选对象,然后使用 SelectObject。常见做法是用 PutModelObjectPutPointCloudPutHandleIconPutStraightLine,或其它可被 SetObjectSelectable 命中的 Workspace/Painter 对象来承载可视化主体。

无特殊情况,不要用 GetPosition 读取鼠标坐标再手写命中测试来选择画布对象。GetPosition 只用于自由坐标拾取、放置点、测量、绘制轮廓、吸附到点位等确实需要返回坐标的位置交互。

Painter 更适合调试绘制和非交互 overlay;需要被用户点击选择的业务对象,应尽量有对应的原生可选对象作为主表示。

var selectOp = new SelectObject { fineSelectOnPointClouds = true };
selectOp.feedback = (result, op) =>
{
    foreach (var (name, selector, firstSub) in result)
        Console.WriteLine(name);
};
selectOp.Start();
selectOp.SetSelectionMode(SelectObject.SelectionMode.Click);
selectOp.SetObjectSelectable("UISite-*");

SetSelectionMode 支持 ClickRectanglePaint

修饰键语义:Shift + 左键追加选择,Alt + 左键从选择集中移除,左键点击空白清空选择。不要重新发明 Ctrl 多选;Ctrl 留给其它操作。

sticky pattern

SetObjectSelectable(pattern) 是 sticky

  • 调用时对已存在对象做一次 glob 匹配并设为可选。
  • 之后新增对象时自动按该 pattern 匹配并设为可选。
  • 不需要在每次 Workspace.AddProp 后重复调用。

取消 sticky 必须用同一个 pattern 字符串:

selectOp.SetObjectUnselectable("UISite-*");

SetObjectUnselectable("UISite-3") 只清具体对象,不会移除 "UISite-*" 的 sticky 规则。

拾取世界坐标

var gp = new GetPosition
{
    method = GetPosition.PickMode.GridPlane,
    snaps = new[] { "UISite-*" },
    realtime = true,
};

gp.feedback = (wp, _) =>
{
    var xy = new Vector2(wp.mouse_pos.X, wp.mouse_pos.Y);
};
gp.finished = () => { };
gp.terminated = () => { };
gp.Start();

realtime = true 时,鼠标移动也会触发 feedback,适合用 Painter 做橡皮筋预览;最终左键点击时再触发 finished

SelectObject 和 GetPosition 互斥

Workspace 输入由当前 UI operation 消费。进入放置/拾取模式前先 selectOp.End();结束后再创建并启动新的 SelectObject。sticky pattern 是 per-op 状态,重启选择 op 后需要重新设置 pattern。

async/await 包装

WorkspaceUIOperation 包成 Task 时,feedback 只更新数据或 TrySetResult,不要在 feedback 后半段重启新 op 或做全局清理。清理放到调用方 try/finally

推荐模式:

private static Task<Vector2?> PickPoint(State st, Action<Vector2>? onPreview = null)
{
    var tcs = new TaskCompletionSource<Vector2?>();
    Vector2 last = default;
    bool got = false;

    var gp = new GetPosition { realtime = onPreview != null };
    gp.feedback = (wp, _) =>
    {
        last = new Vector2(wp.mouse_pos.X, wp.mouse_pos.Y);
        got = true;
        onPreview?.Invoke(last);
    };
    gp.finished = () => tcs.TrySetResult(got ? last : null);
    gp.terminated = () => tcs.TrySetResult(null);
    gp.Start();
    st.CurrentOp = gp;
    return tcs.Task;
}

业务取消按钮必须显式唤醒 pending task

st.CurrentOp?.End();
st.PendingTcs?.TrySetResult(null);

End() 不会触发 terminated

Guizmo 自由度限制

GuizmoAction 使用 dof 声明允许的平移/旋转自由度。CycleGUI 是 Z-up 语义,Yaw 等价于 RotateZ,常用于地面 XY 平面编辑。

new GuizmoAction
{
    dof = GuizmoAction.GuizmoDof.PlanarXYYaw,
    realtimeResult = true,
    feedback = (items, _) =>
    {
        foreach (var (name, pos, rot) in items)
            Console.WriteLine($"{name}: {pos}, {rot}");
    },
}.Start();

常用组合:

  • TranslateXYZ:只允许三轴平移。
  • PlanarXY:只允许 X/Y 平面平移。
  • PlanarXYYaw:只允许 X/Y 平移和绕 Z 轴旋转。
  • Yaw:只允许绕 Z 轴旋转。
  • RotateXYZ:只允许三轴旋转。
  • All:允许三轴平移和三轴旋转。