feat: support outerBorder / innerBorder for polygon graphic - #2103
Conversation
39d02f3 to
e31507c
Compare
xuefei1313
left a comment
There was a problem hiding this comment.
发现 3 个需要修正的边界条件,均已附在对应行:缩放下 AABB 与实际边框坐标不一致、凹角圆角偏移方向错误,以及重复顶点在圆角分支中仍会产生 NaN。
| strokeBoundsBuffer = defaultOuterBorder.strokeBoundsBuffer | ||
| } = outerBorder; | ||
|
|
||
| offsetPolygonPoints(points, distance as number, closePath).forEach(point => { |
There was a problem hiding this comment.
[P1] 这里的 AABB 没有和实际绘制使用同一坐标空间。keepStrokeScale 默认为 false 时,渲染端会用 getScaledStroke 将 distance 转为局部坐标;例如 scaleX = scaleY = 0.5、distance = 10 时,绘制的局部偏移是 20(屏幕上仍是 10),但这里仅偏移 10,随后再被矩阵缩小为屏幕上的 5。增量重绘会因此漏清/裁掉外边框。请在这里使用等价的局部缩放距离(或在变换后的坐标中合并边界),并覆盖缩小变换的回归测试。
| const nextPoints = offsetPolygonPoints(points, d, closePath); | ||
| // 圆角随外扩 / 内缩同步变化,保证边框与图元轮廓等距 | ||
| const nextCornerRadius = isArray(cornerRadius) | ||
| ? (<number[]>cornerRadius).map(r => Math.max(0, r + d)) |
There was a problem hiding this comment.
[P1] r + d 只适用于凸角;凹角的平行偏移必须反向调整半径。以带凹口的 L 形 polygon 为例,cornerRadius: 10、outerBorder.distance: 5 时,凹角的两条偏移边仍应围绕原圆心使用半径 5,这里却变成 15,因此边框不再等距,并可能在凹口处相交。请按每个顶点的转向分别选择 r + d 或 r - d(innerBorder 相应反向),并添加凹多边形的回归用例。
| if (noCorner) { | ||
| drawPolygon(context.camera ? context : context.nativeContext, nextPoints, x, y); | ||
| } else { | ||
| drawRoundedPolygon( |
There was a problem hiding this comment.
[P1] offsetPolygonPoints 虽然让重复顶点的结果保持有限值,但圆角分支仍会把重复点传给 drawRoundedPolygon。现有 duplicate-edge 测试的输入只要再设置 cornerRadius: 2,该函数就在该顶点得到 length2 = 0,随后 getProportionPoint 发生 0 / 0 并将 NaN 写入 lineTo/arcTo。Polygon 目前接受重复顶点,请在圆角绘制前收敛退化边,或让 drawRoundedPolygon 跳过零长度边,并补充实际 border-render 回归测试。
Add equidistant polygon border outlines and register the polygon render contribution. Handle transformed bounds, open paths, convex and concave rounded corners, and degenerate or duplicate edges without emitting non-finite coordinates. Add focused geometry, bounds, and render regression coverage.
e31507c to
c543c0f
Compare
xuefei1313
left a comment
There was a problem hiding this comment.
上轮提出的凹角圆角、重复顶点和图元自身缩放场景均已处理;但非缩放边框在父级缩放下的 AABB 仍会漏算,需修正后再合入。
| const scale = Math.abs(scaleX + scaleY); | ||
| let scaledDistance = distance as number; | ||
| if (!keepStrokeScale) { | ||
| scaledDistance = scale === 0 ? 0 : (scaledDistance * 2) / scale; |
There was a problem hiding this comment.
[P1] 这里仍只根据 polygon 自身的 scaleX/scaleY 反算距离,但渲染端的 getScaledStroke(context, ...) 依据的是 context.currentMatrix;该矩阵已经包含父级 group(以及 viewBox)的变换。比如父 group 为 scaleX = scaleY = 0.5、polygon 自身 scale 为 1、keepStrokeScale: false、distance: 10 时,绘制会在 polygon 局部偏移 20,最终屏幕边界到 -10;这里仍只偏移 10,随后 globalAABBBounds 被父级缩到 -5,增量重绘会裁掉外边框。boundStroke 的 lineWidth 也同样未按该矩阵反算(当前回归用例设为 0,因此覆盖不到)。请基于与 getScaledStroke 等价的完整变换计算边界,或在变换后的坐标中合并边界,并添加父级缩放 + 非零 outerBorder.lineWidth 的回归测试。
Closes #2102
What
outerBorder/innerBorderare implemented per graphic type in the render contributions. rect / arc / circle / symbol have them;polygon-contribution-render.tsonly re-exports texture and background, so configuringouterBorderon a polygon silently renders nothing.This adds the polygon implementation.
How
common/polygon.ts—offsetPolygonPoints(points, distance)Offsets every edge along its normal and intersects adjacent offset lines to get an equidistant outline. The polygon's signed area decides the normal direction, so a positive
distancealways expands outwards regardless of vertex winding;innerBorderpasses a negative distance. Degenerate (zero-length) and collinear edges fall back to the original vertex, so noNaNcoordinate can reach the path.polygon-contribution-render.ts—DefaultPolygonRenderContributionMirrors the rect contribution:
afterFillStroke, honourskeepStrokeScale/getScaledStroke, supportsstrokeCb. Corner radius is shifted by the same distance (max(0, cornerRadius ± d)) so the border stays equidistant from the outline instead of drifting at the corners. Registered inDefaultCanvasPolygonRender.builtinContributions.It also reproduces rect's
opacityinject/restore aroundsetStrokeStyle— the theme defaults forouterBordercarrystrokeOpacitybut noopacity, and without the injectionsetStrokeStylebails out silently and the border is stroked with leftover canvas state. See the issue for details.graphic/polygon.tsupdatePolygonAABBBoundsImprecisenow callsupdateBoundsOfCommonOuterBorder, same as rect — otherwise the border sits outside the AABB and the dirty rect clips it on incremental repaints.Verification
Verified against the equivalent change backported into
1.1.5and running in our product (funnel charts in VChart, both the funnel body and the conversion bands are polygons):outerBorderrenders as a closed ring equidistant from the trapezoid, following the rounded corners;innerBorderrenders on the inside;strokeis untouched (which was the whole point — writingstrokedirectly would overwrite it);No behaviour change for polygons that don't configure a border: the contribution returns immediately when neither
outerBordernorinnerBorderis set.