geotree 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,471 @@
1
+ require_relative 'tools'
2
+
3
+ module PS_Private
4
+ class SOper
5
+ attr_reader :type
6
+ def self.null
7
+ @@null_oper
8
+ end
9
+
10
+ def initialize(type, *args)
11
+ @type = type
12
+ @args = *args.dup
13
+ end
14
+
15
+ def arg(offset)
16
+ @args[offset]
17
+ end
18
+
19
+ def nargs
20
+ @args.size
21
+ end
22
+ @@null_oper = SOper.new(-1)
23
+
24
+ end
25
+ end
26
+
27
+ # A debugging / demonstration utility class that
28
+ # generates postscript images
29
+ #
30
+ class PSWriter
31
+ include PS_Private
32
+
33
+ LT_SOLID = 1900
34
+ LT_DASHED = 1901
35
+ LT_DOTTED = 1902
36
+
37
+ LINEWIDTH_FACTOR = 3.0
38
+ LINETYPE = 1
39
+ LINEWIDTH = 2
40
+ TRANS = 3
41
+ RGB = 4
42
+ FONTHEIGHT = 5
43
+ SCALE = 6
44
+
45
+ S_START = 0
46
+ S_OPEN = 1
47
+ S_CLOSED = 2
48
+ # @param path path of file to write (e.g. xxx.ps)
49
+ def initialize(path)
50
+ @path = path
51
+ @line_width = -1
52
+ @rgb = [-1,0,0]
53
+ @phys_size = [612,792]
54
+ @state = S_START
55
+ @stack = []
56
+
57
+ set_logical_page_size(1000,1200)
58
+
59
+ @line_type = 0
60
+ @scale_ = 0
61
+ @font_height = 0
62
+ @scaled_font_height = 0
63
+
64
+ @page_used = false
65
+ @s = ''
66
+ end
67
+
68
+ # Close document and write to disk
69
+ def close
70
+ if @state < S_CLOSED
71
+ if @stack.size != 0
72
+ warn("state stack nonempty for #{@path}")
73
+ end
74
+ flush_page
75
+ set_state(S_CLOSED)
76
+ write_text_file(@path, @s)
77
+ end
78
+ end
79
+
80
+ # Set logical page size. Subsequent drawing operations will be scaled
81
+ # appropriately. Default logical page size is 1000 x 1200 units.
82
+ def set_logical_page_size( width, height)
83
+ raise IllegalStateException if @state != S_START
84
+ @document_size = [width,height]
85
+ end
86
+
87
+ def page_size
88
+ return @document_size
89
+ end
90
+
91
+ # Draw a rectangle
92
+ # @param inset distance to inset rectangle boundary (positive: shrink; negative:expand)
93
+ def draw_rect(x,y,w,h,inset = 0)
94
+ a(x + inset)
95
+ a(y + inset)
96
+ a(w - 2 * inset);
97
+ a(h - 2 * inset);
98
+ a(" RECT\n");
99
+ end
100
+
101
+ def set_font_size(height)
102
+ raise IllegalStateException if @state != S_OPEN
103
+
104
+ ret = SOper.null
105
+
106
+ if @font_height != height
107
+ ret = SOper.new(FONTHEIGHT, @font_height)
108
+ @font_height = height;
109
+ @scaled_font_height = height / @scale;
110
+ a("/Monaco findfont ");
111
+ a(@scaled_font_height);
112
+ a("scalefont setfont\n");
113
+ end
114
+ ret
115
+ end
116
+
117
+ def draw_string(string, x, y)
118
+ a(x)
119
+ a(y - @scaled_font_height / 2);
120
+ a("M ")
121
+ work = make_eps_safe(string)
122
+ a(work)
123
+ a(" TEXTC\n")
124
+ end
125
+
126
+ def draw_disc(cx, cy, radius)
127
+ a("NP");
128
+ a(cx);
129
+ a(cy);
130
+ a(radius);
131
+ a("0 360 A CP F\n");
132
+ end
133
+
134
+ def draw_circle(cx, cy, radius)
135
+ a("NP");
136
+ a(cx);
137
+ a(cy);
138
+ a(radius);
139
+ a("0 360 A CP S\n");
140
+ end
141
+
142
+ def draw_line(x1,y1,x2,y2)
143
+ a("NP ");
144
+ a(x1);
145
+ a(y1);
146
+ a(" M ");
147
+ a(x2);
148
+ a(y2);
149
+ a(" L CP S\n");
150
+ end
151
+
152
+ def set_line_solid
153
+ set_line_type(LT_SOLID)
154
+ end
155
+
156
+ def set_line_dashed
157
+ set_line_type(LT_DASHED)
158
+ end
159
+
160
+ def set_line_dotted
161
+ set_line_type(LT_DOTTED)
162
+ end
163
+
164
+ def set_scale(f)
165
+ ret = SOper.null
166
+ if f != 1
167
+ ret = SOper.new(SCALE, 1.0 / f)
168
+ a(f);
169
+ a(f);
170
+ a("scale\n");
171
+ end
172
+ ret
173
+ end
174
+
175
+ def set_line_type(type)
176
+ ret = SOper.null
177
+ if @line_type != type
178
+ ret = SOper.new(LINETYPE, @line_type)
179
+ @line_type = type
180
+ case type
181
+ when LT_DASHED
182
+ n = (@scale * 30).to_i
183
+ a("[");
184
+ a(n);
185
+ a(n);
186
+ a("] 0 setdash\n");
187
+ when LT_DOTTED
188
+ int n = (@scale * 30).to_i
189
+ n2 = n / 4
190
+ a("[");
191
+ a(n2);
192
+ a(n);
193
+ a("] 0 setdash\n");
194
+ else # LT_SOLID
195
+ a("[] 0 setdash\n");
196
+ end
197
+ end
198
+ ret
199
+ end
200
+
201
+ # Draw a polygon
202
+ # @param polygon array of 2n coordinates, defining closed n-gon
203
+ # @param x translation to apply to coordinates
204
+ # @param y
205
+ def draw_polygon(polygon, x, y)
206
+ push(translate(x, y))
207
+ a("NP ")
208
+ i = 0
209
+ while i < polygon.size
210
+ a(polygon[i])
211
+ a(' ');
212
+ a((polygon[i + 1]))
213
+ if (i == 0)
214
+ a(" M ");
215
+ else
216
+ a(" L ");
217
+ end
218
+ i += 2
219
+ end
220
+
221
+ a(" CP S\n")
222
+ pop()
223
+ end
224
+
225
+ # Translate subsequent drawing operations
226
+ def translate(tx,ty,neg=false)
227
+ ret = SOper.null
228
+ if (neg)
229
+ tx = -tx;
230
+ ty = -ty;
231
+ end
232
+ if (tx != 0 || ty != 0)
233
+ ret = SOper.new(TRANS, -tx, -ty)
234
+
235
+ a(tx);
236
+ a(ty);
237
+ a("TR\n");
238
+ end
239
+ ret
240
+ end
241
+
242
+ def set_line_width(w)
243
+ ret = SOper.null
244
+ if @line_width != w
245
+ ret = SOper.new(LINEWIDTH, @line_width)
246
+ @line_width = w
247
+ a(LINEWIDTH_FACTOR * @scale * @line_width)
248
+ a("SLW\n");
249
+ end
250
+ ret
251
+ end
252
+
253
+ def set_rgb(r,g,b)
254
+ ret = SOper.null
255
+ if (r != @rgb[0] || g != @rgb[1] || b != @rgb[2])
256
+ ret = SOper.new(RGB, @rgb[0], @rgb[1], @rgb[2])
257
+ a(r)
258
+ a(g)
259
+ a(b)
260
+ @rgb = [r,g,b]
261
+ a("SRGB\n");
262
+ end
263
+ ret
264
+ end
265
+
266
+ def set_gray(f)
267
+ set_rgb(f,f,f)
268
+ end
269
+
270
+ def set_state( s)
271
+ if (@state != s)
272
+ case s
273
+ when S_OPEN
274
+ raise IllegalStateException if @state != S_START
275
+ @state = s
276
+ print_document_header
277
+ when S_START
278
+ raise IllegalStateException
279
+ end
280
+ @state = s
281
+ end
282
+ end
283
+
284
+ # Start a new page
285
+ # @param page_title title of new page
286
+ def new_page(page_title)
287
+ set_state(S_OPEN);
288
+ flush_page
289
+
290
+ print_page_header(page_title);
291
+ @page_used = true;
292
+ end
293
+
294
+ # Push previous state (returned by an operation) onto a stack to be
295
+ # restored later; call must be balanced by later call to pop()
296
+ # @param obj state object returned by operation such as setGray(), translate()
297
+ def push(obj)
298
+ @stack << obj
299
+ end
300
+
301
+ def pop(count = 1)
302
+ count.times do
303
+ n = @stack.size - 1
304
+ op = @stack.pop
305
+ case op.type
306
+ when RGB
307
+ set_rgb(op.arg(0),op.arg(1),op.arg(2))
308
+ when LINETYPE
309
+ set_line_type(op.arg(0))
310
+ when LINEWIDTH
311
+ set_line_width(op.arg(0))
312
+ when TRANS
313
+ translate(op.arg(0),op.arg(1))
314
+ when FONTHEIGHT
315
+ set_font_size(op.arg(0))
316
+ when SCALE
317
+ set_scale(op.arg(0))
318
+ end
319
+ end
320
+ end
321
+
322
+ private
323
+
324
+ def a(obj)
325
+ if obj.is_a? Numeric
326
+ if obj.is_a? Float
327
+ w = sprintf(" %.2f",obj)
328
+ @s << w
329
+ else
330
+ @s << " #{obj}"
331
+ end
332
+ else
333
+ @s <<' ' << obj
334
+ end
335
+ end
336
+
337
+ def print_page_header(page_title)
338
+
339
+ # set up transformation
340
+ lMargin = @phys_size[0] * 0.07
341
+ lWorkX = @phys_size[0] - 2 * lMargin
342
+ lWorkY = @phys_size[1] - 2 * lMargin;
343
+
344
+ scl = [lWorkX / @document_size[0], lWorkY / @document_size[1]].min
345
+ @scale = scl
346
+ lcx = (@phys_size[0] - scl * @document_size[0]) / 2
347
+ lcy = (@phys_size[1] - scl * @document_size[1]) / 2
348
+
349
+ a(lcx)
350
+ a(lcy)
351
+ a("TR\n");
352
+ a(scl);
353
+ a(scl);
354
+ a("SCL\n\n");
355
+ set_line_width(1);
356
+ set_gray(0);
357
+
358
+ if false
359
+ warn("drawing doc rect");
360
+ draw_rect(0, 0, @document_size[0], @document_size[1], 0)
361
+ end
362
+ set_font_size(24);
363
+
364
+ if (page_title)
365
+ draw_string(page_title, (@document_size[0] / 2), (@document_size[1] + @scaled_font_height * 2))
366
+ end
367
+ end
368
+
369
+ def print_document_header
370
+
371
+ a("%!PS\n");
372
+
373
+ a("/Monaco findfont 28 scalefont setfont\n")
374
+ a(<<"TXT"
375
+ /A {arc} bind def
376
+ /CP {closepath} bind def
377
+ /NP {newpath} bind def
378
+ /TR {translate} bind def
379
+ /SCL {scale} bind def
380
+ /SLW {setlinewidth} bind def
381
+ /SRGB {setrgbcolor} bind def
382
+ /S {stroke} bind def
383
+ /F {fill} bind def
384
+ /M {moveto} bind def
385
+ /L {lineto} bind def
386
+ /R {rmoveto} bind def
387
+ /V {rlineto} bind def
388
+ % Left-justified text
389
+ /TEXTL { currentpoint S M 0 0 R show } def
390
+ % Right-justified text
391
+ /TEXTR { currentpoint S M dup stringwidth pop neg 0 R show } def
392
+ % Centered text
393
+ /TEXTC { currentpoint S M dup stringwidth pop -2 div 0 R show } def
394
+
395
+ % Plot rectangle at x,y,w,h
396
+ /RECT { 3 index 3 index M
397
+ 1 index 0 V
398
+ 0 1 index V
399
+ 1 index neg 0 V
400
+ pop pop pop pop CP
401
+ S } def
402
+ TXT
403
+ )
404
+ end
405
+
406
+ def flush_page
407
+ if @page_used
408
+ @s << "showpage\n\n"
409
+ @page_used = false
410
+ end
411
+ end
412
+
413
+ def make_eps_safe(str)
414
+ w = '('
415
+ str.length.times do |i|
416
+ c = str[i]
417
+ esc = false
418
+ if c.ord < 32
419
+ c = '_'
420
+ end
421
+ case c
422
+ when '(',')'
423
+ esc = true
424
+ end
425
+
426
+ w << '\\' if esc
427
+ w << c
428
+ end
429
+ w << ')'
430
+ w
431
+ end
432
+
433
+ end
434
+
435
+ if __FILE__ == $0
436
+
437
+ poly = [ 0, 0, 50, 30, 30, 80]
438
+
439
+ w = PSWriter.new("_jeff_.ps")
440
+
441
+ w.set_logical_page_size(1000, 1000)
442
+ 10.times do |i|
443
+
444
+ w.new_page("example page #{i}" )
445
+
446
+ w.draw_disc(200, 700, 200 - i * 18)
447
+ w.draw_circle(700, 220, 10 + i * 5);
448
+
449
+ w.push(w.set_gray(0.8));
450
+ w.draw_disc(500, 500, 30 + i * 10);
451
+ w.pop();
452
+ w.push(w.set_line_width(3));
453
+ w.push(w.set_line_dashed());
454
+ w.draw_circle(500, 500, 30 + i * 10);
455
+ w.pop(2);
456
+
457
+ w.draw_line(20 + i * 10, 20, 980, 500 + i * 48);
458
+ w.push(w.set_line_width(1.2 + 0.3 * i));
459
+ w.push(w.translate(500 - i * 40, 500 - i * 40));
460
+ w.push(w.set_scale(1 + i * 0.7));
461
+ w.draw_polygon(poly, 0, 0);
462
+ w.pop(3);
463
+
464
+ w.push(w.set_rgb(1, 0.2, 0.2));
465
+ w.push(w.set_font_size(10 + i * 3));
466
+ w.draw_string("Hello", 10 + i * 30, 900 - i * 40);
467
+ w.pop();
468
+ w.pop();
469
+ end
470
+ w.close();
471
+ end