mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-16 14:16:09 -06:00
272 lines
4.6 KiB
C++
272 lines
4.6 KiB
C++
#include "Draw.h"
|
|
|
|
namespace Upp {
|
|
|
|
DDARasterizer::DDARasterizer()
|
|
{
|
|
cy = 0;
|
|
p0 = p1 = Point(0, 0);
|
|
j1 = j2 = Null;
|
|
b1 = b2 = Null;
|
|
close = false;
|
|
width = 1;
|
|
}
|
|
|
|
void DDARasterizer::AHorz(int x, int y, int cx)
|
|
{
|
|
if(cx) {
|
|
if(cx < 0)
|
|
PutHorz(x + cx + 1, y, -cx);
|
|
else
|
|
PutHorz(x, y, cx);
|
|
}
|
|
}
|
|
|
|
void DDARasterizer::AVert(int x, int y, int cy)
|
|
{
|
|
if(cy) {
|
|
if(cy < 0)
|
|
PutVert(x, y + cy + 1, -cy);
|
|
else
|
|
PutVert(x, y, cy);
|
|
}
|
|
}
|
|
|
|
void DDARasterizer::DoLine(Point p1, Point p2, bool last)
|
|
{
|
|
dirx = sgn(p2.x - p1.x);
|
|
diry = sgn(p2.y - p1.y);
|
|
int dx = abs(p2.x - p1.x);
|
|
int dy = abs(p2.y - p1.y);
|
|
int x = p1.x;
|
|
int y = p1.y;
|
|
int x0 = x;
|
|
int y0 = y;
|
|
if(dx < dy) {
|
|
int dda = dy >> 1;
|
|
int n = dy + last;
|
|
for(;;) {
|
|
if(x != x0) {
|
|
AVert(x0, y0, y - y0);
|
|
x0 = x;
|
|
y0 = y;
|
|
}
|
|
if(n-- <= 0)
|
|
break;
|
|
y += diry;
|
|
dda -= dx;
|
|
if(dda < 0) {
|
|
dda += dy;
|
|
x += dirx;
|
|
}
|
|
}
|
|
AVert(x0, y0, y - y0);
|
|
}
|
|
else {
|
|
int dda = dx >> 1;
|
|
int n = dx + last;
|
|
for(;;) {
|
|
if(y != y0) {
|
|
AHorz(x0, y0, x - x0);
|
|
x0 = x;
|
|
y0 = y;
|
|
}
|
|
if(n-- <= 0)
|
|
break;
|
|
x += dirx;
|
|
dda -= dy;
|
|
if(dda < 0) {
|
|
dda += dx;
|
|
y += diry;
|
|
}
|
|
}
|
|
AHorz(x0, y0, x - x0);
|
|
}
|
|
}
|
|
|
|
struct DDARasterizer::Segments : DDARasterizer {
|
|
int miny;
|
|
int maxy;
|
|
bool flag;
|
|
Vector< Vector< Segment > > segment;
|
|
|
|
virtual void PutHorz(int x, int y, int cx) {
|
|
if(y >= 0 && y < cy) {
|
|
Segment& m = segment.At(y).Add();
|
|
m.l = x;
|
|
m.h = x + cx;
|
|
m.flag = flag;
|
|
miny = min(y, miny);
|
|
maxy = max(y, maxy);
|
|
}
|
|
flag = true;
|
|
}
|
|
virtual void PutVert(int x, int y, int cy) {
|
|
for(int i = 0; i < cy; i++)
|
|
PutHorz(x, y + i, 1);
|
|
}
|
|
|
|
Segments() { miny = INT_MAX; maxy = 0; }
|
|
};
|
|
|
|
void DDARasterizer::FatLine(Point p2)
|
|
{
|
|
if(p1 == p2)
|
|
return;
|
|
Point pp = p0;
|
|
p0 = p1; // avoid Close
|
|
Point v = p2 - p1;
|
|
Point shift = (width - 0.5) * Orthogonal(Pointf(v) / Length((Pointf(v))));
|
|
Point p = p1 + shift / 2;
|
|
Point bj2 = j2;
|
|
Polygon();
|
|
if(!IsNull(j1)) {
|
|
Move(j1);
|
|
Line(p);
|
|
}
|
|
else
|
|
Move(p);
|
|
if(IsNull(b1))
|
|
b1 = p;
|
|
Line(p += v);
|
|
j1 = p;
|
|
if(close && !IsNull(b1)) {
|
|
Line(b1);
|
|
Line(b2);
|
|
}
|
|
Line(p -= shift);
|
|
j2 = p;
|
|
Line(p -= v);
|
|
if(!IsNull(bj2))
|
|
Line(bj2);
|
|
if(IsNull(b2))
|
|
b2 = p;
|
|
Fill();
|
|
p0 = pp;
|
|
}
|
|
|
|
DDARasterizer& DDARasterizer::Move(Point p)
|
|
{
|
|
if(pseg)
|
|
Close();
|
|
p0 = p1 = p;
|
|
j1 = j2 = Null;
|
|
return *this;
|
|
}
|
|
|
|
DDARasterizer& DDARasterizer::Line(Point p)
|
|
{
|
|
if(pseg) {
|
|
Point a = p1;
|
|
Point b = p;
|
|
if(a.y > b.y)
|
|
Swap(a, b);
|
|
pseg->flag = false;
|
|
pseg->DoLine(a, b, true);
|
|
p1 = p;
|
|
}
|
|
else {
|
|
if(width <= 1)
|
|
DoLine(p1, p, false);
|
|
else
|
|
FatLine(p);
|
|
p1 = p;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
DDARasterizer& DDARasterizer::Close()
|
|
{
|
|
if(p1 != p0) {
|
|
close = true;
|
|
Line(p0);
|
|
close = false;
|
|
if(!pseg)
|
|
b1 = b2 = Null;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
DDARasterizer& DDARasterizer::Polygon()
|
|
{
|
|
pseg.Create();
|
|
pseg->Cy(cy);
|
|
return *this;
|
|
}
|
|
|
|
DDARasterizer& DDARasterizer::Fill()
|
|
{
|
|
ASSERT(pseg);
|
|
Close();
|
|
for(int y = pseg->miny; y <= pseg->maxy; y++) {
|
|
Vector<Segment>& gg = pseg->segment[y];
|
|
Sort(gg);
|
|
int i = 0;
|
|
bool flag = false;
|
|
while(i < gg.GetCount()) {
|
|
int l = gg[i].l;
|
|
int h = gg[i].h;
|
|
flag ^= gg[i++].flag;
|
|
while(i < gg.GetCount() && flag) {
|
|
h = gg[i].h;
|
|
flag ^= gg[i++].flag;
|
|
}
|
|
PutHorz(l, y, h - l);
|
|
}
|
|
}
|
|
pseg.Clear();
|
|
return *this;
|
|
}
|
|
|
|
DDARasterizer& DDARasterizer::Ellipse(const Rect& r, int width)
|
|
{
|
|
double rx = r.GetWidth() * 0.5;
|
|
double ry = r.GetHeight() * 0.5;
|
|
double r2 = sqr(ry);
|
|
double rr = rx / ry;
|
|
|
|
if(rx <= 0 || ry <= 0)
|
|
return *this;
|
|
|
|
double wy, w2, wr;
|
|
if(width > 1) {
|
|
wy = ry - width;
|
|
w2 = sqr(wy);
|
|
wr = (rx - width) / wy;
|
|
}
|
|
|
|
|
|
int y = 0;
|
|
int px = (int)rx;
|
|
for(;;) {
|
|
int x = int(rr * (ry - sqrt(r2 - sqr(ry - y - 0.5))) + 0.5);
|
|
int y1 = y + r.top;
|
|
int y2 = r.bottom - y - 1;
|
|
int n = max(px - x, 1);
|
|
int n2 = n;
|
|
int x1 = x + r.left;
|
|
int x2 = r.right - x - n;
|
|
if(width < 0 || y == 0 || width > 1 && y < width || width >= ry) {
|
|
n = x2 - x1 + n;
|
|
n2 = 0;
|
|
}
|
|
else
|
|
if(width > 1) {
|
|
int wx = int(wr * (wy - sqrt(max(w2 - sqr(wy - (y - width) - 0.5), 0.0))) + 0.5) + int(ry - wy);
|
|
n = n2 = wx - x;
|
|
x2 = r.right - x - n;
|
|
}
|
|
PutHorz(x1, y1, n);
|
|
PutHorz(x2, y1, n2);
|
|
if(y2 < y1) break;
|
|
PutHorz(x1, y2, n);
|
|
PutHorz(x2, y2, n2);
|
|
px = x;
|
|
y++;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
DDARasterizer::~DDARasterizer() {}
|
|
|
|
}
|