Skip to content

Commit 87d64d9

Browse files
committed
fix: preserve clipped line pixels and equal axis limits
1 parent 2f251ba commit 87d64d9

3 files changed

Lines changed: 50 additions & 14 deletions

File tree

‎spec/audit_axis_spec.cr‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,4 +95,23 @@ describe "audited axis and line behavior" do
9595
end
9696
end
9797
end
98+
99+
it "keeps the original pixel phase when a vertical line enters the view" do
100+
canvas = UnicodePlot::BrailleCanvas.new(15, 40,
101+
origin_x: 1.0, origin_y: 1.0, width: 7.0, height: 6.0)
102+
canvas.lines!(7.8, 3.5, 7.8, 8.5)
103+
canvas.grid_at(5, 39).should eq(0x28b8_u32)
104+
end
105+
106+
it "expands equal finite limits by one unit when representable" do
107+
plot = UnicodePlot.lineplot([50.0], [0.0],
108+
xlim: {50.0, 50.0}, ylim: {-1.0, 1.0})
109+
plot.raw_xlim.should eq({49.0, 51.0})
110+
111+
large = UnicodePlot.lineplot([1e20], [0.0],
112+
xlim: {1e20, 1e20}, ylim: {-1.0, 1.0})
113+
lower, upper = large.raw_xlim.not_nil!
114+
lower.should be < 1e20
115+
upper.should be > 1e20
116+
end
98117
end

‎src/unicode_plot/canvas.cr‎

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,9 @@ module UnicodePlot
210210
self
211211
end
212212

213-
# Liang-Barsky clipping in pixel coordinates, before rasterization.
214-
private def clip_line_to_pixels(x1 : Float64, y1 : Float64, x2 : Float64, y2 : Float64) : {Float64, Float64, Float64, Float64}?
213+
# Liang-Barsky clipping in pixel coordinates. Return the visible portion
214+
# as parameters of the original segment so rasterization keeps its phase.
215+
private def clip_line_to_pixels(x1 : Float64, y1 : Float64, x2 : Float64, y2 : Float64) : {Float64, Float64}?
215216
return unless x1.finite? && y1.finite? && x2.finite? && y2.finite?
216217
dx = x2 - x1
217218
dy = y2 - y1
@@ -231,7 +232,7 @@ module UnicodePlot
231232
return if first > last
232233
end
233234
end
234-
{x1 + first * dx, y1 + first * dy, x1 + last * dx, y1 + last * dy}
235+
{first, last}
235236
end
236237

237238
def lines!(
@@ -267,17 +268,34 @@ module UnicodePlot
267268
plot_pixel_if_visible(end_x, end_y, color, blend, 0.0, @pixel_width.to_f, 0.0, @pixel_height.to_f)
268269
return self
269270
end
270-
sx, sy, ex, ey = clipped
271-
steps = Math.max((ex - sx).abs, (ey - sy).abs).ceil.to_i32
272-
if steps == 0
273-
plot_pixel_if_visible(sx, sy, color, blend, 0.0, @pixel_width.to_f, 0.0, @pixel_height.to_f)
274-
else
275-
(0..steps).each do |i|
276-
t = i.to_f / steps
277-
plot_pixel_if_visible(sx + (ex - sx) * t, sy + (ey - sy) * t,
271+
first, last = clipped
272+
delta_x = end_x - start_x
273+
delta_y = end_y - start_y
274+
nsteps = Math.max(delta_x.abs, delta_y.abs)
275+
sx, sy = start_x + first * delta_x, start_y + first * delta_y
276+
ex, ey = start_x + last * delta_x, start_y + last * delta_y
277+
plot_pixel_if_visible(sx, sy, color, blend, 0.0, @pixel_width.to_f, 0.0, @pixel_height.to_f)
278+
if nsteps > 0.0 && nsteps < Int64::MAX.to_f
279+
step_x, step_y = delta_x / nsteps, delta_y / nsteps
280+
first_step = Math.max(0_i64, (first * nsteps).floor.to_i64 - 1)
281+
last_step = Math.min(nsteps.ceil.to_i64, (last * nsteps).ceil.to_i64 + 1)
282+
(first_step..last_step).each do |step|
283+
plot_pixel_if_visible(start_x + step * step_x, start_y + step * step_y,
278284
color, blend, 0.0, @pixel_width.to_f, 0.0, @pixel_height.to_f)
279285
end
286+
elsif nsteps >= Int64::MAX.to_f
287+
# The original step index cannot be represented; sample the clipped
288+
# portion directly so extremely long lines still cross the canvas.
289+
steps = Math.max((ex - sx).abs, (ey - sy).abs).ceil.to_i32
290+
if steps > 0
291+
(0..steps).each do |step|
292+
t = step.to_f / steps
293+
plot_pixel_if_visible(sx + (ex - sx) * t, sy + (ey - sy) * t,
294+
color, blend, 0.0, @pixel_width.to_f, 0.0, @pixel_height.to_f)
295+
end
296+
end
280297
end
298+
plot_pixel_if_visible(ex, ey, color, blend, 0.0, @pixel_width.to_f, 0.0, @pixel_height.to_f)
281299
self
282300
end
283301

‎src/unicode_plot/common.cr‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -411,9 +411,8 @@ module UnicodePlot
411411
end
412412

413413
private def expand_equal_limits(value : Float64) : {Float64, Float64}
414-
delta = Math.max(value.abs * 0.1, 1.0)
415-
lower = value - delta
416-
upper = value + delta
414+
lower = value - 1.0
415+
upper = value + 1.0
417416
lower = value.prev_float unless lower.finite? && lower < value
418417
upper = value.next_float unless upper.finite? && upper > value
419418
lower = value if !lower.finite? && upper.finite?

0 commit comments

Comments
 (0)