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 :option)) (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=)))
113 (:option `(when (string/= ,line ,(third def)) ,line))
115 (when val-getter (list (intern (symbol-name (cadr def)) :keyword) val-getter))))
117 (loop for i to (length definitions) collect i))))))
120 (defwidget-definition view
121 (:specified "GRAPHICS-WINDOW")
135 (:boolean wrapping-allowed-in-x)
136 (:boolean wrapping-allowed-in-y)
142 (:choice update-mode (("0" :continuous) ("1" :tick-based)))
144 (:boolean show-tick-counter)
145 (:string tick-counter-label)
146 (:double frame-rate 30))
148 (defwidget-definition slider
149 (:specified "SLIDER")
161 (:option units "NIL")
162 (:choice direction (("HORIZONTAL" :horizontal) ("VERTICAL" :vertical))))
164 (defun parse-interface (interface-as-strings)
168 ((separate-widgets-as-strings (lines &optional widget-as-strings)
170 (if (string= "" (car lines))
171 (cons widget-as-strings (separate-widgets-as-strings (cdr lines)))
172 (separate-widgets-as-strings (cdr lines) (append widget-as-strings (list (car lines))))))))
173 (separate-widgets-as-strings interface-as-strings))))
177 (lambda (widget-as-strings)
179 ((parser (find-if (lambda (validator) (funcall validator widget-as-strings)) *widget-parsers* :key #'car)))
180 (when parser (funcall (cadr parser) widget-as-strings))))
181 widgets-as-strings))))
183 ;; INFORMATION ABOUT MODEL
185 (defun world-dimensions (model)
186 "WORLD-DIMENSIONS MODEL => DIMS
188 DIMS: (:xmin XMIN :xmax XMAX :ymin YMIN :ymax YMAX)
190 ARGUMENTS AND VALUES:
192 MODEL: A valid model containing a view
193 XMIN: An integer representing the minimum patch coord in X
194 XMAX: An integer representing the maximum patch coord in X
195 YMIN: An integer representing the minimum patch coord in Y
196 YMAX: An integer representing the maximum patch coord in Y
200 Returns the dimensions of MODEL. MODEL must be a valid model
201 as parsed by CLNL, and have a valid view in it."
203 ((view (find-if #'view-p (model-interface model))))
205 :xmin (view-min-pxcor view)
206 :xmax (view-max-pxcor view)
207 :ymin (view-min-pycor view)
208 :ymax (view-max-pycor view))))