ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Penrose 交互性详解:Edit Mode 与 Play Mode 的完整指南

Penrose 交互性详解:Edit Mode 与 Play Mode 的完整指南 开发工具数据可视化【免费下载链接】penroseCreate beautiful diagrams just by typing notation in plain text.项目地址https://gitcode.com/gh_mirrors/pe/penrose点击查看免费下载本指南聚焦 Penrose 编辑器位于 packages/editor中两种实验性交互模式的完整原理与实操Edit Mode用于对已生成图形做“最后微调”平移、缩放、钉住Play Mode则用于通过draggingConstraint约束拖拽来快速原型化交互式示意图。阅读本文后你将掌握如何判断哪些形状可平移/可缩放、如何手动钉住与取消钉住形状以及如何编写自己的 JavaScript 拖拽约束函数来限制形状只能在特定区域内移动。两种交互模式的定位Penrose 的核心思想是“用纯文本符号描述图表由优化器自动布局”。而交互性则是编辑器在其之上补充的能力——它让你在优化器完成布局之后仍能通过鼠标直接操纵画布上的形状。根据 Interactivity.md 的定义编辑器支持两种形式的交互Edit Mode编辑模式对大多数形状进行平移translation和缩放scaling用于最终的“临场微调”last-minute adjustmentsPlay Mode播放模式带约束的拖拽constrained dragging用于快速原型化交互式示意图。两者共用同一套底层平移逻辑makeTranslateOnMouseDown区别在于是否显示包围盒控件、以及 Play Mode 只对声明了draggingConstraint的形状生效。值得注意的是官方文档将该功能标注为experimental实验性意味着相关 API 与行为仍可能随版本演进而变化。交互性的底层数据模型在深入两种模式之前先理解交互性在代码层面的承载结构这对判断“哪些形状能交互”至关重要。Penrose 的编译器Style.ts在编译 Style 程序后会构建一个InteractivityInfo结构并挂到State上其定义位于 state.tsexport interface InteractivityInfo { inputIdxsByPath: IdxsByPath; translatableShapePaths: Setstring; scalableShapePaths: Setstring; // Map of paths to strings of bodies of functions over mouse coordinates draggingConstraints: Mapstring, string; shapesByPath: Mapstring, Shapead.Num; }其中translatableShapePaths/scalableShapePaths分别记录“可平移”与“可缩放”的形状路径集合由编译期判定见下文draggingConstraints从每个形状的draggingConstraint透传属性中提取的字符串形式的拖拽约束函数体键为形状路径对应 Style.ts 中遍历nameShapeMap、读取shape.passthrough.get(draggingConstraint)并校验其标签为StrV的逻辑inputIdxsByPath/shapesByPath分别将形状属性映射到优化问题的输入变量索引即ad.Var在varyingValues数组中的位置以及映射到带自动微分数值的Shape对象。理解这一点非常关键交互本质上是在改写优化问题的输入变量。平移/缩放最终都会被转换成对varyingValues中某些索引的数值替换因此只有那些由“采样变量”驱动的属性才可能被交互操纵。Edit Mode选中、平移、缩放与钉住在编辑器的Settings - Interactive Mode - Edit Mode中开启 Edit Mode 后部分形状会变为可选中状态光标变为 crosshair。选中一个形状后可以平移translate直接拖拽形状本体缩放scale拖拽包围盒四角的缩放把手钉住pin平移或缩放之后形状会被自动“钉住”以红色包围盒标示此时优化器不再尝试优化它的位置或尺寸你也可以通过右键手动钉住/取消钉住。这些交互的 UI 层实现在 InteractiveWidget.tsx左键e.button 0触发translateOnMouseDown开始拖拽右键则切换钉住状态changePin(props.path, !currentlyPinned)见 InteractiveWidget.tsx包围盒颜色由mainCol props.pinnedPaths.has(props.path) ? red : black决定——钉住为红色、未钉住为黑色InteractiveWidget.tsx缩放把手只在scalableShapePaths包含该路径时渲染四个角的鼠标处理器分别由makeScaleOnMouseDown以topLeft/topRight/bottomLeft/bottomRight四个 corner 参数创建InteractiveWidget.tsx。平移/缩放动作最终通过interactAndUpdate提交Translation/Scale/ChangePin三种交互消息renderUtils.ts再交给优化器 worker 处理并更新历史记录。可平移性的判定规则并非所有形状都可平移。官方文档给出的判定标准是形状的中心坐标center必须是采样值sampled value否则拖拽就没有可以影响的优化问题输入。从源码看这个规则由 Shapes.ts 中的isTranslatable精确实现Circle、Ellipse、Equation、Image、Rectangle、Text要求center的两个分量都是ad.VarisVar即它们确实是优化变量Line要求start与end的全部四个坐标都是变量Polygon/Polyline要求points列表中每个点的 x、y 都是变量Group、Path固定不可平移源码直接返回false这对应文档中“Paths and groups are not currently interactive”的说明。文档中的例子正好与此对应——以下三种写法是可平移的c1 Circle {} // center is by default sampled c2 Circle { center: (?, ?) } center3 (?, ?) c3 Circle { center: center3 }而以下两种是不可平移的c4 Circle { center: (?, 0) } center5 (?, ?) c5 Circle { center: center (0, 10) }c4的center只有一个分量是采样变量另一个被固定为0c5的center则是对采样变量center的算术变换center (0, 10)其结果是ad.Num表达式而非单纯的变量引用——因此isVar判定失败形状不可交互。这也解释了文档所说“there is no input to the optimization problem that dragging could affect”拖拽只能改写直接对应优化输入变量的索引无法改写由表达式推导出的值。可缩放性的判定规则类似地可缩放性取决于尺寸类属性是否为采样值。isScalableShapes.ts按形状类型区分Circler必须是变量Ellipserx与ry都必须是变量Image/Rectanglewidth与height都必须是变量Equation/Text/Line/Polygon/Polyline/Group/Path不可缩放统一返回false。注意Equation与Text可平移但不可缩放Line、Polygon、Polyline可平移但缩放目前未实现。这一点在 InteractionUtils.ts 的getScalingInfo中体现得更具体Circle走RadiusScaling使用path .r的输入索引Ellipse走WidthHeightScaling使用rx/ryEquation、Image、Rectangle、Text走WidthHeightScaling使用width/heightLine、Polygon、Polyline走PointsScaling但 InteractionUtils.ts 中PointsScaling分支目前会抛出Points scaling not implemented——即点类形状的缩放是一个未完成的 TODO。平移与缩放的数值语义一旦形状被标记为可交互编辑器就可以把鼠标事件转译成对优化输入数值的修改平移translateVaryingValuesInteractionUtils.ts通过getTranslatedInputsIdxs拿到该形状所有受控的[xIdx, yIdx]索引对然后对recentVaryingValues的副本中这些索引分别加上dx、dy缩放scaleVaryingValuesInteractionUtils.ts对RadiusScaling使用start * min(sx, sy)取缩放因子的较小值保证等比缩放对WidthHeightScaling使用start[0] * sx、start[1] * sy。鼠标坐标到 SVG 坐标的换算由getScreenToSvgPosition利用DOMPoint.matrixTransform(CTM.inverse())反转屏幕坐标系变换矩阵完成而拖拽过程中形状被限制在画布范围内makeTranslateOnMouseDown用_.clamp(x, minX, maxX)保证形状不会拖出画布InteractionUtils.ts。Play Mode用 draggingConstraint 约束拖拽Play Mode 在Settings - Interactive Mode - Play Mode中开启。它同样允许拖拽形状受与 Edit Mode 相同的可平移条件限制但不显示包围盒控件且只有声明了draggingConstraint属性的形状才可拖拽c1 Circle { -- draggable anywhere on canvas draggingConstraint: return [x, y]; } c2 Circle { -- draggable only in the top-right quadrant draggingConstraint: return [Math.max(0, x), Math.max(0, y)]; }draggingConstraint的取值是一个JavaScript 函数体字符串该函数接收鼠标的x与y位置返回对象的期望位置。如果你想将拖拽约束在某个区域内这个函数应当计算“到该区域的投影”projection。约束函数是如何被执行的从 renderUtils.ts 的renderPlayModeInteractivity可以看到 Play Mode 的完整装配流程遍历state.interactivityInfo.draggingConstraints按形状路径取出约束函数体字符串通过new Function([x, y], constraint)在运行时构造真正的约束函数renderUtils.ts——这正是文档所说“the body of a javascript function”的实现方式对该形状的 SVG 元素族包括元素自身及所有子元素逐个挂载makeTranslateOnMouseDown并把约束函数作为constraint参数传入renderUtils.ts同时设置cursor: grab与pointer-events: visiblePainted拖拽结束时通过ChangePinactive: false清理钉住状态。约束函数在 InteractionUtils.ts 中被应用原始指针位置会先转换为以形状中心为原点的画布相对坐标经过约束函数投影后再换算回 SVG 坐标用于平移。也就是说draggingConstraint返回的[x, y]是以画布中心为原点、y 轴向上的坐标系中的期望位置——这与 Penrose 内部center的语义一致。真实示例轨道行星仓库中的 planets.style 提供了一个非常直观的 Play Mode 应用用draggingConstraint把四颗行星的拖拽限制在以恒星为中心的圆形轨道上forall Planet Venus { Venus.icon Circle { fillColor: #ffbb00ff r: 9 draggingConstraint: const a Math.atan2(y, x); return [107 * Math.cos(a), 107 * Math.sin(a)]; } }这里约束函数先用Math.atan2(y, x)计算鼠标相对恒星的极角a再投影到半径 107金星轨道半径的圆上从而实现“沿轨道拖动”的效果。该示例对应的完整 trio 配置为 planets.trio.json配套的 domain 与 substance 分别是 planets.domain 与 planets.substance。这类“投影到轨道/线段/矩形区域”的写法正是draggingConstraint最典型的应用模式。实战建议与已知限制综合文档与源码使用交互功能时需要注意以下几点可交互性由 Style 写法决定想要一个形状可平移务必让它的center或Line的start/end、Polygon/Polyline的points由采样变量直接驱动不要套上算术表达式或固定常量分量路径Path与组Group暂不支持交互不要在这类形状上期望 Edit Mode 的平移缩放或 Play Mode 的拖拽点类形状的缩放尚未实现源码中PointsScaling分支抛出未实现错误Line/Polygon/Polyline目前只能平移钉住是优化器的“锁定”语义钉住后优化器不再调整该形状的位置/尺寸适合在自动布局完成后固定局部元素的最终微调draggingConstraint是运行时求值的 JavaScript由于通过new Function构造函数体内可以使用标准 JavaScript API如Math.atan2、Math.max、Math.cos并依赖参数x、y该功能整体仍处于实验阶段文档标题与源码注释如Points scaling not implemented都表明相关行为可能在后续版本中调整。如果需要进一步研究实现细节可以重点阅读以下源码可平移/可缩放判定Shapes.ts平移与缩放的数值改写、鼠标坐标换算InteractionUtils.ts交互数据结构与状态挂载state.ts、Style.ts编辑模式 UI 控件InteractiveWidget.tsx播放模式装配与约束函数求值renderUtils.ts交互示例planets.style赞分享开发工具数据可视化【免费下载链接】penroseCreate beautiful diagrams just by typing notation in plain text.项目地址https://gitcode.com/gh_mirrors/pe/penrose点击查看免费下载相关推荐Vitest Browser Mode 交互 APIuserEvent完整实战指南Vitest Browser Mode 交互 APIuserEvent完整实战指南 Vitest 的 Browser Mode 通过 vitest/brow测试前端开发工具Vitest browser.enabled 配置详解开启 Browser Mode 的完整实战指南Vitest browser.enabled 配置详解开启 Browser Mode 的完整实战指南 browser.enabled 是 Vitest 中开启测试前端开发工具OHIF Mode 有效性校验Mode Validity机制详解从 isValidMode 到数据驱动的 Mode 可用性配置OHIF Mode 有效性校验Mode Validity机制详解从 isValidMode 到数据驱动的 Mode 可用性配置 导读 本文围绕 OHIF医疗健康前端音视频上一篇Argos Translate 离线翻译完整指南三步搞定断网也能精准翻译下一篇ParlAI 中的 ConvAI2 任务基于 PersonaChat 的闲聊对话数据集与 Teacher 体系全解析创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表