This is an automated email from the ASF dual-hosted git repository.

hansva pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/hop.git


The following commit(s) were added to refs/heads/main by this push:
     new 684514c9eb Refactor grid drawing logic for better performance #7332 
(#7333)
684514c9eb is described below

commit 684514c9ebaf1c7f76232ac47eceae888139d2f3
Author: Nicolas Adment <[email protected]>
AuthorDate: Mon Jun 29 16:04:03 2026 +0200

    Refactor grid drawing logic for better performance #7332 (#7333)
    
    * Refactor grid drawing logic for better performance #7332
    Draw a grid without magnification so that the points are not enlarged
    
    * minor fixes for infinite move
    
    ---------
    
    Co-authored-by: Hans Van Akelyen <[email protected]>
---
 .../java/org/apache/hop/core/gui/BasePainter.java  | 89 +++++++++++-----------
 .../org/apache/hop/pipeline/PipelinePainter.java   | 10 ++-
 .../org/apache/hop/workflow/WorkflowPainter.java   | 10 ++-
 3 files changed, 57 insertions(+), 52 deletions(-)

diff --git a/engine/src/main/java/org/apache/hop/core/gui/BasePainter.java 
b/engine/src/main/java/org/apache/hop/core/gui/BasePainter.java
index 13abdf3dd1..8c41651478 100644
--- a/engine/src/main/java/org/apache/hop/core/gui/BasePainter.java
+++ b/engine/src/main/java/org/apache/hop/core/gui/BasePainter.java
@@ -267,62 +267,59 @@ public abstract class BasePainter<H extends 
BaseHopMeta<?>, P extends IBaseMeta>
    * Maximum grid points to draw; avoids severe slowdown on large/zoomed-out 
canvases (e.g.
    * Windows).
    */
-  private static final int MAX_GRID_POINTS = 50_000;
+  private static final int MAX_GRID_POINTS = 10_000;
 
   protected void drawGrid() {
     if (area == null || area.x <= 0 || area.y <= 0) {
       return;
     }
-    // Grid is drawn in the same coordinate system as drawOriginBoundary: the 
origin (0,0) of the
-    // pipeline is at (offset.x, offset.y) here. The hatched "outside 
workable" area is x < offset.x
-    // or y < offset.y. So we only draw grid where x >= offset.x and y >= 
offset.y.
+    // Grid is drawn at identity transform (screen pixels). offset is in graph 
units, so a graph
+    // point (gx, gy) maps to screen ((offset.x + gx) * mag, (offset.y + gy) * 
mag) -- the same
+    // mapping the painter uses for nodes, which are drawn at (offset + 
location) and scaled by mag.
+    // The pipeline origin (0,0) therefore sits at screen (offset.x * mag, 
offset.y * mag). We only
+    // draw grid points inside the visible area [0, area.x] x [0, area.y] and 
in the workable region
+    // (screen x >= origin and y >= origin).
     float mag = Math.max(0.1f, magnification);
-    int originX = (int) Math.round(offset.x);
-    int originY = (int) Math.round(offset.y);
-    int workableMinX = Math.max(0, originX);
-    int workableMinY = Math.max(0, originY);
-    // Visible extent in this coordinate system is (0,0) to (area.x/mag, 
area.y/mag); workable part
-    // is from (originX, originY) to that right/bottom edge.
-    int workableMaxX = (int) Math.ceil(area.x / mag);
-    int workableMaxY = (int) Math.ceil(area.y / mag);
-    if (workableMaxX <= workableMinX || workableMaxY <= workableMinY) {
+
+    // Pipeline origin (0,0) in screen pixels. offset is in graph units -> 
multiply by
+    // magnification.
+    double originScreenX = offset.x * mag;
+    double originScreenY = offset.y * mag;
+
+    // Visible screen bounds clamped to the workable area (no hatched region)
+    int screenMinX = Math.max(0, (int) Math.round(originScreenX));
+    int screenMinY = Math.max(0, (int) Math.round(originScreenY));
+    int screenMaxX = area.x;
+    int screenMaxY = area.y;
+    if (screenMaxX <= screenMinX || screenMaxY <= screenMinY) {
       return;
     }
 
-    int baseStep = (mag < 1.0f) ? Math.max(gridSize, (int) (gridSize / mag)) : 
gridSize;
-    int rangeX = workableMaxX - workableMinX;
-    int rangeY = workableMaxY - workableMinY;
-    long totalPoints = (long) (rangeX / baseStep + 1) * (rangeY / baseStep + 
1);
-    int step = baseStep;
-    if (totalPoints > MAX_GRID_POINTS) {
-      int minStep =
-          Math.max(
-              gridSize,
-              (int) Math.ceil(Math.sqrt((double) rangeX * rangeY / (double) 
MAX_GRID_POINTS)));
-      step = Math.max(baseStep, minStep);
-    }
-    int offsetX = (int) (offset.x % step);
-    int offsetY = (int) (offset.y % step);
-    if (offsetX < 0) {
-      offsetX += step;
-    }
-    if (offsetY < 0) {
-      offsetY += step;
+    // Grid step in screen pixels: gridSize graph units * mag pixels/unit
+    // Use double precision throughout to avoid rounding errors on large 
canvases
+    double step = Math.max(1.0, gridSize * (double) mag);
+
+    // Visible range in screen pixels
+    int rangeX = screenMaxX - screenMinX;
+    int rangeY = screenMaxY - screenMinY;
+
+    // Adjust the step until we are within the limit
+    while ((rangeX / step + 1) * (rangeY / step + 1) > MAX_GRID_POINTS) {
+      step *= 2;
     }
-    // First grid position at or after workable visible origin (never in 
hatched area)
-    int startX =
-        Math.max(
-            workableMinX,
-            offsetX + step * (int) Math.ceil((double) (workableMinX - offsetX) 
/ step));
-    int startY =
-        Math.max(
-            workableMinY,
-            offsetY + step * (int) Math.ceil((double) (workableMinY - offsetY) 
/ step));
-    for (int x = startX; x < workableMaxX; x += step) {
-      for (int y = startY; y < workableMaxY; y += step) {
-        if (x >= 0 && y >= 0) {
-          gc.drawPoint(x, y);
-        }
+
+    // First grid position at or after the visible workable origin, aligned to 
the graph origin
+    // (grid lines sit at screen position origin + k*step, the same lattice 
nodes snap to).
+    double startX = originScreenX + Math.ceil((screenMinX - originScreenX) / 
step) * step;
+    double startY = originScreenY + Math.ceil((screenMinY - originScreenY) / 
step) * step;
+
+    gc.setForeground(EColor.BLACK);
+
+    // Use double-precision counters in the loop to avoid accumulated rounding 
errors
+    // when step is large or the canvas is wide (many iterations).
+    for (double x = startX; x < screenMaxX; x += step) {
+      for (double y = startY; y < screenMaxY; y += step) {
+        gc.drawPoint((int) x, (int) y);
       }
     }
   }
diff --git a/engine/src/main/java/org/apache/hop/pipeline/PipelinePainter.java 
b/engine/src/main/java/org/apache/hop/pipeline/PipelinePainter.java
index d414eb51c5..06ec62b07a 100644
--- a/engine/src/main/java/org/apache/hop/pipeline/PipelinePainter.java
+++ b/engine/src/main/java/org/apache/hop/pipeline/PipelinePainter.java
@@ -198,10 +198,16 @@ public class PipelinePainter extends 
BasePainter<PipelineHopMeta, TransformMeta>
   public void drawPipelineImage() throws HopException {
     // Make sure the canvas is scaled 100%
     gc.setTransform(0.0f, 0.0f, 1.0f);
+
     // First clear the image in the background color
     gc.setBackground(EColor.BACKGROUND);
     gc.fillRectangle(0, 0, area.x, area.y);
 
+    // Draw the grid if this option is enabled
+    if (gridSize > 1) {
+      drawGrid();
+    }
+
     // Draw the pipeline onto the image
     //
     gc.setTransform((float) offset.x, (float) offset.y, magnification);
@@ -260,9 +266,7 @@ public class PipelinePainter extends 
BasePainter<PipelineHopMeta, TransformMeta>
   }
 
   private void drawPipeline() throws HopException {
-    if (gridSize > 1) {
-      drawGrid();
-    }
+
     drawOriginBoundary();
 
     try {
diff --git a/engine/src/main/java/org/apache/hop/workflow/WorkflowPainter.java 
b/engine/src/main/java/org/apache/hop/workflow/WorkflowPainter.java
index bbc04c8aea..9ff8d11507 100644
--- a/engine/src/main/java/org/apache/hop/workflow/WorkflowPainter.java
+++ b/engine/src/main/java/org/apache/hop/workflow/WorkflowPainter.java
@@ -93,10 +93,16 @@ public class WorkflowPainter extends 
BasePainter<WorkflowHopMeta, ActionMeta> {
   public void drawWorkflow() throws HopException {
     // Make sure the canvas is scaled 100%
     gc.setTransform(0.0f, 0.0f, 1.0f);
+
     // First clear the image in the background color
     gc.setBackground(EColor.BACKGROUND);
     gc.fillRectangle(0, 0, area.x, area.y);
 
+    // Draw the grid if this option is enabled
+    if (gridSize > 1) {
+      drawGrid();
+    }
+
     // Draw the workflow onto the image
     //
     gc.setAlpha(255);
@@ -155,9 +161,7 @@ public class WorkflowPainter extends 
BasePainter<WorkflowHopMeta, ActionMeta> {
   }
 
   private void drawActions() throws HopException {
-    if (gridSize > 1) {
-      drawGrid();
-    }
+
     drawOriginBoundary();
 
     try {

Reply via email to