Qt Example: Colliding Mice
Annotate for file src/qt/colliding-mice.lisp
2010-01-25 tobias 1 ;;; Copyright (c) 2010 Tobias Rautenkranz <tobias@rautenkranz.ch>
21:13:57 ' 2 ;;; Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
' 3 ;;; Contact: Qt Software Information (qt-info@nokia.com)
' 4 ;;;
' 5 ;;; This file is part of the examples of the Qt Toolkit.
' 6 ;;;
' 7 ;;; $QT_BEGIN_LICENSE:LGPL$
' 8 ;;; Commercial Usage
' 9 ;;; Licensees holding valid Qt Commercial licenses may use this file in
' 10 ;;; accordance with the Qt Commercial License Agreement provided with the
' 11 ;;; Software or, alternatively, in accordance with the terms contained in
' 12 ;;; a written agreement between you and Nokia.
' 13 ;;;
' 14 ;;; GNU Lesser General Public License Usage
' 15 ;;; Alternatively, this file may be used under the terms of the GNU Lesser
' 16 ;;; General Public License version 2.1 as published by the Free Software
' 17 ;;; Foundation and appearing in the file LICENSE.LGPL included in the
' 18 ;;; packaging of this file. Please review the following information to
' 19 ;;; ensure the GNU Lesser General Public License version 2.1 requirements
' 20 ;;; will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
' 21 ;;;
' 22 ;;; In addition, as a special exception, Nokia gives you certain
' 23 ;;; additional rights. These rights are described in the Nokia Qt LGPL
' 24 ;;; Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
' 25 ;;; package.
' 26 ;;;
' 27 ;;; GNU General Public License Usage
' 28 ;;; Alternatively, this file may be used under the terms of the GNU
' 29 ;;; General Public License version 3.0 as published by the Free Software
' 30 ;;; Foundation and appearing in the file LICENSE.GPL included in the
' 31 ;;; packaging of this file. Please review the following information to
' 32 ;;; ensure the GNU General Public License version 3.0 requirements will be
' 33 ;;; met: http://www.gnu.org/copyleft/gpl.html.
' 34 ;;;
' 35 ;;; If you are unsure which license is appropriate for your use, please
' 36 ;;; contact the sales department at qt-sales@nokia.com.
' 37 ;;; $QT_END_LICENSE$
' 38
' 39 (in-package :qt.examples)
' 40
' 41 (defun normalize-angle (angle)
' 42 (loop while (< angle 0) do
' 43 (incf angle (* 2 Pi)))
' 44 (loop while (> angle (* 2 Pi)) do
' 45 (decf angle (* 2 Pi)))
' 46 angle)
' 47
' 48 (defclass mouse (qt:graphics-item)
' 49 ((angle :initform 0d0 :accessor angle)
' 50 (speed :initform 0d0 :accessor mouse-speed)
' 51 (eye-direction :initform 0d0 :accessor eye-direction)
' 52 (color :initform (make-instance 'qt:color
' 53 :arg0 (random 256)
' 54 :arg1 (random 256)
' 55 :arg2 (random 256))
' 56 :accessor color))
' 57 (:metaclass cxx:class))
' 58
' 59 (defmethod initialize-instance :after ((mouse mouse) &rest initargs)
' 60 (declare (ignore initargs))
' 61 (cxx:rotate mouse (random (* 360 16))))
' 62
' 63 (defmethod cxx:bounding-rect ((mouse mouse))
' 64 (let ((adjust 0.5d0))
' 65 (make-instance 'qt:rect-f :args (list (- -18 adjust) (- -22 adjust)
' 66 (+ 36 adjust) (+ 60 adjust)))))
' 67
' 68 (defmethod cxx:shape ((mouse mouse))
' 69 (let ((path (make-instance 'qt:painter-path)))
' 70 (cxx:add-rect path -10 -20 20 40)
' 71 path))
' 72
' 73 (defmethod cxx:paint ((mouse mouse) painter option widget)
' 74 (declare (ignore option widget))
' 75 ;; Body
' 76 (cxx:set-brush painter (color mouse))
' 77 (cxx:draw-ellipse painter -10 -20 20 40)
' 78
' 79 ;; Eyes
' 80 (cxx:set-brush painter qt:+white+)
' 81 (cxx:draw-ellipse painter -10 -17 8 8)
' 82 (cxx:draw-ellipse painter 2 -17 8 8)
' 83
' 84 ;; Nose
' 85 (cxx:set-brush painter qt:+black+)
' 86 (cxx:draw-ellipse painter -2 -22 4 4)
' 87
' 88 ;; Pupils
' 89 (cxx:draw-ellipse painter
' 90 (make-instance 'qt:rect-f
' 91 :arg0 (+ -8d0 (eye-direction mouse))
' 92 :arg1 -17
' 93 :arg2 4 :arg3 4))
' 94 (cxx:draw-ellipse painter
' 95 (make-instance 'qt:rect-f
' 96 :arg0 (+ 4d0 (eye-direction mouse))
' 97 :arg1 -17
' 98 :arg2 4 :arg3 4))
' 99
' 100 ;; Ears
' 101 (cxx:set-brush painter
' 102 (if (zerop (length (cxx:colliding-items (cxx:scene mouse)
' 103 mouse)))
' 104 qt:+dark-yellow+
' 105 qt:+red+))
' 106 (cxx:draw-ellipse painter -17 -12 16 16)
' 107 (cxx:draw-ellipse painter 1 -12 16 16)
' 108
' 109 ;; Tail
' 110 (let ((path (make-instance 'qt:painter-path
' 111 :arg0 (make-instance 'qt:point-f :args '(0 20)))))
' 112 (cxx:cubic-to path -5 22 -5 22 0 25)
' 113 (cxx:cubic-to path 5 27 5 32 0 30)
' 114 (cxx:cubic-to path -5 32 -5 42 0 35)
' 115 (cxx:set-brush painter qt:+no-brush+)
' 116 (cxx:draw-path painter path)))
' 117
' 118 (defmethod cxx:advance ((mouse mouse) step)
' 119 (unless (zerop step)
' 120 ;; Don't move too far away
' 121 (let ((line-to-center (make-instance 'qt:line-f
' 122 :arg0 (make-instance 'qt:point-f
' 123 :args '(0 0))
' 124 :arg1 (cxx:map-from-scene mouse 0 0))))
' 125 (if (> (cxx:length line-to-center) 150)
' 126 (let ((angle-to-center (acos (/ (cxx:dx line-to-center)
' 127 (cxx:length line-to-center)))))
' 128 (when (< (cxx:dy line-to-center) 0)
' 129 (setf angle-to-center (- (* 2 Pi) angle-to-center)))
' 130 (setf angle-to-center (normalize-angle (+ (- Pi angle-to-center)
' 131 (/ Pi 2))))
' 132 (if (< (/ pi 4) angle-to-center pi)
' 133 ;; Rotate left
' 134 (incf (angle mouse) (if (< (angle mouse) (/ Pi -2))
' 135 0.25 -0.25))
' 136 (when (and (>= angle-to-center Pi)
' 137 (< angle-to-center (+ Pi (/ Pi 2) (/ Pi 4))))
' 138 ;; Rotate right
' 139 (incf (angle mouse) (if (< (angle mouse) (/ Pi 2))
' 140 0.25 -0.25)))))
' 141 (incf (angle mouse) (* (signum (angle mouse)) 0.25))))
' 142
' 143 ;; Try not to crash with any other mice
' 144 (let ((danger-mice
' 145 (cxx:items (cxx:scene mouse)
' 146 (make-instance 'qt:polygon-f
' 147 :arg0
' 148 (vector (cxx:map-to-scene mouse 0 0)
' 149 (cxx:map-to-scene mouse -30 -50)
' 150 (cxx:map-to-scene mouse 30 -50))))))
' 151 (loop for item across danger-mice
' 152 unless (eq item mouse) do
' 153 (let* ((line-to-mouse (make-instance
' 154 'qt:line-f
' 155 :arg0 (make-instance 'qt:point-f
' 156 :args '(0 0))
' 157 :arg1 (cxx:map-from-item mouse item 0 0)))
' 158 (angle-to-mouse (acos (/ (cxx:dx line-to-mouse)
' 159 (cxx:length line-to-mouse)))))
' 160 (when (< (cxx:dy line-to-mouse) 0)
' 161 (setf angle-to-mouse (- (* 2 Pi) angle-to-mouse)))
' 162 (setf angle-to-mouse (normalize-angle (+ (- Pi angle-to-mouse)
' 163 (/ Pi 2))))
' 164 (if (and (>= angle-to-mouse 0) (< angle-to-mouse (/ Pi 2)))
' 165 ;; Rotate right
' 166 (incf (angle mouse) 0.5)
' 167 (when (and (<= angle-to-mouse (* 2 Pi))
' 168 (> angle-to-mouse (- (* 2 Pi) (/ Pi 2))))
' 169 ;; Rotate left
' 170 (decf (angle mouse) 0.5)))))
' 171
' 172 ;; Add some random movement
' 173 (when (and (> (length danger-mice) 1)
' 174 (zerop (random 10)))
' 175 (if (zerop (random 2))
' 176 (incf (angle mouse) (/ (random 100) 500d0))
' 177 (decf (angle mouse) (/ (random 100) 500d0))))
' 178
' 179 (incf (mouse-speed mouse) (/ (+ -50 (random 100)) 100d0))
' 180
' 181 (let ((dx (* (sin (angle mouse)) 10)))
' 182 (setf (eye-direction mouse)
' 183 (if (< (abs (/ dx 5)) 1) 0 (/ dx 5)))
' 184 (cxx:rotate mouse dx))
' 185 (cxx:set-pos mouse
' 186 (cxx:map-to-parent mouse
' 187 0
' 188 (- (+ 3
' 189 (* (sin (mouse-speed mouse))
' 190 3))))))))
' 191
' 192
' 193 (defconstant +mouse-count+ 7)
' 194
' 195 (defun colliding-mice ()
' 196 "Colliding Mice"
' 197 (qt:with-app ()
' 198 (let* ((scene (make-instance 'qt:graphics-scene))
' 199 (view (make-instance 'qt:graphics-view :arg0 scene))
' 200 (items)
' 201 (timer (make-instance 'qt:timer)))
' 202 ;; Prevent SECENE and TIMER from beeing GCed before VIEW.
' 203 (setf (qt:property view 'closure)
' 204 (qt:make-lisp-variant (cons scene timer)))
' 205 (cxx:set-scene-rect scene -300 -300 600 600)
' 206 (cxx:set-item-index-method scene qt:graphics-scene.+no-index+)
' 207 (dotimes (i +mouse-count+)
' 208 (let ((mouse (make-instance 'mouse)))
' 209 (cxx:set-pos mouse
' 210 (* (sin (/ (* i 6.28) +mouse-count+)) 200)
' 211 (* (cos (/ (* i 6.28) +mouse-count+)) 200))
' 212 (push mouse items)
' 213 (cxx:add-item scene mouse)))
' 214 (cxx:set-render-hint view qt:painter.+antialiasing+)
' 215 (cxx:set-background-brush
' 216 view
' 217 (make-instance 'qt:pixmap
' 218 :arg0 (concatenate 'string
' 219 (namestring *source-path*)
' 220 "/qt/images/cheese.jpg")))
' 221 (cxx:set-cache-mode view qt:graphics-view.+cache-background+)
' 222 (cxx:set-viewport-update-mode view qt:graphics-view.+bounding-rect-viewport-update+)
' 223 (cxx:set-drag-mode view qt:graphics-view.+scroll-hand-drag+)
' 224 (cxx:set-window-title view "Colliding Mice")
' 225 (cxx:resize view 400 300)
' 226 (cxx:show view)
' 227 (qt:connect (qt:get-signal timer "timeout()")
' 228 (qt:get-slot scene "advance()"))
' 229 (cxx:start timer (floor 1000 33))
' 230 (qt:exec))))