Skip to content

feat: support outerBorder / innerBorder for polygon graphic - #2103

Open
g1f9 wants to merge 1 commit into
VisActor:developfrom
g1f9:feat/polygon-outer-border
Open

feat: support outerBorder / innerBorder for polygon graphic#2103
g1f9 wants to merge 1 commit into
VisActor:developfrom
g1f9:feat/polygon-outer-border

Conversation

@g1f9

@g1f9 g1f9 commented Jul 26, 2026

Copy link
Copy Markdown

Closes #2102

What

outerBorder / innerBorder are implemented per graphic type in the render contributions. rect / arc / circle / symbol have them; polygon-contribution-render.ts only re-exports texture and background, so configuring outerBorder on a polygon silently renders nothing.

This adds the polygon implementation.

How

  • common/polygon.tsoffsetPolygonPoints(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 distance always expands outwards regardless of vertex winding; innerBorder passes a negative distance. Degenerate (zero-length) and collinear edges fall back to the original vertex, so no NaN coordinate can reach the path.

  • polygon-contribution-render.tsDefaultPolygonRenderContribution
    Mirrors the rect contribution: afterFillStroke, honours keepStrokeScale / getScaledStroke, supports strokeCb. 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 in DefaultCanvasPolygonRender.builtinContributions.

    It also reproduces rect's opacity inject/restore around setStrokeStyle — the theme defaults for outerBorder carry strokeOpacity but no opacity, and without the injection setStrokeStyle bails out silently and the border is stroked with leftover canvas state. See the issue for details.

  • graphic/polygon.ts
    updatePolygonAABBBoundsImprecise now calls updateBoundsOfCommonOuterBorder, 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.5 and running in our product (funnel charts in VChart, both the funnel body and the conversion bands are polygons):

  • outerBorder renders as a closed ring equidistant from the trapezoid, following the rounded corners;
  • innerBorder renders on the inside;
  • the graphic's own stroke is untouched (which was the whole point — writing stroke directly would overwrite it);
  • no repaint artefacts once the AABB includes the border.

No behaviour change for polygons that don't configure a border: the contribution returns immediately when neither outerBorder nor innerBorder is set.

@g1f9
g1f9 changed the base branch from main to develop July 26, 2026 11:19
@g1f9
g1f9 force-pushed the feat/polygon-outer-border branch from 39d02f3 to e31507c Compare July 27, 2026 06:54

@xuefei1313 xuefei1313 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

发现 3 个需要修正的边界条件,均已附在对应行:缩放下 AABB 与实际边框坐标不一致、凹角圆角偏移方向错误,以及重复顶点在圆角分支中仍会产生 NaN。

strokeBoundsBuffer = defaultOuterBorder.strokeBoundsBuffer
} = outerBorder;

offsetPolygonPoints(points, distance as number, closePath).forEach(point => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] 这里的 AABB 没有和实际绘制使用同一坐标空间。keepStrokeScale 默认为 false 时,渲染端会用 getScaledStrokedistance 转为局部坐标;例如 scaleX = scaleY = 0.5distance = 10 时,绘制的局部偏移是 20(屏幕上仍是 10),但这里仅偏移 10,随后再被矩阵缩小为屏幕上的 5。增量重绘会因此漏清/裁掉外边框。请在这里使用等价的局部缩放距离(或在变换后的坐标中合并边界),并覆盖缩小变换的回归测试。

const nextPoints = offsetPolygonPoints(points, d, closePath);
// 圆角随外扩 / 内缩同步变化,保证边框与图元轮廓等距
const nextCornerRadius = isArray(cornerRadius)
? (<number[]>cornerRadius).map(r => Math.max(0, r + d))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] r + d 只适用于凸角;凹角的平行偏移必须反向调整半径。以带凹口的 L 形 polygon 为例,cornerRadius: 10outerBorder.distance: 5 时,凹角的两条偏移边仍应围绕原圆心使用半径 5,这里却变成 15,因此边框不再等距,并可能在凹口处相交。请按每个顶点的转向分别选择 r + dr - d(innerBorder 相应反向),并添加凹多边形的回归用例。

if (noCorner) {
drawPolygon(context.camera ? context : context.nativeContext, nextPoints, x, y);
} else {
drawRoundedPolygon(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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.
@g1f9
g1f9 force-pushed the feat/polygon-outer-border branch from e31507c to c543c0f Compare July 28, 2026 09:10

@xuefei1313 xuefei1313 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

上轮提出的凹角圆角、重复顶点和图元自身缩放场景均已处理;但非缩放边框在父级缩放下的 AABB 仍会漏算,需修正后再合入。

const scale = Math.abs(scaleX + scaleY);
let scaledDistance = distance as number;
if (!keepStrokeScale) {
scaledDistance = scale === 0 ? 0 : (scaledDistance * 2) / scale;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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 的回归测试。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] polygon does not render outerBorder / innerBorder

2 participants