summaryrefslogtreecommitdiff
path: root/src/sim/sim_primitives.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/sim/sim_primitives.c')
-rw-r--r--src/sim/sim_primitives.c524
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;
+ }
+}