#include #include "libnsfb.h" #include "nsfb.h" #include "plot_util.h" enum { POINT_LEFTOF_REGION = 1, POINT_RIGHTOF_REGION = 2, POINT_ABOVE_REGION = 4, POINT_BELOW_REGION = 8, }; #define REGION(x,y,cx1,cx2,cy1,cy2) \ ( ( (y) > (cy2) ? POINT_BELOW_REGION : 0) | \ ( (y) < (cy1) ? POINT_ABOVE_REGION : 0) | \ ( (x) > (cx2) ? POINT_RIGHTOF_REGION : 0) | \ ( (x) < (cx1) ? POINT_LEFTOF_REGION : 0) ) #define SWAP(a, b) do { int t; t=(a); (a)=(b); (b)=t; } while(0) /* clip a rectangle with another clipping rectangle. * * @param clip The rectangle to clip to. * @param rect The rectangle to clip. * @return false if the \a rect lies completely outside the \a clip rectangle, * true if some of the \a rect is still visible. */ bool nsfb_plot_clip(const nsfb_bbox_t * restrict clip, nsfb_bbox_t * restrict rect) { char region1; char region2; if (rect->x1 < rect->x0) SWAP(rect->x0, rect->x1); if (rect->y1 < rect->y0) SWAP(rect->y0, rect->y1); region1 = REGION(rect->x0, rect->y0, clip->x0, clip->x1 - 1, clip->y0, clip->y1 - 1); region2 = REGION(rect->x1, rect->y1, clip->x0, clip->x1 - 1, clip->y0, clip->y1 - 1); /* area lies entirely outside the clipping rectangle */ if ((region1 | region2) && (region1 & region2)) return false; if (rect->x0 < clip->x0) rect->x0 = clip->x0; if (rect->x0 > clip->x1) rect->x0 = clip->x1; if (rect->x1 < clip->x0) rect->x1 = clip->x0; if (rect->x1 > clip->x1) rect->x1 = clip->x1; if (rect->y0 < clip->y0) rect->y0 = clip->y0; if (rect->y0 > clip->y1) rect->y0 = clip->y1; if (rect->y1 < clip->y0) rect->y1 = clip->y0; if (rect->y1 > clip->y1) rect->y1 = clip->y1; return true; } bool nsfb_plot_clip_ctx(nsfb_t *nsfb, nsfb_bbox_t * restrict rect) { return nsfb_plot_clip(&nsfb->clip, rect); } /** Clip a line to a bounding box. */ bool nsfb_plot_clip_line(const nsfb_bbox_t *clip, nsfb_bbox_t * restrict line) { char region1; char region2; region1 = REGION(line->x0, line->y0, clip->x0, clip->x1 - 1, clip->y0, clip->y1 - 1); region2 = REGION(line->x1, line->y1, clip->x0, clip->x1 - 1, clip->y0, clip->y1 - 1); while (region1 | region2) { if (region1 & region2) { /* line lies entirely outside the clipping rectangle */ return false; } if (region1) { /* first point */ if (region1 & POINT_BELOW_REGION) { /* divide line at bottom */ line->x0 = (line->x0 + (line->x1 - line->x0) * (clip->y1 - 1 - line->y0) / (line->y1 - line->y0)); line->y0 = clip->y1 - 1; } else if (region1 & POINT_ABOVE_REGION) { /* divide line at top */ line->x0 = (line->x0 + (line->x1 - line->x0) * (clip->y0 - line->y0) / (line->y1 - line->y0)); line->y0 = clip->y0; } else if (region1 & POINT_RIGHTOF_REGION) { /* divide line at right */ line->y0 = (line->y0 + (line->y1 - line->y0) * (clip->x1 - 1 - line->x0) / (line->x1 - line->x0)); line->x0 = clip->x1 - 1; } else if (region1 & POINT_LEFTOF_REGION) { /* divide line at right */ line->y0 = (line->y0 + (line->y1 - line->y0) * (clip->x0 - line->x0) / (line->x1 - line->x0)); line->x0 = clip->x0; } region1 = REGION(line->x0, line->y0, clip->x0, clip->x1 - 1, clip->y0, clip->y1 - 1); } else { /* second point */ if (region2 & POINT_BELOW_REGION) { /* divide line at bottom*/ line->x1 = (line->x0 + (line->x1 - line->x0) * (clip->y1 - 1 - line->y0) / (line->y1 - line->y0)); line->y1 = clip->y1 - 1; } else if (region2 & POINT_ABOVE_REGION) { /* divide line at top*/ line->x1 = (line->x0 + (line->x1 - line->x0) * (clip->y0 - line->y0) / (line->y1 - line->y0)); line->y1 = clip->y0; } else if (region2 & POINT_RIGHTOF_REGION) { /* divide line at right*/ line->y1 = (line->y0 + (line->y1 - line->y0) * (clip->x1 - 1 - line->x0) / (line->x1 - line->x0)); line->x1 = clip->x1 - 1; } else if (region2 & POINT_LEFTOF_REGION) { /* divide line at right*/ line->y1 = (line->y0 + (line->y1 - line->y0) * (clip->x0 - line->x0) / (line->x1 - line->x0)); line->x1 = clip->x0; } region2 = REGION(line->x1, line->y1, clip->x0, clip->x1 - 1, clip->y0, clip->y1 - 1); } } return true; } bool nsfb_plot_clip_line_ctx(nsfb_t *nsfb, nsfb_bbox_t * restrict line) { return nsfb_plot_clip_line(&nsfb->clip, line); }