Author Topic: Save Data? (X-data theory by Vladimir Nesterovsky)  (Read 4446 times)

0 Members and 1 Guest are viewing this topic.

JohnK

  • Administrator
  • Seagull
  • Posts: 10626
Save Data? (X-data theory by Vladimir Nesterovsky)
« on: May 12, 2004, 04:55:11 PM »
Coments?

Quote
From: vnestr@netvision.net.il (Vladimir Nesterovsky)
Newsgroups: autodesk.autocad.customization
Subject: Re: Saving *any* Lisp values with a drawing.
Date: Thu, 27 Nov 1997 00:17:29 GMT
X-Newsreader: Forte Agent 1.5/32.451


Here's something that will let us store/retrieve
arbitrary lists of values of (almost) *any* structure
in DWG  *))
with two simple function calls like this:

(dict-put "name" "key" any-value)
(dict-get "name" "key")

These two will be implemented using dictionaries in R14
but can be done via EED just as well.
--------
*))
The limitation is that dotted pairs in your data
aren't handled. Use regular lists here or improve
this code! :) Lists however may be of any structure,
maybe deeply nested lists etc.
================================


So here's something to use. (I've finally got some
free time to write this down) :)


--- How to save Lisp values with drawing? ---


We have two possibilities in R14 -- dictionaries
and EED, extended entity data. Both provide us with
a means of storing information in a DWG persistently.
We can store a series of code pairs in EED, as usual,
only the codes are 1000, 1040 etc. BUT -- in a real
Lisp programs we often have variables that are a *lists*
of data, and in Lisp a list is actually a tree in that it
can contain another list and so on.

So how can we store lists in EED (or in Xrecords to be
put into dictionary)? We must have some way to encode it
into the possible code groups, and later to decode it back
while retrieving the info.

Both EED and Xrecords are very similar in that, and in both
cases we must use this en/decoding.

How can we do that? We need to use some special marker
to mark a beginning/ending of a lists. Let's use LB
as a List Begin marker and LE as a list end.

This all means that we need to find a way to convert e.g.
 (1 (2 (3 "4") 5)) into
 LB 1 LB 2 LB 3 "4" LE 5 LE LE
and vice versa. In other words, we want to find
the conversion between Lisp tree-like lists
and this special one-dimentional list representation.

For that we can use something similar to that RE-LIST
function that I have posted several times in its various
modifications.

Let's use here "{" as LB and "}" as LE.

RE-LIST is decoding a list back into its original LISP form
and may be done like this:


Code: [Select]
;; all functions here are (C.) 1997 by Vladimir Nesterovsky,
;;  vnestr@netvision.net.il, All Rights Reserved
;; please keep this notice intact

(defun re-list ( lst / tok )
 (re-list-aux))

(defun re-list-aux()
 (cond
  ((atom lst) lst)
  ((/= "}" (setq tok (pop 'lst)))
   (cons (if (= "{" tok) (re-list-aux) tok)
         (re-list-aux)))))

{ I'm using here a general utility POP function
  which is stripping first element off the list,
  returning its value:
 (defun pop(a / b)
   (setq b (eval a))
   (set a  (cdr b))
   (car b))
}

Its counterpart, EN-LIST, is for encoding:

(defun en-list ( lst )
  (cond
   ((null lst) lst)
   ((atom lst) (list lst))
   ((cons "{"    ;; add the markers around the list!
      (reverse
        (cons "}"
          (reverse
            (apply 'append  ;; open the lists
              (mapcar 'en-list lst) ;; recursion!
      ))))))))


Quote
Both don't deal with dotted pairs for simplicity.

{A word of caution -- both functions may be memory hungry
 as being recursive, which can lead to problems when using big
 lists, like Autolisp stack overflow etc. I heard that every
 recursive function can be done iteratively, so if you have
 a very big lists in your application, you may need to try
 to prove this theorem in this case. :) }

Now we've seen this encoding/decoding in action and can do
any alterations suitable for EED or Xrecords (the actual codes
for EED are different and long integers must be encoded as
reals and converted to integers back on decoding).
Here's a function for Xrecord encoding,
making code groups automatically:


Code: [Select]
(defun x-enlist ( lst )  ;; encode!
  (cond
   ((null lst) lst)
   ((atom lst)           ;; automatic code groups
     (cond
      ((= 'REAL (type lst))
        (list (cons 40 lst)))
      ((= 'INT (type lst))
        (list (cons 71 lst)))
      ((= 'STR (type lst))
        (list (cons  1 lst)))
      (T nil)))
   ((and (cdr lst) (atom (cdr lst)))
      (list lst))      ;; pass dotted pair AS IS -- must be valid!!
   ((and (= (length lst) 3)
         (apply 'and (mapcar 'numberp lst)))
     (list (cons 10 lst)))
   ((cons '(2 . "{")
     (reverse
      (cons '(2 . "}")
       (reverse
        (apply 'append
         (mapcar 'x-enlist lst) ))))))))
         

(defun x-relist ( lst / tok )  ;; decode it!
 (car (x-relist-aux)))

(defun x-relist-aux()
 (cond
  ((null lst) nil)
  ((not (equal '(2 . "}") (setq tok (pop 'lst))))
   (cons (if (equal '(2 . "{") tok)
           (x-relist-aux)
           (cdr tok)
           )
         (x-relist-aux)))))


Quote
I will post the file SAVEDATA.LSP on my home page
in near days with all these functions and the ones
for EED with special handling of 1070 and 1071 code
groups. The handling of long strings can be done just
as easily via the use of special code 1001 for text
parts (the short string is encoded under 1000 for EED).


The rest is just plain dictionary functions for R14:


Code: [Select]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; dictionary functions for R14
;; POSSIBLE IMPROVEMENTS: support trees of dictionaries
;;      now: all custom dictionaries are under the Root.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; Get Dictionary Object Name From Root Dictionary
(defun dict-name ( name / d0 d ) ;; name As string
  (if (Setq d0 (namedobjdict)
            d  (dictsearch d0 name))
    (cdr (assoc -1 d))))

;; Remove Dictionary <Name> From Root Dictionary
(defun dict-remove ( name / d0 ) ;; name As string
  (if (dictsearch (Setq d0 (namedobjdict)) name)
    (dictremove d0 name))
  )

;; Make A New Empty Dictionary; Reset Existing.
(defun dict-new ( name / d0 ) ;; name As string
  (if (dictsearch (Setq d0 (namedobjdict)) name)
    (dictremove d0 name))
  (dictadd d0 name
      (entmakex '((0 . "DICTIONARY")(100 . "AcDbDictionary")))))

;; List A Dictionary As Pairs {Name . Object Name}
(defun dict-list ( dname / d nl ) ;; dname As object name or string else ROOT
  (cond
    ((and (Setq d (cond
                    ((= 'ENAME (type dname))
                      dname)
                    ((= 'STR (type dname))
                      (dict-name dname))
                    (T (namedobjdict))))
          (Setq d (entget d)))
      (while (setq d (member (assoc 3 d) d))
        (if (= 350 (caadr d))
          (setq nl (cons (cons (cdar d) (cdadr d)) nl)))
        (setq d (cdr d)))
      (reverse nl))))

;; Get Raw <Name> Dictionary Data for <Key>
(defun dict-getrawdata ( name key / d ) ;; name and key As strings
  (if (setq d (dict-name name))
   (dictsearch d key)))

;; Clear The <Key> From Dictionary <Name>
(defun dict-clear ( name key / d) ;; name and key As strings
  (if (setq d (dict-name name))
    (dictremove d key)))

;;;;;;;~~~~~~~~~~~~~~~~~~~~~~~~~~~~;;;;;;;
;;;;;;; The two working functions: ;;;;;;;
;;;;;;;____________________________;;;;;;;

;; Get A <Key> Value From Dictionary <Name>
(defun dict-get ( name key ) ;; name and key As strings
  (x-relist                  ;; decode the data!
    (cdr (member '(100 . "AcDbXrecord")
(dict-getrawdata name key)))))

;; Put A <Val>ue Into <Name> Dictionary Under <Key>
;; Dictionary is created automatically if was not existing
(defun dict-put ( name key val ) ;; three arguments As strings
  (dict-clear name key) ;;  clear the old value under this Key
  (dictadd
    (cond ((dict-name name)) ((dict-new name)) )
    key
    (entmakex
      (cons '(0 . "XRECORD")
        (cons '(100 . "AcDbXrecord")
          (x-enlist val)))) )) ;; encode the data!
;;
;; POSSIBLE IMPROVEMENTS:
;;    implement dict-append ( APPEND PREPEND )
;;;;;;;;;;;;;;;;;;;;;;;


Quote
Sample use:

(dict-put "mydict" "mykey" '(1 (2 ("3" (44.4) (5.5 6.6 7.7) 8)) 999999))
(dict-get "mydict" "mykey")
;;;;;;;-------;;;;;;;

So here we have developed a set of functions
that let us store and retrieve an arbitrary values
of *any* structure persistently with the DWG
(with a possible exception of dotted pairs).

In fact it may be easily altered to store even *functions*
with the DWG as functions are _lists_ in AutoLISP and all
that's left to do is to define some special code for string
converted symbols to be stored in Xrecord, e.g. 3, and just
use SYM2STR and READ in the en/decoding functions.

Enjoy!
Comments and improvements are welcome.
LISP code is at http://members.tripod.com/~vnestr/SaveData.lsp

------- Original message: -------
On Thu, 13 Nov 1997 15:21:32 -0500,  - MozartZ <mozartz@cyberdrive.net> wrote in autodesk.autocad.customization:

>I need to know if there is a quike and eazy way to save LISp symble
>values along
>with the the drawing so each time the drawing is opened the LISp
>routines that I'm
>creating be able to remember there state the last time the drawing was
>in the database
>
>MozartZ
TheSwamp.org (serving the CAD community since 2003)
Member location map - Add yourself

Donate to TheSwamp.org

SMadsen

  • Guest
Save Data? (X-data theory by Vladimir Nesterovsky)
« Reply #1 on: May 12, 2004, 05:11:00 PM »
No comments .. other than Autodesk already built XData to be organized into lists or chunks of data.

From the help files:
Quote
Control String

1002. An xdata control string can be either "{" or "}". These braces enable the application to organize its data by subdividing it into lists. The left brace begins a list, and the right brace terminates the most recent list. Lists can be nested.

Note  If a 1001 group appears within a list, it is treated as a string and does not begin a new application group.

JohnK

  • Administrator
  • Seagull
  • Posts: 10626
Save Data? (X-data theory by Vladimir Nesterovsky)
« Reply #2 on: May 12, 2004, 06:30:19 PM »
The idea i got form this article was that you could build a way to store your procedures within a drawing and have them execute with a general startup procedure. (Not to mention they would carry over in a save down to R14.
TheSwamp.org (serving the CAD community since 2003)
Member location map - Add yourself

Donate to TheSwamp.org

JohnK

  • Administrator
  • Seagull
  • Posts: 10626
Save Data? (X-data theory by Vladimir Nesterovsky)
« Reply #3 on: May 13, 2004, 10:03:51 AM »
After thinknig about this a bit more.... It isnt as good as i initialy thought. (I mean dont get me wrong, the idea isnt as good not the code and stuff.)
TheSwamp.org (serving the CAD community since 2003)
Member location map - Add yourself

Donate to TheSwamp.org

SMadsen

  • Guest
Save Data? (X-data theory by Vladimir Nesterovsky)
« Reply #4 on: May 13, 2004, 10:42:31 AM »
Probably for some specialized tasks, it would be worth while. I can't think of one, though. You'd still have to parse the data by some initial procedure stored somewhere outside the drawing.

If you want to tag a drawing to be saved as an earlier version, you could simply tag it with an integer and look it up upon save.

Fuccaro

  • Guest
Save Data? (X-data theory by Vladimir Nesterovsky)
« Reply #5 on: May 14, 2004, 01:36:05 AM »
Se7en
Often I wrote lisp routines to solve a specific problem in a drawing –and probable I will never use them again. Without the drawing the routine itself it is useless.
It is a good idea to store those routines right in the drawing. Just don’t forget to place enough comments or additional text (stored also into dictionary). And also must write a "universal extractor routine" to read those texts -the explanations and the source cod.
It worth a try...