views:

267

answers:

4

In the same thread as this question, I am giving this another shot and ask SO to help address how I should take care of this problem. I'm writing a bash script which needs to perform the following:

  1. I have a circle in x and y with radius r.
  2. I specify resolution which is the distance between points I'm checking.
  3. I need to loop over x and y (from -r to r) and check if the current (x,y) is in the circle, but I loop over discrete i and j instead.
  4. Then i and j need to go from -r/resolution to +r/resolution.
  5. In the loop, what will need to happen is echo "some_text i*resolution j*resolution 15.95 cm" (note lack of $'s because I'm clueless). This output is what I'm really looking for.

My best shot so far:

r=40.5
resolution=2.5
end=$(echo "scale=0;$r/$resolution") | bc

for (( i=-end; i<=end; i++ ));do
    for (( j=-end; j<=end; j++ ));do
        x=$(echo "scale=5;$i*$resolution") | bc
        y=$(echo "scale=5;$j*$resolution") | bc
        if (( x*x + y*y <= r*r ));then      <-- No, r*r will not work
            echo "some_text i*resolution j*resolution 15.95 cm"
        fi
    done
done

I've had just about enough with bash and may look into ksh like was suggested by someone in my last question, but if anyone knows a proper way to execute this, please let me know! What ever the solution to this, it will set my future temperament towards bash scripting for sure.

+1  A: 

You may want to include the pipe into bc in the $()'s. Instead of.

end=$(echo "scale=0;$r/$resolution") | bc

use

end=$(echo "scale=0;$r/$resolution" | bc)

should help a bit.

EDIT And here's a solution.

r=40.5
resolution=2.5
end=$(echo "scale=0;$r/$resolution" | bc)

for i in $(seq -${end} ${end}); do
    for j in $(seq -${end} ${end}); do
        x=$(echo "scale=5;$i*$resolution" | bc)
        y=$(echo "scale=5;$j*$resolution" | bc)

        check=$(echo "($x^2+$y^2)<=$r^2" | bc)    
        if [ ${check} -eq '1' ]; then
            iRes=$(echo "$i*$resolution" | bc)
            jRes=$(echo "$j*$resolution" | bc)      
            echo "some_text $iRes $jRes 15.95 cm"
        fi
    done
done
Adam Luchjenbroers
Adam, the final echo prints nothing but literally "some_text i*resolution j*resolution 15.95 cm"!
vgm64
Correction added, though you may want to change the scale for the iRes / jRes equations depending on your requirements.
Adam Luchjenbroers
Thanks. On my Mac the use of seq had to be replaced with x=-end;x<end kind of thing. Also, the if statement should have [[ ... ]] instead of single brackets.
vgm64
On Macs (and other BSDs?) there's the `jot` command which can be used instead of `seq`. The argument syntax is a little different.
Dennis Williamson
A: 

you haven't heard of (g)awk ??. then you should go learn about it. It will benefit you for the long run. Translation of your bash script to awk.

awk 'BEGIN{
    r=40.5
    resol=2.5
    end = r/resol
    print end
    for (i=-end;i<=end;i++) {
        for( j=-end;j<=end;j++ ){
            x=sprintf("%.5d",i*resol)
            y=sprintf("%.5d",j*resol)
            if ( x*x + y*y <= r*r  ){
                print ".......blah blah ......"
            }
        }    
    }
}'
A: 

It's looking more like a bc script than a Bash one any way, so here goes:

#!/usr/bin/bc -q
/* -q suppresses a welcome banner - GNU extension? */ 
r = 40.5
resolution = 2.5

scale = 0
end = r / resolution

scale = 5

for ( i = -end; i <= end; i++ ) {
    /* moved x outside the j loop since it only changes with i */
    x = i * resolution
    for ( j = -end; j <= end; j++ ) {
        y = j * resolution
        if ( x^2 * y^2 <= r^2 ) {
            /*
               the next few lines output on separate lines, the quote on 
               a line by itself causes a newline to be created in the output
               numeric output includes newlines automatically
               you can comment this out and uncomment the print statement
               to use it which is a GNU extension
            */
            /* */
            "some_text
"
            i * resolution
            j * resolution
            "15.95 cm
"
            /* */
            /* non-POSIX: 
            print "some_text ", i * resolution, " ", j * resolution, " 15.95 cm\n"
            */
        }
    }
}
quit
Dennis Williamson
+1  A: 

As already mentioned this problem is probably best solved using bc, awk, ksh or another scripting language.

Pure Bash. Simple problems which actually need floating point arithmetic sometimes can be transposed to some sort of fixed point arithmetic using only integers. The following solution simulates 2 decimal places after the decimal point. There is no need for pipes and external processes inside the loops if this precision is sufficient.

factor=100                                      # 2 digits after the decimal point
r=4050                                          # the representation of 40.50
resolution=250                                  # the representation of  2.50
end=$(( (r/resolution)*factor ))                # correct the result of the division

for (( i=-end; i<=end; i+=factor )); do
    for (( j=-end; j<=end; j+=factor )); do
        x=$(( (i*resolution)/factor ))          # correct the result of the division
        y=$(( (j*resolution)/factor ))          # correct the result of the division 
        if [ $(( x*x + y*y )) -le $(( r*r )) ] ;then     # no correction needed
         echo "$x $y ... "
        fi
    done
done

echo -e "resolution = $((resolution/factor)).$((resolution%factor))"
echo -e "r          = $((r/factor)).$((r%factor))"
fgm
The script I'm write actual relies on user specified units, so I suppose this actually could work quite well if I specify nanometers!
vgm64