1 (in-package #:clnl-model)
3 (defvar *separator* "@#$#@#$#@")
19 (defun default-model ()
20 "DEFAULT-MODEL => MODEL
24 MODEL: an object representing the model
28 Returns the default startup model."
31 (make-view :min-pxcor -5 :max-pxcor 5 :min-pycor -5 :max-pycor 5))))
33 (defun read-from-nlogo (str)
34 "READ-FROM-NLOGO STR => MODEL
38 STR: a readable stream
39 MODEL: an object representing the model
43 Takes a stream STR, reads in a nlogo file, parses it, and then
44 returns the model object."
48 ((read-sections (&optional section)
50 ((line (read-line str nil)))
52 (if (string= *separator* line)
53 (cons section (read-sections))
54 (read-sections (append section (list line))))))))
57 :code (nth 0 sections)
58 :interface (parse-interface (nth 1 sections))
59 :info (nth 2 sections)
60 :turtle-shapes (nth 3 sections)
61 :version (nth 4 sections)
62 :preview-commands (nth 5 sections)
63 :system-dynamics (nth 6 sections)
64 :behavior-space (nth 7 sections)
65 :hub-net-client (nth 8 sections)
66 :link-shapes (nth 9 sections)
67 :model-settings (nth 10 sections)
68 :delta-tick (nth 11 sections))))
72 (defparameter *widget-parsers* nil)
74 (defmacro defwidget-definition (type &rest definitions)
81 (lambda (def) (when (find (car def) (list :int :double :boolean :choice :string)) (second def)))
91 ((line `(nth ,n ,lines)))
93 (:specified `(string= ,(second def) ,line))
94 (:int `(parse-integer ,line :junk-allowed t))
95 (:double `(ignore-errors (coerce (read-from-string ,line) 'double-float)))
96 (:boolean `(or (string= "1" ,line) (string= "0" ,line)))
97 (:choice `(find ,line ',(mapcar #'car (third def)) :test #'string=)))))
99 (loop for i to (length definitions) collect i)))))
101 (,(read-from-string (format nil "make-~A" type))
106 ((line `(nth ,n ,lines))
109 (:int `(parse-integer ,line))
110 (:double `(coerce (read-from-string ,line) 'double-float))
111 (:boolean `(string= "1" ,line))
112 (:choice `(cadr (find ,line ',(third def) :key #'car :test #'string=)))
114 (when val-getter (list (intern (symbol-name (cadr def)) :keyword) val-getter))))
116 (loop for i to (length definitions) collect i))))))
119 (defwidget-definition view
120 (:specified "GRAPHICS-WINDOW")
134 (:boolean wrapping-allowed-in-x)
135 (:boolean wrapping-allowed-in-y)
141 (:choice update-mode (("0" :continuous) ("1" :tick-based)))
143 (:boolean show-tick-counter)
144 (:string tick-counter-label)
145 (:double frame-rate 30))
147 (defun parse-interface (interface-as-strings)
151 ((separate-widgets-as-strings (lines &optional widget-as-strings)
153 (if (string= "" (car lines))
154 (cons widget-as-strings (separate-widgets-as-strings (cdr lines)))
155 (separate-widgets-as-strings (cdr lines) (append widget-as-strings (list (car lines))))))))
156 (separate-widgets-as-strings interface-as-strings))))
160 (lambda (widget-as-strings)
162 ((parser (find-if (lambda (validator) (funcall validator widget-as-strings)) *widget-parsers* :key #'car)))
163 (when parser (funcall (cadr parser) widget-as-strings))))
164 widgets-as-strings))))
166 ;; INFORMATION ABOUT MODEL
168 (defun world-dimensions (model)
169 "WORLD-DIMENSIONS MODEL => DIMS
171 DIMS: (:xmin XMIN :xmax XMAX :ymin YMIN :ymax YMAX)
173 ARGUMENTS AND VALUES:
175 MODEL: A valid model containing a view
176 XMIN: An integer representing the minimum patch coord in X
177 XMAX: An integer representing the maximum patch coord in X
178 YMIN: An integer representing the minimum patch coord in Y
179 YMAX: An integer representing the maximum patch coord in Y
183 Returns the dimensions of MODEL. MODEL must be a valid model
184 as parsed by CLNL, and have a valid view in it."
186 ((view (find-if #'view-p (model-interface model))))
188 :xmin (view-min-pxcor view)
189 :xmax (view-max-pxcor view)
190 :ymin (view-min-pycor view)
191 :ymax (view-max-pycor view))))