diff options
Diffstat (limited to 'src/sim/sim_primitives.c')
-rw-r--r-- | src/sim/sim_primitives.c | 524 |
1 files changed, 524 insertions, 0 deletions
diff --git a/src/sim/sim_primitives.c b/src/sim/sim_primitives.c new file mode 100644 index 0000000..5f5e0a3 --- /dev/null +++ b/src/sim/sim_primitives.c @@ -0,0 +1,524 @@ +/** \file + * \brief Primitives of the Simulation Base Driver + * + * See Copyright Notice in cd.h + */ + +#include <stdlib.h> +#include <stdio.h> +#include <math.h> +#include <memory.h> + +#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; + } +} |