From 01d39e06356313e334367576f915c213405fc92b Mon Sep 17 00:00:00 2001 From: Vadim Lopatin Date: Tue, 3 Nov 2015 16:34:10 +0300 Subject: [PATCH] implement clipping for line drawing --- src/dlangui/graphics/drawbuf.d | 112 ++++++++++++++++++++++++++++--- src/dlangui/graphics/gldrawbuf.d | 10 +-- 2 files changed, 106 insertions(+), 16 deletions(-) diff --git a/src/dlangui/graphics/drawbuf.d b/src/dlangui/graphics/drawbuf.d index 336a1385..eb76eef6 100644 --- a/src/dlangui/graphics/drawbuf.d +++ b/src/dlangui/graphics/drawbuf.d @@ -342,14 +342,8 @@ class DrawBuf : RefCountedObject { /// draw line from point p1 to p2 with specified color void drawLine(Point p1, Point p2, uint colour) { - if (p1.x < _clipRect.left && p2.x < _clipRect.left) - return; - if (p1.y < _clipRect.top && p2.y < _clipRect.top) - return; - if (p1.x >= _clipRect.right && p2.x >= _clipRect.right) - return; - if (p1.y >= _clipRect.bottom && p2.y >= _clipRect.bottom) - return; + if (!clipLine(_clipRect, p1, p2)) + return; // from rosettacode.org import std.math: abs; immutable int dx = p2.x - p1.x; @@ -1003,3 +997,105 @@ class ColorDrawBuf : ColorDrawBufBase { } +// line clipping algorithm from https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm +private alias OutCode = int; + +private const int INSIDE = 0; // 0000 +private const int LEFT = 1; // 0001 +private const int RIGHT = 2; // 0010 +private const int BOTTOM = 4; // 0100 +private const int TOP = 8; // 1000 + +// Compute the bit code for a point (x, y) using the clip rectangle +// bounded diagonally by (xmin, ymin), and (xmax, ymax) + +// ASSUME THAT xmax, xmin, ymax and ymin are global constants. + +private OutCode ComputeOutCode(Rect clipRect, double x, double y) +{ + OutCode code; + + code = INSIDE; // initialised as being inside of clip window + + if (x < clipRect.left) // to the left of clip window + code |= LEFT; + else if (x > clipRect.right) // to the right of clip window + code |= RIGHT; + if (y < clipRect.top) // below the clip window + code |= BOTTOM; + else if (y > clipRect.bottom) // above the clip window + code |= TOP; + + return code; +} + +package bool clipLine(ref Rect clipRect, ref Point p1, ref Point p2) { + double x0 = p1.x; + double y0 = p1.y; + double x1 = p2.x; + double y1 = p2.y; + bool res = CohenSutherlandLineClipAndDraw(clipRect, x0, y0, x1, y1); + if (res) { + p1.x = cast(int)x0; + p1.y = cast(int)y0; + p2.x = cast(int)x1; + p2.y = cast(int)y1; + } + return res; +} + +// Cohen–Sutherland clipping algorithm clips a line from +// P0 = (x0, y0) to P1 = (x1, y1) against a rectangle with +// diagonal from (xmin, ymin) to (xmax, ymax). +private bool CohenSutherlandLineClipAndDraw(ref Rect clipRect, ref double x0, ref double y0, ref double x1, ref double y1) +{ + // compute outcodes for P0, P1, and whatever point lies outside the clip rectangle + OutCode outcode0 = ComputeOutCode(clipRect, x0, y0); + OutCode outcode1 = ComputeOutCode(clipRect, x1, y1); + bool accept = false; + + while (true) { + if (!(outcode0 | outcode1)) { // Bitwise OR is 0. Trivially accept and get out of loop + accept = true; + break; + } else if (outcode0 & outcode1) { // Bitwise AND is not 0. Trivially reject and get out of loop + break; + } else { + // failed both tests, so calculate the line segment to clip + // from an outside point to an intersection with clip edge + double x, y; + + // At least one endpoint is outside the clip rectangle; pick it. + OutCode outcodeOut = outcode0 ? outcode0 : outcode1; + + // Now find the intersection point; + // use formulas y = y0 + slope * (x - x0), x = x0 + (1 / slope) * (y - y0) + if (outcodeOut & TOP) { // point is above the clip rectangle + x = x0 + (x1 - x0) * (clipRect.bottom - y0) / (y1 - y0); + y = clipRect.bottom; + } else if (outcodeOut & BOTTOM) { // point is below the clip rectangle + x = x0 + (x1 - x0) * (clipRect.top - y0) / (y1 - y0); + y = clipRect.top; + } else if (outcodeOut & RIGHT) { // point is to the right of clip rectangle + y = y0 + (y1 - y0) * (clipRect.right - x0) / (x1 - x0); + x = clipRect.right; + } else if (outcodeOut & LEFT) { // point is to the left of clip rectangle + y = y0 + (y1 - y0) * (clipRect.left - x0) / (x1 - x0); + x = clipRect.left; + } + + // Now we move outside point to intersection point to clip + // and get ready for next pass. + if (outcodeOut == outcode0) { + x0 = x; + y0 = y; + outcode0 = ComputeOutCode(clipRect, x0, y0); + } else { + x1 = x; + y1 = y; + outcode1 = ComputeOutCode(clipRect, x1, y1); + } + } + } + return accept; +} \ No newline at end of file diff --git a/src/dlangui/graphics/gldrawbuf.d b/src/dlangui/graphics/gldrawbuf.d index bc173c8e..a597a2eb 100644 --- a/src/dlangui/graphics/gldrawbuf.d +++ b/src/dlangui/graphics/gldrawbuf.d @@ -152,14 +152,8 @@ class GLDrawBuf : DrawBuf, GLConfigCallback { /// draw line from point p1 to p2 with specified color override void drawLine(Point p1, Point p2, uint colour) { assert(_scene !is null); - if (p1.x < _clipRect.left && p2.x < _clipRect.left) - return; - if (p1.y < _clipRect.top && p2.y < _clipRect.top) - return; - if (p1.x >= _clipRect.right && p2.x >= _clipRect.right) - return; - if (p1.y >= _clipRect.bottom && p2.y >= _clipRect.bottom) - return; + if (!clipLine(_clipRect, p1, p2)) + return; _scene.add(new LineSceneItem(p1, p2, colour)); }