tioga 1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (116) hide show
  1. data/Tioga_README +372 -0
  2. data/lgpl.txt +504 -0
  3. data/split/Dtable/defs.h +33 -0
  4. data/split/Dtable/dtable.c +1928 -0
  5. data/split/Dtable/dtable_intern.h +144 -0
  6. data/split/Dtable/dvector.h +61 -0
  7. data/split/Dtable/extconf.rb +4 -0
  8. data/split/Dtable/include/dtable.h +35 -0
  9. data/split/Dtable/lib/Dtable_extras.rb +90 -0
  10. data/split/Dtable/namespace.h +47 -0
  11. data/split/Dtable/safe_double.h +104 -0
  12. data/split/Dtable/symbols.c +92 -0
  13. data/split/Dtable/symbols.h +52 -0
  14. data/split/Dvector/defs.h +33 -0
  15. data/split/Dvector/dvector.c +5486 -0
  16. data/split/Dvector/dvector_intern.h +142 -0
  17. data/split/Dvector/extconf.rb +4 -0
  18. data/split/Dvector/include/dvector.h +61 -0
  19. data/split/Dvector/lib/Dvector_extras.rb +328 -0
  20. data/split/Dvector/lib/Numeric_extras.rb +134 -0
  21. data/split/Dvector/namespace.h +47 -0
  22. data/split/Dvector/safe_double.h +104 -0
  23. data/split/Dvector/symbols.c +92 -0
  24. data/split/Dvector/symbols.h +52 -0
  25. data/split/Flate/defs.h +33 -0
  26. data/split/Flate/extconf.rb +19 -0
  27. data/split/Flate/flate.c +156 -0
  28. data/split/Flate/flate_intern.h +97 -0
  29. data/split/Flate/include/flate.h +98 -0
  30. data/split/Flate/namespace.h +47 -0
  31. data/split/Flate/safe_double.h +104 -0
  32. data/split/Flate/symbols.c +92 -0
  33. data/split/Flate/symbols.h +52 -0
  34. data/split/Function/defs.h +33 -0
  35. data/split/Function/dvector.h +61 -0
  36. data/split/Function/extconf.rb +4 -0
  37. data/split/Function/function.c +988 -0
  38. data/split/Function/joint_qsort.c +258 -0
  39. data/split/Function/lib/Function_extras.rb +44 -0
  40. data/split/Function/namespace.h +47 -0
  41. data/split/Function/safe_double.h +104 -0
  42. data/split/Function/symbols.c +92 -0
  43. data/split/Function/symbols.h +52 -0
  44. data/split/Tioga/axes.c +774 -0
  45. data/split/Tioga/defs.h +33 -0
  46. data/split/Tioga/dtable.h +35 -0
  47. data/split/Tioga/dvector.h +61 -0
  48. data/split/Tioga/extconf.rb +4 -0
  49. data/split/Tioga/figures.c +672 -0
  50. data/split/Tioga/figures.h +855 -0
  51. data/split/Tioga/flate.h +98 -0
  52. data/split/Tioga/init.c +524 -0
  53. data/split/Tioga/lib/Arcs_and_Circles.rb +64 -0
  54. data/split/Tioga/lib/ColorConstants.rb +274 -0
  55. data/split/Tioga/lib/Colorbars.rb +10 -0
  56. data/split/Tioga/lib/Colormaps.rb +105 -0
  57. data/split/Tioga/lib/Coordinate_Conversions.rb +194 -0
  58. data/split/Tioga/lib/Creating_Paths.rb +94 -0
  59. data/split/Tioga/lib/Doc.rb +91 -0
  60. data/split/Tioga/lib/Executive.rb +515 -0
  61. data/split/Tioga/lib/FigMkr.rb +2224 -0
  62. data/split/Tioga/lib/FigureConstants.rb +125 -0
  63. data/split/Tioga/lib/Figures_and_Plots.rb +268 -0
  64. data/split/Tioga/lib/Images.rb +278 -0
  65. data/split/Tioga/lib/Legends.rb +190 -0
  66. data/split/Tioga/lib/MarkerConstants.rb +122 -0
  67. data/split/Tioga/lib/Markers.rb +129 -0
  68. data/split/Tioga/lib/Page_Frame_Bounds.rb +567 -0
  69. data/split/Tioga/lib/Rectangles.rb +94 -0
  70. data/split/Tioga/lib/Shading.rb +100 -0
  71. data/split/Tioga/lib/Special_Paths.rb +307 -0
  72. data/split/Tioga/lib/Strokes.rb +129 -0
  73. data/split/Tioga/lib/TeX_Text.rb +454 -0
  74. data/split/Tioga/lib/TexPreamble.rb +358 -0
  75. data/split/Tioga/lib/Titles_and_Labels.rb +306 -0
  76. data/split/Tioga/lib/Transparency.rb +89 -0
  77. data/split/Tioga/lib/Using_Paths.rb +164 -0
  78. data/split/Tioga/lib/Utils.rb +74 -0
  79. data/split/Tioga/lib/X_and_Y_Axes.rb +749 -0
  80. data/split/Tioga/lib/irb_tioga.rb +122 -0
  81. data/split/Tioga/lib/tioga.rb +1 -0
  82. data/split/Tioga/lib/tioga_ui.rb +5 -0
  83. data/split/Tioga/lib/tioga_ui_cmds.rb +793 -0
  84. data/split/Tioga/makers.c +989 -0
  85. data/split/Tioga/mk_tioga_sty.rb +53 -0
  86. data/split/Tioga/namespace.h +47 -0
  87. data/split/Tioga/pdf_font_dicts.c +18253 -0
  88. data/split/Tioga/pdfcolor.c +486 -0
  89. data/split/Tioga/pdfcoords.c +505 -0
  90. data/split/Tioga/pdffile.c +342 -0
  91. data/split/Tioga/pdfimage.c +536 -0
  92. data/split/Tioga/pdfpath.c +914 -0
  93. data/split/Tioga/pdfs.h +229 -0
  94. data/split/Tioga/pdftext.c +443 -0
  95. data/split/Tioga/safe_double.h +104 -0
  96. data/split/Tioga/symbols.c +92 -0
  97. data/split/Tioga/symbols.h +52 -0
  98. data/split/Tioga/texout.c +380 -0
  99. data/split/defs.h +33 -0
  100. data/split/extconf.rb +107 -0
  101. data/split/mkmf2.rb +1612 -0
  102. data/split/namespace.h +47 -0
  103. data/split/safe_double.h +104 -0
  104. data/split/scripts/tioga +4 -0
  105. data/split/symbols.c +92 -0
  106. data/split/symbols.h +52 -0
  107. data/tests/dtable_test.data +6 -0
  108. data/tests/dvector_read_test.data +1 -0
  109. data/tests/dvector_test.data +101 -0
  110. data/tests/tc_Dtable.rb +221 -0
  111. data/tests/tc_Dvector.rb +791 -0
  112. data/tests/tc_FMkr.rb +162 -0
  113. data/tests/tc_Flate.rb +45 -0
  114. data/tests/tc_Function.rb +111 -0
  115. data/tests/ts_Tioga.rb +38 -0
  116. metadata +163 -0
@@ -0,0 +1,914 @@
1
+ /* pdfpath.c */
2
+ /*
3
+ Copyright (C) 2005 Bill Paxton
4
+
5
+ This file is part of Tioga.
6
+
7
+ Tioga is free software; you can redistribute it and/or modify
8
+ it under the terms of the GNU General Library Public License as published
9
+ by the Free Software Foundation; either version 2 of the License, or
10
+ (at your option) any later version.
11
+
12
+ Tioga is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU Library General Public License for more details.
16
+
17
+ You should have received a copy of the GNU Library General Public License
18
+ along with Tioga; if not, write to the Free Software
19
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
+ */
21
+
22
+ #include "figures.h"
23
+ #include "pdfs.h"
24
+
25
+ bool have_current_point, constructing_path, writing_file;
26
+ double bbox_llx, bbox_lly, bbox_urx, bbox_ury;
27
+
28
+ /* emits a warning on nonok numbers if croak_on_nonok_numbers is true */
29
+ static void croak_on_nonok(FM *p, const char * function)
30
+ {
31
+ if(p->croak_on_nonok_numbers)
32
+ rb_warn("Illegal coordinates in function %s, element suppressed",
33
+ function);
34
+ }
35
+
36
+ /* small macro to check if a number is ok to be output */
37
+ #define is_okay_number(x) ((x) - (x) == 0.0)
38
+
39
+ #define CROAK_ON_NONOK(p) croak_on_nonok(p, __FUNCTION__)
40
+ #define ARE_OK_NUMBERS(x,y) if(! is_okay_number(x) || ! is_okay_number(y)) {\
41
+ CROAK_ON_NONOK(p); return;}
42
+
43
+
44
+ /* PDF graphics */
45
+
46
+ /* graphics attributes */
47
+
48
+ void Unpack_RGB(VALUE rgb, double *rp, double *gp, double *bp)
49
+ {
50
+ if (rgb == Qnil) { *rp = *gp = *bp = 0.0; return; }
51
+ rgb = rb_Array(rgb);
52
+ if (RARRAY(rgb)->len != 3) rb_raise(rb_eArgError, "Sorry: invalid rgb array for setting color: must have 3 entries");
53
+ VALUE entry = rb_ary_entry(rgb, 0);
54
+ entry = rb_Float(entry);
55
+ double r = NUM2DBL(entry);
56
+ entry = rb_ary_entry(rgb, 1);
57
+ entry = rb_Float(entry);
58
+ double g = NUM2DBL(entry);
59
+ entry = rb_ary_entry(rgb, 2);
60
+ entry = rb_Float(entry);
61
+ double b = NUM2DBL(entry);
62
+ if (r < 0.0 || r > 1.0) rb_raise(rb_eArgError, "Sorry: invalid red (%g) for color: must be between 0 and 1", r);
63
+ if (g < 0.0 || g > 1.0) rb_raise(rb_eArgError, "Sorry: invalid green (%g) for color: must be between 0 and 1", g);
64
+ if (b < 0.0 || b > 1.0) rb_raise(rb_eArgError, "Sorry: invalid blue (%g) for color: must be between 0 and 1", b);
65
+ *rp = r; *gp = g; *bp = b;
66
+ }
67
+
68
+ void c_stroke_color_set(FM *p, double r, double g, double b)
69
+ {
70
+ if (writing_file) fprintf(TF, "%0.3f %0.3f %0.3f RG\n", r, g, b);
71
+ }
72
+
73
+ VALUE FM_stroke_color_set(VALUE fmkr, VALUE value) // value is array of [r, g, b] intensities from 0 to 1
74
+ { // r g b RG
75
+ FM *p = Get_FM(fmkr);
76
+ double r, g, b;
77
+ Unpack_RGB(value, &r, &g, &b);
78
+ c_stroke_color_set(p, r, g, b);
79
+ p->stroke_color = value;
80
+ return value;
81
+ }
82
+
83
+ VALUE FM_fill_color_set(VALUE fmkr, VALUE value) // value is array of [r, g, b] intensities from 0 to 1
84
+ { // r g b rg
85
+ FM *p = Get_FM(fmkr);
86
+ double r, g, b;
87
+ Unpack_RGB(value, &r, &g, &b);
88
+ if (writing_file) fprintf(TF, "%0.3f %0.3f %0.3f rg\n", r, g, b);
89
+ p->fill_color = value;
90
+ return value;
91
+ }
92
+
93
+ void c_line_width_set(FM *p, double line_width)
94
+ {
95
+ if (line_width < 0.0) rb_raise(rb_eArgError, "Sorry: invalid line width (%g points): must be positive", line_width);
96
+ if (line_width > 1e3) rb_raise(rb_eArgError, "Sorry: too large line width (%g points)", line_width);
97
+ if (writing_file) fprintf(TF, "%0.3f w\n", line_width * ENLARGE * p->default_line_scale);
98
+ p->line_width = line_width;
99
+ }
100
+
101
+ VALUE FM_line_width_set(VALUE fmkr, VALUE value) // value is thickness in points
102
+ { // w
103
+ FM *p = Get_FM(fmkr);
104
+ value = rb_Float(value);
105
+ c_line_width_set(p, NUM2DBL(value));
106
+ return value;
107
+ }
108
+
109
+ void c_line_scale_set(FM *p, double new_scale)
110
+ {
111
+ if (new_scale <= 0) rb_raise(rb_eArgError, "Sorry: line scale must be positive");
112
+ p->default_line_scale = new_scale;
113
+ c_line_width_set(p, p->line_width);
114
+ }
115
+
116
+ VALUE FM_rescale_lines(VALUE fmkr, VALUE scaling_factor)
117
+ {
118
+ FM *p = Get_FM(fmkr);
119
+ scaling_factor = rb_Float(scaling_factor);
120
+ c_line_scale_set(p, NUM2DBL(scaling_factor) * p->default_line_scale);
121
+ return fmkr;
122
+ }
123
+
124
+ void c_line_cap_set(FM *p, int line_cap)
125
+ {
126
+ if (line_cap < 0 || line_cap > 3) rb_raise(rb_eArgError, "Sorry: invalid arg for setting line_cap (%i)", line_cap);
127
+ if (writing_file) fprintf(TF, "%d J\n", line_cap);
128
+ p->line_cap = line_cap;
129
+ }
130
+
131
+ VALUE FM_line_cap_set(VALUE fmkr, VALUE value)
132
+ { // J
133
+ FM *p = Get_FM(fmkr);
134
+ value = rb_Integer(value);
135
+ c_line_cap_set(p, NUM2INT(value));
136
+ return value;
137
+ }
138
+
139
+ void c_line_join_set(FM *p, int line_join)
140
+ {
141
+ if (line_join < 0 || line_join > 3) rb_raise(rb_eArgError, "Sorry: invalid arg for setting line_join (%i)", line_join);
142
+ if (writing_file) fprintf(TF, "%d j\n", line_join);
143
+ p->line_join = line_join;
144
+ }
145
+
146
+ VALUE FM_line_join_set(VALUE fmkr, VALUE value)
147
+ { // j
148
+ FM *p = Get_FM(fmkr);
149
+ value = rb_Integer(value);
150
+ c_line_join_set(p, NUM2INT(value));
151
+ return value;
152
+ }
153
+
154
+ void c_miter_limit_set(FM *p, double miter_limit)
155
+ {
156
+ if (miter_limit < 0.0)
157
+ rb_raise(rb_eArgError,
158
+ "Sorry: invalid miter limit (%g): must be positive ratio for max miter length to line width", miter_limit);
159
+ if (writing_file) fprintf(TF, "%0.3f M\n", miter_limit);
160
+ p->miter_limit = miter_limit;
161
+ }
162
+
163
+ VALUE FM_miter_limit_set(VALUE fmkr, VALUE value) // value is max ratio of miter length to line width
164
+ { // M
165
+ FM *p = Get_FM(fmkr);
166
+ if (constructing_path) rb_raise(rb_eArgError, "Sorry: must not be constructing a path when change miter limit");
167
+ value = rb_Float(value);
168
+ c_miter_limit_set(p, NUM2DBL(value));
169
+ return value;
170
+ }
171
+
172
+ VALUE FM_line_type_set(VALUE fmkr, VALUE line_type)
173
+ { // array phase d (distances given in points)
174
+ FM *p = Get_FM(fmkr);
175
+ double sz;
176
+ if (constructing_path) rb_raise(rb_eArgError, "Sorry: must not be constructing a path when change line_type");
177
+ if (line_type == Qnil) {
178
+ fprintf(TF, "[] 0 d\n");
179
+ } else {
180
+ line_type = rb_Array(line_type);
181
+ if (writing_file) {
182
+ if (RARRAY(line_type)->len != 2)
183
+ rb_raise(rb_eArgError, "Sorry: invalid line_type. Must be [ [dash pattern] dash phase ]");
184
+ VALUE dashArray = rb_ary_entry(line_type, 0), dashPhase = rb_ary_entry(line_type, 1);
185
+ fprintf(TF, "[ ");
186
+ if (dashArray != Qnil) {
187
+ dashArray = rb_Array(dashArray);
188
+ long i, len = RARRAY(dashArray)->len;
189
+ for (i=0; i < len; i++) {
190
+ VALUE entry = rb_ary_entry(dashArray, i);
191
+ entry = rb_Float(entry);
192
+ sz = NUM2DBL(entry);
193
+ if (sz < 0.0) rb_raise(rb_eArgError, "Sorry: invalid dash array entry (%g): must be positive", sz);
194
+ fprintf(TF, "%0.3f ", sz * ENLARGE);
195
+ }
196
+ }
197
+ dashPhase = rb_Float(dashPhase);
198
+ sz = NUM2DBL(dashPhase);
199
+ if (sz < 0.0) rb_raise(rb_eArgError, "Sorry: invalid dash phase (%g): must be positive", sz);
200
+ fprintf(TF, "] %0.3f d\n", sz * ENLARGE);
201
+ }
202
+ }
203
+ p->line_type = line_type;
204
+ return fmkr;
205
+ }
206
+
207
+
208
+ /* Path construction operators */
209
+
210
+ /* all locations and vectors are in figure coordinates */
211
+
212
+ long c_round_dev(FM *p, double v) { // make sure that we don't get too far out or can overflow!
213
+ if (v > MAX_DEV_COORD_ALLOWED) {
214
+ //if (p->debug_verbosity_level > 0) printf("c_round_dev clipping %g\n", v);
215
+ return iMAX_DEV_COORD_ALLOWED;
216
+ }
217
+ if (v < -MAX_DEV_COORD_ALLOWED) {
218
+ //if (p->debug_verbosity_level > 0) printf("c_round_dev clipping %g\n", v);
219
+ return -iMAX_DEV_COORD_ALLOWED;
220
+ }
221
+ return ROUND(v);
222
+ }
223
+
224
+ void update_bbox(FM *p, double x, double y)
225
+ {
226
+ if (x >= p->clip_left && x < bbox_llx) bbox_llx = x;
227
+ if (y >= p->clip_bottom && y < bbox_lly) bbox_lly = y;
228
+ if (x <= p->clip_right && x > bbox_urx) bbox_urx = x;
229
+ if (y <= p->clip_top && y > bbox_ury) bbox_ury = y;
230
+ }
231
+
232
+ VALUE FM_update_bbox(VALUE fmkr, VALUE x, VALUE y)
233
+ {
234
+ FM *p = Get_FM(fmkr);
235
+ x = rb_Float(x);
236
+ y = rb_Float(y);
237
+ update_bbox(p, convert_figure_to_output_x(p,NUM2DBL(x)), convert_figure_to_output_y(p,NUM2DBL(y)));
238
+ return fmkr;
239
+ }
240
+
241
+
242
+ VALUE FM_bbox_left(VALUE fmkr)
243
+ {
244
+ return rb_float_new(bbox_llx);
245
+ }
246
+
247
+ VALUE FM_bbox_right(VALUE fmkr)
248
+ {
249
+ return rb_float_new(bbox_urx);
250
+ }
251
+
252
+ VALUE FM_bbox_top(VALUE fmkr)
253
+ {
254
+ return rb_float_new(bbox_ury);
255
+ }
256
+
257
+ VALUE FM_bbox_bottom(VALUE fmkr)
258
+ {
259
+ return rb_float_new(bbox_lly);
260
+ }
261
+
262
+
263
+
264
+ void c_moveto(FM *p, double x, double y)
265
+ {
266
+ ARE_OK_NUMBERS(x,y);
267
+ if (writing_file) fprintf(TF, "%ld %ld m\n", c_round_dev(p,x), c_round_dev(p,y));
268
+ update_bbox(p, x, y);
269
+ have_current_point = constructing_path = true;
270
+ }
271
+
272
+ VALUE FM_move_to_point(VALUE fmkr, VALUE x, VALUE y)
273
+ {
274
+ FM *p = Get_FM(fmkr);
275
+ x = rb_Float(x);
276
+ y = rb_Float(y);
277
+ double dev_x = convert_figure_to_output_x(p,NUM2DBL(x)), dev_y = convert_figure_to_output_y(p,NUM2DBL(y));
278
+ c_moveto(p, dev_x, dev_y);
279
+ return fmkr;
280
+ }
281
+
282
+ void c_lineto(FM *p, double x, double y)
283
+ {
284
+ ARE_OK_NUMBERS(x,y);
285
+ if (!constructing_path) rb_raise(rb_eArgError, "Sorry: must start path with moveto before call lineto");
286
+ if (writing_file) fprintf(TF, "%ld %ld l\n", c_round_dev(p,x), c_round_dev(p,y));
287
+ update_bbox(p, x, y);
288
+ }
289
+
290
+ VALUE FM_append_point_to_path(VALUE fmkr, VALUE x, VALUE y)
291
+ {
292
+ FM *p = Get_FM(fmkr);
293
+ x = rb_Float(x);
294
+ y = rb_Float(y);
295
+ double dev_x = convert_figure_to_output_x(p,NUM2DBL(x)), dev_y = convert_figure_to_output_y(p,NUM2DBL(y));
296
+ c_lineto(p, dev_x, dev_y);
297
+ return fmkr;
298
+ }
299
+
300
+ void c_curveto(FM *p, double x1, double y1, double x2, double y2, double x3, double y3)
301
+ {
302
+ ARE_OK_NUMBERS(x1,y1);
303
+ ARE_OK_NUMBERS(x2,y2);
304
+ ARE_OK_NUMBERS(x3,y3);
305
+ if (!constructing_path) rb_raise(rb_eArgError, "Sorry: must start path with moveto before call curveto");
306
+ if (writing_file) fprintf(TF, "%ld %ld %ld %ld %ld %ld c\n",
307
+ c_round_dev(p,x1), c_round_dev(p,y1), c_round_dev(p,x2), c_round_dev(p,y2), c_round_dev(p,x3), c_round_dev(p,y3));
308
+ update_bbox(p, x1, y1);
309
+ update_bbox(p, x2, y2);
310
+ update_bbox(p, x3, y3);
311
+ }
312
+
313
+ VALUE FM_append_curve_to_path(VALUE fmkr, VALUE x1, VALUE y1, VALUE x2, VALUE y2, VALUE x3, VALUE y3)
314
+ {
315
+ FM *p = Get_FM(fmkr);
316
+ x1 = rb_Float(x1);
317
+ y1 = rb_Float(y1);
318
+ x2 = rb_Float(x2);
319
+ y2 = rb_Float(y2);
320
+ x3 = rb_Float(x3);
321
+ y3 = rb_Float(y3);
322
+ double dev_x1 = convert_figure_to_output_x(p,NUM2DBL(x1)), dev_y1 = convert_figure_to_output_y(p,NUM2DBL(y1));
323
+ double dev_x2 = convert_figure_to_output_x(p,NUM2DBL(x2)), dev_y2 = convert_figure_to_output_y(p,NUM2DBL(y2));
324
+ double dev_x3 = convert_figure_to_output_x(p,NUM2DBL(x3)), dev_y3 = convert_figure_to_output_y(p,NUM2DBL(y3));
325
+ c_curveto(p, dev_x1, dev_y1, dev_x2, dev_y2, dev_x3, dev_y3);
326
+ return fmkr;
327
+ }
328
+
329
+ void c_closepath(FM *p)
330
+ {
331
+ if (!constructing_path) rb_raise(rb_eArgError, "Sorry: must be constructing path when call closepath");
332
+ if (writing_file) fprintf(TF, "h\n");
333
+ have_current_point = false;
334
+ p = NULL;
335
+ }
336
+
337
+ VALUE FM_close_path(VALUE fmkr)
338
+ {
339
+ c_closepath(Get_FM(fmkr));
340
+ return fmkr;
341
+ }
342
+
343
+ void c_append_arc(FM *p, double x_start, double y_start, double x_corner, double y_corner,
344
+ double x_end, double y_end, double radius)
345
+ {
346
+ ARE_OK_NUMBERS(x_start,y_start);
347
+ ARE_OK_NUMBERS(x_corner,y_corner);
348
+ ARE_OK_NUMBERS(x_end,y_end);
349
+
350
+ double x0, y0, x1, y1, x2, y2, x3, y3, udx, udy, vdx, vdy, wdx, wdy, len, x_center, y_center, tmp;
351
+ double psi, sin_psi, cos_psi, theta, cos_alpha, sin_alpha;
352
+ udx = x_start - x_corner; udy = y_start - y_corner;
353
+ vdx = x_end - x_corner; vdy = y_end - y_corner;
354
+ len = sqrt(udx*udx + udy*udy); udx /= len; udy /= len; // u is unit vector from corner to start
355
+ len = sqrt(vdx*vdx + vdy*vdy); vdx /= len; vdy /= len; // v is unit vector from corner to end
356
+ cos_psi = udx*vdx + udy*vdy; sin_psi = udy * vdx - udx * vdy;
357
+ psi = atan2(sin_psi, cos_psi); // psi is angle between u and v
358
+ if (psi > PI) psi = 2*PI - psi;
359
+ theta = PI - psi; // theta is opening angle for the arc
360
+ while (theta < 0) theta += 2*PI;
361
+ if (theta >= PI) rb_raise(rb_eArgError, "Sorry: invalid control point for arc");
362
+ // first compute control points for arc of opening theta with unit radius, bisected by positive x axis
363
+ // based on note by Richard DeVeneza, "How to determine the control points of a Bezier curve that
364
+ // approximates a small circular arc", Nov 2004.
365
+ x0 = cos(theta/2); y0 = sin(theta/2); x3 = x0; y3 = -y0;
366
+ x1 = (4-x0)/3; y1 = (1-x0)*(3-x0)/(3*y0); x2 = x1; y2 = -y1;
367
+ if (sin_psi > 0) { y0 = -y0; y1 = -y1; y2 = -y2; y3 = -y3; }
368
+ wdx = udx + vdx; wdy = udy + vdy;
369
+ len = sqrt(wdx*wdx + wdy*wdy); wdx /= len; wdy /= len; // w is unit vector bisecting the corner angle
370
+ cos_alpha = -wdx; sin_alpha = -wdy; // need to rotate the arc by alpha
371
+ x_center = x_corner + radius * wdx / x0;
372
+ y_center = y_corner + radius * wdy / x0;
373
+ // then translate arc to x_center, y_center
374
+ tmp = x0; x0 = x0*cos_alpha - y0*sin_alpha; y0 = y0*cos_alpha + tmp*sin_alpha;
375
+ tmp = x1; x1 = x1*cos_alpha - y1*sin_alpha; y1 = y1*cos_alpha + tmp*sin_alpha;
376
+ tmp = x2; x2 = x2*cos_alpha - y2*sin_alpha; y2 = y2*cos_alpha + tmp*sin_alpha;
377
+ tmp = x3; x3 = x3*cos_alpha - y3*sin_alpha; y3 = y3*cos_alpha + tmp*sin_alpha;
378
+ x0 *= radius; y0 *= radius;
379
+ x1 *= radius; y1 *= radius;
380
+ x2 *= radius; y2 *= radius;
381
+ x3 *= radius; y3 *= radius;
382
+ x0 += x_center; y0 += y_center;
383
+ x1 += x_center; y1 += y_center;
384
+ x2 += x_center; y2 += y_center;
385
+ x3 += x_center; y3 += y_center;
386
+ if (have_current_point) c_lineto(p,x0,y0);
387
+ else c_moveto(p,x0,y0);
388
+ c_curveto(p,x1,y1,x2,y2,x3,y3);
389
+ }
390
+
391
+ double Get_Arc_Radius(FM *p, VALUE dx, VALUE dy)
392
+ {
393
+ dx = rb_Float(dx);
394
+ dy = rb_Float(dy);
395
+ double rx = NUM2DBL(dx), ry = NUM2DBL(dy);
396
+ rx = convert_figure_to_output_dx(p,rx);
397
+ ry = convert_figure_to_output_dy(p,ry);
398
+ if (rx < 0) rx = -rx;
399
+ if (ry < 0) ry = -ry;
400
+ return MIN(rx,ry);
401
+ }
402
+
403
+ VALUE FM_append_arc_to_path(VALUE fmkr, VALUE x_start, VALUE y_start, VALUE x_corner, VALUE y_corner,
404
+ VALUE x_end, VALUE y_end, VALUE dx, VALUE dy)
405
+ {
406
+ FM *p = Get_FM(fmkr);
407
+ x_start = rb_Float(x_start);
408
+ y_start = rb_Float(y_start);
409
+ x_corner = rb_Float(x_corner);
410
+ y_corner = rb_Float(y_corner);
411
+ x_end = rb_Float(x_end);
412
+ y_end = rb_Float(y_end);
413
+ c_append_arc(p,
414
+ convert_figure_to_output_x(p,NUM2DBL(x_start)), convert_figure_to_output_y(p,NUM2DBL(y_start)),
415
+ convert_figure_to_output_x(p,NUM2DBL(x_corner)), convert_figure_to_output_y(p,NUM2DBL(y_corner)),
416
+ convert_figure_to_output_x(p,NUM2DBL(x_end)), convert_figure_to_output_y(p,NUM2DBL(y_end)),
417
+ Get_Arc_Radius(p,dx,dy));
418
+ return fmkr;
419
+ }
420
+
421
+ void c_append_rect(FM *p, double x, double y, double width, double height)
422
+ {
423
+ c_moveto(p,x,y);
424
+ c_lineto(p,x+width,y);
425
+ c_lineto(p,x+width,y+height);
426
+ c_lineto(p,x,y+height);
427
+ c_closepath(p);
428
+ }
429
+
430
+ VALUE FM_append_rect_to_path(VALUE fmkr, VALUE x, VALUE y, VALUE width, VALUE height)
431
+ {
432
+ FM *p = Get_FM(fmkr);
433
+ x = rb_Float(x);
434
+ y = rb_Float(y);
435
+ width = rb_Float(width);
436
+ height = rb_Float(height);
437
+ c_append_rect(p,
438
+ convert_figure_to_output_x(p,NUM2DBL(x)), convert_figure_to_output_y(p,NUM2DBL(y)),
439
+ convert_figure_to_output_dx(p,NUM2DBL(width)), convert_figure_to_output_dy(p,NUM2DBL(height)));
440
+ return fmkr;
441
+ }
442
+
443
+ void c_append_rounded_rect(FM *p, double x, double y, double width, double height, double radius)
444
+ {
445
+ double xc = x + width/2, yc = y + height/2, xp = x + width, yp = y + height;
446
+ c_moveto(p,xc,y);
447
+ c_append_arc(p,xc,y, xp,y, xp,yc, radius);
448
+ c_append_arc(p,xp,yc, xp,yp, xc,yp, radius);
449
+ c_append_arc(p,xc,yp, x,yp, x,yc, radius);
450
+ c_append_arc(p,x,yc, x,y, xc,y, radius);
451
+ c_closepath(p);
452
+ }
453
+
454
+ VALUE FM_append_rounded_rect_to_path(VALUE fmkr, VALUE x, VALUE y, VALUE width, VALUE height, VALUE dx, VALUE dy)
455
+ {
456
+ FM *p = Get_FM(fmkr);
457
+ x = rb_Float(x);
458
+ y = rb_Float(y);
459
+ width = rb_Float(width);
460
+ height = rb_Float(height);
461
+ c_append_rounded_rect(p,
462
+ convert_figure_to_output_x(p,NUM2DBL(x)), convert_figure_to_output_y(p,NUM2DBL(y)),
463
+ convert_figure_to_output_dx(p,NUM2DBL(width)), convert_figure_to_output_dy(p,NUM2DBL(height)),
464
+ Get_Arc_Radius(p,dx,dy));
465
+ return fmkr;
466
+ }
467
+
468
+ #define ROTATE90(x,y) tmp = x; x = y; y = -tmp;
469
+ #define TRANSFORM(xp,yp,x,y) xp = a*(x)+c*(y)+e; yp = b*(x)+d*(y)+f;
470
+
471
+ void c_append_oval(FM *p, double x, double y, double dx, double dy, double angle)
472
+ {
473
+ double cs = cos(angle/RADIANS_TO_DEGREES), sn = sin(angle/RADIANS_TO_DEGREES);
474
+ double a = cs*dx, b = sn*dx, c = -sn*dy, d = cs*dy, e = x, f = y;
475
+ double x0, y0, x1, y1, x2, y2, x3, y3, x0p, y0p, x1p, y1p, x2p, y2p, x3p, y3p, tmp;
476
+ int i;
477
+ x0 = 0.707107; y0 = 0.707107; x3 = x0; y3 = -y0;
478
+ x1 = 1.09763; y1 = 0.316582; x2 = x1; y2 = -y1;
479
+ TRANSFORM(x0p,y0p,x0,y0)
480
+ TRANSFORM(x1p,y1p,x1,y1)
481
+ TRANSFORM(x2p,y2p,x2,y2)
482
+ TRANSFORM(x3p,y3p,x3,y3)
483
+ c_moveto(p,x0p,y0p);
484
+ c_curveto(p,x1p,y1p,x2p,y2p,x3p,y3p);
485
+ for (i = 0; i < 3; i++) {
486
+ ROTATE90(x0,y0)
487
+ ROTATE90(x1,y1)
488
+ ROTATE90(x2,y2)
489
+ ROTATE90(x3,y3)
490
+ TRANSFORM(x0p,y0p,x0,y0)
491
+ TRANSFORM(x1p,y1p,x1,y1)
492
+ TRANSFORM(x2p,y2p,x2,y2)
493
+ TRANSFORM(x3p,y3p,x3,y3)
494
+ c_curveto(p,x1p,y1p,x2p,y2p,x3p,y3p);
495
+ }
496
+ c_closepath(p);
497
+ }
498
+
499
+ VALUE FM_append_oval_to_path(VALUE fmkr, VALUE x, VALUE y, VALUE dx, VALUE dy, VALUE angle)
500
+ {
501
+ FM *p = Get_FM(fmkr);
502
+ x = rb_Float(x);
503
+ y = rb_Float(y);
504
+ dx = rb_Float(dx);
505
+ dy = rb_Float(dy);
506
+ angle = rb_Float(angle);
507
+ c_append_oval(p,
508
+ convert_figure_to_output_x(p,NUM2DBL(x)), convert_figure_to_output_y(p,NUM2DBL(y)),
509
+ convert_figure_to_output_dx(p,NUM2DBL(dx)), convert_figure_to_output_dy(p,NUM2DBL(dy)),
510
+ NUM2DBL(angle));
511
+ return fmkr;
512
+ }
513
+
514
+ VALUE FM_append_circle_to_path(VALUE fmkr, VALUE x, VALUE y, VALUE dx)
515
+ {
516
+ FM *p = Get_FM(fmkr);
517
+ x = rb_Float(x);
518
+ y = rb_Float(y);
519
+ dx = rb_Float(dx);
520
+ double s = convert_figure_to_output_dx(p,NUM2DBL(dx));
521
+ c_append_oval(p,
522
+ convert_figure_to_output_x(p,NUM2DBL(x)), convert_figure_to_output_y(p,NUM2DBL(y)),
523
+ s, s, 0.0);
524
+ return fmkr;
525
+ }
526
+
527
+ VALUE FM_append_points_to_path(VALUE fmkr, VALUE x_vec, VALUE y_vec)
528
+ {
529
+ FM *p = Get_FM(fmkr);
530
+ long xlen, ylen, i;
531
+ double x0, y0;
532
+ double *xs = Dvector_Data_for_Read(x_vec, &xlen);
533
+ double *ys = Dvector_Data_for_Read(y_vec, &ylen);
534
+ if (xlen != ylen) rb_raise(rb_eArgError, "Sorry: must have same number xs and ys for append_points");
535
+ if (xlen <= 0) return fmkr;
536
+ x0 = convert_figure_to_output_x(p,xs[0]); y0 = convert_figure_to_output_y(p,ys[0]);
537
+ if (have_current_point) c_lineto(p,x0,y0);
538
+ else c_moveto(p,x0,y0);
539
+ for (i=1; i<xlen; i++)
540
+ c_lineto(p,convert_figure_to_output_x(p,xs[i]), convert_figure_to_output_y(p,ys[i]));
541
+ return fmkr;
542
+ }
543
+
544
+ VALUE FM_private_append_points_with_gaps_to_path(VALUE fmkr, VALUE x_vec, VALUE y_vec, VALUE gaps, VALUE close_gaps)
545
+ // where there's a gap, do a moveto instead of a lineto
546
+ {
547
+ if (gaps == Qnil) return FM_append_points_to_path(fmkr, x_vec, y_vec);
548
+ FM *p = Get_FM(fmkr);
549
+ long xlen, ylen, glen, i, j;
550
+ double x0, y0;
551
+ double *xs = Dvector_Data_for_Read(x_vec, &xlen);
552
+ double *ys = Dvector_Data_for_Read(y_vec, &ylen);
553
+ double *gs = Dvector_Data_for_Read(gaps, &glen);
554
+ bool do_close = (close_gaps == Qtrue);
555
+ if (xlen != ylen) rb_raise(rb_eArgError, "Sorry: must have same number xs and ys for append_points_with_gaps");
556
+ if (xlen <= 0) return fmkr;
557
+ x0 = convert_figure_to_output_x(p,xs[0]); y0 = convert_figure_to_output_y(p,ys[0]);
558
+ if (have_current_point) c_lineto(p,x0,y0);
559
+ else c_moveto(p,x0,y0);
560
+ for (i = 1, j = 0; j < glen; j++) {
561
+ int gap_start = ROUND(gs[j]);
562
+ if (gap_start == xlen) break;
563
+ if (gap_start > xlen)
564
+ rb_raise(rb_eArgError, "Sorry: gap value (%i) too large for vectors of length (%i)", gap_start, xlen);
565
+ while (i < gap_start) {
566
+ c_lineto(p,convert_figure_to_output_x(p,xs[i]), convert_figure_to_output_y(p,ys[i]));
567
+ i++;
568
+ }
569
+ if (do_close) c_closepath(p);
570
+ c_moveto(p,convert_figure_to_output_x(p,xs[i]), convert_figure_to_output_y(p,ys[i]));
571
+ i++;
572
+ }
573
+ while (i < xlen) {
574
+ c_lineto(p,convert_figure_to_output_x(p,xs[i]), convert_figure_to_output_y(p,ys[i]));
575
+ i++;
576
+ }
577
+ if (do_close) c_closepath(p);
578
+ return fmkr;
579
+ }
580
+
581
+ /* Path painting operators */
582
+
583
+ VALUE FM_stroke(VALUE fmkr)
584
+ {
585
+ if (!constructing_path) return fmkr;
586
+ if (writing_file) fprintf(TF, "S\n");
587
+ have_current_point = constructing_path = false;
588
+ return fmkr;
589
+ }
590
+
591
+ VALUE FM_close_and_stroke(VALUE fmkr)
592
+ {
593
+ if (!constructing_path) return fmkr;
594
+ if (writing_file) fprintf(TF, "s\n");
595
+ have_current_point = constructing_path = false;
596
+ return fmkr;
597
+ }
598
+
599
+ VALUE FM_fill(VALUE fmkr)
600
+ {
601
+ if (!constructing_path) return fmkr;
602
+ if (writing_file) fprintf(TF, "f\n");
603
+ have_current_point = constructing_path = false;
604
+ return fmkr;
605
+ }
606
+
607
+ VALUE FM_discard_path(VALUE fmkr)
608
+ {
609
+ if (!constructing_path) return fmkr;
610
+ if (writing_file) fprintf(TF, "n\n");
611
+ have_current_point = constructing_path = false;
612
+ return fmkr;
613
+ }
614
+
615
+ VALUE FM_eofill(VALUE fmkr)
616
+ {
617
+ if (!constructing_path) return fmkr;
618
+ if (writing_file) fprintf(TF, "f*\n");
619
+ have_current_point = constructing_path = false;
620
+ return fmkr;
621
+ }
622
+
623
+ VALUE FM_fill_and_stroke(VALUE fmkr)
624
+ {
625
+ if (!constructing_path) return fmkr;
626
+ if (writing_file) fprintf(TF, "B\n");
627
+ have_current_point = constructing_path = false;
628
+ return fmkr;
629
+ }
630
+
631
+ VALUE FM_eofill_and_stroke(VALUE fmkr)
632
+ {
633
+ if (!constructing_path) return fmkr;
634
+ if (writing_file) fprintf(TF, "B*\n");
635
+ have_current_point = constructing_path = false;
636
+ return fmkr;
637
+ }
638
+
639
+ VALUE FM_close_fill_and_stroke(VALUE fmkr)
640
+ {
641
+ if (!constructing_path) return fmkr;
642
+ if (writing_file) fprintf(TF, "b\n");
643
+ have_current_point = constructing_path = false;
644
+ return fmkr;
645
+ }
646
+
647
+ VALUE FM_close_eofill_and_stroke(VALUE fmkr)
648
+ {
649
+ if (!constructing_path) return fmkr;
650
+ if (writing_file) fprintf(TF, "b*\n");
651
+ have_current_point = constructing_path = false;
652
+ return fmkr;
653
+ }
654
+
655
+ void c_clip(FM *p)
656
+ {
657
+ if (!constructing_path) return;
658
+ if (writing_file) fprintf(TF, "W n\n");
659
+ have_current_point = constructing_path = false;
660
+ p = NULL;
661
+ }
662
+
663
+ VALUE FM_clip(VALUE fmkr)
664
+ {
665
+ c_clip(Get_FM(fmkr));
666
+ return fmkr;
667
+ }
668
+
669
+ VALUE FM_eoclip(VALUE fmkr)
670
+ {
671
+ if (!constructing_path) return fmkr;
672
+ if (writing_file) fprintf(TF, "W* n\n");
673
+ have_current_point = constructing_path = false;
674
+ return fmkr;
675
+ }
676
+
677
+ VALUE FM_fill_and_clip(VALUE fmkr)
678
+ {
679
+ if (!constructing_path) return fmkr;
680
+ if (writing_file) fprintf(TF, "q f Q\n");
681
+ c_clip(Get_FM(fmkr));
682
+ return fmkr;
683
+ }
684
+
685
+ VALUE FM_stroke_and_clip(VALUE fmkr)
686
+ {
687
+ if (!constructing_path) return fmkr;
688
+ if (writing_file) fprintf(TF, "q S Q\n");
689
+ c_clip(Get_FM(fmkr));
690
+ return fmkr;
691
+ }
692
+
693
+ VALUE FM_fill_stroke_and_clip(VALUE fmkr)
694
+ {
695
+ if (!constructing_path) return fmkr;
696
+ if (writing_file) fprintf(TF, "q B Q\n");
697
+ c_clip(Get_FM(fmkr));
698
+ return fmkr;
699
+ }
700
+
701
+ /* Combination Path Constructing and Using */
702
+
703
+ VALUE FM_stroke_line(VALUE fmkr, VALUE x1, VALUE y1, VALUE x2, VALUE y2)
704
+ {
705
+ if (constructing_path) rb_raise(rb_eArgError, "Sorry: must finish with current path before calling stroke_line");
706
+ FM_move_to_point(fmkr, x1, y1);
707
+ FM_append_point_to_path(fmkr, x2, y2);
708
+ FM_stroke(fmkr);
709
+ return fmkr;
710
+ }
711
+
712
+ VALUE FM_fill_rect(VALUE fmkr, VALUE x, VALUE y, VALUE width, VALUE height)
713
+ {
714
+ if (constructing_path) rb_raise(rb_eArgError, "Sorry: must finish with current path before calling fill_rect");
715
+ FM_append_rect_to_path(fmkr, x, y, width, height);
716
+ FM_fill(fmkr);
717
+ return fmkr;
718
+ }
719
+
720
+ VALUE FM_stroke_rect(VALUE fmkr, VALUE x, VALUE y, VALUE width, VALUE height)
721
+ {
722
+ if (constructing_path) rb_raise(rb_eArgError, "Sorry: must finish with current path before calling stroke_rect");
723
+ FM_append_rect_to_path(fmkr, x, y, width, height);
724
+ FM_stroke(fmkr);
725
+ return fmkr;
726
+ }
727
+
728
+ VALUE FM_fill_and_stroke_rect(VALUE fmkr, VALUE x, VALUE y, VALUE width, VALUE height)
729
+ {
730
+ if (constructing_path) rb_raise(rb_eArgError, "Sorry: must finish with current path before calling fill_and_stroke_rect");
731
+ FM_append_rect_to_path(fmkr, x, y, width, height);
732
+ FM_fill_and_stroke(fmkr);
733
+ return fmkr;
734
+ }
735
+
736
+ void c_clip_rect(FM *p, double x, double y, double width, double height) // in output coords
737
+ {
738
+ double clip_left=x, clip_right, clip_top, clip_bottom=y, clip_width=width, clip_height=height;
739
+ if (clip_width < 0.0) { clip_right = clip_left; clip_width = -clip_width; clip_left -= clip_width; }
740
+ else clip_right = clip_left + clip_width;
741
+ if (clip_height < 0.0) { clip_top = clip_bottom; clip_height = -clip_height; clip_bottom -= clip_height; }
742
+ else clip_top = clip_bottom + clip_height;
743
+ c_append_rect(p, clip_left, clip_bottom, clip_width, clip_height);
744
+ c_clip(p);
745
+ if (clip_left > p->clip_left) p->clip_left = clip_left;
746
+ if (clip_bottom > p->clip_bottom) p->clip_bottom = clip_bottom;
747
+ if (clip_right < p->clip_right) p->clip_right = clip_right;
748
+ if (clip_top < p->clip_top) p->clip_top = clip_top;
749
+ }
750
+
751
+ VALUE FM_clip_rect(VALUE fmkr, VALUE x, VALUE y, VALUE width, VALUE height)
752
+ {
753
+ if (constructing_path) rb_raise(rb_eArgError, "Sorry: must finish with current path before calling clip_rect");
754
+ FM *p = Get_FM(fmkr);
755
+ x = rb_Float(x);
756
+ y = rb_Float(y);
757
+ width = rb_Float(width);
758
+ height = rb_Float(height);
759
+ c_clip_rect(p,
760
+ convert_figure_to_output_x(p,NUM2DBL(x)), convert_figure_to_output_y(p,NUM2DBL(y)),
761
+ convert_figure_to_output_dx(p,NUM2DBL(width)), convert_figure_to_output_dy(p,NUM2DBL(height)));
762
+ return fmkr;
763
+ }
764
+
765
+ VALUE FM_clip_oval(VALUE fmkr, VALUE x, VALUE y, VALUE dx, VALUE dy, VALUE angle)
766
+ {
767
+ if (constructing_path) rb_raise(rb_eArgError, "Sorry: must finish with current path before calling clip_oval");
768
+ FM_append_oval_to_path(fmkr, x, y, dx, dy, angle);
769
+ FM_clip(fmkr);
770
+ return fmkr;
771
+ }
772
+
773
+ VALUE FM_fill_oval(VALUE fmkr, VALUE x, VALUE y, VALUE dx, VALUE dy, VALUE angle)
774
+ {
775
+ if (constructing_path) rb_raise(rb_eArgError, "Sorry: must finish with current path before calling fill_oval");
776
+ FM_append_oval_to_path(fmkr, x, y, dx, dy, angle);
777
+ FM_fill(fmkr);
778
+ return fmkr;
779
+ }
780
+
781
+ VALUE FM_stroke_oval(VALUE fmkr, VALUE x, VALUE y, VALUE dx, VALUE dy, VALUE angle)
782
+ {
783
+ if (constructing_path) rb_raise(rb_eArgError, "Sorry: must finish with current path before calling stroke_oval");
784
+ FM_append_oval_to_path(fmkr, x, y, dx, dy, angle);
785
+ FM_stroke(fmkr);
786
+ return fmkr;
787
+ }
788
+
789
+ VALUE FM_fill_and_stroke_oval(VALUE fmkr, VALUE x, VALUE y, VALUE dx, VALUE dy, VALUE angle)
790
+ {
791
+ if (constructing_path) rb_raise(rb_eArgError, "Sorry: must finish with current path before calling fill_and_stroke_oval");
792
+ FM_append_oval_to_path(fmkr, x, y, dx, dy, angle);
793
+ FM_fill_and_stroke(fmkr);
794
+ return fmkr;
795
+ }
796
+
797
+ VALUE FM_clip_rounded_rect(VALUE fmkr, VALUE x, VALUE y, VALUE width, VALUE height, VALUE dx, VALUE dy)
798
+ {
799
+ if (constructing_path) rb_raise(rb_eArgError, "Sorry: must finish with current path before calling clip_rounded_rect");
800
+ FM_append_rounded_rect_to_path(fmkr, x, y, width, height, dx, dy);
801
+ FM_clip(fmkr);
802
+ return fmkr;
803
+ }
804
+
805
+ VALUE FM_fill_rounded_rect(VALUE fmkr, VALUE x, VALUE y, VALUE width, VALUE height, VALUE dx, VALUE dy)
806
+ {
807
+ if (constructing_path) rb_raise(rb_eArgError, "Sorry: must finish with current path before calling fill_rounded_rect");
808
+ FM_append_rounded_rect_to_path(fmkr, x, y, width, height, dx, dy);
809
+ FM_fill(fmkr);
810
+ return fmkr;
811
+ }
812
+
813
+ VALUE FM_stroke_rounded_rect(VALUE fmkr, VALUE x, VALUE y, VALUE width, VALUE height, VALUE dx, VALUE dy)
814
+ {
815
+ if (constructing_path) rb_raise(rb_eArgError, "Sorry: must finish with current path before calling stroke_rounded_rect");
816
+ FM_append_rounded_rect_to_path(fmkr, x, y, width, height, dx, dy);
817
+ FM_stroke(fmkr);
818
+ return fmkr;
819
+ }
820
+
821
+ VALUE FM_fill_and_stroke_rounded_rect(VALUE fmkr, VALUE x, VALUE y, VALUE width, VALUE height, VALUE dx, VALUE dy)
822
+ {
823
+ if (constructing_path) rb_raise(rb_eArgError, "Sorry: must finish with current path before calling fill_and_stroke_rounded_rect");
824
+ FM_append_rounded_rect_to_path(fmkr, x, y, width, height, dx, dy);
825
+ FM_fill_and_stroke(fmkr);
826
+ return fmkr;
827
+ }
828
+
829
+ VALUE FM_clip_circle(VALUE fmkr, VALUE x, VALUE y, VALUE dx)
830
+ {
831
+ if (constructing_path) rb_raise(rb_eArgError, "Sorry: must finish with current path before calling clip_circle");
832
+ FM_append_circle_to_path(fmkr, x, y, dx);
833
+ FM_clip(fmkr);
834
+ return fmkr;
835
+ }
836
+
837
+ VALUE FM_fill_circle(VALUE fmkr, VALUE x, VALUE y, VALUE dx)
838
+ {
839
+ if (constructing_path) rb_raise(rb_eArgError, "Sorry: must finish with current path before calling fill_circle");
840
+ FM_append_circle_to_path(fmkr, x, y, dx);
841
+ FM_fill(fmkr);
842
+ return fmkr;
843
+ }
844
+
845
+ VALUE FM_stroke_circle(VALUE fmkr, VALUE x, VALUE y, VALUE dx)
846
+ {
847
+ if (constructing_path) rb_raise(rb_eArgError, "Sorry: must finish with current path before calling stroke_circle");
848
+ FM_append_circle_to_path(fmkr, x, y, dx);
849
+ FM_stroke(fmkr);
850
+ return fmkr;
851
+ }
852
+
853
+ VALUE FM_fill_and_stroke_circle(VALUE fmkr, VALUE x, VALUE y, VALUE dx)
854
+ {
855
+ if (constructing_path) rb_raise(rb_eArgError, "Sorry: must finish with current path before calling fill_and_stroke_circle");
856
+ FM_append_circle_to_path(fmkr, x, y, dx);
857
+ FM_fill_and_stroke(fmkr);
858
+ return fmkr;
859
+ }
860
+
861
+ static void c_append_frame(FM *p, bool clip)
862
+ {
863
+ double frame_left = convert_page_to_output_x(p, p->frame_left);
864
+ double frame_bottom = convert_page_to_output_y(p, p->frame_bottom);
865
+ double frame_width = convert_page_to_output_dx(p, p->frame_width);
866
+ double frame_height = convert_page_to_output_dy(p, p->frame_height);
867
+ double frame_right = frame_left + frame_width;
868
+ double frame_top = frame_bottom + frame_height;
869
+ c_append_rect(p, frame_left, frame_bottom, frame_width, frame_height);
870
+ if (!clip) return;
871
+ if (frame_left > p->clip_left) p->clip_left = frame_left;
872
+ if (frame_bottom > p->clip_bottom) p->clip_bottom = frame_bottom;
873
+ if (frame_right < p->clip_right) p->clip_right = frame_right;
874
+ if (frame_top < p->clip_top) p->clip_top = frame_top;
875
+ }
876
+
877
+ VALUE FM_append_frame_to_path(VALUE fmkr)
878
+ {
879
+ FM *p = Get_FM(fmkr);
880
+ c_append_frame(p, false);
881
+ return fmkr;
882
+ }
883
+
884
+ VALUE FM_fill_frame(VALUE fmkr)
885
+ {
886
+ FM *p = Get_FM(fmkr);
887
+ if (constructing_path) rb_raise(rb_eArgError, "Sorry: must finish with current path before calling fill_frame");
888
+ c_append_frame(p, false); FM_fill(fmkr);
889
+ return fmkr;
890
+ }
891
+
892
+ VALUE FM_stroke_frame(VALUE fmkr)
893
+ {
894
+ FM *p = Get_FM(fmkr);
895
+ if (constructing_path) rb_raise(rb_eArgError, "Sorry: must finish with current path before calling stroke_frame");
896
+ c_append_frame(p, false); FM_stroke(fmkr);
897
+ return fmkr;
898
+ }
899
+
900
+ VALUE FM_fill_and_stroke_frame(VALUE fmkr)
901
+ {
902
+ FM *p = Get_FM(fmkr);
903
+ if (constructing_path) rb_raise(rb_eArgError, "Sorry: must finish with current path before calling fill_and_stroke_frame");
904
+ c_append_frame(p, false); FM_fill_and_stroke(fmkr);
905
+ return fmkr;
906
+ }
907
+
908
+ VALUE FM_clip_to_frame(VALUE fmkr)
909
+ {
910
+ FM *p = Get_FM(fmkr);
911
+ if (constructing_path) rb_raise(rb_eArgError, "Sorry: must finish with current path before calling clip_to_frame");
912
+ c_append_frame(p, true); FM_clip(fmkr);
913
+ return fmkr;
914
+ }