efficient grid lookup by coordinate in Tcl - tcl

I'm trying to implement a simple grid lookup in Tcl. You can think of the grid items as boxes within a grid. Something like the following.
I have the x and y coordinates of the boundaries (left, right , top bottom) of each of the blue boxes within the coordinate space shown in a dictionary called boxcoordinates
given an arbitrary X and Y point, what is the most efficient way to identify which (if any) of the blue boxes are intercepted by the X,Y pair?
I'm currently doing a check for each box, on the conditions where Left < X < Right and Bottom < Y < Top to see which box satisfies those conditions.
Something like
foreach boxid [dict keys boxcoordinates] {
if {([dict get $boxcoordinates $boxid LEFT] < $x) && ([dict get $boxcoordinates $boxid RIGHT] > $x) && ([dict get $boxcoordinates $boxid BOTTOM] < $y) && ([dict get $boxcoordinates $boxid TOP] > $y)} {
set selected $boxid
break
}
}
But that seems very inefficient since there are a lot of boxes to scan through. Is there a more efficient way to do this?

If you sort the coordinate list in a regular fashion, you can do a binary search to find the coordinates you are looking for. The example below only has 9 entries, but should give you the idea. The coordinates used in this exampled are ordered as x1, x2, y1, y2.
global vars
proc init { } {
global vars
set vars(d) {
0 {1 4 1 4}
1 {1 4 6 8}
2 {1 4 10 12}
3 {6 8 1 4}
4 {6 8 6 8}
5 {6 8 10 12}
6 {10 12 1 4}
7 {10 12 6 8}
8 {10 12 10 12}
}
}
proc lCompare { a b } {
lassign $a ax1 ax2 ay1 ay2
lassign $b bx1 bx2 by1 by2
if { $bx1 < $ax1 } {
return -1
} elseif { $bx2 > $ax2 } {
return 1
} elseif { $by1 < $ay1 } {
return -1
} elseif { $by2 > $ay2 } {
return 1
}
return 0
}
proc bsearch { mx my } {
global vars
set target [list $mx $mx $my $my]
set low 0
set high [expr {[dict size $vars(d)]-1}]
while { $low <= $high } {
set mid [expr {($low+$high)/2}] ; # integer division
set lrc [lCompare [dict get $vars(d) $mid] $target]
if { $lrc > 0 } {
set low [expr {$mid+1}]
} elseif { $lrc < 0 } {
set high [expr {$mid-1}]
} else {
return $mid
}
}
return -1
}
init
set idx [bsearch 3 10]
puts "A:$idx"
set idx [bsearch 10 10]
puts "B:$idx"
set idx [bsearch 3 3]
puts "C:$idx"
set idx [bsearch 5 9]
puts "D:$idx"
Output:
bll-tecra:bll$ tclsh z.tcl
A:2
B:8
C:0
D:-1
References: wikipedia: Binary Search Algorithm

Related

Does TCL containg built in functions of type get_bits and set_bits?

The get_bits will return specific bits of a value and set_bits will set specific bits of a value to a specified value. Does TCL contain such functions built in or should they be written by the user?
The binary scan command does come close to the get_bits function but is not the same thing.
There's no specific function for getting or setting a particular bit. We can make them.
proc get_bit {value bit} {
expr {($value & (1 << $bit)) != 0}
}
proc set_bit {varName bit {value 1}} {
upvar 1 $varName var
if {$value} {
set var [expr {$var | (1 << $bit)}]
} else {
set var [expr {$var & ~(1 << $bit)}]
}
}
Those will work with integer values of any width; you're not restricted to 32 bits or 64 bits.
# Lots of bits!
set x 123456789012345678901234567890
# Fetch a particular bit
puts [get_bit $x 17]
# Set a bit to 1
set_bit x 78
puts "x = $x"
# Set a bit to 0
set_bit x 75 0
puts "x = $x"

finding difference between list elements in Tcl

I am a beginner in using Tcl. I am using it as VMD (molecular visualization) software uses it as a scripting language.
I have a list of co-ordinates of atom positions for a protein like: {{1 2 3} {7 9 13}, ...} I have a separate list of the same length with different positions say: {{3 5 2} {7 3 8}, ...}.
VMD has an inbuilt vecsub function which can subtract {1 2 3} and {3 5 2} to give {-2 -3 1}. I have written a foreach loop to iterate on the entire list and calculate vecsub.
My code is as follows:\
set sel1 [atomselect 0 "protein"] # selecting protein1
set sel2 [atomselect 1 "protein"] # selecting protein2
# create a list of same length as protein to store values
# $sel1 get index returns length of protein
foreach i [$sel1 get index] {
lappend x 0
}
# veclength2 returns square of vector length
# $sel1 get {x y z} returns a position list as described earlier
foreach i [$sel1 get index] {
lset x $i [expr [veclength2 [vecsub [lindex [$sel1 get {x y z}] $i] [lindex [$sel2 get {x y z}] $i]]]]
}
Is there another way to do this in Tcl? Similar to python array subtraction perhaps?
I would try this, but it's just a guess
set x [lmap p1 [$sel1 get {x y z}] p2 [$sel2 get {x y z}] {
expr [veclength2 [vecsub $p1 $p2]]
}]
With this, there's no need to pre-declare $x

Why does not work using while for looping process

Why my script does not work. I am using while for looping process.
Hope anyone could help me for my case. The script as below;
Set global
global xmin xmax ymin ymax xVer yVer x1 y1 Count
Set paramaters
set xmin 0
set xmax 51
set ymin 0
set ymax 51
set x1 2
set y1 2
set xVer 2
set yVer 2
set Count 1
set goToVer "n"
Do looping process
while {$x1 > $xmin && $x1 < $xmax && $y1 > $ymin && $y1 < $ymax} {
# For horizontal axis
while {$x1 > $xmin && $x1 < $xmax} {
set azi [expr (45+90)]
set dip 0
set length 2
set dist [expr (cos($dip) * $length)]
set x1 [expr ($x1 + (sin($azimuth) * $dist))]
set y1 [expr ($y1 + (cos($azimuth) * $dist))]
set goToVer "y"
incr Count
}
# For vertical axis
if {$goToVer == "y"} {
set azi 45
set dip 0
set length 5
set dist [expr (cos($dip) * $length)]
set x1 [expr ($xVer + (sin($azimuth) * $dist))]
set y1 [expr ($yVer + (cos($azimuth) * $dist))]
set xVer $x1
set yVer $y1
incr Count
}
}
Thanks in advance!
I don't know what the problem is, but there are some things that we can do to make everything better. First step: let's ry factoring out the coordinate conversion code itself into a little procedure (I've fixed the braces around the expressions too):
proc convert {x y length azimuth dip} {
set dist [expr {cos($dip) * $length}]
set x1 [expr {$x + sin($azimuth) * $dist}]
set y1 [expr {$y + cos($azimuth) * $dist}]
return [list $x1 $y1]
}
while {$x1 > $xmin && $x1 < $xmax && $y1 > $ymin && $y1 < $ymax} {
# For horizontal axis
while {$x1 > $xmin && $x1 < $xmax} {
set azi [expr (45+90)]
set dip 0
set length 2
lassign [convert $x1 $y1 $length $azi $dip] x1 y1
set goToVer "y"
incr Count
}
# For vertical axis
if {$goToVer == "y"} {
set azi 45
set dip 0
set length 5
lassign [convert $xVer $yVer $length $azi $dip] x1 y1
incr Count
}
}
Next, the value of azi in the inner loop is suspicious; it looks like it is in degrees but Tcl's trigonometry functions (like those in most other programming languages) take their argument in radians. Multiply it by π/180°.
Finally, the logic of the loops is weird. I'm not saying it is wrong… but I really find it hard to comprehend what you'd use looping like that for. To loop a pair of coordinates over some space using equal steps on the axes, you use for loops with integer iterator variables and then apply a conversion to get your floating point coordinates (this is best because it limits cumulative errors):
set azi [expr {(45 + 90) * 3.1415927/180}]
set dip 0
set length 2
for {set x $xmin} {$x <= $xmax} {incr x} {
for {set y $ymin} {$y <= $ymax} {incr y} {
set dist [expr {cos($dip) * $length}]
set x1 [expr {$x + sin($azimuth) * $dist}]
set y1 [expr {$y + cos($azimuth) * $dist}]
# I assume you want to do something with $x1,$y1 here…
}
}
Alternatively, you could use regular spacing in polar coordinates, or any other regular scheme; it's just that good code exploits regularity and you're strongly recommended to work that way if you can. But that might not be what you were trying to do at all. Your code is confusing in its intent.
Which brings me to your actual bugs, which appear to revolve around state management. The logic with the goToVer was confused, BTW, and that might've been the problem you were having. You were setting it in the inner loop, but from that point on it was always set. I recommend not doing things like that as it is quite difficult to debug (there are cases where it can make sense, but it doesn't look like you're doing them) and instead sticking to regular grids, but they can work. I'm guessing that you are missing a reset of the variable to 0 at some point in the outer loop, probably just before the inner loop starts.

combining the elements of several lists

I want to take an arbitrary number of lists and return list of combinations of elements, but only combining one element from each list. All I have is sudo code because I don't know where to start.
I did find the solution to this program in the question of Combining the elements of 2 lists but I don't understand that scala code. I'm writing my program in Tcl but if you're able to help me feel free to write your answer in anything like java or python or pseudo code or whatever. Can anyone help me bring the following pseudo code to life?
for example:
# example: {a b} {c} {d e}
# returns: {a c d} {a c e} {b c d} {b c e}
# how?
# example: {a b} {c} {d e}
# iters: 0 0 0
# 0 0 1
# 1 0 0
# 1 0 1
#
#
# set done false
#
# while {!done} {
#
# list append combination_of_list due to iteration counts
#
# foreach list $lists {
# increment the correct count (specifically {0->1} {0->0} {0->1}) }
# reset the approapraite counts to 0
# }
#
# if all the counts in all the lists are at or above their max {
# set done true
# }
# }
Various Tcl solutions are discussed on this page: Cartesian product of a list of lists
Donal Fellows presents this variation at the end of the page:
proc product args {
set xs {{}}
foreach ys $args {
set result {}
foreach x $xs {
foreach y $ys {
lappend result [list {*}$x $y]
}
}
set xs $result
}
return $xs
}
Running that like this gives you the result:
% product {a b} {c} {d e}
{a c d} {a c e} {b c d} {b c e}
Documentation:
foreach,
lappend,
list,
proc,
return,
set,
{*} (syntax)
Here is a pseudocode describing an algorithm that generates all the combinations:
list_of_lists = {{a b}{c}{d e}}
def copy(list):
copy = {}
for element in list:
copy.add(element)
return copy;
def combine(list1, list2):
combinations = {}
for item1 in list1:
for item2 in list2:
combination = copy(item1)
combination.add(item2)
combinations.add(combination)
return combinations
results = {{}}
while list_of_lists.length>0:
results = combine(results, list_of_lists[0])
list_of_lists.remove(0)
It starts by combining {{}} with {a b c}
which produces {{a} {b}} which will be combined with {c} to generate {{a c} {b c}} on the next iteration etc.
Update:
Javascript version:
var list_of_lists = [["a", "b"],["c"],["d", "e"]];
function copy(list) {
var copy = [];
for (element of list) {
copy.push(element);
}
return copy;
}
function combine(list1, list2) {
var combinations = [];
for (let item1 of list1) {
var combination = copy(item1);
for (let item2 of list2){
combination.push(item2);
combinations.push(combination);
}
}
return combinations;
}
results = [[]]
while (list_of_lists.length>0) {
results = combine(results, list_of_lists[0]);
list_of_lists.splice(0,1);
}
console.log(results);

Converting this algorithm in TCL

I need help in converting this algorithm into tcl for my work, I am not so good in tcl language.
Inputs: STA−1, STA−2, STA−3, ..., STA−n
//requests from various stations for channel access
Shared Variables:
for every i, 1 ≤ i ≤ n
counter[i] ∈ { / 0, 1, 2,..., N}, initially 0, updated by stations
Sequence Number, K ∈ { / 0, 1, 2,..., N}, initially 0, will be set to a positive integer
Procedure:
//Initialization
Set sequence number K = m; //based on the action selected
for (i = 1 to n)
counter[i] = 0;
for (i = 1 to n)
{
while (channel access[i])
if (counter[i]! = K)
{
if (channel == idle)
{
if (counter[i]<min(counter[i+1], counter[i+2], ..., counter[i + n]))
access channel;
else
defer access;
}
counter[i]+ +;
}
else
defer access;
}
This is for CPS devices to access internet using a WSN in between..basic network is done but need help with adding this algo to it..
Can someone help me code that algo in tcl?
Your question isn't clear enough.
For syntax, I'd recommend referring TCL online help.
Some quick snippets:
# Inputs: STA−1, STA−2, STA−3, ..., STA−n
set stations [list "STA−1" "STA−2" "STA−3"]
# Shared Variables:
# for every i, 1 ≤ i ≤ n
# counter[i] ∈ { / 0, 1, 2,..., N}, initially 0, updated by stations
array set counter {}
set n 10
set i 0
while {$i < $n} {
set counter($i) 0
incr i
}
The core of your method is converted to this, assuming that counter is converted to an (associative) array, and that channel identifiers are stored in the channel array:
variable K 0
for {set i 1} {$i <= $n} {incr i} {
set counter($i) 0
}
for {set i 1} {$i <= $n} {incr i} {
while {[channelAccess $channel($i)]} {
if {$counter($i) != $K} {
if {[channelIdle $channel($i)]} {
set minimum [getMinimum [expr {$i + 1}] [expr {$i + $n}]]
if {$counter($i) < $minimum} {
accessChannel $channel($i)
} else {
deferAccess $channel($i)
}
}
incr counter($i)
} else {
deferAccess $channel($i)
}
}
}
You'll also need this procedure:
proc getMinimum {from to} {
upvar 1 counter counter
set minVal $counter($from)
for {set i $from} {$i <= $to} {incr i} {
set minVal [expr {min($minVal, $counter($i))}]
}
return $minVal
}
And you'll need to define channelAccess, channelIdle, accessChannel and deferAccess; they're things that your algorithm doesn't specify. There's also nothing to say what various variable are actually updated by. But that's the algorithm converted.
Note the patterns for using for; those are idiomatic Tcl for this sort of thing. Also note the brace positioning; your life will be easiest in Tcl if you use that style.