1 from __future__ import division
2
3 import math
4 import struct
5
6 import numpy as N
7 import stsci.numdisplay as numdisplay
8 import ichar
9
10 """The public functions are the following. For point, rectangle, circle
11 and polyline, arguments shown on separate lines are alternate ways to
12 specify the location and size of the figure to be drawn.
13
14 point (x=x0, y=y0,
15 center=(x0,y0))
16 rectangle (left=x1, right=x2, lower=y1, upper=y2,
17 center=(x0,y0), width=w, height=h)
18 circle (x=x0, y=y0, radius=r,
19 center=(x0,y0), radius=r)
20 polyline (points=[(x1,y1), (x2,y2), (x3,y3), ...],
21 vertices=[(x1,y1), (x2,y2), (x3,y3), ...])
22 undo()
23 set (color, radius)
24
25 color is an optional argument to point, rectangle, circle, and polyline.
26 The allowed values for color are:
27 C_BLACK, C_WHITE, C_RED, C_GREEN, C_BLUE, C_YELLOW, C_CYAN, C_MAGENTA,
28 C_CORAL, C_MAROON, C_ORANGE, C_KHAKI, C_ORCHID, C_TURQUOISE, C_VIOLET, C_WHEAT
29 """
30
31 C_BLACK = 202
32 C_WHITE = 203
33 C_RED = 204
34 C_GREEN = 205
35 C_BLUE = 206
36 C_YELLOW = 207
37 C_CYAN = 208
38 C_MAGENTA = 209
39
40 C_CORAL = 210
41 C_MAROON = 211
42 C_ORANGE = 212
43 C_KHAKI = 213
44 C_ORCHID = 214
45 C_TURQUOISE = 215
46 C_VIOLET = 216
47 C_WHEAT = 217
48
49
50
51 global_save = []
52 global_byte_buf = N.zeros (1, dtype=N.uint8)
53
54
55
56
57
58 global_color = N.array ((C_CYAN,), dtype=N.uint8)
59 global_radius = 3
60
62 """Open the device."""
63 fd = numdisplay.getHandle()
64
65 fd.setFrame(frame_num=frame)
66 (tx, ty, fbwidth, fbheight) = fd.readInfo()
67 return (fd, tx, ty, fbwidth, fbheight)
68
74
75 -def set (color=None, radius=None):
76 """Specify the color or the radius.
77
78 @param color: color code to use for graphic overlays; the allowed
79 values (202..217) are:
80 C_BLACK, C_WHITE, C_RED, C_GREEN, C_BLUE, C_YELLOW, C_CYAN, C_MAGENTA,
81 C_CORAL, C_MAROON, C_ORANGE, C_KHAKI, C_ORCHID, C_TURQUOISE, C_VIOLET,
82 C_WHEAT
83 @type color: int
84 @param radius: radius to use when drawing circles
85 @type radius: int
86 """
87
88 global global_color, global_radius
89
90 if color is not None:
91 global_color = _checkColor (color)
92 if radius is not None:
93 if radius < 0:
94 raise ValueError, "radius must be non-negative"
95 global_radius = radius
96
97
114
116 """Return a valid color.
117
118 @param color: color code to use; if color=None, use default
119 @type color: int
120 """
121
122 global global_color
123
124 if color is None:
125 color = global_color
126 elif color < C_BLACK or color > C_WHEAT:
127 raise ValueError, "%d is not a valid color" % color
128 else:
129 color = N.array ((color,), dtype=N.uint8)
130 return color
131
132 -def _update_save (fd, x, y, list_of_points, last_overlay, undo=True):
133 """Save info in local lists list_of_points and last_overlay.
134
135 @param fd: for reading from image display
136 @type fd: file handle
137 @param x: X coordinate (IIS convention, not image coordinates)
138 @type x: int
139 @param y: Y coordinate (IIS convention, not image coordinates)
140 @type y: int
141 @param list_of_points: pixels that have been written to by the graphic
142 overlay that called this function (updated by this function)
143 @type list_of_points: list of (x,y) tuples
144 @param last_overlay: pixel coordinates and current value of display
145 (updated by this function)
146 @type last_overlay: list of (x,y,value) tuples
147 """
148
149 global global_byte_buf
150 if undo:
151 if (x, y) not in list_of_points:
152 value = fd.readData (x, y, global_byte_buf)
153 value = struct.unpack ('B', value)
154 list_of_points.append ((x, y))
155 last_overlay.append ((x, y, value[0]))
156
158 """Draw a point.
159
160 @param x: image X coordinate of point
161 @type x: int
162 @param y: image Y coordinate of point
163 @type y: int
164 @param center: (x,y) coordinates of point
165 @type center: tuple
166 @param color: color code to use; if not specified, use default
167 @type color: int
168 @param undo: if specified [default=True], keep track of overlays for undo()
169 @type undo: bool
170
171 syntax:
172 overlay.point (x=x0, y=y0)
173 overlay.point (center=(x0,y0))
174 overlay.point (x=x0, y=y0, color=overlay.C_<color>)
175 """
176
177
178
179 global global_save, global_byte_buf
180 last_overlay = []
181 list_of_points = []
182
183 allowed_arguments = ["x", "y", "center", "color", "frame", "undo"]
184 x = None; y = None; center = None; color = None; frame = None; undo = True
185 keys = kwargs.keys()
186 if "center" in keys and ("x" in keys or "y" in keys):
187 raise ValueError, \
188 "Specify either 'center' or 'x' and 'y', but not both."
189 for key in keys:
190 if key in allowed_arguments:
191 if key == "center":
192 (x, y) = kwargs["center"]
193 elif key == "x":
194 x = kwargs["x"]
195 elif key == "y":
196 y = kwargs["y"]
197 elif key == "color":
198 color = kwargs["color"]
199 elif key == "frame":
200 frame = kwargs["frame"]
201 elif key == "undo":
202 undo = kwargs["undo"]
203 else:
204 raise ValueError, \
205 "Invalid argument to 'point'; use 'x', 'y', 'center', 'color', 'frame' or 'undo'."
206 if x is None or y is None:
207 raise ValueError, "You must specify either 'x' and 'y' or 'center'."
208
209 color = _checkColor (color)
210
211 (fd, tx, ty, fbwidth, fbheight) = _open_display(frame=frame)
212
213 (x, y) = _transformPoint (x, y, tx, ty)
214 if x >= 0 and y >= 0 and x < fbwidth and y < fbheight:
215
216 _update_save (fd, x, y, list_of_points, last_overlay,undo=undo)
217
218 fd.writeData (x, y, color)
219
220 global_save.append (last_overlay)
221
222
223
224
226 """Draw a character.
227
228 @param x: image X coordinate of point
229 @type x: int
230 @param y: image Y coordinate of point
231 @type y: int
232 @param mark: character to be drawn
233 @type mark: string
234 @param size: magnification to be used in drawing the character
235 @type size: int
236 @param color: color code to use; if not specified, use default
237 @type color: int
238 @param undo: if specified [default=True], keep track of overlays for undo()
239 @type undo: bool
240
241 syntax:
242 overlay.marker (x=x0, y=y0, mark='+')
243 overlay.marker (x=x0, y=y0, mark='+', size=2)
244 overlay.marker (x=x0, y=y0, mark='+', color=overlay.C_<color>)
245 """
246
247
248
249 global global_save, global_byte_buf
250 last_overlay = []
251 list_of_points = []
252
253 allowed_arguments = ["x", "y", "mark", "color", "frame", "size", "undo"]
254 x = None; y = None; center = None; color = None; frame = None; undo=True
255 keys = kwargs.keys()
256
257 for key in keys:
258 if key in allowed_arguments:
259 if key == "x":
260 x = kwargs["x"]
261 elif key == "y":
262 y = kwargs["y"]
263 elif key == "color":
264 color = kwargs["color"]
265 elif key == "mark":
266 mark = kwargs["mark"]
267 elif key == "size":
268 txsize = kwargs["size"]
269 elif key == "frame":
270 frame = kwargs["frame"]
271 elif key == "undo":
272 undo = kwargs["undo"]
273 else:
274 raise ValueError, \
275 "Invalid argument to 'point'; use 'x', 'y', 'mark', 'size', 'color', 'frame' or 'undo'."
276 if x is None or y is None:
277 raise ValueError, "You must specify 'x' and 'y'."
278
279 color = _checkColor (color)
280
281 (fd, tx, ty, fbwidth, fbheight) = _open_display(frame=frame)
282
283 (x, y) = _transformPoint (x, y, tx, ty)
284
285 idict = ichar.initichar()
286 sprite = idict[mark]
287
288 sprite = ichar.expandchar(sprite, txsize)
289 ixsize = 5*txsize
290 iysize = 7*txsize
291
292 points = sprite
293 npts = len(points[0])
294 for i in range(npts):
295 iy = y - iysize//2 + points[0][i]
296 ix = x - ixsize//2 + points[1][i]
297
298 if ix >= 0 and iy >= 0 and ix < fbwidth and iy < fbheight:
299
300 _update_save (fd, ix, iy, list_of_points, last_overlay,undo=undo)
301
302 fd.writeData (ix, iy, color)
303
304 global_save.append (last_overlay)
305
306
307
308
310 """Draw a rectangle.
311
312 @param left: image X coordinate of left edge
313 @type left: int
314 @param right: image X coordinate of right edge
315 @type right: int
316 @param lower: image Y coordinate of lower edge
317 @type lower: int
318 @param upper: image Y coordinate of upper edge
319 @type upper: int
320 @param center: (x,y) coordinates of middle of rectangle
321 @type center: tuple
322 @param width: width of rectangle (X direction)
323 @type width: int
324 @param height: height of rectangle (Y direction)
325 @type height: int
326 @param color: color code to use; if not specified, use default
327 @type color: int
328 @param undo: if specified [default=True], keep track of overlays for undo()
329 @type undo: bool
330
331 syntax:
332 overlay.rectangle (left=x1, right=x2, lower=y1, upper=y2)
333 overlay.rectangle (center=(x0,y0), width=w, height=h)
334 overlay.rectangle (left=x1, lower=y1, center=(x0,y0))
335 overlay.rectangle (left=x1, lower=y1, width=w, height=h)
336 overlay.rectangle (right=x2, upper=y2, width=w, height=h)
337 overlay.rectangle (right=x2, upper=y2, center=(x0,y0))
338 overlay.rectangle (left=x1, right=x2, lower=y1, upper=y2,
339 color=overlay.C_<color>)
340 """
341
342
343
344 global global_save, global_byte_buf
345 last_overlay = []
346 list_of_points = []
347
348 allowed_arguments = ["left", "right", "lower", "upper",
349 "center", "width", "height", "color", "undo"]
350 x1 = None; x2 = None; y1 = None; y2 = None
351 center = None; width = None; height = None; color = None; undo = True
352
353 error_message = "Invalid argument(s) to 'rectangle'; use one of:\n" \
354 " left=x1, right=x2, lower=y1, upper=y2, or\n" \
355 " center=(x0,y0), width=w, height=h, or\n" \
356 " left=x1, lower=y1, width=w, height=h, or\n" \
357 " right=x2, upper=y2, width=w, height=h\n" \
358 " color and undo may also be specified."
359
360 keys = kwargs.keys()
361 if "center" in keys:
362 if "width" not in keys or "height" not in keys:
363 raise ValueError, error_message
364 if "left" in keys:
365 if "right" not in keys and "width" not in keys:
366 raise ValueError, error_message
367 if "lower" in keys:
368 if "upper" not in keys and "height" not in keys:
369 raise ValueError, error_message
370 if "right" in keys:
371 if "left" not in keys and "width" not in keys:
372 raise ValueError, error_message
373 if "upper" in keys:
374 if "lower" not in keys and "height" not in keys:
375 raise ValueError, error_message
376
377 for key in keys:
378 if key in allowed_arguments:
379 if key == "center":
380 center = kwargs["center"]
381 if not isinstance (center, (list, tuple)):
382 raise ValueError, error_message
383 (x0, y0) = center
384 elif key == "left":
385 x1 = kwargs["left"]
386 elif key == "right":
387 x2 = kwargs["right"]
388 elif key == "lower":
389 y1 = kwargs["lower"]
390 elif key == "upper":
391 y2 = kwargs["upper"]
392 elif key == "width":
393 width = kwargs["width"]
394 elif key == "height":
395 height = kwargs["height"]
396 elif key == "color":
397 color = kwargs["color"]
398 elif key == "undo":
399 undo = kwargs["undo"]
400 else:
401 raise ValueError, error_message
402
403 if x1 is None:
404 if center is not None and width is not None:
405 x1 = int (round (x0 - width/2.))
406 elif x2 is not None and width is not None:
407 x1 = x2 - width
408 if x2 is None:
409 if center is not None and width is not None:
410 x2 = int (round (x0 + width/2.))
411 elif x1 is not None and width is not None:
412 x2 = x1 + width
413 if y1 is None:
414 if center is not None and height is not None:
415 y1 = int (round (y0 - height/2.))
416 elif y2 is not None and height is not None:
417 y1 = y2 - height
418 if y2 is None:
419 if center is not None and height is not None:
420 y2 = int (round (y0 + height/2.))
421 elif y1 is not None and height is not None:
422 y2 = y1 + height
423
424 if x1 is None or x2 is None or y1 is None or y2 is None:
425 raise ValueError, error_message
426
427 color = _checkColor (color)
428
429 (fd, tx, ty, fbwidth, fbheight) = _open_display()
430
431 (x1, y1) = _transformPoint (x1, y1, tx, ty)
432 (x2, y2) = _transformPoint (x2, y2, tx, ty)
433
434 if x2 < x1:
435 (x1, x2) = (x2, x1)
436 if y2 < y1:
437 (y1, y2) = (y2, y1)
438
439 imin = max (0, x1)
440 imax = min (x2+1, fbwidth)
441 if y1 >= 0 and y1 < fbheight:
442 for i in range (imin, imax):
443 _update_save (fd, i, y1, list_of_points, last_overlay, undo=undo)
444 fd.writeData (i, y1, color)
445 if y2 >= 0 and y2 < fbheight:
446 for i in range (imin, imax):
447 _update_save (fd, i, y2, list_of_points, last_overlay, undo=undo)
448 fd.writeData (i, y2, color)
449
450 jmin = max (0, y1)
451 jmax = min (y2+1, fbheight)
452 if x1 >= 0 and x1 < fbwidth:
453 for j in range (jmin, jmax):
454 _update_save (fd, x1, j, list_of_points, last_overlay, undo=undo)
455 fd.writeData (x1, j, color)
456 if x2 >= 0 and x2 < fbwidth:
457 for j in range (jmin, jmax):
458 _update_save (fd, x2, j, list_of_points, last_overlay, undo=undo)
459 fd.writeData (x2, j, color)
460
461 global_save.append (last_overlay)
462
463
464
465
467 """Draw a circle.
468
469 @param x: image X coordinate of center
470 @type x: int
471 @param y: image Y coordinate of center
472 @type y: int
473 @param center: (x,y) coordinates of center
474 @type center: tuple
475 @param radius: radius of circle
476 @type radius: int
477 @param color: color code to use; if not specified, use default
478 @type color: int
479 @param undo: if specified [default=True], keep track of overlays for undo()
480 @type undo: bool
481
482 syntax:
483 overlay.circle (x=x0, y=y0, radius=r)
484 overlay.circle (center=(x0,y0), radius=r)
485 overlay.circle (x=x0, y=y0, radius=r, color=overlay.C_<color>)
486 """
487
488
489
490 global global_save, global_byte_buf
491 last_overlay = []
492 list_of_points = []
493
494 allowed_arguments = ["x", "y", "center", "radius", "color", "frame", "undo"]
495 x0 = None; y0 = None; center = None;
496 radius = global_radius; color = None; frame = None; undo = True
497
498 error_message = "Invalid argument(s) to 'circle'; use either:\n" \
499 " x=x0, y=x0, radius=r, or\n" \
500 " center=(x0,y0), radius=r\n" \
501 " color, frame and undo may also be specified."
502
503 keys = kwargs.keys()
504 if "center" in keys and ("x" in keys or "y" in keys):
505 raise ValueError, error_message
506 for key in keys:
507 if key in allowed_arguments:
508 if key == "center":
509 center = kwargs["center"]
510 if not isinstance (center, (list, tuple)):
511 raise ValueError, error_message
512 (x0, y0) = center
513 elif key == "x":
514 x0 = kwargs["x"]
515 elif key == "y":
516 y0 = kwargs["y"]
517 elif key == "radius":
518 radius = kwargs["radius"]
519 elif key == "color":
520 color = kwargs["color"]
521 elif key == "frame":
522 frame = kwargs["frame"]
523 elif key == "undo":
524 undo = kwargs["undo"]
525 else:
526 raise ValueError, error_message
527 if x0 is None or y0 is None or radius is None:
528 raise ValueError, error_message
529
530 color = _checkColor (color)
531
532 (fd, tx, ty, fbwidth, fbheight) = _open_display(frame=frame)
533
534 (x0, y0) = _transformPoint (x0, y0, tx, ty)
535 quarter = int (math.ceil (radius * math.sqrt (0.5)))
536 r2 = radius**2
537
538 for dy in range (-quarter, quarter+1):
539 dx = math.sqrt (r2 - dy**2)
540 j = int (round (dy + y0))
541 i = int (round (x0 - dx))
542 if i >= 0 and j >= 0 and i < fbwidth and j < fbheight:
543 _update_save (fd, i, j, list_of_points, last_overlay, undo = undo)
544 fd.writeData (i, j, color)
545 i = int (round (x0 + dx))
546 if i >= 0 and j >= 0 and i < fbwidth and j < fbheight:
547 _update_save (fd, i, j, list_of_points, last_overlay, undo = undo)
548 fd.writeData (i, j, color)
549
550 for dx in range (-quarter, quarter+1):
551 dy = math.sqrt (r2 - dx**2)
552 i = int (round (dx + x0))
553 j = int (round (y0 - dy))
554 if i >= 0 and j >= 0 and i < fbwidth and j < fbheight:
555 _update_save (fd, i, j, list_of_points, last_overlay, undo=undo)
556 fd.writeData (i, j, color)
557 j = int (round (y0 + dy))
558 if i >= 0 and j >= 0 and i < fbwidth and j < fbheight:
559 _update_save (fd, i, j, list_of_points, last_overlay, undo=undo)
560 fd.writeData (i, j, color)
561
562 global_save.append (last_overlay)
563
564
565
566
568 """Draw a series of connected line segments.
569
570 @param points: (x,y) points to connect with line segments
571 @type points: list of tuples
572 @param vertices: (x,y) points to connect with line segments
573 @type vertices: list of tuples
574 @param color: color code to use; if not specified, use default
575 @type color: int
576 @param undo: if specified [default=True], keep track of overlays for undo()
577 @type undo: bool
578
579 syntax:
580 overlay.polyline (points=[(x1,y1), (x2,y2), (x3,y3)])
581 overlay.polyline (vertices=[(x1,y1), (x2,y2), (x3,y3)])
582 overlay.polyline (points=[(x1,y1), (x2,y2), (x3,y3)],
583 color=overlay.C_<color>)
584 """
585
586
587
588 global global_save, global_byte_buf
589 last_overlay = []
590 list_of_points = []
591
592 allowed_arguments = ["points", "vertices", "color", "frame", "undo"]
593 points = None; vertices = None; color = None; frame = None; undo=True
594
595 error_message = "Invalid argument(s) to 'polyline'; use either:\n" \
596 " points=[(x1,y1), (x2,y2), (x3,y3), <...>] or\n" \
597 " vertices=[(x1,y1), (x2,y2), (x3,y3), <...>]\n" \
598 " color, frame, or undo may also be specified."
599
600 keys = kwargs.keys()
601 if "points" not in keys and "vertices" not in keys:
602 raise ValueError, error_message
603 for key in keys:
604 if key in allowed_arguments:
605 if key == "points":
606 points = kwargs["points"]
607 if not isinstance (points, (list, tuple)):
608 raise ValueError, error_message
609 elif key == "vertices":
610 vertices = kwargs["vertices"]
611 if not isinstance (vertices, (list, tuple)):
612 raise ValueError, error_message
613 elif key == "color":
614 color = kwargs["color"]
615 elif key == "frame":
616 frame = kwargs["frame"]
617 elif key == "undo":
618 undo = kwargs["undo"]
619 else:
620 raise ValueError, error_message
621
622 if points is not None and vertices is not None:
623 raise ValueError, error_message
624 if vertices is not None:
625 keyword = "vertices"
626 points = vertices
627 else:
628 keyword = "points"
629
630 color = _checkColor (color)
631
632 (fd, tx, ty, fbwidth, fbheight) = _open_display(frame=frame)
633
634 expected_a_tuple = \
635 "Each point in %s for polyline must be a two-element list or tuple,\n" \
636 "giving the X and Y image pixel coordinates of a vertex."
637
638 first = True
639 for point in points:
640 if not isinstance (point, (list, tuple)):
641 raise ValueError, expected_a_tuple
642 (x, y) = point
643 (x, y) = _transformPoint (x, y, tx, ty)
644 if first:
645 (xlast, ylast) = (x, y)
646 first = False
647 continue
648 if x == xlast and y == ylast:
649 continue
650 dx = x - xlast
651 dy = y - ylast
652 (x1, x2, y1, y2) = (xlast, x, ylast, y)
653 if abs (dy) <= abs (dx):
654 if x >= xlast:
655 step = 1
656 else:
657 step = -1
658 slope = float(dy) / float(dx)
659 if step > 0:
660 imin = max (0, x1)
661 imax = min (x2+1, fbwidth)
662 else:
663 imin = min (x1, fbwidth-1)
664 imax = max (x2-1, -1)
665 for i in range (imin, imax, step):
666 j = slope * (i - x1) + y1
667 j = int (round (j))
668 if j >= 0 and j < fbheight:
669 _update_save (fd, i, j, list_of_points, last_overlay, undo=undo)
670 fd.writeData (i, j, color)
671 else:
672 if y >= ylast:
673 step = 1
674 else:
675 step = -1
676 slope = float(dx) / float(dy)
677 if step > 0:
678 jmin = max (0, y1)
679 jmax = min (y2+1, fbheight)
680 else:
681 jmin = min (y1, fbheight-1)
682 jmax = max (y2-1, -1)
683 for j in range (jmin, jmax, step):
684 i = slope * (j - y1) + x1
685 i = int (round (i))
686 if i >= 0 and i < fbwidth:
687 _update_save (fd, i, j, list_of_points, last_overlay, undo=undo)
688 fd.writeData (i, j, color)
689 xlast = x
690 ylast = y
691
692 global_save.append (last_overlay)
693
694
695
696
711
712
713
714