1
votes

I am new to Tcl arrays. My question is as follows.

I have a rectangle box with two rows R1 and R2. Each of these rows has 8 different values. I want to return these 16 values (x and y coordinates) either in a text file or as an list output from a proc. I read some earlier posts about Tcl proc cannot output an array unless we use dict. So, I will try to draw a picture so u can understand my question better.

R1  x1y1    x2y2        ... x8,y8
R2  x9,y9       ...     x16, y16

Expected output when I run the proc either on command prompt or in a file with dummy values as an example

$>    (1,2)  (2,3) (3,4) ....... (7,8)
      (9,10) (10,11) ......... (15,16)

So this is what I tried and I am getting the results that I need. But this is hardcoded for two rows. I want to make it able to detect how many rows are there and then accordingly output the number of rows.

proc getPointList {rect_boundary rowOffset colOffset rowIncr colIncr } {
 set cordlist $rect_boundary

 set xl  [lindex $cordlist 0]
 set yl  [lindex $cordlist 1]
 set xh  [lindex $cordlist 2]      
 set yh  [lindex $cordlist 3]

 set list "" ;

 for {set y [expr {$yh - $colOffset}]} {$y >= [expr {$yl + $colOffset}]} { incr y $colIncr } {

    for {set x [expr {$xl + $rowOffset}]} {$x <= [expr {$xh - $rowOffset}]} { incr x $rowIncr } {

            set list "$list $x $y" ;
            puts "Value of x is: $x"; puts "\t Value of y is: $y" ;  
        } 
 }
return $list  
}

set rect_boundary {10 15 100 40}     # xl yl xh yh
set rowOffset 5
set colOffset 5
set rowIncr 10
set colIncr 15

Some Logic I need to implement in this code based on yh-yl and xh-xl to calculate height and width of rectangle and accordingly output rows

Command to call the proc

$> getPointList $rect_boundary $rowOffset $colOffset $rowIncr $colIncr

Just for your understanding there are eight x,y points inside the rectangle on a particular row. x offset is the first x point on a row from the left or roght boundary, thereafter all the points are separated by an increment value which I call rowIncr. Same holds true for column.

Expected output : This is what the above code does but it is hardcoded for two rows. I want to increase and implement the logic if the rows and column are variable.

$>  R1:  (15 40) (25 40) (35 40) (45 40) (55 40) (65 40) (75 40) (85 40) (95 40)
    R2:  (15 15) (25 15) (35 15) (45 15) (55 15) (65 15) (75 15) (85 15) (95 15)

Rectangle Image for better clarity as this thing wont let me update pictures

__________________________________________________________________________ (100,40)
|                       |- 5                                              |
|   .          .        .       .        .       .        .        .      |
|                               |- 15                                     |
|-5-.          . --10---.       .        .       .        .        .      |
|                                                                         |
|_________________________________________________________________________|
(10,15)    

For Jerry:

Case1  rowIncr 10 colIncr 20
__________________________________________________________________________ (80,40)
|                       |- 5                                              |
|   .          .        .       .        .       .        .        .      |
|                               |- 20                                     |
|-5-.          . --10---.       .        .       .        .        .      |
|                                                                         |
|_________________________________________________________________________|
(10,10)

Case2   rowIncr 20 colIncr 35
_________________________________________________ (100,70)
|                       |- 5                     |
|   .          .        .       .        .       |
|                               |- 35            |
|-5-.          . --20---.       .        .       |
|              |                         |-5     |
|________________________________________________|
(10,25)

and so on ...

3
I understand that you want to extract the coordinates from the rectangle? Are the coordinates separated by a tab character and each x and y coordinate separated by comma?Jerry
Hi Jerry: I only need the xy coordinates from inside the rectangle. I With or without comma separated value is not a question. Once I am able to do that I can use split to spearate the xy values and remove the comma too. Comma used in my question is an example, if it's not there then better.user2643899
Hi, could you also put a sample input with all the numbers and how you're calling the procedure? I'm having trouble understanding how is your actual input and required output with this proc... :(Jerry
We would like to help, so please help us by showing us what the input and expected output looks like, how you read the input, pass it to your function. You stated your input looks like 8 numbers per row, but your proc only takes in 5--the first is a list of 4--very confusing.Hai Vu
Hai Vu and Jerry: proc takes arguments, i will put in my question how to call the proc and sample outputuser2643899

3 Answers

1
votes

Okay, I think I now understand what you were trying to do, and I think that your proc would have worked for any number of rows after some fixing:

set output [open "output.txt" w]

proc getPointList {rect_boundary rowOffset colOffset plist} {
    global output

    set cordlist $rect_boundary
    set xl  [lindex $cordlist 0]
    set yl  [lindex $cordlist 1]
    set xh  [lindex $cordlist 2]      
    set yh  [lindex $cordlist 3]

    set xpoints [llength [lindex $plist 0]]
    set ypoints [llength $plist]
    set rowIncr [expr {($xh-$xl-2*$rowOffset)/($xpoints-1)}]
    set colIncr [expr {($yh-$yl-2*$colOffset)/($ypoints-1)}]

    set count 0
    set list ""

    for {set y [expr {$yh - $colOffset}]} {$y >= [expr {$yl + $colOffset}]} {incr y -$colIncr} {

        for {set x [expr {$xl + $rowOffset}]} {$x <= [expr {$xh - $rowOffset}]} {incr x $rowIncr} {
            lappend list "($x,$y)"
        }
        incr count
        puts $output "R$count: [join $list " "]"
        set list ""
    }
}

set plist {{A B C D E} {A B C D E} {A B C D E} {A B C D E} {A B C D E}}
set rect_boundary {0 0 100 100}
set rowOffset 0
set colOffset 0

getPointList $rect_boundary $rowOffset $colOffset $plist

close $output

I changed the colIncr to put more rows.

In the first loop, I used incr y -$colIncr because this is actually a decrement if you start with the higher y coordinate.

I also changed the output structure to match the one you were looking for. The above snippet returns the coordinates:

R1: (0,100) (25,100) (50,100) (75,100) (100,100) 
R2: (0,75) (25,75) (50,75) (75,75) (100,75) 
R3: (0,50) (25,50) (50,50) (75,50) (100,50) 
R4: (0,25) (25,25) (50,25) (75,25) (100,25) 
R5: (0,0) (25,0) (50,0) (75,0) (100,0)

EDIT: Added variable offsets, blank final row and variable columns per row.

proc getPointList {rect_boundary uRowOffset lRowOffset uColOffset lColOffset plist} {
    set cordlist $rect_boundary
    set xl  [lindex $cordlist 0]
    set yl  [lindex $cordlist 1]
    set xh  [lindex $cordlist 2]      
    set yh  [lindex $cordlist 3]

    set xpoints 0
    foreach r $plist {
        if {[llength $r] > $xpoints} {set xpoints [llength $r]}
    }

    set ypoints [llength $plist]
    set rowIncr [expr {($xh-$xl-$lRowOffset-$uRowOffset)/($xpoints-1)}]
    set colIncr [expr {($yh-$yl-$lColOffset-$uColOffset)/$ypoints}]

    set count 0
    set list ""

    for {set y [expr {$yh - $uColOffset}]} {$y >= [expr {$yl + $lColOffset}]} {incr y -$colIncr} {
        set x [expr {$xl + $lRowOffset}]
        foreach n [lindex $plist $count] {
            lappend list $x $y
            incr x $rowIncr
        }
        incr count
        if {$count == $ypoints} {return $list}
    }
}

set plist {{A B C D X} {E F G H} {I K L} {M N}}
set qlist 1
foreach n $plist {
    set pattern$plist $n
    incr qlist
}

set rect_boundary {0 0 100 100}
set upperRowOffset 0
set lowerRowOffset 0
set upperColOffset 0
set lowerColOffset 0

set pointList [getPointList $rect_boundary $upperRowOffset $lowerRowOffset $upperColOffset $lowerColOffset $plist]

set count 1
foreach sub_list $plist {
    foreach n $sub_list {
        set pattern$count $n
        incr count
    }
}

set count 1
foreach {a b} $pointList {
    set text "pattern$count"
    puts "command -point $a,$b -text [set $text]"
    incr count 
}
0
votes

It is up to you how to organize the nested lists. In simplest form, return a single list:

set result {x1 y1 x2 y2 ... x16 y16}

Or, you can have a list of two rows:

set result {
    {x1 y1 x2 y2 ... x8 y8}
    {x9 y9 x10 y10 ... x16 y16}
}

Or, more complex: each pair is a sub-list:

set result {
    { {x1 y1} {x2 y2} ... }
    { {x9 y9} {x10 y10} ... }
}

I don't think you want to use array in this case. FYI, an "array" in TCL is equivalent to a hash in other languages.

0
votes

Well, with Tcl 8.6

proc parsedata {input} {
    lmap a [split $input \n] {
        lmap b [split $b \t] {
           regexp {x(\d+),?\s*y(\d+)} $b -> c d
           list $c $d
        }
    }
}

Now you can process the data, for example:

foreach line [parsedata $input] {
    puts [lmap pair $line {
        expr {"([lindex $line 0],[linedex $line 1])"}
    }
}