/* * Copyright (c) 1987, 1988, 1989 Stanford University * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided * that the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Stanford not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. Stanford makes no representations about * the suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * STANFORD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. * IN NO EVENT SHALL STANFORD BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Graphics primitives. */ #include #include #include #include #include #include #include PainterRep* Painter::Rep () { return rep; } /* * For reasons of caching, it is important that the attributes * are set to nil (or 0) before being set to their default values. * Also, it is important that the colors are set before the fill pattern. */ void Painter::Init () { if (solid == nil) { solid = new Pattern(0xffff); solid->Reference(); clear = new Pattern(0); clear->Reference(); lightgray = new Pattern(0x8020); lightgray->Reference(); gray = new Pattern(0xa5a5); gray->Reference(); darkgray = new Pattern(0xfafa); darkgray->Reference(); singleWidth = new Brush(0xffff, 0); singleWidth->Reference(); } foreground = nil; background = nil; pattern = nil; br = nil; font = nil; style = 0; matrix = nil; SetColors(black, white); SetPattern(solid); FillBg(true); SetBrush(singleWidth); SetFont(stdfont); SetStyle(Plain); SetOrigin(0, 0); MoveTo(0, 0); } void Painter::Copy (Painter* copy) { foreground = nil; background = nil; pattern = nil; br = nil; font = nil; style = 0; matrix = nil; SetColors(copy->foreground, copy->background); SetPattern(copy->pattern); SetBrush(copy->br); SetFont(copy->font); SetStyle(copy->style); SetTransformer(copy->matrix); SetOrigin(copy->xoff, copy->yoff); MoveTo(copy->curx, copy->cury); } Color* Painter::GetFgColor () { return foreground; } Color* Painter::GetBgColor () { return background; } Pattern* Painter::GetPattern () { return pattern; } Brush* Painter::GetBrush () { return br; } Font* Painter::GetFont () { return font; } void Painter::SetStyle (int s) { style = s; } int Painter::GetStyle () { return style; } void Painter::SetTransformer (Transformer *t) { if (matrix != t) { Unref(matrix); matrix = t; if (matrix != nil) { matrix->Reference(); } } } Transformer* Painter::GetTransformer () { return matrix; } void Painter::MoveTo (int x, int y) { curx = x; cury = y; } void Painter::GetPosition (int& x, int& y) { x = curx; y = cury; } void Painter::SetOrigin (int x0, int y0) { xoff = x0; yoff = y0; } void Painter::GetOrigin (int& x0, int& y0) { x0 = xoff; y0 = yoff; } void Painter::Translate (float dx, float dy) { if (dx != 0.0 || dy != 0.0) { if (matrix == nil) { matrix = new Transformer; } matrix->Translate(dx, dy); } } void Painter::Scale (float sx, float sy) { if (sx != 1.0 || sy != 1.0) { if (matrix == nil) { matrix = new Transformer; } matrix->Scale(sx, sy); } } void Painter::Rotate (float angle) { if (angle - int(angle) != 0.0 || int(angle) % 360 != 0) { if (matrix == nil) { matrix = new Transformer; } matrix->Rotate(angle); } } void Painter::CurveTo (Canvas* c, Coord x0, Coord y0, Coord x1, Coord y1, Coord x2, Coord y2 ) { Curve(c, curx, cury, x0, y0, x1, y1, x2, y2); curx = x2; cury = y2; } void Painter::Text (Canvas* c, const char* s) { int len = strlen(s); Text(c, s, len, curx, cury); curx += font->Width(s, len); } void Painter::Text (Canvas* c, const char* s, int len) { Text(c, s, len, curx, cury); curx += font->Width(s, len); } void Painter::Text (Canvas* c, const char* s, Coord x, Coord y) { Text(c, s, strlen(s), x, y); } /* * Spline drawing. */ const int INITBUFSIZE = 100; const double SMOOTHNESS = 1.0; static int llsize = 0; static int llcount = 0; static Coord* llx; static Coord* lly; static void GrowBufs (Coord*& b1, Coord*& b2, int& cur) { Coord* newb1; Coord* newb2; int newsize; if (cur == 0) { cur = INITBUFSIZE; b1 = new Coord[INITBUFSIZE]; b2 = new Coord[INITBUFSIZE]; } else { newsize = cur * 2; newb1 = new Coord[newsize]; newb2 = new Coord[newsize]; memmove(newb1, b1, newsize * sizeof(Coord)); memmove(newb2, b2, newsize * sizeof(Coord)); delete b1; delete b2; b1 = newb1; b2 = newb2; cur = newsize; } } inline void Midpoint ( double x0, double y0, double x1, double y1, double& mx, double& my ) { mx = (x0 + x1) / 2.0; my = (y0 + y1) / 2.0; } inline void ThirdPoint ( double x0, double y0, double x1, double y1, double& tx, double& ty ) { tx = (2*x0 + x1) / 3.0; ty = (2*y0 + y1) / 3.0; } inline bool CanApproxWithLine ( double x0, double y0, double x2, double y2, double x3, double y3 ) { double triangleArea, sideSquared, dx, dy; triangleArea = x0*y2 - x2*y0 + x2*y3 - x3*y2 + x3*y0 - x0*y3; triangleArea *= triangleArea; // actually 4 times the area dx = x3 - x0; dy = y3 - y0; sideSquared = dx*dx + dy*dy; return triangleArea <= SMOOTHNESS * sideSquared; } inline void AddLine (double x0, double y0, double x1, double y1) { if (llcount >= llsize) { GrowBufs(llx, lly, llsize); } if (llcount == 0) { llx[llcount] = round(x0); lly[llcount] = round(y0); ++llcount; } llx[llcount] = round(x1); lly[llcount] = round(y1); ++llcount; } static void AddBezierCurve ( double x0, double y0, double x1, double y1, double x2, double y2, double x3, double y3 ) { double midx01, midx12, midx23, midlsegx, midrsegx, cx; double midy01, midy12, midy23, midlsegy, midrsegy, cy; Midpoint(x0, y0, x1, y1, midx01, midy01); Midpoint(x1, y1, x2, y2, midx12, midy12); Midpoint(x2, y2, x3, y3, midx23, midy23); Midpoint(midx01, midy01, midx12, midy12, midlsegx, midlsegy); Midpoint(midx12, midy12, midx23, midy23, midrsegx, midrsegy); Midpoint(midlsegx, midlsegy, midrsegx, midrsegy, cx, cy); if (CanApproxWithLine(x0, y0, midlsegx, midlsegy, cx, cy)) { AddLine(x0, y0, cx, cy); } else if ( (midx01 != x1) || (midy01 != y1) || (midlsegx != x2) || (midlsegy != y2) || (cx != x3) || (cy != y3) ) { AddBezierCurve( x0, y0, midx01, midy01, midlsegx, midlsegy, cx, cy ); } if (CanApproxWithLine(cx, cy, midx23, midy23, x3, y3)) { AddLine(cx, cy, x3, y3); } else if ( (cx != x0) || (cy != y0) || (midrsegx != x1) || (midrsegy != y1) || (midx23 != x2) || (midy23 != y2) ) { AddBezierCurve( cx, cy, midrsegx, midrsegy, midx23, midy23, x3, y3 ); } } static void CalcBSpline ( Coord cminus1x, Coord cminus1y, Coord cx, Coord cy, Coord cplus1x, Coord cplus1y, Coord cplus2x, Coord cplus2y ) { double p0x, p1x, p2x, p3x, tempx, p0y, p1y, p2y, p3y, tempy; ThirdPoint( double(cx), double(cy), double(cplus1x), double(cplus1y), p1x, p1y ); ThirdPoint( double(cplus1x), double(cplus1y), double(cx), double(cy), p2x, p2y ); ThirdPoint( double(cx), double(cy), double(cminus1x), double(cminus1y), tempx, tempy ); Midpoint(tempx, tempy, p1x, p1y, p0x, p0y); ThirdPoint( double(cplus1x), double(cplus1y), double(cplus2x), double(cplus2y), tempx, tempy ); Midpoint(tempx, tempy, p2x, p2y, p3x, p3y); AddBezierCurve(p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y); } inline void CreateOpenLineList (Coord *cpx, Coord *cpy, int cpcount) { int cpi; llcount = 0; CalcBSpline( cpx[0], cpy[0], cpx[0], cpy[0], cpx[0], cpy[0], cpx[1], cpy[1] ); CalcBSpline( cpx[0], cpy[0], cpx[0], cpy[0], cpx[1], cpy[1], cpx[2], cpy[2] ); for (cpi = 1; cpi < cpcount - 2; ++cpi) { CalcBSpline( cpx[cpi - 1], cpy[cpi - 1], cpx[cpi], cpy[cpi], cpx[cpi + 1], cpy[cpi + 1], cpx[cpi + 2], cpy[cpi + 2] ); } CalcBSpline( cpx[cpi - 1], cpy[cpi - 1], cpx[cpi], cpy[cpi], cpx[cpi + 1], cpy[cpi + 1], cpx[cpi + 1], cpy[cpi + 1] ); CalcBSpline( cpx[cpi], cpy[cpi], cpx[cpi + 1], cpy[cpi + 1], cpx[cpi + 1], cpy[cpi + 1], cpx[cpi + 1], cpy[cpi + 1] ); } inline void CreateClosedLineList (Coord *cpx, Coord *cpy, int cpcount) { int cpi; llcount = 0; CalcBSpline( cpx[cpcount - 1], cpy[cpcount - 1], cpx[0], cpy[0], cpx[1], cpy[1], cpx[2], cpy[2] ); for (cpi = 1; cpi < cpcount - 2; ++cpi) { CalcBSpline( cpx[cpi - 1], cpy[cpi - 1], cpx[cpi], cpy[cpi], cpx[cpi + 1], cpy[cpi + 1], cpx[cpi + 2], cpy[cpi + 2] ); } CalcBSpline( cpx[cpi - 1], cpy[cpi - 1], cpx[cpi], cpy[cpi], cpx[cpi + 1], cpy[cpi + 1], cpx[0], cpy[0] ); CalcBSpline( cpx[cpi], cpy[cpi], cpx[cpi + 1], cpy[cpi + 1], cpx[0], cpy[0], cpx[1], cpy[1] ); } static int bufsize = 0; static Coord* bufx, * bufy; static void CheckBufs (Coord*& b1, Coord*& b2, int& cur, int desired) { if (cur < desired) { if (cur == 0) { cur = max(INITBUFSIZE, desired); } else { delete b1; delete b2; cur = max(cur * 2, desired); } b1 = new Coord[cur]; b2 = new Coord[cur]; } } void Painter::Curve ( Canvas* c, Coord x0, Coord y0, Coord x1, Coord y1, Coord x2, Coord y2, Coord x3, Coord y3 ) { Coord tx0, ty0, tx1, ty1, tx2, ty2, tx3, ty3; llcount = 0; Map(c, x0, y0, tx0, ty0); Map(c, x1, y1, tx1, ty1); Map(c, x2, y2, tx2, ty2); Map(c, x3, y3, tx3, ty3); AddBezierCurve(tx0, ty0, tx1, ty1, tx2, ty2, tx3, ty3); MultiLineNoMap(c, llx, lly, llcount); } void Painter::BSpline (Canvas* c, Coord x[], Coord y[], int count) { CheckBufs(bufx, bufy, bufsize, count); MapList(c, x, y, count, bufx, bufy); if (count < 3) { MultiLineNoMap(c, bufx, bufy, count); } else { CreateOpenLineList(bufx, bufy, count); MultiLineNoMap(c, llx, lly, llcount); } } void Painter::ClosedBSpline (Canvas* c, Coord x[], Coord y[], int count) { CheckBufs(bufx, bufy, bufsize, count); MapList(c, x, y, count, bufx, bufy); if (count < 3) { MultiLineNoMap(c, bufx, bufy, count); } else { CreateClosedLineList(bufx, bufy, count); MultiLineNoMap(c, llx, lly, llcount); } } void Painter::FillBSpline (Canvas* c, Coord x[], Coord y[], int count) { CheckBufs(bufx, bufy, bufsize, count); MapList(c, x, y, count, bufx, bufy); if (count < 3) { FillPolygonNoMap(c, bufx, bufy, count); } else { CreateClosedLineList(bufx, bufy, count); FillPolygonNoMap(c, llx, lly, llcount); } } void Painter::Map (Canvas* c, Coord x, Coord y, short& sx, short& sy) { Coord cx, cy; Map(c, x, y, cx, cy); sx = short(cx); sy = short(cy); } const float axis = 0.42; const float seen = 1.025; void Painter::Ellipse (Canvas* c, Coord cx, Coord cy, int r1, int r2) { float px1, py1, px2, py2, x[8], y[8]; px1 = float(r1)*axis; py1 = float(r2)*axis; px2 = float(r1)*seen; py2 = float(r2)*seen; x[0] = cx + px1; y[0] = cy + py2; x[1] = cx - px1; y[1] = y[0]; x[2] = cx - px2; y[2] = cy + py1; x[3] = x[2]; y[3] = cy - py1; x[4] = x[1]; y[4] = cy - py2; x[5] = x[0]; y[5] = y[4]; x[6] = cx + px2; y[6] = y[3]; x[7] = x[6]; y[7] = y[2]; CheckBufs(bufx, bufy, bufsize, 8); MapList(c, (float*) x, (float*) y, 8, bufx, bufy); CreateClosedLineList(bufx, bufy, 8); MultiLineNoMap(c, llx, lly, llcount); } void Painter::FillEllipse (Canvas* c, Coord cx, Coord cy, int r1, int r2) { float px1, py1, px2, py2, x[8], y[8]; px1 = float(r1)*axis; py1 = float(r2)*axis; px2 = float(r1)*seen; py2 = float(r2)*seen; x[0] = cx + px1; y[0] = cy + py2; x[1] = cx - px1; y[1] = y[0]; x[2] = cx - px2; y[2] = cy + py1; x[3] = x[2]; y[3] = cy - py1; x[4] = x[1]; y[4] = cy - py2; x[5] = x[0]; y[5] = y[4]; x[6] = cx + px2; y[6] = y[3]; x[7] = x[6]; y[7] = y[2]; CheckBufs(bufx, bufy, bufsize, 8); MapList(c, (float*) x, (float*) y, 8, bufx, bufy); CreateClosedLineList(bufx, bufy, 8); FillPolygonNoMap(c, llx, lly, llcount); }