Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations SkipVought on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

How to draw an image using radii? 1

Status
Not open for further replies.

Hemo

Programmer
Apr 9, 2003
190
0
0
US
First.. I'm just getting started in PostScript, any tips or pointers welcomed.


I need to draw an image based on radii I am provided by a source application.

Is there a method by which I can take this stream of data (400 points) and specify the center of the image x,y coordinates, then starting at given angle, draw a dot at each given radius, move to the next given angle, draw a dot at the next given radius.. and so on until completed?

I start drawing along axis 0 and then move .8975 degrees and draw the next radii, until all 400 are drawn and I've moved to angle 359.

I will need to make the image as close to accurate size as possible, but I think I get work that part out later.. I'm just stuck on drawing images based on point data.
 
Not sure I understand completely... look at the "r" operators, meaning rmoveto and rlineto.
 
Yeah.. was afraid I'd make it more complex that it should be. :)

rmoveto I understand... and then I want to draw a single dot at a given point. I don't want lines.

so I could start somehwere.. I do this as an example:

moveto 288 288 % make this the 'center' of my shape
rmoveto 0 300 % first 'dot'
% how to draw a single pixel dot?

I want to plot individual dots, these will make the image.

 
Ok. There isn't a "dot" operator, this is because PostScript is intentionally device-independent. A 300-dpi printer has bigger "dots" than a 1200-dpi printer. RIPs even have different dot shapes.

So what you need to do is decide what shape you want to draw, and how big. You can draw little squares with "rectfill", you can draw little circles with "arc".

You can even use a Symbol font and show single character strings, like a bullet or dash.

Is each position based on the previous position? Or are all positions offset from one original position?

Are you drawing 400 evenly spaced dots around a center?
 
>> Are you drawing 400 evenly spaced dots around a center?

Yes! Each dot is based on a fixed center position.

Think of it as a circle, but not circular in shape.

I will get a stream of 400 radii, and I will evenly space these 400 'points' around a single point, using 360 degrees.

I like the idea of the symbol font and the bullet character!
 
Ok, can you control how the radii are passed in? I would suggest an array of numbers. Then you can use a forall loop to perform a procedure per number in the array.

Is the center position 1) passed in or 2) controlled by you?

Are there always 400 data-points? Or do you want a procedure that counts the number of data-points and calculates the angular offset?

What's all this for? (Just curious!)

 
Here's your program:
Code:
%!PS

/plot
  { /Times-Roman 12 selectfont  % set the fontsize

     center {} forall translate % set the center
     gsave                      % draw the center in red
       1 0 0 setrgbcolor
       0 0 moveto
       (\267) show
     grestore

    /angleDelta 360 dataPoints % calculate evenly spaced angle
    length div angleDir mul def
    
    dataPoints                 
      { % procedure to plot the points
        0 moveto
        (\267) show
        angleDelta rotate
      } forall
    
  } bind def


/center [306 396] def
/dataPoints [72 36 9 18 144] def
/angleDir 1 def  % 1=clockwise, -1=counterclockwise

plot
 
Wow, the formatting went a little off there. Here it is without indents or comments:

%!PS

/plot
{ /Times-Roman 12 selectfont
center {} forall translate
gsave
1 0 0 setrgbcolor
0 0 moveto
(\267) show
grestore

/angleDelta 360 dataPoints
length div angleDir mul def

dataPoints
{
0 moveto
(\267) show
angleDelta rotate
} forall

} bind def

/center [306 396] def
/dataPoints [72 36 9 18 144] def
/angleDir 1 def % 1=clockwise, -1=counterclockwise
plot

 
Oh, and I should warn you: the bullets won't be exactly centered on the "point". Characters are drawn with the current point as the lowerleft corner of the "bounding box", that is, the smallest rectangle that could contain the character.

Let me know how precise you want to be. To demonstrate the problem, use an array of 4 numbers, all the same, with a large pointsize. This ideally should print dots at the compass points. They will be slightly skewed because of this problem.

It's fixable, of course. [smile]
 
Code:
%!PS
% [URL unfurl="true"]http://www.tgreer.com[/URL]
% Here's a version that makes
% perfectly positioned dots,
% no skew from using a bullet character

/plot
  { % set the centerpoint
    center {} forall translate 

    % calculate evenly spaced angle
    /angleDelta 360 dataPoints
    length div angleDir mul def
    
    dataPoints                 
      { % procedure to plot the points
        0 moveto
        currentpoint dotDiameter 0 360 arc
        % use stroke for interesting
        % "vector" effect
        % for a true dot,
        % replace "stroke" with "fill"
        stroke
        angleDelta rotate
      } forall
  } bind def

% data to pass in
/dotDiameter 9 def % 18 point dot
/center [306 396] def
/dataPoints [72 72 72 72] def
/angleDir 1 def % 1=clockwise, -1=counterclockwise

plot
 
wow.

W O W.

the concept is perfect. I'll have to play with data a bit to get the shape more life-size, since my original data is in mm, not pixels.

You asked what this is for? We aquire data from an instrument that traces the outside or inside shape and measurement of an item and then dumps that data to us. I want to take that data and draw an image to our paper output of other information. (No, it's not ahoemwork assignment :)

I will most likely pick a centerpoint on the paper for the image, the center point will most likely stay at that point, there is always 400 data point, and I can create the array on the fly from the data stream coming from another interface.

You have no idea how exciting this is to see this idea on paper like this. (well, perhaps you do)
 
Happy to help! One refinement you might need to add is line width.

You can pass in another parameter such as: "/lineWeight .25 def". Then in the "plot" procedure, do a "lineWeight setlinewidth".

Keep in mind, PostScript does NOT deal in "pixels". The unit is "points", 72 points per inch. This is an important distinction, since every device has it's own pixel shapes and sizes.

You can also switch to millimeters, since the PostScript coordinate system can be scaled. Points are smaller than millimeters. If I recall, you should do a "2.845 2.845 scale" to make your units=millimeters. Do this at the top of your program.


Thomas D. Greer
 
Code:
%!PS
% Thomas D. Greer
% [URL unfurl="true"]www.tgreer.com[/URL]
%
% As a personal challenge,
% I added the ability
% to "connect-the-dots"
% with a thin red line.
% This required a lot of 
% trigonometry, with the need
% to check/correct obtuse angles.
% It was fun, you're welcome to
% use it.
%
% Just remember who to send your
% friends to for PostScript work!

/drawLine
  { 
    /hyp d1 dup mul d2 dup mul add 
    2 d1 mul d2 mul angleDelta cos mul
    sub sqrt def
  
    /A angleDelta sin hyp div d2 mul
      dup 1 eq
        {90}
        {dup dup mul 1 sub neg sqrt div 1 atan}
      ifelse
    def
    
    /B angleDelta sin hyp div d1 mul
      dup 1 eq
        {90}
        {dup dup mul 1 sub neg sqrt div 1 atan}
      ifelse
    def
       
    A B gt {/A 180 angleDelta sub B sub def} if
    
    /x hyp 90 A sub sin mul neg def
    /y hyp A sin mul def 
    
    d1 0 moveto x y rlineto stroke
    angleDelta rotate
    
  } bind def


/plot
  { lineWeight setlinewidth
    center {} forall translate 

    /angleDelta 360 dataPoints length div def
    
    dataPoints                 
      { 0 moveto
        currentpoint dotDiameter 0 360 arc
        fill
        angleDelta rotate
      } forall

    1 0 0 setrgbcolor
    dataPoints dup dup length 
    1 1 3 -1 roll
    { 
      dup dataPoints length eq
      { pop 0 dataPoints length 1 sub }
      { dup 1 sub } ifelse
      3 -2 roll get
      /d2 exch def
      get /d1 exch def
      drawLine
      dataPoints dup
    } for
    pop pop

  } bind def

/lineWeight .25 def
/dotDiameter .5 def 
/center [306 396] def
/dataPoints
  [
    100 250 100 100 100 100
    100 250 100 100 100 100
    100 250 100 100 100 100
    100 250 100 100 100 100
    100 250 100 100 100 100
    100 250 100 100 100 100
    100 250 100 100 100 100
    100 250 100 100 100 100
    100 250 100 100 100 100
    100 250 100 100 100 100
    100 250 100 100 100 100
    100 250 100 100 100 100
    100 250 100 100 100 100
    100 250 100 100 100 100
  ] def

plot

Thomas D. Greer
 
Code:
%!PS
% Thomas D. Greer
% [URL unfurl="true"]www.tgreer.com[/URL]
% corrected some weird trig problems
% caused by rounding errors in PostScript
% Failure would occur on 360th datapoint

/drawLine
  { 
    /hyp d1 dup mul d2 dup mul add 
    2 d1 mul d2 mul angleDelta cos mul
    sub sqrt def
  
    /A angleDelta sin hyp div d2 mul
      dup 1 eq 
        {90}
        {dup dup mul 1 sub abs sqrt div 1 atan}
      ifelse
    def
    
    /B angleDelta sin hyp div d1 mul
      dup 1 eq
        {90}
        {dup dup mul 1 sub abs sqrt div 1 atan}
      ifelse
    def
       
    A B gt {/A 180 angleDelta sub B sub def} if
    
    /x hyp 90 A sub sin mul neg def
    /y hyp A sin mul def 
    
    d1 0 moveto x y rlineto stroke
    angleDelta rotate
    
  } bind def


/plot
  { lineWeight setlinewidth
    center {} forall translate 

    /angleDelta 360 dataPoints length div def
    
    dataPoints                 
      { 0 moveto
        currentpoint dotDiameter 0 360 arc
        fill
        angleDelta rotate
      } forall

    1 0 0 setrgbcolor
    dataPoints dup dup length 
    1 1 3 -1 roll
    { 
      dup dataPoints length eq
      { pop 0 dataPoints length 1 sub }
      { dup 1 sub } ifelse
      3 -2 roll get
      /d2 exch def
      get /d1 exch def
      drawLine
      dataPoints dup
    } for
    pop pop

  } bind def

/lineWeight .2 def
/dotDiameter .25 def 
/center [306 396] def
/dataPoints
  [
    95 100 100 100 100 122 100 100 100 100
    95 100 100 100 100 122 100 100 100 100
    95 100 100 100 100 122 100 100 100 100
    95 100 100 100 100 122 100 100 100 100
    95 100 100 100 100 122 100 100 100 100
    95 100 100 100 100 122 100 100 100 100
    95 100 100 100 100 122 100 100 100 100
    95 100 100 100 100 122 100 100 100 100
    95 100 100 100 100 122 100 100 100 100
    95 100 100 100 100 122 100 100 100 100  %100

    95 100 100 100 100 122 100 100 100 100
    95 100 100 100 100 122 100 100 100 100
    95 100 100 100 100 122 100 100 100 100
    95 100 105 100 100 122 100 100 100 100
    95 100 100 100 100 122 100 100 100 100
    95 100 100 100 100 122 100 100 100 100
    95 100 100 100 100 122 100 100 100 100
    95 100 100 100 100 122 100 100 100 100
    95 100 100 100 100 122 100 100 100 100
    95 100 100 100 100 122 100 100 100 100 %200

    95 100 100 100 100 122 100 100 100 100
    95 100 100 100 100 122 100 100 100 100
    95 100 100 100 100 122 100 100 100 100
    95 100 100 100 100 122 100 100 100 100
    95 100 100 100 100 122 100 100 100 100
    95 100 100 100 100 122 100 100 100 100
    95 100 100 100 100 122 100 100 100 100
    95 100 100 100 100 122 100 100 100 100
    95 100 100 100 100 122 100 100 100 100
    95 100 100 100 100 122 100 100 100 100 %300

    95 100 100 100 100 122 100 100 100 100
    95 100 100 100 100 122 100 100 100 100
    95 100 100 100 100 122 100 250 100 100
    95 100 100 100 100 122 100 100 100 100
    95 100 100 100 100 122 100 100 100 100
    95 100 100 100 100 122 100 100 100 100
    95 100 100 100 100 122 100 100 100 100
    95 100 100 100 100 122 100 100 100 100
    95 100 100 100 100 122 100 100 100 100
    95 100 100 100 100 122 100 100 100 100 %400

  ] def

plot

Thomas D. Greer
 
Thomas, here's an example using real data. I added a cross to the center of the image. I will also be mirroring this image, I thought I'd reverse the radii points and re-plot, or is there a way I can read datapoints from end to start instead?

Code:
%!PS
% [URL unfurl="true"]http://www.tgreer.com[/URL]
% Here's a version that makes
% perfectly positioned dots,
% no skew from using a bullet character

2.845 2.845 scale	%set scale to mm

/plot
  { % set the centerpoint
    center {} forall translate

    % calculate evenly spaced angle
    /angleDelta 360 dataPoints
    length div angleDir mul def
    
    dataPoints                 
      { % procedure to plot the points
        0 moveto
        currentpoint dotDiameter 0 360 arc
        % use stroke for interesting
        % "vector" effect
        % for a true dot, use 'fill'
        fill
        angleDelta rotate
      } forall
  } bind def

% data to pass in
/dataPoints 
    [
	25.45 25.50 25.55 25.60 25.65 25.69 25.73 25.78 25.81 25.84
	25.87 25.89 25.92 25.94 25.95 25.96 25.95 25.95 25.94 25.93
	25.91 25.88 25.86 25.83 25.80 25.77 25.74 25.70 25.66 25.61
	25.56 25.51 25.46 25.40 25.33 25.26 25.18 25.10 25.02 24.95
	24.86 24.76 24.66 24.57 24.48 24.38 24.27 24.17 24.07 23.96
	23.86 23.75 23.65 23.54 23.42 23.31 23.21 23.10 22.99 22.90
	22.80 22.71 22.61 22.50 22.41 22.32 22.24 22.14 22.05 21.96
	21.88 21.79 21.71 21.62 21.54 21.47 21.40 21.32 21.24 21.17
	21.11 21.05 21.00 20.94 20.88 20.82 20.78 20.74 20.70 20.67
	20.63 20.61 20.58 20.56 20.54 20.52 20.51 20.49 20.49 20.48
	20.48 20.48 20.47 20.48 20.48 20.49 20.49 20.49 20.50 20.51
	20.54 20.56 20.60 20.63 20.66 20.69 20.72 20.75 20.77 20.81
	20.86 20.91 20.96 21.01 21.07 21.14 21.20 21.25 21.33 21.40
	21.47 21.55 21.64 21.72 21.81 21.88 21.97 22.07 22.17 22.27
	22.36 22.46 22.56 22.67 22.77 22.88 22.99 23.10 23.21 23.32
	23.44 23.57 23.69 23.80 23.91 24.02 24.14 24.26 24.38 24.49
	24.60 24.71 24.82 24.94 25.04 25.14 25.25 25.36 25.45 25.54
	25.63 25.72 25.79 25.84 25.91 25.96 25.96 25.89 25.82 25.82
	25.86 25.89 25.91 25.93 25.94 25.94 25.94 25.93 25.91 25.89
	25.87 25.84 25.81 25.77 25.73 25.70 25.66 25.62 25.58 25.54
	25.50 25.47 25.43 25.38 25.34 25.30 25.25 25.20 25.16 25.12
	25.07 25.02 24.98 24.94 24.88 24.82 24.76 24.71 24.65 24.59
	24.55 24.51 24.46 24.39 24.33 24.26 24.21 24.16 24.10 24.03
	23.98 23.95 23.88 23.81 23.75 23.69 23.64 23.56 23.51 23.46
	23.40 23.34 23.27 23.20 23.14 23.07 23.01 22.95 22.90 22.85
	22.78 22.71 22.64 22.59 22.52 22.46 22.39 22.34 22.28 22.22
	22.15 22.09 22.01 21.96 21.90 21.84 21.78 21.72 21.65 21.59
	21.52 21.46 21.41 21.37 21.31 21.26 21.21 21.16 21.11 21.06
	21.01 20.97 20.93 20.89 20.86 20.82 20.79 20.76 20.72 20.69
	20.65 20.62 20.60 20.57 20.56 20.53 20.51 20.49 20.48 20.46
	20.45 20.44 20.43 20.43 20.41 20.41 20.41 20.41 20.41 20.41
	20.42 20.43 20.44 20.45 20.46 20.48 20.51 20.53 20.55 20.56
	20.58 20.61 20.64 20.68 20.71 20.75 20.78 20.82 20.85 20.89
	20.94 20.99 21.04 21.08 21.14 21.20 21.26 21.32 21.38 21.44
	21.50 21.56 21.63 21.69 21.76 21.83 21.89 21.96 22.02 22.08
	22.15 22.23 22.31 22.37 22.43 22.50 22.57 22.63 22.70 22.77
	22.84 22.90 22.98 23.05 23.12 23.18 23.25 23.32 23.40 23.45
	23.52 23.58 23.65 23.72 23.78 23.85 23.91 23.98 24.04 24.10
	24.17 24.24 24.30 24.37 24.43 24.50 24.56 24.63 24.70 24.78
	24.85 24.91 24.98 25.04 25.11 25.17 25.23 25.29 25.34 25.39
    ] def
/dotDiameter .4 def
/center [100 100] def
/angleDir 1 def % 1=clockwise, -1=counterclockwise
/lineWeight .2 def

lineWeight setlinewidth
95 100 moveto 15 0 rlineto stroke % Horizontal intentially longer on one side
100 95 moveto 0 10 rlineto stroke % Vertical even length from center
plot
showpage
 
You can reverse the contents of an array by several methods. The techniques aren't much different than you'd use in any other language.

For example, create an empty 400 element array ("array" operator), a for or forall loop, an index or counter, and for every element in the first array, copy it into the second array, counting backwards.

Thomas D. Greer
 
cool. Got the array reading in backwards so I can do mirrored images. Having a problem doing anything after calling the plot routine, though.

This order looks good:

Code:
95 100 moveto 15 0 rlineto stroke % Horizontal intentially longer on one side
100 95 moveto 0 10 rlineto stroke % Vertical even length from center
100 100 35 0 360 arc stroke % 70mm circle
plot

but this order is not...:
Code:
plot
95 100 moveto 15 0 rlineto stroke % Horizontal intentially longer on one side
100 95 moveto 0 10 rlineto stroke % Vertical even length from center
100 100 35 0 360 arc stroke % 70mm circle

again, not understanding some of this, but the
Code:
/center [100 100]
line along with the line
Code:
center {} forall translate
seems to get me in trouble. Even if I call plot and then issue a command
Code:
0 0 translate
my reference point is lost, so if do any moveto commands after calling plot, they aren't relative to the bottom left of my page anylonger.

Am I even close to grasping this? I'm gonna see if my boss won't send me your way for training pertty soon.. ;)
 
Great! Actually, I would come YOUR way for training... but we can talk about that offline. Email me.

PostScript uses a "current transformation matrix", or CTM for short. This is a combination of the numbers used for all rotates, scales, and translates. Every time you do a rotate, translate, or scale, it puts the numbers you provide into a matrix, multiplies it against the current CTM, and so generates a new CTM.

The "plot" procedure does a LOT of rotates, massive changes to the CTM. So when you call it AGAIN, who knows where you're current-point is in relation to the page? The procedure is likely working, but it's drawing your stuff way off the page somewhere.

You need to protect the CTM if you're calling "plot" more than once. You do this with a save/restore pair:

% set your array, other parameters, etc.
save plot restore

% next set
save plot restore

%next, etc
save plot restore

Save "saves" everything, including the CTM. plot does what it does... restore "restores" everything, including the CTM, to the last "save".

You can modify the plot procedure itself to do this for you, too. Just make the first thing it does a "save", the last thing, "restore".



Thomas D. Greer
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top