 |
AutoHotkey Community Let's help each other out
|
| View previous topic :: View next topic |
| Author |
Message |
[VxE]
Joined: 07 Oct 2006 Posts: 3244 Location: Simi Valley, CA
|
|
| Back to top |
|
 |
Guest
|
Posted: Sat Mar 22, 2008 6:33 am Post subject: |
|
|
this post sucks
but not as much as this thread  |
|
| Back to top |
|
 |
[VxE]
Joined: 07 Oct 2006 Posts: 3244 Location: Simi Valley, CA
|
Posted: Sat Mar 22, 2008 8:12 am Post subject: Hashes and HMAC: SHA1, SHA256 |
|
|
Although this post previously contained a hash function based on AdvAPI32.dll, I have removed it due to the availability of superior alternatives that use the same dll.
Instead, here is a library-style function collection for computing hashes ( SHA1 and SHA256 ) and the related HMACs.
Note: I have not conducted speed tests between these functions and AdvAPI32.dll-powered hash functions. | Code: | /*
####################################################################################################
####################################################################################################
###### ######
###### Secure Hash Algorithms: SHA-1 and SHA-256 ######
###### ######
###### Also: HMAC_SHA1 and HMAC_SHA256 ######
###### ######
####################################################################################################
####################################################################################################
This library contains functions for calculating cryptographic hashes using the SHA1 and SHA256
algorithms and functions for signing messages using those hashes (HMAC).
*/
SHA() { ; ------------------------------------------------------------------------------------------
; Library inclusion stub. Name this file 'SHA.ahk' and save it to your Autohotkey Lib folder.
; To include these functions in your script, simply call this function near the top of your script.
Return "Hash functions SHA1 and SHA256 as well as HMAC_SHA1 and HMAC_SHA256 are available "
. "in this script. These functions were written by [VxE] and are available publicly "
. "at http://www.autohotkey.com/forum/viewtopic.php?p=186095#186095"
} ; SHA() ------------------------------------------------------------------------------------------
SHA1( byref data, len = 0 ) { ; --------------------------------------------------------------------
; This implementation of the SHA1 algorithm was written by [VxE] on 09-13-2010.
; More info about the SHA-1 hash algorithm at http://en.wikipedia.org/wiki/SHA-1
; Also see http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf
m32 := 0xffffffff ; this stands for modulo 32-bit, since AHK's numbers are all 64 bit, we'll
; need to explicitly truncate bits beyond 32 by bitwise-ANDing them with this number.
IfLessOrEqual, Len, 0, StringLen, Len, data ; autodetect data length if it's not explicitly declared
; initialize the 5 inter-block collector variables
_a := 0x67452301, _b := 0xEFCDAB89, _c := 0x98BADCFE, _d := 0x10325476, _e := 0xC3D2E1F0
; The number of blocks is the number of bytes in the data plus 8 (for the data length) divided by 64
Loop % blocks := ( Len + 8 >> 6 ) + 1
{
VarSetCapacity( block, 320, 0 ) ; clear a temporary buffer for a copy of this block
a := _a, b := _b, c := _c, d := _d, e := _e ; initialize the intra-block collector variables
; 'o' becomes the base offset, in bytes, of the current block
If ( Len - 64 >= o := A_Index - 1 << 6 ) ; Copy the current block into the buffer
DllCall("RtlMoveMemory", "UInt", &block, "UInt", &data + o, "Int", 64 )
Else If ( Len - o > 0 ) ; if this is the last block, only copy the remaining bytes (less than 64)
DllCall("RtlMoveMemory", "UInt", &block, "UInt", &data + o, "Int", Len - o )
If ( Len - o >= 0 && Len - o < 64 ) ; if this is the last block with actual data in it, then
NumPut( 128, block, Len - o, "Char" ) ; append a 0x80 byte to terminate the message data
If ( A_Index = blocks ) ; if this is the actual last block, inject the data length in bits
Loop 8 ; as a 64-bit big-endian integer into the last 8 bytes of the buffer.
NumPut( Len << 3 >> ( 8 - A_Index << 3 ) & 255, block, 55 + A_Index, "Char" )
Loop 80 ; Since the block is 320 bytes, it contains 80 dwords
{
; dwords 1 thru 16 are 4-byte chunks of the buffer interpereted as big-endian 32-bit integers
If ( 64 > o := A_Index - 1 << 2 ) ; 'o' is now the offset of the next dword in the buffer
NumPut( w := NumGet( block, o + 3, "UChar" )
| NumGet( block, o + 2, "UChar" ) << 8
| NumGet( block, o + 1, "UChar" ) << 16
| NumGet( block, o, "UChar" ) << 24, block, o, "UInt" )
Else ; dwords 17 through 80 are each derived from 4 other dwords, XORed together and
{ ; then left rotated by 1 bit (using 32-bit arithmetic, don't forget)
w := NumGet( block, o - 4 * 3, "UInt" ) ^ NumGet( block, o - 4 * 8, "UInt" )
^ NumGet( block, o - 4 * 14, "UInt" ) ^ NumGet( block, o - 4 * 16, "UInt" )
NumPut( w := ( w << 1 | ( w >> 31 )) & m32, block, o, "UInt" )
}
; A value ('f') is calculated from a constant, a dword from the buffer, and the intra-block variables
; The formula for this value changes depending on how far into the block 'o' is.
If ( o < 80 )
f := m32 & 0x5A827999 + ( ( b & c ) | ((~b) & d) ) + ( a << 5 | ( a >> 27 )) + e + w
Else If ( o < 160 )
f := m32 & 0x6ED9EBA1 + ( b ^ c ^ d ) + ( a << 5 | ( a >> 27 )) + e + w
Else If ( o < 240 )
f := m32 & 0x8F1BBCDC + ( ( b & c ) | ( b & d ) | ( c & d ) ) + ( a << 5 | ( a >> 27 )) + e + w
Else f := m32 & 0xCA62C1D6 + ( b ^ c ^ d ) + ( a << 5 | ( a >> 27 )) + e + w
; once 'f' is calculated, the intra-block variables are rolled, with 'a' receiving 'f's value
e := d, d := c, c := m32 & ( b << 30 | ( b >> 2 )), b := a, a := f
}
; Now that the block has been processed, add the intra-block variables to their inter-block counterparts
_a := _a + a & m32, _b := _b + b & m32, _c := _c + c & m32, _d := _d + d & m32, _e := _e + e & m32
}
VarSetCapacity( block, 40, 0 ) ; we re-use the block buffer to hold the hash string
Loop 40 ; The SHA1 hash is, by definition, 40 hex digits long.
; This is an obfuscated way of taking the 5 inter-block variables and converting their contents
; into hex representation and concatenating them.
j := Chr( 97 + ( A_Index - 1 >> 3 ) )
, j := _%j% >> ( ( 8 - A_Index & 7 ) << 2 ) & 15
, block .= Chr( 48 + j + 39 * ( j > 9 ) )
Return block ; return the hex string
} ; SHA1( byref data, len = 0 ) --------------------------------------------------------------------
HMAC_SHA1( byref key, byref message, keylen = 0, msglen = 0 ) { ; ----------------------------------
; Function by [VxE]. See RFC 2104 ( http://www.ietf.org/rfc/rfc2104.txt )
; test vectors may be found at ( http://tools.ietf.org/html/rfc2202 )
Static hex := "123456789abcdef" ; this is for converting hex into byte values
; Autodetect the lengths of the key and message if they aren't explicitly set
IfLessOrEqual, keylen, 0, StringLen, keylen, key
IfLessOrEqual, msglen, 0, StringLen, msglen, message
; define a buffer to hold the message AND the inner padding and copy the message into it
VarSetCapacity( buffer, msglen + 64, 54 )
DllCall("RtlMoveMemory", "UInt", &buffer + 64, "UInt", &message, "Int", msglen )
If ( keylen > 64 ) ; deal with long keys by hashing them
{
key := SHA1( key, keylen ), keylen := 20
Loop, Parse, key
IfEqual, True, % A_Index & 1, SetEnv, nbl, % InStr( hex, A_LoopField ) << 4
Else NumPut( nbl | InStr( hex, A_LoopField ), key, A_Index - 2 >> 1, "Char" )
}
; Take the key and for each byte, XOR it with 0x36 and insert the result into the inner padding
Loop %keylen%
NumPut( NumGet( key, A_Index - 1, "UChar" ) ^ 54, buffer, A_Index - 1, "Char" )
; Hash the inner padding + the message and prep the buffer for the outer hash
msglen := SHA1( buffer, 64 + msglen ), VarSetCapacity( buffer, 96, 92 )
; Take the key and for each byte, XOR it with 5C and insert the result into the outer padding
Loop %keylen% ;
NumPut( NumGet( key, A_Index - 1, "UChar" ) ^ 92, buffer, A_Index - 1, "Char" )
; Append the message hash to the outer padding ( converting hex into bytes )
Loop, Parse, msglen
IfEqual, True, % A_Index & 1, SetEnv, nbl, % InStr( hex, A_LoopField ) << 4
Else NumPut( nbl | InStr( hex, A_LoopField ), buffer, 64 + ( A_Index - 2 >> 1 ), "Char" )
; Return the hash of the modified message. Note that (84) = block size (64) + digest length (20)
; If you're modifying this function for other hash algorithms, you must change this value.
Return SHA1( buffer, 84 ), VarSetCapacity( buffer, 0 )
} ; HMAC_SHA1( byref key, byref message, keylen = 0, msglen = 0 ) ----------------------------------
SHA256( byref data, len = 0 ) { ; ------------------------------------------------------------------
; This implementation of the SHA256 algorithm was written by [VxE] on 08-29-2010.
; More info about the SHA-1 hash algorithm at http://en.wikipedia.org/wiki/SHA-2
; Also see http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf
; The first time this function is run, the static variable 'k' is transformed into an array
; of 64 dwords. These are the constants used when processing the data blocks
Static m32 := 0, k := "
( ltrim join
982f8a4291443771cffbc0b5a5dbb5e95bc25639f111f159a4823f92d55e1cab
98aa07d8015b8312be853124c37d0c55745dbe72feb1de80a706dc9b74f19bc1
c1699be48647beefc69dc10fcca10c246f2ce92daa84744adca9b05cda88f976
52513e986dc631a8c82703b0c77f59bff30be0c64791a7d55163ca0667292914
850ab72738211b2efc6d2c4d130d385354730a65bb0a6a762ec9c281852c7292
a1e8bfa24b661aa8708b4bc2a3516cc719e892d1240699d685350ef470a06a10
16c1a419086c371e4c774827b5bcb034b30c1c394aaad84e4fca9c5bf36f2e68
ee828f746f63a5781478c8840802c78cfaffbe90eb6c50a4f7a3f9bef27871c6
)"
If !m32 ; this variable is static, but it also serves as an initialization flag
{ ; notice the k-constants are originally in hex format... they get changed here
m32 := 0xffffffff
; m32 stands for modulo 32-bit, because AHK's numbers are all 64-bit, we'll
; need to explicitly truncate bits beyond 32 by bitwise-ANDing them with this number.
Loop, Parse, k
IfEqual, True, % A_Index & 1, SetEnv, i, % InStr( "123456789abcdef", A_LoopField ) << 4
Else NumPut( i | InStr( "123456789abcdef", A_LoopField ), k, A_Index - 2 >> 1, "UChar" )
}
IfLessOrEqual, Len, 0, StringLen, Len, data ; autodetect data length if it's not explicitly declared
; initialize the 8 inter-block collector variables
_a := 0x6a09e667, _b := 0xbb67ae85, _c := 0x3c6ef372, _d := 0xa54ff53a
, _e := 0x510e527f, _f := 0x9b05688c, _g := 0x1f83d9ab, _h := 0x5be0cd19
; The number of blocks is the number of bytes in the data plus 8 (for the data length) divided by 64
Loop % blocks := ( Len + 8 >> 6 ) + 1
{
VarSetCapacity( block, 256, 0 ) ; clear a temporary buffer for a copy of this block
a := _a, b := _b, c := _c, d := _d, e := _e, f := _f, g := _g, h := _h ; initialize the intra-block collector variables
; 'o' becomes the base offset, in bytes, of the current block
If ( Len - 64 >= o := A_Index - 1 << 6 ) ; Copy the current block into the buffer
DllCall("RtlMoveMemory", "UInt", &block, "UInt", &data + o, "Int", 64 )
Else If ( Len - o > 0 ) ; if this is the last block, only copy the remaining bytes (less than 64)
DllCall("RtlMoveMemory", "UInt", &block, "UInt", &data + o, "Int", Len - o )
If ( Len - o >= 0 && Len - o < 64 ) ; if this is the last block with actual data in it, then
NumPut( 128, block, Len - o, "Char" ) ; append a 0x80 byte to terminate the message data
If ( A_Index = blocks ) ; if this is the actual last block, inject the data length in bits
Loop 8 ; as a 64-bit big-endian integer into the last 8 bytes of the buffer.
NumPut( Len << 3 >> ( 8 - A_Index << 3 ) & 255, block, 55 + A_Index, "Char" )
Loop 64 ; Since the block is 256 bytes, it contains 64 dwords
{
; dwords 1 thru 16 are 4-byte chunks of the buffer interpereted as big-endian 32-bit integers
If ( 64 > o := A_Index - 1 << 2 ) ; 'o' is now the offset of the next dword in the buffer
NumPut( w := NumGet( block, o + 3, "UChar" )
| NumGet( block, o + 2, "UChar" ) << 8
| NumGet( block, o + 1, "UChar" ) << 16
| NumGet( block, o, "UChar" ) << 24, block, o, "UInt" )
Else ; dwords 17 through 64 are each derived from 4 other dwords
{
sig1 := NumGet( block, o - 4 * 15, "UInt" )
sig2 := NumGet( block, o - 4 * 2, "UInt" )
sig1 := (( sig1 >> 7 ) | ( ( sig1 & 0x7f ) << 25 ))
^ (( sig1 >> 18 ) | ( ( sig1 & 0x3ffff ) << 14 ))
^ ( sig1 >> 3 )
sig2 := (( sig2 >> 17 ) | ( ( sig2 & 0x1ffff ) << 15 ))
^ (( sig2 >> 19 ) | ( ( sig2 & 0x7ffff ) << 13 ))
^ ( sig2 >> 10 )
sig3 := NumGet( block, o - 4 * 7, "UInt" ) + NumGet( block, o - 4 * 16, "UInt" )
NumPut( w := m32 & sig1 + sig2 + sig3, block, o, "UInt" )
}
j := NumGet( k, o, "Uint" ) ; obtain a value from the constant static array
; t1 and t2 are temporary variables to hold a value that need to be calculated before
; the intra-block variables are rolled.
t2 := ( (( a >> 2 ) | ( ( a & 0x3 ) << 30 ))
^ (( a >> 13 ) | ( ( a & 0x1fff ) << 19 ))
^ (( a >> 22 ) | ( ( a & 0x3fffff ) << 10 )) )
+ ( ( a & b ) ^ ( a & c ) ^ ( b & c ) ) & m32
t1 := ( (( e >> 6 ) | ( ( e & 0x3f ) << 26 ))
^ (( e >> 11 ) | ( ( e & 0x7ff ) << 21 ))
^ (( e >> 25 ) | ( ( e & 0x1ffffff ) << 7 )) )
+ ( ( e & f ) ^ ( (~e) & g ) ) + h + j + w & m32
; roll the intra-block variables, 'a' receives the temporary values.
h := g, g := f, f := e, e := d + t1 & m32, d := c, c := b, b := a, a := t1 + t2 & m32
}
; Now that the block has been processed, add the intra-block variables to their inter-block counterparts
_a := _a + a & m32, _b := _b + b & m32, _c := _c + c & m32, _d := _d + d & m32
, _e := _e + e & m32, _f := _f + f & m32, _g := _g + g & m32, _h := _h + h & m32
}
VarSetCapacity( block, 64, 0 ) ; we re-use the block buffer to hold the hash string
Loop 64 ; The SHA256 hash is, by definition, 64 hex digits long.
; This is an obfuscated way of taking the 5 inter-block variables and converting their contents
; into hex representation and concatenating them.
j := Chr( 97 + ( A_Index - 1 >> 3 ) )
, j := _%j% >> ( ( 8 - A_Index & 7 ) << 2 ) & 15
, block .= Chr( 48 + j + 39 * ( j > 9 ) )
Return block ; return the hex string
} ; SHA256( byref data, len = 0 ) ------------------------------------------------------------------
HMAC_SHA256( byref key, byref message, keylen = 0, msglen = 0 ) { ; --------------------------------
; Function by [VxE]. See RFC 2104 ( http://www.ietf.org/rfc/rfc2104.txt )
; test vectors may be found at ( http://tools.ietf.org/html/rfc4231 )
Static hex := "123456789abcdef" ; this is for converting hex into byte values
; Autodetect the lengths of the key and message if they aren't explicitly set
IfLessOrEqual, keylen, 0, StringLen, keylen, key
IfLessOrEqual, msglen, 0, StringLen, msglen, message
; define a buffer to hold the message AND the inner padding and copy the message into it
VarSetCapacity( buffer, msglen + 64, 54 )
DllCall("RtlMoveMemory", "UInt", &buffer + 64, "UInt", &message, "Int", msglen )
If ( keylen > 64 ) ; deal with long keys by hashing them
{
key := SHA256( key, keylen ), keylen := 32
Loop, Parse, key
IfEqual, True, % A_Index & 1, SetEnv, nbl, % InStr( hex, A_LoopField ) << 4
Else NumPut( nbl | InStr( hex, A_LoopField ), key, A_Index - 2 >> 1, "Char" )
}
; Take the key and for each byte, XOR it with 0x36 and insert the result into the inner padding
Loop %keylen%
NumPut( NumGet( key, A_Index - 1, "UChar" ) ^ 54, buffer, A_Index - 1, "Char" )
; Hash the inner padding + the message and prep the buffer for the outer hash
msglen := SHA256( buffer, 64 + msglen ), VarSetCapacity( buffer, 96, 92 )
; Take the key and for each byte, XOR it with 5C and insert the result into the outer padding
Loop %keylen% ;
NumPut( NumGet( key, A_Index - 1, "UChar" ) ^ 92, buffer, A_Index - 1, "Char" )
; Append the message hash to the outer padding ( converting hex into bytes )
Loop, Parse, msglen
IfEqual, True, % A_Index & 1, SetEnv, nbl, % InStr( hex, A_LoopField ) << 4
Else NumPut( nbl | InStr( hex, A_LoopField ), buffer, 64 + ( A_Index - 2 >> 1 ), "Char" )
; Return the hash of the modified message. Note that (96) = block size (64) + digest length (32)
; If you're modifying this function for other hash algorithms, you must change this value.
Return SHA256( buffer, 96 ), VarSetCapacity( buffer, 0 )
} ; HMAC_SHA256( byref key, byref message, keylen = 0, msglen = 0 ) -------------------------------- |
another edit, this is my version of MD5 done in AHK: | Code: | MD5( byref data, len=0 ) { ; -----------------------------------------------------------------------
; Function by [VxE]. Specify the data length if 'data' may possibly contain null bytes.
; static variables and constants r[0~63], encoded here as bytes with an offset of 64
; ( that means the real value is the byte value minus 64, e.g: r[0] = 7, so 7 + 64 = 71 = 'G' )
Static S, k, p:=0, r := "GLQVGLQVGLQVGLQVEINTEINTEINTEINTDKPWDKPWDKPWDKPWFJOUFJOUFJOUFJOU"
VarSetCapacity( S, 64, 0 ) ; Initialize the block buffer S and constants p and k[0~63]
IfEqual, p, 0, Loop % VarSetCapacity( k, 256 + !( p := &S ) ) >> 2 & 64
NumPut( Floor(Abs(Sin(A_Index)) * 2**32 ), k, A_Index - 1 << 2, "UInt" )
; autodetect message length if it's not specified (or is not positive)
IfLess, len, 1, StringLen, len, data
; initialize running accumulators and terminator (the terminator is appended to the message)
ha := 0x67452301, hb := 0xEFCDAB89, hc := 0x98BADCFE, hd := 0x10325476, term := 0x80
; Begin rolling the message. This loop does 1 iteration for each 64 byte block such that the
; last block has fewer than 55 bytes in it ( to leave room for the terminator and data length )
Loop % len + 72 >> 6
{
If ( f := len - 64 > ( e := A_Index - 1 << 6 ) ? 64 : len > e ? len - e : 0 )
DllCall( "RtlMoveMemory", "UInt", p, "UInt", &data + e, "Int", f ) ; copy the block
IfLess, f, 64 ; append the terminator to the message, then wipe the terminator since
NumPut( ( term | 0 ) | term := 0, S, f, "UChar" ) ; this may not be the last block
IfLess, f, 56, Loop 8 ; if this is the real last block, insert the data length in BITS
NumPut( ( len << 3 >> ( A_Index - 1 << 3 ) ) & 255, S, 55 + A_Index, "UChar" )
a := ha, b := hb, c := hc, d := hd ; copy running accumulators to intermediate variables
Loop 64 ; begin rolling the block. These operations have been condensed and obfuscated.
{ ; For i from 0 to 63 {
e := NumGet( r, i := A_Index - 1, "UChar" ) & 31 ; extract the constant r[i]
f := 0 = ( j := i >> 4 ) ? (b&c)|(~b&d) : j=1 ? (d&b)|(~d&c) : j=2 ? b^c^d : c^(~d|b)
g := (( i * ( 3817 >> j * 3 & 7 ) + ( 328 >> j * 3 & 7 ) & 15 ) << 2 ) + p
w := (*(g+3) << 24 | *(g+2) << 16 | *(g+1) << 8 | *g) + a + f + NumGet(k,i<<2,"UInt")
a := d, d := c, c := b, b += w << e | (( w & 0xFFFFFFFF ) >> ( 32 - e ))
}
; add the intermediate variables to the running accumlators (making sure to mod by 2**32)
ha := ha+a&0xFFFFFFFF, hb := hb+b&0xFFFFFFFF, hc := hc+c&0xFFFFFFFF, hd := hd+d&0xFFFFFFFF
VarSetCapacity( S, 64, 0 ) ; Clear the block ( set bits to zero )
}
Loop 32 ; convert the running accumulators into 32 hex digits
i := Chr( 96 + ( A_Index + 7 >> 3 ) ), S .= SubStr( "123456789abcdef0"
, h%i% >> ( Mod( A_Index - 1 + (A_Index & 1) - !(A_Index & 1), 8 ) << 2 ) & 15, 1 )
Return S ; return the hex digits
} ; MD5( byref data, len=0 ) ----------------------------------------------------------------------- |
_________________ Ternary (a ? b : c) guide TSV Table Manipulation Library
Post code inside [code][/code] tags!
Last edited by [VxE] on Sun Oct 31, 2010 1:06 am; edited 7 times in total |
|
| Back to top |
|
 |
_adam Guest
|
Posted: Sat Mar 22, 2008 4:19 pm Post subject: |
|
|
somebody doesn't like you.
can't win them all though. thanks. I picked up some parts. |
|
| Back to top |
|
 |
BoBo¨ Guest
|
Posted: Wed Apr 02, 2008 9:51 am Post subject: |
|
|
Saw your 'hint' to this thread at your signature. Have you thought about to 'rearrange' the threads apperance a little with using BBCodeWriter?
Currently it's quite a tough challenge to get 'the idea behind' a script within a snap.
JM2€Cs + thanks for sharing your scripts with us. Much appreciated  |
|
| Back to top |
|
 |
[VxE]
Joined: 07 Oct 2006 Posts: 3244 Location: Simi Valley, CA
|
Posted: Wed Apr 02, 2008 8:10 pm Post subject: |
|
|
Hmmm... there is indeed a lot of housekeeping that I should do with this thread, but recently I've been up to my eyeballs in other work. I suppose I really should break each scriptlet into its own post and keep a directory in the OP.
I'm glad you found something useful here
[Edit: 3-2-2010] More Set Functions (I added the fancy ones: permutation, power, combination, and transpose) | Code: | ;
; Set functions library (Upgraded, String-Based), by [VxE]
;
; Introduction:
; Sets are an important concept in OOP, data management, and
; discrete mathematics. This function library allows you to
; define sets of string objects and perform typical set functions
; on them. Additionally, this library offers six functions for
; handling multi-dimensional sets: Set_Zip joins two sets
; on a 1:1 basis and Set_Column extracts a set from a complex
; set. Set_Combinations, Set_Permutate, and Set_Power return
; supersets of an input set ( combinations are subsets of N
; distinct members from the input set ). Set_Transpose returns
; a set with the same elements, but flipped on the diagonal.
; The real power behind this library is the ability to use
; any string as a delimiter, rather than just a single character.
; This feature makes it easy to work with data collections in
; multiple text formats and to construct multi-dimensional sets
; without limits.
;
; Definitions:
; a 'delimiter' is a string with a length greater than zero.
; a 'member' is a string within a 'set' bounded by delimiters and/or
; the ends of the string.
; a 'set' is a string containing zero or more members, each separated
; from the previous and next member by a delimiter.
;
; Functions:
; Set_Column
; Set_Combinations ( see http://en.wikipedia.org/wiki/Combination )
; Set_Delimit
; Set_GetPos
; Set_GetPosRegEx
; Set_Intersect
; Set_Member
; Set_Permutations ( see http://en.wikipedia.org/wiki/Permutation )
; Set_Power ( see http://en.wikipedia.org/wiki/Power_set )
; Set_Remove
; Set_Size
; Set_Transpose ( see http://en.wikipedia.org/wiki/Transpose )
; Set_Union
; Set_Zip
;
; Set_SampleSet
;
; Notes:
; All string comparisons are case sensitive with the exception of
; Set_GetPosRegEx, which leaves case sensitivity up to the regex.
Set_Column( Set1, column, PrimaryDelimiter="`n", SecondaryDelimiter="," ) {
; Returns a set which is comprised of the 'column'th sub-member of
; each of 'Set1's members. Essentially, if each member were like a
; row in a table, this function would yield a column from that table.
VarSetCapacity( Set, StrLen( Set1 ))
column := Floor( column ) ; truncate decimals
Set1 .= PrimaryDelimiter
Stuff := StrLen( PrimaryDelimiter )
iStuff := StrLen( SecondaryDelimiter )
Pos := 1 - Stuff
Lead := 1
Loop
If ( 0 < Pos := InStr( Set1, PrimaryDelimiter, 1, Pos + Stuff ) )
{
StringMid, member, Set1, %Lead%, Pos - Lead
Lead := Pos + Stuff
member .= SecondaryDelimiter
ilead := 1
ipos := 1 - istuff
Loop
If ( 0 < ipos := InStr( member, SecondaryDelimiter, 1, ipos + Stuff ) )
{
StringMid, imember, member, %ilead%, ipos - ilead
ilead := ipos + iStuff
If ( A_Index < column )
Continue
Set .= PrimaryDelimiter . imember
Break
}
Else Break
}
Else Break
StringTrimLeft, Set, Set, %Stuff%
Return Set
}
Set_Combinations( Set1, members=1, PrimaryDelimiter="`n", SecondaryDelimiter="," ) {
; Returns a complex set where each member is composed of 'members' members from
; Set1. The resulting set contains only unique members ( no two members will
; contains exactly the same sub-members, regardless of order ) assuming, of course,
; that the input set contains all unique members in the first place.
Size := Set_Size( Set1, PrimaryDelimiter ), Pz := 1
If ( members <= 0 ) || ( members > Size )
Return "" ; pre-check for neg/zero members
If ( members = 1 )
Return Set_Union( Set1, "", PrimaryDelimiter ) ; 1-member -> set as-is
If ( members = Size )
Return Set_Delimit( Set1, SecondaryDelimiter, PrimaryDelimiter )
Set1 .= PrimaryDelimiter
Stuff := StrLen( PrimaryDelimiter )
Pos := 1 - Stuff
Lead := 1
Loop ; separate the set into an array of members
If ( 0 < Pos := InStr( Set1, PrimaryDelimiter, 1, Pos + Stuff ) )
{
StringMid, member%A_Index%, Set1, %Lead%, Pos - Lead
Lead := Pos + Stuff
}
Else Break
StringTrimRight, Set1, Set1, %Stuff%
If ( members + 1 = Size ) ; this is the opposite of 'members' == 1
{
Loop, %Size% ; omit each member in turn and append to result
Set := Set_Delimit( Set_Remove( Set1, member%A_Index%, PrimaryDelimiter ), SecondaryDelimiter, PrimaryDelimiter ) . ( A_Index = 1 ? "" : PrimaryDelimiter . Set )
Return Set
}
Loop, %members% ; calculate the number of combinations (pascal's triangle)
Pz := Round( pz * ( Size + 1.0 - A_Index ) / ( i%A_Index% := A_Index ) )
VarSetCapacity( Set, StrLen( Set1 ) * Pz * Size, 0 ) ; a very rough estimate
Loop, %Pz% ; for each potential combination
{
Loop, %members% ; choose N members
{
m := i%A_Index%
Set .= ( A_Index = 1 ? PrimaryDelimiter : SecondaryDelimiter ) . member%m%
If ( Size - members + A_Index > i%A_Index% )
c := A_Index ; this is the thing we should increment next
}
i%c% += 1 ; increment something
Loop % members - c ; we need to adjust some things
k := c + A_Index, i%k% := i%c% + A_Index
}
StringTrimLeft, Set, Set, %Stuff%
Return Set
}
Set_Delimit( Set1, NewD, PrimaryDelimiter="`n" ) {
; Replaces every instance of 'PrimaryDelimiter' in 'Set1' with 'NewD', effectively
; changing the set's delimiter.
oel := ErrorLevel ; preserve errorlevel
StringReplace, Set1, Set1, %PrimaryDelimiter%, %NewD%, All
Return Set1, ErrorLevel := oel
}
Set_GetPos( Set1, string, PrimaryDelimiter="`n" ) {
; Returns the index of the first member of 'Set1' that is an exact
; match of 'string'. The return value is zero if there is no match.
If ( 1 >= Pos := InStr( PrimaryDelimiter . Set1 . PrimaryDelimiter, PrimaryDelimiter . string . PrimaryDelimiter, 1 ) )
Return Pos
StringLeft, Set1, Set1, % Pos - 1
Return Set_Size( Set1, PrimaryDelimiter)
}
Set_GetPosRegEx( Set1, NeedleRegex, PrimaryDelimiter="`n" ) {
; Returns the index of the first member of 'Set1' that is a match
; of 'NeedleRegex'. The return value is zero if there is no match.
; NOTE: the regex needle should NOT account for the delimiters.
; Anchors "^$" will match a delimiter boundary. For example, the
; needleregex "^$" will match the first empty member in the set.
Stuff := StrLen( PrimaryDelimiter )
Set1 .= PrimaryDelimiter
Pos := 1 - Stuff
Lead := 1
Loop
If ( 0 < Pos := InStr( Set1, PrimaryDelimiter, 1, Pos + Stuff ) )
{
StringMid, member, Set1, %Lead%, Pos - Lead
Lead := Pos + Stuff
If RegexMatch( member, NeedleRegex )
Return A_Index
}
Else Break
Return 0
}
Set_Intersect( Set1, Set2, PrimaryDelimiter="`n" ) {
; Performs a set intersection of 'Set1' and 'Set2'. Only the members
; that are in both sets will be in the resulting set.
If ( Set_Size( Set1 ) > Set_Size( Set2 ) )
Set := Set2, Set2 := Set1, Set1 := Set
VarSetCapacity( Set, StrLen( Set1 ), 0)
Set1 .= PrimaryDelimiter
Set2 := PrimaryDelimiter . Set2 . PrimaryDelimiter
Stuff := StrLen( PrimaryDelimiter )
Pos := 1 - Stuff
Lead := 1
Loop
If ( 0 < Pos := InStr( Set1, PrimaryDelimiter, 1, Pos + Stuff ) )
{
StringMid, member, Set1, %Lead%, Pos - Lead
Lead := Pos + Stuff
If InStr( Set2, PrimaryDelimiter . member . PrimaryDelimiter, 1 )
Set .= PrimaryDelimiter . member
}
Else Break
StringTrimLeft, Set, Set, %Stuff%
Return Set
}
Set_Member( Set1, Which, PrimaryDelimiter="n" ) {
; Returns the 'Which'th member of Set1. A 'Which' less than 1 is
; considered an offset from the last member ('0' being the last)
Len := Set_Size( Set1, PrimaryDelimiter )
Which := Floor( Which )
If ( Which <= 0 ) && ( Len + Which > 0 )
Which := Len + Which
If ( Which > Len ) || ( Which <= 0 )
Return ""
Set1 .= PrimaryDelimiter
Stuff := StrLen( PrimaryDelimiter )
Pos := 1 - Stuff
Lead := 1
Loop
If ( 0 < Pos := InStr( Set1, PrimaryDelimiter, 1, Pos + Stuff ) )
&& ( A_Index < which )
Lead := Pos + Stuff
Else Break
StringMid, member, Set1, %Lead%, Pos - Lead
Return member
}
Set_Permutations( Set1, PrimaryDelimiter="`n", SecondaryDelimiter=",", p="" ) {
; Returns a complex set where each member is a unique permutation of
; the input set and which contains a number of members equal to the
; factorial of the input set's membercount. Note that 'PrimaryDelimiter'
; is the primary delimiter for both the input and output sets.
; WARNING! This function's output set has a size equal to the FACTORIAL
; of the input set's size. This means that a set of 10 members will
; have 3,628,800 permutations and could easily hit AHK's default
; variable limit of 64 MB. My laptop took nearly 3½ minutes to compute
; the permutations for the 10-member sample set (with setbatchlines,-1)
Size := Set_Size( Set1, PrimaryDelimiter )
IfLess, Size, 2, Return p . SecondaryDelimiter . Set1 ; only 1 member, so return !
VarSetCapacity( Set, StrLen(Set1) * Round(Sqrt(Size*6.283)*(Size/2.718)**Size), 0)
; This VarSetCapacity mutiplies the length of the input set string
; by an approximation of the factorial of 'Size'. Check wikipedia
; for 'factorial' to find out more.
Set1 .= PrimaryDelimiter
Stuff := StrLen( PrimaryDelimiter )
Pos := 1 - Stuff
Lead := 1
Loop ; separate the set into an array of members
If ( 0 < Pos := InStr( Set1, PrimaryDelimiter, 1, Pos + Stuff ) )
{
StringMid, member%A_Index%, Set1, %Lead%, Pos - Lead
Lead := Pos + Stuff
}
Else Break
Loop %Size% ; for each member, generate the permutation of the
{ ; other members and append that onto our result
Pos := member1
Loop % Size-1
{
Lead := A_Index + 1
member%A_Index% := member%Lead%
leftover := ( A_Index = 1 ? member%A_Index%
: leftover . PrimaryDelimiter . member%A_Index% )
}
member%Size% := Pos
Set .= PrimaryDelimiter . Set_Permutations( leftover, PrimaryDelimiter, SecondaryDelimiter , ( StrLen(p) ? p SecondaryDelimiter : "" ) . member%Size% )
}
StringTrimLeft, Set, Set, %Stuff%
Return Set
}
Set_Power( Set1, PrimaryDelimiter="`n", SecondaryDelimiter="," ) {
; Returns the power set of 'Set1'. In other words, every distinct combination
; of members from 'Set1' ( separated by 'SecondaryDelimiter' ) is returned in
; a list with a size equal to 2^(||Set1||). Also, the resulting list is in
; order by number of sub-members in each member, starting with zero (null)
Size := Set_Size( Set1, PrimaryDelimiter )
VarSetCapacity( Set, StrLen(Set1) * ( 1 << Size ), 0 )
Loop, %Size%
Set .= PrimaryDelimiter . Set_Combinations( Set1, A_Index, PrimaryDelimiter, SecondaryDelimiter )
Return Set
}
Set_Remove( Set1, Set2, PrimaryDelimiter="`n" ) {
; Performs a set intersection of 'Set1' and the complement of 'Set2'
Set1 .= PrimaryDelimiter
Set2 := PrimaryDelimiter . Set2 . PrimaryDelimiter
VarSetCapacity( Set, StrLen( Set1 ) )
Stuff := StrLen( PrimaryDelimiter )
Pos := 1 - Stuff
Lead := 1
Loop
If ( 0 < Pos := InStr( Set1, PrimaryDelimiter, 1, Pos + Stuff ) )
{
StringMid, member, Set1, %Lead%, Pos - Lead
Lead := Pos + Stuff
If !InStr( Set2, PrimaryDelimiter . member . PrimaryDelimiter, 1 )
Set .= PrimaryDelimiter . member
}
Else Break
StringTrimLeft, Set, Set, %Stuff%
Return Set
}
Set_Transpose( Set1, PrimaryDelimiter="`n", SecondaryDelimiter="," ) {
; Flips a set so that the columns become the rows.
; Any mising members will be made blank.
VarSetCapacity( Set, StrLen( Set1 ))
Set1 .= PrimaryDelimiter
Stuff := StrLen( PrimaryDelimiter )
iStuff := StrLen( SecondaryDelimiter )
Pos := 1 - Stuff
Lead := 1
w := 0
Loop ; convert the set into a local array
If ( 0 < Pos := InStr( Set1, PrimaryDelimiter, 1, Pos + Stuff ) )
{
StringMid, member, Set1, %Lead%, Pos - Lead
i := A_Index
Lead := Pos + Stuff
member .= SecondaryDelimiter
ilead := 1
ipos := 1 - istuff
Loop
If ( 0 < ipos := InStr( member, SecondaryDelimiter, 1, ipos + Stuff ) )
{
StringMid, member%i%_%A_Index%, member, %ilead%, ipos - ilead
ilead := ipos + iStuff
If ( A_Index > w )
w := A_Index
}
Else Break
member := A_Index
}
Else Break
Loop, %w%
{
i := A_Index
Loop, %member%
Set .= ( A_Index = 1 ? PrimaryDelimiter : SecondaryDelimiter ) . member%A_Index%_%i%
}
StringTrimLeft, Set, Set, %Stuff%
Return Set
}
Set_Size( Set, PrimaryDelimiter="`n" ) {
; Returns the total number of members in 'Set'. NOTE: an empty string
; always contains ZERO members, while any non-empty string contains
; a MINIMUM of ONE member.
If ( StrLen( Set ) = 0 )
Return 0
oscs := A_StringCaseSense
StringCaseSense, On
oel := ErrorLevel
StringReplace, Set, Set, %PrimaryDelimiter%, %PrimaryDelimiter%, UseErrorLevel
StringCaseSense, %oscs%
Return ErrorLevel + 1, ErrorLevel := oel
}
Set_Union( Set1, Set2, PrimaryDelimiter="`n" ) {
; Returns the set which is the union of the members in Set1 and Set2
; This function also removes duplicate members in the union.
; ( Removing duplicates is really the point of this function )
Set1 .= PrimaryDelimiter
If StrLen( Set2 ) > 0
Set1 .= Set2 . PrimaryDelimiter
VarSetCapacity( Set, StrLen( Set1 ) )
Stuff := StrLen( PrimaryDelimiter )
Pos := 1 - Stuff
Lead := 1
Loop
If ( 0 < Pos := InStr( Set1, PrimaryDelimiter, 1, Pos + Stuff ) )
{
StringMid, member, Set1, %Lead%, Pos - Lead
Lead := Pos + Stuff
If !InStr( Set . PrimaryDelimiter, PrimaryDelimiter . member . PrimaryDelimiter, 1 )
Set .= PrimaryDelimiter . member
}
Else Break
StringTrimLeft, Set, Set, %Stuff%
Return Set
}
Set_Zip( Set1, Set2, PrimaryDelimiter="`n", SecondaryDelimiter="," ) {
; Returns a new set where the 'N'th member is a concatenation of
; the 'N'th member of Set1, SecondaryDelimiter, and the 'N'th member of Set2
; If a set doesn't have an 'N'th member, its 'N'th member is
; considered blank.
Set1 .= PrimaryDelimiter
Set2 .= PrimaryDelimiter
VarSetCapacity( Set, StrLen( Set1 ) + StrLen( Set2 ) )
Stuff := StrLen( PrimaryDelimiter )
Pos1 := Pos2 := 1 - Stuff
Lead1 := Lead2 := Out1 := Out2 := 1
Loop
If ( 0 < Pos1 := Out1 * InStr( Set1, PrimaryDelimiter, 1, Pos1 + Stuff ) )
+ ( 0 < Pos2 := Out2 * InStr( Set2, PrimaryDelimiter, 1, Pos2 + Stuff ) )
{
If ( Out1 := ( Pos1 != 0 ) )
StringMid, member1, Set1, %Lead1%, Pos1 - Lead1
Else
member1 := ""
Lead1 := Pos1 + Stuff
If ( Out2 := ( Pos2 != 0 ) )
StringMid, member2, Set2, %Lead2%, Pos2 - Lead2
Else
member2 := ""
Lead2 := Pos2 + Stuff
Set .= PrimaryDelimiter . member1 . SecondaryDelimiter . member2
}
Else Break
StringTrimLeft, Set, Set, %Stuff%
Return Set
}
Set_SampleSet( n ) {
; Here are 8 sample sets for use in testing various parts of this library
Return n = 3 ? "
( LTRIM
False
[VxE]
True
)" : n = 4 ? "
( LTRIM
Cherry
Tangerine
Chocolate
BubbleGum
)" : n = 5 ? "
( LTRIM
Butterfinger
3 Musketeers
Milky Way
Baby Ruth
Kit Kat
)" : n = 6 ? "
( LTRIM
Puppet
Roller Skates
Guitar
Slingshot
Bandage
Handcuffs
)" : n = 7 ? "
( LTRIM
Katar
Cutlass
Gladius
Flamberge
Dirk
Stiletto
Claymore
)" : n = 8 ? "
( LTRIM
Baby Ruth
Twix
Kit Kat
Butterfinger
100 Grand
3 Musketeers
Almond Joy
Milky Way
)" : n = 9 ? "
( LTRIM
Horse
Turtle
Gibbon
Elephant
Kitten
Chicken
Hampster
Crocodile
Pigeon
)" : n = 10 ? "
( LTRIM
Rabbit
Monkey
Pony
Kitten
Puppy
Seal
Goat
Chicken
Piglet
Donkey
)" : "0`n1"
} |
[Edit: 2-10-2010] Set functions | Code: | ;
; Set functions library (Upgraded), By [VxE]
;
; Introduction:
; Sets are an important concept in OOP, data management, and
; discrete mathematics. This function library allows you to
; define sets of string objects and perform basic set functions
; on them. Additionally, this library offers two functions for
; handling multi-dimensional sets: Set_Zip joins two sets
; on a 1:1 basis and Set_Column extracts a set from a complex
; set. The real power behind this library is the ability to use
; any string as a delimiter, rather than just a single character.
; This feature makes it easy to work with data collections in
; multiple text formats and to construct multi-dimensional sets
; without limits.
;
; Definitions:
; a 'delimiter' is a substring with a length greater than zero.
; a 'member' is a string within a 'set' bounded by delimiters and/or
; the ends of the string.
; a 'set' is a string containing zero or more members, each separated
; from the previous and next member by an instance of a delimiter.
;
; Functions:
; Set_Column
; Set_Delimit
; Set_GetPos
; Set_GetPosRegEx
; Set_Intersect
; Set_Member
; Set_Remove
; Set_Size
; Set_Union
; Set_Zip
;
; Notes:
; All string comparisons are case sensitive with the exception of
; Set_GetPosRegEx, which leaves case sensitivity up to the regex.
; When using Set_GetPosRegEx, it is highly recommended to use the
; 'S' option in the needleregex since the function loops RegexMatch
; internally and therefore can be very slow.
Set_Column( Set1, column, PD="`n", SD="," ) {
; Returns a set which is comprised of the 'column'th sub-member of
; each of 'Set1's members. Essentially, if each member were like a
; row in a table, this function would pull a column out of that table.
VarSetCapacity( Set, StrLen( Set1 ))
column := Floor( column )
Set1 .= PD
Stuff := StrLen( PD )
iStuff := StrLen( SD )
Pos := 1 - Stuff
Lead := 1
Loop
If ( 0 < Pos := InStr( Set1, PD, 1, Pos + Stuff ) )
{
StringMid, member, Set1, %Lead%, Pos - Lead
Lead := Pos + Stuff
member .= SD
ilead := 1
ipos := 1 - istuff
Loop
If ( 0 < ipos := InStr( member, SD, 1, ipos + Stuff ) )
{
StringMid, imember, member, %ilead%, ipos - ilead
ilead := ipos + iStuff
If ( A_Index < column )
Continue
Set .= PD . imember
Break
}
Else Break
}
Else Break
StringTrimLeft, Set, Set, %Stuff%
Return Set
}
Set_Delimit( Set1, NewD, PD="`n" ) {
; Replaces every instance of 'PD' in 'Set1' with 'NewD', effectively
; changing the set's delimiter.
oel := ErrorLevel
StringReplace, Set1, Set1, %PD%, %NewD%, All
Return Set1, ErrorLevel := oel
}
Set_GetPos( Set1, string, PD="`n" ) {
; Returns the index of the first member of 'Set1' that is an exact
; match of 'string'. The return value is zero if there is no match.
If ( 1 >= Pos := InStr( PD . Set1 . PD, PD . string . PD, 1 ) )
Return Pos
StringLeft, Set1, Set1, % Pos - 1
Return Set_Size( Set1, PD)
}
Set_GetPosRegEx( Set1, NeedleRegex, PD="`n" ) {
; Returns the index of the first member of 'Set1' that is a match
; of 'NeedleRegex'. The return value is zero if there is no match.
; NOTE: the regex needle should not account for the delimiters.
Stuff := StrLen( PD )
Set1 .= PD
Pos := 1 - Stuff
Lead := 1
Loop
If ( 0 < Pos := InStr( Set1, PD, 1, Pos + Stuff ) )
{
StringMid, member, Set1, %Lead%, Pos - Lead
Lead := Pos + Stuff
If RegexMatch( member, NeedleRegex )
Return A_Index
}
Else Break
Return 0
}
Set_Intersect( Set1, Set2, PD="`n" ) {
; Performs a set intersection of 'Set1' and 'Set2'. Only the members
; that are in both sets will be in the resulting set.
If ( Set_Size( Set1 ) > Set_Size( Set2 ) )
Set := Set2, Set2 := Set1, Set1 := Set
VarSetCapacity( Set, StrLen( Set1 ), 0)
Set1 .= PD
Set2 := PD . Set2 . PD
Stuff := StrLen( PD )
Pos := 1 - Stuff
Lead := 1
Loop
If ( 0 < Pos := InStr( Set1, PD, 1, Pos + Stuff ) )
{
StringMid, member, Set1, %Lead%, Pos - Lead
Lead := Pos + Stuff
If InStr( Set2, PD . member . PD, 1 )
Set .= PD . member
}
Else Break
StringTrimLeft, Set, Set, %Stuff%
Return Set
}
Set_Member( Set1, Which, PD="n" ) {
; Returns the 'Which'th member of Set1. A 'Which' less than 1 is
; considered an offset from the last member ('0' being the last)
Len := Set_Size( Set1, PD )
Which := Floor( Which )
If ( Which <= 0 ) && ( Len + Which > 0 )
Which := Len + Which
If ( Which > Len ) || ( Which <= 0 )
Return ""
Set1 .= PD
Stuff := StrLen( PD )
Pos := 1 - Stuff
Lead := 1
Loop
If ( 0 < Pos := InStr( Set1, PD, 1, Pos + Stuff ) )
&& ( A_Index < which )
Lead := Pos + Stuff
Else Break
StringMid, member, Set1, %Lead%, Pos - Lead
Return member
}
Set_Remove( Set1, Set2, PD="`n" ) {
; Performs a set intersection of 'Set1' and the complement of 'Set2'
Set1 .= PD
Set2 := PD . Set2 . PD
VarSetCapacity( Set, StrLen( Set1 ) )
Stuff := StrLen( PD )
Pos := 1 - Stuff
Lead := 1
Loop
If ( 0 < Pos := InStr( Set1, PD, 1, Pos + Stuff ) )
{
StringMid, member, Set1, %Lead%, Pos - Lead
Lead := Pos + Stuff
If !InStr( Set2, PD . member . PD, 1 )
Set .= PD . member
}
Else Break
StringTrimLeft, Set, Set, %Stuff%
Return Set
}
Set_Size( Set, PD="`n" ) {
; Returns the total number of members in 'Set'. NOTE: an empty string
; always contains ZERO members, while any non-empty string contains
; a MINIMUM of ONE member.
If ( StrLen( Set ) = 0 )
Return 0
oscs := A_StringCaseSense
StringCaseSense, On
oel := ErrorLevel
StringReplace, Set, Set, %PD%, %PD%, UseErrorLevel
StringCaseSense, %oscs%
Return ErrorLevel + 1, ErrorLevel := oel
}
Set_Union( Set1, Set2, PD="`n" ) {
; Returns the set which is the union of the members in Set1 and Set2
; This function also removes duplicate members in the union.
; ( Removing duplicates is really the point of this function )
Set1 .= PD
If StrLen( Set2 ) > 0
Set1 .= Set2 . PD
VarSetCapacity( Set, StrLen( Set1 ) )
Stuff := StrLen( PD )
Pos := 1 - Stuff
Lead := 1
Loop
If ( 0 < Pos := InStr( Set1, PD, 1, Pos + Stuff ) )
{
StringMid, member, Set1, %Lead%, Pos - Lead
Lead := Pos + Stuff
If !InStr( Set . PD, PD . member . PD, 1 )
Set .= PD . member
}
Else Break
StringTrimLeft, Set, Set, %Stuff%
Return Set
}
Set_Zip( Set1, Set2, PD="`n", SD="," ) {
; Returns a new set where the 'N'th member is a concatenation of
; the 'N'th member of Set1, SD, and the 'N'th member of Set2
; If a set doesn't have an 'N'th member, its 'N'th member is
; considered blank.
Set1 .= PD
Set2 .= PD
VarSetCapacity( Set, StrLen( Set1 ) + StrLen( Set2 ) )
Stuff := StrLen( PD )
Pos1 := Pos2 := 1 - Stuff
Lead1 := Lead2 := Out1 := Out2 := 1
Loop
If ( 0 < Pos1 := Out1 * InStr( Set1, PD, 1, Pos1 + Stuff ) )
+ ( 0 < Pos2 := Out2 * InStr( Set2, PD, 1, Pos2 + Stuff ) )
{
If ( Out1 := ( Pos1 != 0 ) )
StringMid, member1, Set1, %Lead1%, Pos1 - Lead1
Else
member1 := ""
Lead1 := Pos1 + Stuff
If ( Out2 := ( Pos2 != 0 ) )
StringMid, member2, Set2, %Lead2%, Pos2 - Lead2
Else
member2 := ""
Lead2 := Pos2 + Stuff
Set .= PD . member1 . SD . member2
}
Else Break
StringTrimLeft, Set, Set, %Stuff%
Return Set
} |
_________________ Ternary (a ? b : c) guide TSV Table Manipulation Library
Post code inside [code][/code] tags!
Last edited by [VxE] on Wed Mar 03, 2010 3:26 am; edited 3 times in total |
|
| Back to top |
|
 |
[VxE]
Joined: 07 Oct 2006 Posts: 3244 Location: Simi Valley, CA
|
Posted: Fri Apr 04, 2008 7:38 pm Post subject: Wheel Spinners for Edit Boxes with Numbers in them. |
|
|
[Edit: Update ( 6-3-08 )] | Code: | ; [VxE]'s Number Wheel Script
; Updated version of number control spinner
; Hover the mouse over an edit control with a number in
; its text and the scrollwheel can change the number
SetTimer, CheckMouseOverControl, 50
*WheelUp::
*WheelDown::
CheckMouseOverControl:
MouseGetPos,,,oWin,oCon
ControlGetText, cs, %oCon%, AHK_ID %oWin%
If !InStr(oCon, "Edit") || StrLen(cs) > 199
{
If (WheelsOnOff != "off")
{
WheelsOnOff := "off"
Hotkey, *Wheelup, *WheelUp, % WheelsOnOff
Hotkey, *WheelDown, *WheelDown, % WheelsOnOff
}
}
else
{
If (WheelsOnOff != "on")
{
WheelsOnOff := "on"
Hotkey, *Wheelup, *WheelUp, % WheelsOnOff
Hotkey, *WheelDown, *WheelDown, % WheelsOnOff
}
If (A_ThisLabel = "CheckMouseOverControl")
return
InPos := OutPos := 0 ; operate on first number found
Loop, Parse, cs
{
np := Asc(A_LoopField)
If ( np >= 45 && np <= 57 && np != 47 )
If ( InPos = 0 )
InPos := OutPos := A_Index
else
OutPos := (A_Index-Outpos-1) ? OutPos : A_Index
}
np := SubStr( cs, InPos, OutPos - InPos + 1 )
tic := (InStr(np, ".") ? np/20 : Ceil(np/20))
If (!GetKeyState("Shift") || !tic )
tic := 1
If (GetKeyState("Ctrl"))
tic := 0.001 + GetKeyState("shift") * 0.024
np -= tic * ((!InStr(A_ThisHotkey, "up")) * 2 - 1 )
If InStr( np, "." )
Loop 9
If !SubStr(np,0)
StringTrimRight, np, np, 1
else
break
ControlSetText, %oCon%, % SubStr(cs,1,InPos-1) np SubStr(cs,OutPos+1),AHK_ID %oWin%
}
return |
new version (above) replaces old version (below).
Changes 1: Uses a timer to watch the mouse cursor to see if it's over a valid control, then if it is, the wheel keys become active and block the wheel events from reaching the target window, thus preventing any native wheel support the target app might have for such controls. To bypass this, simply put the control in focus and move the cursor off the control before using the mousewheel (that allows the wheel event to reach the app's window).
2: To adjust the rate faster (5%), hold [shift] while scrolling. To force adjustments by 0.001, hold [ctrl]. To force an increment of 0.025, hold [ctrl][shift].
3: This script is meant to make adjusting numbers in edit controls easier for someone using a laptop (no numpad) and a logitech mouse with flywheel scrolling.
[End Edit]
Numerical Control Spinner. This script simulates the ability of the scroll wheel to adjust numbers in edit boxes. This behavior is commonly seen in programs like photoshop, where there are a number of edit boxes for brush settings and stuff. This script operates on the control that is currently under the mouse cursor, so if an edit box already has the wheelie change built in, this script will interfere with that (just don't hover the mouse over those controls and it will work OK)
The Default increment is 1, although I have the script set to increase this increment to 5% of the current value if the user waits for 2 seconds between two wheel ticks. The incrementing value can be reset to 1 by activating any other non-wheel hotkey.
Additional Support Idea for Future Update: Support selecting a number in text and changing it with the scrollwheel.
| Code: | ; Auto spinners for numerical controls
~*LButton::return
~*WheelUp::
~*WheelDown::
MouseGetPos, X, Y, oWin, oCon
If !InStr(oCon, "Edit") ; we only want spinners to work on edit
return ; controls, not for things like buttons and such
ControlGetText, gvar, %oCon%, AHK_ID %oWin%
ovar := gvar ; chop up a copy
Loop 8 ; provide accomodation to remove stupid words after numbers in the
If ( ovar + 1 = "" ) ; edit box like "inches" or "pixels"
ovar := SubStr(ovar, 1, -1)
else
break
If ( ovar + 1 != "" ) ; so, our edit control contains a number, brilliant!
{
gvar := SubStr(gvar, StrLen(ovar)) ; remove the number part
If !InStr(A_PriorHotkey, "~*Whee") ; use of a non-wheel hotkey will
uinc := 1 ; reset the step size to 1
Else If A_TimeSincePriorHotkey > 2000 ; wait for 2 seconds to set the
uinc := ovar // 20 ; step size to approx 5% of the current value
ovar += ((A_ThisHotkey = "~*WheelUp")*2-1) * uinc * ((uinc > 0)*2-1) + (uinc + 0.0 = 0)
ControlSetText, %oCon%, % ovar gvar, AHK_ID %oWin% ; note the %ovar%%gvar%...
}
return |
_________________ Ternary (a ? b : c) guide TSV Table Manipulation Library
Post code inside [code][/code] tags!
Last edited by [VxE] on Wed Jun 04, 2008 6:37 am; edited 1 time in total |
|
| Back to top |
|
 |
[VxE]
Joined: 07 Oct 2006 Posts: 3244 Location: Simi Valley, CA
|
Posted: Fri Apr 04, 2008 7:47 pm Post subject: [VxE]'s Encryption With GUI |
|
|
My Encryption Function... this examples has a simple gui but you can strip out the functions if you'd like. The function's parameters are as follows: str, str, bool.
The first parameter is the key (i.e. password), the second is the message, the third is a switch to specify wither forwards or backwards.
I have added another function () for users who desire greater readability. All that function does is call the main function with the 3rd parameter set to "1".
The only difference between 'encryption' and 'decryption' is the direction of the algorithm. In other words, 'decryption' is the inverse function of 'encryption' and vice versa ad infinitum.
| Code: | ; [VxE]'s Double-Dynamic Key-Based Encryption Script
; A fully functional security script for protecting text
; version 1.b, Optimized key handling for large keys
; last edit: [7-10-08]
#SingleInstance force
wid := A_ScreenWidth // 2
bwid := (wid - 100) // 5
cwid := wid // 5
dwid := wid - 100
edh := A_ScreenHeight //100
Gui, font, s16, Arial
Gui, Add, Text, w90, Key
Gui, Add, Edit, X100 R1 vMyKey Y10 W%dwid%
Gui, Add, Edit, +wrap R%edh% vInMessage X10 W%wid%
Gui, Add, Edit, Disabled +wrap R%edh% vOutMessage X10 W%wid%
Gui, Add, Button, w%bwid% x25 gEncrypt, Encrypt
Gui, Add, Button, w%bwid% yp xp+%cwid% gDecrypt, Decrypt
Gui, Add, Button, w%bwid% yp xp+%cwid% gSwap, Swap Fields
Gui, Add, Button, w%bwid% yp xp+%cwid% gcopy, Copy Text
Gui, Add, Button, w%bwid% yp xp+%cwid% gGuiClose, &Quit
Gui, Show, AutoSize, [VxE]'s Double-Dynamic Key-Based Encryption Function
return
GuiClose:
GuiEscape:
exitapp
copy:
Swap:
GuiControlGet, OutMessage,,
If A_ThisLabel = copy
StringReplace, clipboard, OutMessage, `n, `r`n, all
else
{
GuiControlGet, InMessage,,
StringReplace, OutMessage, OutMessage, `r`n, `n, all
StringReplace, InMessage, InMessage, `r`n, `n, all
GuiControl,, InMessage, %OutMessage%
GuiControl,, OutMessage, %InMessage%
}
return
Decrypt:
Encrypt:
GuiControlGet, MyKey,,
GuiControlGet, InMessage,,
GuiControl,, OutMessage, % Encrypt( mykey, InMessage, A_ThisLabel )
return
; Readability consideration, example:
; result := Decrypt( key, message ) ; will attempt to decrypt the message
Decrypt( key, message, optional = "" )
{
return Encrypt( key, message, !optional )
}
; Param 3 is a binary switch to determine this function's direction.
; [blank], [0], or ["e*"] yields forwards, anything else yields backwards.
; Readability consideration: simply omit the third parameter and use "Decrypt()" to decrypt.
Encrypt(key, message, CryptEvent = "")
{ ; CryptEvent defaults to 'encrypt', but really only the first letter is needed
LowerBound = 34
UpperBound = 126
StringReplace, Message, Message, `r`n, `n, all
StringLeft, CryptEvent, CryptEvent, 1
If !CryptEvent
CryptEvent = e
Loop, parse, key
key .= "`n" Asc(A_LoopField)
key := SubStr(key, InStr(Key, "`n",0,1)+1)
NewMessage =
Loop, Parse, Message
{
code := Asc(A_LoopField)
If ( code >= LowerBound ) && ( code <= UpperBound )
{
mco := code
prevc := A_Index
newkey := ""
Loop, Parse, key, `n
code := Clamp(( code - (A_LoopField * ( Mod(A_LoopField, 2)
* 2 - 1 )) * ((CryptEvent = "e") * 2 - 1) ), LowerBound, UpperBound )
If CryptEvent = e
mco := code
Loop, Parse, key, `n
newkey .= "`n" (prevc := Clamp((A_LoopField + mco * (Mod((mco
+Prevc), 2) * 2 - 1) + Prevc), 1, 255))
key := SubStr( newkey, 2 )
}
NewMessage .= Chr(code)
}
return % NewMessage
}
clamp( number, low, high ) ; altered mutation of Mod() function
{ ; 7-10-2008 edit:
span := (high - low + (high > low)*2 - 1)
return number + span * Round(((low-number)/2 + (high-number)/2)/span)
} |
_________________ Ternary (a ? b : c) guide TSV Table Manipulation Library
Post code inside [code][/code] tags!
Last edited by [VxE] on Thu Jul 10, 2008 9:10 pm; edited 3 times in total |
|
| Back to top |
|
 |
[VxE]
Joined: 07 Oct 2006 Posts: 3244 Location: Simi Valley, CA
|
Posted: Fri Apr 04, 2008 7:52 pm Post subject: Joystick to Numberpad |
|
|
G-Functions: make your gui controls trigger a function in the same manner as they would trigger a g-label.
| Code: | ; G-Functions: Set up gui controls with a function instead of a g-label. This technique
; was proposed by yanjchan # http://www.autohotkey.com/forum/viewtopic.php?t=53227
; though there have been several threads asking for functions to be associated with
; gui controls the way g-labels can. Format a gui control's options like this example:
; Gui, Add, Edit, gGFunctionHandler vFunction[%A_Now%?#this#?#A_GuiControl#]
; In this example, 'Function' is the name of a function in the script, %A_Now% is
; resolved when the control is added to the gui. #this# is resolved when the g-label
; triggers (and it becomes the contents of the control, via GuiControlGet). Similarly,
; #A_GuiControl# is resolved when the g-label triggers and it becomes the exact string
; following the 'v' in the example. The question marks delimit the parameters the way
; commas delimit parameters in a function call.
GFunctions() { ; For library inclusion, name this file "GFunctions.ahk".
; Calling this function returns the name of the label needed to use g-functions.
Return "GFunctionHandler"
}
GFunctionHandler:
Critical
ErrorLevel := GFunctionHandler(A_GuiControl)
If InStr( ErrorLevel, "Error: " ) = 1
MsgBox, %ErrorLevel%
Return
GFunctionHandler(¿f) {
; This function examines a control's variable name and constructs a function
; call with parameters based on it.
Local ¿0, ¿1, ¿2, ¿3, ¿4, ¿5, ¿6, ¿7, ¿8, ¿9, ¿10, ¿11, ¿12, ¿13
, ¿14, ¿15, ¿16, ¿17, ¿18, ¿19, ¿20, ¿21, ¿22, ¿23, ¿24, ¿25, ¿26
, ¿27, ¿28, ¿29, ¿30, ¿31, ¿32, ¿33, ¿34, ¿35, ¿36, ¿37, ¿38, ¿39
, ¿40, ¿41, ¿42, ¿43, ¿44, ¿45, ¿46, ¿47, ¿48, ¿49, ¿50, ¿51, ¿52
, ¿53, ¿54, ¿55, ¿56, ¿57, ¿58, ¿59, ¿60, ¿61, ¿62, ¿63, ¿64, this
If RegexMatch( ¿f, "[^\w@$?#\[\]\x80-\xff]")
Return "Error: Invalid G-Function"
; error: the input contains an illegal character
GuiControlGet, ¿0, HWND, %¿f% ; get the HWND of the control
VarSetCapacity( ¿1, 256, 0 ) ; make space in this variable
; get the control class name for this control
DllCall("GetClassName", "uint", ¿0, "str", ¿1, "int", 255 )
If ( ¿1 == "SysListView32" ) ; it's a listview
Gui, Listview, %¿f%
Else If ( ¿1 == "SysTreeView32" ) ; it'a a treeview
Gui, Treeview, %¿f%
Else If ( ¿1 != "" ) ; Load the reserved variable 'this'
GuiControlGet, this,, %¿f%
If InStr( ¿f, "]", 0, 0 ) == StrLen( ¿f )
{ ; parse the parameters listed in the g-function
¿0 := SubStr( ¿f, ¿1 := InStr( ¿f, "[" ) + 1, -1 )
StringLeft, ¿f, ¿f, % ¿1 - 2
¿1 := ""
Loop, Parse, ¿0, ?
{
StringReplace, ¿0, A_LoopField, #, `%, all
Transform, ¿%A_Index%, DEREF, %¿0%
If ( 64 <= ¿0 := A_Index ) ; we have a hard limit of 64 parameters
Break
}
}
Else ; the g-function specifies no parameters
¿0 := 0
If StrLen( ¿f ) == 0
Return "Error: The G-Function name is blank"
StringReplace, ¿f, ¿f, #, #, UseErrorLevel
If !Mod( ErrorLevel, 2 ) && Errorlevel > 0
{
StringReplace, ¿f, ¿f, #, `%, All
Transform, ¿f, DEREF, %¿f%
}
; ¿0 contains the number of parameters in the g-function. Now check the function name
If ( 0 < this := IsFunc( ¿f )) ; if the function exists...
&& ( this-1 <= ¿0 ) ; ... and we have enough parameters, then do it
Return %¿f%( ¿1, ¿2, ¿3, ¿4, ¿5, ¿6, ¿7, ¿8, ¿9, ¿10, ¿11, ¿12, ¿13
, ¿14, ¿15, ¿16, ¿17, ¿18, ¿19, ¿20, ¿21, ¿22, ¿23, ¿24, ¿25, ¿26
, ¿27, ¿28, ¿29, ¿30, ¿31, ¿32, ¿33, ¿34, ¿35, ¿36, ¿37, ¿38, ¿39
, ¿40, ¿41, ¿42, ¿43, ¿44, ¿45, ¿46, ¿47, ¿48, ¿49, ¿50, ¿51, ¿52
, ¿53, ¿54, ¿55, ¿56, ¿57, ¿58, ¿59, ¿60, ¿61, ¿62, ¿63, ¿64 )
Return this ? "Error: Not enough parameters in the g-function"
: "Error: The g-function does not exist"
} |
_________________ Ternary (a ? b : c) guide TSV Table Manipulation Library
Post code inside [code][/code] tags!
Last edited by [VxE] on Thu Feb 11, 2010 7:29 am; edited 4 times in total |
|
| Back to top |
|
 |
[VxE]
Joined: 07 Oct 2006 Posts: 3244 Location: Simi Valley, CA
|
Posted: Fri Apr 04, 2008 8:11 pm Post subject: Clipboard Execute |
|
|
A more recent manifestation of one of my earlier scripts. This script attempts to run the contents of the clipboard as an AHK script with added safeguards and self-cleaning features.
Update: 1-10-10. New version (5).
Features:
- Any number of files may be included (via #Include) in the script generated by the ClipEx function. See the comments regarding the 'ClipEx_Includes' variable
- Certain keywords are under the 'Blacklist' variable, if any of those words are found on the clipboard, a confirmation dialogue will ask whether you wish to run the code anyways.
- The script file that is generated each time the function runs is deleted if it is no longer running.
- Script files are given unique names
- When executing code with a gui, the label "GuiClose" is added if it isn't already in the code. The label is followed by "Exitapp"
- The format of this code is flow-neutral, meaning that it will not interrupt the Auto-Execute section if placed there (or anywhere else, for that matter).
| Code: | ; ClipEx5
; Clipboard Execute, version 5 (by [VxE])
; To use: call the function ClipEx() from anywhere in your script.
; Reserved global variable: "ClipEx_p" -> contains a list of active PIDs and script file paths.
; Reserved label: "ClipEx_check_existence" -> parses "ClipEx_p" and cleans up once scripts have finished.
ClipEx()
{
Global ClipEx_p
Static IsInitialized, ClipEx_Lifespan, ClipEx_CancelKey, Keywords_Blacklist
, ClipEx_Includes, Blacklist_Warning, Write_Run_Delay
If ! IsInitialized
{ ; begin static initialization
Write_Run_Delay = 50 ; milliseconds to wait between writing the clipex file and running it
ClipEx_Lifespan = 60 ; seconds until the script terminates unconditionally
ClipEx_CancelKey = Esc ; put a hotkey here to be a killswitch for the script
Keywords_Blacklist =
( ltrim C
; ClipEx will show a confirmation MsgBox before running code which contains any of these words.
regwrite
filedelete
A_Startup
shutdown
; onexit
UrlDownloadToFile
)
Blacklist_Warning =
( ltrim % join`s
The word%plural% "%Matched_Blacklist%" %was_were%
found in the clipboard.`nThis could cause
undesired or malicious effects if run.
`n`nDo you wish to continue ?
)
ClipEx_Includes =
( ltrim C
; List here all files, or directories containing .ahk files, that should be
; #included in the ClipEx script. There is no message when any of these fails.
; These are checked / updated on the fly, and non-absolute paths use the current working directory
%A_MyDocuments%\AutoHotkey\MyProject\Functions ; default example
; Also see 'libraries of functions' in AutoHotkey.chm
)
IsInitialized = 1
} ; end static initialization
Thread, NoTimers ; cuz we don't need interruptions
; Use the current working direcrtory for non-absolute paths to #include
ClipEx_included := ""
Loop, Parse, ClipEx_Includes, `n, `r %A_Tab%
If InStr( ClipEx_m := FileExist( A_LoopField ), "D" ) ; if the file is a directory
Loop, % A_LoopField . ( SubStr( A_LoopField, 0 ) = "\" ? "" : "\" ) . "*.ahk"
ClipEx_included .= "`r`n#Include, " A_LoopFileLongPath
Else If (ClipEx_m)
ClipEx_included .= "`r`n#Include, " A_LoopField
ClipEx_Template = ; load the template
( ltrim
; [VxE]'s ClipEx, version 5.
; This file was generated by inserting the clipobard's text into a template
SetTimer, ClipEx_Cleanup, -%ClipEx_LifeSpan%000
Hotkey, %ClipEx_CancelKey%, ClipEx_Cleanup, on
; This is the beginning of the clipboard's text
%Clipboard%
; This is the end of the clipboard's text.
ClipEx_Cleanup: ; this label is called by a timer set at the top of the script
critical
Exitapp
; #Included Files
%ClipEx_included%
)
; Automatically replace the cleanup label with 'guiclose' if the clipboard contains
; 'Gui' but does not contain 'guiclose'. This is simply a convenience so that the
; script will exit when you close the clipex Gui, instead of simply hiding the gui
; and waiting for its lifespan to run out
If InStr( ClipEx_Template, "Gui" ) && !InStr( ClipEx_Template, "GuiClose:" )
StringReplace, ClipEx_Template, ClipEx_Template, ClipEx_Cleanup, GuiClose, All
; Check the script text for any blacklisted words
Matched_Blacklist := "", plural := 0
Loop, parse, Keywords_Blacklist, `n
If InStr(ClipEx_Template, A_LoopField)
Matched_Blacklist .= A_Tab . A_LoopField, plural++
StringTrimLeft, Matched_Blacklist, Matched_Blacklist, 1
Loop, % plural - 2 ; replace all but the last delimiter with ", "
StringReplace, Matched_Blacklist, Matched_Blacklist, %A_Tab%, % """, """
; Replace the last delimiter with " and ". This is only a superficial feature
StringReplace, Matched_Blacklist, Matched_Blacklist, %A_Tab%, " and "
If plural ; if there are more then zero blacklisted words in the script
{
was_were := plural = 1 ? "was" : "were" ; what it is
plural := plural = 1 ? "" : "s" ; gramMaR!
Transform, text, DEREF, %Blacklist_Warning%
MsgBox, 52, Security Warning !!, %text% ; show a msgbox with the warning
IfMsgBox, No ; the user decided not to run the code after all
return
}
ClipEx := "ClipEx" . A_Now . SubStr( A_TickCount, -2 ) . ".ahk"
FileAppend, %ClipEx_Template%, %ClipEx%
Sleep %Write_Run_Delay%
Run, %A_AhkPath% "%ClipEx%",,, ClipEx_m ; keep the PID of the spawned script
ClipEx_p .= ClipEx_m . "?" . ClipEx . "`n" ; append it to our list
SetTimer, ClipEx_check_existence, 200
return
} ; end of ClipEx() function
Loop 0 ; this causes script execution to skip the following block
{
ClipEx_check_existence:
; Check our spawned ClipEx processes. Once the process terminates, delete the ClipEx file
DetectHiddenWindows, on
If ( ClipEx_p = "`n" )
SetTimer, ClipEx_check_existence, off
Else
Loop, Parse, ClipEx_p, `n ; parse through the list
{
StringSplit, ClipEx_, A_LoopField, ?
If ClipEx_0 <> 2
continue
If !WinExist( "Ahk_PID " ClipEx_1 ) ; if the script's window can't be found
{
StringReplace, ClipEx_p, ClipEx_p, `n%A_LoopField%`n, `n
FileDelete, %ClipEx_2% ; delete it and remove it from our list
}
}
return
} |
_________________ Ternary (a ? b : c) guide TSV Table Manipulation Library
Post code inside [code][/code] tags!
Last edited by [VxE] on Sun Jan 10, 2010 9:50 pm; edited 6 times in total |
|
| Back to top |
|
 |
[VxE]
Joined: 07 Oct 2006 Posts: 3244 Location: Simi Valley, CA
|
Posted: Fri Apr 04, 2008 8:32 pm Post subject: Online Dictionary Easy Lookup Script |
|
|
Don't know what a word means? Just Control+Double-Click it (while this script is running)
The variable at the top (page) contains the URL for the results page of a dictionary or search page, some other popular ones are listed in comments below. To use a different search site, simply go to that site, type in a unique word into the search field, click 'search', then get the URL from the results page and replace your unique word with []MySearchTarget[] and put that into the script.
| Code: | ; [VxE]'s Ctrl+DblClick word lookup script
page = http://dictionary.cambridge.org/results.asp?searchword=[]MySearchTarget[]&x=48&y=1
; page = http://www.merriam-webster.com/dictionary/[]MySearchTarget[]
; page = http://www.google.com/search?hl=en&q=[]MySearchTarget[]&btnG=Google+Search
; page = http://search.yahoo.com/search?p=[]MySearchTarget[]&fr=yfp-t-501&toggle=1&cop=mss&ei=UTF-8
; page = http://en.wikipedia.org/wiki/Special:Search?search=[]MySearchTarget[]&fulltext=Search
~^LButton::
MouseGetPos, px, py
pm := px . py
If ( A_PriorHotkey = A_ThisHotkey )
&& (A_TimeSincePriorHotkey < DllCall("GetDoubleClickTime"))
&& (om = pm) ; if the mouse moved, it wasn't a double-click
{
Sleep 150
sclip := clipboardall
Clipboard := ""
Send ^c
Clipwait, 1
StringReplace, specpage, page, []MySearchTarget[], %Clipboard%
Clipboard := sclip
Run %specpage% ; open page in default browser
}
Else
om := pm
return |
_________________ Ternary (a ? b : c) guide TSV Table Manipulation Library
Post code inside [code][/code] tags! |
|
| Back to top |
|
 |
[VxE]
Joined: 07 Oct 2006 Posts: 3244 Location: Simi Valley, CA
|
Posted: Fri Apr 04, 2008 8:35 pm Post subject: Unicode in gui controls |
|
|
These functions are workarounds for using unicode in various circumstances. They are as follows: | Quote: | A2W( byref string ) - converts an ansi string that contains html-unicode into a bona fide wide-char string. Note that AHK will no longer be able to manipulate it as a string.
ControlSetTextW( Control OR HWND, string ) - Converts 'string' into a wide-char string and sets the designated control's text to that string.
ControlGetTextW( Control OR HWND ) - Retrieves wide-char text from a control and converts it into a mix of ascii and html-type character codes.
FileReadU( filepath ) - Returns the text inside the designated .txt file. It can handle files that contain unicode characters by converting such characters into their html-type codes. |
| Code: | A2W( byref str ) ; Ascii to Wide-char. Version 2.0
{ ; function by [VxE], changes a string into unicode format. Uses html-style
; codes for unicode characters (i.e. ሴ or 〹 ). Intended for
; external functions (via DllCall). Return value is the number of characters.
text := str
u := v := pos := 0
Loop
If (pos := InStr( text, "&#", 0, pos+1 ))
&& (pos2 := InStr( text, "`;", 0, pos ))
{
If (num := Abs("0" . SubStr( text, pos+2, pos2-pos-2 )))
{
u++
u%u% := num
StringReplace, text, text, % SubStr(text, pos, pos2-pos+1), % chr(4)
}
}
Else Break
VarSetCapacity(str, 2+2*len:=StrLen(text), 0) ; make space and wipe bits
Loop, Parse, text
If (num := Asc(A_LoopField)) = 4
v++, NumPut( u%v%, str, 2*A_Index-2, "UShort" )
Else NumPut( num, str, 2*A_Index-2, "UShort" )
return len
} |
| Code: | ; Unicode ControlSetText. Note: when using a ClassNN as the 'control' parameter
; this function tries to find it on the Last Found Window. Preceed any such calls
; with a command that updates the Last Found Window, such as IfWinExist
ControlSetTextW( control, newtext="" ) ; For best results, use the control's hwnd as the 'control' parameter
{
If control is INTEGER
hwnd := control
Else ControlGet, hwnd, hwnd,, %control%
A2W( newtext )
Return DllCall("SendMessageW", "uint", hwnd, "int", 0xC, "int", 0, "int", &newtext )
} |
| Code: | ; Unicode ControlGetText. Note: when using a ClassNN as the 'control' parameter
; this function tries to find it on the Last Found Window. Preceed any such calls
; with a command that updates the Last Found Window, such as IfWinExist
ControlGetTextW( control ) ; For best results, use the control's hwnd as the 'control' parameter
{
Static SizeLimit := 65536 ; aka 64 KB
If control is INTEGER
hwnd := control
Else ControlGet, hwnd, hwnd,, %control%
oif := A_FormatInteger
SetFormat, IntegerFast, D
VarSetCapacity( text, SizeLimit, 0 )
Size := DllCall("SendMessageW", "uint", hwnd, "int", 0xD, "int", SizeLimit//2, "int", &text )
VarSetCapacity( control, Size, 0 ) ; because of gradual concatenation
Loop %Size%
If ( Num := NumGet( text, 2*A_Index-2, "UShort" ) )
If ( Num < 0x100 )
control .= Chr(Num)
Else
control .= "&#" Num "`;"
SetFormat, IntegerFast, %oif%
return control
} |
| Code: | ; Save to file named "FileReadU.txt" in a lib folder or resource folder
; This function set reads notepad's .txt files and replaces any unicode
; characters with their html codes (i.e. "〹" )
FileReadU( filepath, ForceType="Auto: BOM" ) ; reads a txt file (notepad) even if it is in unicode format
{ ; function by [VxE], Version 1.3. Read the file and its BOM. Then, based on the BOM
; decode any unicode ( if there is any ). Supports all 4 of notepad's available formats,
; including Big-Endian Unicode and UTF-8.
; For filetypes without a BOM, specify ForceType = "UTF-8" or "Unicode" or "Big-endian Unicode"
;
FileGetSize, size, %filepath%
FileRead, file, %filepath%
oif := A_FormatInteger
SetFormat, IntegerFast, D
Type := NumGet( file, 0, "UShort" )
T2 := NumGet( file, 2, "UChar" )
ErrorLevel := Size
If Instr( ForceType, "Big" )
ErrorLevel := TxUnicodeBe( file, 0, size )
Else If InStr( ForceType, "UTF-8" ) = 1
ErrorLevel := TxUTF_8( file, 0, size )
Else If InStr(ForceType, "Unicode") = 1
ErrorLevel := TxUnicodeLe( file, 0, size )
Else If ( Type = 0xBBEF ) && ( T2 = 0xBF ) ; UTF-8 BOM
ErrorLevel := TxUTF_8( file, 2, size )
Else If (Type = 0xFEFF) ; 'normal' unicode BOM
ErrorLevel := TxUnicodeLe( file, 2, size )
Else If (Type = 0xFFFE) ; big-endian unicode BOM
ErrorLevel := TxUnicodeBe( file, 2, size )
SetFormat, Integer, %oif%
Return file
}
TxUTF_8( byref data, offset=3, size=0 ) ; Transcode a bitstream as though it were
{ ; function by [VxE]. Decode a UTF-8 string
VarSetCapacity( text, size+0 ? size : VarSetCapacity( data ), 0 )
Index := 0
Loop
If ( ch := NumGet( data, offset++, "UChar" ) )
If ( 2 = ( ch >> 6 ) ) ; this byte is part of a code sequence
code := (code << 6) | ( ch & 0x3F )
Else
{
If code
text .= code < 0x100 ? Chr(code) : "&#" code "`;"
code := 0
Index++
If ( 0x6 = ( ch >> 5 ) ) ; the beginning of a 2-byte sequence
code := ch & 0x1F
Else If ( 0xE = ( ch >> 4 ) ) ; the beginning of a 3-byte sequence
code := ch & 0xF
Else If ( 0x1E = ( ch >> 3 ) ) ; the beginning of a 4-byte sequence
code := ch & 0x7
Else text .= Chr( ch )
}
Else
{
data := text . ( code ? "&#" code "`;" : "" )
return A_Index - 1
}
}
TxUnicodeBe( byref data, offset=2, size=0 )
{ ; function by [VxE]. Decode a big-endian unicode string
VarSetCapacity( text, size+0 ? size : VarSetCapacity( data ), 0 )
Loop
If ( ch := NumGet( data, 2*A_Index-2+offset, "UShort" ) )
If !( ch & 0xFF )
text .= Chr(ch >> 8)
Else text .= "&#" (ch >> 8) | ((ch & 255) << 8) "`;"
Else
{
data := text
return A_Index-1
}
}
TxUnicodeLe( byref data, offset=2, size=0 )
{ ; function by [VxE]. Decode a little-endian unicode string
VarSetCapacity( text, size+0 ? size : VarSetCapacity( data ), 0 )
Loop
If ( ch := NumGet( data, 2*A_Index-2+offset, "UShort" ) )
If ( ch < 0x100 )
text .= Chr(ch)
Else text .= "&#" ch "`;"
Else
{
data := text
return A_Index-1
}
} |
_________________ Ternary (a ? b : c) guide TSV Table Manipulation Library
Post code inside [code][/code] tags!
Last edited by [VxE] on Sun Aug 09, 2009 8:43 pm; edited 10 times in total |
|
| Back to top |
|
 |
[VxE]
Joined: 07 Oct 2006 Posts: 3244 Location: Simi Valley, CA
|
Posted: Fri Apr 04, 2008 8:49 pm Post subject: Vector Function Library |
|
|
This is my vector function library. I wrote it because I needed easy vector support for some of my other projects. I tried to optimize these functions for speed without sacrificing ease of use. I'm also working on a matrix function library as a companion to this vector lib.
| Code: | ; [VxE]'s vector function library [10-10-08]
; A collection of vector functions
; A formal vector has the format
; ##, ##, ##, ##;
; where ## indicates a real number.
; Formalized vector format is used internally to
; increase efficiency. Formalization optimizations
; are not strictly for internal use, if your input
; vectors are in the formal format, you may specify
; 1 as the second parameter to increase performance.
; Supported operations currently include:
; Float Vect_Item( Vector Index )
; Float Vect_Len( Vector )
; Float Vect_Dot( Vector . Vector )
; Float Vect_Angle(["deg"] Vector . Vector )
; Vector Vect_Formal( string )
; Vector Vect_Norm( Vector )
; Vector Vect_Sub( Vector ... Vector )
; Vector Vect_Add( Vector ... Vector )
; Vector Vect_Mult( Vector . Float )
; Vector Vect_Proj( Vector . Vector )
; Vector Vect_Reflect( Vector . Vector )
; Vector Vect_Cross( Vector . Vector )
;
; Returns the Nth member of the input vector
; NOTE: seperate the vector and the index
; by a semicolor or a newline character
; When using a formal vector, you may
; simply use the following format:
; result := Vect_Item( FormalVector . 3, 1 )
; This function will probably never be used
; because you can just use StringSplit and
; put all of the vector's members into vars
Vect_Item(vect, formal=0)
{
IfNotEqual, formal, 1
{
Loop, Parse, vect, `;`n, %A_Space%%A_Tab%`r`,
IfLess, A_Index, 3, SetEnv, factor%A_Index%, % Vect_Formal(A_LoopField)
Else Break
factor2 := SubStr(Factor2, 1, -1+InStr(Factor2, ","))
} Else StringSplit, factor, vect, `;, %A_Space%%A_Tab%`r`n`,
Loop, Parse, factor1, `,, %A_Space%`;
IfEqual, A_Index, % Round( factor2 ), return %A_LoopField%
return ""
}
; Returns the absolute length of the input vector
Vect_Len(vect, formal=0)
{
off := A_FormatFloat
SetFormat, Float, 0.15
IfNotEqual, formal, 1, SetEnv, vect, % Vect_Formal(vect)
Loop, Parse, vect, `,, %A_Space%`;
sum += A_LoopField**2
SetFormat, Float, %off%
return sqrt(sum)
}
; returns the dot product of the first two vectors.
; the dot product is an easy test of orthogonality
; if the dot product is zero, the two vectors are
; orthogonal (at a right-angle to each other)
Vect_Dot(vlist, formal=0)
{
off := A_FormatFloat
SetFormat, Float, 0.15
IfNotEqual, formal, 1, Loop, Parse, vlist, `;`n, %A_Space%%A_Tab%`r`,
{
IfEqual, A_LoopField,, Continue
IfEqual, True, % !(vect := Vect_Formal(A_LoopField)), continue
u++
StringSplit, v%u%d, vect, `,, %A_Space%%A_Tab%`r`;
IfLess, vorder, % v%u%d0, SetEnv, vorder, % v%u%d0
}
else Loop, Parse, vlist, `;`n, `r%A_Space%%A_Tab%
{
IfEqual, A_LoopField,, Continue
u++
StringSplit, v%u%d, A_LoopField, `,, `n%A_Space%
IfLess, vorder, % v%u%d0, SetEnv, vorder, % v%u%d0
}
Loop %vorder%
EnvAdd, acc, v1d%A_Index% * v2d%A_Index%
SetFormat, Float, %off%
return acc + 0
}
; returns the angle between the first two vectors in vlist
; Essentially, return the arccosine of the dot product of the
; normals of two vectors.
; default return value is in radians, specify "deg" before
; any vectors to return the angle in degrees. Example:
; ResultInDegrees := Vect_Angle( "deg" Vector1 Vector2 )
Vect_Angle(vlist, formal=0)
{
off := A_FormatFloat
SetFormat, Float, 0.15
deg := u := 1
IfEqual,True, % InStr(vlist,"deg")
SetEnv,vlist, % SubStr(vlist,53.3-deg:=45/ATan(1))
IfNotEqual, formal, 1, Loop, Parse, vlist, `;`n, %A_Space%%A_Tab%`r`,
{
IfEqual, A_LoopField,, Continue
IfEqual, True, % !(vect%u% := Vect_Formal(A_LoopField)), continue
else u++
}
else Loop, Parse, vlist, `;`n, `r%A_Space%%A_Tab%
IfEqual, A_LoopField,, Continue
else vect%u% := A_LoopField ";", u++
angle := ACos( Vect_Dot( Vect_Norm( vect1, 1 ) . Vect_Norm( vect2, 1 ), 1 ) )
SetFormat, Float, %off%
return angle * deg
}
; Formalize a string into a vector by parsing through
; any numeric characters (includes '-' and '.') and
; appending them according to the following rules:
; 0. Characters listed in the second parameter
; are ignored (for instance: commas in large numbers)
; 1. The first semicolon or newline reached marks
; the end of the input string. This is to stop multiple
; vectors from being formalized into a frankenvector
; 2. all non-numeric chars become item breaks
; 3. an item break preceeds every minus sign
; 4. within an unbroken numeric string, the second
; decimal point is dropped along with all chars
; following it until the next item break is reached
; 5. There are no empty vector members unless the
; input contains no numeric characters.
; NOTE: If there is a way to do this in one line
; with regex, I'm not interested! Either release
; it or append it to this library yourself.
Vect_Formal(string, ignorechars="")
{
Loop, Parse, string, , %ignorechars%
If A_LoopField !=
IfEqual, A_LoopField, `;, break
else nv .= (u := InStr("-.1234567890", A_LoopField)) ? (u
=1 ? " " : "") A_LoopField : " "
Loop, Parse, nv, %A_Space%
IfEqual, A_LoopField,, Continue
Else IfNotEqual, A_LoopField, -
{
stringsplit, q, A_LoopField, .
vect .= ", " q1 + 0 (q0>1 ? "." q2 : "")
}
return StrLen(vect) > 3 ? SubStr(vect,3) ";" : ""
}
; Returns the normal of the input vector
Vect_Norm(vect, formal=0)
{
off := A_FormatFloat
SetFormat, Float, 0.15
IfNotEqual, formal, 1, SetEnv, vect, % Vect_Formal(vect)
len := Vect_Len(vect, 1)
Loop, Parse, vect, `,, %A_Space%`;
nv .= ", " A_LoopField / len
SetFormat, Float, %off%
return substr(nv,3) ";"
}
; Returns the sum of the first vector and the negatives
; of all subsequent vectors in the list
Vect_Sub(vlist, formal=0)
{
off := A_FormatFloat
SetFormat, Float, 0.15
IfNotEqual, formal, 1, Loop, Parse, vlist, `;`n, %A_Space%%A_Tab%`r`,
{
IfEqual, A_LoopField,, Continue
IfEqual, True, % !(vect := Vect_Formal(A_LoopField)), continue
u++
StringSplit, v%u%d, vect, `,, %A_Space%%A_Tab%`r`;
IfLess, vorder, % v%u%d0, SetEnv, vorder, % v%u%d0
}
else Loop, Parse, vlist, `;`n, `r%A_Space%%A_Tab%
{
IfEqual, A_LoopField,, Continue
u++
StringSplit, v%u%d, A_LoopField, `,, `n%A_Space%
IfLess, vorder, % v%u%d0, SetEnv, vorder, % v%u%d0
}
Loop %vorder%
{
v := A_Index
Loop %u%
IfEqual, A_Index, 1, SetEnv, acc, % v1d%v%
else EnvSub, acc, % v%A_Index%d%v%
nv .= ", " acc
}
SetFormat, Float, %off%
return substr(nv,3) ";"
}
; Adds the vectors in the list.
Vect_Add(vlist, formal=0)
{
off := A_FormatFloat
SetFormat, Float, 0.15
IfNotEqual, formal, 1, Loop, Parse, vlist, `;`n, %A_Space%%A_Tab%`r`,
{
IfEqual, A_LoopField,, Continue
IfEqual, True, % !(vect := Vect_Formal(A_LoopField)), continue
u++
StringSplit, v%u%d, vect, `,, %A_Space%%A_Tab%`r`;
IfLess, vorder, % v%u%d0, SetEnv, vorder, % v%u%d0
}
else Loop, Parse, vlist, `;`n, `r%A_Space%%A_Tab%
{
IfEqual, A_LoopField,, Continue
u++
StringSplit, v%u%d, A_LoopField, `,, `n%A_Space%
IfLess, vorder, % v%u%d0, SetEnv, vorder, % v%u%d0
}
Loop %vorder%
{
v := A_Index
acc := 0
Loop %u%
EnvAdd, acc, % v%A_Index%d%v%
nv .= ", " acc
}
SetFormat, Float, %off%
return substr(nv,3) ";"
}
; Returns the input vector with length
; multiplitd by a factor. NOTE: seperate
; the vector and the factor by a
; semicolor or a newline character
; When using a formal vector, you may
; simply use the following format:
; result := Vect_Mult( FormalVector . 3, 1 )
Vect_Mult(vect, formal=0)
{
off := A_FormatFloat
SetFormat, Float, 0.15
IfNotEqual, formal, 1
{
Loop, Parse, vect, `;`n, %A_Space%%A_Tab%`r`,
IfLess, A_Index, 3, SetEnv, factor%A_Index%, % Vect_Formal(A_LoopField)
Else Break
factor2 := SubStr(Factor2, 1, -1+InStr(Factor2, ","))
} Else StringSplit, factor, vect, `;, %A_Space%%A_Tab%`r`n`,
Loop, Parse, factor1, `,, %A_Space%`;
nv .= ", " A_LoopField * factor2
SetFormat, Float, %off%
return substr(nv,3) ";"
}
; returns a vector describing the projection A onto B
; where A is the first vector in the list and B is the next
; The result is linearly dependant to vector B
Vect_Proj(vlist, formal=0)
{
u=1
IfNotEqual, formal, 1, Loop, Parse, vlist, `;`n, %A_Space%%A_Tab%`r`,
{
IfEqual, A_LoopField,, Continue
IfEqual, True, % !(vect%u% := Vect_Formal(A_LoopField)), continue
else u++
}
else Loop, Parse, vlist, `;`n, `r%A_Space%%A_Tab%
IfEqual, A_LoopField,, Continue
else vect%u% := A_LoopField ";", u++
return Vect_Mult(vect2 . Vect_Dot(vect1 . vect2, 1) / (Vect_Len(vect2, 1)**2), 1)
}
; if the second vector is normal to a plane, and
; the first vector is not paralell to that plane
; then this function returns the first vector
; after it bounces off the plane
; In essense, this function subtracts twice the
; projection of v1 onto v2 from v1, clear?
; Also, the angle between a vector and its reflection
; is exactly equal to twice the angle betwen that
; vector and the normal it is being reflected from
; so, Vect_Angle( v1 v2 ) is the same as
; Vect_Angle( Vect_Reflect( v1 v2 ) v2 )
Vect_Reflect(vlist, formal=0)
{
u=1
IfNotEqual, formal, 1, Loop, Parse, vlist, `;`n, %A_Space%%A_Tab%`r`,
{
IfEqual, A_LoopField,, Continue
IfEqual, True, % !(vect%u% := Vect_Formal(A_LoopField)), continue
else u++
}
else Loop, Parse, vlist, `;`n, `r%A_Space%%A_Tab%
IfEqual, A_LoopField,, Continue
else vect%u% := A_LoopField ";", u++
vx := Vect_Proj(vect1 . vect2, 1)
return Vect_Sub(vect1 . vx . vx, 1)
}
; returns a vector that is the cross-product of
; the first two vectors in vlist provided that
; those two vectors are independant and are in R3
; Note: the cross product in Rn requires n-1 vectors
; and is produced by calculating the determinants of
; n square matrices of size (n-1)x(n-1)
; For example, in R5...
;
; v1 = v1a, v1b, v1c, v1d, v1e
; v2 = v2a, v2b, v2c, v2d, v2e
; v3 = v3a, v3b, v3c, v3d, v3e
; v4 = v4a, v4b, v4c, v4d, v4e
; Cross = I, J, K, L, M
; I = Det( v1b, v1c, v1d, v1e; v2b, v2c, v2d, v2e; v3b, v3c, v3d, v3e; v4b, v4c, v4d, v4e)
; J = Det( v1c, v1d, v1e, v1a; v2c, v2d, v2e, v2a; v3c, v3d, v3e, v3a; v4c, v4d, v4e, v4a)
; K = Det( v1d, v1e, v1a, v1b; v2d, v2e, v2a, v2b; v3d, v3e, v3a, v3b; v4d, v4e, v4a, v4b)
; L = Det( v1e, v1a, v1b, v1c; v2e, v2a, v2b, v2c; v3e, v3a, v3b, v3c; v4e, v4a, v4b, v4c)
; M = Det( v1a, v1b, v1c, v1d; v2a, v2b, v2c, v2d; v3a, v3b, v3c, v3d; v4a, v4b, v4c, v4d)
; where Det() is the determinate of the specified matrix
; And for large n's, the fastest way to get the determinant
; is through row-reduction to row-echelon form and then
; multiply along the diagonal. Since row-reduction would
; have a big-O notation of O(n²) whereas finding
; the determinate by laplace expansion would have a
; big-O of O(n!) (and that's really bad!!)
;
Vect_Cross(vlist, formal=0) ; WARNING: R3 supported only!!!!
{
off := A_FormatFloat
SetFormat, Float, 0.15
IfNotEqual, formal, 1, Loop, Parse, vlist, `;`n, %A_Space%%A_Tab%`r`,
{
IfEqual, A_LoopField,, Continue
IfEqual, True, % !(vect := Vect_Formal(A_LoopField)), continue
u++
StringSplit, v%u%d, vect, `,, %A_Space%%A_Tab%`r`;
}
else Loop, Parse, vlist, `;`n, `r%A_Space%%A_Tab%
{
IfEqual, A_LoopField,, Continue
u++
StringSplit, v%u%d, A_LoopField, `,, `n%A_Space%
}
vr := (v1d2 * v2d3 - v1d3 * v2d2) ", "
vr .= (v1d3 * v2d1 - v1d1 * v2d3) ", "
vr .= (v1d1 * v2d2 - v1d2 * v2d1) ";"
SetFormat, Float, %off%
return vr
} |
_________________ Ternary (a ? b : c) guide TSV Table Manipulation Library
Post code inside [code][/code] tags!
Last edited by [VxE] on Fri Oct 10, 2008 10:13 am; edited 3 times in total |
|
| Back to top |
|
 |
[VxE]
Joined: 07 Oct 2006 Posts: 3244 Location: Simi Valley, CA
|
Posted: Sat Jun 07, 2008 3:44 am Post subject: |
|
|
This script turns the 4 arrow keys into the number keys on the numberpad. Press one or two arrow keys to get the appropriate numpad number or pgup/home/end or numpadarrows (depending on the numlock state). Feel free to change the actual toggle to something other than Numlock (the appskey is pretty handy ). Also the alternate functions can be easily customized.
| Code: | ; [VxE]'s Arrow Keys To Numpad Multiplexer.
ArrowKeys = Up,Left,Down,Right
NumPadAliases = End,Down,PgDn,Left,NumpadClear,Right,Home,Up,PgUp
StringSplit, k, ArrowKeys, `,
StringSplit, NumAlias, NumPadAliases, `,
NumAlias0 = Ins ; I'd recommend 'BackSpace' as it seems more useful
Loop, Parse, ArrowKeys, `,
Hotkey, $*%A_LoopField%, $*Up, on
$*Up::
mark := SubStr(((sk1:=GetKeyState(k1,"P"))-(sk3:=GetKeyState(k3,"P"))+1)*3
+(sk4:=GetKeyState(k4,"P"))-(sk2:=GetKeyState(k2,"P"))+2-5*(!sk2 & !sk4 & sk1 & sk3),0)
If (!InThread)
{
InThread = 1
Sleep 40 ; delay to allow coordination of multiple keys
If (GetKeyState("NumLock", "T")) ; put any toggle you want
Send {blind}{NumPad%mark%}
else
Send % "{blind}{" NumAlias%mark% "}" ; "{blind}{Numpad" NumAlias%mark% "}"
InThread = 0
}
return |
_________________ Ternary (a ? b : c) guide TSV Table Manipulation Library
Post code inside [code][/code] tags! |
|
| Back to top |
|
 |
[VxE]
Joined: 07 Oct 2006 Posts: 3244 Location: Simi Valley, CA
|
Posted: Sun Jun 08, 2008 6:24 am Post subject: |
|
|
I know that RGB to HSV has been done before in AHK (props, tonne), but here's another manifestation of it which handles hex-code BGR (like what PixelGetColor returns) as well as the reverse: HSL to BGR
| Code: | Hue(BGR) ; Hue: output range [0,360) float
{ ; Function by [VxE], formula from wikipedia.org/wiki/HSV_color_space
OFF := A_FormatFloat
SetFormat, Float, 0.16
; pi := ATan(1) * 4
pi := 360 / 2 ; what do you consider a full circle ? 360º or 2pi ?
hex := pi / 3
blue := (BGR >> 16) / 256
green := ((BGR >> 8) & 255) / 256
red := (BGR & 255) / 256
max := ((blue > green) && (blue > red)) ? blue : (green > red ? green : red)
min := ((blue < green) && (blue < red)) ? blue : (green < red ? green : red)
If (min = max)
res := 0
else If (max = red)
res := hex * ((green - blue) / (max - min))
else If (max = green)
res := hex * ((blue - red) / (max - min)) + 2 * hex
else If (max = blue)
res := hex * ((red - green) / (max - min)) + 4 * hex
If (res >= 2 * pi )
res -= 2 * pi
If (res < 0 )
res += 2 * pi
SetFormat, Float, %OFF%
return res
} |
| Code: | Sat(BGR) ; Saturation: output range [0,1)
{ ; Function by [VxE], formula from wikipedia.org/wiki/HSV_color_space
OFF := A_FormatFloat
SetFormat, Float, 0.16
blue := (BGR >> 16) / 256
green := ((BGR >> 8) & 255) / 256
red := (BGR & 255) / 256
max := ((blue > green) && (blue > red)) ? blue : (green > red ? green : red)
min := ((blue < green) && (blue < red)) ? blue : (green < red ? green : red)
lum := (min + max) / 2
If (min = max)
res := 0
else If (lum <= 0.5)
res := (max - min) / (2*lum)
else
res := (max - min) / (2-2*lum)
SetFormat, Float, %OFF%
return res
} |
| Code: | Lum(BGR) ; output range [0,1)
{ ; Function by [VxE], formula from wikipedia.org/wiki/HSV_color_space
OFF := A_FormatFloat
SetFormat, Float, 0.16
blue := (BGR >> 16) / 256
green := ((BGR >> 8) & 255) / 256
red := (BGR & 255) / 256
max := ((blue > green) && (blue > red)) ? blue : (green > red ? green : red)
min := ((blue < green) && (blue < red)) ? blue : (green < red ? green : red)
res := (min + max) / 2
SetFormat, Float, %OFF%
return res
} |
| Code: | HSL_to_RGB( hue, sat, lum ) ; inputs should be [0,360) : [0,1) : [0,1), output = 24b hex BGR
{ ; Function by [VxE], formula from wikipedia.org/wiki/HSV_color_space
OFF := A_FormatFloat
SetFormat, Float, 0.16
If ( lum < 0.5 )
weight := Lum + Lum * Sat
else
weight := Lum + Sat - Lum * Sat
size := 2 * Lum - weight
value := hue / 360
red := value + 1/3 - (value > 2/3)
green := value
blue := value - 1/3 + (value < 1/3)
If (blue < 1/6)
blue := (blue * 6 * (weight - size)) + size
else if (blue < 1/2)
blue := weight
else if (blue < 2/3)
blue := ((2/3 - blue) * 6 * (weight - size)) + size
else
blue := size
If (green < 1/6)
green := (green * 6 * (weight - size)) + size
else if (green < 1/2)
green := weight
else if (green < 2/3)
green := ((2/3 - green) * 6 * (weight - size)) + size
else
green := size
If (red < 1/6)
red := (red * 6 * (weight - size)) + size
else if (red < 1/2)
red := weight
else if (red < 2/3)
red := ((2/3 - red) * 6 * (weight - size)) + size
else
red := size
OIF := A_FormatInteger
SetFormat, Integer, Hex
ret := (Round(blue*256) << 16) | (Round(green*256) << 8) | Round(red*256)
SetFormat, Float, %OFF%
SetFormat, Integer, %OIF%
ret := "0x" SubStr( "00000" SubStr(ret, 3), -5 )
StringUpper, ret, ret
return ret
} |
And of course, the accessory function | Code: | FlipBlueAndRed(c) ; takes RGB or BGR and swaps the R and B
{
return (c&255)<<16 | (c&65280) | (c>>16)
} |
_________________ Ternary (a ? b : c) guide TSV Table Manipulation Library
Post code inside [code][/code] tags! |
|
| Back to top |
|
 |
|
|
You can post new topics in this forum You can reply to topics in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|