OSDN Git Service

60449c0c5cee328b6d7228d382147545e6f3a2ec
[psychlops/cpp_document.git] / Psychlops.manual / Psychlops.manual.html
1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
2 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
3 <head>
4 <script type="text/javascript">
5 //<![CDATA[
6 var version = {title: "TiddlyWiki", major: 2, minor: 2, revision: 4, date: new Date("Jun 19, 2007"), extensions: {}};
7 //]]>
8 </script>
9 <!--
10 TiddlyWiki created by Jeremy Ruston, (jeremy [at] osmosoft [dot] com)
11
12 Copyright (c) UnaMesa Association 2004-2007
13
14 Redistribution and use in source and binary forms, with or without modification,
15 are permitted provided that the following conditions are met:
16
17 Redistributions of source code must retain the above copyright notice, this
18 list of conditions and the following disclaimer.
19
20 Redistributions in binary form must reproduce the above copyright notice, this
21 list of conditions and the following disclaimer in the documentation and/or other
22 materials provided with the distribution.
23
24 Neither the name of the UnaMesa Association nor the names of its contributors may be
25 used to endorse or promote products derived from this software without specific
26 prior written permission.
27
28 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
29 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
30 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
31 SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
32 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
33 TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
34 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
36 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
37 DAMAGE.
38 -->
39 <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
40 <!--PRE-HEAD-START-->
41 <!--{{{-->
42 <link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml'/>
43 <!--}}}-->
44 <!--PRE-HEAD-END-->
45 <title> Psychlops Manual - Version 1.3.5 (2009/September)Generated by TiddlyWikiAuthored by Psychlops Admin Group </title>
46 <style type="text/css">
47 #saveTest {display:none;}
48 #messageArea {display:none;}
49 #copyright {display:none;}
50 #storeArea {display:none;}
51 #storeArea div {padding:0.5em; margin:1em 0em 0em 0em; border-color:#fff #666 #444 #ddd; border-style:solid; border-width:2px; overflow:auto;}
52 #shadowArea {display:none;}
53 #javascriptWarning {width:100%; text-align:center; font-weight:bold; background-color:#dd1100; color:#fff; padding:1em 0em;}
54 </style>
55 <!--POST-HEAD-START-->
56
57 <!--POST-HEAD-END-->
58 </head>
59 <body onload="main();" onunload="if(window.checkUnsavedChanges) checkUnsavedChanges(); if(window.scrubNodes) scrubNodes(document.body);">
60 <!--PRE-BODY-START-->
61
62 <!--PRE-BODY-END-->
63 <div id="copyright">
64 Welcome to TiddlyWiki created by Jeremy Ruston, Copyright &copy; 2007 UnaMesa Association
65 </div>
66 <noscript>
67         <div id="javascriptWarning">This page requires JavaScript to function properly.<br /><br />If you are using Microsoft Internet Explorer you may need to click on the yellow bar above and select 'Allow Blocked Content'. You must then click 'Yes' on the following security warning.</div>
68 </noscript>
69 <div id="saveTest"></div>
70 <div id="backstageCloak"></div>
71 <div id="backstageButton"></div>
72 <div id="backstageArea"><div id="backstageToolbar"></div></div>
73 <div id="backstage">
74         <div id="backstagePanel"></div>
75 </div>
76 <div id="contentWrapper"></div>
77 <div id="contentStash"></div>
78 <div id="shadowArea">
79 <div title="ColorPalette">
80 <pre>Background: #fff
81 Foreground: #000
82 PrimaryPale: #8cf
83 PrimaryLight: #18f
84 PrimaryMid: #04b
85 PrimaryDark: #014
86 SecondaryPale: #ffc
87 SecondaryLight: #fe8
88 SecondaryMid: #db4
89 SecondaryDark: #841
90 TertiaryPale: #eee
91 TertiaryLight: #ccc
92 TertiaryMid: #999
93 TertiaryDark: #666
94 Error: #f88</pre>
95 </div>
96 <div title="StyleSheetColors">
97 <pre>/*{{{*/
98 body {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
99
100 a {color:[[ColorPalette::PrimaryMid]];}
101 a:hover {background-color:[[ColorPalette::PrimaryMid]]; color:[[ColorPalette::Background]];}
102 a img {border:0;}
103
104 h1,h2,h3,h4,h5,h6 {color:[[ColorPalette::SecondaryDark]]; background:transparent;}
105 h1 {border-bottom:2px solid [[ColorPalette::TertiaryLight]];}
106 h2,h3 {border-bottom:1px solid [[ColorPalette::TertiaryLight]];}
107
108 .button {color:[[ColorPalette::PrimaryDark]]; border:1px solid [[ColorPalette::Background]];}
109 .button:hover {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::SecondaryLight]]; border-color:[[ColorPalette::SecondaryMid]];}
110 .button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::SecondaryDark]];}
111
112 .header {background:[[ColorPalette::PrimaryMid]];}
113 .headerShadow {color:[[ColorPalette::Foreground]];}
114 .headerShadow a {font-weight:normal; color:[[ColorPalette::Foreground]];}
115 .headerForeground {color:[[ColorPalette::Background]];}
116 .headerForeground a {font-weight:normal; color:[[ColorPalette::PrimaryPale]];}
117
118 .tabSelected{color:[[ColorPalette::PrimaryDark]];
119         background:[[ColorPalette::TertiaryPale]];
120         border-left:1px solid [[ColorPalette::TertiaryLight]];
121         border-top:1px solid [[ColorPalette::TertiaryLight]];
122         border-right:1px solid [[ColorPalette::TertiaryLight]];
123 }
124 .tabUnselected {color:[[ColorPalette::Background]]; background:[[ColorPalette::TertiaryMid]];}
125 .tabContents {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::TertiaryPale]]; border:1px solid [[ColorPalette::TertiaryLight]];}
126 .tabContents .button {border:0;}
127
128 #sidebar {}
129 #sidebarOptions input {border:1px solid [[ColorPalette::PrimaryMid]];}
130 #sidebarOptions .sliderPanel {background:[[ColorPalette::PrimaryPale]];}
131 #sidebarOptions .sliderPanel a {border:none;color:[[ColorPalette::PrimaryMid]];}
132 #sidebarOptions .sliderPanel a:hover {color:[[ColorPalette::Background]]; background:[[ColorPalette::PrimaryMid]];}
133 #sidebarOptions .sliderPanel a:active {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::Background]];}
134
135 .wizard {background:[[ColorPalette::PrimaryPale]]; border:1px solid [[ColorPalette::PrimaryMid]];}
136 .wizard h1 {color:[[ColorPalette::PrimaryDark]]; border:none;}
137 .wizard h2 {color:[[ColorPalette::Foreground]]; border:none;}
138 .wizardStep {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];
139         border:1px solid [[ColorPalette::PrimaryMid]];}
140 .wizardStep.wizardStepDone {background::[[ColorPalette::TertiaryLight]];}
141 .wizardFooter {background:[[ColorPalette::PrimaryPale]];}
142 .wizardFooter .status {background:[[ColorPalette::PrimaryDark]]; color:[[ColorPalette::Background]];}
143 .wizard .button {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryLight]]; border: 1px solid;
144         border-color:[[ColorPalette::SecondaryPale]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryPale]];}
145 .wizard .button:hover {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Background]];}
146 .wizard .button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::Foreground]]; border: 1px solid;
147         border-color:[[ColorPalette::PrimaryDark]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryDark]];}
148
149 #messageArea {border:1px solid [[ColorPalette::SecondaryMid]]; background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]];}
150 #messageArea .button {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::SecondaryPale]]; border:none;}
151
152 .popupTiddler {background:[[ColorPalette::TertiaryPale]]; border:2px solid [[ColorPalette::TertiaryMid]];}
153
154 .popup {background:[[ColorPalette::TertiaryPale]]; color:[[ColorPalette::TertiaryDark]]; border-left:1px solid [[ColorPalette::TertiaryMid]]; border-top:1px solid [[ColorPalette::TertiaryMid]]; border-right:2px solid [[ColorPalette::TertiaryDark]]; border-bottom:2px solid [[ColorPalette::TertiaryDark]];}
155 .popup hr {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::PrimaryDark]]; border-bottom:1px;}
156 .popup li.disabled {color:[[ColorPalette::TertiaryMid]];}
157 .popup li a, .popup li a:visited {color:[[ColorPalette::Foreground]]; border: none;}
158 .popup li a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border: none;}
159 .popup li a:active {background:[[ColorPalette::SecondaryPale]]; color:[[ColorPalette::Foreground]]; border: none;}
160 .popupHighlight {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
161 .listBreak div {border-bottom:1px solid [[ColorPalette::TertiaryDark]];}
162
163 .tiddler .defaultCommand {font-weight:bold;}
164
165 .shadow .title {color:[[ColorPalette::TertiaryDark]];}
166
167 .title {color:[[ColorPalette::SecondaryDark]];}
168 .subtitle {color:[[ColorPalette::TertiaryDark]];}
169
170 .toolbar {color:[[ColorPalette::PrimaryMid]];}
171 .toolbar a {color:[[ColorPalette::TertiaryLight]];}
172 .selected .toolbar a {color:[[ColorPalette::TertiaryMid]];}
173 .selected .toolbar a:hover {color:[[ColorPalette::Foreground]];}
174
175 .tagging, .tagged {border:1px solid [[ColorPalette::TertiaryPale]]; background-color:[[ColorPalette::TertiaryPale]];}
176 .selected .tagging, .selected .tagged {background-color:[[ColorPalette::TertiaryLight]]; border:1px solid [[ColorPalette::TertiaryMid]];}
177 .tagging .listTitle, .tagged .listTitle {color:[[ColorPalette::PrimaryDark]];}
178 .tagging .button, .tagged .button {border:none;}
179
180 .footer {color:[[ColorPalette::TertiaryLight]];}
181 .selected .footer {color:[[ColorPalette::TertiaryMid]];}
182
183 .sparkline {background:[[ColorPalette::PrimaryPale]]; border:0;}
184 .sparktick {background:[[ColorPalette::PrimaryDark]];}
185
186 .error, .errorButton {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Error]];}
187 .warning {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryPale]];}
188 .lowlight {background:[[ColorPalette::TertiaryLight]];}
189
190 .zoomer {background:none; color:[[ColorPalette::TertiaryMid]]; border:3px solid [[ColorPalette::TertiaryMid]];}
191
192 .imageLink, #displayArea .imageLink {background:transparent;}
193
194 .annotation {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border:2px solid [[ColorPalette::SecondaryMid]];}
195
196 .viewer .listTitle {list-style-type:none; margin-left:-2em;}
197 .viewer .button {border:1px solid [[ColorPalette::SecondaryMid]];}
198 .viewer blockquote {border-left:3px solid [[ColorPalette::TertiaryDark]];}
199
200 .viewer table, table.twtable {border:2px solid [[ColorPalette::TertiaryDark]];}
201 .viewer th, .viewer thead td, .twtable th, .twtable thead td {background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::Background]];}
202 .viewer td, .viewer tr, .twtable td, .twtable tr {border:1px solid [[ColorPalette::TertiaryDark]];}
203
204 .viewer pre {border:1px solid [[ColorPalette::SecondaryLight]]; background:[[ColorPalette::SecondaryPale]];}
205 .viewer code {color:[[ColorPalette::SecondaryDark]];}
206 .viewer hr {border:0; border-top:dashed 1px [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::TertiaryDark]];}
207
208 .highlight, .marked {background:[[ColorPalette::SecondaryLight]];}
209
210 .editor input {border:1px solid [[ColorPalette::PrimaryMid]];}
211 .editor textarea {border:1px solid [[ColorPalette::PrimaryMid]]; width:100%;}
212 .editorFooter {color:[[ColorPalette::TertiaryMid]];}
213
214 #backstageArea {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::TertiaryMid]];}
215 #backstageArea a {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
216 #backstageArea a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; }
217 #backstageArea a.backstageSelTab {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
218 #backstageButton a {background:none; color:[[ColorPalette::Background]]; border:none;}
219 #backstageButton a:hover {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
220 #backstagePanel {background:[[ColorPalette::Background]]; border-color: [[ColorPalette::Background]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]];}
221 .backstagePanelFooter .button {border:none; color:[[ColorPalette::Background]];}
222 .backstagePanelFooter .button:hover {color:[[ColorPalette::Foreground]];}
223 #backstageCloak {background:[[ColorPalette::Foreground]]; opacity:0.6; filter:'alpha(opacity:60)';}
224 /*}}}*/</pre>
225 </div>
226 <div title="StyleSheetLayout">
227 <pre>/*{{{*/
228 * html .tiddler {height:1%;}
229
230 body {font-size:.75em; font-family:arial,helvetica; margin:0; padding:0;}
231
232 h1,h2,h3,h4,h5,h6 {font-weight:bold; text-decoration:none;}
233 h1,h2,h3 {padding-bottom:1px; margin-top:1.2em;margin-bottom:0.3em;}
234 h4,h5,h6 {margin-top:1em;}
235 h1 {font-size:1.35em;}
236 h2 {font-size:1.25em;}
237 h3 {font-size:1.1em;}
238 h4 {font-size:1em;}
239 h5 {font-size:.9em;}
240
241 hr {height:1px;}
242
243 a {text-decoration:none;}
244
245 dt {font-weight:bold;}
246
247 ol {list-style-type:decimal;}
248 ol ol {list-style-type:lower-alpha;}
249 ol ol ol {list-style-type:lower-roman;}
250 ol ol ol ol {list-style-type:decimal;}
251 ol ol ol ol ol {list-style-type:lower-alpha;}
252 ol ol ol ol ol ol {list-style-type:lower-roman;}
253 ol ol ol ol ol ol ol {list-style-type:decimal;}
254
255 .txtOptionInput {width:11em;}
256
257 #contentWrapper .chkOptionInput {border:0;}
258
259 .externalLink {text-decoration:underline;}
260
261 .indent {margin-left:3em;}
262 .outdent {margin-left:3em; text-indent:-3em;}
263 code.escaped {white-space:nowrap;}
264
265 .tiddlyLinkExisting {font-weight:bold;}
266 .tiddlyLinkNonExisting {font-style:italic;}
267
268 /* the 'a' is required for IE, otherwise it renders the whole tiddler in bold */
269 a.tiddlyLinkNonExisting.shadow {font-weight:bold;}
270
271 #mainMenu .tiddlyLinkExisting,
272         #mainMenu .tiddlyLinkNonExisting,
273         #sidebarTabs .tiddlyLinkNonExisting {font-weight:normal; font-style:normal;}
274 #sidebarTabs .tiddlyLinkExisting {font-weight:bold; font-style:normal;}
275
276 .header {position:relative;}
277 .header a:hover {background:transparent;}
278 .headerShadow {position:relative; padding:4.5em 0em 1em 1em; left:-1px; top:-1px;}
279 .headerForeground {position:absolute; padding:4.5em 0em 1em 1em; left:0px; top:0px;}
280
281 .siteTitle {font-size:3em;}
282 .siteSubtitle {font-size:1.2em;}
283
284 #mainMenu {position:absolute; left:0; width:10em; text-align:right; line-height:1.6em; padding:1.5em 0.5em 0.5em 0.5em; font-size:1.1em;}
285
286 #sidebar {position:absolute; right:3px; width:16em; font-size:.9em;}
287 #sidebarOptions {padding-top:0.3em;}
288 #sidebarOptions a {margin:0em 0.2em; padding:0.2em 0.3em; display:block;}
289 #sidebarOptions input {margin:0.4em 0.5em;}
290 #sidebarOptions .sliderPanel {margin-left:1em; padding:0.5em; font-size:.85em;}
291 #sidebarOptions .sliderPanel a {font-weight:bold; display:inline; padding:0;}
292 #sidebarOptions .sliderPanel input {margin:0 0 .3em 0;}
293 #sidebarTabs .tabContents {width:15em; overflow:hidden;}
294
295 .wizard {padding:0.1em 1em 0em 2em;}
296 .wizard h1 {font-size:2em; font-weight:bold; background:none; padding:0em 0em 0em 0em; margin:0.4em 0em 0.2em 0em;}
297 .wizard h2 {font-size:1.2em; font-weight:bold; background:none; padding:0em 0em 0em 0em; margin:0.4em 0em 0.2em 0em;}
298 .wizardStep {padding:1em 1em 1em 1em;}
299 .wizard .button {margin:0.5em 0em 0em 0em; font-size:1.2em;}
300 .wizardFooter {padding:0.8em 0.4em 0.8em 0em;}
301 .wizardFooter .status {padding:0em 0.4em 0em 0.4em; margin-left:1em;}
302 .wizard .button {padding:0.1em 0.2em 0.1em 0.2em;}
303
304 #messageArea {position:fixed; top:2em; right:0em; margin:0.5em; padding:0.5em; z-index:2000; _position:absolute;}
305 .messageToolbar {display:block; text-align:right; padding:0.2em 0.2em 0.2em 0.2em;}
306 #messageArea a {text-decoration:underline;}
307
308 .tiddlerPopupButton {padding:0.2em 0.2em 0.2em 0.2em;}
309 .popupTiddler {position: absolute; z-index:300; padding:1em 1em 1em 1em; margin:0;}
310
311 .popup {position:absolute; z-index:300; font-size:.9em; padding:0; list-style:none; margin:0;}
312 .popup .popupMessage {padding:0.4em;}
313 .popup hr {display:block; height:1px; width:auto; padding:0; margin:0.2em 0em;}
314 .popup li.disabled {padding:0.4em;}
315 .popup li a {display:block; padding:0.4em; font-weight:normal; cursor:pointer;}
316 .listBreak {font-size:1px; line-height:1px;}
317 .listBreak div {margin:2px 0;}
318
319 .tabset {padding:1em 0em 0em 0.5em;}
320 .tab {margin:0em 0em 0em 0.25em; padding:2px;}
321 .tabContents {padding:0.5em;}
322 .tabContents ul, .tabContents ol {margin:0; padding:0;}
323 .txtMainTab .tabContents li {list-style:none;}
324 .tabContents li.listLink { margin-left:.75em;}
325
326 #contentWrapper {display:block;}
327 #splashScreen {display:none;}
328
329 #displayArea {margin:1em 17em 0em 14em;}
330
331 .toolbar {text-align:right; font-size:.9em;}
332
333 .tiddler {padding:1em 1em 0em 1em;}
334
335 .missing .viewer,.missing .title {font-style:italic;}
336
337 .title {font-size:1.6em; font-weight:bold;}
338
339 .missing .subtitle {display:none;}
340 .subtitle {font-size:1.1em;}
341
342 .tiddler .button {padding:0.2em 0.4em;}
343
344 .tagging {margin:0.5em 0.5em 0.5em 0; float:left; display:none;}
345 .isTag .tagging {display:block;}
346 .tagged {margin:0.5em; float:right;}
347 .tagging, .tagged {font-size:0.9em; padding:0.25em;}
348 .tagging ul, .tagged ul {list-style:none; margin:0.25em; padding:0;}
349 .tagClear {clear:both;}
350
351 .footer {font-size:.9em;}
352 .footer li {display:inline;}
353
354 .annotation {padding:0.5em; margin:0.5em;}
355
356 * html .viewer pre {width:99%; padding:0 0 1em 0;}
357 .viewer {line-height:1.4em; padding-top:0.5em;}
358 .viewer .button {margin:0em 0.25em; padding:0em 0.25em;}
359 .viewer blockquote {line-height:1.5em; padding-left:0.8em;margin-left:2.5em;}
360 .viewer ul, .viewer ol {margin-left:0.5em; padding-left:1.5em;}
361
362 .viewer table, table.twtable {border-collapse:collapse; margin:0.8em 1.0em;}
363 .viewer th, .viewer td, .viewer tr,.viewer caption,.twtable th, .twtable td, .twtable tr,.twtable caption {padding:3px;}
364 table.listView {font-size:0.85em; margin:0.8em 1.0em;}
365 table.listView th, table.listView td, table.listView tr {padding:0px 3px 0px 3px;}
366
367 .viewer pre {padding:0.5em; margin-left:0.5em; font-size:1.2em; line-height:1.4em; overflow:auto;}
368 .viewer code {font-size:1.2em; line-height:1.4em;}
369
370 .editor {font-size:1.1em;}
371 .editor input, .editor textarea {display:block; width:100%; font:inherit;}
372 .editorFooter {padding:0.25em 0em; font-size:.9em;}
373 .editorFooter .button {padding-top:0px; padding-bottom:0px;}
374
375 .fieldsetFix {border:0; padding:0; margin:1px 0px 1px 0px;}
376
377 .sparkline {line-height:1em;}
378 .sparktick {outline:0;}
379
380 .zoomer {font-size:1.1em; position:absolute; overflow:hidden;}
381 .zoomer div {padding:1em;}
382
383 * html #backstage {width:99%;}
384 * html #backstageArea {width:99%;}
385 #backstageArea {display:none; position:relative; overflow: hidden; z-index:150; padding:0.3em 0.5em 0.3em 0.5em;}
386 #backstageToolbar {position:relative;}
387 #backstageArea a {font-weight:bold; margin-left:0.5em; padding:0.3em 0.5em 0.3em 0.5em;}
388 #backstageButton {display:none; position:absolute; z-index:175; top:0em; right:0em;}
389 #backstageButton a {padding:0.1em 0.4em 0.1em 0.4em; margin:0.1em 0.1em 0.1em 0.1em;}
390 #backstage {position:relative; width:100%; z-index:50;}
391 #backstagePanel {display:none; z-index:100; position:absolute; margin:0em 3em 0em 3em; padding:1em 1em 1em 1em;}
392 .backstagePanelFooter {padding-top:0.2em; float:right;}
393 .backstagePanelFooter a {padding:0.2em 0.4em 0.2em 0.4em;}
394 #backstageCloak {display:none; z-index:20; position:absolute; width:100%; height:100px;}
395
396 .whenBackstage {display:none;}
397 .backstageVisible .whenBackstage {display:block;}
398 /*}}}*/</pre>
399 </div>
400 <div title="StyleSheetLocale">
401 <pre>/***
402 StyleSheet for use when a translation requires any css style changes.
403 This StyleSheet can be used directly by languages such as Chinese, Japanese and Korean which use a logographic writing system and need larger font sizes.
404 ***/
405
406 /*{{{*/
407 body {font-size:0.8em;}
408
409 #sidebarOptions {font-size:1.05em;}
410 #sidebarOptions a {font-style:normal;}
411 #sidebarOptions .sliderPanel {font-size:0.95em;}
412
413 .subtitle {font-size:0.8em;}
414
415 .viewer table.listView {font-size:0.95em;}
416
417 .htmlarea .toolbarHA table {border:1px solid ButtonFace; margin:0em 0em;}
418 /*}}}*/</pre>
419 </div>
420 <div title="StyleSheetPrint">
421 <pre>/*{{{*/
422 @media print {
423 #mainMenu, #sidebar, #messageArea, .toolbar, #backstageButton {display: none ! important;}
424 #displayArea {margin: 1em 1em 0em 1em;}
425 /* Fixes a feature in Firefox 1.5.0.2 where print preview displays the noscript content */
426 noscript {display:none;}
427 }
428 /*}}}*/</pre>
429 </div>
430 <div title="PageTemplate">
431 <pre>&lt;!--{{{--&gt;
432 &lt;div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'&gt;
433 &lt;div class='headerShadow'&gt;
434 &lt;span class='siteTitle' refresh='content' tiddler='SiteTitle'&gt;&lt;/span&gt;&amp;nbsp;
435 &lt;span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'&gt;&lt;/span&gt;
436 &lt;/div&gt;
437 &lt;div class='headerForeground'&gt;
438 &lt;span class='siteTitle' refresh='content' tiddler='SiteTitle'&gt;&lt;/span&gt;&amp;nbsp;
439 &lt;span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'&gt;&lt;/span&gt;
440 &lt;/div&gt;
441 &lt;/div&gt;
442 &lt;div id='mainMenu' refresh='content' tiddler='MainMenu'&gt;&lt;/div&gt;
443 &lt;div id='sidebar'&gt;
444 &lt;div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'&gt;&lt;/div&gt;
445 &lt;div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'&gt;&lt;/div&gt;
446 &lt;/div&gt;
447 &lt;div id='displayArea'&gt;
448 &lt;div id='messageArea'&gt;&lt;/div&gt;
449 &lt;div id='tiddlerDisplay'&gt;&lt;/div&gt;
450 &lt;/div&gt;
451 &lt;!--}}}--&gt;</pre>
452 </div>
453 <div title="ViewTemplate">
454 <pre>&lt;!--{{{--&gt;
455 &lt;div class='toolbar' macro='toolbar closeTiddler closeOthers +editTiddler &gt; fields syncing permalink references jump'&gt;&lt;/div&gt;
456 &lt;div class='title' macro='view title'&gt;&lt;/div&gt;
457 &lt;div class='subtitle'&gt;&lt;span macro='view modifier link'&gt;&lt;/span&gt;, &lt;span macro='view modified date'&gt;&lt;/span&gt; (&lt;span macro='message views.wikified.createdPrompt'&gt;&lt;/span&gt; &lt;span macro='view created date'&gt;&lt;/span&gt;)&lt;/div&gt;
458 &lt;div class='tagging' macro='tagging'&gt;&lt;/div&gt;
459 &lt;div class='tagged' macro='tags'&gt;&lt;/div&gt;
460 &lt;div class='viewer' macro='view text wikified'&gt;&lt;/div&gt;
461 &lt;div class='tagClear'&gt;&lt;/div&gt;
462 &lt;!--}}}--&gt;</pre>
463 </div>
464 <div title="EditTemplate">
465 <pre>&lt;!--{{{--&gt;
466 &lt;div class='toolbar' macro='toolbar +saveTiddler -cancelTiddler deleteTiddler'&gt;&lt;/div&gt;
467 &lt;div class='title' macro='view title'&gt;&lt;/div&gt;
468 &lt;div class='editor' macro='edit title'&gt;&lt;/div&gt;
469 &lt;div macro='annotations'&gt;&lt;/div&gt;
470 &lt;div class='editor' macro='edit text'&gt;&lt;/div&gt;
471 &lt;div class='editor' macro='edit tags'&gt;&lt;/div&gt;&lt;div class='editorFooter'&gt;&lt;span macro='message views.editor.tagPrompt'&gt;&lt;/span&gt;&lt;span macro='tagChooser'&gt;&lt;/span&gt;&lt;/div&gt;
472 &lt;!--}}}--&gt;</pre>
473 </div>
474 <div title="GettingStarted">
475 <pre>To get started with this blank TiddlyWiki, you'll need to modify the following tiddlers:
476 * SiteTitle &amp; SiteSubtitle: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)
477 * MainMenu: The menu (usually on the left)
478 * DefaultTiddlers: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened
479 You'll also need to enter your username for signing your edits: &lt;&lt;option txtUserName&gt;&gt;</pre>
480 </div>
481 <div title="OptionsPanel">
482 <pre>These InterfaceOptions for customising TiddlyWiki are saved in your browser
483
484 Your username for signing your edits. Write it as a WikiWord (eg JoeBloggs)
485
486 &lt;&lt;option txtUserName&gt;&gt;
487 &lt;&lt;option chkSaveBackups&gt;&gt; SaveBackups
488 &lt;&lt;option chkAutoSave&gt;&gt; AutoSave
489 &lt;&lt;option chkRegExpSearch&gt;&gt; RegExpSearch
490 &lt;&lt;option chkCaseSensitiveSearch&gt;&gt; CaseSensitiveSearch
491 &lt;&lt;option chkAnimate&gt;&gt; EnableAnimations
492
493 ----
494 Also see AdvancedOptions</pre>
495 </div>
496 </div>
497 <!--POST-SHADOWAREA-->
498 <div id="storeArea">
499 <div title="0. Psyclopsとは" modifier="Kazushi Maruya" modified="200712061845" created="200712061745" changecount="4">
500 <pre>Psychlopsは、できるだけ簡単に正確でフレキシブルな画面描画をするためのツールです。
501 Psychlopsを使うと、Windows/MacのCGアプリケーションを作成することができます。
502
503 &quot;C++言語ライブラリ&quot;の形で提供されていますが、C++言語の複雑な構造の知識はほとんど必要ありません。
504 C++プログラミングの事前の知識がなくても、できる限り簡単に使用できるように設計されています。C++の文法については[[1.6節|1.6 Psychlopsを使ったプログラミング]]にまとめてある程度の知識があれば一通りの機能を利用することができるでしょう。
505
506 以下の項目ではPsychlopsの概要についてもう少し詳しく説明します。とにかく使ってみたい方は[[1節|1. インストールを行う]]に進んでください。
507
508 ! Psychlopsの主な特徴
509 * 複雑な操作を正確に行えることをコンセプトとしています。
510 * 刺激提示に特化し、視覚実験での使いやすさを追求しています。
511 * Mac OS XでもWindowsでも使える汎用性があります。
512 * 無償のツールのみで使用可能です。
513 * 他のC/C++コードと混在させることができます。
514
515 ! 複雑な操作を正確に行う
516
517 Psychlopsはプログラミング初心者でも取り扱いやすいライブラリをコンセプトとしています。習得難易度は各種ソフト付属のスクリプト程度を目標としています。
518 C/C++言語で刺激を記述しなければならない場合、どうしても初期化処理が煩雑になり、制御の正確さまで手が回りません。例えば、OpenGL標準の拡張ライブラリGLUTは、時間の制御精度は全く保証されていません。Psychlopsは初期化処理を全自動で行いますが、その制御処理は逐一検証して作成しています。
519
520 * PCの機能の最小単位で視覚刺激を操作できます
521 ** 画素単位で操作が可能です。
522 ** 垂直同期ひとつひとつにフレームを埋め込むことができます。
523 * プログラム言語を直接うつことで、パラメタをシステマティックに変えるなどの複雑な実験パラダイムを組むことも可能です。
524
525
526 ! 刺激提示に特化した内容
527 刺激提示の準備処理を自動化し、刺激に必要な部分だけを書けば動かすことができます。以下の例は、画像ファイルを読み込んで提示する最短のプログラム例です。
528
529 {{{
530 #include &lt;psychlops.h&gt;
531 using namespace Psychlops;                      // Psychlopsの呼び出し
532
533 void psychlops_main() {
534         Canvas display( Canvas::fullscreen );           // フルスクリーン表示領域の確保
535         Image natural_iamge;
536         natural_iamge.load(“Natural Image.png”)     // 自然画像の読み込み
537
538     while(Input::get(Keyboard::esc)) {      // ESCキーが押されるまで刺激提示
539         natural_iamge.centering().draw();               // 自然画像の位置あわせと提示
540            display.flip();                              // フレームの提示
541         }
542 }
543 }}}
544
545 ! 汎用性
546 Psychlopsの基本コンセプトは、OpenGL等のOSやドライバに直結した機能を用い、汎用ビデオカードを正確に制御することを目標としています。このため、特定のソフトや機械に依存せず、様々な環境で実行することが可能です。
547
548 * Mac OS XとWindowsで同一コードで動作可能です。
549 * 特定のソフトウェア・機器に依存しません。
550 ** 一定以上古い環境はサポートしておりません。[[1.1 必要な環境]]をご確認ください。
551 * 生成した実行ファイルを頒布可能です。
552
553
554 ! 無償のツールのみで使用可能
555 Psychloplsプログラムの作成実行は、OS以外はすべて無償のソフトを利用して行うことができます。
556
557 * Psychlops自体はオープンソースで公開されています。
558 * Mac OS X PPC mac, intel mac
559 ** Apple社より無償提供のXcode上でPsychlopsプログラムを作成できます
560 * Windows 2000, XP, Vista
561 ** 他ベンダ製無償の開発ツール上でPsychlopsプログラムを作成できます
562
563 </pre>
564 </div>
565 <div title="1. インストールを行う" modifier="YourName" modified="201002040400" created="200708211944" changecount="12">
566 <pre>Psychlopsによるアプリケーション作成には以下の2つのステップがあります。
567 * プログラム本体(cppの拡張子がついたテキストファイル)の作成
568 * 作成されたプログラムの実行(アプリケーション)ファイルへの変換(コンパイル)
569 これを実現するには、Psychlops本体のインストールとは別に、変換を行うためのコンパイラと呼ばれるプログラムのインストールが必要です。さらに、この2つのステップをスムーズに行うための支援プログラム(開発環境)をインストールすることを強く推奨します。
570
571 以下では、Macintosh, Windowsの各環境でこれらのアプリケーションをインストールしてPsychlopsによるC++アプリケーションの作成が可能な環境の準備を行う方法を説明します。
572
573 [[1.1 必要な環境]] 
574   [[1.1.1 ハードウェア環境]]
575   [[1.1.2 ソフトウェア環境]]
576 [[1.2 インストール作業(Mac) ]]
577   OSX 10.4の場合は[[こちら|1.2 インストール作業(Mac OS X 10.4) ]]
578 [[1.3 インストール作業(Windows) ]]
579   Borland + Reloの場合は[[こちら|1.3 インストール作業(Windows + BCC) ]]
580 [[1.4 更新とアンインストール]] 
581 [[1.5 Psychlopsの基本構造]] </pre>
582 </div>
583 <div title="1. 既成クラスを用いた簡単な実験プログラムを作成する" modifier="Psychlops_DevelopperG" modified="200908190217" created="200709252143" changecount="5">
584 <pre>この節では、ここまでのまとめとして、これまで説明してきた関数群を用いて簡単な恒常法の実験プログラム作成例について説明します。
585 一般的な恒常法を用いた心理学実験のプログラム上の大まかな流れは以下のようになります。
586
587 # 実際の試行に先立つ計算
588 ## 実験計画のプログラミング:独立変数の範囲設定と、ラテン格子法に基づいた各試行への割り振り
589 ## 実験刺激の事前描画(ある場合のみ):リアルタイム描画が不可能な精密な刺激の描画とバッファリング
590 # 各試行の反復実行
591 ## 各試行における描画(刺激の提示):各試行における独立変数の読み込みと、それらを用いた実際の描画
592 ## 反応の取得と 配列への記録
593 # 終了処理:反応が記録された配列の保存
594
595 この節では例としてGaborパターンの方位判断を恒常法で行わせる実験プログラムを考えます。
596 この実験の実験計画は以下のようなものであるとしましょう。
597 独立変数はGaborパターンの傾き量(2度, 4度, 8度, 16度, 32度の5水準)とパターンの提示時間(100, 250, 500 msの3水準)です。
598 この5水準のいずれかの量だけ垂直から右・左いずれかの方向に傾いたGaborパターンが100-500 msのうちのいずれかの時間だけ提示されます。
599 被験者の課題は提示された刺激パターンが右・左のいずれに傾いていたかをfキー(左)かjキー(右)を使って反応することです。
600 この試行をランダム順で各10回ずつ行うと1セッションが終わりです。
601
602 この実験計画を上の一般的なプログラムの流れにあわせてみると以下のようになります。
603
604 # 実際の試行に先立つ計算
605 ## 実験計画のプログラミング:Gabor方位の範囲設定と、ラテン格子法に基づいた各試行への割り振り
606 ## 実験刺激の事前描画:各方位のGaborパターンのオフスクリーン(Image)への描画
607 # 各試行の反復実行
608 ## 各試行における描画(刺激の提示):該当するGaborが描画されたImageインスタンスを用いた描画
609 ## 反応の取得と 配列への記録
610 # 終了処理:反応が記録された配列の保存:各試行の独立変数条件と被験者の反応(正答: 1, 誤答: 0)をファイルに記録
611
612 このそれぞれのパーツをC++のプログラムとして実装していきます。
613 [[1.1 実験計画のプログラミング]] (1-a)
614 [[1.2 Gaborパッチの事前描画]] (1-b)
615 [[1.3 各試行部分のプログラミング]] (2)
616 [[1.4 終了処理とまとめ]](3)</pre>
617 </div>
618 <div title="1.1 実験計画のプログラミング" modifier="Psychlops_DevelopperG" modified="200908190212" created="200709252144" changecount="1">
619 <pre>!ラテン格子法の実装
620 はじめに、実験計画の実装を行います。
621 ここはPsychlopsの使用とは直接関係ありませんが、初心者が実験プログラムを書くときに最も戸惑うところかもしれません。
622 このマニュアルでは、汎用性を意識したラテン格子法を実行する独立変数のクラスIndependentVariablesを追加して、使用してみます。
623 ここでは、このクラスの詳細な解説は行いません。
624 C++のプログラミングに慣れていない方はここで作成したクラスを単にプログラムに追加して使ってみることをお勧めします。
625
626 !!~IndependentVariablesクラスの概要
627 このクラスには、基本的な情報(メンバ)として
628 # 独立変数の数(int ~MaxVarNum)と、独立変数の水準数の最大値(int ~MaxStepNum)
629 #それぞれの水準の値(double * ~VariableStepNumber)
630 # 各水準ごとの繰り返しの数(int repeatNumber)
631 # ある特定の試行時の各独立変数の情報(Matrix ~CondMtx)
632 が格納されています。
633
634 次に実際に恒常法の実験プログラムで使用する命令(メソッド)としては、
635 * これらの値を設定するためのset()命令
636 * 各試行において必要な独立変数を取り出すためのget()命令
637 * ラテン格子法を実行するrandomize()命令
638 があります。
639
640 [[IndependentVariablesクラスのソースコード|IndependentVariables]]
641 上のリンクのソースコードを実際の宣言が起こる前(普通は、グローバル変数の宣言直後)にコピーアンドペーストして、プログラムに追加してください。さらに、このクラスは標準の入出力ライブラリを使用しているので、プログラムの一番始めに
642 {{{
643 #include &lt;stdio.h&gt;
644 }}}
645 と書いて、標準の入出力ライブラリを使用できるようにしてください。
646 以上の処理を行うと、このクラスが使用できるようになります。
647
648 !!IndependentVariablesクラス変数の宣言
649 このクラスのコードをプログラムに追加したら、次はこのクラスのインスタンス(変数)を宣言します。
650 宣言の書式は以下の通りです
651 |~IndependentVariablesの宣言|~IndependentVariables(int varnumber, int maxstep, int iteration)|
652 |~|~|int ~varnumber:独立変数の数を指定|
653 |~|~|int maxstep:独立変数の水準の最大値を指定|
654 |~|~|int iteration:各水準の繰り返し回数|
655
656 ここで作成する実験の計画では
657 # 独立変数は2個だったので、第1引数は2
658 # 水準数の最大値はGaborの方位の5水準なので、第2引数は5
659 # 繰り返しは各10回なので、第3引数は10
660 となります。
661 インスタンス名をinvarにして宣言すると、以下のようになります。
662 {{{
663 IndependentVariables invar(2,5,10) ;
664 }}}
665
666 !!独立変数の各水準の設定
667 次に宣言したインスタンスinvarに各変数の水準を設定していきます。
668
669 はじめに各水準の値をあらかじめ1次元のdouble型配列に格納しておきます。配列名は適当でかまいません。
670
671 {{{
672 double gaborOrientation[5]={0, 1, 2, 3, 4}; 
673         //傾き量(絶対値) 0: 2 deg, 1: 4 deg,  2: 8 deg 3: 16 deg 4: 32 deg
674 double duration[3]={100, 250, 500}; //Stimulus Duration
675 }}}
676 Gaborの方位については実際の値を入れても良いのですが、後々のために各方位に番号を振ってその番号を格納する形になっています。
677
678 次に、これらの配列名を使ってinvarに変数の水準数と各水準の値を登録します。
679 これにはset()命令を使います。
680 |int ~IndependentVariables::set()|set(int vnum, int arraynum, double *array)|
681 |~|~|int vnum:独立変数の識別番号|
682 |~|~|int arraynum:独立変数の水準数|
683 |~|~|double *array:登録する値が格納された配列の名前(ポインタ)|
684
685 独立変数の識別番号は適当でかまいませんが、各独立変数に異なる値を割り振る必要があります。さらに、この識別番号の値の範囲は0~varnumber(インスタンスの宣言時に設定した独立変数の数)でなくてはいけません。この関数の返り値は登録が正常に行われた時は設定された独立変数の水準数、失敗したときは-1です。
686
687 Gaborの方位に0,提示時間に1を割り振ると、以下のようになります。
688 {{{
689 invar.set(0, 5, gaborOrientation);
690 invar.set(1, 3, duration);
691 }}}
692
693 !!ラテン格子を組む
694 最後に登録した値を使って、ラテン格子を組みます。これにはrandomize()命令を使用します。
695 |int ~IndependentVariables::randomize()|randomize([char *dataname])|char *dataname:組み上がったラテン格子の出力名 &lt;br&gt; (省略可能。省略時は出力されません)|
696 ここでは、&quot;~ConditionMatrix.dat&quot;というデータファイル(タブ区切りテキスト形式)を出力させることにしましょう。
697 {{{
698 invar.randomize(&quot;ConditionMatrix.dat&quot;);
699 }}}
700
701 !!計画部のプログラムのまとめと組み上がったラテン格子の利用
702 これで、実験計画のプログラミングは完了です。ここまでの所をまとめてみましょう。
703 {{{
704 IndependentVaribales invar(2,5,10) ;
705
706 double gaborOrientation[5]={0, 1, 2, 3, 4}; 
707         //傾き量(絶対値) 0: 2 deg, 1: 4 deg,  2: 8 deg 3: 16 deg 4: 32 deg
708 double duration[3]={100, 250, 500}; //Stimulus Duration
709
710 invar.set(0, 5, gaborOrientation);
711 invar.set(1, 3, duration);
712 invar.randomize(&quot;ConditionMatrix.dat&quot;);
713 }}}
714
715 この手続きによって計算されたラテン格子から各試行における独立変数の値を取得するにはget()命令を使用します。
716 |int ~IndependentVariables::get()|set(int vnum, int trial_now)|
717 |~|~|int vnum: 独立変数の識別番号|
718 |~|~|int trial_now: 試行番号|
719
720 例えば第5試行における刺激の提示時間を取得してdouble型の変数dura_nowに格納するには以下の様に書きます。
721 {{{
722 double dura_now;
723 dura_now=invar.get(1,5);
724 }}}
725 </pre>
726 </div>
727 <div title="1.1 必要な環境" modifier="Psychlops_Admin" created="200709131944" changecount="1">
728 <pre>Psychlopsの実行には、Mac OS X 10.4またはWindows 2000以降がインストールされているコンピュータが必要です。なるべく最近のコンピュータの使用をお勧めしますが、2003年以降に製造されたものであれば概ね動作します。
729
730 [[1.1.1 ハードウェア環境]]
731 [[1.1.2 ソフトウェア環境]]</pre>
732 </div>
733 <div title="1.1.1 ハードウェア環境" modifier="Psychlops_Admin" modified="200709131949" created="200709131947" changecount="3">
734 <pre>! Mac OS X
735
736 !! 最低動作環境
737 * Mac OS X 10.4以降
738 * 1GHz以上の~PowerPC G4を搭載した以降の世代のMacintosh
739 * (Power Macの場合)~OpenGL 1.4以降に対応したビデオカード
740 **  Mac OS X10.4の動作環境は[[アップル社のサイト|&lt;a class=&quot;linkification-ext&quot; href=&quot;&lt;a class=&quot;linkification-ext&quot; href=&quot;&lt;a class=&quot;linkification-ext&quot; href=&quot;&lt;a class=&quot;linkification-ext&quot; href=&quot;&lt;a class=&quot;linkification-ext&quot; href=&quot;&lt;a class=&quot;linkification-ext&quot; href=&quot;&lt;a class=&quot;linkification-ext&quot; href=&quot;&lt;a class=&quot;linkification-ext&quot; href=&quot;&lt;a class=&quot;linkification-ext&quot; href=&quot;&lt;a class=&quot;linkification-ext&quot; href=&quot;&lt;a class=&quot;linkification-ext&quot; href=&quot;&lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.apple.com/jp/macosx/upgrade/requirements.html&quot; title=&quot;Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html&quot;&gt;http://www.apple.com/jp/macosx/upgrade/requirements.html&lt;/a&gt;&quot; title=&quot;Linkification: &lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.apple.com/jp/macosx/upgrade/requirements.html&quot; title=&quot;Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html&quot;&gt;http://www.apple.com/jp/macosx/upgrade/requirements.html&lt;/a&gt;&quot;&gt;&lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.apple.com/jp/macosx/upgrade/requirements.html&quot; title=&quot;Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html&quot;&gt;http://www.apple.com/jp/macosx/upgrade/requirements.html&lt;/a&gt;&lt;/a&gt;&quot; title=&quot;Linkification: &lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.apple.com/jp/macosx/upgrade/requirements.html&quot; title=&quot;Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html&quot;&gt;http://www.apple.com/jp/macosx/upgrade/requirements.html&lt;/a&gt;&quot;&gt;&lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.apple.com/jp/macosx/upgrade/requirements.html&quot; title=&quot;Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html&quot;&gt;http://www.apple.com/jp/macosx/upgrade/requirements.html&lt;/a&gt;&lt;/a&gt;&quot; title=&quot;Linkification: &lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.apple.com/jp/macosx/upgrade/requirements.html&quot; title=&quot;Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html&quot;&gt;http://www.apple.com/jp/macosx/upgrade/requirements.html&lt;/a&gt;&quot;&gt;&lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.apple.com/jp/macosx/upgrade/requirements.html&quot; title=&quot;Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html&quot;&gt;http://www.apple.com/jp/macosx/upgrade/requirements.html&lt;/a&gt;&lt;/a&gt;&quot; title=&quot;Linkification: &lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.apple.com/jp/macosx/upgrade/requirements.html&quot; title=&quot;Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html&quot;&gt;http://www.apple.com/jp/macosx/upgrade/requirements.html&lt;/a&gt;&quot;&gt;&lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.apple.com/jp/macosx/upgrade/requirements.html&quot; title=&quot;Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html&quot;&gt;http://www.apple.com/jp/macosx/upgrade/requirements.html&lt;/a&gt;&lt;/a&gt;&quot; title=&quot;Linkification: &lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.apple.com/jp/macosx/upgrade/requirements.html&quot; title=&quot;Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html&quot;&gt;http://www.apple.com/jp/macosx/upgrade/requirements.html&lt;/a&gt;&quot;&gt;&lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.apple.com/jp/macosx/upgrade/requirements.html&quot; title=&quot;Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html&quot;&gt;http://www.apple.com/jp/macosx/upgrade/requirements.html&lt;/a&gt;&lt;/a&gt;&quot; title=&quot;Linkification: &lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.apple.com/jp/macosx/upgrade/requirements.html&quot; title=&quot;Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html&quot;&gt;http://www.apple.com/jp/macosx/upgrade/requirements.html&lt;/a&gt;&quot;&gt;&lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.apple.com/jp/macosx/upgrade/requirements.html&quot; title=&quot;Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html&quot;&gt;http://www.apple.com/jp/macosx/upgrade/requirements.html&lt;/a&gt;&lt;/a&gt;&quot; title=&quot;Linkification: &lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.apple.com/jp/macosx/upgrade/requirements.html&quot; title=&quot;Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html&quot;&gt;http://www.apple.com/jp/macosx/upgrade/requirements.html&lt;/a&gt;&quot;&gt;&lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.apple.com/jp/macosx/upgrade/requirements.html&quot; title=&quot;Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html&quot;&gt;http://www.apple.com/jp/macosx/upgrade/requirements.html&lt;/a&gt;&lt;/a&gt;&quot; title=&quot;Linkification: &lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.apple.com/jp/macosx/upgrade/requirements.html&quot; title=&quot;Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html&quot;&gt;http://www.apple.com/jp/macosx/upgrade/requirements.html&lt;/a&gt;&quot;&gt;&lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.apple.com/jp/macosx/upgrade/requirements.html&quot; title=&quot;Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html&quot;&gt;http://www.apple.com/jp/macosx/upgrade/requirements.html&lt;/a&gt;&lt;/a&gt;&quot; title=&quot;Linkification: &lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.apple.com/jp/macosx/upgrade/requirements.html&quot; title=&quot;Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html&quot;&gt;http://www.apple.com/jp/macosx/upgrade/requirements.html&lt;/a&gt;&quot;&gt;&lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.apple.com/jp/macosx/upgrade/requirements.html&quot; title=&quot;Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html&quot;&gt;http://www.apple.com/jp/macosx/upgrade/requirements.html&lt;/a&gt;&lt;/a&gt;&quot; title=&quot;Linkification: &lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.apple.com/jp/macosx/upgrade/requirements.html&quot; title=&quot;Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html&quot;&gt;http://www.apple.com/jp/macosx/upgrade/requirements.html&lt;/a&gt;&quot;&gt;&lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.apple.com/jp/macosx/upgrade/requirements.html&quot; title=&quot;Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html&quot;&gt;http://www.apple.com/jp/macosx/upgrade/requirements.html&lt;/a&gt;&lt;/a&gt;&quot; title=&quot;Linkification: &lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.apple.com/jp/macosx/upgrade/requirements.html&quot; title=&quot;Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html&quot;&gt;http://www.apple.com/jp/macosx/upgrade/requirements.html&lt;/a&gt;&quot;&gt;&lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.apple.com/jp/macosx/upgrade/requirements.html&quot; title=&quot;Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html&quot;&gt;http://www.apple.com/jp/macosx/upgrade/requirements.html&lt;/a&gt;&lt;/a&gt;]]よりご確認ください
741
742 ! Windows
743
744 !!最低動作環境
745 * Windows 2000 sp4、XP sp2、Vista
746 * ~OpenGL 1.4以降に対応したビデオカード
747 **  [[Tips: OpenGL 1.4に対応した一般向けビデオチップ]]を参照してください。
748 ** ノートPCなどグラフィックチップ内臓の場合、VRAMサイズを32MB以上にしてください
749 ** (注)Windows Vistaではビデオカードメーカーより提供されている~OpenGLドライバがVistaに対応している必要があります。現状(2007年9月現在)、nVidia社の製品は対応していることを確認しています。
750
751 !! 推奨動作環境
752 * 1GHz以上の速度を持つCPU
753 * 512MB以上のメモリ
754 * なるべく最新のビデオカード、なるべく多めのVRAM</pre>
755 </div>
756 <div title="1.1.2 ソフトウェア環境" modifier="YourName" modified="201003111850" created="200709131950" changecount="8">
757 <pre>PsychlopsはC++のライブラリとして提供されます。つまり、Psychlopsを使ったプログラミングには、C++の開発環境が必要です。これには様々なものがありますが、以下のものの使用を強くおすすめします。入手・インストール方法の詳細は1.2以降で説明します。
758
759 ! Mac OSX
760 * Xcode 1.2 以降
761 ** [[Apple社が提供している純正開発環境|&lt;a class=&quot;linkification-ext&quot; href=&quot;&lt;a class=&quot;linkification-ext&quot; href=&quot;&lt;a class=&quot;linkification-ext&quot; href=&quot;&lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.apple.com/jp/macosx/developertools/&quot; title=&quot;Linkification: http://www.apple.com/jp/macosx/developertools/&quot;&gt;http://www.apple.com/jp/macosx/developertools/&lt;/a&gt;&quot; title=&quot;Linkification: &lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.apple.com/jp/macosx/developertools/&quot; title=&quot;Linkification: http://www.apple.com/jp/macosx/developertools/&quot;&gt;http://www.apple.com/jp/macosx/developertools/&lt;/a&gt;&quot;&gt;&lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.apple.com/jp/macosx/developertools/&quot; title=&quot;Linkification: http://www.apple.com/jp/macosx/developertools/&quot;&gt;http://www.apple.com/jp/macosx/developertools/&lt;/a&gt;&lt;/a&gt;&quot; title=&quot;Linkification: &lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.apple.com/jp/macosx/developertools/&quot; title=&quot;Linkification: http://www.apple.com/jp/macosx/developertools/&quot;&gt;http://www.apple.com/jp/macosx/developertools/&lt;/a&gt;&quot;&gt;&lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.apple.com/jp/macosx/developertools/&quot; title=&quot;Linkification: http://www.apple.com/jp/macosx/developertools/&quot;&gt;http://www.apple.com/jp/macosx/developertools/&lt;/a&gt;&lt;/a&gt;&quot; title=&quot;Linkification: &lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.apple.com/jp/macosx/developertools/&quot; title=&quot;Linkification: http://www.apple.com/jp/macosx/developertools/&quot;&gt;http://www.apple.com/jp/macosx/developertools/&lt;/a&gt;&quot;&gt;&lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.apple.com/jp/macosx/developertools/&quot; title=&quot;Linkification: http://www.apple.com/jp/macosx/developertools/&quot;&gt;http://www.apple.com/jp/macosx/developertools/&lt;/a&gt;&lt;/a&gt;]]を参考にApple社のサイトからダウンロードしてください。
762 ** ファイルサイズがとても大きいので、注意してください。
763
764 ! Windows
765 * コンパイラ本体:~MinGW 3.4 or later, VC Toolkit 2003 with Platform SDK, ~BCC5.5など
766 * フリーの開発環境である[[Code::blocks|&lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.codeblocks.org/&quot; title=&quot;Linkification: http://www.codeblocks.org/&quot;&gt;http://www.codeblocks.org/&lt;/a&gt;]], [[Relo|&lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.fifsoft.com/relo/&quot; title=&quot;Linkification: http://www.fifsoft.com/relo/&quot;&gt;http://www.fifsoft.com/relo/&lt;/a&gt;]] を使用することをおすすめします。これら2つのためのプロジェクトテンプレート(psychlopsを利用した実行ファイルを作成するためのテンプレート)がPsychlopsのインストールキットに含まれています。
767 ** Code::blocksを使用する場合は、~MinGWが同時にインストールされます。</pre>
768 </div>
769 <div title="1.2 Gaborパッチの事前描画" modifier="Psychlops_DevelopperG" modified="200908190214" created="200709252145" changecount="2">
770 <pre>この節では実験刺激のGaborパッチを事前描画するプログラムを作ります。
771 Gaborパッチとは、以下の図のような正弦波にGauss関数によるエンベロープをかけて、刺激中心から周辺に離れるに従って正弦波のコントラストが低くなっていく様な刺激です。
772 |[Img[image/gabor.png]]| [Img[image/gabor3d.png]]|
773 この刺激は複雑な2次元パターンなので、Canvas::pix()を使ってリアルタイム描画をするよりは、Imageにあらかじめ描画を行った方がいいでしょう。
774
775 Imageを使った描画については[[3章|3. 複雑な描画を行う1(オフスクリーンへの描画)]]で既に触れましたが、この節では[[1.1節|1.1 実験計画のプログラミング]]と同様に汎用性を意識してGaborパッチを描画するためのクラス~GaborImageを作成して、これを利用した刺激描画を行います。
776
777 !!~GaborImageクラスの概要
778 [[GaborImageクラスのソースコード|GaborImage]]
779 [[1.1節|1.1 実験計画のプログラミング]]のIndependentVariablesクラス同様上のリンクのソースコードを実際の宣言が起こる前にコピーアンドペーストして、プログラムに追加するとこのクラスを使用することができます。
780
781 このクラスはdraw()命令のみから構成されています。この命令は指定したImageにImageサイズの1/6をσとするエンベロープのGaborパッチを描画します。
782 |!~GaborImage::draw()|draw(Image &amp;area,  double ori, double freq, double phase, double Lmean, double *contrast)|カラーのGaborパッチをImageに描画します。|
783 |~|draw(Image &amp;area,  double ori, double freq, double phase, double Lmean, double contrast)|グレースケールのGaborパッチをImage上に描画します。|
784 |~|!引数|&gt;|
785 |~|Image &amp;area: Gaborパッチを描画するImageの名前|&gt;|
786 |~|double freq: キャリアの空間周波数(pixel)|&gt;|
787 |~|double phase: キャリアの位相(度)|&gt;|
788 |~|double Lmean: パッチの平均輝度 |&gt;|
789 |~|double *contrast: &lt;br&gt; キャリアのR,G,B各チャンネルのコントラストの配列名 (double[3]/範囲0.0-1.0)|double contrast: &lt;br&gt; キャリアのコントラスト &lt;br&gt; (範囲0.0-1.0)|
790
791 !!~GaborImageクラスを用いた刺激の事前描画
792 ここでは、上で説明した~GaborImageクラスを使って刺激の事前描画を行います。
793 まず、ここで追加したクラスのインスタンスを作成することにします。インスタンス名はgaborIMGにします。
794 {{{
795 GaborImage gaborIMG;
796 }}}
797 次にGaborパッチを描画するImageを配列gIMGとして宣言します。
798 {{{
799 Psychlops::Image gIMG[10];
800 }}}
801
802 このImageの10枚分の配列に(傾き量5水準) x (傾きの方向:右/左の2水準)の10枚のGaborパッチを書けばよいのですが、どの配列にどの方位のGaborパッチを書けば良いかを考えなくてはいけません。6.1でGaborパッチの傾き量の水準は0~4の番号を振られていたことを思い出してください。各試行ではこの番号を元にで、各試行でランダムに与えられた傾きの方向を考慮して、呼び出すgIMGの番号を決める必要があります。
803
804 少し複雑になってきたので、与えられた傾き量の番号と傾きの方向に対して、どの方位のGaborパッチが呼び出されるべきかを表にしておきましょう。垂直方位は90度なのでこれに対して傾き量が加減されていることに気をつけてください。
805
806 |&gt;|!傾き量の番号| 0 | 1 | 2 | 3 | 4 |
807 |!傾きの方向|右|90-2|90-4|90-8|90-16|90-32|
808 |~|左|90+2|90+4|90+8|90+16|90+32|
809 | (単位は度)|c
810
811 ここでは、以下の表の様にgIMGの番号とGaborパッチの方位を対応させることにします。ついでに、傾きの方向に対しても番号を振っておきます。右を0左を1としてみましょう。
812
813 |&gt;|!傾き量の番号|&gt;| 0 |&gt;| 1 |&gt;| 2 |&gt;| 3 |&gt;| 4 |&gt;|
814 |!傾きの方向|右(0)|90-2|bgcolor(#a0ffa0):gIMG[0]|90-4|bgcolor(#a0ffa0):gIMG[1]|90-8|bgcolor(#a0ffa0):gIMG[2]|90-16|bgcolor(#a0ffa0):gIMG[3]|90-32|bgcolor(#a0ffa0):gIMG[4]|
815 |~|左(1)|90+2|bgcolor(#a0ffa0):gIMG[5]|90+4|bgcolor(#a0ffa0):gIMG[6]|90+8|bgcolor(#a0ffa0):gIMG[7]|90+16|bgcolor(#a0ffa0):gIMG[8]|90+32|bgcolor(#a0ffa0):gIMG[9]|
816 | (単位は度)|c
817
818 さらに、これをgIMGの番号順に並べ替えて、各番号に対する計算式を考えます。以下のようになるでしょう。
819
820 |!gIMGの配列番号|gIMG[0]|gIMG[1]|gIMG[2]|gIMG[3]|gIMG[4]|gIMG[5]|gIMG[6]|gIMG[7]|gIMG[8]|gIMG[9]|
821 |!(傾き量, 方向) 番号|(0,0)|(1,0)|(2,0)|(3,0)|(4,0)|(0,1)|(1,1)|(2,1)|(3,1)|(4,1)|
822 |!Gaborパッチの方位(度)|90-2|90-4|90-8|90-16|90-32|90+2|90+4|90+8|90+16|90+32|
823 |!配列番号iに対する方位の計算式|&gt;|&gt;|&gt;|&gt;| 90-pow(2, i) |&gt;|&gt;|&gt;|&gt;| 90+pow(2,i-4) |
824 | ^^Pow(a,b)はaのb乗を示す^^|c
825
826 これで、10枚のGaborパッチを各準備が整いました。~GaborImage::draw()命令を使って実際のプログラムを組んでみます。ここでは最終引数に単なる数値を入れて、グレースケールのGaborパッチを描画することにします。
827
828 最後にdraw()命令を使ってGaborパッチの描画を行います。刺激サイズは100 pixel四方、キャリアの波長は30 pix (空間周波数は1.0/30.0), キャリアのコントラストは0.2 (20%), 位相は0, パッチの平均輝度は0.5にします。
829 また、あらかじめ刺激提示位置(画面中央)にイメージをシフトしておきます。
830 {{{
831 double gIMGsize=100, ori;
832         for(int i=0; i&lt;10; i++){
833                 if(i&lt;5){ ori=90.0-pow(2.0, i);}
834                         else{ ori=90.0+pow(2.0,i-4);}
835                 gIMG[i].set(gIMGsize, gIMGsize);
836                 gaborIMG.draw(gIMG[i], ori, 1.0/30.0, 0.0, 0.5, 0.2);
837                 gIMG[i].centering();
838         }
839 }}}
840
841 この節のコードをまとめると以下のようになります。
842 {{{
843 GaborImage gaborIMG;
844 Psychlops::Image gIMG[10];
845 double gIMGsize=100, ori;
846         for(int i=0; i&lt;10; i++){
847                 if(i&lt;5){ ori=90.0-pow(2.0, i);}
848                         else{ ori=90.0+pow(2.0,i-4);}
849                 gIMG[i].set(gIMGsize, gIMGsize);
850                 gaborIMG.draw(gIMG[i], ori, 1.0/30.0, 0.0, 0.5, 0.2);
851                 gIMG[i].centering();
852         }
853 }}}</pre>
854 </div>
855 <div title="1.2 インストール作業(Mac OS X 10.4) " modifier="YourName" modified="201002040347" created="200709131951" changecount="3">
856 <pre>!! Xcodeのインストール
857 PsychlopsのOSXでのインストールには、まずXcodeをインストールする必要があります。XcodeはApple社が提供する標準の開発環境で、無償で利用できます(ダウンロードにはApple Developers Connectionへの登録が必要です)。
858
859 * [[ダウンロード|http://developer.apple.com/tools/xcode/]]
860 * [[機能紹介|http://www.apple.com/jp/macosx/features/xcode/]]
861
862 ''Mac OS X 10.4向けのXcode 2.x系はApple社よりの配布がすでに終了しています。OSX 10.4のインストール/リカバリディスク中に開発用ツールキットがない場合、10.6へのアップグレードをご検討ください''
863
864 ''xcode.mpkg''をダブルクリックしてインストールを始めます。
865 [img[Xcodeインストール|image/OSX/Xcode2_10.4.png]]
866
867 インストーラの指示に従ってインストールを完了します。
868 [img[Xcodeインストール|image/OSX/Xcode3_10.4.png]]
869
870 !! Psychlopsの本体をインストール
871 * [[Psychlops Download|http://psychlops.l.u-tokyo.ac.jp/?Download]]
872
873 ダウンロードページへ行き、Xcode版の最新版をクリックします。
874 [img[Psychlopsダウンロード|image/OSX/Psychlops_OSX1png]]
875
876 zipファイルを解凍するとパッケージファイルが現れますので、これをダブルクリックしてインストールを開始します。
877 [img[Psychlopsインストール|image/OSX/Psychlops_OSX2.png]]
878
879 インストーラの指示に従ってインストールを完了します。
880 [img[Psychlopsインストール|image/OSX/Psychlops_OSX3.png]]
881
882 !! 新しいプロジェクト
883 Xcodeを起動し、''メニューパー → ファイル → 新しいプロジェクト''を選択します。
884 [img[新しいプロジェクト|image/OSX/Psychlops_OSX_10.4_New1.png]]
885
886 新しいプロジェクトのウィザードが開いたら、Applicationカテゴリにある''Psychlops C++ Application''を選択します。
887 [img[新しいプロジェクト|image/OSX/Psychlops_OSX_10.4_New2.png]]
888
889 新しいプロジェクトを作成する場所と名前を選びます。作成後に場所は変更可能ですが、名前は作り直す以外に変更できませんのでご注意ください。また、パスに日本語を含んでいると実行に失敗することがありますので、ASCII文字のみのフォルダの中に作成してください。
890 [img[新しいプロジェクト|image/OSX/Psychlops_OSX_10.4_New3.png]]
891
892 プログラムはSourcesカテゴリの中にある(プロジェクト名).cppにあります。標準のサンプルプログラムが入っていますので、まずはダブルクリックしてエディタを開き、中身を見てみます(最初から開いていることもあります)。
893 [img[新しいプロジェクト|image/OSX/Psychlops_OSX_10.4_New5.png]]
894
895 下のようなウィンドウが開いたでしょうか。
896 [img[ウインドウ説明|image/OSX/Psychlops_OSX_10.4_New6.png]]
897 プログラム編集エディタでブログラムを編集することができます。実際にはこのサンプルを消して、新しく自分のプログラムを書いていくわけですが、まず最初は正しくインストールされているかどうかを確かめるために、標準のサンプルをそのまま実行してみましょう。ツールバーの実行ボタンをおすと、プログラムが機械語に翻訳されて実行ファイルの作成・実行が行われます。
898
899 出来上がった実行ファイルは、''プロジェクトのあるフォルダ &gt; buildフォルダ &gt; Releaseフォルダ '' の中にあります。この実行ファイルをコピーすることで頒布が可能ですが、メールなどに添付する際は必ずzipファイル等に圧縮してください。(ここで見えている実行プログラムは実際はフォルダです。Macのプログラムの仕様で、実際の実行プログラムは複数存在して、実行プログラムに見えるフォルダに格納されています。これを他のマシンに頒布するためにアップロードしたり、メールに添付したりするときには圧縮して実際に一つのファイルにまとめる必要があります。)
900 [img[ウインドウ説明|image/OSX/Psychlops_OSX_10.4_New7.png]]
901 </pre>
902 </div>
903 <div title="1.2 インストール作業(Mac) " modifier="YourName" created="201002040339" changecount="1">
904 <pre>!! Xcodeのインストール
905 PsychlopsのOSXでのインストールには、まずXcodeをインストールする必要があります。XcodeはApple社が提供する標準の開発環境で、無償で利用できます(ダウンロードにはApple Developers Connectionへの登録が必要です)。対応バージョンについては、2010/01/31現在、Xcode2.5と3.x系に対応しておりますが、最新の対応状況につきましては[[http://psychlops.l.u-tokyo.ac.jp/?Environment]]からご確認ください。スクリーンショットはマニュアル作成時のものですので、バージョンが更新されていると異なる場合があります。ご了承ください。
906
907 * [[ダウンロード|http://developer.apple.com/technology/xcode.html]]
908 * [[機能紹介|http://www.apple.com/jp/macosx/features/xcode/]]
909
910
911 !!! OSXインストールディスクを利用する場合
912
913  OSXインストールディスクに開発環境のインストーラがあります。ディスクの内容は購入時期によって異なる可能性があります。ここでは10.5を例に説明いたします。
914
915 [img[Xcodeインストール|image/OSX/Xcode_DVD_Screen.png]]
916
917  OSXインストールディスク中をFinderで開くと、「オプションインストール」フォルダがあります。このなかの「Xcode Tools」フォルダを開き、「XcodeTools.mpkg」(拡張子は見えない場合があります)をダブルクリックすると、Xcodeインストーラが実行されます。
918
919
920 !!! ダウンロードする場合
921
922 Xcodeをダウンロードするには、まず[[Appleのダウンロードサイト|http://developer.apple.com/technology/xcode.html]]へ行きます。その際、初めての方はまずApple Developers Connectionへの登録をする必要があります。
923
924 [img[Xcodeインストール|image/OSX/Xcode_01.png]]
925
926 「ADC membership」と書かれたリンクをクリックすると「Join Now」というボタンがありますので、それをクリックして
927
928 [img[Xcodeインストール|image/OSX/Xcode_02.png]]
929
930 もう一度[[ダウンロードページ|http://developer.apple.com/technology/xcode.html]]へ行き下側の「Xcode for Mac-only Development」の「Download now」をクリックしてください。その後のページでアカウントを入力すると、ダウンロードリンクが現れますので、本体のdmgファイルをダウンロードしてください。
931
932 [img[Xcodeインストール|image/OSX/Xcode_03.png]]
933
934 ダウンロードが終わると自動的にディスクイメージが展開されますので、''XcodeTools.mpkg''をダブルクリックしてインストールを始めます。
935
936 [img[Xcodeインストール|image/OSX/Xcode2.png]]
937
938 インストーラの指示に従ってインストールを完了します。
939 [img[Xcodeインストール|image/OSX/Xcode3.png]]
940
941
942 !! Psychlopsの本体をインストール
943 * [[Psychlops Download|http://psychlops.l.u-tokyo.ac.jp/?Download]]
944
945 ダウンロードページへ行き、Xcode版の最新版をクリックします。
946 [img[Psychlopsダウンロード|image/OSX/Psychlops_OSX1png]]
947
948 zipファイルを解凍するとパッケージファイルが現れますので、これをダブルクリックしてインストールを開始します。
949 [img[Psychlopsインストール|image/OSX/Psychlops_OSX2.png]]
950
951 インストーラの指示に従ってインストールを完了します。
952 [img[Psychlopsインストール|image/OSX/Psychlops_OSX3.png]]
953
954 !! 新しいプロジェクト
955 Xcodeを起動し、''メニューパー → ファイル → 新しいプロジェクト''を選択します。
956 [img[新しいプロジェクト|image/OSX/Psychlops_OSX_New1.png]]
957
958 新しいプロジェクトのウィザードが開いたら、Applicationカテゴリにある''Psychlops C++ Application''を選択します。
959 [img[新しいプロジェクト|image/OSX/Psychlops_OSX_New2.png]]
960
961 新しいプロジェクトを作成する場所と名前を選びます。作成後に場所は変更可能ですが、名前は作り直す以外に変更できませんのでご注意ください。また、パスに日本語を含んでいると実行に失敗することがありますので、ASCII文字のみのフォルダの中に作成してください。
962 [img[新しいプロジェクト|image/OSX/Psychlops_OSX_New3.png]]
963
964 プログラムはSourcesカテゴリの中にある(プロジェクト名).cppにあります。標準のサンプルプログラムが入っていますので、まずはダブルクリックしてエディタを開き、中身を見てみます(最初から開いていることもあります)。
965 [img[新しいプロジェクト|image/OSX/Psychlops_OSX_New5.png]]
966
967 下のようなウィンドウが開いたでしょうか。
968 [img[ウインドウ説明|image/OSX/Psychlops_OSX_New6.png]]
969 プログラム編集エディタでブログラムを編集することができます。実際にはこのサンプルを消して、新しく自分のプログラムを書いていくわけですが、まず最初は正しくインストールされているかどうかを確かめるために、標準のサンプルをそのまま実行してみましょう。ツールバーの実行ボタンをおすと、プログラムが機械語に翻訳されて実行ファイルの作成・実行が行われます。
970
971 出来上がった実行ファイルは、''プロジェクトのあるフォルダ &gt; buildフォルダ &gt; Releaseフォルダ または Debugフォルダ'' の中にあります。この実行ファイルをコピーすることで頒布が可能ですが、メールなどに添付する際は必ずzipファイル等に圧縮してください。(ここで見えている実行プログラムは実際はフォルダです。Macのプログラムの仕様で、実際の実行プログラムは複数存在して、実行プログラムに見えるフォルダに格納されています。これを他のマシンに頒布するためにアップロードしたり、メールに添付したりするときには圧縮して実際に一つのファイルにまとめる必要があります。)
972 [img[ウインドウ説明|image/OSX/Psychlops_OSX_New7.png]]
973 </pre>
974 </div>
975 <div title="1.3 インストール作業(Windows + BCC) " modifier="YourName" modified="201002040332" created="200709131955" changecount="2">
976 <pre>インストールには管理者権限のあるユーザである必要があります。
977 Windows Vistaに関しては、インストールが自動化されていませんがお使いいただくことは可能です。
978 [[Tips: Vistaにおけるインストール]]をご覧ください。
979
980 !! OpenGLのドライバ設定の確認
981 Psychlopsは画面の垂直同期信号に合わせて画面を更新しますが、この機能を正常に動作させるには、ディスプレイドライバのOpenGL描画設定で垂直同期にあわせるオプションを常にオンにする必要があります。
982 一般的には''コントロールパネル &gt; 画面''の設定の''モニタ''タブを選び、''詳細設定''ボタンから設定します。詳しくはご使用のビデオカードのマニュアルをご覧ください。
983
984 !! BCCのインストール
985 PsychlopsのWindowsでのインストールには、まずコンパイラをインストールする必要があります。ここではBorland社が無償で提供するBorland C++ Compiler 5.5をインストールしてみます。Cマガジンから提供されている設定ツールも一緒にダウンロードしましょう。
986
987 * [[BCCダウンロード|http://www.codegear.com/jp/downloads/free/cppbuilder]]
988 * [[設定ツールダウンロード|http://www.cmagazine.jp/setbcc.html]]
989
990 まずダウンロードページへ行き、''Borland C++Compiler / Turbo Debugger''をクリックします。この後個人情報を入力してダウンロードを開始します。
991 [img[BCCダウンロード|image/Win/BCC0.png]]
992
993 ダウンロードが終わるとインストーラの実行ファイルがありますので、''freecommandlinetools2.exe''をダブルクリックしてインストールを始めます。インストーラの指示に従ってインストールを完了します。
994 [img[BCCインストール|image/Win/BCC1.png]]
995
996 次に設定ツールである''setbcc15b.exe''をダウンロードし、実行します。
997 [img[BCCインストール|image/Win/BCC2.png]]
998
999 特に細かい設定をする必要はありません。''進む&gt;&gt;''をクリックし続けて実行すれば実行可能になります。
1000 [img[BCCインストール|image/Win/BCC4.png]]
1001
1002 !! 開発環境Reloのインストール
1003 次に開発環境をインストールします。ここではオープンソースのRelo2を利用します。
1004
1005 * [[Reloダウンロード|http://www.fifsoft.com/relo/download.php]]
1006
1007 まずダウンロードページへ行き、''RELO v2.0 INSTALLER''をクリックし、ダウンロードします。
1008 [img[Reloダウンロード|image/Win/Psychlops_Relo1.png]]
1009
1010 ダウンロードが終わるとインストーラの実行ファイルがありますので、''relosetup20.exe''をダブルクリックしてインストールを始めます。インストーラの指示に従ってインストールを完了します。
1011 [img[Reloインストール|image/Win/Psychlops_Relo2.png]]
1012
1013 インストール終了後、まず最初に環境設定を行うためにRelo2を起動します。起動したらメニューバーのTools から Compilers を選択します。
1014 [img[Reloインストール|image/Win/Psychlops_Relo_New5.png]]
1015
1016 開いたウィンドウで、Newボタンを押し、そのまま進め、New CompilerをBCCにする設定をします。各フィールドを以下のように指定します。
1017 * Name : BCC
1018 * Compiler : bcc32
1019 * Path : C:\borland\bcc55\Bin\
1020 * Type : Borland
1021 [img[Reloインストール|image/Win/Psychlops_Relo4.png]]
1022
1023
1024 !! Psychlopsの本体をインストール
1025 * [[Psychlops Download|http://psychlops.l.u-tokyo.ac.jp/?Download]]
1026
1027 ダウンロードページへ行き、BCC版の最新版をクリックします。
1028 [img[Psychlopsダウンロード|image/Win/Psychlops_WinGL1.png]]
1029
1030 zipファイルを解凍するとインストーラフォルダが現れますので、この中の''PsychlopsLib''フォルダ内にある''PsychlopsLib_xxxx.exe''をダブルクリックしてインストールを開始します。
1031 [img[Psychlopsインストール|image/Win/Psychlops_WinGL3.png]]
1032
1033 インストーラの指示に従ってインストールを完了します。
1034 [img[Psychlopsインストール|image/Win/Psychlops_WinGL3.png]]
1035
1036 !! Relo用のテンプレートをインストール
1037 zipファイルを解凍してできたインストーラフォルダの中の''ReloTemplate''フォルダ内にある''Psychlops_Relo2_Template_xxxxxxxx.exe''をダブルクリックしてインストールを開始します。インストーラの指示に従ってインストールを完了します。
1038 [img[Psychlopsインストール|image/Win/Psychlops_Relo3.png]]
1039
1040 !! 新しいプロジェクト
1041
1042 Reloを起動し、''メニューパー → File → New''を選択します。
1043 [img[新しいプロジェクト|image/Win/Psychlops_Relo_New1.png]]
1044
1045 新しいプロジェクトのウィザードが開いたら、''Psychlops Application''を選択します。
1046 [img[新しいプロジェクト|image/Win/Psychlops_Relo_New2.png]]
1047
1048 新しいプロジェクトの名前を選びます。作成後に場所は変更可能ですが、名前は作り直す以外に変更できませんのでご注意ください。
1049 [img[新しいプロジェクト|image/Win/Psychlops_Relo_New3.png]]
1050
1051 Reloはプロジェクトの作成直後は保存場所が決まっておりませんので、まずはプロジェクトを保存します。パスにスペースまたは日本語を含んでいると実行に失敗することがありますので、ASCII文字のみのフォルダの中に作成してください。
1052 [img[新しいプロジェクト|image/Win/Psychlops_Relo_New4.png]]
1053
1054 プログラムはSourcesカテゴリの中にある(プロジェクト名).cppにあります。標準のサンプルプログラムが入っていますので、まずはダブルクリックしてエディタを開き、中身を見てみます(最初から開いていることもあります)。
1055
1056 下のようなウィンドウが開いたでしょうか?
1057 [img[ウインドウ説明|image/Win/Psychlops_Relo_New6.png]]
1058 プログラム編集エディタでブログラムを編集することができます。実際にはこのサンプルを消して、新しく自分のプログラムを書いていくわけですが、まず最初は正しくインストールされているかどうかを確かめるために、標準のサンプルをそのまま実行してみましょう。ツールバーの実行ボタンをおすと、プログラムが機械語に翻訳されて実行ファイルの作成・実行が行われます。
1059
1060 Reloは現在のバージョンでは、起動直後にコンパイラの設定ウィンドウを開いてOKボタンを押す必要があります。''メニューパー → Tools → Compilers''を選択して、開いたダイアログでOKを押します。
1061 [img[新しいプロジェクト|image/Win/Psychlops_Relo_New5.png]]
1062
1063 出来上がった実行ファイルはプロジェクトと同じフォルダにあります。
1064 [img[新しいプロジェクト|image/Win/Psychlops_Relo_New7.png]]
1065 </pre>
1066 </div>
1067 <div title="1.3 インストール作業(Windows) " modifier="YourName" created="201002040143" changecount="1">
1068 <pre>!! OpenGLのドライバ設定の確認
1069 Psychlopsは画面の垂直同期信号に合わせて画面を更新しますが、この機能を正常に動作させるには、ディスプレイドライバのOpenGL描画設定で垂直同期にあわせるオプションを常にオンにする必要があります。
1070 一般的には''コントロールパネル &gt; 画面''の設定の''モニタ''タブを選び、''詳細設定''ボタンから設定します。詳しくはご使用のビデオカードのマニュアルをご覧ください。
1071
1072 !! CodeBlocks + GCCのインストール
1073 PsychlopsのWindowsで利用するには、まずコンパイラと開発環境をインストールする必要があります。ここでは両者がセットになっているCodeBlocks(MinGW gccコンパイラつき)をダウンロードして利用する例を紹介します。
1074
1075 2010/01/31現在、CodeBlocks8.02 / Windows XP, Vista, 7に対応しておりますが、最新の対応状況につきましてはhttp://psychlops.l.u-tokyo.ac.jp/?Environmentからご確認ください。スクリーンショットはマニュアル作成時のものですので、バージョンが更新されていると異なる場合があります。ご了承ください。
1076
1077 * [[CodeBlocks + GCCダウンロード|http://www.codeblocks.org/downloads/5]]
1078
1079 まずダウンロードページへ行き、''codeblocks-8.02mingw-setup.exe''をSourceforgeまたはBerliOSのサーバからダウンロードします。どちらのサーバからダウンロードしても中身は同じです。
1080 [img[CodeBlocksダウンロード|image/Win/CodeBlocks_Win01.png]]
1081
1082 ダウンロードが終わるとインストーラの実行ファイルがありますので、''codeblocks-8.02mingw-setup.exe''をダブルクリックしてインストールを始めます。インストーラの指示に従ってインストールを完了します。
1083 [img[BCCインストール|image/Win/CodeBlocks_Win02.png]]
1084
1085
1086
1087 !! Psychlopsの本体をインストール
1088 * [[Psychlops Download|http://psychlops.l.u-tokyo.ac.jp/?Download]]
1089
1090 ダウンロードページへ行き、Win32GL版の最新版をクリックします。
1091 [img[Psychlopsダウンロード|image/Win/Psychlops_WinGL1.png]]
1092
1093 zipファイルを解凍するとインストーラフォルダが現れますので、この中の''PsychlopsLib''フォルダ内にある''PsychlopsFramework_Win32_x.x.x.exe''をダブルクリックしてインストールを開始します。
1094 [img[Psychlopsインストール|image/Win/Psychlops_WinGL3.png]]
1095
1096 インストーラの指示に従ってインストールを完了します。
1097 [img[Psychlopsインストール|image/Win/Psychlops_WinGL5.png]]
1098
1099
1100 !! CodeBlocks用のテンプレートをインストール
1101 zipファイルを解凍してできたインストーラフォルダの中の''CodeblocksTemplate''フォルダ内にある''Psychlops_Codeblocks_Template_xxxxxxxx.exe''をダブルクリックしてインストールを開始します。インストーラの指示に従ってインストールを完了します。
1102 [img[Psychlopsインストール|image/Win/Psychlops_WinGL4.png]]
1103
1104 !! 新しいプロジェクト
1105
1106 CodeBlocksを起動し、''メニューパー → File → New → Project''を選択します。
1107 [img[新しいプロジェクト|image/Win/CodeBlocks_Win10.png]]
1108
1109 新しいプロジェクトのウィザードが開いたら、''Psychlops GL Ploject''を選択します(見つからなければ選択ボックスをスクロールしてください)。
1110 [img[新しいプロジェクト|image/Win/CodeBlocks_Win11.png]]
1111
1112 新しいプロジェクトの名前を決め、ファイルを保存する場所を選びます。作成後にフォルダごと移動することで場所は変更可能ですが、名前は作り直す以外に変更できませんのでご注意ください。
1113 [img[新しいプロジェクト|image/Win/CodeBlocks_Win13.png]]
1114
1115 プログラムはSourcesカテゴリの中にあるmain.cppにあります。標準のサンプルプログラムが入っていますので、まずはダブルクリックしてエディタを開き、中身を見てみます(最初から開いていることもあります)。
1116
1117 下のようなウィンドウが開いたでしょうか?
1118 [img[ウインドウ説明|image/Win/CodeBlocks_Win14.png]]
1119 プログラム編集エディタでブログラムを編集することができます。実際にはこのサンプルを消して、新しく自分のプログラムを書いていくわけですが、まず最初は正しくインストールされているかどうかを確かめるために、標準のサンプルをそのまま実行してみましょう。ツールバーの実行ボタンをおすと、プログラムが機械語に翻訳されて実行ファイルの作成・実行が行われます。
1120
1121
1122 出来上がった実行ファイルは、''プロジェクトのあるフォルダ &gt; binフォルダ &gt; Releaseフォルダ または Debugフォルダ'' の中にあります。この実行ファイルをコピーすることで頒布が可能ですが、メールなどに添付する際は必ずzipファイル等に圧縮してください。。
1123 </pre>
1124 </div>
1125 <div title="1.3 各試行部分のプログラミング" modifier="Psychlops_DevelopperG" modified="200908190216" created="200709252145" changecount="2">
1126 <pre>[[1.1|1.1 実験計画のプログラミング]], [[1.2|1.2 Gaborパッチの事前描画]]節で実際の試行に先立つ計算は終了したことになります。
1127 ここでは実際の試行部分のプログラミングを行います。
1128 各試行の実行時はここで作成している実験の場合大きく分けて2つに分かれるのでした。
1129    a. 各試行における描画(刺激の提示):該当するGaborが描画されたImageインスタンスを用いた描画
1130    b. 反応の取得と 配列への記録
1131 まずa.の部分についてもう少し詳しく考えてみると、以下のような手順をループさせれば良いことがわかります。
1132
1133 # 各試行に対応した独立変数の取得
1134 # 試行開始待ち(被験者のキー入力待ち)
1135 # 刺激描画
1136 # 反応待ち(被験者のキー入力待ち)
1137 # 正誤判断とデータの格納
1138
1139 この流れに従って試行番号&quot;trial&quot;番の試行をプログラムしてみます。
1140 ここで使う変数はあとでwhileループの外で宣言を行うので、とりあえず、宣言なしで変数を使っていきます。
1141
1142 !各試行に対応した独立変数の取得
1143 まず、この試行におけるGaborパッチの傾きの方向&quot;direction&quot;をPsychlops::random()命令を使って0か1にランダムに決定します。
1144 次に、試行番号trialから6.1で作成したクラスインスタンスinvarを用いて各独立変数を取得します。
1145 各試行における傾き量の番号(&quot;ori_now&quot;)と提示時間(&quot;dura_now&quot;)を求めるには~IndependentVariables::get()を使います。
1146 |!int ~IndependentVariables::get()|set(int vnum, int trial_now)|
1147 |~|~|int vnum: 独立変数の識別番号|
1148 |~|~|int trial_now: 試行番号|
1149
1150 単に設定したinvarから各試行に対応した変数を取得するのであれば、以下のコードの様になるでしょう。
1151 {{{
1152 ori_now=invar.get(0, trial);
1153 dura_now=invar.get(1, trial);
1154 }}}
1155
1156 しかしここでは実際に欲しい以下の2つの値を計算します
1157 * [[1.2|1.2 Gaborパッチの事前描画]]節で事前描画したImageの配列gIMGの中でこの試行でCanvasに転送されるImageの番号
1158 * 刺激提示のフレーム数
1159
1160 転送番号については、[[1.2|1.2 Gaborパッチの事前描画]]節で作った表をみると、呼び出す配列番号が(傾き量番号)+5 x (方向)であることがわかります。
1161 |!gIMGの配列番号|gIMG[0]|gIMG[1]|gIMG[2]|gIMG[3]|gIMG[4]|gIMG[5]|gIMG[6]|gIMG[7]|gIMG[8]|gIMG[9]|
1162 |!(傾き量, 方向) 番号|(0,0)|(1,0)|(2,0)|(3,0)|(4,0)|(0,1)|(1,1)|(2,1)|(3,1)|(4,1)|
1163
1164 次に、刺激提示のフレーム数ですが、これはCanvasクラスのリフレッシュレートを取得する関数getRefreshRate()を使えばCanvasの宣言に依存せずに提示時間を制御できます。^^*1^^
1165
1166 これらをまとめると変数の取得部は、以下の様なコードになります。
1167 {{{
1168 direction=Psychlops::random(2); //0:right 1:left
1169 ori_now=invar.get(0,trial)+5*direction;
1170 dura_now=sampleA.getRefreshRate()*(invar.get(1,trial)/1000);
1171 }}}
1172 ^^*1 この実験では必ず計算結果が整数フレームとなるようにあらかじめ変数を設定してあることに注意してください。^^
1173
1174 !試行開始待ち(被験者のキー入力待ち)
1175 次にユーザー(被験者)の準備が整って、キーを押すまでプログラムを&quot;待ち&quot;の状態にする部分をコーディングします。
1176 ここでは、試行開始のキーにはスペースキーを用います。さらに、被験者がわかりやすいように、プログラム側の準備が整って「待ち」の状態になっている間は試行が何番目に当たるかを表示するようにしてみます。
1177 キー入力待ちのコードは今まで何度も使ってきたとおりに、以下のように書きます。
1178 {{{
1179 while(!Input::get(Keyboard::spc));
1180 }}}
1181 試行番号を表示するにはCanvas::message()命令を使えばよいのですが、試行番号は試行毎に変化するので少し工夫が必要です。
1182 ここでは、Cの標準関数であるsprintf()関数を使って、試行番号を文字列に埋め込みます。この部分はあまりプログラムの流れには関係ないので、良くわからない方はとりあえず無視して先に進んでください。以上をまとめると以下のようなコードになります。
1183 {{{
1184 sampleA.clear(0.5);
1185
1186 for(int i=0; i&lt;64; i++) trial_header[i] = 0; // 文字列の初期化
1187 sprintf(trial_header, &quot;%s%d%s%d&quot;,&quot;Trial: &quot;,  trial, &quot; /&quot;, trialNum ); //試行番号の埋め込み
1188 sampleA.message(trial_header, sampleA.getHcenter(), sampleA.getVcenter()-100, Color::green);//キー待ちメッセージの描画
1189 sampleA.flip();
1190
1191 while(!Input::get(Keyboard::spc));
1192 }}}
1193
1194 !刺激描画
1195 いよいよ実際の刺激の描画ですが、これは特に難しくありません。Image::draw()命令を使って裏画面にori_now番のイメージを転送し、Canvas::flip(int)命令を使って、dura_nowフレーム分の描画時間を予約して描画を反映させます。この後に、Canvas::clear()命令を使えば、一定時間たつと自動的に刺激が消えるプログラムになります。
1196 {{{
1197 sampleA.clear(0.5);
1198 gIMG[ori_now].draw();
1199 sampleA.flip(dura_now);
1200 sampleA.clear(0.5);
1201 sampleA.flip();
1202 }}}
1203
1204 ! 反応待ち(被験者のキー入力待ち)とデータの格納
1205
1206 刺激描画が終わったら被験者のキー入力(f: 左/j: 右)を待ちます。プログラムは&quot;f&quot;か&quot;j&quot;が押されるまでは待ち、押されたら押されたキーに従って、0(j), 1(f)のいずれかの値を変数ansに格納します。[[6.1|6.1 Gaborパッチの事前描画]]節においてgIMGを描画する際、傾きの方向番号にふられた番号が0が右、1が左であったことに注意してください。また、ここでescを押すと、プログラムを途中終了するコードも入れておきます。
1207
1208 {{{
1209 while(1){
1210         if(Input::get(Keyboard::j)){ans=0;break;} //&quot;j&quot;なら右(0)
1211         if(Input::get(Keyboard::f)){ans=1;break;}//&quot;f&quot;なら左(1)
1212         if(Input::get(Keyboard::esc)){exit(0);}
1213 }
1214 }}}
1215
1216 最後はデータの格納です。上の方でランダムに決定したdirectionとキー入力によって得られた値ansが同じならば正答(1),異なれば誤答(0)を配列answer のtrial番目に格納します。ついでに、後でデータを見やすくするためにここで使った2つ独立変数の値も配列orientationConditionと配列durationConditionに格納しておきます。
1217 {{{
1218 if(ans==direction) answer[trial]=1;
1219         else answer[trial]=0;
1220 orientationCondition[trial]=invar.get(0, trial);
1221 durationCondition[trial]=invar.get(1, trial);
1222 }}}
1223
1224 !まとめ
1225 以上をまとめて、ループの中に入れると、各試行部分のプログラミングは完成です。
1226 これまで保留していた変数の宣言がループの前にきちんと記述されていることやそれぞれの型がどのようになっているかにも注目してください。特に、answer, orientationCondition, durationConditionの配列を確保するためにはtrialNumをconst int型として宣言する必要があることには注意が必要です。
1227
1228 {{{
1229         const int trialNum=2*5*10;
1230         int answer[trialNum], orientationCondition[trialNum], durationCondition[trialNum], ans;
1231         int ori_now, dura_now;
1232         int direction;
1233         char trial_header[64];
1234
1235         for(int trial=0; trial&lt;trialNum; trial++){
1236         //各試行に対する変数の取得
1237                 direction=Psychlops::random(2); //0:right 1:left
1238                 ori_now=invar.get(0,trial)+5*direction;
1239                 dura_now=sampleA.getRefreshRate()*(invar.get(1,trial)/1000);
1240
1241         //被験者のキー入力待ち
1242                 sampleA.clear(0.5);
1243                 for(int i=0; i&lt;64; i++) trial_header[i] = 0; // 文字列の初期化
1244                 sprintf(trial_header, &quot;%s%d%s%d&quot;,&quot;Trial: &quot;,  trial, &quot; /&quot;, trialNum ); //試行番号の埋め込み
1245                 sampleA.message(trial_header, sampleA.getHcenter(), sampleA.getVcenter()-100, Color::green);//キー待ちメッセージの描画
1246                 sampleA.flip();
1247                 while(!Input::get(Keyboard::spc));
1248
1249         //刺激描画
1250                 sampleA.clear(0.5);
1251                 gIMG[ori_now].draw();
1252                 sampleA.flip(dura_now);
1253                 sampleA.clear(0.5);
1254                 sampleA.flip();
1255
1256         //反応の取得と格納
1257                 while(1){
1258                         if(Input::get(Keyboard::j)){ans=0;break;}       
1259                         if(Input::get(Keyboard::f)){ans=1;break;}
1260                         if(Input::get(Keyboard::esc)){exit(0);}
1261                 }
1262                 if(ans==direction)answer[trial]=1;
1263                         else answer[trial]=0;
1264                 orientationCondition[trial]=invar.get(0, trial);
1265                 durationCondition[trial]=invar.get(1, trial);
1266         }
1267 }}}</pre>
1268 </div>
1269 <div title="1.4 更新とアンインストール" modifier="YourName" modified="200802191314" created="200709131956" changecount="3">
1270 <pre>2007/8/13以降のバージョンを使用していた場合は、Psychlopsの更新は通常のインストールとまったく同じです。ダウングレードする場合も同様です(つまり、単純に上書きしてしまってかまいません)。それ以前のバージョンが既にインストールされたシステムについては、一度アンインストールをしてからもう一度インストールを行ってください(下記参照)。
1271
1272 Psychlopsは特にレジストリ等の設定を行いませんので、アンインストール時はインストールされたフォルダごと削除してください。Psychlopsは単なるC++ライブラリで、常駐等はしませんので、削除しない場合でもシステムに影響を与えることはありません。
1273
1274 また、C++開発環境を自分で既に運用していて、デフォルトの場所以外の所にライブラリを置くことも可能です。この場合は、インストールを行った後に、自分でFrameworks以下にあるファイル・フォルダを目的の場所に移動して、ご自分でパスの設定をなさってください。
1275
1276 * ~MacOSX
1277 ** /Library/Frameworks/Psychlops.framework 
1278
1279 * Windows
1280 ** (システムドライブ)\Library\Frameworks\Psychlops.framework
1281 *** Windowsには通常Libraryフォルダがありませんので、このフォルダ内にPsychlopsしか入っていなければLibraryフォルダごと削除してかまいません。
1282 ** Relo、Code::Blocksのテンプレートについては、テンプレート一覧ファイルを上書きしています。元に戻す必要がある場合は、再インストールする必要があります。</pre>
1283 </div>
1284 <div title="1.4 終了処理とまとめ" modifier="Psychlops_DevelopperG" modified="200908190216" created="200709252145" changecount="1">
1285 <pre>最後にデータをファイルにセーブして、実験の終了メッセージを記録すれば、プログラミングは完了です。
1286 被験者の反応を記録した配列answerと実験条件を記録した配列orientationCondition, durationConditionをData::savearray()命令を使用して&quot;~ExptData.dat&quot;という名前のテキストファイル(tab区切り)にセーブします。
1287
1288 {{{
1289 sampleA.clear(0.5);
1290 sampleA.message(&quot;Press space key to exit.&quot;, 
1291               sampleA.getHcenter(), sampleA.getVcenter()-100, Color::green);
1292 Data::savearray(&quot;ExptData.dat&quot;, 
1293            &quot;Duration /t Orientation /t Answer&quot;, trialNum, 
1294            durationCondition, orientationCondition, answer );
1295 }}}
1296
1297 これで、すべてのパーツが完成しました。一つにまとめると以下の様になります。
1298 [[Gabor方位判断の実験プログラム例]]
1299 ぱっと見たときには長そうなプログラムですが、よく見ると実際にはクラスの宣言部分がほとんどを占めていて、実験に必要な部分のプログラム(つまりあなたが理解して、書かなければいけない部分!) は比較的短いコードであることがわかります。
1300 コンパイルして1つ1つの部分が正しく実行されているかどうか確かめてみてください。</pre>
1301 </div>
1302 <div title="1.5 Psychlopsの基本構造" modifier="Psychlops_DevelopperG" modified="200908190224" created="200711081840" changecount="3">
1303 <pre>次節で詳しく述べるように、PsychlopsはC++のライブラリになっています。このライブラリは、いくつかの命令のグループから成り立っています。それぞれの命令のことをメソッド、グループのことをクラスと呼びます。Psychlopsを用いた描画は、ある変数がどのクラスに属する変数なのかを宣言し、この変数に対してメソッドを指定することで行います。より詳細な具体例は次節や、このマニュアルの中で随時見ていくと理解できるでしょう。今は、このようなグループがいくつかPsychlopsの中にあると言うことだけを理解しておけば十分です。以下に代表的なPsychlopsのクラスをまとめておきます。
1304
1305 |!Psychlops::Canvas|描画ウィンドウの確保と基本的な描画の命令セット|
1306 |!Psychlops::Point|ドット描画と座標指定のための命令セット|
1307 |!Psychlops::Rectangle|矩形描画と矩形範囲指定のための命令セット|
1308 |!Psychlops::Color|描画色指定のための命令セット|
1309 |!Psychlops::Image|オフスクリーン描画のための命令セット|
1310 |!Psychlops::Matrix|行列演算のための命令セット|
1311 |!Psychlops::Clock|時間計測のための命令セット|
1312 |!Psychlops::Input|入出力取得のための命令セット|
1313
1314 それぞれのクラスの先頭についているPsychlops::はこれらのクラスがPsychlopsというライブラリの中のクラスであることを示しています。たとえば、Colorと言うクラスは、別のライブラリの中にも存在するかも知れません。その場合、プログラムを解釈する側は、このクラスがどのライブラリのクラスであるかを判断できなくなってしまいます。これを避けるために、スコープ演算子&quot;::&quot;を使って、あるクラスがどのライブラリに所属するかを示すのです。名字のようなものだと思うとわかりやすいかも知れません。
1315
1316 このほかにもPsychlopsにはData, File, Range等の特殊なクラスが存在しますが、これらについては具体的な使い方とともに解説します。
1317
1318 </pre>
1319 </div>
1320 <div title="1.6 Psychlopsを使ったプログラミング" modifier="Kazushi Maruya" modified="200711081830" created="200709170801" changecount="11">
1321 <pre>Psychlopsを使ったプログラミングはC++のプログラミングと同様ですが、必ずしもその全てを理解しておく必要はありません。(もちろん、理解しているほうがより柔軟なプログラミングができますが。)
1322 ここでは、psychlopsを用いたプログラミングの際に、必要不可欠だと思われる基本要素についてごく簡単な説明を行います。より詳細なC++プログラミングを学びたい方は入門書・専門書を参考にされることをおすすめしますが、ここで説明するいくつかのことに対する大まかなイメージをもっておけばPsychlopsのプログラミングにとっては十分なはずです。より細かなことは実際のプログラミングを通して理解することをおすすめします。
1323
1324 [[1.6.1 プログラミングとは?]]
1325 [[1.6.2 Psychlopsを使ったプログラムの形]]
1326 [[1.6.3 配列]]
1327 [[1.6.4 for命令]]
1328 [[1.6.5 変数型]]
1329 [[1.6.6 クラスと関数]]</pre>
1330 </div>
1331 <div title="1.6.1 プログラミングとは?" modifier="YourName" modified="200802191315" created="200709170805" changecount="4">
1332 <pre>まず最初に、プログラムとは何かについて説明します。
1333
1334 プログラムとは、画像や数値などのデータを、一定の手続きに従って操作する過程を、文字で表記したものです。たとえば、Excelなどの表計算でセルに書き込む式も、一種のプログラムということができます。
1335 ここでは、Psychlopsのプログラムを例に考えてみます。
1336
1337 プログラムでまず最初に必要なことは、取り扱うデータや装置を準備することです。
1338 コンピュータを使わない実験でも、画像や画像を配置する画面などをあらかじめ準備しなければなりませんが、
1339 これと同じことをコンピューター上でも行います。
1340
1341 この準備を文字で表記すると、
1342 ''準備したい物 好きな名前''
1343 という書き方になります。たとえば、
1344 {{{
1345 Image natural_image;
1346 }}}
1347 この1行は、画像(Iamge)にnatural_imageという名前をつけて取り扱う準備をしたことになります。
1348
1349 次に、データを操作する手続きを記述します。手続きは
1350 ''操作が行われる対象.操作方法(関係するデータ)''
1351 の方法で記述します。英語のSVO文法に近い記述法ですね。
1352
1353 {{{
1354 natural_image.load(&quot;Natural Image.png&quot;);
1355 }}}
1356 この一行は、natural_imageという画像として、「Natural Image.png」という名前の画像ファイルを読み込む手続きを表します。
1357
1358 {{{
1359 natural_image.centering().draw();
1360 }}}
1361 この一行は、先ほど読み込んだ画像を、画面の中央に配置(センタリング)し、画面に描画する手続きを表します。
1362
1363 プログラムとは、「データを準備する」「データを操作する」ことについて、行う順番どおりに書いていくことなのです。
1364 準備や操作に区切りをつけるには「;」(セミコロン)を記述します。</pre>
1365 </div>
1366 <div title="1.6.2 Psychlopsを使ったプログラムの形" modifier="YourName" modified="200802191316" created="200709170816" changecount="5">
1367 <pre>では、Psychlopsで動く簡単なプログラムを例にとって最も簡単なPsychlopsのプログラムの形を見てみます。
1368
1369 {{{
1370 #include &lt;psychlops.h&gt;
1371 using namespace Psychlops;
1372
1373 void psychlops_main() {
1374         Canvas display( Canvas::fullscreen );
1375         Image natural_image;
1376         natural_image.load(&quot;Natural Image.png&quot;);
1377         natural_image.centering().draw();
1378         display.flip();
1379 }
1380 }}}
1381
1382 先頭の2行はPsychlopsを使うための命令です。
1383 意味はわからなくてもかまいませんが、Psychlopsの命令をコンピュータに理解させるためには必ずこの2行をプログラムの先頭に書いておく必要があります。
1384
1385 {{{
1386 #include &lt;psychlops.h&gt;    // このプログラムにPsychlopsを含める
1387 using namespace Psychlops; // このプログラムはPsychlopsを使う
1388 }}}
1389
1390 次の行は、Psychlopsを用いたプログラム本体を書く場所(メインルーチン)を示す行です。
1391 {{{
1392 void psychlops_main() {         // psychlopsが主に扱うブロック
1393 }}}
1394 Psychlopsは、ここから最後の行の閉じ括弧(})までのブロックをまず最初に実行します。
1395 つまり、この括弧の中にある
1396 {{{
1397         Canvas display( Canvas::fullscreen );
1398         Image natural_image;
1399         natural_image.load(&quot;Natural Image.png&quot;);
1400         natural_image.centering().draw();
1401         display.flip();
1402 }}}
1403 この5行が実際にコンピュータに対して与える命令になります。
1404 今はこの5行の具体的な内容については述べませんが、ユーザーは基本的にはこの部分を書き換えて描画を実行させることになります。</pre>
1405 </div>
1406 <div title="1.6.3 配列" modifier="PsychlopsAdmin" modified="200709170821" created="200709170817" changecount="1">
1407 <pre>これまでの例では画像はひとつだけでしたが、何枚かの画像を選んで出したい場合があります。
1408 いくつかのデータに似たような操作をしたい場合、「配列」が役に立ちます。配列とは、データがまとめて並んでいるもののことを言います。内部にいくつも区切りがある箱のようなものを想像するとよいかもしれません。
1409
1410 たとえば、画像を3枚まとめて扱いたい場合、以下のようにデータを準備します。
1411 {{{
1412 Image natural_images[3];
1413 }}}
1414
1415 これで3枚の画像配列が用意されました。次に、データの並びの中のそれぞれの画像を操作してみましょう。
1416
1417 {{{
1418 natural_images[0].load(&quot;Natural Image0.png&quot;);
1419 natural_images[1].load(&quot;Natural Image1.png&quot;);
1420 natural_images[2].load(&quot;Natural Image2.png&quot;);
1421 }}}
1422
1423 '''配列[ 数字 ]'''
1424 と書くことで、配列のうち数字で指定した順番の要素(この場合は画像)を操作することができます。
1425 順番は0から始まることに注意してください。画像3枚を用意した場合、0,1,2になります。</pre>
1426 </div>
1427 <div title="1.6.4 for命令" modifier="Kazushi Maruya" modified="200711082123" created="200709170819" changecount="7">
1428 <pre>この画像配列を順番に表示してみましょう。ここでは、繰り返し操作を記述するfor文を使うことにします。
1429 {{{
1430 for(int i=0; i&lt;3; i++) {
1431         natural_images[i].centering().draw();
1432         display.flip(60);
1433 }
1434 }}}
1435 for文は、{ } で囲まれたブロックを複数回実行することを示します。
1436 回数は自分で指定することができます。
1437 [img[image/formethod.png]]
1438 この命令の後に続く{}で囲まれた部分のブロックは繰り返し、カウンタ(i)の値を0,1,2の順番で変更しながら実行されます。
1439 {{{
1440 {
1441         natural_images[i].centering().draw();
1442         display.flip(60);
1443 }
1444 }}}
1445
1446 つまり
1447 {{{
1448         for(int i=0; i&lt;3; i++) {
1449                 natural_images[i].centering().draw();
1450                 display.flip(60);
1451         }
1452 }}}
1453
1454 {{{
1455         natural_images[1].centering().draw();
1456         display.flip(60);
1457         natural_images[2].centering().draw();
1458         display.flip(60);
1459         natural_images[3].centering().draw();
1460         display.flip(60);
1461 }}}
1462 は全く同じ結果になります。
1463
1464 image.centering().draw()は画像をセンタリングして描画する操作なので、以下のプログラムをNatural Image0.png, Natural Image1.png, Natural Image2.pngという3つのファイルがあるディレクトリで実行すると、3つの画像が順番に表示されます。
1465
1466 {{{
1467 #include &lt;psychlops.h&gt;
1468 using namespace Psychlops;
1469
1470 void psychlops_main() {
1471         Canvas display( Canvas::fullscreen );
1472         Image natural_image;
1473
1474         natural_images[0].load(&quot;Natural Image0.png&quot;);
1475         natural_images[1].load(&quot;Natural Image1.png&quot;);
1476         natural_images[2].load(&quot;Natural Image2.png&quot;);
1477
1478         for(int i=0; i&lt;3; i++) {
1479                 natural_images[i].centering().draw();
1480                 display.flip(60);
1481         }
1482         
1483 }
1484
1485 }}}</pre>
1486 </div>
1487 <div title="1.6.5 変数型" modifier="PsychlopsAdmin" modified="200709170825" created="200709170821" changecount="3">
1488 <pre>ここまではPsychlopsで扱えるデータを題材に解説してきましたが、最後に、C++言語で取り扱うことのできる基本的なデータについて説明します。
1489 これらのデータの中でもっとも基本的なものは数値です。数値は、主に整数型と小数点型に分かれます。
1490 * int
1491 ** 整数(''int''eger)の略で、整数を取り扱います。小数点以下は切り捨てられますので、注意してください。
1492 * double
1493 ** 浮動小数点を取り扱います(倍精度浮動小数点 ''double'' precision floating point number)
1494
1495 これらのデータも宣言方法は同じで、
1496 {{{
1497 int delay;
1498 double contrast;
1499 }}}
1500
1501 のように使います。データに特定の数値を代入する(格納する)場合は、
1502 {{{
1503 contast = 0.5;
1504 }}}
1505 のように = を使って指定します。
1506
1507 C++にはこのほかにも文字列型(char *やstd::string)、真偽値型(bool)などがありますが、ここでは詳しくは説明しません。これらの型の使い方については、これ以降のプログラミングの実例のなかで必要なところだけを説明していきます。詳細を知りたい型は、CやC++の入門書をご覧ください。</pre>
1508 </div>
1509 <div title="1.6.6 クラスと関数" modifier="YourName" modified="200802191319" created="200709171609" changecount="14">
1510 <pre>! クラス
1511 クラスとは、関係する変数(データ)や関数(メソッド)をまとめたものです。
1512 [[1.5節|1.5 Psychlopsの基本構造]]で概観したように、Psychlopsでは、画面(Canvas)や画像(Image)、図形(Ractangle)など、描画に関するさまざまなクラスを用意しています。各クラスには、その機能に関連した固有の関数があります。
1513 たとえばImageは
1514 * Image.draw()
1515 * Image.pix(x, y, color)
1516 * Image.rect(rect, color)
1517 * Image.save(filename)
1518 *Image. load(filename)
1519 などさまざまな関数を持っています。
1520
1521 !! 関数
1522 関数はには''返り値''と''引数''があります。たとえば累乗関数(power)を例に見てみましょう。
1523 {{{
1524 a = pow( 10, 3 );
1525 }}}
1526 この式を実行すると、変数aには10の3乗である1000が代入されます。累乗関数が必要とする値である10と3を引数といい、返される計算結果1000を返り値と呼びます。
1527
1528 pow関数の引数のうち、1番目は累乗される数、2番目は累乗する数です。順番や型は固定されていますのでご注意ください。順番を間違えると、計算結果も変わってきてしまいます。たとえば、
1529 {{{
1530 a = pow( 3, 10 );
1531 }}}
1532 を実行すると、3の10乗である59049がaに代入されます。
1533
1534
1535 !! 関数の続け書き
1536 Psychclopsでは、一部の関数を.でつなぎながら連続で書くことができます。
1537 {{{
1538 rect.centering().shift(x, y).draw(Color::red);
1539 }}}
1540 この書き方は他のC言語のプログラムではあまり見かけませんが、Psychlopsでは多用されています。プログラムの長さを短くし読みやすくすることができます。
1541
1542 この書き方ができるものは、関数の返り値として変数自身が返されるものに限ります。
1543
1544
1545 !! 関数表の読み方
1546
1547 このマニュアルでは、各クラスの関数について表で説明が出てきます。この表の読み方を、Imageを表示する関数を題材に説明します。
1548
1549 |!Image::draw()|draw()|指定したオフスクリーンを描画します|
1550 |~|draw(double left, double top)|指定したオフスクリーン領域を(left,top)を中心とした座標に設定します|
1551 |~|~|double left: 描画するオフスクリーン領域の座標軸xを指定|
1552 |~|~|double top: 描画するオフスクリーン領域の座標軸yを指定|
1553
1554 一番左の列は関数の名前を示します。ここでまたスコープ演算子が関数名の名前についていますが、これは1.5節と同様にその関数がどのクラスに所属しているかを示すためのものです。
1555 これを見ると、実際のプログラムでも
1556 {{{
1557 Image::draw(10,10);
1558 }}}
1559 と書く様な気がしてしまいますが、いくつかのクラスをのぞいて^^*1^^このような書き方をすることはありません。なぜなら、実際のプログラムの中は、あるクラスに属する変数(インスタンス、と呼ばれます)に対しての命令しか行わないからです。Image型として宣言されたインスタンスに対してメソッドを実行するときには&quot;.&quot;演算子を用います。たとえば、Image::draw()命令を実際に使うときには以下のような形になるでしょう。
1560 {{{
1561 Psychlops::Image img;
1562 img.draw(10,10);
1563 }}}
1564
1565 *1 Input, Display等のハードウェアと直結したクラスはインスタンスを宣言せずに使います。これは、これらのクラスがPsychlopsのイニシャライズ時にあらかじめ宣言されたデフォルトのインスタンスを持っていて、これに対するメソッドの実行を行うように設計されているためです。
1566
1567 上記の表は、Image::draw()というメソッドに
1568 * Image::draw()
1569 * Image::draw(double left, double top)
1570 という二つの使い方があることを示しています。
1571
1572
1573 二番目の列は、引数の違ういくつかのバージョンが列挙されています。
1574 2つめの使い方Image::draw(double left, double top)では、括弧の中にコンマで区切られた2つの文字列があります。
1575 これは、この使い方では関数の引数として2つの変数を代入することができることを示しています。
1576 [img[image/FunctionNotation.png]]
1577 一つめの引数はdouble leftと書かれています。これは、一つめの引数がdouble型の変数でなくてはいけないことを示しています。(ただし、後で書くようにleftと言う名前にする必要はありません。)このマニュアルの中ではそれぞれのメソッド型名(スペース)名前という形でそれぞれの使い方に必要な引数の形式を示しています。
1578
1579 実際に使用するときは、型名を書く必要はありません。left,topという2つの引数をこの関数に渡すときには以下のように書きます。
1580 {{{
1581 Psychlops::Image img;
1582 double left=10,top=10;
1583 img.draw(left,top);
1584 }}}
1585
1586 一番右の列は、各バージョンでの引数の説明になっています。Image::draw(double left, double top)の右側の欄を見ると、各引数について、一番目が表示したいx座標、二番目がy座標であることがわかります。
1587
1588 各引数には名前がついていますが、関数を呼び出す際に引き渡す変数はこの名前である必要はありません。マニュアルでは
1589 * image.draw(left, top)
1590 となっていますが、プログラム中では
1591 * image.draw(100, 200)
1592 * image.draw( x, y )
1593 など任意の値を渡すことが可能です。</pre>
1594 </div>
1595 <div title="2. デモを作成する" modifier="Psychlops_DevelopperG" modified="200908190221" created="200712061915" changecount="3">
1596 <pre>Psychlopsには、プログラム内の変数をプログラムのユーザがインタラクティブに操作できるデモアプリケーションを簡単に作成するためのクラスIndependentが用意されています。
1597 この節では、Independentクラスについて簡単に説明して、これを用いたプログラムの作成例について解説します。
1598
1599 [[2.1 独立変数Independent]] 
1600 [[2.2 デモ環境の表示]] 
1601 </pre>
1602 </div>
1603 <div title="2. 基本的な描画を行う" modifier="Psychlops_DevelopperG" modified="200909300724" created="200708211944" changecount="16">
1604 <pre>[[2.1 描画領域の宣言]]  
1605   [[2.1.1 Canvasの宣言]]
1606   [[2.1.2 Canvasの基本構造と操作]]
1607   [[2.1.3 Canvasのメンバ取得と変数の表示]]       
1608 [[2.2 図形描画のコマンド]]
1609   [[2.2.1 画面上に点を描画する-Pointクラス1-]]           
1610   [[2.2.2 画面上に線を描画する]]           
1611   [[2.2.3 画面上に四角形を描画する-Rectangleクラス1-]]            
1612   [[2.2.4 画面上に円を描画する]]            
1613   [[2.2.5 描画する図形の色を指定する-Colorクラス-]]           
1614   [[2.2.6 画面上に縞を描画する-Rectangleクラス2-]]
1615   [[2.2.7 画面上に縞を描画する-Pointクラス2-]] 
1616   [[2.2.8 運動する図形を描画する-Rectangleクラス3-]] 
1617 [[2.3 文字列の描画]]</pre>
1618 </div>
1619 <div title="2.1 描画領域の宣言" modifier="Psychlops_DevelopperG" modified="200908190225" created="200708212010" changecount="6">
1620 <pre>この節ではPsychlopsのもっとも基本的なクラスであるCanvasクラスの宣言方法と初歩的な操作を説明します。
1621 [[2.1.1 Canvasの宣言]]
1622 [[2.1.2 Canvasの基本構造と操作]]
1623 [[2.1.3 Canvasのメンバ取得と変数の表示]]</pre>
1624 </div>
1625 <div title="2.1 独立変数Independent" modifier="Psychlops_DevelopperG" modified="200908190217" created="200710220054" changecount="1">
1626 <pre>!Independentとは
1627
1628 Independentクラスは、プログラム中で独立変数に当たる変数を制御するためのオプション機能です。現在はデモ用のコンソールで使用することが出来ます。
1629
1630 通常、プログラムは画面を描画するなどの作業を行っています。プログラムの中にはたくさんの変数や関数が入っています。
1631 [img[Independentなし|image/Independent1.png]]
1632
1633 プログラム中の変数をIndependentに登録すると、もともとのプログラムの動作を変えずにデモ用のコンソール機能などを追加することが出来ます。
1634 [img[Independentあり|image/Independent2.png]]
1635
1636
1637 !Independentクラスの設定
1638
1639 (Tiddly Wikiの機能上の都合、記号が全角で表示されていますが、実際は半角です)
1640 |!Independent|<< 変数|Independentクラスに登録する変数を指定します。|
1641 |~|| 文字列|変数につけるラベルを指定します|
1642 |~|| Range|Independenクラスtに指定する変数の変動範囲を指定します。変動範囲チェックはデモコンソールなどからの書き換え時にしか行われないため、元のプログラムで変動範囲外に変化する場合は検出できません。|
1643 |~|| 数値|デモコンソールでのキー押しあたり変化量を設定します。2つ続けて書いた場合、一方がShiftキーとの同時押し時の変化量になります。|
1644 現在のところ(2007年11月現在)、Independentクラスに登録できる変数は数値型(int, double等)のみです。
1645
1646 !!Independentクラスへの登録
1647 以下では、例として3つの変数をIndependentクラスに登録する書き方例を示します。
1648 {{{
1649         double rect_size = 100;
1650         double rect_lum  = 0.5;
1651         double bg_lum    = 0.2;
1652
1653         Range rng;
1654         Independent &lt;&lt; rect_size | &quot;Rect Size&quot;      |   1&lt; rng&lt; 500 | 10.0 | 2.0 ;
1655         Independent &lt;&lt; rect_lum  | &quot;Rect Luminance&quot; | 0.0&lt;=rng&lt;=1.0 |  0.1 | 0.05;
1656         Independent &lt;&lt; bg_lum    | &quot;BG Luminance&quot;   | 0.0&lt;=rng&lt;=1.0 |  0.1 | 0.05;
1657 }}}
1658
1659 ここで注意することは、変数に適正な初期値を与えておくことです。もしも、Indpendentで設定される範囲の外に初期値が設定されてしまうと、実行時に変更することができなくなります。変数の宣言の時に忘れずに初期値の設定をしておくことをおすすめします。
1660 </pre>
1661 </div>
1662 <div title="2.1.1 Canvasの宣言" modifier="Psychlops_DevelopperG" modified="200910080812" created="200708240239" changecount="25">
1663 <pre>!Canvasとは?
1664
1665 絵を書くときには描く筆や絵の具のほかに紙などのキャンバスが必要です。
1666 この処理はコンピュータ上に絵を描くための&quot;Canvas(描画のためのウィンドウ)&quot;を用意します。
1667
1668 Canvasが確保されていないと描画処理を記述しても処理されません。
1669 描画処理を行う前に必ずCanvasの宣言が必要となります。
1670
1671 !Canvasの宣言文
1672 キャンバスを用意する命令には様々な書式があるのですが、その基本は以下の2つです。
1673
1674 一つは解像度、リフレッシュレートを変更せずに、画面いっぱいに領域を確保する
1675 Canvas(Canvas::fullscreen)
1676
1677 もう一つは解像度、リフレッシュレートを変更して、画面いっぱいに領域を確保する
1678 Canvas(int,int,int,double)
1679 です。
1680
1681 後者で指定した画面モードが選べなかった場合は、例外が送出されプログラムは強制終了します。
1682
1683 そのほかに Canvas::windowスイッチを使ってウインドウを開いて画面を取得する方法があります。
1684 この方法では描画の垂直同期信号への同期が保証されないので(くわしくは[[2.1.2 Canvasの基本構造と操作]]で説明します)、デモやプログラムのデバッグ時に使うとよいでしょう。
1685 詳しくは[[関数一覧|Canvasの宣言]]をご覧ください。
1686 * 参考[[Tips: Canvasとリフレッシュレートの詳細]]
1687
1688 |!Canvasの宣言|
1689 |~|Canvas(Canvas::fullscreen, const Display disp)|&gt;|!現在の画面モードで画面を確保する場合に使います|
1690 |~|~|Canvasmode mode|[[表示モード|CanvasMode]](フルスクリーンorウィンドウ)を選択|
1691 |~|~|const [[Display|Displayの指定値]] disp|[[表示するディスプレイ|Displayの指定値]]を選択(省略可能)|
1692 |~|~|&gt;|マルチディスプレイ環境では、最後の引数でどのディスプレイに表示するか指定できます。省略時はプライマリディスプレイになります。|
1693 |~|Canvas(int width, int height, int colordepth, double refreshrate, const Display disp)|&gt;|!画面モードを指定してフルスクリーン画面を確保します|
1694 |~|~|int width|水平方向の解像度(画面の横幅)を指定|
1695 |~|~|int height|垂直方向の解像度(画面の縦幅)を指定|
1696 |~|~|int colordepth|カラーモード(ビット)を指定|
1697 |~|~|double refreshrate|リフレッシュレート( Hz )を指定|
1698 |~|~|const [[Display|Displayの指定値]] disp|[[表示するディスプレイ|Displayの指定値]]を選択(省略可能)|
1699 |~|~|&gt;|指定した画面モードが選べなかった場合は、例外が送出されプログラムは強制終了します。|
1700
1701 !!宣言文の書き方
1702 {{{
1703 &lt;例1: Canvas::fullscreenオプションを使った宣言例&gt;
1704 #include &lt;psychlops.h&gt;
1705 using namespace Psychlops;
1706
1707 void  psychlops_main() {
1708         Canvas sampleA(Canvas::fullscreen);
1709 }
1710 }}}
1711 {{{
1712 &lt;例2: 各属性値を指定した宣言例&gt;
1713 #include &lt;psychlops.h&gt;
1714 using namespace Psychlops;
1715
1716 void psychlops_main() {
1717         Canvas sampleB(1024,768,32,60.0);
1718 }
1719 }}}
1720
1721 {{{
1722 &lt;例2: 各属性値を指定した宣言例&gt;
1723 #include &lt;psychlops.h&gt;
1724 using namespace Psychlops;
1725
1726 void psychlops_main() {
1727         Canvas sampleC(Canvas1024,768,32,60.0);
1728 }
1729 }}}
1730
1731 @@◎クラスとインスタンスについて@@
1732 上記のソースでは「psychlops_main()」クラスの中で
1733 sampleA,sampleBという名前のCanvasを宣言しています。
1734 これらのプログラム内でCanvas型(クラス)として宣言された変数はインスタンスと呼ばれます。
1735 マニュアル上では、ある命令hoge()の形式を説明するときにCanvas::hoge()と書きますが、実際にプログラム内で宣言されたCanvasに対して命令を実行したいときは、このインスタンス名を使って『インスタンス名.命令文()』という形式で記述します。たとえば、Sample.hoge(hoge)といった形です。より具体的な例はこれ以降に何回も現れます。
1736 ここで注意しなくてはならない点として、ふつうCanvasを宣言する場所はpsychlops_main() 内であることです。つまり、この宣言名はpsychlops_main() 内でしか有効ではありません。
1737 psychlops_main()で呼び出す他の関数内でCanvasを使用したいときは、その関数にCanvasのポインタを引き渡すかDisplayクラスを使って描画する必要があります。
1738 * 参考[[Tips::CanvasクラスとDisplayクラス]]</pre>
1739 </div>
1740 <div title="2.1.2 Canvasの基本構造と操作" modifier="Psychlops_DevelopperG" modified="200910080810" created="200708240240" changecount="7">
1741 <pre>Psychlopsでは2つの画面を交互にディスプレイに提示します。
1742 この2つの画面が対となって、1つのCanvasを構成しています。たとえば、紙に裏表があるようなものだと思ってください。
1743 この2枚の画面に描画をすることとflip()メソッドを使って表画面と裏画面を切り替えることがCanvasクラスに対する主な操作です。
1744
1745 [img[image/canvasstructure.png]]
1746
1747 !Canvas::clear() -画面を塗りつぶす- 
1748 はじめにCanvasを何かの色で塗りつぶしてみましょう。
1749 そのための処理がCanvas::clear()命令です。
1750 書式はCanvas::clear([[Psychlops::Color]] col,TargetSurface)です。いずれの引数も省略可能です。
1751 |!Canvas::clear()|clear([[Psychlops::Color]] col,TargetSurface)|指定したCanvasを指定色で塗りつぶします|
1752 |~|~|[[Psychlops::Color]] col :塗りつぶす色を指定|
1753 |~|~|TargetSurface:塗りつぶす画面を指定|
1754 |~|clear(TargetSurface)|指定したCanvasを塗りつぶします。色の指定には[[Canvas::setClearColor()]]命令を使用します|
1755 |~|~|TargetSurface:塗りつぶす画面を指定|
1756
1757 2つめの引数は少しわかりにくいかもしれません。
1758 clear()命令は2つめの引数を指定することで、今ディスプレイに表示されている画面(紙の表)と、表示されていない画面(紙の裏)のいずれを(あるいは両方を)塗りつぶすのかを指定することができます。
1759 省略した場合は表示されていない画面を塗りつぶします。
1760
1761
1762 !!Canvas::clear()の書き方
1763 {{{
1764 &lt;例1&gt;
1765 #include &lt;psychlops.h&gt;
1766 using namespace Psychlops;
1767
1768 void psychlops_main() {
1769
1770         Canvas sampleA(Canvas::fullscreen);
1771         sampleA.clear(Color::white,Canvas::FRONT);
1772 }
1773 }}}
1774 今ディスプレイに表示されている画面(紙の表)を白く塗りつぶせ、という命令文ですが
1775 これでは描画が一瞬で結果がわかりにくいですね。
1776 「while(!Input::get(Keyboard::spc));」を追加してスペースキーが押されるまで今ディスプレイに表示されている画面(紙の表)が白く表示されるかを確認できるようにしましょう。
1777 この命令の詳細については、[[4.1節|4.1 外部装置(キーボード/マウス)の操作内容を取得する]] で詳しく説明します。今はこの文がスペースキーの入力待ちをする文であると思っていてください。
1778 {{{
1779 &lt;例2&gt;
1780 #include &lt;psychlops.h&gt;
1781 using namespace Psychlops;
1782
1783 void psychlops_main() {
1784
1785         Canvas sampleA(Canvas::fullscreen);
1786         sampleA.clear(Color::white,Canvas::FRONT);
1787         while(!Input::get(Keyboard::spc));
1788 }
1789 }}}
1790
1791 !Canvas::flip() -画面を入れ替える-
1792 上の例では、オプションを使って現在表示されている画面に対して描画を実行しました。
1793 しかし、これは例外的な描画で、Psychlopsでは描画処理は表示されていない画面(裏画面)に対して行われます。
1794 従って、描画内容を画面に反映させるためには、表示画面と裏画面を入れ替える必要があります。
1795 この表と裏の2つの画面を入れ替え処理を行う命令がCanvas::flip()命令です。
1796 つまり、描画内容はCanvas::flip() 処理で画面を入れ替えることで初めて画面上に表示されます。
1797 この命令は画面の垂直同期信号(=ビデオカードが1枚の画面を書ききったことを示す信号; Vsync)に同期して、画面の切り替えを行います。
1798 * 参考 [[Tips: Canvas::flip()とコマ落ち]]
1799
1800 |!Canvas::flip()|flip(int)|int:入力値のリフレッシュ分描画内容を表示します|
1801 |~|~|引数を入力しない場合(flip())、flip(1)が指定されます|
1802 |~|flipAfter(int wait)|int wait回のリフレッシュ後まで待ってからflip()を実行して描画内容を画面に反映させます|
1803 |~|flipAndWait(int wait)|もっとも近い垂直同期のタイミングでflip()を実行した後に、int wait回ののリフレッシュ分の表示を予約します。次にflip()命令がコールされても、int wait回ののリフレッシュ分が経過するまでは何も起こりません(flip()と全く同じ効果ですが、引数は省略不可)|
1804
1805 flip()系の命令は画面描画の時間制御をする上でもっとも重要な命令です。上の表だけではわかりにくいでしょうから、図と実例を用いて各命令の働きをもう少し詳しく見ていきましょう。
1806
1807 flipAfter(), flipAndWait()命令の動きを図解すると以下のようになります。以下の図ではflipAfter(3)とflip(3)(あるいはflipAndWait(3))の場合を例としてあげました。
1808 [img[image/psychlops_flip.png]]
1809
1810 !!Canvas::flip()の書き方1
1811 {{{
1812 &lt;例3&gt;
1813 #include &lt;psychlops.h&gt;
1814 using namespace Psychlops;
1815
1816 void psychlops_main() {
1817
1818         Canvas sampleA(Canvas::fullscreen);
1819         sampleA.clear(Color::blue,Canvas::BACK);
1820         while(!Input::get(Keyboard::spc));
1821         sampleA.flip();
1822 }
1823 }}}
1824 例2では今ディスプレイに表示されている画面(紙の表)の色を塗りつぶしましたが
1825 今回は表示されていない画面(紙の裏)を塗りつぶし、それをflip()命令で表画面に入れ替えました。
1826
1827 !!Canvas::flip()の書き方2
1828 次は、flip()が実行されるタイミングを制御してみましょう。
1829 flip()命令の引数を省略すると、flip()命令は、描画の計算が終了した時点からもっとも早い同期タイミングで画面の切り替えを行います。
1830
1831 {{{
1832 &lt;例4&gt;
1833 #include &lt;psychlops.h&gt;
1834 using namespace Psychlops;
1835
1836 void psychlops_main() {
1837         Canvas sampleA(Canvas::fullscreen);
1838         sampleA.clear(Color::blue,Canvas::BACK);
1839         sampleA.flip(60);
1840         sampleA.clear(Color::cyan,Canvas::BACK);
1841         sampleA.flip(60);
1842         while(!Input::get(Keyboard::spc));
1843 }
1844 }}}
1845 青→シアンに画面の色が塗りつぶされるのを確認できたでしょうか。
1846 次はflipAfter()で同様のソースを実行します。
1847 {{{
1848 &lt;例5&gt;
1849 #include &lt;psychlops.h&gt;
1850 using namespace Psychlops;
1851
1852 void psychlops_main() {
1853         Canvas sampleA(Canvas::fullscreen);
1854         sampleA.clear(Color::blue,Canvas::BACK);
1855         sampleA.flipAfter(60);
1856         sampleA.clear(Color::cyan,Canvas::BACK);
1857         sampleA.flipAfter(60);
1858         while(!Input::get(Keyboard::spc));
1859 }
1860 }}}
1861 &lt;例4&gt;と&lt;例5&gt;の結果の差が見えたでしょうか。
1862 &lt;例4&gt;は60フレームのリフレッシュ分描画内容を表示したのに対し
1863 &lt;例5&gt;は60フレームのリフレッシュ分後、描画内容を表示しました。
1864 &lt;例6&gt;はflipAndWait()による画面入替えの確認をします。
1865 こちらはflip();と同様の結果を返します。
1866 &lt;例4&gt;と同じ結果が返るか確認してください。
1867 {{{
1868 &lt;例6&gt;
1869 #include &lt;psychlops.h&gt;
1870 using namespace Psychlops;
1871
1872 void psychlops_main() {
1873         Canvas sampleA(Canvas::fullscreen);
1874    sampleA.clear(Color::blue,Canvas::BACK);
1875         sampleA.flipAndWait(60);
1876         sampleA.clear(Color::cyan,Canvas::BACK);
1877         sampleA.flipAndWait(60);
1878         while(!Input::get(Keyboard::spc));
1879 }
1880 }}}</pre>
1881 </div>
1882 <div title="2.1.3 Canvasのメンバ取得と変数の表示" modifier="Psychlops_DevelopperG" modified="200910080827" created="200708240242" changecount="29">
1883 <pre>!Canvas::getXXXX()
1884 Psychlopsではより複雑な描画に必要なCanvasの様々な画面情報を、Canvas::getXXXX()命令を使って取得します(XXXXにはそれぞれの情報に対応した名前が入ります)。
1885 |!Canvas::getXXXX()|getCenter()|画面の中心座標(x,y)を取得します|
1886 |~|getHcenter()|横方向の中心座標(x)を取得します|
1887 |~|getVcenter()|縦方向の中心座標(y)を取得します|
1888 |~|getHeight()|画面の高さ(ピクセル)を取得します|
1889 |~|getWidth()|画面の幅(ピクセル)を取得します|
1890 |~|getColorDepth()|カラーモード(ビット)を取得します|
1891 |~|getRefreshRate()|リフレッシュレート(Hz)を取得します|
1892
1893 [img[image/canvasget.png]]
1894
1895 !!Canvas::getXXXX()の書き方
1896 実際に取得してみます。
1897 {{{
1898 &lt;例1&gt;
1899 #include &lt;psychlops.h&gt;
1900 using namespace Psychlops;
1901
1902 void psychlops_main() {
1903
1904         Canvas sampleA(Canvas::fullscreen);
1905         sampleA.getCenter();
1906         sampleA.getHcenter();
1907         sampleA.getVcenter();
1908         sampleA.getHeight();
1909         sampleA.getWidth();
1910         sampleA.getColorDepth();
1911         sampleA.getRefreshRate();
1912
1913                 
1914         sampleA.flip();
1915         while(!Input::get(Keyboard::spc));
1916 }
1917 }}}
1918 このコードを実行するだけでは、取得結果を確認できません。
1919 次に取得結果を確認するため、画面に取得した変数の値を表示してみましょう。
1920
1921 !Canvas::var()とCanvas::msg()
1922 取得した変数の値を画面に表示するにはCanvas::var()命令を使います。
1923 |!Canvas::var()|var(string str, double x, double y,[[Psychlops::Color]] col, bool fillForward)|
1924 |~|~|&gt;|&gt;|string str|画面表示をする変数を指定|
1925 |~|~|&gt;|&gt;|double x|画面表示をする座標(x)を指定|
1926 |~|~|&gt;|&gt;|double y|画面表示をする座標(y)を指定|
1927 |~|~|&gt;|&gt;|[[Psychlops::Color]] col|描画する点の色を指定(設定しない場合、白が設定される)|
1928 |~|~|~|bool fillForward|表示位置の指定|true |指定座標(x,y)から左部分に表示する|
1929 |~|~|~|~|~|false |指定座標(x,y)から右部分に表示する|
1930 |~|~|~|~|&gt;|&gt;|(指定しない場合、falseが設定される)|
1931
1932
1933 さらに、わかりやすくするためにラベルをつけましょう。
1934 定型の短い文章を画面上に表示する方法としてCanvas::msg()命令があります。
1935 |!Canvas::msg()|msg(char* string,double x,double y,[[Psychlops::Color]] col)|char* stringを座標(x,y)に指定色で表示します|
1936 |~|~|schar* string:画面表示をする文字列を指定|
1937 |~|~|double x:画面表示をする座標(x)を指定|
1938 |~|~|double y:画面表示をする座標(y)を指定|
1939 |~|~|[[Psychlops::Color]] col:描画する点の色を指定(設定しない場合、白が設定される)|
1940 |~|msg(string str,double x,double y,[[Psychlops::Color]] col)|string strを座標(x,y)に指定色で表示します|
1941 |~|~|string str:画面表示をする文字列を指定|
1942 |~|~|double x:画面表示をする座標(x)を指定|
1943 |~|~|double y:画面表示をする座標(y)を指定|
1944 |~|~|[[Psychlops::Color]] col:描画する点の色を指定(設定しない場合、白が設定される)|
1945
1946 |[img[image/canvasvar.png]]|[img[image/canvasmsg.png]]
1947
1948 !!Canvas::var()の書き方
1949 {{{
1950 &lt;例2&gt;
1951 #include &lt;psychlops.h&gt;
1952 using namespace Psychlops;
1953
1954 int a1,a2,a3,a4,a5;
1955 double d1,d2,d3,x,y;
1956 Psychlops::Point point1;
1957
1958 void psychlops_main() {
1959
1960         Canvas sampleA(Canvas::fullscreen);
1961         
1962         point1=sampleA.getCenter();
1963         d1=sampleA.getHcenter();
1964         d2=sampleA.getVcenter();
1965         a1=sampleA.getHeight();
1966         a2=sampleA.getWidth();
1967         a3=sampleA.getColorDepth();
1968         d3=sampleA.getRefreshRate();
1969         
1970         //get命令の内容を画面表示する
1971         x=point1.getX();
1972         y=point1.getY();
1973         sampleA.msg(&quot;getcenter_x:&quot;,50,200,Color::yellow);
1974         sampleA.var(x,200,200,Color::yellow,true);//getCenter:X
1975         sampleA.msg(&quot;getcenter_y:&quot;,50,250,Color::yellow);
1976         sampleA.var(y,200,250,Color::yellow,true);//getCenter:Y         
1977         sampleA.msg(&quot;getHcenter:&quot;,50,300,Color::green);
1978         sampleA.var(d1,200,300,Color::green,true);//getHcenter
1979         sampleA.msg(&quot;getVcenter:&quot;,50,350,Color::green);
1980         sampleA.var(d2,200,350,Color::green,true);//getVcenter
1981         sampleA.msg(&quot;gettHeight:&quot;,50,400,Color::red);
1982         sampleA.var(a1,200,400,Color::red,false);//getHeight
1983         sampleA.msg(&quot;getWidth:&quot;,50,450,Color::red);
1984         sampleA.var(a2,200,450,Color::red,true);//getWidth
1985
1986         sampleA.msg(&quot;getColorDepth:&quot;,250,200);
1987         sampleA.var(a3,500,200);//getColorDepth
1988         sampleA.msg(&quot;getRefreshRate:&quot;,250,250);
1989         sampleA.var(d3,500,250);//getRefreshRate
1990         
1991         sampleA.flip();
1992         while(!Input::get(Keyboard::spc));
1993 }
1994 }}}
1995 このコードを実行すると、宣言されたCanvasの各属性値が画面上に表示されます。</pre>
1996 </div>
1997 <div title="2.2 デモ環境の表示" modifier="Psychlops_DevelopperG" modified="200908190221" created="200710220134" changecount="3">
1998 <pre>Independentクラスを使って、デモ環境を自動的に構成することが出来ます。
1999 以下では具体例を挙げて簡単なデモ環境の構築を説明します。
2000
2001 1章で述べたやり方に従って、新規プロジェクトを作成すると、cppファイルの中に自動的にPsychlopsのデモプログラムが
2002 書き込まれています。このデモプログラムはIndependentクラスを用いた簡単なデモンストレーションのソースファイルになっています。ここでは、このデモプログラムを題材にIndependentを用いたデモ環境の構築について説明します。
2003 [[Psychlopsプログラムのデフォルトテンプレート]]
2004
2005 [[2.2.1 プログラムの基本構造]]
2006 [[2.2.2 デモ環境に使う描画命令の設定]]
2007 [[2.2.3 デモ環境の実行]]</pre>
2008 </div>
2009 <div title="2.2 図形描画のコマンド" modifier="Psychlops_DevelopperG" modified="200908190227" created="200708212305" changecount="14">
2010 <pre>[[2.1|2.1 描画領域の宣言]]節はCanvasそのものについて説明しました。
2011 本節ではCanvas上に点や線、図形を描画します。
2012
2013 [[2.2.1 画面上に点を描画する-Pointクラス1-]]           
2014 [[2.2.2 画面上に線を描画する]]           
2015 [[2.2.3 画面上に四角形を描画する-Rectangleクラス1-]]            
2016 [[2.2.4 画面上に円を描画する]]            
2017 [[2.2.5 描画する図形の色を指定する-Colorクラス-]]           
2018 [[2.2.6 画面上に縞を描画する-Rectangleクラス2-]]
2019 [[2.2.7 画面上に縞を描画する-Pointクラス2-]] 
2020 [[2.2.8 運動する図形を描画する-Rectangleクラス3-]] </pre>
2021 </div>
2022 <div title="2.2.1 プログラムの基本構造" modifier="Psychlops_DevelopperG" modified="200908190218" created="200711072356" changecount="1">
2023 <pre>このデモプログラムは大きく分けて2つの部分からなっています。
2024 {{{
2025 void RectLuminance() {
2026         double rect_size = 100;
2027         double rect_lum  = 0.5;
2028         double bg_lum    = 0.2;
2029
2030         Psychlops::Rectangle rect(rect_size,rect_size);
2031         rect.centering();
2032
2033         Range rng;
2034         Independent &lt;&lt; rect_size | &quot;Rect Size&quot;      |   1&lt; rng&lt; 500 | 10.0 | 2.0 ;
2035         Independent &lt;&lt; rect_lum  | &quot;Rect Luminance&quot; | 0.0&lt;=rng&lt;=1.0 |  0.1 | 0.05;
2036         Independent &lt;&lt; bg_lum    | &quot;BG Luminance&quot;   | 0.0&lt;=rng&lt;=1.0 |  0.1 | 0.05;
2037
2038         while(!Input::get(Keyboard::esc)) {
2039                 Display::clear(Color(bg_lum));
2040                 rect.resize(rect_size,rect_size);
2041                 rect.display(rect_lum);
2042                 Display::flip();
2043         }
2044 }
2045 }}}
2046 の部分と
2047 {{{
2048 void psychlops_main() {
2049         Canvas display(Canvas::fullscreen);
2050
2051         Procedure p;
2052         p.setDesign(Procedure::DEMO);
2053         p.setProcedure(RectLuminance);
2054         p.run();
2055 }
2056 }}}
2057 の部分です。
2058
2059 ぱっと見てわかることは、Psychlops_mainの中に描画命令が存在しないことでしょう。実際の描画命令はPsychlops_main()の上にある~RectLuminance()という関数の中にかかれています。
2060
2061 代わりに個々まで一度も触れられていないようなProcedureと言うクラスのインスタンスが宣言されて、これに対してsetDesign(), setProcedure(), run()といったメソッドがかかれています。
2062 実はデモを構築する時には、このProcedureクラスの詳細については理解する必要はありません。
2063 setProcedure()の引数としてかかれている文字列が、実際の描画を行う関数としてプログラム中にかかれていれば、後の部分は変更しなくてもデモ環境を構築することができます。
2064 つまり、もし実際の描画命令を書く関数の名前が~DemoDraw()であったとすると、この描画内容を使ったデモンストレーションを構築するためには、
2065 {{{
2066 void DemoDraw(){
2067        ......
2068 }
2069 void psychlops_main() {
2070         Canvas display(Canvas::fullscreen);
2071
2072         Procedure p;
2073         p.setDesign(Procedure::DEMO);
2074         p.setProcedure(DemoDraw);
2075         p.run();
2076 }
2077 }}}
2078 と書けば良いと言うことです。デフォルトのテンプレートと比較して、p.setProcedure()の行だけが異なっていることに注意してください。</pre>
2079 </div>
2080 <div title="2.2.1 画面上に点を描画する-Pointクラス1-" modifier="Psychlops_DevelopperG" modified="200910080836" created="200708212310" changecount="29">
2081 <pre>!Canvas::pix()
2082 Canvas上に点を描画するための命令です。
2083 Canvasに点や線等を描画するときに必要なのは「どこに」「何色」のものを描画するか、です。
2084 ここでの「どこが」が点を描画する「座標」、「何色」が「色の指定」となります。
2085 座標はCanvasの左上端が座標の基点(x=0,y=0)となっています。
2086 色の指定についての詳細は[[2.2.5 描画する図形の色を指定する-Colorクラス-]]を参照してください。ここでは、必要最小限の命令のみを使用・説明します。
2087
2088 |!Canvas::pix()|pix(double x,double y,[[Psychlops::Color]] col) |&gt;|!座標(x,y)に指定色の点を描画します&lt;例1&gt;|
2089 |~|~|double x|描画する点の座標軸xを指定|
2090 |~|~|double y|描画する点の座標軸yを指定|
2091 |~|~|[[Psychlops::Color]] col|描画する点の色を指定|
2092 |~|pix([[Psychlops::Point]] point,[[Psychlops::Color]] col) |&gt;|!Point座標(x,y)に指定色の点を描画します^^*1^^&lt;例4&gt;|
2093 |~|~|[[Psychlops::Point]] point |座標(x,y)を指定|
2094 |~|~|[[Psychlops::Color]] col|描画する点の色を指定|
2095 |~|pix(int dotsCnt,double* xArray,double* yArray),[[Psychlops::Color]]* colArray)|&gt;|!配列の座標(x[],y[])に指定色(配列を用いて複数色指定)の点を描画します&lt;例2&gt;|
2096 |~|~|int dotsCnt|描画する点の数を指定(後ろの引数の配列の個数を超えてはいけない)|
2097 |~|~|double* xArray|描画する点の座標軸xが格納された配列名(ポインタ)を指定|
2098 |~|~|double* yArray|描画する点の座標軸yが格納された配列名(ポインタ)を指定|
2099 |~|~|[[Psychlops::Color]]* colArray|描画する点の色が格納された配列名(ポインタ)を指定|
2100 |~|pix(int dotsCnt,double* xArray,double* yArray,[[Psychlops::Color]] col)|&gt;|!配列の座標(x[],y[])に指定色(一色のみ)の点を描画します&lt;例3&gt;|
2101 |~|~|int dotsCnt|描画する点の数(配列数)を指定|
2102 |~|~|double* xArray|描画する点の座標軸xが格納された配列名(ポインタ)を指定|
2103 |~|~|double* yArray|描画する点の座標軸yが格納された配列名(ポインタ)を指定|
2104 |~|~|[[Psychlops::Color]] col|描画する点の色を指定|
2105 |~|pix(int dotsCnt,[[Psychlops::Point]]* pointArray,[[Psychlops::Color]]* colArray)|&gt;|!配列Point座標(x[],y[])に指定色の点を描画します^^*1^^&lt;例5&gt;|
2106 |~|~|int dotsCnt|描画する点の数(配列数)を指定|
2107 |~|~|[[Psychlops::Point]]* pointArray |配列の座標(x[],y[])が格納された配列名(ポインタ)を指定|
2108 |~|~|[[Psychlops::Color]]* colArray|描画する点の色が格納された配列名(ポインタ)を指定|
2109 ^^*1 次のPoint::set()で説明します。^^
2110
2111 [img[image/canvaspix.png]]
2112
2113 !!Canvas::pix()の書き方
2114 座標(50,50)に青の点を描画します。
2115 {{{
2116 &lt;例1&gt;
2117 #include &lt;psychlops.h&gt;
2118 using namespace Psychlops;
2119
2120 void psychlops_main() {
2121
2122         Canvas sampleA(Canvas::fullscreen);
2123         while(!Input::get(Keyboard::spc)){
2124                 sampleA.pix(50,50,Color::blue);
2125                 sampleA.flip();
2126         }
2127 }
2128 }}}
2129 次は一命令で複数の異なる輝度の点を描画します。一つの命令を繰り返し実行する[[for文|1.6.4 for命令]]を使用しなくても複数個の点が描画される点に注目してください。
2130 {{{
2131 &lt;例2&gt;
2132 #include &lt;psychlops.h&gt;
2133 using namespace Psychlops;
2134
2135 //色の配列を宣言. 輝度値は0.0-1.0の範囲で指定
2136 Psychlops::Color colArray[3]={1.0,0.5,0.25};
2137 //X座標の配列を宣言
2138 double xArray[3] = {50,100,150};
2139 //Y座標の配列を宣言
2140 double yArray[3] = {50,50,50};
2141
2142 void psychlops_main() {
2143
2144         Canvas sampleA(Canvas::fullscreen);
2145         sampleA.pix(3,xArray,yArray,colArray);
2146         sampleA.flip();
2147         while(!Input::get(Keyboard::spc));
2148 }
2149 }}}
2150
2151 次は&lt;例2&gt;と同様に一命令で複数の点を描画しますが、全点の色は同一です。
2152 {{{
2153 &lt;例3&gt;
2154 #include &lt;psychlops.h&gt;
2155 using namespace Psychlops;
2156
2157 //X座標の配列を宣言
2158 double xArray[3] = {50,100,150};
2159 //Y座標の配列を宣言
2160 double yArray[3] = {50,50,50};
2161
2162 void psychlops_main() {
2163
2164         Canvas sampleA(Canvas::fullscreen);
2165         sampleA.pix(3,xArray,yArray,Psychlops::Color(1.0,0.0,0.0));//3つめの引数は描画色に赤を指定
2166         sampleA.flip();
2167         while(!Input::get(Keyboard::spc));
2168 }
2169 }}}
2170
2171 @@◎カラーについて@@
2172 &lt;例1&gt;~&lt;例3&gt;で使用している[[Psychlops::Color]]型について簡単に説明します。
2173 PsychlopsではRGB,GRAY(輝度)の色を描画することが可能です。
2174 &lt;例1&gt;で青の点を描画するため「Color::blue」という命令を使用しています。
2175 Psychlopsではいくつかのよく使用する色をあらかじめ予約語として設定してあります。
2176 これらのプリセットされた色は「Color::色名」と記述することで、使用することができます。
2177 あらかじめプリセットされた色については[[Color::XXXX]]を参照してください。
2178 &lt;例2&gt;では輝度値を使用しています。
2179 輝度値は0.0(黒)~1.0(白)の範囲で指定されています。
2180 Psychlops::Color colArray[3]={1.0,0.5,0.25};
2181 の命令でそれぞれの点に輝度値が設定されました。
2182 &lt;例3&gt;では赤の点を描画しています。
2183 Psychlops::Color型の変数に3つの引数を指定したときは、それぞれの値が順に赤、緑、青の各画素の輝度の指定として解釈されます。
2184 red,green,blueはそれぞれ0.0~1.0の範囲で指定されます。
2185 この例では[[Psychlops::Color]](red=1.0,green=0.0,blue=0.0)と指定することで赤が設定されました。
2186
2187 !Point::set()
2188 今まで座標はそれぞれx,y座標の変数を作成して値を設定していました。
2189 今回はPointクラスを使用して座標(x,y),ないし座標X,Yを指定し点を描画します。
2190 Point::set()を使用し座標を取得することでソースの簡略化がなされ
2191 また、修正がしやすくなります。
2192
2193 |!Point::set()|set(double x, double y)|座標(x,y)の値を設定します|
2194 |~|~|double x:座標xを指定|
2195 |~|~|double y:座標yを指定|
2196 |~|setX(double val)|X座標の値を設定します|
2197 |~|~|double val :座標xを指定|
2198 |~|setY(double val)|Y座標の値を設定します|
2199 |~|~|double val :座標yを指定|
2200
2201 !!Point::set()の書き方
2202 座標(50,50)に青の点を描画します。
2203 &lt;例1&gt;との差分は、直接座標を記述するかPoint::set()で設定するか、だけです。
2204 {{{
2205 &lt;例4&gt;
2206 #include &lt;psychlops.h&gt;
2207 using namespace Psychlops;
2208
2209 //Point型を宣言
2210 Psychlops::Point point1;
2211
2212 void psychlops_main() {
2213
2214         Canvas sampleA(Canvas::fullscreen);
2215         //座標(X,Y)を設定
2216         point1.set(50,50);
2217         sampleA.pix(point1,Psychlops::Color(0.0,0.0,1.0));
2218         sampleA.flip();
2219         while(!Input::get(Keyboard::spc));
2220 }
2221 }}}
2222 次はX,Y座標それぞれ座標を取得し、一命令で複数の点を描画します。
2223 &lt;例2&gt;との差分は、直接座標を記述するかPoint::set()で設定するか、だけです。
2224 {{{
2225 &lt;例5&gt;
2226 #include &lt;psychlops.h&gt;
2227 using namespace Psychlops;
2228
2229 //色の配列を宣言
2230 Psychlops::Color colArray[3]={1.0,0.5,0.25};
2231 //座標(X,Y)の配列を宣言
2232 Psychlops::Point pointArray[3];
2233
2234 void psychlops_main() {
2235
2236         Canvas sampleA(Canvas::fullscreen);
2237         //座標(X[],Y[])にそれぞれ値((50,50),(100,50),(150,50)を設定
2238         for(int i=0; i&lt;3; i++){
2239                 pointArray[i].setX((i+1)*50);
2240                 pointArray[i].setY(50);
2241         }
2242
2243         sampleA.pix(3,pointArray,colArray);
2244         sampleA.flip();
2245         while(!Input::get(Keyboard::spc));
2246 }
2247 }}}
2248
2249 !Point::centering()
2250 今度は点を画面中心に描画してみましょう。
2251 画面の大きさは環境により変化します。
2252 今までの命令では画面中心に点を描画することは難しいです。
2253 そこでPoint::centering()を使用します。
2254 この命令を使用することで、点を自動的に画面中心に移動します。
2255
2256 |!Point::centering()|centering()|座標(x,y)の点を画面の中心に移動します|
2257 |~|centering(double h, double v)|任意の座標(h,v)に点を移動します|
2258 |~|~|double h:移動するx軸の値を指定|
2259 |~|~|double v:移動するy軸の値を指定|
2260
2261 !Point::centering()の書き方
2262 {{{
2263 &lt;例6&gt;
2264 #include &lt;psychlops.h&gt;
2265 using namespace Psychlops;
2266
2267 //座標(X,Y)を宣言
2268 Psychlops::Point point1;
2269
2270 void psychlops_main() {
2271
2272         Canvas sampleA(Canvas::fullscreen);
2273         //座標(X,Y)を画面中心に移動
2274         point1.centering();
2275
2276         //点を描画する
2277         sampleA.pix(point1,Color::red);
2278         sampleA.flip();
2279         while(!Input::get(Keyboard::spc));
2280 }
2281 }}}
2282
2283 !Point::shift()
2284 この命令もPoint::centering()と同様に点に対する移動命令です。
2285 元の座標(x,y)を基点に入力値(h,v)だけ移動します。
2286
2287 |!Point::shift()|shift(double h, double v)|座標(X,Y)を(h,v)だけ移動します|
2288 |~|~|double h:右水平方向に移動する値を指定|
2289 |~|~|double v:下垂直方向に移動する値を指定|
2290
2291 [img[image/pointshift.png]]
2292
2293 !!Point::shift()の書き方
2294 &lt;例6&gt;を元に画面中央から(100,100)移動した青の点を描画します。
2295 {{{
2296 &lt;例7&gt;
2297 #include &lt;psychlops.h&gt;
2298 using namespace Psychlops;
2299
2300 //座標(X,Y)を宣言
2301 Psychlops::Point point1,point2;
2302
2303 void psychlops_main() {
2304
2305         Canvas sampleA(Canvas::fullscreen);
2306         //座標(X,Y)を画面中心に移動
2307         point1.centering();
2308         //座標(X,Y)を画面中心に移動し、更に(100,100)移動する
2309         point2.centering().shift(100,100);
2310
2311         //点を描画する
2312         sampleA.pix(point1,Color::red);
2313         sampleA.pix(point2,Color::blue);
2314         sampleA.flip();
2315         while(!Input::get(Keyboard::spc));
2316 }
2317 }}}
2318 画面中央の赤色の点(point1)と画面中央の右下に描画された青色の点(point2)が確認できたでしょうか。
2319 </pre>
2320 </div>
2321 <div title="2.2.2 デモ環境に使う描画命令の設定" modifier="Psychlops_DevelopperG" modified="200908190219" created="200711080010" changecount="2">
2322 <pre>さて、次に実際に描画を行っている関数~RectLuminance()について見てみましょう。[[2.2.1節|2.2.1 プログラムの基本構造]]で触れたとおり、デフォルトのデモプログラムでは、デモをする本体がpsychlops_mainとは別の関数になっています。
2323
2324 {{{
2325 void RectLuminance() {
2326         double rect_size = 100;
2327         double rect_lum  = 0.5;
2328         double bg_lum    = 0.2;
2329
2330         Psychlops::Rectangle rect(rect_size,rect_size);
2331         rect.centering();
2332
2333         Range rng;
2334         Independent &lt;&lt; rect_size | &quot;Rect Size&quot;      |   1&lt; rng&lt; 500 | 10.0 | 2.0 ;
2335         Independent &lt;&lt; rect_lum  | &quot;Rect Luminance&quot; | 0.0&lt;=rng&lt;=1.0 |  0.1 | 0.05;
2336         Independent &lt;&lt; bg_lum    | &quot;BG Luminance&quot;   | 0.0&lt;=rng&lt;=1.0 |  0.1 | 0.05;
2337
2338
2339         while(!Input::get(Keyboard::esc)) {
2340                 Display::clear(Color(bg_lum));
2341                 rect.resize(rect_size,rect_size);
2342                 rect.display(rect_lum);
2343                 Display::flip();
2344         }
2345
2346 }
2347 }}}
2348
2349 現段階ではこのデモ環境に使う描画関数は引数をとることができません。同様に値を返すこともできないので、かならずvoid XXXXXXX()という形で宣言が行われることになります。[[2.2.1節|2.2.1 プログラムの基本構造]]でも触れたとおり、名前は何でもかまいません。
2350
2351 次に中身を見てみると、はじめに変数の宣言とIndependentクラスへの変数の登録が行われています。
2352 この部分は、[[前節2.1|2.1 独立変数Independent]]で上げた例と全く同じです。良くわからなくなったら[[2.1節|2.1 独立変数Independent]]も参照してみてください。
2353 {{{
2354         double rect_size = 100;
2355         double rect_lum  = 0.5;
2356         double bg_lum    = 0.2;
2357
2358         Range rng;
2359         Independent &lt;&lt; rect_size | &quot;Rect Size&quot;      |   1&lt; rng&lt; 500 | 10.0 | 2.0 ;
2360         Independent &lt;&lt; rect_lum  | &quot;Rect Luminance&quot; | 0.0&lt;=rng&lt;=1.0 |  0.1 | 0.05;
2361         Independent &lt;&lt; bg_lum    | &quot;BG Luminance&quot;   | 0.0&lt;=rng&lt;=1.0 |  0.1 | 0.05;
2362 }}}
2363
2364
2365 次の部分では、これまでにも見てきたようにwhile文を使ってループが設定され、その中に描画命令がかかれています。デモ実行は永久ループさせておきます。ここではESCキーを押すと終了するように設定されています。
2366 {{{
2367 while(!Input::get(Keyboard::esc)) {
2368                 Display::clear(Color(bg_lum));
2369                 rect.resize(rect_size,rect_size);
2370                 rect.display(rect_lum);
2371                 Display::flip();
2372         }
2373 }}}
2374
2375 ここでは、描画時に&quot;Display::&quot;命令が使われていることに注意してください。
2376 Canvasクラスの宣言はPsychlops_main()の中で行われるので、ここで宣言されたCanvas名displayを使うことはできません。たとえば、1行目はdisplay.clear(Color(bg_lum));と書いてしまうとコンパイル時にエラーとなってしまいます。そこでデフォルトのCanvasへの描画をするためのクラス&quot;Display::&quot;を用いて宣言されたCanvasへの描画を行っています。</pre>
2377 </div>
2378 <div title="2.2.2 画面上に線を描画する" modifier="Psychlops_DevelopperG" modified="200910080837" created="200708230440" changecount="11">
2379 <pre>!Canvas::line()
2380 Canvas上に線を描画するためにはCanvas::line()命令を使います。
2381
2382 |!Canvas::line()|line(double x1, double y1, double x2, double y2, [[Psychlops::Color]] col)|&gt;|!開始座標(X1,Y1)から終端座標(X2,Y2)まで指定色の線を描画します|
2383 |~|~|double x1|描画する線の開始座標x1を指定|
2384 |~|~|double y1|描画する線の開始座標y1を指定|
2385 |~|~|double x2|描画する線の終端座標x2を指定|
2386 |~|~|double y2|描画する線の終端座標y2を指定|
2387 |~|~|[[Psychlops::Color]] col|描画する点の色を指定|
2388 |~|line([[Psychlops::Point]] point1, [[Psychlops::Point]] point2,[[Psychlops::Color]] col)|&gt;|!開始Point座標(x1,x1)から終端Point座標(X2,Y2)まで指定色の線を描画します|
2389 |~|~|[[Psychlops::Point]] point1|開始座標(x1,y1)を指定|
2390 |~|~|[[Psychlops::Point]] point2|終端座標(x2,y2)を指定|
2391 |~|~|[[Psychlops::Color]] col|描画する点の色を指定|
2392 画面上に線を描画するためには、「どこから」「どこに」「何色」の線を引くのかの指定が必要です。
2393 「どこから」は開始座標(x1,y1)、「どこに」は終端座標(x2,y2)、「何色」は[[Psychlops::Color]]型で指定します。
2394 座標はCanvasの左上端が座標の基点(x=0,y=0)となっています。
2395 これらの座標はPoint型の変数を使用して指定することもできます。
2396
2397 [img[image/canvasline.png]]
2398
2399 !!Canvas::line()の書き方
2400 座標(100,100)から(300,300)まで赤い線を描画します。
2401 {{{
2402 &lt;例1&gt;
2403 #include &lt;psychlops.h&gt;
2404 using namespace Psychlops;
2405
2406 void psychlops_main() {
2407
2408         Canvas sampleA(Canvas::fullscreen);
2409         sampleA.line(100, 100,300,300,Color::red);
2410         sampleA.flip();
2411         while(!Input::get(Keyboard::spc));
2412 }
2413 }}}
2414 次に[[Psychlops::Point]]型を使用して黄色の線を描画します。
2415 {{{
2416 &lt;例2&gt;
2417 #include &lt;psychlops.h&gt;
2418 using namespace Psychlops;
2419
2420 Psychlops::Point point1,point2;
2421
2422 void psychlops_main() {
2423
2424         Canvas sampleA(Canvas::fullscreen);
2425         //座標(x,y)を設定
2426         point1.set(100,100);
2427         point2.set(300,300);
2428         //線を描画
2429         sampleA.line(point1,point2,Color::yellow);;
2430         sampleA.flip();
2431         while(!Input::get(Keyboard::spc));
2432 }
2433 }}}</pre>
2434 </div>
2435 <div title="2.2.3 デモ環境の実行" modifier="Psychlops_DevelopperG" modified="200908190221" created="200712061912" changecount="1">
2436 <pre>[[2.2.1節|2.2.1 プログラムの基本構造]]、[[2.2.2節|2.2.2 デモ環境に使う描画命令の設定]]で作成したプログラムを実行すると、デモ環境を使用したデモを使うことができます。
2437
2438 画面左上にデモ環境コンソールが表示されています。コンソールには、Independentで指定した変数が列挙されています。
2439
2440 [img[部分行列|image/console_1.png]]
2441
2442 コンソールで明るく表示されている変数が変更対象となる(アクティブな)変数です。アクティブな変数は上下(↓↑)キーで切り替えることができます。
2443
2444 [img[部分行列|image/console_2.png]]
2445
2446
2447 コンソールでは、左右キーで変数の値を変更することができます(減←、→増)。shiftキーを押しながら←→キーを押すと、変量が変わります。
2448 [img[部分行列|image/console_3.png]]
2449
2450 [img[部分行列|image/console_4.png]]</pre>
2451 </div>
2452 <div title="2.2.3 画面上に四角形を描画する-Rectangleクラス1-" modifier="Psychlops_DevelopperG" modified="200912010832" created="200708231915" changecount="24">
2453 <pre>PsychlopsでCanvasと並んで重要なクラスにRectangleクラスがあります。
2454 RectangleはCanvas上に四角形の領域を設定する命令です。
2455 この領域の設定はコピーやペーストのために使用することもできますし、領域自体を塗りつぶすことで四角形を表示することもできます。
2456 PsychlopsにおけるCanvas上の描画はほとんどこのRectangleクラスを使用して行うことになります。
2457
2458 !Rectangle::set()
2459 Rectangle::set()は四角形の領域を設定します。
2460 領域の設定は、四角形の左上座標(x1,y1)と四角形の右下座標(x2,y2)を指定することで行います。
2461 また色指定は[[Psychlops::Color]]型で指定します。
2462
2463 |!Rectangle::set()|set(double width, double height)|&gt;|!width×heightの四角形を設定します|
2464 |~|~|double width|描画する四角形の横幅|
2465 |~|~|double height|描画する四角形の縦幅|
2466 |~|set(double l, double t, double r, double b)|&gt;|!左上座標(l,t)から右下座標(r,b)までの四角形を設定します|
2467 |~|~|double l|描画する四角形の左上座標x1を指定|
2468 |~|~|double t|描画する四角形の左上座標y1を指定|
2469 |~|~|double r|描画する四角形の右下座標x2を指定|
2470 |~|~|double b|描画する四角形の右下座標y2を指定|
2471 |~|set([[Psychlops::Point]] point1, [[Psychlops::Point]] point2)|&gt;|!左上Point座標(x1,x1)から右下Point座標(X2,Y2)までの四角形を設定します|
2472 |~|~|[[Psychlops::Point]] point1|左上座標(x1,y1)を指定|
2473 |~|~|[[Psychlops::Point]] point2|右下座標(x2,y2)を指定|
2474
2475 [img[image/rectset.png]]
2476
2477 !Canvas::rect()
2478 画面上に四角形を描画するにはCanvas::rect()命令を使用します。
2479 Rectangle::set命令で四角形の領域設定が終わったらCanvas::rect命令で描画処理を行います。
2480 この二つの命令は四角形の描画において対になっているので併せて覚えてください。
2481
2482 |!Canvas::rect()|rect([[Psychlops::Rectangle]] rect, [[Psychlops::Color]] col)|&gt;|!四角形を指定色で描画します|
2483 |~|~|([[Psychlops::Rectangle]] rect|描画する四角形を指定|
2484 |~|~|[[Psychlops::Color]] col|描画する四角形の色を指定|
2485
2486 [img[image/canvasrect.png]]
2487
2488 !!Rectangle::set()とCanvas::rect()の書き方
2489 Rectangleを宣言して、画面上に四角形を描画して見ましょう。
2490 Rectangleを縦横のサイズのみを用いて宣言したときには、左上の座標は(0,0)に設定されています。
2491 下の例では、200×100ピクセルの白色の四角形を画面左上隅に描画します。
2492 {{{
2493 &lt;例1&gt;
2494 #include &lt;psychlops.h&gt;
2495 using namespace Psychlops;
2496 //Rectangleの宣言
2497 Psychlops::Rectangle rect1;
2498
2499 void psychlops_main() {
2500
2501         Canvas sampleA(Canvas::fullscreen);
2502         rect1.set(200,100);
2503         sampleA.rect(rect1,Color::white);
2504         sampleA.flip();
2505         while(!Input::get(Keyboard::spc));
2506 }
2507 }}}
2508 次に(50,100)と(100,150)を頂点とする大きさ50×50ピクセルの四角形を描画します。
2509 描画命令にはCanvas::rect()を使用し、色に黄色を指定しています。
2510 {{{
2511 &lt;例2&gt;
2512 #include &lt;psychlops.h&gt;
2513 using namespace Psychlops;
2514
2515 Psychlops::Rectangle rect1;
2516
2517 void psychlops_main() {
2518
2519         Canvas sampleA(Canvas::fullscreen);
2520         rect1.set(50,100,100,150);
2521         sampleA.rect(rect1,Color::yellow);
2522         sampleA.flip();
2523         while(!Input::get(Keyboard::spc));
2524 }
2525 }}}
2526 次に[[Psychlops::Point]]型を使用して頂点の座標を設定し、四角形を描画します。
2527 描画命令にはCanvas::rect()を使用し、色に緑を指定しています。
2528 {{{
2529 &lt;例3&gt;
2530 #include &lt;psychlops.h&gt;
2531 using namespace Psychlops;
2532
2533 Psychlops::Rectangle rect1;
2534 Psychlops::Point point1,point2;
2535
2536 void psychlops_main() {
2537
2538         Canvas sampleA(Canvas::fullscreen);
2539         //座標(x,y)を設定
2540         point1.set(100,100);
2541         point2.set(300,300);
2542         rect1.set(point1,point2);
2543         sampleA.rect(rect1,Color::green);
2544         sampleA.flip();
2545         while(!Input::get(Keyboard::spc));
2546 }
2547 }}}
2548
2549 ここまでは、基本的な四角形の設定について実行してきました。次に複数の同一四角形を描画します。
2550 複数の同一四角形を描画するのにそれぞれに対応したRectangleを宣言することもできますが、よりよい方法があります。
2551 一つだけRectangleを宣言して、これをシフトしながら描画する方法です。一つのはんこを紙の上に何度も押すようなものです。
2552 Psychlopsでは、Canvas::flip()が起こるまでは、画面上に描画内容が表示されませんから、このような方法を用いても複数の四角形が同時に画面に表示されます。
2553 &lt;例1&gt;の四角形を座標(0,0),画面中心,画面中心から(200,200)移動したところに四角形を描画してみましょう。
2554 {{{
2555 &lt;例4&gt;
2556 #include &lt;psychlops.h&gt;
2557 using namespace Psychlops;
2558
2559 Psychlops::Rectangle rect1,rect2,rect3;
2560
2561 void psychlops_main() {
2562
2563         Canvas sampleA(Canvas::fullscreen);
2564         rect1.set(200,100);
2565         rect2.set(rect1);
2566         rect3.set(rect1);
2567         rect2.centering();
2568         rect3.centering().shift(200,200);
2569         sampleA.rect(rect1,Color::white);
2570         sampleA.rect(rect2,Color::white);
2571         sampleA.rect(rect3,Color::white);
2572         sampleA.flip();
2573         while(!Input::get(Keyboard::spc));
2574 }
2575 }}}
2576 </pre>
2577 </div>
2578 <div title="2.2.4 画面上に円を描画する" modifier="Psychlops_DevelopperG" modified="200908190229" created="200708232150" changecount="15">
2579 <pre>Canvas上に円を描画します。
2580 座標はCanvasの左上端が座標の基点(x=0,y=0)となっています。
2581
2582 !Canvas::oval()
2583 Rectangleを使用してこれに内接するような円を描画することもできます。
2584 [[Rectangle.set()|2.2.3 画面上に四角形を描画する-Rectangleクラス1-]]命令で横軸と縦軸の直径を設定し、Canvas::oval()命令で円を描画します。
2585 色指定は[[Psychlops::Color]]型で指定します。
2586
2587 |!Canvas::oval()|oval([[Psychlops::Rectangle]] rect, [[Psychlops::Color]] col)|[[Psychlops::Rectangle]] rectで指定された直径の円を指定色で塗りつぶし描画します|
2588 |~|~|[[Psychlops::Rectangle]] rect:描画する円の直径|
2589 |~|~|[[Psychlops::Color]] col:描画する点の円を指定|
2590
2591 [img[image/canvasoval.png]]
2592
2593 !!Canvas::oval()の書き方
2594 Rectangleを宣言し、直径100ピクセルの白い円を描画します。
2595 {{{
2596 &lt;例1&gt;
2597 #include &lt;psychlops.h&gt;
2598 using namespace Psychlops;
2599
2600 Psychlops::Rectangle rect1;
2601
2602 void psychlops_main() {
2603
2604         Canvas sampleA(Canvas::fullscreen);
2605         rect1.set(100,100);
2606         sampleA.oval(rect1,Color::white);
2607         sampleA.flip();
2608         while(!Input::get(Keyboard::spc));
2609 }
2610 }}}</pre>
2611 </div>
2612 <div title="2.2.5 描画する図形の色を指定する-Colorクラス-" modifier="YourName" modified="200803100611" created="200708232340" changecount="24">
2613 <pre>今までCanvas、描画図形に色を塗りつぶしてきましたが、色の設定については詳しくは触れませんでした。
2614 ここでは色設定について詳しく説明します。
2615
2616 !Color::set()
2617 描画対象に対して色を設定します。
2618 描画対象はCanvas,点・線・四角形・多角形・円等多岐にわたります。
2619 色の設定はおおまかに2パターンあります。
2620 RGBと~GRAYLevel(輝度)です。
2621 RGBは色の三原色に基づいた赤・緑・青で構成されています。
2622 引数が3つ(4つ)あるColor::set()命令はRGB(A)指定になります。
2623 それぞれ値を0.0~1.0の範囲で持ち、3色のバランスで色を構成します。
2624 赤・緑・青の全ての値が0.0の場合は黒、全ての値が1.0の場合は白になります。
2625 4つめの引数(A)は色の透明度を決定します。0.0の場合は完全透過、1.0の場合は完全に不透明になります。省略したときはA=1.0(完全不透過)と解釈されます
2626 ~GRAYLevel(輝度)はグレースケールの輝度値を決定します。
2627 引数は1つで0.0~1.0の範囲を持ち、0.0の場合は黒、1.0の場合は白になります。
2628
2629 カラーで描画したい場合はRGB(A)
2630 白~黒を描画したい場合は~GRAYLevel
2631 と覚えておくとよいでしょう。
2632 |!Color::set()|set(double r, double g, double b, double a)|r,g,bと透明度aで構成されるカラー情報を設定します。|
2633 |~|~|double r:赤の輝度を指定。範囲は0.0~1.0|
2634 |~|~|double g:緑の輝度を指定。範囲は0.0~1.0|
2635 |~|~|double b:青の輝度を指定。範囲は0.0~1.0|
2636 |~|~|double a:透明度を指定。範囲は0.0~1.0 (0.0に近いほど透明度が高い)|
2637 |~|set(double r, double g, double b)|r,g,bで構成されるカラー情報を設定します。|
2638 |~|~|double r:赤の輝度を指定。範囲は0.0~1.0|
2639 |~|~|double g:緑の輝度を指定。範囲は0.0~1.0|
2640 |~|~|double b:青の輝度を指定。範囲は0.0~1.0|
2641 |~|set(double gray)|グレースケールの輝度を設定します。|
2642 |~|~|double gray: 輝度を指定。範囲は0.0~1.0|
2643
2644
2645 !!Color::set()の書き方
2646 RBGで様々な色を描画してみましょう。
2647 {{{
2648 &lt;例1&gt;
2649 #include &lt;psychlops.h&gt;
2650 using namespace Psychlops;
2651
2652 Psychlops::Rectangle rect1,rect2,rect3;
2653 Psychlops::Color col1,col2,col3;
2654
2655 void psychlops_main() {
2656
2657         Canvas sampleA(Canvas::fullscreen);
2658         sampleA.clear(Color(0.25,0.25,0.25));
2659         rect1.set(50,50);
2660         rect2.set(rect1);
2661         rect3.set(rect1);
2662         rect1.centering().shift(-100,0);
2663         rect2.centering();
2664         rect3.centering().shift(100,0);
2665         //色の設定
2666         col1.set(0.7,0.2,0.4);
2667         col2.set(0.5,0.8,1.0);
2668         col3.set(0.2,0.2,0.5);
2669         sampleA.rect(rect1,col1);
2670         sampleA.rect(rect2,col2);
2671         sampleA.rect(rect3,col3);
2672         sampleA.flip();
2673         while(!Input::get(Keyboard::spc));
2674 }
2675 }}}
2676 では次に透明度の値を変えたときにどう描画されるか確認しましょう。
2677 色は赤(固定)で透明度だけ0.0,0.5,1.0と変化させてみます。
2678 {{{
2679 &lt;例2&gt;
2680 #include &lt;psychlops.h&gt;
2681 using namespace Psychlops;
2682
2683 Psychlops::Rectangle rect1,rect2,rect3;
2684 Psychlops::Color col1,col2,col3;
2685
2686 void psychlops_main() {
2687
2688         Canvas sampleA(Canvas::fullscreen);
2689         sampleA.clear(Color(0.25,0.25,0.25));
2690         rect1.set(50,50);
2691         rect2.set(rect1);
2692         rect3.set(rect1);
2693         rect1.centering().shift(-100,0);
2694         rect2.centering();
2695         rect3.centering().shift(100,0);
2696         //色の設定
2697         col1.set(1.0,0.0,0.0,0.25);
2698         col2.set(1.0,0.0,0.0,0.5);
2699         col3.set(1.0,0.0,0.0,1.0);
2700         sampleA.rect(rect1,col1);
2701         sampleA.rect(rect2,col2);
2702         sampleA.rect(rect3,col3);
2703         sampleA.flip();
2704         while(!Input::get(Keyboard::spc));
2705 }
2706 }}}
2707 今度はグレースケールで描画してみましょう。
2708 {{{
2709 &lt;例3&gt;
2710 #include &lt;psychlops.h&gt;
2711 using namespace Psychlops;
2712
2713 Psychlops::Rectangle rect1,rect2,rect3;
2714 Psychlops::Color col1,col2,col3;
2715
2716 void psychlops_main() {
2717
2718         Canvas sampleA(Canvas::fullscreen);
2719         rect1.set(50,50);
2720         rect2.set(rect1);
2721         rect3.set(rect1);
2722         rect1.centering().shift(-100,0);
2723         rect2.centering();
2724         rect3.centering().shift(100,0);
2725         //色の設定
2726         col1.set(0.1);
2727         col2.set(0.5);
2728         col3.set(1.0);
2729         sampleA.rect(rect1,col1);
2730         sampleA.rect(rect2,col2);
2731         sampleA.rect(rect3,col3);
2732         sampleA.flip();
2733         while(!Input::get(Keyboard::spc));
2734 }
2735 }}}
2736
2737 !Canvas::setGammaValue()
2738 他の多くの描画ライブラリ同様、CRTの各画素における輝度レベルとPsychlopsで指定する輝度レベルは線形の関係を維持しません。
2739 これを簡易的に補正するために、R,G,Bの各色チャンネルについてGamma関数を用いた補正を行う方法が広く用いられています。
2740 Psychlopsでは、このGamma関数の係数を指定することで、Gamma補正された色を表示することが可能です。
2741 このための命令として、setGammaValue()命令があります。
2742
2743 |!Canvas::setGammaValue()|setGammaValue(double Cr, double Cg, double Cb)|この命令以降実行されるPsychlops::Colorに対してGamma補正を自動的に行います|
2744 |~|~|Cr: R(赤)チャンネルのガンマ係数|
2745 |~|~|Cg: G(緑)チャンネルのガンマ係数|
2746 |~|~|Cb: B(青)チャンネルのガンマ係数|
2747
2748 コード例は[[ガンマ補正|Tips: ガンマ補正]]を参照ください。
2749 </pre>
2750 </div>
2751 <div title="2.2.6 画面上に縞を描画する-Rectangleクラス2-" modifier="Psychlops_Admin" modified="200709262314" created="200708242023" changecount="82">
2752 <pre>2.2.1節から2.2.5節まで基本的な描画記法を説明してきました。
2753 この節ではここまでのまとめとして、[[Psychlops::Rectangle]]型を使って「縞」を描画します。
2754
2755 ^^ここは、ステップが多いので、かなりかみ砕いた解説をしています。縞の描画になれた方は使用する関数の説明とプログラム例のみをざっとご覧になられることをおすすめいたします。^^
2756
2757 !縞を描いてみようStep1~グラデーションの四角形の描画~
2758 これから[[Psychlops::Rectangle]]型を用いて黒(0.0)と白(1.0)の縞を作ります。
2759
2760 どういう風に縞を作成するか想像してみてください。
2761 白と黒の四角形を並べて描画するのでしょうか。
2762 [img[image/縞1.png]]
2763 しかし、これでは、単純な白と黒の繰り返しの縞しか描くことができません。
2764
2765 もう少し複雑な縞を描くためにできそうなことを順に整理してみましょう。
2766 #四角形(w × h)を描画する
2767 #四角形内の色を変化させる
2768 #四角形の位置を移動させる
2769
2770 前の2つはこれまでに説明した[[Canvas::rect()]]命令を使えばできそうです。実際にはこの節では[[Canvas::rect()]]命令の代わりに''Rectangle::draw()''命令を用います。この命令は[[Canvas::rect()]]命令と全く同じ効果を持っているので、あなたの好みでどちらを使用してもかまいません。
2771 四角形の位置を移動させるには、''Rectangle::centering()'',''Rectangle::shift()''命令を使います。
2772 以下でまずこれらの命令の書式と使い方を簡単な例とともに説明します。
2773
2774 !!Rectangle::centering()
2775 [[Psychlops::Rectangle]]型の図形を移動します。
2776 [img[image/rect_centering().png]]
2777 座標を指定する場合、その座標に図形の中心が設定されます。
2778 |!Rectangle::centering()|centering()|[[Psychlops::Rectangle]] rectを画面中心に移動します|
2779 |~|centering(double x, double y)|[[Psychlops::Rectangle]] rectを指定した座標(x,y)に移動します|
2780 |~|~|double x:移動する図形のx座標を指定|
2781 |~|~|double y:移動する図形のy座標を指定|
2782 |~|centering([[Psychlops::Point]] po)|[[Psychlops::Rectangle]] rectを指定したpoint座標(x,y)に移動します|
2783 |~|~|[[Psychlops::Point]] po:移動する図形の(x,y)座標を指定|
2784 |~|centering(Rectangle rect)|[[Psychlops::Rectangle]] rectを指定した[[Psychlops::Rectangle]] rectの中心座標(x,y)に移動します|
2785 |~|~|[[Psychlops::Rectangle]] rect :移動する図形の中心座標となる[[Psychlops::Rectangle]] rectを指定|
2786
2787 !!Rectangle::shift()
2788 [[Psychlops::Rectangle]]型の図形を移動します。
2789 元の座標(x,y)を基点に入力値(h,v)だけ移動します。
2790 |!Rectangle::shift()|shift(double h, double v)|座標(X,Y)を(h,v)だけ移動します|
2791 |~|~|double h:入力値分、右水平方向に移動|
2792 |~|~|double v:入力値分、下垂直方向に移動|
2793 [img[image/rect_shift().png]]
2794
2795 !!!Rectangle::centering()とRectangle::shift()の書き方
2796 200×100の四角形を中心座標から(-100,-200)だけ移動します。
2797 {{{
2798 &lt;例1&gt;
2799 #include &lt;psychlops.h&gt;
2800 using namespace Psychlops;
2801
2802 Psychlops::Rectangle rect1;
2803
2804 void psychlops_main() {
2805
2806         Canvas sampleA(Canvas::fullscreen);
2807         rect1.set(200,100);
2808         rect1.centering().shift(-100,-200);
2809         sampleA.rect(rect1,Color::white);
2810         sampleA.flip();
2811         while(!Input::get(Keyboard::spc));
2812 }
2813 }}}
2814
2815 !!Rectangle::draw()
2816 今まで図を描画する時、[[Psychlops::Canvas]]型の命令を使用しました。
2817 今回は描画命令にRectangle::draw()命令を使用します。
2818 |!Rectangle::draw()|draw()|対象の[[Psychlops::Rectangle]]型を描画します|
2819 |~|draw([[Psychlops::Color]] col)|対象の[[Psychlops::Rectangle]]型を[[Psychlops::Color]] colで塗りつぶし描画します|
2820 |~|~|[[Psychlops::Color]] col:描画する四角形の色を指定|
2821
2822 !!Rectangle::draw()の書き方
2823 &lt;例1&gt;の四角形の色をRectangle::draw()命令を使用して「cyan」に塗りつぶします。
2824 {{{
2825 &lt;例2&gt;
2826 #include &lt;psychlops.h&gt;
2827 using namespace Psychlops;
2828
2829 Psychlops::Rectangle rect1;
2830
2831 void psychlops_main() {
2832
2833         Canvas sampleA(Canvas::fullscreen);
2834         rect1.set(200,100);
2835         rect1.centering().shift(-100,-200);
2836         rect1.draw(Color::cyan);
2837         sampleA.flip();
2838         while(!Input::get(Keyboard::spc));
2839 }
2840 }}}
2841
2842 !!実際に縞を描いてみましょう
2843 とりあえず四角形(w × h)を(200 × 100)の大きさに決めます。
2844 まず黒(0.0)から白(1.0)へ左から右にグラデーションする縞を描画してみましょう。
2845
2846 {{{
2847 &lt;例3&gt;
2848 #include &lt;psychlops.h&gt;
2849 using namespace Psychlops;
2850
2851 Psychlops::Rectangle rect1;
2852 Psychlops::Color col1;
2853
2854 void psychlops_main() {
2855
2856         Canvas sampleA(Canvas::fullscreen);
2857         rect1.set(1,100);
2858         for(int i=0;i&lt;200;i++){
2859                 col1.set((double)i/200);
2860                 rect1.centering().shift((double)-1/2*200+i,0);
2861                 rect1.draw(col1);
2862         }
2863         sampleA.flip();
2864         while(!Input::get(Keyboard::spc));
2865 }  
2866 }}}
2867
2868 このプログラムでは、200個の1 x 100ピクセルのRectangleを描画しています。
2869 左から数えてi個目の四角形の色をi / 200とすると、一番左i = 0では色は0.0(黒)となって、右に行くに従ってRectangleの輝度が大きくなるように設定できます。
2870 描画する場所は最終的な縞の中心が画面中央(Xc,Yc)に来るように計算します。
2871 [img[image/縞2.png]]
2872 描画する縞は200 × 100ピクセルなので、一番左のRectangleの中心位置は(Xc - 100,Yc)、一番右の位置は(Xc + 100,Yc)です。
2873 一般に幅wの縞を描くときには、一番左のRectangleの位置は(Xc - 1 / 2 * w ,Yc)、一番右の位置は(Xc + 1/2 * w ,Yc)になります。
2874
2875 次にこの一番左のRectangleの位置を使って、i番目のRectangleの座標位置を考えてみます。
2876 i番目のRectangleは一番左のRectangleから見て+ i だけx座標を移動した位置にあるはずです。
2877 従って、i番目の座標は(Xc -1 / 2 * w + i ,Yc)となります。
2878
2879 !!縞を描いてみようStep2~正弦波で縞を作成する~
2880 Step1で黒から白のグラデーション(色変化)の縞を作成しました。
2881 次にもう少し複雑な正弦縞を作成してみましょう。
2882 Step1から使えそうな要素を抜き出しましょう。
2883 *描画する四角形(w × h):(1 × hピクセルの四角形をw回描画する)
2884 *座標:Xc - 1 / 2 * w + i ,Yc
2885 [img[image/正弦波1.png]]
2886 {{{
2887 * 正弦波の一般式
2888 }}}
2889 [img[image/正弦波の一般式.png]]
2890 A:振幅,λ:波長,θ:位相,x:i
2891
2892 但しy&gt;0であるので(扱うのが色のため)
2893 [img[image/正弦波2.png]]
2894 [img[image/色の式.png]]
2895 となる。
2896
2897 上記式に値をあてはめていきます。
2898 A:振動をC:コントラストに置き換えます。
2899 Michelsonコントラストの定義式を用います。
2900 [img[image/contrastEq.png]]
2901 C=2A / 2Lmean = A / Lmean
2902 A=C * Lmean
2903 今回は位相0の縞を描画するのでθ = 0
2904 コントラスト:1.0
2905 波長λ:60
2906 平均輝度(Lmean): 0.5の縞を描画します。
2907
2908 y=Asin{ ( 2π * x / λ ) + θ } + Lmean
2909 y=( C * Lmean ) sin ( 2π * i / λ ) + Lmean
2910 y=Lmean { C sin ( 2π * i / λ ) + 1 }
2911 i番目の色=Lmean { C sin ( 2π * i / λ ) + 1 } 
2912 {{{
2913 i番目の色=1.0 { 0.5 sin ( ( 2π * i / 60 ) + 1 }
2914 }}}
2915 これで色が求まりました。
2916 実際にコードを書いてみましょう。  
2917                                                                                                                            
2918 {{{
2919 &lt;例4&gt;
2920 #include &lt;psychlops.h&gt;
2921 using namespace Psychlops;
2922
2923 Psychlops::Rectangle rect1;
2924 Psychlops::Color col1;
2925 double width=200;
2926 double height=100;
2927 double lambda=60;
2928 double lmean=0.5, contrast=1.0, theta=0;
2929
2930 void psychlops_main() {
2931
2932         Canvas sampleA(Canvas::fullscreen);
2933         rect1.set(1,height);
2934         for(int i=0;i&lt;width;i++){
2935                 col1.set(lmean*((contrast*sin((2*PI*i/lambda)+theta))+1));
2936                 rect1.centering().shift((double)-1/2*width+i,0);
2937                 rect1.draw(col1);
2938         }
2939         sampleA.flip();
2940         while(!Input::get(Keyboard::spc));
2941
2942 }}}
2943
2944 !!縞を描いてみようStep3~位相が異なる縞~
2945 Step2では位相0の縞を描画しました。
2946 位相に値を入れて位相90の縞を描画しましょう。
2947 わかりやすいように位相0の下に並べて描画します。
2948 {{{
2949 &lt;例5&gt;
2950 #include &lt;psychlops.h&gt;
2951 using namespace Psychlops;
2952
2953 Psychlops::Rectangle rect1,rect2;
2954 Psychlops::Color col1,col2;
2955 double width=200;
2956 double height=100;
2957 double lambda=60;
2958 double lmean=0.5, contrast=1.0, theta1=0, theta2=90;
2959
2960 void psychlops_main() {
2961
2962         Canvas sampleA(Canvas::fullscreen);
2963         rect1.set(1,height);
2964         rect2.set(rect1);
2965         for(int i=0;i&lt;width;i++){
2966                 //位相=0の縞
2967                 col1.set(lmean*((contrast*sin((2*PI*i/lambda)+theta1))+1));
2968                 rect1.centering().shift((double)-1/2*width+i,0);
2969                 rect1.draw(col1);
2970                 //位相=90の縞
2971                 col2.set(lmean*((contrast*sin((2*PI*i/lambda)+theta2))+1));
2972                 rect2.centering().shift((double)-1/2*width+i,150);
2973                 rect2.draw(col2);
2974         }
2975         sampleA.flip();
2976         while(!Input::get(Keyboard::spc));
2977
2978 }}}
2979 !縞を描いてみようStep4~矩形波の縞を作成する~
2980 正弦波の縞の場合、白と黒の境目が曖昧です。
2981 境目をはっきり描画したい場合、矩形波の縞を描画します。
2982 ですが実際には正弦波の式を使います。
2983 {{{
2984 * 正弦波の一般式
2985 }}}
2986 [img[image/正弦波の一般式.png]]
2987 y = 0を軸に正弦波が展開します。
2988 なのでこれを利用し、y &gt; 0の場合はLmax , y &lt; 0の場合はLminを描画するという判定分を追加することで矩形波の縞が描画できます。
2989
2990 コントラスト、Lmeanを使ってLmax,Lmeanの値を求めておくと、
2991 Lmax = Lmean + Lmean * コントラストが求まります。
2992 Lmin = Lmean - Lmean * コントラストが求まります。
2993 これらを使用してコードを書いてみましょう。
2994 {{{
2995 &lt;例6&gt;
2996 #include &lt;psychlops.h&gt;
2997 using namespace Psychlops;
2998
2999 Psychlops::Rectangle rect1;
3000 double col1;
3001 double width=200;
3002 double height=100;
3003 double lambda=60;
3004 double lmean=0.5, contrast=1.0, theta=0;
3005
3006 void psychlops_main() {
3007
3008         Canvas sampleA(Canvas::fullscreen);
3009         rect1.set(1,height);
3010         for(int i=0;i&lt;width;i++){
3011                 col1=(lmean*contrast*sin((2*PI*i/lambda)+theta));
3012                 if(col1&gt;0) col1=lmean+contrast*lmean;
3013                         else   col1=lmean-contrast*lmean;
3014                 rect1.centering().shift((double)-1/2*width+i,0);
3015                 rect1.draw(col1);
3016         }
3017         sampleA.flip();
3018         while(!Input::get(Keyboard::spc));
3019
3020 }}}
3021 !!縞を描いてみようStep5~縞の角度を変えてみる~
3022 今まで縦(90°)の縞を描画してきました。
3023 次は横(0°)の縞を描画してみましょう。
3024 w × hの縦の縞を描画するために1 × hの四角形をw個並べました。
3025 横の縞を描画するにはどうしたらいいか。
3026 w × hの横の縞を描画するためにw × 1の四角形をh個並べたら作れないでしょうか。
3027
3028 まずわかっていることを書いてみます。
3029 *描画する四角形( w × h ) : ( w × 1の四角形をh回描画する)
3030 *座標X:Xc - 1 / 2 * w + i 
3031 *色:y = A sin { ( 2π * x / λ ) + θ } + Lmean
3032
3033 今回はX座標ではなくY座標を指定する必要があります。
3034 よって以下の通りになります。
3035 *座標Y:Yc - 1 / 2 * h + j (考え方はX座標と同じなので[[Step1|2.2.6 画面上に縞を描画する-Rectangleクラス2-]]参照のこと)
3036
3037 これらから実際にコードを書いて試してみましょう。
3038 {{{
3039 &lt;例7&gt;
3040 #include &lt;psychlops.h&gt;
3041 using namespace Psychlops;
3042
3043 Psychlops::Rectangle rect1;
3044 Psychlops::Color col1;
3045 double width=200;
3046 double height=100;
3047 double lambda=60;
3048 double lmean=0.5, contrast=1.0, theta=0;
3049
3050 void psychlops_main() {
3051
3052         Canvas sampleA(Canvas::fullscreen);
3053         rect1.set(width,1);
3054         for(int j=0;j&lt;height;j++){
3055                 col1.set(lmean*((contrast*sin((2*PI*j/lambda)+theta))+1));
3056                 rect1.centering().shift(0,(double)-1/2*height+j);
3057                 rect1.draw(col1);
3058         }
3059         sampleA.flip();
3060         while(!Input::get(Keyboard::spc));
3061
3062 }}}
3063 確認できたでしょうか。
3064 矩形波の横の縞はStep4を参考に自分で試してみてください。</pre>
3065 </div>
3066 <div title="2.2.7 画面上に縞を描画する-Pointクラス2-" modifier="YourName" modified="200802191327" created="200709252135" changecount="11">
3067 <pre>[[2.2.6節|2.2.6 画面上に縞を描画する-Rectangleクラス2-]]では[[Psychlops::Rectangle]]型を用いて縞を描画しました。
3068 この節では[[Psychlops::Point]]型を用いて縞を描画します。
3069
3070 四角形(w × h)を描画する際、[[Psychlops::Rectangle]]型では1 × hの四角形をw個
3071 もしくはw × 1の四角形を h個並べて描画しました。
3072 [[Psychlops::Point]]型ではw × hの点を並べて描画します。
3073
3074 !縞を描いてみようStep1~正弦縞を描画する~
3075 はじめに一番簡単な垂直方位の縞を書いてみましょう。
3076
3077 まずわかっていることを書いてみます。
3078 *描画する縞のサイズ:w × h (1ピクセルの点をw×h回描画する)
3079 *縞の中心座標X:Xc - 1 / 2 * w + i
3080 *縞の中心座標Y:Yc - 1 / 2 * h + j
3081 *縞の式:L(x,y) = Lmean*(1+contrast*sin { ( 2π * x / λ ) + θ } )
3082
3083 式のパラメータは以下の通りとします。
3084 |位相θ:0|
3085 |コントラスト:0.5|
3086 |波長λ:60|
3087 |平均輝度(Lmean): 0.25|
3088
3089 では以上をふまえてコードを書いてみましょう。
3090 {{{
3091 &lt;例1&gt;
3092 #include &lt;psychlops.h&gt;
3093 using namespace Psychlops;
3094
3095 Psychlops::Point p1;
3096 Psychlops::Color col1;
3097 double width=200;
3098 double height=100;
3099 double lambda=60;
3100 double lmean=0.25, contrast=0.5, theta=0;
3101
3102 void psychlops_main() {
3103
3104         Canvas sampleA(Canvas::fullscreen);
3105         for(int i=0;i&lt;width;i++){
3106                 for(int j=0;j&lt;height;j++){
3107                         col1.set(lmean*((contrast*sin((2*PI*i/lambda)+theta))+1));
3108                         p1.centering().shift((double)-1/2*width+i,(double)-1/2*height+j);
3109                         sampleA.pix(p1,col1);
3110                 }
3111         }
3112         sampleA.flip();
3113         while(!Input::get(Keyboard::spc));
3114 }
3115 }}}
3116
3117 [img[image/grating01.png]]
3118
3119 !縞を描いてみようStep2~斜めの縞を描画する~
3120 [[Psychlops::Rectangle]]型を使った書き方では方位90°の縞(縦)と0°の縞(横)のみを描画しました。
3121 Pointクラスを使った縞の描画では、これ以外の方位の縞も描画することができます。
3122 ここでは回転を使って方位45°の縞(斜め)を描画します。
3123
3124 !!図形の回転
3125 点(i, j)をθラジアン回転させて(xx,yy)に変換する式は以下の通りです。:
3126 {{{
3127 xx = cosθ  * i - sinθ * j
3128 yy = sinθ * i + cosθ * j
3129 }}}
3130 この式を用いて、斜めの方位を持つ縞を描画してみましょう。
3131 θはラジアン単位です。度数法d(°)で値を指定したいときは下の式で変換した値を用います。
3132 {{{
3133 θ ( rad ) = d° * π / 180°
3134 }}}
3135
3136 以上をから&lt;例1&gt;を修正して方位が45°の縞を描画してみましょう。
3137 {{{
3138 &lt;例2&gt;
3139 #include &lt;psychlops.h&gt;
3140 using namespace Psychlops;
3141
3142 Psychlops::Point p1;
3143 Psychlops::Color col1;
3144 double width=200;
3145 double height=100;
3146 double lambda=60;
3147 double lmean=0.5, contrast=0.5, theta=0;
3148 double xx,yy;
3149 double orientation;
3150
3151 void psychlops_main() {
3152
3153         Canvas sampleA(Canvas::fullscreen);
3154         orientation=45*PI/180;
3155         for(int i=0;i&lt;width;i++){
3156                 for(int j=0;j&lt;height;j++){
3157                         //回転
3158                         xx=cos(orientation)*i-sin(orientation)*j;
3159                         yy=sin(orientation)*i+cos(orientation)*j;
3160                         
3161                         col1.set(lmean*((contrast*sin((2*PI*yy/lambda)+theta))+1));
3162                         p1.centering().shift((double)-1/2*width+i,(double)-1/2*height+j);
3163                         sampleA.pix(p1,col1);
3164                 }
3165         }
3166         sampleA.flip();
3167         while(!Input::get(Keyboard::spc));
3168 }
3169 }}}
3170
3171 [img[image/grating45.png]]
3172
3173 !縞を描いてみようStep3~交差した縞(Plaid)を描画する~
3174 では次にStep2で作成した45°の縞に135°の縞を交差させた縞を描画してみましょう。
3175
3176 |位相θ:0|
3177 |コントラスト:0.5|
3178 |波長λ:60|
3179 |平均輝度(Lmean): 0.25|
3180 |方位:45°|
3181
3182 交差する縞の情報を設定します。
3183 |位相θ:0|
3184 |コントラスト:0.25|
3185 |波長λ:45|
3186 |平均輝度(Lmean): 0.25|
3187 |方位:135°|
3188
3189 {{{
3190 &lt;例3&gt;
3191 #include &lt;psychlops.h&gt;
3192 using namespace Psychlops;
3193
3194 Psychlops::Point p1;
3195 Psychlops::Color col1;
3196 double width=200;
3197 double height=100;
3198 double lambda=60;
3199 double lmean=0.25, contrast=0.5, theta=0;
3200 double xx,yy;
3201 double orientation=45*PI/180;
3202         
3203 //交差する縞の情報
3204 double lambda2=45;
3205 double lmean2=0.25, contrast2=0.25, theta2=0;
3206 double xx2,yy2;
3207 double orientation2=135*PI/180;
3208
3209 void psychlops_main() {
3210
3211         Canvas sampleA(Canvas::fullscreen);
3212         
3213         for(int i=0;i&lt;width;i++){
3214                 for(int j=0;j&lt;height;j++){
3215                         //回転
3216                         xx=cos(orientation)*i-sin(orientation)*j;
3217                         yy=sin(orientation)*i+cos(orientation)*j;
3218                         xx2=cos(orientation2)*i-sin(orientation2)*j;
3219                         yy2=sin(orientation2)*i+cos(orientation2)*j;
3220                         
3221                         col1.set( lmean*(1+contrast*sin(theta+(2*PI*yy/lambda)))+
3222                                                  lmean2*(1+contrast2*sin(theta2+(2*PI*yy2/lambda2)))
3223                                           );
3224                         p1.centering().shift((double)-1/2*width+i,(double)-1/2*height+j);
3225                         sampleA.pix(p1,col1);
3226                 }
3227         }
3228         sampleA.flip();
3229         while(!Input::get(Keyboard::spc));
3230 }
3231 }}}
3232
3233 [img[image/plaid45_135.png]]
3234
3235 難しいと思われた方は、orientationやcontrast等のパラメタを変えて実行してみるとわかりやすいかもしれません。</pre>
3236 </div>
3237 <div title="2.2.8 運動する図形を描画する-Rectangleクラス3-" modifier="Psychlops_DevelopperG" modified="200908190231" created="200709252136" changecount="4">
3238 <pre>この節では[[2.2.6節|2.2.6 画面上に縞を描画する-Rectangleクラス2-]]で作成した縞を動かします。
3239
3240 [[2.2.6節|2.2.6 画面上に縞を描画する-Rectangleクラス2-]]では、位相を変えてみることで縞の位置を移動させました。
3241 「縞を動かす」ことは一見難しそうですが位相を変化させて描画することで
3242 縞が運動しているように見えます。
3243
3244 ちょっと復習しましょう。
3245 下のコードを実行すると位相 = 0と位相 = 90の四角形がそれぞれ描画されます。
3246 {{{
3247 #include &lt;psychlops.h&gt;
3248 using namespace Psychlops;
3249
3250 Psychlops::Rectangle rect1,rect2;
3251 Psychlops::Color col1,col2;
3252 double width=200;
3253 double height=100;
3254 double lambda=60;
3255 double lmean=0.5, contrast=1.0, theta1=0, theta2=90;
3256
3257 void psychlops_main() {
3258
3259         Canvas sampleA(Canvas::fullscreen);
3260         rect1.set(1,height);
3261         rect2.set(rect1);
3262         for(int i=0;i&lt;width;i++){
3263                 //位相=0の縞
3264                 col1.set(lmean*((contrast*sin((2*PI*i/lambda)+theta1))+1));
3265                 rect1.centering().shift((double)-1/2*width+i,0);
3266                 rect1.draw(col1);
3267                 //位相=90の縞
3268                 col2.set(lmean*((contrast*sin((2*PI*i/lambda)+theta2))+1));
3269                 rect2.centering().shift((double)-1/2*width+i,150);
3270                 rect2.draw(col2);
3271         }
3272         sampleA.flip();
3273         while(!Input::get(Keyboard::spc));
3274
3275 }}}
3276 ずれているのを確認できたでしょうか。
3277 ここで、描画する毎にθの値を変更して入力すれば四角形が常に移動しているように見えます。
3278 実際に試してみましょう。
3279 {{{
3280 &lt;例1&gt;
3281 #include &lt;psychlops.h&gt;
3282 using namespace Psychlops;
3283
3284 Psychlops::Rectangle rect1;
3285 Psychlops::Color col1;
3286 double width=200;
3287 double height=100;
3288 double lambda=60;
3289 double lmean=0.5, contrast=1.0, theta=0;
3290 double speed=PI/90;
3291
3292 void psychlops_main() {
3293
3294         Canvas sampleA(Canvas::fullscreen);
3295         while(!Input::get(Keyboard::spc)){
3296                 rect1.set(1,height);
3297                 for(int i=0;i&lt;width;i++){
3298                         col1.set(lmean*((contrast*sin((2*PI*i/lambda)+theta))+1));
3299                         rect1.centering().shift((double)-1/2*width+i,0);
3300                         rect1.draw(col1);
3301                 }
3302         sampleA.flip();
3303         theta+=speed;
3304         }
3305
3306 }}}
3307 &lt;例2&gt;は&lt;例1&gt;の1箇所だけを変えたコードです。
3308 どこが変わっているのか見つけてみてください。
3309 {{{
3310 &lt;例2&gt;
3311 #include &lt;psychlops.h&gt;
3312 using namespace Psychlops;
3313
3314 Psychlops::Rectangle rect1;
3315 Psychlops::Color col1;
3316 double width=200;
3317 double height=100;
3318 double lambda=60;
3319 double lmean=0.5, contrast=1.0, theta=0;
3320 double speed=PI/90;
3321
3322 void psychlops_main() {
3323
3324         Canvas sampleA(Canvas::fullscreen);
3325         while(!Input::get(Keyboard::spc)){
3326                 rect1.set(1,height);
3327                 for(int i=0;i&lt;width;i++){
3328                         col1.set(lmean*((contrast*sin((2*PI*i/lambda)+theta))+1));
3329                         rect1.centering().shift((double)-1/2*width+i,0);
3330                         rect1.draw(col1);
3331                 }
3332         sampleA.flip(6);
3333         theta+=speed;
3334         }
3335
3336 }}}
3337 答えはflip()です。&lt;例1&gt;ではflip(),&lt;例2&gt;ではflip(6)になっています。
3338 実行すると運動の速度が随分変化していることに気づくでしょう。
3339 flip(6)では6回リフレッシュが行われるまで描画したままの状態で待ち、次の垂直同期で画面が入れ替わります。
3340 60Hzの画面でflip(6)を実行する場合、100ミリsec描画したままの状態を維持できます。
3341 このようにして、flip(int waitframe)命令を活用すると運動刺激のリフレッシュレート(各フレームの長さ)や細かい描画の待ち時間を設定できます。
3342
3343 flip(int waitframe)について詳しい説明は[[2.1.2節|2.1.2 Canvasの基本構造と操作]]を参照してください。</pre>
3344 </div>
3345 <div title="2.3 文字列の描画" modifier="Psychlops_DevelopperG" modified="200912010809" created="200909300449" changecount="25">
3346 <pre>[[2.1.3節|2.1.3 Canvasのメンバ取得と変数の表示]]では簡単な文字列の表示について説明しました。
3347 この節ではより本格的な2バイト文字を用いた文字列の描画について取り扱います。
3348
3349 Psychlopsで2バイト文字を描画するには2つの方法があります。
3350 いずれの方法もCanvas::msg()命令を使用します。
3351
3352 |!Canvas::msg()|msg(char* string,double x,double y,[[Psychlops::Color]] col)|&gt;|char* stringを座標(x,y)に指定色で表示します|
3353 |~|~|schar* string|画面表示をする文字列を指定|
3354 |~|~|double x|画面表示をする座標(x)を指定|
3355 |~|~|double y|画面表示をする座標(y)を指定|
3356 |~|~|[[Psychlops::Color]] col|描画する点の色を指定(設定しない場合、白が設定される)|
3357 |~|msg(string str,double x,double y,[[Psychlops::Color]] col)|&gt;|string strを座標(x,y)に指定色で表示します|
3358 |~|~|string str|画面表示をする文字列を指定|
3359 |~|~|double x|画面表示をする座標(x)を指定|
3360 |~|~|double y|画面表示をする座標(y)を指定|
3361 |~|~|[[Psychlops::Color]] col|描画する点の色を指定(設定しない場合、白が設定される)|
3362
3363 2つの方法とはこの命令を単体で使う方法とLettersと呼ばれるクラスを用いた方法です。
3364 前者の方法は簡便ですが、フォント指定などはできず、それほど美しく表示することはできません。
3365 後者の方法はフォント指定など文字列表示の方法を細かく指定できますが、文字列を表示する前にメモリ領域に文字列を描画しておく必要があります。
3366 以下の節でそれぞれの方法を説明します。
3367
3368 [[2.3.1 Canvas::msg()命令単体での文字列描画]]
3369 [[2.3.2 Lettersクラスを用いた文字列描画]]
3370 また、これらのクラスの詳細については[[3.5節|3.5 文字列のレンダリングの仕様と関連情報]]をご覧ください。
3371
3372 </pre>
3373 </div>
3374 <div title="2.3.1 Canvas::msg()命令単体での文字列描画" modifier="Psychlops_DevelopperG" modified="200910260740" created="200910020306" changecount="13">
3375 <pre>!!Canvas::msg()命令単体での文字列描画
3376 まず、[[2.1.3節|2.1.3 Canvasのメンバ取得と変数の表示]]で説明したCanvas::msg()命令をそのまま用いるやり方について説明します。
3377 基本的には[[2.1.3節|2.1.3 Canvasのメンバ取得と変数の表示]]とほとんど同じですが、これまではここにアルファベットや記号、いわゆる1バイト文字でできた文字列を代入してきました。
3378 {{{
3379 &lt;例0&gt;
3380 #include &lt;psychlops.h&gt;
3381 using namespace Psychlops;
3382
3383 void psychlops_main() {
3384         Canvas sampleA(Canvas::fullscreen);
3385
3386         sampleA.msg(&quot;A test of Letter Drawing&quot;, SampleA.getCenter());
3387
3388         sampleA.flip();
3389         while(!Input::get(Keyboard::spc));
3390 }
3391 }}}
3392
3393 ここで、表示したい文字列がひらがな・カタカナなどの2バイト文字とか全角文字と呼ばれる文字でできている場合は「”」(ダブルクォーテーション)の前に大文字のLを入れます。以下の例を見てください。
3394
3395 {{{
3396 &lt;例1&gt;
3397 #include &lt;psychlops.h&gt;
3398 using namespace Psychlops;
3399
3400 void psychlops_main() {
3401         Canvas sampleA(Canvas::fullscreen);
3402
3403         sampleA.msg(L&quot;日本語の表示&quot;, SampleA.getCenter());
3404
3405         sampleA.flip();
3406         while(!Input::get(Keyboard::spc));
3407 }
3408 }}}
3409
3410 {{{
3411         sampleA.msg(L&quot;日本語の表示&quot;, SampleA.getCenter());
3412 }}}
3413 の命令を見てわかるとおり、&quot;日本語の表示&quot; という文字列のまえに大文字のLが入っています。
3414
3415 !!文字変数を使った代入
3416 さらにこの部分に文字変数を入れたい場合はどうすればよいでしょうか。
3417 普通のプログラムで文字列によく使うstring, char型を使ってもコンパイルできません。
3418 このような場合はstring型を拡張したwstring型の変数を宣言してCanvas::msg()命令に代入します。
3419
3420
3421 |!Canvas::msg()|msg(std::wstring string,double x,double y,[[Psychlops::Color]] col)|char* stringを座標(x,y)に指定色で表示します|
3422 |~|~|string str:画面表示をする文字列を指定|
3423 |~|~|double x:画面表示をする座標(x)を指定|
3424 |~|~|double y:画面表示をする座標(y)を指定|
3425 |~|~|[[Psychlops::Color]] col:描画する点の色を指定(設定しない場合、白が設定される)|
3426
3427 {{{
3428 &lt;例2&gt;
3429 #include &lt;psychlops.h&gt;
3430 using namespace Psychlops;
3431
3432 void psychlops_main() {
3433
3434         Canvas sampleA(Canvas::fullscreen);
3435         std::wstring wstr;
3436         wstr=L&quot;日本語の表示&quot;;
3437         sampleA.msg(wstr, SampleA.getCenter());
3438         sampleA.flip();
3439
3440         while(!Input::get(Keyboard::spc));
3441 }
3442 }}}
3443
3444 ワイド文字列についてはいくつか知っておいた方が便利なことがあります。これらの詳細な情報については[[3.5節|3.5 文字列のレンダリングの仕様と関連情報]]をご覧ください。
3445
3446 </pre>
3447 </div>
3448 <div title="2.3.2 Lettersクラスを用いた文字列描画" modifier="Psychlops_DevelopperG" modified="200910260748" created="200910020318" changecount="34">
3449 <pre>Canvas命令単体での文字列描画は簡単ですが、フォントがぎざぎざしていたりしてあまり美しくは表示されません。
3450 また、もう少し大きな文字を表示したい場合やイタリック体で文字を表示したい場合にはこの方法では対応できません。
3451 この様な複雑な指定をして文字列を描画したい場合には事前にメモリに文字列を描画して、これを画面にコピーするという方法を行います。本や大量の部数の文書を作るときに、活字を組んで版をつくり、インクで紙に刷るのと同じ事です。
3452 この事前のメモリ領域への文字列描画のためのクラスとして、[[Letters]],  [[Font]]クラスが用意されています。
3453
3454 この方法による文字列描画の流れを簡単に説明すると以下のようになります
3455 # [[Font]]クラスを用いてフォントを指定する
3456 # [[Letters]]クラスを用いてメモリに文字列を描画
3457 # [[Canvas::msg()命令を用いてメモリ内の文字列を画面に描画]]
3458 以下でそれぞれの段階の具体的な方法について説明します。
3459
3460 ![[Font]]クラスを用いたフォントの指定
3461 Psychlopsにおけるフォントの指定は、[[Font]]クラスの変数を宣言し、これを引数として代入することによって行います。
3462 Fontの宣言は以下の書式に従って行います。
3463 |!Fontの宣言|font(double size, int weight, Style style, std::wstring family);|!フォントを指定します。|&gt;|
3464 |~|~|double size|フォントサイズ(pixel)を指定|
3465 |~|~|int weight|線幅を指定 &lt;br&gt;  (normal_style (400) / bold (700) : 省略可能)|
3466 |~|~|Style style|斜体の有無を指定 &lt;br&gt; (normal_style / italic : 省略可能)|
3467 |~|~|std::wstring family|フォント名を指定 &lt;br&gt;      (省略可能)|
3468
3469 実際の例をいくつかあげてみましょう。
3470 {{{
3471 Font font1(24, Font::bold, Font::italic, L&quot;ArialMT&quot;); 
3472 }}}
3473 {{{
3474 Font font2(12, Font::bold, Font::normal_style, L&quot;メイリオ&quot;);
3475 }}}
3476 {{{
3477 Font font3(18, Font::normal_weight, Font::normal_style, L&quot;ヒラギノ角ゴ Pro&quot;);
3478 }}}
3479
3480 第2引数は文字の太さ、第3引数で斜体の有無を選択します。
3481 第4引数はフォントの名前を2重引用符(&quot;)でくくって指定します。
3482 これらは省略可能です。
3483
3484 Canvas.msg関数などで使われるデフォルトフォントを変更するには、Font::default_font変数に新しいフォント構造体を指定します。
3485 {{{
3486 Font::default_font = Font(L&quot;Arial&quot;, 20);
3487 }}}
3488
3489 フォントはインストールされていなければその環境標準のものになります。一般的にWindowsとMacOSXでは同じ名前のフォントはありませんので、注意してください。
3490
3491 ![[Letters]]クラスによる文字列のプリレンダリングと描画
3492 次のステップでは、上で用意したフォントを使って、文字列の元版(レンダリング済みの文字列、といいます)をメモリ内に作成します。
3493 これにはLettersクラスを使用します。
3494
3495 |!Lettersの宣言|Letters(std::wstring str, Font font );|!文字列を指定されたフォントでレンダリングします。|&gt;|
3496 |~|~|std::wstring str|描画する文字列を指定|
3497 |~|~|Font font|フォントを指定 &lt;br&gt;      (省略可能)|
3498
3499 ここでも実際の例をいくつかあげてみましょう。
3500 {{{
3501 Letters let1(L&quot;Space Barを押すと文字が変わります&quot;, Font(20, Font::bold));
3502 }}}
3503 {{{
3504 Letters let2(L&quot;矢印キーを押してください。配置が変わります。&quot;);
3505 }}}
3506 {{{
3507 Letters let3(L&quot;Font \&quot;Arial\&quot; have only Latain characters. 日本語フォントが必要です.&quot;, font1);
3508 }}}
3509
3510 文字列の内容をプログラム内で変更したいときなど、宣言とは違うところで文字列をレンダリングしたいときには、Letters::string()命令を使います。
3511
3512 |!Letters::string()|string(std::wstring str);|!文字列を指定されたフォントでレンダリングします。|&gt;|
3513 |~|~|std::wstring str|描画する文字列を指定|
3514
3515 このとき、font指定はLetters::font()命令を使用して別に行う事になりますが、何も指定せずデフォルトのフォントでレンダリングする事もできます。
3516
3517 |!Letters::font()|font(Font font);|!文字列を指定されたフォントでレンダリングします。|&gt;|
3518 |~|~|std::wstring str|描画する文字列を指定|
3519 |~|~|Font font|フォントを指定 |
3520
3521 例えば上にあげた一つ目の例
3522 {{{
3523 Letters let1(L&quot;Space Barを押すと文字が変わります&quot;, Font(20, Font::bold));
3524 }}}
3525 をこれらの命令を使って書き直してみるとこの様になるでしょう。
3526 {{{
3527 Letters let1;
3528 let1.string(L&quot;Space Barを押すと文字が変わります&quot;);
3529 let1.font(Font(20, Font::bold));
3530 }}}
3531
3532
3533
3534 ! Canvas::msg()命令を用いてメモリ内の文字列を画面に描画
3535 ここまでの段階で、指定されたフォントを使って表示したい文字列の元版ができあがりました。実際に画面にこれを表示するにはCanvas::msg()命令を使います。
3536
3537 |!Canvas::msg()|msg(Letters let, double x, double y, Color col, align, algn );|!文字列を指定されたフォントでレンダリングします。|&gt;|
3538 |~|~|Letters let|描画するレンダリング済み文字列を指定|
3539 |~|~|double x|文字列描画の水平位置を指定|
3540 |~|~|double y|文字列描画の垂直位置を指定|
3541 |~|~|Color col|文字列の描画色を指定 &lt;br&gt;      (省略可能)|
3542 |~|~|align, algn|文字詰めの方法を指定 &lt;br&gt;      (省略可能)|
3543
3544 {{{
3545 Letters let1(L&quot;Space Barを押すと文字が変わります&quot;, Font(20, Font::bold));
3546 Display::msg(let1, 100, 100);
3547 }}}
3548
3549
3550 ! Letters::draw()命令を用いてメモリ内の文字列を画面に描画
3551 もう一つ同じ目的の命令としてLetters::draw()命令があります。
3552 LettersクラスにはRectangle型に対して使用できるcentering(), shift()などの命令が使えます。
3553 これらの命令を使って同じ文字列を異なる位置に簡単に描画することができます。
3554 |!Letters::centering()|centering(double x, double y)|!レンダリング済みの文字列の描画位置をその中央の座標で指定する。|&gt;|
3555 |~|~|double x|センタリングの水平位置を指定&lt;br&gt;      (省略可能)|
3556 |~|~|double y|センタリングの垂直位置を指定&lt;br&gt;      (省略可能)|
3557 |~|~|&gt;|&gt;|これらの値が省略されるときは、画面中央に描画位置が設定される|
3558
3559 |!Letters::shift()|shift(double x, double y)|!レンダリング済みの文字列の描画位置を現在の指定位置から移動する。|&gt;|
3560 |~|~|double x|水平方向の移動量を指定|
3561 |~|~|double y|垂直方向の移動量を指定|
3562
3563 |!Letters::draw()|draw(Color col)|!レンダリング済みの文字列を描画する|&gt;|
3564 |~|~|double x|水平方向の移動量を指定|
3565 |~|~|double y|垂直方向の移動量を指定|
3566 |~|~|Color col|文字列の描画色を指定 &lt;br&gt;      (省略可能)|
3567
3568 以下のように使います。
3569 {{{
3570 Letters let1(L&quot;Space Barを押すと文字が変わります&quot;, Font(20, Font::bold));
3571 let1.centering(Mouse::x, Mouse::y+30).draw(Color::green);
3572 }}}
3573
3574
3575 !まとめ
3576
3577 ここまでをまとめて、文字列描画の簡単なプログラム例を作ってみましょう。
3578 {{{
3579 #include &lt;psychlops.h&gt;
3580 using namespace Psychlops;
3581
3582 void psychlops_main() {
3583
3584         Canvas sampleA(Canvas::fullscreen); //描画用windowの取得
3585
3586         Letters let1(L&quot;Space Barを押すと文字が変わります&quot;, Font(20, Font::bold));
3587         Letters let2;
3588         let2.string(L&quot;文字が変わりました&quot;);
3589         let2.font(Font(20, Font::bold));
3590
3591         sampleA.clear(Color(0.0));
3592         sampleA.msg(let1, 100, 100);
3593         sampleA.flip();
3594         while(!Input::get(Keyboard::spc));
3595         
3596         sampleA.clear(Color(0.5));
3597         let2.centering().draw(Color::red);
3598         while(!Input::get(Keyboard::spc));
3599 }
3600
3601 }}}
3602 </pre>
3603 </div>
3604 <div title="3. 複雑な描画を行う1(オフスクリーンへの描画)" modifier="Psychlops_DevelopperG" modified="200910260736" created="200708211945" changecount="2">
3605 <pre>[[3.1 オフスクリーン描画の基本]]
3606   [[3.1.1 オフスクリーン領域の宣言]]
3607   [[3.1.2 オフスクリーン上に点・四角形を描画する]] 
3608   [[3.1.3 オフスクリーン上の任意の位置に描画する]]
3609   [[3.1.4 発展的な内容]] 
3610 [[3.2 オフスクリーンを用いた描画例-ランダムドットパターン-]]
3611 [[3.3 画像ファイルの取り扱い]]
3612   [[3.3.1 画像ファイルを保存する]] 
3613   [[3.3.2 画像ファイルを読み込み表示する]] 
3614   [[3.3.3 複数の画像ファイルからアニメーションを作成する]]
3615 [[3.4 Canvasのオフスクリーンへの取り込み]]
3616 [[3.5 文字列のレンダリングの仕様と関連情報]]</pre>
3617 </div>
3618 <div title="3.1 オフスクリーン描画の基本" modifier="Psychlops_DevelopperG" modified="200908190234" created="200708292225" changecount="7">
3619 <pre>[[3.1.1 オフスクリーン領域の宣言]]
3620 [[3.1.2 オフスクリーン上に点・四角形を描画する]] 
3621 [[3.1.3 オフスクリーン上の任意の位置に描画する]] 
3622 [[3.1.4 発展的な内容]] </pre>
3623 </div>
3624 <div title="3.1.1 オフスクリーン領域の宣言" modifier="Psychlops_Admin" modified="200709142108" created="200708292249" changecount="31">
3625 <pre>!Imageクラスの概要
3626 !!オフスクリーン領域とは?
3627 第2章では直にCanvasに描画してきました。第3章ではオフスクリーンへ領域の描画ないし取扱いをします。
3628 Canvasは裏表2枚しかないので、複雑な動画を描画するには不便です。
3629 裏画面に描画が終わるまでflip()を実行できないので(もし強引にflip()を実行してしまったら、描画途中の中途半端なイメージが表示されてしまいます)、複雑な動画ではアニメーションの質が下がってしまいます。
3630 このような動画の描画や、とても時間のかかる内容の描画を行うときにはオフスクリーン領域(描画バッファともいいます)と呼ばれる特殊な描画領域を用います。
3631 このオフスクリーン領域はメモリ上に確保された画像ファイルのようなものだと考えるとよいかもしれません。
3632 この画像ファイルに複雑な描画内容を保存しておいて、後で紙芝居のように画面に表示することができれば、複雑な内容の描画も素早く行えるわけです。^^*1^^
3633 Psychlopsではこのオフスクリーン領域をImageというクラスを用いて操作します。
3634
3635 ^^ *1もう少し正しく表現するなら、オフスクリーン領域とはコンピュータのメインメモリ(RAM 1GBなどという”あれ”のことです)内に確保された描画内容の保存場所(描画用バッファ)のことです。^^
3636
3637 !!ImageとCanvasの違い
3638 ここではImageとCanvasの違いについて詳しく説明することはしませんが、これら2つのクラスは物理的に保管場所が違うということは頭に置いておいてください。
3639 Canvasの内容は画面上に素早く提示することのできる位置に保管されています。flip()命令を実行すると正確なタイミングで描画が起こるわけです。
3640 これに対して、ImageはCanvasよりも遙かに大量のデータを保管することができますが、画面上に描画内容を提示するには、ある程度の時間が必要です。
3641 これは、Canvas(の保管場所)にImageの内容を転送する必要があるからです。
3642 より詳しい内容については[[Tips: Image使用上の注意]]を参考にしてください。
3643 [img[image/オフスクリーン領域.png]]
3644
3645 !オフスクリーン領域の宣言文
3646 オフスクリーン領域は画像を取り扱うので
3647 宣言時には取り扱いたい画像の大きさやカラーモード、透明度の指定が必要となってきます。
3648 Image()のように宣言のみの場合は''Image::set()''命令で指定します。
3649
3650 |!Imageの宣言|Image()|オフスクリーン領域を宣言します|
3651 |~|Image([[Psychlops::Rectangle]] rec,PixelComponentsCnt pixcompcntval)|指定された[[Psychlops::Rectangle]] 型と同じ大きさのオフスクリーン領域を宣言します|
3652 |~|~|[[Psychlops::Rectangle]] rect :[[Psychlops::Rectangle]] 型を指定|
3653 |~|~|PixelComponentsCnt pixcompcntval :[[カラーモード]]を指定。省略した場合はRGBが指定される|
3654 |~|Image(long x, long y, PixelComponentsCnt pixcompcntval)|指定されたx×yと同じ大きさのオフスクリーン領域を宣言します|
3655 |~|~|long x :オフスクリーン領域の横幅を指定|
3656 |~|~|long y :オフスクリーン領域の縦幅を指定|
3657 |~|~|PixelComponentsCnt pixcompcntval :[[カラーモード]]を指定。省略した場合はRGBが指定される|
3658
3659 !Image::set()
3660 オフスクリーン領域の宣言時に何の指定もしなかった場合、Image::set()命令を使用します。
3661 |!Image::set()|set([[Psychlops::Rectangle]] rect, PixelComponentsCnt pixcompcntval)|指定された[[Psychlops::Rectangle]] 型と同じ大きさのオフスクリーン領域を設定します|
3662 |~|~|[[Psychlops::Rectangle]] rect :[[Psychlops::Rectangle]] 型を指定|
3663 |~|~|PixelComponentsCnt pixcompcntval :[[カラーモード]]を指定。省略した場合はRGBが指定される|
3664 |~|set(long x, long y, PixelComponentsCnt pixcompcntval)|指定されたx×yと同じ大きさのオフスクリーン領域を設定します|
3665 |~|~|long x :オフスクリーン領域の横幅を指定|
3666 |~|~|long y :オフスクリーン領域の縦幅を指定|
3667 |~|~|PixelComponentsCnt pixcompcntval :[[カラーモード]]を指定。省略した場合はRGBが指定される|
3668
3669 !!宣言文の書き方
3670 {{{
3671 &lt;例1&gt;
3672 #include &lt;psychlops.h&gt;
3673 using namespace Psychlops;
3674
3675 Psychlops::Rectangle rect1(200,100);
3676 Psychlops::Image Noise1(rect1,Image::GRAY);
3677
3678 void psychlops_main() {
3679
3680         Canvas sampleA(Canvas::fullscreen);
3681         sampleA.flip();
3682         while(!Input::get(Keyboard::spc));
3683
3684 }
3685 }}}
3686 次はImageの宣言と[[Image::set()]]命令を組み合わせたコードです。
3687 {{{
3688 &lt;例2&gt;
3689 #include &lt;psychlops.h&gt;
3690 using namespace Psychlops;
3691
3692 Psychlops::Image Noise1;
3693 long Width=200, Height=100;
3694
3695 void psychlops_main() {
3696
3697         Canvas sampleA(Canvas::fullscreen);
3698         Noise1.set(Width,Height,Image::RGBA);
3699         sampleA.flip();
3700         while(!Input::get(Keyboard::spc));
3701 }
3702 }}}
3703 これらのコードは実行しても黒い画面が表示されるだけでした。
3704 次の3.1.2節で実際にImage上に描画を行います。</pre>
3705 </div>
3706 <div title="3.1.2 オフスクリーン上に点・四角形を描画する" modifier="Psychlops_DevelopperG" modified="200908190233" created="200709032203" changecount="8">
3707 <pre>[[3.1.1|3.1.1 オフスクリーン領域の宣言]]節ではImageの宣言(領域確保)のみを行っていたので、コードを実行しても黒い画面が表示されるだけでした。
3708 本節では実際にImage上に点・四角形を描画して、これを画面に表示してみます。
3709 Imageクラスの命令(メソッド)はCanvasに対する命令とほとんど変わりません。
3710
3711 !Image::pix()
3712 オフスクリーン領域に点を描画するための命令です。
3713 [[Canvas::pix()|2.2.1 画面上に点を描画する-Pointクラス1-]]命令同様に点を描画するために「座標」と「色の指定」を行います。
3714 色の指定についての詳細は[[2.2.5 描画する図形の色を指定する-Colorクラス-]]を参照してください。
3715
3716 |!Image::pix()|pix(int x, int y, [[Psychlops::Color]] col)|座標(x,y)に[[Psychlops::Color]] col色の点を描画します|
3717 |~|~|int x: 描画する点の座標軸xを指定|
3718 |~|~|int y: 描画する点の座標軸yを指定|
3719 |~|~|[[Psychlops::Color]] col:描画する点の色を指定|
3720 |~|pix(double x, double y, [[Psychlops::Color]] col)|座標(x,y)に[[Psychlops::Color]] col色の点を描画します|
3721 |~|~|double x: 描画する点の座標軸xを指定|
3722 |~|~|double y: 描画する点の座標軸yを指定|
3723 |~|~|[[Psychlops::Color]] col:描画する点の色を指定|
3724 |~|pix([[Psychlops::Point]] po, [[Psychlops::Color]] col)|Point座標(x,y)に[[Psychlops::Color]] col色の点を描画します|
3725 |~|~|[[Psychlops::Point]] po :描画する点の座標(x,y)を指定|
3726 |~|~|[[Psychlops::Color]] col:描画する点の色を指定|
3727
3728 !Image::draw()
3729 実際にオフスクリーン領域に描画する際の命令です。
3730 この命令を記述しないといくら''Image::pix()''命令やこの後出てくる''Image::rect()''命令を記述しても描画されませんので注意してください。
3731
3732 |!Image::draw()|draw()|指定したオフスクリーンを描画します|
3733 |~|draw(double left, double top)|指定したオフスクリーン領域を(left,top)を中心とした座標に設定します|
3734 |~|~|double left: 描画するオフスクリーン領域の座標軸xを指定|
3735 |~|~|double top: 描画するオフスクリーン領域の座標軸yを指定|
3736
3737 !!Image::pix(),Image::draw()の書き方
3738 100×100のImageに点を描画します。
3739 {{{
3740 &lt;例1&gt;
3741 #include &lt;psychlops.h&gt;
3742 using namespace Psychlops;
3743
3744 double width=100,height=100;
3745 Psychlops::Rectangle rect1(width,height);
3746 Psychlops::Color col;
3747 Psychlops::Image Noise1(rect1,Image::GRAY);
3748
3749 void psychlops_main() {
3750
3751         Canvas sampleA(Canvas::fullscreen);
3752         //水平方向に2,垂直方向に5間隔の点を描画する。
3753         for(int x=0; x&lt;width; x+=2){
3754                 for(int y=0; y&lt;height; y+=5){
3755                         col.set(0.5);
3756                         Noise1.pix(x,y,col);
3757                 }
3758         }
3759         Noise1.draw();
3760         sampleA.flip();
3761         while(!Input::get(Keyboard::spc));
3762 }
3763 }}}
3764
3765 !Image::rect()
3766 オフスクリーン領域に四角形を描画するための命令です。
3767 実際の描画処理は''Image::draw()''命令で行われます。
3768 色の指定についての詳細は[[2.2.5 描画する図形の色を指定する-Colorクラス-]]を参照してください。
3769
3770 |!Image::rect()|rect([[Psychlops::Rectangle]] rect, [[Psychlops::Color]] col)|[[Psychlops::Rectangle]]型の四角形を[[Psychlops::Color]] col色で描画します|
3771 |~|~|[[Psychlops::Rectangle]] rect : 描画する四角形を指定|
3772 |~|~|[[Psychlops::Color]] col:描画する四角形の色を指定|
3773
3774 !!Image::rect()の書き方
3775 100×100のImageに青の四角形を描画します。
3776 {{{
3777 &lt;例2&gt;
3778 #include &lt;psychlops.h&gt;
3779 using namespace Psychlops;
3780
3781 Psychlops::Rectangle rect1(100,100);
3782 Psychlops::Image Noise1(rect1);
3783
3784 void psychlops_main() {
3785
3786         Canvas sampleA(Canvas::fullscreen);
3787         Noise1.rect(rect1,Color::blue);
3788         Noise1.draw();
3789         sampleA.flip();
3790         while(!Input::get(Keyboard::spc));
3791 }
3792 }}}</pre>
3793 </div>
3794 <div title="3.1.3 オフスクリーン上の任意の位置に描画する" modifier="Psychlops_DevelopperG" modified="200908190258" created="200709042020" changecount="16">
3795 <pre>前節でオフスクリーン上に図形の描画を行いました。
3796 描画位置の指定がない場合、オフスクリーン領域は常に画面の左上に設定されます。
3797 この節ではオフスクリーン領域を任意の場所に移動します。
3798 2.2節ではそれぞれ[[Psychlops::Point型の移動|2.2.1 画面上に点を描画する-Pointクラス1-]]と[[Psychlops::Rectangle型の移動|2.2.6 画面上に縞を描画する-Rectangleクラス2-]]の移動について説明しました。
3799 オフスクリーン領域の移動も上記の型と同様の命令文を使用します。
3800
3801 !Image::centering()
3802 [[Psychlops::Image]]型の領域を移動します。
3803 座標を指定する場合、その座標に領域の中心が設定されます。
3804 |!Image::centering()|centering()|[[Psychlops::Image]]型の領域を画面中心に移動します|
3805 |~|centering(double x, double y)|[[Psychlops::Image]]型の領域の中心を指定した座標(x,y)に移動します|
3806 |~|~|double x:移動する領域のx座標を指定|
3807 |~|~|double y:移動する領域のy座標を指定|
3808
3809 !!Image::centering()の書き方
3810 前節の&lt;例1&gt;で作成したImageを画面中心に移動します。
3811 {{{
3812 &lt;例1&gt;
3813 #include &lt;psychlops.h&gt;
3814 using namespace Psychlops;
3815
3816 double width=100,height=100;
3817 Psychlops::Rectangle rect1(width,height);
3818 Psychlops::Color col;
3819 Psychlops::Image Noise1(rect1,Image::GRAY);
3820
3821 void psychlops_main() {
3822
3823         Canvas sampleA(Canvas::fullscreen);
3824         Noise1.centering();
3825         
3826         //水平方向に2,垂直方向に5間隔の点を描画する。
3827         for(int x=0; x&lt;width; x+=2){
3828                 for(int y=0; y&lt;height; y+=5){
3829                         col.set(0.5);
3830                         Noise1.pix(x,y,col);
3831                 }
3832         }
3833         Noise1.draw();
3834         sampleA.flip();
3835         while(!Input::get(Keyboard::spc));
3836 }
3837 }}}
3838
3839 !Image::shift()
3840 [[Psychlops::Image]]型の領域を移動します。
3841 元の座標(x1,y1)を基点に入力値(x,y)だけ移動します。
3842 |!Image::shift()|shift(double h, double v)|座標(X,Y)を(h,v)だけ移動します|
3843 |~|~|double x:入力値分、右水平方向に移動|
3844 |~|~|double y:入力値分、下垂直方向に移動|
3845
3846 !!Image::shift()の書き方
3847 前節の&lt;例2&gt;で作成したImageを座標(100,50)分移動します。
3848 {{{
3849 &lt;例2&gt;
3850 #include &lt;psychlops.h&gt;
3851 using namespace Psychlops;
3852
3853 Psychlops::Rectangle rect1(100,100);
3854 Psychlops::Image Noise1(rect1);
3855
3856 void psychlops_main() {
3857
3858         Canvas sampleA(Canvas::fullscreen);
3859         Noise1.shift(100,50);
3860         Noise1.rect(rect1,Color::blue);
3861         Noise1.draw();
3862         sampleA.flip();
3863         while(!Input::get(Keyboard::spc));
3864 }
3865 }}}</pre>
3866 </div>
3867 <div title="3.1.4 発展的な内容" modifier="Psychlops_Admin" created="200709252141" changecount="1">
3868 <pre>この節ではは少し発展的で込み入った内容を取り扱います。ここまでの命令を使ってとりあえず自分の目的が達成できている場合は無視しても構いません。
3869 複雑で大きなサイズのイメージを高速でどうしても表示したい時にだけ、読んでみてください。
3870
3871 !Image::quicken()
3872 3.1.1節でサイズが大きなImageをCanvasに転送するには時間がかかるという説明をしました。
3873 この問題を解決する方法が一つあります。
3874 転送するのにかかる時間の総和を短縮することはできませんが、PsychlopsはImageをCanvasへより早く転送することのできるメモリ領域へ転送することができます。
3875
3876 Canvasに描画する場合はCanvasの裏に描画して[[Canvas::flip()]]命令で画面に表示しています。
3877 Imageはコードの記述はCanvasと大差ありませんが、Canvasの裏ではなくメインメモリ(主記憶装置)に描画しています。
3878 [[Image::draw()]]命令を実行することで初めてメインメモリ(主記憶装置)にある図形がVRAMに転送され、flip()とともに画面に表示されます。
3879 &lt;[[Image::draw()]]命令実行時のイメージ図&gt;
3880 [img[image/draw()命令.png]]
3881
3882 つまり普通であればImageからCanvasへ直接転送が起こります。
3883 ここで、この2つの中間地点にあらかじめ転送しておくことで、Canvasへの転送時間を見かけ上小さくすることができるのです。
3884 この中間地点はVRAMと呼ばれるビデオボード上のメモリチップの中に作られます。
3885 実は、VRAM内でのメモリの転送速度に比べてメインメモリからVRAMへの転送はとても時間がかかるのです。
3886 この時間がかかる部分を事前に行っておくことで、見かけ上の転送時間を大きく縮めることができます。
3887 この転送を事前に一部行っておく命令がImage::quicken()命令です。
3888 <<図>>
3889 [img[image/quicken()命令.png]]
3890 &lt;quickenモード&gt;
3891
3892 通常、[[Image::draw()]]命令を使用すると上図のようにメインメモリ(主記憶装置)から画面まで一気に転送し描画します。
3893 quicken()を実行すると、Imageの図形をVRAMに転送し、quickenモードに切り替えます。
3894 そして[[Image::draw()]]命令でVRAMにある図形を画面に転送し表示します。
3895
3896 |!Image::quicken()|quicken()|メインメモリ(主記憶装置)にあるImageの内容をあらかじめVRAMに送ります|
3897 |~|~|解除する場合はquicken(false)と記述|
3898
3899 !!Image::quicken()の書き方
3900 [[Image::draw()]]命令の直前に記述します。
3901 {{{
3902 &lt;例1&gt;
3903 #include &lt;psychlops.h&gt;
3904 using namespace Psychlops;
3905
3906 double width=100,height=100;
3907 Psychlops::Rectangle rect1(width,height);
3908 Psychlops::Color col;
3909 Psychlops::Image Noise1(rect1);
3910
3911 void psychlops_main() {
3912
3913         Canvas sampleA(Canvas::fullscreen);
3914         Noise1.centering();
3915         for(int x=0; x&lt;width; x+=3){
3916                 for(int y=0; y&lt;height; y+=3){
3917                         col.set(0.5,0.2,0.8);
3918                         Noise1.pix(x,y,col);
3919                 }
3920         }
3921         Noise1.quicken();
3922         Noise1.draw();
3923         sampleA.flip();
3924         while(!Input::get(Keyboard::spc));
3925 }
3926 }}}
3927
3928 !!Image::quicken()の使用上の注意
3929 ただし、この転送を行うためには
3930 # ImageがRGB/RGBAモードで宣言されていること
3931 # Imageへの描画がすでに完了していること
3932 が必要です。
3933
3934 特に2番目については注意が必要です。
3935 Imageに対する描画命令は全てメインメモリ上にあるImageに対して行われます。
3936 したがって、すでにquicken()してしまったimageに対して描画を行っても、変更が反映されません。
3937 {{{
3938 &lt;例2&gt;
3939 #include &lt;psychlops.h&gt;
3940 using namespace Psychlops;
3941
3942 double width=100,height=100;
3943 Psychlops::Rectangle rect1(width,height);
3944 Psychlops::Color col;
3945 Psychlops::Image Noise1(rect1);
3946
3947 void psychlops_main() {
3948
3949         Canvas sampleA(Canvas::fullscreen);
3950         Noise1.quicken();
3951         Noise1.centering();
3952         while(!Input::get(Keyboard::spc)){
3953                 for(int x=0; x&lt;width; x+=3){
3954                         for(int y=0; y&lt;height; y+=3){
3955                                 col.set(0.5,0.2,0.8);
3956                                 Noise1.pix(x,y,col);
3957                         }
3958                 }
3959         Noise1.draw();
3960         sampleA.flip();
3961         }
3962 }
3963 }}}
3964 この新しい描画内容を画面に反映させるには、quicken(false)命令を使ってquicken()モードを解除するか、再度Image::quicken()命令を実行してもう一度転送を行う必要があります。&lt;プログラム例&gt;
3965 (転送を行ってもメインメモリ上にコピーは残っているので、単にこれをもう一度転送し直すことで描画を反映させることができます)
3966 {{{
3967 &lt;例3&gt;
3968 #include &lt;psychlops.h&gt;
3969 using namespace Psychlops;
3970
3971 double width=100,height=100;
3972 Psychlops::Rectangle rect1(width,height);
3973 Psychlops::Color col;
3974 Psychlops::Image Noise1(rect1);
3975
3976 void psychlops_main() {
3977
3978         Canvas sampleA(Canvas::fullscreen);
3979         Noise1.quicken();
3980         Noise1.centering();
3981         while(!Input::get(Keyboard::spc)){
3982                 for(int x=0; x&lt;width; x+=3){
3983                         for(int y=0; y&lt;height; y+=3){
3984                                 col.set(0.5,0.2,0.8);
3985                                 Noise1.pix(x,y,col);
3986                         }
3987                 }
3988         Noise1.draw();
3989         sampleA.flip();
3990         Noise1.quicken(false);
3991         }
3992 }
3993 }}}
3994 ※2007年9月現在、quicken()命令はRGB/RGBAでのみ稼働</pre>
3995 </div>
3996 <div title="3.2 オフスクリーンを用いた描画例-ランダムドットパターン-" modifier="YourName" modified="200802191418" created="200709052137" changecount="18">
3997 <pre>この節ではImageクラスを用いたオフスクリーンへの描画の利用例として、下図に示した砂嵐のような図、ランダムドットパターンを描画してみます。
3998 [img[image/randomdot.png]]
3999 はじめにドットサイズが1ピクセル四方の基本的なパターンを描画する例を説明した後、ドットサイズがもっと大きいものについて説明します。
4000
4001 [[3.2.1 基本的なランダムドットを描画する]]
4002 [[3.2.2 ドットサイズの異なるランダムドットパターンの描画]]
4003 [[3.2.3 ランダムドットキネマトグラムの描画]]
4004
4005 この節では、複数の細いImageをshift()命令を使って並べることによって、ランダムドットを描画しています。この方法は簡単ですが、ドット密度やドットの運動方向を精密に操作したい場合は、Rectangleの配列を用いた方が描画がスムーズな場合があります。これについては、[[Tips: Rectangleの配列を用いたドットパターンの描画例]]を参照してください。</pre>
4006 </div>
4007 <div title="3.2.1 基本的なランダムドットを描画する" modifier="YourName" modified="200802232254" created="200709130524" changecount="7">
4008 <pre>PsychlopsではImageを用いてランダムドットパターンが比較的簡単に描画できます。
4009 ランダムドットを描画するためには''Psychlops::random()''命令を使用します。
4010 色の返値を''Psychlops::random()''命令を使用しランダムに値を返すことでランダムドットパターンが描画できます。
4011
4012 !!Psychlops::random()
4013 0~1,0~任意の数等好きに設定した値の中でランダムに数を返します。
4014
4015 |!Psychlops::random()|random()|0~1の値をランダムに返します。|
4016 |~|random(double N)|0~Nの値をランダムに返します。|
4017 |~|~|double N: ランダムで返す値の最大値を指定|
4018 |~|random(int N)|0~Nの整数値をランダムに返します。|
4019 |~|~|int N: ランダムで返す整数値の最大値を指定|
4020 |~|random(double min, double max)|min~maxの値をランダムに返します。|
4021 |~|~|double min: ランダムで返す値の最小値を指定|
4022 |~|~|double max: ランダムで返す値の最大値を指定|
4023
4024 引数が実数(double)か整数(int)かで動作が異なりますので、実数で返したい場合は最大値が整数でも必ず1.0などのように小数点をつけて指定してください。整数変数を指定する場合は(double)でキャストしてください。
4025 {{{
4026 random(5)   ← [0,1,2,3,4] のいずれかの数がint型で返る
4027 random(5.0) ← [0, 5)の間の浮動小数点値が返る
4028
4029 int i = 5;
4030 random(i)         ← [0,1,2,3,4] のいずれかの数がint型で返る
4031 random((double)i) ← [0, 5)の間の浮動小数点値が返る
4032 }}}
4033
4034 !!!Psychlops::random()の書き方
4035 3.1.3節の&lt;例1&gt;では画面中央のImage上に点を描画しました。
4036 このコードを応用してランダムドットを描画します。
4037 3.1.3節の&lt;例1&gt;では水平方向に2 ピクセル,垂直方向に5 ピクセル間隔に色:0.5の点を描画しました。
4038 ここではグレースケールのランダムドットを描画するために、色(輝度)が0 ~ 1の間でランダムに設定された、100 x 100 ピクセル四方の隙間のないドット列を描画します。
4039 {{{
4040 &lt;例1&gt;
4041 #include &lt;psychlops.h&gt;
4042 using namespace Psychlops;
4043
4044 double width=100,height=100;
4045 double temp1;
4046 Psychlops::Rectangle rect1(width,height);
4047 Psychlops::Color col;
4048 Psychlops::Image Noise1(rect1,Image::GRAY);
4049
4050 void psychlops_main() {
4051
4052         Canvas sampleA(Canvas::fullscreen);
4053         Noise1.centering();
4054         for(int x=0; x&lt;width; x++){
4055                 for(int y=0; y&lt;height; y++){
4056                         //0~1の値をランダムに返す
4057                         temp1=Psychlops::random();
4058                         col.set(temp1);
4059                         Noise1.pix(x,y,col);
4060                 }
4061         }
4062         Noise1.draw();
4063         sampleA.flip();
4064         while(!Input::get(Keyboard::spc));
4065 }
4066 }}}
4067 &lt;例1&gt;では[[カラーモード]]をGRAYで描画しましたがRGB,RBGAでも描画可能です。
4068 &lt;例2&gt;ではRGBAでランダムドットを描画します。
4069 {{{
4070 &lt;例2&gt;
4071 #include &lt;psychlops.h&gt;
4072 using namespace Psychlops;
4073
4074 double width=100,height=100;
4075 double temp1,temp2,temp3,temp4;
4076 Psychlops::Rectangle rect1(width,height);
4077 Psychlops::Color col;
4078 Psychlops::Image Noise1(rect1,Image::RGBA);
4079
4080 void psychlops_main() {
4081
4082         Canvas sampleA(Canvas::fullscreen);
4083         Noise1.centering();
4084         for(int x=0; x&lt;width; x++){
4085                 for(int y=0; y&lt;height; y++){
4086                         //R,G,B,Aそれぞれに0~1の値をランダムに返します
4087                         temp1=Psychlops::random();
4088                         temp2=Psychlops::random();
4089                         temp3=Psychlops::random();
4090                         temp4=Psychlops::random();
4091                         col.set(temp1,temp2,temp3,temp4);
4092                         Noise1.pix(x,y,col);
4093                 }
4094         }
4095         Noise1.draw();
4096         sampleA.flip();
4097         while(!Input::get(Keyboard::spc));
4098 }
4099 }}}
4100
4101 [img[image/colornoise.png]]
4102
4103 </pre>
4104 </div>
4105 <div title="3.2.2 ドットサイズの異なるランダムドットパターンの描画" modifier="Psychlops_Admin" modified="200709131828" created="200709130500" changecount="4">
4106 <pre>今まで描画したランダムドットパターンのドットサイズは1 ピクセル四方でした。
4107 次はピクセルサイズがn( n &gt; 1 )のランダムドットパターンを描画します。
4108 1 ピクセルのドットを描画する時[[Image::pix()]]命令を使用していましたが
4109 n ピクセルのドットを描画するためには[[Image::rect()]]命令を使用します。
4110 横幅×高さの領域にn ピクセルのランダムドットパターンを描画する場合、以下の要領で描画します。
4111 {{{
4112 1.ドットサイズ × ( 高さ × ドットサイズ )のImageをN個(横幅/ドットサイズ)用意する。
4113 2.ドットサイズ × ドットサイズのrectをN個( 横幅 / ドットサイズ )用意する。
4114 3.1で作成したImage(x)に2で作成したrect(x)をセットする。
4115 4.2で作成したrect(x)にランダムドットパターンを描画する。
4116 5.rectをドットサイズ縦方向に移動し、Image(x)全てに描画されるまで3~4を行う。
4117 6.Imageを横幅の長さまで並べる。
4118 }}}
4119 [img[image/dotsize.png]]
4120
4121 下の例では、横幅がwドット分、縦幅がhドット分のランダムドットパターンを描画しています。ここで指定した単位がドット単位でピクセル単位でないことに注意して、例を見てみてください。
4122 {{{
4123 &lt;例3&gt;
4124 #include &lt;psychlops.h&gt;
4125 using namespace Psychlops;
4126
4127 const int dotsize=4;
4128 const int width=100;
4129 double height=50;
4130 double temp1;
4131 Psychlops::Rectangle rect1[width];
4132 Psychlops::Color col;
4133 Psychlops::Image Noise1[width];
4134
4135 void psychlops_main() {
4136
4137         Canvas sampleA(Canvas::fullscreen);
4138         
4139         for(int x=0; x&lt;width; x++){
4140                 Noise1[x].set(dotsize,height*dotsize);
4141                 rect1[x].set(dotsize, dotsize);
4142                 for(int y=0; y&lt;height; y++){
4143                         temp1=Psychlops::random();
4144                         col.set(temp1);
4145                         Noise1[x].rect(rect1[x],col);
4146                         //ドットサイズ分下に移動する                       
4147                         rect1[x].shift(0, dotsize);
4148                 }       
4149                 Noise1[x].centering().shift((double)-1/2*(width*dotsize)+x*dotsize,0);
4150                 Noise1[x].draw();
4151         }
4152         sampleA.flip();
4153         while(!Input::get(Keyboard::spc));
4154 }
4155 }}}
4156 ドットサイズを他の値に変えて実行したい場合は『const int dotsize=4;』の値を変更してください。
4157 この例では、画面中央にランダムドットパターンを描画しました。画面中央に描画するための式は[[2.2.6節|2.2.6 画面上に縞を描画する-Rectangleクラス2-]]を参照してください。
4158
4159 ここでは( Xc - 1 / 2 * w + i , Yc )のwとiにドットサイズ dotsize を乗算しています。
4160 ドットサイズを乗算することでImageが重ならないように移動して描画しています。
4161 試しにドットサイズの乗算を外すと、1ピクセル分のみ移動されて最後の列のみ正常に、他の列は細長いドットが表示されます。これはImageが重なって描画されてしまっているためです。
4162 [img[image/dotsize2.png]]
4163 </pre>
4164 </div>
4165 <div title="3.2.3 ランダムドットキネマトグラムの描画" modifier="Psychlops_Admin" created="200709252142" changecount="1">
4166 <pre>!ランダムドットキネマトグラムを描画しよう
4167 次は上記のランダムドットを水平方向に動かしてみましょう。
4168 このような運動するランダムドットパターンをランダムドットキネマトグラムといいます。
4169
4170 今回は水平方向に移動するのでX座標( Xc - 1 / 2 * w + i )について考慮します。
4171 ( Xc - 1 / 2 * w + i ) + α と記述すると α 分移動します。
4172 しかし既存の横幅内での運動を行いたいので
4173 下図のように右に飛び出たα を前に移動します。
4174 移動要素である i + α に対しwで除算し余りを算出することで求まります。
4175 {{{
4176 ( Xc - 1 / 2 * w ) + ( i + α ) % w
4177 }}}
4178 [img[image/rdk.png]]
4179 &lt;例3&gt;ではドットサイズを変更しているので、それぞれの値をドットサイズ倍する必要があります。
4180 {{{
4181 ( Xc - 1 / 2 * w*dotsize ) + ( i *dotsize+ α*dotsize ) % w*dotsize
4182 }}}
4183
4184 次に α の内容について考えてみましょう。
4185 1フレームあたりの移動量をnドット分とすると、mフレーム目での0フレーム目の位置からのピクセル単位での移動量αは、 n* dotsize * m となります。
4186 つまりi番目のimageの水平方向の移動量は、以下のようになります。
4187 {{{
4188  ( Xc - 1 / 2 * w * dotsize ) + ( i *dotsize+ frame*speed*dotsize ) % w*dotsize
4189 }}}
4190 第1項(+の前)はImageをXcを中心として並べるための移動量、第2項が運動するパターンを実現するための移動量です。
4191
4192 早速上記式を用いて描画します。
4193 {{{
4194 &lt;例4&gt;
4195 #include &lt;psychlops.h&gt;
4196 using namespace Psychlops;
4197
4198 const int dotsize=4;
4199 const int width=100;
4200 double height=50;
4201 double temp1;
4202 double speed=1;
4203 double phase=0;
4204 int frame=0;
4205 Psychlops::Rectangle rect1[width];
4206 Psychlops::Color col;
4207 Psychlops::Image Noise1[width];
4208
4209 void psychlops_main() {
4210
4211         Canvas sampleA(Canvas::fullscreen);
4212         
4213         for(int x=0; x&lt;width; x++){
4214                 Noise1[x].set(dotsize,height*dotsize);
4215                 rect1[x].set(dotsize, dotsize);
4216                 for(int y=0; y&lt;height; y++){
4217                         temp1=Psychlops::random();
4218                         col.set(temp1);
4219                         Noise1[x].rect(rect1[x],col);
4220                         rect1[x].shift(0, dotsize);
4221                 }       
4222         }
4223         while(!Input::get(Keyboard::spc)){
4224                 for(int x=0; x&lt;width; x++){
4225                         phase=(double)-1/2*(width*dotsize)+(int)(x*dotsize+frame*speed*dotsize)%(int)(width*dotsize);
4226                         Noise1[x].centering().shift(phase,0);
4227                         Noise1[x].draw();
4228                 }
4229                 sampleA.flip();
4230                 frame++;
4231         }
4232 }
4233 }}}
4234
4235 水平方向へ移動するドットパターンが描画されたでしょうか?
4236 これと同様にしてランダムドットステレオグラムを書くこともできます。[[Tips: ランダムドットステレオグラムの描画コード]]を参照してみてください。</pre>
4237 </div>
4238 <div title="3.3 画像ファイルの取り扱い" modifier="Psychlops_Admin" modified="200709121657" created="200708292225" changecount="4">
4239 <pre>この節では、画像ファイルの取扱いについて説明します。
4240 現バージョンのPsychlopsではpng形式のファイルの読込と保存が可能です。
4241 この読込と保存はImage型を通して行われます。
4242 読み込まれた画像ファイルはPsychlops内部ではImage型の変数(インスタンス)として扱われます。
4243 同様に保存はImage型の描画内容をPng形式でファイルに書き出すことによって行われます。
4244 以下では、この2つについて説明します。
4245
4246 [[3.3.1 画像ファイルを保存する]] 
4247 [[3.3.2 画像ファイルを読み込み表示する]] 
4248 </pre>
4249 </div>
4250 <div title="3.3.1 画像ファイルを保存する" modifier="Psychlops_DevelopperG" modified="200910080846" created="200709111925" changecount="34">
4251 <pre>ディスプレイに提示した画像をプログラムが終了した後でも使えるように保存したい時がよくあります。
4252 この節では、画面上に描画した図形を外部ファイルとして保存する方法について説明します。
4253 PsychlopsではImageの描画内容を、PNG形式の画像ファイルとして保存することができます。
4254 これにはImage::save()命令を使用します。はじめにこのImage::save()命令の使い方と例を見てみましょう。
4255
4256 !Image::save()
4257 画像ファイルをpng形式で保存します。
4258
4259 |!Image::save()|save(filename)|!指定したImageを保存します|&gt;|&gt;|
4260 |~|~|filename| 保存するImageの画像ファイル名称(拡張子まで)を指定|&gt;|&gt;|
4261 |~|~|保存先を指定しない場合のパス|Mac:Macintosh HD/ユーザ/ユーザ名(任意)/書類/Psychlops/ ^^*1^^|&gt;|
4262 |~|~|~|Win:実行ファイルと同じパス|&gt;|
4263 |~|~|保存をする場合のパス指定|%APP%|Mac:実行バンドル(.app)と同じパス|
4264 |~|~|~|~|Win:実行ファイルと同じパス|
4265 |~|~|~|%RESOURCES%|Mac:実行バンドルの内部のResourcesフォルダ|
4266 |~|~|~|~|Win:実行ファイルのパスの直下のrscという名前のフォルダ|
4267 |~|~|~|%~USER_HOME%|Mac:&quot;~/&quot;と同じ(/Users/(user)/)|
4268 |~|~|~|~|Win:ユーザープロファイルフォルダ(C:\Documents and Settings\(user)\)|
4269 |~|~|~|%~USER_SETTING%|Mac:&quot;~/Library/Psychlops/&quot;|
4270 |~|~|~|~|Win:&quot;%~AppData%Psychlops&quot;|
4271 |~|~|~|%~USER_DOCUMENTS%|Mac:&quot;~/Documents/Psychlops&quot;|
4272 |~|~|~|~|Win:ユーザープロファイルフォルダ\My Documents\Psychlops|
4273 |~|~|~|%~USER_DOCUMENTS_ROOT%|Mac:&quot;~/Documents&quot;|
4274 |~|~|~|~|Win:ユーザープロファイルフォルダMy Documents\|
4275 *1 Finderでの表示は上記のとおりですが、Psychlopsで指定する場合は下記のUnix形式でパスを指定してください
4276 ** &quot;~/Documents/Psychlops/&quot;
4277 *2 MacOSX版では、日本語パス名は使用できません。強制終了してしまいます。
4278 *3 VISTA環境の場合、パス指定が正常に動作しません。VISTA環境の方はお手数ですが保存先を指定しないでお使いください。
4279
4280 !Canvas::to()
4281 この命令は[[Psychlops::Rectangle]]型の情報を[[Psychlops::Image]]型に移動する命令です。
4282 この命令と''Image::save()''命令を組み合わせることによってImage上以外で描画した画像が保存できます。
4283 |!Canvas::to()|to([[Psychlops::Image]] img, [[Psychlops::Rectangle]] rect) |&gt;|![[Psychlops::Rectangle]]型の情報を[[Psychlops::Image]]型に移動する|
4284 |~|~|[[Psychlops::Image]] img |移動先のImageを指定|
4285 |~|~|[[Psychlops::Rectangle]] rect |移動元のRectangleを指定|
4286
4287 ここで注意する点が一つあります。
4288 Canvas::to()命令は[[Psychlops::Rectangle]]型の引数で指定された大きさを取得して移動先のImageのサイズに指定します。
4289 このときに移動先のImageのサイズがあらかじめセットされていると、プログラムが予期しない動作をすることがあります。
4290 変数の宣言だけが行われていて、まだセットをされていないImage型を使用するか、
4291 Image::release()命令を使って一度Imageの中身をクリアしてからCanvas::to()命令を使うようにして下さい。
4292
4293 |!Image::release()|release(void) |メモリ内のイメージの内容を解放する|
4294
4295 !!Image::save()の書き方
4296 [[Canvas::pix()|2.2.1 画面上に点を描画する-Pointクラス1-]]命令でCanvas上に描画したランダムドットをImageに移動し保存します。
4297 ''Canvas::to()''命令は[[Psychlops::Rectangle]] 型が移動元になるので
4298 描画した点と同じ場所に空の[[Psychlops::Rectangle]] 型を設定する必要があります。
4299
4300 &lt;例1&gt;では座標(400,500)に100 × 100のランダムドットを描画し保存します。
4301 centerX,centerYの値を変更することで描画する座標位置が変わります。
4302 0,0を代入もしくは削除すると左上に描画されます。
4303 {{{
4304 &lt;例1&gt;
4305 #include &lt;psychlops.h&gt;
4306 using namespace Psychlops;
4307
4308 double width=100,height=100;
4309 double temp1;
4310 double centerX=700,centerY=500;
4311 Psychlops::Rectangle rect1(width,height);
4312 Psychlops::Color col;
4313 Psychlops::Image Noise1;
4314
4315 void psychlops_main() {
4316
4317         Canvas sampleA(Canvas::fullscreen);
4318         rect1.shift(centerX,centerY);
4319         for(int x=0; x&lt;width; x++){
4320                 for(int y=0; y&lt;height; y++){
4321                         temp1=Psychlops::random();
4322                         col.set(temp1);
4323                         sampleA.pix(x+centerX,y+centerY,col);
4324                 }
4325         }
4326         sampleA.to(Noise1,rect1);
4327         sampleA.flip();
4328         while(!Input::get(Keyboard::spc));
4329         Noise1.save(&quot;test.png&quot;);
4330 }
4331 }}}
4332 次は保存するパスを指定して保存します。
4333 {{{
4334 &lt;例2&gt;
4335 #include &lt;psychlops.h&gt;
4336 using namespace Psychlops;
4337
4338 double width=100,height=100;
4339 double temp1;
4340 double centerX=700,centerY=500;
4341 Psychlops::Rectangle rect1(width,height);
4342 Psychlops::Color col;
4343 Psychlops::Image Noise1;
4344
4345 void psychlops_main() {
4346
4347  Canvas sampleA(Canvas::fullscreen);
4348  rect1.shift(centerX,centerY);
4349  for(int x=0; x&lt;width; x++){
4350   for(int y=0; y&lt;height; y++){
4351    temp1=Psychlops::random();
4352    col.set(temp1);
4353    sampleA.pix(x+centerX,y+centerY,col);
4354   }
4355  }
4356  sampleA.to(Noise1,rect1);
4357  sampleA.flip();
4358  while(!Input::get(Keyboard::spc));
4359  Noise1.save(&quot;%APP%test.png&quot;);
4360 }
4361 }}}
4362 指定のパスに保存されたか確認してください。</pre>
4363 </div>
4364 <div title="3.3.2 画像ファイルを読み込み表示する" modifier="YourName" modified="200802131204" created="200709111918" changecount="13">
4365 <pre>今までの節では自分でオフスクリーン上に図形を描画しました。
4366 この節では既存の画像ファイルを読み込みます。
4367
4368 !Image::load()
4369 既存の任意の画像ファイルを読み込みます。
4370
4371 |!Image::load()|load(filename)|指定した画像ファイルを読み込みます|&gt;|&gt;|
4372 |~|~|filename: 読み込む画像ファイルの名前を指定|&gt;|&gt;|
4373 |~|~|保存先を指定しない場合のパス|Mac:Macintosh HD/ユーザ/ユーザ名(任意)/書類/Psychlops/ ^^*1^^|&gt;|
4374 |~|~|~|Win:実行ファイルと同じパス|&gt;|
4375 |~|~|読込みをする場合のパス指定|%APP%|Mac:実行バンドル(.app)と同じパス|
4376 |~|~|~|~|Win:実行ファイルと同じパス|
4377 |~|~|~|%RESOURCES%|Mac:実行バンドルの内部のResourcesフォルダ|
4378 |~|~|~|~|Win:実行ファイルのパスの直下のrscという名前のフォルダ|
4379 |~|~|~|%~USER_HOME%|Mac:&quot;~/&quot;と同じ(/Users/(user)/)|
4380 |~|~|~|~|Win:ユーザープロファイルフォルダ(C:\Documents and Settings\(user)\)|
4381 |~|~|~|%~USER_SETTING%|Mac:&quot;~/Library/Psychlops/&quot;|
4382 |~|~|~|~|Win:&quot;%~AppData%Psychlops&quot;|
4383 |~|~|~|%~USER_DOCUMENTS%|Mac:&quot;~/Documents/Psychlops&quot;|
4384 |~|~|~|~|Win:ユーザープロファイルフォルダ\My Documents\Psychlops|
4385 |~|~|~|%~USER_DOCUMENTS_ROOT%|Mac:&quot;~/Documents&quot;|
4386 |~|~|~|~|Win:ユーザープロファイルフォルダMy Documents\|
4387 *1 Finderでの表示は上記のとおりですが、Psychlopsで指定する場合は下記のUnix形式でパスを指定してください
4388 ** &quot;~/Documents/Psychlops/&quot;
4389 *2 MacOSX版では、日本語パス名は使用できません。強制終了してしまいます。
4390 *3 VISTA環境の場合、パス指定が正常に動作しません。VISTA環境の方はお手数ですが保存先を指定しないでお使いください。
4391
4392
4393 !Image::load()の書き方
4394 [[3.3.1節|3.3.1 画像ファイルを保存する]]の&lt;例1&gt;で保存したランダムドットを読み込みます。
4395 表示は[[Image::draw()]]で行います。
4396 {{{
4397 &lt;例1&gt;
4398 #include &lt;psychlops.h&gt;
4399 using namespace Psychlops;
4400
4401 Psychlops::Image Noise1;
4402
4403 void psychlops_main() {
4404
4405         Canvas sampleA(Canvas::fullscreen);
4406         Noise1.load(&quot;test.png&quot;);
4407         Noise1.centering();
4408         Noise1.draw();
4409         sampleA.flip();
4410         while(!Input::get(Keyboard::spc));
4411 }
4412 }}}
4413 次にパス指定を行い、画像ファイルを読み込みます。
4414 {{{
4415 &lt;例2&gt;
4416 #include &lt;psychlops.h&gt;
4417 using namespace Psychlops;
4418
4419 Psychlops::Image Noise1;
4420
4421 void psychlops_main() {
4422
4423         Canvas sampleA(Canvas::fullscreen);
4424         Noise1.load(&quot;%APP%test.png&quot;);
4425         Noise1.centering();
4426         Noise1.draw();
4427         sampleA.flip();
4428         while(!Input::get(Keyboard::spc));
4429 }
4430 }}}</pre>
4431 </div>
4432 <div title="3.4 Canvasのオフスクリーンへの取り込み" modifier="YourName" modified="200802191336" created="200710240512" changecount="3">
4433 <pre>スクリーンキャプチャのように、画面上に描画したイメージをオフスクリーンに取り込むことができます。
4434 |!Canvas::to()|to(Image target, Rectangle source)|VRAM上にある画面イメージをメインメモリ(主記憶装置)にあるImageに転送します|
4435 |~|~|Image target: 転送先のImageを指定します|
4436 |~|~|Rectangle source: 転送する画面上の範囲を指定します|
4437 この際、転送先のImageがsetされていなければ自動的にsetします。setされていた場合、破棄されて上書きされます。
4438
4439 {{{
4440 #include &lt;psychlops.h&gt;
4441 using namespace Psychlops;
4442
4443 void psychlops_main() {
4444
4445         Canvas d(Canvas::fullscreen);
4446
4447         Psychlops::Rectangle rect(100,100);
4448         rect.centering();
4449
4450         // check rect external bordar and Rectangle.resize validity
4451         rect.resize(102,102).draw(Color::red);
4452
4453         // check rect internal bordar and Rectangle.resize validity
4454         rect.resize(100,100).draw(Color::green);
4455
4456         // rect reference
4457         rect.resize(98,98).draw(Color::gray);
4458
4459
4460         rect.resize(100,100);
4461
4462         // check Canvas.to(Image, Rectangle) validity
4463         Image img;
4464         d.to(img, rect);
4465         img.centering().shift(0,-200).draw();
4466         d.msg(&quot;Canvas.to(Image)&quot;, d.getHcenter(), d.getVcenter()-200);
4467
4468         // check Canvas.copy(Rectangle, Rectangle) validity
4469         d.copy(rect, rect.dup().shift(200,0));
4470         d.msg(&quot;Canvas.copy&quot;, d.getHcenter()+200, d.getVcenter());
4471         d.flip();
4472
4473         // Wating roop
4474         while(!Input::get(Keyboard::esc)) {
4475         }
4476
4477 }
4478 }}}
4479 </pre>
4480 </div>
4481 <div title="3.5 文字列のレンダリングの仕様と関連情報" modifier="Psychlops_DevelopperG" modified="200910260739" created="200910260730" changecount="1">
4482 <pre>[[2.3節|2.3 文字列の描画]]ではLettersクラスを使った2バイト文字列の描画について最低限の事だけを説明しました。実はLettersクラスはImageクラスを応用して作成されています。
4483 ここでは、やや応用的ですが、本節で説明したImageクラスの仕様を踏まえてより詳細なLettersクラスの仕様と関連情報について説明します。
4484 * [[レンダリングのタイミング]]
4485 * [[ワイド文字列の入出力]]</pre>
4486 </div>
4487 <div title="4. 複雑な描画を行う2(複数オブジェクトのグループ化と回転・拡大縮小)" modifier="Psychlops_DevelopperG" modified="200912010726" created="200909300419" changecount="4">
4488 <pre>この章ではここまで用いてきたいくつかの描画のためのクラスをまとめて一つのグループとして取り扱う方法を紹介します。
4489 たとえば、いくつかのRectangleやImageをまとめて動かしたい時に、それぞれに対してshiftメソッドを使うというやり方は少し面倒です。
4490 こんな時に、これらを一つのグループにまとめてしまって、そのグループ全体に対して移動の操作をすることができれば、プログラムはかなり見通しがよくなるでしょう。
4491 また、描画したものをある点を中心に回転させたいというような時に、回転のための数学操作を考えるのはかなり面倒です。
4492 PsychlopsはOpenGLを用いて描画を行っているのですが、OpenGLは描画内容の回転や拡大・縮小をグラフィックチップに計算させることが簡単にできます。Psychlopsではグループ化したオブジェクトに対してこれらの変換を行うことが可能です。
4493
4494 [[4.1 複数の描画オブジェクトをグループ化するーGroupクラスー]]
4495 [[4.2 描画内容の回転および拡大縮小]]
4496
4497
4498 </pre>
4499 </div>
4500 <div title="4.1 複数の描画オブジェクトをグループ化するーGroupクラスー" modifier="Psychlops_DevelopperG" modified="200912010828" created="200912010750" changecount="27">
4501 <pre>!Groupクラスの構造
4502
4503 1. Groupクラスの変数を宣言する
4504 2. GroupクラスにShapeを追加する
4505 3. Groupクラスに対して操作を行う
4506 4.(必要に応じて)ShapeをGroupから外す
4507
4508 ! 1. Groupクラスの宣言
4509 グループクラスの変数宣言は特に引数などは必要ではありません。
4510 Canvasの取得後である必要がある?
4511 {{{
4512 code sample
4513 }}}
4514
4515 ! 2. GroupクラスにShapeを追加する
4516  ここまで取り扱ってきたRectangleやImageといったクラスはすべてShapeという大きなクラスを元に構成されています(クラスの継承)。Shapeクラスに属するクラスは以下の通りです。
4517 |Line|線領域|
4518 |Rectangle|四角形の領域|
4519 |Polygon|多角形の領域|
4520 |Ellipse|円を含む楕円形の領域|
4521 |Letters|文字列を格納するための領域(テキストボックス)|
4522
4523 Shapeクラスの詳細については[[]]
4524 これらのクラスはここまででも扱ってきたようなcentering(), shift(), draw()などの共通する命令セットを持っています。これらの命令が実際にどのように振る舞うかはクラスの内容に合わせて異なっていますが、引数などの命令の形はどれも共通になっています。これらの共通の命令セットの一つとしてadd()命令があります。
4525
4526 |!Shape::add()|add(Group gp)|&gt;|Shapeをグループクラスに追加します。|
4527 |~|~|Group gp|追加先のグループを指定|
4528
4529 コードの説明
4530 {{{
4531 code sample
4532 }}}
4533
4534 またGroupに対してappend()命令を使用しても同じことができます。
4535 使いやすい方を使うとよいでしょう。
4536 |!Group::append()|append(Shape shp)|&gt;|Shapeをグループクラスに追加します。|
4537 |~|~|Shape shp|追加するShapeを指定|
4538
4539 コードの説明
4540 {{{
4541 code sample
4542 }}}</pre>
4543 </div>
4544 <div title="5. 入出力を行う" modifier="Psychlops_DevelopperG" modified="200908190158" created="200708211945" changecount="3">
4545 <pre>[[5.1 外部装置(キーボード/マウス)の操作内容を取得する]] 
4546 [[5.2 ファイルの入出力]]</pre>
4547 </div>
4548 <div title="5.1 外部装置(キーボード/マウス)の操作内容を取得する" modifier="Psychlops_DevelopperG" modified="200908190201" created="200708232250" changecount="2">
4549 <pre>!Inputクラスとは
4550 コンピュータにあるキーボードやマウスから反応を取得するためのクラスとしてInputクラスがあります。
4551 現バージョンでPsychlopsはキーボードとマウスの反応を取得することが可能です。Psychlopsでは、キーボードのそれぞれのキーやマウスのボタン、カーソル位置をそれぞれKeyboard, Mouseクラスのメンバとして取り扱っています。具体的な使い方を以下で説明します。
4552
4553 !Input::get() -反応を取得する-
4554 これまでのソースの中でwhile(!Input::get(Keyboard::spc))という命令をスペースキー待ちの命令として使ってきました。これは、&quot;スペースキーが押されるまでループを繰り返しなさい&quot;という命令です。この文でも使っているようにInput::get()命令は引数として指定された入力機器の反応があれば1(TRUE)を返します。入力が無ければは0(FALSE)を返します。Input::get()命令の第2引数は省略可能です。この場合には、Input::get()命令は~KeyStateにはpushedが指定されたとみなします。
4555 |!Input::get()|Input::get([[Keyboard::KeyCode|キーコード表]] code, [[Keyboard::KeyState]] state)|キーボードの反応を取得します|
4556 |~|~|code 反応を取得するキーの名前 [[キーコード表]] 参照|
4557 |~|~|state 反応状態のオプション (省略時はpushed) [[Keyboard::KeyState]]参照|
4558 |~|Input::get([[Mouse::ButtonCode|キーコード表]] code, [[Mouse::ButtonState]] state)|マウスの反応を取得します|
4559 |~|~|code 反応を取得するマウスボタンの名前 [[キーコード表]] 参照|
4560 |~|~|state 反応状態のオプション (省略時はpushed) [[Mouse::ButtonState]]参照|
4561
4562 !! ~KeyState(~MouseState)の使い方
4563 第2引数のの&quot;~KeyState&quot;・&quot;~MouseState&quot;の使い方は少し難しいかもしれません。以下でこれらの&quot;~KeyState&quot;の使い方を見てみましょう。
4564 ~KeyStateには&quot;pushed&quot;, &quot;pressed&quot;, &quot;released&quot;の3つのモードが存在します。これら3つのモードの意味を実例とともに見ていきましょう。
4565
4566 はじめに&quot;pushed&quot;を使った例を見てみましょう。ここでは、Input::get()命令の第2引数として&quot;pushed&quot;を明示的に指示しましたが、省略しても、挙動は変わりません。
4567 [[例1: pushedを使ったソース]]
4568 このソースを実行すると中央に表示された四角形がカーソルの左右キーで左右に動きます。キーを押し続けても一度しかシフトが起こらないことに注目してください。&quot;pushed&quot;を指定したときにInput::get()命令がTRUEを返すのは、キーが押し込まれたフレームより後に実行されたうちのはじめの一回だけです。キーを離すと元の状態に戻って、またキーが押し込まれたフレーム以降に一度だけTRUEが返されます。
4569
4570 次に、&quot;pressed&quot;を指定した例です。例1とソースはほとんど変わりませんが、Input::get()命令の第2引数だけが&quot;pressed&quot;に変わっています。
4571 [[例2: pressedを使ったソース]]
4572 このソースを実行すると、やはりカーソルキーにしたがって四角形が左右に動きますが、例1と違って押している間、常に動き続けています。&quot;pressed&quot;を指定すると、キーが押し込まれている間ずっとInput::get()命令はTRUEを返します。
4573
4574 次に&quot;released&quot;を使った例について見てみましょう
4575 [[例3: releasedを使ったソース]]
4576 この例では、例2に&quot;released&quot;を指定したInput::get()命令の文が付け加わっています。実行してみると、四角形はやはりキーを押している間、常に動き続けますが、キーを離すと中央に戻ってしまいます。キーが離されたことが&quot;released&quot;を指定したInput::get()命令によって検出されると、四角形がcentering()命令を使って中央に戻るようにコードが組まれています。このように&quot;released&quot;は指定したキーが離されたことを検出するためのオプションです。
4577 ここで、例の中にInput::refresh()という命令が使われています。これは、キーボードやマウスのボタン検出を初期化する命令です。複雑な入出力動作をするプログラムを書くときにはこの命令でキーボード・マウスの初期化をしておくことをおすすめします。
4578 |!Input::refresh()|refresh(void)|キーボード・マウスの入出力ルーチンを初期化します|
4579 参考[[Tips: Psychlopsにおけるキーボードマウスの入出力検出ルーチン]]
4580
4581
4582 最後に、これらをまとめて比較するためのデモプログラム例を載せておきます。a/sでpushedのデモ, z/xでpressed, q/wでpressed &amp; released, cで四角形の位置初期化です。
4583 [[例4: pushed,pressed,releasedを使ったソース]]
4584
4585 以上では、キーボードの入力取得オプションである&quot;~KeyState&quot;を例として扱いましたが、&quot;~MouseState&quot;の使い方もまったく同じです。以下に&quot;~MouseState&quot;を使った簡単なプログラム例を挙げます。上の例2とほとんど変わりません。右クリックで右方向、左クリックで左方向へ移動します。
4586 [[例5: Mouse::pressedを使ったソース]]
4587
4588 ! カーソル座標の取得 とマウスカーソルの表示切替
4589 マウスの座標はMouseクラスのメンバMouse::x(水平座標), Mouse::y(垂直座標)に記録されています。このメンバに対して代入を行うとマウスの位置も移動します。
4590 |!Mouseのメンバ|Mouse::x|マウスの水平座標(代入可能)|
4591 |~|Mouse::y|マウスの水平座標(垂直座標)|
4592 マウスカーソルの表示切替はMouse::show(), Mouse::hide()命令を使用してください。
4593 |!Mouse::show()|show()|マウスカーソルを表示する|
4594 |!Mouse::hide()|hide()|マウスカーソルを非表示にする|
4595 次の例では、マウスカーソルを最初に画面中央に移動した後に、ユーザーのマウスの動きに合わせてマウスカーソルを動かします。左クリックすると、マウスカーソルの下にあるオブジェクトを選択して、ドラッグできます。また、右クリックでマウスカーソルを描画しないようにします。
4596 [[例6: マウスのデモ]]
4597 </pre>
4598 </div>
4599 <div title="5.2 ファイルの入出力" modifier="Psychlops_DevelopperG" modified="200908190200" created="200709170703" changecount="2">
4600 <pre>この節では簡易的なファイルの入出力命令について説明します。
4601 PsychlopsはC, C++の標準命令と混在が可能なので、そちらの方法になれている方は、標準の入出力関数を用いてデータの保存、読み込みを行っていただいても問題ありません。
4602 しかし、単にプログラム中で用いた配列に格納されたデータを保存するだけの時に標準の入出力関数を使うのが面倒な時があります。
4603 Psychlopsではファイルの入出力を簡易的に行う命令Data::savearray(), Data::loadarray()が準備されています。ただし、これらの命令を使用してデータを保存するには以下の条件が満たされている必要があります。
4604
4605 # 保存する複数の配列のサイズが同じ
4606 # 保存する配列の数が20個以下
4607
4608 Data::loadarray()も同様にサイズが同じ20個以下の配列を読み込むことができます。
4609
4610 [[5.2.1 ファイルへの出力]]
4611 [[5.2.2 ファイルからの入力]]</pre>
4612 </div>
4613 <div title="5.2.1 ファイルへの出力" modifier="Psychlops_DevelopperG" modified="200908190159" created="200709170712" changecount="1">
4614 <pre>!ファイルへの出力
4615 はじめに配列を保存するための命令Data::savearray()から説明を行います。
4616
4617 |!Data::savearray()|savearray(filename, header, length, ...)|指定した配列を保存します|&gt;|&gt;|
4618 |~|~|filename: 保存する配列のファイル名称(拡張子まで)を指定|&gt;|&gt;|
4619 |~|~|保存先を指定しない場合のパス|Mac:Macintosh HD/ユーザ/ユーザ名(任意)/書類/Psychlops/ ^^*1^^|&gt;|
4620 |~|~|~|Win:実行ファイルと同じパス|&gt;|
4621 |~|~|保存をする場合のパス指定|%APP%|Mac:実行バンドル(.app)と同じパス|
4622 |~|~|~|~|Win:実行ファイルと同じパス|
4623 |~|~|~|%RESOURCES%|Mac:実行バンドルの内部のResourcesフォルダ|
4624 |~|~|~|~|Win:実行ファイルのパスの直下のrscという名前のフォルダ|
4625 |~|~|~|%~USER_HOME%|Mac:&quot;~/&quot;と同じ(/Users/(user)/)|
4626 |~|~|~|~|Win:ユーザープロファイルフォルダ(C:\Documents and Settings\(user)\)|
4627 |~|~|~|%~USER_SETTING%|Mac:&quot;~/Library/Psychlops/&quot;|
4628 |~|~|~|~|Win:&quot;%~AppData%Psychlops&quot;|
4629 |~|~|~|%~USER_DOCUMENTS%|Mac:&quot;~/Documents/Psychlops&quot;|
4630 |~|~|~|~|Win:ユーザープロファイルフォルダ\My Documents\Psychlops|
4631 |~|~|~|%~USER_DOCUMENTS_ROOT%|Mac:&quot;~/Documents&quot;|
4632 |~|~|~|~|Win:ユーザープロファイルフォルダMy Documents\|
4633 |~|~|ファイル名に%TIME_という文字列があると、日時分秒に自動変換されます|&gt;|
4634 |~|~|header: 配列の冒頭に加える説明などを記述します|&gt;|&gt;|
4635 |~|~|length: 保存する配列の大きさを指定します|&gt;|&gt;|
4636 |~|~|...: 保存する配列を指定します。最大20個まで指定できます|&gt;|&gt;|
4637 *1 Finderでの表示は上記のとおりですが、Psychlopsで指定する場合は下記のUnix形式でパスを指定してください
4638 ** &quot;~/Documents/Psychlops/&quot;
4639 *2 MacOSX版では、日本語パス名は使用できません。強制終了してしまいます。
4640 *3 VISTA環境の場合、パス指定が正常に動作しません。VISTA環境の方はお手数ですが保存先を指定しないでお使いください。
4641
4642
4643 以下でこの命令の使い方の例を見てみましょう。
4644 {{{
4645 &lt;例1&gt;
4646 #include &lt;psychlops.h&gt;
4647 using namespace Psychlops;
4648
4649 const int MaxTrials = 100;
4650 int condition[MaxTrials], result[MaxTrials]
4651
4652 void psychlops_main() {
4653
4654         for(int CurrentTrial=0; CurrentTrial&lt;MaxTrials; CurrentTrial++) {
4655
4656                 condition[CurrentTrial] = Psychlops::random(2);
4657
4658                 // ...実験の描画処理
4659
4660                 if(Input::get(Keyboard::left))
4661                         result[CurrentTrial] = 0;
4662                 if(Input::get(Keyboard::right))
4663                         result[CurrentTrial] = 1;
4664
4665         }
4666
4667 }
4668
4669 }}}
4670 上のプログラム例では、変数配列resultには、被験者のキー押しによって0か1かの数値が入ります。この配列を配列conditionに保存されている実験条件とともにsavearray()命令を用いて、”sample_保存時間.txt&quot;という名前のファイルに保存するには以下のように命令を書きます。
4671 {{{
4672 Data::savearray(&quot;sample_%TIME_.txt&quot;, &quot;conditions\tresults&quot;, MaxTrials, condition, result);
4673 }}}
4674
4675 第1引数はファイル名の指定、第2引数はファイルのヘッダ(1行目の内容)です。conditionおよび、resultの配列の個数はMaxTrials(100)個なので、第3引数はMaxTrials, それ以下に実際の配列の名前であるcondition, resultが続きます。
4676
4677 実際のプログラムは以下の様になります。
4678
4679 {{{
4680 &lt;例2&gt;
4681 #include &lt;psychlops.h&gt;
4682 using namespace Psychlops;
4683
4684 const int MaxTrials = 100;
4685 int condition[MaxTrials], result[MaxTrials]
4686
4687 void psychlops_main() {
4688
4689         for(int CurrentTrial=0; CurrentTrial&lt;MaxTrials; CurrentTrial++) {
4690
4691                 condition[CurrentTrial] = Psychlops::random(2);
4692
4693                 // ...実験の描画処理
4694
4695                 if(Input::get(Keyboard::left))
4696                         result[CurrentTrial] = 0;
4697                 if(Input::get(Keyboard::right))
4698                         result[CurrentTrial] = 1;
4699
4700         }
4701
4702         Data::savearray(&quot;sample_%TIME_.txt&quot;, &quot;conditions\tresults&quot;, MaxTrials, condition, result);
4703
4704 }
4705
4706 }}}
4707 標準の出力パスに「sample_(日時).txt」というファイルが出力されたでしょうか?
4708
4709 ファイルは、配列を縦に並べたタブ区切り形式となります。上記の例を適用すると
4710 | ヘッダ| |
4711 | Condition[0] | Result[0] |
4712 | Condition[1] | Result[1] |
4713 | Condition[2] | Result[2] |
4714 | ...          | ...       |
4715 の順番で記録されています。このファイルを実際に開くと、以下の例のようになっています。
4716 {{{
4717 &lt;例3&gt;
4718
4719 conditions results
4720 0       1
4721 1       1
4722 0       0
4723 0       1
4724 1       0
4725 ...
4726 }}}
4727 この結果ファイルはそのままExcelなどに読み込むことが出来ます。
4728 </pre>
4729 </div>
4730 <div title="5.2.2 ファイルからの入力" modifier="Psychlops_DevelopperG" modified="200908190200" created="200709170714" changecount="1">
4731 <pre>Data::savearray()とは逆に、ファイルからデータを読み込むことも出来ます。
4732 データは配列に読み込まれるため、savearrayで書き込んだものをそのまま読み込むことが出来ます。
4733
4734
4735 !Data::savearray()
4736 テキストファイルから配列に読み込みます。
4737
4738 |!Data::savearray()|savearray(filename, skipped_lines, length, ...)|指定した配列を読み込みます|&gt;|&gt;|
4739 |~|~|filename: 読み込むファイル名称(拡張子まで)を指定|&gt;|&gt;|
4740 |~|~|保存先を指定しない場合のパス|Mac:Macintosh HD/ユーザ/ユーザ名(任意)/書類/Psychlops/ ^^*1^^|&gt;|
4741 |~|~|~|Win:実行ファイルと同じパス|&gt;|
4742 |~|~|読み込む場合のパス指定|%APP%|Mac:実行バンドル(.app)と同じパス|
4743 |~|~|~|~|Win:実行ファイルと同じパス|
4744 |~|~|~|%RESOURCES%|Mac:実行バンドルの内部のResourcesフォルダ|
4745 |~|~|~|~|Win:実行ファイルのパスの直下のrscという名前のフォルダ|
4746 |~|~|~|%~USER_HOME%|Mac:&quot;~/&quot;と同じ(/Users/(user)/)|
4747 |~|~|~|~|Win:ユーザープロファイルフォルダ(C:\Documents and Settings\(user)\)|
4748 |~|~|~|%~USER_SETTING%|Mac:&quot;~/Library/Psychlops/&quot;|
4749 |~|~|~|~|Win:&quot;%~AppData%Psychlops&quot;|
4750 |~|~|~|%~USER_DOCUMENTS%|Mac:&quot;~/Documents/Psychlops&quot;|
4751 |~|~|~|~|Win:ユーザープロファイルフォルダ\My Documents\Psychlops|
4752 |~|~|~|%~USER_DOCUMENTS_ROOT%|Mac:&quot;~/Documents&quot;|
4753 |~|~|~|~|Win:ユーザープロファイルフォルダMy Documents\|
4754 |~|~|skipped_lines: 冒頭から読み込みを飛ばす行数を指定します|&gt;|&gt;|
4755 |~|~|length: 読み込む配列の大きさを指定します|&gt;|&gt;|
4756 |~|~|...: 読み込む配列を指定します。最大20個まで指定できます|&gt;|&gt;|
4757 *1 Finderでの表示は上記のとおりですが、Psychlopsで指定する場合は下記のUnix形式でパスを指定してください
4758 ** &quot;~/Documents/Psychlops/&quot;
4759 *2 MacOSX版では、日本語パス名は使用できません。強制終了してしまいます。
4760 *3 VISTA環境の場合、パス指定が正常に動作しません。VISTA環境の方はお手数ですが保存先を指定しないでお使いください。
4761
4762
4763
4764 savearrayで保存したファイルを読み込んでみましょう。savearrayでは通常冒頭2行にファイルの説明を加えるため、skipped_linesには2を指定します。ファイル名中で%TIME_を指定すると現在時刻に変換されてしまうため、読み込むファイルはあらかじめ別の名前にしておきます。ここではsample.txtとしました。
4765 {{{
4766 &lt;例1&gt;
4767 #include &lt;psychlops.h&gt;
4768 using namespace Psychlops;
4769
4770 const int MaxTrials = 100;
4771 int condition[MaxTrials], result[MaxTrials]
4772
4773 void psychlops_main() {
4774
4775         Data::loadarray(&quot;sample.txt&quot;, 2, MaxTrials, condition, result)
4776
4777 }
4778
4779 }}}
4780 これでsample.txtの中身が配列に読み込まれました</pre>
4781 </div>
4782 <div title="6. 時間制御と各種ツールを使用する" modifier="Psychlops_DevelopperG" modified="200908190210" created="200709132309" changecount="6">
4783 <pre>[[6.1 時間を計測する]] 
4784 [[6.2 各種ツールを使ってみる]] 
4785   [[6.2.1 描画の時間精度を確認する-FPSチェッカー]]
4786   [[6.2.2 画面に反映されない描画の進行度を表示する-プログレスバー]]
4787 [[6.3 行列演算を行う]]
4788   [[6.3.1 行列の宣言]]
4789   [[6.3.2 行列の要素の操作]]
4790   [[6.3.3 行列全体の操作]]
4791   [[6.3.4 行列の加減乗除]]
4792   [[6.3.5 行列の中身をオフスクリーンに描画する]]
4793   [[6.3.6 メッシュグリッドの作成]]</pre>
4794 </div>
4795 <div title="6.1 時間を計測する" modifier="Psychlops_DevelopperG" modified="200908190204" created="200708242225" changecount="1">
4796 <pre>!Clockクラスの概要
4797 Psychlopsで時間を計測する方法は2種類あります。
4798 画面のリフレッシュを基準として、リフレッシュされたフレーム数をカウントすることです。この方法は比較的簡単ですが、1フレームの時間以上の精度で時間を制御することができません。
4799 そこで、より高い精度で時間を計測するためのクラスとしてPsychlopsにはClockクラスが用意されています。このClockクラスはCPUタイマーを利用することによって高い精度の時間間隔の計測を実現しています。
4800
4801 !Clockクラスの使い方
4802 Clockの宣言は特に難しくありません。[[Psychlops::Clock]]型のインスタンスを以下のような形式で単に宣言するだけです。
4803 {{{
4804 Psychlops::clock Timer;
4805 }}}
4806 このTimerに現在のCPUタイマーの値を入力するにはClock::update()命令を使用します。
4807 |!Clock::update()| Clock::update(void)| 現在のCPUタイマーの値を入力する|
4808 このままでは、保持されている時刻はCPUタイマーのカウントなので、実際にプログラム内でこのクラスを使うときにはClock::at_msec()命令を使用してタイマーのカウントをmsec単位に変換して使用します。
4809 |!Clock::at_msec()| double Clock::at_msec(void)| カウントをmsec単位に変換(double)して返す|
4810
4811 下にClockクラスを用いた簡単な時間計測例を挙げて見ました。スペースキーを押している時間が画面に表示されます。
4812 {{{
4813 * Clockクラスの使用例
4814 #include &lt;psychlops.h&gt;
4815 using namespace Psychlops;
4816
4817 void psychlops_main() {
4818  
4819  Canvas sampleA(Canvas::fullscreen);
4820  double elapsed=0.0, dcx, dcy;
4821  Clock Start, End;
4822  
4823  dcx=sampleA.getHcenter();
4824  dcy=sampleA.getVcenter();
4825  sampleA.clear(Color(0.5));
4826  sampleA.flip();
4827  
4828  while(!Input::get(Keyboard::esc)) {
4829  
4830         if(Input::get(Keyboard::spc, Keyboard::released)){
4831                 End.update();
4832                 elapsed=(End-Start).at_msec();
4833                 sampleA.clear(Color(0.5));
4834                 sampleA.flip();
4835                 sampleA.clear(Color(0.5));
4836                 sampleA.msg(&quot;Elapsed Time(msec)&quot;, dcx-100,dcy-30, Color::white);
4837                 sampleA.var((int)elapsed,dcx ,dcy, Color::white);
4838                 sampleA.flip();
4839                 }
4840   
4841         if(Input::get(Keyboard::spc, Keyboard::pushed)){
4842                 Start.update();
4843                 Input::refresh();
4844                 } 
4845         }
4846 }
4847 }}}
4848 ^^ごく稀にWindows環境下でこのプログラムを実行すると、キー押し時間がマイナスになることがあるようです。このような状態が起こったら、並行して動いているプログラムを終了させて、再起動をしてからこのプログラムを実行してみてください。参考[[Tips: 時間精度が必要なプログラムを実行するとき]]^^</pre>
4849 </div>
4850 <div title="6.2 各種ツールを使ってみる" modifier="Psychlops_DevelopperG" modified="200908190206" created="200709120042" changecount="3">
4851 <pre>Psychlopsにはここまで扱ったような簡単な描画命令だけではなく、描画内容をチェックするためのツールが実装されています。ここでは、これらのツールについて説明します。
4852 [[6.2.1 描画の時間精度を確認する-FPSチェッカー]]
4853 [[6.2.2 画面に反映されない描画の進行度を表示する-プログレスバー]]</pre>
4854 </div>
4855 <div title="6.2.1 描画の時間精度を確認する-FPSチェッカー" modifier="Psychlops_DevelopperG" modified="200908190205" created="200709120043" changecount="1">
4856 <pre>!!FPSチェッカの概要
4857 Psychlopsには、現在描画している画面がプログラムで指定した時間通りに実行されているかどうかをチェックするツールがあります。
4858 たとえば、以下のようなコードを考えて見ます。
4859 {{{
4860 void psychlops_main() {
4861         Canvas sampleA(Canvas::fullscreen);
4862         while(true){
4863         非常に時間のかかる描画コード
4864         sampleA.flip(); 
4865 }
4866 }}}
4867 このプログラムを書いたときの意図は、描画を毎フレームごとに更新するというものでしょう。(でなければ、なんらかの「待ち」処理がsampleA.flip()の前後に入っているはずです。)
4868 しかし、非常に複雑な描画内容をCanvasの裏画面に書いたり、大きなImageを転送するときは、描画に1フレーム分以上の時間かかることがあります。この場合、psychlopsは描画が完了するまで待ってから[[Canvas::flip()]]命令を実行するので実際に描画が更新されるのは2フレーム以上たってからということになります。
4869 問題は、このような&quot;こま落ち&quot;はただ見ているだけではわからないことが多いことです(複雑な刺激呈示の途中に16.7 msと33.3msの区別がつくでしょうか?)。
4870 psychlopsにはこのような&quot;こま落ち&quot;を検出するためのFPSチェッカと呼ばれるツールが準備されています。
4871 このFPSチェッカを使うには、まず時間計測を行いたい部分の前にFPSチェッカを起動する命令を書きます。
4872 これには、Canvas::watchFPS()命令を使用します。
4873 |!Canvas::watchFPS()|void watchFPS(void)|FPSチェッカをonにします|
4874 このままでは、FPSチェッカが起動しているだけで、その内容を見ることはできません。
4875 FPSチェッカの内容を表示するためには、[[Canvas::flip()]]命令の直前にCanvas::showFPS()命令を書いてFPSチェッカのウィンドウを表示させます。
4876 |!Canvas::showFPS()|void showFPS(void)|FPSチェッカのレポートを画面左上に表示します|
4877 Canvas::showFPS()命令を実行すると、半透明のレポートウィンドウが画面右上に表示されます。このレポートウィンドウは、すべての描画内容の上に重ねて表示されます(以下を参照してください)
4878
4879 !!FPSチェッカの使い方
4880 FPSチェッカを実際に使ってみましょう
4881 以下のコードは5フレームに一度描画内容を更新して、コマ落ちが発生しているかどうかを表示します。
4882 {{{
4883 #include &lt;psychlops.h&gt;
4884 using namespace Psychlops;
4885
4886 void psychlops_main() {
4887         Canvas sampleA(Canvas::fullscreen);
4888         sampleA.watchFPS();
4889         while(!Input::get(Keyboard::spc)){  
4890                 sampleA.showFPS();
4891                 sampleA.clear(0.5);
4892                 sampleA.flip(5); 
4893         }
4894 }
4895 }}}
4896
4897 このコードを60Hzのディスプレイで実行すると、以下のような画面が表示されます。
4898
4899 [img[image/FPSchecker.png]]
4900
4901 showFPS()命令を実行した後に、Canvas::clear()命令を実行しているにもかかわらずレポートウィンドウが表示されていることに注意してください。このように、実際にはレポートウィンドウは全ての描画が終了した後に描画されます。
4902 このレポートウィンドウを拡大したものが下図です。
4903
4904 [img[image/FPSwindow.png]]
4905
4906 レポートウィンドウは3行から成り立っています。一番上の赤い数字は、これまでにコマ落ちしたフレーム数、真ん中の白い数字は表示された総フレーム数、一番下の青い数字は前回のflip()から今回のflip()までにかかった時間(msec単位)です。
4907 この例では、5フレームに一度描画が更新されているので、60Hzだと画面更新間の時間は1/12 sec = 83.3 msecで、一番下の青い数字と一致していることに注目してください。</pre>
4908 </div>
4909 <div title="6.2.2 画面に反映されない描画の進行度を表示する-プログレスバー" modifier="Psychlops_DevelopperG" modified="200908190206" created="200709252142" changecount="1">
4910 <pre>!プログレスバーの概要
4911 大量の計算をコンピュータに行わせているとき、画面には何の変化も起きずにプログラムが暴走しているのか、単に計算に時間がかかっているだけなのかの区別がつかないときが良くあります(本当は、暴走をさせないようなプログラム構造にするべきですが・・・)。進行度を文字で画面に表示する方法もありますが、文字表示のために[[Canvas::flip()]]命令を挟んでしまうと計算速度が落ちてしまいます。
4912 このような状況で、画面に計算の総量に対して今どのくらいの割合が終了したかを表示するためのツールにプログレスバーがあります。このプログレスバーは強制的にフロントバッファ(現在表示中の画面)に表示されるため、[[Canvas::flip()]]命令は必要ありません。
4913
4914 このプログレスバーを表示するにはCanvas::progressbar()命令を使用します。
4915 |!Canvas::progressbar()|void progressbar(X now, X max)|現在のnowの値のmaxに対する比率をプログレスバーとして表示します。now, maxは数値型であればどの型でもかまいません|&gt;|
4916 |~|~|X now|現在の進行度を示す変数値(型は数値型)|
4917 |~|~|X max|nowの最終的な到達値(型は数値型)|
4918 |~|void progressbar(double ratio)|ratioで示される比率をプログレスバーとして表示します。|&gt;|
4919 |~|~|double ratio|比率の値(0.0~1.0)|
4920 この命令を使用すると、画面左上に灰色の四角のなかに時間とともに伸びる青い四角形が表示されます。この青い四角形が灰色の四角いっぱいまで伸びると、nowの値がmaxまで到達した(ratioが1.0になる)ことを示しています。
4921
4922
4923 !!プログレスバーの使い方
4924 実際の使い方の例を見てみましょう。
4925 下の例では100 msecと50 msecの&quot;待ち&quot;を100回繰り返します。
4926 時間経過とともに左上に提示されるプログレスバーが伸びていく様子に注目してください。
4927
4928 {{{
4929 #include &lt;psychlops.h&gt;
4930 using namespace Psychlops;
4931
4932 void psychlops_main() {
4933
4934         Canvas sampleA(Canvas::fullscreen);
4935         Clock timer1, timer2; 
4936         int temp;
4937
4938         sampleA.watchFPS();
4939
4940         sampleA.clear(0.5);
4941         sampleA.message(&quot;calculating...&quot;, sampleA.getHcenter(), sampleA.getVcenter());
4942         sampleA.flip();
4943
4944         temp=0;
4945         for(int i=0; i&lt;100; i++){
4946                 timer1.update();
4947                 sampleA.progressbar(i,100);
4948                 while((timer2-timer1).at_msec()&lt;100){timer2.update();}
4949                 if(Input::get(Keyboard::esc))break;
4950         }
4951  
4952         sampleA.clear(0.5);
4953         sampleA.message(&quot;10 sec elapsed&quot;, sampleA.getHcenter(), sampleA.getVcenter());
4954         sampleA.flip();
4955         while(!Input::get(Keyboard::spc));
4956         sampleA.clear(0.5);
4957         sampleA.message(&quot;calculating...&quot;, sampleA.getHcenter(), sampleA.getVcenter());
4958         sampleA.flip();
4959
4960
4961         temp=0;
4962         for(int i=0; i&lt;100; i++){
4963                 timer1.update();
4964                 sampleA.progressbar((double)i/100.0);
4965                 while((timer2-timer1).at_msec()&lt;50){timer2.update();}
4966                 if(Input::get(Keyboard::esc))break;
4967         }
4968
4969         sampleA.clear(0.5);
4970         sampleA.message(&quot;5 sec elapsed&quot;, sampleA.getHcenter(), sampleA.getVcenter());
4971         sampleA.flip();
4972         while(!Input::get(Keyboard::spc));
4973 }
4974 }}}</pre>
4975 </div>
4976 <div title="6.3 行列演算を行う" modifier="Psychlops_DevelopperG" modified="200908190211" created="200709252143" changecount="3">
4977 <pre>!Matrixクラスの概要
4978 !!行列とは?
4979 Matrixクラスは行列を取り扱います。線形代数としての取り扱いもできますが、Matlabの行列のように数値を2次元的に配置した表や画像(Image)のようなものとしても利用できます。Imageは精度が低いため、Imageに保存した色データをもとに再計算するのはお勧めできませんが、Matrixではそのような計算をしても十分な精度があります。
4980
4981 !!MatrixとImageの違い
4982 MatrixとImageは基本的に2次元のよく似たデータ構造を持っており、相互に変換が可能です。(実際にはImageは下で述べるようなより複雑なデータ構造を持っています。)ただし、両者にはいくつかの相違点があります。
4983 [Img[Image/Image_Matrix.png]]
4984
4985 * Matrixの要素アクセスは行(縦)列(横)の順で指定し、起点は1です。ImageはX(横)Y(縦)の順で指定し、起点は0です。
4986 [Img[Image/ImagetoMatrix.png]]
4987
4988
4989 * Imageは一つのx,y値に対してRGB 3値、RGBA 4値を取り扱うことが出来ます(数学的にはテンソルの構造を持っています)。Matrixは行列の要素として一つの値しか取り扱えないため、3値または4値のImageに変換する場合、それぞれの座標におけるR,G,B,(A)の値を示す同じ次元を持った3つまたは4つの行列を用意する必要があります。
4990 [Img[Image/ColImagetoMatrix.png]]
4991
4992
4993 * Matrixは精度を重視して64bit浮動小数点(double型)を取り扱います。Imageは速度を重視して8bit整数(unsigned char型)を基本として取り扱います(32bit浮動小数点 float型にすることもできます)
4994
4995 [[6.3.1 行列の宣言]]
4996 [[6.3.2 行列の要素の操作]]
4997 [[6.3.3 行列全体の操作]]
4998 [[6.3.4 行列の加減乗除]]
4999 [[6.3.5 行列の中身をオフスクリーンに描画する]]
5000 [[6.3.6 メッシュグリッドの作成]]</pre>
5001 </div>
5002 <div title="6.3.1 行列の宣言" modifier="Psychlops_DevelopperG" modified="200908190207" created="200710171011" changecount="1">
5003 <pre>!Matrixクラスの概要
5004 !!行列とは?
5005 Matrixクラスは行列を取り扱います。線形代数としての取り扱いもできますが、Matlabの行列のように数値を2次元的に配置した表や画像(Image)のようなものとしても利用できます。Imageは精度が低いため、Imageに保存した色データをもとに再計算するのはお勧めできませんが、Matrixではそのような計算をしても十分な精度があります。
5006
5007 !!MatrixとImageの違い
5008 MatrixとImageはよく似ており、相互に変換が可能です。
5009
5010 * Matrixは精度を重視して64bit浮動小数点(double型)を取り扱います。Imageは速度を重視して8bit整数(unsigned char型)を基本として取り扱います(32bit浮動小数点 float型にすることもできます)
5011 * ImageはRGB 3値、RGBA 4値を取り扱うことが出来ます。Matrixは1値しか取り扱えないため、3値または4値のImageに変換する場合、3つまたは4つの行列を用意する必要があります。
5012 * Matrixの要素アクセスは行(縦)列(横)の順で指定し、起点は1です。ImageはX(横)Y(縦)の順で指定し、起点は0です。
5013
5014 !行列の宣言文
5015 行列を確保するには、行数と列数を指定します。
5016 Matrix()のように宣言のみの場合は''Matrix::set()''命令で指定します。
5017
5018 |!Matrixの宣言|Matrix()|行列の使用を宣言します|
5019 |~|Matrix(rows, cols)|指定されたrows行cols列の行列を宣言します|
5020 |~|~|long rows :行数を指定|
5021 |~|~|long cols :列数を指定|
5022
5023
5024 !Matrix::set()
5025 行列の宣言時に何の指定もしなかった場合、Matrix::set()命令を使用します。
5026 |!Matrix::set()|Matrix(rows, cols)|指定されたrows行cols列の行列を設定します|
5027 |~|~|long rows :行数を指定|
5028 |~|~|long cols :列数を指定|
5029
5030
5031 !!宣言文の書き方
5032 {{{
5033 &lt;例1&gt;
5034 #include &lt;fstream&gt;
5035 #include &lt;psychlops.h&gt;
5036 using namespace Psychlops;
5037
5038 Matrix LuminanceMap1(200, 100);
5039
5040 void psychlops_main() {
5041         std::ofstream result(&quot;dump.txt&quot;);
5042
5043         result &lt;&lt; LuminanceMap1;
5044 }
5045 }}}
5046 次はMatrixの宣言と[[Matrix::set()]]命令を組み合わせたコードです。
5047 {{{
5048 &lt;例2&gt;
5049 #include &lt;fstream&gt;
5050 #include &lt;psychlops.h&gt;
5051 using namespace Psychlops;
5052
5053 Matrix LuminanceMap1;
5054 long Width=10, Height=10;
5055
5056 void psychlops_main() {
5057         std::ofstream result(&quot;dump.txt&quot;);
5058
5059         LuminanceMap1.set(Height, Width);
5060         result &lt;&lt; LuminanceMap1;
5061
5062 }
5063 }}}
5064 これらのコードを実行すると、プログラムと同じ場所に作成されるファイルdump.txtにMatrixの中身が保存されます。</pre>
5065 </div>
5066 <div title="6.3.2 行列の要素の操作" modifier="Psychlops_DevelopperG" modified="200908190204" created="200710171131" changecount="2">
5067 <pre>6.3.1節ではMatrixの宣言(領域確保)のみを行っていたので、Matrixの要素に0が代入されるだけでした。
5068 この節では実際にMatrix中に数値を代入していきます。
5069
5070 !Matrix全体への代入
5071 Matrix全体をひとつの値で埋め尽くすには、Matrixに対して数値の代入を行います。
5072 {{{
5073 &lt;例1&gt;
5074 #include &lt;fstream&gt;
5075 #include &lt;psychlops.h&gt;
5076 using namespace Psychlops;
5077
5078 void psychlops_main() {
5079         std::ofstream result(&quot;dump.txt&quot;);
5080
5081         Matrix LuminanceMap1(5, 5);
5082         LuminanceMap1 = 7.0;
5083         result &lt;&lt; LuminanceMap1;
5084 }
5085 }}}
5086 これで全体に7が代入されました。
5087
5088
5089 !Matrix(row, col) 単独要素
5090 Matrix中の要素にアクセスするための命令です。値を取り出すことも代入することも出来ます。二次元の配列に似た扱い方になりますが、要素の指定が行(縦)列(横)の順番であること、起点が0ではなく1であることに注意してください。
5091
5092 |!Matrix()|Matrix(row, col) |要素を取り出します。ちょうど配列のように、値を取り出すことも代入することもできます。|
5093 |~|~|int row: 要素の行を指定します。|
5094 |~|~|int col: 要素の列を指定します。|
5095 行列の範囲外の値が指定された場合はエラーになります。エラーメッセージを表示して強制終了します。
5096
5097 !!Matrixの要素へのアクセスの仕方
5098 2行2列目の要素に数値を代入し、さらにその値を取り出します。
5099 {{{
5100 &lt;例2&gt;
5101 #include &lt;fstream&gt;
5102 #include &lt;psychlops.h&gt;
5103 using namespace Psychlops;
5104
5105 const int Width = 10, Height = 10;
5106 Matrix LuminanceMap1(Height, Width);
5107
5108 void psychlops_main() {
5109         std::ofstream result(&quot;dump.txt&quot;);
5110
5111         LuminanceMap1(2,2) = 1.0;
5112         std::cout &lt;&lt; LuminanceMap1;
5113
5114         double a = LuminanceMap1(2,2);
5115         result &lt;&lt; a;
5116
5117 }
5118 }}}
5119 このプログラムを実行すると、行列の中身が10行10列で出力されます。次の行に、行列の(2,2)の値である1.0が出力されます。
5120
5121 !!Matrixの全要素へのアクセスの仕方
5122 全要素にアクセスするには、forループを使って順次アクセスしていきます。
5123 {{{
5124 &lt;例3&gt;
5125 #include &lt;fstream&gt;
5126 #include &lt;psychlops.h&gt;
5127 using namespace Psychlops;
5128
5129 const int Width = 10, Height = 10;
5130 Matrix LuminanceMap1(Height, Width);
5131
5132 void psychlops_main() {
5133         std::ofstream result(&quot;dump.txt&quot;);
5134
5135         for(int row; row&lt;Width; row++) {
5136                 for(int col; col&lt;Width; col++) {
5137                         LuminanceMap1(row, col) = row+col;
5138                 }
5139         }
5140         result &lt;&lt; LuminanceMap1;
5141
5142 }
5143 }}}
5144
5145 !Matrix(row, col) 部分行列
5146 要素アクセスで整数を指定すると単一の要素にアクセスできました。行列のある範囲をまとめて取り扱いたい場合は、Range型を使って範囲を指定し、部分行列にアクセスします。
5147
5148
5149 [img[部分行列|image/MatrixPartial.png]]
5150 部分行列は、行列の一部分を切り取ってサイズの異なる行列であるかのように振る舞わせます。部分行列は元の行列の窓のように振る舞うため、部分行列に代入すると元の行列の一部分に代入するのと同じように作用します。
5151
5152 |!Matrix()|Matrix(Range row, Range col)|行列の一部分から部分行列を作ります。|
5153 |~|~|Range row: 要素の行を指定します。|
5154 |~|~|Range col: 要素の列を指定します。|
5155
5156 !!部分行列の書き方
5157 10×10のMatrixの一部に数値を代入します。部分行列のさらに一部の要素にもアクセスできます。
5158 {{{
5159 &lt;例4&gt;
5160 #include &lt;fstream&gt;
5161 #include &lt;psychlops.h&gt;
5162 using namespace Psychlops;
5163
5164 const int Width = 10, Height = 10;
5165 Matrix LuminanceMap1(Width, Height);
5166
5167 void psychlops_main() {
5168         std::ofstream result(&quot;dump.txt&quot;);
5169
5170         Range row, col;
5171         LuminanceMap1(2&lt;=row&lt;=5, 3&lt;=col&lt;=6) = 1.0;
5172         LuminanceMap1(2&lt;=row&lt;=5, 3&lt;=col&lt;=6)(2,2) = 2.0;
5173         result &lt;&lt; LuminanceMap1;
5174
5175 }
5176 }}}
5177
5178
5179 !!部分行列のコピー
5180 部分行列は元の行列の窓として機能するので、部分行列への代入をすると元の行列にも代入されます。元の行列を壊したくない場合は、明示的にコピーをとっておく必要があります。
5181 {{{
5182 &lt;例4&gt;
5183 #include &lt;fstream&gt;
5184 #include &lt;psychlops.h&gt;
5185 using namespace Psychlops;
5186
5187 const int Width = 10, Height = 10;
5188 Matrix LuminanceMap1(Width, Height), Copy;
5189
5190 void psychlops_main() {
5191         std::ofstream result(&quot;dump.txt&quot;);
5192
5193         Range row, col;
5194         LuminanceMap1(2&lt;=row&lt;=5, 3&lt;=col&lt;=6) = 1.0;
5195         Copy = LuminanceMap1(2&lt;=row&lt;=5, 3&lt;=col&lt;=6);
5196         Copy = 2.0;
5197         result &lt;&lt; LuminanceMap1 &lt;&lt; Copy;
5198
5199 }
5200 }}}</pre>
5201 </div>
5202 <div title="6.3.3 行列全体の操作" modifier="Psychlops_DevelopperG" modified="200908190207" created="200710171157" changecount="1">
5203 <pre>!要素の再配置
5204 ここでは、行列全体に対して回転、列のスライドなどの操作を行う命令について解説します。
5205 Psychlopsが行列全体に対して行える操作は大まかに以下の表に示した5つです。ここではこれらの命令について解説します。
5206
5207 |slide|行列のスライド|
5208 |transpose|行列の転置|
5209 |rotation|行列全体の回転|
5210 |cat|行列の結合|
5211 |reshape|行列要素の再配置|
5212
5213 上記命令はすべて自己破壊的に作用します。
5214 つまり、命令を実行した段階で、元の行列が命令が適用された行列に置き換わってしまいます。
5215 これには、行列の次元の変換も含まれるので、上記命令を式の中に書いた場合には注意が必要です。
5216 いかに注意した方がよい命令例を挙げてみます。
5217 {{{
5218 Matrix a(5, 3), b(3, 5);
5219 b = a.transpose() + a;  
5220 b = b.transpose(); 
5221 }}}
5222
5223 一行目では、 左側のa.transpose()でaが自己破壊的に転置されてしまうため、右側のaも転置されたものとして扱われてしまいます。つまり、このような命令を実行すると、
5224 {{{
5225 b=2*a.transpose();
5226 }}}
5227 と同じ結果がえられます。
5228 2行目では、左辺のbは3行5列、右辺のbは5行3列で不正な代入にはならず、右辺bが自己破壊的に転置されて代入されます。
5229
5230 !slide(行・列のスライド)
5231
5232 |!Matrix::slide|slide(row, col)|要素を行方向、列方向にずらします。あふれた分は、反対側に移動します。|
5233 |~|~|int row 行方向にずらす量を指定します。|
5234 |~|~|int col 列方向にずらす量を指定します。|
5235
5236 !!slide操作の内容
5237 {{{
5238 1 2 3
5239 4 5 6
5240 をslide(1,0)すると
5241 4 5 6
5242 1 2 3
5243
5244 1 2 3
5245 4 5 6
5246 をslide(0,1)すると
5247 3 1 2
5248 6 4 5
5249
5250 1 2 3
5251 4 5 6
5252 をslide(1,1)すると
5253 6 4 5
5254 3 1 2
5255 }}}
5256
5257 !!slideの書き方
5258 {{{
5259 #include &lt;fstream&gt;
5260 #include &lt;psychlops.h&gt;
5261 using namespace Psychlops;
5262
5263 Psychlops::Matrix LuminanceMap1;
5264 long Width=10, Height=10;
5265
5266 void psychlops_main() {
5267         std::ofstream result(&quot;dump.txt&quot;);
5268
5269
5270         LuminanceMap1.set(Height, Width);
5271         for(int row; row&lt;Width; row++) {
5272                 for(int col; col&lt;Width; col++) {
5273                         LuminanceMap1(row, col) = row*Height + col;
5274                 }
5275         }
5276         result &lt;&lt; LuminanceMap1.slide(1,0);
5277 }
5278 }}}
5279
5280
5281 !転置
5282
5283 |!Matrix::transpose|transpose()|行列を転置(行と列の読み替え)します。自分自身を書き換えるので注意してください。|
5284
5285 !!転置操作の内容
5286 {{{
5287 1 2 3
5288 4 5 6
5289 をtransposeすると
5290 1 4
5291 2 5
5292 3 6
5293 }}}
5294
5295 !!transposeの書き方
5296 {{{
5297 #include &lt;fstream&gt;
5298 #include &lt;psychlops.h&gt;
5299 using namespace Psychlops;
5300
5301 Psychlops::Matrix LuminanceMap1;
5302 long Width=10, Height=10;
5303
5304 void psychlops_main() {
5305         std::ofstream result(&quot;dump.txt&quot;);
5306
5307
5308         LuminanceMap1.set(Height, Width);
5309         for(int row; row&lt;Width; row++) {
5310                 for(int col; col&lt;Width; col++) {
5311                         LuminanceMap1(row, col) = row*Height + col;
5312                 }
5313         }
5314         result &lt;&lt; LuminanceMap1.transpose();
5315 }
5316 }}}
5317
5318 !行列の回転
5319
5320 |!Matrix::rot90|rot90(count)|行列を90度単位で反時計回りに回転します。|
5321 |~|~|int count 回転角度を90度単位で指定します。-3,1,5のとき90度、-2,2,6のとき180度、-1,3,7のとき270度回転します。|
5322
5323 !!回転操作の内容
5324 {{{
5325 1 2 3
5326 4 5 6
5327 をrot90(1)すると
5328 3 6
5329 2 5
5330 1 4
5331 }}}
5332
5333 !!rot90()の書き方
5334 {{{
5335 #include &lt;fstream&gt;
5336 #include &lt;psychlops.h&gt;
5337 using namespace Psychlops;
5338
5339 Psychlops::Matrix LuminanceMap1;
5340 long Width=16, Height=1;
5341
5342 void psychlops_main() {
5343         std::ofstream result(&quot;dump.txt&quot;);
5344
5345
5346         LuminanceMap1.set(Height, Width);
5347         for(int col; col&lt;Width; col++) {
5348                 LuminanceMap1(1, col) = col;
5349         }
5350         result &lt;&lt; LuminanceMap1.rot90(1);
5351 }
5352 }}}
5353
5354
5355 !行列の結合
5356
5357 |!Matrix::catRow|catRow(other)|この行列の次の行から別の行列を継ぎ足します。|
5358 |~|~|Matrix other: 付け足す行列を指定します。付け足してもこれ自身は消されません。|
5359
5360 {{{
5361 #include &lt;fstream&gt;
5362 #include &lt;psychlops.h&gt;
5363 using namespace Psychlops;
5364
5365 Psychlops::Matrix LuminanceMap1, LuminanceMap2;
5366 long Width=5, Height=1;
5367
5368 void psychlops_main() {
5369         std::ofstream result(&quot;dump.txt&quot;);
5370
5371
5372         LuminanceMap1.set(Height, Width);
5373         LuminanceMap1 = 1;
5374         LuminanceMap2.set(Height, Width);
5375         LuminanceMap2 = 2;
5376
5377         LuminanceMap1.catRows(LuminanceMap2);
5378         result &lt;&lt; LuminanceMap1;
5379 }
5380 }}}
5381
5382
5383 !!reshape(行列配置の変換)
5384
5385 |!Matrix::reshape|reshape(row, col)|要素を変えずに、行と列の数のみを変更します。要素数(行*列)は変更前と変更後で同じでなければなりません。|
5386 |~|~|int row 新しい行数を指定します。|
5387 |~|~|int col 新しい列数を指定します。|
5388
5389 !!reshape操作の内容
5390 {{{
5391 1 2 3
5392 4 5 6
5393 をreshape(3, 2)すると
5394 1 2
5395 3 4
5396 5 6
5397 }}}
5398
5399 !!reshapeの書き方
5400 {{{
5401 #include &lt;fstream&gt;
5402 #include &lt;psychlops.h&gt;
5403 using namespace Psychlops;
5404
5405 Psychlops::Matrix LuminanceMap1;
5406 long Width=16, Height=1;
5407
5408 void psychlops_main() {
5409         std::ofstream result(&quot;dump.txt&quot;);
5410
5411
5412         LuminanceMap1.set(Height, Width);
5413         for(int col; col&lt;Width; col++) {
5414                 LuminanceMap1(1, col) = col;
5415         }
5416         result &lt;&lt; LuminanceMap1.reshape(4, 4);
5417 }
5418 }}}
5419
5420 !その他
5421 |!Matrix::min|min()|全要素中の最小値を得ます。|
5422 |!Matrix::max|max()|全要素中の最大値を得ます。|
5423 {{{
5424 // 行列の要素に乱数を代入し、その中の最大値と最小値を抽出します。
5425 #include &lt;fstream&gt;
5426 #include &lt;psychlops.h&gt;
5427 using namespace Psychlops;
5428
5429 Psychlops::Matrix LuminanceMap1;
5430 long Width=10, Height=10;
5431
5432 void psychlops_main() {
5433         std::ofstream result(&quot;dump.txt&quot;);
5434
5435
5436         LuminanceMap1.set(Height, Width);
5437         for(int row; row&lt;Width; row++) {
5438                 for(int col; col&lt;Width; col++) {
5439                         LuminanceMap1(row, col) = Psychlops::random();
5440                 }
5441         }
5442         result &lt;&lt; LuminanceMap1;
5443         result &lt;&lt; LuminanceMap1.max() &lt;&lt; &quot; &quot; &lt;&lt; LuminanceMap1.min();
5444
5445 }
5446 }}}
5447
5448 |!Matrix::getRows|getRows()|行数を得ます。|
5449 |!Matrix::getCols|getCols()|列数を得ます。|
5450 {{{
5451 #include &lt;fstream&gt;
5452 #include &lt;psychlops.h&gt;
5453 using namespace Psychlops;
5454
5455 Psychlops::Matrix LuminanceMap1;
5456 long Width=5, Height=3;
5457
5458 void psychlops_main() {
5459         std::ofstream result(&quot;dump.txt&quot;);
5460
5461
5462         LuminanceMap1.set(Height, Width);
5463
5464         result &lt;&lt; LuminanceMap1.getRows() &lt;&lt; &quot; &quot; &lt;&lt; LuminanceMap1.getCols();
5465 }
5466 }}}</pre>
5467 </div>
5468 <div title="6.3.4 行列の加減乗除" modifier="Psychlops_DevelopperG" modified="200908190207" created="200710171242" changecount="1">
5469 <pre>!実数との演算子
5470
5471 実数と行列の四則演算は、行列の各要素と実数の演算として定義されています。例えば、matrix + 1; というコードは、行列のすべての要素に1を足すことを意味しています。演算子の優先順位はC++の仕様に従います。
5472 {{{
5473  matrix * 10 + 1;
5474  ( matrix * 10 ) + 1; // 上の式と同じ
5475
5476 matrix + 1 * 10;
5477 matrix + ( 1 * 10 ); // 上の式と同じ
5478 }}}
5479
5480 *現在の仕様では、実数は必ず演算子の後ろに書いてください。演算子の前に実数があるとコンパイルエラーになります。
5481 {{{
5482 matrix + 1; // OK
5483 1 + matrix; // NG
5484 }}}
5485
5486 |!Matrix 演算子 実数|&gt;|
5487 |+|行列の要素すべてに実数を加算します|
5488 |-|行列の要素すべてに実数を減算します|
5489 |*|行列の要素すべてに実数を乗算します|
5490 |/|行列の要素すべてに実数を除算します|
5491
5492 !! プログラム例
5493 {{{
5494 #include &lt;fstream&gt;
5495 #include &lt;psychlops.h&gt;
5496 using namespace Psychlops;
5497
5498 void psychlops_main() {
5499         std::ofstream result(&quot;dump.txt&quot;);
5500
5501         Psychlops::Matrix a(5, 5),  b(5, 5);
5502         a = 3;
5503         b = a + 5;
5504         result &lt;&lt; b;
5505
5506         b(2,2) = 0;
5507         b = b * 10 - 6;
5508         result &lt;&lt; b;
5509 }
5510 }}}
5511
5512
5513 !行列との演算子
5514
5515 行列同士の演算は一般的な線形代数の定義に従います。行列の要素同士の乗算(Matlabでのドット演算子)は *~ 演算子でサポートしています。行列同士の除算は定義されていません。
5516
5517 演算の実行前には、行列どうしの行数・列数が適切かどうかチェックし、不適切であればC++の例外機構によってプログラムが強制終了します。コンパイル時にはチェックされません。
5518
5519 実数との演算と行列どうしの演算は同じ式中に混在させることができます。演算子の優先順位はC++の仕様に従います。
5520
5521 |!Matrix 演算子 線形代数演算|&gt;|
5522 |+|行列の加算をします(可換)。両辺の行数・列数は等しくなければなりません。|
5523 |-|行列の減算をします(不可換)。両辺の行数・列数は等しくなければなりません。|
5524 |*|行列の乗算をします(不可換)。左辺項の行数と右辺項の列数は等しくなければなりません。|
5525 |*~|行列の要素どうしを乗算します。Matlabでの.演算子に相当します(可換)。両辺の行数・列数は等しくなければなりません。|
5526
5527 不可換な演算子は、右と左のMatrixを入れ替えると演算結果が変わります。
5528 {{{
5529 Matrix A, B;
5530 A+B == B+A;
5531 A-B != B-A;
5532 A*B != B*A;
5533 A*~B == B*~A;
5534 }}}
5535
5536 !! プログラム例
5537 {{{
5538 #include &lt;fstream&gt;
5539 #include &lt;psychlops.h&gt;
5540 using namespace Psychlops;
5541
5542 void psychlops_main() {
5543         std::ofstream result(&quot;dump.txt&quot;);
5544
5545         Psychlops::Matrix a(5, 5),  b(5, 5), c(5, 5);
5546         a = 3;
5547         b = 2;
5548         c = a + b;
5549         result &lt;&lt; c;
5550
5551         b(2,2) = 0;
5552         c = b * a - 6;
5553         result &lt;&lt; c;
5554 }
5555 }}}
5556
5557
5558 !演算時の制限
5559 Psychlopsの行列は、四則演算において最低限の最適化のために、= 演算子で代入を行ったときにすべての計算を行うよう定義されています。このため、=演算子による代入が行われないと機能しないことがあります。
5560
5561 以下のコードは機能します。
5562 {{{
5563 Matrix a, b, c, d;
5564 c = a - b;
5565 d = max(c, d);
5566 }}}
5567 以下のコードは機能しません
5568 {{{
5569 Matrix a, b, c;
5570 d = max(a - b, c);
5571 }}}
5572 また、再帰的代入(代入先の左辺の変数が右辺にも出てくること)を行う場合は、当該行列が右辺に2回以上出現すると問題が生じる可能性があります。なるべく再帰的代入は行わないでください。
5573 {{{
5574 Matrix a, b;
5575 a = a + a * a;  // 右辺にaが2回以上出てくるのは危険
5576 b = a + a * a;  // 問題なし
5577 }}}
5578 </pre>
5579 </div>
5580 <div title="6.3.5 行列の中身をオフスクリーンに描画する" modifier="Psychlops_DevelopperG" modified="200908190208" created="200710171234" changecount="1">
5581 <pre>MatrixとImageは両方とも2次元に並べられた値(輝度や色)で、相互に変換が可能です。ImageをMatrixの計算機能を使って生成・加工するときに役立ちます。また、Matrixに変換すると各色チャネルが独立して制御しやすいため、各色チャネルを別々に操作する場合などにも役立ちます。
5582 ただし、[[この節の最初で述べたように|6.3 行列演算を行う]]、Matrixクラスの書式とImageクラスの書式がずれていることは注意して下さい。
5583
5584 Matrix / Imageの変換命令はImage型の一部になっています。
5585
5586
5587 !MatrixからImageへの変換
5588
5589 |!Image::from|from( luminance )|Matrixを輝度としてImageに読み込み、グレースケール画像を作成します。|
5590 |~|~|Matrix luminance: |
5591 |~|from( r, g, b )|Matrixを赤、緑、青チャネルとして読み込み、RGB画像を作成します。|
5592 |~|~|Matrix r: |
5593 |~|~|Matrix g: |
5594 |~|~|Matrix b: |
5595 |~|from( r, g, b, a) |Matrixを赤、緑、青、透明度チャネルとして読み込み、RGBA画像を作成します。|
5596 |~|~|Matrix r: |
5597 |~|~|Matrix g: |
5598 |~|~|Matrix b: |
5599 |~|~|Matrix a: |
5600 Imageの中身がすでに確保されていた場合は、破棄されます。
5601
5602
5603 !ImageからMatrixへの変換
5604
5605 |!Image::to|to( luminance )|グレースケール画像の輝度値をMatrixに書き込みます。|
5606 |~|~|Matrix luminance: |
5607 |~|to( r, g, b ) |RGB画像のMatrixを赤、緑、青チャネル値をMatrixに書き込みます。|
5608 |~|~|Matrix r: |
5609 |~|~|Matrix g: |
5610 |~|~|Matrix b: |
5611 |~|to( r, g, b, a ) |RGBA画像の赤、緑、青、透明度チャネルをMatrixに書き込みます。|
5612 |~|~|Matrix r: |
5613 |~|~|Matrix g: |
5614 |~|~|Matrix b: |
5615 |~|~|Matrix a: |
5616 書き込み先のMatrixは空である必要があります。大きさ等がImageと等しくなるように自動的に調整されます。
5617
5618
5619 !Image/Matrix変換の使用例
5620 以下のプログラムは、&quot;sample.png&quot;という名前のPNG画像を読み込み、赤チャネルと緑チャネルの差分を計算して&quot;diff_sample.png&quot;という名前で保存します。
5621
5622 &lt;注&gt;:場合によってはプログラムが正常終了せず、CPUを使い切ってしまうことがあります。そのときは、タスクマネージャ等で強制終了させてください。
5623 {{{
5624 &lt;例1&gt;
5625 #include &lt;psychlops.h&gt;
5626 using namespace Psychlops;
5627
5628 void psychlops_main() {
5629
5630         Image img;
5631         Matrix R, G, B;
5632         Matrix dR, dG;
5633
5634         img.load(&quot;sample.png&quot;);
5635
5636         img.to(R, G, B);
5637
5638         dR = R - G;
5639         dG = G - R;
5640
5641         B = 0;
5642         dR = max(dR, B);
5643         dG = max(dG, B);
5644
5645         img.from(dR, dG, B);
5646
5647         img.save(&quot;diff_sample.png&quot;);
5648
5649 }
5650 }}}</pre>
5651 </div>
5652 <div title="6.3.6 メッシュグリッドの作成" modifier="Psychlops_DevelopperG" modified="200908190210" created="200712061907" changecount="1">
5653 <pre>!メッシュグリッドとは
5654 メッシュグリッドとは、行列の各要素への代入を、ループを使わず普通の数式に見える形式で書くテクニックです。Matlabでよく使用されています。
5655
5656 5 x 5 の大きさの行列に対する操作を行う場合、まず以下のような行列(メッシュグリッド)を作成します。
5657 {{{
5658 X:
5659 0 1 2 3 4
5660 0 1 2 3 4
5661 0 1 2 3 4
5662 0 1 2 3 4
5663 0 1 2 3 4
5664
5665 Y:
5666 0 0 0 0 0
5667 1 1 1 1 1
5668 2 2 2 2 2
5669 3 3 3 3 3
5670 4 4 4 4 4
5671 }}}
5672
5673 この行列を座標値とみなし、各要素について演算する関数を適用します。
5674 {{{
5675 Patch = cos(X) * sin(Y);
5676 }}}
5677 これは、以下のプログラムと同等の計算を行います。
5678 {{{
5679 Matrix Patch(5,5);
5680 for(int Y = 0; Y&lt;5; Y++) {
5681         for(int X = 0; X&lt;5; X++) {
5682                 Patch(X, Y) = cos(X) * sin(Y);
5683         }
5684 }
5685 }}}
5686
5687
5688 !メッシュグリッドの生成
5689 |!Matrix::mesh|mesh(Range row, int col)|行を変動値とするmeshgridを生成します|
5690 |~|~|row: 行(Imageではy座標に相当)の範囲を指定します。行数は範囲から自動決定されます。|
5691 |~|~|col: 列(Imageではx座標に相当)の列数を指定します。|
5692 |~|mesh(int row, Range col)|列を変動値とするmeshgridを生成します|
5693 |~|~|row: 行(Imageではy座標に相当)の行数を指定します。|
5694 |~|~|col: 列(Imageではx座標に相当)の範囲を指定します。行数は範囲から自動決定されます。|
5695 {{{
5696 &lt;使用例&gt;
5697 Range row, col;
5698 Matrix m, y, x;
5699 y = Matrix::mesh(-2&lt;=row&lt;=2, 5);
5700 x = Matrix::mesh(5, -2&lt;=col&lt;=2);
5701
5702 =&gt;
5703 Y;
5704 -2 -2 -2 -2 -2
5705 -1 -1 -1 -1 -1
5706  0  0  0  0  0
5707  1  1  1  1  1
5708  2  2  2  2  2
5709 X;
5710 -2 -1  0  1  2 
5711 -2 -1  0  1  2 
5712 -2 -1  0  1  2 
5713 -2 -1  0  1  2 
5714 -2 -1  0  1  2 
5715 }}}
5716
5717 行の範囲、列の範囲を同時に指定する方法もあります。引数で指定した行列にメッシュグリッドの内容が入ります。
5718 |!Matrix::mesh|mesh(Range row, Matrix r, Range col, Matrix c)|行、列を変動値とするmeshgridを生成します|
5719 |~|~|row: 行(Imageではy座標に相当)の範囲を指定します。行数は範囲から自動決定されます。|
5720 |~|~|r: 行用のメッシュグリッドを指定します。|
5721 |~|~|col: 列(Imageではx座標に相当)の範囲を指定します。列数は範囲から自動決定されます。|
5722 |~|~|c: 列用のメッシュグリッドを指定します。|
5723 {{{
5724 &lt;使用例&gt;
5725 Range row, col;
5726 Matrix m, y, x;
5727 Matrix::mesh(-2&lt;=row&lt;=2, y,-2&lt;=col&lt;=2,x);
5728
5729 =&gt;
5730 Y;
5731 -2 -2 -2 -2 -2
5732 -1 -1 -1 -1 -1
5733  0  0  0  0  0
5734  1  1  1  1  1
5735  2  2  2  2  2
5736 X;
5737 -2 -1  0  1  2 
5738 -2 -1  0  1  2 
5739 -2 -1  0  1  2 
5740 -2 -1  0  1  2 
5741 -2 -1  0  1  2 
5742 }}}
5743
5744 !!Meshgridに使用可能な関数
5745 ここに記述されている関数はすべて自己破壊せず、新たな行列を生成して返します。
5746 |!Matrix::sin|sin(Matrix m)|mの各要素に対してsin関数の返り値を代入した行列を返します。|
5747 |!Matrix::cos|cos(Matrix m)|mの各要素にcos関数の返り値を代入した行列を返します。|
5748 |!Matrix::tan|tan(Matrix m)|mの各要素にtan関数の返り値を代入した行列を返します。|
5749 |!Matrix::exp|exp(Matrix m)|mの各要素にexp関数の返り値を代入した行列を返します。|
5750 |!Matrix::sqrt|sqrt(Matrix m)|mの各要素にsqrt関数の返り値を代入した行列を返します。|
5751 |!Matrix::pow|pow(Matrix m, double ex)|mの各要素を引数としてex乗した値を代入した行列を返します。|
5752
5753
5754 {{{
5755 #include &lt;psychlops.h&gt;
5756 using namespace Psychlops;
5757
5758 void psychlops_main()
5759 {
5760         double wave_length;
5761
5762         Canvas display(Canvas::fullscreen);
5763         Range row, col;
5764
5765         Matrix m, y, x;
5766         y = Matrix::mesh(-100&lt;=row&lt;=100, 201);
5767         x = Matrix::mesh(201, -100&lt;=col&lt;=100);
5768
5769         m = pow(sin(x/wave_length * 2*PI) + cos(y/wave_length * 2*PI), 4.0);
5770
5771         Image img;
5772         img.from(m);
5773
5774         while(!Input::get(Keyboard::esc)) {
5775                 img.draw();
5776                 display.flip();
5777         }
5778 }
5779 }}}
5780
5781
5782
5783 !その他
5784 |!Matrix::min|min(Matrix a, Matrix b)|aとbの各要素を比較し、小さいほうを要素とする行列を返します。両Matrixの大きさは同じでなければなりません。|
5785 |!Matrix::max|max(Matrix a, Matrix b)|aとbの各要素を比較し、大きいほうを要素とする行列を返します。両Matrixの大きさは同じでなければなりません。|
5786
5787 {{{
5788 A = 
5789 1 2 3
5790 0 0 2
5791
5792 B =
5793 5 3 0
5794 1 1 1
5795
5796 C = max(A, B);
5797
5798 C =&gt;
5799 5 3 3
5800 1 1 2
5801
5802 }}}
5803 </pre>
5804 </div>
5805 <div title="CSSEdit_Code" modifier="Psychlops_Admin" modified="200708280258" created="200708280257" tags="systemConfig" changecount="1">
5806 <pre>config.cssEdit ={};
5807 config.cssEdit.settings = {
5808     tags:&quot;&quot;,                         //記事のタグ
5809     user:&quot;CSSEditBackup&quot;,            //作成者名
5810     backupname:&quot;CSSEditBackup&quot;       //バックアップタイトル名
5811 }
5812
5813 config.macros.cssEdit = {};
5814 config.macros.cssEdit.handler = function(place,macroName,params){
5815  if(readOnly){return;}
5816
5817     var s =  '&lt;form mime=&quot;text/plain&quot; name=&quot;CSSEditForm&quot;&gt;'
5818            + '&lt;p&gt;&lt;textarea name=&quot;i&quot; rows=&quot;20&quot; style=&quot;width:90%;&quot;&gt;&lt;/textarea&gt;&lt;/p&gt;'
5819            + '&lt;p&gt;'
5820            + '&lt;input type=&quot;button&quot; name=&quot;go&quot; value=&quot;GO&quot; onclick=&quot;config.macros.cssEdit.go();&quot;&gt;&amp;nbsp;'
5821            + '&lt;input type=&quot;button&quot; name=&quot;ReadStyleSheet&quot; value=&quot;Read StyleSheet&quot; onclick=&quot;config.macros.cssEdit.readStyleSheet();&quot;&gt;&amp;nbsp;' 
5822            + '&lt;input type=&quot;button&quot; name=&quot;Backup&quot; value=&quot;Backup&quot; onclick=&quot;config.macros.cssEdit.Backup();&quot;&gt;&amp;nbsp;'
5823            + '&lt;input type=&quot;button&quot; name=&quot;Restore&quot; value=&quot;Restore&quot; onclick=&quot;config.macros.cssEdit.restore();&quot;&gt;&amp;nbsp;'
5824            + '&lt;input type=&quot;reset&quot; value=&quot;reset&quot;&gt;&amp;nbsp;'
5825            + '&lt;/p&gt;'
5826            + '&lt;/form&gt;'
5827            + '&lt;p style=&quot;font-size:small;&quot;&gt;CSSEdit Ver 0.1.0 Copyright &amp;copy; 2006 &lt;a href=&quot;&lt;a class=&quot;linkification-ext&quot; href=&quot;&lt;a class=&quot;linkification-ext&quot; href=&quot;&lt;a class=&quot;linkification-ext&quot; href=&quot;&lt;a class=&quot;linkification-ext&quot; href=&quot;&lt;a class=&quot;linkification-ext&quot; href=&quot;&lt;a class=&quot;linkification-ext&quot; href=&quot;&lt;a class=&quot;linkification-ext&quot; href=&quot;&lt;a class=&quot;linkification-ext&quot; href=&quot;&lt;a class=&quot;linkification-ext&quot; href=&quot;&lt;a class=&quot;linkification-ext&quot; href=&quot;&lt;a class=&quot;linkification-ext&quot; href=&quot;&lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.potto.client.jp/&quot; title=&quot;Linkification: http://www.potto.client.jp/&quot;&gt;http://www.potto.client.jp/&lt;/a&gt;&quot; title=&quot;Linkification: &lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.potto.client.jp/&quot; title=&quot;Linkification: http://www.potto.client.jp/&quot;&gt;http://www.potto.client.jp/&lt;/a&gt;&quot;&gt;&lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.potto.client.jp/&quot; title=&quot;Linkification: http://www.potto.client.jp/&quot;&gt;http://www.potto.client.jp/&lt;/a&gt;&lt;/a&gt;&quot; title=&quot;Linkification: &lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.potto.client.jp/&quot; title=&quot;Linkification: http://www.potto.client.jp/&quot;&gt;http://www.potto.client.jp/&lt;/a&gt;&quot;&gt;&lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.potto.client.jp/&quot; title=&quot;Linkification: http://www.potto.client.jp/&quot;&gt;http://www.potto.client.jp/&lt;/a&gt;&lt;/a&gt;&quot; title=&quot;Linkification: &lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.potto.client.jp/&quot; title=&quot;Linkification: http://www.potto.client.jp/&quot;&gt;http://www.potto.client.jp/&lt;/a&gt;&quot;&gt;&lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.potto.client.jp/&quot; title=&quot;Linkification: http://www.potto.client.jp/&quot;&gt;http://www.potto.client.jp/&lt;/a&gt;&lt;/a&gt;&quot; title=&quot;Linkification: &lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.potto.client.jp/&quot; title=&quot;Linkification: http://www.potto.client.jp/&quot;&gt;http://www.potto.client.jp/&lt;/a&gt;&quot;&gt;&lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.potto.client.jp/&quot; title=&quot;Linkification: http://www.potto.client.jp/&quot;&gt;http://www.potto.client.jp/&lt;/a&gt;&lt;/a&gt;&quot; title=&quot;Linkification: &lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.potto.client.jp/&quot; title=&quot;Linkification: http://www.potto.client.jp/&quot;&gt;http://www.potto.client.jp/&lt;/a&gt;&quot;&gt;&lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.potto.client.jp/&quot; title=&quot;Linkification: http://www.potto.client.jp/&quot;&gt;http://www.potto.client.jp/&lt;/a&gt;&lt;/a&gt;&quot; title=&quot;Linkification: &lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.potto.client.jp/&quot; title=&quot;Linkification: http://www.potto.client.jp/&quot;&gt;http://www.potto.client.jp/&lt;/a&gt;&quot;&gt;&lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.potto.client.jp/&quot; title=&quot;Linkification: http://www.potto.client.jp/&quot;&gt;http://www.potto.client.jp/&lt;/a&gt;&lt;/a&gt;&quot; title=&quot;Linkification: &lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.potto.client.jp/&quot; title=&quot;Linkification: http://www.potto.client.jp/&quot;&gt;http://www.potto.client.jp/&lt;/a&gt;&quot;&gt;&lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.potto.client.jp/&quot; title=&quot;Linkification: http://www.potto.client.jp/&quot;&gt;http://www.potto.client.jp/&lt;/a&gt;&lt;/a&gt;&quot; title=&quot;Linkification: &lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.potto.client.jp/&quot; title=&quot;Linkification: http://www.potto.client.jp/&quot;&gt;http://www.potto.client.jp/&lt;/a&gt;&quot;&gt;&lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.potto.client.jp/&quot; title=&quot;Linkification: http://www.potto.client.jp/&quot;&gt;http://www.potto.client.jp/&lt;/a&gt;&lt;/a&gt;&quot; title=&quot;Linkification: &lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.potto.client.jp/&quot; title=&quot;Linkification: http://www.potto.client.jp/&quot;&gt;http://www.potto.client.jp/&lt;/a&gt;&quot;&gt;&lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.potto.client.jp/&quot; title=&quot;Linkification: http://www.potto.client.jp/&quot;&gt;http://www.potto.client.jp/&lt;/a&gt;&lt;/a&gt;&quot; title=&quot;Linkification: &lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.potto.client.jp/&quot; title=&quot;Linkification: http://www.potto.client.jp/&quot;&gt;http://www.potto.client.jp/&lt;/a&gt;&quot;&gt;&lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.potto.client.jp/&quot; title=&quot;Linkification: http://www.potto.client.jp/&quot;&gt;http://www.potto.client.jp/&lt;/a&gt;&lt;/a&gt;&quot; title=&quot;Linkification: &lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.potto.client.jp/&quot; title=&quot;Linkification: http://www.potto.client.jp/&quot;&gt;http://www.potto.client.jp/&lt;/a&gt;&quot;&gt;&lt;a class=&quot;linkification-ext&quot; href=&quot;http://www.potto.client.jp/&quot; title=&quot;Linkification: http://www.potto.client.jp/&quot;&gt;http://www.potto.client.jp/&lt;/a&gt;&lt;/a&gt;&quot;&gt;potto&lt;/a&gt;&lt;/p&gt;';
5828
5829
5830     var e = createTiddlyElement(place,&quot;div&quot;,null,null,&quot;&quot;);
5831
5832     e.innerHTML = s;
5833  
5834 }
5835 config.macros.cssEdit.go=function(){
5836   //setStylesheet(document.CSSEditForm.i.value,&quot;CSSEdit&quot;);
5837   var s =document.CSSEditForm.i.value;
5838   var title = &quot;CSSEdit_temp&quot;;
5839    config.macros.cssEdit.save(title,s,&quot;temp&quot;,&quot;&quot;);
5840    var html   = store.getRecursiveTiddlerText(title,null,10);
5841    store.deleteTiddler(title);
5842
5843    setStylesheet(html,&quot;CSSEdit&quot;);
5844 };
5845
5846 config.macros.cssEdit.readStyleSheet=function(){
5847     if (! window.confirm(&quot;Read StyleSheet OK ?&quot;))
5848         return;
5849     document.CSSEditForm.i.value = store.getTiddlerText(&quot;StyleSheet&quot;);
5850 };
5851
5852 config.macros.cssEdit.Backup=function(){
5853     if (! window.confirm(&quot;Backup OK?&quot;))
5854          return;
5855
5856     var p = config.cssEdit.settings;
5857     var s = document.CSSEditForm.i.value;
5858     var title = p.backupname;
5859     config.macros.cssEdit.save(title,s,p.user,p.tags)
5860     saveChanges();
5861 };
5862
5863 config.macros.cssEdit.restore=function(){
5864     if (! window.confirm(&quot;Restore OK ?&quot;))
5865         return;
5866
5867     var p = config.cssEdit.settings;
5868     document.CSSEditForm.i.value = store.getTiddlerText(p.backupname);
5869 };
5870
5871
5872 config.macros.cssEdit.save = function(title,s,user,tags){
5873     var p = config.HTMLEdit.settings;
5874     var now = new Date();
5875     var tiddler = store.fetchTiddler(title);
5876     var created;
5877     if(tiddler){
5878          created = tiddler.created;
5879         store.deleteTiddler(title);
5880     }else{
5881          tiddler = new Tiddler();
5882          created = now;
5883      }
5884     tiddler.set(title,s,user,now,tags,created);
5885     store.addTiddler(tiddler);
5886 };</pre>
5887 </div>
5888 <div title="Canvas::clear()" modifier="Psychlops_DevelopperG" modified="200910080819" created="200712131650" changecount="4">
5889 <pre>|!Canvas::clear()|clear([[Psychlops::Color]] col,TargetSurface)|&gt;|指定したCanvasを指定色で塗りつぶします|
5890 |~|~|[[Psychlops::Color]] col |塗りつぶす色を指定|
5891 |~|~|TargetSurface|塗りつぶす画面を指定|
5892 |~|clear(TargetSurface)|指定したCanvasを塗りつぶします。色の指定には[[Canvas::setClearColor()]]命令を使用します|
5893 |~|~|TargetSurface|塗りつぶす画面を指定|
5894
5895 [[2.1.2 Canvasの基本構造と操作]]</pre>
5896 </div>
5897 <div title="Canvas::copy()" modifier="Psychlops_DevelopperG" modified="200910080847" created="200709170159" changecount="24">
5898 <pre>[[Psychlops::Rectangle]]型(source)で指定される範囲内の情報を[[Psychlops::Rectangle]]型(target)の範囲に複写または移動します。移動元と移動先の矩形の大きさは同じにする必要があります。
5899 |!Canvas::copy()|copy([[Psychlops::Rectangle]] source, [[Psychlops::Rectangle]] target, bool copymode, [[Psychlops::Color]] col)|&gt;|! [[Psychlops::Rectangle]]型の情報を[[Psychlops::Image]]型に複写または移動する|&gt;|&gt;|
5900 |~|~|[[Psychlops::Rectangle]] source|移動(複写)元のRectangleを指定|&gt;|
5901 |~|~|[[Psychlops::Rectangle]] target|移動(複写)先のRectangleを指定|&gt;|
5902 |~|~|bool copymode| trueを指定する場合は移動処理(カット), falseを指定する場合は複写処理(コピー)|指定しない場合、false(複写)が指定される|
5903 |~|~|[[Psychlops::Color]] col|移動処理時の移動元の塗りつぶし色を指定|指定しない場合、黒色が指定される|
5904 |~|~|~|~|複写の場合、指定不要|
5905
5906 !!Canvas::copy()の書き方
5907 rect1の内部にある描画内容ををrect2の内部に複写します。
5908 {{{
5909 &lt;例&gt;
5910 #include &lt;psychlops.h&gt;
5911 using namespace Psychlops;
5912
5913 Psychlops::Rectangle rect1(100,100);
5914 Psychlops::Rectangle rect2(100,100);
5915
5916 void psychlops_main() {
5917
5918         Canvas sampleA(Canvas::fullscreen);
5919         rect1.centering();
5920         while(!Input::get(Keyboard::spc)){
5921         sampleA.rect(rect1,Color::yellow); 
5922         sampleA.copy(rect1,rect2);
5923         sampleA.flip();
5924         }
5925 }
5926 }}}</pre>
5927 </div>
5928 <div title="Canvas::flip()" modifier="Psychlops_DevelopperG" modified="200910080820" created="200709130046" changecount="6">
5929 <pre>表と裏の2つの画面の入れ替え処理です。
5930 描画内容はCanvas::flip() 処理で画面を入れ替えることで初めて画面上に表示されます。
5931 この命令は画面の垂直同期信号(=ビデオカードが1枚の画面を書ききったことを示す信号; Vsync)に同期して、画面の切り替えを行います。
5932 * 参考 [[Tips: Canvas::flip()とコマ落ち]]
5933
5934 |!Canvas::flip()|flip(int)|int入力値のリフレッシュ分描画内容を表示します|
5935 |~|~|引数を入力しない場合(flip())、flip(1)が指定されます|
5936 |~|flipAfter(int wait)|int wait回のリフレッシュ後まで待ってからflip()を実行して描画内容を画面に反映させます|
5937 |~|flipAndWait(int wait)|もっとも近い垂直同期のタイミングでflip()を実行した後に、int wait回ののリフレッシュ分の表示を予約します。次にflip()命令がコールされても、int wait回ののリフレッシュ分が経過するまでは何も起こりません(flip()と全く同じ効果ですが、引数は省略不可)|
5938
5939 flip()系の命令は画面描画の時間制御をする上でもっとも重要な命令です。上の表だけではわかりにくいでしょうから、図と実例を用いて各命令の働きをもう少し詳しく見ていきましょう。
5940
5941 flipAfter(), flipAndWait()命令の動きを図解すると以下のようになります。以下の図ではflipAfter(3)とflip(3)(あるいはflipAndWait(3))の場合を例としてあげました。
5942 [img[image/psychlops_flip.png]]
5943
5944 コード例は[[2.1.3節|2.1.3 Canvasのメンバ取得と変数の表示]]参照</pre>
5945 </div>
5946 <div title="Canvas::getXXXX()" modifier="Kazushi Maruya" modified="200712131657" created="200712131652" changecount="2">
5947 <pre>|!Canvas::getXXXX()|getCenter()|画面の中心座標(x,y)を取得します|
5948 |~|getHcenter()|横方向の中心座標(x)を取得します|
5949 |~|getVcenter()|縦方向の中心座標(y)を取得します|
5950 |~|getHeight()|画面の高さ(ピクセル)を取得します|
5951 |~|getWidth()|画面の幅(ピクセル)を取得します|
5952 |~|getColorDepth()|カラーモード(ビット)を取得します|
5953 |~|getRefreshRate()|リフレッシュレート(Hz)を取得します|
5954
5955 [[2.1.3 Canvasのメンバ取得と変数の表示]]</pre>
5956 </div>
5957 <div title="Canvas::lastFailedFrames()" modifier="Psychlops_DevelopperG" modified="200910080852" created="200709170302" changecount="6">
5958 <pre>[[Canvas::flip()]]命令で画面の切り替えをする時にコマ落ちする場合があります。
5959 その時に落ちたフレーム数を取得します。
5960 * 参考 [[Tips: Canvas::flip()とコマ落ち]]
5961 |!Canvas::lastFailedFrames()|lastFailedFrames()|!1秒間で落ちたフレーム数を取得します|
5962
5963 !!Canvas::lastFailedFrames()の書き方
5964 フレームが落ちたときにグレーで画面をクリアしています。
5965 (フレームが落ちるのが一瞬のため[[Canvas::var()]]命令で落ちた値を確認することは困難なため)
5966 何枚落ちているかは右上の赤い数字が落ちたフレーム数の和算となっているので確認してください。
5967 Display::~の書き方については [[Tips: CanvasクラスとDisplayクラス]]を参照してください。
5968
5969 {{{
5970 &lt;例&gt;
5971 #include &lt;psychlops.h&gt;
5972 using namespace Psychlops;
5973
5974 void ullmancylinder() {
5975         const int DOTCNT = 15000;
5976
5977         int dotcnt = 100;
5978         double bg_lum = 0.0;
5979         double velocity = 1.0;
5980         double SOA_pp;
5981         int SIZE = 3;
5982         int SOA = 1;
5983         double CylinderRadius=200;
5984         int a1=0;
5985         
5986         Range rng;
5987         Independent &lt;&lt; dotcnt   | &quot;Number of Dots&quot; |   1&lt; rng&lt; DOTCNT |  10   | 1000 ;
5988         Independent &lt;&lt; velocity | &quot;Dot Velocity&quot;   | 0.0&lt;=rng&lt;=10.0   |  0.1 | 0.05;
5989         Independent &lt;&lt; SOA      | &quot;SOA Frames&quot;     |   1.0&lt;=rng&lt;=60.0     |  1   | 1;
5990         Independent &lt;&lt; bg_lum   | &quot;Luminance Inv.&quot; | 0.0&lt;=rng&lt;=1.0    |  1   | 1;
5991         Independent &lt;&lt; SIZE     | &quot;Dot Size&quot;       |   0&lt; rng&lt;=10    |  1   | 1;
5992         Independent &lt;&lt; CylinderRadius|  &quot;Cylinder Size&quot;  |  0&lt; rng&lt;= 400   |  1   | 50;
5993
5994
5995         Canvas canvas(Canvas::fullscreen);
5996         Psychlops::Rectangle rect(SIZE,SIZE);
5997         Psychlops::Color bgcolor(0), dotcolor(1.0-bg_lum);
5998
5999         double phase=0.0;
6000         double x[DOTCNT], dx[DOTCNT], y[DOTCNT];
6001
6002         for(int i=0; i&lt;DOTCNT; i++) {
6003                 x[i] = Psychlops::random(CylinderRadius*2*PI);
6004                 y[i] = Psychlops::random(CylinderRadius*2)+200;
6005         }
6006         Display::watchFPS();
6007         while(!Input::get(Keyboard::spc)) {
6008                 bgcolor.set(bg_lum);
6009                 dotcolor.set(1.0-bg_lum);
6010                 canvas.clear(bgcolor);
6011                 rect.resize(SIZE,SIZE);
6012
6013                 phase += velocity;      
6014                 SOA_pp =SOA+1;
6015
6016                 for(int i=0; i&lt;DOTCNT; i++)  dx[i] = (int)(CylinderRadius*sin(x[i]+SOA_pp*phase/100.0))+canvas.getHcenter();//各ドット位置の計算
6017                 for(int i=0; i&lt;dotcnt; i++)  rect.centering(dx[i], y[i]).draw(dotcolor);
6018                 //落ちたフレーム数を返します
6019                 a1=Display::lastFailedFrames();
6020                 if(a1)Display::clear(Color(0.25,0.25,0.25));
6021                 Display::showFPS();
6022                 canvas.flip(SOA);
6023         }
6024 }
6025
6026
6027 void psychlops_main()
6028 {
6029
6030         Procedure p;
6031         p.setDesign(Procedure::DEMO);
6032         p.setProcedure(ullmancylinder);
6033         p.run();
6034
6035 }
6036 }}}
6037
6038 </pre>
6039 </div>
6040 <div title="Canvas::line()" modifier="Psychlops_DevelopperG" modified="200910080837" created="200712131651" changecount="4">
6041 <pre>|!Canvas::line()|line(double x1, double y1, double x2, double y2, [[Psychlops::Color]] col)|&gt;|!開始座標(X1,Y1)から終端座標(X2,Y2)まで指定色の線を描画します|
6042 |~|~|double x1|描画する線の開始座標x1を指定|
6043 |~|~|double y1|描画する線の開始座標y1を指定|
6044 |~|~|double x2|描画する線の終端座標x2を指定|
6045 |~|~|double y2|描画する線の終端座標y2を指定|
6046 |~|~|[[Psychlops::Color]] col|描画する点の色を指定|
6047 |~|line([[Psychlops::Point]] point1, [[Psychlops::Point]] point2,[[Psychlops::Color]] col)|&gt;|!開始Point座標(x1,x1)から終端Point座標(X2,Y2)まで指定色の線を描画します|
6048 |~|~|[[Psychlops::Point]] point1|開始座標(x1,y1)を指定|
6049 |~|~|[[Psychlops::Point]] point2|終端座標(x2,y2)を指定|
6050 |~|~|[[Psychlops::Color]] col|描画する点の色を指定|
6051 画面上に線を描画するためには、「どこから」「どこに」「何色」の線を引くのかの指定が必要です。
6052 「どこから」は開始座標(x1,y1)、「どこに」は終端座標(x2,y2)、「何色」は[[Psychlops::Color]]型で指定します。
6053 座標はCanvasの左上端が座標の基点(x=0,y=0)となっています。
6054 これらの座標はPoint型の変数を使用して指定することもできます。
6055
6056 [img[image/canvasline.png]]
6057
6058 [[2.2.2 画面上に線を描画する]]</pre>
6059 </div>
6060 <div title="Canvas::msg()" modifier="Psychlops_DevelopperG" modified="200910080829" created="200709130031" changecount="8">
6061 <pre>画面上に任意の文字列を表示します。
6062 |!Canvas::msg()|msg(char* string,double x,double y,[[Psychlops::Color]] col)|&gt;|!char* stringを座標(x,y)に指定色で表示します|
6063 |~|~|schar* string|画面表示をする文字列を指定|
6064 |~|~|double x|画面表示をする座標(x)を指定|
6065 |~|~|double y|画面表示をする座標(y)を指定|
6066 |~|~|[[Psychlops::Color]] col|描画する点の色を指定&lt;br&gt;(設定しない場合、白が設定される)|
6067 |~|msg(string str,double x,double y,[[Psychlops::Color]] col)|&gt;|!string strを座標(x,y)に指定色で表示します|
6068 |~|~|string str|画面表示をする文字列を指定|
6069 |~|~|double x|画面表示をする座標(x)を指定|
6070 |~|~|double y|画面表示をする座標(y)を指定|
6071 |~|~|[[Psychlops::Color]] col|描画する点の色を指定&lt;br&gt;(設定しない場合、白が設定される)|
6072
6073 [[2.1.3 Canvasのメンバ取得と変数の表示]]</pre>
6074 </div>
6075 <div title="Canvas::oval()" modifier="Psychlops_DevelopperG" modified="200910080839" created="200712131653" changecount="2">
6076 <pre>|!Canvas::oval()|oval([[Psychlops::Rectangle]] rect, [[Psychlops::Color]] col)|&gt;|![[Psychlops::Rectangle]] rectで指定された直径の円を指定色で塗りつぶし描画します|
6077 |~|~|[[Psychlops::Rectangle]] rect|描画する円の直径|
6078 |~|~|[[Psychlops::Color]] col|描画する点の円を指定|
6079
6080 [img[image/canvasoval.png]]</pre>
6081 </div>
6082 <div title="Canvas::pix()" modifier="Psychlops_DevelopperG" modified="200910080833" created="200712131650" changecount="6">
6083 <pre>|!Canvas::pix()|pix(double x,double y,[[Psychlops::Color]] col) |&gt;|!座標(x,y)に指定色の点を描画します|
6084 |~|~|double x|描画する点の座標軸xを指定|
6085 |~|~|double y|描画する点の座標軸yを指定|
6086 |~|~|[[Psychlops::Color]] col|描画する点の色を指定|
6087 |~|pix([[Psychlops::Point]] point,[[Psychlops::Color]] col) |&gt;|!Point座標(x,y)に指定色の点を描画します|
6088 |~|~|[[Psychlops::Point]] point |座標(x,y)を指定|
6089 |~|~|[[Psychlops::Color]] col|描画する点の色を指定|
6090 |~|pix(int dotsCnt,double* xArray,double* yArray),[[Psychlops::Color]]* colArray )|&gt;|!配列の座標(x[],y[])に指定色(配列を用いて複数色指定)の点を描画します|
6091 |~|~|int dotsCnt|描画する点の数を指定(後ろの引数の配列の個数を超えてはいけない)|
6092 |~|~|double* xArray| 描画する点の座標軸xが格納された配列名(ポインタ)を指定|
6093 |~|~|double* yArray| 描画する点の座標軸yが格納された配列名(ポインタ)を指定|
6094 |~|~|[[Psychlops::Color]]* colArray|描画する点の色が格納された配列名(ポインタ)を指定|
6095 |~|pix(int dotsCnt,double* xArray,double* yArray,[[Psychlops::Color]] col)|&gt;|!配列の座標(x[],y[])に指定色(一色のみ)の点を描画します|
6096 |~|~|int dotsCnt|描画する点の数(配列数)を指定|
6097 |~|~|double* xArray|描画する点の座標軸xが格納された配列名(ポインタ)を指定|
6098 |~|~|double* yArray|描画する点の座標軸yが格納された配列名(ポインタ)を指定|
6099 |~|~|[[Psychlops::Color]] col|描画する点の色を指定|
6100 |~|pix(int dotsCnt,[[Psychlops::Point]]* pointArray,[[Psychlops::Color]]* colArray)|&gt;|!配列Point座標(x[],y[])に指定色の点を描画します|
6101 |~|~|int dotsCnt|描画する点の数(配列数)を指定|
6102 |~|~|[[Psychlops::Point]]* pointArray|配列の座標(x[],y[])が格納された配列名(ポインタ)を指定|
6103 |~|~|[[Psychlops::Color]]* colArray|描画する点の色が格納された配列名(ポインタ)を指定|
6104
6105 [img[image/canvaspix.png]]
6106
6107 [[2.2.1 画面上に点を描画する-Pointクラス1-]]</pre>
6108 </div>
6109 <div title="Canvas::pix(double, double, double)という命令はコンパイルできないのですか?" modifier="Psychlops_DevelopperG" created="200908100457" changecount="1">
6110 <pre>Canvas::pix(double, double, double)という命令はコンパイルできないのですか?
6111
6112 できません。最後の色の引数をColor(double)としてColor型にキャスト(変換)してください。
6113 {{{
6114 (正) display.pix(100,100,Color(0.5));
6115 (誤) display.pix(100,100,0.5);
6116 }}}</pre>
6117 </div>
6118 <div title="Canvas::progressbar()" modifier="Psychlops_DevelopperG" modified="200910080852" created="200712131654" changecount="4">
6119 <pre>|!Canvas::progressbar()|void progressbar(X now, X max)|!現在のnowの値のmaxに対する比率をプログレスバーとして表示します。now, maxは数値型であればどの型でもかまいません|&gt;|
6120 |~|~|X now|現在の進行度を示す変数値(型は数値型)|
6121 |~|~|X max|nowの最終的な到達値(型は数値型)|
6122 |~|void progressbar(double ratio)|!ratioで示される比率をプログレスバーとして表示します。|&gt;|
6123 |~|~|double ratio|比率の値(0.0~1.0)|
6124
6125 [[5.2.2 画面に反映されない描画の進行度を表示する-プログレスバー]]</pre>
6126 </div>
6127 <div title="Canvas::rect()" modifier="Psychlops_DevelopperG" modified="200910080838" created="200709122308" changecount="13">
6128 <pre>画面上に四角形を描画します。
6129
6130 |!Canvas::rect()|rect([[Psychlops::Rectangle]] rect, [[Psychlops::Color]] col)|&gt;|!四角形を指定色で描画します|
6131 |~|~|([[Psychlops::Rectangle]] rect|描画する四角形を指定|
6132 |~|~|[[Psychlops::Color]] col|描画する四角形の色を指定|
6133 [[2.2.3 画面上に四角形を描画する-Rectangleクラス1-]]</pre>
6134 </div>
6135 <div title="Canvas::setClearColor()" modifier="Psychlops_DevelopperG" modified="200910080852" created="200709170246" changecount="6">
6136 <pre>[[Canvas::clear()|2.1.2 Canvasの基本構造と操作]]命令使用時に塗りつぶす色の設定を行います。
6137 setClearColor(Color)命令使用時には必ず[[Canvas::clear()|2.1.2 Canvasの基本構造と操作]]命令を使用してください。
6138 |!Canvas::setClearColor()|setClearColor([[Psychlops::Color]] col)|&gt;|!指定したCanvasを塗りつぶすための色を設定します|
6139 |~|~|[[Psychlops::Color]] col |塗りつぶす色を指定|
6140
6141 !!Canvas::setClearColor()の書き方
6142 今ディスプレイに表示されている画面(紙の表)を白く塗りつぶします。
6143 {{{
6144 &lt;例&gt;
6145 #include &lt;psychlops.h&gt;
6146 using namespace Psychlops;
6147
6148 void psychlops_main() {
6149
6150         Canvas sampleA(Canvas::fullscreen);
6151         sampleA.setClearColor(Color::white);
6152         sampleA.clear(Canvas::FRONT);
6153         while(!Input::get(Keyboard::spc));
6154 }
6155 }}}</pre>
6156 </div>
6157 <div title="Canvas::setGammaTable()" modifier="Psychlops_DevelopperG" modified="200910080850" created="200802130702" changecount="5">
6158 <pre>他の多くの描画ライブラリ同様、CRTの各画素における輝度レベルとPsychlopsで指定する輝度レベルは線形の関係を維持しません。これを簡易的に補正するために、R,G,Bの各色チャンネルについて変換表(LUTテーブル)を用いた補正を行う方法が広く用いられています。
6159 Psychlopsでは、この変換表を指定することで、[[ガンマ補正|Tips: ガンマ補正]]された色を表示することが可能です。このための命令として、setGammaTable()命令があります。
6160
6161 通常は[[Color::setGammaTable()]]を用いますが、将来的にCanvasごとに別の補正値が必要なときのためにこの関数が予約されています。
6162
6163 |!Canvas::setGammaTable()|setGammaTable(std::vector&lt;double&gt; Cr, std::vector&lt;double&gt; Cg, std::vector&lt;double&gt; Cb)|&gt;|!Canvasが確保している表示装置のDACテーブルを直接書き換え、ガンマ補正を行います。|
6164 |~|~|std::vector&lt;double&gt; Cr|R(赤)チャンネルの変換表|
6165 |~|~|std::vector&lt;double&gt; Cg|G(緑)チャンネルの変換表|
6166 |~|~|std::vector&lt;double&gt; Cb|B(青)チャンネルの変換表|
6167 *2 256個以下でも自動的に線形補完されますが、正確な補完は256個すべて指定することで実現されます。
6168 *3 値は配列またはC++STLの動的配列(std::vector&lt;double&gt;)で指定し、8ビット環境なら256段階の理論値を配列の要素番号とし、それぞれの段階の補償値を配列要素に書き込みます。
6169
6170 コード例は[[ガンマ補正|Tips: ガンマ補正]]参照</pre>
6171 </div>
6172 <div title="Canvas::setGammaValue()" modifier="Psychlops_DevelopperG" modified="200910080849" created="200709170234" changecount="11">
6173 <pre>他の多くの描画ライブラリ同様、CRTの各画素における輝度レベルとPsychlopsで指定する輝度レベルは線形の関係を維持しません。これを簡易的に補正するために、R,G,Bの各色チャンネルについてGamma関数を用いた補正を行う方法が広く用いられています。
6174 Psychlopsでは、このGamma関数の係数を指定することで、[[ガンマ補正|Tips: ガンマ補正]]された色を表示することが可能です。このための命令として、setGammaValue()命令があります。
6175
6176 通常は[[Color::setGammaValue()]]を用いますが、将来的にCanvasごとに別の補正値が必要なときのためにこの関数が予約されています。
6177
6178
6179 |!Canvas::setGammaValue()|setGammaValue(double Cr, double Cg, double Cb)|&gt;|!Canvasが確保している表示装置のDACテーブルを直接書き換え、ガンマ補正を行います。|
6180 |~|~|double Cr|表示装置のR(赤)チャンネルのガンマ係数|
6181 |~|~|double Cg|表示装置のG(緑)チャンネルのガンマ係数|
6182 |~|~|double Cb|表示装置のB(青)チャンネルのガンマ係数|
6183 * 指定する値は表示装置のガンマ値そのものです。Psychlops側で補償指数を自動決定します。
6184
6185 コード例は[[ガンマ補正|Tips: ガンマ補正]]参照</pre>
6186 </div>
6187 <div title="Canvas::showFPS()" modifier="Psychlops_DevelopperG" modified="200910080852" created="200712131656" changecount="2">
6188 <pre>|!Canvas::showFPS()|void showFPS(void)|!FPSチェッカのレポートを画面左上に表示します|
6189
6190 [[5.2.1 描画の時間精度を確認する-FPSチェッカー]]
6191 </pre>
6192 </div>
6193 <div title="Canvas::to()" modifier="Psychlops_DevelopperG" modified="200910080843" created="200709130159" changecount="6">
6194 <pre>[[Psychlops::Rectangle]]型で指定される範囲内の情報を[[Psychlops::Image]]型に複写します。
6195 この命令と''Image::save()''命令を組み合わせることによってImage上以外で描画した画像が保存できます。
6196 |!Canvas::to()|to([[Psychlops::Image]] img, [[Psychlops::Rectangle]] rect)|&gt;|![[Psychlops::Rectangle]]型の情報を[[Psychlops::Image]]型に複写する|
6197 |~|~|[[Psychlops::Image]] img |複写先のImage|
6198 |~|~|[[Psychlops::Rectangle]] rect |複写元のRectangle|
6199
6200 コード例は[[3.3.1節|3.3.1 画像ファイルを保存する]]参照</pre>
6201 </div>
6202 <div title="Canvas::var()" modifier="Psychlops_DevelopperG" modified="200910080830" created="200709130030" changecount="26">
6203 <pre>取得した変数の値を画面に表示します。
6204 |!Canvas::var()|var(string str, double x, double y,[[Psychlops::Color]] col, bool fillForward)|&gt;|&gt;|&gt;|!string strで指定された変数の値を取得して座標(x,y)に指定色で表示します|
6205 |~|~|&gt;|&gt;|string str|画面表示をする変数を指定|
6206 |~|~|&gt;|&gt;|double x|画面表示をする座標(x)を指定|
6207 |~|~|&gt;|&gt;|double y|画面表示をする座標(y)を指定|
6208 |~|~|&gt;|&gt;|[[Psychlops::Color]] col|描画する点の色を指定(設定しない場合、白が設定される)|
6209 |~|~|~|bool fillForward|表示位置の指定|true |指定座標(x,y)から左部分に表示する|
6210 |~|~|~|~|~|false |指定座標(x,y)から右部分に表示する|
6211 |~|~|~|~|&gt;|&gt;|(指定しない場合、falseが設定される)|
6212
6213 [[2.1.3 Canvasのメンバ取得と変数の表示]]</pre>
6214 </div>
6215 <div title="Canvas::watchFPS()" modifier="Psychlops_DevelopperG" modified="200910080852" created="200712131655" changecount="2">
6216 <pre>|!Canvas::watchFPS()|void watchFPS(void)|!FPSチェッカをonにします|
6217
6218 [[5.2.1 描画の時間精度を確認する-FPSチェッカー]]</pre>
6219 </div>
6220 <div title="CanvasMode" modifier="Psychlops_DevelopperG" modified="200910080814" created="200708212026" changecount="7">
6221 <pre>描画モード
6222 |Canvas::fullscreen()|画面いっぱいに描画領域を確保する|
6223 |Canvas::window()|指定サイズの描画領域を確保する|</pre>
6224 </div>
6225 <div title="Canvasの宣言" modifier="Psychlops_DevelopperG" modified="200910080808" created="200712131704" changecount="1">
6226 <pre>|!Canvasの宣言|
6227 |~|Canvas(int width,int height,int colordepth,double refreshrate)|&gt;|!全画面表示をします|
6228 |~|~|int width|水平方向の解像度(画面の横幅)を指定|
6229 |~|~|int height|垂直方向の解像度(画面の縦幅)を指定|
6230 |~|~|int colordepth|カラーモード(ビット)を指定|
6231 |~|~|double refreshrate|リフレッシュレート( Hz )を指定|
6232 |~|Canvas(Canvasmode mode, const Display disp)|&gt;|!現在の画面モードで画面を確保する場合に使います|
6233 |~|~|Canvasmode mode|[[表示モード|CanvasMode]](フルスクリーンorウィンドウ)を選択|
6234 |~|~|const [[Display|Displayの指定値]] disp|[[表示するディスプレイ|Displayの指定値]]を選択(省略可能)|
6235 |~|~|&gt;|マルチディスプレイ環境では、最後の引数でどのディスプレイに表示するか指定できます。省略時はプライマリディスプレイになります。詳細は [[マルチディスプレイの選択]]を参照ください。|
6236 |~|Canvas(int width, int height, int colordepth, double refreshrate, const Display disp)|&gt;|!画面モードを指定してフルスクリーン画面を確保します|
6237 |~|~|int width|水平方向の解像度(画面の横幅)を指定|
6238 |~|~|int height|垂直方向の解像度(画面の縦幅)を指定|
6239 |~|~|int colordepth|カラーモード(ビット)を指定|
6240 |~|~|double refreshrate|リフレッシュレート( Hz )を指定|
6241 |~|~|const [[Display|Displayの指定値]] disp|[[表示するディスプレイ|Displayの指定値]]を選択(省略可能)|
6242 |~|~|&gt;|指定した画面モードが選べなかった場合は、例外が送出されプログラムは強制終了します。|
6243 |~|Canvas(int width, int height, CanvasMode mode, const Display disp)|&gt;|!指定の幅と高さでウィンドウを確保します。|
6244 |~|~|int width|水平方向の解像度(画面の横幅)を指定|
6245 |~|~|int height|垂直方向の解像度(画面の縦幅)を指定|
6246 |~|~|Canvasmode mode|[[表示モード|CanvasMode]](フルスクリーンorウィンドウ)を選択|
6247 |~|~|&gt;|指定した画面モードが選べなかった場合は、例外が送出されプログラムは強制終了します。|
6248 |~|Canvas(Rectangle rect, CanvasMode mode , const Display disp)|&gt;|!バーチャルスクリーン上の指定の矩形でウィンドウを確保します。|
6249 |~|~|Rectangle rect|ディスプレイの大きさ、ディスプレイの位置を指定|
6250 |~|~|Canvasmode mode|[[表示モード|CanvasMode]](フルスクリーンorウィンドウ)を選択|
6251 |~|~|const [[Display|Displayの指定値]] disp|[[表示するディスプレイ|Displayの指定値]]を選択(省略可能)|
6252 |~|~|&gt;|指定した画面モードが選べなかった場合は、例外が送出されプログラムは強制終了します。|
6253
6254 * ここではコンストラクタ(宣言)の書式のみ記述していますが、同じ引数セットのset関数がすべて用意されています。
6255 * CRT は機器によってリフレッシュレートが小数点以下の桁でわずかに異なります(60Hzと表示されていても、厳密には60.2Hzなど小数点以下の桁があります)。初期化時には小数点以下の桁は無視して確保しますが、正確なタイミングでflipするには、ディスプレイ装置の設定等で小数点以下の桁をあらかじめ調べて、正確な値を指定することをお勧めします。
6256 * 1.3以降でではウィンドウモードが実装されました。
6257
6258 [[2.1.1 Canvasの宣言]]</pre>
6259 </div>
6260 <div title="Canvas型に関して" modifier="Psychlops_DevelopperG" modified="200908100506" created="200712131716" changecount="9">
6261 <pre>* [[描画内容を画面に反映させたい|Canvas::flip()]]
6262 * [[描画内容を画面に反映させて、一定時間表示したい|Canvas::flip()]]
6263 * [[Canvas型の変数のメンバを取得したい|Canvas::getXXXX()]]
6264
6265 * [[プログラム内の変数の値を表示したい|Canvas::var()]]
6266 * [[簡単なメッセージを表示したい|Canvas::msg()]]
6267
6268 * [[画面領域全てを塗りつぶしたい|Canvas::clear()]]
6269 * [[点を打ちたい|Canvas::pix()]]
6270 * [[四角形を描画したい|Canvas::rect()]]
6271 * [[円・楕円を描画したい|Canvas::oval()]]
6272
6273 * [[描画内容を画面上で複写したい|Canvas::copy()]]
6274 * [[描画内容をオフスクリーンへ複写したい|Canvas::to()]]
6275
6276 * [[FPSチェッカを使用したい|5.2.1 描画の時間精度を確認する-FPSチェッカー]]
6277
6278 * [[Canvas::pix(double, double, double)という命令はコンパイルできないのですか?]]
6279 * [[止まった絵を描画するときCanvas.flip()を単独でwhileループの中に書くと、画面がちらつきます。]]</pre>
6280 </div>
6281 <div title="Clock::at_msec()" modifier="Psychlops_Admin" created="200709142037" changecount="1">
6282 <pre>タイマーのカウントをmsec単位に変換します。
6283 |!Clock::at_msec()| double Clock::at_msec(void)| カウントをmsec単位に変換(double)して返す|
6284
6285 詳細とコード例は[[5.1節|5.1 時間を計測する]]参照</pre>
6286 </div>
6287 <div title="Clock::update()" modifier="Psychlops_Admin" created="200709142036" changecount="1">
6288 <pre>Timerに現在のCPUタイマーの値を入力します。
6289 |!Clock::update()| Clock::update(void)| 現在のCPUタイマーの値を入力する|
6290
6291 詳細とコード例は[[5.1節|5.1 時間を計測する]]参照</pre>
6292 </div>
6293 <div title="Color()の宣言" modifier="Psychlops_Admin" modified="200709132234" created="200709131904" changecount="4">
6294 <pre>描画対象に色をつけたい場合、Color()の宣言を行い色の設定をします。
6295 宣言のみの場合、[[Color::set()|2.2.5 描画する図形の色を指定する-Colorクラス-]]で色の詳細情報を設定しますが
6296 宣言文のみでも色の情報の設定が可能です。
6297
6298 |!Color()の宣言|Color()|[[Psychlops::Color]]型の宣言を行います|
6299 |~|Color(int r, int g, int b, int a)|[[Psychlops::Color]]型の宣言を行いr,g,bと透明度aで構成されるカラー情報を設定します|
6300 |~|~|int r:赤の輝度を指定。範囲は0~1|
6301 |~|~|int g:緑の輝度を指定。範囲は0~1|
6302 |~|~|int b:青の輝度を指定。範囲は0~1|
6303 |~|~|int a:透明度を指定。範囲は0~1。0に近いほど透明度が高い|
6304 |~|Color(double r, double g, double b, double a)|[[Psychlops::Color]]型の宣言を行いr,g,bと透明度aで構成されるカラー情報を設定します|
6305 |~|~|double r:赤の輝度を指定。範囲は0.0~1.0|
6306 |~|~|double g:緑の輝度を指定。範囲は0.0~1.0|
6307 |~|~|double b:青の輝度を指定。範囲は0.0~1.0|
6308 |~|~|double a:透明度を指定。範囲は0.0~1.0。0.0に近いほど透明度が高い|
6309 |~|Color(int r, int g, int b)|[[Psychlops::Color]]型の宣言を行いr,g,bで構成されるカラー情報を設定します|
6310 |~|~|int r:赤の輝度を指定。範囲は0.0~1.0|
6311 |~|~|int g:緑の輝度を指定。範囲は0.0~1.0|
6312 |~|~|int b:青の輝度を指定。範囲は0.0~1.0|
6313 |~|~|透明度は1.0が固定で指定されている|
6314 |~|Color(long r, long g, long b)|[[Psychlops::Color]]型の宣言を行いr,g,bで構成されるカラー情報を設定します|
6315 |~|~|long r:赤の輝度を指定。範囲は0.0~1.0|
6316 |~|~|long g:緑の輝度を指定。範囲は0.0~1.0|
6317 |~|~|long b:青の輝度を指定。範囲は0.0~1.0|
6318 |~|~|透明度は1.0が固定で指定されている|
6319 |~|Color(float r, float g, float b)|[[Psychlops::Color]]型の宣言を行いr,g,bで構成されるカラー情報を設定します|
6320 |~|~|float r:赤の輝度を指定。範囲は0.0~1.0|
6321 |~|~|float g:緑の輝度を指定。範囲は0.0~1.0|
6322 |~|~|float b:青の輝度を指定。範囲は0.0~1.0|
6323 |~|~|透明度は1.0が固定で指定されている|
6324 |~|Color(double r, double g, double b)|[[Psychlops::Color]]型の宣言を行いr,g,bで構成されるカラー情報を設定します|
6325 |~|~|double r:赤の輝度を指定。範囲は0.0~1.0|
6326 |~|~|double g:緑の輝度を指定。範囲は0.0~1.0|
6327 |~|~|double b:青の輝度を指定。範囲は0.0~1.0|
6328 |~|~|透明度は1.0が固定で指定されている|
6329 |~|Color(int gray)|[[Psychlops::Color]]型の宣言を行いグレースケールの輝度を設定します|
6330 |~|~|int gray: 輝度を指定。範囲は0.0~1.0|
6331 |~|~|透明度は1.0が固定で指定されている|
6332 |~|Color(long gray)|[[Psychlops::Color]]型の宣言を行いグレースケールの輝度を設定します|
6333 |~|~|long gray: 輝度を指定。範囲は0.0~1.0|
6334 |~|~|透明度は1.0が固定で指定されている|
6335 |~|Color(float gray)|[[Psychlops::Color]]型の宣言を行いグレースケールの輝度を設定します|
6336 |~|~|透明度は1.0が固定で指定されている|
6337 |~|~|float gray: 輝度を指定。範囲は0.0~1.0|
6338 |~|~|透明度は1.0が固定で指定されている|
6339 |~|Color(double gray)|[[Psychlops::Color]]型の宣言を行いグレースケールの輝度を設定します|
6340 |~|~|double gray: 輝度を指定。範囲は0.0~1.0|
6341 |~|~|透明度は1.0が固定で指定されている|
6342 </pre>
6343 </div>
6344 <div title="Color::XXXX" modifier="YourName" modified="200803100613" created="200708231845" changecount="2">
6345 <pre>あらかじめ用意された基本的な色指定を呼び出すことが可能です。
6346 呼び出せる色は以下の通りです。
6347 |!Color::white|白(1.0, 1.0, 1.0)|
6348 |!Color::black|黒(0.0, 0.0, 0.0)|
6349 |!Color::gray|灰色(0.5, 0.5, 0.5)|
6350 |!Color::red|赤(1.0, 0.0, 0.0)|
6351 |!Color::green|緑( 0.0, 1.0, 0.0)|
6352 |!Color::blue|青(0.0, 0.0, 1.0)|
6353 |!Color::cyan|シアン(0.0, 1.0, 1.0)|
6354 |!Color::magenta|マゼンタ(1.0, 1.0, 0.0)|
6355 |!Color::yellow|黄(1.0, 1.0, 0.0)|</pre>
6356 </div>
6357 <div title="Color::get()" modifier="YourName" modified="200803140530" created="200802130630" changecount="5">
6358 <pre>Color型に格納されている各値を得ます。
6359
6360 |!Color::get()|get(double r, double g, double b, double a)|r,g,bと透明度aで構成されるカラー情報を取得します。|
6361 |~|~|double r:赤の輝度(0.0~1.0)|
6362 |~|~|double g:緑の輝度(0.0~1.0)|
6363 |~|~|double b:青の輝度(0.0~1.0)|
6364 |~|~|double a:透明度(0.0~1.0)。0に近いほど透明度が高い|
6365
6366 |!Color::getX()|getR()|赤チャネルの値を返します|
6367 |~|getG()|緑チャネルの値を返します|
6368 |~|getB()|青チャネルの値を返します|
6369 |~|getA()|透明チャネルの値を返します|
6370
6371 {{{
6372 double r, g, b, a;
6373 Color col
6374
6375 col.set(0.5);
6376 col.get(r, g, b, a);
6377 // r =&gt; 0.5, g =&gt; 0.5, b =&gt; 0.5, a =&gt; 1
6378
6379 col.set(1.0, 0.5, 0.0, 0.5);
6380 r = col.getR();
6381
6382 // r =&gt; 1.0
6383
6384 }}}
6385 </pre>
6386 </div>
6387 <div title="Color::set()" modifier="YourName" modified="200803100609" created="200712131736" changecount="4">
6388 <pre>|!Color::set()|set(double r, double g, double b, double a)|r,g,bと透明度aで構成されるカラー情報を設定します。|
6389 |~|~|double r:赤の輝度を指定。範囲は0.0~1.0|
6390 |~|~|double g:緑の輝度を指定。範囲は0.0~1.0|
6391 |~|~|double b:青の輝度を指定。範囲は0.0~1.0|
6392 |~|~|double a:透明度を指定。範囲は0.0~1.0 (0.0に近いほど透明度が高い)|
6393 |~|set(double r, double g, double b)|r,g,bで構成されるカラー情報を設定します。|
6394 |~|~|double r:赤の輝度を指定。範囲は0.0~1.0|
6395 |~|~|double g:緑の輝度を指定。範囲は0.0~1.0|
6396 |~|~|double b:青の輝度を指定。範囲は0.0~1.0|
6397 |~|set(double gray)|グレースケールの輝度を設定します。|
6398 |~|~|double gray: 輝度を指定。範囲は0.0~1.0|
6399
6400 [[2.2.5 描画する図形の色を指定する-Colorクラス-]]</pre>
6401 </div>
6402 <div title="Color::setGammaTable()" modifier="YourName" modified="200802131125" created="200802130701" changecount="3">
6403 <pre>他の多くの描画ライブラリ同様、CRTの各画素における輝度レベルとPsychlopsで指定する輝度レベルは線形の関係を維持しません。
6404 これを簡易的に補正するために、R,G,Bの各色チャンネルについて変換表(LUTテーブル)を用いた補正を行う方法が広く用いられています。
6405 Psychlopsでは、この変換表を指定することで、[[ガンマ補正|Tips: ガンマ補正]]された色を表示することが可能です。
6406 このための命令として、setGammaTable()命令があります。
6407
6408 |!Canvas::setGammaTable()|setGammaTable(double Cr[], double Cg[], double Cb[], int table_size)|この命令以降実行されるPsychlops::Colorに対して変換表によるを自動的に行います^^*1^^|
6409 |~|~|Cr: R(赤)チャンネルの変換表|
6410 |~|~|Cg: G(緑)チャンネルの変換表|
6411 |~|~|Cb: B(青)チャンネルの変換表|
6412 |~|~|table_size: 表の項目数。8ビット環境下ではできれば256個の項目を用意してください。^^*2^^|
6413 |~|setGammaTable(std::vector&lt;double&gt; Cr, std::vector&lt;double&gt; Cg, std::vector&lt;double&gt; Cb)|この命令以降実行されるPsychlops::Colorに対してGamma補正を自動的に行います^^*1^^|
6414 |~|~|Cr: R(赤)チャンネルの変換表|
6415 |~|~|Cg: G(緑)チャンネルの変換表|
6416 |~|~|Cb: B(青)チャンネルの変換表|
6417 *1 Psychlopsはまずハードウェアによるガンマ補正([[Canvas::setGammaTable()]])を試みます。失敗すると、ソフトウェアによるエミュレーションを試みます。
6418 *2 256個以下でも自動的に線形補完されますが、正確な補完は256個すべて指定することで実現されます。
6419 *3 値は配列またはC++STLの動的配列(std::vector&lt;double&gt;)で指定し、8ビット環境なら256段階の理論値を配列の要素番号とし、それぞれの段階の補償値を配列要素に書き込みます。
6420
6421 コード例は[[ガンマ補正|Tips: ガンマ補正]]参照
6422 </pre>
6423 </div>
6424 <div title="Color::setGammaValue" modifier="YourName" modified="200802130652" created="200802130648" changecount="4">
6425 <pre></pre>
6426 </div>
6427 <div title="Color::setGammaValue()" modifier="YourName" modified="200802131124" created="200802130652" changecount="3">
6428 <pre>他の多くの描画ライブラリ同様、CRTの各画素における輝度レベルとPsychlopsで指定する輝度レベルは線形の関係を維持しません。これを簡易的に補正するために、R,G,Bの各色チャンネルについてGamma関数を用いた補正を行う方法が広く用いられています。
6429 Psychlopsでは、このGamma関数の係数を指定することで、[[ガンマ補正|Tips: ガンマ補正]]された色を表示することが可能です。
6430 このための命令として、setGammaValue()命令があります。
6431
6432 |!Canvas::setGammaValue()|setGammaValue(double Cr, double Cg, double Cb)|この命令以降実行されるPsychlops::Colorに対してGamma補正を自動的に行います^^*1^^|
6433 |~|~|Cr: R(赤)チャンネルのガンマ係数|
6434 |~|~|Cg: G(緑)チャンネルのガンマ係数|
6435 |~|~|Cb: B(青)チャンネルのガンマ係数|
6436 *1 Psychlopsはまずハードウェアによるガンマ補正([[Canvas::setGammaValue()]])を試みます。失敗すると、ソフトウェアによるエミュレーションを試みます。
6437 *2 指定する値は表示装置のガンマ値そのものです。Psychlops側で補償指数を自動決定します。
6438
6439 コード例は[[ガンマ補正|Tips: ガンマ補正]]参照
6440 </pre>
6441 </div>
6442 <div title="Color::strict_match" modifier="YourName" modified="200802201822" created="200802130704" changecount="3">
6443 <pre>実験時に色指定を厳密に行わせるよう強制させることができます。具体的には、ガンマ補正や色指定で値が0以上1以下にならないケースで警告する、ソフトウェアエミュレーション(後述)を禁止して警告を出す、という規制が行われます。以下のコードをpsychlops_main()の冒頭に記述してください。
6444 {{{
6445  Color::strict_match = true;
6446 }}}
6447
6448 なお、警告はC++の例外機能を使って送出されます。警告を受け入れる構文を書かない限り強制終了します。警告を受け入れるtry..catch構文については、C++の参考書をお読みください。
6449 </pre>
6450 </div>
6451 <div title="Color型に関して" modifier="Kazushi Maruya" created="200712131735" changecount="1">
6452 <pre>* [[描画色を設定したい|Color::set()]]
6453 * [[白、黒などのデフォルトの描画色を使用したい|Color::XXXX]]</pre>
6454 </div>
6455 <div title="DefaultTiddlers" modifier="PsychlopsAdmin" created="200709161806" changecount="1">
6456 <pre>MainMenu</pre>
6457 </div>
6458 <div title="Displayの指定値" modifier="Psychlops_DevelopperG" modified="200910080812" created="200910080753" changecount="5">
6459 <pre>クラス定数として以下のものが用意されています。
6460 |Display::secondary|その時点でのプライマリディスプレイを取得します|
6461 |Display::primary|その時点でのセカンダリディスプレイを取得します|
6462
6463 より複雑な環境では以下の命令を使ってリストを取得してください。
6464 |!Display::list()|ディスプレイのリストを動的配列(std::vector&lt;Display&gt;)で取得します。|
6465
6466
6467 </pre>
6468 </div>
6469 <div title="GaborImage" modifier="Kazushi Maruya" modified="200712061920" created="200710070016" changecount="3">
6470 <pre>{{{
6471 class GaborImage {
6472 private:
6473 public:
6474         virtual void draw(Image &amp;area,  double ori, double freq, double phase, double Lmean, double *contrast) {
6475                 int center_x = area.getCenter().getX(), center_y = area.getCenter().getY();
6476                 int width = area.getWidth()/2, height = area.getHeight()/2;
6477                 Color tmpcol;
6478                 double sigma = (width)/3;
6479                 double SINORI, COSORI, _x, gr, ga, ph, R, G, B;
6480                 
6481
6482                 COSORI=cos(-ori*PI/180);
6483                 SINORI=sin(-ori*PI/180);
6484                 
6485                 ph=phase*PI/180;
6486                 
6487                 for(int y=-height; y&lt;height; y++) {
6488                         for(int x=-width; x&lt;width; x++) {
6489                                 _x=     COSORI * x - SINORI * y;
6490                                 ga=exp(-0.5*(pow(x/(sigma),2))-0.5*(pow(y/(sigma), 2)));
6491                                 gr=(1+cos(2*PI*freq*_x+ph))*0.5;
6492                                 R=Lmean+Lmean*contrast[0]*(gr*ga);
6493                                 G=Lmean+Lmean*contrast[1]*(gr*ga);
6494                                 B=Lmean+Lmean*contrast[2]*(gr*ga);
6495                                 tmpcol.set(R, G, B);
6496                                 area.pix(x+width,y+height,tmpcol); 
6497                         }
6498                 }
6499         }
6500
6501         virtual void draw(Image &amp;area,  double ori, double freq, double phase, double Lmean, double contrast) {
6502                 int center_x = area.getCenter().getX(), center_y = area.getCenter().getY();
6503                 int width = area.getWidth()/2, height = area.getHeight()/2;
6504                 Color tmpcol;
6505                 double sigma = (width)/3;
6506                 
6507                 double SINORI, COSORI, _y, gr, ga, ph;
6508                 
6509                 COSORI=cos(ori*PI/180);
6510                 SINORI=sin(ori*PI/180);
6511                 
6512                 ph=phase*PI/180;
6513                 
6514                 for(int y=-height; y&lt;height; y++) {
6515                         for(int x=-width; x&lt;width; x++) {
6516                                 _y=     SINORI * x + COSORI * y;
6517                                 ga=exp(-0.5*(pow(x/(sigma),2))-0.5*(pow(y/(sigma), 2)));
6518                                 gr=(1+cos(2*PI*freq*_y+ph))*0.5;
6519                                 tmpcol.set(Lmean+Lmean*contrast*(gr*ga));
6520                                 area.pix(x+width,y+height,tmpcol);
6521                         }
6522                 }
6523         }
6524 };
6525 }}}</pre>
6526 </div>
6527 <div title="Gabor方位判断の実験プログラム例" modifier="Kazushi Maruya" modified="200712061920" created="200709162000" changecount="6">
6528 <pre>{{{
6529 #include &lt;psychlops.h&gt;
6530 #include &lt;stdio.h&gt;
6531 using namespace Psychlops;
6532
6533 class IndependentVariables
6534 {
6535         int MaxVarNum, MaxStepNum;
6536         double *VariableData;
6537         int *VariableStepNumber;
6538         int conditionNumber;
6539         int repeatNumber;
6540         
6541         Psychlops::Matrix CondMtx;
6542
6543         public:
6544         int trialNumber;
6545         
6546         IndependentVariables(int n, int m, int repeat): MaxVarNum(n), MaxStepNum(m){
6547                 VariableData = (double *)malloc(sizeof(double)*(n+1)*m);
6548                 VariableStepNumber = (int *)malloc(sizeof(double)*m);
6549                 repeatNumber=repeat;
6550                 trialNumber=-1;
6551         }
6552         
6553         virtual ~IndependentVariables(void){
6554                 free(VariableData);
6555                 free(VariableStepNumber);
6556         };
6557         
6558         virtual int set(int vnum, int arraynum, double *array){
6559                 if(vnum&lt;MaxVarNum+1 &amp;&amp; arraynum&lt;MaxStepNum+1){
6560                                   VariableStepNumber[vnum]=arraynum;
6561                                   for(int i=0; i&lt;arraynum; i++){*(VariableData+(vnum+1)*MaxStepNum+i)=*(array+i);}
6562                                   return arraynum;
6563                 }
6564                 else return -1; 
6565         };
6566         
6567         
6568         virtual int randomize(char* dataname=NULL){
6569                 trialNumber=repeatNumber;
6570                 for(int i=0; i&lt;MaxVarNum; i++)trialNumber*=VariableStepNumber[i];
6571                 if(trialNumber&lt;1){return -1;}
6572                 CondMtx.set(trialNumber, MaxVarNum);
6573                 
6574                 int i,j,a;
6575                 double temp=1, temp2;
6576
6577                 //Make Output Matrix
6578                 
6579                 for(i=0; i&lt;MaxVarNum; i++){
6580                         for(j=0; j&lt;i; j++){temp*=(double)VariableStepNumber[j];}
6581                         for(j=0; j&lt;trialNumber; j++){CondMtx(j+1,i+1)=(int)ceil(j/temp)% VariableStepNumber[i];}
6582                         temp=1;
6583                 }
6584         
6585                 //Randomize Output Matrix
6586                 for(i=1; i&lt;trialNumber;i++){
6587                         a = Psychlops::random(i-1)+1;
6588                         for(j=1;j&lt;MaxVarNum+1;j++){
6589                                 temp2=CondMtx(a,j); 
6590                                 CondMtx(a,j)=CondMtx(i,j); 
6591                                 CondMtx(i,j)=temp2;
6592                         }
6593                 }
6594                 
6595                 //swtich of the condition matrix save
6596                 if(dataname){   
6597                         std::ofstream output;
6598                         output.open(dataname);
6599                         output &lt;&lt; CondMtx &lt;&lt; std::endl;
6600                         output.close();
6601                 }
6602                 
6603         }
6604         
6605         virtual double get(int vnum, int trial_now){
6606                 if(vnum&lt;MaxVarNum &amp;&amp; (int)trial_now&lt;trialNumber)
6607                         return *(VariableData+(vnum+1)*MaxStepNum+getCondMtx(vnum, trial_now));
6608                 else return -1;
6609         };
6610         
6611         virtual int getCondMtx(int vnum, unsigned int trial_now){
6612                 if(vnum&lt;MaxVarNum &amp;&amp; (int)trial_now&lt;trialNumber)
6613                         return CondMtx( trial_now+1, vnum+1);
6614                 else return -1;
6615         };
6616
6617         virtual double getIndependent(int vnum, int stepnum){
6618                 if(vnum&lt;MaxVarNum &amp;&amp; stepnum&lt;VariableStepNumber[vnum])
6619                         return *(VariableData+(vnum)*MaxStepNum+stepnum);
6620                 else return -1;
6621         };
6622         
6623         
6624         virtual int getMStep(int vnum){
6625                 if(vnum&lt;MaxVarNum)return VariableStepNumber[vnum];
6626                         else return -1;
6627         };
6628 }; 
6629
6630 class GaborImage {
6631 private:
6632 public:
6633         virtual void draw(Image &amp;area,  double ori, double freq, double phase, double Lmean, double *contrast) {
6634                 int center_x = area.getCenter().getX(), center_y = area.getCenter().getY();
6635                 int width = area.getWidth()/2, height = area.getHeight()/2;
6636                 Color tmpcol;
6637                 double sigma = (width)/3;
6638                 double SINORI, COSORI, _x, gr, ga, ph, R, G, B;
6639                 
6640
6641                 COSORI=cos(-ori*PI/180);
6642                 SINORI=sin(-ori*PI/180);
6643                 
6644                 ph=phase*PI/180;
6645                 
6646                 for(int y=-height; y&lt;height; y++) {
6647                         for(int x=-width; x&lt;width; x++) {
6648                                 _x=     COSORI * x - SINORI * y;
6649                                 ga=exp(-0.5*(pow(x/(sigma),2))-0.5*(pow(y/(sigma), 2)));
6650                                 gr=(1+cos(2*PI*freq*_x+ph))*0.5;
6651                                 R=Lmean+Lmean*contrast[0]*(gr*ga);
6652                                 G=Lmean+Lmean*contrast[1]*(gr*ga);
6653                                 B=Lmean+Lmean*contrast[2]*(gr*ga);
6654                                 tmpcol.set(R, G, B);
6655                                 area.pix(x+width,y+height,tmpcol); 
6656                         }
6657                 }
6658         }
6659
6660         virtual void draw(Image &amp;area,  double ori, double freq, double phase, double Lmean, double contrast) {
6661                 int center_x = area.getCenter().getX(), center_y = area.getCenter().getY();
6662                 int width = area.getWidth()/2, height = area.getHeight()/2;
6663                 Color tmpcol;
6664                 double sigma = (width)/3;
6665                 
6666                 double SINORI, COSORI, _y, gr, ga, ph;
6667                 
6668                 COSORI=cos(ori*PI/180);
6669                 SINORI=sin(ori*PI/180);
6670                 
6671                 ph=phase*PI/180;
6672                 
6673                 for(int y=-height; y&lt;height; y++) {
6674                         for(int x=-width; x&lt;width; x++) {
6675                                 _y=     SINORI * x + COSORI * y;
6676                                 ga=exp(-0.5*(pow(x/(sigma),2))-0.5*(pow(y/(sigma), 2)));
6677                                 gr=(1+cos(2*PI*freq*_y+ph))*0.5;
6678                                 tmpcol.set(Lmean+Lmean*contrast*(gr*ga));
6679                                 area.pix(x+width,y+height,tmpcol);
6680                         }
6681                 }
6682         }
6683 };
6684
6685 void psychlops_main() {
6686
6687         Canvas sampleA(Canvas::fullscreen);
6688         IndependentVariables invar(2,5,10);
6689
6690         double gaborOrientation[5]={0,1,2,3,4}; //Gaborの方位
6691         double duration[3]={100,250,500}; //刺激の提示時間
6692         
6693         invar.set(0, 5, gaborOrientation);
6694         invar.set(1, 3, duration);
6695         invar.randomize(&quot;ConditionMatrix.dat&quot;);
6696
6697         GaborImage gaborIMG;
6698         Psychlops::Image gIMG[10];
6699         double gIMGsize=100, ori;
6700         for(int i=0; i&lt;10; i++){
6701                 if(i&lt;5){ ori=90.0-pow(2.0, i);}
6702                         else{ ori=90.0+pow(2.0,i-4);}
6703                 gIMG[i].set(gIMGsize, gIMGsize);
6704                 gaborIMG.draw(gIMG[i], ori, 1.0/30.0, 0.0, 0.5, 0.2);
6705                 gIMG[i].centering();
6706         }
6707
6708
6709         const int trialNum=2*5*10;
6710         int answer[trialNum], orientationCondition[trialNum], durationCondition[trialNum], ans;
6711         int ori_now, dura_now;
6712         int direction;
6713         char trial_header[64];
6714
6715         for(int trial=0; trial&lt;trialNum; trial++){
6716         //各試行に対する変数の取得
6717                 direction=Psychlops::random(2); //0:right 1:left
6718                 ori_now=invar.get(0,trial)+5*direction;
6719                 dura_now=sampleA.getRefreshRate()*(invar.get(1,trial)/1000);
6720
6721         //被験者のキー入力待ち
6722                 sampleA.clear(0.5);
6723                 for(int i=0; i&lt;64; i++) trial_header[i] = 0;// 文字列の初期化
6724                 sprintf(trial_header, &quot;%s%d%s%d&quot;,&quot;Trial: &quot;,  trial, &quot; /&quot;, trialNum );//試行番号の埋め込み
6725                 sampleA.message(trial_header, sampleA.getHcenter(), sampleA.getVcenter()-100, Color::green);//キー待ちメッセージの描画
6726                 sampleA.flip();
6727                 while(!Input::get(Keyboard::spc));
6728
6729         //刺激描画
6730                 sampleA.clear(0.5);
6731                 gIMG[ori_now].draw();
6732                 sampleA.flip(dura_now);
6733                 sampleA.clear(0.5);
6734                 sampleA.flip();
6735
6736         //反応の取得と格納
6737                 while(1){
6738                         if(Input::get(Keyboard::j)){ans=0;break;} //&quot;j&quot;なら右(0)
6739                         if(Input::get(Keyboard::f)){ans=1;break;}//&quot;f&quot;なら左(1)
6740                         if(Input::get(Keyboard::esc)){exit(0);}
6741                 }
6742                 if(ans==direction)answer[trial]=1;
6743                         else answer[trial]=0;
6744                 orientationCondition[trial]=invar.get(0, trial);
6745                 durationCondition[trial]=invar.get(1, trial);
6746         }
6747
6748         sampleA.clear(0.5);
6749         sampleA.message(&quot;Press space key to exit.&quot;, sampleA.getHcenter(), sampleA.getVcenter()-100, Color::green);
6750         Data::savearray(&quot;ExptData.dat&quot;, &quot;Duration /t Orientation /t Answer&quot;, trialNum, durationCondition, orientationCondition, answer );
6751 }
6752
6753 }}}</pre>
6754 </div>
6755 <div title="GettingStarted" modifier="PsychlopsAdmin" created="200707220303" changecount="1">
6756 <pre>To get started with this blank TiddlyWiki, you'll need to modify the following tiddlers:
6757 * SiteTitle &amp; SiteSubtitle: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)
6758 * MainMenu: The menu (usually on the left)
6759 * DefaultTiddlers: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened
6760 You'll also need to enter your username for signing your edits: &lt;&lt;option txtUserName&gt;&gt;</pre>
6761 </div>
6762 <div title="Image()の宣言" modifier="Psychlops_Admin" modified="200709142109" created="200709142045" changecount="2">
6763 <pre>[[Psychlops::Image]]型は主に画像を取り扱います。
6764 [[Psychlops::Image]]型の命令を使用する前に必ず宣言が必要です。
6765 宣言のみの場合、[[Image::set()]]でImageの詳細情報を設定しますが
6766 宣言文のみでも情報の設定が可能です。
6767
6768 |!Imageの宣言|Image()|オフスクリーン領域を宣言します|
6769 |~|Image([[Psychlops::Rectangle]] rec,PixelComponentsCnt pixcompcntval)|指定された[[Psychlops::Rectangle]] 型と同じ大きさのオフスクリーン領域を宣言します|
6770 |~|~|[[Psychlops::Rectangle]] rect :[[Psychlops::Rectangle]] 型を指定|
6771 |~|~|PixelComponentsCnt pixcompcntval :[[カラーモード]]を指定。省略した場合はRGBが指定される|
6772 |~|Image(long x, long y, PixelComponentsCnt pixcompcntval)|指定されたx×yと同じ大きさのオフスクリーン領域を宣言します|
6773 |~|~|long x :オフスクリーン領域の横幅を指定|
6774 |~|~|long y :オフスクリーン領域の縦幅を指定|
6775 |~|~|PixelComponentsCnt pixcompcntval :[[カラーモード]]を指定。省略した場合はRGBが指定される|
6776
6777 コード例は[[3.1.1節|3.1.1 オフスクリーン領域の宣言]]参照</pre>
6778 </div>
6779 <div title="Image::centering()" modifier="Kazushi Maruya" modified="200712131731" created="200712131730" changecount="2">
6780 <pre>!Image::centering()
6781 [[Psychlops::Image]]型の領域を移動します。
6782 座標を指定する場合、その座標に領域の中心が設定されます。
6783 |!Image::centering()|centering()|[[Psychlops::Image]]型の領域を画面中心に移動します|
6784 |~|centering(double x, double y)|[[Psychlops::Image]]型の領域の中心を指定した座標(x,y)に移動します|
6785 |~|~|double x:移動する領域のx座標を指定|
6786 |~|~|double y:移動する領域のy座標を指定|
6787
6788 [[3.1.3 オフスクリーン上の任意の位置に描画する]]</pre>
6789 </div>
6790 <div title="Image::clear()" modifier="Psychlops_Admin" modified="200709142113" created="200709142111" changecount="2">
6791 <pre>オフスクリーン領域をクリアします。
6792 |!Image::clear()|clear([[Psychlops::Color]] col)|指定したオフスクリーン領域を指定色でクリアします|
6793 |~|~|[[Psychlops::Color]] col:クリアする色を指定|
6794
6795 !!Image::clear()の書き方
6796 Imageをマゼンダでクリアします。
6797 {{{
6798 &lt;例&gt;
6799 #include &lt;psychlops.h&gt;
6800 using namespace Psychlops;
6801
6802 double width=100,height=100;
6803 Psychlops::Rectangle rect1(width,height);
6804 Psychlops::Image Noise1(rect1);
6805
6806 void psychlops_main() {
6807
6808         Canvas sampleA(Canvas::fullscreen);
6809         Noise1.clear(Color::magenta);
6810         Noise1.draw();
6811         sampleA.flip();
6812         while(!Input::get(Keyboard::spc));
6813 }
6814 }}}</pre>
6815 </div>
6816 <div title="Image::draw()" modifier="YourName" modified="200803100605" created="200709142049" changecount="5">
6817 <pre>!Image::draw()
6818 実際にオフスクリーン領域に描画する際の命令です。
6819 この命令を記述しないといくら[[Image::pix()]]命令や[[Image::rect()]]命令を記述しても描画されませんので注意してください。
6820
6821 |!Image::draw()|draw()|指定したオフスクリーンを描画します|
6822 |~|draw(double left, double top)|指定したオフスクリーン領域を(left,top)を中心とした座標に設定します|
6823 |~|~|double left: 描画するオフスクリーン領域の水平座標値|
6824 |~|~|double top: 描画するオフスクリーン領域の垂直座標値|
6825
6826 コード例は[[3.1.2節|3.1.2 オフスクリーン上に点・四角形を描画する]]参照</pre>
6827 </div>
6828 <div title="Image::from()" modifier="Kazushi Maruya" created="200712131721" changecount="1">
6829 <pre>|!Image::from|from( luminance )|Matrixを輝度としてImageに読み込み、グレースケール画像を作成します。|
6830 |~|~|Matrix luminance: |
6831 |~|from( r, g, b )|Matrixを赤、緑、青チャネルとして読み込み、RGB画像を作成します。|
6832 |~|~|Matrix r: |
6833 |~|~|Matrix g: |
6834 |~|~|Matrix b: |
6835 |~|from( r, g, b, a) |Matrixを赤、緑、青、透明度チャネルとして読み込み、RGBA画像を作成します。|
6836 |~|~|Matrix r: |
6837 |~|~|Matrix g: |
6838 |~|~|Matrix b: |
6839 |~|~|Matrix a: |
6840 Imageの中身がすでに確保されていた場合は、破棄されます。
6841
6842 [[5.3.5 行列の中身をオフスクリーンに描画する]]</pre>
6843 </div>
6844 <div title="Image::getXXXX()" modifier="Psychlops_Admin" modified="200709142210" created="200709142100" changecount="7">
6845 <pre>Imageの様々な情報を、Image::getXXXX()命令を使って取得します(XXXXにはそれぞれの情報に対応した名前が入ります)。
6846 |!Image::getXXXX()|getCenter()|オフスクリーン領域の中心座標(x,y)を取得します|
6847 |~|getHcenter()|オフスクリーン領域の横方向の中心座標(x)を取得します|
6848 |~|getVcenter()|オフスクリーン領域の縦方向の中心座標(y)を取得します|
6849 |~|getHeight()|オフスクリーン領域の高さ(ピクセル)を取得します|
6850 |~|getWidth()|オフスクリーン領域の幅(ピクセル)を取得します|
6851 |~|getPix(int x, int y)|オフスクリーン領域上の座標(x,y)の色を取得します|
6852 |~|~|int x :オフスクリーン領域上の座標xを指定|
6853 |~|~|int y :オフスクリーン領域上の座標yを指定|
6854 |~|~|注:オフスクリーン領域の左上を座標(0,0)とする。オフスクリーン領域外の座標は指定しないこと|
6855
6856 !!getXXXX()の書き方
6857 [[Psychlops::Image]]型の情報を取得し画面表示を行います。
6858 画面表示は[[Canvas::var()]]命令と[[Canvas::msg()]]命令を使用します。
6859 {{{
6860 &lt;例&gt;
6861 #include &lt;psychlops.h&gt;
6862 using namespace Psychlops;
6863
6864 Psychlops::Image Noise1(300,300);
6865 Psychlops::Point point1;
6866 Psychlops::Color col1,col2;
6867 Psychlops::Rectangle rect1(50,50);
6868 double d1,d2,d3,d4,x,y;
6869
6870 void psychlops_main() {
6871
6872         Canvas sampleA(Canvas::fullscreen);
6873         Noise1.centering();
6874         col1.set(0.8,0.4,0.1);
6875         Noise1.clear(col1);
6876         
6877         point1=Noise1.getCenter();
6878         d1=Noise1.getHcenter();
6879         d2=Noise1.getVcenter();
6880         d3=Noise1.getHeight();
6881         d4=Noise1.getWidth();
6882         col2=Noise1.getPix(100,100);
6883         
6884         //get命令の内容を画面表示する
6885         x=point1.getX();
6886         y=point1.getY();
6887         sampleA.msg(&quot;getCenter_x:&quot;,50,200);
6888         sampleA.var(x,200,200);//getCenter:X
6889         sampleA.msg(&quot;getCenter_y:&quot;,50,250);
6890         sampleA.var(y,200,250);//getCenter:Y            
6891         sampleA.msg(&quot;getHcenter:&quot;,50,300);
6892         sampleA.var(d1,200,300);//getHcenter
6893         sampleA.msg(&quot;getVcenter:&quot;,50,350);
6894         sampleA.var(d2,200,350);//getVcenter
6895         sampleA.msg(&quot;gettHeight:&quot;,50,400);
6896         sampleA.var(d3,200,400);//getHeight
6897         sampleA.msg(&quot;getWidth:&quot;,50,450);
6898         sampleA.var(d4,200,450);//getWidth
6899         //取得した色で円を描画する
6900         sampleA.oval(rect1,col2);
6901
6902         Noise1.draw();
6903         sampleA.flip();
6904         while(!Input::get(Keyboard::spc));
6905 }
6906 }}}</pre>
6907 </div>
6908 <div title="Image::line()" modifier="YourName" modified="200803100604" created="200709142118" changecount="4">
6909 <pre>オフスクリーン領域に線を描画します。
6910 指定する座標は画面上左上を座標(0,0)にするのではなく
6911 オフスクリーン領域の左上を座標(0,0)とします。
6912 |!Image::line()|line(double x1, double y1, double x2, double y2, [[Psychlops::Color]] col)|座標(x1,y1)から座標(x2,y2)に指定色で線を描画します|
6913 |~|~|double x1 :描画する線の開始水平座標値|
6914 |~|~|double y1 :描画する線の開始垂直座標値|
6915 |~|~|double x2 :描画する線の終端水平座標値|
6916 |~|~|double y2 :描画する線の終端垂直座標値|
6917 |~|~|[[Psychlops::Color]] col:描画する線の描画色|
6918
6919 !!Image::line()の書き方
6920 座標(100,100)から座標(200,200)に赤線を描画します。
6921 指定Image外の座標に設定すると描画されないのでオフスクリーンの位置に注意すること。
6922 {{{
6923 &lt;例&gt;
6924 #include &lt;psychlops.h&gt;
6925 using namespace Psychlops;
6926
6927 Psychlops::Image Noise1(500,500);
6928
6929 void psychlops_main() {
6930
6931         Canvas sampleA(Canvas::fullscreen);
6932         Noise1.line(100,100,200,200,Color::red);
6933         Noise1.draw();
6934         sampleA.flip();
6935         while(!Input::get(Keyboard::spc));
6936 }
6937 }}}</pre>
6938 </div>
6939 <div title="Image::load()" modifier="Kazushi Maruya" modified="200712131733" created="200712131731" changecount="2">
6940 <pre>!Image::load()
6941 既存の任意の画像ファイルを読み込みます。
6942
6943 |!Image::load()|load(filename)|指定した画像ファイルを読み込みます|&gt;|&gt;|
6944 |~|~|filename: 読み込む画像ファイルの名前を指定|&gt;|&gt;|
6945 |~|~|読込み先を指定しない場合のパス|Mac:Macintosh HD¥ユーザ¥ユーザ名(任意)¥書類¥Psychlops¥|&gt;|
6946 |~|~|~|Win:実行ファイルと同じパス|&gt;|
6947 |~|~|読込みをする場合のパス指定|%APP%|Mac:実行バンドル(.app)と同じパス|
6948 |~|~|~|~|Win:実行ファイルと同じパス|
6949 |~|~|~|%RESOURCES%|Mac:実行バンドルの内部のResourcesフォルダ|
6950 |~|~|~|~|Win:実行ファイルのパスの直下のrscという名前のフォルダ|
6951 |~|~|~|%~USER_HOME%|Mac:&quot;~/&quot;と同じ(/Users/(user)/)|
6952 |~|~|~|~|Win:ユーザープロファイルフォルダ(C:\Documents and Settings\(user)\)|
6953 |~|~|~|%~USER_SETTING%|Mac:&quot;~/Library/Psychlops/&quot;|
6954 |~|~|~|~|Win:&quot;%~AppData%Psychlops&quot;|
6955 |~|~|~|%~USER_DOCUMENTS%|Mac:&quot;~/Documents/Psychlops&quot;|
6956 |~|~|~|~|Win:ユーザープロファイルフォルダ\My Documents\Psychlops|
6957 |~|~|~|%~USER_DOCUMENTS_ROOT%|Mac:&quot;~/Documents&quot;|
6958 |~|~|~|~|Win:ユーザープロファイルフォルダMy Documents\|
6959 **VISTA環境の場合、パス指定が正常に動作しません。VISTA環境の方はお手数ですが保存先を指定しないでお使いください。
6960
6961 [[3.3.2 画像ファイルを読み込み表示する]]</pre>
6962 </div>
6963 <div title="Image::oval()" modifier="Psychlops_Admin" modified="200709142129" created="200709142125" changecount="2">
6964 <pre>オフスクリーン領域に円を描画します。
6965 円の描画位置はオフスクリーン領域の座標(0,0)に描画されます。
6966 |!Image::oval()|([[Psychlops::Rectangle]] rect, [[Psychlops::Color]] col)|[[Psychlops::Rectangle]] rectで指定された直径の円を指定色で塗りつぶし描画します|
6967 |~|~|[[Psychlops::Rectangle]] rect:描画する円の直径|
6968 |~|~|[[Psychlops::Color]] col:描画する円の色を指定|
6969
6970 !!Image::oval()の書き方
6971 座標(300,300)からはじまるオフスクリーン領域に直径(100,100)の赤円を描画します。
6972 {{{
6973 &lt;例&gt;
6974 #include &lt;psychlops.h&gt;
6975 using namespace Psychlops;
6976
6977 Psychlops::Image Noise1(300,300);
6978 Psychlops::Rectangle rect1(100,100);
6979
6980 void psychlops_main() {
6981
6982         Canvas sampleA(Canvas::fullscreen);
6983         Noise1.centering();
6984         Noise1.oval(rect1,Color::red);
6985         Noise1.draw();
6986         sampleA.flip();
6987         while(!Input::get(Keyboard::spc));
6988 }
6989 }}}</pre>
6990 </div>
6991 <div title="Image::pix()" modifier="YourName" modified="200803100603" created="200709142049" changecount="4">
6992 <pre>!Image::pix()
6993 オフスクリーン領域に点を描画するための命令です。
6994 [[Canvas::pix()|2.2.1 画面上に点を描画する-Pointクラス1-]]命令同様に点を描画するために「座標」と「色の指定」を行います。
6995 色の指定についての詳細は[[2.2.5 描画する図形の色を指定する-Colorクラス-]]を参照してください。
6996
6997 |!Image::pix()|pix(double x, double y, [[Psychlops::Color]] col)|座標(x,y)に[[Psychlops::Color]] col色の点を描画します。|
6998 |~|~|double x: 描画する点の水平座標値|
6999 |~|~|double y: 描画する点の垂直座標値|
7000 |~|~|[[Psychlops::Color]] col:描画する点の描画色|
7001 |~|pix([[Psychlops::Point]] po, [[Psychlops::Color]] col)|Point座標(x,y)に[[Psychlops::Color]] col色の点を描画します|
7002 |~|~|[[Psychlops::Point]] po :描画する点の座標|
7003 |~|~|[[Psychlops::Color]] col:描画する点の色|
7004
7005 コード例は[[3.1.2節|3.1.2 オフスクリーン上に点・四角形を描画する]]参照</pre>
7006 </div>
7007 <div title="Image::rect()" modifier="YourName" modified="200803100619" created="200709142050" changecount="5">
7008 <pre>オフスクリーン領域に四角形を描画するための命令です。
7009 実際の描画処理は[[Image::draw()]]命令で行われます。
7010 色の指定についての詳細は[[2.2.5 描画する図形の色を指定する-Colorクラス-]]を参照してください。
7011
7012 |!Image::rect()|rect([[Psychlops::Rectangle]] rect, [[Psychlops::Color]] col)|[[Psychlops::Rectangle]]型の四角形を[[Psychlops::Color]] col色で描画します|
7013 |~|~|[[Psychlops::Rectangle]] rect : 矩形の描画範囲|
7014 |~|~|[[Psychlops::Color]] col:描画する四角形の描画色|
7015
7016 コード例は[[3.1.2節|3.1.2 オフスクリーン上に点・四角形を描画する]]参照</pre>
7017 </div>
7018 <div title="Image::save()" modifier="Kazushi Maruya" created="200712131732" changecount="1">
7019 <pre>|!Image::save()|save(filename)|指定したImageを保存します|&gt;|&gt;|
7020 |~|~|filename: 保存するImageの画像ファイル名称(拡張子まで)を指定|&gt;|&gt;|
7021 |~|~|保存先を指定しない場合のパス|Mac:Macintosh HD¥ユーザ¥ユーザ名(任意)¥書類¥Psychlops¥|&gt;|
7022 |~|~|~|Win:実行ファイルと同じパス|&gt;|
7023 |~|~|保存をする場合のパス指定|%APP%|Mac:実行バンドル(.app)と同じパス|
7024 |~|~|~|~|Win:実行ファイルと同じパス|
7025 |~|~|~|%RESOURCES%|Mac:実行バンドルの内部のResourcesフォルダ|
7026 |~|~|~|~|Win:実行ファイルのパスの直下のrscという名前のフォルダ|
7027 |~|~|~|%~USER_HOME%|Mac:&quot;~/&quot;と同じ(/Users/(user)/)|
7028 |~|~|~|~|Win:ユーザープロファイルフォルダ(C:\Documents and Settings\(user)\)|
7029 |~|~|~|%~USER_SETTING%|Mac:&quot;~/Library/Psychlops/&quot;|
7030 |~|~|~|~|Win:&quot;%~AppData%Psychlops&quot;|
7031 |~|~|~|%~USER_DOCUMENTS%|Mac:&quot;~/Documents/Psychlops&quot;|
7032 |~|~|~|~|Win:ユーザープロファイルフォルダ\My Documents\Psychlops|
7033 |~|~|~|%~USER_DOCUMENTS_ROOT%|Mac:&quot;~/Documents&quot;|
7034 |~|~|~|~|Win:ユーザープロファイルフォルダMy Documents\|
7035 **VISTA環境の場合、パス指定が正常に動作しません。VISTA環境の方はお手数ですが保存先を指定しないでお使いください。
7036
7037 [[3.3.1 画像ファイルを保存する]]</pre>
7038 </div>
7039 <div title="Image::set()" modifier="YourName" modified="200803100559" created="200709142046" changecount="4">
7040 <pre>宣言時に何の指定もしなかった場合、Image::set()命令を使用します。
7041 |!Image::set()|set([[Psychlops::Rectangle]] rect, PixelComponentsCnt pixcompcntval)|指定された[[Psychlops::Rectangle]] 型と同じ大きさのオフスクリーン領域を設定します|
7042 |~|~|[[Psychlops::Rectangle]] rect :オフスクリーンの大きさを与える[[Psychlops::Rectangle]] 型を指定(座標は引き継がれない)|
7043 |~|~|PixelComponentsCnt pixcompcntval :[[カラーモード]]を指定。省略した場合はRGBが指定される|
7044 |~|set(long x, long y, PixelComponentsCnt pixcompcntval)|指定されたx×yと同じ大きさのオフスクリーン領域を設定します|
7045 |~|~|long x :オフスクリーン領域の横幅|
7046 |~|~|long y :オフスクリーン領域の縦幅|
7047 |~|~|PixelComponentsCnt pixcompcntval :[[カラーモード]]を指定。省略した場合はRGBが指定される|
7048
7049 コード例は[[3.1.1節|3.1.1 オフスクリーン領域の宣言]]参照</pre>
7050 </div>
7051 <div title="Image::shift()" modifier="YourName" modified="200803100601" created="200709142053" changecount="2">
7052 <pre>[[Psychlops::Image]]型の領域を移動します。
7053 元の座標(x1,y1)を基点に入力値(x,y)だけ移動します。
7054 |!Image::shift()|shift(double h, double v)|座標(X,Y)を(h,v)だけ移動します|
7055 |~|~|double x:右水平方向の移動量|
7056 |~|~|double y:下垂直方向の移動量|
7057
7058 コード例は[[3.1.3節|3.1.3 オフスクリーン上の任意の位置に描画する]]参照</pre>
7059 </div>
7060 <div title="Image::to()" modifier="Psychlops_DevelopperG" modified="200910070830" created="200712131722" changecount="31">
7061 <pre>|!Image::to|&gt;|&gt;|!グレースケール画像の輝度値をMatrixに書き込みます|
7062 |~|to( Matrix  luminance )|
7063 |~|~|Matrix luminance|輝度値の行列|
7064 |~|to( Point po, double width, double height, Matrix  luminance)|
7065 |~|~|Point po |コピー元領域の左上座標&lt;br&gt;(省略時は画像左上を起点とします)|
7066 |~|~|double width |コピー元領域の幅(pixel)|
7067 |~|~|double height |コピー元領域の高さ(pixel)|
7068 |~|~|Matrix luminance |輝度値の行列|
7069 |~|to( Point po, Rectangle rect, Matrix lumiannce)  |
7070 |~|~|Point po |コピー元領域の左上座標&lt;br&gt;(省略時は画像左上を起点とします)|
7071 |~|~|Rectangle rect |コピー元領域のサイズを持つ矩形|
7072 |~|~|Matrix luminance|輝度値の行列|
7073 |~|to( Interval h_intvl, Interval v_intvl, Matrix luminance) |
7074 |~|~|Interval h_intvl |コピー元領域の横座標の範囲(pixel)|
7075 |~|~|Interval v_intvl |コピー元領域の縦座標の範囲(pixel)|
7076 |~|~|Matrix luminance |輝度値の行列|
7077
7078 |!Image::to|&gt;|&gt;|!RGB画像の赤、緑、青チャネル値をMatrixに書き込みます|
7079 |~|to( r, g, b ) |
7080 |~|~|Matrix r |赤チャンネルの輝度値の行列|
7081 |~|~|Matrix g |緑チャンネルの輝度値の行列|
7082 |~|~|Matrix b |青チャンネルの輝度値の行列|
7083 |~|to( Point po, double width, double height, Matrix r, Matrix g, Matrix b) |
7084 |~|~|Point po |コピー元領域の左上座標&lt;br&gt;(省略時は画像左上を起点とします)|
7085 |~|~|double width |コピー元領域の幅(pixel)|
7086 |~|~|double height |コピー元領域の高さ(pixel)|
7087 |~|~|Matrix r |赤チャネルの輝度値の行列|
7088 |~|~|Matrix g |緑チャネルの輝度値の行列|
7089 |~|~|Matrix b |青チャネルの輝度値の行列|
7090 |~|to( Point po, Rectangle rect, Matrix r, Matrix g, Matrix b)  |
7091 |~|~|Point po |コピー元領域の左上座標&lt;br&gt;(省略時は画像左上を起点とします)|
7092 |~|~|Rectangle rect |コピー元領域のサイズを持つ矩形|
7093 |~|~|Matrix r |赤チャンネルの輝度値の行列|
7094 |~|~|Matrix g |緑チャンネルの輝度値の行列|
7095 |~|~|Matrix b |青チャンネルの輝度値の行列|
7096 |~|to( Interval h_intvl, Interval v_intvl, Matrix r, Matrix g, Matrix b) |
7097 |~|~|Interval h_intvl |コピー元領域の横座標の範囲(pixel)|
7098 |~|~|Interval v_intvl |コピー元領域の縦座標の範囲(pixel)|
7099 |~|~|Matrix r |赤チャネルの輝度値の行列|
7100 |~|~|Matrix g |緑チャネルの輝度値の行列|
7101 |~|~|Matrix b |青チャネルの輝度値の行列|
7102
7103
7104 |!Image::to|&gt;|&gt;|!RGBA画像の各チャネルの輝度値・透明度チャネルの値をMatrixに書き込みます|
7105 |~|to( Matrix r, Matrix g, Matrix b, Matrix a) |
7106 |~|~|Matrix r |赤チャネルの輝度値の行列|
7107 |~|~|Matrix g |緑チャネルの輝度値の行列|
7108 |~|~|Matrix b |青チャネルの輝度値の行列|
7109 |~|~|Matrix a |透明度チャネルの行列|
7110 |~|to( Point po, double width, double height, Matrix r, Matrix g, Matrix b, Matrix a ) |
7111 |~|~|Point po |コピー元領域の左上座標&lt;br&gt;(省略時は画像左上を起点とします)|
7112 |~|~|double width |コピー元領域の幅(pixel)|
7113 |~|~|double height |コピー元領域の高さ(pixel)|
7114 |~|~|Matrix r |赤チャネルの輝度値の行列|
7115 |~|~|Matrix g |緑チャネルの輝度値の行列|
7116 |~|~|Matrix b |青チャネルの輝度値の行列|
7117 |~|~|Matrix a |透明度チャネルの行列|
7118 |~|to( Point po, Rectangle rect, Matrix r, Matrix g, Matrix b, Matrix a)  |
7119 |~|~|Point po |コピー元領域の左上座標&lt;br&gt;(省略時は画像左上を起点とします)|
7120 |~|~|Rectangle rect |コピー元領域のサイズを持つ矩形|
7121 |~|~|Matrix r |赤チャンネルの輝度値の行列|
7122 |~|~|Matrix g |緑チャンネルの輝度値の行列|
7123 |~|~|Matrix b |青チャンネルの輝度値の行列|
7124 |~|~|Matrix a |透明度チャネルの行列|
7125 |~|to( Interval h_intvl, Interval v_intvl, Matrix r, Matrix g, Matrix b, Matrix a ) |
7126 |~|~|Interval h_intvl |コピー元領域の横座標の範囲(pixel)|
7127 |~|~|Interval v_intvl |コピー元領域の縦座標の範囲(pixel)|
7128 |~|~|Matrix r |赤チャネルの輝度値の行列|
7129 |~|~|Matrix g |緑チャネルの輝度値の行列|
7130 |~|~|Matrix b |青チャネルの輝度値の行列|
7131 |~|~|Matrix a |透明度チャネルの行列|
7132
7133 サンプル
7134 {{{
7135 Img.to(1&lt;=rng&lt;=100, 1&lt;=rng&lt;=50, luminance);
7136 }}}
7137
7138 書き込み先のMatrixはいったん破棄されて作り直されます。
7139 大きさ等はImageのサイズと等しくなるように自動的に調整されます。
7140
7141 [[5.3.5 行列の中身をオフスクリーンに描画する]]</pre>
7142 </div>
7143 <div title="Image型に関して" modifier="Kazushi Maruya" modified="200712131731" created="200712131730" changecount="2">
7144 <pre>*[[オフスクリーンのサイズを指定したい|Image::set()]]
7145 *[[オフスクリーンを特定の描画色で塗りつぶしたい|Image::clear()]]
7146 *[[オフスクリーン上に点を打ちたい|Image::pix()]]
7147 *[[オフスクリーン上に線を引きたい|Image::line()]]
7148 *[[オフスクリーン上に四角形を描画したい|Image::rect()]]
7149 *[[オフスクリーン上に円を描画したい|Image::oval()]]
7150 *[[オフスクリーンの内容を画面に描画したい|Image::draw()]]
7151 *[[オフスクリーンの描画位置を画面中央や特定の座標にセンタリングしたい|Image::centering()]]
7152 *[[オフスクリーンの描画位置を移動させたい|Image::shift()]]
7153 *[[オフスクリーンのメンバ(情報)を取得したい|Image::getXXXX()]]
7154 *[[オフスクリーンに画像を読み込みたい|Image::load()]]
7155 *[[オフスクリーンの描画内容をファイルとして保存したい|Image::save()]]
7156 *[[Matrix型を用いて計算した描画内容をオフスクリーンへ読み込みたい|Image::to()]]
7157 *[[オフスクリーンの描画内容をMatrix型へ変換したい|Image::to()]]</pre>
7158 </div>
7159 <div title="IndependentVariables" modifier="Psychlops_Admin" modified="200710070017" created="200709162126" changecount="3">
7160 <pre>{{{
7161 class IndependentVariables
7162 {
7163         int MaxVarNum, MaxStepNum;
7164         double *VariableData;
7165         int *VariableStepNumber;
7166         int conditionNumber;
7167         int repeatNumber;
7168         
7169         Psychlops::Matrix CondMtx;
7170
7171         public:
7172         int trialNumber;
7173         
7174         IndependentVariables(int n, int m, int repeat): MaxVarNum(n), MaxStepNum(m){
7175                 VariableData = (double *)malloc(sizeof(double)*(n+1)*m);
7176                 VariableStepNumber = (int *)malloc(sizeof(double)*m);
7177                 repeatNumber=repeat;
7178                 trialNumber=-1;
7179         }
7180         
7181         virtual ~IndependentVariables(void){
7182                 free(VariableData);
7183                 free(VariableStepNumber);
7184         };
7185         
7186         virtual int set(int vnum, int arraynum, double *array){
7187                 if(vnum&lt;MaxVarNum+1 &amp;&amp; arraynum&lt;MaxStepNum+1){
7188                                   VariableStepNumber[vnum]=arraynum;
7189                                   for(int i=0; i&lt;arraynum; i++){*(VariableData+(vnum+1)*MaxStepNum+i)=*(array+i);}
7190                                   return arraynum;
7191                 }
7192                 else return -1; 
7193         };
7194         
7195         
7196         virtual int randomize(char* dataname=NULL){
7197                 trialNumber=repeatNumber;
7198                 for(int i=0; i&lt;MaxVarNum; i++)trialNumber*=VariableStepNumber[i];
7199                 if(trialNumber&lt;1){return -1;}
7200                 CondMtx.set(trialNumber, MaxVarNum);
7201                 
7202                 int i,j,a;
7203                 double temp=1, temp2;
7204
7205                 //Make Output Matrix
7206                 
7207                 for(i=0; i&lt;MaxVarNum; i++){
7208                         for(j=0; j&lt;i; j++){temp*=(double)VariableStepNumber[j];}
7209                         for(j=0; j&lt;trialNumber; j++){CondMtx(j+1,i+1)=(int)ceil(j/temp)% VariableStepNumber[i];}
7210                         temp=1;
7211                 }
7212         
7213                 //Randomize Output Matrix
7214                 for(i=1; i&lt;trialNumber;i++){
7215                         a = Psychlops::random(i-1)+1;
7216                         for(j=1;j&lt;MaxVarNum+1;j++){
7217                                 temp2=CondMtx(a,j); 
7218                                 CondMtx(a,j)=CondMtx(i,j); 
7219                                 CondMtx(i,j)=temp2;
7220                         }
7221                 }
7222                 
7223                 //swtich of the condition matrix save
7224                 if(dataname){   
7225                         std::ofstream output;
7226                         output.open(dataname);
7227                         output &lt;&lt; CondMtx &lt;&lt; std::endl;
7228                         output.close();
7229                 }
7230                 
7231         }
7232         
7233         virtual double get(int vnum, int trial_now){
7234                 if(vnum&lt;MaxVarNum &amp;&amp; (int)trial_now&lt;trialNumber)
7235                         return *(VariableData+(vnum+1)*MaxStepNum+getCondMtx(vnum, trial_now));
7236                 else return -1;
7237         };
7238         
7239         virtual int getCondMtx(int vnum, unsigned int trial_now){
7240                 if(vnum&lt;MaxVarNum &amp;&amp; (int)trial_now&lt;trialNumber)
7241                         return CondMtx( trial_now+1, vnum+1);
7242                 else return -1;
7243         };
7244
7245         virtual double getIndependent(int vnum, int stepnum){
7246                 if(vnum&lt;MaxVarNum &amp;&amp; stepnum&lt;VariableStepNumber[vnum])
7247                         return *(VariableData+(vnum)*MaxStepNum+stepnum);
7248                 else return -1;
7249         };
7250         
7251         
7252         virtual int getMStep(int vnum){
7253                 if(vnum&lt;MaxVarNum)return VariableStepNumber[vnum];
7254                         else return -1;
7255         };
7256 }; 
7257  
7258 }}}</pre>
7259 </div>
7260 <div title="Input::get()" modifier="Kazushi Maruya" created="200712131738" changecount="1">
7261 <pre>|!Input::get()|Input::get([[Keyboard::KeyCode|キーコード表]] code, [[Keyboard::KeyState]] state)|キーボードの反応を取得します|
7262 |~|~|code 反応を取得するキーの名前 [[キーコード表]] 参照|
7263 |~|~|state 反応状態のオプション (省略時はpushed) [[Keyboard::KeyState]]参照|
7264 |~|Input::get([[Mouse::ButtonCode|キーコード表]] code, [[Mouse::ButtonState]] state)|マウスの反応を取得します|
7265 |~|~|code 反応を取得するマウスボタンの名前 [[キーコード表]] 参照|
7266 |~|~|state 反応状態のオプション (省略時はpushed) [[Mouse::ButtonState]]参照|
7267
7268 [[4.1 外部装置(キーボード/マウス)の操作内容を取得する]]</pre>
7269 </div>
7270 <div title="Input::refresh()" modifier="PsychlopsAdmin" modified="200709171526" created="200709171520" changecount="2">
7271 <pre>キーボードやマウスといった外部入力装置の入出力ルーチンの初期化処理
7272 |!Input::refresh()|refresh(void)|キーボード・マウスの入出力ルーチンを初期化します|
7273
7274 コード例は[[Tips: Psychlopsにおけるキーボードマウスの入出力検出ルーチン]]参照</pre>
7275 </div>
7276 <div title="Keyboard::KeyState" modifier="Psychlops_Admin" modified="200709142243" created="200708240330" changecount="3">
7277 <pre>| !~ButtonState | pressed |ボタンが押し続けられている |
7278 |~| pushed | 判定が行われるまでにボタンが押された |
7279 |~| released | 判定が行われるまでにボタンが離された |</pre>
7280 </div>
7281 <div title="MainMenu" modifier="Psychlops_DevelopperG" modified="200909300421" created="200707220305" changecount="64">
7282 <pre>[[このマニュアルの見方について(ここをクリックしてください)]]
7283
7284 [[0. Psyclopsとは]]
7285
7286 [[第I部 Psychlopsの基本]]
7287 [[1. インストールを行う]]
7288 [[2. 基本的な描画を行う]]
7289 [[3. 複雑な描画を行う1(オフスクリーンへの描画)]]
7290 [[4. 複雑な描画を行う2(複数オブジェクトのグループ化と回転・拡大縮小)]]
7291 [[5. 入出力を行う]]
7292 [[6. 時間制御と各種ツールを使用する]]
7293
7294 [[第II部 Psychlopsの応用]]
7295 [[1. 既成クラスを用いた簡単な実験プログラムを作成する]]
7296 [[2. デモを作成する]]
7297 [[3. 透明度を使って刺激描画を高速化する]]
7298 [[4. アナログ入出力を行う]]
7299
7300 付録
7301 [[関数一覧_FunctionList]]
7302 [[関数逆引きとQ&amp;A]]
7303 [[Tips]]
7304
7305 [[MainMenu]]</pre>
7306 </div>
7307 <div title="Math::mod" modifier="Kazushi Maruya" modified="200712180137" created="200712100223" changecount="5">
7308 <pre>剰余の計算を行います。
7309
7310 負の数を割ったときの剰余は多くの言語で未定義とされていますが、C++(%演算子やfmod関数)では負の数になります。また、C++の剰余演算子は整数型のみ定義されています。
7311 Math::modは剰余の計算を浮動小数点型に拡張するとともに、負の数を割ったときの挙動を周期関数的にしてあります。視覚実験では周期関数の位相を値とすることが多くありますが、この関数を使うと位相が負でも正でも一定の範囲の剰余が得られます。ifやswitchで値ごとに条件分岐している場合には特に役立ちます。
7312
7313 たとえば、-450を360で割った剰余をとる場合、
7314 {{{
7315  -90 == -450 % 360
7316  270 == Math::mod(-450, 360)
7317 }}}
7318 %演算子では剰余が負の数になってしまいますが、Math::modを使うと正の数で得ることができます。両関数をグラフで描画すると下図のようになります。
7319
7320 [img[image/Math.mod.png]]
7321
7322
7323 |!Math::mod()|mod(double lhs, double rhs)|剰余を計算します。|
7324 |~|~|double lhs: 左辺項(割られる数)を指定します。|
7325 |~|~|double rhs: 右辺項(割る数)を指定します。|
7326
7327 Math::mod()の使用例
7328 以下の例では%演算子とMath::mod()関数の返り値をプロットしています。
7329 上と同様の図が画面に表示されます
7330
7331 {{{
7332 #include &lt;psychlops.h&gt;
7333 using namespace Psychlops;
7334
7335 void psychlops_main() {
7336         Canvas display(Canvas::fullscreen);
7337
7338         double upper_v = display.getVcenter()-100, lower_v = display.getVcenter()+100;
7339
7340         while(!Input::get(Keyboard::esc)) {
7341                 //Draw Axis
7342                 display.line(display.getHcenter()-300, upper_v, display.getHcenter()+300, upper_v, Color::white);
7343                 display.line(display.getHcenter()-300, lower_v, display.getHcenter()+300, lower_v, Color::white);
7344                 display.line(display.getHcenter(), upper_v+60, display.getHcenter(), upper_v-60, Color::white);
7345                 display.line(display.getHcenter(), lower_v+60, display.getHcenter(), lower_v-60, Color::white);
7346                 display.msg(&quot;C++ %&quot;, display.getHcenter()-100, upper_v -20, Color::white);
7347                 display.msg(&quot;Math::mod&quot;, display.getHcenter()-100, lower_v -20, Color::white);
7348
7349                 //Plot Mod Results
7350                 for(int i=-300; i&lt;300; i++) {
7351                         display.pix(i+display.getHcenter(), upper_v - i%50, Color::red);
7352                         display.pix(i+display.getHcenter(), lower_v - Math::mod(i, 50), Color::green);
7353                 }
7354                 display.flip();
7355         }
7356 }
7357 }}}</pre>
7358 </div>
7359 <div title="Math::shuffle" modifier="YourName" modified="200712100209" created="200712100206" changecount="2">
7360 <pre>配列の中身をシャッフルします。
7361
7362 |!Psychlops::shuffle()|shuffle(X array, int num)|配列の中身をシャッフルします。|
7363 |~|~|X array: 配列変数を指定します。型=代入が可能であればなんでもかまいません。|
7364 |~|~|int num: 配列の要素数を指定します。|
7365 |~|shuffle(X array, int num, X init)|配列を初期化してから中身をシャッフルします。|
7366 |~|~|X array: 配列変数を指定します。型はint, double等で有効です。|
7367 |~|~|int num: 配列の要素数を指定します。|
7368 |~|~|X init: 配列の初期値です。0を指定すると、配列が0~num-1の数で埋められてから、シャッフルを行います。|
7369
7370 !!shuffleの使い方
7371
7372 {{{
7373 #include &lt;psychlops.h&gt;
7374 using namespace Psychlops;
7375
7376 void psychlops_main() {
7377         Canvas display(Canvas::fullscreen);
7378
7379         const int NUM = 10;
7380         int array[NUM];
7381         Math::shuffle(array, NUM, 0);
7382
7383         while(!Input::get(Keyboard::esc)) {
7384                 for(int i=0; i&lt;NUM; i++) {
7385                         display.var(array[i], 100, 20*i + 100);
7386                 }
7387                 display.flip();
7388         }
7389 }
7390 }}}</pre>
7391 </div>
7392 <div title="Matrix::catRow()" modifier="YourName" created="200712100125" changecount="1">
7393 <pre>この行列の下に他の行列を継ぎ足します。
7394
7395 |!Matrix::catRow|catRow(other)|この行列の次の行から別の行列を継ぎ足します。|
7396 |~|~|Matrix other: 付け足す行列を指定します。付け足してもこれ自身は消されません。|
7397
7398 {{{
7399 #include &lt;fstream&gt;
7400 #include &lt;psychlops.h&gt;
7401 using namespace Psychlops;
7402
7403 Psychlops::Matrix LuminanceMap1, LuminanceMap2;
7404 long Width=5, Height=1;
7405
7406 void psychlops_main() {
7407         std::ofstream result(&quot;dump.txt&quot;);
7408
7409
7410         LuminanceMap1.set(Height, Width);
7411         LuminanceMap1 = 1;
7412         LuminanceMap2.set(Height, Width);
7413         LuminanceMap2 = 2;
7414
7415         LuminanceMap1.catRows(LuminanceMap2);
7416         result &lt;&lt; LuminanceMap1;
7417 }
7418 }}}
7419
7420
7421 </pre>
7422 </div>
7423 <div title="Matrix::getXXXX()" modifier="YourName" created="200712100131" changecount="2">
7424 <pre>[[Psychlops::Matrix]]型の情報を、Matrix::getXXXX()命令を使って取得します(XXXXにはそれぞれの情報に対応した
7425
7426 名前が入ります)。
7427 |!Matrix::getXXXX()|getRows()|行列の行数を取得します|
7428 |~|getCols()|行列の列数を取得します|
7429
7430 !!Matrix::getXXXX()の書き方
7431 [[Psychlops::Matrix]]型の情報を取得しファイルに出力します。
7432
7433 {{{
7434 #include &lt;fstream&gt;
7435 #include &lt;psychlops.h&gt;
7436 using namespace Psychlops;
7437
7438 Psychlops::Matrix LuminanceMap1;
7439 long Width=5, Height=3;
7440
7441 void psychlops_main() {
7442         std::ofstream result(&quot;dump.txt&quot;);
7443
7444
7445         LuminanceMap1.set(Height, Width);
7446
7447         result &lt;&lt; LuminanceMap1.getRows() &lt;&lt; &quot; &quot; &lt;&lt; LuminanceMap1.getCols();
7448 }
7449 }}}
7450 </pre>
7451 </div>
7452 <div title="Matrix::min() / max()" modifier="YourName" created="200712100135" changecount="1">
7453 <pre>行列の全要素中の最大値・最小値を得ます
7454
7455 |!Matrix::min|min()|全要素中の最小値を得ます。|
7456 |!Matrix::max|max()|全要素中の最大値を得ます。|
7457 {{{
7458 // 行列の要素に乱数を代入し、その中の最大値と最小値を抽出します。
7459 #include &lt;fstream&gt;
7460 #include &lt;psychlops.h&gt;
7461 using namespace Psychlops;
7462
7463 Psychlops::Matrix LuminanceMap1;
7464 long Width=10, Height=10;
7465
7466 void psychlops_main() {
7467         std::ofstream result(&quot;dump.txt&quot;);
7468
7469
7470         LuminanceMap1.set(Height, Width);
7471         for(int row; row&lt;Width; row++) {
7472                 for(int col; col&lt;Width; col++) {
7473                         LuminanceMap1(row, col) = Psychlops::random();
7474                 }
7475         }
7476         result &lt;&lt; LuminanceMap1;
7477         result &lt;&lt; LuminanceMap1.max() &lt;&lt; &quot; &quot; &lt;&lt; LuminanceMap1.min();
7478
7479 }
7480 }}}
7481
7482
7483
7484 要素どうしで大きいものをまとめた行列、小さいものをまとめた行列を返すmax/minもあります。
7485
7486 |!Matrix::min|min(Matrix a, Matrix b)|aとbの各要素を比較し、小さいほうを要素とする行列を返します。両Matrixの大きさは同じでなければなりません。|
7487 |!Matrix::max|max(Matrix a, Matrix b)|aとbの各要素を比較し、大きいほうを要素とする行列を返します。両Matrixの大きさは同じでなければなりません。|
7488
7489 {{{
7490 A = 
7491 1 2 3
7492 0 0 2
7493
7494 B =
7495 5 3 0
7496 1 1 1
7497
7498 C = max(A, B);
7499
7500 C =&gt;
7501 5 3 3
7502 1 1 2
7503
7504 }}}
7505 </pre>
7506 </div>
7507 <div title="Matrix::reshape()" modifier="YourName" created="200712100127" changecount="1">
7508 <pre>行列をいったん1行の配列に並べ、新たに行を仕切りなおします。
7509
7510 |!Matrix::reshape|reshape(row, col)|要素を変えずに、行と列の数のみを変更します。要素数(行*列)は変更前と変更後で同じでなければなりません。|
7511 |~|~|int row 新しい行数を指定します。|
7512 |~|~|int col 新しい列数を指定します。|
7513
7514 !!reshape操作の内容
7515 {{{
7516 1 2 3
7517 4 5 6
7518 をreshape(3, 2)すると
7519 1 2
7520 3 4
7521 5 6
7522 }}}
7523
7524 !!reshapeの書き方
7525 {{{
7526 #include &lt;fstream&gt;
7527 #include &lt;psychlops.h&gt;
7528 using namespace Psychlops;
7529
7530 Psychlops::Matrix LuminanceMap1;
7531 long Width=16, Height=1;
7532
7533 void psychlops_main() {
7534         std::ofstream result(&quot;dump.txt&quot;);
7535
7536
7537         LuminanceMap1.set(Height, Width);
7538         for(int col; col&lt;Width; col++) {
7539                 LuminanceMap1(1, col) = col;
7540         }
7541         result &lt;&lt; LuminanceMap1.reshape(4, 4);
7542 }
7543 }}}
7544
7545 </pre>
7546 </div>
7547 <div title="Matrix::rot90()" modifier="YourName" created="200712100103" changecount="1">
7548 <pre>行列を90度単位で回転させます。
7549
7550 |!Matrix::rot90|rot90(count)|行列を90度単位で反時計回りに回転します。|
7551 |~|~|int count 回転角度を90度単位で指定します。-3,1,5のとき90度、-2,2,6のとき180度、-1,3,7のとき270度回転します。|
7552
7553 !!回転操作の内容
7554 {{{
7555 1 2 3
7556 4 5 6
7557 をrot90(1)すると
7558 3 6
7559 2 5
7560 1 4
7561 }}}
7562
7563 !!rot90()の書き方
7564 {{{
7565 #include &lt;fstream&gt;
7566 #include &lt;psychlops.h&gt;
7567 using namespace Psychlops;
7568
7569 Psychlops::Matrix LuminanceMap1;
7570 long Width=16, Height=1;
7571
7572 void psychlops_main() {
7573         std::ofstream result(&quot;dump.txt&quot;);
7574
7575
7576         LuminanceMap1.set(Height, Width);
7577         for(int col; col&lt;Width; col++) {
7578                 LuminanceMap1(1, col) = col;
7579         }
7580         result &lt;&lt; LuminanceMap1.rot90(1);
7581 }
7582 }}}
7583 </pre>
7584 </div>
7585 <div title="Matrix::set()" modifier="YourName" modified="200803100614" created="200712131744" changecount="3">
7586 <pre>!Matrix::set()
7587 行列の宣言時に何の指定もしなかった場合、Matrix::set()命令を使用します。
7588 |!Matrix::set()|Matrix(rows, cols)|指定されたrows行cols列の行列を設定します|
7589 |~|~|long rows :行列の行数|
7590 |~|~|long cols :行列の列数|
7591
7592 [[5.3.1 行列の宣言]]</pre>
7593 </div>
7594 <div title="Matrix::slide()" modifier="YourName" created="200712100100" changecount="1">
7595 <pre>各要素を行・列各方向にずらす命令です。
7596
7597 |!Matrix::slide|slide(row, col)|要素を行方向、列方向にずらします。あふれた分は、反対側に移動します。|
7598 |~|~|int row 行方向にずらす量を指定します。|
7599 |~|~|int col 列方向にずらす量を指定します。|
7600
7601 !!slide操作の内容
7602 {{{
7603 1 2 3
7604 4 5 6
7605 をslide(1,0)すると
7606 4 5 6
7607 1 2 3
7608
7609 1 2 3
7610 4 5 6
7611 をslide(0,1)すると
7612 3 1 2
7613 6 4 5
7614
7615 1 2 3
7616 4 5 6
7617 をslide(1,1)すると
7618 6 4 5
7619 3 1 2
7620 }}}
7621
7622 !!slideの書き方
7623 {{{
7624 #include &lt;fstream&gt;
7625 #include &lt;psychlops.h&gt;
7626 using namespace Psychlops;
7627
7628 Psychlops::Matrix LuminanceMap1;
7629 long Width=10, Height=10;
7630
7631 void psychlops_main() {
7632         std::ofstream result(&quot;dump.txt&quot;);
7633
7634
7635         LuminanceMap1.set(Height, Width);
7636         for(int row; row&lt;Width; row++) {
7637                 for(int col; col&lt;Width; col++) {
7638                         LuminanceMap1(row, col) = row*Height + col;
7639                 }
7640         }
7641         result &lt;&lt; LuminanceMap1.slide(1,0);
7642 }
7643 }}}
7644 </pre>
7645 </div>
7646 <div title="Matrix::transpose()" modifier="YourName" modified="200803140532" created="200712100101" changecount="2">
7647 <pre>行列を転置します。
7648
7649 |!Matrix::transpose|Matrix transpose(void)|行列を転置(行と列の読み替え)します。自分自身を書き換えるので注意してください。|
7650
7651 !!転置操作の内容
7652 {{{
7653 1 2 3
7654 4 5 6
7655 をtransposeすると
7656 1 4
7657 2 5
7658 3 6
7659 }}}
7660
7661 !!transposeの書き方
7662 {{{
7663 #include &lt;fstream&gt;
7664 #include &lt;psychlops.h&gt;
7665 using namespace Psychlops;
7666
7667 Psychlops::Matrix LuminanceMap1;
7668 long Width=10, Height=10;
7669
7670 void psychlops_main() {
7671         std::ofstream result(&quot;dump.txt&quot;);
7672
7673
7674         LuminanceMap1.set(Height, Width);
7675         for(int row; row&lt;Width; row++) {
7676                 for(int col; col&lt;Width; col++) {
7677                         LuminanceMap1(row, col) = row*Height + col;
7678                 }
7679         }
7680         result &lt;&lt; LuminanceMap1.transpose();
7681 }
7682 }}}
7683 </pre>
7684 </div>
7685 <div title="Matrix::要素アクセス・部分行列" modifier="Psychlops_DevelopperG" modified="200908190257" created="200712131744" changecount="2">
7686 <pre>[[6.3.2 行列の要素の操作]]</pre>
7687 </div>
7688 <div title="Mouse::ButtonState" modifier="Psychlops_Admin" created="200708240330" changecount="1">
7689 <pre>| ~KeyState | pressed | キーが押し続けられている |
7690 |~| pushed | 判定が行われるまでにキーが押された |
7691 |~| released | 判定が行われるまでにキーが離された |</pre>
7692 </div>
7693 <div title="Mouse::hide()" modifier="PsychlopsAdmin" created="200709171527" changecount="1">
7694 <pre>マウスカーソルの表示切り替え。
7695 [[Mouse::show()]]命令と対になっている命令です。
7696 |!Mouse::hide()|hide()|マウスカーソルを非表示にする|
7697
7698 コード例は[[例6: マウスのデモ]]参照</pre>
7699 </div>
7700 <div title="Mouse::show()" modifier="PsychlopsAdmin" created="200709171527" changecount="2">
7701 <pre>マウスカーソルの表示切り替え。
7702 [[Mouse::hide()]]命令と対になっている命令です。
7703 |!Mouse::show()|show()|マウスカーソルを表示する|
7704
7705 コード例は[[例6: マウスのデモ]]参照</pre>
7706 </div>
7707 <div title="PixelComponentsCnt" modifier="Psychlops_Admin" modified="200709041934" created="200709032052" changecount="5">
7708 <pre>PixelComponentsCntの指定方法
7709 |GRAY|グレースケールの輝度値|
7710 |RGB|色の三原色に基づいた赤・緑・青で構成された色|
7711 |RGBA|RGBに透明度が追加された色|
7712 ※省略時にはRGBが指定される
7713 詳しくは[[2.2.5 描画する図形の色を指定する-Colorクラス-]]を参照</pre>
7714 </div>
7715 <div title="PluginManager" modifier="PsychlopsAdmin" created="200707220245" changecount="1">
7716 <pre>&lt;&lt;plugins&gt;&gt;</pre>
7717 </div>
7718 <div title="Point()の宣言" modifier="Psychlops_Admin" modified="200709140117" created="200709132353" changecount="3">
7719 <pre>点を設定します。
7720 [[Psychlops::Point]]型の命令を使用する前に必ず宣言が必要です。
7721 宣言のみの場合、[[Point::set()]]で点の詳細情報を設定しますが
7722 宣言文のみでも情報の設定が可能です。
7723
7724 |!Pointの宣言|Point()|[[Psychlops::Point]]型の宣言を行います|
7725 |~|Point(double x, double y)|[[Psychlops::Point]]型の宣言を行い座標(x,y)に値を指定します|
7726 |~|~|double x:座標xを指定|
7727 |~|~|double y:座標yを指定|
7728
7729 !!Point()の宣言の書き方
7730 座標(100 × 100)に赤の点を描画します。
7731 {{{
7732 &lt;例&gt;
7733 #include &lt;psychlops.h&gt;
7734 using namespace Psychlops;
7735
7736 Psychlops::Point point1;
7737 void psychlops_main() {
7738
7739         Canvas sampleA(Canvas::fullscreen);
7740         point1.set(100,100);
7741         sampleA.pix(point1,Color::red);
7742         sampleA.flip();
7743         while(!Input::get(Keyboard::spc));
7744 }
7745 }}}</pre>
7746 </div>
7747 <div title="Point::centering()" modifier="YourName" modified="200803100601" created="200709132303" changecount="4">
7748 <pre>点を画面中心に描画します。
7749
7750 |!Point::centering()|centering()|座標(x,y)の点を画面の中心に移動します|
7751 |~|centering(Psychlops::Rectangle rect)|rectの中心に点を移動します|
7752
7753
7754 コード例は[[2.2.1節|2.2.1 画面上に点を描画する-Pointクラス1-]]参照</pre>
7755 </div>
7756 <div title="Point::get()" modifier="Psychlops_Admin" modified="200709142232" created="200709140110" changecount="4">
7757 <pre>対象の点の座標を取得します。
7758
7759 |!Point::get()|getX()|対象の点のX座標の値を取得します|
7760 |~|getY()|対象の点のY座標の値を取得します|
7761
7762 !!Point::get()の書き方
7763 {{{
7764 &lt;例&gt;
7765 #include &lt;psychlops.h&gt;
7766 using namespace Psychlops;
7767
7768 Psychlops::Point point1(100,100);
7769 double d1,d2;
7770
7771 void psychlops_main() {
7772
7773         Canvas sampleA(Canvas::fullscreen);
7774         point1.centering();
7775         sampleA.pix(point1,Color::red);
7776         
7777         d1=point1.getX();
7778         d2=point1.getY();
7779         
7780         //get命令の内容を画面表示する
7781         sampleA.msg(&quot;getX:&quot;,50,200);
7782         sampleA.var(d1,200,200);//getX
7783         sampleA.msg(&quot;getY:&quot;,50,250);
7784         sampleA.var(d2,200,250);//getY
7785         
7786         sampleA.flip();
7787         while(!Input::get(Keyboard::spc));
7788 }
7789 }}}</pre>
7790 </div>
7791 <div title="Point::set()" modifier="YourName" modified="200803100556" created="200709132300" changecount="6">
7792 <pre>座標(x,y),ないし座標X,Yを指定し点を描画します。
7793 Point::set()を使用し座標を取得することでソースの簡略化がなされ
7794 また、修正がしやすくなります。
7795
7796 |!Point::set()|set(double x, double y)|座標(x,y)の値を設定します|
7797 |~|~|double x:水平座標|
7798 |~|~|double y:垂直座標|
7799 |~|setX(double val)|水平座標の値を設定します|
7800 |~|~|double val :水平座標値|
7801 |~|setY(double val)|垂直座標の値を設定します|
7802 |~|~|double val :垂直座標値|
7803
7804 コード例は[[2.2.1節|2.2.1 画面上に点を描画する-Pointクラス1-]]参照</pre>
7805 </div>
7806 <div title="Point::shift()" modifier="YourName" modified="200803100557" created="200709132303" changecount="3">
7807 <pre>点の移動命令です。
7808 元の座標(x,y)を基点に入力値(h,v)だけ移動します。
7809
7810 |!Point::shift()|shift(double h, double v)|座標(X,Y)を(h,v)だけ移動します|
7811 |~|~|double h:右水平方向の移動量|
7812 |~|~|double v:下垂直方向の移動量|
7813
7814 コード例は[[2.2.1節|2.2.1 画面上に点を描画する-Pointクラス1-]]参照</pre>
7815 </div>
7816 <div title="Point型に関して" modifier="Kazushi Maruya" created="200712131720" changecount="1">
7817 <pre>* [[Point型の変数の座標を設定したい|Point::set()]]
7818 * [[Point型の変数を画面中央やRectangle型の変数の中央に移動したい|Point::centering()]]
7819 * [[Point型の変数の座標を移動させたい|Point::shift()]]
7820 * [[Point型の現在の座標を取得したい|Point::get()]]</pre>
7821 </div>
7822 <div title="Psychlops::Canvas" modifier="Psychlops_DevelopperG" modified="200910080816" created="200707220311" changecount="40">
7823 <pre>*メソッド
7824 **[[Canvasの宣言]]
7825 **[[Canvas::clear()]]
7826 **[[Canvas::flip()]]
7827 **[[Canvas::flipAfter()|Canvas::flip()]]
7828 **[[Canvas::flipAndWait()|Canvas::flip()]]
7829 **[[Canvas::getXXXX()]]
7830 **[[Canvas::var()]]
7831 **[[Canvas::msg()]]
7832 **[[Canvas::pix()]]
7833 **[[Canvas::line()]]
7834 **[[Canvas::rect()]]
7835 **[[Canvas::oval()]]
7836 **[[Canvas::to()]]
7837 **[[Canvas::copy()]]
7838 **[[Canvas::setGammaValue()]]
7839 **[[Canvas::setGammaTable()]]
7840 **[[Canvas::setClearColor()]]
7841 **[[Canvas::progressbar()]]
7842 **[[Canvas::watchFPS()]]
7843 **[[Canvas::showFPS()]]
7844 **[[Canvas::lastFailedFrames()]]
7845 *メンバ
7846 **TargetSurface</pre>
7847 </div>
7848 <div title="Psychlops::Clock" modifier="Psychlops_Admin" modified="200709142035" created="200709142034" changecount="2">
7849 <pre>*メソッド
7850 **[[Clock::update()]]
7851 **[[Clock::at_msec()]]</pre>
7852 </div>
7853 <div title="Psychlops::Color" modifier="YourName" modified="200802131229" created="200708212259" changecount="17">
7854 <pre>*メソッド
7855 ** [[Color()の宣言]]
7856 ** [[Color::set()]]
7857 ** [[Color::get()]]
7858 ** [[Color::setGammaValue()]]
7859 ** [[Color::strict_match]]
7860 ** [[Color::XXXX]]</pre>
7861 </div>
7862 <div title="Psychlops::Image" modifier="YourName" modified="200803050504" created="200709032052" changecount="15">
7863 <pre>*メソッド
7864 **[[Image()の宣言]]
7865 **[[Image::set()]]
7866 **[[Image::clear()]]
7867 **[[Image::pix()]]
7868 **[[Image::line()]]
7869 **[[Image::rect()]]
7870 **[[Image::oval()]]
7871 **[[Image::draw()]]
7872 **[[Image::quicken()|3.1.4 発展的な内容]]
7873 **[[Image::centering()]]
7874 **[[Image::shift()]]
7875 **[[Image::getXXXX()]]
7876 **[[Image::load()]]
7877 **[[Image::save()]]
7878 **[[Image::from()]]
7879 **[[Image::to()]]</pre>
7880 </div>
7881 <div title="Psychlops::Input" modifier="Kazushi Maruya" modified="200712131738" created="200709142237" changecount="6">
7882 <pre>*メソッド
7883 **[[Input::get()]]
7884 **[[Input::refresh()]]</pre>
7885 </div>
7886 <div title="Psychlops::Keyboad" modifier="PsychlopsAdmin" modified="200709170639" created="200709142246" changecount="2">
7887 <pre>*メソッド
7888 **[[Keyboad::get()]]
7889 </pre>
7890 </div>
7891 <div title="Psychlops::Math" modifier="Kazushi Maruya" modified="200712131628" created="200712100137" changecount="4">
7892 <pre>*メソッド
7893 ** [[Psychlops::random]]
7894 ** [[Math::shuffle]]
7895 ** [[Math::mod]]</pre>
7896 </div>
7897 <div title="Psychlops::Matrix" modifier="Psychlops_DevelopperG" modified="200910070546" created="200712100024" changecount="9">
7898 <pre>*メソッド
7899 **[[Matrix()の宣言|6.3.1 行列の宣言]]
7900 **[[Matrix::set()]]
7901 **[[Matrix::要素アクセス・部分行列|6.3.2 行列の要素の操作]]
7902 **[[Matrix::四則演算|6.3.4 行列の加減乗除]]
7903 **[[Matrix::slide()]]
7904 **[[Matrix::transpose()]]
7905 **[[Matrix::rot90()]]
7906 **[[Matrix::catRow()]]
7907 **[[Matrix::reshape()]]
7908 **[[Matrix::getXXXX()]]
7909 **[[Matrix::min() / max()]]
7910 **[[Matrix::mesh()|6.3.6 メッシュグリッドの作成]]</pre>
7911 </div>
7912 <div title="Psychlops::Mouse" modifier="PsychlopsAdmin" modified="200709170736" created="200709142247" changecount="3">
7913 <pre>*メソッド
7914 **[[Mouse::hide()]]
7915 **[[Mouse::show()]]</pre>
7916 </div>
7917 <div title="Psychlops::Point" modifier="Psychlops_Admin" modified="200709140121" created="200709122119" changecount="8">
7918 <pre>*メソッド
7919 **[[Point()の宣言]]
7920 **[[Point::set()]]
7921 **[[Point::centering()]]
7922 **[[Point::shift()]]
7923 **[[Point::get()]]</pre>
7924 </div>
7925 <div title="Psychlops::Rectangle" modifier="Psychlops_DevelopperG" modified="200910080843" created="200708230127" changecount="15">
7926 <pre>*メソッド
7927 **[[Rectangleの宣言]]
7928 **[[Rectangle::set()]]
7929 **[[Rectangle::centering()]]
7930 **[[Rectangle::shift()]]
7931 **[[Rectangle::draw()]]
7932 **[[Rectangle::resize()]]
7933 **[[Rectangle::setColor()]]
7934 **[[Rectangle::getXXXX()]]
7935 **[[Rectangle::include()]]
7936 **[[Rectangle::dup()]]</pre>
7937 </div>
7938 <div title="Psychlops::random" modifier="YourName" modified="200802232256" created="200712100149" changecount="11">
7939 <pre>乱数を返します。標準の擬似乱数生成アルゴリズムは[[dSFMT|http://www.math.sci.hiroshima-u.ac.jp/%7Em-mat/MT/SFMT/index-jp.html]]を利用しています。
7940 |!Psychlops::random()|random()|[0.0, 1.0)の範囲の一様乱数を生成します。|
7941 |~|random(double max)|[0.0, max)の範囲の一様乱数を生成します。|
7942 |~|random(int levels)|[0, levels)の範囲の離散的一様乱数を生成します。|
7943 |~|random(double min, double max)|[min, max)の値をランダムに返します。|
7944 |~|~|double min: ランダムで返す値の最小値を指定|
7945 |~|~|double max: ランダムで返す値の最大値を指定|
7946
7947 引数が実数(double)か整数(int)かで動作が異なりますので、実数で返したい場合は最大値が整数でも必ず1.0などのように小数点をつけて指定してください。整数変数を指定する場合は(double)でキャストしてください。
7948 {{{
7949 random(5)   ← [0,1,2,3,4] のいずれかの数がint型で返る
7950 random(5.0) ← [0, 5)の間の浮動小数点値が返る
7951
7952 int i = 5;
7953 random(i)         ← [0,1,2,3,4] のいずれかの数がint型で返る
7954 random((double)i) ← [0, 5)の間の浮動小数点値が返る
7955 }}}
7956
7957
7958 以下の例では100 ms毎にrandom命令を使用して作成したランダム配列を元に10000個のドットを画面に表示します。
7959
7960 {{{
7961 #include &lt;psychlops.h&gt;
7962 using namespace Psychlops;
7963
7964 void psychlops_main() {
7965         Canvas display(Canvas::fullscreen);
7966         double wid = 200, hei = 200;
7967         int refresh=display.getRefreshRate()/10;
7968         const int DOTNUM = 1000;
7969         double x[DOTNUM], y[DOTNUM];
7970         
7971
7972         while(!Input::get(Keyboard::esc)) {
7973                 display.clear(0.0);
7974                 for(int i=0; i&lt;DOTNUM; i++) {
7975                         x[i] = random(wid)+display.getHcenter()-wid*0.5;
7976                         y[i] = random(hei)+display.getVcenter()-hei*0.5;
7977                 }
7978                 display.pix(DOTNUM, x, y, Color::white);
7979                 display.flip(refresh);
7980         }
7981 }
7982 }}}</pre>
7983 </div>
7984 <div title="Psychlopsについて開発者に質問するには?" modifier="Kazushi Maruya" modified="200712180142" created="200712131755" changecount="2">
7985 <pre>直接の質問には(開発者と面識がある場合を除いて)お答えしておりません。
7986 Psychlops Authors Mailingリストに入っていただくのが便利です。
7987 このメーリングリストはGoogleグループサービスを利用しております。
7988 このメーリングリストに参加なされたい場合は、[[ここ|http://groups.google.com/group/psychlops/post?sendowner=1]]から申請してください。</pre>
7989 </div>
7990 <div title="Psychlopsを既存・自作のC/C++ライブラリと併用することができますか?" modifier="Kazushi Maruya" created="200712180146" changecount="1">
7991 <pre>可能です。
7992 OpenGL命令に関しては、Psychlopsライブラリのインクルード時に既にOpenGLライブラリのインクルードが行われていますので、そのまま書いていただいてコンパイル・実行可能となっています。
7993 自作ライブラリについては、関数名の衝突が無ければそのまま書いていただいて結構です。関数名が衝突する場合は、スコープ演算子などを用いて衝突を回避すれば利用が可能です。</pre>
7994 </div>
7995 <div title="Psychlopsプログラムのデフォルトテンプレート" modifier="YourName" created="200802191428" changecount="1">
7996 <pre>{{{
7997 void RectLuminance() {
7998         double rect_size = 100;
7999         double rect_lum  = 0.5;
8000         double bg_lum    = 0.2;
8001
8002         Psychlops::Rectangle rect(rect_size,rect_size);
8003         rect.centering();
8004
8005         Range rng;
8006         Independent &lt;&lt; rect_size | &quot;Rect Size&quot;      |   1&lt; rng&lt; 500 | 10.0 | 2.0 ;
8007         Independent &lt;&lt; rect_lum  | &quot;Rect Luminance&quot; | 0.0&lt;=rng&lt;=1.0 |  0.1 | 0.05;
8008         Independent &lt;&lt; bg_lum    | &quot;BG Luminance&quot;   | 0.0&lt;=rng&lt;=1.0 |  0.1 | 0.05;
8009
8010         while(!Input::get(Keyboard::esc)) {
8011                 Display::clear(Color(bg_lum));
8012                 rect.resize(rect_size,rect_size);
8013                 rect.display(rect_lum);
8014                 Display::flip();
8015         }
8016 }
8017
8018 void psychlops_main() {
8019         Canvas display(Canvas::fullscreen);
8020
8021         Procedure p;
8022         p.setDesign(Procedure::DEMO);
8023         p.setProcedure(RectLuminance);
8024         p.run();
8025 }
8026 }}}</pre>
8027 </div>
8028 <div title="Rectangle::centering()" modifier="Psychlops_DevelopperG" modified="200910080843" created="200709131913" changecount="8">
8029 <pre>[[Psychlops::Rectangle]]型の図形を移動します。
8030 [img[image/rect_centering().png]]
8031 座標を指定する場合、その座標に図形の中心が設定されます。
8032 |!Rectangle::centering()|centering()|[[Psychlops::Rectangle]] を画面中心に移動します|
8033 |~|centering(double x, double y)|[[Psychlops::Rectangle]] を指定した座標(x,y)に移動します|
8034 |~|~|double x:移動する図形のx座標|
8035 |~|~|double y:移動する図形のy座標|
8036 |~|centering([[Psychlops::Point]] po)|[[Psychlops::Rectangle]]を指定したpoint座標(x,y)に移動します|
8037 |~|~|[[Psychlops::Point]] po:移動する図形の(x,y)座標|
8038 |~|centering(Rectangle rect)|[[Psychlops::Rectangle]] を指定した[[Psychlops::Rectangle]] rectの中心座標(x,y)に移動します(2つのRectangleを中央ぞろえします)|
8039 |~|~|[[Psychlops::Rectangle]] rect :センタリングの中心座標を与える[[Psychlops::Rectangle]]|
8040
8041 コード例は[[2.2.6節|2.2.6 画面上に縞を描画する-Rectangleクラス2-]]参照</pre>
8042 </div>
8043 <div title="Rectangle::draw()" modifier="YourName" modified="200803100553" created="200709131911" changecount="7">
8044 <pre>[[Psychlops::Rectangle]]型の図形の描画命令です。
8045 |!Rectangle::draw()|draw()|対象の[[Psychlops::Rectangle]]型を描画します|
8046 |~|draw([[Psychlops::Color]] col)|対象の[[Psychlops::Rectangle]]型を[[Psychlops::Color]] colで塗りつぶし描画します|
8047 |~|~|[[Psychlops::Color]] col:描画する四角形の描画色|
8048
8049 コード例は[[2.2.6節|2.2.6 画面上に縞を描画する-Rectangleクラス2-]]参照</pre>
8050 </div>
8051 <div title="Rectangle::dup()" modifier="YourName" modified="200803100555" created="200802281432" changecount="2">
8052 <pre>[[Psychlops::Rectangle]]型を複製した一時オブジェクトを作成します。
8053
8054 |!Rectangle::dup()|矩形領域の複製を行います|
8055
8056 {{{
8057  Psychlops::Rectangle rect1(10,10), rect2(10,10);
8058  rect1.centering();             // rect1の中心位置は変わっている
8059  rect2.dup().centering()     // rect2の中心位置は変わっていないが、複製がセンタリングされている
8060 }}}</pre>
8061 </div>
8062 <div title="Rectangle::getXXXX()" modifier="Psychlops_Admin" modified="200709132236" created="200709131915" changecount="12">
8063 <pre>[[Psychlops::Rectangle]]型の情報を、Rectangle::getXXXX()命令を使って取得します(XXXXにはそれぞれの情報に対応した名前が入ります)。
8064 |!Rectangle::getXXXX()|getCenter()|四角形の中心座標(x,y)を取得します|
8065 |~|getHcenter()|横方向の中心座標(x)を取得します|
8066 |~|getVcenter()|縦方向の中心座標(y)を取得します|
8067 |~|getHeight()|四角形の高さ(ピクセル)を取得します|
8068 |~|getWidth()|四角形の幅(ピクセル)を取得します|
8069 |~|getTop()|四角形上辺のX座標(x)を取得します|
8070 |~|getLeft()|四角形左辺のX座標(x)を取得します|
8071 |~|getBottom()|四角形下辺のY座標(y)を取得します|
8072 |~|getRight()|四角形右辺のY座標(y)を取得します|
8073 [img[image/RectangleGet.png]]
8074
8075 !!Rectangle::getXXXX()の書き方
8076 [[Psychlops::Rectangle]]型の情報を取得し画面表示を行います。
8077 画面表示は[[Canvas::var()]]命令と[[Canvas::msg()]]命令を使用します。
8078 {{{
8079 &lt;例&gt;
8080 #include &lt;psychlops.h&gt;
8081 using namespace Psychlops;
8082
8083 double d1,d2,d3,d4,d5,d6,d7,d8,x,y;
8084 Psychlops::Point point1;
8085 Psychlops::Rectangle rect1;
8086
8087 void psychlops_main() {
8088
8089         Canvas sampleA(Canvas::fullscreen);
8090         rect1.set(100,100);
8091         rect1.centering();
8092         sampleA.rect(rect1,Color::cyan);
8093
8094         point1=rect1.getCenter();
8095         d1=rect1.getHcenter();
8096         d2=rect1.getVcenter();
8097         d3=rect1.getHeight();
8098         d4=rect1.getWidth();
8099         d5=rect1.getTop();
8100         d6=rect1.getLeft();
8101         d7=rect1.getBottom();
8102         d8=rect1.getRight();
8103         
8104         //get命令の内容を画面表示する
8105         x=point1.getX();
8106         y=point1.getY();
8107         sampleA.msg(&quot;getCenter_x:&quot;,50,200);
8108         sampleA.var(x,200,200);//getCenter:X
8109         sampleA.msg(&quot;getCenter_y:&quot;,50,250);
8110         sampleA.var(y,200,250);//getCenter:Y            
8111         sampleA.msg(&quot;getHcenter:&quot;,50,300);
8112         sampleA.var(d1,200,300);//getHcenter
8113         sampleA.msg(&quot;getVcenter:&quot;,50,350);
8114         sampleA.var(d2,200,350);//getVcenter
8115         sampleA.msg(&quot;gettHeight:&quot;,50,400);
8116         sampleA.var(d3,200,400);//getHeight
8117         sampleA.msg(&quot;getWidth:&quot;,50,450);
8118         sampleA.var(d4,200,450);//getWidth
8119
8120         sampleA.msg(&quot;getTop:&quot;,250,200);
8121         sampleA.var(d5,400,200);//getTop
8122         sampleA.msg(&quot;getLeft:&quot;,250,250);
8123         sampleA.var(d6,400,250);//getLeft
8124         sampleA.msg(&quot;getBottom:&quot;,250,300);
8125         sampleA.var(d7,400,300);//getBottom
8126         sampleA.msg(&quot;getRight:&quot;,250,350);
8127         sampleA.var(d8,400,350);//getRight
8128                 
8129         sampleA.flip();
8130         while(!Input::get(Keyboard::spc));
8131 }
8132 }}}</pre>
8133 </div>
8134 <div title="Rectangle::include()" modifier="YourName" modified="200803100554" created="200712100008" changecount="2">
8135 <pre>[[Psychlops::Rectangle]]型の座標の中にに他のRectangleや点が含まれるかどうかを判定します。
8136
8137 |!Rectangle::include()|include(double x, double y)|対象の座標を指定|
8138 |~|include(Point point)|[[Psychlops::Point]] point:判定対象の点|
8139 |~|include(Rectangle rect)|[[Psychlops::Rectangle]] rect:判定対象の四角形|
8140
8141
8142 !!Rectangle::include()の書き方
8143
8144 [[例6: マウスのデモ]]をRectangle::include()を使って判定してみます。
8145 実行すると画面左上に黒い四角形が、中央に白色の四角形が表示されます。
8146
8147 以下の2行でマウスのポインタ位置が白色の四角形の上にあるかどうかを判定しています。
8148 もし、マウスポインタが四角形の上にあって、マウスの左ボタンが押され続けていれば、変数draggingに1が代入されます。
8149 {{{
8150 if(Input::get(Mouse::left, Mouse::pressed) &amp;&amp; rect.include(Mouse::x, Mouse::y)) dragging = 1;
8151 if(Input::get(Mouse::left, Mouse::released)) dragging = 0;
8152 }}}
8153 マウスで白色の四角形をドラッグアンドドロップして、黒い四角形に重ねてみましょう。
8154
8155 {{{
8156 #include &lt;psychlops.h&gt;
8157 using namespace Psychlops;
8158
8159
8160 void psychlops_main() {
8161
8162        Canvas display(Canvas::fullscreen);
8163        Mouse::show();
8164
8165        double rectsize=80;
8166        Psychlops::Rectangle rect(rectsize,rectsize);
8167                rect.centering();
8168        Psychlops::Rectangle drop_here(rectsize*2,rectsize*2);
8169                drop_here.shift(10,10);
8170        Color rectcol, dropareacol;
8171        int dragging = 0;
8172        Input::refresh();
8173
8174        while(!Input::get(Keyboard::spc)){
8175                display.clear(Color::gray);
8176
8177                //      Checking rects
8178                if(Input::get(Mouse::left, Mouse::pressed) &amp;&amp; rect.include(Mouse::x,
8179 Mouse::y)) dragging = 1;
8180                if(Input::get(Mouse::left, Mouse::released)) dragging = 0;
8181                if(dragging) {
8182                        rect.centering(Mouse::x, Mouse::y);
8183                        rectcol.set(1.0);
8184                } else {
8185                        rectcol.set(0.25);
8186                }
8187
8188                if(drop_here.include(rect)) {
8189                        dropareacol.set(1.0,0.0,0.0);
8190                } else {
8191                        dropareacol.set(0.0,0.0,0.0);
8192                }
8193
8194                drop_here.draw(dropareacol);
8195                display.msg(&quot;Drop a rect here!&quot;, drop_here.getLeft()+10,
8196 drop_here.getTop()+10);
8197                rect.draw(rectcol);
8198                display.flip();
8199        }
8200 }
8201
8202 }}}
8203 </pre>
8204 </div>
8205 <div title="Rectangle::resize()" modifier="YourName" modified="200803100553" created="200709132255" changecount="2">
8206 <pre>[[Psychlops::Rectangle]]型のサイズの再設定を行います。
8207
8208 |!Rectangle::resize()|resize(double w, double h)|対象の[[Psychlops::Rectangle]]型のサイズをw×hに変更します|
8209 |~|~|double w:変更後の横幅|
8210 |~|~|double h:変更後の高さ|
8211
8212 !!Rectangle::resize()の書き方
8213 200 × 200の四角形を50 × 100に変更します。
8214 {{{
8215 &lt;例&gt;
8216 #include &lt;psychlops.h&gt;
8217 using namespace Psychlops;
8218
8219 Psychlops::Rectangle rect1(200,200);
8220
8221 void psychlops_main() {
8222
8223         Canvas sampleA(Canvas::fullscreen);
8224         rect1.setColor(Color::green);
8225         rect1.resize(50,100);
8226         rect1.centering();
8227         rect1.draw();
8228
8229         sampleA.flip();
8230         while(!Input::get(Keyboard::spc));
8231 }
8232 }}}</pre>
8233 </div>
8234 <div title="Rectangle::set()" modifier="Psychlops_DevelopperG" created="200910080842" changecount="1">
8235 <pre>|!Rectangle::set()|set(double width, double height)|&gt;|!width×heightの四角形を設定します|
8236 |~|~|double width|描画する四角形の横幅|
8237 |~|~|double height|描画する四角形の縦幅|
8238 |~|set(double l, double t, double r, double b)|&gt;|!左上座標(l,t)から右下座標(r,b)までの四角形を設定します|
8239 |~|~|double l|描画する四角形の左上座標x1を指定|
8240 |~|~|double t|描画する四角形の左上座標y1を指定|
8241 |~|~|double r|描画する四角形の右下座標x2を指定|
8242 |~|~|double b|描画する四角形の右下座標y2を指定|
8243 |~|set([[Psychlops::Point]] point1, [[Psychlops::Point]] point2)|&gt;|!左上Point座標(x1,x1)から右下Point座標(X2,Y2)までの四角形を設定します|
8244 |~|~|[[Psychlops::Point]] point1|左上座標(x1,y1)を指定|
8245 |~|~|[[Psychlops::Point]] point2|右下座標(x2,y2)を指定|</pre>
8246 </div>
8247 <div title="Rectangle::setColor()" modifier="YourName" modified="200803100554" created="200709132244" changecount="5">
8248 <pre>[[Psychlops::Rectangle]]型の色設定を行います。
8249 この命令を使用する場合は、[[Rectangle::draw()]]命令を使用してください。
8250
8251 |!Rectangle::setColor()|setColor([[Psychlops::Color]] col)|対象の[[Psychlops::Rectangle]]型を[[Psychlops::Color]] colで塗りつぶします|
8252 |~|~|[[Psychlops::Color]] col:四角形の描画色|
8253
8254 !!Rectangle::setColor()の書き方
8255 {{{
8256 &lt;例&gt;
8257 #include &lt;psychlops.h&gt;
8258 using namespace Psychlops;
8259
8260 Psychlops::Rectangle rect1;
8261
8262 void psychlops_main() {
8263
8264         Canvas sampleA(Canvas::fullscreen);
8265         rect1.set(100,100);
8266         rect1.setColor(Color::red);
8267         rect1.centering();
8268         rect1.draw();
8269
8270         sampleA.flip();
8271         while(!Input::get(Keyboard::spc));
8272 }
8273 }}}</pre>
8274 </div>
8275 <div title="Rectangle::shift()" modifier="YourName" modified="200803100552" created="200709131914" changecount="3">
8276 <pre>[[Psychlops::Rectangle]]型の図形を移動します。
8277 元の座標(x,y)を基点に入力値(h,v)だけ移動します。
8278 |!Rectangle::shift()|shift(double h, double v)|座標(X,Y)を(h,v)だけ移動します|
8279 |~|~|double h:右水平方向の移動量|
8280 |~|~|double v:下垂直方向の移動量|
8281 [img[image/rect_shift().png]]
8282
8283 コード例は[[2.2.6節|2.2.6 画面上に縞を描画する-Rectangleクラス2-]]参照</pre>
8284 </div>
8285 <div title="Rectangleの宣言" modifier="Psychlops_DevelopperG" modified="200910080842" created="200709132233" changecount="1">
8286 <pre>RectangleはCanvas上に四角形の領域を設定する命令です。
8287 [[Psychlops::Rectangle]]型の命令を使用する前に必ず宣言が必要です。
8288 宣言のみの場合、[[Rectangle::set()|2.2.3 画面上に四角形を描画する-Rectangleクラス1-]]で四角形の詳細情報を設定しますが
8289 宣言文のみでも情報の設定が可能です。
8290
8291 |!Rectangleの宣言|Rectangle()|[[Psychlops::Rectangle]]型の宣言を行います|
8292 |~|Rectangle(double width, double height)|[[Psychlops::Rectangle]]型の宣言を行いwidth×heightの四角形を設定します|
8293 |~|~|double width:描画する四角形の横幅|
8294 |~|~|double height:描画する四角形の縦幅|
8295 |~|Rectangle(double l, double t, double r, double b)|[[Psychlops::Rectangle]]型の宣言を行い左上座標(l,t)から右下座標(r,b)までの四角形を設定します|
8296 |~|~|double l:描画する四角形の左上座標x1を指定|
8297 |~|~|double t:描画する四角形の左上座標y1を指定|
8298 |~|~|double r:描画する四角形の右下座標x2を指定|
8299 |~|~|double b:描画する四角形の右下座標y2を指定|
8300
8301 !!Rectangle()の宣言の書き方
8302 100 × 100の四角形を描画します。
8303 {{{
8304 &lt;例&gt;
8305 #include &lt;psychlops.h&gt;
8306 using namespace Psychlops;
8307
8308 Psychlops::Rectangle rect1(100,100);
8309
8310 void psychlops_main() {
8311
8312         Canvas sampleA(Canvas::fullscreen);
8313         sampleA.rect(rect1,Color::cyan);
8314
8315         sampleA.flip();
8316         while(!Input::get(Keyboard::spc));
8317 }
8318 }}}</pre>
8319 </div>
8320 <div title="Rectangle型に関して" modifier="Kazushi Maruya" modified="200712131648" created="200712131647" changecount="2">
8321 <pre>* [[Rectangle型の変数を画面中央に持って行きたい|Rectangle::centering()]]
8322 * [[Rectangle型の変数を任意の位置に持って行きたい|Rectangle::shift()]]
8323 * [[Rectangle型の領域を塗りつぶしたい|Rectangle::draw()]]
8324 * [[Rectangle型の変数の画面上のサイズを再設定したい|Rectangle::resize()]]
8325 * [[Rectangle型の変数のメンバを取得したい|Rectangle::getXXXX()]]
8326 * [[Rectanble型の変数に対して包含関係を判定したい|Reectangle::include()]]</pre>
8327 </div>
8328 <div title="SideBarTabs" modifier="PsychlopsAdmin" created="200709170655" changecount="1">
8329 <pre>&lt;&lt;tabs TabTimeline 'タグ' 'タグ一覧' TabTags '詳細' '詳細' TabMore&gt;&gt;</pre>
8330 </div>
8331 <div title="SiteSubtitle" modifier="YourName" modified="200908100431" created="200707220301" changecount="9">
8332 <pre>Version 1.3.5 (2009/September)
8333 Generated by TiddlyWiki
8334 Authored by Psychlops Admin Group</pre>
8335 </div>
8336 <div title="SiteTitle" modifier="YourName" modified="200908100430" created="200707220259" changecount="19">
8337 <pre>Psychlops Manual
8338 </pre>
8339 </div>
8340 <div title="StyleSheet" modifier="Psychlops_DevelopperG" modified="200910020133" created="200708280415" changecount="169">
8341 <pre>/***
8342 CSSカスタマイズ
8343 ***/
8344 body {
8345         background-color: #f0f8f0;
8346         margin: 0 auto;
8347         padding:0;
8348         font-family: arial,helvetica, centurygothic;
8349 }
8350
8351 #displayArea {
8352       color: #1c311c;
8353       background-color: #e0f8e0;
8354       position: relative;  
8355       width: 70%;
8356       top : 12px;
8357       margin-left:235px;
8358       font-size: 1.1em;
8359 }
8360
8361 #mainMenu{
8362         color: #1c311c;
8363         font-family: arial,helvetica, centurygothic;
8364         font-size:100%;
8365         background: #ffffcc;
8366         border: 1px dotted #54c354;
8367         position: absolute;
8368         top : 165px;
8369         left: 10px;
8370         width:200px;
8371         text-align: left
8372 }
8373
8374 #sidebar {
8375  position: absolute;
8376  top: 750px; 
8377  left: 10px;
8378  width: 200px;
8379  font-size: .9em;
8380  }
8381
8382
8383 /*{{{*/
8384 h1,h2,h3,h4,h5 {
8385         padding-top: 0.3em;
8386 }
8387
8388 .headerShadow {
8389         padding: 2.0em 0em 0.7em 1em;
8390 }
8391
8392 .headerForeground {
8393         padding: 2.0em 0em 0.7em 1em;
8394 }
8395
8396
8397 .siteTitle {
8398         font-size: 3.0em;
8399 }
8400
8401 .siteSubtitle {
8402         font-size: 1.2em;
8403 }
8404
8405 .subtitle {
8406         font-size: 0.9em;
8407 }
8408 /*}}}*/
8409
8410 /*{{{*/
8411
8412
8413 h1,h2,h3,h4,h5 {
8414         color: #1c411c;
8415         background: #8bd78b;
8416 }
8417
8418 .button {
8419         border: 0px;
8420 }
8421
8422 .header {
8423         background: #8bd78b;
8424 }
8425
8426 .shadow .title {
8427         color: #fff;
8428 }
8429
8430 .title {
8431         color: #1c411c;
8432         background: #8bd78b;
8433 }
8434
8435 .viewer th {
8436         background: #996;
8437 }
8438
8439 .viewer pre {
8440         border: 1px solid #6a6;
8441         background: #cfc;
8442         font-size:1.1em;
8443         font-family: Helvetica, Arial;
8444 }
8445
8446 .viewer thead td {
8447         background: #8d8;
8448 }
8449 /*}}}*/
8450
8451 /*{{{*/
8452 .viewer thead td {
8453         background: #8d8;
8454 }
8455
8456 .viewer tfoot td {
8457         background: #bd6;
8458 }
8459
8460 .MyStyle {
8461         background: #aff;
8462 }
8463 /*}}}*/
8464 </pre>
8465 </div>
8466 <div title="TargetSurface" modifier="YourName" modified="200803140518" created="200708212223" changecount="4">
8467 <pre>Canvasの指定方法
8468 |!ALL|すべてのCanvas|
8469 |!BACK|バックグラウンドのCanvas|
8470 |!FRONT|表示中のCanvas|
8471 ※省略時にはBACKが指定される</pre>
8472 </div>
8473 <div title="TiddlyWiki" modifier="YourName" created="200707220242" changecount="1">
8474 <pre>Type the text for 'Psychlops Manual'</pre>
8475 </div>
8476 <div title="Tips" modifier="Psychlops_DevelopperG" modified="200912010831" created="200708240236" changecount="41">
8477 <pre>!! Psychlopsの基本
8478 * [[Tips: OpenGL 1.4に対応した一般向けビデオチップ]]
8479 * [[Tips: OS環境による関数名の衝突について]]
8480 * [[Tips: ガンマ補正]]
8481 * [[Tips: Canvasとリフレッシュレートの詳細]]
8482 * [[Tips: Canvas::flip()とコマ落ち]]
8483 * [[Tips: CanvasクラスとDisplayクラス]]
8484 * [[Tips: Canvasを宣言しないと動作しない命令群]]
8485 * [[Tips: Image使用上の注意]]
8486 * [[Tips: 時間精度が必要なプログラムを実行するとき]]
8487 * [[Tips: プログラムが暴走したとき]]
8488 * [[Tips: Xcodeで実行時エラーが起きるとフリーズする]]
8489 * [[Tips: intどうしの比率の計算がおかしい]]
8490 * [[Tips: 機器間の同期]]
8491 * [[Tips: Psychlopsにおけるキーボードマウスの入出力検出ルーチン]]
8492 * [[Tips: トリガーとアナログ入力]]
8493
8494 !! Psychlopsの応用
8495 * [[Tips: Shapeクラスの概要]]
8496 * [[Tips: Strokeクラスの概要]]
8497 * [[Tips: ランダムドットステレオグラムの描画コード]]
8498 * [[Tips: Rectangleの配列を用いたドットパターンの描画例]]
8499 * [[Tips: アルファ混色の活用]]
8500 * [[Tips: プログラムを複数のファイルに分割する]]
8501 * [[Tips: QUESTの実装]]
8502 * [[Tips: 自分の機器の描画性能を確かめる]]
8503 * [[Tips: 独自のクラスを作る]]
8504
8505 !! 他ライブラリの命令を使用した機能の補完
8506 * [[Tips: ビープを出す]]
8507 * [[Tips: OpenGL命令の混ぜ方]]
8508 * [[Tips: 画像のフィルタリング処理]]
8509 * [[Tips: トリガーとアナログ入力2-外部機器を利用した例-]]
8510 * [[Tips: Bits++の利用]]</pre>
8511 </div>
8512 <div title="Tips: Bits++の利用" modifier="YourName" modified="200803050448" created="200802201839" changecount="5">
8513 <pre>Cambridge Research Systems社Bits++は14bit DACを備えるアナログ画像信号出力用機器で、DVIとUSBにつなぐだけで機能し、特殊な関数などを使う必要はありません。従って、Psychlopsの命令系と問題なく結合することが可能です^^*1^^。 Bits++の動作モード別に説明します。
8514
8515 公式サイト: [[http://www.crsltd.com/catalog/bits++/modes.html]]
8516
8517 なお、Bits++を利用する場合、Psychlops側の一切の[[ガンマ補正]]機能は以下の命令で無効化し、LUTテーブルを管理するなどの方法で自力で補正をかけてください。
8518 {{{
8519 Color::setGammaValue(1,1,1);
8520 }}}
8521
8522 *1 現時点(2008/3/1現在)でBits++で扱える3つのモード(basic/mono/color)のうちで、実際に動作することを確認しているのはmonoモード(下記参照)だけですが、他の2つのモードについても、仕様上は問題なく動作をすると考えられます。この2モードでプログラムコードを実装された方がいらっしゃいましたら、ぜひPsychlops開発フォーラム(http://psychlops.l.u-tokyo.ac.jp/?forum)までご一報ください。
8523
8524
8525 ! BITS++ mono
8526 DVI出力のうち、赤8bit+緑6bitを合成して14bitの輝度値として解釈します。 → 全色14bitで、連動します。青8bitについては別に指定したパレットオーバーレイとして動作すると記述されていますが、詳細についてはまだわかりません。使用法としては、Imageであれば、RGB, BYTEで確保した上で値を16bit整数に変換してビットシフトでRとGに書き込む方法があります(確認済)。
8527 {{{
8528 Color luminance_to_Color(double luminance) {
8529     unsigned short bits_mono = (unsigned short)(65535 * luminance);
8530     unsigned char upper = (unsigned char)(bits_mono &gt;&gt; 8), (unsigned char)(lower = bits_mono &amp;&amp; 255) ;
8531     return Color(upper/255.0, lower/255.0, 0);
8532 }
8533
8534 Display::pix(1,1,luminance_to_Color(0.567));
8535 }}}
8536
8537 ! BITS++ basic(未確認)
8538 単純にDVI出力をトラップし、RGB各色をLUTに従って変換して14bitアナログ出力します。 → 各色14 bit中8 bitの色を使うことができます。 Bits++では、(0,0)から横に数ピクセルが特定の値であるとき、 y軸が0のラインのデジタル値をLUT配列として解釈する、というシステムになっています。従って、パレットとなるImageを(0,0)起点で描画すればLUTを書き換えることができます。パレットは配列アクセス演算子 [] を備えるクラスでくるめば扱いやすいかもしれません。このモードを使用するとLUTアニメーションが使えます。
8539
8540 ! BITS++ color(未確認)
8541 DVI出力の2ピクセル分の値を合成して各色16bitの値を得ます(実効14bit)。従って、解像度は半分になります。
8542 </pre>
8543 </div>
8544 <div title="Tips: Canvas::flip()とコマ落ち" modifier="YourName" modified="200802201734" created="200709171713" changecount="1">
8545 <pre>実験やデモには、各フレーム(コマ)がCanvas.flip(duration)で指定した正しい時間にに出ている必要があります。しかし、実際には正しい時間にコマが表示されないことがあります。この状態をコマ落ちと呼びます。
8546
8547 フレームが正しく表示されない原因は、フレームを提示する時間内に計算が終わらないためです。
8548 Psychlopsでは指定された時間よりも描画の計算時間が大きくなったときは描画終了時以降でもっとも近い垂直同期にあわせて描画内容を更新します。描画途中のちぎれた画像が表示される可能性はありませんが、その代わり指定された時間に表示されない可能性があります。
8549 現在のコンピュータは同時に多数のソフトが起動しており、その計算に処理時間をとられてフレーム落ちする場合があります。これを回避するには、[[Tips: 時間精度が必要なプログラムを実行するとき]]をご覧ください。</pre>
8550 </div>
8551 <div title="Tips: Canvasとリフレッシュレートの詳細" modifier="Psychlops_Admin" created="200708240232" changecount="1">
8552 <pre>CRTは機器によってリフレッシュレートが小数点以下の桁でわずかに異なります(60Hzと表示されていても、厳密には60.2Hzなど小数点以下の桁があります)。
8553 Psychlopsでは、初期化時には小数点以下の桁は無視して確保しますが、正確なタイミングでflipするには、ディスプレイ装置の設定等で小数点以下の桁をあらかじめ調べて、正確な値を指定することをお勧めします。</pre>
8554 </div>
8555 <div title="Tips: Canvasを宣言しないと動作しない命令群" modifier="YourName" modified="200908100434" created="200908100433" changecount="4">
8556 <pre>下記命令群はCanvasの実体が確保されていることを前提に動作します。
8557
8558 !!! Display::に属するすべての命令
8559 事前に確保されたCanvas実体への抜け道命令群です。
8560 事前にCanvas実体を確保することが必要です。
8561
8562 !!! Figure.draw() / Shape.draw(Color)
8563 Image.draw()やRectangle.draw(Color)はDisplay::に属する命令と同様に、事前に確保されたCanvas実体を探索して実行する抜け道命令です。
8564 事前にCanvas実体を確保することが必要です。
8565
8566 !!! Color::setGammaValue()
8567 この命令はハードウェアガンマを設定するためにCanvas実体を探索します。
8568 Canvas確保前にこの命令を実行しようとすると失敗します。
8569
8570 !!! Independent
8571 デモ用の変数操作コンソールをCanvas上に作ろうとするためにCanvasを探索します。
8572 Canvas確保前にこの命令を実行しようとすると失敗します。
8573
8574 !!! ExperimentalMethods::Demo
8575 Independentと同様ですが、initialize時にCanvasが確保されている必要があります
8576
8577 !!! Widgets::に属するクラスの大半
8578 自身をCanvas上に自動登録しようとするためにCanvasを探索します。
8579 Canvas確保前にこの命令を実行しようとすると失敗します。</pre>
8580 </div>
8581 <div title="Tips: CanvasクラスとDisplayクラス" modifier="PsychlopsAdmin" created="200709171711" changecount="1">
8582 <pre>Canvasクラスと同様に使えるクラスとして、Displayクラスがあります。
8583
8584 Cの変数は、その変数が定義されている関数やクラス以外からは操作することができません。
8585 {{{
8586 void psychlops_main() {
8587         Canvas display( Canvas::fullscreen );
8588 }
8589 void func() {
8590         display.flip(); // エラー、displayはこの関数からは操作できない
8591 }
8592 }}}
8593
8594 Displayクラスは、最後に宣言したCanvasクラスの別名として機能します。このクラスはどこからでもアクセスすることができますが、Canvasの宣言の代わりはできません。
8595 {{{
8596 void psychlops_main() {
8597         Canvas display( Canvas::fullscreen );
8598 }
8599 void func() {
8600         Display::flip();        // OK
8601 }
8602 }}}
8603 </pre>
8604 </div>
8605 <div title="Tips: Image使用上の注意" modifier="YourName" modified="200803050504" created="200709040623" changecount="4">
8606 <pre>また、この転送はグラフィックボードやマザーボードなどの規格やOSの種類によって速度が違いますが、一般にある程度の時間がかかります。この転送はCanvasの描画内容を画面上へ反映させるよりはずっと大きいもので、かつ信号転送量が大きくなればなるほど、時間が必要になります。
8607 2008年現在の平均的なマシン(iMacやメーカー製のWindowsデスクトップマシン)で、100 Hzの描画に間に合う(つまり10ms以内に転送と描画反映が完了する)Imageのサイズはおおよそ300 x 300 pixel程度でしょう。以上のようなことを考えて、Imageを用いたオフスクリーン描画とCanvasに対する直接の描画を上手く使い分ける必要があります。
8608 また、高速のオフスクリーン転送を実行したい場合には[[Image::quicken()|3.1.4 発展的な内容]]命令が有効な場合があります。詳しくは、関数一覧の[[Image::quicken()|3.1.4 発展的な内容]]の項を参照してください。</pre>
8609 </div>
8610 <div title="Tips: OS環境による関数名の衝突について" modifier="Psychlops_DevelopperG" modified="200908100455" created="200908100437" changecount="1">
8611 <pre>ほかのライブラリ、特にOS・コンパイラ標準のライブラリにあるクラスとこの特定のクラスが同じ名前を持っているために、名前が衝突してしまってコンパイルができなくなっていることが考えられます。
8612 このような衝突を起こすことがある命令は以下の通りです。
8613
8614 !! windows.hで問題が生じるシンボル
8615
8616 | !Rectangle | Psychlops::Rectangle |
8617 | !Ellipse | Psychlops::Ellipse |
8618 | !round | Math::round |
8619 | !random | Math::random |
8620
8621 !! MacOSXで問題が生じるシンボル
8622
8623 | !Point | Psychlops::Point |
8624 | !round | Math::round |
8625 | !random | Math::random |
8626 </pre>
8627 </div>
8628 <div title="Tips: OpenGL 1.4に対応した一般向けビデオチップ" modifier="YourName" modified="200803141457" created="200709131950" changecount="15">
8629 <pre>OpenGL 1.4仕様は2002年7月24日に発行されており、基本的にはそれ以降に設計されたビデオチップはこれに対応しています。
8630 http://www.opengl.org/documentation/specs/version1.4/glspec14.pdf
8631
8632 現在対応を確認しておりますのは、
8633 |!開発元|!汎用カード|!OpenGL特殊化カード|
8634 |nVidia|GeForce 6000(NV30)系、FX5000(NV40)系 以降|Quadro FX系|
8635 |AMD(旧ATI)|RADEON 8000(R200)以降、X系|FireGL 8000以降、X, T, Z, V系|
8636 |Intel|945チップセット(GMA950)以降(915でも一応の動作確認はあり)| |
8637 |VIA(S3 Graphics)|DeltaChrome(S8)以降| |
8638 |SiS|SiS 671以降| |
8639
8640 ただし2008年3月現在で以下のマシンにおける動作不良を確認しております。
8641 これらのマシンについてはドライバレベルでのOpenGL動作の改変が行われているようです。
8642 現在のところ対応は未定です。また、今後の改良のため、動作不良等がございましたら、ぜひ[[Psychlops WIKI Trouble Report|http://psychlops.l.u-tokyo.ac.jp/?Trouble+Report]]までご報告をお願いいたします。
8643
8644 |!製造会社名|!機種名|!搭載ビデオチップ|!動作不良の内容|!暫定的な回避策|
8645 |SONY|Vaio Type-T VGN-TZ92S|Intel 945GM|垂直同期を待たずに画面の描画が行われてしまう|未発見|
8646 |Apple|Mac Pro MA463J/A |Radeon X1900XT(他ビデオボードのものについては未検証) |一定以下の負荷しかない描画内容が画面に反映されないことがある|各フレームの描画命令の前に大きなサイズ(全画面)のRectangleを1~数枚表示する|
8647
8648 より新しい情報は、 [[Psychlops WIKI 動作環境 |http://psychlops.l.u-tokyo.ac.jp/?Environment]]に随時反映されます。
8649 また、これらの動作改変はOpenGLを用いたソフトウェア全般で起こっている可能性があります。これらの他のアプリケーションの情報についてもよろしければ[[Psychlops WIKI Trouble Report|http://psychlops.l.u-tokyo.ac.jp/?Trouble+Report]]に情報をあげていただきますよう、お願い申し上げます。
8650 </pre>
8651 </div>
8652 <div title="Tips: OpenGL命令の混ぜ方" modifier="YourName" created="200802201824" changecount="1">
8653 <pre>現在Psychlopsの描画命令形はOpenGLのみで行われており、OpenGLの描画命令を混ぜることができます。この際、いくつかの注意が必要です。
8654
8655 * 回転(glRotate)と拡大縮小(glScale)はほとんどのビデオカードで近似値を使うよう実装されており、ピクセル単位での正しい計算ができなくなりジャギやモアレの原因になります。これを了解した上でお使いください。
8656 * glRotateとglScaleはPsychlops命令形の原点座標を中心に行われます。これらの命令を使った上で表示位置を指定する場合は、回転中心からの相対座標に描画した後、glTranslateをご使用ください。
8657 * Image型はquicken()を適用しないと使うことができません。
8658
8659 Imageを回転させるサンプル(モアレが発生していることをお確かめください)
8660 {{{
8661 #include &lt;psychlops.h&gt;
8662 using namespace Psychlops;      // Initially developed with Psychlops Win32 20070313a
8663
8664
8665 void psychlops_main() {
8666
8667         Canvas display(Canvas::fullscreen);
8668
8669         // Internal Image
8670         Image img(100, 100);
8671         Color col;
8672         for(int i=0; i&lt;100; i++) {
8673                 for(int j=0; j&lt;100; j++) {
8674                         col.set(0.25+sin(i/10.0)/4 + 0.25+sin(j/10.0)/4);
8675                         img.pix(i,j,col);
8676                 }
8677         }
8678
8679         // External Image File
8680         /*
8681         Image img;
8682         img.load(&quot;sample.png&quot;);
8683         */
8684
8685         img.quicken();
8686
8687         double orientation = 0.0;
8688
8689
8690         while(!Input::get(Keyboard::esc)) {
8691                 display.clear(0.5);
8692
8693                 orientation += PI/10;
8694
8695                 glPushMatrix();
8696                 glTranslated(display.getHcenter(),display.getVcenter(),0);
8697                 glRotated(orientation,0,0,1);
8698                 img.draw();
8699                 glPopMatrix();
8700
8701                 display.flip();
8702         }
8703
8704 }
8705 }}}
8706
8707 </pre>
8708 </div>
8709 <div title="Tips: Psychlopsにおけるキーボードマウスの入出力検出ルーチン" modifier="YourName" modified="200802201727" created="200709150147" changecount="2">
8710 <pre>Psychlopsのキーボード・マウスの入力監視は、主のプログラムとは独立に自動的に一定の間隔(マシンに依存しますが、通常0.1ミリ秒程度)で行われています。
8711
8712 監視ルーチンは各キーについてPushed, Pressed, Releasedのイベントがあったかどうかの表を内部に保持しています。
8713
8714 監視ルーチンではまず入力デバイスから送られてくるPushed, Releasedのイベントを監視し、イベントがあれば当該キーの当該イベントの表に記録します。この記録はInput::refresh()をするまで残ります。次に、PushedされてからReleasedされるまでの間、Pressedであると表に書き込みます。これはrefreshしなくても常時更新されています。
8715
8716 本来はPressedのデータを基本に取得すべきなのですが、近年のコンピュータではUSB化やOSのセキュリティ強化のためにイベント依存のデータしか取得できないようになっており、このような形式になっています。
8717
8718 マウスの座標系に関してはMac OS X、Windowsともに常時取得・設定が可能です。
8719
8720 現在、多くのUSBキーボード・マウスでは、コンピュータに転送する時間精度は1 msより悪いものとなっています。このため、PsychlopsのKeyboardやMouse命令は、精密な反応時間の取得(数 ms以下のオーダーの精度が必要な反応時間取得)には向きません(ただし、この転送の時間ノイズは数多くの試行を平均加算することで除去できる可能性はあります)。ただし、この値は表示装置の更新間隔(フレームレート)よりは十分に短いため、キーボードやマウスの反応を表示に反映する場合には最悪でも1フレーム遅れる程度にとどまります</pre>
8721 </div>
8722 <div title="Tips: QUESTの実装" modifier="YourName" modified="200803140630" created="200803100446" changecount="11">
8723 <pre>QUESTは上下法の一種です。何らかの閾値を測定する場合に恒常法よりも効率的に測定することができます。最尤法とベイズ推定を用いて閾値を推定するのが特徴です。今回は、Psychlopsに追加で組み込めるQUESTのサンプルを実行してみます。
8724
8725 ! ダウンロード
8726
8727 まず最初に、[[QUEST Sample|http://satolab.l.u-tokyo.ac.jp/~psychlops_site/download/BasicSamples/QUEST_Sample.zip]]をダウンロードしてください。
8728
8729 圧縮ファイルを展開すると、3つのファイルが生成されます。
8730
8731 * QUEST.h
8732 * QUEST.cpp
8733 QUESTの本体が書かれています。XcodeやReloで新規プロジェクトを作成した後、以上の2ファイルをプロジェクトと同じフォルダにおいてください。次に、[[プロジェクトにこのプログラムを追加|Tips: プログラムを複数のファイルに分割する]]してください
8734
8735 * ~QUESTDemo.cpp
8736 上記QUESTを呼び出してコントラスト閾値測定を行うデモプログラムです。新しいプロジェクトのプログラムにコピーして貼り付けてください。
8737
8738 ! QUEST使用の大まかな流れ
8739
8740 このQUESTの実装では、QUEST用の確率表を内部で作成し、それをユーザプログラムからメソッドを呼び出して更新していくモデルをとります。
8741
8742 [img[image/tips/QUEST.png]]
8743
8744
8745 ! 初期化
8746
8747 QUESTでは様々な事前情報が必要です。
8748 * 閾値は大体どのくらいにあるか
8749 * その閾値の推測の確からしさはどの程度か
8750 * QUESTで求める閾値は何%に設定するのか
8751 * 被験者がボタンを押し間違える確率はどの程度ぐらいか
8752 * チャンスレベルは何%か
8753 といった情報が必要です。これを実験を始める前に設定します。
8754
8755 {{{
8756         QUEST(double priorEstimatedThreshold, double logPriorSD, double thresholdProbability,
8757                   double slope, double errorRate, double chanceLevel,
8758                   Range thresholdRange, double logStep = 0.005, Measure placementMeasure=MEAN);
8759 }}}
8760
8761 |!QUEST()|QUEST(double priorEstimatedThreshold, double logPriorSD, double thresholdProbability, double slope, double errorRate, double chanceLevel, Range thresholdRange, double logStep, Measure placementMeasure);|QUEST進行用のクラスを作ります|&gt;|
8762 |~|~|double priorEstimatedThreshold|閾値が大体どのくらいにあるかという推測値です。|
8763 |~|~|double priorSD|推測値の確かさ。閾値が推測値とあまり離れていない場合は小さな値を、離れているかもしれない場合は大きめの値を代入してください。|
8764 |~|~|double thresholdProbability|閾値とみなす正答率。たとえば2肢強制選択(チャンスレベル0.5)の場合は、正答率0.75が閾値とみなされることが多いです。|
8765 |~|~|double slope|心理物理関数の傾き。傾きが既知の場合はその値を代入してください。傾きがわからない場合は効率は落ちますが大きめ値を代入しておいたほうがよいでしょう。|
8766 |~|~|double errorRate|被験者がボタン押しを間違える確率|&gt;|
8767 |~|~|double chanceLevel|チャンスレベル。2肢強制選択なら0.5を代入してください。|
8768 |~|~|Range thresholdRange|閾値の存在範囲を0以上の範囲で指定|
8769 |~|~|double logStep|ステップの値|
8770 |~|~|Measure placementMeasure|推定値を確率分布の最頻値か平均値にしたがって推定します(MODEかMEAN)|
8771
8772
8773 ! 実行
8774
8775 初期化が終われば試行を40-100回ほど繰り返します。
8776 各試行は次のステップで行います。
8777
8778 # ループ開始前に刺激の初期値を得ます。
8779 ** 初期値はdouble QUEST::nextTrial()を実行することで最適な値を求めることができます。
8780 ** 通常このようにして求められた値をそのまま使います(手を加える例もあります)。
8781 # 刺激を提示し、被験者からの反応を得ます。
8782 # 実験結果をQUESTに与え、次の試行で使う値を決定します。
8783 ** 2で提示するとき実際に用いた刺激強度と、被験者が成功したか失敗したかをvoid QUEST::next(double intensity, int result)でQUESTに通知します。成功した場合はresultに1を、失敗した場合は0を渡してください。
8784 # 1へと戻り所定の試行数が終わるまでこれを繰り返します
8785 # 所定の試行数が終了したら最後にfinalEstimate()を呼び出すことで実験の結果、推定された閾値を得ることができます。
8786
8787 |!next()|試行の結果をQUESTに通知し、次の試行で用いる値を返します(下記2関数の合成)|&gt;|
8788 |~|double intensity|前回の試行で使われた刺激強度|
8789 |~|int result|前回の試行の被験者の回答。成功した場合はresultに1を、失敗した場合は0を渡してください。|
8790
8791 |!QUEST::nextTrial()|double nextTrial(void)|次の試行で用いる値を返します|&gt;|
8792
8793 |!QUEST::update()|void update(double intencity, int result)|試行の結果をQUESTに通知し、状態をアップデートします。|&gt;|
8794 |~|~|double intensity|前回の試行で使われた刺激強度|
8795 |~|~|int result|前回の試行の被験者の回答。成功した場合はresultに1を、失敗した場合は0を渡してください。|
8796
8797 |!QUEST::finalEstimate()|double finalEstimate(void)|最終結果を返します|&gt;|
8798
8799 {{{
8800 #include &lt;psychlops.h&gt;
8801 using namespace Psychlops;
8802
8803 #include &quot;QUEST.h&quot;
8804
8805 void psychlops_main() {
8806
8807         //initialize QUEST method
8808         const double estimatedThreshold = 0.05; //estimated threshold, mean of prior pdf
8809         const double pdfSD = 2.0; //standard deviation of prior pdf
8810         const double thresholdProbability = 0.75; //the probability defined as threshold
8811         const double slope = 3.5; //slope of psychometric function
8812         const double errorRate = 0.01; //error rate
8813         const double chanceLevel = 0.5; //chance level
8814         Range  contrastRange;
8815         0&lt; contrastRange &lt;=1.0;
8816         
8817         QUEST quest(estimatedThreshold, pdfSD, thresholdProbability, slope, errorRate, chanceLevel, contrastRange);
8818
8819         //other variable
8820         double contrast = quest.nextTrial(); //contrast of stimuli at each trial
8821         int result; //result of trial
8822         int trial = 50; //number of trial
8823         
8824
8825         // Drawing Tools
8826         Canvas canvas(Canvas::fullscreen);
8827         QuickGabor gabor;
8828         gabor.set(50, 50, 1.0, 0, 0).centering();
8829         int display_frames = 1*canvas.getRefreshRate(); // display gabor for 1 second. 
8830
8831         //trial loop
8832         for (int i = 0; i&lt;trial; i++) {
8833
8834                 //replace this section to your experiment
8835                         // displaying stimuli
8836                         for(int i=0; i&lt;display_frames; i++) {
8837                                 canvas.clear(0.5);
8838                                 gabor.draw();
8839                                 canvas.flip();
8840                         }
8841         
8842                         // waiting response
8843                         canvas.clear(0.5);
8844                         canvas.flip();
8845                         while(true) {
8846                                 if(Input::get(Keyboard::left))  { result = 1; break; }  // positive reaction
8847                                 if(Input::get(Keyboard::right)) { result = 0; break; }  // negative reaction
8848                         }
8849                 
8850                 //update QUEST
8851                 gabor.contrast = quest.next(contrast, result);
8852         }
8853
8854         //final estimate
8855         double threshold = quest.finalEstimate();
8856         double sd = quest.getSD();
8857         
8858 }
8859 }}}
8860
8861 ! 注意点
8862
8863 QUESTでは心理物理関数にWeibull分布を仮定しています。そのため、心理物理関数がWeibull分布で近似できない場合は使えませんのでご注意ください。
8864
8865 </pre>
8866 </div>
8867 <div title="Tips: Rectangleの配列を用いたドットパターンの描画例" modifier="YourName" modified="200802201227" created="200802191422" changecount="5">
8868 <pre>ランダムドットパターンなどの複数のドットを使ったパターンの描画で、
8869 ドット密度や各ドットの運動方向を精密に操作したいときは、
8870 [[3.2節|3.2 オフスクリーンを用いた描画例-ランダムドットパターン-]]で紹介した方法よりは、
8871 Rectangleの配列を用いた方法のほうがプログラミングが容易です。
8872 いかにRectangleの配列を用いたドットパターンの描画の典型的なプログラム例をしめします。
8873 ここでは、Rectangleの配列のうちの一部をランダムな方向に運動させ(Noise)、残りを右方向へ運動させています(Signal)。
8874
8875 {{{
8876 #include &lt;psychlops.h&gt;
8877 using namespace Psychlops;
8878
8879 void psychlops_main() {
8880
8881         Canvas display(Canvas::fullscreen);
8882
8883         //      Generic Random Dot Parameters
8884         const int DOT_SIZE = 3;
8885         const int MAX_NUM_DOTS = 1000;
8886         const double speed=3.0;
8887         const double noise_dots_ratio = 0.5;
8888         
8889         // Parameters of each dots
8890         Psychlops::Rectangle dot[MAX_NUM_DOTS];
8891         Psychlops::Rectangle canvas_rect(display.getWidth(), display.getHeight());
8892         double direction;
8893
8894         // Randomize Dots' Position
8895         for(int i=0; i&lt;MAX_NUM_DOTS; i++) {
8896                 dot[i].set(DOT_SIZE, DOT_SIZE);
8897                 dot[i].centering(
8898                 Psychlops::random(display.getWidth()), 
8899                 Psychlops::random(display.getHeight())
8900       );
8901         }
8902
8903         while(!Input::get(Keyboard::esc)) {
8904                 display.clear();
8905
8906                 //      Update noise dots' position
8907                 for(int i=0; i&lt;MAX_NUM_DOTS*noise_dots_ratio; i++) {
8908                         direction=Psychlops::random(2*PI);
8909                         dot[i].shift(speed*cos(direction), speed*sin(direction));
8910                         if(!canvas_rect.include(dot[i]))
8911                                 dot[i].centering(
8912                 Psychlops::random(display.getWidth()), 
8913                 Psychlops::random(display.getHeight())
8914              );
8915
8916                         dot[i].draw(Color::white);
8917                 }
8918
8919                 //      Update signal dots' position
8920                 for(int i=MAX_NUM_DOTS*noise_dots_ratio; i&lt;MAX_NUM_DOTS; i++) {
8921                         dot[i].shift(speed, 0.0);
8922                         if(!canvas_rect.include(dot[i])) dot[i].shift(-display.getWidth(), 0.0);
8923                         dot[i].draw(Color::white);
8924                 }
8925
8926                 display.flip(3);
8927         }
8928 }
8929 }}}
8930 </pre>
8931 </div>
8932 <div title="Tips: Shapeクラスの概要" modifier="Psychlops_DevelopperG" modified="200912010828" created="200912010822" changecount="1">
8933 <pre>Shapeクラスに属するクラスは、デフォルトの描画色や枠線を指定することができます。
8934 このデフォルトの色と線Groupでまとめたりする際にとくに有用です。
8935 これらの指定はShapeクラスのメンバ fill, strokeに対して代入を行うことで実行します。
8936
8937 |!Shape::fill|デフォルトの塗り色です。Colorが代入できます。初期状態では無色です。|
8938 |!Shape::stroke|デフォルトの枠線をStroke型で指定します。初期状態では太さ0の線です。|
8939
8940 枠線を指定ためにはstrokeクラスの変数を宣言して、書式指定を行っておく必要があります。
8941 書式指定には宣言時に指定をする方法とset()命令を使用する方法があります。
8942 これらの詳細については[[Tips: Strokeクラスの概要]]をご覧ください。
8943
8944 {{{
8945 #include &lt;psychlops.h&gt;
8946 using namespace Psychlops;
8947
8948 void psychlops_main() {
8949         Canvas canvas(Canvas::window);
8950
8951         Psychlops::Rectangle rect(100,100);
8952         rect.centering().shift( 100, 0);
8953         rect.stroke.set(Color::red, 5, Stroke::DOTTED);
8954
8955         Shape::Colored&lt;Psychlops::Ellipse&gt; ellipse;
8956         ellipse.set(rect);
8957         ellipse.centering().shift(-100, 0);
8958         ellipse.fill = Color::white;
8959
8960         while(!Keyboard::esc.pushed()) {
8961                 canvas.clear();
8962                 ellipse.draw();
8963                 rect.draw();
8964                 canvas.flip();
8965         }
8966 }
8967
8968 }}}
8969
8970 この命令セットのうちでここまで扱わなかったものとしてgetDatum()があります。
8971
8972 |!Point Shape::getDatum()|getDatum(void)|Figureの「基準座標」をPoint型で返します. &lt;br&gt; 基準座標の定義は各クラスによりますが、原則として中心点や起点(図形の左上の点)です|</pre>
8973 </div>
8974 <div title="Tips: Strokeクラスの概要" modifier="Psychlops_DevelopperG" modified="200912010835" created="200912010830" changecount="5">
8975 <pre>Strokeクラスは、線の書式を表現するためのクラスです。
8976 描画系の関数にColorの代わりに Stroke構造体を与えることで線を描画します。
8977
8978 !現状の制限
8979     * ImageにStrokeを描画することはできません。
8980     * 線が角ごとに途切れるうえ、円が多角形近似のため、ある程度以上太くしたり破線を指定したりするとアラが目に見えてわかります。
8981
8982 !関数
8983 コンストラクタ, set関数 (Color color, double thick, Stroke::Pattern pattern)
8984
8985 color
8986     線の色です。
8987 thick
8988     線の太さです。現状ピクセル単位になります。
8989 pattern
8990     バターンの種類はSOLID, DASHED, DOTTEDです
8991
8992 コード例
8993 {{{
8994    Stroke solid(Color::red, 1, Stroke::SOLID); 
8995   // 宣言と設定を同時に行う方法は関数内でのみ使用できます。
8996    Stroke dashed, dotted;
8997    dashed.set(Color::green, 3, Stroke::DASHED);
8998    dotted.color = Color::blue;
8999    dotted.width = 5;
9000    dotted.pattern = Stroke::DOTTED;
9001
9002    Psychlops::Rectangle rect(100,100);
9003
9004    while(!Keyboard::esc.pushed()) {
9005        canvas.clear();
9006        rect.centering();
9007        rect.shift(-150,0).draw(solid);
9008        rect.shift( 150,0).draw(dashed);
9009        rect.shift( 150,0).draw(dotted);
9010        canvas.flip();
9011    }
9012 }}}
9013
9014 実行結果
9015 [img[image/Stroke_Sample.png]]
9016
9017 </pre>
9018 </div>
9019 <div title="Tips: Vistaにおけるインストール" modifier="YourName" modified="200802210940" created="200802191504" changecount="3">
9020 <pre>Windows VistaでPsychlopsを利用する場合、いくつかの注意が必要です。
9021
9022 !! ビデオカードのドライバがOpenGLをサポートしているか確かめる
9023 Windows Vistaでは、DirectXがOpenGLより優先されたため、OpenGLへのサポートが十分でないビデオカードがあります。2008/2/14現在でWindows VistaでOpenGLが十分にサポートされているビデオカードは以下のとおりです。
9024
9025 * nVidia GeForce 7000番台以降
9026 * AMD(ATI) Radeon Xシリーズ
9027
9028
9029 !! インストーラの実行時に管理者権限で実行する
9030
9031 BCC、SetBCC、Psychlopsのインストーラをダブルクリックしても、何も起きない(ように見える)、あるいは明らかに実行に失敗しているように見える場合があります。その場合、実行ファイルを右クリックして「管理者として実行」をクリックし、管理者のパスワードを入力して実行してください。
9032 [img[Vista|image/Win/VistaBCC_1.png]]
9033
9034
9035 !! Borland C++ Compilerの環境変数を設定する
9036
9037 [[BCCのインストール|1.3 インストール作業(Windows)]]を実行した後、追加で以下の作業を行います。
9038
9039 0. スタートボタン → コントロールパネルを開きます
9040 1. 「環境変数」を検索します
9041 2. 「環境変数を編集」をクリックします
9042 3. 開いたウインドウで、「変数」の中に「PATH」があるかどうかを調べます
9043 4.1. 「PATH」があった場合、それを選んで「編集」ボタンを押します。開いたウィンドウで変数値の末尾に以下のテキストを''追加します''。セミコロンは必要なので落とさないようにしてください。
9044 {{{
9045  ; C:\borland\bcc55\Bin;
9046 }}}
9047 4.2. 「PATH」がなかった場合、「新規」ボタンを押します。開いたウィンドウで、変数名に「PATH」、変数値に以下のテキストを書き込みます
9048 {{{
9049  C:\borland\bcc55\Bin;
9050 }}}
9051 [img[Vista|image/Win/VistaBCC_2.png]]
9052
9053 </pre>
9054 </div>
9055 <div title="Tips: Xcodeで実行時エラーが起きるとフリーズする" modifier="YourName" modified="200802130846" created="200709171710" changecount="5">
9056 <pre>Xcodeで実行時エラーが起きると操作を受け付けなくなる現象があります。これは実際は実行時エラー発生時にデバッガが起動しており、フルスクリーンのために見えないだけです。フリーズはしていません。この現象は以下に例示する方法で回避できます。
9057
9058 ! 複数ディスプレイを使用する
9059 複数ディスプレイを使用した場合、Psychlopsは主ディスプレイ(メニューバーのあるディスプレイ)に表示され、副ディスプレイを拘束しません。副ディスプレイにデバッガウィンドウを表示しておくことで、デバッガからのプログラム終了やエラー追跡ができます。
9060
9061 ! デバッガの起動を防ぐ
9062 デバッガの起動をとめるには、プロジェクト中の「実行可能ファイル」タブから、
9063 * (プロジェクト名) static
9064 * (プロジェクト名) dynamic
9065 * (プロジェクト名) 10.3
9066 と並んでいるうちの一番上のstaticを右クリックまたはctrl+クリックして、「情報を見る」を選びます。
9067
9068 [img[実行ファイルの情報|image/OSX/Psychlops_OSX_dbg1.png]]
9069
9070 開いたダイアログで「デバッガ」タブを選び、すべてのチェックボックスをオフにします。
9071 [img[実行ファイルの情報|image/OSX/Psychlops_OSX_dbg2.png]]
9072
9073 ! 待つ
9074 演算性能によりますが、数分間待つと回復することがあります
9075
9076 </pre>
9077 </div>
9078 <div title="Tips: intどうしの比率の計算がおかしい" modifier="YourName" modified="200803050457" created="200802202143" changecount="2">
9079 <pre>C++では、intどうしの割り算を行うと小数点切捨ての整数で帰ってきます。このため、特にfor文のカウント変数と最大値から比率を求めようとするときなどで精度差が出ることがあります。これを回避するには型キャストしなければなりません。また、定数項はなるべく「.0」をつけて強制的に浮動小数点値と解釈させると、その項の演算結果を利用する演算子の結果はすべてdouble型にキャストされます。これを覚えておけば「式はあっているはずなのに何も表示されない」といったバグはずいぶんと減るはずです。
9080
9081 {{{
9082 for(int i=0; i&lt;max; i++) {
9083     Display.pix(i, 0, Color( (double)(i)/max ));
9084     Display.pix(i, 2, Color( i*2.0 / max));
9085 }
9086 }}}
9087
9088 </pre>
9089 </div>
9090 <div title="Tips: アルファ混色の活用" modifier="YourName" modified="200803140631" created="200802201822" changecount="7">
9091 <pre>Psychlopsが標準で扱うアルファ値(透明度)は、以下のように定義されています。
9092
9093 {{{
9094  結果 = α×上書き色 + (1-α)×元の色
9095 }}}
9096
9097 したがって、アルファ値を0.5とおくと元の色と上書き色の平均値になります。アルファ合成は合成結果が重み付け平均として表せるときに有効です。これ以外の計算方法でアルファ合成を行いたい場合には[[glBlendFunc命令を使用して直接指定をおこなってください|Tips: OpenGL命令の混ぜ方]]。
9098
9099 アルファ合成はビデオカード上で行われるために高速ですが、[[ガンマ補正|Tips: ガンマ補正]]がハードウェア合成の場合にのみ正常に機能し、ソフトウェアエミュレーションである場合には補正済みの値どうしの平均を行ってしまうために誤った値が表示されます。これを防ぐためには、[[Color::strict_match = true;|Color::strict_match]]を指定してソフトウェアエミュレーションを機能させないようにしてください。
9100
9101
9102 以下のコードを実行すると、横縞のガボール刺激に縦縞のダイナミックノイズがかかった刺激が描画されます。ここでは、縦縞のダイナミックノイズをアルファ合成を用いてガボール刺激に重ね合わせています。[[Tips: 独自のクラスを作る]]で例示されている~QuickGaborもアルファ合成を用いています。
9103
9104 [img[image/tips/Sample_alpha.png]]
9105
9106 {{{
9107 #include &lt;psychlops.h&gt;
9108 using namespace Psychlops;
9109
9110 void psychlops_main() {
9111
9112         Canvas display(Canvas::fullscreen);
9113
9114         Psychlops::Rectangle noise;
9115         double noise_lum;
9116
9117         QuickGabor gabor;
9118         gabor.set(30,30).centering();
9119         gabor.orientation = 90.0; // don't
9120
9121         while(!Input::get(Keyboard::esc)) {
9122                 display.clear(0.5);
9123
9124                 // draw gabor
9125                 gabor.phase += 6.0;
9126                 gabor.contrast = sin(PI*gabor.phase/360.0);
9127                 gabor.draw();
9128
9129                 // draw noise
9130                 noise.set(1, display.getHeight()).centering(-1,display.getVcenter());
9131                 for(int i=0; i&lt;display.getWidth(); i++) {
9132                         noise_lum = Psychlops::random();
9133
9134                         // **************draw with alpha-blending*****************************
9135                         noise.shift(1,0).draw(Color(noise_lum, noise_lum, noise_lum, 0.5));
9136                 }
9137
9138                 display.flip();
9139         }
9140
9141 }
9142 }}}
9143 </pre>
9144 </div>
9145 <div title="Tips: ガンマ補正" modifier="Psychlops_DevelopperG" modified="200910080850" created="200802130705" changecount="15">
9146 <pre>ディスプレイやプリンタなど、コンピュータから利用できる画像表示機器では、明るさが指定した値通りに表示されず、歪みが生じます。多くの場合はこの歪みは指数関数に従います。その指数を'''ガンマ値'''と呼び、表示装置の特性を記述するパラメタになります。CRTの場合、ガンマ値は赤、緑、青の蛍光体の間で独立です。
9147
9148 [img[輝度歪み|image/DisplayGamma.png]]
9149
9150 PsychlopsのColor型はガンマ補正機能を備えています。ガンマ補正にはガンマ値による指定のほかに、テーブル(補正表)による指定方法があります。CRT以外の表示装置の特性は必ずしもガンマ値型指数関数に従うとは限りませんので、液晶やプラズマ、DLPプロジェクタなどではテーブルによる指定を試みる必要があります(番、山本、江島、2006)。
9151
9152 表示装置のガンマ値を指定する
9153 {{{
9154  double gamma_r=1.7, gamma_g=1.6, gamma_b = 0.6;
9155  Color::setGammaValue(gamma_r, gamma_g, gamma_b);
9156 }}}
9157
9158
9159 ! ハードウェア補正とソフトウェア補正
9160 Psychlopsのガンマ補正は、標準でハードウェアの調整機能を起動し、それに失敗した場合ソフトウェアによるエミュレーションを試みます。
9161
9162 [img[ソフト補正とハード補正|image/ColorCalibrationModel.png]]
9163
9164 !! ハードウェア補正
9165 最近のビデオ表示機器は、理論値と電気出力値の変換テーブルを内部に保持しており、このテーブルに補正値を書き込むことで補正機能を有効にすることができます。書き込みはDVI接続時でも成功しますが、有効に機能するかはハードウェアに依存します。確認するためには実測が必要です。
9166
9167 ハードウェア補正では、以下のOSのAPI関数を呼び出します。
9168 {{{
9169  * Windows
9170  SetDeviceGammaRamp
9171  * Mac OS X
9172  CGSetDisplayTransferByFormula
9173  CGSetDisplayTransferByTable
9174 }}}
9175
9176 !! ソフトウェア補正
9177 ソフトウェア補正では、Color型とImage型の入出力時に変換関数を適用することで、ハードウェア補正と同様の機能を実現します。Color型とImage型の入出力関数で補正を行い、補正後の値を保存することで実現しています。
9178
9179 ソフトウェア補正はハードウェア補正に比べいくつかの点で劣ります。
9180 * 理論値の変化に対して鋭敏に輝度が変化する部分とそうでない部分が生じます。鋭敏でない部分では輝度変化がつぶれてしまいます(下図)
9181 * ガンマ値をプログラム中で変更した場合、それ以前までにsetされたColor情報は古いガンマ値のエミュレーション結果に従った値が保存されており、新しいガンマ値に従わない誤った色が表示されます。いっさいの警告が出ませんのでご注意ください。
9182 * 変換をCPUで計算するため、速度が低下します。
9183 * ソフトウェア補正で補正テーブルを使用した場合、さらに問題が生じます
9184 ** 色指定は通常0~1の実数ですが、補正テーブル適用時は入力値がテーブルの行数にあわせて離散化されます。このため、Color.set()で入力した値とget()で取得した値が異なることがあります。
9185 ** 複数の理論値に対して同一の補正値が割り当てられている場合、get()で復元する場合に入力理論値と異なることがあります。
9186
9187 [img[ソフトウェア補正の鋭敏さ|image/SoftwareColorCalibration.png]]
9188
9189
9190 !! 表示装置固有の輝度特性測定時の注意
9191 ハードウェア補正が適用されたPsychlopsを用いて表示装置の特性を測定する場合には、OSが標準で行っている補正(ほとんどの場合非線形ですを外すために、以下の関数を実行してください。
9192 {{{
9193  Color::setGammaValue(1,1,1);
9194 }}}
9195
9196 *この記述はver1.0 (Psychlops20080214)以降での注意です。これ以前のバージョンに使用されているsetGammaValue命令は常にソフトウェア補正を用いているために、OSによる標準の補正を気にする必要はありません。
9197
9198 !! 厳密なガンマ補正
9199 特に厳密な色補正を保証したい時に、ガンマ補正や色指定で値が0以上1以下にならないケースやソフトウェア補正が適用される環境下ではプログラムが停止されるように強制させることができます。以下のコードをpsychlops_main()の冒頭に記述してください。
9200 {{{
9201  Color::strict_match = true;
9202 }}}
9203
9204
9205 ! 脚注
9206
9207 番浩志、山本洋紀、江島義道 (2006) &quot;Mcalibrator: MATLAB言語を用いたディスプレイ較正のための統合型GUIソフトウェアの開発&quot; 基礎心理学研究、第24巻、149-161頁
9208 </pre>
9209 </div>
9210 <div title="Tips: トリガーとアナログ入力" modifier="YourName" modified="200802201728" created="200802201438" changecount="4">
9211 <pre>脳波やfMRI研究では電気的トリガを出す必要がありますが、[[Tips: 機器間の同期]]で説明したとおり、機器間の同期を取ることが非常に困難であり、視覚刺激提示装置と電気パルスを同期制御することは難しくなっています。
9212
9213 この問題を確実に解決する方法としては、画面の輝度をフォトトランジスタで電気信号に変換する方法があります。フォトトランジスタの照度に対する反応はきわめて高い時間精度を持つため、輝度の状態を得るには適しているといえます。
9214
9215 一般的な電気信号入出力装置は電流ではなく電圧を信号として扱いますので、フォトトランジスタの照度への反応を電圧の変化として取り出す回路が必要です。一般的な回路図を以下に示します。
9216
9217 [img[image/Tips/phototrigger.png]]
9218
9219 フォトトランジスタを吸盤などに埋め込んで画面に設置できるようにして、フォトトランジスタに直列につないだ抵抗器の両端の電圧を電圧計に入力します。
9220
9221 * たとえばCRTの場合、陰極線が画面を走査するには一定の時間が必要です。したがって、画面上部と画面下部では輝度の変化する時間に数ミリ秒の差があります。
9222 * 電圧の範囲は、測定照度、フォトトランジスタの特性、抵抗器の抵抗値、によって決まります。一般的には、抵抗を変えることで必要な電圧範囲になるようにします。
9223 * 刺激提示画面上に電圧計を設置するのが難しい場合、マルチディスプレイを用いて2画面に同じ画像を提示し、実験に使わないほうの画面でトリガーをとる方法があります。この方法では、画面間の同期はとれていませんが、垂直同期間隔は一定です。あらかじめ両画面の同期の時間差を測定しておくことで、正確な同期タイミングを算出することが可能です。
9224 </pre>
9225 </div>
9226 <div title="Tips: トリガーとアナログ入力2-外部機器を利用した例-" modifier="YourName" modified="200803062207" created="200803062201" changecount="6">
9227 <pre>脳波実験等では参照トリガ電圧が必要になることがありますが、近年のコンピュータでは、電気的外部入出力端子(~RS-232Cなど)を持つものが少なく利用しにくくなっています。そこで今回は、比較的容易に入手可能なNational Instruments社製[[USB-6009|http://sine.ni.com/nips/cds/view/p/lang/en/nid/14605]]を用いてアナログ入出力をしてみます。
9228
9229 ~USB-6009は電圧データ入出力用機器です。
9230 * アナログ入力 4 ch、アナログ出力 1 ch
9231 * 時間分解能:入力 48 kHz 出力 150 kHz
9232 * 電圧分解能:入力 14 bit 出力  12 bit
9233 と、心理物理学には十分な性能を持っています。価格は35000円程度で、電圧入出力機器としては比較的安価に入手できます。
9234
9235 実際に実験に使う上では、他の入出力機器との時間同期を気にする必要があります。
9236
9237 * 一般的注意
9238 ** USBにはキーボード・マウス等、最低限のものしかつながないでください。USBメモリ・ハードディスク・USBオーディオ・Webカメラ等、大量のデータを転送するものとの併用は危険です。
9239 * 入力
9240 ** データ取得までの時間遅れは、少なくとも10ms以下程度に抑えられています。したがって、入力を元に画像を制御する用途には用いることができます。
9241 ** 入力は4ch同時に取ることができるので、画面のトリガと反応ボックス等を組み合わせて入力することで、入力した機器間で厳密な同期をとることができます。
9242 * 出力
9243 ** データを~NI-6009に送信した後、~USB-6009のデジタル入力端子に外部から別のトリガ信号を入力することで、そのトリガに同期してデータを出力することができます。
9244 *** たとえば、VGAの垂直同期信号をデジタル入力端子に誘導しておくと、任意の時点で送信したアナログ出力データを次のflipに同期して出す、といったことが可能になります。
9245
9246
9247 今回は、もっとも簡単なサンプルとして、~USB-6009をひとつ接続し、10ms程度の精度で「電圧値を得る」「電圧を出す」を単発で行う拡張サンプルを実行してみます。高度な時間制御は行っておりません。なお、このサンプルを応用したプログラムを配布する場合は、配布先の環境でも同様の装置が必要です。
9248
9249
9250 ! セットアップ
9251
9252
9253 !! Mac OS XでXcodeを使用する場合
9254 * [[NI-DAQmx Base|http://joule.ni.com/nidu/cds/fn/p/sn/n19:MacOS.MACOS10/sb/navsRel/lang/en?q=NI-DAQmx+Base&amp;x=10&amp;y=7]]ドライバの最新版をダウンロードします。
9255 * ダウンロードしたファイルを実行し、ドライバをインストールします(要再起動)。
9256
9257 * [[NIDAQmxBase_Sample_OSX.zip|http://psychlops.l.u-tokyo.ac.jp/download/BasicSamples/NIDAQmxBase_Sample_OSX.zip]]をダウンロードし、解凍します
9258 * 解凍されてできたフォルダから
9259 ** 「~NIDAQmxBase_Ext_Installer」をダブルクリックして実行します(下記動作を行うシェルスクリプトです)
9260 *** 失敗した場合、手動で「nidaqmxbase_ex_sample.cpp」を/Library/Frameworks/Psychlops.framework/Headers/Extentionsにコピーします。
9261
9262 新規プロジェクトを作成し、プロジェクトのプロパティでNI-DAQmx Baseドライバを読み込む設定を行います。
9263 * 「External Frameworks and Libraries」をCtrl+クリックし、追加→既存のフレームワークから「nidaqmxbase.framework」を選択
9264 [img[image/OSX/nidacmxbase_OSX_1.png]]
9265
9266 !! WindowsでBCC5.5 + Reloを使用する場合
9267
9268 まず最初に、必要なファイルのセットアップを行います。
9269
9270 * [[NI-DAQmx Base|http://joule.ni.com/nidu/cds/fn/p/sn/n19:Windows,n23:3478/sb/navsRel/lang/en?q=NI-DAQmx+Base&amp;x=0&amp;y=0]]ドライバの最新版をダウンロードします。
9271 * ダウンロードしたファイルを実行し、ドライバをインストールします(要再起動)。
9272
9273 * [[NIDAQmxBase_Sample_win32.zip|http://psychlops.l.u-tokyo.ac.jp/download/BasicSamples/NIDAQmxBase_Sample_win32.zip]]をダウンロードし、解凍します
9274 * 解凍されてできたフォルダから、
9275 ** 「~NIDAQmxBase対応Psychlops拡張のインストーラ.bat」をダブルクリックして実行します(nidaqmxbase_ex_sample.cppをExtentionフォルダにコピーします。
9276 ** 「~NIDAQmxBaseをBCCに対応させる.bat」をダブルクリックして実行します(bccのツールを呼び出します・要管理者権限)
9277
9278
9279 新規プロジェクトを作成し、プロジェクトのOptionsダイアログで、NI-DAQmx Baseドライバを読み込む設定を行います。
9280 *「Linker」タブの下部にある「Library」に「nidaqmxbase_bcc.lib」を追加(末尾にスペースをひとつ空けて追加します)
9281 * DirectoriesタブにNI-DAQmx Baseを追加する
9282 ** 「Extra Include Paths」に「C:\Program Files\National Instruments\NI-DAQmx Base\Include\」を追加
9283 ** 「Extra Library Paths」に「C:\Program Files\National Instruments\NI-DAQmx Base\Lib\」を追加
9284 [img[image/win/nidaqmxbase_Win32_1.png]]
9285
9286
9287 ! プログラミング
9288
9289 !! アナログ入力
9290
9291 * NI ~USB-6009 の2番(+)、3番(-)ポートに入力線を接続する
9292 * #include &lt;Extentions/Samples/nidaqmxbase_ex_sample.cpp&gt;を追加
9293 * ~NIDAQmxBase_AnalogInput_Dev1 クラスの変数を作成し、ポートを初期化する
9294 * 変数のget()メソッドを呼び、ポート間の電位差を得る
9295 これで、0Vから5Vまでの電位差を得ることが可能になります。
9296
9297 以下のサンプルでは、中心に長方形が表示され、その縦幅が電圧値(V) * 100 ピクセルとしてリアルタイムに表示されます。
9298
9299 {{{
9300 #include &lt;psychlops.h&gt;
9301 using namespace Psychlops;
9302 #include &lt;Extentions/Samples/nidaqmxbase_ex_sample.cpp&gt;
9303
9304 void psychlops_main() {
9305         Canvas canvas(Canvas::fullscreen);
9306
9307         double rect_size = 100;
9308         Psychlops::Rectangle rect;
9309         rect.centering();
9310
9311         NIDAQmxBase_AnalogInput_Dev1 analog_in;
9312
9313         while(!Input::get(Keyboard::esc)) {
9314                 canvas.clear();
9315                 rect.resize(rect_size, rect_size * analog_in.get()).draw(Color::white);
9316                 canvas.flip();
9317         }
9318 }
9319 }}}
9320
9321 このサンプルをフォトトランジスタと接続して照度に連動する実行例が[[Cliplife|http://cliplife.jp/clip/?content_id=usqdz3cv]]にアップロードされています。
9322
9323
9324 !! アナログ出力
9325
9326 * NI USB-6009 の14番、15番ポートに出力線を接続する
9327 * ~NIDAQmxBase_AnalogOutput_Dev1 クラスの変数を作成し、ポートを初期化する
9328 * 変数のput(double voltage)メソッドを呼び、ポート間の電位差を渡す
9329 これで、0Vから5Vまでの電位差の出力が可能になります。
9330
9331 以下のサンプルでは、マウスが画面最上部にあるとき1V、最下部にあるとき0Vになります。
9332
9333 {{{
9334 #include &lt;psychlops.h&gt;
9335 using namespace Psychlops;
9336 #include &lt;Extentions/nidaqmxbase_ex_sample.cpp&gt;
9337
9338 void psychlops_main() {
9339         Canvas canvas(Canvas::fullscreen);
9340
9341         NIDAQmxBase_AnalogOutput_Dev1 analog_out;
9342         Mouse::show();
9343
9344         while(!Input::get(Keyboard::esc)) {
9345                 canvas.clear();
9346                 analog_out.put(1.0-1.0*Mouse::y/Display::getHeight());
9347                 canvas.flip();
9348         }
9349 }
9350 }}}
9351
9352 このサンプルを電圧計で計測した実行例が[[cliplife|http://cliplife.jp/clip/?content_id=opt044zr]]にアップロードされています。
9353 出力を直接ヘッドホンにつなぐと音が鳴りますが、解像度が150 Hzしかないので精度には期待しないほうがいいでしょう。
9354 </pre>
9355 </div>
9356 <div title="Tips: ビープを出す" modifier="YourName" modified="201003111856" created="200802201253" changecount="5">
9357 <pre>精度をまったく気にせず単に音がなればいい場合、システムのビープ音を利用する方法があります。
9358
9359
9360 Windowsの場合、以下の命令でPCのビープ音を発生させます。ただし、PCによっては機械ビープが省略されているものがあります。
9361 {{{
9362   Beep(int 持続時間, int 周波数);
9363 }}}
9364
9365
9366 Mac OS Xの場合、以下の命令でOSの警告音を発生させることができます
9367 {{{
9368   SysBeep(int 持続時間);
9369 }}}
9370 この命令にはいくつかの問題があります
9371 * Apple社より、この命令は将来的に廃止されることが予告されています。
9372 * OSの警告音が1度なるだけで、持続時間の指定にはまったく効果はありません。
9373
9374
9375 将来的には、Psychlopsで機種非依存の命令を追加する予定です。
9376 {{{
9377   Sound::Beep();
9378 }}}
9379 </pre>
9380 </div>
9381 <div title="Tips: プログラムが暴走したとき" modifier="PsychlopsAdmin" created="200709171720" changecount="1">
9382 <pre>プログラムの実行中に強制的に終了したい場合があります。このような場合はOSの強制終了コマンドを使ってください。
9383
9384 * MacOSX
9385 ** ESC + Option + Command(花記号)キーを同時に押す
9386 * Windows
9387 ** Alt + F4キーを同時に押す
9388 ** 上記方法で失敗した場合、Ctrl + Alt + Delを同時に押して、タスクマネージャから強制終了する</pre>
9389 </div>
9390 <div title="Tips: プログラムを複数のファイルに分割する" modifier="YourName" modified="200803140500" created="200803110823" changecount="4">
9391 <pre>実験間で共通の処理がある場合、管理を容易にするためにその部分のプログラムを別ファイルに分けて使いまわすことができます。
9392
9393 ! 既存のファイルの追加
9394 既存のファイルをプロジェクトに追加する場合は、以下の操作を行います。
9395
9396 !! Xcodeの場合
9397
9398 # 「ループとファイル」の「Sources」を右クリックし、「追加」「既存のファイル」と選ぶ
9399 # 開いたウィンドウでファイルを選択し、追加する
9400
9401 [img[image/osx/Xcode_addFiles.png]]
9402
9403 !! Reloの場合
9404
9405 # プロジェクトのファイル一覧を右クリックし、「Add Files」を押す
9406 # 開いたウィンドウでファイルを選択し、追加する
9407
9408 [img[image/win/Relo_addFiles.png]]
9409
9410 ! 新規追加
9411 新規にファイルを作ってプロジェクトに追加する場合は、以下の操作を行います。
9412
9413 !! Xcodeの場合
9414
9415 # 「ループとファイル」の「Sources」を右クリックし、「追加」「新規ファイル」と選ぶ
9416 # ファイル名等を決めて新規ファイルを作成
9417
9418 !! Reloの場合
9419
9420 # プロジェクトのファイル一覧を右クリックし、「New Souce File」を押す
9421 # 新しいタブ「untitled」が開かれているので、保存ボタンを押し、名前と保存場所を決める
9422
9423 </pre>
9424 </div>
9425 <div title="Tips: ランダムドットステレオグラムの描画コード" modifier="Psychlops_Admin" created="200709130536" changecount="1">
9426 <pre>下の例ではダイナミックランダムドットステレオグラムを描画します。
9427 スペースキーを押すと、ポーズがかかります。
9428 リターンキーを押すと、対応領域の可視化モードが切り替わります。
9429 エスケープキーを押すと、プログラムが終了します。
9430
9431 {{{
9432 #include &lt;psychlops.h&gt;
9433 using namespace Psychlops;
9434
9435 void psychlops_main()
9436 {
9437
9438         Canvas display(Canvas::fullscreen);
9439         const int BUFFER = 10;
9440         const int whole_size = 200;
9441         const int stereo_size = 100;
9442         double disprity = 10.0;
9443         bool pause = false;
9444         int dotsurface = 0;
9445         int SOA = 5;
9446
9447         Image base[2][BUFFER], stereo[2][BUFFER];
9448         for(int i=0; i&lt;BUFFER; i++) {
9449                 for(int j=0; j&lt;2; j++) {
9450                         base[j][i].set(whole_size, whole_size);
9451                         for(int y=0; y&lt;whole_size; y++) { for(int x=0; x&lt;whole_size; x++) { base[j][i].pix(x,y,Color(Psychlops::random())); }}
9452                 }
9453                 stereo[0][i].set(stereo_size, stereo_size);
9454                 stereo[1][i].set(stereo_size, stereo_size);
9455                         for(int y=0; y&lt;stereo_size; y++) { for(int x=0; x&lt;stereo_size; x++) { stereo[0][i].pix(x,y,Color(Psychlops::random()*0.5,0.0,0.0)); }}
9456                         for(int y=0; y&lt;stereo_size; y++) { for(int x=0; x&lt;stereo_size; x++) { stereo[1][i].pix(x,y,Color(Psychlops::random())); }}
9457         }
9458
9459         int loop = 0;
9460         while(!Input::get(Keyboard::esc)) {
9461                 if(Input::get(Keyboard::spc)) pause = !pause;
9462                 if(Input::get(Keyboard::rtn)) dotsurface = 1-dotsurface;
9463                 if(!pause){
9464                     loop++;
9465                          loop %= BUFFER;
9466                         }
9467                 Display::clear();
9468                         base[0][loop].centering().shift(-whole_size, 0).display();
9469                         stereo[dotsurface][loop].centering().shift(-whole_size, 0).display();
9470                         base[1][loop].centering().shift(whole_size, 0).display();
9471                         stereo[dotsurface][loop].centering().shift(whole_size+disprity, 0).display();
9472                 Display::msg(&quot;Press space key to pause.&quot;, 400, 100);
9473                 Display::msg(&quot;Press return key to switch visualize mode.&quot;, 400, 120);
9474                 Display::msg(&quot;Press esc key to exit.&quot;, 400, 140);
9475                 Display::flip(SOA);
9476         }
9477 }
9478
9479
9480 }}}</pre>
9481 </div>
9482 <div title="Tips: 時間精度が必要なプログラムを実行するとき" modifier="PsychlopsAdmin" created="200709171719" changecount="1">
9483 <pre>現在のコンピュータは同時に多数のソフトが起動しており、その計算に処理時間をとられてフレーム落ちする場合があります。これを回避するにはいくつかの方法があります。
9484
9485 * いったん再起動する(余計なソフトウェアを強制終了させることができます)
9486 * AppState::setThreadPriority( AppState::HIGH );関数を実行する(今後改名するかもしれません)
9487
9488 それでもコマ落ちする場合は、
9489 * 計算量の少ない等価な計算を行う
9490 ** forやwhile内で不必要な繰り返し計算を除去する
9491 ** pixの配列を書き込む計算の場合、for文をやめてCanvas.pix(ドット数, 配列)の呼び出しを行う
9492 * Imageが原因ならquicken()の使用を検討する
9493 * より処理速度の速いコンピュータを使用する
9494 などの方法が考えられます。</pre>
9495 </div>
9496 <div title="Tips: 機器間の同期" modifier="YourName" modified="200802201725" created="200802201301" changecount="4">
9497 <pre>現代のコンピュータはOSやハードウェア規格のレベルで機器間の同期を保証できないシステムに置き換えられています。このTipsでは、各機器の同期精度について説明します。
9498
9499 ! 一般論
9500 現代のコンピュータは、互換性の保持、失敗からの回復、暴走の回避を目的として、さまざまなインターフェースが仮想化され、パケット通信化されています。このため、コンピュータのクロックのみに依存した同期確保方法が利用できなくなっています。
9501
9502 ただし時間管理が必要な機器、たとえばCPUのクロック、ビデオカードの垂直同期信号、サウンドカードの連続的音声出力などはそれぞれ独自のクロック信号により精確な時間管理を行うことができます。これらの機器内部で精確なインターバルを得ることはできます。
9503
9504 しかし、機器間で同期を取ることは困難です。視聴覚時間同期といった信号は生成するのが非常に困難です。機器間で同期を取る方法としては、同期を制御する装置をひとつに絞り、その装置の外部にアナログ的な方法で同期装置を設ける方法が推奨されます。この方法は非常に精確です。
9505
9506
9507 ! OS
9508 最近のOS(Windows 2000以降やMac OS X)はプリエンプティブマルチタスク方式と呼ばれる、ひとつのアプリケーションが中央演算装置(CPU)を独占できない方式を採用しています。このため、あるアプリケーションが処理される速度は不定で、周辺機器の割り込みを実時間で拾えるとは限りません。
9509
9510
9511 ! ビデオカード
9512 近年のビデオカードはダブルバッファリングとバッファ切り替えの垂直同期信号との同期を行うことが標準化されており、ドライバの設定によって(Windowsでは画面の設定で指定、Mac OS Xでは標準)でその機能を利用できるようになり、描画途中で描画内容の転送が起こることのないように設計されています。Psychlopsではこれらのビデオボードに指示を行い、各描画フレームの垂直同期信号との同期を実現しています。
9513
9514 しかし、バッファ切り替えの行われた正確な時間を知ることはできません。
9515
9516
9517 ! キーボード、マウス
9518 キーボード、マウスなどHID(Human Interface Devices)は、一般的にはインタラプト転送と呼ばれる、一定間隔で行われる転送を用いて入力されます。これは比較的よい時間精度を持っていますが、一般的なGUIを持つOSでは、ウィンドウの配置を加味してカーネル等から各アプリケーションにプロセス間通信(時間がかかり、同期が維持されない)を用いてHIDイベント(キーを押した、離したなど)の情報が送られます。このため、アプリケーション側がHIDイベントを受け取るタイミングは同期が保証されません。
9519
9520 多くのOSでは、HIDイベントにそれが発生した時間情報が付加されます。アプリケーションが知ることができるHIDの時間情報はそれに制限されます。
9521
9522 HIDイベントに付加された時間情報が十分に正確であるならば、たとえば押し続けた時間は比較的正確に得ることができます。しかし視覚刺激提示からの反応時間をとることは、視覚刺激提示装置とHIDの同期が必要であり、そのままではできません。
9523
9524
9525 ! サウンドカード
9526 一般的なサウンドカードは、同期信号出力に用いるには向いていません。
9527 * 音声信号が途切れないように大き目のバッファを設けているため、数10ms以上のレイテンシがあります。
9528 ** ASIOとよばれる規格に対応したドライバ、サウンドカードをセットで使用すると、低レイテンシでの通信ができることがあります。一般に、5ms以下のレイテンシで動きます。
9529 * 信号の正確性があまり求められていないため、エラー訂正が不十分である場合や、通信失敗時に再送出をあきらめる場合があります。
9530
9531
9532 </pre>
9533 </div>
9534 <div title="Tips: 独自のクラスを作る" modifier="YourName" modified="200803140504" created="200803062209" changecount="6">
9535 <pre>自分で作成した機能(コード)を多数のプロジェクトで使いまわす場合、ユーザ独自の拡張としてクラス化し、Psychlopsのライブラリフォルダ追加すると簡単に利用することができます。
9536
9537 ここでは、例として扇形(パックマン図形)を多角形近似したものをクラス化してみます。このクラスは、以下の動作を行います。
9538 * 扇の半径、開口角、開口方向を指定して、多角形として作成する。
9539 * Rectangleと同じようにshift, centering, drawできるようにする。
9540
9541 クラスの中身はひとまず置いておき、ひとまずこのクラスを拡張として登録してみましょう。テキストエディタ(Windows: メモ帳、MacOSX: Xcode→メニュバー→ファイル→新規ファイル)に下記コードをコピー&ペーストし、「pacman.cpp」という名前をつけて保存します。
9542
9543 {{{
9544 #include &lt;psychlops_lib.h&gt;
9545 using namespace Psychlops;
9546
9547
9548 // Polygonally approximated Pacman(sector) shape
9549 class Pacman {
9550
9551         //      Sector Elements
9552         Psychlops::Point center;
9553         Psychlops::Point *vertices;
9554         int num_vertices;
9555
9556         public:
9557         //      Initialize
9558         Pacman(double radius, double open_angle_radian, double orientation_radian) {
9559                 int end = floor(radius * (1 - Math::mod(open_angle_radian, 2*PI)/(2*PI)));
9560                 num_vertices = end+2;
9561                 vertices = new Psychlops::Point[num_vertices];
9562
9563                 double offset = Math::mod(orientation_radian+open_angle_radian/2.0, 2*PI);
9564                 vertices[0].set(0,0);
9565                 for(int i=0; i&lt;end; i++) {
9566                         vertices[i+1].set(
9567                                 radius*cos(offset+2.0*((double)i/radius)*PI),
9568                                 radius*sin(offset+2.0*((double)i/radius)*PI)
9569                         );
9570                 }
9571                 vertices[num_vertices-1].set(
9572                                 radius*cos(offset+2*PI-open_angle_radian),
9573                                 radius*sin(offset+2*PI-open_angle_radian)
9574                         );
9575         }
9576         ~Pacman() {
9577                 delete [] vertices;
9578         }
9579
9580         //      Other Functions
9581         Pacman&amp; shift(double x, double y) {
9582                 center.set(x, y);
9583                 for(int i=0; i&lt;num_vertices; i++) vertices[i].shift(x,y);
9584                 return *this;
9585         }
9586         Pacman&amp; centering() {
9587                 shift(Display::getHcenter()-center.getX(), Display::getVcenter()-center.getY());
9588                 return *this;
9589         }
9590         Pacman&amp; draw(Color col) {
9591                 Display::polygon(vertices, num_vertices, col);
9592                 return *this;
9593         }
9594 };
9595 }}}
9596
9597
9598 次に、pacman.cppをPsychlops拡張フォルダ(以下のフォルダの中の適当な場所)にコピーします。この際、MacOSXやWindows Vistaでは管理者パスワードを求められることがあります。
9599
9600 * C:\Library\Frameworks\Psychlops.framework\Headers\Extentions
9601 * /Library/Frameworks/Psychlops.framework/Headers/Extentions
9602
9603 ここでは「Samples」というフォルダをつくり、ここに置いてみます。
9604
9605 次に、呼び出し側コードで今回作った独自拡張を#incluide指令を使って呼び出します。#include指令のオプションには、/Library/Frameworks/Psychlops.framework/Headers/より後の部分のファイルパスを指定します。
9606 {{{
9607  #include &lt;Extentions/Samples/pacman.cpp&gt;
9608 }}}
9609
9610
9611 {{{
9612 #include &lt;psychlops.h&gt;
9613 using namespace Psychlops;
9614 #include &lt;Extentions/Samples/pacman.cpp&gt;
9615
9616 void psychlops_main() {
9617
9618         Canvas display(Canvas::fullscreen);
9619         Pacman pacman(100, PI/2, 0); // radius: 100 px, opening: 90 deg, orientation: 0 deg (right)
9620         pacman.centering();
9621
9622         while(!Input::get(Keyboard::esc)) {
9623                 display.clear();
9624                 pacman.draw(Color::red);
9625                 display.flip();
9626         }
9627
9628 }
9629 }}}
9630
9631
9632  #includeでPacman拡張を呼び出すことで、いつでもPacman図形を使用することができるようになりました。
9633
9634
9635
9636 !! 上級者向け:ソース配布時の注意
9637 Extentionとして別ファイルに分離したものを配布する際は、必要なすべてのファイルを配布する必要があります。もしExtentionフォルダに入れることが前提のファイルを配布するのであれば、その旨を添付テキスト等に記述し、登録するよう指示しましょう。拡張ではない通常の複数ファイル使用の場合は、プロジェクトへの追加が必要な旨を書きましょう。
9638
9639
9640 !! 上級者向け:複数ファイルから拡張を呼び出す場合
9641 この解説は、ユーザ側のプログラムが単一のファイルになるという初心者向けの状況を想定して書かれています。単一バイナリを生成するのに複数のファイルを使い、それらが同時にひとつの拡張を#includeするような場合は、この方法は使うことができません。このような場合、ヘッダファイルと実装の分離をする必要がありますが、これについては[[Wikipediaの項目|http://ja.wikipedia.org/wiki/%E3%83%98%E3%83%83%E3%83%80%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB]]を参照ください。
9642
9643
9644 !! 上級者向け:クラス設計の解説
9645
9646 クラスを作る際には、
9647 * 保持するデータ
9648 * 自動的な初期化処理と廃棄処理
9649 * 各種命令(メソッド)
9650 の3つを用意します。
9651 簡単な解説については、下記サイトが参考になります。
9652 [[C++入門:クラス|http://wisdom.sakura.ne.jp/programming/cpp/cpp5.html]]
9653
9654 また、psychlops.hの中には真のmain / WinMain関数が入っているため、これを2回以上includeするとコンパイラは本体にも拡張側にもmain関数があるかのようにみなし、衝突してしまいます。これを防ぐために、拡張側ではpsychlops_lib.hをインクルードしてください。
9655
9656 {{{
9657  #include &lt;psychlops_lib.h&gt;
9658 }}}
9659
9660 </pre>
9661 </div>
9662 <div title="Tips: 画像のフィルタリング処理" modifier="YourName" modified="200803140632" created="200803100447" changecount="27">
9663 <pre>視覚刺激の中には特定の空間周波数でローパス・ハイパスのフィルタをかけた画像を使用することがあります。現在Psychlops本体ではFFT([[高速フーリエ変換|http://ja.wikipedia.org/wiki/%E9%AB%98%E9%80%9F%E3%83%95%E3%83%BC%E3%83%AA%E3%82%A8%E5%A4%89%E6%8F%9B]])機能を持っていませんが、既存のライブラリと併用することで実現できます。ここでは、MATLABでも採用されている[[FFTW|http://www.fftw.org/]]をPsychlops拡張化して利用してみます。
9664
9665
9666 * FFTWはGPLと有料ライセンスの選択制です。GPLを選択される場合、GPLの通称「感染条項」によりこのライブラリを使用したブログラムはすべてGPLとしなければなりません。このプログラムの配布に当たっては、下記の添付ファイルを必ず添付し、パッケージ作成に必要なソースコードをすべて公開しなければなりません。
9667
9668
9669 ! FFTWのPsychlops拡張のインストール
9670
9671 !! Mac OS X向け
9672
9673 # [[FFTW_demo_osx.zip|http://psychlops.l.u-tokyo.ac.jp/download/BasicSamples/FFTW_demo_osx.zip]]をダウンロードし、解答します。
9674 # 「~FFTW-Installer」をダブルクリックして拡張ををインストールします(要管理者権限)。
9675 #* インストール時にはTerminalが開き、ユーザのパスワードを聞かれますので、入力してください。
9676
9677 次に、拡張を使ったプロジェクトを作成します。
9678 # 新しくプロジェクトを作成します。
9679 # 「External Frameworks and Libraries」を右クリックし、「追加」→「既存のファイルの追加」をクリックします
9680 ## ファイル選択ウィンドウが開いたら、右上の検索ボックスで以下のパスを''手打ちしてください''(コピー&amp;ペースト不可)
9681 ##* /usr/local/lib/
9682 ## 開いたフォルダで以下のファイルを選択してください。リンクするライブラリ一覧に追加されます。
9683 ##* libfftw3.a
9684 # デモを実行する場合、~FilterDemo.cppの内容をコピー&ペーストします
9685 # ビルドして実行してみましょう。
9686 [img[Image/OSX/Xcode_addLib1.png]]
9687 [img[Image/OSX/Xcode_addLib2.png]]
9688
9689 !! Windows向け
9690
9691 # [[FFTW_demo_win.zip|http://psychlops.l.u-tokyo.ac.jp/download/BasicSamples/FFTW_demo_win.zip]]をダウンロードし、解凍します。
9692 # 「FFTWのPsychlops拡張インストーラ.bat」を使って拡張ををインストールします(要管理者権限)。
9693
9694 次に、拡張を使ったプロジェクトを作成します。
9695 # 新しくプロジェクトを作成します。
9696 # いったんテンプレートをビルドします。
9697 # 作成された実行ファイルと同じフォルダに「libfftw3-3.dll」をコピーします
9698 # プロジェクトのオプションを開き、リンクするライブラリに「libfftw3-3_bcc.lib」を加えます(Codeblocks+gccであれば「libfftw3-3.def」を加えます)
9699 # デモを実行する場合、~FilterDemo.cppの内容をコピー&ペーストします
9700 # ビルドして実行してみましょう。
9701 [img[Image/Win/Relo_addLib_1.png]]
9702 [img[Image/Win/Relo_addLib_2.png]]
9703
9704
9705 ! 使用法
9706
9707 このサンプルでは、FFTWを用いてローパスフィルタを作成するための関数群が用意されています。配列の大きさが奇数の配列に対しては適用しないでください。
9708
9709 !関数一覧
9710
9711 !! フィルタリング関数
9712
9713 |!Matrix makeLowPassFilter()|makeLowPassFilter(int height, int width, double frequency)|高さheight, 幅width, カットオフ周波数がfrequencyのローパスフィルターとなるMatrixを作成します。|
9714 |~|~|int height: ローパスフィルタの高さ|
9715 |~|~|int width: ローパスフィルタの幅|
9716 |~|~|double frequency: カットオフ周波数|
9717
9718 内部では、カットオフより内側が1、カットオフより外側が0の行列を作成します。
9719
9720
9721 |!Matrix filterImage()|filterImage(Matrix image, Matrix filter)|imageに対してfilterで指定した行列を元にフィルタリングを適用します。|
9722 |~|~|Matrix image: フィルタをかける元画像|
9723 |~|~|Matrix filter: 適用するフィルタ|
9724
9725 内部では、
9726 # imageにFFTをかけ、周波数表現を作る
9727 # 周波数表現でフィルタを要素ごとに乗算する
9728 # フィルタリング後の周波数表現を逆FFTする
9729 という作業が行われています。
9730
9731 [img[image/tips/sample_FFTW.png]]
9732
9733
9734 !! FFT・逆FFT
9735
9736 2次元のFFTを実行します。ただし、fft2やifft2で使う配列は行および列が偶数である必要があります。奇数の場合は正常に動作しない可能性があります。
9737
9738 fft2を用いてFFTを行った後の行列は中心に直流成分が存在し、周辺部に高周波数成分が存在します。行列の大きさがMatrix(rows, cols)の場合、直流成分はMatrix(rows / 2 + 1, cols / 2 + 1)に存在します。
9739
9740 周波数分解能Δf(Matrixの要素が1つずれた場合にどれだけ周波数が変化するか)は、通常の視覚実験においてΔfは1 / 画像の大きさ(視角)となります。
9741
9742 FFTにおけるΔfはサンプリング周波数 / データ数です。視覚実験においてはサンプリング周波数は視角1度が何ピクセルかに相当します。データ数は画像の高さもしくは幅のピクセル数となります。画像の高さや幅(ピクセル数)はサンプリング周波数に画像の高さや幅(視角)をかけたものに等しくなります。このことから通常の視覚実験においてΔfは1 / 画像の大きさ(視角)となります。
9743
9744 |!void fft2()|fft2(Matrix src, Matrix dstReal, Matrix dstImaginary)|2次元のフーリエ変換を実行します。|
9745 |~|~|Matrix src:フーリエ変換を適用する配列。|
9746 |~|~|Matrix dstReal:フーリエ変換後の配列の実部が代入されます。|
9747 |~|~|Matrix dstImaginary:フーリエ変換後の配列の虚部が代入されます。|
9748 |!void fft2()|fft2(Matrix srcReal, Matrix srcImaginary, Matrix dstReal, Matrix dstImaginary)|2次元のフーリエ変換を実行します。|
9749 |~|~|Matrix srcReal:フーリエ変換を適用する配列の実部。|
9750 |~|~|Matrix srcImaginary:フーリエ変換を適用する配列の虚部。|
9751 |~|~|Matrix dstReal:フーリエ変換後の配列の実部が代入されます。|
9752 |~|~|Matrix dstImaginary:フーリエ変換後の配列の虚部が代入されます。|
9753
9754 実行後の配列は中心部に低周波数領域があります。
9755
9756 |!void ifft2()|ifft2(Matrix srcReal, const Matrix srcImaginary, Matrix dst)|2次元の逆フーリエ変換を実行します。|
9757 |~|~|Matrix srcReal:逆フーリエ変換を適用する配列の実部。|
9758 |~|~|Matrix srcImaginary:逆フーリエ変換後の配列の虚部。|
9759 |~|~|Matrix dst:逆フーリエ変換後の配列の絶対値が代入されます。|
9760 |!void ifft2()|ifft2(Matrix srcReal, Matrix srcImaginary, Matrix dstReal, Matrix dstImaginary)|2次元の逆フーリエ変換を実行します。|
9761 |~|~|Matrix srcReal:逆フーリエ変換を適用する配列の実部。|
9762 |~|~|Matrix srcImaginary:逆フーリエ変換後の配列の虚部。|
9763 |~|~|Matrix dstReal:逆フーリエ変換後の配列の実部が代入されます。|
9764 |~|~|Matrix dstImaginary:逆フーリエ変換後の配列の虚が代入されます。|
9765
9766
9767 !! 調整用関数
9768
9769 |!Matrix normalize()|normalize(Matrix matrix)|行列の各要素の平均値が0、標準偏差が1となるよう正規化します。|
9770 |~|~|Matrix matrix: 正規化対象の行列|
9771
9772 フィルター処理をしたあとの画像は[0, 1]の範囲に収まらず、表示可能な領域からはみ出してしまいます。そこで表示可能な範囲に収める処理が必要となります。フィルター処理をした後の画像の輝度値の分布は正規分布に従うことが知られています。そのため、一度正規化をします。normalizeはその正規化を行うための関数です。
9773
9774 正規化を行った後はコントラストをかけ、平均輝度を足します。このときのコントラストはマイケルソンコントラストではなく、RMSコントラストです。RMSコントラストが1/3を超えると表示可能な領域からはみ出してしまう点が多くなるのでなるべくこの値を最大値としてください。
9775
9776 最後に、それでも表示可能な領域が残る可能性は存在しますので、それを表示可能な領域に納めます。
9777
9778
9779 |!Matrix makeNoise()|makeNoise(int height, int width)|高さheight、幅widthのホワイトノイズ行列を作成します。|
9780 |~|~|int height: 縦の大きさ|
9781 |~|~|int width: 横の大きさ|
9782
9783
9784
9785 !! 内部用調整関数
9786
9787 以下の関数はfft2やifft2内部で呼び出される関数です。通常ユーザーがこの関数を実行する必要はありません。
9788
9789 |!Matrix fftShift()|fftShift(const Matrix &amp;matrix)|FFTをかけた後の周波数ごとの分布を移動させます。|
9790 |~|~|Matrix matrix: シフトを行う対象を指定します|
9791 FFTWではFFTを行った後の行列は低周波成分が4隅にあり、中心部には高周波成分が存在します。このままではフィルターを作成する際に計算しづらいので、中心部に低周波数成分を、周辺部に高周波数成分が並ぶようにします。そのための関数です。
9792
9793 逆に中心にある低周波数成分を周辺部に、周辺にある高周波数成分を中心にするように並べ替えることもできます。
9794
9795 |!void fftExecute()|fftExecute(const Matrix &amp;srcReal, const Matrix &amp;srcImaginary, Matrix &amp;dstReal, Matrix &amp;dstImaginary, fftw_complex *input, fftw_complex *output, const fftw_plan &amp;plan)|フーリエ変換を実行します。|
9796 |~|~|Matrix srcReal:フーリエ変換を適用する配列の実部。|
9797 |~|~|Matrix srcImaginary:フーリエ変換を適用する配列の虚部。|
9798 |~|~|Matrix dstReal:フーリエ変換後の配列の実部が代入されます。|
9799 |~|~|Matrix dstImaginary:フーリエ変換後の配列の虚部が代入されます。|
9800 |~|~|fftw_complex *input: FFTW作業用の変数|
9801 |~|~|fftw_complex *output: FFTW作業用の変数|
9802 |~|~|fftw_plan plan: FFTW作業用の変数|
9803 </pre>
9804 </div>
9805 <div title="Tips: 自分の機器の描画性能を確かめる" modifier="YourName" modified="200803062209" created="200803062208" changecount="2">
9806 <pre>Psychlopsの実行速度は環境によって異なります。現在の実行環境での実行速度を知りたい場合は、簡易に計測するプログラムを用意しましたので、これを実行して調べることができます。下記ファイルをダウンロードし、コンパイルして実行してみてください。
9807
9808 [[Benchmark.cpp|http://psychlops.l.u-tokyo.ac.jp/download/BasicSamples/Benchmark.cpp]]
9809
9810 2~5分程度のテストを行い、各種命令の「フレームが落ちずに実行できる最大処理能力」を測ります。結果は終了後に画面に表示されるほか、実行ファイルと同じ場所に結果ファイルを出力します(Mac OS Xの場合、プロジェクトフォルダ/build/Release/)。
9811
9812 現在のバージョンでは4つのテストを行います。各テストの項目のうち、
9813 * 上「今回のテストで使われたリフレッシュレートで、1フレームで表示できる最大数」
9814 * 下「100 Hzで正規化した場合に、1フレームで表示できる最大サイズ」(この値が1000の場合、100 Hz時に1000x1000ピクセルの画像が1枚表示可能ということを示します。)
9815
9816 !!! Pixel test
9817 Canvas.pix命令の処理能力を計測します。
9818 GPUの頂点処理能力と、CPUからGPUに命令を出す帯域幅が主な律速要因になります。
9819
9820 !!! Rectangle test
9821 Canvas.rect命令の処理能力を計測します。
9822 GPUのピクセル処理能力がが主な律速要因になります。
9823
9824 !!! Image test
9825 オフスクリーン(Image.draw)の処理能力を調べます。
9826 メインメモリからグラフィックカードへImageを転送する帯域幅が主な律速要因になります。
9827
9828 !!! VRAM Image test
9829 Image.quicken()されたオフスクリーン画像の処理能力を計測します。
9830 GPUのピクセル処理能力がが主な律速要因になります。
9831
9832
9833 実験を行ううえでの目安としてください。
9834 また、計測結果には10%前後の誤差があります。</pre>
9835 </div>
9836 <div title="Tips::CanvasクラスとDisplayクラス" modifier="Psychlops_Admin" modified="200708230528" created="200708230500" changecount="1">
9837 <pre>PsychlopsではデフォルトのCanvasに対して(インスタンス名を無視して)描画を強制的に行うDisplayクラスが用意されています。
9838 現バージョンでのPsychlopsはCanvasを同時に1つしか確保できない仕様となっているので(つまり宣言したCanvasインスタンスは常にデフォルトになる)、Displayクラスを使って描画を行っても、インスタンス名を使って描画を行った場合と同じ結果が得られます。たとえば、以下の2つのプログラムは同じ結果になります。
9839 {{{
9840 &lt;インスタンス名を使用したソース&gt;
9841 #include &lt;psychlops.h&gt;
9842 using namespace Psychlops;
9843 void psychlops_main() {
9844         Canvas sampleB(Canvas::fullscreen);
9845         sampleB.clear(Color::blue);
9846         sampleB.flip();
9847         while(!Input::get(Keyboard::spc));
9848 }
9849 }}}
9850
9851 {{{
9852 &lt;Displayクラスを使用したソース&gt;
9853 #include &lt;psychlops.h&gt;
9854 using namespace Psychlops;
9855 void psychlops_main() {
9856         Canvas sampleB(Canvas::fullscreen);
9857         Display::clear(Color::blue);
9858         Display::flip();
9859         while(!Input::get(Keyboard::spc));
9860 }
9861 }}}
9862
9863 また、psychlops_main外の関数で描画を行うときには、Canvasのポインタを渡す必要のないDisplayクラスを使った方法のほうが便利かもしれません。下の例を比べて見てください。
9864
9865 {{{
9866 &lt;インスタンス名を使用したソース&gt;
9867 #include &lt;psychlops.h&gt;
9868 using namespace Psychlops;
9869
9870 void stimulusDraw(Canvas &amp;sample){
9871         sample.clear(Color::blue);
9872         sample.flip();
9873         while(!Input::get(Keyboard::spc));
9874 }
9875
9876 void psychlops_main() {
9877         Canvas sampleB(Canvas::fullscreen);
9878         stimulusDraw(sampleB);
9879 }
9880
9881 }}}
9882
9883 {{{
9884 &lt;Displayクラスを使用したソース&gt;
9885 #include &lt;psychlops.h&gt;
9886 using namespace Psychlops;
9887 void stimulusDraw(){
9888         Display::clear(Color::blue);
9889         Display::flip();
9890         while(!Input::get(Keyboard::spc));
9891 }
9892 void psychlops_main() {
9893         Canvas sampleB(Canvas::fullscreen);
9894         stimulusDraw();
9895 }
9896 }}}
9897
9898
9899 </pre>
9900 </div>
9901 <div title="Update from 1.0 to 1.3" modifier="Psychlops_DevelopperG" modified="200912010831" created="200908100506" changecount="18">
9902 <pre>* 2.1.1節のCanvasの宣言をマルチスクリーンに対応したバージョンに書き換え
9903
9904 [[2.3 文字列の描画]]
9905 * [[2.3.1 Canvas::msg()命令単体での文字列描画]]
9906 * [[2.3.2 Lettersクラスを用いた文字列描画]]
9907 * [[3.5 文字列のレンダリングの仕様と関連情報]]
9908
9909 * 3.3.1節にCanvas::toの注意事項とImage::release()についての説明を追加。
9910
9911 [[3.3.3 複数の画像ファイルからアニメーションを作成する]]
9912
9913 [[4. 複雑な描画を行う2(複数オブジェクトのグループ化と回転・拡大縮小)]]
9914 [[8. 透明度を使って刺激描画を高速化する]]
9915 [[9. アナログ入出力を行う]]
9916
9917 ! [[Tips]]
9918 * [[Tips: Shapeクラスの概要]]
9919 *  [[Tips: Strokeクラスの概要]]
9920 * [[Tips: Canvasを宣言しないと動作しない命令群]]
9921 * [[Tips: OS環境による関数名の衝突について]]
9922 * [[Tips: ガンマ補正]]
9923 setGammaTable(std::vector&lt;double&gt; Cr, std::vector&lt;double&gt; Cg, std::vector&lt;double&gt; Cb)のコード例
9924 ![[関数一覧]]
9925 * [[Canvasの宣言]]
9926 * [[Image::to()]]
9927 * [[Displayの指定値]]
9928 * [[CanvasMode]]
9929
9930 ! [[関数逆引きとQ&amp;A]]
9931
9932 * [[Canvas::pix(double, double, double)という命令はコンパイルできないのですか?]]
9933 * [[止まった絵を描画するときCanvas.flip()を単独でwhileループの中に書くと、画面がちらつきます。]]
9934 * [[特定の命令に対してコンパイル時にエラーが出る]]
9935
9936
9937 </pre>
9938 </div>
9939 <div title="memo" modifier="Psychlops_Admin" modified="200708280427" created="200708280335" changecount="2">
9940 <pre>/***
9941 CSSカスタマイズ
9942 ***/
9943 body {
9944         background: #f0efc0;
9945 }
9946
9947 #mainMenu{
9948         background: #f0efc0;
9949         border: 2px dotted #6a6;
9950 }
9951
9952 /*{{{*/
9953 h1,h2,h3,h4,h5 {
9954         padding-top: 0.3em;
9955 }
9956
9957 .headerShadow {
9958         padding: 2.0em 0em 0.7em 1em;
9959 }
9960
9961 .headerForeground {
9962         padding: 2.0em 0em 0.7em 1em;
9963 }
9964
9965
9966 .siteTitle {
9967         font-size: 3.0em;
9968 }
9969
9970 .siteSubtitle {
9971         font-size: 1.2em;
9972 }
9973
9974 .subtitle {
9975         font-size: 0.9em;
9976 }
9977 /*}}}*/
9978
9979 /*{{{*/
9980
9981
9982 h1,h2,h3,h4,h5 {
9983         color: #fff;
9984         background: #4a7;
9985 }
9986
9987 .button {
9988         border: 0px;
9989 }
9990
9991 .header {
9992         background: #084;
9993 }
9994
9995 .shadow .title {
9996         color: #fff;
9997 }
9998
9999 .title {
10000         color: #fff;
10001         background: #084;
10002 }
10003
10004 .viewer th {
10005         background: #996;
10006 }
10007
10008 .viewer pre {
10009         border: 1px solid #6a6;
10010         background: #cfc;
10011         font-size:0.9em
10012 }
10013
10014 .viewer thead td {
10015         background: #8d8;
10016 }
10017 /*}}}*/
10018
10019 /*{{{*/
10020 .viewer thead td {
10021         background: #8d8;
10022 }
10023
10024 .viewer tfoot td {
10025         background: #bd6;
10026 }
10027
10028 .MyStyle {
10029         background: #aff;
10030 }
10031 /*}}}*/
10032
10033
10034
10035 body{
10036  margin: 0 auto;
10037  padding:0;
10038  width: 100%;
10039  font: 80% arial,sans-serif;
10040 }
10041
10042 #mainMenu {
10043  position: absolute;
10044  width: 16%;
10045  text-align:left;
10046  padding: 0em 1em 0em 1em;
10047
10048  background-color: #eee;
10049  color:#cccccc;
10050 font-size: .9em;
10051  }
10052
10053 #sidebar {
10054  position: absolute;
10055  right: 10px;
10056  width: 10%;
10057  font-size: .9em;
10058  }
10059
10060 .headerShadow,
10061 .headerForeground {
10062  padding: 1em;
10063  }
10064
10065 .viewer blockquote {
10066  font-size: 8pt;
10067  line-height: 150%;
10068  border-left: 3px solid #666666;
10069  padding-left: 1.0em;
10070  margin-left: 2.5em;
10071 }
10072
10073 .siteTitle {
10074  font-size: 24pt;
10075 }
10076 .siteSubtitle {
10077  font-size: 12pt;
10078 }
10079
10080 .title {
10081  color:#55221f;
10082  font-size: 1.5 em;
10083  padding-left:0.1em;
10084
10085  margin-bottom:10px;
10086  margin-top:4px;
10087
10088  border-bottom:2px solid #ccc;
10089 }
10090 </pre>
10091 </div>
10092 <div title="wiki文法" modifier="Psychlops_DevelopperG" modified="200912010832" created="200707220320" changecount="6">
10093 <pre>http://hsj.jp/junknews/archives/tiddlywiki_susume.html
10094
10095 図の引用
10096 {{{
10097 [img[image/canvasrect.png]]
10098 }}}</pre>
10099 </div>
10100 <div title="このマニュアルの見方について(ここをクリックしてください)" modifier="PsychlopsAdmin" created="200709171623" changecount="1">
10101 <pre>このマニュアルはTiddelyWikiによって作成されています。
10102 今、このウィンドウが開いたように、見出しをダブルクリックすると、該当する記事のウィンドウが自動的に開きます。必要なくなった(見終わった)ウィンドウは、見出しの左上のコマンドから&quot;close&quot;をクリックすれば閉じることができます。
10103 Editを押すと内容を改変して、ご自分でコメントをメモすることができますが、改変した部分は元には戻せないのでご注意ください。
10104 また、画面左端にメニューバーから各記事にアクセスしたり記事の内容を検索することもできます。</pre>
10105 </div>
10106 <div title="その他" modifier="Kazushi Maruya" modified="200712180151" created="200712131752" changecount="4">
10107 <pre>* [[Psychlopsについて開発者に質問するには?]]
10108 * [[素晴らしいデモプログラムを作成したので、公開したい]]
10109 * [[Psychlopsを既存・自作のC/C++ライブラリと併用することができますか?]]</pre>
10110 </div>
10111 <div title="カラーモード" modifier="Psychlops_Admin" created="200709041935" changecount="1">
10112 <pre>|GRAY|グレースケールの輝度値|
10113 |RGB|色の三原色に基づいた赤・緑・青で構成された色|
10114 |RGBA|RGBに透明度が追加された色|
10115 詳しくは[[2.2.5 描画する図形の色を指定する-Colorクラス-]]を参照</pre>
10116 </div>
10117 <div title="ガンマ補正" modifier="YourName" modified="200802130705" created="200802130649" changecount="4">
10118 <pre></pre>
10119 </div>
10120 <div title="キーコード表" modifier="Psychlops_DevelopperG" modified="200908100437" created="200708240330" changecount="3">
10121 <pre>!キーボード
10122
10123 |アルファベットキー (a~z)|a~zのまま(小文字)|
10124
10125 |キーボード最上段(1~10) |1|2|3|4|5|6|7|8|9|0|
10126 |~|one|two|three|four|five|six|seven|eight|nine|zero|
10127
10128 |特殊キー|Return|Space|Escape|カーソル上|カーソル下|カーソル左|カーソル右|Shift|Control|Alt|
10129 |~| rtn | spc | esc | up | down | left | right | shift | ctrl | alt |
10130
10131 |テンキー|0|1|2|3|4|5|6|7|8|9|
10132 |~|pad0|pad1|pad2|pad3|pad4|pad5|pad6|pad7|pad8|pad9|
10133 |~|ー|&gt;|+|&gt;|*|&gt;|/|&gt;|Enter|&gt;|.|
10134 |~|padminus|&gt;|padpuls|&gt;|padasterisk|&gt;|padslash|&gt;|padenter|&gt;|padperiod|&gt;|
10135
10136 !マウス
10137 |左ボタン|右ボタン|中ボタン|すべてのボタンのいずれか|
10138 | left | right | middle | any |
10139 </pre>
10140 </div>
10141 <div title="例1: pushedを使ったソース" modifier="Psychlops_Admin" modified="200708272051" created="200708272048" changecount="2">
10142 <pre>{{{
10143 *例1: pushedを使ったソース
10144 #include &lt;psychlops.h&gt;
10145 using namespace Psychlops;
10146
10147 int code, oldcode;
10148 Psychlops::Rectangle rect(60,60), rect2(60,60);
10149
10150 void psychlops_main() {
10151         
10152         Canvas display(Canvas::fullscreen);
10153         display.clear(Color::gray);
10154         rect.centering();
10155         
10156         Input::refresh();
10157         
10158         while(!Input::get(Keyboard::spc)){
10159                 oldcode=code;
10160                 code=-1;
10161                 display.clear(Color::gray);
10162                 
10163                 if(Input::get(Keyboard::c, Keyboard::pushed))code=0; //centering
10164                 if(Input::get(Keyboard::left, Keyboard::pushed))code=1;
10165                 else if(Input::get(Keyboard::right, Keyboard::pushed))code=2;
10166
10167                 switch(code){
10168                         case -1: break;
10169                         case 0: rect.centering(); break;
10170                         case 1: rect.shift(-1,0); break;
10171                         case 2: rect.shift(1,0); break;
10172                 }
10173                         
10174                 rect2.centering().draw(Psychlops::Color(0.75,0.75,0.75)); 
10175                 rect.draw(Psychlops::Color(1.0,1.0,1.0,0.25));
10176                 display.flip();
10177         }
10178 }
10179 }}}
10180 </pre>
10181 </div>
10182 <div title="例2: pressedを使ったソース" modifier="Psychlops_Admin" modified="200708272052" created="200708272051" changecount="2">
10183 <pre>{{{
10184 *例2: pressedを使ったソース
10185 #include &lt;psychlops.h&gt;
10186 using namespace Psychlops;      
10187
10188 int code, oldcode;
10189 Psychlops::Rectangle rect(60,60), rect2(60,60);
10190
10191 void psychlops_main() {
10192         
10193         Canvas display(Canvas::fullscreen);
10194         display.clear(Color::gray);
10195         rect.centering();
10196         
10197         Input::refresh();
10198         
10199         while(!Input::get(Keyboard::spc)){
10200                 oldcode=code;
10201                 code=-1;
10202                 display.clear(Color::gray);
10203                 
10204                 if(Input::get(Keyboard::c, Keyboard::pushed))code=0; //centering
10205                 if(Input::get(Keyboard::left, Keyboard::pressed))code=1;
10206                 else if(Input::get(Keyboard::right, Keyboard::pressed))code=2;
10207
10208                 switch(code){
10209                         case -1: break;
10210                         case 0: rect.centering(); break;
10211                         case 1: rect.shift(-1,0); break;
10212                         case 2: rect.shift(1,0); break;
10213                 }
10214                         
10215                 rect2.centering().draw(Psychlops::Color(0.75,0.75,0.75)); 
10216                 rect.draw(Psychlops::Color(1.0,1.0,1.0,0.25));
10217                 display.flip();
10218         }
10219 }
10220 }}}</pre>
10221 </div>
10222 <div title="例3: releasedを使ったソース" modifier="Psychlops_Admin" created="200708272053" changecount="1">
10223 <pre>{{{
10224 *例3: releasedを使ったソース
10225 #include &lt;psychlops.h&gt;
10226 using namespace Psychlops;
10227
10228 int code, oldcode;
10229 Psychlops::Rectangle rect(60,60), rect2(60,60);
10230
10231 void psychlops_main() {
10232         
10233         Canvas display(Canvas::fullscreen);
10234         display.clear(Color::gray);
10235         rect.centering();
10236         
10237         Input::refresh();
10238         
10239         while(!Input::get(Keyboard::spc)){
10240                 oldcode=code;
10241                 code=-1;
10242                 display.clear(Color::gray);
10243                 
10244                 if(Input::get(Keyboard::c, Keyboard::pushed))code=0; //centering
10245                 
10246                 //state &quot;pressed&quot; and &quot;released&quot; Demo
10247                 if(Input::get(Keyboard::left, Keyboard::pressed))code=1;
10248                 else if(Input::get(Keyboard::right, Keyboard::pressed))code=2;
10249                    
10250                 if(Input::get(Keyboard::left, Keyboard::released))code=3;
10251                 else if(Input::get(Keyboard::right, Keyboard::released))code=3;
10252                 
10253                 switch(code){
10254                         case -1: break;
10255                         case 0: rect.centering(); break;
10256                         case 1: rect.shift(-1,0); break;
10257                         case 2: rect.shift(1,0); break;
10258                         case 3: rect.centering(); break;
10259                 }
10260                 
10261                 rect2.centering().draw(Psychlops::Color(0.75,0.75,0.75)); 
10262                 rect.draw(Psychlops::Color(1.0,1.0,1.0,0.25));
10263                 display.flip();
10264         }
10265 }
10266 }}}</pre>
10267 </div>
10268 <div title="例4: pushed,pressed,releasedを使ったソース" modifier="Psychlops_Admin" modified="200708272058" created="200708272055" changecount="1">
10269 <pre>{{{
10270 *例4 KeyStateの使用例
10271 #include &lt;psychlops.h&gt;
10272 using namespace Psychlops;      
10273
10274 double x=0,y=0, dX,dY;
10275 int code, oldcode;
10276 Psychlops::Rectangle rect(60,60), rect2(60,60);
10277
10278 void psychlops_main() {
10279         
10280         Canvas display(Canvas::fullscreen);
10281         display.clear(Color::gray);
10282         rect.centering();
10283         dX= display.getHcenter();
10284         dY= display.getVcenter();
10285         x=dX;
10286         y=dY;
10287         
10288         Input::refresh();
10289         
10290         while(!Input::get(Keyboard::spc)){
10291                 oldcode=code;
10292                 code=-1;
10293                 
10294                 //state &quot;pushed&quot; Demo
10295                 if(Input::get(Keyboard::c, Keyboard::pushed))code=0; //centering
10296                 
10297                 if(Input::get(Keyboard::a, Keyboard::pushed))code=1;
10298                 else if(Input::get(Keyboard::s, Keyboard::pushed))code=2;
10299         
10300                 //state &quot;pressed&quot; Demo
10301                 if(Input::get(Keyboard::z, Keyboard::pressed))code=3;
10302                 else if(Input::get(Keyboard::x, Keyboard::pressed))code=4;
10303                 
10304                 //state &quot;pressed&quot; and &quot;released&quot; Demo
10305                 if(Input::get(Keyboard::q, Keyboard::pressed)){
10306                         code=5;
10307                    if(oldcode==5||oldcode==7)code=7;
10308                 }
10309                 else if(Input::get(Keyboard::w, Keyboard::pressed)){
10310                    code=6;
10311                    if(oldcode==6||oldcode==8)code=8;
10312                 }
10313                 if(Input::get(Keyboard::q, Keyboard::released))code=9;
10314                 else if(Input::get(Keyboard::w, Keyboard::released))code=9;
10315                 
10316                 switch(code){
10317                         case -1: break;
10318                         case 0: rect.centering(); break;
10319                         case 1: rect.shift(-1,0); break;
10320                         case 2: rect.shift(1,0); break;
10321                         case 3: rect.shift(-1,0); break;
10322                         case 4: rect.shift(1,0); break;
10323                         case 5: x=ceil(rect.getHcenter()); y=ceil(rect.getVcenter()); rect.shift(-1,0);break;
10324                         case 6: x=ceil(rect.getHcenter()); y=ceil(rect.getVcenter()); rect.shift(1,0);break;
10325                         case 7: rect.shift(-1,0); break;
10326                         case 8: rect.shift(1,0); break;
10327                         case 9: rect.centering(x,y); break;
10328                 }
10329                 display.clear(Color::gray);
10330                 
10331                 //Show variables 
10332                 display.message(&quot;KeyState:&quot;, dX-100, dY-120, Color::green);
10333                 display.var(code, dX,dY-120, Color::green);
10334                 display.message(&quot;X:&quot;, dX-100, dY-100, Color::white); 
10335                 display.var(ceil(rect.getHcenter()),dX,dY-100, Color::white);
10336                 display.message(&quot;Y:&quot;, dX-100, dY-80, Color::white);
10337                 display.var(ceil(rect.getVcenter()),dX,dY-80, Color::white);
10338                 
10339                 if(code&gt;5){ 
10340                                         rect2.centering(x,y).draw(Psychlops::Color(0.75,0.75,0.75)); 
10341                                         rect.draw(Psychlops::Color(1.0,1.0,1.0,0.25));
10342                         }
10343                 else rect.draw(1.0);
10344                 display.flip();
10345         }
10346 }
10347 }}}
10348 このプログラム内で使用されているCanvas::message, Canvas::var命令については[[6.1 時間を計測する]]を参照してください。
10349 </pre>
10350 </div>
10351 <div title="例5: Mouse::pressedを使ったソース" modifier="Psychlops_Admin" created="200708272055" changecount="1">
10352 <pre>{{{
10353 *例5 MouseStateの使用例
10354 #include &lt;psychlops.h&gt;
10355 using namespace Psychlops;
10356
10357 double x=0,y=0;
10358 int code, oldcode;
10359 Psychlops::Rectangle rect(60,60), rect2(60,60);
10360
10361 void psychlops_main() {
10362         
10363         Canvas display(Canvas::fullscreen);
10364         display.clear(Color::gray);
10365         rect.centering();
10366         
10367         Input::refresh();
10368         
10369         while(!Input::get(Keyboard::spc)){
10370                 oldcode=code;
10371                 code=-1;
10372                 display.clear(Color::gray);
10373                 
10374                 if(Input::get(Keyboard::c, Keyboard::pushed))code=0; //centering
10375                 
10376                 //state &quot;pressed&quot; and &quot;released&quot; Demo
10377                 if(Input::get(Mouse::left, Mouse::pressed))code=1;
10378                 else if(Input::get(Mouse::right, Mouse::pressed))code=2;
10379                 
10380                 switch(code){
10381                         case -1: break;
10382                         case 0: rect.centering(); break;
10383                         case 1: rect.shift(-1,0); break;
10384                         case 2: rect.shift(1,0); break;
10385                 }
10386                 
10387                 rect2.centering().draw(Psychlops::Color(0.75,0.75,0.75)); 
10388                 rect.draw(Psychlops::Color(1.0,1.0,1.0,0.25));
10389                 display.flip();
10390         }
10391 }
10392 }}}
10393 </pre>
10394 </div>
10395 <div title="例6: マウスのデモ" modifier="Psychlops_Admin" created="200708272056" changecount="1">
10396 <pre>{{{
10397 *例6 マウスデモ
10398 #include &lt;psychlops.h&gt;
10399 using namespace Psychlops;
10400
10401 double dcX,dcY, rectcol;
10402 double rectsize=80;
10403 int display_cursor=1, in_rect=0;
10404 Psychlops::Rectangle rect(rectsize*2,rectsize*2);
10405 Psychlops::Range rngx,rngy;
10406
10407 void psychlops_main() {
10408         
10409         Canvas display(Canvas::fullscreen);
10410         display.clear(Color::gray);
10411         AppState::setThreadPriority(AppState::HIGH);
10412         rect.centering();
10413         dcX= display.getHcenter();
10414         dcY= display.getVcenter();
10415         Input::refresh();
10416         
10417         //Move mouse cursor to the center 
10418         Mouse::x=dcX;
10419         Mouse::y=dcY;
10420         
10421         while(!Input::get(Keyboard::spc)){
10422                 display.clear(Color::gray);
10423                 rectcol=0.25;
10424                 //set current rect area
10425                 rect.getHcenter()-rectsize&lt;rngx&lt;rect.getHcenter()+rectsize;
10426                 rect.getVcenter()-rectsize&lt;rngy&lt;rect.getVcenter()+rectsize;
10427         
10428                 if(display_cursor)Mouse::show();
10429                         else Mouse::hide();
10430                 
10431                 if(Input::get(Mouse::right, Mouse::pushed))display_cursor=1-display_cursor;     
10432                 if(in_rect){
10433                         rect.centering(Mouse::x, Mouse::y);
10434                         rectcol=1.0;
10435                 }
10436                         
10437                 rect.draw(rectcol);
10438                 display.flip();
10439                 if(Input::get(Mouse::left, Mouse::pushed)){
10440                                 if (rngx.includes(Mouse::x) &amp;&amp; rngy.includes(Mouse::y))in_rect=1;
10441                                         else in_rect=0;
10442                 }
10443                 if(Input::get(Mouse::left, Mouse::released))    {
10444                                 if (rngx.includes(Mouse::x) &amp;&amp; rngy.includes(Mouse::y))in_rect=0;
10445                 }
10446         }
10447 }
10448 }}}</pre>
10449 </div>
10450 <div title="入力系のクラスに関して" modifier="Kazushi Maruya" modified="200712131742" created="200712131740" changecount="2">
10451 <pre>* [[キーボード/マウスの反応を取得したい|Input::get()]]
10452 * [[キーボード/マウスの反応をリセットしたい|Input::refresh()]]
10453 * [[マウスカーソルを表示したい|Mouse::show()]]
10454 * [[マウスカーソルを非表示にしたい|Mouse::hide()]]</pre>
10455 </div>
10456 <div title="基本的な命令の使用" modifier="Psychlops_DevelopperG" modified="200908100506" created="200712131619" changecount="5">
10457 <pre>* [[描画画面を取得したい|2.1 描画領域の宣言]]
10458 * [[取得した画面の情報を取得したい|2.1.3 Canvasのメンバ取得と変数の表示]]
10459 * [[画面を特定の色で塗りつぶしたい|2.1.2 Canvasの基本構造と操作]]
10460 * [[画面に点を打ちたい|2.2.1 画面上に点を描画する-Pointクラス1-]]
10461 * [[画面に四角形を書きたい|2.2.3 画面上に四角形を描画する-Rectangleクラス1-]]
10462
10463 * [[画面に円を描きたい|2.2.4 画面上に円を描画する]]
10464 * [[描画色を指定したい|2.2.5 描画する図形の色を指定する-Colorクラス-]]
10465 * [[リアルタイムでは描画できない複雑な図形描画を事前に計算したい|3. 複雑な描画を行う(オフスクリーンへの描画)]]
10466 * [[自然画像を読み込み・表示したい|3.3 画像ファイルの取り扱い]]
10467
10468 * [[図形をXX msecだけ提示したい|2.1.2 Canvasの基本構造と操作]]
10469 * [[時間を正確に計測したい|5.1 時間を計測する]]
10470
10471 * [[キーボードの入力を取得したい|4.1 外部装置(キーボード/マウス)の操作内容を取得する]]
10472 * [[マウス入力を取得したい|4.1 外部装置(キーボード/マウス)の操作内容を取得する]]
10473 * [[配列の中身をファイルに記録したい|4.2 ファイルの入出力]]
10474
10475 * [[特定の命令に対してコンパイル時にエラーが出る]]
10476 </pre>
10477 </div>
10478 <div title="数値計算" modifier="Kazushi Maruya" modified="200712131748" created="200712131747" changecount="2">
10479 <pre>*[[簡単な行列演算がしたい|5.3 行列演算を行う]]
10480 *[[配列の要素をランダムに並べ替えたい|Math::shuffle]]
10481 *[[負の値、浮動小数点型に対して剰余を求めたい|Math::mod]]</pre>
10482 </div>
10483 <div title="日本語化" modifier="Psychlops_Admin" modified="200709121644" created="200707220238" tags="systemConfig systemConfigForce" changecount="19">
10484 <pre>// Shadow tiddlers for emergencies
10485 config.shadowTiddlers.SideBarOptions = &quot;&lt;&lt;search&gt;&gt;&lt;&lt;closeAll&gt;&gt;&lt;&lt;permaview&gt;&gt;&lt;&lt;newTiddler&gt;&gt;&lt;&lt;saveChanges&gt;&gt;&lt;&lt;slider chkSliderOptionsPanel OptionsPanel 設定 'TiddlyWikiの設定を変更する。'&gt;&gt;&quot;;
10486 config.shadowTiddlers.OptionsPanel = &quot;これらの設定はご使用のブラウザ内に保存されます。\n\n署名として使用するあなたの名前をWikiWord(eg JoeBloggs)の形式で入力してください。\n\n&lt;&lt;option txtUserName&gt;&gt;\n&lt;&lt;option chkSaveBackups&gt;&gt; バックアップ取得\n&lt;&lt;option chkAutoSave&gt;&gt; 自動保存\n&lt;&lt;option chkGenerateAnRssFeed&gt;&gt; RSSファイル生成\n&lt;&lt;option chkRegExpSearch&gt;&gt; 正規表現による検索\n&lt;&lt;option chkCaseSensitiveSearch&gt;&gt; 英文字大小区別検索\n&lt;&lt;option chkAnimate&gt;&gt; アニメーション\n\n[[詳細な設定|AdvancedOptions]]&quot;;
10487 config.shadowTiddlers.AdvancedOptions = &quot;&lt;&lt;option chkOpenInNewWindow&gt;&gt; 新しいウィンドウでリンクを開く\n&lt;&lt;option chkSaveEmptyTemplate&gt;&gt; 空のテンプレートファイル(empty.html)を保存する\n&lt;&lt;option chkToggleLinks&gt;&gt; 既に開いているTiddlerをクリックした時に閉じる\n^^(override with Control or other modifier key)^^&quot;;
10488 config.shadowTiddlers.SideBarTabs = &quot;&lt;&lt;tabs txtMainTab '更新順' '更新順に表示する' TabTimeline 'タグ' 'タグ一覧' TabTags '詳細' '詳細' TabMore&gt;&gt;&quot;;
10489 config.shadowTiddlers.TabTimeline = &quot;&lt;&lt;timeline&gt;&gt;&quot;;
10490 config.shadowTiddlers.TabTags = &quot;&lt;&lt;allTags&gt;&gt;&quot;;
10491 config.shadowTiddlers.TabMore = &quot;&lt;&lt;tabs txtMoreTab '全部' 'すべてのTiddler' TabMoreAll '定義なし' '定義されていないTiddler一覧' TabMoreMissing 'リンク切れ' 'リンク切れしている単独のTiddler' TabMoreOrphans&gt;&gt;&quot;;
10492 config.shadowTiddlers.TabMoreAll = &quot;&lt;&lt;list all&gt;&gt;&quot;;
10493 config.shadowTiddlers.TabMoreMissing = &quot;&lt;&lt;list missing&gt;&gt;&quot;;
10494 config.shadowTiddlers.TabMoreOrphans = &quot;&lt;&lt;list orphans&gt;&gt;&quot;;
10495 // メッセージ
10496 config.messages.customConfigError = &quot;カスタムコンフィグにてエラー発生。 - %0&quot;;
10497 config.messages.savedSnapshotError = &quot;保存に失敗しました。&quot;;
10498 config.messages.subtitleUnknown = &quot;(不明)&quot;;
10499 config.messages.undefinedTiddlerToolTip = &quot;'%0'というTiddlerはまだ存在しません。&quot;;
10500 config.messages.externalLinkTooltip = &quot;(外部リンク) %0&quot;;
10501 config.messages.noTags = &quot;タグの設定されていないTiddler&quot;;
10502 config.messages.notFileUrlError = &quot;変更を保存したい場合、このTiddlyWikiをファイルに保存(ダウンロード)する必要があります。&quot;;
10503 config.messages.cantSaveError = &quot;このブラウザでは保存することができません。できればFirefoxを使ってください。&quot;;
10504 config.messages.invalidFileError = &quot;元のファイル '%0' は妥当なTiddlyWikiのファイルではありません。&quot;;
10505 config.messages.backupSaved = &quot;バックアップファイルを保存しました。&quot;;
10506 config.messages.backupFailed = &quot;バックアップファイルの保存に失敗しました。&quot;;
10507 config.messages.rssSaved = &quot;RSSファイルを保存しました。&quot;;
10508 config.messages.rssFailed = &quot;RSSファイルの保存に失敗しました。&quot;;
10509 config.messages.emptySaved = &quot;空のテンプレートファイルを保存しました。&quot;;
10510 config.messages.emptyFailed = &quot;空のテンプレートファイルの保存に失敗しました。&quot;;
10511 config.messages.mainSaved = &quot;TiddlyWikiファイルを保存しました。&quot;;
10512 config.messages.mainFailed = &quot;TiddlyWikiファイルの保存に失敗しました。修正内容は保存されていません。&quot;;
10513 config.messages.macroError = &quot;マクロ実行時エラー: '%0'&quot;;
10514 config.messages.overwriteWarning = &quot;'%0'というTiddlerはすでに存在します。OKを選択すると上書きします。&quot;;
10515 config.messages.unsavedChangesWarning = &quot;警告! 保存されていない変更が存在します。\n\nOKを選択:保存\nCancelを選択:編集内容を破棄&quot;;
10516 config.messages.months = [&quot;1月&quot;, &quot;2月&quot;, &quot;3月&quot;, &quot;4月&quot;, &quot;5月&quot;, &quot;6月&quot;, &quot;7月&quot;, &quot;8月&quot;, &quot;9月&quot;, &quot;10月&quot;, &quot;11月&quot;,&quot;12月&quot;];
10517 config.messages.dates.days   = [&quot;日&quot;, &quot;月&quot;, &quot;火&quot;, &quot;水&quot;, &quot;木&quot;, &quot;金&quot;, &quot;土&quot;];
10518 //  Tiddler表示時ツールバーなど
10519 config.views.wikified.tag.labelNoTags           = &quot;no tags&quot;;
10520 config.views.wikified.tag.labelTags             = &quot;タグ = &quot;;
10521 config.views.wikified.tag.tooltip               = &quot;タグ'%0'が設定されたTiddlerを閲覧する。&quot;;
10522 config.views.wikified.tag.openAllText           = &quot;タグ'%0'のTiddlerをすべて開く。&quot;;
10523 config.views.wikified.tag.openAllTooltip        = &quot;このTiddlerをすべて開く。&quot;;
10524 config.views.wikified.tag.popupNone             = &quot;タグ'%0'はこれ以外のTiddlerに設定されていません。&quot;;
10525 config.views.wikified.toolbarEdit          = &quot;編集&quot;;
10526 config.views.wikified.toolbarPermalink    = &quot;permalink&quot;;
10527 config.views.wikified.toolbarReferences   = &quot;参照一覧&quot;;
10528 config.views.wikified.toolbarReferences.popupNone = &quot;参照されていません。&quot;;
10529 config.views.wikified.defaultText               = &quot;'%0'はまだ存在していません。ダブルクリックで作成できます。&quot;;
10530 //  Tiddler編集時ツールバーなど
10531 config.views.editor.tagPrompt             = &quot;[[tags]]のスタイルでtagsをスペース区切りに入力します。&quot;;
10532 config.views.editor.tagChooser.text       = &quot;タグ&quot;;
10533 config.views.editor.tagChooser.tooltip    = &quot;既存のタグから追加するものを選択してください。&quot;;
10534 config.views.editor.tagChooser.popupNone  = &quot;タグが設定されていません。&quot;;
10535 config.views.editor.toolbarDone     = &quot;確定&quot;;
10536 config.views.editor.toolbarCancel   = &quot;編集中止&quot;;
10537 config.views.editor.toolbarDelete    = &quot;削除&quot;;
10538
10539 config.views.editor.defaultText           = &quot;'%0'の内容を入力してください。&quot;;
10540 // Each has a 'handler' member that is inserted later
10541 config.macros.search.label        = &quot;Wiki内検索&quot;;
10542 config.macros.search.prompt       = &quot;このTiddlyWiki内を検索します。&quot;
10543 config.macros.search.successMsg   = &quot;%0 件見つかりました。- %1&quot;;
10544 config.macros.search.failureMsg   = &quot;%0 に該当するデータはありません。&quot;;
10545 config.macros.timeline.dateFormat = &quot;YYYY年MM月DD日&quot;;
10546 config.macros.allTags.tooltip     = &quot;タグ'%0'のデータをすべて表示します。&quot;;
10547 config.macros.allTags.noTags      = &quot;タグが設定されていません。&quot;;
10548 config.macros.list.all.prompt     = &quot;アルファベット順のTiddler一覧&quot;;
10549 config.macros.list.missing.prompt = &quot;リンクはされているが定義されていないTiddler一覧&quot;;
10550 config.macros.list.orphans.prompt = &quot;他のどこからもリンクされていないTiddler一覧&quot;;
10551 config.macros.closeAll.label      = &quot;すべて閉じる&quot;;
10552 config.macros.closeAll.prompt     = &quot;編集されているもの以外の表示されているすべてTiddlerを閉じます。&quot;;
10553 config.macros.permaview.label     = &quot;permaview&quot;;
10554 config.macros.permaview.prompt    = &quot;現在表示されているTiddlerの状態を表示するURLです。&quot;
10555 config.macros.saveChanges.label   = &quot;保存&quot;;
10556 config.macros.saveChanges.prompt  = &quot;すべてのTiddlerを保存します。&quot;;
10557 config.macros.newTiddler.label    = &quot;新規作成&quot;;
10558 config.macros.newTiddler.Title    = &quot;新規Tiddler&quot;;
10559 config.macros.newTiddler.prompt   = &quot;新しいTiddlerを作成します。&quot;;
10560 config.macros.newJournal.label    = &quot;新規日報&quot;;
10561 config.macros.newJournal.prompt   = &quot;新しいTiddlerを現在の日時をタイトルとして作成します&quot;;</pre>
10562 </div>
10563 <div title="時間計測系のクラスに関して" modifier="Kazushi Maruya" created="200712131743" changecount="1">
10564 <pre>* [[現在のCPU時間を取得したい|Clock::update()]]
10565 * [[clock型の値をmsec単位で変数に代入したい|Clock::at_msec()]]</pre>
10566 </div>
10567 <div title="止まった絵を描画するときCanvas.flip()を単独でwhileループの中に書くと、画面がちらつきます。" modifier="Psychlops_DevelopperG" created="200908100500" changecount="1">
10568 <pre>Psychlopsの仕様として、描画はすべてダブルバッファリングの裏画面に行っています。
10569 このため、描画命令を書いた後に、単にCanvas.flip()命令だけを繰り返すと、2枚に1枚は何も描画されていない画面が表示されます。
10570 止まった絵を表示させたいときにこれを回避するには、2枚の画面の両方に同じ刺激を描画してください。</pre>
10571 </div>
10572 <div title="特定の命令に対してコンパイル時にエラーが出る" modifier="Psychlops_DevelopperG" modified="200908100456" created="200908100444" changecount="6">
10573 <pre>! 名前空間に関する問題
10574 Psychlopsで使っている命令をコンパイルするときに、ある特定のクラスについて大量のエラーが出ることがあります。
10575 とくに、変数の宣言自体についてエラーが出る場合のの原因の一つとして、ほかのライブラリ、特にOS・コンパイラ標準のライブラリにあるクラスとこの特定のクラスが同じ名前を持っているために、名前が衝突してしまってコンパイルができなくなっていることが考えられます。
10576 このような場合、以下のように スコープ演算子 &quot;Psychlops::&quot; をクラス名の前に付け加えることで問題を解決できます。
10577
10578
10579 !!! (1-1) Macintoshでコンパイル時に、Point型を使った命令すべてに対してエラーが出ます。
10580
10581 Macintoshでは標準ライブラリにPoint型がすでにあるために、名前が衝突してしまい、コンパイルができません。スコープ演算子Psychlops::を使って、衝突を回避してください。
10582 {{{
10583  (コンパイル不可)Point A[n]
10584   (コンパイル可) Psychlops::Point A[n]
10585 }}}
10586
10587 !!! (1-2) Windowsでコンパイル時に、Rectangle型を使った命令すべてに対してエラーが出ます。
10588
10589 (1-1)と同様にWindowsでは標準ライブラリにRectangle型がすでにあるために、名前が衝突してしまい、コンパイルができません。スコープ演算子Psychlops::を使って、衝突を回避してください。
10590 {{{
10591  (コンパイル不可)Rectangle A[n]
10592   (コンパイル可) Psychlops::Rectangle A[n]
10593 }}}
10594
10595 !!! (1-3) random()命令に対してエラーが出ます。
10596
10597 スコープ演算子Psychlops::を使って、衝突を回避してください。
10598 {{{
10599 (コンパイル不可)random()
10600 (コンパイル可) Psychlops::random()
10601 }}}
10602
10603 名前空間の問題についての詳細は、[[OS環境による関数名の衝突について]]をご覧ください。
10604 </pre>
10605 </div>
10606 <div title="特定の種類の刺激描画" modifier="YourName" modified="200802191333" created="200712131629" changecount="3">
10607 <pre>[[ランダムドットを書きたい|3.2 オフスクリーンを用いた描画例-ランダムドットパターン-]]
10608 [[ダイナミックランダムドットを書きたい|Psychlops::random]]
10609 [[一定方向に運動するランダムドットを書きたい|3.2.3 ランダムドットキネマトグラムの描画]]
10610 [[ランダムドットステレオグラムを書きたい|Tips: ランダムドットステレオグラムの描画コード]]
10611
10612 [[垂直・水平方位の正弦縞を書きたい|2.2.6 画面上に縞を描画する-Rectangleクラス2-]]
10613 [[任意の方位の正弦縞を書きたい|2.2.7 画面上に縞を描画する-Pointクラス2-]]
10614 [[運動する縞を書きたい|2.2.8 運動する図形を描画する-Rectangleクラス3-]]
10615 [[Plaid刺激を書きたい|2.2.7 画面上に縞を描画する-Pointクラス2-]]
10616 [[Gabor Patchを書きたい|6.2 Gaborパッチの事前描画]]
10617
10618 [[インタラクティブなデモプログラムを作成したい|7. デモを作成する]]</pre>
10619 </div>
10620 <div title="第II部 Psychlopsの応用" modifier="Psychlops_DevelopperG" modified="200909150151" created="200908100512" changecount="2">
10621 <pre>[[1. 既成クラスを用いた簡単な実験プログラムを作成する]]
10622   [[1.1 実験計画のプログラミング]] 
10623   [[1.2 Gaborパッチの事前描画]] 
10624   [[1.3 各試行部分のプログラミング]] 
10625   [[1.4 終了処理とまとめ]]
10626
10627 [[2. デモを作成する]]
10628   [[2.1 独立変数Independent]] 
10629   [[2.2 デモ環境の表示]] </pre>
10630 </div>
10631 <div title="第I部 Psychlopsの基本" modifier="Psychlops_DevelopperG" modified="200908190223" created="200908100511" changecount="3">
10632 <pre>[[1. インストールを行う]]
10633   [[1.1 必要な環境]] 
10634     [[1.1.1 ハードウェア環境]]
10635     [[1.1.2 ソフトウェア環境]]
10636   [[1.2 インストール作業(Mac) ]]
10637   [[1.3 インストール作業(Windows) ]]
10638   [[1.4 更新とアンインストール]] 
10639   [[1.5 Psychlopsの基本構造]] 
10640   [[1.6 Psychlopsを使ったプログラミング]]
10641     [[1.6.1 プログラミングとは?]]
10642     [[1.6.2 Psychlopsを使ったプログラムの形]]
10643     [[1.6.3 配列]]
10644     [[1.6.4 for命令]]
10645     [[1.6.5 変数型]]
10646
10647 [[2. 基本的な描画を行う]]
10648   [[2.1 描画領域の宣言]]
10649     [[2.1.1 Canvasの宣言]]
10650     [[2.1.2 Canvasの基本構造と操作]]
10651     [[2.1.3 Canvasのメンバ取得と変数の表示]]         
10652   [[2.2 図形描画のコマンド]]
10653     [[2.2.1 画面上に点を描画する-Pointクラス1-]]           
10654     [[2.2.2 画面上に線を描画する]]           
10655     [[2.2.3 画面上に四角形を描画する-Rectangleクラス1-]]            
10656     [[2.2.4 画面上に円を描画する]]            
10657     [[2.2.5 描画する図形の色を指定する-Colorクラス-]]           
10658     [[2.2.6 画面上に縞を描画する-Rectangleクラス2-]]
10659     [[2.2.7 画面上に縞を描画する-Pointクラス2-]] 
10660     [[2.2.8 運動する図形を描画する-Rectangleクラス3-]] 
10661            
10662 [[3. 複雑な描画を行う1(オフスクリーンへの描画)]]
10663   [[3.1 オフスクリーン描画の基本]]
10664     [[3.1.1 オフスクリーン領域の宣言]]
10665     [[3.1.2 オフスクリーン上に点・四角形を描画する]] 
10666     [[3.1.3 オフスクリーン上の任意の位置に描画する]] 
10667     [[3.1.4 発展的な内容]]
10668   [[3.2 オフスクリーンを用いた描画例-ランダムドットパターン-]]
10669     [[3.2.1 基本的なランダムドットを描画する]]
10670     [[3.2.2 ドットサイズの異なるランダムドットパターンの描画]]
10671     [[3.2.3 ランダムドットキネマトグラムの描画]]
10672   [[3.3 画像ファイルの取り扱い]]
10673     [[3.3.1 画像ファイルを保存する]]
10674     [[3.3.2 画像ファイルを読み込み表示する]] 
10675   [[3.4 Canvasのオフスクリーンへの取り込み]]
10676
10677 [[5. 入出力を行う]]
10678   [[5.1 外部装置(キーボード/マウス)の操作内容を取得する]] 
10679   [[5.2 ファイルの入出力]]
10680
10681 [[6. 時間制御と各種ツールを使用する]]
10682   [[6.1 時間を計測する]] 
10683   [[6.2 各種ツールを使ってみる]] 
10684     [[6.2.1 描画の時間精度を確認する-FPSチェッカー]]
10685     [[6.2.2 画面に反映されない描画の進行度を表示する-プログレスバー]]
10686   [[6.3 行列演算を行う]]
10687     [[6.3.1 行列の宣言]]
10688     [[6.3.2 行列の要素の操作]]
10689     [[6.3.3 行列全体の操作]]
10690     [[6.3.4 行列の加減乗除]]
10691     [[6.3.5 行列の中身をオフスクリーンに描画する]]
10692     [[6.3.6 メッシュグリッドの作成]]</pre>
10693 </div>
10694 <div title="素晴らしいデモプログラムを作成したので、公開したい" modifier="Kazushi Maruya" modified="200712180151" created="200712131803" changecount="3">
10695 <pre>[[Visiome Platform|http://platform.visiome.neuroinf.jp]]には様々なデモンストレーションが公開されています。
10696 Psychlopsによるデモプログラムも多数公開されておりますので、是非ご参加ください。
10697 (現在の所このサイトにアップロードするためのアカウント取得には制限があります。詳しくはVisiomeのホームページをご覧下さい。)</pre>
10698 </div>
10699 <div title="関数一覧_FunctionList" modifier="Psychlops_DevelopperG" modified="200910080818" created="200707220316" changecount="22">
10700 <pre>*Graphic
10701 **[[Psychlops::Canvas]]
10702 **[[Psychlops::Rectangle]]
10703 **[[Psychlops::Point]]
10704 **[[Psychlops::Image]]
10705 **[[Psychlops::Color]]
10706 *Devices
10707 **[[Psychlops::Input]]
10708 **[[Psychlops::Mouse]]
10709 **[[Psychlops::Clock]]
10710 *Matrix
10711 **[[Psychlops::Matrix]]
10712 **[[Psychlops::Math]]</pre>
10713 </div>
10714 <div title="関数逆引きとQ&amp;A" modifier="Kazushi Maruya" modified="200712131749" created="200712131608" changecount="1">
10715 <pre>*[[Canvas型に関して]] 
10716 *[[Rectangle型に関して]] 
10717 *[[Point型に関して]]
10718 *[[Image型に関して]]
10719 *[[Color型に関して]]
10720 *[[入力系のクラスに関して]]
10721 *[[時間計測系のクラスに関して]]
10722
10723 [[基本的な命令の使用]]
10724 [[特定の種類の刺激描画]]
10725 [[数値計算]]
10726 [[その他]]</pre>
10727 </div>
10728 </div>
10729 <!--POST-STOREAREA-->
10730 <!--POST-BODY-START-->
10731 <!--POST-BODY-END-->
10732 <script type="text/javascript">
10733 //<![CDATA[
10734 //
10735 // Please note:
10736 // 
10737 // * This code is designed to be readable but for compactness it only includes brief comments. You can see fuller comments
10738 //   in the project Subversion repository at http://svn.tiddlywiki.org/Trunk/core/
10739 //
10740 // * You should never need to modify this source code directly. TiddlyWiki is carefully designed to allow deep customisation
10741 //   without changing the core code. Please consult the development group at http://groups.google.com/group/TiddlyWikiDev
10742 // 
10743
10744 //--
10745 //-- Configuration repository
10746 //--
10747
10748 // Miscellaneous options
10749 var config = {
10750         numRssItems: 20, // Number of items in the RSS feed
10751         animDuration: 400, // Duration of UI animations in milliseconds
10752         cascadeFast: 20, // Speed for cascade animations (higher == slower)
10753         cascadeSlow: 60, // Speed for EasterEgg cascade animations
10754         cascadeDepth: 5 // Depth of cascade animation
10755 };
10756
10757 // Adaptors
10758 config.adaptors = {};
10759
10760 // Backstage tasks
10761 config.tasks = {};
10762
10763 // Annotations
10764 config.annotations = {};
10765
10766 // Custom fields to be automatically added to new tiddlers
10767 config.defaultCustomFields = {};
10768
10769 // Messages
10770 config.messages = {
10771         messageClose: {},
10772         dates: {},
10773         tiddlerPopup: {}
10774 };
10775
10776 // Options that can be set in the options panel and/or cookies
10777 config.options = {
10778         chkRegExpSearch: false,
10779         chkCaseSensitiveSearch: false,
10780         chkAnimate: true,
10781         chkSaveBackups: true,
10782         chkAutoSave: false,
10783         chkGenerateAnRssFeed: false,
10784         chkSaveEmptyTemplate: false,
10785         chkOpenInNewWindow: true,
10786         chkToggleLinks: false,
10787         chkHttpReadOnly: true,
10788         chkForceMinorUpdate: false,
10789         chkConfirmDelete: true,
10790         chkInsertTabs: false,
10791         chkUsePreForStorage: true, // Whether to use <pre> format for storage
10792         chkDisplayStartupTime: false,
10793         txtBackupFolder: "",
10794         txtMainTab: "tabTimeline",
10795         txtMoreTab: "moreTabAll",
10796         txtMaxEditRows: "30",
10797         txtFileSystemCharSet: "UTF-8"
10798         };
10799 config.optionsDesc = {};
10800         
10801 // List of notification functions to be called when certain tiddlers are changed or deleted
10802 config.notifyTiddlers = [
10803         {name: "StyleSheetLayout", notify: refreshStyles},
10804         {name: "StyleSheetColors", notify: refreshStyles},
10805         {name: "StyleSheet", notify: refreshStyles},
10806         {name: "StyleSheetPrint", notify: refreshStyles},
10807         {name: "PageTemplate", notify: refreshPageTemplate},
10808         {name: "SiteTitle", notify: refreshPageTitle},
10809         {name: "SiteSubtitle", notify: refreshPageTitle},
10810         {name: "ColorPalette", notify: refreshColorPalette},
10811         {name: null, notify: refreshDisplay}
10812 ];
10813
10814 // Default tiddler templates
10815 var DEFAULT_VIEW_TEMPLATE = 1;
10816 var DEFAULT_EDIT_TEMPLATE = 2;
10817 config.tiddlerTemplates = {
10818         1: "ViewTemplate",
10819         2: "EditTemplate"
10820 };
10821
10822 // More messages (rather a legacy layout that shouldn't really be like this)
10823 config.views = {
10824         wikified: {
10825                 tag: {}
10826         },
10827         editor: {
10828                 tagChooser: {}
10829         }
10830 };
10831
10832 // Backstage tasks
10833 config.backstageTasks = ["save","sync","importTask","tweak","plugins"];
10834
10835 // Macros; each has a 'handler' member that is inserted later
10836 config.macros = {
10837         today: {},
10838         version: {},
10839         search: {sizeTextbox: 15},
10840         tiddler: {},
10841         tag: {},
10842         tags: {},
10843         tagging: {},
10844         timeline: {},
10845         allTags: {},
10846         list: {
10847                 all: {},
10848                 missing: {},
10849                 orphans: {},
10850                 shadowed: {},
10851                 touched: {}
10852         },
10853         closeAll: {},
10854         permaview: {},
10855         saveChanges: {},
10856         slider: {},
10857         option: {},
10858         options: {},
10859         newTiddler: {},
10860         newJournal: {},
10861         sparkline: {},
10862         tabs: {},
10863         gradient: {},
10864         message: {},
10865         view: {},
10866         edit: {},
10867         tagChooser: {},
10868         toolbar: {},
10869         br: {},
10870         plugins: {},
10871         refreshDisplay: {},
10872         importTiddlers: {},
10873         sync: {},
10874         annotations: {}
10875 };
10876
10877 // Commands supported by the toolbar macro
10878 config.commands = {
10879         closeTiddler: {},
10880         closeOthers: {},
10881         editTiddler: {},
10882         saveTiddler: {hideReadOnly: true},
10883         cancelTiddler: {},
10884         deleteTiddler: {hideReadOnly: true},
10885         permalink: {},
10886         references: {type: "popup"},
10887         jump: {type: "popup"},
10888         syncing: {type: "popup"},
10889         fields: {type: "popup"}
10890 };
10891
10892 // Browser detection... In a very few places, there's nothing else for it but to know what browser we're using.
10893 config.userAgent = navigator.userAgent.toLowerCase();
10894 config.browser = {
10895         isIE: config.userAgent.indexOf("msie") != -1 && config.userAgent.indexOf("opera") == -1,
10896         isGecko: config.userAgent.indexOf("gecko") != -1,
10897         ieVersion: /MSIE (\d.\d)/i.exec(config.userAgent), // config.browser.ieVersion[1], if it exists, will be the IE version string, eg "6.0"
10898         isSafari: config.userAgent.indexOf("applewebkit") != -1,
10899         isBadSafari: !((new RegExp("[\u0150\u0170]","g")).test("\u0150")),
10900         firefoxDate: /gecko\/(\d{8})/i.exec(config.userAgent), // config.browser.firefoxDate[1], if it exists, will be Firefox release date as "YYYYMMDD"
10901         isOpera: config.userAgent.indexOf("opera") != -1,
10902         isLinux: config.userAgent.indexOf("linux") != -1,
10903         isUnix: config.userAgent.indexOf("x11") != -1,
10904         isMac: config.userAgent.indexOf("mac") != -1,
10905         isWindows: config.userAgent.indexOf("win") != -1
10906 };
10907
10908 // Basic regular expressions
10909 config.textPrimitives = {
10910         upperLetter: "[A-Z\u00c0-\u00de\u0150\u0170]",
10911         lowerLetter: "[a-z0-9_\\-\u00df-\u00ff\u0151\u0171]",
10912         anyLetter:   "[A-Za-z0-9_\\-\u00c0-\u00de\u00df-\u00ff\u0150\u0170\u0151\u0171]",
10913         anyLetterStrict: "[A-Za-z0-9\u00c0-\u00de\u00df-\u00ff\u0150\u0170\u0151\u0171]"
10914 };
10915 if(config.browser.isBadSafari) {
10916         config.textPrimitives = {
10917                 upperLetter: "[A-Z\u00c0-\u00de]",
10918                 lowerLetter: "[a-z0-9_\\-\u00df-\u00ff]",
10919                 anyLetter:   "[A-Za-z0-9_\\-\u00c0-\u00de\u00df-\u00ff]",
10920                 anyLetterStrict: "[A-Za-z0-9\u00c0-\u00de\u00df-\u00ff]"
10921         };
10922 }
10923 config.textPrimitives.sliceSeparator = "::";
10924 config.textPrimitives.urlPattern = "[a-z]{3,8}:[^\\s:'\"][^\\s'\"]*(?:/|\\b)";
10925 config.textPrimitives.unWikiLink = "~";
10926 config.textPrimitives.wikiLink = "(?:(?:" + config.textPrimitives.upperLetter + "+" +
10927         config.textPrimitives.lowerLetter + "+" +
10928         config.textPrimitives.upperLetter +
10929         config.textPrimitives.anyLetter + "*)|(?:" +
10930         config.textPrimitives.upperLetter + "{2,}" +
10931         config.textPrimitives.lowerLetter + "+))";
10932
10933 config.textPrimitives.cssLookahead = "(?:(" + config.textPrimitives.anyLetter + "+)\\(([^\\)\\|\\n]+)(?:\\):))|(?:(" + config.textPrimitives.anyLetter + "+):([^;\\|\\n]+);)";
10934 config.textPrimitives.cssLookaheadRegExp = new RegExp(config.textPrimitives.cssLookahead,"mg");
10935
10936 config.textPrimitives.brackettedLink = "\\[\\[([^\\]]+)\\]\\]";
10937 config.textPrimitives.titledBrackettedLink = "\\[\\[([^\\[\\]\\|]+)\\|([^\\[\\]\\|]+)\\]\\]";
10938 config.textPrimitives.tiddlerForcedLinkRegExp = new RegExp("(?:" + config.textPrimitives.titledBrackettedLink + ")|(?:" +
10939         config.textPrimitives.brackettedLink + ")|(?:" + 
10940         config.textPrimitives.urlPattern + ")","mg");
10941 config.textPrimitives.tiddlerAnyLinkRegExp = new RegExp("("+ config.textPrimitives.wikiLink + ")|(?:" +
10942         config.textPrimitives.titledBrackettedLink + ")|(?:" +
10943         config.textPrimitives.brackettedLink + ")|(?:" +
10944         config.textPrimitives.urlPattern + ")","mg");
10945
10946 config.glyphs = {
10947         browsers: [
10948                 function() {return config.browser.isIE;},
10949                 function() {return true}
10950         ],
10951         currBrowser: null,
10952         codes: {
10953                 downTriangle: ["\u25BC","\u25BE"],
10954                 downArrow: ["\u2193","\u2193"],
10955                 bentArrowLeft: ["\u2190","\u21A9"],
10956                 bentArrowRight: ["\u2192","\u21AA"]
10957         }
10958 };
10959
10960 //--
10961 //-- Shadow tiddlers
10962 //--
10963
10964 config.shadowTiddlers = {
10965         StyleSheet: "",
10966         MarkupPreHead: "<!--{{{-->\n<link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml'/>\n<!--}}}-->",
10967         MarkupPostHead: "",
10968         MarkupPreBody: "",
10969         MarkupPostBody: "",
10970         TabTimeline: '<<timeline>>',
10971         TabAll: '<<list all>>',
10972         TabTags: '<<allTags excludeLists>>',
10973         TabMoreMissing: '<<list missing>>',
10974         TabMoreOrphans: '<<list orphans>>',
10975         TabMoreShadowed: '<<list shadowed>>',
10976         AdvancedOptions: '<<options>>',
10977         PluginManager: '<<plugins>>',
10978         ImportTiddlers: '<<importTiddlers>>'
10979 };
10980
10981 //--
10982 //-- Translateable strings
10983 //--
10984
10985 // Strings in "double quotes" should be translated; strings in 'single quotes' should be left alone
10986
10987 merge(config.options,{
10988         txtUserName: "YourName"});
10989
10990 merge(config.tasks,{
10991         save: {text: "save", tooltip: "Save your changes to this TiddlyWiki", action: saveChanges},
10992         sync: {text: "sync", tooltip: "Synchronise changes with other TiddlyWiki files and servers", content: '<<sync>>'},
10993         importTask: {text: "import", tooltip: "Import tiddlers and plugins from other TiddlyWiki files and servers", content: '<<importTiddlers>>'},
10994         tweak: {text: "tweak", tooltip: "Tweak the appearance and behaviour of TiddlyWiki", content: '<<options>>'},
10995         plugins: {text: "plugins", tooltip: "Manage installed plugins", content: '<<plugins>>'}
10996 });
10997
10998 // Options that can be set in the options panel and/or cookies
10999 merge(config.optionsDesc,{
11000         txtUserName: "Username for signing your edits",
11001         chkRegExpSearch: "Enable regular expressions for searches",
11002         chkCaseSensitiveSearch: "Case-sensitive searching",
11003         chkAnimate: "Enable animations",
11004         chkSaveBackups: "Keep backup file when saving changes",
11005         chkAutoSave: "Automatically save changes",
11006         chkGenerateAnRssFeed: "Generate an RSS feed when saving changes",
11007         chkSaveEmptyTemplate: "Generate an empty template when saving changes",
11008         chkOpenInNewWindow: "Open external links in a new window",
11009         chkToggleLinks: "Clicking on links to open tiddlers causes them to close",
11010         chkHttpReadOnly: "Hide editing features when viewed over HTTP",
11011         chkForceMinorUpdate: "Don't update modifier username and date when editing tiddlers",
11012         chkConfirmDelete: "Require confirmation before deleting tiddlers",
11013         chkInsertTabs: "Use the tab key to insert tab characters instead of moving between fields",
11014         txtBackupFolder: "Name of folder to use for backups",
11015         txtMaxEditRows: "Maximum number of rows in edit boxes",
11016         txtFileSystemCharSet: "Default character set for saving changes (Firefox/Mozilla only)"});
11017
11018 merge(config.messages,{
11019         customConfigError: "Problems were encountered loading plugins. See PluginManager for details",
11020         pluginError: "Error: %0",
11021         pluginDisabled: "Not executed because disabled via 'systemConfigDisable' tag",
11022         pluginForced: "Executed because forced via 'systemConfigForce' tag",
11023         pluginVersionError: "Not executed because this plugin needs a newer version of TiddlyWiki",
11024         nothingSelected: "Nothing is selected. You must select one or more items first",
11025         savedSnapshotError: "It appears that this TiddlyWiki has been incorrectly saved. Please see http://www.tiddlywiki.com/#DownloadSoftware for details",
11026         subtitleUnknown: "(unknown)",
11027         undefinedTiddlerToolTip: "The tiddler '%0' doesn't yet exist",
11028         shadowedTiddlerToolTip: "The tiddler '%0' doesn't yet exist, but has a pre-defined shadow value",
11029         tiddlerLinkTooltip: "%0 - %1, %2",
11030         externalLinkTooltip: "External link to %0",
11031         noTags: "There are no tagged tiddlers",
11032         notFileUrlError: "You need to save this TiddlyWiki to a file before you can save changes",
11033         cantSaveError: "It's not possible to save changes. Possible reasons include:\n- your browser doesn't support saving (Firefox, Internet Explorer, Safari and Opera all work if properly configured)\n- the pathname to your TiddlyWiki file contains illegal characters\n- the TiddlyWiki HTML file has been moved or renamed",
11034         invalidFileError: "The original file '%0' does not appear to be a valid TiddlyWiki",
11035         backupSaved: "Backup saved",
11036         backupFailed: "Failed to save backup file",
11037         rssSaved: "RSS feed saved",
11038         rssFailed: "Failed to save RSS feed file",
11039         emptySaved: "Empty template saved",
11040         emptyFailed: "Failed to save empty template file",
11041         mainSaved: "Main TiddlyWiki file saved",
11042         mainFailed: "Failed to save main TiddlyWiki file. Your changes have not been saved",
11043         macroError: "Error in macro <<\%0>>",
11044         macroErrorDetails: "Error while executing macro <<\%0>>:\n%1",
11045         missingMacro: "No such macro",
11046         overwriteWarning: "A tiddler named '%0' already exists. Choose OK to overwrite it",
11047         unsavedChangesWarning: "WARNING! There are unsaved changes in TiddlyWiki\n\nChoose OK to save\nChoose CANCEL to discard",
11048         confirmExit: "--------------------------------\n\nThere are unsaved changes in TiddlyWiki. If you continue you will lose those changes\n\n--------------------------------",
11049         saveInstructions: "SaveChanges",
11050         unsupportedTWFormat: "Unsupported TiddlyWiki format '%0'",
11051         tiddlerSaveError: "Error when saving tiddler '%0'",
11052         tiddlerLoadError: "Error when loading tiddler '%0'",
11053         wrongSaveFormat: "Cannot save with storage format '%0'. Using standard format for save.",
11054         invalidFieldName: "Invalid field name %0",
11055         fieldCannotBeChanged: "Field '%0' cannot be changed",
11056         loadingMissingTiddler: "Attempting to retrieve the tiddler '%0' from the '%1' server at:\n\n'%2' in the workspace '%3'"});
11057
11058 merge(config.messages.messageClose,{
11059         text: "close",
11060         tooltip: "close this message area"});
11061
11062 config.messages.backstage = {
11063         open: {text: "backstage", tooltip: "Open the backstage area to perform authoring and editing tasks"},
11064         close: {text: "close", tooltip: "Close the backstage area"},
11065         prompt: "backstage: ",
11066         decal: {
11067                 edit: {text: "edit", tooltip: "Edit the tiddler '%0'"}
11068         }
11069 };
11070
11071 config.messages.listView = {
11072         tiddlerTooltip: "Click for the full text of this tiddler",
11073         previewUnavailable: "(preview not available)"
11074 };
11075
11076 config.messages.dates.months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November","December"];
11077 config.messages.dates.days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
11078 config.messages.dates.shortMonths = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
11079 config.messages.dates.shortDays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
11080 // suffixes for dates, eg "1st","2nd","3rd"..."30th","31st"
11081 config.messages.dates.daySuffixes = ["st","nd","rd","th","th","th","th","th","th","th",
11082                 "th","th","th","th","th","th","th","th","th","th",
11083                 "st","nd","rd","th","th","th","th","th","th","th",
11084                 "st"];
11085 config.messages.dates.am = "am";
11086 config.messages.dates.pm = "pm";
11087
11088 merge(config.messages.tiddlerPopup,{
11089         });
11090
11091 merge(config.views.wikified.tag,{
11092         labelNoTags: "no tags",
11093         labelTags: "tags: ",
11094         openTag: "Open tag '%0'",
11095         tooltip: "Show tiddlers tagged with '%0'",
11096         openAllText: "Open all",
11097         openAllTooltip: "Open all of these tiddlers",
11098         popupNone: "No other tiddlers tagged with '%0'"});
11099
11100 merge(config.views.wikified,{
11101         defaultText: "The tiddler '%0' doesn't yet exist. Double-click to create it",
11102         defaultModifier: "(missing)",
11103         shadowModifier: "(built-in shadow tiddler)",
11104         dateFormat: "DD MMM YYYY",
11105         createdPrompt: "created"});
11106
11107 merge(config.views.editor,{
11108         tagPrompt: "Type tags separated with spaces, [[use double square brackets]] if necessary, or add existing",
11109         defaultText: "Type the text for '%0'"});
11110
11111 merge(config.views.editor.tagChooser,{
11112         text: "tags",
11113         tooltip: "Choose existing tags to add to this tiddler",
11114         popupNone: "There are no tags defined",
11115         tagTooltip: "Add the tag '%0'"});
11116
11117 merge(config.messages,{
11118         sizeTemplates:
11119                 [
11120                 {unit: 1024*1024*1024, template: "%0\u00a0GB"},
11121                 {unit: 1024*1024, template: "%0\u00a0MB"},
11122                 {unit: 1024, template: "%0\u00a0KB"},
11123                 {unit: 1, template: "%0\u00a0B"}
11124                 ]});
11125
11126 merge(config.macros.search,{
11127         label: "search",
11128         prompt: "Search this TiddlyWiki",
11129         accessKey: "F",
11130         successMsg: "%0 tiddlers found matching %1",
11131         failureMsg: "No tiddlers found matching %0"});
11132
11133 merge(config.macros.tagging,{
11134         label: "tagging: ",
11135         labelNotTag: "not tagging",
11136         tooltip: "List of tiddlers tagged with '%0'"});
11137
11138 merge(config.macros.timeline,{
11139         dateFormat: "DD MMM YYYY"});
11140
11141 merge(config.macros.allTags,{
11142         tooltip: "Show tiddlers tagged with '%0'",
11143         noTags: "There are no tagged tiddlers"});
11144
11145 config.macros.list.all.prompt = "All tiddlers in alphabetical order";
11146 config.macros.list.missing.prompt = "Tiddlers that have links to them but are not defined";
11147 config.macros.list.orphans.prompt = "Tiddlers that are not linked to from any other tiddlers";
11148 config.macros.list.shadowed.prompt = "Tiddlers shadowed with default contents";
11149 config.macros.list.touched.prompt = "Tiddlers that have been modified locally";
11150
11151 merge(config.macros.closeAll,{
11152         label: "close all",
11153         prompt: "Close all displayed tiddlers (except any that are being edited)"});
11154
11155 merge(config.macros.permaview,{
11156         label: "permaview",
11157         prompt: "Link to an URL that retrieves all the currently displayed tiddlers"});
11158
11159 merge(config.macros.saveChanges,{
11160         label: "save changes",
11161         prompt: "Save all tiddlers to create a new TiddlyWiki",
11162         accessKey: "S"});
11163
11164 merge(config.macros.newTiddler,{
11165         label: "new tiddler",
11166         prompt: "Create a new tiddler",
11167         title: "New Tiddler",
11168         accessKey: "N"});
11169
11170 merge(config.macros.newJournal,{
11171         label: "new journal",
11172         prompt: "Create a new tiddler from the current date and time",
11173         accessKey: "J"});
11174
11175 merge(config.macros.options,{
11176         wizardTitle: "Tweak advanced options",
11177         step1Title: "These options are saved in cookies in your browser",
11178         step1Html: "<input type='hidden' name='markList'></input><br><input type='checkbox' checked='false' name='chkUnknown'>Show unknown options</input>",
11179         unknownDescription: "//(unknown)//",
11180         listViewTemplate: {
11181                 columns: [
11182                         {name: 'Option', field: 'option', title: "Option", type: 'String'},
11183                         {name: 'Description', field: 'description', title: "Description", type: 'WikiText'},
11184                         {name: 'Name', field: 'name', title: "Name", type: 'String'}
11185                         ],
11186                 rowClasses: [
11187                         {className: 'lowlight', field: 'lowlight'} 
11188                         ]}
11189         });
11190
11191 merge(config.macros.plugins,{
11192         wizardTitle: "Manage plugins",
11193         step1Title: "Currently loaded plugins",
11194         step1Html: "<input type='hidden' name='markList'></input>", // DO NOT TRANSLATE
11195         skippedText: "(This plugin has not been executed because it was added since startup)",
11196         noPluginText: "There are no plugins installed",
11197         confirmDeleteText: "Are you sure you want to delete these plugins:\n\n%0",
11198         removeLabel: "remove systemConfig tag",
11199         removePrompt: "Remove systemConfig tag",
11200         deleteLabel: "delete",
11201         deletePrompt: "Delete these tiddlers forever",
11202         listViewTemplate: {
11203                 columns: [
11204                         {name: 'Selected', field: 'Selected', rowName: 'title', type: 'Selector'},
11205                         {name: 'Tiddler', field: 'tiddler', title: "Tiddler", type: 'Tiddler'},
11206                         {name: 'Size', field: 'size', tiddlerLink: 'size', title: "Size", type: 'Size'},
11207                         {name: 'Forced', field: 'forced', title: "Forced", tag: 'systemConfigForce', type: 'TagCheckbox'},
11208                         {name: 'Disabled', field: 'disabled', title: "Disabled", tag: 'systemConfigDisable', type: 'TagCheckbox'},
11209                         {name: 'Executed', field: 'executed', title: "Loaded", type: 'Boolean', trueText: "Yes", falseText: "No"},
11210                         {name: 'Startup Time', field: 'startupTime', title: "Startup Time", type: 'String'},
11211                         {name: 'Error', field: 'error', title: "Status", type: 'Boolean', trueText: "Error", falseText: "OK"},
11212                         {name: 'Log', field: 'log', title: "Log", type: 'StringList'}
11213                         ],
11214                 rowClasses: [
11215                         {className: 'error', field: 'error'},
11216                         {className: 'warning', field: 'warning'}
11217                         ]}
11218         });
11219
11220 merge(config.macros.toolbar,{
11221         moreLabel: "more",
11222         morePrompt: "Reveal further commands"
11223         });
11224
11225 merge(config.macros.refreshDisplay,{
11226         label: "refresh",
11227         prompt: "Redraw the entire TiddlyWiki display"
11228         });
11229
11230 merge(config.macros.importTiddlers,{
11231         readOnlyWarning: "You cannot import into a read-only TiddlyWiki file. Try opening it from a file:// URL",
11232         wizardTitle: "Import tiddlers from another file or server",
11233         step1Title: "Step 1: Locate the server or TiddlyWiki file",
11234         step1Html: "Specify the type of the server: <select name='selTypes'><option value=''>Choose...</option></select><br>Enter the URL or pathname here: <input type='text' size=50 name='txtPath'><br>...or browse for a file: <input type='file' size=50 name='txtBrowse'><br><hr>...or select a pre-defined feed: <select name='selFeeds'><option value=''>Choose...</option></select>",
11235         openLabel: "open",
11236         openPrompt: "Open the connection to this file or server",
11237         openError: "There were problems fetching the tiddlywiki file",
11238         statusOpenHost: "Opening the host",
11239         statusGetWorkspaceList: "Getting the list of available workspaces",
11240         step2Title: "Step 2: Choose the workspace",
11241         step2Html: "Enter a workspace name: <input type='text' size=50 name='txtWorkspace'><br>...or select a workspace: <select name='selWorkspace'><option value=''>Choose...</option></select>",
11242         cancelLabel: "cancel",
11243         cancelPrompt: "Cancel this import",
11244         statusOpenWorkspace: "Opening the workspace",
11245         statusGetTiddlerList: "Getting the list of available tiddlers",
11246         step3Title: "Step 3: Choose the tiddlers to import",
11247         step3Html: "<input type='hidden' name='markList'></input><br><input type='checkbox' checked='true' name='chkSync'>Keep these tiddlers linked to this server so that you can synchronise subsequent changes</input><br><input type='checkbox' name='chkSave'>Save the details of this server in a 'systemServer' tiddler called:</input> <input type='text' size=25 name='txtSaveTiddler'>",
11248         importLabel: "import",
11249         importPrompt: "Import these tiddlers",
11250         confirmOverwriteText: "Are you sure you want to overwrite these tiddlers:\n\n%0",
11251         step4Title: "Step 4: Importing %0 tiddler(s)",
11252         step4Html: "<input type='hidden' name='markReport'></input>", // DO NOT TRANSLATE
11253         doneLabel: "done",
11254         donePrompt: "Close this wizard",
11255         statusDoingImport: "Importing tiddlers",
11256         statusDoneImport: "All tiddlers imported",
11257         systemServerNamePattern: "%2 on %1",
11258         systemServerNamePatternNoWorkspace: "%1",
11259         confirmOverwriteSaveTiddler: "The tiddler '%0' already exists. Click 'OK' to overwrite it with the details of this server, or 'Cancel' to leave it unchanged",
11260         serverSaveTemplate: "|''Type:''|%0|\n|''URL:''|%1|\n|''Workspace:''|%2|\n\nThis tiddler was automatically created to record the details of this server",
11261         serverSaveModifier: "(System)",
11262         listViewTemplate: {
11263                 columns: [
11264                         {name: 'Selected', field: 'Selected', rowName: 'title', type: 'Selector'},
11265                         {name: 'Tiddler', field: 'tiddler', title: "Tiddler", type: 'Tiddler'},
11266                         {name: 'Size', field: 'size', tiddlerLink: 'size', title: "Size", type: 'Size'},
11267                         {name: 'Tags', field: 'tags', title: "Tags", type: 'Tags'}
11268                         ],
11269                 rowClasses: [
11270                         ]}
11271         });
11272
11273 merge(config.macros.sync,{
11274         listViewTemplate: {
11275                 columns: [
11276                         {name: 'Selected', field: 'selected', rowName: 'title', type: 'Selector'},
11277                         {name: 'Tiddler', field: 'tiddler', title: "Tiddler", type: 'Tiddler'},
11278                         {name: 'Server Type', field: 'serverType', title: "Server type", type: 'String'},
11279                         {name: 'Server Host', field: 'serverHost', title: "Server host", type: 'String'},
11280                         {name: 'Server Workspace', field: 'serverWorkspace', title: "Server workspace", type: 'String'},
11281                         {name: 'Status', field: 'status', title: "Synchronisation status", type: 'String'},
11282                         {name: 'Server URL', field: 'serverUrl', title: "Server URL", text: "View", type: 'Link'}
11283                         ],
11284                 rowClasses: [
11285                         ],
11286                 buttons: [
11287                         {caption: "Sync these tiddlers", name: 'sync'}
11288                         ]},
11289         wizardTitle: "Synchronize with external servers and files",
11290         step1Title: "Choose the tiddlers you want to synchronize",
11291         step1Html: "<input type='hidden' name='markList'></input>", // DO NOT TRANSLATE
11292         syncLabel: "sync",
11293         syncPrompt: "Sync these tiddlers",
11294         hasChanged: "Changed while unplugged",
11295         hasNotChanged: "Unchanged while unplugged",
11296         syncStatusList: {
11297                 none: {text: "...", color: "none"},
11298                 changedServer: {text: "Changed on server", color: '#80ff80'},
11299                 changedLocally: {text: "Changed while unplugged", color: '#80ff80'},
11300                 changedBoth: {text: "Changed while unplugged and on server", color: '#ff8080'},
11301                 notFound: {text: "Not found on server", color: '#ffff80'},
11302                 putToServer: {text: "Saved update on server", color: '#ff80ff'},
11303                 gotFromServer: {text: "Retrieved update from server", color: '#80ffff'}
11304                 }
11305         });
11306
11307 merge(config.macros.annotations,{
11308         });
11309
11310 merge(config.commands.closeTiddler,{
11311         text: "close",
11312         tooltip: "Close this tiddler"});
11313
11314 merge(config.commands.closeOthers,{
11315         text: "close others",
11316         tooltip: "Close all other tiddlers"});
11317
11318 merge(config.commands.editTiddler,{
11319         text: "edit",
11320         tooltip: "Edit this tiddler",
11321         readOnlyText: "view",
11322         readOnlyTooltip: "View the source of this tiddler"});
11323
11324 merge(config.commands.saveTiddler,{
11325         text: "done",
11326         tooltip: "Save changes to this tiddler"});
11327
11328 merge(config.commands.cancelTiddler,{
11329         text: "cancel",
11330         tooltip: "Undo changes to this tiddler",
11331         warning: "Are you sure you want to abandon your changes to '%0'?",
11332         readOnlyText: "done",
11333         readOnlyTooltip: "View this tiddler normally"});
11334
11335 merge(config.commands.deleteTiddler,{
11336         text: "delete",
11337         tooltip: "Delete this tiddler",
11338         warning: "Are you sure you want to delete '%0'?"});
11339
11340 merge(config.commands.permalink,{
11341         text: "permalink",
11342         tooltip: "Permalink for this tiddler"});
11343
11344 merge(config.commands.references,{
11345         text: "references",
11346         tooltip: "Show tiddlers that link to this one",
11347         popupNone: "No references"});
11348
11349 merge(config.commands.jump,{
11350         text: "jump",
11351         tooltip: "Jump to another open tiddler"});
11352
11353 merge(config.commands.syncing,{
11354         text: "syncing",
11355         tooltip: "Control synchronisation of this tiddler with a server or external file",
11356         currentlySyncing: "<div>Currently syncing via <span class='popupHighlight'>'%0'</span> to:</"+"div><div>host: <span class='popupHighlight'>%1</span></"+"div><div>workspace: <span class='popupHighlight'>%2</span></"+"div>", // Note escaping of closing <div> tag
11357         notCurrentlySyncing: "Not currently syncing",
11358         captionUnSync: "Stop synchronising this tiddler",
11359         chooseServer: "Synchronise this tiddler with another server:",
11360         currServerMarker: "\u25cf ",
11361         notCurrServerMarker: "  "});
11362
11363 merge(config.commands.fields,{
11364         text: "fields",
11365         tooltip: "Show the extended fields of this tiddler",
11366         emptyText: "There are no extended fields for this tiddler",
11367         listViewTemplate: {
11368                 columns: [
11369                         {name: 'Field', field: 'field', title: "Field", type: 'String'},
11370                         {name: 'Value', field: 'value', title: "Value", type: 'String'}
11371                         ],
11372                 rowClasses: [
11373                         ],
11374                 buttons: [
11375                         ]}});
11376
11377 merge(config.shadowTiddlers,{
11378         DefaultTiddlers: "GettingStarted",
11379         MainMenu: "GettingStarted",
11380         SiteTitle: "My TiddlyWiki",
11381         SiteSubtitle: "a reusable non-linear personal web notebook",
11382         SiteUrl: "http://www.tiddlywiki.com/",
11383         SideBarOptions: '<<search>><<closeAll>><<permaview>><<newTiddler>><<newJournal "DD MMM YYYY">><<saveChanges>><<slider chkSliderOptionsPanel OptionsPanel "options »" "Change TiddlyWiki advanced options">>',
11384         SideBarTabs: '<<tabs txtMainTab "Timeline" "Timeline" TabTimeline "All" "All tiddlers" TabAll "Tags" "All tags" TabTags "More" "More lists" TabMore>>',
11385         TabMore: '<<tabs txtMoreTab "Missing" "Missing tiddlers" TabMoreMissing "Orphans" "Orphaned tiddlers" TabMoreOrphans "Shadowed" "Shadowed tiddlers" TabMoreShadowed>>'});
11386
11387 merge(config.annotations,{
11388         AdvancedOptions: "This shadow tiddler provides access to several advanced options",
11389         ColorPalette: "These values in this shadow tiddler determine the colour scheme of the ~TiddlyWiki user interface",
11390         DefaultTiddlers: "The tiddlers listed in this shadow tiddler will be automatically displayed when ~TiddlyWiki starts up",
11391         EditTemplate: "The HTML template in this shadow tiddler determines how tiddlers look while they are being edited",
11392         GettingStarted: "This shadow tiddler provides basic usage instructions",
11393         ImportTiddlers: "This shadow tiddler provides access to importing tiddlers",
11394         MainMenu: "This shadow tiddler is used as the contents of the main menu in the left-hand column of the screen",
11395         MarkupPreHead: "This tiddler is inserted at the top of the <head> section of the TiddlyWiki HTML file",
11396         MarkupPostHead: "This tiddler is inserted at the bottom of the <head> section of the TiddlyWiki HTML file",
11397         MarkupPreBody: "This tiddler is inserted at the top of the <body> section of the TiddlyWiki HTML file",
11398         MarkupPostBody: "This tiddler is inserted at the end of the <body> section of the TiddlyWiki HTML file immediately before the script block",
11399         OptionsPanel: "This shadow tiddler is used as the contents of the options panel slider in the right-hand sidebar",
11400         PageTemplate: "The HTML template in this shadow tiddler determines the overall ~TiddlyWiki layout",
11401         PluginManager: "This shadow tiddler provides access to the plugin manager",
11402         SideBarOptions: "This shadow tiddler is used as the contents of the option panel in the right-hand sidebar",
11403         SideBarTabs: "This shadow tiddler is used as the contents of the tabs panel in the right-hand sidebar",
11404         SiteSubtitle: "This shadow tiddler is used as the second part of the page title",
11405         SiteTitle: "This shadow tiddler is used as the first part of the page title",
11406         SiteUrl: "This shadow tiddler should be set to the full target URL for publication",
11407         StyleSheetColours: "This shadow tiddler contains CSS definitions related to the color of page elements",
11408         StyleSheet: "This tiddler can contain custom CSS definitions",
11409         StyleSheetLayout: "This shadow tiddler contains CSS definitions related to the layout of page elements",
11410         StyleSheetLocale: "This shadow tiddler contains CSS definitions related to the translation locale",
11411         StyleSheetPrint: "This shadow tiddler contains CSS definitions for printing",
11412         TabAll: "This shadow tiddler contains the contents of the 'All' tab in the right-hand sidebar",
11413         TabMore: "This shadow tiddler contains the contents of the 'More' tab in the right-hand sidebar",
11414         TabMoreMissing: "This shadow tiddler contains the contents of the 'Missing' tab in the right-hand sidebar",
11415         TabMoreOrphans: "This shadow tiddler contains the contents of the 'Orphans' tab in the right-hand sidebar",
11416         TabMoreShadowed: "This shadow tiddler contains the contents of the 'Shadowed' tab in the right-hand sidebar",
11417         TabTags: "This shadow tiddler contains the contents of the 'Tags' tab in the right-hand sidebar",
11418         TabTimeline: "This shadow tiddler contains the contents of the 'Timeline' tab in the right-hand sidebar",
11419         ViewTemplate: "The HTML template in this shadow tiddler determines how tiddlers look"
11420         });
11421
11422 //--
11423 //-- Main
11424 //--
11425
11426 var params = null; // Command line parameters
11427 var store = null; // TiddlyWiki storage
11428 var story = null; // Main story
11429 var formatter = null; // Default formatters for the wikifier
11430 config.parsers = {}; // Hashmap of alternative parsers for the wikifier
11431 var anim = new Animator(); // Animation engine
11432 var readOnly = false; // Whether we're in readonly mode
11433 var highlightHack = null; // Embarrassing hack department...
11434 var hadConfirmExit = false; // Don't warn more than once
11435 var safeMode = false; // Disable all plugins and cookies
11436 var installedPlugins = []; // Information filled in when plugins are executed
11437 var startingUp = false; // Whether we're in the process of starting up
11438 var pluginInfo,tiddler; // Used to pass information to plugins in loadPlugins()
11439
11440 // Whether to use the JavaSaver applet
11441 var useJavaSaver = config.browser.isSafari || config.browser.isOpera;
11442
11443 // Starting up
11444 function main()
11445 {
11446         var t9,t8,t7,t6,t5,t4,t3,t2,t1,t0 = new Date();
11447         startingUp = true;
11448         window.onbeforeunload = function(e) {if(window.confirmExit) return confirmExit();};
11449         params = getParameters();
11450         if(params)
11451                 params = params.parseParams("open",null,false);
11452         store = new TiddlyWiki();
11453         invokeParamifier(params,"oninit");
11454         story = new Story("tiddlerDisplay","tiddler");
11455         addEvent(document,"click",Popup.onDocumentClick);
11456         saveTest();
11457         loadOptionsCookie();
11458         for(var s=0; s<config.notifyTiddlers.length; s++)
11459                 store.addNotification(config.notifyTiddlers[s].name,config.notifyTiddlers[s].notify);
11460         t1 = new Date();
11461         store.loadFromDiv("storeArea","store",true);
11462         t2 = new Date();
11463         loadShadowTiddlers();
11464         t3 = new Date();
11465         invokeParamifier(params,"onload");
11466         t4 = new Date();
11467         readOnly = (window.location.protocol == "file:") ? false : config.options.chkHttpReadOnly;
11468         var pluginProblem = loadPlugins();
11469         t5 = new Date();
11470         formatter = new Formatter(config.formatters);
11471         invokeParamifier(params,"onconfig");
11472         t6 = new Date();
11473         store.notifyAll();
11474         t7 = new Date();
11475         restart();
11476         t8 = new Date();
11477         if(pluginProblem) {
11478                 story.displayTiddler(null,"PluginManager");
11479                 displayMessage(config.messages.customConfigError);
11480         }
11481         for(var m in config.macros) {
11482                 if(config.macros[m].init)
11483                         config.macros[m].init();
11484         }
11485         if(!readOnly)
11486                 backstage.init();
11487         t9 = new Date();
11488         if(config.options.chkDisplayStartupTime) {
11489                 displayMessage("Load in " + (t2-t1) + " ms");
11490                 displayMessage("Loadshadows in " + (t3-t2) + " ms");
11491                 displayMessage("Loadplugins in " + (t5-t4) + " ms");
11492                 displayMessage("Notify in " + (t7-t6) + " ms");
11493                 displayMessage("Restart in " + (t8-t7) + " ms");
11494                 displayMessage("Total startup in " + (t9-t0) + " ms");
11495         }
11496         startingUp = false;
11497 }
11498
11499 // Restarting
11500 function restart()
11501 {
11502         invokeParamifier(params,"onstart");
11503         if(story.isEmpty()) {
11504                 var defaultParams = store.getTiddlerText("DefaultTiddlers").parseParams("open",null,false);
11505                 invokeParamifier(defaultParams,"onstart");
11506         }
11507         window.scrollTo(0,0);
11508 }
11509
11510 function saveTest()
11511 {
11512         var s = document.getElementById("saveTest");
11513         if(s.hasChildNodes())
11514                 alert(config.messages.savedSnapshotError);
11515         s.appendChild(document.createTextNode("savetest"));
11516 }
11517
11518 function loadShadowTiddlers()
11519 {
11520         var shadows = new TiddlyWiki();
11521         shadows.loadFromDiv("shadowArea","shadows",true);
11522         shadows.forEachTiddler(function(title,tiddler){config.shadowTiddlers[title] = tiddler.text;});
11523         delete shadows;
11524 }
11525
11526 function loadPlugins()
11527 {
11528         if(safeMode)
11529                 return false;
11530         var tiddlers = store.getTaggedTiddlers("systemConfig");
11531         var toLoad = [];
11532         var nLoaded = 0;
11533         var map = {};
11534         var nPlugins = tiddlers.length;
11535         installedPlugins = [];
11536         for(var i=0; i<nPlugins; i++) {
11537                 var p = getPluginInfo(tiddlers[i]);
11538                 installedPlugins[i] = p;
11539                 var n = p.Name;
11540                 if(n) 
11541                         map[n] = p;
11542                 if(n = p.Source) 
11543                         map[n] = p;
11544         }
11545         var visit = function(p) {
11546                 if(!p || p.done)
11547                         return;
11548                 p.done = 1;
11549                 var reqs = p.Requires;
11550                 if(reqs) {
11551                         reqs = reqs.readBracketedList();
11552                         for(var i=0; i<reqs.length; i++)
11553                                 visit(map[reqs[i]]);
11554                 }
11555                 toLoad.push(p);
11556         };
11557         for(i=0; i<nPlugins; i++) 
11558                 visit(installedPlugins[i]);     
11559         for(i=0; i<toLoad.length; i++) {
11560                 p = toLoad[i];
11561                 pluginInfo = p;
11562                 tiddler = p.tiddler;
11563                 if(isPluginExecutable(p)) {
11564                         if(isPluginEnabled(p)) {
11565                                 p.executed = true;
11566                                 var startTime = new Date();
11567                                 try {
11568                                         if(tiddler.text)
11569                                                 window.eval(tiddler.text);
11570                                         nLoaded++;
11571                                 } catch(ex) {
11572                                         p.log.push(config.messages.pluginError.format([exceptionText(ex)]));
11573                                         p.error = true;
11574                                 }
11575                                 pluginInfo.startupTime = String((new Date()) - startTime) + "ms"; 
11576                         } else {
11577                                 nPlugins--;
11578                         }
11579                 } else {
11580                         p.warning = true;
11581                 }
11582         }
11583         return nLoaded != nPlugins;
11584 }
11585
11586 function getPluginInfo(tiddler)
11587 {
11588         var p = store.getTiddlerSlices(tiddler.title,["Name","Description","Version","Requires","CoreVersion","Date","Source","Author","License","Browsers"]);
11589         p.tiddler = tiddler;
11590         p.title = tiddler.title;
11591         p.log = [];
11592         return p;
11593 }
11594
11595 // Check that a particular plugin is valid for execution
11596 function isPluginExecutable(plugin)
11597 {
11598         if(plugin.tiddler.isTagged("systemConfigForce"))
11599                 return verifyTail(plugin,true,config.messages.pluginForced);
11600         if(plugin["CoreVersion"]) {
11601                 var coreVersion = plugin["CoreVersion"].split(".");
11602                 var w = parseInt(coreVersion[0]) - version.major;
11603                 if(w == 0 && coreVersion[1])
11604                         w = parseInt(coreVersion[1]) - version.minor;
11605                 if(w == 0 && coreVersion[2])
11606                         w = parseInt(coreVersion[2]) - version.revision;
11607                 if(w > 0)
11608                         return verifyTail(plugin,false,config.messages.pluginVersionError);
11609                 }
11610         return true;
11611 }
11612
11613 function isPluginEnabled(plugin)
11614 {
11615         if(plugin.tiddler.isTagged("systemConfigDisable"))
11616                 return verifyTail(plugin,false,config.messages.pluginDisabled);
11617         return true;
11618 }
11619
11620 function verifyTail(plugin,result,message)
11621 {
11622         plugin.log.push(message);
11623         return result;
11624 }
11625
11626 function invokeMacro(place,macro,params,wikifier,tiddler)
11627 {
11628         try {
11629                 var m = config.macros[macro];
11630                 if(m && m.handler)
11631                         m.handler(place,macro,params.readMacroParams(),wikifier,params,tiddler);
11632                 else
11633                         createTiddlyError(place,config.messages.macroError.format([macro]),config.messages.macroErrorDetails.format([macro,config.messages.missingMacro]));
11634         } catch(ex) {
11635                 createTiddlyError(place,config.messages.macroError.format([macro]),config.messages.macroErrorDetails.format([macro,ex.toString()]));
11636         }
11637 }
11638
11639 //--
11640 //-- Paramifiers
11641 //--
11642
11643 function getParameters()
11644 {
11645         var p = null;
11646         if(window.location.hash) {
11647                 p = decodeURI(window.location.hash.substr(1));
11648                 if(config.browser.firefoxDate != null && config.browser.firefoxDate[1] < "20051111")
11649                         p = convertUTF8ToUnicode(p);
11650         }
11651         return p;
11652 }
11653
11654 function invokeParamifier(params,handler)
11655 {
11656         if(!params || params.length == undefined || params.length <= 1)
11657                 return;
11658         for(var t=1; t<params.length; t++) {
11659                 var p = config.paramifiers[params[t].name];
11660                 if(p && p[handler] instanceof Function)
11661                         p[handler](params[t].value);
11662         }
11663 }
11664
11665 config.paramifiers = {};
11666
11667 config.paramifiers.start = {
11668         oninit: function(v) {
11669                 safeMode = v.toLowerCase() == "safe";
11670         }
11671 };
11672
11673 config.paramifiers.open = {
11674         onstart: function(v) {
11675                 story.displayTiddler("bottom",v,null,false,null);
11676         }
11677 };
11678
11679 config.paramifiers.story = {
11680         onstart: function(v) {
11681                 var list = store.getTiddlerText(v,"").parseParams("open",null,false);
11682                 invokeParamifier(list,"onstart");
11683         }
11684 };
11685
11686 config.paramifiers.search = {
11687         onstart: function(v) {
11688                 story.search(v,false,false);
11689         }
11690 };
11691
11692 config.paramifiers.searchRegExp = {
11693         onstart: function(v) {
11694                 story.prototype.search(v,false,true);
11695         }
11696 };
11697
11698 config.paramifiers.tag = {
11699         onstart: function(v) {
11700                 var tagged = store.getTaggedTiddlers(v,"title");
11701                 for(var t=0; t<tagged.length; t++)
11702                         story.displayTiddler("bottom",tagged[t].title,null,false,null);
11703         }
11704 };
11705
11706 config.paramifiers.newTiddler = {
11707         onstart: function(v) {
11708                 if(!readOnly) {
11709                         story.displayTiddler(null,v,DEFAULT_EDIT_TEMPLATE);
11710                         story.focusTiddler(v,"text");
11711                 }
11712         }
11713 };
11714
11715 config.paramifiers.newJournal = {
11716         onstart: function(v) {
11717                 if(!readOnly) {
11718                         var now = new Date();
11719                         var title = now.formatString(v.trim());
11720                         story.displayTiddler(null,title,DEFAULT_EDIT_TEMPLATE);
11721                         story.focusTiddler(title,"text");
11722                 }
11723         }
11724 };
11725
11726 config.paramifiers.readOnly = {
11727         onconfig: function(v) {
11728                 var p = v.toLowerCase();
11729                 readOnly = p == "yes" ? true : (p == "no" ? false : readOnly);
11730         }
11731 };
11732
11733 //--
11734 //-- Formatter helpers
11735 //--
11736
11737 function Formatter(formatters)
11738 {
11739         this.formatters = [];
11740         var pattern = [];
11741         for(var n=0; n<formatters.length; n++) {
11742                 pattern.push("(" + formatters[n].match + ")");
11743                 this.formatters.push(formatters[n]);
11744         }
11745         this.formatterRegExp = new RegExp(pattern.join("|"),"mg");
11746 }
11747
11748 config.formatterHelpers = {
11749
11750         createElementAndWikify: function(w)
11751         {
11752                 w.subWikifyTerm(createTiddlyElement(w.output,this.element),this.termRegExp);
11753         },
11754         
11755         inlineCssHelper: function(w)
11756         {
11757                 var styles = [];
11758                 config.textPrimitives.cssLookaheadRegExp.lastIndex = w.nextMatch;
11759                 var lookaheadMatch = config.textPrimitives.cssLookaheadRegExp.exec(w.source);
11760                 while(lookaheadMatch && lookaheadMatch.index == w.nextMatch) {
11761                         var s,v;
11762                         if(lookaheadMatch[1]) {
11763                                 s = lookaheadMatch[1].unDash();
11764                                 v = lookaheadMatch[2];
11765                         } else {
11766                                 s = lookaheadMatch[3].unDash();
11767                                 v = lookaheadMatch[4];
11768                         }
11769                         if (s=="bgcolor")
11770                                 s = "backgroundColor";
11771                         styles.push({style: s, value: v});
11772                         w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
11773                         config.textPrimitives.cssLookaheadRegExp.lastIndex = w.nextMatch;
11774                         lookaheadMatch = config.textPrimitives.cssLookaheadRegExp.exec(w.source);
11775                 }
11776                 return styles;
11777         },
11778
11779         applyCssHelper: function(e,styles)
11780         {
11781                 for(var t=0; t< styles.length; t++) {
11782                         try {
11783                                 e.style[styles[t].style] = styles[t].value;
11784                         } catch (ex) {
11785                         }
11786                 }
11787         },
11788
11789         enclosedTextHelper: function(w)
11790         {
11791                 this.lookaheadRegExp.lastIndex = w.matchStart;
11792                 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
11793                 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
11794                         var text = lookaheadMatch[1];
11795                         if(config.browser.isIE)
11796                                 text = text.replace(/\n/g,"\r");
11797                         createTiddlyElement(w.output,this.element,null,null,text);
11798                         w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
11799                 }
11800         },
11801
11802         isExternalLink: function(link)
11803         {
11804                 if(store.tiddlerExists(link) || store.isShadowTiddler(link)) {
11805                         return false;
11806                 }
11807                 var urlRegExp = new RegExp(config.textPrimitives.urlPattern,"mg");
11808                 if(urlRegExp.exec(link)) {
11809                         return true;
11810                 }
11811                 if (link.indexOf(".")!=-1 || link.indexOf("\\")!=-1 || link.indexOf("/")!=-1){
11812                         return true;
11813                 }
11814                 return false;
11815         }
11816
11817 };
11818
11819 //--
11820 //-- Standard formatters
11821 //--
11822
11823 config.formatters = [
11824 {
11825         name: "table",
11826         match: "^\\|(?:[^\\n]*)\\|(?:[fhck]?)$",
11827         lookaheadRegExp: /^\|([^\n]*)\|([fhck]?)$/mg,
11828         rowTermRegExp: /(\|(?:[fhck]?)$\n?)/mg,
11829         cellRegExp: /(?:\|([^\n\|]*)\|)|(\|[fhck]?$\n?)/mg,
11830         cellTermRegExp: /((?:\x20*)\|)/mg,
11831         rowTypes: {"c":"caption", "h":"thead", "":"tbody", "f":"tfoot"},
11832
11833         handler: function(w)
11834         {
11835                 var table = createTiddlyElement(w.output,"table",null,"twtable");
11836                 var prevColumns = [];
11837                 var currRowType = null;
11838                 var rowContainer;
11839                 var rowCount = 0;
11840                 w.nextMatch = w.matchStart;
11841                 this.lookaheadRegExp.lastIndex = w.nextMatch;
11842                 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
11843                 while(lookaheadMatch && lookaheadMatch.index == w.nextMatch) {
11844                         var nextRowType = lookaheadMatch[2];
11845                         if(nextRowType == "k") {
11846                                 table.className = lookaheadMatch[1];
11847                                 w.nextMatch += lookaheadMatch[0].length+1;
11848                         } else {
11849                                 if(nextRowType != currRowType) {
11850                                         rowContainer = createTiddlyElement(table,this.rowTypes[nextRowType]);
11851                                         currRowType = nextRowType;
11852                                 }
11853                                 if(currRowType == "c") {
11854                                         // Caption
11855                                         w.nextMatch++;
11856                                         if(rowContainer != table.firstChild)
11857                                                 table.insertBefore(rowContainer,table.firstChild);
11858                                         rowContainer.setAttribute("align",rowCount == 0?"top":"bottom");
11859                                         w.subWikifyTerm(rowContainer,this.rowTermRegExp);
11860                                 } else {
11861                                         this.rowHandler(w,createTiddlyElement(rowContainer,"tr",null,(rowCount&1)?"oddRow":"evenRow"),prevColumns);
11862                                         rowCount++;
11863                                 }
11864                         }
11865                         this.lookaheadRegExp.lastIndex = w.nextMatch;
11866                         lookaheadMatch = this.lookaheadRegExp.exec(w.source);
11867                 }
11868         },
11869         rowHandler: function(w,e,prevColumns)
11870         {
11871                 var col = 0;
11872                 var colSpanCount = 1;
11873                 var prevCell = null;
11874                 this.cellRegExp.lastIndex = w.nextMatch;
11875                 var cellMatch = this.cellRegExp.exec(w.source);
11876                 while(cellMatch && cellMatch.index == w.nextMatch) {
11877                         if(cellMatch[1] == "~") {
11878                                 // Rowspan
11879                                 var last = prevColumns[col];
11880                                 if(last) {
11881                                         last.rowSpanCount++;
11882                                         last.element.setAttribute("rowspan",last.rowSpanCount);
11883                                         last.element.setAttribute("rowSpan",last.rowSpanCount); // Needed for IE
11884                                         last.element.valign = "center";
11885                                 }
11886                                 w.nextMatch = this.cellRegExp.lastIndex-1;
11887                         } else if(cellMatch[1] == ">") {
11888                                 // Colspan
11889                                 colSpanCount++;
11890                                 w.nextMatch = this.cellRegExp.lastIndex-1;
11891                         } else if(cellMatch[2]) {
11892                                 // End of row
11893                                 if(prevCell && colSpanCount > 1) {
11894                                         prevCell.setAttribute("colspan",colSpanCount);
11895                                         prevCell.setAttribute("colSpan",colSpanCount); // Needed for IE
11896                                 }
11897                                 w.nextMatch = this.cellRegExp.lastIndex;
11898                                 break;
11899                         } else {
11900                                 // Cell
11901                                 w.nextMatch++;
11902                                 var styles = config.formatterHelpers.inlineCssHelper(w);
11903                                 var spaceLeft = false;
11904                                 var chr = w.source.substr(w.nextMatch,1);
11905                                 while(chr == " ") {
11906                                         spaceLeft = true;
11907                                         w.nextMatch++;
11908                                         chr = w.source.substr(w.nextMatch,1);
11909                                 }
11910                                 var cell;
11911                                 if(chr == "!") {
11912                                         cell = createTiddlyElement(e,"th");
11913                                         w.nextMatch++;
11914                                 } else {
11915                                         cell = createTiddlyElement(e,"td");
11916                                 }
11917                                 prevCell = cell;
11918                                 prevColumns[col] = {rowSpanCount:1,element:cell};
11919                                 if(colSpanCount > 1) {
11920                                         cell.setAttribute("colspan",colSpanCount);
11921                                         cell.setAttribute("colSpan",colSpanCount); // Needed for IE
11922                                         colSpanCount = 1;
11923                                 }
11924                                 config.formatterHelpers.applyCssHelper(cell,styles);
11925                                 w.subWikifyTerm(cell,this.cellTermRegExp);
11926                                 if(w.matchText.substr(w.matchText.length-2,1) == " ") // spaceRight
11927                                         cell.align = spaceLeft ? "center" : "left";
11928                                 else if(spaceLeft)
11929                                         cell.align = "right";
11930                                 w.nextMatch--;
11931                         }
11932                         col++;
11933                         this.cellRegExp.lastIndex = w.nextMatch;
11934                         cellMatch = this.cellRegExp.exec(w.source);
11935                 }
11936         }
11937 },
11938
11939 {
11940         name: "heading",
11941         match: "^!{1,6}",
11942         termRegExp: /(\n)/mg,
11943         handler: function(w)
11944         {
11945                 w.subWikifyTerm(createTiddlyElement(w.output,"h" + w.matchLength),this.termRegExp);
11946         }
11947 },
11948
11949 {
11950         name: "list",
11951         match: "^(?:[\\*#;:]+)",
11952         lookaheadRegExp: /^(?:(?:(\*)|(#)|(;)|(:))+)/mg,
11953         termRegExp: /(\n)/mg,
11954         handler: function(w)
11955         {
11956                 var stack = [w.output];
11957                 var currLevel = 0, currType = null;
11958                 var listLevel, listType, itemType;
11959                 w.nextMatch = w.matchStart;
11960                 this.lookaheadRegExp.lastIndex = w.nextMatch;
11961                 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
11962                 while(lookaheadMatch && lookaheadMatch.index == w.nextMatch) {
11963                         if(lookaheadMatch[1]) {
11964                                 listType = "ul";
11965                                 itemType = "li";
11966                         } else if(lookaheadMatch[2]) {
11967                                 listType = "ol";
11968                                 itemType = "li";
11969                         } else if(lookaheadMatch[3]) {
11970                                 listType = "dl";
11971                                 itemType = "dt";
11972                         } else if(lookaheadMatch[4]) {
11973                                 listType = "dl";
11974                                 itemType = "dd";
11975                         }
11976                         listLevel = lookaheadMatch[0].length;
11977                         w.nextMatch += lookaheadMatch[0].length;
11978                         var t;
11979                         if(listLevel > currLevel) {
11980                                 for(t=currLevel; t<listLevel; t++) {
11981                                         var target = (currLevel == 0) ? stack[stack.length-1] : stack[stack.length-1].lastChild;
11982                                         stack.push(createTiddlyElement(target,listType));
11983                                 }
11984                         } else if(listLevel < currLevel) {
11985                                 for(t=currLevel; t>listLevel; t--)
11986                                         stack.pop();
11987                         } else if(listLevel == currLevel && listType != currType) {
11988                                 stack.pop();
11989                                 stack.push(createTiddlyElement(stack[stack.length-1].lastChild,listType));
11990                         }
11991                         currLevel = listLevel;
11992                         currType = listType;
11993                         var e = createTiddlyElement(stack[stack.length-1],itemType);
11994                         w.subWikifyTerm(e,this.termRegExp);
11995                         this.lookaheadRegExp.lastIndex = w.nextMatch;
11996                         lookaheadMatch = this.lookaheadRegExp.exec(w.source);
11997                 }
11998         }
11999 },
12000
12001 {
12002         name: "quoteByBlock",
12003         match: "^<<<\\n",
12004         termRegExp: /(^<<<(\n|$))/mg,
12005         element: "blockquote",
12006         handler: config.formatterHelpers.createElementAndWikify
12007 },
12008
12009 {
12010         name: "quoteByLine",
12011         match: "^>+",
12012         lookaheadRegExp: /^>+/mg,
12013         termRegExp: /(\n)/mg,
12014         element: "blockquote",
12015         handler: function(w)
12016         {
12017                 var stack = [w.output];
12018                 var currLevel = 0;
12019                 var newLevel = w.matchLength;
12020                 var t;
12021                 do {
12022                         if(newLevel > currLevel) {
12023                                 for(t=currLevel; t<newLevel; t++)
12024                                         stack.push(createTiddlyElement(stack[stack.length-1],this.element));
12025                         } else if(newLevel < currLevel) {
12026                                 for(t=currLevel; t>newLevel; t--)
12027                                         stack.pop();
12028                         }
12029                         currLevel = newLevel;
12030                         w.subWikifyTerm(stack[stack.length-1],this.termRegExp);
12031                         createTiddlyElement(stack[stack.length-1],"br");
12032                         this.lookaheadRegExp.lastIndex = w.nextMatch;
12033                         var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
12034                         var matched = lookaheadMatch && lookaheadMatch.index == w.nextMatch;
12035                         if(matched) {
12036                                 newLevel = lookaheadMatch[0].length;
12037                                 w.nextMatch += lookaheadMatch[0].length;
12038                         }
12039                 } while(matched);
12040         }
12041 },
12042
12043 {
12044         name: "rule",
12045         match: "^----+$\\n?",
12046         handler: function(w)
12047         {
12048                 createTiddlyElement(w.output,"hr");
12049         }
12050 },
12051
12052 {
12053         name: "monospacedByLine",
12054         match: "^\\{\\{\\{\\n",
12055         lookaheadRegExp: /^\{\{\{\n((?:^[^\n]*\n)+?)(^\}\}\}$\n?)/mg,
12056         element: "pre",
12057         handler: config.formatterHelpers.enclosedTextHelper
12058 },
12059
12060 {
12061         name: "monospacedByLineForCSS",
12062         match: "^/\\*\\{\\{\\{\\*/\\n",
12063         lookaheadRegExp: /\/\*\{\{\{\*\/\n*((?:^[^\n]*\n)+?)(\n*^\/\*\}\}\}\*\/$\n?)/mg,
12064         element: "pre",
12065         handler: config.formatterHelpers.enclosedTextHelper
12066 },
12067
12068 {
12069         name: "monospacedByLineForPlugin",
12070         match: "^//\\{\\{\\{\\n",
12071         lookaheadRegExp: /^\/\/\{\{\{\n\n*((?:^[^\n]*\n)+?)(\n*^\/\/\}\}\}$\n?)/mg,
12072         element: "pre",
12073         handler: config.formatterHelpers.enclosedTextHelper
12074 },
12075
12076 {
12077         name: "monospacedByLineForTemplate",
12078         match: "^<!--\\{\\{\\{-->\\n",
12079         lookaheadRegExp: /<!--\{\{\{-->\n*((?:^[^\n]*\n)+?)(\n*^<!--\}\}\}-->$\n?)/mg,
12080         element: "pre",
12081         handler: config.formatterHelpers.enclosedTextHelper
12082 },
12083
12084 {
12085         name: "wikifyCommentForPlugin",
12086         match: "^/\\*\\*\\*\\n",
12087         termRegExp: /(^\*\*\*\/\n)/mg,
12088         handler: function(w)
12089         {
12090                 w.subWikifyTerm(w.output,this.termRegExp);
12091         }
12092 },
12093
12094 {
12095         name: "wikifyCommentForTemplate",
12096         match: "^<!---\\n",
12097         termRegExp: /(^--->\n)/mg,
12098         handler: function(w) 
12099         {
12100                 w.subWikifyTerm(w.output,this.termRegExp);
12101         }
12102 },
12103
12104 {
12105         name: "macro",
12106         match: "<<",
12107         lookaheadRegExp: /<<([^>\s]+)(?:\s*)((?:[^>]|(?:>(?!>)))*)>>/mg,
12108         handler: function(w)
12109         {
12110                 this.lookaheadRegExp.lastIndex = w.matchStart;
12111                 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
12112                 if(lookaheadMatch && lookaheadMatch.index == w.matchStart && lookaheadMatch[1]) {
12113                         w.nextMatch = this.lookaheadRegExp.lastIndex;
12114                         invokeMacro(w.output,lookaheadMatch[1],lookaheadMatch[2],w,w.tiddler);
12115                 }
12116         }
12117 },
12118
12119 {
12120         name: "prettyLink",
12121         match: "\\[\\[",
12122         lookaheadRegExp: /\[\[(.*?)(?:\|(~)?(.*?))?\]\]/mg,
12123         handler: function(w)
12124         {
12125                 this.lookaheadRegExp.lastIndex = w.matchStart;
12126                 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
12127                 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
12128                         var e;
12129                         var text = lookaheadMatch[1];
12130                         if(lookaheadMatch[3]) {
12131                                 // Pretty bracketted link
12132                                 var link = lookaheadMatch[3];
12133                                 e = (!lookaheadMatch[2] && config.formatterHelpers.isExternalLink(link)) ?
12134                                                 createExternalLink(w.output,link) : createTiddlyLink(w.output,link,false,null,w.isStatic,w.tiddler);
12135                         } else {
12136                                 // Simple bracketted link
12137                                 e = createTiddlyLink(w.output,text,false,null,w.isStatic,w.tiddler);
12138                         }
12139                         createTiddlyText(e,text);
12140                         w.nextMatch = this.lookaheadRegExp.lastIndex;
12141                 }
12142         }
12143 },
12144
12145 {
12146         name: "unWikiLink",
12147         match: config.textPrimitives.unWikiLink+config.textPrimitives.wikiLink,
12148         handler: function(w)
12149         {
12150                 w.outputText(w.output,w.matchStart+1,w.nextMatch);
12151         }
12152 },
12153
12154 {
12155         name: "wikiLink",
12156         match: config.textPrimitives.wikiLink,
12157         handler: function(w)
12158         {
12159                 if(w.matchStart > 0) {
12160                         var preRegExp = new RegExp(config.textPrimitives.anyLetterStrict,"mg");
12161                         preRegExp.lastIndex = w.matchStart-1;
12162                         var preMatch = preRegExp.exec(w.source);
12163                         if(preMatch.index == w.matchStart-1) {
12164                                 w.outputText(w.output,w.matchStart,w.nextMatch);
12165                                 return;
12166                         }
12167                 }
12168                 if(w.autoLinkWikiWords == true || store.isShadowTiddler(w.matchText)) {
12169                         var link = createTiddlyLink(w.output,w.matchText,false,null,w.isStatic,w.tiddler);
12170                         w.outputText(link,w.matchStart,w.nextMatch);
12171                 } else {
12172                         w.outputText(w.output,w.matchStart,w.nextMatch);
12173                 }
12174         }
12175 },
12176
12177 {
12178         name: "urlLink",
12179         match: config.textPrimitives.urlPattern,
12180         handler: function(w)
12181         {
12182                 w.outputText(createExternalLink(w.output,w.matchText),w.matchStart,w.nextMatch);
12183         }
12184 },
12185
12186 {
12187         name: "image",
12188         match: "\\[[<>]?[Ii][Mm][Gg]\\[",
12189         lookaheadRegExp: /\[([<]?)(>?)[Ii][Mm][Gg]\[(?:([^\|\]]+)\|)?([^\[\]\|]+)\](?:\[([^\]]*)\])?\]/mg,
12190         handler: function(w)
12191         {
12192                 this.lookaheadRegExp.lastIndex = w.matchStart;
12193                 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
12194                 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
12195                         var e = w.output;
12196                         if(lookaheadMatch[5]) {
12197                                 var link = lookaheadMatch[5];
12198                                 e = config.formatterHelpers.isExternalLink(link) ? createExternalLink(w.output,link) : createTiddlyLink(w.output,link,false,null,w.isStatic,w.tiddler);
12199                                 addClass(e,"imageLink");
12200                         }
12201                         var img = createTiddlyElement(e,"img");
12202                         if(lookaheadMatch[1])
12203                                 img.align = "left";
12204                         else if(lookaheadMatch[2])
12205                                 img.align = "right";
12206                         if(lookaheadMatch[3])
12207                                 img.title = lookaheadMatch[3];
12208                         img.src = lookaheadMatch[4];
12209                         w.nextMatch = this.lookaheadRegExp.lastIndex;
12210                 }
12211         }
12212 },
12213
12214 {
12215         name: "html",
12216         match: "<[Hh][Tt][Mm][Ll]>",
12217         lookaheadRegExp: /<[Hh][Tt][Mm][Ll]>((?:.|\n)*?)<\/[Hh][Tt][Mm][Ll]>/mg,
12218         handler: function(w)
12219         {
12220                 this.lookaheadRegExp.lastIndex = w.matchStart;
12221                 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
12222                 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
12223                         createTiddlyElement(w.output,"span").innerHTML = lookaheadMatch[1];
12224                         w.nextMatch = this.lookaheadRegExp.lastIndex;
12225                 }
12226         }
12227 },
12228
12229 {
12230         name: "commentByBlock",
12231         match: "/%",
12232         lookaheadRegExp: /\/%((?:.|\n)*?)%\//mg,
12233         handler: function(w)
12234         {
12235                 this.lookaheadRegExp.lastIndex = w.matchStart;
12236                 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
12237                 if(lookaheadMatch && lookaheadMatch.index == w.matchStart)
12238                         w.nextMatch = this.lookaheadRegExp.lastIndex;
12239         }
12240 },
12241
12242 {
12243         name: "boldByChar",
12244         match: "''",
12245         termRegExp: /('')/mg,
12246         element: "strong",
12247         handler: config.formatterHelpers.createElementAndWikify
12248 },
12249
12250 {
12251         name: "italicByChar",
12252         match: "//",
12253         termRegExp: /(\/\/)/mg,
12254         element: "em",
12255         handler: config.formatterHelpers.createElementAndWikify
12256 },
12257
12258 {
12259         name: "underlineByChar",
12260         match: "__",
12261         termRegExp: /(__)/mg,
12262         element: "u",
12263         handler: config.formatterHelpers.createElementAndWikify
12264 },
12265
12266 {
12267         name: "strikeByChar",
12268         match: "--(?!\\s|$)",
12269         termRegExp: /((?!\s)--|(?=\n\n))/mg,
12270         element: "strike",
12271         handler: config.formatterHelpers.createElementAndWikify
12272 },
12273
12274 {
12275         name: "superscriptByChar",
12276         match: "\\^\\^",
12277         termRegExp: /(\^\^)/mg,
12278         element: "sup",
12279         handler: config.formatterHelpers.createElementAndWikify
12280 },
12281
12282 {
12283         name: "subscriptByChar",
12284         match: "~~",
12285         termRegExp: /(~~)/mg,
12286         element: "sub",
12287         handler: config.formatterHelpers.createElementAndWikify
12288 },
12289
12290 {
12291         name: "monospacedByChar",
12292         match: "\\{\\{\\{",
12293         lookaheadRegExp: /\{\{\{((?:.|\n)*?)\}\}\}/mg,
12294         handler: function(w)
12295         {
12296                 this.lookaheadRegExp.lastIndex = w.matchStart;
12297                 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
12298                 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
12299                         createTiddlyElement(w.output,"code",null,null,lookaheadMatch[1]);
12300                         w.nextMatch = this.lookaheadRegExp.lastIndex;
12301                 }
12302         }
12303 },
12304
12305 {
12306         name: "styleByChar",
12307         match: "@@",
12308         termRegExp: /(@@)/mg,
12309         handler: function(w)
12310         {
12311                 var e = createTiddlyElement(w.output,"span");
12312                 var styles = config.formatterHelpers.inlineCssHelper(w);
12313                 if(styles.length == 0)
12314                         e.className = "marked";
12315                 else
12316                         config.formatterHelpers.applyCssHelper(e,styles);
12317                 w.subWikifyTerm(e,this.termRegExp);
12318         }
12319 },
12320
12321 {
12322         name: "lineBreak",
12323         match: "\\n|<br ?/?>",
12324         handler: function(w)
12325         {
12326                 createTiddlyElement(w.output,"br");
12327         }
12328 },
12329
12330 {
12331         name: "rawText",
12332         match: "\\\"{3}|<nowiki>",
12333         lookaheadRegExp: /(?:\"{3}|<nowiki>)((?:.|\n)*?)(?:\"{3}|<\/nowiki>)/mg,
12334         handler: function(w)
12335         {
12336                 this.lookaheadRegExp.lastIndex = w.matchStart;
12337                 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
12338                 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
12339                         createTiddlyElement(w.output,"span",null,null,lookaheadMatch[1]);
12340                         w.nextMatch = this.lookaheadRegExp.lastIndex;
12341                 }
12342         }
12343 },
12344
12345 {
12346         name: "mdash",
12347         match: "--",
12348         handler: function(w)
12349         {
12350                 createTiddlyElement(w.output,"span").innerHTML = "&mdash;";
12351         }
12352 },
12353
12354 {
12355         name: "htmlEntitiesEncoding",
12356         match: "(?:(?:&#?[a-zA-Z0-9]{2,8};|.)(?:&#?(?:x0*(?:3[0-6][0-9a-fA-F]|1D[c-fC-F][0-9a-fA-F]|20[d-fD-F][0-9a-fA-F]|FE2[0-9a-fA-F])|0*(?:76[89]|7[7-9][0-9]|8[0-7][0-9]|761[6-9]|76[2-7][0-9]|84[0-3][0-9]|844[0-7]|6505[6-9]|6506[0-9]|6507[0-1]));)+|&#?[a-zA-Z0-9]{2,8};)",
12357         handler: function(w)
12358         {
12359                 createTiddlyElement(w.output,"span").innerHTML = w.matchText;
12360         }
12361 },
12362
12363 {
12364         name: "customClasses",
12365         match: "\\{\\{",
12366         termRegExp: /(\}\}\})/mg,
12367         lookaheadRegExp: /\{\{[\s]*([\w]+[\s\w]*)[\s]*\{(\n?)/mg,
12368         handler: function(w)
12369         {
12370                 this.lookaheadRegExp.lastIndex = w.matchStart;
12371                 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
12372                 if(lookaheadMatch) {
12373                         var e = createTiddlyElement(w.output,lookaheadMatch[2] == "\n" ? "div" : "span",null,lookaheadMatch[1]);
12374                         w.nextMatch = this.lookaheadRegExp.lastIndex;
12375                         w.subWikifyTerm(e,this.termRegExp);
12376                 }
12377         }
12378 }
12379
12380 ];
12381
12382 //--
12383 //-- Wikifier
12384 //--
12385
12386 function getParser(tiddler,format)
12387 {
12388         if(tiddler) {
12389                 if(!format)
12390                         format = tiddler.fields["wikiformat"];
12391                 if(format) {
12392                         for(var i in config.parsers) {
12393                                 if(format == config.parsers[i].format)
12394                                         return config.parsers[i];
12395                         }
12396                 } else {
12397                         for(var i in config.parsers) {
12398                                 if(tiddler.isTagged(config.parsers[i].formatTag))
12399                                         return config.parsers[i];
12400                         }
12401                 }
12402         }
12403         return formatter;
12404 }
12405
12406 function wikify(source,output,highlightRegExp,tiddler)
12407 {
12408         if(source && source != "") {
12409                 var wikifier = new Wikifier(source,getParser(tiddler),highlightRegExp,tiddler);
12410                 wikifier.subWikifyUnterm(output);
12411         }
12412 }
12413
12414 function wikifyStatic(source,highlightRegExp,tiddler,format)
12415 {
12416         var e = createTiddlyElement(document.body,"div");
12417         e.style.display = "none";
12418         var html = "";
12419         if(source && source != "") {
12420                 var wikifier = new Wikifier(source,getParser(tiddler,format),highlightRegExp,tiddler);
12421                 wikifier.isStatic = true;
12422                 wikifier.subWikifyUnterm(e);
12423                 html = e.innerHTML;
12424                 removeNode(e);
12425         }
12426         return html;
12427 }
12428
12429 function wikifyPlain(title,theStore,limit)
12430 {
12431         if(!theStore)
12432                 theStore = store;
12433         if(theStore.tiddlerExists(title) || theStore.isShadowTiddler(title)) {
12434                 return wikifyPlainText(theStore.getTiddlerText(title),limit,tiddler);
12435         } else {
12436                 return "";
12437         }
12438 }
12439
12440 function wikifyPlainText(text,limit,tiddler)
12441 {
12442         if(limit > 0)
12443                 text = text.substr(0,limit);
12444         var wikifier = new Wikifier(text,formatter,null,tiddler);
12445         return wikifier.wikifyPlain();
12446 }
12447
12448 function highlightify(source,output,highlightRegExp,tiddler)
12449 {
12450         if(source && source != "") {
12451                 var wikifier = new Wikifier(source,formatter,highlightRegExp,tiddler);
12452                 wikifier.outputText(output,0,source.length);
12453         }
12454 }
12455
12456 function Wikifier(source,formatter,highlightRegExp,tiddler)
12457 {
12458         this.source = source;
12459         this.output = null;
12460         this.formatter = formatter;
12461         this.nextMatch = 0;
12462         this.autoLinkWikiWords = tiddler && tiddler.autoLinkWikiWords() == false ? false : true;
12463         this.highlightRegExp = highlightRegExp;
12464         this.highlightMatch = null;
12465         this.isStatic = false;
12466         if(highlightRegExp) {
12467                 highlightRegExp.lastIndex = 0;
12468                 this.highlightMatch = highlightRegExp.exec(source);
12469         }
12470         this.tiddler = tiddler;
12471 }
12472
12473 Wikifier.prototype.wikifyPlain = function()
12474 {
12475         var e = createTiddlyElement(document.body,"div");
12476         this.subWikify(e);
12477         var text = getPlainText(e);
12478         removeNode(e);
12479         return text;
12480 };
12481
12482 Wikifier.prototype.subWikify = function(output,terminator)
12483 {
12484         if(terminator)
12485                 this.subWikifyTerm(output,new RegExp("(" + terminator + ")","mg"));
12486         else
12487                 this.subWikifyUnterm(output);
12488 };
12489
12490 Wikifier.prototype.subWikifyUnterm = function(output)
12491 {
12492         // subWikify() can be indirectly recursive, so we need to save the old output pointer
12493         var oldOutput = this.output;
12494         this.output = output;
12495         this.formatter.formatterRegExp.lastIndex = this.nextMatch;
12496         var formatterMatch = this.formatter.formatterRegExp.exec(this.source);
12497         while(formatterMatch) {
12498                 // Output any text before the match
12499                 if(formatterMatch.index > this.nextMatch)
12500                         this.outputText(this.output,this.nextMatch,formatterMatch.index);
12501                 // Set the match parameters for the handler
12502                 this.matchStart = formatterMatch.index;
12503                 this.matchLength = formatterMatch[0].length;
12504                 this.matchText = formatterMatch[0];
12505                 this.nextMatch = this.formatter.formatterRegExp.lastIndex;
12506                 for(var t=1; t<formatterMatch.length; t++) {
12507                         if(formatterMatch[t]) {
12508                                 this.formatter.formatters[t-1].handler(this);
12509                                 this.formatter.formatterRegExp.lastIndex = this.nextMatch;
12510                                 break;
12511                         }
12512                 }
12513                 formatterMatch = this.formatter.formatterRegExp.exec(this.source);
12514         }
12515         if(this.nextMatch < this.source.length) {
12516                 this.outputText(this.output,this.nextMatch,this.source.length);
12517                 this.nextMatch = this.source.length;
12518         }
12519         this.output = oldOutput;
12520 };
12521
12522 Wikifier.prototype.subWikifyTerm = function(output,terminatorRegExp)
12523 {
12524         // subWikify() can be indirectly recursive, so we need to save the old output pointer
12525         var oldOutput = this.output;
12526         this.output = output;
12527         // Get the first matches for the formatter and terminator RegExps
12528         terminatorRegExp.lastIndex = this.nextMatch;
12529         var terminatorMatch = terminatorRegExp.exec(this.source);
12530         this.formatter.formatterRegExp.lastIndex = this.nextMatch;
12531         var formatterMatch = this.formatter.formatterRegExp.exec(terminatorMatch ? this.source.substr(0,terminatorMatch.index) : this.source);
12532         while(terminatorMatch || formatterMatch) {
12533                 if(terminatorMatch && (!formatterMatch || terminatorMatch.index <= formatterMatch.index)) {
12534                         if(terminatorMatch.index > this.nextMatch)
12535                                 this.outputText(this.output,this.nextMatch,terminatorMatch.index);
12536                         this.matchText = terminatorMatch[1];
12537                         this.matchLength = terminatorMatch[1].length;
12538                         this.matchStart = terminatorMatch.index;
12539                         this.nextMatch = this.matchStart + this.matchLength;
12540                         this.output = oldOutput;
12541                         return;
12542                 }
12543                 if(formatterMatch.index > this.nextMatch)
12544                         this.outputText(this.output,this.nextMatch,formatterMatch.index);
12545                 this.matchStart = formatterMatch.index;
12546                 this.matchLength = formatterMatch[0].length;
12547                 this.matchText = formatterMatch[0];
12548                 this.nextMatch = this.formatter.formatterRegExp.lastIndex;
12549                 for(var t=1; t<formatterMatch.length; t++) {
12550                         if(formatterMatch[t]) {
12551                                 this.formatter.formatters[t-1].handler(this);
12552                                 this.formatter.formatterRegExp.lastIndex = this.nextMatch;
12553                                 break;
12554                         }
12555                 }
12556                 terminatorRegExp.lastIndex = this.nextMatch;
12557                 terminatorMatch = terminatorRegExp.exec(this.source);
12558                 formatterMatch = this.formatter.formatterRegExp.exec(terminatorMatch ? this.source.substr(0,terminatorMatch.index) : this.source);
12559         }
12560         if(this.nextMatch < this.source.length) {
12561                 this.outputText(this.output,this.nextMatch,this.source.length);
12562                 this.nextMatch = this.source.length;
12563         }
12564         this.output = oldOutput;
12565 };
12566
12567 Wikifier.prototype.outputText = function(place,startPos,endPos)
12568 {
12569         while(this.highlightMatch && (this.highlightRegExp.lastIndex > startPos) && (this.highlightMatch.index < endPos) && (startPos < endPos)) {
12570                 if(this.highlightMatch.index > startPos) {
12571                         createTiddlyText(place,this.source.substring(startPos,this.highlightMatch.index));
12572                         startPos = this.highlightMatch.index;
12573                 }
12574                 var highlightEnd = Math.min(this.highlightRegExp.lastIndex,endPos);
12575                 var theHighlight = createTiddlyElement(place,"span",null,"highlight",this.source.substring(startPos,highlightEnd));
12576                 startPos = highlightEnd;
12577                 if(startPos >= this.highlightRegExp.lastIndex)
12578                         this.highlightMatch = this.highlightRegExp.exec(this.source);
12579         }
12580         if(startPos < endPos) {
12581                 createTiddlyText(place,this.source.substring(startPos,endPos));
12582         }
12583 };
12584
12585 //--
12586 //-- Macro definitions
12587 //--
12588
12589 config.macros.today.handler = function(place,macroName,params)
12590 {
12591         var now = new Date();
12592         var text;
12593         if(params[0])
12594                 text = now.formatString(params[0].trim());
12595         else
12596                 text = now.toLocaleString();
12597         createTiddlyElement(place,"span",null,null,text);
12598 };
12599
12600 config.macros.version.handler = function(place)
12601 {
12602         createTiddlyElement(place,"span",null,null,version.major + "." + version.minor + "." + version.revision + (version.beta ? " (beta " + version.beta + ")" : ""));
12603 };
12604
12605 config.macros.list.handler = function(place,macroName,params)
12606 {
12607         var type = params[0] ? params[0] : "all";
12608         var list = document.createElement("ul");
12609         place.appendChild(list);
12610         if(this[type].prompt)
12611                 createTiddlyElement(list,"li",null,"listTitle",this[type].prompt);
12612         var results;
12613         if(this[type].handler)
12614                 results = this[type].handler(params);
12615         for(var t = 0; t < results.length; t++) {
12616                 var li = document.createElement("li");
12617                 list.appendChild(li);
12618                 createTiddlyLink(li,typeof results[t] == "string" ? results[t] : results[t].title,true);
12619         }
12620 };
12621
12622 config.macros.list.all.handler = function(params)
12623 {
12624         return store.reverseLookup("tags","excludeLists",false,"title");
12625 };
12626
12627 config.macros.list.missing.handler = function(params)
12628 {
12629         return store.getMissingLinks();
12630 };
12631
12632 config.macros.list.orphans.handler = function(params)
12633 {
12634         return store.getOrphans();
12635 };
12636
12637 config.macros.list.shadowed.handler = function(params)
12638 {
12639         return store.getShadowed();
12640 };
12641
12642 config.macros.list.touched.handler = function(params)
12643 {
12644         return store.getTouched();
12645 };
12646
12647 config.macros.allTags.handler = function(place,macroName,params)
12648 {
12649         var tags = store.getTags(params[0]);
12650         var ul = createTiddlyElement(place,"ul");
12651         if(tags.length == 0)
12652                 createTiddlyElement(ul,"li",null,"listTitle",this.noTags);
12653         for(var t=0; t<tags.length; t++) {
12654                 var title = tags[t][0];
12655                 var info = getTiddlyLinkInfo(title);
12656                 var li =createTiddlyElement(ul,"li");
12657                 var btn = createTiddlyButton(li,title + " (" + tags[t][1] + ")",this.tooltip.format([title]),onClickTag,info.classes);
12658                 btn.setAttribute("tag",title);
12659                 btn.setAttribute("refresh","link");
12660                 btn.setAttribute("tiddlyLink",title);
12661         }
12662 };
12663
12664 config.macros.timeline.handler = function(place,macroName,params)
12665 {
12666         var field = params[0] ? params[0] : "modified";
12667         var tiddlers = store.reverseLookup("tags","excludeLists",false,field);
12668         var lastDay = "";
12669         var last = params[1] ? tiddlers.length-Math.min(tiddlers.length,parseInt(params[1])) : 0;
12670         for(var t=tiddlers.length-1; t>=last; t--) {
12671                 var tiddler = tiddlers[t];
12672                 var theDay = tiddler[field].convertToLocalYYYYMMDDHHMM().substr(0,8);
12673                 if(theDay != lastDay) {
12674                         var theDateList = document.createElement("ul");
12675                         place.appendChild(theDateList);
12676                         createTiddlyElement(theDateList,"li",null,"listTitle",tiddler[field].formatString(this.dateFormat));
12677                         lastDay = theDay;
12678                 }
12679                 var theDateListItem = createTiddlyElement(theDateList,"li",null,"listLink");
12680                 theDateListItem.appendChild(createTiddlyLink(place,tiddler.title,true));
12681         }
12682 };
12683
12684 config.macros.search.handler = function(place,macroName,params)
12685 {
12686         var searchTimeout = null;
12687         var btn = createTiddlyButton(place,this.label,this.prompt,this.onClick);
12688         var txt = createTiddlyElement(place,"input",null,"txtOptionInput");
12689         if(params[0])
12690                 txt.value = params[0];
12691         txt.onkeyup = this.onKeyPress;
12692         txt.onfocus = this.onFocus;
12693         txt.setAttribute("size",this.sizeTextbox);
12694         txt.setAttribute("accessKey",this.accessKey);
12695         txt.setAttribute("autocomplete","off");
12696         txt.setAttribute("lastSearchText","");
12697         if(config.browser.isSafari) {
12698                 txt.setAttribute("type","search");
12699                 txt.setAttribute("results","5");
12700         } else {
12701                 txt.setAttribute("type","text");
12702         }
12703 };
12704
12705 // Global because there's only ever one outstanding incremental search timer
12706 config.macros.search.timeout = null;
12707
12708 config.macros.search.doSearch = function(txt)
12709 {
12710         if(txt.value.length > 0) {
12711                 story.search(txt.value,config.options.chkCaseSensitiveSearch,config.options.chkRegExpSearch);
12712                 txt.setAttribute("lastSearchText",txt.value);
12713         }
12714 };
12715
12716 config.macros.search.onClick = function(e)
12717 {
12718         config.macros.search.doSearch(this.nextSibling);
12719         return false;
12720 };
12721
12722 config.macros.search.onKeyPress = function(e)
12723 {
12724         if(!e) var e = window.event;
12725         switch(e.keyCode) {
12726                 case 13: // Ctrl-Enter
12727                 case 10: // Ctrl-Enter on IE PC
12728                         config.macros.search.doSearch(this);
12729                         break;
12730                 case 27: // Escape
12731                         this.value = "";
12732                         clearMessage();
12733                         break;
12734         }
12735         if(this.value.length > 2) {
12736                 if(this.value != this.getAttribute("lastSearchText")) {
12737                         if(config.macros.search.timeout)
12738                                 clearTimeout(config.macros.search.timeout);
12739                         var txt = this;
12740                         config.macros.search.timeout = setTimeout(function() {config.macros.search.doSearch(txt);},500);
12741                 }
12742         } else {
12743                 if(config.macros.search.timeout)
12744                         clearTimeout(config.macros.search.timeout);
12745         }
12746 };
12747
12748 config.macros.search.onFocus = function(e)
12749 {
12750         this.select();
12751 };
12752
12753 config.macros.tiddler.handler = function(place,macroName,params,wikifier,paramString,tiddler)
12754 {
12755         params = paramString.parseParams("name",null,true,false,true);
12756         var names = params[0]["name"];
12757         var tiddlerName = names[0];
12758         var className = names[1] ? names[1] : null;
12759         var args = params[0]["with"];
12760         var wrapper = createTiddlyElement(place,"span",null,className);
12761         if(!args) {
12762                 wrapper.setAttribute("refresh","content");
12763                 wrapper.setAttribute("tiddler",tiddlerName);
12764         }
12765         var text = store.getTiddlerText(tiddlerName);
12766         if(text) {
12767                 var stack = config.macros.tiddler.tiddlerStack;
12768                 if(stack.indexOf(tiddlerName) !== -1)
12769                         return;
12770                 stack.push(tiddlerName);
12771                 try {
12772                         var n = args ? Math.min(args.length,9) : 0;
12773                         for(var i=0; i<n; i++) {
12774                                 var placeholderRE = new RegExp("\\$" + (i + 1),"mg");
12775                                 text = text.replace(placeholderRE,args[i]);
12776                         }
12777                         config.macros.tiddler.renderText(wrapper,text,tiddlerName,params);
12778                 } finally {
12779                         stack.pop();
12780                 }
12781         }
12782 };
12783
12784 config.macros.tiddler.renderText = function(place,text,tiddlerName,params) 
12785 {
12786         wikify(text,place,null,store.getTiddler(tiddlerName));
12787 };
12788
12789 config.macros.tiddler.tiddlerStack = [];
12790
12791 config.macros.tag.handler = function(place,macroName,params)
12792 {
12793         createTagButton(place,params[0]);
12794 };
12795
12796 config.macros.tags.handler = function(place,macroName,params,wikifier,paramString,tiddler)
12797 {
12798         params = paramString.parseParams("anon",null,true,false,false);
12799         var theList = createTiddlyElement(place,"ul");
12800         var title = getParam(params,"anon","");
12801         if(title && store.tiddlerExists(title))
12802                 tiddler = store.getTiddler(title);
12803         var sep = getParam(params,"sep"," ");
12804         var lingo = config.views.wikified.tag;
12805         var prompt = tiddler.tags.length == 0 ? lingo.labelNoTags : lingo.labelTags;
12806         createTiddlyElement(theList,"li",null,"listTitle",prompt.format([tiddler.title]));
12807         for(var t=0; t<tiddler.tags.length; t++) {
12808                 createTagButton(createTiddlyElement(theList,"li"),tiddler.tags[t],tiddler.title);
12809                 if(t<tiddler.tags.length-1)
12810                         createTiddlyText(theList,sep);
12811         }
12812 };
12813
12814 config.macros.tagging.handler = function(place,macroName,params,wikifier,paramString,tiddler)
12815 {
12816         params = paramString.parseParams("anon",null,true,false,false);
12817         var theList = createTiddlyElement(place,"ul");
12818         var title = getParam(params,"anon","");
12819         if(title == "" && tiddler instanceof Tiddler)
12820                 title = tiddler.title;
12821         var sep = getParam(params,"sep"," ");
12822         theList.setAttribute("title",this.tooltip.format([title]));
12823         var tagged = store.getTaggedTiddlers(title);
12824         var prompt = tagged.length == 0 ? this.labelNotTag : this.label;
12825         createTiddlyElement(theList,"li",null,"listTitle",prompt.format([title,tagged.length]));
12826         for(var t=0; t<tagged.length; t++) {
12827                 createTiddlyLink(createTiddlyElement(theList,"li"),tagged[t].title,true);
12828                 if(t<tagged.length-1)
12829                         createTiddlyText(theList,sep);
12830         }
12831 };
12832
12833 config.macros.closeAll.handler = function(place)
12834 {
12835         createTiddlyButton(place,this.label,this.prompt,this.onClick);
12836 };
12837
12838 config.macros.closeAll.onClick = function(e)
12839 {
12840         story.closeAllTiddlers();
12841         return false;
12842 };
12843
12844 config.macros.permaview.handler = function(place)
12845 {
12846         createTiddlyButton(place,this.label,this.prompt,this.onClick);
12847 };
12848
12849 config.macros.permaview.onClick = function(e)
12850 {
12851         story.permaView();
12852         return false;
12853 };
12854
12855 config.macros.saveChanges.handler = function(place)
12856 {
12857         if(!readOnly)
12858                 createTiddlyButton(place,this.label,this.prompt,this.onClick,null,null,this.accessKey);
12859 };
12860
12861 config.macros.saveChanges.onClick = function(e)
12862 {
12863         saveChanges();
12864         return false;
12865 };
12866
12867 config.macros.slider.onClickSlider = function(e)
12868 {
12869         if(!e) var e = window.event;
12870         var n = this.nextSibling;
12871         var cookie = n.getAttribute("cookie");
12872         var isOpen = n.style.display != "none";
12873         if(config.options.chkAnimate && anim && typeof Slider == "function")
12874                 anim.startAnimating(new Slider(n,!isOpen,null,"none"));
12875         else
12876                 n.style.display = isOpen ? "none" : "block";
12877         config.options[cookie] = !isOpen;
12878         saveOptionCookie(cookie);
12879         return false;
12880 };
12881
12882 config.macros.slider.createSlider = function(place,cookie,title,tooltip)
12883 {
12884         var cookie = cookie ? cookie : "";
12885         var btn = createTiddlyButton(place,title,tooltip,this.onClickSlider);
12886         var panel = createTiddlyElement(null,"div",null,"sliderPanel");
12887         panel.setAttribute("cookie",cookie);
12888         panel.style.display = config.options[cookie] ? "block" : "none";
12889         place.appendChild(panel);
12890         return panel;
12891 };
12892
12893 config.macros.slider.handler = function(place,macroName,params)
12894 {
12895         var panel = this.createSlider(place,params[0],params[2],params[3]);
12896         var text = store.getTiddlerText(params[1]);
12897         panel.setAttribute("refresh","content");
12898         panel.setAttribute("tiddler",params[1]);
12899         if(text)
12900                 wikify(text,panel,null,store.getTiddler(params[1]));
12901 };
12902
12903 config.macros.option.genericCreate = function(place,type,opt,className,desc)
12904 {
12905         var typeInfo = config.macros.option.types[type];
12906         var c = document.createElement(typeInfo.elementType);
12907         if(typeInfo.typeValue)
12908                 c.setAttribute("type",typeInfo.typeValue);
12909         c[typeInfo.eventName] = typeInfo.onChange;
12910         c.setAttribute("option",opt);
12911         if(className)
12912                 c.className = className;
12913         else
12914                 c.className = typeInfo.className;
12915         if(config.optionsDesc[opt])
12916                 c.setAttribute("title",config.optionsDesc[opt]);
12917         place.appendChild(c);
12918         if(desc != "no")
12919                 createTiddlyText(place,config.optionsDesc[opt] ? config.optionsDesc[opt] : opt);
12920         c[typeInfo.valueField] = config.options[opt];
12921         return c;
12922 };
12923
12924 config.macros.option.genericOnChange = function(e)
12925 {
12926         var opt = this.getAttribute("option");
12927         if(opt) {
12928                 var optType = opt.substr(0,3);
12929                 var handler = config.macros.option.types[optType];
12930                 if (handler.elementType && handler.valueField)
12931                         config.macros.option.propagateOption(opt,handler.valueField,this[handler.valueField],handler.elementType);
12932                 }
12933         return true;
12934 };
12935
12936 config.macros.option.types = {
12937         'txt': {
12938                 elementType: "input",
12939                 valueField: "value",
12940                 eventName: "onkeyup",
12941                 className: "txtOptionInput",
12942                 create: config.macros.option.genericCreate,
12943                 onChange: config.macros.option.genericOnChange
12944         },
12945         'chk': {
12946                 elementType: "input",
12947                 valueField: "checked",
12948                 eventName: "onclick",
12949                 className: "chkOptionInput",
12950                 typeValue: "checkbox",
12951                 create: config.macros.option.genericCreate,
12952                 onChange: config.macros.option.genericOnChange
12953         }
12954 };
12955
12956 config.macros.option.propagateOption = function(opt,valueField,value,elementType)
12957 {
12958         config.options[opt] = value;
12959         saveOptionCookie(opt);
12960         var nodes = document.getElementsByTagName(elementType);
12961         for(var t=0; t<nodes.length; t++) {
12962                 var optNode = nodes[t].getAttribute("option");
12963                 if(opt == optNode)
12964                         nodes[t][valueField] = value;
12965                 }
12966 };
12967
12968 config.macros.option.handler = function(place,macroName,params,wikifier,paramString,tiddler)
12969 {
12970         params = paramString.parseParams("anon",null,true,false,false);
12971         var opt = (params[1] && params[1].name == "anon") ? params[1].value : getParam(params,"name",null);
12972         var className = (params[2] && params[2].name == "anon") ? params[2].value : getParam(params,"class",null);
12973         var desc = getParam(params,"desc","no");
12974         var type = opt.substr(0,3);
12975         var h = config.macros.option.types[type];
12976         if (h && h.create)
12977                 h.create(place,type,opt,className,desc);
12978 };
12979
12980 config.macros.options.handler = function(place,macroName,params,wikifier,paramString,tiddler)
12981 {
12982         params = paramString.parseParams("anon",null,true,false,false);
12983         var showUnknown = getParam(params,"showUnknown","no");
12984         var wizard = new Wizard();
12985         wizard.createWizard(place,this.wizardTitle);
12986         wizard.addStep(this.step1Title,this.step1Html);
12987         var markList = wizard.getElement("markList");
12988         var chkUnknown = wizard.getElement("chkUnknown");
12989         chkUnknown.checked = showUnknown == "yes";
12990         chkUnknown.onchange = this.onChangeUnknown;
12991         var listWrapper = document.createElement("div");
12992         markList.parentNode.insertBefore(listWrapper,markList);
12993         wizard.setValue("listWrapper",listWrapper);
12994         this.refreshOptions(listWrapper,showUnknown == "yes");
12995 };
12996
12997 config.macros.options.refreshOptions = function(listWrapper,showUnknown)
12998 {       
12999         var opts = [];
13000         for(var n in config.options) {
13001                 var opt = {};
13002                 opt.option = "";
13003                 opt.name = n;
13004                 opt.lowlight = !config.optionsDesc[n];
13005                 opt.description = opt.lowlight ? this.unknownDescription : config.optionsDesc[n];
13006                 if(!opt.lowlight || showUnknown)
13007                         opts.push(opt);
13008         }
13009         opts.sort(function(a,b) {return a.name.substr(3) < b.name.substr(3) ? -1 : (a.name.substr(3) == b.name.substr(3) ? 0 : +1);});
13010         var listview = ListView.create(listWrapper,opts,this.listViewTemplate);
13011         for(n=0; n<opts.length; n++) {
13012                 var type = opts[n].name.substr(0,3);
13013                 var h = config.macros.option.types[type];
13014                 if (h && h.create) {
13015                         h.create(opts[n].colElements['option'],type,opts[n].name,null,"no");
13016                 }
13017         }
13018 };
13019
13020 config.macros.options.onChangeUnknown = function(e)
13021 {
13022         var wizard = new Wizard(this);
13023         var listWrapper = wizard.getValue("listWrapper");
13024         removeChildren(listWrapper);
13025         config.macros.options.refreshOptions(listWrapper,this.checked);
13026         return false;
13027 };
13028
13029 config.macros.newTiddler.createNewTiddlerButton = function(place,title,params,label,prompt,accessKey,newFocus,isJournal)
13030 {
13031         var tags = [];
13032         for(var t=1; t<params.length; t++) {
13033                 if((params[t].name == "anon" && t != 1) || (params[t].name == "tag"))
13034                         tags.push(params[t].value);
13035         }
13036         label = getParam(params,"label",label);
13037         prompt = getParam(params,"prompt",prompt);
13038         accessKey = getParam(params,"accessKey",accessKey);
13039         newFocus = getParam(params,"focus",newFocus);
13040         var customFields = getParam(params,"fields","");
13041         if(!customFields && !store.isShadowTiddler(title))
13042                 customFields = String.encodeHashMap(config.defaultCustomFields);
13043         var btn = createTiddlyButton(place,label,prompt,this.onClickNewTiddler,null,null,accessKey);
13044         btn.setAttribute("newTitle",title);
13045         btn.setAttribute("isJournal",isJournal ? "true" : "false");
13046         if(tags.length > 0)
13047                 btn.setAttribute("params",tags.join("|"));
13048         btn.setAttribute("newFocus",newFocus);
13049         btn.setAttribute("newTemplate",getParam(params,"template",DEFAULT_EDIT_TEMPLATE));
13050         if(customFields !== "")
13051                 btn.setAttribute("customFields",customFields);
13052         var text = getParam(params,"text");
13053         if(text !== undefined) 
13054                 btn.setAttribute("newText",text);
13055         return btn;
13056 };
13057
13058 config.macros.newTiddler.onClickNewTiddler = function()
13059 {
13060         var title = this.getAttribute("newTitle");
13061         if(this.getAttribute("isJournal") == "true") {
13062                 var now = new Date();
13063                 title = now.formatString(title.trim());
13064         }
13065         var params = this.getAttribute("params");
13066         var tags = params ? params.split("|") : [];
13067         var focus = this.getAttribute("newFocus");
13068         var template = this.getAttribute("newTemplate");
13069         var customFields = this.getAttribute("customFields");
13070         story.displayTiddler(null,title,template,false,null,null);
13071         var tiddlerElem = document.getElementById(story.idPrefix + title);
13072         if(customFields)
13073                 story.addCustomFields(tiddlerElem,customFields);
13074         var text = this.getAttribute("newText");
13075         if(typeof text == "string")
13076                 story.getTiddlerField(title,"text").value = text.format([title]);
13077         for(var t=0;t<tags.length;t++)
13078                 story.setTiddlerTag(title,tags[t],+1);
13079         story.focusTiddler(title,focus);
13080         return false;
13081 };
13082
13083 config.macros.newTiddler.handler = function(place,macroName,params,wikifier,paramString,tiddler)
13084 {
13085         if(!readOnly) {
13086                 params = paramString.parseParams("anon",null,true,false,false);
13087                 var title = params[1] && params[1].name == "anon" ? params[1].value : this.title;
13088                 title = getParam(params,"title",title);
13089                 this.createNewTiddlerButton(place,title,params,this.label,this.prompt,this.accessKey,"title",false);
13090         }
13091 };
13092
13093 config.macros.newJournal.handler = function(place,macroName,params,wikifier,paramString,tiddler)
13094 {
13095         if(!readOnly) {
13096                 params = paramString.parseParams("anon",null,true,false,false);
13097                 var title = params[1] && params[1].name == "anon" ? params[1].value : "";
13098                 title = getParam(params,"title",title);
13099                 config.macros.newTiddler.createNewTiddlerButton(place,title,params,this.label,this.prompt,this.accessKey,"text",true);
13100         }
13101 };
13102
13103 config.macros.sparkline.handler = function(place,macroName,params)
13104 {
13105         var data = [];
13106         var min = 0;
13107         var max = 0;
13108         for(var t=0; t<params.length; t++) {
13109                 var v = parseInt(params[t]);
13110                 if(v < min)
13111                         min = v;
13112                 if(v > max)
13113                         max = v;
13114                 data.push(v);
13115         }
13116         if(data.length < 1)
13117                 return;
13118         var box = createTiddlyElement(place,"span",null,"sparkline",String.fromCharCode(160));
13119         box.title = data.join(",");
13120         var w = box.offsetWidth;
13121         var h = box.offsetHeight;
13122         box.style.paddingRight = (data.length * 2 - w) + "px";
13123         box.style.position = "relative";
13124         for(var d=0; d<data.length; d++) {
13125                 var tick = document.createElement("img");
13126                 tick.border = 0;
13127                 tick.className = "sparktick";
13128                 tick.style.position = "absolute";
13129                 tick.src = "data:image/gif,GIF89a%01%00%01%00%91%FF%00%FF%FF%FF%00%00%00%C0%C0%C0%00%00%00!%F9%04%01%00%00%02%00%2C%00%00%00%00%01%00%01%00%40%02%02T%01%00%3B";
13130                 tick.style.left = d*2 + "px";
13131                 tick.style.width = "2px";
13132                 var v = Math.floor(((data[d] - min)/(max-min)) * h);
13133                 tick.style.top = (h-v) + "px";
13134                 tick.style.height = v + "px";
13135                 box.appendChild(tick);
13136         }
13137 };
13138
13139 config.macros.tabs.handler = function(place,macroName,params)
13140 {
13141         var cookie = params[0];
13142         var numTabs = (params.length-1)/3;
13143         var wrapper = createTiddlyElement(null,"div",null,cookie);
13144         var tabset = createTiddlyElement(wrapper,"div",null,"tabset");
13145         tabset.setAttribute("cookie",cookie);
13146         var validTab = false;
13147         for(var t=0; t<numTabs; t++) {
13148                 var label = params[t*3+1];
13149                 var prompt = params[t*3+2];
13150                 var content = params[t*3+3];
13151                 var tab = createTiddlyButton(tabset,label,prompt,this.onClickTab,"tab tabUnselected");
13152                 tab.setAttribute("tab",label);
13153                 tab.setAttribute("content",content);
13154                 tab.title = prompt;
13155                 if(config.options[cookie] == label)
13156                         validTab = true;
13157         }
13158         if(!validTab)
13159                 config.options[cookie] = params[1];
13160         place.appendChild(wrapper);
13161         this.switchTab(tabset,config.options[cookie]);
13162 };
13163
13164 config.macros.tabs.onClickTab = function(e)
13165 {
13166         config.macros.tabs.switchTab(this.parentNode,this.getAttribute("tab"));
13167         return false;
13168 };
13169
13170 config.macros.tabs.switchTab = function(tabset,tab)
13171 {
13172         var cookie = tabset.getAttribute("cookie");
13173         var theTab = null;
13174         var nodes = tabset.childNodes;
13175         for(var t=0; t<nodes.length; t++) {
13176                 if(nodes[t].getAttribute && nodes[t].getAttribute("tab") == tab) {
13177                         theTab = nodes[t];
13178                         theTab.className = "tab tabSelected";
13179                 } else {
13180                         nodes[t].className = "tab tabUnselected";
13181                 }
13182         }
13183         if(theTab) {
13184                 if(tabset.nextSibling && tabset.nextSibling.className == "tabContents")
13185                         removeNode(tabset.nextSibling);
13186                 var tabContent = createTiddlyElement(null,"div",null,"tabContents");
13187                 tabset.parentNode.insertBefore(tabContent,tabset.nextSibling);
13188                 var contentTitle = theTab.getAttribute("content");
13189                 wikify(store.getTiddlerText(contentTitle),tabContent,null,store.getTiddler(contentTitle));
13190                 if(cookie) {
13191                         config.options[cookie] = tab;
13192                         saveOptionCookie(cookie);
13193                 }
13194         }
13195 };
13196
13197 // <<gradient [[tiddler name]] vert|horiz rgb rgb rgb rgb... >>
13198 config.macros.gradient.handler = function(place,macroName,params,wikifier)
13199 {
13200         var terminator = ">>";
13201         var panel;
13202         if(wikifier)
13203                 panel = createTiddlyElement(place,"div",null,"gradient");
13204         else
13205                 panel = place;
13206         panel.style.position = "relative";
13207         panel.style.overflow = "hidden";
13208         panel.style.zIndex = "0";
13209         var t;
13210         if(wikifier) {
13211                 var styles = config.formatterHelpers.inlineCssHelper(wikifier);
13212                 config.formatterHelpers.applyCssHelper(panel,styles);
13213         }
13214         var colours = [];
13215         for(t=1; t<params.length; t++) {
13216                 var c = new RGB(params[t]);
13217                 if(c)
13218                         colours.push(c);
13219         }
13220         drawGradient(panel,params[0] != "vert",colours);
13221         if(wikifier)
13222                 wikifier.subWikify(panel,terminator);
13223         if(document.all) {
13224                 panel.style.height = "100%";
13225                 panel.style.width = "100%";
13226         }
13227 };
13228
13229 config.macros.message.handler = function(place,macroName,params)
13230 {
13231         if(params[0]) {
13232                 var m = config;
13233                 var p = params[0].split(".");
13234                 for(var t=0; t<p.length; t++) {
13235                         if(p[t] in m)
13236                                 m = m[p[t]];
13237                         else
13238                                 break;
13239                 }
13240                 createTiddlyText(place,m.toString().format(params.splice(1)));
13241         }
13242 };
13243
13244 config.macros.view.handler = function(place,macroName,params,wikifier,paramString,tiddler)
13245 {
13246         if((tiddler instanceof Tiddler) && params[0]) {
13247                 var value = store.getValue(tiddler,params[0]);
13248                 if(value != undefined) {
13249                         switch(params[1]) {
13250                                 case undefined:
13251                                         highlightify(value,place,highlightHack,tiddler);
13252                                         break;
13253                                 case "link":
13254                                         createTiddlyLink(place,value,true);
13255                                         break;
13256                                 case "wikified":
13257                                         wikify(value,place,highlightHack,tiddler);
13258                                         break;
13259                                 case "date":
13260                                         value = Date.convertFromYYYYMMDDHHMM(value);
13261                                         createTiddlyText(place,value.formatString(params[2] ? params[2] : config.views.wikified.dateFormat));
13262                                         break;
13263                         }
13264                 }
13265         }
13266 };
13267
13268 config.macros.edit.handler = function(place,macroName,params,wikifier,paramString,tiddler)
13269 {
13270         var field = params[0];
13271         var rows = params[1];
13272         if((tiddler instanceof Tiddler) && field) {
13273                 story.setDirty(tiddler.title,true);
13274                 if(field != "text" && !rows) {
13275                         var e = createTiddlyElement(null,"input");
13276                         if(tiddler.isReadOnly())
13277                                 e.setAttribute("readOnly","readOnly");
13278                         e.setAttribute("edit",field);
13279                         e.setAttribute("type","text");
13280                         var v = store.getValue(tiddler,field);
13281                         if(!v) 
13282                                 v = "";
13283                         e.value = v;
13284                         e.setAttribute("size","40");
13285                         e.setAttribute("autocomplete","off");
13286                         place.appendChild(e);
13287                 } else {
13288                         var wrapper1 = createTiddlyElement(null,"fieldset",null,"fieldsetFix");
13289                         var wrapper2 = createTiddlyElement(wrapper1,"div");
13290                         var e = createTiddlyElement(wrapper2,"textarea");
13291                         if(tiddler.isReadOnly())
13292                                 e.setAttribute("readOnly","readOnly");
13293                         var v = store.getValue(tiddler,field);
13294                         if(!v) 
13295                                 v = "";
13296                         e.value = v;
13297                         var rows = rows ? rows : 10;
13298                         var lines = v.match(/\n/mg);
13299                         var maxLines = Math.max(parseInt(config.options.txtMaxEditRows),5);
13300                         if(lines != null && lines.length > rows)
13301                                 rows = lines.length + 5;
13302                         rows = Math.min(rows,maxLines);
13303                         e.setAttribute("rows",rows);
13304                         e.setAttribute("edit",field);
13305                         place.appendChild(wrapper1);
13306                 }
13307         }
13308 };
13309
13310 config.macros.tagChooser.onClick = function(e)
13311 {
13312         if(!e) var e = window.event;
13313         var lingo = config.views.editor.tagChooser;
13314         var popup = Popup.create(this);
13315         var tags = store.getTags();
13316         if(tags.length == 0)
13317                 createTiddlyText(createTiddlyElement(popup,"li"),lingo.popupNone);
13318         for(var t=0; t<tags.length; t++) {
13319                 var theTag = createTiddlyButton(createTiddlyElement(popup,"li"),tags[t][0],lingo.tagTooltip.format([tags[t][0]]),config.macros.tagChooser.onTagClick);
13320                 theTag.setAttribute("tag",tags[t][0]);
13321                 theTag.setAttribute("tiddler",this.getAttribute("tiddler"));
13322         }
13323         Popup.show();
13324         e.cancelBubble = true;
13325         if(e.stopPropagation) e.stopPropagation();
13326         return false;
13327 };
13328
13329 config.macros.tagChooser.onTagClick = function(e)
13330 {
13331         if(!e) var e = window.event;
13332         var tag = this.getAttribute("tag");
13333         var title = this.getAttribute("tiddler");
13334         if(!readOnly)
13335                 story.setTiddlerTag(title,tag,0);
13336         return false;
13337 };
13338
13339 config.macros.tagChooser.handler = function(place,macroName,params,wikifier,paramString,tiddler)
13340 {
13341         if(tiddler instanceof Tiddler) {
13342                 var title = tiddler.title;
13343                 var lingo = config.views.editor.tagChooser;
13344                 var btn = createTiddlyButton(place,lingo.text,lingo.tooltip,this.onClick);
13345                 btn.setAttribute("tiddler",title);
13346         }
13347 };
13348
13349 // Create a toolbar command button
13350 config.macros.toolbar.createCommand = function(place,commandName,tiddler,theClass)
13351 {
13352         if(typeof commandName != "string") {
13353                 var c = null;
13354                 for(var t in config.commands) {
13355                         if(config.commands[t] == commandName)
13356                                 c = t;
13357                 }
13358                 commandName = c;
13359         }
13360         if((tiddler instanceof Tiddler) && (typeof commandName == "string")) {
13361                 var command = config.commands[commandName];
13362                 if(command.isEnabled ? command.isEnabled(tiddler) : this.isCommandEnabled(command,tiddler)) {
13363                         var text = command.getText ? command.getText(tiddler) : this.getCommandText(command,tiddler);
13364                         var tooltip = command.getTooltip ? command.getTooltip(tiddler) : this.getCommandTooltip(command,tiddler);
13365                         var cmd;
13366                         switch(command.type) {
13367                                 case "popup":
13368                                         cmd = this.onClickPopup;
13369                                         break;
13370                                 case "command":
13371                                 default:
13372                                         cmd = this.onClickCommand;
13373                                         break;
13374                         }
13375                         var btn = createTiddlyButton(null,text,tooltip,cmd);
13376                         btn.setAttribute("commandName",commandName);
13377                         btn.setAttribute("tiddler",tiddler.title);
13378                         if(theClass)
13379                                 addClass(btn,theClass);
13380                         place.appendChild(btn);
13381                 }
13382         }
13383 };
13384
13385 config.macros.toolbar.isCommandEnabled = function(command,tiddler)
13386 {
13387         var title = tiddler.title;
13388         var ro = tiddler.isReadOnly();
13389         var shadow = store.isShadowTiddler(title) && !store.tiddlerExists(title);
13390         return (!ro || (ro && !command.hideReadOnly)) && !(shadow && command.hideShadow);
13391 };
13392
13393 config.macros.toolbar.getCommandText = function(command,tiddler)
13394 {
13395         return tiddler.isReadOnly() && command.readOnlyText ? command.readOnlyText : command.text;
13396 };
13397
13398 config.macros.toolbar.getCommandTooltip = function(command,tiddler)
13399 {
13400         return tiddler.isReadOnly() && command.readOnlyTooltip ? command.readOnlyTooltip : command.tooltip;
13401 };
13402
13403 config.macros.toolbar.onClickCommand = function(e)
13404 {
13405         if(!e) var e = window.event;
13406         e.cancelBubble = true;
13407         if (e.stopPropagation) e.stopPropagation();
13408         var command = config.commands[this.getAttribute("commandName")];
13409         return command.handler(e,this,this.getAttribute("tiddler"));
13410 };
13411
13412 config.macros.toolbar.onClickPopup = function(e)
13413 {
13414         if(!e) var e = window.event;
13415         e.cancelBubble = true;
13416         if (e.stopPropagation) e.stopPropagation();
13417         var popup = Popup.create(this);
13418         var command = config.commands[this.getAttribute("commandName")];
13419         var title = this.getAttribute("tiddler");
13420         var tiddler = store.fetchTiddler(title);
13421         popup.setAttribute("tiddler",title);
13422         command.handlePopup(popup,title);
13423         Popup.show();
13424         return false;
13425 };
13426
13427 // Invoke the first command encountered from a given place that is tagged with a specified class
13428 config.macros.toolbar.invokeCommand = function(place,theClass,event)
13429 {
13430         var children = place.getElementsByTagName("a");
13431         for(var t=0; t<children.length; t++) {
13432                 var c = children[t];
13433                 if(hasClass(c,theClass) && c.getAttribute && c.getAttribute("commandName")) {
13434                         if(c.onclick instanceof Function)
13435                                 c.onclick.call(c,event);
13436                         break;
13437                 }
13438         }
13439 };
13440
13441 config.macros.toolbar.onClickMore = function(e)
13442 {
13443         var e = this.nextSibling;
13444         e.style.display = "inline";
13445         removeNode(this);
13446         return false;
13447 };
13448
13449 config.macros.toolbar.handler = function(place,macroName,params,wikifier,paramString,tiddler)
13450 {
13451         for(var t=0; t<params.length; t++) {
13452                 var c = params[t];
13453                 switch(c) {
13454                         case '>':
13455                                 var btn = createTiddlyButton(place,this.moreLabel,this.morePrompt,config.macros.toolbar.onClickMore);
13456                                 addClass(btn,"moreCommand");
13457                                 var e = createTiddlyElement(place,"span",null,"moreCommand");
13458                                 e.style.display = "none";
13459                                 place = e;
13460                                 break;
13461                         default:
13462                                 var theClass = "";
13463                                 switch(c.substr(0,1)) {
13464                                         case "+":
13465                                                 theClass = "defaultCommand";
13466                                                 c = c.substr(1);
13467                                                 break;
13468                                         case "-":
13469                                                 theClass = "cancelCommand";
13470                                                 c = c.substr(1);
13471                                                 break;
13472                                 }
13473                                 if(c in config.commands)
13474                                         this.createCommand(place,c,tiddler,theClass);
13475                                 break;
13476                 }
13477         }
13478 };
13479
13480 config.macros.refreshDisplay.handler = function(place)
13481 {
13482         createTiddlyButton(place,this.label,this.prompt,this.onClick);
13483 };
13484
13485 config.macros.refreshDisplay.onClick = function(e)
13486 {
13487         refreshAll();
13488         return false;
13489 };
13490
13491 config.macros.annotations.handler = function(place,macroName,params,wikifier,paramString,tiddler)
13492 {
13493         var title = tiddler ? tiddler.title : null;
13494         var a = title ? config.annotations[title] : null;
13495         if(!tiddler || !title || !a)
13496                 return;
13497         var text = a.format([title]);
13498         wikify(text,createTiddlyElement(place,"div",null,"annotation"),null,tiddler);
13499 };
13500
13501 //--
13502 //-- Menu and toolbar commands
13503 //--
13504
13505 config.commands.closeTiddler.handler = function(event,src,title)
13506 {
13507         story.closeTiddler(title,true);
13508         return false;
13509 };
13510
13511 config.commands.closeOthers.handler = function(event,src,title)
13512 {
13513         story.closeAllTiddlers(title);
13514         return false;
13515 };
13516
13517 config.commands.editTiddler.handler = function(event,src,title)
13518 {
13519         clearMessage();
13520         var tiddlerElem = document.getElementById(story.idPrefix + title);
13521         var fields = tiddlerElem.getAttribute("tiddlyFields");
13522         story.displayTiddler(null,title,DEFAULT_EDIT_TEMPLATE,false,null,fields);
13523         story.focusTiddler(title,"text");
13524         return false;
13525 };
13526
13527 config.commands.saveTiddler.handler = function(event,src,title)
13528 {
13529         var newTitle = story.saveTiddler(title,event.shiftKey);
13530         if(newTitle)
13531                 story.displayTiddler(null,newTitle);
13532         return false;
13533 };
13534
13535 config.commands.cancelTiddler.handler = function(event,src,title)
13536 {
13537         if(story.hasChanges(title) && !readOnly) {
13538                 if(!confirm(this.warning.format([title])))
13539                         return false;
13540         }
13541         story.setDirty(title,false);
13542         story.displayTiddler(null,title);
13543         return false;
13544 };
13545
13546 config.commands.deleteTiddler.handler = function(event,src,title)
13547 {
13548         var deleteIt = true;
13549         if (config.options.chkConfirmDelete)
13550                 deleteIt = confirm(this.warning.format([title]));
13551         if (deleteIt) {
13552                 store.removeTiddler(title);
13553                 story.closeTiddler(title,true);
13554                 autoSaveChanges();
13555         }
13556         return false;
13557 };
13558
13559 config.commands.permalink.handler = function(event,src,title)
13560 {
13561         var t = encodeURIComponent(String.encodeTiddlyLink(title));
13562         if(window.location.hash != t)
13563                 window.location.hash = t;
13564         return false;
13565 };
13566
13567 config.commands.references.handlePopup = function(popup,title)
13568 {
13569         var references = store.getReferringTiddlers(title);
13570         var c = false;
13571         for(var r=0; r<references.length; r++) {
13572                 if(references[r].title != title && !references[r].isTagged("excludeLists")) {
13573                         createTiddlyLink(createTiddlyElement(popup,"li"),references[r].title,true);
13574                         c = true;
13575                 }
13576         }
13577         if(!c)
13578                 createTiddlyText(createTiddlyElement(popup,"li",null,"disabled"),this.popupNone);
13579 };
13580
13581 config.commands.jump.handlePopup = function(popup,title)
13582 {
13583         story.forEachTiddler(function(title,element) {
13584                 createTiddlyLink(createTiddlyElement(popup,"li"),title,true,null,false,null,true);
13585                 });
13586 };
13587
13588 config.commands.syncing.handlePopup = function(popup,title)
13589 {
13590         var tiddler = store.fetchTiddler(title);
13591         if(!tiddler)
13592                 return;
13593         var serverType = tiddler.getServerType();
13594         var serverHost = tiddler.fields['server.host'];
13595         var serverWorkspace = tiddler.fields['server.workspace'];
13596         if(!serverWorkspace)
13597                 serverWorkspace = "";
13598         if(serverType) {
13599                 var e = createTiddlyElement(popup,"li",null,"popupMessage");
13600                 e.innerHTML = config.commands.syncing.currentlySyncing.format([serverType,serverHost,serverWorkspace]);
13601         } else {
13602                 createTiddlyElement(popup,"li",null,"popupMessage",config.commands.syncing.notCurrentlySyncing);
13603         }
13604         if(serverType) {
13605                 createTiddlyElement(createTiddlyElement(popup,"li",null,"listBreak"),"div");
13606                 var btn = createTiddlyButton(createTiddlyElement(popup,"li"),this.captionUnSync,null,config.commands.syncing.onChooseServer);
13607                 btn.setAttribute("tiddler",title);
13608                 btn.setAttribute("server.type","");
13609         }
13610         createTiddlyElement(createTiddlyElement(popup,"li",null,"listBreak"),"div");
13611         createTiddlyElement(popup,"li",null,"popupMessage",config.commands.syncing.chooseServer);
13612         var feeds = store.getTaggedTiddlers("systemServer","title");
13613         for(var t=0; t<feeds.length; t++) {
13614                 var f = feeds[t];
13615                 var feedServerType = store.getTiddlerSlice(f.title,"Type");
13616                 if(!feedServerType)
13617                         feedServerType = "file";
13618                 var feedServerHost = store.getTiddlerSlice(f.title,"URL");
13619                 if(!feedServerHost)
13620                         feedServerHost = "";
13621                 var feedServerWorkspace = store.getTiddlerSlice(f.title,"Workspace");
13622                 if(!feedServerWorkspace)
13623                         feedServerWorkspace = "";
13624                 var caption = f.title;
13625                 if(serverType == feedServerType && serverHost == feedServerHost && serverWorkspace == feedServerWorkspace) {
13626                         caption = config.commands.syncing.currServerMarker + caption;
13627                 } else {
13628                         caption = config.commands.syncing.notCurrServerMarker + caption;
13629                 }
13630                 btn = createTiddlyButton(createTiddlyElement(popup,"li"),caption,null,config.commands.syncing.onChooseServer);
13631                 btn.setAttribute("tiddler",title);
13632                 btn.setAttribute("server.type",feedServerType);
13633                 btn.setAttribute("server.host",feedServerHost);
13634                 btn.setAttribute("server.workspace",feedServerWorkspace);
13635         }
13636 };
13637
13638 config.commands.syncing.onChooseServer = function(e)
13639 {
13640         var tiddler = this.getAttribute("tiddler");
13641         var serverType = this.getAttribute("server.type");
13642         if(serverType) {
13643                 store.addTiddlerFields(tiddler,{
13644                         'server.type': serverType,
13645                         'server.host': this.getAttribute("server.host"),
13646                         'server.workspace': this.getAttribute("server.workspace")
13647                         });
13648         } else {
13649                 store.setValue(tiddler,'server',null);
13650         }
13651         return false;
13652 };
13653
13654 config.commands.fields.handlePopup = function(popup,title)
13655 {
13656         var tiddler = store.fetchTiddler(title);
13657         if(!tiddler)
13658                 return;
13659         var fields = {};
13660         store.forEachField(tiddler,function(tiddler,fieldName,value) {fields[fieldName] = value;},true);
13661         var items = [];
13662         for(var t in fields) {
13663                 items.push({field: t,value: fields[t]});
13664         }
13665         items.sort(function(a,b) {return a.field < b.field ? -1 : (a.field == b.field ? 0 : +1);});
13666         if(items.length > 0)
13667                 ListView.create(popup,items,this.listViewTemplate);
13668         else
13669                 createTiddlyElement(popup,"div",null,null,this.emptyText);
13670 };
13671
13672 //--
13673 //-- Tiddler() object
13674 //--
13675
13676 function Tiddler(title)
13677 {
13678         this.title = title;
13679         this.text = null;
13680         this.modifier = null;
13681         this.modified = new Date();
13682         this.created = new Date();
13683         this.links = [];
13684         this.linksUpdated = false;
13685         this.tags = [];
13686         this.fields = {};
13687         return this;
13688 }
13689
13690 Tiddler.prototype.getLinks = function()
13691 {
13692         if(this.linksUpdated==false)
13693                 this.changed();
13694         return this.links;
13695 };
13696
13697 // Returns the fields that are inherited in string field:"value" field2:"value2" format
13698 Tiddler.prototype.getInheritedFields = function()
13699 {
13700         var f = {};
13701         for(i in this.fields) {
13702                 if(i=="server.host" || i=="server.workspace" || i=="wikiformat"|| i=="server.type") {
13703                         f[i] = this.fields[i];
13704                 }
13705         }
13706         return String.encodeHashMap(f);
13707 };
13708
13709 // Increment the changeCount of a tiddler
13710 Tiddler.prototype.incChangeCount = function()
13711 {
13712         var c = this.fields['changecount'];
13713         c = c ? parseInt(c) : 0;
13714         this.fields['changecount'] = String(c+1);
13715 };
13716
13717 // Clear the changeCount of a tiddler
13718 Tiddler.prototype.clearChangeCount = function()
13719 {
13720         if(this.fields['changecount']) {
13721                 delete this.fields['changecount'];
13722         }
13723 };
13724
13725 // Returns true if the tiddler has been updated since the tiddler was created or downloaded
13726 Tiddler.prototype.isTouched = function()
13727 {
13728         var changeCount = this.fields['changecount'];
13729         if(changeCount === undefined)
13730                 changeCount = 0;
13731         return changeCount > 0;
13732 };
13733
13734 // Format the text for storage in an RSS item
13735 Tiddler.prototype.saveToRss = function(url)
13736 {
13737         var s = [];
13738         s.push("<item>");
13739         s.push("<title" + ">" + this.title.htmlEncode() + "</title" + ">");
13740         s.push("<description>" + wikifyStatic(this.text,null,this).htmlEncode() + "</description>");
13741         for(var t=0; t<this.tags.length; t++)
13742                 s.push("<category>" + this.tags[t] + "</category>");
13743         s.push("<link>" + url + "#" + encodeURIComponent(String.encodeTiddlyLink(this.title)) + "</link>");
13744         s.push("<pubDate>" + this.modified.toGMTString() + "</pubDate>");
13745         s.push("</item>");
13746         return s.join("\n");
13747 };
13748
13749 // Change the text and other attributes of a tiddler
13750 Tiddler.prototype.set = function(title,text,modifier,modified,tags,created,fields)
13751 {
13752         this.assign(title,text,modifier,modified,tags,created,fields);
13753         this.changed();
13754         return this;
13755 };
13756
13757 // Change the text and other attributes of a tiddler without triggered a tiddler.changed() call
13758 Tiddler.prototype.assign = function(title,text,modifier,modified,tags,created,fields)
13759 {
13760         if(title != undefined)
13761                 this.title = title;
13762         if(text != undefined)
13763                 this.text = text;
13764         if(modifier != undefined)
13765                 this.modifier = modifier;
13766         if(modified != undefined)
13767                 this.modified = modified;
13768         if(created != undefined)
13769                 this.created = created;
13770         if(fields != undefined)
13771                 this.fields = fields;
13772         if(tags != undefined)
13773                 this.tags = (typeof tags == "string") ? tags.readBracketedList() : tags;
13774         else if(this.tags == undefined)
13775                 this.tags = [];
13776         return this;
13777 };
13778
13779 // Get the tags for a tiddler as a string (space delimited, using [[brackets]] for tags containing spaces)
13780 Tiddler.prototype.getTags = function()
13781 {
13782         return String.encodeTiddlyLinkList(this.tags);
13783 };
13784
13785 // Test if a tiddler carries a tag
13786 Tiddler.prototype.isTagged = function(tag)
13787 {
13788         return this.tags.indexOf(tag) != -1;
13789 };
13790
13791 // Static method to convert "\n" to newlines, "\s" to "\"
13792 Tiddler.unescapeLineBreaks = function(text)
13793 {
13794         return text ? text.unescapeLineBreaks() : "";
13795 };
13796
13797 // Convert newlines to "\n", "\" to "\s"
13798 Tiddler.prototype.escapeLineBreaks = function()
13799 {
13800         return this.text.escapeLineBreaks();
13801 };
13802
13803 // Updates the secondary information (like links[] array) after a change to a tiddler
13804 Tiddler.prototype.changed = function()
13805 {
13806         this.links = [];
13807         var t = this.autoLinkWikiWords() ? 0 : 1;
13808         var tiddlerLinkRegExp = t==0 ? config.textPrimitives.tiddlerAnyLinkRegExp : config.textPrimitives.tiddlerForcedLinkRegExp;
13809         tiddlerLinkRegExp.lastIndex = 0;
13810         var formatMatch = tiddlerLinkRegExp.exec(this.text);
13811         while(formatMatch) {
13812                 var lastIndex = tiddlerLinkRegExp.lastIndex;
13813                 if(t==0 && formatMatch[1] && formatMatch[1] != this.title) {
13814                         // wikiWordLink
13815                         if(formatMatch.index > 0) {
13816                                 var preRegExp = new RegExp(config.textPrimitives.unWikiLink+"|"+config.textPrimitives.anyLetter,"mg");
13817                                 preRegExp.lastIndex = formatMatch.index-1;
13818                                 var preMatch = preRegExp.exec(this.text);
13819                                 if(preMatch.index != formatMatch.index-1)
13820                                         this.links.pushUnique(formatMatch[1]);
13821                         } else {
13822                                 this.links.pushUnique(formatMatch[1]);
13823                         }
13824                 }
13825                 else if(formatMatch[2-t] && !config.formatterHelpers.isExternalLink(formatMatch[3-t])) // titledBrackettedLink
13826                         this.links.pushUnique(formatMatch[3-t]);
13827                 else if(formatMatch[4-t] && formatMatch[4-t] != this.title) // brackettedLink
13828                         this.links.pushUnique(formatMatch[4-t]);
13829                 tiddlerLinkRegExp.lastIndex = lastIndex;
13830                 formatMatch = tiddlerLinkRegExp.exec(this.text);
13831         }
13832         this.linksUpdated = true;
13833 };
13834
13835 Tiddler.prototype.getSubtitle = function()
13836 {
13837         var theModifier = this.modifier;
13838         if(!theModifier)
13839                 theModifier = config.messages.subtitleUnknown;
13840         var theModified = this.modified;
13841         if(theModified)
13842                 theModified = theModified.toLocaleString();
13843         else
13844                 theModified = config.messages.subtitleUnknown;
13845         return config.messages.tiddlerLinkTooltip.format([this.title,theModifier,theModified]);
13846 };
13847
13848 Tiddler.prototype.isReadOnly = function()
13849 {
13850         return readOnly;
13851 };
13852
13853 Tiddler.prototype.autoLinkWikiWords = function()
13854 {
13855         return !(this.isTagged("systemConfig") || this.isTagged("excludeMissing"));
13856 };
13857
13858 Tiddler.prototype.generateFingerprint = function()
13859 {
13860         return "0x" + Crypto.hexSha1Str(this.text);
13861 };
13862
13863 Tiddler.prototype.getServerType = function()
13864 {
13865         var serverType = null;
13866         if(this.fields && this.fields['server.type'])
13867                 serverType = this.fields['server.type'];
13868         if(!serverType)
13869                 serverType = this.fields['wikiformat'];
13870         if(serverType && !config.adaptors[serverType])
13871                 serverType = null;
13872         return serverType;
13873 };
13874
13875 Tiddler.prototype.getAdaptor = function()
13876 {
13877         var serverType = this.getServerType();
13878         if(serverType)
13879                 return new config.adaptors[serverType];
13880         else
13881                 return null;
13882 };
13883
13884 //--
13885 //-- TiddlyWiki() object contains Tiddler()s
13886 //--
13887
13888 function TiddlyWiki()
13889 {
13890         var tiddlers = {}; // Hashmap by name of tiddlers
13891         this.tiddlersUpdated = false;
13892         this.namedNotifications = []; // Array of {name:,notify:} of notification functions
13893         this.notificationLevel = 0;
13894         this.slices = {}; // map tiddlerName->(map sliceName->sliceValue). Lazy.
13895         this.clear = function() {
13896                 tiddlers = {};
13897                 this.setDirty(false);
13898         };
13899         this.fetchTiddler = function(title) {
13900                 return tiddlers[title];
13901         };
13902         this.deleteTiddler = function(title) {
13903                 delete this.slices[title];
13904                 delete tiddlers[title];
13905         };
13906         this.addTiddler = function(tiddler) {
13907                 delete this.slices[tiddler.title];
13908                 tiddlers[tiddler.title] = tiddler;
13909         };
13910         this.forEachTiddler = function(callback) {
13911                 for(var t in tiddlers) {
13912                         var tiddler = tiddlers[t];
13913                         if(tiddler instanceof Tiddler)
13914                                 callback.call(this,t,tiddler);
13915                 }
13916         };
13917 }
13918
13919 TiddlyWiki.prototype.setDirty = function(dirty)
13920 {
13921         this.dirty = dirty;
13922 };
13923
13924 TiddlyWiki.prototype.isDirty = function()
13925 {
13926         return this.dirty;
13927 };
13928
13929 TiddlyWiki.prototype.suspendNotifications = function()
13930 {
13931         this.notificationLevel--;
13932 };
13933
13934 TiddlyWiki.prototype.resumeNotifications = function()
13935 {
13936         this.notificationLevel++;
13937 };
13938
13939 // Invoke the notification handlers for a particular tiddler
13940 TiddlyWiki.prototype.notify = function(title,doBlanket)
13941 {
13942         if(!this.notificationLevel) {
13943                 for(var t=0; t<this.namedNotifications.length; t++) {
13944                         var n = this.namedNotifications[t];
13945                         if((n.name == null && doBlanket) || (n.name == title))
13946                                 n.notify(title);
13947                 }
13948         }
13949 };
13950
13951 // Invoke the notification handlers for all tiddlers
13952 TiddlyWiki.prototype.notifyAll = function()
13953 {
13954         if(!this.notificationLevel) {
13955                 for(var t=0; t<this.namedNotifications.length; t++) {
13956                         var n = this.namedNotifications[t];
13957                         if(n.name)
13958                                 n.notify(n.name);
13959                 }
13960         }
13961 };
13962
13963 // Add a notification handler to a tiddler
13964 TiddlyWiki.prototype.addNotification = function(title,fn)
13965 {
13966         for(var i=0; i<this.namedNotifications.length; i++) {
13967                 if((this.namedNotifications[i].name == title) && (this.namedNotifications[i].notify == fn))
13968                         return this;
13969         }
13970         this.namedNotifications.push({name: title, notify: fn});
13971         return this;
13972 };
13973
13974 TiddlyWiki.prototype.removeTiddler = function(title)
13975 {
13976         var tiddler = this.fetchTiddler(title);
13977         if(tiddler) {
13978                 this.deleteTiddler(title);
13979                 this.notify(title,true);
13980                 this.setDirty(true);
13981         }
13982 };
13983
13984 TiddlyWiki.prototype.tiddlerExists = function(title)
13985 {
13986         var t = this.fetchTiddler(title);
13987         return t != undefined;
13988 };
13989
13990 TiddlyWiki.prototype.isShadowTiddler = function(title)
13991 {
13992         return typeof config.shadowTiddlers[title] == "string";
13993 };
13994
13995 TiddlyWiki.prototype.getTiddler = function(title)
13996 {
13997         var t = this.fetchTiddler(title);
13998         if(t != undefined)
13999                 return t;
14000         else
14001                 return null;
14002 };
14003
14004 TiddlyWiki.prototype.getTiddlerText = function(title,defaultText)
14005 {
14006         var tiddler = this.fetchTiddler(title);
14007         if(tiddler)
14008                 return tiddler.text;
14009         if(!title)
14010                 return defaultText;
14011         var pos = title.indexOf(config.textPrimitives.sliceSeparator);
14012         if(pos != -1) {
14013                 var slice = this.getTiddlerSlice(title.substr(0,pos),title.substr(pos + config.textPrimitives.sliceSeparator.length));
14014                 if(slice)
14015                         return slice;
14016         }
14017         if(this.isShadowTiddler(title))
14018                 return config.shadowTiddlers[title];
14019         if(defaultText != undefined)
14020                 return defaultText;
14021         return null;
14022 };
14023
14024 TiddlyWiki.prototype.slicesRE = /(?:[\'\/]*~?([\.\w]+)[\'\/]*\:[\'\/]*\s*(.*?)\s*$)|(?:\|[\'\/]*~?([\.\w]+)\:?[\'\/]*\|\s*(.*?)\s*\|)/gm;
14025
14026 // @internal
14027 TiddlyWiki.prototype.calcAllSlices = function(title)
14028 {
14029         var slices = {};
14030         var text = this.getTiddlerText(title,"");
14031         this.slicesRE.lastIndex = 0;
14032         do {
14033                 var m = this.slicesRE.exec(text);
14034                 if(m) {
14035                         if(m[1])
14036                                 slices[m[1]] = m[2];
14037                         else
14038                                 slices[m[3]] = m[4];
14039                 }
14040         } while(m);
14041         return slices;
14042 };
14043
14044 // Returns the slice of text of the given name
14045 TiddlyWiki.prototype.getTiddlerSlice = function(title,sliceName)
14046 {
14047         var slices = this.slices[title];
14048         if(!slices) {
14049                 slices = this.calcAllSlices(title);
14050                 this.slices[title] = slices;
14051         }
14052         return slices[sliceName];
14053 };
14054
14055 // Build an hashmap of the specified named slices of a tiddler
14056 TiddlyWiki.prototype.getTiddlerSlices = function(title,sliceNames)
14057 {
14058         var r = {};
14059         for(var t=0; t<sliceNames.length; t++) {
14060                 var slice = this.getTiddlerSlice(title,sliceNames[t]);
14061                 if(slice)
14062                         r[sliceNames[t]] = slice;
14063         }
14064         return r;
14065 };
14066
14067 TiddlyWiki.prototype.getRecursiveTiddlerText = function(title,defaultText,depth)
14068 {
14069         var bracketRegExp = new RegExp("(?:\\[\\[([^\\]]+)\\]\\])","mg");
14070         var text = this.getTiddlerText(title,null);
14071         if(text == null)
14072                 return defaultText;
14073         var textOut = [];
14074         var lastPos = 0;
14075         do {
14076                 var match = bracketRegExp.exec(text);
14077                 if(match) {
14078                         textOut.push(text.substr(lastPos,match.index-lastPos));
14079                         if(match[1]) {
14080                                 if(depth <= 0)
14081                                         textOut.push(match[1]);
14082                                 else
14083                                         textOut.push(this.getRecursiveTiddlerText(match[1],"[[" + match[1] + "]]",depth-1));
14084                         }
14085                         lastPos = match.index + match[0].length;
14086                 } else {
14087                         textOut.push(text.substr(lastPos));
14088                 }
14089         } while(match);
14090         return textOut.join("");
14091 };
14092
14093 TiddlyWiki.prototype.setTiddlerTag = function(title,status,tag)
14094 {
14095         var tiddler = this.fetchTiddler(title);
14096         if(tiddler) {
14097                 var t = tiddler.tags.indexOf(tag);
14098                 if(t != -1)
14099                         tiddler.tags.splice(t,1);
14100                 if(status)
14101                         tiddler.tags.push(tag);
14102                 tiddler.changed();
14103                 this.incChangeCount(title);
14104                 this.notify(title,true);
14105                 this.setDirty(true);
14106         }
14107 };
14108
14109 TiddlyWiki.prototype.addTiddlerFields = function(title,fields)
14110 {
14111         var tiddler = this.fetchTiddler(title);
14112         if(!tiddler)
14113                 return;
14114         merge(tiddler.fields,fields);
14115         tiddler.changed();
14116         this.incChangeCount(title);
14117         this.notify(title,true);
14118         this.setDirty(true);
14119 };
14120
14121 TiddlyWiki.prototype.saveTiddler = function(title,newTitle,newBody,modifier,modified,tags,fields,clearChangeCount,created)
14122 {
14123         var tiddler = this.fetchTiddler(title);
14124         if(tiddler) {
14125                 created = created ? created : tiddler.created; // Preserve created date
14126                 this.deleteTiddler(title);
14127         } else {
14128                 created = created ? created : modified;
14129                 tiddler = new Tiddler();
14130         }
14131         tiddler.set(newTitle,newBody,modifier,modified,tags,created,fields);
14132         this.addTiddler(tiddler);
14133         if(clearChangeCount)
14134                 tiddler.clearChangeCount();
14135         else
14136                 tiddler.incChangeCount();
14137         if(title != newTitle)
14138                 this.notify(title,true);
14139         this.notify(newTitle,true);
14140         this.setDirty(true);
14141         return tiddler;
14142 };
14143
14144 // Reset the sync status of a freshly synced tiddler
14145 TiddlyWiki.prototype.resetTiddler = function(title)
14146 {
14147         var tiddler = this.fetchTiddler(title);
14148         if(tiddler) {
14149                 tiddler.clearChangeCount();
14150                 this.notify(title,true);
14151                 this.setDirty(true);
14152         }
14153 };
14154
14155 TiddlyWiki.prototype.incChangeCount = function(title)
14156 {
14157         var tiddler = this.fetchTiddler(title);
14158         if(tiddler)
14159                 tiddler.incChangeCount();
14160 };
14161
14162 TiddlyWiki.prototype.createTiddler = function(title)
14163 {
14164         var tiddler = this.fetchTiddler(title);
14165         if(!tiddler) {
14166                 tiddler = new Tiddler();
14167                 tiddler.title = title;
14168                 this.addTiddler(tiddler);
14169                 this.setDirty(true);
14170         }
14171         return tiddler;
14172 };
14173
14174 // Load contents of a TiddlyWiki from an HTML DIV
14175 TiddlyWiki.prototype.loadFromDiv = function(src,idPrefix,noUpdate)
14176 {
14177         this.idPrefix = idPrefix;
14178         var storeElem = (typeof src == "string") ? document.getElementById(src) : src;
14179         if(!storeElem)
14180                 return;
14181         var tiddlers = this.getLoader().loadTiddlers(this,storeElem.childNodes);
14182         this.setDirty(false);
14183         if(!noUpdate) {
14184                 for(var i = 0;i<tiddlers.length; i++)
14185                         tiddlers[i].changed();
14186         }
14187 };
14188
14189 // Load contents of a TiddlyWiki from a string
14190 // Returns null if there's an error
14191 TiddlyWiki.prototype.importTiddlyWiki = function(text)
14192 {
14193         var posDiv = locateStoreArea(text);
14194         if(!posDiv)
14195                 return null;
14196         var content = "<" + "html><" + "body>" + text.substring(posDiv[0],posDiv[1] + endSaveArea.length) + "<" + "/body><" + "/html>";
14197         // Create the iframe
14198         var iframe = document.createElement("iframe");
14199         iframe.style.display = "none";
14200         document.body.appendChild(iframe);
14201         var doc = iframe.document;
14202         if(iframe.contentDocument)
14203                 doc = iframe.contentDocument; // For NS6
14204         else if(iframe.contentWindow)
14205                 doc = iframe.contentWindow.document; // For IE5.5 and IE6
14206         // Put the content in the iframe
14207         doc.open();
14208         doc.writeln(content);
14209         doc.close();
14210         // Load the content into a TiddlyWiki() object
14211         var storeArea = doc.getElementById("storeArea");
14212         this.loadFromDiv(storeArea,"store");
14213         // Get rid of the iframe
14214         iframe.parentNode.removeChild(iframe);
14215         return this;
14216 };
14217
14218 TiddlyWiki.prototype.updateTiddlers = function()
14219 {
14220         this.tiddlersUpdated = true;
14221         this.forEachTiddler(function(title,tiddler) {
14222                 tiddler.changed();
14223         });
14224 };
14225
14226 // Return all tiddlers formatted as an HTML string
14227 TiddlyWiki.prototype.allTiddlersAsHtml = function()
14228 {
14229         return store.getSaver().externalize(store);
14230 };
14231
14232 // Return an array of tiddlers matching a search regular expression
14233 TiddlyWiki.prototype.search = function(searchRegExp,sortField,excludeTag)
14234 {
14235         var candidates = this.reverseLookup("tags",excludeTag,false);
14236         var results = [];
14237         for(var t=0; t<candidates.length; t++) {
14238                 if((candidates[t].title.search(searchRegExp) != -1) || (candidates[t].text.search(searchRegExp) != -1))
14239                         results.push(candidates[t]);
14240         }
14241         if(!sortField)
14242                 sortField = "title";
14243         results.sort(function(a,b) {return a[sortField] < b[sortField] ? -1 : (a[sortField] == b[sortField] ? 0 : +1);});
14244         return results;
14245 };
14246
14247 // Return an array of all the tags in use. Each member of the array is another array where [0] is the name of the tag and [1] is the number of occurances
14248 TiddlyWiki.prototype.getTags = function(excludeTag)
14249 {
14250         var results = [];
14251         this.forEachTiddler(function(title,tiddler) {
14252                 for(var g=0; g<tiddler.tags.length; g++) {
14253                         var tag = tiddler.tags[g];
14254                         if(excludeTag) {
14255                                 var t = store.fetchTiddler(tag);
14256                                 if(t && t.isTagged(excludeTag))
14257                                         return false;
14258                         }
14259                         var f = false;
14260                         for(var c=0; c<results.length; c++) {
14261                                 if(results[c][0] == tag) {
14262                                         f = true;
14263                                         results[c][1]++;
14264                                 }
14265                         }
14266                         if(!f)
14267                                 results.push([tag,1]);
14268                 }
14269         });
14270         results.sort(function(a,b) {return a[0].toLowerCase() < b[0].toLowerCase() ? -1 : (a[0].toLowerCase() == b[0].toLowerCase() ? 0 : +1);});
14271         return results;
14272 };
14273
14274 // Return an array of the tiddlers that are tagged with a given tag
14275 TiddlyWiki.prototype.getTaggedTiddlers = function(tag,sortField)
14276 {
14277         return this.reverseLookup("tags",tag,true,sortField);
14278 };
14279
14280 // Return an array of the tiddlers that link to a given tiddler
14281 TiddlyWiki.prototype.getReferringTiddlers = function(title,unusedParameter,sortField)
14282 {
14283         if(!this.tiddlersUpdated)
14284                 this.updateTiddlers();
14285         return this.reverseLookup("links",title,true,sortField);
14286 };
14287
14288 // Return an array of the tiddlers that do or do not have a specified entry in the specified storage array (ie, "links" or "tags")
14289 // lookupMatch == true to match tiddlers, false to exclude tiddlers
14290 TiddlyWiki.prototype.reverseLookup = function(lookupField,lookupValue,lookupMatch,sortField)
14291 {
14292         var results = [];
14293         this.forEachTiddler(function(title,tiddler) {
14294                 var f = !lookupMatch;
14295                 for(var lookup=0; lookup<tiddler[lookupField].length; lookup++) {
14296                         if(tiddler[lookupField][lookup] == lookupValue)
14297                                 f = lookupMatch;
14298                 }
14299                 if(f)
14300                         results.push(tiddler);
14301         });
14302         if(!sortField)
14303                 sortField = "title";
14304         results.sort(function(a,b) {return a[sortField] < b[sortField] ? -1 : (a[sortField] == b[sortField] ? 0 : +1);});
14305         return results;
14306 };
14307
14308 // Return the tiddlers as a sorted array
14309 TiddlyWiki.prototype.getTiddlers = function(field,excludeTag)
14310 {
14311         var results = [];
14312         this.forEachTiddler(function(title,tiddler) {
14313                 if(excludeTag == undefined || !tiddler.isTagged(excludeTag))
14314                         results.push(tiddler);
14315         });
14316         if(field)
14317                 results.sort(function(a,b) {return a[field] < b[field] ? -1 : (a[field] == b[field] ? 0 : +1);});
14318         return results;
14319 };
14320
14321 // Return array of names of tiddlers that are referred to but not defined
14322 TiddlyWiki.prototype.getMissingLinks = function(sortField)
14323 {
14324         if(!this.tiddlersUpdated)
14325                 this.updateTiddlers();
14326         var results = [];
14327         this.forEachTiddler(function (title,tiddler) {
14328                 if(tiddler.isTagged("excludeMissing") || tiddler.isTagged("systemConfig"))
14329                         return;
14330                 for(var n=0; n<tiddler.links.length;n++) {
14331                         var link = tiddler.links[n];
14332                         if(this.fetchTiddler(link) == null && !this.isShadowTiddler(link))
14333                                 results.pushUnique(link);
14334                 }
14335         });
14336         results.sort();
14337         return results;
14338 };
14339
14340 // Return an array of names of tiddlers that are defined but not referred to
14341 TiddlyWiki.prototype.getOrphans = function()
14342 {
14343         var results = [];
14344         this.forEachTiddler(function (title,tiddler) {
14345                 if(this.getReferringTiddlers(title).length == 0 && !tiddler.isTagged("excludeLists"))
14346                         results.push(title);
14347         });
14348         results.sort();
14349         return results;
14350 };
14351
14352 // Return an array of names of all the shadow tiddlers
14353 TiddlyWiki.prototype.getShadowed = function()
14354 {
14355         var results = [];
14356         for(var t in config.shadowTiddlers) {
14357                 if(typeof config.shadowTiddlers[t] == "string")
14358                         results.push(t);
14359         }
14360         results.sort();
14361         return results;
14362 };
14363
14364 // Return an array of tiddlers that have been touched since they were downloaded or created
14365 TiddlyWiki.prototype.getTouched = function()
14366 {
14367         var results = [];
14368         this.forEachTiddler(function(title,tiddler) {
14369                 if(tiddler.isTouched())
14370                         results.push(tiddler);
14371                 });
14372         results.sort();
14373         return results;
14374 };
14375
14376 // Resolves a Tiddler reference or tiddler title into a Tiddler object, or null if it doesn't exist
14377 TiddlyWiki.prototype.resolveTiddler = function(tiddler)
14378 {
14379         var t = (typeof tiddler == 'string') ? this.getTiddler(tiddler) : tiddler;
14380         return t instanceof Tiddler ? t : null;
14381 };
14382
14383 TiddlyWiki.prototype.getLoader = function()
14384 {
14385         if(!this.loader)
14386                 this.loader = new TW21Loader();
14387         return this.loader;
14388 };
14389
14390 TiddlyWiki.prototype.getSaver = function()
14391 {
14392         if(!this.saver)
14393                 this.saver = new TW21Saver();
14394         return this.saver;
14395 };
14396
14397 // Returns true if path is a valid field name (path),
14398 // i.e. a sequence of identifiers, separated by '.'
14399 TiddlyWiki.isValidFieldName = function(name)
14400 {
14401         var match = /[a-zA-Z_]\w*(\.[a-zA-Z_]\w*)*/.exec(name);
14402         return match && (match[0] == name);
14403 };
14404
14405 // Throws an exception when name is not a valid field name.
14406 TiddlyWiki.checkFieldName = function(name)
14407 {
14408         if(!TiddlyWiki.isValidFieldName(name))
14409                 throw config.messages.invalidFieldName.format([name]);
14410 };
14411
14412 function StringFieldAccess(n,readOnly)
14413 {
14414         this.set = readOnly ?
14415                         function(t,v) {if(v != t[n]) throw config.messages.fieldCannotBeChanged.format([n]);} :
14416                         function(t,v) {if(v != t[n]) {t[n] = v; return true;}};
14417         this.get = function(t) {return t[n];};
14418 }
14419
14420 function DateFieldAccess(n)
14421 {
14422         this.set = function(t,v) {
14423                 var d = v instanceof Date ? v : Date.convertFromYYYYMMDDHHMM(v);
14424                 if(d != t[n]) {
14425                         t[n] = d; return true;
14426                 }
14427         };
14428         this.get = function(t) {return t[n].convertToYYYYMMDDHHMM();};
14429 }
14430
14431 function LinksFieldAccess(n)
14432 {
14433         this.set = function(t,v) {
14434                 var s = (typeof v == "string") ? v.readBracketedList() : v;
14435                 if(s.toString() != t[n].toString()) {
14436                         t[n] = s; return true;
14437                 }
14438         };
14439         this.get = function(t) {return String.encodeTiddlyLinkList(t[n]);};
14440 }
14441
14442 TiddlyWiki.standardFieldAccess = {
14443         // The set functions return true when setting the data has changed the value.
14444         "title":    new StringFieldAccess("title",true),
14445         // Handle the "tiddler" field name as the title
14446         "tiddler":  new StringFieldAccess("title",true),
14447         "text":     new StringFieldAccess("text"),
14448         "modifier": new StringFieldAccess("modifier"),
14449         "modified": new DateFieldAccess("modified"),
14450         "created":  new DateFieldAccess("created"),
14451         "tags":     new LinksFieldAccess("tags")
14452 };
14453
14454 TiddlyWiki.isStandardField = function(name)
14455 {
14456         return TiddlyWiki.standardFieldAccess[name] != undefined;
14457 };
14458
14459 // Sets the value of the given field of the tiddler to the value.
14460 // Setting an ExtendedField's value to null or undefined removes the field.
14461 // Setting a namespace to undefined removes all fields of that namespace.
14462 // The fieldName is case-insensitive.
14463 // All values will be converted to a string value.
14464 TiddlyWiki.prototype.setValue = function(tiddler,fieldName,value)
14465 {
14466         TiddlyWiki.checkFieldName(fieldName);
14467         var t = this.resolveTiddler(tiddler);
14468         if(!t)
14469                 return;
14470         fieldName = fieldName.toLowerCase();
14471         var isRemove = (value === undefined) || (value === null);
14472         var accessor = TiddlyWiki.standardFieldAccess[fieldName];
14473         if(accessor) {
14474                 if(isRemove)
14475                         // don't remove StandardFields
14476                         return;
14477                 var h = TiddlyWiki.standardFieldAccess[fieldName];
14478                 if(!h.set(t,value))
14479                         return;
14480         } else {
14481                 var oldValue = t.fields[fieldName];
14482                 if(isRemove) {
14483                         if(oldValue !== undefined) {
14484                                 // deletes a single field
14485                                 delete t.fields[fieldName];
14486                         } else {
14487                                 // no concrete value is defined for the fieldName
14488                                 // so we guess this is a namespace path.
14489                                 // delete all fields in a namespace
14490                                 var re = new RegExp('^'+fieldName+'\\.');
14491                                 var dirty = false;
14492                                 for(var n in t.fields) {
14493                                         if(n.match(re)) {
14494                                                 delete t.fields[n];
14495                                                 dirty = true;
14496                                         }
14497                                 }
14498                                 if(!dirty)
14499                                         return;
14500                         }
14501                 } else {
14502                         // the "normal" set case. value is defined (not null/undefined)
14503                         // For convenience provide a nicer conversion Date->String
14504                         value = value instanceof Date ? value.convertToYYYYMMDDHHMMSSMMM() : String(value);
14505                         if(oldValue == value)
14506                                 return;
14507                         t.fields[fieldName] = value;
14508                 }
14509         }
14510         // When we are here the tiddler/store really was changed.
14511         this.notify(t.title,true);
14512         if(!fieldName.match(/^temp\./))
14513                 this.setDirty(true);
14514 };
14515
14516 // Returns the value of the given field of the tiddler.
14517 // The fieldName is case-insensitive.
14518 // Will only return String values (or undefined).
14519 TiddlyWiki.prototype.getValue = function(tiddler,fieldName)
14520 {
14521         var t = this.resolveTiddler(tiddler);
14522         if(!t)
14523                 return undefined;
14524         fieldName = fieldName.toLowerCase();
14525         var accessor = TiddlyWiki.standardFieldAccess[fieldName];
14526         if(accessor) {
14527                 return accessor.get(t);
14528         }
14529         return t.fields[fieldName];
14530 };
14531
14532 // Calls the callback function for every field in the tiddler.
14533 // When callback function returns a non-false value the iteration stops
14534 // and that value is returned.
14535 // The order of the fields is not defined.
14536 // @param callback a function(tiddler,fieldName,value).
14537 TiddlyWiki.prototype.forEachField = function(tiddler,callback,onlyExtendedFields)
14538 {
14539         var t = this.resolveTiddler(tiddler);
14540         if(!t)
14541                 return undefined;
14542         for(var n in t.fields) {
14543                 var result = callback(t,n,t.fields[n]);
14544                 if(result)
14545                         return result;
14546                 }
14547         if(onlyExtendedFields)
14548                 return undefined;
14549         for(var n in TiddlyWiki.standardFieldAccess) {
14550                 if(n == "tiddler")
14551                         // even though the "title" field can also be referenced through the name "tiddler"
14552                         // we only visit this field once.
14553                         continue;
14554                 var result = callback(t,n,TiddlyWiki.standardFieldAccess[n].get(t));
14555                 if(result)
14556                         return result;
14557         }
14558         return undefined;
14559 };
14560
14561 //--
14562 //-- Story functions
14563 //--
14564
14565 function Story(container,idPrefix)
14566 {
14567         this.container = container;
14568         this.idPrefix = idPrefix;
14569         this.highlightRegExp = null;
14570 }
14571
14572 Story.prototype.forEachTiddler = function(fn)
14573 {
14574         var place = document.getElementById(this.container);
14575         if(!place)
14576                 return;
14577         var e = place.firstChild;
14578         while(e) {
14579                 var n = e.nextSibling;
14580                 var title = e.getAttribute("tiddler");
14581                 fn.call(this,title,e);
14582                 e = n;
14583         }
14584 };
14585
14586 Story.prototype.displayTiddlers = function(srcElement,titles,template,animate,unused,customFields,toggle)
14587 {
14588         for(var t = titles.length-1;t>=0;t--)
14589                 this.displayTiddler(srcElement,titles[t],template,animate,unused,customFields);
14590 };
14591
14592 Story.prototype.displayTiddler = function(srcElement,title,template,animate,unused,customFields,toggle)
14593 {
14594         var place = document.getElementById(this.container);
14595         var tiddlerElem = document.getElementById(this.idPrefix + title);
14596         if(tiddlerElem) {
14597                 if(toggle)
14598                         this.closeTiddler(title,true);
14599                 else
14600                         this.refreshTiddler(title,template,false,customFields);
14601         } else {
14602                 var before = this.positionTiddler(srcElement);
14603                 tiddlerElem = this.createTiddler(place,before,title,template,customFields);
14604         }
14605         if(srcElement && typeof srcElement !== "string") {
14606                 if(config.options.chkAnimate && (animate == undefined || animate == true) && anim && typeof Zoomer == "function" && typeof Scroller == "function")
14607                         anim.startAnimating(new Zoomer(title,srcElement,tiddlerElem),new Scroller(tiddlerElem));
14608                 else
14609                         window.scrollTo(0,ensureVisible(tiddlerElem));
14610         }
14611 };
14612
14613 Story.prototype.positionTiddler = function(srcElement)
14614 {
14615         var place = document.getElementById(this.container);
14616         var before = null;
14617         if(typeof srcElement == "string") {
14618                 switch(srcElement) {
14619                         case "top":
14620                                 before = place.firstChild;
14621                                 break;
14622                         case "bottom":
14623                                 before = null;
14624                                 break;
14625                 }
14626         } else {
14627                 var after = this.findContainingTiddler(srcElement);
14628                 if(after == null) {
14629                         before = place.firstChild;
14630                 } else if(after.nextSibling) {
14631                         before = after.nextSibling;
14632                         if(before.nodeType != 1)
14633                                 before = null;
14634                 }
14635         }
14636         return before;
14637 };
14638
14639 Story.prototype.createTiddler = function(place,before,title,template,customFields)
14640 {
14641         var tiddlerElem = createTiddlyElement(null,"div",this.idPrefix + title,"tiddler");
14642         tiddlerElem.setAttribute("refresh","tiddler");
14643         if(customFields)
14644                 tiddlerElem.setAttribute("tiddlyFields",customFields);
14645         place.insertBefore(tiddlerElem,before);
14646         var defaultText = null;
14647         if(!store.tiddlerExists(title) && !store.isShadowTiddler(title))
14648                 defaultText = this.loadMissingTiddler(title,customFields,tiddlerElem);
14649         this.refreshTiddler(title,template,false,customFields,defaultText);
14650         return tiddlerElem;
14651 };
14652
14653 Story.prototype.loadMissingTiddler = function(title,fields,tiddlerElem)
14654 {
14655         var tiddler = new Tiddler(title);
14656         tiddler.fields = typeof fields == "string" ?  fields.decodeHashMap() : (fields ? fields : {});
14657         var serverType = tiddler.getServerType();
14658         var host = tiddler.fields['server.host'];
14659         var workspace = tiddler.fields['server.workspace'];
14660         if(!serverType | !host)
14661                 return null;
14662         var sm = new SyncMachine(serverType,{
14663                         start: function() {
14664                                 return this.openHost(host,"openWorkspace");
14665                         },
14666                         openWorkspace: function() {
14667                                 return this.openWorkspace(workspace,"getTiddler");
14668                         },
14669                         getTiddler: function() {
14670                                 return this.getTiddler(title,"gotTiddler");
14671                         },
14672                         gotTiddler: function(tiddler) {
14673                                 if(tiddler && tiddler.text) {
14674                                         var downloaded = new Date();
14675                                         if(!tiddler.created)
14676                                                 tiddler.created = downloaded;
14677                                         if(!tiddler.modified)
14678                                                 tiddler.modified = tiddler.created;
14679                                         store.saveTiddler(tiddler.title,tiddler.title,tiddler.text,tiddler.modifier,tiddler.modified,tiddler.tags,tiddler.fields,true,tiddler.created);
14680                                         autoSaveChanges();
14681                                 }
14682                                 delete this;
14683                                 return true;
14684                         },
14685                         error: function(message) {
14686                                 displayMessage("Error loading missing tiddler from %0: %1".format([host,message]));
14687                         }
14688                 });
14689         sm.go();
14690         return config.messages.loadingMissingTiddler.format([title,serverType,host,workspace]);
14691 };
14692
14693 Story.prototype.chooseTemplateForTiddler = function(title,template)
14694 {
14695         if(!template)
14696                 template = DEFAULT_VIEW_TEMPLATE;
14697         if(template == DEFAULT_VIEW_TEMPLATE || template == DEFAULT_EDIT_TEMPLATE)
14698                 template = config.tiddlerTemplates[template];
14699         return template;
14700 };
14701
14702 Story.prototype.getTemplateForTiddler = function(title,template,tiddler)
14703 {
14704         return store.getRecursiveTiddlerText(template,null,10);
14705 };
14706
14707 Story.prototype.refreshTiddler = function(title,template,force,customFields,defaultText)
14708 {
14709         var tiddlerElem = document.getElementById(this.idPrefix + title);
14710         if(tiddlerElem) {
14711                 if(tiddlerElem.getAttribute("dirty") == "true" && !force)
14712                         return tiddlerElem;
14713                 template = this.chooseTemplateForTiddler(title,template);
14714                 var currTemplate = tiddlerElem.getAttribute("template");
14715                 if((template != currTemplate) || force) {
14716                         var tiddler = store.getTiddler(title);
14717                         if(!tiddler) {
14718                                 tiddler = new Tiddler();
14719                                 if(store.isShadowTiddler(title)) {
14720                                         tiddler.set(title,store.getTiddlerText(title),config.views.wikified.shadowModifier,version.date,[],version.date);
14721                                 } else {
14722                                         var text = template=="EditTemplate" ?
14723                                                                 config.views.editor.defaultText.format([title]) :
14724                                                                 config.views.wikified.defaultText.format([title]);
14725                                         text = defaultText ? defaultText : text;
14726                                         var fields = customFields ? customFields.decodeHashMap() : null;
14727                                         tiddler.set(title,text,config.views.wikified.defaultModifier,version.date,[],version.date,fields);
14728                                 }
14729                         }
14730                         tiddlerElem.setAttribute("tags",tiddler.tags.join(" "));
14731                         tiddlerElem.setAttribute("tiddler",title);
14732                         tiddlerElem.setAttribute("template",template);
14733                         var me = this;
14734                         tiddlerElem.onmouseover = this.onTiddlerMouseOver;
14735                         tiddlerElem.onmouseout = this.onTiddlerMouseOut;
14736                         tiddlerElem.ondblclick = this.onTiddlerDblClick;
14737                         tiddlerElem[window.event?"onkeydown":"onkeypress"] = this.onTiddlerKeyPress;
14738                         var html = this.getTemplateForTiddler(title,template,tiddler);
14739                         tiddlerElem.innerHTML = html;
14740                         applyHtmlMacros(tiddlerElem,tiddler);
14741                         if(store.getTaggedTiddlers(title).length > 0)
14742                                 addClass(tiddlerElem,"isTag");
14743                         else
14744                                 removeClass(tiddlerElem,"isTag");
14745                         if(!store.tiddlerExists(title)) {
14746                                 if(store.isShadowTiddler(title))
14747                                         addClass(tiddlerElem,"shadow");
14748                                 else
14749                                         addClass(tiddlerElem,"missing");
14750                         } else {
14751                                 removeClass(tiddlerElem,"shadow");
14752                                 removeClass(tiddlerElem,"missing");
14753                         }
14754                         if(customFields)
14755                                 this.addCustomFields(tiddlerElem,customFields);
14756                         forceReflow();
14757                 }
14758         }
14759         return tiddlerElem;
14760 };
14761
14762 Story.prototype.addCustomFields = function(place,customFields)
14763 {
14764         var fields = customFields.decodeHashMap();
14765         var w = document.createElement("div");
14766         w.style.display = "none";
14767         place.appendChild(w);
14768         for(var t in fields) {
14769                 var e = document.createElement("input");
14770                 e.setAttribute("type","text");
14771                 e.setAttribute("value",fields[t]);
14772                 w.appendChild(e);
14773                 e.setAttribute("edit",t);
14774         }
14775 };
14776
14777 Story.prototype.refreshAllTiddlers = function() 
14778 {
14779         var place = document.getElementById(this.container);
14780         var e = place.firstChild;
14781         if(!e)
14782                 return;
14783         this.refreshTiddler(e.getAttribute("tiddler"),e.getAttribute("template"),true);
14784         while((e = e.nextSibling) != null) 
14785                 this.refreshTiddler(e.getAttribute("tiddler"),e.getAttribute("template"),true);
14786 };
14787
14788 Story.prototype.onTiddlerMouseOver = function(e)
14789 {
14790         if(window.addClass instanceof Function)
14791                 addClass(this,"selected");
14792 };
14793
14794 Story.prototype.onTiddlerMouseOut = function(e)
14795 {
14796         if(window.removeClass instanceof Function)
14797                 removeClass(this,"selected");
14798 };
14799
14800 Story.prototype.onTiddlerDblClick = function(e)
14801 {
14802         if(!e) var e = window.event;
14803         var theTarget = resolveTarget(e);
14804         if(theTarget && theTarget.nodeName.toLowerCase() != "input" && theTarget.nodeName.toLowerCase() != "textarea") {
14805                 if(document.selection && document.selection.empty)
14806                         document.selection.empty();
14807                 config.macros.toolbar.invokeCommand(this,"defaultCommand",e);
14808                 e.cancelBubble = true;
14809                 if(e.stopPropagation) e.stopPropagation();
14810                 return true;
14811         } else {
14812                 return false;
14813         }
14814 };
14815
14816 Story.prototype.onTiddlerKeyPress = function(e)
14817 {
14818         if(!e) var e = window.event;
14819         clearMessage();
14820         var consume = false; 
14821         var title = this.getAttribute("tiddler");
14822         var target = resolveTarget(e);
14823         switch(e.keyCode) {
14824                 case 9: // Tab
14825                         if(config.options.chkInsertTabs && target.tagName.toLowerCase() == "textarea") {
14826                                 replaceSelection(target,String.fromCharCode(9));
14827                                 consume = true; 
14828                         }
14829                         if(config.isOpera) {
14830                                 target.onblur = function() {
14831                                         this.focus();
14832                                         this.onblur = null;
14833                                 };
14834                         }
14835                         break;
14836                 case 13: // Ctrl-Enter
14837                 case 10: // Ctrl-Enter on IE PC
14838                 case 77: // Ctrl-Enter is "M" on some platforms
14839                         if(e.ctrlKey) {
14840                                 blurElement(this);
14841                                 config.macros.toolbar.invokeCommand(this,"defaultCommand",e);
14842                                 consume = true;
14843                         }
14844                         break; 
14845                 case 27: // Escape
14846                         blurElement(this);
14847                         config.macros.toolbar.invokeCommand(this,"cancelCommand",e);
14848                         consume = true;
14849                         break;
14850         }
14851         e.cancelBubble = consume;
14852         if(consume) {
14853                 if(e.stopPropagation) e.stopPropagation(); // Stop Propagation
14854                 e.returnValue = true; // Cancel The Event in IE
14855                 if(e.preventDefault ) e.preventDefault(); // Cancel The Event in Moz
14856         }
14857         return !consume;
14858 };
14859
14860 Story.prototype.getTiddlerField = function(title,field)
14861 {
14862         var tiddlerElem = document.getElementById(this.idPrefix + title);
14863         var e = null;
14864         if(tiddlerElem != null) {
14865                 var children = tiddlerElem.getElementsByTagName("*");
14866                 for(var t=0; t<children.length; t++) {
14867                         var c = children[t];
14868                         if(c.tagName.toLowerCase() == "input" || c.tagName.toLowerCase() == "textarea") {
14869                                 if(!e)
14870                                         e = c;
14871                                 if(c.getAttribute("edit") == field)
14872                                         e = c;
14873                         }
14874                 }
14875         }
14876         return e;
14877 };
14878
14879 Story.prototype.focusTiddler = function(title,field)
14880 {
14881         var e = this.getTiddlerField(title,field);
14882         if(e) {
14883                 e.focus();
14884                 e.select();
14885         }
14886 };
14887
14888 Story.prototype.blurTiddler = function(title)
14889 {
14890         var tiddlerElem = document.getElementById(this.idPrefix + title);
14891         if(tiddlerElem != null && tiddlerElem.focus && tiddlerElem.blur) {
14892                 tiddlerElem.focus();
14893                 tiddlerElem.blur();
14894         }
14895 };
14896
14897 Story.prototype.setTiddlerField = function(title,tag,mode,field)
14898 {
14899         var c = story.getTiddlerField(title,field);
14900
14901         var tags = c.value.readBracketedList();
14902         tags.setItem(tag,mode);
14903         c.value = String.encodeTiddlyLinkList(tags);
14904 };
14905
14906 Story.prototype.setTiddlerTag = function(title,tag,mode)
14907 {
14908         Story.prototype.setTiddlerField(title,tag,mode,"tags");
14909 };
14910
14911 Story.prototype.closeTiddler = function(title,animate,unused)
14912 {
14913         var tiddlerElem = document.getElementById(this.idPrefix + title);
14914         if(tiddlerElem != null) {
14915                 clearMessage();
14916                 this.scrubTiddler(tiddlerElem);
14917                 if(config.options.chkAnimate && animate && anim && typeof Slider == "function")
14918                         anim.startAnimating(new Slider(tiddlerElem,false,null,"all"));
14919                 else {
14920                         removeNode(tiddlerElem);
14921                         forceReflow();
14922                 }
14923         }
14924 };
14925
14926 Story.prototype.scrubTiddler = function(tiddlerElem)
14927 {
14928         tiddlerElem.id = null;
14929 };
14930
14931 Story.prototype.setDirty = function(title,dirty)
14932 {
14933         var tiddlerElem = document.getElementById(this.idPrefix + title);
14934         if(tiddlerElem != null)
14935                 tiddlerElem.setAttribute("dirty",dirty ? "true" : "false");
14936 };
14937
14938 Story.prototype.isDirty = function(title)
14939 {
14940         var tiddlerElem = document.getElementById(this.idPrefix + title);
14941         if(tiddlerElem != null)
14942                 return tiddlerElem.getAttribute("dirty") == "true";
14943         return null;
14944 };
14945
14946 Story.prototype.areAnyDirty = function()
14947 {
14948         var r = false;
14949         this.forEachTiddler(function(title,element) {
14950                 if(this.isDirty(title))
14951                         r = true;
14952         });
14953         return r;
14954 };
14955
14956 Story.prototype.closeAllTiddlers = function(exclude)
14957 {
14958         clearMessage();
14959         this.forEachTiddler(function(title,element) {
14960                 if((title != exclude) && element.getAttribute("dirty") != "true")
14961                         this.closeTiddler(title);
14962         });
14963         window.scrollTo(0,ensureVisible(this.container));
14964 };
14965
14966 Story.prototype.isEmpty = function()
14967 {
14968         var place = document.getElementById(this.container);
14969         return place && place.firstChild == null;
14970 };
14971
14972 Story.prototype.search = function(text,useCaseSensitive,useRegExp)
14973 {
14974         this.closeAllTiddlers();
14975         highlightHack = new RegExp(useRegExp ?   text : text.escapeRegExp(),useCaseSensitive ? "mg" : "img");
14976         var matches = store.search(highlightHack,"title","excludeSearch");
14977         var titles = [];
14978         for(var t=0;t<matches.length;t++)
14979                 titles.push(matches[t].title);
14980         this.displayTiddlers(null,titles);
14981         highlightHack = null;
14982         var q = useRegExp ? "/" : "'";
14983         if(matches.length > 0)
14984                 displayMessage(config.macros.search.successMsg.format([titles.length.toString(),q + text + q]));
14985         else
14986                 displayMessage(config.macros.search.failureMsg.format([q + text + q]));
14987 };
14988
14989 Story.prototype.findContainingTiddler = function(e)
14990 {
14991         while(e && !hasClass(e,"tiddler"))
14992                 e = e.parentNode;
14993         return e;
14994 };
14995
14996 Story.prototype.gatherSaveFields = function(e,fields)
14997 {
14998         if(e && e.getAttribute) {
14999                 var f = e.getAttribute("edit");
15000                 if(f)
15001                         fields[f] = e.value.replace(/\r/mg,"");
15002                 if(e.hasChildNodes()) {
15003                         var c = e.childNodes;
15004                         for(var t=0; t<c.length; t++)
15005                                 this.gatherSaveFields(c[t],fields);
15006                 }
15007         }
15008 };
15009
15010 Story.prototype.hasChanges = function(title)
15011 {
15012         var e = document.getElementById(this.idPrefix + title);
15013         if(e != null) {
15014                 var fields = {};
15015                 this.gatherSaveFields(e,fields);
15016                 var tiddler = store.fetchTiddler(title);
15017                 if(!tiddler)
15018                         return false;
15019                 for(var n in fields) {
15020                         if(store.getValue(title,n) != fields[n])
15021                                 return true;
15022                 }
15023         }
15024         return false;
15025 };
15026
15027 Story.prototype.saveTiddler = function(title,minorUpdate)
15028 {
15029         var tiddlerElem = document.getElementById(this.idPrefix + title);
15030         if(tiddlerElem != null) {
15031                 var fields = {};
15032                 this.gatherSaveFields(tiddlerElem,fields);
15033                 var newTitle = fields.title ? fields.title : title;
15034                 if(store.tiddlerExists(newTitle) && newTitle != title) {
15035                         if(!confirm(config.messages.overwriteWarning.format([newTitle.toString()])))
15036                                 return null;
15037                 }
15038                 if(newTitle != title)
15039                         this.closeTiddler(newTitle,false);
15040                 tiddlerElem.id = this.idPrefix + newTitle;
15041                 tiddlerElem.setAttribute("tiddler",newTitle);
15042                 tiddlerElem.setAttribute("template",DEFAULT_VIEW_TEMPLATE);
15043                 tiddlerElem.setAttribute("dirty","false");
15044                 if(config.options.chkForceMinorUpdate)
15045                         minorUpdate = !minorUpdate;
15046                 if(!store.tiddlerExists(newTitle))
15047                         minorUpdate = false;
15048                 var newDate = new Date();
15049                 var extendedFields = store.tiddlerExists(newTitle) ? store.fetchTiddler(newTitle).fields : {};
15050                 for(var n in fields) {
15051                         if(!TiddlyWiki.isStandardField(n))
15052                                 extendedFields[n] = fields[n];
15053                         }
15054                 var tiddler = store.saveTiddler(title,newTitle,fields.text,minorUpdate ? undefined : config.options.txtUserName,minorUpdate ? undefined : newDate,fields.tags,extendedFields);
15055                 autoSaveChanges(null,[tiddler]);
15056                 return newTitle;
15057         }
15058         return null;
15059 };
15060
15061 Story.prototype.permaView = function()
15062 {
15063         var links = [];
15064         this.forEachTiddler(function(title,element) {
15065                 links.push(String.encodeTiddlyLink(title));
15066         });
15067         var t = encodeURIComponent(links.join(" "));
15068         if(t == "")
15069                 t = "#";
15070         if(window.location.hash != t)
15071                 window.location.hash = t;
15072 };
15073
15074 //--
15075 //-- Backstage
15076 //--
15077
15078 var backstage = {
15079         area: null,
15080         toolbar: null,
15081         button: null,
15082         showButton: null,
15083         hideButton: null,
15084         cloak: null,
15085         panel: null,
15086         panelBody: null,
15087         panelFooter: null,
15088         currTabName: null,
15089         currTabElem: null,
15090         content: null,
15091
15092         init: function() {
15093                 var cmb = config.messages.backstage;
15094                 this.area = document.getElementById("backstageArea");
15095                 this.toolbar = document.getElementById("backstageToolbar");
15096                 this.button = document.getElementById("backstageButton");
15097                 this.button.style.display = "block";
15098                 var t = cmb.open.text + " " + glyph("bentArrowLeft");
15099                 this.showButton = createTiddlyButton(this.button,t,cmb.open.tooltip,
15100                                                 function (e) {backstage.show(); return false;},null,"backstageShow");
15101                 t = glyph("bentArrowRight") + " " + cmb.close.text;
15102                 this.hideButton = createTiddlyButton(this.button,t,cmb.close.tooltip,
15103                                                 function (e) {backstage.hide(); return false;},null,"backstageHide");
15104                 this.cloak = document.getElementById("backstageCloak");
15105                 this.panel = document.getElementById("backstagePanel");
15106                 this.panelFooter = createTiddlyElement(this.panel,"div",null,"backstagePanelFooter");
15107                 this.panelBody = createTiddlyElement(this.panel,"div",null,"backstagePanelBody");
15108                 this.cloak.onmousedown = function(e) {
15109                         backstage.switchTab(null);
15110                 };
15111                 createTiddlyText(this.toolbar,cmb.prompt);
15112                 for(t=0; t<config.backstageTasks.length; t++) {
15113                         var taskName = config.backstageTasks[t];
15114                         var task = config.tasks[taskName];
15115                         var handler = task.action ? this.onClickCommand : this.onClickTab;
15116                         var text = task.text + (task.action ? "" : glyph("downTriangle"));
15117                         var btn = createTiddlyButton(this.toolbar,text,task.tooltip,handler,"backstageTab");
15118                         btn.setAttribute("task",taskName);
15119                         addClass(btn,task.action ? "backstageAction" : "backstageTask");
15120                         }
15121                 this.content = document.getElementById("contentWrapper");
15122                 if(config.options.chkBackstage)
15123                         this.show();
15124                 else
15125                         this.hide();
15126         },
15127         
15128         isVisible: function () {
15129                 return this.area ? this.area.style.display == "block" : false;
15130         },
15131         
15132         show: function() {
15133                 this.area.style.display = "block";
15134                 if(anim && config.options.chkAnimate) {
15135                         backstage.toolbar.style.left = findWindowWidth() + "px";
15136                         var p = [
15137                                 {style: "left", start: findWindowWidth(), end: 0, template: "%0px"}
15138                         ];
15139                         anim.startAnimating(new Morpher(backstage.toolbar,config.animDuration,p));
15140                 } else {
15141                         backstage.area.style.left = "0px";
15142                 }
15143                 this.showButton.style.display = "none";
15144                 this.hideButton.style.display = "block";
15145                 config.options.chkBackstage = true;
15146                 saveOptionCookie("chkBackstage");
15147                 addClass(this.content,"backstageVisible");
15148         },
15149
15150         hide: function() {
15151                 if(this.currTabElem) {
15152                         this.switchTab(null);
15153                 } else {
15154                         backstage.toolbar.style.left = "0px";
15155                         if(anim && config.options.chkAnimate) {
15156                                 var p = [
15157                                         {style: "left", start: 0, end: findWindowWidth(), template: "%0px"}
15158                                 ];
15159                                 var c = function(element,properties) {backstage.area.style.display = "none";};
15160                                 anim.startAnimating(new Morpher(backstage.toolbar,config.animDuration,p,c));
15161                         } else {
15162                                 this.area.style.display = "none";
15163                         }
15164                         this.showButton.style.display = "block";
15165                         this.hideButton.style.display = "none";
15166                         config.options.chkBackstage = false;
15167                         saveOptionCookie("chkBackstage");
15168                         removeClass(this.content,"backstageVisible");
15169                 }
15170         },
15171
15172         onClickCommand: function(e) {
15173                 var task = config.tasks[this.getAttribute("task")];
15174                 displayMessage(task);
15175                 if(task.action) {
15176                         backstage.switchTab(null);
15177                         task.action();
15178                 }
15179                 return false;
15180         },
15181
15182         onClickTab: function(e) {
15183                 backstage.switchTab(this.getAttribute("task"));
15184                 return false;
15185         },
15186
15187         // Switch to a given tab, or none if null is passed
15188         switchTab: function(tabName) {
15189                 var tabElem = null;
15190                 var e = this.toolbar.firstChild;
15191                 while(e)
15192                         {
15193                         if(e.getAttribute && e.getAttribute("task") == tabName)
15194                                 tabElem = e;
15195                         e = e.nextSibling;
15196                         }
15197                 if(tabName == backstage.currTabName)
15198                         return;
15199                 if(backstage.currTabElem) {
15200                         removeClass(this.currTabElem,"backstageSelTab");
15201                 }
15202                 if(tabElem && tabName) {
15203                         backstage.preparePanel();
15204                         addClass(tabElem,"backstageSelTab");
15205                         var task = config.tasks[tabName];
15206                         wikify(task.content,backstage.panelBody,null,null);
15207                         backstage.showPanel();
15208                 } else if(backstage.currTabElem) {
15209                         backstage.hidePanel();
15210                 }
15211                 backstage.currTabName = tabName;
15212                 backstage.currTabElem = tabElem;
15213         },
15214
15215         isPanelVisible: function() {
15216                 return backstage.panel ? backstage.panel.style.display == "block" : false;
15217         },
15218
15219         preparePanel: function() {
15220                 backstage.cloak.style.height = findWindowHeight() + "px";
15221                 backstage.cloak.style.display = "block";
15222                 removeChildren(backstage.panelBody);
15223                 return backstage.panelBody;
15224         },
15225         
15226         showPanel: function() {
15227                 backstage.panel.style.display = "block";
15228                 if(anim && config.options.chkAnimate) {
15229                         backstage.panel.style.top = (-backstage.panel.offsetHeight) + "px";
15230                         var p = [
15231                                 {style: "top", start: -backstage.panel.offsetHeight, end: 0, template: "%0px"}
15232                         ];
15233                         anim.startAnimating(new Morpher(backstage.panel,config.animDuration,p),new Scroller(backstage.panel,false));
15234                 } else {
15235                         backstage.panel.style.top = "0px";
15236                 }
15237                 return backstage.panelBody;
15238         },
15239         
15240         hidePanel: function() {
15241                 backstage.currTabName = null;
15242                 backstage.currTabElem = null;
15243                 if(anim && config.options.chkAnimate) {
15244                         var p = [
15245                                 {style: "top", start: 0, end: -(backstage.panel.offsetHeight), template: "%0px"},
15246                                 {style: "display", atEnd: "none"}
15247                         ];
15248                         var c = function(element,properties) {backstage.cloak.style.display = "none";};
15249                         anim.startAnimating(new Morpher(backstage.panel,config.animDuration,p,c));
15250                  } else {
15251                         backstage.panel.style.display = "none";
15252                         backstage.cloak.style.display = "none";
15253                 }
15254         }
15255 };
15256
15257 config.macros.backstage = {};
15258
15259 config.macros.backstage.handler = function(place,macroName,params,wikifier,paramString,tiddler)
15260 {
15261         var backstageTask = config.tasks[params[0]];
15262         if(backstageTask)
15263                 createTiddlyButton(place,backstageTask.text,backstageTask.tooltip,function(e) {backstage.switchTab(params[0]); return false;});
15264 };
15265
15266 //--
15267 //-- ImportTiddlers macro
15268 //--
15269
15270 config.macros.importTiddlers.handler = function(place,macroName,params,wikifier,paramString,tiddler)
15271 {
15272         if(readOnly) {
15273                 createTiddlyElement(place,"div",null,"marked",this.readOnlyWarning);
15274                 return;
15275         }
15276         var w = new Wizard();
15277         w.createWizard(place,this.wizardTitle);
15278         this.restart(w);
15279 };
15280
15281 config.macros.importTiddlers.onCancel = function(e)
15282 {
15283         var wizard = new Wizard(this);
15284         var place = wizard.clear();
15285         config.macros.importTiddlers.restart(wizard);
15286         return false;
15287 };
15288
15289 config.macros.importTiddlers.restart = function(wizard)
15290 {
15291         wizard.addStep(this.step1Title,this.step1Html);
15292         var s = wizard.getElement("selTypes");
15293         for(var t in config.adaptors) {
15294                 var e = createTiddlyElement(s,"option",null,null,t);
15295                 e.value = t;
15296         }
15297         s = wizard.getElement("selFeeds");
15298         var feeds = this.getFeeds();
15299         for(t in feeds) {
15300                 e = createTiddlyElement(s,"option",null,null,t);
15301                 e.value = t;
15302         }
15303         wizard.setValue("feeds",feeds);
15304         s.onchange = config.macros.importTiddlers.onFeedChange;
15305         var fileInput = wizard.getElement("txtBrowse");
15306         fileInput.onchange = config.macros.importTiddlers.onBrowseChange;
15307         fileInput.onkeyup = config.macros.importTiddlers.onBrowseChange;
15308         wizard.setButtons([{caption: this.openLabel, tooltip: this.openPrompt, onClick: config.macros.importTiddlers.onOpen}]);
15309 };
15310
15311 config.macros.importTiddlers.getFeeds = function()
15312 {
15313         var feeds = {};
15314         var tagged = store.getTaggedTiddlers("systemServer","title");
15315         for(var t=0; t<tagged.length; t++) {
15316                 var title = tagged[t].title;
15317                 var serverType = store.getTiddlerSlice(title,"Type");
15318                 if(!serverType)
15319                         serverType = "file";
15320                 feeds[title] = {title: title,
15321                                                 url: store.getTiddlerSlice(title,"URL"),
15322                                                 workspace: store.getTiddlerSlice(title,"Workspace"),
15323                                                 workspaceList: store.getTiddlerSlice(title,"WorkspaceList"),
15324                                                 tiddlerFilter: store.getTiddlerSlice(title,"TiddlerFilter"),
15325                                                 serverType: serverType,
15326                                                 description: store.getTiddlerSlice(title,"Description")};
15327         }
15328         return feeds;
15329 };
15330
15331 config.macros.importTiddlers.onFeedChange = function(e)
15332 {
15333         var wizard = new Wizard(this);
15334         var selTypes = wizard.getElement("selTypes");
15335         var fileInput = wizard.getElement("txtPath");
15336         var feeds = wizard.getValue("feeds");
15337         var f = feeds[this.value];
15338         if(f) {
15339                 selTypes.value = f.serverType;
15340                 fileInput.value = f.url;
15341                 this.selectedIndex = 0;
15342                 wizard.setValue("feedName",f.serverType);
15343                 wizard.setValue("feedHost",f.url);
15344                 wizard.setValue("feedWorkspace",f.workspace);
15345                 wizard.setValue("feedWorkspaceList",f.workspaceList);
15346                 wizard.setValue("feedTiddlerFilter",f.tiddlerFilter);
15347         }
15348         return false;
15349 };
15350
15351 config.macros.importTiddlers.onBrowseChange = function(e)
15352 {
15353         var wizard = new Wizard(this);
15354         var fileInput = wizard.getElement("txtPath");
15355         fileInput.value = "file://" + this.value;
15356         var serverType = wizard.getElement("selTypes");
15357         serverType.value = "file";
15358         return false;
15359 };
15360
15361 config.macros.importTiddlers.onOpen = function(e)
15362 {
15363         var wizard = new Wizard(this);
15364         var fileInput = wizard.getElement("txtPath");
15365         var url = fileInput.value;
15366         var serverType = wizard.getElement("selTypes").value;
15367         var adaptor = new config.adaptors[serverType];
15368         wizard.setValue("adaptor",adaptor);
15369         wizard.setValue("serverType",serverType);
15370         wizard.setValue("host",url);
15371         var context = {};
15372         var ret = adaptor.openHost(url,context,wizard,config.macros.importTiddlers.onOpenHost);
15373         if(ret !== true)
15374                 displayMessage(ret);
15375         wizard.setButtons([{caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}],config.macros.importTiddlers.statusOpenHost);
15376         return false;
15377 };
15378
15379 config.macros.importTiddlers.onOpenHost = function(context,wizard)
15380 {
15381         var adaptor = wizard.getValue("adaptor");
15382         if(context.status !== true)
15383                 displayMessage("Error in importTiddlers.onOpenHost: " + context.statusText);
15384         var ret = adaptor.getWorkspaceList(context,wizard,config.macros.importTiddlers.onGetWorkspaceList);
15385         if(ret !== true)
15386                 displayMessage(ret);
15387         wizard.setButtons([{caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}],config.macros.importTiddlers.statusGetWorkspaceList);
15388 };
15389
15390 config.macros.importTiddlers.onGetWorkspaceList = function(context,wizard)
15391 {
15392         if(context.status !== true)
15393                 displayMessage("Error in importTiddlers.onGetWorkspaceList: " + context.statusText);
15394         wizard.addStep(config.macros.importTiddlers.step2Title,config.macros.importTiddlers.step2Html);
15395         var s = wizard.getElement("selWorkspace");
15396         s.onchange = config.macros.importTiddlers.onWorkspaceChange;
15397         for(var t=0; t<context.workspaces.length; t++) {
15398                 var e = createTiddlyElement(s,"option",null,null,context.workspaces[t].title);
15399                 e.value = context.workspaces[t].title;
15400         }
15401         var workspaceList = wizard.getValue("feedWorkspaceList");
15402         if(workspaceList) {
15403                 var list = workspaceList.parseParams("workspace",null,false,true);
15404                 for(var n=1; n<list.length; n++) {
15405                         if(context.workspaces.findByField("title",list[n].value) == null) {
15406                                 e = createTiddlyElement(s,"option",null,null,list[n].value);
15407                                 e.value = list[n].value;
15408                         }
15409                 }
15410         }
15411         var workspace = wizard.getValue("feedWorkspace");
15412         if(workspace) {
15413                 t = wizard.getElement("txtWorkspace");
15414                 t.value = workspace;
15415         }
15416         wizard.setButtons([{caption: config.macros.importTiddlers.openLabel, tooltip: config.macros.importTiddlers.openPrompt, onClick: config.macros.importTiddlers.onChooseWorkspace}]);
15417 };
15418
15419 config.macros.importTiddlers.onWorkspaceChange = function(e)
15420 {
15421         var wizard = new Wizard(this);
15422         var t = wizard.getElement("txtWorkspace");
15423         t.value  = this.value;
15424         this.selectedIndex = 0;
15425         return false;
15426 };
15427
15428 config.macros.importTiddlers.onChooseWorkspace = function(e)
15429 {
15430         var wizard = new Wizard(this);
15431         var adaptor = wizard.getValue("adaptor");
15432         var workspace = wizard.getElement("txtWorkspace").value;
15433         wizard.setValue("workspace",workspace);
15434         var context = {};
15435         var ret = adaptor.openWorkspace(workspace,context,wizard,config.macros.importTiddlers.onOpenWorkspace);
15436         if(ret !== true)
15437                 displayMessage(ret);
15438         wizard.setButtons([{caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}],config.macros.importTiddlers.statusOpenWorkspace);
15439         return false;
15440 };
15441
15442 config.macros.importTiddlers.onOpenWorkspace = function(context,wizard)
15443 {
15444         if(context.status !== true)
15445                 displayMessage("Error in importTiddlers.onOpenWorkspace: " + context.statusText);
15446         var adaptor = wizard.getValue("adaptor");
15447         var ret = adaptor.getTiddlerList(context,wizard,config.macros.importTiddlers.onGetTiddlerList,wizard.getValue("feedTiddlerFilter"));
15448         if(ret !== true)
15449                 displayMessage(ret);
15450         wizard.setButtons([{caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}],config.macros.importTiddlers.statusGetTiddlerList);
15451 };
15452
15453 config.macros.importTiddlers.onGetTiddlerList = function(context,wizard)
15454 {
15455         if(context.status !== true)
15456                 displayMessage("Error in importTiddlers.onGetTiddlerList: " + context.statusText);
15457         // Extract data for the listview
15458         var listedTiddlers = [];
15459         if(context.tiddlers) {
15460                 for(var n=0; n<context.tiddlers.length; n++) {
15461                         var tiddler = context.tiddlers[n];
15462                         listedTiddlers.push({
15463                                 title: tiddler.title,
15464                                 modified: tiddler.modified,
15465                                 modifier: tiddler.modifier,
15466                                 text: tiddler.text ? wikifyPlainText(tiddler.text,100) : "",
15467                                 tags: tiddler.tags,
15468                                 size: tiddler.text ? tiddler.text.length : 0,
15469                                 tiddler: tiddler
15470                         });
15471                 }
15472         }
15473         listedTiddlers.sort(function(a,b) {return a.title < b.title ? -1 : (a.title == b.title ? 0 : +1);});
15474         // Display the listview
15475         wizard.addStep(config.macros.importTiddlers.step3Title,config.macros.importTiddlers.step3Html);
15476         var markList = wizard.getElement("markList");
15477         var listWrapper = document.createElement("div");
15478         markList.parentNode.insertBefore(listWrapper,markList);
15479         var listView = ListView.create(listWrapper,listedTiddlers,config.macros.importTiddlers.listViewTemplate);
15480         wizard.setValue("listView",listView);
15481         var txtSaveTiddler = wizard.getElement("txtSaveTiddler");
15482         txtSaveTiddler.value = config.macros.importTiddlers.generateSystemServerName(wizard);
15483         wizard.setButtons([
15484                         {caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel},
15485                         {caption: config.macros.importTiddlers.importLabel, tooltip: config.macros.importTiddlers.importPrompt, onClick:  config.macros.importTiddlers.doImport}
15486                 ]);
15487 };
15488
15489 config.macros.importTiddlers.generateSystemServerName = function(wizard)
15490 {
15491         var serverType = wizard.getValue("serverType");
15492         var host = wizard.getValue("host");
15493         var workspace = wizard.getValue("workspace");
15494         var pattern = config.macros.importTiddlers[workspace ? "systemServerNamePattern" : "systemServerNamePatternNoWorkspace"];
15495         return pattern.format([serverType,host,workspace]);
15496 };
15497
15498 config.macros.importTiddlers.saveServerTiddler = function(wizard)
15499 {
15500         var txtSaveTiddler = wizard.getElement("txtSaveTiddler").value;
15501         if(store.tiddlerExists(txtSaveTiddler)) {
15502                 if(!confirm(config.macros.importTiddlers.confirmOverwriteSaveTiddler.format([txtSaveTiddler])))
15503                         return;
15504                 store.suspendNotifications();
15505                 store.removeTiddler(txtSaveTiddler);
15506                 store.resumeNotifications();
15507         }
15508         var serverType = wizard.getValue("serverType");
15509         var host = wizard.getValue("host");
15510         var workspace = wizard.getValue("workspace");
15511         var text = config.macros.importTiddlers.serverSaveTemplate.format([serverType,host,workspace]);
15512         store.saveTiddler(txtSaveTiddler,txtSaveTiddler,text,config.macros.importTiddlers.serverSaveModifier,new Date(),["systemServer"]);
15513 };
15514
15515 config.macros.importTiddlers.doImport = function(e)
15516 {
15517         var wizard = new Wizard(this);
15518         if(wizard.getElement("chkSave").checked)
15519                 config.macros.importTiddlers.saveServerTiddler(wizard);
15520         var chkSync = wizard.getElement("chkSync").checked;
15521         wizard.setValue("sync",chkSync);
15522         var listView = wizard.getValue("listView");
15523         var rowNames = ListView.getSelectedRows(listView);
15524         var adaptor = wizard.getValue("adaptor");
15525         var overwrite = new Array();
15526         var t;
15527         for(t=0; t<rowNames.length; t++) {
15528                 if(store.tiddlerExists(rowNames[t]))
15529                         overwrite.push(rowNames[t]);
15530         }
15531         if(overwrite.length > 0) {
15532                 if(!confirm(config.macros.importTiddlers.confirmOverwriteText.format([overwrite.join(", ")])))
15533                         return false;
15534         }
15535         wizard.addStep(config.macros.importTiddlers.step4Title.format([rowNames.length]),config.macros.importTiddlers.step4Html);
15536         for(t=0; t<rowNames.length; t++) {
15537                 var link = document.createElement("div");
15538                 createTiddlyLink(link,rowNames[t],true);
15539                 var place = wizard.getElement("markReport");
15540                 place.parentNode.insertBefore(link,place);
15541         }
15542         wizard.setValue("remainingImports",rowNames.length);
15543         wizard.setButtons([
15544                         {caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}
15545                 ],config.macros.importTiddlers.statusDoingImport);
15546         for(t=0; t<rowNames.length; t++) {
15547                 var context = {};
15548                 context.allowSynchronous = true;
15549                 var inbound = adaptor.getTiddler(rowNames[t],context,wizard,config.macros.importTiddlers.onGetTiddler);
15550         }
15551         return false;
15552 };
15553
15554 config.macros.importTiddlers.onGetTiddler = function(context,wizard)
15555 {
15556         if(!context.status)
15557                 displayMessage("Error in importTiddlers.onGetTiddler: " + context.statusText);
15558         var tiddler = context.tiddler;
15559         store.suspendNotifications();
15560         store.saveTiddler(tiddler.title, tiddler.title, tiddler.text, tiddler.modifier, tiddler.modified, tiddler.tags, tiddler.fields, true, tiddler.created);
15561         if(!wizard.getValue("sync")) {
15562                 store.setValue(tiddler.title,'server',null);
15563         }
15564         store.resumeNotifications();
15565         if(!context.isSynchronous) 
15566                 store.notify(tiddler.title,true);
15567         var remainingImports = wizard.getValue("remainingImports")-1;
15568         wizard.setValue("remainingImports",remainingImports);
15569         if(remainingImports == 0) {
15570                 if(context.isSynchronous) {
15571                         store.notifyAll();
15572                         refreshDisplay();
15573                 }
15574                 wizard.setButtons([
15575                                 {caption: config.macros.importTiddlers.doneLabel, tooltip: config.macros.importTiddlers.donePrompt, onClick: config.macros.importTiddlers.onCancel}
15576                         ],config.macros.importTiddlers.statusDoneImport);
15577                 autoSaveChanges();
15578         }
15579 };
15580
15581 //--
15582 //-- Sync macro
15583 //--
15584
15585 // Synchronisation handlers
15586 config.syncers = {};
15587
15588 // Sync state.
15589 var currSync = null;
15590
15591 // sync macro
15592 config.macros.sync.handler = function(place,macroName,params,wikifier,paramString,tiddler)
15593 {
15594         if(!wikifier.isStatic)
15595                 this.startSync(place);
15596 };
15597
15598 config.macros.sync.startSync = function(place)
15599 {
15600         if(currSync)
15601                 config.macros.sync.cancelSync();
15602         currSync = {};
15603         currSync.syncList = this.getSyncableTiddlers();
15604         this.createSyncTasks();
15605         this.preProcessSyncableTiddlers();
15606         var wizard = new Wizard();
15607         currSync.wizard = wizard;
15608         wizard.createWizard(place,this.wizardTitle);
15609         wizard.addStep(this.step1Title,this.step1Html);
15610         var markList = wizard.getElement("markList");
15611         var listWrapper = document.createElement("div");
15612         markList.parentNode.insertBefore(listWrapper,markList);
15613         currSync.listView = ListView.create(listWrapper,currSync.syncList,this.listViewTemplate);
15614         this.processSyncableTiddlers();
15615         wizard.setButtons([
15616                         {caption: this.syncLabel, tooltip: this.syncPrompt, onClick: this.doSync}
15617                 ]);
15618 };
15619
15620 config.macros.sync.getSyncableTiddlers = function ()
15621 {
15622         var list = [];
15623         store.forEachTiddler(function(title,tiddler) {
15624                 var syncItem = {};
15625                 syncItem.serverType = tiddler.getServerType();
15626                 syncItem.serverHost = tiddler.fields['server.host'];
15627                 syncItem.serverWorkspace = tiddler.fields['server.workspace'];
15628                 syncItem.tiddler = tiddler;
15629                 syncItem.title = tiddler.title;
15630                 syncItem.isTouched = tiddler.isTouched();
15631                 syncItem.selected = syncItem.isTouched;
15632                 syncItem.syncStatus = config.macros.sync.syncStatusList[syncItem.isTouched ? "changedLocally" : "none"];
15633                 syncItem.status = syncItem.syncStatus.text;
15634                 if(syncItem.serverType && syncItem.serverHost)
15635                         list.push(syncItem);
15636                 });
15637         list.sort(function(a,b) {return a.title < b.title ? -1 : (a.title == b.title ? 0 : +1);});
15638         return list;
15639 };
15640
15641 config.macros.sync.preProcessSyncableTiddlers = function()
15642 {
15643         for(var t=0; t<currSync.syncList.length; t++) {
15644                 si = currSync.syncList[t];
15645                 var ti = si.syncTask.syncMachine.generateTiddlerInfo(si.tiddler);
15646                 si.serverUrl = ti.uri;
15647         }
15648 };
15649
15650 config.macros.sync.processSyncableTiddlers = function()
15651 {
15652         for(var t=0; t<currSync.syncList.length; t++) {
15653                 si = currSync.syncList[t];
15654                 si.rowElement.style.backgroundColor = si.syncStatus.color;
15655         }
15656 };
15657
15658 config.macros.sync.createSyncTasks = function()
15659 {
15660         currSync.syncTasks = [];
15661         for(var t=0; t<currSync.syncList.length; t++) {
15662                 var si = currSync.syncList[t];
15663                 var r = null;
15664                 for(var st=0; st<currSync.syncTasks.length; st++) {
15665                         var cst = currSync.syncTasks[st];
15666                         if(si.serverType == cst.serverType && si.serverHost == cst.serverHost && si.serverWorkspace == cst.serverWorkspace)
15667                                 r = cst;
15668                 }
15669                 if(r == null) {
15670                         si.syncTask = this.createSyncTask(si);
15671                         currSync.syncTasks.push(si.syncTask);
15672                 } else {
15673                         si.syncTask = r;
15674                         r.syncItems.push(si);
15675                 }
15676         }
15677 };
15678
15679 config.macros.sync.createSyncTask = function(syncItem)
15680 {
15681         var st = {};
15682         st.serverType = syncItem.serverType;
15683         st.serverHost = syncItem.serverHost;
15684         st.serverWorkspace = syncItem.serverWorkspace;
15685         st.syncItems = [syncItem];
15686         st.syncMachine = new SyncMachine(st.serverType,{
15687                 start: function() {
15688                         return this.openHost(st.serverHost,"openWorkspace");
15689                 },
15690                 openWorkspace: function() {
15691                         return this.openWorkspace(st.serverWorkspace,"getTiddlerList");
15692                 },
15693                 getTiddlerList: function() {
15694                         return this.getTiddlerList("gotTiddlerList");
15695                 },
15696                 gotTiddlerList: function(tiddlers) {
15697                         for(var t=0; t<st.syncItems.length; t++) {
15698                                 var si = st.syncItems[t];
15699                                 var f = tiddlers.findByField("title",si.title);
15700                                 if(f !== null) {
15701                                         if(tiddlers[f].fields['server.page.revision'] > si.tiddler.fields['server.page.revision']) {
15702                                                 si.syncStatus = config.macros.sync.syncStatusList[si.isTouched ? 'changedBoth' : 'changedServer'];
15703                                         }
15704                                 } else {
15705                                         si.syncStatus = config.macros.sync.syncStatusList.notFound;
15706                                 }
15707                                 config.macros.sync.updateSyncStatus(si);
15708                         }
15709                 },
15710                 getTiddler: function(title) {
15711                         return this.getTiddler(title,"onGetTiddler");
15712                 },
15713                 onGetTiddler: function(tiddler) {
15714                         var syncItem = st.syncItems.findByField("title",tiddler.title);
15715                         if(syncItem !== null) {
15716                                 syncItem = st.syncItems[syncItem];
15717                                 store.saveTiddler(tiddler.title, tiddler.title, tiddler.text, tiddler.modifier, tiddler.modified, tiddler.tags, tiddler.fields, true, tiddler.created);
15718                                 syncItem.syncStatus = config.macros.sync.syncStatusList.gotFromServer;
15719                                 config.macros.sync.updateSyncStatus(syncItem);
15720                         }
15721                 },
15722                 putTiddler: function(tiddler) {
15723                         return this.putTiddler(tiddler,"onPutTiddler");
15724                 },
15725                 onPutTiddler: function(tiddler) {
15726                         var syncItem = st.syncItems.findByField("title",tiddler.title);
15727                         if(syncItem !== null) {
15728                                 syncItem = st.syncItems[syncItem];
15729                                 store.resetTiddler(tiddler.title);
15730                                 syncItem.syncStatus = config.macros.sync.syncStatusList.putToServer;
15731                                 config.macros.sync.updateSyncStatus(syncItem);
15732                         }
15733                 }
15734         });
15735         st.syncMachine.go();
15736         return st;
15737 };
15738
15739 config.macros.sync.updateSyncStatus = function(syncItem)
15740 {
15741         var e = syncItem.colElements["status"];
15742         removeChildren(e);
15743         createTiddlyText(e,syncItem.syncStatus.text);
15744         syncItem.rowElement.style.backgroundColor = syncItem.syncStatus.color;
15745 };
15746
15747 config.macros.sync.doSync = function(e)
15748 {
15749         var rowNames = ListView.getSelectedRows(currSync.listView);
15750         for(var t=0; t<currSync.syncList.length; t++) {
15751                 var si = currSync.syncList[t];
15752                 if(rowNames.indexOf(si.title) != -1) {
15753                         config.macros.sync.doSyncItem(si);
15754                 }
15755         }
15756         return false;
15757 };
15758
15759 config.macros.sync.doSyncItem = function(syncItem)
15760 {
15761         var r = true;
15762         var sl = config.macros.sync.syncStatusList;
15763         switch(syncItem.syncStatus) {
15764                 case sl.changedServer:
15765                         r = syncItem.syncTask.syncMachine.go("getTiddler",syncItem.title);
15766                         break;
15767                 case sl.notFound:
15768                 case sl.changedLocally:
15769                 case sl.changedBoth:
15770                         r = syncItem.syncTask.syncMachine.go("putTiddler",syncItem.tiddler);
15771                         break;
15772                 default:
15773                         break;
15774         }
15775         if(r !== true)
15776                 displayMessage("Error in doSyncItem: " + r);
15777 };
15778
15779 config.macros.sync.cancelSync = function()
15780 {
15781         currSync = null;
15782 };
15783
15784 function SyncMachine(serverType,steps)
15785 {
15786         this.serverType = serverType;
15787         this.adaptor = new config.adaptors[serverType];
15788         this.steps = steps;
15789 }
15790
15791 SyncMachine.prototype.go = function(step,varargs)
15792 {
15793         if(!step)
15794                 step = "start";
15795         var h = this.steps[step];
15796         if(!h)
15797                 return null;
15798         var a = [];
15799         for(var t=1; t<arguments.length; t++)
15800                 a.push(arguments[t]);
15801         var r = h.apply(this,a);
15802         if(typeof r == "string")
15803                 this.invokeError(r);
15804         return r;
15805 };
15806
15807 SyncMachine.prototype.invokeError = function(message)
15808 {
15809         if(this.steps.error)
15810                 this.steps.error(message);
15811 };
15812
15813 SyncMachine.prototype.openHost = function(host,nextStep)
15814 {
15815         var me = this;
15816         return me.adaptor.openHost(host,null,null,function(context) {
15817                 if(typeof context.status == "string")
15818                         me.invokeError(context.status);
15819                 else
15820                         me.go(nextStep);
15821         });
15822 };
15823
15824 SyncMachine.prototype.getWorkspaceList = function(nextStep)
15825 {
15826         var me = this;
15827         return me.adaptor.getWorkspaceList(null,null,function(context) {
15828                 if(typeof context.status == "string")
15829                         me.invokeError(context.status);
15830                 else
15831                         me.go(nextStep,context.workspaces);
15832         });
15833 };
15834
15835 SyncMachine.prototype.openWorkspace = function(workspace,nextStep)
15836 {
15837         var me = this;
15838         return me.adaptor.openWorkspace(workspace,null,null,function(context) {
15839                 if(typeof context.status == "string")
15840                         me.invokeError(context.status);
15841                 else
15842                         me.go(nextStep);
15843         });
15844 };
15845
15846 SyncMachine.prototype.getTiddlerList = function(nextStep)
15847 {
15848         var me = this;
15849         return me.adaptor.getTiddlerList(null,null,function(context) {
15850                 if(typeof context.status == "string")
15851                         me.invokeError(context.status);
15852                 else
15853                         me.go(nextStep,context.tiddlers);
15854         });
15855 };
15856
15857 SyncMachine.prototype.generateTiddlerInfo = function(tiddler)
15858 {
15859         return this.adaptor.generateTiddlerInfo(tiddler);
15860 };
15861
15862 SyncMachine.prototype.getTiddler = function(title,nextStep)
15863 {
15864         var me = this;
15865         return me.adaptor.getTiddler(title,null,null,function(context) {
15866                 if(typeof context.status == "string")
15867                         me.invokeError(context.status);
15868                 else
15869                         me.go(nextStep,context.tiddler);
15870         });
15871 };
15872
15873 SyncMachine.prototype.putTiddler = function(tiddler,nextStep)
15874 {
15875         var me = this;
15876         return me.adaptor.putTiddler(tiddler,null,null,function(context) {
15877                 if(typeof context.status == "string")
15878                         me.invokeError(context.status);
15879                 else
15880                         me.go(nextStep,tiddler);
15881         });
15882 };
15883
15884 //--
15885 //-- Manager UI for groups of tiddlers
15886 //--
15887
15888 config.macros.plugins.handler = function(place,macroName,params,wikifier,paramString,tiddler)
15889 {
15890         var wizard = new Wizard();
15891         wizard.createWizard(place,this.wizardTitle);
15892         wizard.addStep(this.step1Title,this.step1Html);
15893         var markList = wizard.getElement("markList");
15894         var listWrapper = document.createElement("div");
15895         markList.parentNode.insertBefore(listWrapper,markList);
15896         listWrapper.setAttribute("refresh","macro");
15897         listWrapper.setAttribute("macroName","plugins");
15898         listWrapper.setAttribute("params",paramString);
15899         this.refresh(listWrapper,paramString);
15900 };
15901
15902 config.macros.plugins.refresh = function(listWrapper,params)
15903 {
15904         var wizard = new Wizard(listWrapper);
15905         var selectedRows = [];
15906         ListView.forEachSelector(listWrapper,function(e,rowName) {
15907                         if(e.checked)
15908                                 selectedRows.push(e.getAttribute("rowName"));
15909                 });
15910         removeChildren(listWrapper);
15911         params = params.parseParams("anon");
15912         var plugins = installedPlugins.slice(0);
15913         var t,tiddler,p;
15914         var configTiddlers = store.getTaggedTiddlers("systemConfig");
15915         for(t=0; t<configTiddlers.length; t++) {
15916                 tiddler = configTiddlers[t];
15917                 if(plugins.findByField("title",tiddler.title) == null) {
15918                         p = getPluginInfo(tiddler);
15919                         p.executed = false;
15920                         p.log.splice(0,0,this.skippedText);
15921                         plugins.push(p);
15922                 }
15923         }
15924         for(t=0; t<plugins.length; t++) {
15925                 p = plugins[t];
15926                 p.size = p.tiddler.text ? p.tiddler.text.length : 0;
15927                 p.forced = p.tiddler.isTagged("systemConfigForce");
15928                 p.disabled = p.tiddler.isTagged("systemConfigDisable");
15929                 p.Selected = selectedRows.indexOf(plugins[t].title) != -1;
15930         }
15931         if(plugins.length == 0) {
15932                 createTiddlyElement(listWrapper,"em",null,null,this.noPluginText);
15933                 wizard.setButtons([]);
15934         } else {
15935                 var listView = ListView.create(listWrapper,plugins,this.listViewTemplate,this.onSelectCommand);
15936                 wizard.setValue("listView",listView);
15937                 wizard.setButtons([
15938                                 {caption: config.macros.plugins.removeLabel, tooltip: config.macros.plugins.removePrompt, onClick:  config.macros.plugins.doRemoveTag},
15939                                 {caption: config.macros.plugins.deleteLabel, tooltip: config.macros.plugins.deletePrompt, onClick:  config.macros.plugins.doDelete}
15940                         ]);
15941         }
15942 };
15943
15944 config.macros.plugins.doRemoveTag = function(e)
15945 {
15946         var wizard = new Wizard(this);
15947         var listView = wizard.getValue("listView");
15948         var rowNames = ListView.getSelectedRows(listView);
15949         if(rowNames.length == 0) {
15950                 alert(config.messages.nothingSelected);
15951         } else {
15952                 for(var t=0; t<rowNames.length; t++)
15953                         store.setTiddlerTag(rowNames[t],false,"systemConfig");
15954         }
15955 };
15956
15957 config.macros.plugins.doDelete = function(e)
15958 {
15959         var wizard = new Wizard(this);
15960         var listView = wizard.getValue("listView");
15961         var rowNames = ListView.getSelectedRows(listView);
15962         if(rowNames.length == 0) {
15963                 alert(config.messages.nothingSelected);
15964         } else {
15965                 if(confirm(config.macros.plugins.confirmDeleteText.format([rowNames.join(", ")]))) {
15966                         for(t=0; t<rowNames.length; t++) {
15967                                 store.removeTiddler(rowNames[t]);
15968                                 story.closeTiddler(rowNames[t],true);
15969                         }
15970                 }
15971         }
15972 };
15973
15974 //--
15975 //-- Message area
15976 //--
15977
15978 function getMessageDiv()
15979 {
15980         var msgArea = document.getElementById("messageArea");
15981         if(!msgArea)
15982                 return null;
15983         if(!msgArea.hasChildNodes())
15984                 createTiddlyButton(createTiddlyElement(msgArea,"div",null,"messageToolbar"),
15985                         config.messages.messageClose.text,
15986                         config.messages.messageClose.tooltip,
15987                         clearMessage);
15988         msgArea.style.display = "block";
15989         return createTiddlyElement(msgArea,"div");
15990 }
15991
15992 function displayMessage(text,linkText)
15993 {
15994         var e = getMessageDiv();
15995         if(!e) {
15996                 alert(text);
15997                 return;
15998         }
15999         if(linkText) {
16000                 var link = createTiddlyElement(e,"a",null,null,text);
16001                 link.href = linkText;
16002                 link.target = "_blank";
16003         } else {
16004                 e.appendChild(document.createTextNode(text));
16005         }
16006 }
16007
16008 function clearMessage()
16009 {
16010         var msgArea = document.getElementById("messageArea");
16011         if(msgArea) {
16012                 removeChildren(msgArea);
16013                 msgArea.style.display = "none";
16014         }
16015         return false;
16016 }
16017
16018 //--
16019 //-- Refresh mechanism
16020 //--
16021
16022 config.refreshers = {
16023         link: function(e,changeList)
16024                 {
16025                 var title = e.getAttribute("tiddlyLink");
16026                 refreshTiddlyLink(e,title);
16027                 return true;
16028                 },
16029         
16030         tiddler: function(e,changeList)
16031                 {
16032                 var title = e.getAttribute("tiddler");
16033                 var template = e.getAttribute("template");
16034                 if(changeList && changeList.indexOf(title) != -1 && !story.isDirty(title))
16035                         story.refreshTiddler(title,template,true);
16036                 else
16037                         refreshElements(e,changeList);
16038                 return true;
16039                 },
16040
16041         content: function(e,changeList)
16042                 {
16043                 var title = e.getAttribute("tiddler");
16044                 var force = e.getAttribute("force");
16045                 if(force != null || changeList == null || changeList.indexOf(title) != -1) {
16046                         removeChildren(e);
16047                         wikify(store.getTiddlerText(title,title),e,null);
16048                         return true;
16049                 } else
16050                         return false;
16051                 },
16052
16053         macro: function(e,changeList)
16054                 {
16055                 var macro = e.getAttribute("macroName");
16056                 var params = e.getAttribute("params");
16057                 if(macro)
16058                         macro = config.macros[macro];
16059                 if(macro && macro.refresh)
16060                         macro.refresh(e,params);
16061                 return true;
16062                 }
16063 };
16064
16065 function refreshElements(root,changeList)
16066 {
16067         var nodes = root.childNodes;
16068         for(var c=0; c<nodes.length; c++) {
16069                 var e = nodes[c], type = null;
16070                 if(e.getAttribute  && (e.tagName ? e.tagName != "IFRAME" : true))
16071                         type = e.getAttribute("refresh");
16072                 var refresher = config.refreshers[type];
16073                 var refreshed = false;
16074                 if(refresher != undefined)
16075                         refreshed = refresher(e,changeList);
16076                 if(e.hasChildNodes() && !refreshed)
16077                         refreshElements(e,changeList);
16078         }
16079 }
16080
16081 function applyHtmlMacros(root,tiddler)
16082 {
16083         var e = root.firstChild;
16084         while(e) {
16085                 var nextChild = e.nextSibling;
16086                 if(e.getAttribute) {
16087                         var macro = e.getAttribute("macro");
16088                         if(macro) {
16089                                 var params = "";
16090                                 var p = macro.indexOf(" ");
16091                                 if(p != -1) {
16092                                         params = macro.substr(p+1);
16093                                         macro = macro.substr(0,p);
16094                                 }
16095                                 invokeMacro(e,macro,params,null,tiddler);
16096                         }
16097                 }
16098                 if(e.hasChildNodes())
16099                         applyHtmlMacros(e,tiddler);
16100                 e = nextChild;
16101         }
16102 }
16103
16104 function refreshPageTemplate(title)
16105 {
16106         var stash = createTiddlyElement(document.body,"div");
16107         stash.style.display = "none";
16108         var display = document.getElementById("tiddlerDisplay");
16109         var nodes,t;
16110         if(display) {
16111                 nodes = display.childNodes;
16112                 for(t=nodes.length-1; t>=0; t--)
16113                         stash.appendChild(nodes[t]);
16114         }
16115         var wrapper = document.getElementById("contentWrapper");
16116         if(!title)
16117                 title = "PageTemplate";
16118         var html = store.getRecursiveTiddlerText(title,null,10);
16119         wrapper.innerHTML = html;
16120         applyHtmlMacros(wrapper);
16121         refreshElements(wrapper);
16122         display = document.getElementById("tiddlerDisplay");
16123         removeChildren(display);
16124         if(!display)
16125                 display = createTiddlyElement(wrapper,"div","tiddlerDisplay");
16126         nodes = stash.childNodes;
16127         for(t=nodes.length-1; t>=0; t--)
16128                 display.appendChild(nodes[t]);
16129         removeNode(stash);
16130 }
16131
16132 function refreshDisplay(hint)
16133 {
16134         if(typeof hint == "string")
16135                 hint = [hint];
16136         var e = document.getElementById("contentWrapper");
16137         refreshElements(e,hint);
16138         if(backstage.isPanelVisible()) {
16139                 e = document.getElementById("backstage");
16140                 refreshElements(e,hint);
16141         }
16142 }
16143
16144 function refreshPageTitle()
16145 {
16146         document.title = getPageTitle();
16147 }
16148
16149 function getPageTitle()
16150 {
16151         var st = wikifyPlain("SiteTitle");
16152         var ss = wikifyPlain("SiteSubtitle");
16153         return st + ((st == "" || ss == "") ? "" : " - ") + ss;
16154 }
16155
16156 function refreshStyles(title,doc)
16157 {
16158         if(!doc)
16159                 doc = document;
16160         setStylesheet(title == null ? "" : store.getRecursiveTiddlerText(title,"",10),title,doc);
16161 }
16162
16163 function refreshColorPalette(title)
16164 {
16165         if(!startingUp)
16166                 refreshAll();
16167 }
16168
16169 function refreshAll()
16170 {
16171         refreshPageTemplate();
16172         refreshDisplay();
16173         refreshStyles("StyleSheetLayout");
16174         refreshStyles("StyleSheetColors");
16175         refreshStyles("StyleSheet");
16176         refreshStyles("StyleSheetPrint");
16177 }
16178
16179 //--
16180 //-- Options cookie stuff
16181 //--
16182
16183 config.optionHandlers = {
16184         'txt': {
16185                 get: function(name) {return encodeCookie(config.options[name].toString());},
16186                 set: function(name,value) {config.options[name] = decodeCookie(value);}
16187         },
16188         'chk': {
16189                 get: function(name) {return config.options[name] ? "true" : "false";},
16190                 set: function(name,value) {config.options[name] = value == "true";}
16191         }
16192 };
16193
16194 function loadOptionsCookie()
16195 {
16196         if(safeMode)
16197                 return;
16198         var cookies = document.cookie.split(";");
16199         for(var c=0; c<cookies.length; c++) {
16200                 var p = cookies[c].indexOf("=");
16201                 if(p != -1) {
16202                         var name = cookies[c].substr(0,p).trim();
16203                         var value = cookies[c].substr(p+1).trim();
16204                         var optType = name.substr(0,3);
16205                         if(config.optionHandlers[optType] && config.optionHandlers[optType].set)
16206                                 config.optionHandlers[optType].set(name,value);
16207                 }
16208         }
16209 }
16210
16211 function saveOptionCookie(name)
16212 {
16213         if(safeMode)
16214                 return;
16215         var c = name + "=";
16216         var optType = name.substr(0,3);
16217         if(config.optionHandlers[optType] && config.optionHandlers[optType].get)
16218                 c += config.optionHandlers[optType].get(name);
16219         c += "; expires=Fri, 1 Jan 2038 12:00:00 UTC; path=/";
16220         document.cookie = c;
16221 }
16222
16223 function encodeCookie(s)
16224 {
16225         return escape(manualConvertUnicodeToUTF8(s));
16226 }
16227
16228 function decodeCookie(s)
16229 {
16230         s = unescape(s);
16231         var re = /&#[0-9]{1,5};/g;
16232         return s.replace(re,function($0) {return String.fromCharCode(eval($0.replace(/[&#;]/g,"")));});
16233 }
16234
16235 //--
16236 //-- Saving
16237 //--
16238
16239 var saveUsingSafari = false;
16240
16241 var startSaveArea = '<div id="' + 'storeArea">'; // Split up into two so that indexOf() of this source doesn't find it
16242 var endSaveArea = '</d' + 'iv>';
16243
16244 // If there are unsaved changes, force the user to confirm before exitting
16245 function confirmExit()
16246 {
16247         hadConfirmExit = true;
16248         if((store && store.isDirty && store.isDirty()) || (story && story.areAnyDirty && story.areAnyDirty()))
16249                 return config.messages.confirmExit;
16250 }
16251
16252 // Give the user a chance to save changes before exitting
16253 function checkUnsavedChanges()
16254 {
16255         if(store && store.isDirty && store.isDirty() && window.hadConfirmExit === false) {
16256                 if(confirm(config.messages.unsavedChangesWarning))
16257                         saveChanges();
16258         }
16259 }
16260
16261 function updateLanguageAttribute(s)
16262 {
16263         if(config.locale) {
16264                 var mRE = /(<html(?:.*?)?)(?: xml:lang\="([a-z]+)")?(?: lang\="([a-z]+)")?>/;
16265                 var m = mRE.exec(s);
16266                 if(m) {
16267                         var t = m[1];
16268                         if(m[2])
16269                                 t += ' xml:lang="' + config.locale + '"';
16270                         if(m[3])
16271                                 t += ' lang="' + config.locale + '"';
16272                         t += ">";
16273                         s = s.substr(0,m.index) + t + s.substr(m.index+m[0].length);
16274                 }
16275         }
16276         return s;
16277 }
16278
16279 function updateMarkupBlock(s,blockName,tiddlerName)
16280 {
16281         return s.replaceChunk(
16282                         "<!--%0-START-->".format([blockName]),
16283                         "<!--%0-END-->".format([blockName]),
16284                         "\n" + store.getRecursiveTiddlerText(tiddlerName,"") + "\n");
16285 }
16286
16287 function updateOriginal(original,posDiv)
16288 {
16289         if(!posDiv)
16290                 posDiv = locateStoreArea(original);
16291         if(!posDiv) {
16292                 alert(config.messages.invalidFileError.format([localPath]));
16293                 return null;
16294         }
16295         var revised = original.substr(0,posDiv[0] + startSaveArea.length) + "\n" +
16296                                 convertUnicodeToUTF8(store.allTiddlersAsHtml()) + "\n" +
16297                                 original.substr(posDiv[1]);
16298         var newSiteTitle = convertUnicodeToUTF8(getPageTitle()).htmlEncode();
16299         revised = revised.replaceChunk("<title"+">","</title"+">"," " + newSiteTitle + " ");
16300         revised = updateLanguageAttribute(revised);
16301         revised = updateMarkupBlock(revised,"PRE-HEAD","MarkupPreHead");
16302         revised = updateMarkupBlock(revised,"POST-HEAD","MarkupPostHead");
16303         revised = updateMarkupBlock(revised,"PRE-BODY","MarkupPreBody");
16304         revised = updateMarkupBlock(revised,"POST-SCRIPT","MarkupPostBody");
16305         return revised;
16306 }
16307
16308 function locateStoreArea(original)
16309 {
16310         // Locate the storeArea div's
16311         var posOpeningDiv = original.indexOf(startSaveArea);
16312         var limitClosingDiv = original.indexOf("<"+"!--POST-STOREAREA--"+">");
16313         if(limitClosingDiv == -1)
16314                 limitClosingDiv = original.indexOf("<"+"!--POST-BODY-START--"+">");
16315         var posClosingDiv = original.lastIndexOf(endSaveArea,limitClosingDiv == -1 ? original.length : limitClosingDiv);
16316         return (posOpeningDiv != -1 && posClosingDiv != -1) ? [posOpeningDiv,posClosingDiv] : null;
16317 }
16318
16319 function autoSaveChanges(onlyIfDirty,tiddlers)
16320 {
16321         if(config.options.chkAutoSave)
16322                 saveChanges(onlyIfDirty,tiddlers);
16323 }
16324
16325 // Save this tiddlywiki with the pending changes
16326 function saveChanges(onlyIfDirty,tiddlers)
16327 {
16328         if(onlyIfDirty && !store.isDirty())
16329                 return;
16330         clearMessage();
16331         // Get the URL of the document
16332         var originalPath = document.location.toString();
16333         // Check we were loaded from a file URL
16334         if(originalPath.substr(0,5) != "file:") {
16335                 alert(config.messages.notFileUrlError);
16336                 if(store.tiddlerExists(config.messages.saveInstructions))
16337                         story.displayTiddler(null,config.messages.saveInstructions);
16338                 return;
16339         }
16340         var localPath = getLocalPath(originalPath);
16341         // Load the original file
16342         var original = loadFile(localPath);
16343         if(original == null) {
16344                 alert(config.messages.cantSaveError);
16345                 if(store.tiddlerExists(config.messages.saveInstructions))
16346                         story.displayTiddler(null,config.messages.saveInstructions);
16347                 return;
16348         }
16349         // Locate the storeArea div's
16350         var posDiv = locateStoreArea(original);
16351         if(!posDiv) {
16352                 alert(config.messages.invalidFileError.format([localPath]));
16353                 return;
16354         }
16355         saveBackup(localPath,original);
16356         saveRss(localPath);
16357         saveEmpty(localPath,original,posDiv);
16358         saveMain(localPath,original,posDiv);
16359 }
16360
16361 function saveBackup(localPath,original)
16362 {
16363         // Save the backup
16364         if(config.options.chkSaveBackups) {
16365                 var backupPath = getBackupPath(localPath);
16366                 var backup = config.browser.isIE ? ieCopyFile(backupPath,localPath) : saveFile(backupPath,original);
16367                 if(backup)
16368                         displayMessage(config.messages.backupSaved,"file://" + backupPath);
16369                 else
16370                         alert(config.messages.backupFailed);
16371         }
16372 }
16373
16374 function saveRss(localPath)
16375 {
16376         if(config.options.chkGenerateAnRssFeed) {
16377                 var rssPath = localPath.substr(0,localPath.lastIndexOf(".")) + ".xml";
16378                 var rssSave = saveFile(rssPath,convertUnicodeToUTF8(generateRss()));
16379                 if(rssSave)
16380                         displayMessage(config.messages.rssSaved,"file://" + rssPath);
16381                 else
16382                         alert(config.messages.rssFailed);
16383         }
16384 }
16385
16386 function saveEmpty(localPath,original,posDiv)
16387 {
16388         if(config.options.chkSaveEmptyTemplate) {
16389                 var emptyPath,p;
16390                 if((p = localPath.lastIndexOf("/")) != -1)
16391                         emptyPath = localPath.substr(0,p) + "/empty.html";
16392                 else if((p = localPath.lastIndexOf("\\")) != -1)
16393                         emptyPath = localPath.substr(0,p) + "\\empty.html";
16394                 else
16395                         emptyPath = localPath + ".empty.html";
16396                 var empty = original.substr(0,posDiv[0] + startSaveArea.length) + original.substr(posDiv[1]);
16397                 var emptySave = saveFile(emptyPath,empty);
16398                 if(emptySave)
16399                         displayMessage(config.messages.emptySaved,"file://" + emptyPath);
16400                 else
16401                         alert(config.messages.emptyFailed);
16402         }
16403 }
16404
16405 function saveMain(localPath,original,posDiv)
16406 {
16407         var save;
16408         try {
16409                 var revised = updateOriginal(original,posDiv);
16410                 save = saveFile(localPath,revised);
16411         } catch (ex) {
16412                 showException(ex);
16413         }
16414         if(save) {
16415                 displayMessage(config.messages.mainSaved,"file://" + localPath);
16416                 store.setDirty(false);
16417         } else {
16418                 alert(config.messages.mainFailed);
16419         }
16420 }
16421
16422 function getLocalPath(origPath)
16423 {
16424         var originalPath = convertUriToUTF8(origPath,config.options.txtFileSystemCharSet);
16425         // Remove any location or query part of the URL
16426         var argPos = originalPath.indexOf("?");
16427         if(argPos != -1)
16428                 originalPath = originalPath.substr(0,argPos);
16429         var hashPos = originalPath.indexOf("#");
16430         if(hashPos != -1)
16431                 originalPath = originalPath.substr(0,hashPos);
16432         // Convert file://localhost/ to file:///
16433         if(originalPath.indexOf("file://localhost/") == 0)
16434                 originalPath = "file://" + originalPath.substr(16);
16435         // Convert to a native file format
16436         var localPath;
16437         if(originalPath.charAt(9) == ":") // pc local file
16438                 localPath = unescape(originalPath.substr(8)).replace(new RegExp("/","g"),"\\");
16439         else if(originalPath.indexOf("file://///") == 0) // FireFox pc network file
16440                 localPath = "\\\\" + unescape(originalPath.substr(10)).replace(new RegExp("/","g"),"\\");
16441         else if(originalPath.indexOf("file:///") == 0) // mac/unix local file
16442                 localPath = unescape(originalPath.substr(7));
16443         else if(originalPath.indexOf("file:/") == 0) // mac/unix local file
16444                 localPath = unescape(originalPath.substr(5));
16445         else // pc network file
16446                 localPath = "\\\\" + unescape(originalPath.substr(7)).replace(new RegExp("/","g"),"\\");
16447         return localPath;
16448 }
16449
16450 function getBackupPath(localPath)
16451 {
16452         var backSlash = true;
16453         var dirPathPos = localPath.lastIndexOf("\\");
16454         if(dirPathPos == -1) {
16455                 dirPathPos = localPath.lastIndexOf("/");
16456                 backSlash = false;
16457         }
16458         var backupFolder = config.options.txtBackupFolder;
16459         if(!backupFolder || backupFolder == "")
16460                 backupFolder = ".";
16461         var backupPath = localPath.substr(0,dirPathPos) + (backSlash ? "\\" : "/") + backupFolder + localPath.substr(dirPathPos);
16462         backupPath = backupPath.substr(0,backupPath.lastIndexOf(".")) + "." + (new Date()).convertToYYYYMMDDHHMMSSMMM() + ".html";
16463         return backupPath;
16464 }
16465
16466 function generateRss()
16467 {
16468         var s = [];
16469         var d = new Date();
16470         var u = store.getTiddlerText("SiteUrl");
16471         // Assemble the header
16472         s.push("<" + "?xml version=\"1.0\"?" + ">");
16473         s.push("<rss version=\"2.0\">");
16474         s.push("<channel>");
16475         s.push("<title" + ">" + wikifyPlain("SiteTitle").htmlEncode() + "</title" + ">");
16476         if(u)
16477                 s.push("<link>" + u.htmlEncode() + "</link>");
16478         s.push("<description>" + wikifyPlain("SiteSubtitle").htmlEncode() + "</description>");
16479         s.push("<language>en-us</language>");
16480         s.push("<copyright>Copyright " + d.getFullYear() + " " + config.options.txtUserName.htmlEncode() + "</copyright>");
16481         s.push("<pubDate>" + d.toGMTString() + "</pubDate>");
16482         s.push("<lastBuildDate>" + d.toGMTString() + "</lastBuildDate>");
16483         s.push("<docs>http://blogs.law.harvard.edu/tech/rss</docs>");
16484         s.push("<generator>TiddlyWiki " + version.major + "." + version.minor + "." + version.revision + "</generator>");
16485         // The body
16486         var tiddlers = store.getTiddlers("modified","excludeLists");
16487         var n = config.numRssItems > tiddlers.length ? 0 : tiddlers.length-config.numRssItems;
16488         for (var t=tiddlers.length-1; t>=n; t--)
16489                 s.push(tiddlers[t].saveToRss(u));
16490         // And footer
16491         s.push("</channel>");
16492         s.push("</rss>");
16493         // Save it all
16494         return s.join("\n");
16495 }
16496
16497 //--
16498 //-- Filesystem code
16499 //--
16500
16501 function convertUTF8ToUnicode(u)
16502 {
16503         if(window.netscape == undefined)
16504                 return manualConvertUTF8ToUnicode(u);
16505         else
16506                 return mozConvertUTF8ToUnicode(u);
16507 }
16508
16509 function manualConvertUTF8ToUnicode(utf)
16510 {
16511         var uni = utf;
16512         var src = 0;
16513         var dst = 0;
16514         var b1, b2, b3;
16515         var c;
16516         while(src < utf.length) {
16517                 b1 = utf.charCodeAt(src++);
16518                 if(b1 < 0x80) {
16519                         dst++;
16520                 } else if(b1 < 0xE0) {
16521                         b2 = utf.charCodeAt(src++);
16522                         c = String.fromCharCode(((b1 & 0x1F) << 6) | (b2 & 0x3F));
16523                         uni = uni.substring(0,dst++).concat(c,utf.substr(src));
16524                 } else {
16525                         b2 = utf.charCodeAt(src++);
16526                         b3 = utf.charCodeAt(src++);
16527                         c = String.fromCharCode(((b1 & 0xF) << 12) | ((b2 & 0x3F) << 6) | (b3 & 0x3F));
16528                         uni = uni.substring(0,dst++).concat(c,utf.substr(src));
16529                 }
16530         }
16531         return uni;
16532 }
16533
16534 function mozConvertUTF8ToUnicode(u)
16535 {
16536         try {
16537                 netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
16538                 var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
16539                 converter.charset = "UTF-8";
16540         } catch(ex) {
16541                 return manualConvertUTF8ToUnicode(u);
16542         } // fallback
16543         var s = converter.ConvertToUnicode(u);
16544         var fin = converter.Finish();
16545         return (fin.length > 0) ? s+fin : s;
16546 }
16547
16548 function convertUnicodeToUTF8(s)
16549 {
16550         if(window.netscape == undefined)
16551                 return manualConvertUnicodeToUTF8(s);
16552         else
16553                 return mozConvertUnicodeToUTF8(s);
16554 }
16555
16556 function manualConvertUnicodeToUTF8(s)
16557 {
16558         var re = /[^\u0000-\u007F]/g ;
16559         return s.replace(re,function($0) {return "&#" + $0.charCodeAt(0).toString() + ";";});
16560 }
16561
16562 function mozConvertUnicodeToUTF8(s)
16563 {
16564         try {
16565                 netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
16566                 var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
16567                 converter.charset = "UTF-8";
16568         } catch(ex) {
16569                 return manualConvertUnicodeToUTF8(s);
16570         } // fallback
16571         var u = converter.ConvertFromUnicode(s);
16572         var fin = converter.Finish();
16573         if(fin.length > 0)
16574                 return u + fin;
16575         else
16576                 return u;
16577 }
16578
16579 function convertUriToUTF8(uri,charSet)
16580 {
16581         if(window.netscape == undefined || charSet == undefined || charSet == "")
16582                 return uri;
16583         try {
16584                 netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
16585                 var converter = Components.classes["@mozilla.org/intl/utf8converterservice;1"].getService(Components.interfaces.nsIUTF8ConverterService);
16586         } catch(ex) {
16587                 return uri;
16588         }
16589         return converter.convertURISpecToUTF8(uri,charSet);
16590 }
16591
16592 function saveFile(fileUrl,content)
16593 {
16594         var r = null;
16595         if(!r)
16596                 r = mozillaSaveFile(fileUrl,content);
16597         if(!r)
16598                 r = ieSaveFile(fileUrl,content);
16599         if(!r)
16600                 r = javaSaveFile(fileUrl,content);
16601         return r;
16602 }
16603
16604 function loadFile(fileUrl)
16605 {
16606         var r = null;
16607         if((r == null) || (r == false))
16608                 r = mozillaLoadFile(fileUrl);
16609         if((r == null) || (r == false))
16610                 r = ieLoadFile(fileUrl);
16611         if((r == null) || (r == false))
16612                 r = javaLoadFile(fileUrl);
16613         return r;
16614 }
16615
16616 // Returns null if it can't do it, false if there's an error, true if it saved OK
16617 function ieSaveFile(filePath,content)
16618 {
16619         try {
16620                 var fso = new ActiveXObject("Scripting.FileSystemObject");
16621         } catch(ex) {
16622                 return null;
16623         }
16624         var file = fso.OpenTextFile(filePath,2,-1,0);
16625         file.Write(content);
16626         file.Close();
16627         return true;
16628 }
16629
16630 // Returns null if it can't do it, false if there's an error, or a string of the content if successful
16631 function ieLoadFile(filePath)
16632 {
16633         try {
16634                 var fso = new ActiveXObject("Scripting.FileSystemObject");
16635                 var file = fso.OpenTextFile(filePath,1);
16636                 var content = file.ReadAll();
16637                 file.Close();
16638         } catch(ex) {
16639                 return null;
16640         }
16641         return content;
16642 }
16643
16644 function ieCopyFile(dest,source)
16645 {
16646         try {
16647                 var fso = new ActiveXObject("Scripting.FileSystemObject");
16648                 fso.GetFile(source).Copy(dest);
16649         } catch(ex) {
16650                 return false;
16651         }
16652         return true;
16653 }
16654
16655 // Returns null if it can't do it, false if there's an error, true if it saved OK
16656 function mozillaSaveFile(filePath,content)
16657 {
16658         if(window.Components) {
16659                 try {
16660                         netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
16661                         var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
16662                         file.initWithPath(filePath);
16663                         if(!file.exists())
16664                                 file.create(0,0664);
16665                         var out = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream);
16666                         out.init(file,0x20|0x02,00004,null);
16667                         out.write(content,content.length);
16668                         out.flush();
16669                         out.close();
16670                         return true;
16671                 } catch(ex) {
16672                         return false;
16673                 }
16674         }
16675         return null;
16676 }
16677
16678 // Returns null if it can't do it, false if there's an error, or a string of the content if successful
16679 function mozillaLoadFile(filePath)
16680 {
16681         if(window.Components) {
16682                 try {
16683                         netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
16684                         var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
16685                         file.initWithPath(filePath);
16686                         if(!file.exists())
16687                                 return null;
16688                         var inputStream = Components.classes["@mozilla.org/network/file-input-stream;1"].createInstance(Components.interfaces.nsIFileInputStream);
16689                         inputStream.init(file,0x01,00004,null);
16690                         var sInputStream = Components.classes["@mozilla.org/scriptableinputstream;1"].createInstance(Components.interfaces.nsIScriptableInputStream);
16691                         sInputStream.init(inputStream);
16692                         return sInputStream.read(sInputStream.available());
16693                 } catch(ex) {
16694                         return false;
16695                 }
16696         }
16697         return null;
16698 }
16699
16700 function javaUrlToFilename(url)
16701 {
16702         var f = "//localhost";
16703         if(url.indexOf(f) == 0)
16704                 return url.substring(f.length);
16705         var i = url.indexOf(":");
16706         if(i > 0)
16707                 return url.substring(i-1);
16708         return url;
16709 }
16710
16711 function javaSaveFile(filePath,content)
16712 {
16713         try {
16714                 if(document.applets["TiddlySaver"])
16715                         return document.applets["TiddlySaver"].saveFile(javaUrlToFilename(filePath),"UTF-8",content);
16716         } catch(ex) {
16717         }
16718         try {
16719                 var s = new java.io.PrintStream(new java.io.FileOutputStream(javaUrlToFilename(filePath)));
16720                 s.print(content);
16721                 s.close();
16722         } catch(ex) {
16723                 return null;
16724         }
16725         return true;
16726 }
16727
16728 function javaLoadFile(filePath)
16729 {
16730         try {
16731                 if(document.applets["TiddlySaver"])
16732                         return String(document.applets["TiddlySaver"].loadFile(javaUrlToFilename(filePath),"UTF-8"));
16733         } catch(ex) {
16734         }
16735         var content = [];
16736         try {
16737                 var r = new java.io.BufferedReader(new java.io.FileReader(javaUrlToFilename(filePath)));
16738                 var line;
16739                 while((line = r.readLine()) != null)
16740                         content.push(new String(line));
16741                 r.close();
16742         } catch(ex) {
16743                 return null;
16744         }
16745         return content.join("\n");
16746 }
16747
16748 //--
16749 //-- Server adaptor for talking to static files
16750 //--
16751
16752 function FileAdaptor()
16753 {
16754         this.host = null;
16755         this.store = null;
16756         return this;
16757 }
16758
16759 FileAdaptor.NotLoadedError = "TiddlyWiki file has not been loaded";
16760 FileAdaptor.serverType = 'file';
16761
16762 // Open the specified host/server
16763 FileAdaptor.prototype.openHost = function(host,context,userParams,callback)
16764 {
16765         this.host = host;
16766         if(!context)
16767                 context = {};
16768         context.adaptor = this;
16769         context.callback = callback;
16770         context.userParams = userParams;
16771         var ret = loadRemoteFile(host,FileAdaptor.openHostCallback,context);
16772         return typeof(ret) == "string" ? ret : true;
16773 };
16774
16775 FileAdaptor.openHostCallback = function(status,context,responseText,url,xhr)
16776 {
16777         var adaptor = context.adaptor;
16778         context.status = status;
16779         if(!status) {
16780                 context.statusText = "Error reading file: " + xhr.statusText;
16781         } else {
16782                 // Load the content into a TiddlyWiki() object
16783                 adaptor.store = new TiddlyWiki();
16784                 if(!adaptor.store.importTiddlyWiki(responseText))
16785                         context.statusText = config.messages.invalidFileError.format([url]);
16786         }
16787         context.callback(context,context.userParams);
16788 };
16789
16790 // Gets the list of workspaces on a given server
16791 FileAdaptor.prototype.getWorkspaceList = function(context,userParams,callback)
16792 {
16793         if(!context)
16794                 context = {};
16795         context.workspaces = [{title:"(default)"}];
16796         context.status = true;
16797         window.setTimeout(function() {callback(context,userParams);},10);
16798         return true;
16799 };
16800
16801 // Open the specified workspace
16802 FileAdaptor.prototype.openWorkspace = function(workspace,context,userParams,callback)
16803 {
16804         if(!context)
16805                 context = {};
16806         context.status = true;
16807         window.setTimeout(function() {callback(context,userParams);},10);
16808         return true;
16809 };
16810
16811 // Gets the list of tiddlers within a given workspace
16812 FileAdaptor.prototype.getTiddlerList = function(context,userParams,callback)
16813 {
16814         if(!this.store)
16815                 return FileAdaptor.NotLoadedError;
16816         if(!context)
16817                 context = {};
16818         context.tiddlers = [];
16819         this.store.forEachTiddler(function(title,tiddler)
16820                 {
16821                 var t = new Tiddler(title);
16822                 t.text = tiddler.text;
16823                 t.modified = tiddler.modified;
16824                 t.modifier = tiddler.modifier;
16825                 t.fields['server.page.revision'] = tiddler.modified.convertToYYYYMMDDHHMM();
16826                 t.tags = tiddler.tags;
16827                 context.tiddlers.push(t);
16828                 });
16829         context.status = true;
16830         window.setTimeout(function() {callback(context,userParams);},10);
16831         return true;
16832 };
16833
16834 FileAdaptor.prototype.generateTiddlerInfo = function(tiddler)
16835 {
16836         var info = {};
16837         info.uri = tiddler.fields['server.host'] + "#" + tiddler.title;
16838         return info;
16839 };
16840
16841 // Retrieves a tiddler from a given workspace on a given server
16842 FileAdaptor.prototype.getTiddler = function(title,context,userParams,callback)
16843 {
16844         if(!this.store)
16845                 return FileAdaptor.NotLoadedError;
16846         if(!context)
16847                 context = {};
16848         context.tiddler = this.store.fetchTiddler(title);
16849         if(context.tiddler) {
16850                 context.tiddler.fields['server.type'] = FileAdaptor.serverType;
16851                 context.tiddler.fields['server.host'] = this.host;
16852                 context.tiddler.fields['server.page.revision'] = context.tiddler.modified.convertToYYYYMMDDHHMM();
16853         }
16854         context.status = true;
16855         if(context.allowSynchronous) {
16856                 context.isSynchronous = true;
16857                 callback(context,userParams);
16858         } else {
16859                 window.setTimeout(function() {callback(context,userParams);},10);
16860         }
16861         return true;
16862 };
16863
16864 FileAdaptor.prototype.close = function()
16865 {
16866         delete this.store;
16867         this.store = null;
16868 };
16869
16870 config.adaptors[FileAdaptor.serverType] = FileAdaptor;
16871
16872 //--
16873 //-- Remote HTTP requests
16874 //--
16875
16876 function loadRemoteFile(url,callback,params)
16877 {
16878         return doHttp("GET",url,null,null,null,null,callback,params,null);
16879 }
16880
16881 // HTTP status codes
16882 var httpStatus = {
16883         OK: 200,
16884         ContentCreated: 201,
16885         NoContent: 204,
16886         Unauthorized: 401,
16887         Forbidden: 403,
16888         NotFound: 404,
16889         MethodNotAllowed: 405
16890 };
16891
16892 function doHttp(type,url,data,contentType,username,password,callback,params,headers)
16893 {
16894         // Get an xhr object
16895         var x = getXMLHttpRequest();
16896         if(!x)
16897                 return "Can't create XMLHttpRequest object";
16898         // Install callback
16899         x.onreadystatechange = function() {
16900                 if (x.readyState == 4 && callback && (x.status !== undefined)) {
16901                         if([0, httpStatus.OK, httpStatus.ContentCreated, httpStatus.NoContent].contains(x.status))
16902                                 callback(true,params,x.responseText,url,x);
16903                         else
16904                                 callback(false,params,null,url,x);
16905                         x.onreadystatechange = function(){};
16906                         x = null;
16907                 }
16908         };
16909         // Send request
16910         if(window.Components && window.netscape && window.netscape.security && document.location.protocol.indexOf("http") == -1)
16911                 window.netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
16912         try {
16913                 url = url + (url.indexOf("?") < 0 ? "?" : "&") + "nocache=" + Math.random();
16914                 x.open(type,url,true,username,password);
16915                 if (data)
16916                         x.setRequestHeader("Content-Type", contentType ? contentType : "application/x-www-form-urlencoded");
16917                 if (x.overrideMimeType)
16918                         x.setRequestHeader("Connection", "close");
16919                 if(headers) {
16920                         for(n in headers)
16921                                 x.setRequestHeader(n,headers[n]);
16922                 }
16923                 x.setRequestHeader("X-Requested-With", "TiddlyWiki " + version.major + "." + version.minor + "." + version.revision + (version.beta ? " (beta " + version.beta + ")" : ""));
16924                 x.send(data);
16925         } catch (ex) {
16926                 return exceptionText(ex);
16927         }
16928         return x;
16929 }
16930
16931 function getXMLHttpRequest()
16932 {
16933         try {
16934                 var x = new XMLHttpRequest(); // Modern
16935         } catch(ex) {
16936                 try {
16937                         x = new ActiveXObject("Msxml2.XMLHTTP"); // IE 6
16938                 } catch (ex2) {
16939                         return null;
16940                 }
16941         }
16942         return x;
16943 }
16944
16945 //--
16946 //-- TiddlyWiki-specific utility functions
16947 //--
16948
16949 function createTiddlyButton(theParent,theText,theTooltip,theAction,theClass,theId,theAccessKey)
16950 {
16951         var theButton = document.createElement("a");
16952         if(theAction) {
16953                 theButton.onclick = theAction;
16954                 theButton.setAttribute("href","javascript:;");
16955         }
16956         if(theTooltip)
16957                 theButton.setAttribute("title",theTooltip);
16958         if(theText)
16959                 theButton.appendChild(document.createTextNode(theText));
16960         if(theClass)
16961                 theButton.className = theClass;
16962         else
16963                 theButton.className = "button";
16964         if(theId)
16965                 theButton.id = theId;
16966         if(theParent)
16967                 theParent.appendChild(theButton);
16968         if(theAccessKey)
16969                 theButton.setAttribute("accessKey",theAccessKey);
16970         return theButton;
16971 }
16972
16973 function createTiddlyLink(place,title,includeText,theClass,isStatic,linkedFromTiddler,noToggle)
16974 {
16975         var text = includeText ? title : null;
16976         var i = getTiddlyLinkInfo(title,theClass);
16977         var btn = isStatic ? createExternalLink(place,store.getTiddlerText("SiteUrl",null) + "#" + title) : createTiddlyButton(place,text,i.subTitle,onClickTiddlerLink,i.classes);
16978         btn.setAttribute("refresh","link");
16979         btn.setAttribute("tiddlyLink",title);
16980         if(noToggle)
16981                 btn.setAttribute("noToggle","true");
16982         if(linkedFromTiddler) {
16983                 var fields = linkedFromTiddler.getInheritedFields();
16984                 if(fields)
16985                         btn.setAttribute("tiddlyFields",fields);
16986         }
16987         return btn;
16988 }
16989
16990 function refreshTiddlyLink(e,title)
16991 {
16992         var i = getTiddlyLinkInfo(title,e.className);
16993         e.className = i.classes;
16994         e.title = i.subTitle;
16995 }
16996
16997 function getTiddlyLinkInfo(title,currClasses)
16998 {
16999         var classes = currClasses ? currClasses.split(" ") : [];
17000         classes.pushUnique("tiddlyLink");
17001         var tiddler = store.fetchTiddler(title);
17002         var subTitle;
17003         if(tiddler) {
17004                 subTitle = tiddler.getSubtitle();
17005                 classes.pushUnique("tiddlyLinkExisting");
17006                 classes.remove("tiddlyLinkNonExisting");
17007                 classes.remove("shadow");
17008         } else {
17009                 classes.remove("tiddlyLinkExisting");
17010                 classes.pushUnique("tiddlyLinkNonExisting");
17011                 if(store.isShadowTiddler(title)) {
17012                         subTitle = config.messages.shadowedTiddlerToolTip.format([title]);
17013                         classes.pushUnique("shadow");
17014                 } else {
17015                         subTitle = config.messages.undefinedTiddlerToolTip.format([title]);
17016                         classes.remove("shadow");
17017                 }
17018         }
17019         if(config.annotations[title])
17020                 subTitle = config.annotations[title];
17021         return {classes: classes.join(" "),subTitle: subTitle};
17022 }
17023
17024 function createExternalLink(place,url)
17025 {
17026         var theLink = document.createElement("a");
17027         theLink.className = "externalLink";
17028         theLink.href = url;
17029         theLink.title = config.messages.externalLinkTooltip.format([url]);
17030         if(config.options.chkOpenInNewWindow)
17031                 theLink.target = "_blank";
17032         place.appendChild(theLink);
17033         return theLink;
17034 }
17035
17036 // Event handler for clicking on a tiddly link
17037 function onClickTiddlerLink(e)
17038 {
17039         if(!e) e = window.event;
17040         var theTarget = resolveTarget(e);
17041         var theLink = theTarget;
17042         var title = null;
17043         var fields = null;
17044         var noToggle = null;
17045         do {
17046                 title = theLink.getAttribute("tiddlyLink");
17047                 fields = theLink.getAttribute("tiddlyFields");
17048                 noToggle = theLink.getAttribute("noToggle");
17049                 theLink = theLink.parentNode;
17050         } while(title == null && theLink != null);
17051         if(!fields && !store.isShadowTiddler(title))
17052                 fields = String.encodeHashMap(config.defaultCustomFields);
17053         if(title) {
17054                 var toggling = e.metaKey || e.ctrlKey;
17055                 if(config.options.chkToggleLinks)
17056                         toggling = !toggling;
17057                 if(noToggle)
17058                         toggling = false;
17059                 story.displayTiddler(theTarget,title,null,true,null,fields,toggling);
17060         }
17061         clearMessage();
17062         return false;
17063 }
17064
17065 // Create a button for a tag with a popup listing all the tiddlers that it tags
17066 function createTagButton(place,tag,excludeTiddler)
17067 {
17068         var theTag = createTiddlyButton(place,tag,config.views.wikified.tag.tooltip.format([tag]),onClickTag);
17069         theTag.setAttribute("tag",tag);
17070         if(excludeTiddler)
17071                 theTag.setAttribute("tiddler",excludeTiddler);
17072         return theTag;
17073 }
17074
17075 // Event handler for clicking on a tiddler tag
17076 function onClickTag(e)
17077 {
17078         if(!e) var e = window.event;
17079         var theTarget = resolveTarget(e);
17080         var popup = Popup.create(this);
17081         var tag = this.getAttribute("tag");
17082         var title = this.getAttribute("tiddler");
17083         if(popup && tag) {
17084                 var tagged = store.getTaggedTiddlers(tag);
17085                 var titles = [];
17086                 var li,r;
17087                 for(r=0;r<tagged.length;r++) {
17088                         if(tagged[r].title != title)
17089                                 titles.push(tagged[r].title);
17090                 }
17091                 var lingo = config.views.wikified.tag;
17092                 if(titles.length > 0) {
17093                         var openAll = createTiddlyButton(createTiddlyElement(popup,"li"),lingo.openAllText.format([tag]),lingo.openAllTooltip,onClickTagOpenAll);
17094                         openAll.setAttribute("tag",tag);
17095                         createTiddlyElement(createTiddlyElement(popup,"li",null,"listBreak"),"div");
17096                         for(r=0; r<titles.length; r++) {
17097                                 createTiddlyLink(createTiddlyElement(popup,"li"),titles[r],true);
17098                         }
17099                 } else {
17100                         createTiddlyText(createTiddlyElement(popup,"li",null,"disabled"),lingo.popupNone.format([tag]));
17101                 }
17102                 createTiddlyElement(createTiddlyElement(popup,"li",null,"listBreak"),"div");
17103                 var h = createTiddlyLink(createTiddlyElement(popup,"li"),tag,false);
17104                 createTiddlyText(h,lingo.openTag.format([tag]));
17105         }
17106         Popup.show();
17107         e.cancelBubble = true;
17108         if(e.stopPropagation) e.stopPropagation();
17109         return false;
17110 }
17111
17112 // Event handler for 'open all' on a tiddler popup
17113 function onClickTagOpenAll(e)
17114 {
17115         if(!e) var e = window.event;
17116         var tag = this.getAttribute("tag");
17117         var tagged = store.getTaggedTiddlers(tag);
17118         var titles = [];
17119         for(var t=0; t<tagged.length; t++)
17120                 titles.push(tagged[t].title);
17121         story.displayTiddlers(this,titles);
17122         return false;
17123 }
17124
17125 function onClickError(e)
17126 {
17127         if(!e) var e = window.event;
17128         var popup = Popup.create(this);
17129         var lines = this.getAttribute("errorText").split("\n");
17130         for(var t=0; t<lines.length; t++)
17131                 createTiddlyElement(popup,"li",null,null,lines[t]);
17132         Popup.show();
17133         e.cancelBubble = true;
17134         if(e.stopPropagation) e.stopPropagation();
17135         return false;
17136 }
17137
17138 function createTiddlyDropDown(place,onchange,options,defaultValue)
17139 {
17140         var sel = createTiddlyElement(place,"select");
17141         sel.onchange = onchange;
17142         for(var t=0; t<options.length; t++) {
17143                 var e = createTiddlyElement(sel,"option",null,null,options[t].caption);
17144                 e.value = options[t].name;
17145                 if(options[t].name == defaultValue)
17146                         e.selected = true;
17147         }
17148         return sel;
17149 }
17150
17151 function createTiddlyPopup(place,caption,tooltip,tiddler)
17152 {
17153         if(tiddler.text) {
17154                 createTiddlyLink(place,caption,true);
17155                 var btn = createTiddlyButton(place,glyph("downArrow"),tooltip,onClickTiddlyPopup,"tiddlerPopupButton");
17156                 btn.tiddler = tiddler;
17157         } else {
17158                 createTiddlyText(place,caption);
17159         }
17160 }
17161
17162 function onClickTiddlyPopup(e)
17163 {
17164         if(!e) var e = window.event;
17165         var tiddler = this.tiddler;
17166         if(tiddler.text) {
17167                 var popup = Popup.create(this,"div","popupTiddler");
17168                 wikify(tiddler.text,popup,null,tiddler);
17169                 Popup.show();
17170         }
17171         if(e) e.cancelBubble = true;
17172         if(e && e.stopPropagation) e.stopPropagation();
17173         return false;
17174 }
17175
17176 function createTiddlyError(place,title,text)
17177 {
17178         var btn = createTiddlyButton(place,title,null,onClickError,"errorButton");
17179         if(text) btn.setAttribute("errorText",text);
17180 }
17181
17182 function merge(dst,src,preserveExisting)
17183 {
17184         for(p in src) {
17185                 if(!preserveExisting || dst[p] === undefined)
17186                         dst[p] = src[p];
17187         }
17188         return dst;
17189 }
17190
17191 // Returns a string containing the description of an exception, optionally prepended by a message
17192 function exceptionText(e,message)
17193 {
17194         var s = e.description ? e.description : e.toString();
17195         return message ? "%0:\n%1".format([message,s]) : s;
17196 }
17197
17198 // Displays an alert of an exception description with optional message
17199 function showException(e,message)
17200 {
17201         alert(exceptionText(e,message));
17202 }
17203
17204 function alertAndThrow(m)
17205 {
17206         alert(m);
17207         throw(m);
17208 }
17209
17210 function glyph(name)
17211 {
17212         var g = config.glyphs;
17213         var b = g.currBrowser;
17214         if(b == null) {
17215                 b = 0;
17216                 while(!g.browsers[b]() && b < g.browsers.length-1)
17217                         b++;
17218                 g.currBrowser = b;
17219         }
17220         if(!g.codes[name])
17221                 return "";
17222         return g.codes[name][b];
17223 }
17224 //-
17225 //- Animation engine
17226 //-
17227
17228 function Animator()
17229 {
17230         this.running = 0; // Incremented at start of each animation, decremented afterwards. If zero, the interval timer is disabled
17231         this.timerID = 0; // ID of the timer used for animating
17232         this.animations = []; // List of animations in progress
17233         return this;
17234 }
17235
17236 // Start animation engine
17237 Animator.prototype.startAnimating = function() // Variable number of arguments
17238 {
17239         for(var t=0; t<arguments.length; t++)
17240                 this.animations.push(arguments[t]);
17241         if(this.running == 0) {
17242                 var me = this;
17243                 this.timerID = window.setInterval(function() {me.doAnimate(me);},10);
17244         }
17245         this.running += arguments.length;
17246 };
17247
17248 // Perform an animation engine tick, calling each of the known animation modules
17249 Animator.prototype.doAnimate = function(me)
17250 {
17251         var a = 0;
17252         while(a < me.animations.length) {
17253                 var animation = me.animations[a];
17254                 if(animation.tick()) {
17255                         a++;
17256                 } else {
17257                         me.animations.splice(a,1);
17258                         if(--me.running == 0)
17259                                 window.clearInterval(me.timerID);
17260                 }
17261         }
17262 };
17263
17264 // Map a 0..1 value to 0..1, but slow down at the start and end
17265 Animator.slowInSlowOut = function(progress)
17266 {
17267         return(1-((Math.cos(progress * Math.PI)+1)/2));
17268 };
17269
17270 //--
17271 //-- Morpher animation
17272 //--
17273
17274 // Animate a set of properties of an element
17275 function Morpher(element,duration,properties,callback)
17276 {
17277         this.element = element;
17278         this.duration = duration;
17279         this.properties = properties;
17280         this.startTime = new Date();
17281         this.endTime = Number(this.startTime) + duration;
17282         this.callback = callback;
17283         this.tick();
17284         return this;
17285 }
17286
17287 Morpher.prototype.assignStyle = function(element,style,value)
17288 {
17289         switch(style) {
17290                 case "-tw-vertScroll":
17291                         window.scrollTo(findScrollX(),value);
17292                         break;
17293                 case "-tw-horizScroll":
17294                         window.scrollTo(value,findScrollY());
17295                         break;
17296                 default:
17297                         element.style[style] = value;
17298                         break;
17299         }
17300 };
17301
17302 Morpher.prototype.stop = function()
17303 {
17304         for(var t=0; t<this.properties.length; t++) {
17305                 var p = this.properties[t];
17306                 if(p.atEnd !== undefined) {
17307                         this.assignStyle(this.element,p.style,p.atEnd);
17308                 }
17309         }
17310         if(this.callback)
17311                 this.callback(this.element,this.properties);
17312 };
17313
17314 Morpher.prototype.tick = function()
17315 {
17316         var currTime = Number(new Date());
17317         progress = Animator.slowInSlowOut(Math.min(1,(currTime-this.startTime)/this.duration));
17318         for(var t=0; t<this.properties.length; t++) {
17319                 var p = this.properties[t];
17320                 if(p.start !== undefined && p.end !== undefined) {
17321                         var template = p.template ? p.template : "%0";
17322                         switch(p.format) {
17323                                 case undefined:
17324                                 case "style":
17325                                         var v = p.start + (p.end-p.start) * progress;
17326                                         this.assignStyle(this.element,p.style,template.format([v]));
17327                                         break;
17328                                 case "color":
17329                                         break;
17330                         }
17331                 }
17332         }
17333         if(currTime >= this.endTime) {
17334                 this.stop();
17335                 return false;
17336         }
17337         return true;
17338 };
17339
17340 //--
17341 //-- Zoomer animation
17342 //--
17343
17344 function Zoomer(text,startElement,targetElement,unused)
17345 {
17346         var e = createTiddlyElement(document.body,"div",null,"zoomer");
17347         createTiddlyElement(e,"div",null,null,text);
17348         var winWidth = findWindowWidth();
17349         var winHeight = findWindowHeight();
17350         var p = [
17351                 {style: 'left', start: findPosX(startElement), end: findPosX(targetElement), template: '%0px'},
17352                 {style: 'top', start: findPosY(startElement), end: findPosY(targetElement), template: '%0px'},
17353                 {style: 'width', start: Math.min(startElement.scrollWidth,winWidth), end: Math.min(targetElement.scrollWidth,winWidth), template: '%0px', atEnd: 'auto'},
17354                 {style: 'height', start: Math.min(startElement.scrollHeight,winHeight), end: Math.min(targetElement.scrollHeight,winHeight), template: '%0px', atEnd: 'auto'},
17355                 {style: 'fontSize', start: 8, end: 24, template: '%0pt'}
17356         ];
17357         var c = function(element,properties) {removeNode(element);};
17358         return new Morpher(e,config.animDuration,p,c);
17359 }
17360
17361 //--
17362 //-- Scroller animation
17363 //--
17364
17365 function Scroller(targetElement,unused)
17366 {
17367         var p = [
17368                 {style: '-tw-vertScroll', start: findScrollY(), end: ensureVisible(targetElement)}
17369         ];
17370         return new Morpher(targetElement,config.animDuration,p);
17371 }
17372
17373 //--
17374 //-- Slider animation
17375 //--
17376
17377 // deleteMode - "none", "all" [delete target element and it's children], [only] "children" [but not the target element]
17378 function Slider(element,opening,unused,deleteMode)
17379 {
17380         element.style.overflow = 'hidden';
17381         if(opening)
17382                 element.style.height = '0px'; // Resolves a Firefox flashing bug
17383         element.style.display = 'block';
17384         var left = findPosX(element);
17385         var width = element.scrollWidth;
17386         var height = element.scrollHeight;
17387         var winWidth = findWindowWidth();
17388         var p = [];
17389         var c = null;
17390         if(opening) {
17391                 p.push({style: 'height', start: 0, end: height, template: '%0px', atEnd: 'auto'});
17392                 p.push({style: 'opacity', start: 0, end: 1, template: '%0'});
17393                 p.push({style: 'filter', start: 0, end: 100, template: 'alpha(opacity:%0)'});
17394         } else {
17395                 p.push({style: 'height', start: height, end: 0, template: '%0px'});
17396                 p.push({style: 'display', atEnd: 'none'});
17397                 p.push({style: 'opacity', start: 1, end: 0, template: '%0'});
17398                 p.push({style: 'filter', start: 100, end: 0, template: 'alpha(opacity:%0)'});
17399                 switch(deleteMode) {
17400                         case "all":
17401                                 c = function(element,properties) {removeNode(element);};
17402                                 break;
17403                         case "children":
17404                                 c = function(element,properties) {removeChildren(element);};
17405                                 break;
17406                 }
17407         }
17408         return new Morpher(element,config.animDuration,p,c);
17409 }
17410
17411 //--
17412 //-- Popup menu
17413 //--
17414
17415 var Popup = {
17416         stack: [] // Array of objects with members root: and popup:
17417         };
17418
17419 Popup.create = function(root,elem,theClass)
17420 {
17421         Popup.remove();
17422         var popup = createTiddlyElement(document.body,elem ? elem : "ol","popup",theClass ? theClass : "popup");
17423         Popup.stack.push({root: root, popup: popup});
17424         return popup;
17425 };
17426
17427 Popup.onDocumentClick = function(e)
17428 {
17429         if (!e) var e = window.event;
17430         var target = resolveTarget(e);
17431         if(e.eventPhase == undefined)
17432                 Popup.remove();
17433         else if(e.eventPhase == Event.BUBBLING_PHASE || e.eventPhase == Event.AT_TARGET)
17434                 Popup.remove();
17435         return true;
17436 };
17437
17438 Popup.show = function(unused1,unused2)
17439 {
17440         var curr = Popup.stack[Popup.stack.length-1];
17441         this.place(curr.root,curr.popup);
17442         addClass(curr.root,"highlight");
17443         if(config.options.chkAnimate && anim && typeof Scroller == "function")
17444                 anim.startAnimating(new Scroller(curr.popup));
17445         else
17446                 window.scrollTo(0,ensureVisible(curr.popup));
17447 };
17448
17449 Popup.place = function(root,popup,offset)
17450 {
17451         if(!offset) var offset = {x:0, y:0};
17452         var rootLeft = findPosX(root);
17453         var rootTop = findPosY(root);
17454         var rootHeight = root.offsetHeight;
17455         var popupLeft = rootLeft + offset.x;
17456         var popupTop = rootTop + rootHeight + offset.y;
17457         var winWidth = findWindowWidth();
17458         if(popup.offsetWidth > winWidth*0.75)
17459                 popup.style.width = winWidth*0.75 + "px";
17460         var popupWidth = popup.offsetWidth;
17461         if(popupLeft + popupWidth > winWidth)
17462                 popupLeft = winWidth - popupWidth;
17463         popup.style.left = popupLeft + "px";
17464         popup.style.top = popupTop + "px";
17465         popup.style.display = "block";
17466 }
17467
17468 Popup.remove = function()
17469 {
17470         if(Popup.stack.length > 0) {
17471                 Popup.removeFrom(0);
17472         }
17473 };
17474
17475 Popup.removeFrom = function(from)
17476 {
17477         for(var t=Popup.stack.length-1; t>=from; t--) {
17478                 var p = Popup.stack[t];
17479                 removeClass(p.root,"highlight");
17480                 removeNode(p.popup);
17481         }
17482         Popup.stack = Popup.stack.slice(0,from);
17483 };
17484
17485 //--
17486 //-- Wizard support
17487 //--
17488
17489 function Wizard(elem)
17490 {
17491         if(elem) {
17492                 this.formElem = findRelated(elem,"wizard","className");
17493                 this.bodyElem = findRelated(this.formElem.firstChild,"wizardBody","className","nextSibling");
17494                 this.footElem = findRelated(this.formElem.firstChild,"wizardFooter","className","nextSibling");
17495         } else {
17496                 this.formElem = null;
17497                 this.bodyElem = null;
17498                 this.footElem = null;
17499         }
17500 }
17501
17502 Wizard.prototype.setValue = function(name,value)
17503 {
17504         if(this.formElem)
17505                 this.formElem[name] = value;
17506 };
17507
17508 Wizard.prototype.getValue = function(name)
17509 {
17510         return this.formElem ? this.formElem[name] : null;
17511 };
17512
17513 Wizard.prototype.createWizard = function(place,title)
17514 {
17515         this.formElem = createTiddlyElement(place,"form",null,"wizard");
17516         createTiddlyElement(this.formElem,"h1",null,null,title);
17517         this.bodyElem = createTiddlyElement(this.formElem,"div",null,"wizardBody");
17518         this.footElem = createTiddlyElement(this.formElem,"div",null,"wizardFooter");
17519 };
17520
17521 Wizard.prototype.clear = function()
17522 {
17523         removeChildren(this.bodyElem);
17524 };
17525
17526 Wizard.prototype.setButtons = function(buttonInfo,status)
17527 {
17528         removeChildren(this.footElem);
17529         for(var t=0; t<buttonInfo.length; t++) {
17530                 createTiddlyButton(this.footElem,buttonInfo[t].caption,buttonInfo[t].tooltip,buttonInfo[t].onClick);
17531                 insertSpacer(this.footElem);
17532                 }
17533         if(typeof status == "string") {
17534                 createTiddlyElement(this.footElem,"span",null,"status",status);
17535         }
17536 };
17537
17538 Wizard.prototype.addStep = function(stepTitle,html)
17539 {
17540         removeChildren(this.bodyElem);
17541         var w = createTiddlyElement(this.bodyElem,"div");
17542         createTiddlyElement(w,"h2",null,null,stepTitle);
17543         var step = createTiddlyElement(w,"div",null,"wizardStep");
17544         step.innerHTML = html;
17545         applyHtmlMacros(step,tiddler);
17546 };
17547
17548 Wizard.prototype.getElement = function(name)
17549 {
17550         return this.formElem.elements[name];
17551 };
17552
17553 //--
17554 //-- ListView gadget
17555 //--
17556
17557 var ListView = {};
17558
17559 // Create a listview
17560 ListView.create = function(place,listObject,listTemplate,callback,className)
17561 {
17562         var table = createTiddlyElement(place,"table",null,className ? className : "listView twtable");
17563         var thead = createTiddlyElement(table,"thead");
17564         var r = createTiddlyElement(thead,"tr");
17565         for(var t=0; t<listTemplate.columns.length; t++) {
17566                 var columnTemplate = listTemplate.columns[t];
17567                 var c = createTiddlyElement(r,"th");
17568                 var colType = ListView.columnTypes[columnTemplate.type];
17569                 if(colType && colType.createHeader)
17570                         colType.createHeader(c,columnTemplate,t);
17571         }
17572         var tbody = createTiddlyElement(table,"tbody");
17573         for(var rc=0; rc<listObject.length; rc++) {
17574                 rowObject = listObject[rc];
17575                 r = createTiddlyElement(tbody,"tr");
17576                 for(c=0; c<listTemplate.rowClasses.length; c++) {
17577                         if(rowObject[listTemplate.rowClasses[c].field])
17578                                 addClass(r,listTemplate.rowClasses[c].className);
17579                 }
17580                 rowObject.rowElement = r;
17581                 rowObject.colElements = {};
17582                 for(var cc=0; cc<listTemplate.columns.length; cc++) {
17583                         c = createTiddlyElement(r,"td");
17584                         columnTemplate = listTemplate.columns[cc];
17585                         var field = columnTemplate.field;
17586                         colType = ListView.columnTypes[columnTemplate.type];
17587                         if(colType && colType.createItem)
17588                                 colType.createItem(c,rowObject,field,columnTemplate,cc,rc);
17589                         rowObject.colElements[field] = c;
17590                 }
17591         }
17592         if(callback && listTemplate.actions)
17593                 createTiddlyDropDown(place,ListView.getCommandHandler(callback),listTemplate.actions);
17594         if(callback && listTemplate.buttons) {
17595                 for(t=0; t<listTemplate.buttons.length; t++) {
17596                         var a = listTemplate.buttons[t];
17597                         if(a && a.name != "")
17598                                 createTiddlyButton(place,a.caption,null,ListView.getCommandHandler(callback,a.name,a.allowEmptySelection));
17599                 }
17600         }
17601         return table;
17602 };
17603
17604 ListView.getCommandHandler = function(callback,name,allowEmptySelection)
17605 {
17606         return function(e) {
17607                 var view = findRelated(this,"TABLE",null,"previousSibling");
17608                 var tiddlers = [];
17609                 ListView.forEachSelector(view,function(e,rowName) {
17610                                         if(e.checked)
17611                                                 tiddlers.push(rowName);
17612                                         });
17613                 if(tiddlers.length == 0 && !allowEmptySelection) {
17614                         alert(config.messages.nothingSelected);
17615                 } else {
17616                         if(this.nodeName.toLowerCase() == "select") {
17617                                 callback(view,this.value,tiddlers);
17618                                 this.selectedIndex = 0;
17619                         } else {
17620                                 callback(view,name,tiddlers);
17621                         }
17622                 }
17623         };
17624 };
17625
17626 // Invoke a callback for each selector checkbox in the listview
17627 ListView.forEachSelector = function(view,callback)
17628 {
17629         var checkboxes = view.getElementsByTagName("input");
17630         var hadOne = false;
17631         for(var t=0; t<checkboxes.length; t++) {
17632                 var cb = checkboxes[t];
17633                 if(cb.getAttribute("type") == "checkbox") {
17634                         var rn = cb.getAttribute("rowName");
17635                         if(rn) {
17636                                 callback(cb,rn);
17637                                 hadOne = true;
17638                         }
17639                 }
17640         }
17641         return hadOne;
17642 };
17643
17644 ListView.getSelectedRows = function(view)
17645 {
17646         var rowNames = [];
17647         ListView.forEachSelector(view,function(e,rowName) {
17648                                 if(e.checked)
17649                                         rowNames.push(rowName);
17650                                 });
17651         return rowNames;
17652 };
17653
17654 ListView.columnTypes = {};
17655
17656 ListView.columnTypes.String = {
17657         createHeader: function(place,columnTemplate,col)
17658                 {
17659                         createTiddlyText(place,columnTemplate.title);
17660                 },
17661         createItem: function(place,listObject,field,columnTemplate,col,row)
17662                 {
17663                         var v = listObject[field];
17664                         if(v != undefined)
17665                                 createTiddlyText(place,v);
17666                 }
17667 };
17668
17669 ListView.columnTypes.WikiText = {
17670         createHeader: ListView.columnTypes.String.createHeader,
17671         createItem: function(place,listObject,field,columnTemplate,col,row)
17672                 {
17673                         var v = listObject[field];
17674                         if(v != undefined)
17675                                 wikify(v,place,null,null);
17676                 }
17677 };
17678
17679 ListView.columnTypes.Tiddler = {
17680         createHeader: ListView.columnTypes.String.createHeader,
17681         createItem: function(place,listObject,field,columnTemplate,col,row)
17682                 {
17683                         var v = listObject[field];
17684                         if(v != undefined && v.title)
17685                                 createTiddlyPopup(place,v.title,config.messages.listView.tiddlerTooltip,v);
17686                 }
17687 };
17688
17689 ListView.columnTypes.Size = {
17690         createHeader: ListView.columnTypes.String.createHeader,
17691         createItem: function(place,listObject,field,columnTemplate,col,row)
17692                 {
17693                         var v = listObject[field];
17694                         if(v != undefined) {
17695                                 var t = 0;
17696                                 while(t<config.messages.sizeTemplates.length-1 && v<config.messages.sizeTemplates[t].unit)
17697                                         t++;
17698                                 createTiddlyText(place,config.messages.sizeTemplates[t].template.format([Math.round(v/config.messages.sizeTemplates[t].unit)]));
17699                         }
17700                 }
17701 };
17702
17703 ListView.columnTypes.Link = {
17704         createHeader: ListView.columnTypes.String.createHeader,
17705         createItem: function(place,listObject,field,columnTemplate,col,row)
17706                 {
17707                         var v = listObject[field];
17708                         var c = columnTemplate.text;
17709                         if(v != undefined)
17710                                 createTiddlyText(createExternalLink(place,v),c ? c : v);
17711                 }
17712 };
17713
17714 ListView.columnTypes.Date = {
17715         createHeader: ListView.columnTypes.String.createHeader,
17716         createItem: function(place,listObject,field,columnTemplate,col,row)
17717                 {
17718                         var v = listObject[field];
17719                         if(v != undefined)
17720                                 createTiddlyText(place,v.formatString(columnTemplate.dateFormat));
17721                 }
17722 };
17723
17724 ListView.columnTypes.StringList = {
17725         createHeader: ListView.columnTypes.String.createHeader,
17726         createItem: function(place,listObject,field,columnTemplate,col,row)
17727                 {
17728                         var v = listObject[field];
17729                         if(v != undefined) {
17730                                 for(var t=0; t<v.length; t++) {
17731                                         createTiddlyText(place,v[t]);
17732                                         createTiddlyElement(place,"br");
17733                                 }
17734                         }
17735                 }
17736 };
17737
17738 ListView.columnTypes.Selector = {
17739         createHeader: function(place,columnTemplate,col)
17740                 {
17741                         createTiddlyCheckbox(place,null,false,this.onHeaderChange);
17742                 },
17743         createItem: function(place,listObject,field,columnTemplate,col,row)
17744                 {
17745                         var e = createTiddlyCheckbox(place,null,listObject[field],null);
17746                         e.setAttribute("rowName",listObject[columnTemplate.rowName]);
17747                 },
17748         onHeaderChange: function(e)
17749                 {
17750                         var state = this.checked;
17751                         var view = findRelated(this,"TABLE");
17752                         if(!view)
17753                                 return;
17754                         ListView.forEachSelector(view,function(e,rowName) {
17755                                                                 e.checked = state;
17756                                                         });
17757                 }
17758 };
17759
17760 ListView.columnTypes.Tags = {
17761         createHeader: ListView.columnTypes.String.createHeader,
17762         createItem: function(place,listObject,field,columnTemplate,col,row)
17763                 {
17764                         var tags = listObject[field];
17765                         createTiddlyText(place,String.encodeTiddlyLinkList(tags));
17766                 }
17767 };
17768
17769 ListView.columnTypes.Boolean = {
17770         createHeader: ListView.columnTypes.String.createHeader,
17771         createItem: function(place,listObject,field,columnTemplate,col,row)
17772                 {
17773                         if(listObject[field] == true)
17774                                 createTiddlyText(place,columnTemplate.trueText);
17775                         if(listObject[field] == false)
17776                                 createTiddlyText(place,columnTemplate.falseText);
17777                 }
17778 };
17779
17780 ListView.columnTypes.TagCheckbox = {
17781         createHeader: ListView.columnTypes.String.createHeader,
17782         createItem: function(place,listObject,field,columnTemplate,col,row)
17783                 {
17784                         var e = createTiddlyCheckbox(place,null,listObject[field],this.onChange);
17785                         e.setAttribute("tiddler",listObject.title);
17786                         e.setAttribute("tag",columnTemplate.tag);
17787                 },
17788         onChange : function(e)
17789                 {
17790                         var tag = this.getAttribute("tag");
17791                         var tiddler = this.getAttribute("tiddler");
17792                         store.setTiddlerTag(tiddler,this.checked,tag);
17793                 }
17794 };
17795
17796 ListView.columnTypes.TiddlerLink = {
17797         createHeader: ListView.columnTypes.String.createHeader,
17798         createItem: function(place,listObject,field,columnTemplate,col,row)
17799                 {
17800                         var v = listObject[field];
17801                         if(v != undefined) {
17802                                 var link = createTiddlyLink(place,listObject[columnTemplate.tiddlerLink],false,null);
17803                                 createTiddlyText(link,listObject[field]);
17804                         }
17805                 }
17806 };
17807
17808 //--
17809 //-- Augmented methods for the JavaScript Number(), Array(), String() and Date() objects
17810 //--
17811
17812 // Clamp a number to a range
17813 Number.prototype.clamp = function(min,max)
17814 {
17815         var c = this;
17816         if(c < min)
17817                 c = min;
17818         if(c > max)
17819                 c = max;
17820         return c;
17821 };
17822
17823 // Add indexOf function if browser does not support it
17824 if(!Array.indexOf) {
17825 Array.prototype.indexOf = function(item,from)
17826 {
17827         if(!from)
17828                 from = 0;
17829         for(var i=from; i<this.length; i++) {
17830                 if(this[i] === item)
17831                         return i;
17832         }
17833         return -1;
17834 };}
17835
17836 // Find an entry in a given field of the members of an array
17837 Array.prototype.findByField = function(field,value)
17838 {
17839         for(var t=0; t<this.length; t++) {
17840                 if(this[t][field] == value)
17841                         return t;
17842         }
17843         return null;
17844 };
17845
17846 // Return whether an entry exists in an array
17847 Array.prototype.contains = function(item)
17848 {
17849         return this.indexOf(item) != -1;
17850 };
17851
17852 // Adds, removes or toggles a particular value within an array
17853 //  value - value to add
17854 //  mode - +1 to add value, -1 to remove value, 0 to toggle it
17855 Array.prototype.setItem = function(value,mode)
17856 {
17857         var p = this.indexOf(value);
17858         if(mode == 0)
17859                 mode = (p == -1) ? +1 : -1;
17860         if(mode == +1) {
17861                 if(p == -1)
17862                         this.push(value);
17863         } else if(mode == -1) {
17864                 if(p != -1)
17865                         this.splice(p,1);
17866         }
17867 };
17868
17869 // Return whether one of a list of values exists in an array
17870 Array.prototype.containsAny = function(items)
17871 {
17872         for(var i=0; i<items.length; i++) {
17873                 if (this.indexOf(items[i]) != -1)
17874                         return true;
17875         }
17876         return false;
17877 };
17878
17879 // Return whether all of a list of values exists in an array
17880 Array.prototype.containsAll = function(items)
17881 {
17882         for (var i = 0; i<items.length; i++) {
17883                 if (this.indexOf(items[i]) == -1)
17884                         return false;
17885         }
17886         return true;
17887 };
17888
17889 // Push a new value into an array only if it is not already present in the array. If the optional unique parameter is false, it reverts to a normal push
17890 Array.prototype.pushUnique = function(item,unique)
17891 {
17892         if(unique === false) {
17893                 this.push(item);
17894         } else {
17895                 if(this.indexOf(item) == -1)
17896                         this.push(item);
17897         }
17898 };
17899
17900 Array.prototype.remove = function(item)
17901 {
17902         var p = this.indexOf(item);
17903         if(p != -1)
17904                 this.splice(p,1);
17905 };
17906
17907 // Get characters from the right end of a string
17908 String.prototype.right = function(n)
17909 {
17910         return n < this.length ? this.slice(this.length-n) : this;
17911 };
17912
17913 // Trim whitespace from both ends of a string
17914 String.prototype.trim = function()
17915 {
17916         return this.replace(/^\s*|\s*$/g,"");
17917 };
17918
17919 // Convert a string from a CSS style property name to a JavaScript style name ("background-color" -> "backgroundColor")
17920 String.prototype.unDash = function()
17921 {
17922         var s = this.split("-");
17923         if(s.length > 1) {
17924                 for(var t=1; t<s.length; t++)
17925                         s[t] = s[t].substr(0,1).toUpperCase() + s[t].substr(1);
17926         }
17927         return s.join("");
17928 };
17929
17930 // Substitute substrings from an array into a format string that includes '%1'-type specifiers
17931 String.prototype.format = function(substrings)
17932 {
17933         var subRegExp = /(?:%(\d+))/mg;
17934         var currPos = 0;
17935         var r = [];
17936         do {
17937                 var match = subRegExp.exec(this);
17938                 if(match && match[1]) {
17939                         if(match.index > currPos)
17940                                 r.push(this.substring(currPos,match.index));
17941                         r.push(substrings[parseInt(match[1])]);
17942                         currPos = subRegExp.lastIndex;
17943                 }
17944         } while(match);
17945         if(currPos < this.length)
17946                 r.push(this.substring(currPos,this.length));
17947         return r.join("");
17948 };
17949
17950 // Escape any special RegExp characters with that character preceded by a backslash
17951 String.prototype.escapeRegExp = function()
17952 {
17953         var s = "\\^$*+?()=!|,{}[].";
17954         var c = this;
17955         for(var t=0; t<s.length; t++)
17956                 c = c.replace(new RegExp("\\" + s.substr(t,1),"g"),"\\" + s.substr(t,1));
17957         return c;
17958 };
17959
17960 // Convert "\" to "\s", newlines to "\n" (and remove carriage returns)
17961 String.prototype.escapeLineBreaks = function()
17962 {
17963         return this.replace(/\\/mg,"\\s").replace(/\n/mg,"\\n").replace(/\r/mg,"");
17964 };
17965
17966 // Convert "\n" to newlines, "\b" to " ", "\s" to "\" (and remove carriage returns)
17967 String.prototype.unescapeLineBreaks = function()
17968 {
17969         return this.replace(/\\n/mg,"\n").replace(/\\b/mg," ").replace(/\\s/mg,"\\").replace(/\r/mg,"");
17970 };
17971
17972 // Convert & to "&amp;", < to "&lt;", > to "&gt;" and " to "&quot;"
17973 String.prototype.htmlEncode = function()
17974 {
17975         return this.replace(/&/mg,"&amp;").replace(/</mg,"&lt;").replace(/>/mg,"&gt;").replace(/\"/mg,"&quot;");
17976 };
17977
17978 // Convert "&amp;" to &, "&lt;" to <, "&gt;" to > and "&quot;" to "
17979 String.prototype.htmlDecode = function()
17980 {
17981         return this.replace(/&lt;/mg,"<").replace(/&gt;/mg,">").replace(/&quot;/mg,"\"").replace(/&amp;/mg,"&");
17982 };
17983
17984 // Convert a string to it's JSON representation by encoding control characters, double quotes and backslash. See json.org
17985 String.prototype.toJSONString = function()
17986 {
17987         var m = {
17988                 '\b': '\\b',
17989                 '\f': '\\f',
17990                 '\n': '\\n',
17991                 '\r': '\\r',
17992                 '\t': '\\t',
17993                 '"' : '\\"',
17994                 '\\': '\\\\'
17995                 };
17996         var replaceFn = function(a,b) {
17997                 var c = m[b];
17998                 if(c)
17999                         return c;
18000                 c = b.charCodeAt();
18001                 return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
18002                 };
18003         if(/["\\\x00-\x1f]/.test(this))
18004                 return '"' + this.replace(/([\x00-\x1f\\"])/g,replaceFn) + '"';
18005         return '"' + this + '"';
18006 };
18007
18008 // Parse a space-separated string of name:value parameters
18009 // The result is an array of objects:
18010 //   result[0] = object with a member for each parameter name, value of that member being an array of values
18011 //   result[1..n] = one object for each parameter, with 'name' and 'value' members
18012 String.prototype.parseParams = function(defaultName,defaultValue,allowEval,noNames,cascadeDefaults)
18013 {
18014         var parseToken = function(match,p) {
18015                 var n;
18016                 if(match[p]) // Double quoted
18017                         n = match[p];
18018                 else if(match[p+1]) // Single quoted
18019                         n = match[p+1];
18020                 else if(match[p+2]) // Double-square-bracket quoted
18021                         n = match[p+2];
18022                 else if(match[p+3]) // Double-brace quoted
18023                         try {
18024                                 n = match[p+3];
18025                                 if(allowEval)
18026                                         n = window.eval(n);
18027                         } catch(ex) {
18028                                 throw "Unable to evaluate {{" + match[p+3] + "}}: " + exceptionText(ex);
18029                         }
18030                 else if(match[p+4]) // Unquoted
18031                         n = match[p+4];
18032                 else if(match[p+5]) // empty quote
18033                         n = "";
18034                 return n;
18035         };
18036         var r = [{}];
18037         var dblQuote = "(?:\"((?:(?:\\\\\")|[^\"])+)\")";
18038         var sngQuote = "(?:'((?:(?:\\\\\')|[^'])+)')";
18039         var dblSquare = "(?:\\[\\[((?:\\s|\\S)*?)\\]\\])";
18040         var dblBrace = "(?:\\{\\{((?:\\s|\\S)*?)\\}\\})";
18041         var unQuoted = noNames ? "([^\"'\\s]\\S*)" : "([^\"':\\s][^\\s:]*)";
18042         var emptyQuote = "((?:\"\")|(?:''))";
18043         var skipSpace = "(?:\\s*)";
18044         var token = "(?:" + dblQuote + "|" + sngQuote + "|" + dblSquare + "|" + dblBrace + "|" + unQuoted + "|" + emptyQuote + ")";
18045         var re = noNames ? new RegExp(token,"mg") : new RegExp(skipSpace + token + skipSpace + "(?:(\\:)" + skipSpace + token + ")?","mg");
18046         var params = [];
18047         do {
18048                 var match = re.exec(this);
18049                 if(match) {
18050                         var n = parseToken(match,1);
18051                         if(noNames) {
18052                                 r.push({name:"",value:n});
18053                         } else {
18054                                 var v = parseToken(match,8);
18055                                 if(v == null && defaultName) {
18056                                         v = n;
18057                                         n = defaultName;
18058                                 } else if(v == null && defaultValue) {
18059                                         v = defaultValue;
18060                                 }
18061                                 r.push({name:n,value:v});
18062                                 if(cascadeDefaults) {
18063                                         defaultName = n;
18064                                         defaultValue = v;
18065                                 }
18066                         }
18067                 }
18068         } while(match);
18069         // Summarise parameters into first element
18070         for(var t=1; t<r.length; t++) {
18071                 if(r[0][r[t].name])
18072                         r[0][r[t].name].push(r[t].value);
18073                 else
18074                         r[0][r[t].name] = [r[t].value];
18075         }
18076         return r;
18077 };
18078
18079 // Process a string list of macro parameters into an array. Parameters can be quoted with "", '',
18080 // [[]], {{ }} or left unquoted (and therefore space-separated). Double-braces {{}} results in
18081 // an *evaluated* parameter: e.g. {{config.options.txtUserName}} results in the current user's name.
18082 String.prototype.readMacroParams = function()
18083 {
18084         var p = this.parseParams("list",null,true,true);
18085         var n = [];
18086         for(var t=1; t<p.length; t++)
18087                 n.push(p[t].value);
18088         return n;
18089 };
18090
18091 // Process a string list of unique tiddler names into an array. Tiddler names that have spaces in them must be [[bracketed]]
18092 String.prototype.readBracketedList = function(unique)
18093 {
18094         var p = this.parseParams("list",null,false,true);
18095         var n = [];
18096         for(var t=1; t<p.length; t++)
18097                 n.pushUnique(p[t].value,unique);
18098         return n;
18099 };
18100
18101 // Returns array with start and end index of chunk between given start and end marker, or undefined.
18102 String.prototype.getChunkRange = function(start,end) 
18103 {
18104         var s = this.indexOf(start);
18105         if(s != -1) {
18106                 s += start.length;
18107                 var e = this.indexOf(end,s);
18108                 if(e != -1)
18109                         return [s,e];
18110         }
18111 };
18112
18113 // Replace a chunk of a string given start and end markers
18114 String.prototype.replaceChunk = function(start,end,sub)
18115 {
18116         var r = this.getChunkRange(start,end);
18117         return r ? this.substring(0,r[0]) + sub + this.substring(r[1]) : this;
18118 };
18119
18120 // Returns a chunk of a string between start and end markers, or undefined
18121 String.prototype.getChunk = function(start,end)
18122 {
18123         var r = this.getChunkRange(start,end);
18124         if(r)
18125                 return this.substring(r[0],r[1]);
18126 };
18127
18128
18129 // Static method to bracket a string with double square brackets if it contains a space
18130 String.encodeTiddlyLink = function(title)
18131 {
18132         return title.indexOf(" ") == -1 ? title : "[[" + title + "]]";
18133 };
18134
18135 // Static method to encodeTiddlyLink for every item in an array and join them with spaces
18136 String.encodeTiddlyLinkList = function(list)
18137 {
18138         if(list) {
18139                 var results = [];
18140                 for(var t=0; t<list.length; t++)
18141                         results.push(String.encodeTiddlyLink(list[t]));
18142                 return results.join(" ");
18143         } else {
18144                 return "";
18145         }
18146 };
18147
18148 // Convert a string as a sequence of name:"value" pairs into a hashmap
18149 String.prototype.decodeHashMap = function()
18150 {
18151         var fields = this.parseParams("anon","",false);
18152         var r = {};
18153         for(var t=1; t<fields.length; t++)
18154                 r[fields[t].name] = fields[t].value;
18155         return r;
18156 };
18157
18158 // Static method to encode a hashmap into a name:"value"... string
18159 String.encodeHashMap = function(hashmap)
18160 {
18161         var r = [];
18162         for(var t in hashmap)
18163                 r.push(t + ':"' + hashmap[t] + '"');
18164         return r.join(" ");
18165 };
18166
18167 // Static method to left-pad a string with 0s to a certain width
18168 String.zeroPad = function(n,d)
18169 {
18170         var s = n.toString();
18171         if(s.length < d)
18172                 s = "000000000000000000000000000".substr(0,d-s.length) + s;
18173         return s;
18174 };
18175
18176 String.prototype.startsWith = function(prefix) 
18177 {
18178         return !prefix || this.substring(0,prefix.length) == prefix;
18179 };
18180
18181 // Returns the first value of the given named parameter.
18182 function getParam(params,name,defaultValue)
18183 {
18184         if(!params)
18185                 return defaultValue;
18186         var p = params[0][name];
18187         return p ? p[0] : defaultValue;
18188 }
18189
18190 // Returns the first value of the given boolean named parameter.
18191 function getFlag(params,name,defaultValue)
18192 {
18193         return !!getParam(params,name,defaultValue);
18194 }
18195
18196 // Substitute date components into a string
18197 Date.prototype.formatString = function(template)
18198 {
18199         var t = template.replace(/0hh12/g,String.zeroPad(this.getHours12(),2));
18200         t = t.replace(/hh12/g,this.getHours12());
18201         t = t.replace(/0hh/g,String.zeroPad(this.getHours(),2));
18202         t = t.replace(/hh/g,this.getHours());
18203         t = t.replace(/mmm/g,config.messages.dates.shortMonths[this.getMonth()]);
18204         t = t.replace(/0mm/g,String.zeroPad(this.getMinutes(),2));
18205         t = t.replace(/mm/g,this.getMinutes());
18206         t = t.replace(/0ss/g,String.zeroPad(this.getSeconds(),2));
18207         t = t.replace(/ss/g,this.getSeconds());
18208         t = t.replace(/[ap]m/g,this.getAmPm().toLowerCase());
18209         t = t.replace(/[AP]M/g,this.getAmPm().toUpperCase());
18210         t = t.replace(/wYYYY/g,this.getYearForWeekNo());
18211         t = t.replace(/wYY/g,String.zeroPad(this.getYearForWeekNo()-2000,2));
18212         t = t.replace(/YYYY/g,this.getFullYear());
18213         t = t.replace(/YY/g,String.zeroPad(this.getFullYear()-2000,2));
18214         t = t.replace(/MMM/g,config.messages.dates.months[this.getMonth()]);
18215         t = t.replace(/0MM/g,String.zeroPad(this.getMonth()+1,2));
18216         t = t.replace(/MM/g,this.getMonth()+1);
18217         t = t.replace(/0WW/g,String.zeroPad(this.getWeek(),2));
18218         t = t.replace(/WW/g,this.getWeek());
18219         t = t.replace(/DDD/g,config.messages.dates.days[this.getDay()]);
18220         t = t.replace(/ddd/g,config.messages.dates.shortDays[this.getDay()]);
18221         t = t.replace(/0DD/g,String.zeroPad(this.getDate(),2));
18222         t = t.replace(/DDth/g,this.getDate()+this.daySuffix());
18223         t = t.replace(/DD/g,this.getDate());
18224         return t;
18225 };
18226
18227 Date.prototype.getWeek = function()
18228 {
18229         var dt = new Date(this.getTime());
18230         var d = dt.getDay();
18231         if (d==0) d=7;// JavaScript Sun=0, ISO Sun=7
18232         dt.setTime(dt.getTime()+(4-d)*86400000);// shift day to Thurs of same week to calculate weekNo
18233         var n = Math.floor((dt.getTime()-new Date(dt.getFullYear(),0,1)+3600000)/86400000); 
18234         return Math.floor(n/7)+1;
18235 };
18236
18237 Date.prototype.getYearForWeekNo = function()
18238 {
18239         var dt = new Date(this.getTime());
18240         var d = dt.getDay();
18241         if (d==0) d=7;// JavaScript Sun=0, ISO Sun=7
18242         dt.setTime(dt.getTime()+(4-d)*86400000);// shift day to Thurs of same week
18243         return dt.getFullYear();
18244 };
18245
18246 Date.prototype.getHours12 = function()
18247 {
18248         var h = this.getHours();
18249         return h > 12 ? h-12 : ( h > 0 ? h : 12 );
18250 };
18251
18252 Date.prototype.getAmPm = function()
18253 {
18254         return this.getHours() >= 12 ? config.messages.dates.pm : config.messages.dates.am;
18255 };
18256
18257 Date.prototype.daySuffix = function()
18258 {
18259         return config.messages.dates.daySuffixes[this.getDate()-1];
18260 };
18261
18262 // Convert a date to local YYYYMMDDHHMM string format
18263 Date.prototype.convertToLocalYYYYMMDDHHMM = function()
18264 {
18265         return String.zeroPad(this.getFullYear(),4) + String.zeroPad(this.getMonth()+1,2) + String.zeroPad(this.getDate(),2) + String.zeroPad(this.getHours(),2) + String.zeroPad(this.getMinutes(),2);
18266 };
18267
18268 // Convert a date to UTC YYYYMMDDHHMM string format
18269 Date.prototype.convertToYYYYMMDDHHMM = function()
18270 {
18271         return String.zeroPad(this.getUTCFullYear(),4) + String.zeroPad(this.getUTCMonth()+1,2) + String.zeroPad(this.getUTCDate(),2) + String.zeroPad(this.getUTCHours(),2) + String.zeroPad(this.getUTCMinutes(),2);
18272 };
18273
18274 // Convert a date to UTC YYYYMMDD.HHMMSSMMM string format
18275 Date.prototype.convertToYYYYMMDDHHMMSSMMM = function()
18276 {
18277         return String.zeroPad(this.getUTCFullYear(),4) + String.zeroPad(this.getUTCMonth()+1,2) + String.zeroPad(this.getUTCDate(),2) + "." + String.zeroPad(this.getUTCHours(),2) + String.zeroPad(this.getUTCMinutes(),2) + String.zeroPad(this.getUTCSeconds(),2) + String.zeroPad(this.getUTCMilliseconds(),4);
18278 };
18279
18280 // Static method to create a date from a UTC YYYYMMDDHHMM format string
18281 Date.convertFromYYYYMMDDHHMM = function(d)
18282 {
18283         return new Date(Date.UTC(parseInt(d.substr(0,4),10),
18284                         parseInt(d.substr(4,2),10)-1,
18285                         parseInt(d.substr(6,2),10),
18286                         parseInt(d.substr(8,2),10),
18287                         parseInt(d.substr(10,2),10),0,0));
18288 };
18289
18290 //--
18291 //-- Crypto functions and associated conversion routines
18292 //--
18293
18294 // Crypto "namespace"
18295 function Crypto() {}
18296
18297 // Convert a string to an array of big-endian 32-bit words
18298 Crypto.strToBe32s = function(str)
18299 {
18300         var be = Array();
18301         var len = Math.floor(str.length/4);
18302         var i, j;
18303         for(i=0, j=0; i<len; i++, j+=4) {
18304                 be[i] = ((str.charCodeAt(j)&0xff) << 24)|((str.charCodeAt(j+1)&0xff) << 16)|((str.charCodeAt(j+2)&0xff) << 8)|(str.charCodeAt(j+3)&0xff);
18305         }
18306         while (j<str.length) {
18307                 be[j>>2] |= (str.charCodeAt(j)&0xff)<<(24-(j*8)%32);
18308                 j++;
18309         }
18310         return be;
18311 };
18312
18313 // Convert an array of big-endian 32-bit words to a string
18314 Crypto.be32sToStr = function(be)
18315 {
18316         var str = "";
18317         for(var i=0;i<be.length*32;i+=8)
18318                 str += String.fromCharCode((be[i>>5]>>>(24-i%32)) & 0xff);
18319         return str;
18320 };
18321
18322 // Convert an array of big-endian 32-bit words to a hex string
18323 Crypto.be32sToHex = function(be)
18324 {
18325         var hex = "0123456789ABCDEF";
18326         var str = "";
18327         for(var i=0;i<be.length*4;i++)
18328                 str += hex.charAt((be[i>>2]>>((3-i%4)*8+4))&0xF) + hex.charAt((be[i>>2]>>((3-i%4)*8))&0xF);
18329         return str;
18330 };
18331
18332 // Return, in hex, the SHA-1 hash of a string
18333 Crypto.hexSha1Str = function(str)
18334 {
18335         return Crypto.be32sToHex(Crypto.sha1Str(str));
18336 };
18337
18338 // Return the SHA-1 hash of a string
18339 Crypto.sha1Str = function(str)
18340 {
18341         return Crypto.sha1(Crypto.strToBe32s(str),str.length);
18342 };
18343
18344 // Calculate the SHA-1 hash of an array of blen bytes of big-endian 32-bit words
18345 Crypto.sha1 = function(x,blen)
18346 {
18347         // Add 32-bit integers, wrapping at 32 bits
18348         add32 = function(a,b)
18349         {
18350                 var lsw = (a&0xFFFF)+(b&0xFFFF);
18351                 var msw = (a>>16)+(b>>16)+(lsw>>16);
18352                 return (msw<<16)|(lsw&0xFFFF);
18353         };
18354         // Add five 32-bit integers, wrapping at 32 bits
18355         add32x5 = function(a,b,c,d,e)
18356         {
18357                 var lsw = (a&0xFFFF)+(b&0xFFFF)+(c&0xFFFF)+(d&0xFFFF)+(e&0xFFFF);
18358                 var msw = (a>>16)+(b>>16)+(c>>16)+(d>>16)+(e>>16)+(lsw>>16);
18359                 return (msw<<16)|(lsw&0xFFFF);
18360         };
18361         // Bitwise rotate left a 32-bit integer by 1 bit
18362         rol32 = function(n)
18363         {
18364                 return (n>>>31)|(n<<1);
18365         };
18366
18367         var len = blen*8;
18368         // Append padding so length in bits is 448 mod 512
18369         x[len>>5] |= 0x80 << (24-len%32);
18370         // Append length
18371         x[((len+64>>9)<<4)+15] = len;
18372         var w = Array(80);
18373
18374         var k1 = 0x5A827999;
18375         var k2 = 0x6ED9EBA1;
18376         var k3 = 0x8F1BBCDC;
18377         var k4 = 0xCA62C1D6;
18378
18379         var h0 = 0x67452301;
18380         var h1 = 0xEFCDAB89;
18381         var h2 = 0x98BADCFE;
18382         var h3 = 0x10325476;
18383         var h4 = 0xC3D2E1F0;
18384
18385         for(var i=0;i<x.length;i+=16) {
18386                 var j,t;
18387                 var a = h0;
18388                 var b = h1;
18389                 var c = h2;
18390                 var d = h3;
18391                 var e = h4;
18392                 for(j = 0;j<16;j++) {
18393                         w[j] = x[i+j];
18394                         t = add32x5(e,(a>>>27)|(a<<5),d^(b&(c^d)),w[j],k1);
18395                         e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
18396                 }
18397                 for(j=16;j<20;j++) {
18398                         w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
18399                         t = add32x5(e,(a>>>27)|(a<<5),d^(b&(c^d)),w[j],k1);
18400                         e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
18401                 }
18402                 for(j=20;j<40;j++) {
18403                         w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
18404                         t = add32x5(e,(a>>>27)|(a<<5),b^c^d,w[j],k2);
18405                         e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
18406                 }
18407                 for(j=40;j<60;j++) {
18408                         w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
18409                         t = add32x5(e,(a>>>27)|(a<<5),(b&c)|(d&(b|c)),w[j],k3);
18410                         e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
18411                 }
18412                 for(j=60;j<80;j++) {
18413                         w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
18414                         t = add32x5(e,(a>>>27)|(a<<5),b^c^d,w[j],k4);
18415                         e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
18416                 }
18417
18418                 h0 = add32(h0,a);
18419                 h1 = add32(h1,b);
18420                 h2 = add32(h2,c);
18421                 h3 = add32(h3,d);
18422                 h4 = add32(h4,e);
18423         }
18424         return Array(h0,h1,h2,h3,h4);
18425 };
18426
18427 //--
18428 //-- RGB colour object
18429 //--
18430
18431 // Construct an RGB colour object from a '#rrggbb', '#rgb' or 'rgb(n,n,n)' string or from separate r,g,b values
18432 function RGB(r,g,b)
18433 {
18434         this.r = 0;
18435         this.g = 0;
18436         this.b = 0;
18437         if(typeof r == "string") {
18438                 if(r.substr(0,1) == "#") {
18439                         if(r.length == 7) {
18440                                 this.r = parseInt(r.substr(1,2),16)/255;
18441                                 this.g = parseInt(r.substr(3,2),16)/255;
18442                                 this.b = parseInt(r.substr(5,2),16)/255;
18443                         } else {
18444                                 this.r = parseInt(r.substr(1,1),16)/15;
18445                                 this.g = parseInt(r.substr(2,1),16)/15;
18446                                 this.b = parseInt(r.substr(3,1),16)/15;
18447                         }
18448                 } else {
18449                         var rgbPattern = /rgb\s*\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)/;
18450                         var c = r.match(rgbPattern);
18451                         if(c) {
18452                                 this.r = parseInt(c[1],10)/255;
18453                                 this.g = parseInt(c[2],10)/255;
18454                                 this.b = parseInt(c[3],10)/255;
18455                         }
18456                 }
18457         } else {
18458                 this.r = r;
18459                 this.g = g;
18460                 this.b = b;
18461         }
18462         return this;
18463 }
18464
18465 // Mixes this colour with another in a specified proportion
18466 // c = other colour to mix
18467 // f = 0..1 where 0 is this colour and 1 is the new colour
18468 // Returns an RGB object
18469 RGB.prototype.mix = function(c,f)
18470 {
18471         return new RGB(this.r + (c.r-this.r) * f,this.g + (c.g-this.g) * f,this.b + (c.b-this.b) * f);
18472 };
18473
18474 // Return an rgb colour as a #rrggbb format hex string
18475 RGB.prototype.toString = function()
18476 {
18477         return "#" + ("0" + Math.floor(this.r.clamp(0,1) * 255).toString(16)).right(2) +
18478                                  ("0" + Math.floor(this.g.clamp(0,1) * 255).toString(16)).right(2) +
18479                                  ("0" + Math.floor(this.b.clamp(0,1) * 255).toString(16)).right(2);
18480 };
18481
18482 //--
18483 //-- DOM utilities - many derived from www.quirksmode.org
18484 //--
18485
18486 function drawGradient(place,horiz,colours)
18487 {
18488         for(var t=0; t<= 100; t+=2) {
18489                 var bar = document.createElement("div");
18490                 place.appendChild(bar);
18491                 bar.style.position = "absolute";
18492                 bar.style.left = horiz ? t + "%" : 0;
18493                 bar.style.top = horiz ? 0 : t + "%";
18494                 bar.style.width = horiz ? (101-t) + "%" : "100%";
18495                 bar.style.height = horiz ? "100%" : (101-t) + "%";
18496                 bar.style.zIndex = -1;
18497                 var f = t/100;
18498                 var p = f*(colours.length-1);
18499                 bar.style.backgroundColor = colours[Math.floor(p)].mix(colours[Math.ceil(p)],p-Math.floor(p)).toString();
18500         }
18501 }
18502
18503 function createTiddlyText(theParent,theText)
18504 {
18505         return theParent.appendChild(document.createTextNode(theText));
18506 }
18507
18508 function createTiddlyCheckbox(theParent,caption,checked,onChange)
18509 {
18510         var cb = document.createElement("input");
18511         cb.setAttribute("type","checkbox");
18512         cb.onclick = onChange;
18513         theParent.appendChild(cb);
18514         cb.checked = checked;
18515         cb.className = "chkOptionInput";
18516         if(caption)
18517                 wikify(caption,theParent);
18518         return cb;
18519 }
18520
18521 function createTiddlyElement(theParent,theElement,theID,theClass,theText)
18522 {
18523         var e = document.createElement(theElement);
18524         if(theClass != null)
18525                 e.className = theClass;
18526         if(theID != null)
18527                 e.setAttribute("id",theID);
18528         if(theText != null)
18529                 e.appendChild(document.createTextNode(theText));
18530         if(theParent != null)
18531                 theParent.appendChild(e);
18532         return e;
18533 }
18534
18535 function addEvent(obj,type,fn)
18536 {
18537         if(obj.attachEvent) {
18538                 obj['e'+type+fn] = fn;
18539                 obj[type+fn] = function(){obj['e'+type+fn](window.event);};
18540                 obj.attachEvent('on'+type,obj[type+fn]);
18541         } else {
18542                 obj.addEventListener(type,fn,false);
18543         }
18544 }
18545
18546 function removeEvent(obj,type,fn)
18547 {
18548         if(obj.detachEvent) {
18549                 obj.detachEvent('on'+type,obj[type+fn]);
18550                 obj[type+fn] = null;
18551         } else {
18552                 obj.removeEventListener(type,fn,false);
18553         }
18554 }
18555
18556 function addClass(e,theClass)
18557 {
18558         var currClass = e.className.split(" ");
18559         if(currClass.indexOf(theClass) == -1)
18560                 e.className += " " + theClass;
18561 }
18562
18563 function removeClass(e,theClass)
18564 {
18565         var currClass = e.className.split(" ");
18566         var i = currClass.indexOf(theClass);
18567         while(i != -1) {
18568                 currClass.splice(i,1);
18569                 i = currClass.indexOf(theClass);
18570         }
18571         e.className = currClass.join(" ");
18572 }
18573
18574 function hasClass(e,theClass)
18575 {
18576         if(e.className) {
18577                 if(e.className.split(" ").indexOf(theClass) != -1)
18578                         return true;
18579         }
18580         return false;
18581 }
18582
18583 // Find the closest relative with a given property value (property defaults to tagName, relative defaults to parentNode)
18584 function findRelated(e,value,name,relative)
18585 {
18586         name = name ? name : "tagName";
18587         relative = relative ? relative : "parentNode";
18588         if(name == "className") {
18589                 while(e && !hasClass(e,value)) {
18590                         e = e[relative];
18591                 }
18592         } else {
18593                 while(e && e[name] != value) {
18594                         e = e[relative];
18595                 }
18596         }
18597         return e;
18598 }
18599
18600 // Resolve the target object of an event
18601 function resolveTarget(e)
18602 {
18603         var obj;
18604         if(e.target)
18605                 obj = e.target;
18606         else if(e.srcElement)
18607                 obj = e.srcElement;
18608         if(obj.nodeType == 3) // defeat Safari bug
18609                 obj = obj.parentNode;
18610         return obj;
18611 }
18612
18613 // Return the content of an element as plain text with no formatting
18614 function getPlainText(e)
18615 {
18616         var text = "";
18617         if(e.innerText)
18618                 text = e.innerText;
18619         else if(e.textContent)
18620                 text = e.textContent;
18621         return text;
18622 }
18623
18624 // Get the scroll position for window.scrollTo necessary to scroll a given element into view
18625 function ensureVisible(e)
18626 {
18627         var posTop = findPosY(e);
18628         var posBot = posTop + e.offsetHeight;
18629         var winTop = findScrollY();
18630         var winHeight = findWindowHeight();
18631         var winBot = winTop + winHeight;
18632         if(posTop < winTop) {
18633                 return posTop;
18634         } else if(posBot > winBot) {
18635                 if(e.offsetHeight < winHeight)
18636                         return posTop - (winHeight - e.offsetHeight);
18637                 else
18638                         return posTop;
18639         } else {
18640                 return winTop;
18641         }
18642 }
18643
18644 // Get the current width of the display window
18645 function findWindowWidth()
18646 {
18647         return window.innerWidth ? window.innerWidth : document.documentElement.clientWidth;
18648 }
18649
18650 // Get the current height of the display window
18651 function findWindowHeight()
18652 {
18653         return window.innerHeight ? window.innerHeight : document.documentElement.clientHeight;
18654 }
18655
18656 // Get the current horizontal page scroll position
18657 function findScrollX()
18658 {
18659         return window.scrollX ? window.scrollX : document.documentElement.scrollLeft;
18660 }
18661
18662 // Get the current vertical page scroll position
18663 function findScrollY()
18664 {
18665         return window.scrollY ? window.scrollY : document.documentElement.scrollTop;
18666 }
18667
18668 function findPosX(obj)
18669 {
18670         var curleft = 0;
18671         while(obj.offsetParent) {
18672                 curleft += obj.offsetLeft;
18673                 obj = obj.offsetParent;
18674         }
18675         return curleft;
18676 }
18677
18678 function findPosY(obj)
18679 {
18680         var curtop = 0;
18681         while(obj.offsetParent) {
18682                 curtop += obj.offsetTop;
18683                 obj = obj.offsetParent;
18684         }
18685         return curtop;
18686 }
18687
18688 // Blur a particular element
18689 function blurElement(e)
18690 {
18691         if(e != null && e.focus && e.blur) {
18692                 e.focus();
18693                 e.blur();
18694         }
18695 }
18696
18697 // Create a non-breaking space
18698 function insertSpacer(place)
18699 {
18700         var e = document.createTextNode(String.fromCharCode(160));
18701         if(place)
18702                 place.appendChild(e);
18703         return e;
18704 }
18705
18706 // Remove all children of a node
18707 function removeChildren(e)
18708 {
18709         while(e && e.hasChildNodes())
18710                 removeNode(e.firstChild);
18711 }
18712
18713 // Remove a node and all it's children
18714 function removeNode(e)
18715 {
18716         scrubNode(e);
18717         e.parentNode.removeChild(e);
18718 }
18719
18720 // Remove any event handlers or non-primitve custom attributes
18721 function scrubNode(e)
18722 {
18723         var att = e.attributes;
18724         if(att) {
18725                 for(var t=0; t<att.length; t++) {
18726                         var n = att[t].name;
18727                         if(n !== 'style' && (typeof e[n] === 'function' || (typeof e[n] === 'object' && e[n] != null))) {
18728                                 try {
18729                                         e[n] = null;
18730                                 } catch(ex) {
18731                                 }
18732                         }
18733                 }
18734         }
18735         var c = e.firstChild;
18736         while(c) {
18737                 scrubNode(c);
18738                 c = c.nextSibling;
18739         }
18740 }
18741
18742 // Add a stylesheet, replacing any previous custom stylesheet
18743 function setStylesheet(s,id,doc)
18744 {
18745         if(!id)
18746                 id = "customStyleSheet";
18747         if(!doc)
18748                 doc = document;
18749         var n = doc.getElementById(id);
18750         if(doc.createStyleSheet) {
18751                 // Test for IE's non-standard createStyleSheet method
18752                 if(n)
18753                         n.parentNode.removeChild(n);
18754                 // This failed without the &nbsp;
18755                 doc.getElementsByTagName("head")[0].insertAdjacentHTML("beforeEnd","&nbsp;<style id='" + id + "'>" + s + "</style>");
18756         } else {
18757                 if(n) {
18758                         n.replaceChild(doc.createTextNode(s),n.firstChild);
18759                 } else {
18760                         n = doc.createElement("style");
18761                         n.type = "text/css";
18762                         n.id = id;
18763                         n.appendChild(doc.createTextNode(s));
18764                         doc.getElementsByTagName("head")[0].appendChild(n);
18765                 }
18766         }
18767 }
18768
18769 // Force the browser to do a document reflow when needed to workaround browser bugs
18770 function forceReflow()
18771 {
18772         if(config.browser.isGecko) {
18773                 setStylesheet("body {top:-1em;margin-top:1em;}");
18774                 setStylesheet("");
18775         }
18776 }
18777
18778 // Replace the current selection of a textarea or text input and scroll it into view
18779 function replaceSelection(e,text)
18780 {
18781         if(e.setSelectionRange) {
18782                 var oldpos = e.selectionStart;
18783                 var isRange = e.selectionEnd > e.selectionStart;
18784                 e.value = e.value.substr(0,e.selectionStart) + text + e.value.substr(e.selectionEnd);
18785                 e.setSelectionRange(isRange ? oldpos : oldpos + text.length,oldpos + text.length);
18786                 var linecount = e.value.split('\n').length;
18787                 var thisline = e.value.substr(0,e.selectionStart).split('\n').length-1;
18788                 e.scrollTop = Math.floor((thisline - e.rows / 2) * e.scrollHeight / linecount);
18789         } else if(document.selection) {
18790                 var range = document.selection.createRange();
18791                 if(range.parentElement() == e) {
18792                         var isCollapsed = range.text == "";
18793                         range.text = text;
18794                         if(!isCollapsed) {
18795                                 range.moveStart('character', -text.length);
18796                                 range.select();
18797                         }
18798                 }
18799         }
18800 }
18801
18802 // Returns the text of the given (text) node, possibly merging subsequent text nodes
18803 function getNodeText(e)
18804 {
18805         var t = ""; 
18806         while(e && e.nodeName == "#text") {
18807                 t += e.nodeValue;
18808                 e = e.nextSibling;
18809         }
18810         return t;
18811 }
18812
18813 //--
18814 //-- LoaderBase and SaverBase
18815 //--
18816
18817 function LoaderBase() {}
18818
18819 LoaderBase.prototype.loadTiddler = function(store,node,tiddlers)
18820 {
18821         var title = this.getTitle(store,node);
18822         if(title) {
18823                 var tiddler = store.createTiddler(title);
18824                 this.internalizeTiddler(store,tiddler,title,node);
18825                 tiddlers.push(tiddler);
18826         }
18827 };
18828
18829 LoaderBase.prototype.loadTiddlers = function(store,nodes)
18830 {
18831         var tiddlers = [];
18832         for(var t = 0; t < nodes.length; t++) {
18833                 try {
18834                         this.loadTiddler(store,nodes[t],tiddlers);
18835                 } catch(ex) {
18836                         showException(ex,config.messages.tiddlerLoadError.format([this.getTitle(store,nodes[t])]));
18837                 }
18838         }
18839         return tiddlers;
18840 };
18841
18842 function SaverBase() {}
18843
18844 SaverBase.prototype.externalize = function(store)
18845 {
18846         var results = [];
18847         var tiddlers = store.getTiddlers("title");
18848         for(var t = 0; t < tiddlers.length; t++)
18849                 results.push(this.externalizeTiddler(store,tiddlers[t]));
18850         return results.join("\n");
18851 };
18852
18853 //--
18854 //-- TW21Loader (inherits from LoaderBase)
18855 //--
18856
18857 function TW21Loader() {}
18858
18859 TW21Loader.prototype = new LoaderBase();
18860
18861 TW21Loader.prototype.getTitle = function(store,node)
18862 {
18863         var title = null;
18864         if(node.getAttribute) {
18865                 title = node.getAttribute("title");
18866                 if(!title)
18867                         title = node.getAttribute("tiddler");
18868         }
18869         if(!title && node.id) {
18870                 var lenPrefix = store.idPrefix.length;
18871                 if (node.id.substr(0,lenPrefix) == store.idPrefix)
18872                         title = node.id.substr(lenPrefix);
18873         }
18874         return title;
18875 };
18876
18877 TW21Loader.prototype.internalizeTiddler = function(store,tiddler,title,node)
18878 {
18879         var e = node.firstChild;
18880         var text = null;
18881         if(node.getAttribute("tiddler")) {
18882                 text = getNodeText(e).unescapeLineBreaks();
18883         } else {
18884                 while(e.nodeName!="PRE" && e.nodeName!="pre") {
18885                         e = e.nextSibling;
18886                 }
18887                 text = e.innerHTML.replace(/\r/mg,"").htmlDecode();
18888         }
18889         var modifier = node.getAttribute("modifier");
18890         var c = node.getAttribute("created");
18891         var m = node.getAttribute("modified");
18892         var created = c ? Date.convertFromYYYYMMDDHHMM(c) : version.date;
18893         var modified = m ? Date.convertFromYYYYMMDDHHMM(m) : created;
18894         var tags = node.getAttribute("tags");
18895         var fields = {};
18896         var attrs = node.attributes;
18897         for(var i = attrs.length-1; i >= 0; i--) {
18898                 var name = attrs[i].name;
18899                 if (attrs[i].specified && !TiddlyWiki.isStandardField(name)) {
18900                         fields[name] = attrs[i].value.unescapeLineBreaks();
18901                 }
18902         }
18903         tiddler.assign(title,text,modifier,modified,tags,created,fields);
18904         return tiddler;
18905 };
18906
18907 //--
18908 //-- TW21Saver (inherits from SaverBase)
18909 //--
18910
18911 function TW21Saver() {}
18912
18913 TW21Saver.prototype = new SaverBase();
18914
18915 TW21Saver.prototype.externalizeTiddler = function(store,tiddler)
18916 {
18917         try {
18918                 var extendedAttributes = "";
18919                 var usePre = config.options.chkUsePreForStorage;
18920                 store.forEachField(tiddler,
18921                         function(tiddler,fieldName,value) {
18922                                 // don't store stuff from the temp namespace
18923                                 if(typeof value != "string")
18924                                         value = "";
18925                                 if (!fieldName.match(/^temp\./))
18926                                         extendedAttributes += ' %0="%1"'.format([fieldName,value.escapeLineBreaks().htmlEncode()]);
18927                         },true);
18928                 var created = tiddler.created.convertToYYYYMMDDHHMM();
18929                 var modified = tiddler.modified.convertToYYYYMMDDHHMM();
18930                 var vdate = version.date.convertToYYYYMMDDHHMM();
18931                 var attributes = tiddler.modifier ? ' modifier="' + tiddler.modifier.htmlEncode() + '"' : "";
18932                 attributes += (usePre && modified == created) ? "" : ' modified="' + modified +'"';
18933                 attributes += (usePre && created == vdate) ? "" :' created="' + created + '"';
18934                 var tags = tiddler.getTags();
18935                 if(!usePre || tags)
18936                         attributes += ' tags="' + tags.htmlEncode() + '"';
18937                 return ('<div %0="%1"%2%3>%4</'+'div>').format([
18938                                 usePre ? "title" : "tiddler",
18939                                 tiddler.title.htmlEncode(),
18940                                 attributes,
18941                                 extendedAttributes,
18942                                 usePre ? "\n<pre>" + tiddler.text.htmlEncode() + "</pre>\n" : tiddler.text.escapeLineBreaks().htmlEncode()
18943                         ]);
18944         } catch (ex) {
18945                 throw exceptionText(ex,config.messages.tiddlerSaveError.format([tiddler.title]));
18946         }
18947 };
18948
18949 //--
18950 //-- Deprecated code
18951 //--
18952
18953 // @Deprecated: Use createElementAndWikify and this.termRegExp instead
18954 config.formatterHelpers.charFormatHelper = function(w)
18955 {
18956         w.subWikify(createTiddlyElement(w.output,this.element),this.terminator);
18957 };
18958
18959 // @Deprecated: Use enclosedTextHelper and this.lookaheadRegExp instead
18960 config.formatterHelpers.monospacedByLineHelper = function(w)
18961 {
18962         var lookaheadRegExp = new RegExp(this.lookahead,"mg");
18963         lookaheadRegExp.lastIndex = w.matchStart;
18964         var lookaheadMatch = lookaheadRegExp.exec(w.source);
18965         if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
18966                 var text = lookaheadMatch[1];
18967                 if(config.browser.isIE)
18968                         text = text.replace(/\n/g,"\r");
18969                 createTiddlyElement(w.output,"pre",null,null,text);
18970                 w.nextMatch = lookaheadRegExp.lastIndex;
18971         }
18972 };
18973
18974 // @Deprecated: Use <br> or <br /> instead of <<br>>
18975 config.macros.br.handler = function(place)
18976 {
18977         createTiddlyElement(place,"br");
18978 };
18979
18980 // Find an entry in an array. Returns the array index or null
18981 // @Deprecated: Use indexOf instead
18982 Array.prototype.find = function(item)
18983 {
18984         var i = this.indexOf(item);
18985         return i == -1 ? null : i;
18986 };
18987
18988 // Load a tiddler from an HTML DIV. The caller should make sure to later call Tiddler.changed()
18989 // @Deprecated: Use store.getLoader().internalizeTiddler instead
18990 Tiddler.prototype.loadFromDiv = function(divRef,title)
18991 {
18992         return store.getLoader().internalizeTiddler(store,this,title,divRef);
18993 };
18994
18995 // Format the text for storage in an HTML DIV
18996 // @Deprecated Use store.getSaver().externalizeTiddler instead.
18997 Tiddler.prototype.saveToDiv = function()
18998 {
18999         return store.getSaver().externalizeTiddler(store,this);
19000 };
19001
19002 // @Deprecated: Use store.allTiddlersAsHtml() instead
19003 function allTiddlersAsHtml()
19004 {
19005         return store.allTiddlersAsHtml();
19006 }
19007
19008 // @Deprecated: Use refreshPageTemplate instead
19009 function applyPageTemplate(title)
19010 {
19011         refreshPageTemplate(title);
19012 }
19013
19014 // @Deprecated: Use story.displayTiddlers instead
19015 function displayTiddlers(srcElement,titles,template,unused1,unused2,animate,unused3)
19016 {
19017         story.displayTiddlers(srcElement,titles,template,animate);
19018 }
19019
19020 // @Deprecated: Use story.displayTiddler instead
19021 function displayTiddler(srcElement,title,template,unused1,unused2,animate,unused3)
19022 {
19023         story.displayTiddler(srcElement,title,template,animate);
19024 }
19025
19026 // @Deprecated: Use functions on right hand side directly instead
19027 var createTiddlerPopup = Popup.create;
19028 var scrollToTiddlerPopup = Popup.show;
19029 var hideTiddlerPopup = Popup.remove;
19030
19031 // @Deprecated: Use right hand side directly instead
19032 var regexpBackSlashEn = new RegExp("\\\\n","mg");
19033 var regexpBackSlash = new RegExp("\\\\","mg");
19034 var regexpBackSlashEss = new RegExp("\\\\s","mg");
19035 var regexpNewLine = new RegExp("\n","mg");
19036 var regexpCarriageReturn = new RegExp("\r","mg");
19037 //--
19038 //-- End of scripts
19039 //--
19040 //]]>
19041 </script>
19042 <script type="text/javascript">
19043 //<![CDATA[
19044 if(useJavaSaver)
19045         document.write("<applet style='position:absolute;left:-1px' name='TiddlySaver' code='TiddlySaver.class' archive='TiddlySaver.jar' width='1' height='1'></applet>");
19046 //]]>
19047 </script>
19048 <!--POST-SCRIPT-START-->
19049
19050 <!--POST-SCRIPT-END-->
19051 </body>
19052 </html>