implement clipping for line drawing

This commit is contained in:
Vadim Lopatin 2015-11-03 16:34:10 +03:00
parent 0778b77531
commit 01d39e0635
2 changed files with 106 additions and 16 deletions

View File

@ -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;
}
// CohenSutherland 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;
}

View File

@ -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));
}