Author Topic: CHALLENGE: osmode number conversion  (Read 5282 times)

0 Members and 1 Guest are viewing this topic.

CAB

  • Global Moderator
  • Seagull
  • Posts: 10401
CHALLENGE: osmode number conversion
« on: October 05, 2006, 06:59:59 PM »
The challenge is to convert the osmode number returned by (getvar "OSMODE")
into a string suitable for use in the lisp command (osnap pt mode)
This will allow the OSNAP command to be used with the current OSMOD settings.

Then the usage will be like this:
Code: [Select]
(osnap pt (get_osmode))

PS:
If osnamps are OFF the function should return "", an empty string.
« Last Edit: October 05, 2006, 07:27:41 PM by CAB »
I've reached the age where the happy hour is a nap. (°¿°)
Windows 10 core i7 4790k 4Ghz 32GB GTX 970
Please support this web site.

T.Willey

  • Needs a day job
  • Posts: 5251
Re: CHALLENGE: osmode number conversion
« Reply #1 on: October 05, 2006, 07:27:44 PM »
Here you go.
Code: [Select]
(defun OsText (Osm / SnapList Str)

(setq SnapList
 (list
  '(1 . "END")
  '(2 . "MID")
  '(4 . "CEN")
  '(8 . "NOD")
  '(16 . "QUA")
  '(32 . "INT")
  '(64 . "INS")
  '(128 . "PER")
  '(256 . "TAN")
  '(512 . "NEA")
  '(2048 . "APP")
  '(4096 . "EXT")
  '(8192 . "PAR")
 )
)
(setq Str "")
(foreach pair SnapList
 (if (equal (car pair) (logand (car pair) osm))
  (setq Str (strcat Str (cdr pair) ","))
 )
)
Str
)
Code: [Select]
(osnap (getpoint) (OsText (getvar "osmode")))
Tim

I don't want to ' end-up ', I want to ' become '. - Me

Please think about donating if this post helped you.

MP

  • Seagull
  • Posts: 17750
  • Have thousands of dwgs to process? Contact me.
Re: CHALLENGE: osmode number conversion
« Reply #2 on: October 05, 2006, 09:37:50 PM »
Quick stab --

Code: [Select]
(defun IntToSnaps ( i )
    (if (zerop (logand 16384 i))
        (apply 'strcat
            (mapcar 'cdr
                (vl-remove-if-not
                   '(lambda (x) ((lambda (a) (eq a (logand a i))) (car x)))
                   '(
                        (   0 . "_non,")
                        (   1 . "_end,")
                        (   2 . "_mid,")
                        (   4 . "_cen,")
                        (   8 . "_nod,")
                        (  16 . "_qua,")
                        (  32 . "_int,")
                        (  64 . "_ins,")
                        ( 128 . "_per,")
                        ( 256 . "_tan,")
                        ( 512 . "_nea,")
                        (2048 . "_app,")
                        (4096 . "_ext,")
                        (8192 . "_par")
                    )
                )
            )
        )   
        ""
    )   
)

Example --

Code: [Select]
(osnap
    (getpoint)
    (IntToSnaps (getvar "osmode"))
)

Edit: modified code to honour status bar osnap button state (bit code 16384).
« Last Edit: October 05, 2006, 10:33:43 PM by MP »
Engineering Technologist • CAD Automation Practitioner
Automation ▸ Design ▸ Drafting ▸ Document Control ▸ Client
cadanalyst@gmail.comhttp://cadanalyst.slack.comhttp://linkedin.com/in/cadanalyst

JohnK

  • Administrator
  • Seagull
  • Posts: 10655
Re: CHALLENGE: osmode number conversion
« Reply #3 on: October 05, 2006, 09:59:23 PM »
All nice and fancy, but I'm going to have to side on SMadsen on this one... The ``answer'' to your challange may not be right there in plain code but man, you gotta love this explination.

Code: [Select]
osnap    binary               decimal  powers
name     representation       do.      of two
---------------------------------------------
non      0000 0000 0000 0000  0
end      0000 0000 0000 0001  1        2^0
mid      0000 0000 0000 0010  2        2^1
cen      0000 0000 0000 0100  4        2^2
nod      0000 0000 0000 1000  8        2^3
qua      0000 0000 0001 0000  16       2^4
int      0000 0000 0010 0000  32       2^5
ins      0000 0000 0100 0000  64       2^6
perp     0000 0000 1000 0000  128      2^7
tan      0000 0001 0000 0000  256      2^8
nea      0000 0010 0000 0000  512      2^9
qui      0000 0100 0000 0000  1024     2^10
app      0000 1000 0000 0000  2048     2^11
ext      0001 0000 0000 0000  4096     2^12
par      0010 0000 0000 0000  8192     2^13
toggle   0100 0000 0000 0000  16384    2^14
not used 1000 0000 0000 0000  32768    2^15


Example: end+int+ins = 1+32+64 = 97

end      0000 0000 0000 0001 =   1
int      0000 0000 0010 0000 =  32
ins      0000 0000 0100 0000 =  64
-----------------------------------
         0000 0000 0110 0001 =  97

Now look for intersection, i.e. bit 5.
Run through each bit and do AND comparison
for each bit in the integers.
AND will return 1 if and only if both bits
are 1, otherwise it will return zero. The
truth table for AND looks like this:

  | 0 1                        | false true
-------  or in boolean:  -------------------
0 | 0 0                  false | false false
1 | 0 1                  true  | false true


So, let's apply this to the integers:

         0000 0000 0110 0001 = 97
     AND 0000 0000 0010 0000 = 32
     ----------------------------
         0000 0000 0010 0000 = 32

So, 97 AND 32 = 32, because bit 5 is set in
both integers.


Let's look for quadrant osnap, i.e. bit 4:

         0000 0000 0110 0001 = 97
     AND 0000 0000 0000 1000 = 16
     ----------------------------
         0000 0000 0000 0000 =  0

Because no bits in the integers "match",
AND will return zero for every pair of bits,
so 97 AND 16 = 0

Therefore, you are actually looking for a
number within a number, and saying

   If (varSnap AND 32) = 32 Then MsgBox "Inters is On"

is the correct way instead of

   If varSnap = 32 Then MsgBox "Only inters is On!"

And for goodness sake, lets not forget this one as well.

Code: [Select]
Here is some chitchat on setting and clearing bits. Sorry about the everything-in-courier thing.

Setting bits.
The truth table for OR looks like this:
  | 0 1                       | false true
-------   in boolean:   ------------------
0 | 0 1                 false | false true
1 | 1 1                 true  | true  true

OR returns 1 if both or just one of the input bits are 1. What can we
use that for?

Say we want to set a certain OSNAP. Well, because OR returns 1 if
just one of the input bits are set, we can ensure that as long as we
input a set bit it will return a set bit no matter if the other input
bit is set or not set (1 or 0).

Example:
Osnap is now: end+ins = 1+64 = 65
Osnap wanted: end+int+ins = 1+32+64 = 97

The missing bit is the 5th bit representing the value 32, so we can
try set up an expression and see what happens. varSnap is simply the
osnap value returned by OSMODE:

  newSnap = varSnap OR 32


         0000 0000 0100 0001 =  65
      OR 0000 0000 0010 0000 =  32
-----------------------------------
         0000 0000 0110 0001 =  97

Mission accomplished. We can now put it into a function and test it
(excuse my lack of VBAbilities):

Public Function SetBit(ByVal source As Long, ByVal newBit As Long) As Long
  SetBit = source Or newBit
End Function

Public Sub SetInters()
  Dim varSnap, inters, dest As Long
  varSnap = ThisDrawing.GetVariable("OSMODE")
  inters = 32
  dest = SetBit(varSnap, inters)
  ThisDrawing.SetVariable "OSMODE", dest
End Sub

Of course it doesn't matter what the current OSMODE is. End+Ins was
just used to show the calculation. This operation does not mess with
bits already set because two set bits will simply return a set bit.
So if Intersection was set already, it doesn't harm anything.


To clear bits.
Given the truth tables for AND it should become obvious that
negating one bit has an affect:

A AND B:                         A AND (NOT B):
  | 0 1     when negating          | 0 1
-------     second input bit:    -------
0 | 0 0                          0 | 0 0
1 | 0 1                          1 | 1 0

Because this operation only returns 1 if the first input bit is 1,
we can use it to clear any bit within an integer. Say we want to
turn off Intersection, i.e. bit 5 in OSMODE, we can set up this
expression and test it:

  newSnap = varSnap AND (NOT 32)

First, let's find (NOT 32).

     NOT 0000 0000 0010 0000 =  32
         -------------------------
         1111 1111 1101 1111 =  (doesn't matter)

Easily done - 1 becomes 0 and vice versa. Only 16 bit are shown here,
so the decimal representation doesn't matter (it didn't matter if all
32 bit were shown, anyway)

Now, take varSnap AND (NOT 32):

         0000 0000 0110 0001 =  97
     AND 1111 1111 1101 1111 =  (still doesn't matter)
         -------------------------
         0000 0000 0100 0001 =  65

Mission accomplished. Bit 5 is gone. Again, because the calculation
relies on AND, it doesn't affect the result if the bit we are masking
was already turned off. If intersection was already turned off,
trying to clear it has no effect:

         0000 0000 0100 0001 =  65
     AND 1111 1111 1101 1111 =  (still doesn't matter)
         -------------------------
         0000 0000 0100 0001 =  65


An simple function to clear a bit could be:

Public Function ClearBit(ByVal source As Long, ByVal bit2clear As Long) As Long
  ClearBit = source And (Not bit2clear)
End Function


Other ways to do this is to use XOR as decribed next, or to make a
simple subtraction like this:

Public Function ClearBit2(ByVal source As Long, ByVal bit2clear As Long) As Long
  ClearBit2 = source - (source And bit2clear)
End Function

The home assignment is to figure out why this only affects the bit
we want to clear.


Flipping bits.
XOR is funny. It's related to OR (XOR is called exclusive OR - hence
the X - while OR is called inclusive OR - just with no I) and the
truth table looks like this:

  | 0 1                       | false true
-------   in boolean:   -------------------
0 | 0 1                 false | false true
1 | 1 0                 true  | true  false

A flipping "motion" can already be seen in the truth table. It
returns 1 if only one of the input bits are 1.

Want to construct XOR with AND,OR and NOT? Easy:
(OR (AND A (NOT B))(AND (NOT A) B))

Seriously, say you have an OSMODE of 97 (again) and you want to flip
bit 5:

         0000 0000 0110 0001 =  97
     XOR 0000 0000 0010 0000 =  32
-----------------------------------
         0000 0000 0100 0001 =  65

Away intersection goes! This is the precise same thing as clearing a
bit like above, except that if the first input bit is different than
the second input bit, it will affect the resulting bit. For example,
if intersection was already off it will turn it on instead of leaving
it off - hence the word "flip":

         0000 0000 0100 0001 =  65
     XOR 0000 0000 0010 0000 =  32
-----------------------------------
         0000 0000 0110 0001 =  97

With XOR you can flip all bits at once. So if you want to remove
bit 0 and 5 at once, you can XOR 97 and 65. But you can also flip
back by reversing the operation. A sample flip-function:

Public Function FlipBit(ByVal source As Long, ByVal bits2flip As Long) As Long
  FlipBit = source Xor bits2flip
End Function

Once again, Thank you Stig. You are a true genius.
TheSwamp.org (serving the CAD community since 2003)
Member location map - Add yourself

Donate to TheSwamp.org

MickD

  • King Gator
  • Posts: 3640
  • (x-in)->[process]->(y-out) ... simples!
Re: CHALLENGE: osmode number conversion
« Reply #4 on: October 05, 2006, 10:14:14 PM »
Yes, excellent explanation, thanks Se7en for sharing and thanks Stig for explaining some very useful instances for using boolean logic.
"Programming is really just the mundane aspect of expressing a solution to a problem."
- John Carmack

"Short cuts make long delays,' argued Pippin.”
- J.R.R. Tolkien

CAB

  • Global Moderator
  • Seagull
  • Posts: 10401
Re: CHALLENGE: osmode number conversion
« Reply #5 on: October 05, 2006, 10:17:23 PM »
Thanks John for expanding scope. Very good reading.
MP nice idea putting the comma in the string. & removing for the result. I was thinking like
Tim, add to the result. Good job Tim.

Here was my code, It returns "" if the Osnaps are turned OFF.

Code: [Select]
(defun get_osmode (/ cur_mode mode$)
  (if (< 0 (setq cur_mode (getvar "osmode")) 16383)
    (mapcar
      '(lambda (x)
        (if (not (zerop (logand cur_mode (car x))))
          (if mode$
            (setq mode$ (strcat mode$ "," (cadr x)))
            (setq mode$ (cadr x))
          )
        )
      )
      '(
        (0    "_non")
        (1    "_end")
        (2    "_mid")
        (4    "_cen")
        (8    "_nod")
        (16   "_qua")
        (32   "_int")
        (64   "_ins")
        (128  "_per")
        (256  "_tan")
        (512  "_nea")
        (1024 "_qui")
        (2048 "_app")
        (4096 "_ext")
        (8192 "_par")
       )
    )
    (setq mode$ "")
  )
  mode$
)
I've reached the age where the happy hour is a nap. (°¿°)
Windows 10 core i7 4790k 4Ghz 32GB GTX 970
Please support this web site.

ElpanovEvgeniy

  • Water Moccasin
  • Posts: 1569
  • Moscow (Russia)
Re: CHALLENGE: osmode number conversion
« Reply #6 on: October 06, 2006, 11:27:13 AM »
Code: [Select]
(defun get_osmode (/ get_osmode1)
  (defun get_osmode1 (c i b)
    (if (not (zerop i))
      (if (zerop (logand c b))
        (get_osmode1 c (1- i) (lsh b -1))
        (strcat
          (get_osmode1 c (1- i) (lsh b -1))
          (substr
            ",_non,_end,_mid,_cen,_nod,_qua,_int,_ins,_per,_tan,_nea,_qui,_app,_ext,_par"
            (1+ (* i 5))
            5
          ) ;_  substr
        ) ;_  strcat
      ) ;_  if
      ""
    ) ;_  if
  ) ;_  defun
  (if (< (getvar "osmode") 16384)
    (substr (get_osmode1 (getvar "osmode") 14 8192)2)
    "_non"
  ) ;_  if
)

Crank

  • Water Moccasin
  • Posts: 1503
Re: CHALLENGE: osmode number conversion
« Reply #7 on: October 07, 2006, 07:48:05 AM »
Code: [Select]
(defun get_osmode (/ value bit)
  (setq value "" bit 0)
  (while (< bit 15)
    (if (not (zerop (logand (getvar "OSMODE")(lsh 1 bit))))
      (setq value (strcat value
        (substr "_end,_mid,_cen,_nod,_qua,_int,_ins,_per,_tan,_nea,_qui,_app,_ext,_par,_non"
        (1+ (* bit 5))
        5)
      ))
    )
    (setq bit (1+ bit))
  )
  value
)
Vault Professional 2023     +     AEC Collection