mirror of https://github.com/buggins/dlangui.git
implement clipping for line drawing
This commit is contained in:
parent
0778b77531
commit
01d39e0635
|
@ -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;
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue