@@ -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
0 commit comments