/** \file * \brief Primitives of the Simulation Base Driver * * See Copyright Notice in cd.h */ #include #include #include #include #include "cd.h" #include "cd_private.h" #include "cd_truetype.h" #include "sim.h" void cdlineSIM(cdCtxCanvas* ctxcanvas, int x1, int y1, int x2, int y2) { cdCanvas* canvas = ((cdCtxCanvasBase*)ctxcanvas)->canvas; int old_use_matrix = canvas->use_matrix; if (canvas->use_matrix && !canvas->invert_yaxis) { cdMatrixTransformPoint(canvas->matrix, x1, y1, &x1, &y1); cdMatrixTransformPoint(canvas->matrix, x2, y2, &x2, &y2); } /* must disable transformation here, because line simulation use cxPixel */ canvas->use_matrix = 0; if(canvas->line_width > 1) simLineThick(canvas, x1, y1, x2, y2); else simLineThin(canvas, x1, y1, x2, y2); canvas->use_matrix = old_use_matrix; } void cdrectSIM(cdCtxCanvas* ctxcanvas, int xmin, int xmax, int ymin, int ymax) { cdCanvas* canvas = ((cdCtxCanvasBase*)ctxcanvas)->canvas; cdPoint poly[5]; /* leave room of one more point */ poly[0].x = xmin; poly[0].y = ymin; poly[1].x = xmin; poly[1].y = ymax; poly[2].x = xmax; poly[2].y = ymax; poly[3].x = xmax; poly[3].y = ymin; canvas->cxPoly(canvas->ctxcanvas, CD_CLOSED_LINES, poly, 4); } void cdboxSIM(cdCtxCanvas* ctxcanvas, int xmin, int xmax, int ymin, int ymax) { cdCanvas* canvas = ((cdCtxCanvasBase*)ctxcanvas)->canvas; if (canvas->use_matrix) { cdPoint poly[5]; /* leave room of one more point */ poly[0].x = xmin; poly[0].y = ymin; poly[1].x = xmin; poly[1].y = ymax; poly[2].x = xmax; poly[2].y = ymax; poly[3].x = xmax; poly[3].y = ymin; canvas->cxPoly(canvas->ctxcanvas, CD_FILL, poly, 4); } else { cdSimulation* simulation = canvas->simulation; int y; /* must set line attributes here, because fill simulation use cxLine and cxPixel */ int old_line_style = cdCanvasLineStyle(canvas, CD_CONTINUOUS); int old_line_width = cdCanvasLineWidth(canvas, 1); for(y=ymin;y<=ymax;y++) simFillHorizLine(simulation, xmin, y, xmax); cdCanvasLineStyle(canvas, old_line_style); cdCanvasLineWidth(canvas, old_line_width); } } void cdfSimBox(cdCtxCanvas *ctxcanvas, double xmin, double xmax, double ymin, double ymax) { cdCanvas* canvas = ((cdCtxCanvasBase*)ctxcanvas)->canvas; cdfPoint poly[5]; /* leave room of one more point */ poly[0].x = xmin; poly[0].y = ymin; poly[1].x = xmin; poly[1].y = ymax; poly[2].x = xmax; poly[2].y = ymax; poly[3].x = xmax; poly[3].y = ymin; canvas->cxFPoly(canvas->ctxcanvas, CD_FILL, poly, 4); } void cdfSimRect(cdCtxCanvas *ctxcanvas, double xmin, double xmax, double ymin, double ymax) { cdCanvas* canvas = ((cdCtxCanvasBase*)ctxcanvas)->canvas; cdfPoint poly[5]; /* leave room of one more point */ poly[0].x = xmin; poly[0].y = ymin; poly[1].x = xmin; poly[1].y = ymax; poly[2].x = xmax; poly[2].y = ymax; poly[3].x = xmax; poly[3].y = ymin; canvas->cxFPoly(canvas->ctxcanvas, CD_CLOSED_LINES, poly, 4); } int simCalcEllipseNumSegments(cdCanvas* canvas, int xc, int yc, int width, int height) { int n, dx, dy, hd; int w2 = width/2; int h2 = height/2; int x1 = xc-w2, y1 = yc-h2, x2 = xc+w2, y2 = yc+h2; if (canvas->use_matrix) { cdMatrixTransformPoint(canvas->matrix, x1, y1, &x1, &y1); cdMatrixTransformPoint(canvas->matrix, x2, y2, &x2, &y2); } dx = (x1-x2); dy = (y1-y2); hd = (int)(sqrt(dx*dx + dy*dy)/2); /* Estimation Heuristic: use half diagonal to estimate the number of segments for 360 degrees. Use the difference of the half diagonal and its projection to calculate the minimum angle: cos(min_angle) = hd / (hd + 1) or min_angle = acos(hd / (hd + 1)) The number of segments will be 360 / min_angle. */ n = (int)((360.0*CD_DEG2RAD) / acos((double)hd / (hd + 1.0)) + 0.5); /* round up */ /* multiple of 4 */ n = ((n + 3)/4)*4; /* minimum number is 4 */ if (n < 4) n = 4; return n; } void cdarcSIM(cdCtxCanvas* ctxcanvas, int xc, int yc, int width, int height, double angle1, double angle2) { cdCanvas* canvas = ((cdCtxCanvasBase*)ctxcanvas)->canvas; double c, s, sx, sy, x, y, prev_x, prev_y; double da; int i, yc2 = 2*yc, p, last_xi_a = -65535, last_yi_a = -65535, last_xi_b = -65535, last_yi_b = -65535; cdPoint* poly = NULL; /* number of segments of equivalent poligonal for a full ellipse */ int n = simCalcEllipseNumSegments(canvas, xc, yc, width, height); /* Use special floating point anti-alias line draw when line_width==1, and NOT using cdlineSIM. */ if (canvas->line_width > 1 || canvas->cxLine != cdlineSIM) { poly = (cdPoint*)malloc(sizeof(cdPoint)*(n+1)); /* n+1 points */ if (!poly) return; } /* number of segments for the arc */ n = cdRound((fabs(angle2-angle1)*n)/360); if (n < 1) n = 1; /* converts degrees into radians */ angle1 *= CD_DEG2RAD; angle2 *= CD_DEG2RAD; /* generates arc points at origin with axis x and y */ da = (angle2-angle1)/n; c = cos(da); s = sin(da); sx = -(width*s)/height; sy = (height*s)/width; x = (width/2.0f)*cos(angle1); y = (height/2.0f)*sin(angle1); prev_x = x; prev_y = y; if (poly) { poly[0].x = _cdRound(x)+xc; poly[0].y = _cdRound(y)+yc; if (canvas->invert_yaxis) /* must invert because of the angle orientation */ poly[0].y = yc2 - poly[0].y; p = 1; } else simLineStyleNoReset = 1; for (i = 1; i < n+1; i++) /* n+1 points */ { x = c*prev_x + sx*prev_y; y = sy*prev_x + c*prev_y; if (poly) { poly[p].x = _cdRound(x)+xc; poly[p].y = _cdRound(y)+yc; if (canvas->invert_yaxis) /* must invert because of the angle orientation */ poly[p].y = yc2 - poly[p].y; if (poly[p-1].x != poly[p].x || poly[p-1].y != poly[p].y) p++; } else { int old_use_matrix = canvas->use_matrix; double x1 = prev_x+xc, y1 = prev_y+yc, x2 = x+xc, y2 = y+yc; if (canvas->use_matrix && !canvas->invert_yaxis) { cdfMatrixTransformPoint(canvas->matrix, x1, y1, &x1, &y1); cdfMatrixTransformPoint(canvas->matrix, x2, y2, &x2, &y2); } /* must disable transformation here, because line simulation use cxPixel */ canvas->use_matrix = 0; if (canvas->invert_yaxis) /* must invert because of the angle orientation */ { y1 = yc2 - y1; y2 = yc2 - y2; } simfLineThin(canvas, x1, y1, x2, y2, &last_xi_a, &last_yi_a, &last_xi_b, &last_yi_b); canvas->use_matrix = old_use_matrix; } prev_x = x; prev_y = y; } if (poly) { canvas->cxPoly(canvas->ctxcanvas, CD_OPEN_LINES, poly, p); free(poly); } else simLineStyleNoReset = 0; } void cdfSimArc(cdCtxCanvas *ctxcanvas, double xc, double yc, double width, double height, double angle1, double angle2) { cdCanvas* canvas = ((cdCtxCanvasBase*)ctxcanvas)->canvas; double c, s, sx, sy, x, y, prev_x, prev_y, da; int i, p; cdfPoint* poly = NULL; /* number of segments of equivalent poligonal for a full ellipse */ int n = simCalcEllipseNumSegments(canvas, (int)xc, (int)yc, (int)width, (int)height); poly = (cdfPoint*)malloc(sizeof(cdfPoint)*(n+1)); /* n+1 points */ if (!poly) return; /* number of segments for the arc */ n = cdRound((fabs(angle2-angle1)*n)/360); if (n < 1) n = 1; /* converts degrees into radians */ angle1 *= CD_DEG2RAD; angle2 *= CD_DEG2RAD; /* generates arc points at origin with axis x and y */ da = (angle2-angle1)/n; c = cos(da); s = sin(da); sx = -(width*s)/height; sy = (height*s)/width; x = (width/2.0f)*cos(angle1); y = (height/2.0f)*sin(angle1); prev_x = x; prev_y = y; poly[0].x = x+xc; poly[0].y = y+yc; p = 1; for (i = 1; i < n+1; i++) /* n+1 points */ { x = c*prev_x + sx*prev_y; y = sy*prev_x + c*prev_y; poly[p].x = x+xc; poly[p].y = y+yc; if (poly[p-1].x != poly[p].x || poly[p-1].y != poly[p].y) p++; prev_x = x; prev_y = y; } canvas->cxFPoly(canvas->ctxcanvas, CD_OPEN_LINES, poly, p); free(poly); } void cdfSimElipse(cdCtxCanvas* ctxcanvas, double xc, double yc, double width, double height, double angle1, double angle2, int sector) { cdCanvas* canvas = ((cdCtxCanvasBase*)ctxcanvas)->canvas; double c, s, sx, sy, x, y, prev_x, prev_y, da; int i, p; cdfPoint* poly; /* number of segments of equivalent poligonal for a full ellipse */ int n = simCalcEllipseNumSegments(canvas, (int)xc, (int)yc, (int)width, (int)height); /* number of segments for the arc */ n = cdRound(((angle2-angle1)*n)/360); if (n < 1) n = 1; poly = (cdfPoint*)malloc(sizeof(cdfPoint)*(n+2+1)); /* n+1 points +1 center */ /* converts degrees into radians */ angle1 *= CD_DEG2RAD; angle2 *= CD_DEG2RAD; /* generates arc points at origin with axis x and y */ da = (angle2-angle1)/n; c = cos(da); s = sin(da); sx = -(width*s)/height; sy = (height*s)/width; x = xc + (width/2.0)*cos(angle1); y = yc + (height/2.0)*sin(angle1); prev_x = x; prev_y = y; poly[0].x = x; poly[0].y = y; p = 1; for (i = 1; i < n+1; i++) /* n+1 points */ { x = xc + c*(prev_x-xc) + sx*(prev_y-yc); y = yc + sy*(prev_x-xc) + c*(prev_y-yc); poly[p].x = x; poly[p].y = y; if (poly[p-1].x != poly[p].x || poly[p-1].y != poly[p].y) p++; prev_x = x; prev_y = y; } if (poly[p-1].x != poly[0].x || poly[p-1].y != poly[0].y) { if (sector) /* cdSector */ { /* add center */ poly[p].x = xc; poly[p].y = yc; } else /* cdChord */ { /* add initial point */ poly[p].x = poly[0].x; poly[p].y = poly[0].y; } p++; } canvas->cxFPoly(canvas->ctxcanvas, CD_FILL, poly, p); free(poly); } static void cdSimElipse(cdCtxCanvas* ctxcanvas, int xc, int yc, int width, int height, double angle1, double angle2, int sector) { cdCanvas* canvas = ((cdCtxCanvasBase*)ctxcanvas)->canvas; float c, s, sx, sy, x, y, prev_x, prev_y; double da; int i, p, yc2 = 2*yc; cdPoint* poly; /* number of segments of equivalent poligonal for a full ellipse */ int n = simCalcEllipseNumSegments(canvas, xc, yc, width, height); /* number of segments for the arc */ n = cdRound(((angle2-angle1)*n)/360); if (n < 1) n = 1; poly = (cdPoint*)malloc(sizeof(cdPoint)*(n+2+1)); /* n+1 points +1 center */ /* converts degrees into radians */ angle1 *= CD_DEG2RAD; angle2 *= CD_DEG2RAD; /* generates arc points at origin with axis x and y */ da = (angle2-angle1)/n; c = (float)cos(da); s = (float)sin(da); sx = -(width*s)/height; sy = (height*s)/width; x = xc + (width/2.0f)*(float)cos(angle1); y = yc + (height/2.0f)*(float)sin(angle1); prev_x = x; prev_y = y; poly[0].x = _cdRound(x); poly[0].y = _cdRound(y); if (canvas->invert_yaxis) poly[0].y = yc2 - poly[0].y; p = 1; for (i = 1; i < n+1; i++) /* n+1 points */ { x = xc + c*(prev_x-xc) + sx*(prev_y-yc); y = yc + sy*(prev_x-xc) + c*(prev_y-yc); poly[p].x = _cdRound(x); poly[p].y = _cdRound(y); if (canvas->invert_yaxis) poly[p].y = yc2 - poly[p].y; if (poly[p-1].x != poly[p].x || poly[p-1].y != poly[p].y) p++; prev_x = x; prev_y = y; } if (poly[p-1].x != poly[0].x || poly[p-1].y != poly[0].y) { if (sector) /* cdSector */ { /* add center */ poly[p].x = xc; poly[p].y = yc; } else /* cdChord */ { /* add initial point */ poly[p].x = poly[0].x; poly[p].y = poly[0].y; } p++; } canvas->cxPoly(canvas->ctxcanvas, CD_FILL, poly, p); free(poly); } void cdsectorSIM(cdCtxCanvas* ctxcanvas, int xc, int yc, int width, int height, double angle1, double angle2) { cdSimElipse(ctxcanvas, xc, yc, width, height, angle1, angle2, 1); } void cdchordSIM(cdCtxCanvas* ctxcanvas, int xc, int yc, int width, int height, double angle1, double angle2) { cdSimElipse(ctxcanvas, xc, yc, width, height, angle1, angle2, 0); } void cdpolySIM(cdCtxCanvas* ctxcanvas, int mode, cdPoint* poly, int n) { cdCanvas* canvas = ((cdCtxCanvasBase*)ctxcanvas)->canvas; int i, reset = 1; switch(mode) { case CD_CLOSED_LINES: poly[n] = poly[0]; n++; /* continue */ case CD_OPEN_LINES: if (simLineStyleNoReset) /* Bezier simulation use several poly */ { reset = 0; simLineStyleNoReset = 1; } for (i = 0; i< n - 1; i++) canvas->cxLine(canvas->ctxcanvas, poly[i].x, poly[i].y, poly[i+1].x, poly[i+1].y); if (reset) simLineStyleNoReset = 0; break; case CD_BEZIER: simLineStyleNoReset = 1; cdSimPolyBezier(canvas, poly, n); simLineStyleNoReset = 0; break; case CD_FILL: { /* must set line attributes here, because fill simulation use cxLine */ int oldwidth = cdCanvasLineWidth(canvas, 1); int oldstyle = cdCanvasLineStyle(canvas, CD_CONTINUOUS); int old_use_matrix = canvas->use_matrix; if (canvas->use_matrix && !canvas->invert_yaxis) { for(i = 0; i < n; i++) cdMatrixTransformPoint(canvas->matrix, poly[i].x, poly[i].y, &poly[i].x, &poly[i].y); } /* must disable transformation here, because line simulation use cxPixel */ canvas->use_matrix = 0; simPolyFill(canvas->simulation, poly, n); canvas->use_matrix = old_use_matrix; cdCanvasLineStyle(canvas, oldstyle); cdCanvasLineWidth(canvas, oldwidth); } break; } }