From 4276cb73c2c4155a8037250830bd3d90be8a32f5 Mon Sep 17 00:00:00 2001 From: yxx <2412119399@qq.com> Date: Sun, 6 Sep 2026 23:26:13 +0800 Subject: [PATCH] =?UTF-8?q?fix(plot):=20=E6=B8=90=E8=BF=91=E7=82=B9?= =?UTF-8?q?=E8=90=BD=E5=9C=A8=E9=87=87=E6=A0=B7=E7=82=B9=E4=B9=8B=E9=97=B4?= =?UTF-8?q?=E6=97=B6=E6=96=AD=E6=AE=B5=EF=BC=8C=E9=81=BF=E5=85=8D=E4=BC=AA?= =?UTF-8?q?=E7=AB=96=E7=BA=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 相邻有限采样点分居可见范围上下两侧时说明中间夹着竖直渐近线, 此前只对非有限值断段,会被 Liang-Barsky 裁剪成贯穿绘图区的伪竖线; 现在在共享几何层断段,并新增回归测试断言不存在跨越上下边界的伪连接线段。 Co-Authored-By: Claude Code --- backend/app/plot/render.py | 18 +++++++++++++++++- backend/tests/test_plot.py | 16 ++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/backend/app/plot/render.py b/backend/app/plot/render.py index e49700e..efd4d34 100644 --- a/backend/app/plot/render.py +++ b/backend/app/plot/render.py @@ -226,9 +226,14 @@ def _sample_segments( ymin: float, ymax: float, ) -> list[list[tuple[float, float]]]: - """采样并映射为像素点段,再裁剪到绘图矩形;非有限点处断段,避免画穿渐近线。""" + """采样并映射为像素点段,再裁剪到绘图矩形。 + + 两处断段:非有限点处(画穿渐近线);相邻有限采样点横跨可见范围上下两侧时 + (渐近点恰好落在两个采样点之间,否则会被裁剪成贯穿绘图区的伪竖线)。 + """ segments: list[list[tuple[float, float]]] = [] points: list[tuple[float, float]] = [] + prev_y: float | None = None for i in range(_SAMPLES + 1): x = xmin + (xmax - xmin) * i / _SAMPLES try: @@ -239,6 +244,7 @@ def _sample_segments( if points: segments.append(points) points = [] + prev_y = None continue px = _sx(x, xmin, xmax) py = _sy(y, ymin, ymax) @@ -247,8 +253,18 @@ def _sample_segments( if points: segments.append(points) points = [] + prev_y = None continue + # 渐近线检测:相邻有限采样点分居可见范围上下两侧(一个 < ymin、一个 > ymax), + # 说明两者之间夹着竖直渐近线,断段避免被 Liang-Barsky 裁剪成贯穿绘图区的伪竖线 + if prev_y is not None and ( + (prev_y < ymin and y > ymax) or (prev_y > ymax and y < ymin) + ): + if points: + segments.append(points) + points = [] points.append((px, py)) + prev_y = y if points: segments.append(points) diff --git a/backend/tests/test_plot.py b/backend/tests/test_plot.py index 514aeda..5e547c3 100644 --- a/backend/tests/test_plot.py +++ b/backend/tests/test_plot.py @@ -425,3 +425,19 @@ def test_render_reportlab_ylabel_within_drawing_bounds() -> None: x0, y0, x1, y1 = ylabel_groups[0].getBounds() assert 0 <= x0 <= x1 <= 640 assert 0 <= y0 <= y1 <= 480 + + +def test_compute_geometry_breaks_at_asymptote() -> None: + # P2:渐近点落在两个采样点之间时,两侧采样仍有限,若不断段会被 Liang-Barsky + # 裁剪成贯穿绘图区的伪竖线;这里断言不存在跨越上下边界的伪连接线段。 + from app.plot.render import _PLOT_Y0, _PLOT_Y1, compute_geometry + + plot = parse_source("domain: -1, 1\nrange: -10, 10\ny = 1/(x-0.013)").plot + geo = compute_geometry(plot) + assert any(geo.polylines) # 渐近线两侧的曲线分支仍在绘图区内可见 + full_height = _PLOT_Y1 - _PLOT_Y0 + for segments in geo.polylines: + for seg in segments: + # 相邻点垂直跨度若接近整个绘图区高度,即为渐近线伪连接 + for (_, py0), (_, py1) in zip(seg, seg[1:]): + assert abs(py1 - py0) < full_height * 0.5