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 {