OSDN Git Service

hk
[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="200802191305" created="200709131950" changecount="7">
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が同時にインストールされます。
768 ** ~BCC5.5 + Reloはインストールが比較的簡単で、初心者の型はこちらをお勧めします。
769 ** BCC 5.5 (Borland C++Compiler / Turbo Debugger) はhttp://www.codegear.com/jp/downloads/free/cppbuilderから入手してください。</pre>
770 </div>
771 <div title="1.2 Gaborパッチの事前描画" modifier="Psychlops_DevelopperG" modified="200908190214" created="200709252145" changecount="2">
772 <pre>この節では実験刺激のGaborパッチを事前描画するプログラムを作ります。
773 Gaborパッチとは、以下の図のような正弦波にGauss関数によるエンベロープをかけて、刺激中心から周辺に離れるに従って正弦波のコントラストが低くなっていく様な刺激です。
774 |[Img[image/gabor.png]]| [Img[image/gabor3d.png]]|
775 この刺激は複雑な2次元パターンなので、Canvas::pix()を使ってリアルタイム描画をするよりは、Imageにあらかじめ描画を行った方がいいでしょう。
776
777 Imageを使った描画については[[3章|3. 複雑な描画を行う1(オフスクリーンへの描画)]]で既に触れましたが、この節では[[1.1節|1.1 実験計画のプログラミング]]と同様に汎用性を意識してGaborパッチを描画するためのクラス~GaborImageを作成して、これを利用した刺激描画を行います。
778
779 !!~GaborImageクラスの概要
780 [[GaborImageクラスのソースコード|GaborImage]]
781 [[1.1節|1.1 実験計画のプログラミング]]のIndependentVariablesクラス同様上のリンクのソースコードを実際の宣言が起こる前にコピーアンドペーストして、プログラムに追加するとこのクラスを使用することができます。
782
783 このクラスはdraw()命令のみから構成されています。この命令は指定したImageにImageサイズの1/6をσとするエンベロープのGaborパッチを描画します。
784 |!~GaborImage::draw()|draw(Image &amp;area,  double ori, double freq, double phase, double Lmean, double *contrast)|カラーのGaborパッチをImageに描画します。|
785 |~|draw(Image &amp;area,  double ori, double freq, double phase, double Lmean, double contrast)|グレースケールのGaborパッチをImage上に描画します。|
786 |~|!引数|&gt;|
787 |~|Image &amp;area: Gaborパッチを描画するImageの名前|&gt;|
788 |~|double freq: キャリアの空間周波数(pixel)|&gt;|
789 |~|double phase: キャリアの位相(度)|&gt;|
790 |~|double Lmean: パッチの平均輝度 |&gt;|
791 |~|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)|
792
793 !!~GaborImageクラスを用いた刺激の事前描画
794 ここでは、上で説明した~GaborImageクラスを使って刺激の事前描画を行います。
795 まず、ここで追加したクラスのインスタンスを作成することにします。インスタンス名はgaborIMGにします。
796 {{{
797 GaborImage gaborIMG;
798 }}}
799 次にGaborパッチを描画するImageを配列gIMGとして宣言します。
800 {{{
801 Psychlops::Image gIMG[10];
802 }}}
803
804 このImageの10枚分の配列に(傾き量5水準) x (傾きの方向:右/左の2水準)の10枚のGaborパッチを書けばよいのですが、どの配列にどの方位のGaborパッチを書けば良いかを考えなくてはいけません。6.1でGaborパッチの傾き量の水準は0~4の番号を振られていたことを思い出してください。各試行ではこの番号を元にで、各試行でランダムに与えられた傾きの方向を考慮して、呼び出すgIMGの番号を決める必要があります。
805
806 少し複雑になってきたので、与えられた傾き量の番号と傾きの方向に対して、どの方位のGaborパッチが呼び出されるべきかを表にしておきましょう。垂直方位は90度なのでこれに対して傾き量が加減されていることに気をつけてください。
807
808 |&gt;|!傾き量の番号| 0 | 1 | 2 | 3 | 4 |
809 |!傾きの方向|右|90-2|90-4|90-8|90-16|90-32|
810 |~|左|90+2|90+4|90+8|90+16|90+32|
811 | (単位は度)|c
812
813 ここでは、以下の表の様にgIMGの番号とGaborパッチの方位を対応させることにします。ついでに、傾きの方向に対しても番号を振っておきます。右を0左を1としてみましょう。
814
815 |&gt;|!傾き量の番号|&gt;| 0 |&gt;| 1 |&gt;| 2 |&gt;| 3 |&gt;| 4 |&gt;|
816 |!傾きの方向|右(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]|
817 |~|左(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]|
818 | (単位は度)|c
819
820 さらに、これをgIMGの番号順に並べ替えて、各番号に対する計算式を考えます。以下のようになるでしょう。
821
822 |!gIMGの配列番号|gIMG[0]|gIMG[1]|gIMG[2]|gIMG[3]|gIMG[4]|gIMG[5]|gIMG[6]|gIMG[7]|gIMG[8]|gIMG[9]|
823 |!(傾き量, 方向) 番号|(0,0)|(1,0)|(2,0)|(3,0)|(4,0)|(0,1)|(1,1)|(2,1)|(3,1)|(4,1)|
824 |!Gaborパッチの方位(度)|90-2|90-4|90-8|90-16|90-32|90+2|90+4|90+8|90+16|90+32|
825 |!配列番号iに対する方位の計算式|&gt;|&gt;|&gt;|&gt;| 90-pow(2, i) |&gt;|&gt;|&gt;|&gt;| 90+pow(2,i-4) |
826 | ^^Pow(a,b)はaのb乗を示す^^|c
827
828 これで、10枚のGaborパッチを各準備が整いました。~GaborImage::draw()命令を使って実際のプログラムを組んでみます。ここでは最終引数に単なる数値を入れて、グレースケールのGaborパッチを描画することにします。
829
830 最後にdraw()命令を使ってGaborパッチの描画を行います。刺激サイズは100 pixel四方、キャリアの波長は30 pix (空間周波数は1.0/30.0), キャリアのコントラストは0.2 (20%), 位相は0, パッチの平均輝度は0.5にします。
831 また、あらかじめ刺激提示位置(画面中央)にイメージをシフトしておきます。
832 {{{
833 double gIMGsize=100, ori;
834         for(int i=0; i&lt;10; i++){
835                 if(i&lt;5){ ori=90.0-pow(2.0, i);}
836                         else{ ori=90.0+pow(2.0,i-4);}
837                 gIMG[i].set(gIMGsize, gIMGsize);
838                 gaborIMG.draw(gIMG[i], ori, 1.0/30.0, 0.0, 0.5, 0.2);
839                 gIMG[i].centering();
840         }
841 }}}
842
843 この節のコードをまとめると以下のようになります。
844 {{{
845 GaborImage gaborIMG;
846 Psychlops::Image gIMG[10];
847 double gIMGsize=100, ori;
848         for(int i=0; i&lt;10; i++){
849                 if(i&lt;5){ ori=90.0-pow(2.0, i);}
850                         else{ ori=90.0+pow(2.0,i-4);}
851                 gIMG[i].set(gIMGsize, gIMGsize);
852                 gaborIMG.draw(gIMG[i], ori, 1.0/30.0, 0.0, 0.5, 0.2);
853                 gIMG[i].centering();
854         }
855 }}}</pre>
856 </div>
857 <div title="1.2 インストール作業(Mac OS X 10.4) " modifier="YourName" modified="201002040347" created="200709131951" changecount="3">
858 <pre>!! Xcodeのインストール
859 PsychlopsのOSXでのインストールには、まずXcodeをインストールする必要があります。XcodeはApple社が提供する標準の開発環境で、無償で利用できます(ダウンロードにはApple Developers Connectionへの登録が必要です)。
860
861 * [[ダウンロード|http://developer.apple.com/tools/xcode/]]
862 * [[機能紹介|http://www.apple.com/jp/macosx/features/xcode/]]
863
864 ''Mac OS X 10.4向けのXcode 2.x系はApple社よりの配布がすでに終了しています。OSX 10.4のインストール/リカバリディスク中に開発用ツールキットがない場合、10.6へのアップグレードをご検討ください''
865
866 ''xcode.mpkg''をダブルクリックしてインストールを始めます。
867 [img[Xcodeインストール|image/OSX/Xcode2_10.4.png]]
868
869 インストーラの指示に従ってインストールを完了します。
870 [img[Xcodeインストール|image/OSX/Xcode3_10.4.png]]
871
872 !! Psychlopsの本体をインストール
873 * [[Psychlops Download|http://psychlops.l.u-tokyo.ac.jp/?Download]]
874
875 ダウンロードページへ行き、Xcode版の最新版をクリックします。
876 [img[Psychlopsダウンロード|image/OSX/Psychlops_OSX1png]]
877
878 zipファイルを解凍するとパッケージファイルが現れますので、これをダブルクリックしてインストールを開始します。
879 [img[Psychlopsインストール|image/OSX/Psychlops_OSX2.png]]
880
881 インストーラの指示に従ってインストールを完了します。
882 [img[Psychlopsインストール|image/OSX/Psychlops_OSX3.png]]
883
884 !! 新しいプロジェクト
885 Xcodeを起動し、''メニューパー → ファイル → 新しいプロジェクト''を選択します。
886 [img[新しいプロジェクト|image/OSX/Psychlops_OSX_10.4_New1.png]]
887
888 新しいプロジェクトのウィザードが開いたら、Applicationカテゴリにある''Psychlops C++ Application''を選択します。
889 [img[新しいプロジェクト|image/OSX/Psychlops_OSX_10.4_New2.png]]
890
891 新しいプロジェクトを作成する場所と名前を選びます。作成後に場所は変更可能ですが、名前は作り直す以外に変更できませんのでご注意ください。また、パスに日本語を含んでいると実行に失敗することがありますので、ASCII文字のみのフォルダの中に作成してください。
892 [img[新しいプロジェクト|image/OSX/Psychlops_OSX_10.4_New3.png]]
893
894 プログラムはSourcesカテゴリの中にある(プロジェクト名).cppにあります。標準のサンプルプログラムが入っていますので、まずはダブルクリックしてエディタを開き、中身を見てみます(最初から開いていることもあります)。
895 [img[新しいプロジェクト|image/OSX/Psychlops_OSX_10.4_New5.png]]
896
897 下のようなウィンドウが開いたでしょうか。
898 [img[ウインドウ説明|image/OSX/Psychlops_OSX_10.4_New6.png]]
899 プログラム編集エディタでブログラムを編集することができます。実際にはこのサンプルを消して、新しく自分のプログラムを書いていくわけですが、まず最初は正しくインストールされているかどうかを確かめるために、標準のサンプルをそのまま実行してみましょう。ツールバーの実行ボタンをおすと、プログラムが機械語に翻訳されて実行ファイルの作成・実行が行われます。
900
901 出来上がった実行ファイルは、''プロジェクトのあるフォルダ &gt; buildフォルダ &gt; Releaseフォルダ '' の中にあります。この実行ファイルをコピーすることで頒布が可能ですが、メールなどに添付する際は必ずzipファイル等に圧縮してください。(ここで見えている実行プログラムは実際はフォルダです。Macのプログラムの仕様で、実際の実行プログラムは複数存在して、実行プログラムに見えるフォルダに格納されています。これを他のマシンに頒布するためにアップロードしたり、メールに添付したりするときには圧縮して実際に一つのファイルにまとめる必要があります。)
902 [img[ウインドウ説明|image/OSX/Psychlops_OSX_10.4_New7.png]]
903 </pre>
904 </div>
905 <div title="1.2 インストール作業(Mac) " modifier="YourName" created="201002040339" changecount="1">
906 <pre>!! Xcodeのインストール
907 PsychlopsのOSXでのインストールには、まずXcodeをインストールする必要があります。XcodeはApple社が提供する標準の開発環境で、無償で利用できます(ダウンロードにはApple Developers Connectionへの登録が必要です)。対応バージョンについては、2010/01/31現在、Xcode2.5と3.x系に対応しておりますが、最新の対応状況につきましては[[http://psychlops.l.u-tokyo.ac.jp/?Environment]]からご確認ください。スクリーンショットはマニュアル作成時のものですので、バージョンが更新されていると異なる場合があります。ご了承ください。
908
909 * [[ダウンロード|http://developer.apple.com/technology/xcode.html]]
910 * [[機能紹介|http://www.apple.com/jp/macosx/features/xcode/]]
911
912
913 !!! OSXインストールディスクを利用する場合
914
915  OSXインストールディスクに開発環境のインストーラがあります。ディスクの内容は購入時期によって異なる可能性があります。ここでは10.5を例に説明いたします。
916
917 [img[Xcodeインストール|image/OSX/Xcode_DVD_Screen.png]]
918
919  OSXインストールディスク中をFinderで開くと、「オプションインストール」フォルダがあります。このなかの「Xcode Tools」フォルダを開き、「XcodeTools.mpkg」(拡張子は見えない場合があります)をダブルクリックすると、Xcodeインストーラが実行されます。
920
921
922 !!! ダウンロードする場合
923
924 Xcodeをダウンロードするには、まず[[Appleのダウンロードサイト|http://developer.apple.com/technology/xcode.html]]へ行きます。その際、初めての方はまずApple Developers Connectionへの登録をする必要があります。
925
926 [img[Xcodeインストール|image/OSX/Xcode_01.png]]
927
928 「ADC membership」と書かれたリンクをクリックすると「Join Now」というボタンがありますので、それをクリックして
929
930 [img[Xcodeインストール|image/OSX/Xcode_02.png]]
931
932 もう一度[[ダウンロードページ|http://developer.apple.com/technology/xcode.html]]へ行き下側の「Xcode for Mac-only Development」の「Download now」をクリックしてください。その後のページでアカウントを入力すると、ダウンロードリンクが現れますので、本体のdmgファイルをダウンロードしてください。
933
934 [img[Xcodeインストール|image/OSX/Xcode_03.png]]
935
936 ダウンロードが終わると自動的にディスクイメージが展開されますので、''XcodeTools.mpkg''をダブルクリックしてインストールを始めます。
937
938 [img[Xcodeインストール|image/OSX/Xcode2.png]]
939
940 インストーラの指示に従ってインストールを完了します。
941 [img[Xcodeインストール|image/OSX/Xcode3.png]]
942
943
944 !! Psychlopsの本体をインストール
945 * [[Psychlops Download|http://psychlops.l.u-tokyo.ac.jp/?Download]]
946
947 ダウンロードページへ行き、Xcode版の最新版をクリックします。
948 [img[Psychlopsダウンロード|image/OSX/Psychlops_OSX1png]]
949
950 zipファイルを解凍するとパッケージファイルが現れますので、これをダブルクリックしてインストールを開始します。
951 [img[Psychlopsインストール|image/OSX/Psychlops_OSX2.png]]
952
953 インストーラの指示に従ってインストールを完了します。
954 [img[Psychlopsインストール|image/OSX/Psychlops_OSX3.png]]
955
956 !! 新しいプロジェクト
957 Xcodeを起動し、''メニューパー → ファイル → 新しいプロジェクト''を選択します。
958 [img[新しいプロジェクト|image/OSX/Psychlops_OSX_New1.png]]
959
960 新しいプロジェクトのウィザードが開いたら、Applicationカテゴリにある''Psychlops C++ Application''を選択します。
961 [img[新しいプロジェクト|image/OSX/Psychlops_OSX_New2.png]]
962
963 新しいプロジェクトを作成する場所と名前を選びます。作成後に場所は変更可能ですが、名前は作り直す以外に変更できませんのでご注意ください。また、パスに日本語を含んでいると実行に失敗することがありますので、ASCII文字のみのフォルダの中に作成してください。
964 [img[新しいプロジェクト|image/OSX/Psychlops_OSX_New3.png]]
965
966 プログラムはSourcesカテゴリの中にある(プロジェクト名).cppにあります。標準のサンプルプログラムが入っていますので、まずはダブルクリックしてエディタを開き、中身を見てみます(最初から開いていることもあります)。
967 [img[新しいプロジェクト|image/OSX/Psychlops_OSX_New5.png]]
968
969 下のようなウィンドウが開いたでしょうか。
970 [img[ウインドウ説明|image/OSX/Psychlops_OSX_New6.png]]
971 プログラム編集エディタでブログラムを編集することができます。実際にはこのサンプルを消して、新しく自分のプログラムを書いていくわけですが、まず最初は正しくインストールされているかどうかを確かめるために、標準のサンプルをそのまま実行してみましょう。ツールバーの実行ボタンをおすと、プログラムが機械語に翻訳されて実行ファイルの作成・実行が行われます。
972
973 出来上がった実行ファイルは、''プロジェクトのあるフォルダ &gt; buildフォルダ &gt; Releaseフォルダ または Debugフォルダ'' の中にあります。この実行ファイルをコピーすることで頒布が可能ですが、メールなどに添付する際は必ずzipファイル等に圧縮してください。(ここで見えている実行プログラムは実際はフォルダです。Macのプログラムの仕様で、実際の実行プログラムは複数存在して、実行プログラムに見えるフォルダに格納されています。これを他のマシンに頒布するためにアップロードしたり、メールに添付したりするときには圧縮して実際に一つのファイルにまとめる必要があります。)
974 [img[ウインドウ説明|image/OSX/Psychlops_OSX_New7.png]]
975 </pre>
976 </div>
977 <div title="1.3 インストール作業(Windows + BCC) " modifier="YourName" modified="201002040332" created="200709131955" changecount="2">
978 <pre>インストールには管理者権限のあるユーザである必要があります。
979 Windows Vistaに関しては、インストールが自動化されていませんがお使いいただくことは可能です。
980 [[Tips: Vistaにおけるインストール]]をご覧ください。
981
982 !! OpenGLのドライバ設定の確認
983 Psychlopsは画面の垂直同期信号に合わせて画面を更新しますが、この機能を正常に動作させるには、ディスプレイドライバのOpenGL描画設定で垂直同期にあわせるオプションを常にオンにする必要があります。
984 一般的には''コントロールパネル &gt; 画面''の設定の''モニタ''タブを選び、''詳細設定''ボタンから設定します。詳しくはご使用のビデオカードのマニュアルをご覧ください。
985
986 !! BCCのインストール
987 PsychlopsのWindowsでのインストールには、まずコンパイラをインストールする必要があります。ここではBorland社が無償で提供するBorland C++ Compiler 5.5をインストールしてみます。Cマガジンから提供されている設定ツールも一緒にダウンロードしましょう。
988
989 * [[BCCダウンロード|http://www.codegear.com/jp/downloads/free/cppbuilder]]
990 * [[設定ツールダウンロード|http://www.cmagazine.jp/setbcc.html]]
991
992 まずダウンロードページへ行き、''Borland C++Compiler / Turbo Debugger''をクリックします。この後個人情報を入力してダウンロードを開始します。
993 [img[BCCダウンロード|image/Win/BCC0.png]]
994
995 ダウンロードが終わるとインストーラの実行ファイルがありますので、''freecommandlinetools2.exe''をダブルクリックしてインストールを始めます。インストーラの指示に従ってインストールを完了します。
996 [img[BCCインストール|image/Win/BCC1.png]]
997
998 次に設定ツールである''setbcc15b.exe''をダウンロードし、実行します。
999 [img[BCCインストール|image/Win/BCC2.png]]
1000
1001 特に細かい設定をする必要はありません。''進む&gt;&gt;''をクリックし続けて実行すれば実行可能になります。
1002 [img[BCCインストール|image/Win/BCC4.png]]
1003
1004 !! 開発環境Reloのインストール
1005 次に開発環境をインストールします。ここではオープンソースのRelo2を利用します。
1006
1007 * [[Reloダウンロード|http://www.fifsoft.com/relo/download.php]]
1008
1009 まずダウンロードページへ行き、''RELO v2.0 INSTALLER''をクリックし、ダウンロードします。
1010 [img[Reloダウンロード|image/Win/Psychlops_Relo1.png]]
1011
1012 ダウンロードが終わるとインストーラの実行ファイルがありますので、''relosetup20.exe''をダブルクリックしてインストールを始めます。インストーラの指示に従ってインストールを完了します。
1013 [img[Reloインストール|image/Win/Psychlops_Relo2.png]]
1014
1015 インストール終了後、まず最初に環境設定を行うためにRelo2を起動します。起動したらメニューバーのTools から Compilers を選択します。
1016 [img[Reloインストール|image/Win/Psychlops_Relo_New5.png]]
1017
1018 開いたウィンドウで、Newボタンを押し、そのまま進め、New CompilerをBCCにする設定をします。各フィールドを以下のように指定します。
1019 * Name : BCC
1020 * Compiler : bcc32
1021 * Path : C:\borland\bcc55\Bin\
1022 * Type : Borland
1023 [img[Reloインストール|image/Win/Psychlops_Relo4.png]]
1024
1025
1026 !! Psychlopsの本体をインストール
1027 * [[Psychlops Download|http://psychlops.l.u-tokyo.ac.jp/?Download]]
1028
1029 ダウンロードページへ行き、BCC版の最新版をクリックします。
1030 [img[Psychlopsダウンロード|image/Win/Psychlops_WinGL1.png]]
1031
1032 zipファイルを解凍するとインストーラフォルダが現れますので、この中の''PsychlopsLib''フォルダ内にある''PsychlopsLib_xxxx.exe''をダブルクリックしてインストールを開始します。
1033 [img[Psychlopsインストール|image/Win/Psychlops_WinGL3.png]]
1034
1035 インストーラの指示に従ってインストールを完了します。
1036 [img[Psychlopsインストール|image/Win/Psychlops_WinGL3.png]]
1037
1038 !! Relo用のテンプレートをインストール
1039 zipファイルを解凍してできたインストーラフォルダの中の''ReloTemplate''フォルダ内にある''Psychlops_Relo2_Template_xxxxxxxx.exe''をダブルクリックしてインストールを開始します。インストーラの指示に従ってインストールを完了します。
1040 [img[Psychlopsインストール|image/Win/Psychlops_Relo3.png]]
1041
1042 !! 新しいプロジェクト
1043
1044 Reloを起動し、''メニューパー → File → New''を選択します。
1045 [img[新しいプロジェクト|image/Win/Psychlops_Relo_New1.png]]
1046
1047 新しいプロジェクトのウィザードが開いたら、''Psychlops Application''を選択します。
1048 [img[新しいプロジェクト|image/Win/Psychlops_Relo_New2.png]]
1049
1050 新しいプロジェクトの名前を選びます。作成後に場所は変更可能ですが、名前は作り直す以外に変更できませんのでご注意ください。
1051 [img[新しいプロジェクト|image/Win/Psychlops_Relo_New3.png]]
1052
1053 Reloはプロジェクトの作成直後は保存場所が決まっておりませんので、まずはプロジェクトを保存します。パスにスペースまたは日本語を含んでいると実行に失敗することがありますので、ASCII文字のみのフォルダの中に作成してください。
1054 [img[新しいプロジェクト|image/Win/Psychlops_Relo_New4.png]]
1055
1056 プログラムはSourcesカテゴリの中にある(プロジェクト名).cppにあります。標準のサンプルプログラムが入っていますので、まずはダブルクリックしてエディタを開き、中身を見てみます(最初から開いていることもあります)。
1057
1058 下のようなウィンドウが開いたでしょうか?
1059 [img[ウインドウ説明|image/Win/Psychlops_Relo_New6.png]]
1060 プログラム編集エディタでブログラムを編集することができます。実際にはこのサンプルを消して、新しく自分のプログラムを書いていくわけですが、まず最初は正しくインストールされているかどうかを確かめるために、標準のサンプルをそのまま実行してみましょう。ツールバーの実行ボタンをおすと、プログラムが機械語に翻訳されて実行ファイルの作成・実行が行われます。
1061
1062 Reloは現在のバージョンでは、起動直後にコンパイラの設定ウィンドウを開いてOKボタンを押す必要があります。''メニューパー → Tools → Compilers''を選択して、開いたダイアログでOKを押します。
1063 [img[新しいプロジェクト|image/Win/Psychlops_Relo_New5.png]]
1064
1065 出来上がった実行ファイルはプロジェクトと同じフォルダにあります。
1066 [img[新しいプロジェクト|image/Win/Psychlops_Relo_New7.png]]
1067 </pre>
1068 </div>
1069 <div title="1.3 インストール作業(Windows) " modifier="YourName" created="201002040143" changecount="1">
1070 <pre>!! OpenGLのドライバ設定の確認
1071 Psychlopsは画面の垂直同期信号に合わせて画面を更新しますが、この機能を正常に動作させるには、ディスプレイドライバのOpenGL描画設定で垂直同期にあわせるオプションを常にオンにする必要があります。
1072 一般的には''コントロールパネル &gt; 画面''の設定の''モニタ''タブを選び、''詳細設定''ボタンから設定します。詳しくはご使用のビデオカードのマニュアルをご覧ください。
1073
1074 !! CodeBlocks + GCCのインストール
1075 PsychlopsのWindowsで利用するには、まずコンパイラと開発環境をインストールする必要があります。ここでは両者がセットになっているCodeBlocks(MinGW gccコンパイラつき)をダウンロードして利用する例を紹介します。
1076
1077 2010/01/31現在、CodeBlocks8.02 / Windows XP, Vista, 7に対応しておりますが、最新の対応状況につきましてはhttp://psychlops.l.u-tokyo.ac.jp/?Environmentからご確認ください。スクリーンショットはマニュアル作成時のものですので、バージョンが更新されていると異なる場合があります。ご了承ください。
1078
1079 * [[CodeBlocks + GCCダウンロード|http://www.codeblocks.org/downloads/5]]
1080
1081 まずダウンロードページへ行き、''codeblocks-8.02mingw-setup.exe''をSourceforgeまたはBerliOSのサーバからダウンロードします。どちらのサーバからダウンロードしても中身は同じです。
1082 [img[CodeBlocksダウンロード|image/Win/CodeBlocks_Win01.png]]
1083
1084 ダウンロードが終わるとインストーラの実行ファイルがありますので、''codeblocks-8.02mingw-setup.exe''をダブルクリックしてインストールを始めます。インストーラの指示に従ってインストールを完了します。
1085 [img[BCCインストール|image/Win/CodeBlocks_Win02.png]]
1086
1087
1088
1089 !! Psychlopsの本体をインストール
1090 * [[Psychlops Download|http://psychlops.l.u-tokyo.ac.jp/?Download]]
1091
1092 ダウンロードページへ行き、Win32GL版の最新版をクリックします。
1093 [img[Psychlopsダウンロード|image/Win/Psychlops_WinGL1.png]]
1094
1095 zipファイルを解凍するとインストーラフォルダが現れますので、この中の''PsychlopsLib''フォルダ内にある''PsychlopsFramework_Win32_x.x.x.exe''をダブルクリックしてインストールを開始します。
1096 [img[Psychlopsインストール|image/Win/Psychlops_WinGL3.png]]
1097
1098 インストーラの指示に従ってインストールを完了します。
1099 [img[Psychlopsインストール|image/Win/Psychlops_WinGL5.png]]
1100
1101
1102 !! CodeBlocks用のテンプレートをインストール
1103 zipファイルを解凍してできたインストーラフォルダの中の''CodeblocksTemplate''フォルダ内にある''Psychlops_Codeblocks_Template_xxxxxxxx.exe''をダブルクリックしてインストールを開始します。インストーラの指示に従ってインストールを完了します。
1104 [img[Psychlopsインストール|image/Win/Psychlops_WinGL4.png]]
1105
1106 !! 新しいプロジェクト
1107
1108 CodeBlocksを起動し、''メニューパー → File → New → Project''を選択します。
1109 [img[新しいプロジェクト|image/Win/CodeBlocks_Win10.png]]
1110
1111 新しいプロジェクトのウィザードが開いたら、''Psychlops GL Ploject''を選択します(見つからなければ選択ボックスをスクロールしてください)。
1112 [img[新しいプロジェクト|image/Win/CodeBlocks_Win11.png]]
1113
1114 新しいプロジェクトの名前を決め、ファイルを保存する場所を選びます。作成後にフォルダごと移動することで場所は変更可能ですが、名前は作り直す以外に変更できませんのでご注意ください。
1115 [img[新しいプロジェクト|image/Win/CodeBlocks_Win13.png]]
1116
1117 プログラムはSourcesカテゴリの中にあるmain.cppにあります。標準のサンプルプログラムが入っていますので、まずはダブルクリックしてエディタを開き、中身を見てみます(最初から開いていることもあります)。
1118
1119 下のようなウィンドウが開いたでしょうか?
1120 [img[ウインドウ説明|image/Win/CodeBlocks_Win14.png]]
1121 プログラム編集エディタでブログラムを編集することができます。実際にはこのサンプルを消して、新しく自分のプログラムを書いていくわけですが、まず最初は正しくインストールされているかどうかを確かめるために、標準のサンプルをそのまま実行してみましょう。ツールバーの実行ボタンをおすと、プログラムが機械語に翻訳されて実行ファイルの作成・実行が行われます。
1122
1123
1124 出来上がった実行ファイルは、''プロジェクトのあるフォルダ &gt; binフォルダ &gt; Releaseフォルダ または Debugフォルダ'' の中にあります。この実行ファイルをコピーすることで頒布が可能ですが、メールなどに添付する際は必ずzipファイル等に圧縮してください。。
1125 </pre>
1126 </div>
1127 <div title="1.3 各試行部分のプログラミング" modifier="Psychlops_DevelopperG" modified="200908190216" created="200709252145" changecount="2">
1128 <pre>[[1.1|1.1 実験計画のプログラミング]], [[1.2|1.2 Gaborパッチの事前描画]]節で実際の試行に先立つ計算は終了したことになります。
1129 ここでは実際の試行部分のプログラミングを行います。
1130 各試行の実行時はここで作成している実験の場合大きく分けて2つに分かれるのでした。
1131    a. 各試行における描画(刺激の提示):該当するGaborが描画されたImageインスタンスを用いた描画
1132    b. 反応の取得と 配列への記録
1133 まずa.の部分についてもう少し詳しく考えてみると、以下のような手順をループさせれば良いことがわかります。
1134
1135 # 各試行に対応した独立変数の取得
1136 # 試行開始待ち(被験者のキー入力待ち)
1137 # 刺激描画
1138 # 反応待ち(被験者のキー入力待ち)
1139 # 正誤判断とデータの格納
1140
1141 この流れに従って試行番号&quot;trial&quot;番の試行をプログラムしてみます。
1142 ここで使う変数はあとでwhileループの外で宣言を行うので、とりあえず、宣言なしで変数を使っていきます。
1143
1144 !各試行に対応した独立変数の取得
1145 まず、この試行におけるGaborパッチの傾きの方向&quot;direction&quot;をPsychlops::random()命令を使って0か1にランダムに決定します。
1146 次に、試行番号trialから6.1で作成したクラスインスタンスinvarを用いて各独立変数を取得します。
1147 各試行における傾き量の番号(&quot;ori_now&quot;)と提示時間(&quot;dura_now&quot;)を求めるには~IndependentVariables::get()を使います。
1148 |!int ~IndependentVariables::get()|set(int vnum, int trial_now)|
1149 |~|~|int vnum: 独立変数の識別番号|
1150 |~|~|int trial_now: 試行番号|
1151
1152 単に設定したinvarから各試行に対応した変数を取得するのであれば、以下のコードの様になるでしょう。
1153 {{{
1154 ori_now=invar.get(0, trial);
1155 dura_now=invar.get(1, trial);
1156 }}}
1157
1158 しかしここでは実際に欲しい以下の2つの値を計算します
1159 * [[1.2|1.2 Gaborパッチの事前描画]]節で事前描画したImageの配列gIMGの中でこの試行でCanvasに転送されるImageの番号
1160 * 刺激提示のフレーム数
1161
1162 転送番号については、[[1.2|1.2 Gaborパッチの事前描画]]節で作った表をみると、呼び出す配列番号が(傾き量番号)+5 x (方向)であることがわかります。
1163 |!gIMGの配列番号|gIMG[0]|gIMG[1]|gIMG[2]|gIMG[3]|gIMG[4]|gIMG[5]|gIMG[6]|gIMG[7]|gIMG[8]|gIMG[9]|
1164 |!(傾き量, 方向) 番号|(0,0)|(1,0)|(2,0)|(3,0)|(4,0)|(0,1)|(1,1)|(2,1)|(3,1)|(4,1)|
1165
1166 次に、刺激提示のフレーム数ですが、これはCanvasクラスのリフレッシュレートを取得する関数getRefreshRate()を使えばCanvasの宣言に依存せずに提示時間を制御できます。^^*1^^
1167
1168 これらをまとめると変数の取得部は、以下の様なコードになります。
1169 {{{
1170 direction=Psychlops::random(2); //0:right 1:left
1171 ori_now=invar.get(0,trial)+5*direction;
1172 dura_now=sampleA.getRefreshRate()*(invar.get(1,trial)/1000);
1173 }}}
1174 ^^*1 この実験では必ず計算結果が整数フレームとなるようにあらかじめ変数を設定してあることに注意してください。^^
1175
1176 !試行開始待ち(被験者のキー入力待ち)
1177 次にユーザー(被験者)の準備が整って、キーを押すまでプログラムを&quot;待ち&quot;の状態にする部分をコーディングします。
1178 ここでは、試行開始のキーにはスペースキーを用います。さらに、被験者がわかりやすいように、プログラム側の準備が整って「待ち」の状態になっている間は試行が何番目に当たるかを表示するようにしてみます。
1179 キー入力待ちのコードは今まで何度も使ってきたとおりに、以下のように書きます。
1180 {{{
1181 while(!Input::get(Keyboard::spc));
1182 }}}
1183 試行番号を表示するにはCanvas::message()命令を使えばよいのですが、試行番号は試行毎に変化するので少し工夫が必要です。
1184 ここでは、Cの標準関数であるsprintf()関数を使って、試行番号を文字列に埋め込みます。この部分はあまりプログラムの流れには関係ないので、良くわからない方はとりあえず無視して先に進んでください。以上をまとめると以下のようなコードになります。
1185 {{{
1186 sampleA.clear(0.5);
1187
1188 for(int i=0; i&lt;64; i++) trial_header[i] = 0; // 文字列の初期化
1189 sprintf(trial_header, &quot;%s%d%s%d&quot;,&quot;Trial: &quot;,  trial, &quot; /&quot;, trialNum ); //試行番号の埋め込み
1190 sampleA.message(trial_header, sampleA.getHcenter(), sampleA.getVcenter()-100, Color::green);//キー待ちメッセージの描画
1191 sampleA.flip();
1192
1193 while(!Input::get(Keyboard::spc));
1194 }}}
1195
1196 !刺激描画
1197 いよいよ実際の刺激の描画ですが、これは特に難しくありません。Image::draw()命令を使って裏画面にori_now番のイメージを転送し、Canvas::flip(int)命令を使って、dura_nowフレーム分の描画時間を予約して描画を反映させます。この後に、Canvas::clear()命令を使えば、一定時間たつと自動的に刺激が消えるプログラムになります。
1198 {{{
1199 sampleA.clear(0.5);
1200 gIMG[ori_now].draw();
1201 sampleA.flip(dura_now);
1202 sampleA.clear(0.5);
1203 sampleA.flip();
1204 }}}
1205
1206 ! 反応待ち(被験者のキー入力待ち)とデータの格納
1207
1208 刺激描画が終わったら被験者のキー入力(f: 左/j: 右)を待ちます。プログラムは&quot;f&quot;か&quot;j&quot;が押されるまでは待ち、押されたら押されたキーに従って、0(j), 1(f)のいずれかの値を変数ansに格納します。[[6.1|6.1 Gaborパッチの事前描画]]節においてgIMGを描画する際、傾きの方向番号にふられた番号が0が右、1が左であったことに注意してください。また、ここでescを押すと、プログラムを途中終了するコードも入れておきます。
1209
1210 {{{
1211 while(1){
1212         if(Input::get(Keyboard::j)){ans=0;break;} //&quot;j&quot;なら右(0)
1213         if(Input::get(Keyboard::f)){ans=1;break;}//&quot;f&quot;なら左(1)
1214         if(Input::get(Keyboard::esc)){exit(0);}
1215 }
1216 }}}
1217
1218 最後はデータの格納です。上の方でランダムに決定したdirectionとキー入力によって得られた値ansが同じならば正答(1),異なれば誤答(0)を配列answer のtrial番目に格納します。ついでに、後でデータを見やすくするためにここで使った2つ独立変数の値も配列orientationConditionと配列durationConditionに格納しておきます。
1219 {{{
1220 if(ans==direction) answer[trial]=1;
1221         else answer[trial]=0;
1222 orientationCondition[trial]=invar.get(0, trial);
1223 durationCondition[trial]=invar.get(1, trial);
1224 }}}
1225
1226 !まとめ
1227 以上をまとめて、ループの中に入れると、各試行部分のプログラミングは完成です。
1228 これまで保留していた変数の宣言がループの前にきちんと記述されていることやそれぞれの型がどのようになっているかにも注目してください。特に、answer, orientationCondition, durationConditionの配列を確保するためにはtrialNumをconst int型として宣言する必要があることには注意が必要です。
1229
1230 {{{
1231         const int trialNum=2*5*10;
1232         int answer[trialNum], orientationCondition[trialNum], durationCondition[trialNum], ans;
1233         int ori_now, dura_now;
1234         int direction;
1235         char trial_header[64];
1236
1237         for(int trial=0; trial&lt;trialNum; trial++){
1238         //各試行に対する変数の取得
1239                 direction=Psychlops::random(2); //0:right 1:left
1240                 ori_now=invar.get(0,trial)+5*direction;
1241                 dura_now=sampleA.getRefreshRate()*(invar.get(1,trial)/1000);
1242
1243         //被験者のキー入力待ち
1244                 sampleA.clear(0.5);
1245                 for(int i=0; i&lt;64; i++) trial_header[i] = 0; // 文字列の初期化
1246                 sprintf(trial_header, &quot;%s%d%s%d&quot;,&quot;Trial: &quot;,  trial, &quot; /&quot;, trialNum ); //試行番号の埋め込み
1247                 sampleA.message(trial_header, sampleA.getHcenter(), sampleA.getVcenter()-100, Color::green);//キー待ちメッセージの描画
1248                 sampleA.flip();
1249                 while(!Input::get(Keyboard::spc));
1250
1251         //刺激描画
1252                 sampleA.clear(0.5);
1253                 gIMG[ori_now].draw();
1254                 sampleA.flip(dura_now);
1255                 sampleA.clear(0.5);
1256                 sampleA.flip();
1257
1258         //反応の取得と格納
1259                 while(1){
1260                         if(Input::get(Keyboard::j)){ans=0;break;}       
1261                         if(Input::get(Keyboard::f)){ans=1;break;}
1262                         if(Input::get(Keyboard::esc)){exit(0);}
1263                 }
1264                 if(ans==direction)answer[trial]=1;
1265                         else answer[trial]=0;
1266                 orientationCondition[trial]=invar.get(0, trial);
1267                 durationCondition[trial]=invar.get(1, trial);
1268         }
1269 }}}</pre>
1270 </div>
1271 <div title="1.4 更新とアンインストール" modifier="YourName" modified="200802191314" created="200709131956" changecount="3">
1272 <pre>2007/8/13以降のバージョンを使用していた場合は、Psychlopsの更新は通常のインストールとまったく同じです。ダウングレードする場合も同様です(つまり、単純に上書きしてしまってかまいません)。それ以前のバージョンが既にインストールされたシステムについては、一度アンインストールをしてからもう一度インストールを行ってください(下記参照)。
1273
1274 Psychlopsは特にレジストリ等の設定を行いませんので、アンインストール時はインストールされたフォルダごと削除してください。Psychlopsは単なるC++ライブラリで、常駐等はしませんので、削除しない場合でもシステムに影響を与えることはありません。
1275
1276 また、C++開発環境を自分で既に運用していて、デフォルトの場所以外の所にライブラリを置くことも可能です。この場合は、インストールを行った後に、自分でFrameworks以下にあるファイル・フォルダを目的の場所に移動して、ご自分でパスの設定をなさってください。
1277
1278 * ~MacOSX
1279 ** /Library/Frameworks/Psychlops.framework 
1280
1281 * Windows
1282 ** (システムドライブ)\Library\Frameworks\Psychlops.framework
1283 *** Windowsには通常Libraryフォルダがありませんので、このフォルダ内にPsychlopsしか入っていなければLibraryフォルダごと削除してかまいません。
1284 ** Relo、Code::Blocksのテンプレートについては、テンプレート一覧ファイルを上書きしています。元に戻す必要がある場合は、再インストールする必要があります。</pre>
1285 </div>
1286 <div title="1.4 終了処理とまとめ" modifier="Psychlops_DevelopperG" modified="200908190216" created="200709252145" changecount="1">
1287 <pre>最後にデータをファイルにセーブして、実験の終了メッセージを記録すれば、プログラミングは完了です。
1288 被験者の反応を記録した配列answerと実験条件を記録した配列orientationCondition, durationConditionをData::savearray()命令を使用して&quot;~ExptData.dat&quot;という名前のテキストファイル(tab区切り)にセーブします。
1289
1290 {{{
1291 sampleA.clear(0.5);
1292 sampleA.message(&quot;Press space key to exit.&quot;, 
1293               sampleA.getHcenter(), sampleA.getVcenter()-100, Color::green);
1294 Data::savearray(&quot;ExptData.dat&quot;, 
1295            &quot;Duration /t Orientation /t Answer&quot;, trialNum, 
1296            durationCondition, orientationCondition, answer );
1297 }}}
1298
1299 これで、すべてのパーツが完成しました。一つにまとめると以下の様になります。
1300 [[Gabor方位判断の実験プログラム例]]
1301 ぱっと見たときには長そうなプログラムですが、よく見ると実際にはクラスの宣言部分がほとんどを占めていて、実験に必要な部分のプログラム(つまりあなたが理解して、書かなければいけない部分!) は比較的短いコードであることがわかります。
1302 コンパイルして1つ1つの部分が正しく実行されているかどうか確かめてみてください。</pre>
1303 </div>
1304 <div title="1.5 Psychlopsの基本構造" modifier="Psychlops_DevelopperG" modified="200908190224" created="200711081840" changecount="3">
1305 <pre>次節で詳しく述べるように、PsychlopsはC++のライブラリになっています。このライブラリは、いくつかの命令のグループから成り立っています。それぞれの命令のことをメソッド、グループのことをクラスと呼びます。Psychlopsを用いた描画は、ある変数がどのクラスに属する変数なのかを宣言し、この変数に対してメソッドを指定することで行います。より詳細な具体例は次節や、このマニュアルの中で随時見ていくと理解できるでしょう。今は、このようなグループがいくつかPsychlopsの中にあると言うことだけを理解しておけば十分です。以下に代表的なPsychlopsのクラスをまとめておきます。
1306
1307 |!Psychlops::Canvas|描画ウィンドウの確保と基本的な描画の命令セット|
1308 |!Psychlops::Point|ドット描画と座標指定のための命令セット|
1309 |!Psychlops::Rectangle|矩形描画と矩形範囲指定のための命令セット|
1310 |!Psychlops::Color|描画色指定のための命令セット|
1311 |!Psychlops::Image|オフスクリーン描画のための命令セット|
1312 |!Psychlops::Matrix|行列演算のための命令セット|
1313 |!Psychlops::Clock|時間計測のための命令セット|
1314 |!Psychlops::Input|入出力取得のための命令セット|
1315
1316 それぞれのクラスの先頭についているPsychlops::はこれらのクラスがPsychlopsというライブラリの中のクラスであることを示しています。たとえば、Colorと言うクラスは、別のライブラリの中にも存在するかも知れません。その場合、プログラムを解釈する側は、このクラスがどのライブラリのクラスであるかを判断できなくなってしまいます。これを避けるために、スコープ演算子&quot;::&quot;を使って、あるクラスがどのライブラリに所属するかを示すのです。名字のようなものだと思うとわかりやすいかも知れません。
1317
1318 このほかにもPsychlopsにはData, File, Range等の特殊なクラスが存在しますが、これらについては具体的な使い方とともに解説します。
1319
1320 </pre>
1321 </div>
1322 <div title="1.6 Psychlopsを使ったプログラミング" modifier="Kazushi Maruya" modified="200711081830" created="200709170801" changecount="11">
1323 <pre>Psychlopsを使ったプログラミングはC++のプログラミングと同様ですが、必ずしもその全てを理解しておく必要はありません。(もちろん、理解しているほうがより柔軟なプログラミングができますが。)
1324 ここでは、psychlopsを用いたプログラミングの際に、必要不可欠だと思われる基本要素についてごく簡単な説明を行います。より詳細なC++プログラミングを学びたい方は入門書・専門書を参考にされることをおすすめしますが、ここで説明するいくつかのことに対する大まかなイメージをもっておけばPsychlopsのプログラミングにとっては十分なはずです。より細かなことは実際のプログラミングを通して理解することをおすすめします。
1325
1326 [[1.6.1 プログラミングとは?]]
1327 [[1.6.2 Psychlopsを使ったプログラムの形]]
1328 [[1.6.3 配列]]
1329 [[1.6.4 for命令]]
1330 [[1.6.5 変数型]]
1331 [[1.6.6 クラスと関数]]</pre>
1332 </div>
1333 <div title="1.6.1 プログラミングとは?" modifier="YourName" modified="200802191315" created="200709170805" changecount="4">
1334 <pre>まず最初に、プログラムとは何かについて説明します。
1335
1336 プログラムとは、画像や数値などのデータを、一定の手続きに従って操作する過程を、文字で表記したものです。たとえば、Excelなどの表計算でセルに書き込む式も、一種のプログラムということができます。
1337 ここでは、Psychlopsのプログラムを例に考えてみます。
1338
1339 プログラムでまず最初に必要なことは、取り扱うデータや装置を準備することです。
1340 コンピュータを使わない実験でも、画像や画像を配置する画面などをあらかじめ準備しなければなりませんが、
1341 これと同じことをコンピューター上でも行います。
1342
1343 この準備を文字で表記すると、
1344 ''準備したい物 好きな名前''
1345 という書き方になります。たとえば、
1346 {{{
1347 Image natural_image;
1348 }}}
1349 この1行は、画像(Iamge)にnatural_imageという名前をつけて取り扱う準備をしたことになります。
1350
1351 次に、データを操作する手続きを記述します。手続きは
1352 ''操作が行われる対象.操作方法(関係するデータ)''
1353 の方法で記述します。英語のSVO文法に近い記述法ですね。
1354
1355 {{{
1356 natural_image.load(&quot;Natural Image.png&quot;);
1357 }}}
1358 この一行は、natural_imageという画像として、「Natural Image.png」という名前の画像ファイルを読み込む手続きを表します。
1359
1360 {{{
1361 natural_image.centering().draw();
1362 }}}
1363 この一行は、先ほど読み込んだ画像を、画面の中央に配置(センタリング)し、画面に描画する手続きを表します。
1364
1365 プログラムとは、「データを準備する」「データを操作する」ことについて、行う順番どおりに書いていくことなのです。
1366 準備や操作に区切りをつけるには「;」(セミコロン)を記述します。</pre>
1367 </div>
1368 <div title="1.6.2 Psychlopsを使ったプログラムの形" modifier="YourName" modified="200802191316" created="200709170816" changecount="5">
1369 <pre>では、Psychlopsで動く簡単なプログラムを例にとって最も簡単なPsychlopsのプログラムの形を見てみます。
1370
1371 {{{
1372 #include &lt;psychlops.h&gt;
1373 using namespace Psychlops;
1374
1375 void psychlops_main() {
1376         Canvas display( Canvas::fullscreen );
1377         Image natural_image;
1378         natural_image.load(&quot;Natural Image.png&quot;);
1379         natural_image.centering().draw();
1380         display.flip();
1381 }
1382 }}}
1383
1384 先頭の2行はPsychlopsを使うための命令です。
1385 意味はわからなくてもかまいませんが、Psychlopsの命令をコンピュータに理解させるためには必ずこの2行をプログラムの先頭に書いておく必要があります。
1386
1387 {{{
1388 #include &lt;psychlops.h&gt;    // このプログラムにPsychlopsを含める
1389 using namespace Psychlops; // このプログラムはPsychlopsを使う
1390 }}}
1391
1392 次の行は、Psychlopsを用いたプログラム本体を書く場所(メインルーチン)を示す行です。
1393 {{{
1394 void psychlops_main() {         // psychlopsが主に扱うブロック
1395 }}}
1396 Psychlopsは、ここから最後の行の閉じ括弧(})までのブロックをまず最初に実行します。
1397 つまり、この括弧の中にある
1398 {{{
1399         Canvas display( Canvas::fullscreen );
1400         Image natural_image;
1401         natural_image.load(&quot;Natural Image.png&quot;);
1402         natural_image.centering().draw();
1403         display.flip();
1404 }}}
1405 この5行が実際にコンピュータに対して与える命令になります。
1406 今はこの5行の具体的な内容については述べませんが、ユーザーは基本的にはこの部分を書き換えて描画を実行させることになります。</pre>
1407 </div>
1408 <div title="1.6.3 配列" modifier="PsychlopsAdmin" modified="200709170821" created="200709170817" changecount="1">
1409 <pre>これまでの例では画像はひとつだけでしたが、何枚かの画像を選んで出したい場合があります。
1410 いくつかのデータに似たような操作をしたい場合、「配列」が役に立ちます。配列とは、データがまとめて並んでいるもののことを言います。内部にいくつも区切りがある箱のようなものを想像するとよいかもしれません。
1411
1412 たとえば、画像を3枚まとめて扱いたい場合、以下のようにデータを準備します。
1413 {{{
1414 Image natural_images[3];
1415 }}}
1416
1417 これで3枚の画像配列が用意されました。次に、データの並びの中のそれぞれの画像を操作してみましょう。
1418
1419 {{{
1420 natural_images[0].load(&quot;Natural Image0.png&quot;);
1421 natural_images[1].load(&quot;Natural Image1.png&quot;);
1422 natural_images[2].load(&quot;Natural Image2.png&quot;);
1423 }}}
1424
1425 '''配列[ 数字 ]'''
1426 と書くことで、配列のうち数字で指定した順番の要素(この場合は画像)を操作することができます。
1427 順番は0から始まることに注意してください。画像3枚を用意した場合、0,1,2になります。</pre>
1428 </div>
1429 <div title="1.6.4 for命令" modifier="Kazushi Maruya" modified="200711082123" created="200709170819" changecount="7">
1430 <pre>この画像配列を順番に表示してみましょう。ここでは、繰り返し操作を記述するfor文を使うことにします。
1431 {{{
1432 for(int i=0; i&lt;3; i++) {
1433         natural_images[i].centering().draw();
1434         display.flip(60);
1435 }
1436 }}}
1437 for文は、{ } で囲まれたブロックを複数回実行することを示します。
1438 回数は自分で指定することができます。
1439 [img[image/formethod.png]]
1440 この命令の後に続く{}で囲まれた部分のブロックは繰り返し、カウンタ(i)の値を0,1,2の順番で変更しながら実行されます。
1441 {{{
1442 {
1443         natural_images[i].centering().draw();
1444         display.flip(60);
1445 }
1446 }}}
1447
1448 つまり
1449 {{{
1450         for(int i=0; i&lt;3; i++) {
1451                 natural_images[i].centering().draw();
1452                 display.flip(60);
1453         }
1454 }}}
1455
1456 {{{
1457         natural_images[1].centering().draw();
1458         display.flip(60);
1459         natural_images[2].centering().draw();
1460         display.flip(60);
1461         natural_images[3].centering().draw();
1462         display.flip(60);
1463 }}}
1464 は全く同じ結果になります。
1465
1466 image.centering().draw()は画像をセンタリングして描画する操作なので、以下のプログラムをNatural Image0.png, Natural Image1.png, Natural Image2.pngという3つのファイルがあるディレクトリで実行すると、3つの画像が順番に表示されます。
1467
1468 {{{
1469 #include &lt;psychlops.h&gt;
1470 using namespace Psychlops;
1471
1472 void psychlops_main() {
1473         Canvas display( Canvas::fullscreen );
1474         Image natural_image;
1475
1476         natural_images[0].load(&quot;Natural Image0.png&quot;);
1477         natural_images[1].load(&quot;Natural Image1.png&quot;);
1478         natural_images[2].load(&quot;Natural Image2.png&quot;);
1479
1480         for(int i=0; i&lt;3; i++) {
1481                 natural_images[i].centering().draw();
1482                 display.flip(60);
1483         }
1484         
1485 }
1486
1487 }}}</pre>
1488 </div>
1489 <div title="1.6.5 変数型" modifier="PsychlopsAdmin" modified="200709170825" created="200709170821" changecount="3">
1490 <pre>ここまではPsychlopsで扱えるデータを題材に解説してきましたが、最後に、C++言語で取り扱うことのできる基本的なデータについて説明します。
1491 これらのデータの中でもっとも基本的なものは数値です。数値は、主に整数型と小数点型に分かれます。
1492 * int
1493 ** 整数(''int''eger)の略で、整数を取り扱います。小数点以下は切り捨てられますので、注意してください。
1494 * double
1495 ** 浮動小数点を取り扱います(倍精度浮動小数点 ''double'' precision floating point number)
1496
1497 これらのデータも宣言方法は同じで、
1498 {{{
1499 int delay;
1500 double contrast;
1501 }}}
1502
1503 のように使います。データに特定の数値を代入する(格納する)場合は、
1504 {{{
1505 contast = 0.5;
1506 }}}
1507 のように = を使って指定します。
1508
1509 C++にはこのほかにも文字列型(char *やstd::string)、真偽値型(bool)などがありますが、ここでは詳しくは説明しません。これらの型の使い方については、これ以降のプログラミングの実例のなかで必要なところだけを説明していきます。詳細を知りたい型は、CやC++の入門書をご覧ください。</pre>
1510 </div>
1511 <div title="1.6.6 クラスと関数" modifier="YourName" modified="200802191319" created="200709171609" changecount="14">
1512 <pre>! クラス
1513 クラスとは、関係する変数(データ)や関数(メソッド)をまとめたものです。
1514 [[1.5節|1.5 Psychlopsの基本構造]]で概観したように、Psychlopsでは、画面(Canvas)や画像(Image)、図形(Ractangle)など、描画に関するさまざまなクラスを用意しています。各クラスには、その機能に関連した固有の関数があります。
1515 たとえばImageは
1516 * Image.draw()
1517 * Image.pix(x, y, color)
1518 * Image.rect(rect, color)
1519 * Image.save(filename)
1520 *Image. load(filename)
1521 などさまざまな関数を持っています。
1522
1523 !! 関数
1524 関数はには''返り値''と''引数''があります。たとえば累乗関数(power)を例に見てみましょう。
1525 {{{
1526 a = pow( 10, 3 );
1527 }}}
1528 この式を実行すると、変数aには10の3乗である1000が代入されます。累乗関数が必要とする値である10と3を引数といい、返される計算結果1000を返り値と呼びます。
1529
1530 pow関数の引数のうち、1番目は累乗される数、2番目は累乗する数です。順番や型は固定されていますのでご注意ください。順番を間違えると、計算結果も変わってきてしまいます。たとえば、
1531 {{{
1532 a = pow( 3, 10 );
1533 }}}
1534 を実行すると、3の10乗である59049がaに代入されます。
1535
1536
1537 !! 関数の続け書き
1538 Psychclopsでは、一部の関数を.でつなぎながら連続で書くことができます。
1539 {{{
1540 rect.centering().shift(x, y).draw(Color::red);
1541 }}}
1542 この書き方は他のC言語のプログラムではあまり見かけませんが、Psychlopsでは多用されています。プログラムの長さを短くし読みやすくすることができます。
1543
1544 この書き方ができるものは、関数の返り値として変数自身が返されるものに限ります。
1545
1546
1547 !! 関数表の読み方
1548
1549 このマニュアルでは、各クラスの関数について表で説明が出てきます。この表の読み方を、Imageを表示する関数を題材に説明します。
1550
1551 |!Image::draw()|draw()|指定したオフスクリーンを描画します|
1552 |~|draw(double left, double top)|指定したオフスクリーン領域を(left,top)を中心とした座標に設定します|
1553 |~|~|double left: 描画するオフスクリーン領域の座標軸xを指定|
1554 |~|~|double top: 描画するオフスクリーン領域の座標軸yを指定|
1555
1556 一番左の列は関数の名前を示します。ここでまたスコープ演算子が関数名の名前についていますが、これは1.5節と同様にその関数がどのクラスに所属しているかを示すためのものです。
1557 これを見ると、実際のプログラムでも
1558 {{{
1559 Image::draw(10,10);
1560 }}}
1561 と書く様な気がしてしまいますが、いくつかのクラスをのぞいて^^*1^^このような書き方をすることはありません。なぜなら、実際のプログラムの中は、あるクラスに属する変数(インスタンス、と呼ばれます)に対しての命令しか行わないからです。Image型として宣言されたインスタンスに対してメソッドを実行するときには&quot;.&quot;演算子を用います。たとえば、Image::draw()命令を実際に使うときには以下のような形になるでしょう。
1562 {{{
1563 Psychlops::Image img;
1564 img.draw(10,10);
1565 }}}
1566
1567 *1 Input, Display等のハードウェアと直結したクラスはインスタンスを宣言せずに使います。これは、これらのクラスがPsychlopsのイニシャライズ時にあらかじめ宣言されたデフォルトのインスタンスを持っていて、これに対するメソッドの実行を行うように設計されているためです。
1568
1569 上記の表は、Image::draw()というメソッドに
1570 * Image::draw()
1571 * Image::draw(double left, double top)
1572 という二つの使い方があることを示しています。
1573
1574
1575 二番目の列は、引数の違ういくつかのバージョンが列挙されています。
1576 2つめの使い方Image::draw(double left, double top)では、括弧の中にコンマで区切られた2つの文字列があります。
1577 これは、この使い方では関数の引数として2つの変数を代入することができることを示しています。
1578 [img[image/FunctionNotation.png]]
1579 一つめの引数はdouble leftと書かれています。これは、一つめの引数がdouble型の変数でなくてはいけないことを示しています。(ただし、後で書くようにleftと言う名前にする必要はありません。)このマニュアルの中ではそれぞれのメソッド型名(スペース)名前という形でそれぞれの使い方に必要な引数の形式を示しています。
1580
1581 実際に使用するときは、型名を書く必要はありません。left,topという2つの引数をこの関数に渡すときには以下のように書きます。
1582 {{{
1583 Psychlops::Image img;
1584 double left=10,top=10;
1585 img.draw(left,top);
1586 }}}
1587
1588 一番右の列は、各バージョンでの引数の説明になっています。Image::draw(double left, double top)の右側の欄を見ると、各引数について、一番目が表示したいx座標、二番目がy座標であることがわかります。
1589
1590 各引数には名前がついていますが、関数を呼び出す際に引き渡す変数はこの名前である必要はありません。マニュアルでは
1591 * image.draw(left, top)
1592 となっていますが、プログラム中では
1593 * image.draw(100, 200)
1594 * image.draw( x, y )
1595 など任意の値を渡すことが可能です。</pre>
1596 </div>
1597 <div title="2. デモを作成する" modifier="Psychlops_DevelopperG" modified="200908190221" created="200712061915" changecount="3">
1598 <pre>Psychlopsには、プログラム内の変数をプログラムのユーザがインタラクティブに操作できるデモアプリケーションを簡単に作成するためのクラスIndependentが用意されています。
1599 この節では、Independentクラスについて簡単に説明して、これを用いたプログラムの作成例について解説します。
1600
1601 [[2.1 独立変数Independent]] 
1602 [[2.2 デモ環境の表示]] 
1603 </pre>
1604 </div>
1605 <div title="2. 基本的な描画を行う" modifier="Psychlops_DevelopperG" modified="200909300724" created="200708211944" changecount="16">
1606 <pre>[[2.1 描画領域の宣言]]  
1607   [[2.1.1 Canvasの宣言]]
1608   [[2.1.2 Canvasの基本構造と操作]]
1609   [[2.1.3 Canvasのメンバ取得と変数の表示]]       
1610 [[2.2 図形描画のコマンド]]
1611   [[2.2.1 画面上に点を描画する-Pointクラス1-]]           
1612   [[2.2.2 画面上に線を描画する]]           
1613   [[2.2.3 画面上に四角形を描画する-Rectangleクラス1-]]            
1614   [[2.2.4 画面上に円を描画する]]            
1615   [[2.2.5 描画する図形の色を指定する-Colorクラス-]]           
1616   [[2.2.6 画面上に縞を描画する-Rectangleクラス2-]]
1617   [[2.2.7 画面上に縞を描画する-Pointクラス2-]] 
1618   [[2.2.8 運動する図形を描画する-Rectangleクラス3-]] 
1619 [[2.3 文字列の描画]]</pre>
1620 </div>
1621 <div title="2.1 描画領域の宣言" modifier="Psychlops_DevelopperG" modified="200908190225" created="200708212010" changecount="6">
1622 <pre>この節ではPsychlopsのもっとも基本的なクラスであるCanvasクラスの宣言方法と初歩的な操作を説明します。
1623 [[2.1.1 Canvasの宣言]]
1624 [[2.1.2 Canvasの基本構造と操作]]
1625 [[2.1.3 Canvasのメンバ取得と変数の表示]]</pre>
1626 </div>
1627 <div title="2.1 独立変数Independent" modifier="Psychlops_DevelopperG" modified="200908190217" created="200710220054" changecount="1">
1628 <pre>!Independentとは
1629
1630 Independentクラスは、プログラム中で独立変数に当たる変数を制御するためのオプション機能です。現在はデモ用のコンソールで使用することが出来ます。
1631
1632 通常、プログラムは画面を描画するなどの作業を行っています。プログラムの中にはたくさんの変数や関数が入っています。
1633 [img[Independentなし|image/Independent1.png]]
1634
1635 プログラム中の変数をIndependentに登録すると、もともとのプログラムの動作を変えずにデモ用のコンソール機能などを追加することが出来ます。
1636 [img[Independentあり|image/Independent2.png]]
1637
1638
1639 !Independentクラスの設定
1640
1641 (Tiddly Wikiの機能上の都合、記号が全角で表示されていますが、実際は半角です)
1642 |!Independent|<< 変数|Independentクラスに登録する変数を指定します。|
1643 |~|| 文字列|変数につけるラベルを指定します|
1644 |~|| Range|Independenクラスtに指定する変数の変動範囲を指定します。変動範囲チェックはデモコンソールなどからの書き換え時にしか行われないため、元のプログラムで変動範囲外に変化する場合は検出できません。|
1645 |~|| 数値|デモコンソールでのキー押しあたり変化量を設定します。2つ続けて書いた場合、一方がShiftキーとの同時押し時の変化量になります。|
1646 現在のところ(2007年11月現在)、Independentクラスに登録できる変数は数値型(int, double等)のみです。
1647
1648 !!Independentクラスへの登録
1649 以下では、例として3つの変数をIndependentクラスに登録する書き方例を示します。
1650 {{{
1651         double rect_size = 100;
1652         double rect_lum  = 0.5;
1653         double bg_lum    = 0.2;
1654
1655         Range rng;
1656         Independent &lt;&lt; rect_size | &quot;Rect Size&quot;      |   1&lt; rng&lt; 500 | 10.0 | 2.0 ;
1657         Independent &lt;&lt; rect_lum  | &quot;Rect Luminance&quot; | 0.0&lt;=rng&lt;=1.0 |  0.1 | 0.05;
1658         Independent &lt;&lt; bg_lum    | &quot;BG Luminance&quot;   | 0.0&lt;=rng&lt;=1.0 |  0.1 | 0.05;
1659 }}}
1660
1661 ここで注意することは、変数に適正な初期値を与えておくことです。もしも、Indpendentで設定される範囲の外に初期値が設定されてしまうと、実行時に変更することができなくなります。変数の宣言の時に忘れずに初期値の設定をしておくことをおすすめします。
1662 </pre>
1663 </div>
1664 <div title="2.1.1 Canvasの宣言" modifier="Psychlops_DevelopperG" modified="200910080812" created="200708240239" changecount="25">
1665 <pre>!Canvasとは?
1666
1667 絵を書くときには描く筆や絵の具のほかに紙などのキャンバスが必要です。
1668 この処理はコンピュータ上に絵を描くための&quot;Canvas(描画のためのウィンドウ)&quot;を用意します。
1669
1670 Canvasが確保されていないと描画処理を記述しても処理されません。
1671 描画処理を行う前に必ずCanvasの宣言が必要となります。
1672
1673 !Canvasの宣言文
1674 キャンバスを用意する命令には様々な書式があるのですが、その基本は以下の2つです。
1675
1676 一つは解像度、リフレッシュレートを変更せずに、画面いっぱいに領域を確保する
1677 Canvas(Canvas::fullscreen)
1678
1679 もう一つは解像度、リフレッシュレートを変更して、画面いっぱいに領域を確保する
1680 Canvas(int,int,int,double)
1681 です。
1682
1683 後者で指定した画面モードが選べなかった場合は、例外が送出されプログラムは強制終了します。
1684
1685 そのほかに Canvas::windowスイッチを使ってウインドウを開いて画面を取得する方法があります。
1686 この方法では描画の垂直同期信号への同期が保証されないので(くわしくは[[2.1.2 Canvasの基本構造と操作]]で説明します)、デモやプログラムのデバッグ時に使うとよいでしょう。
1687 詳しくは[[関数一覧|Canvasの宣言]]をご覧ください。
1688 * 参考[[Tips: Canvasとリフレッシュレートの詳細]]
1689
1690 |!Canvasの宣言|
1691 |~|Canvas(Canvas::fullscreen, const Display disp)|&gt;|!現在の画面モードで画面を確保する場合に使います|
1692 |~|~|Canvasmode mode|[[表示モード|CanvasMode]](フルスクリーンorウィンドウ)を選択|
1693 |~|~|const [[Display|Displayの指定値]] disp|[[表示するディスプレイ|Displayの指定値]]を選択(省略可能)|
1694 |~|~|&gt;|マルチディスプレイ環境では、最後の引数でどのディスプレイに表示するか指定できます。省略時はプライマリディスプレイになります。|
1695 |~|Canvas(int width, int height, int colordepth, double refreshrate, const Display disp)|&gt;|!画面モードを指定してフルスクリーン画面を確保します|
1696 |~|~|int width|水平方向の解像度(画面の横幅)を指定|
1697 |~|~|int height|垂直方向の解像度(画面の縦幅)を指定|
1698 |~|~|int colordepth|カラーモード(ビット)を指定|
1699 |~|~|double refreshrate|リフレッシュレート( Hz )を指定|
1700 |~|~|const [[Display|Displayの指定値]] disp|[[表示するディスプレイ|Displayの指定値]]を選択(省略可能)|
1701 |~|~|&gt;|指定した画面モードが選べなかった場合は、例外が送出されプログラムは強制終了します。|
1702
1703 !!宣言文の書き方
1704 {{{
1705 &lt;例1: Canvas::fullscreenオプションを使った宣言例&gt;
1706 #include &lt;psychlops.h&gt;
1707 using namespace Psychlops;
1708
1709 void  psychlops_main() {
1710         Canvas sampleA(Canvas::fullscreen);
1711 }
1712 }}}
1713 {{{
1714 &lt;例2: 各属性値を指定した宣言例&gt;
1715 #include &lt;psychlops.h&gt;
1716 using namespace Psychlops;
1717
1718 void psychlops_main() {
1719         Canvas sampleB(1024,768,32,60.0);
1720 }
1721 }}}
1722
1723 {{{
1724 &lt;例2: 各属性値を指定した宣言例&gt;
1725 #include &lt;psychlops.h&gt;
1726 using namespace Psychlops;
1727
1728 void psychlops_main() {
1729         Canvas sampleC(Canvas1024,768,32,60.0);
1730 }
1731 }}}
1732
1733 @@◎クラスとインスタンスについて@@
1734 上記のソースでは「psychlops_main()」クラスの中で
1735 sampleA,sampleBという名前のCanvasを宣言しています。
1736 これらのプログラム内でCanvas型(クラス)として宣言された変数はインスタンスと呼ばれます。
1737 マニュアル上では、ある命令hoge()の形式を説明するときにCanvas::hoge()と書きますが、実際にプログラム内で宣言されたCanvasに対して命令を実行したいときは、このインスタンス名を使って『インスタンス名.命令文()』という形式で記述します。たとえば、Sample.hoge(hoge)といった形です。より具体的な例はこれ以降に何回も現れます。
1738 ここで注意しなくてはならない点として、ふつうCanvasを宣言する場所はpsychlops_main() 内であることです。つまり、この宣言名はpsychlops_main() 内でしか有効ではありません。
1739 psychlops_main()で呼び出す他の関数内でCanvasを使用したいときは、その関数にCanvasのポインタを引き渡すかDisplayクラスを使って描画する必要があります。
1740 * 参考[[Tips::CanvasクラスとDisplayクラス]]</pre>
1741 </div>
1742 <div title="2.1.2 Canvasの基本構造と操作" modifier="Psychlops_DevelopperG" modified="200910080810" created="200708240240" changecount="7">
1743 <pre>Psychlopsでは2つの画面を交互にディスプレイに提示します。
1744 この2つの画面が対となって、1つのCanvasを構成しています。たとえば、紙に裏表があるようなものだと思ってください。
1745 この2枚の画面に描画をすることとflip()メソッドを使って表画面と裏画面を切り替えることがCanvasクラスに対する主な操作です。
1746
1747 [img[image/canvasstructure.png]]
1748
1749 !Canvas::clear() -画面を塗りつぶす- 
1750 はじめにCanvasを何かの色で塗りつぶしてみましょう。
1751 そのための処理がCanvas::clear()命令です。
1752 書式はCanvas::clear([[Psychlops::Color]] col,TargetSurface)です。いずれの引数も省略可能です。
1753 |!Canvas::clear()|clear([[Psychlops::Color]] col,TargetSurface)|指定したCanvasを指定色で塗りつぶします|
1754 |~|~|[[Psychlops::Color]] col :塗りつぶす色を指定|
1755 |~|~|TargetSurface:塗りつぶす画面を指定|
1756 |~|clear(TargetSurface)|指定したCanvasを塗りつぶします。色の指定には[[Canvas::setClearColor()]]命令を使用します|
1757 |~|~|TargetSurface:塗りつぶす画面を指定|
1758
1759 2つめの引数は少しわかりにくいかもしれません。
1760 clear()命令は2つめの引数を指定することで、今ディスプレイに表示されている画面(紙の表)と、表示されていない画面(紙の裏)のいずれを(あるいは両方を)塗りつぶすのかを指定することができます。
1761 省略した場合は表示されていない画面を塗りつぶします。
1762
1763
1764 !!Canvas::clear()の書き方
1765 {{{
1766 &lt;例1&gt;
1767 #include &lt;psychlops.h&gt;
1768 using namespace Psychlops;
1769
1770 void psychlops_main() {
1771
1772         Canvas sampleA(Canvas::fullscreen);
1773         sampleA.clear(Color::white,Canvas::FRONT);
1774 }
1775 }}}
1776 今ディスプレイに表示されている画面(紙の表)を白く塗りつぶせ、という命令文ですが
1777 これでは描画が一瞬で結果がわかりにくいですね。
1778 「while(!Input::get(Keyboard::spc));」を追加してスペースキーが押されるまで今ディスプレイに表示されている画面(紙の表)が白く表示されるかを確認できるようにしましょう。
1779 この命令の詳細については、[[4.1節|4.1 外部装置(キーボード/マウス)の操作内容を取得する]] で詳しく説明します。今はこの文がスペースキーの入力待ちをする文であると思っていてください。
1780 {{{
1781 &lt;例2&gt;
1782 #include &lt;psychlops.h&gt;
1783 using namespace Psychlops;
1784
1785 void psychlops_main() {
1786
1787         Canvas sampleA(Canvas::fullscreen);
1788         sampleA.clear(Color::white,Canvas::FRONT);
1789         while(!Input::get(Keyboard::spc));
1790 }
1791 }}}
1792
1793 !Canvas::flip() -画面を入れ替える-
1794 上の例では、オプションを使って現在表示されている画面に対して描画を実行しました。
1795 しかし、これは例外的な描画で、Psychlopsでは描画処理は表示されていない画面(裏画面)に対して行われます。
1796 従って、描画内容を画面に反映させるためには、表示画面と裏画面を入れ替える必要があります。
1797 この表と裏の2つの画面を入れ替え処理を行う命令がCanvas::flip()命令です。
1798 つまり、描画内容はCanvas::flip() 処理で画面を入れ替えることで初めて画面上に表示されます。
1799 この命令は画面の垂直同期信号(=ビデオカードが1枚の画面を書ききったことを示す信号; Vsync)に同期して、画面の切り替えを行います。
1800 * 参考 [[Tips: Canvas::flip()とコマ落ち]]
1801
1802 |!Canvas::flip()|flip(int)|int:入力値のリフレッシュ分描画内容を表示します|
1803 |~|~|引数を入力しない場合(flip())、flip(1)が指定されます|
1804 |~|flipAfter(int wait)|int wait回のリフレッシュ後まで待ってからflip()を実行して描画内容を画面に反映させます|
1805 |~|flipAndWait(int wait)|もっとも近い垂直同期のタイミングでflip()を実行した後に、int wait回ののリフレッシュ分の表示を予約します。次にflip()命令がコールされても、int wait回ののリフレッシュ分が経過するまでは何も起こりません(flip()と全く同じ効果ですが、引数は省略不可)|
1806
1807 flip()系の命令は画面描画の時間制御をする上でもっとも重要な命令です。上の表だけではわかりにくいでしょうから、図と実例を用いて各命令の働きをもう少し詳しく見ていきましょう。
1808
1809 flipAfter(), flipAndWait()命令の動きを図解すると以下のようになります。以下の図ではflipAfter(3)とflip(3)(あるいはflipAndWait(3))の場合を例としてあげました。
1810 [img[image/psychlops_flip.png]]
1811
1812 !!Canvas::flip()の書き方1
1813 {{{
1814 &lt;例3&gt;
1815 #include &lt;psychlops.h&gt;
1816 using namespace Psychlops;
1817
1818 void psychlops_main() {
1819
1820         Canvas sampleA(Canvas::fullscreen);
1821         sampleA.clear(Color::blue,Canvas::BACK);
1822         while(!Input::get(Keyboard::spc));
1823         sampleA.flip();
1824 }
1825 }}}
1826 例2では今ディスプレイに表示されている画面(紙の表)の色を塗りつぶしましたが
1827 今回は表示されていない画面(紙の裏)を塗りつぶし、それをflip()命令で表画面に入れ替えました。
1828
1829 !!Canvas::flip()の書き方2
1830 次は、flip()が実行されるタイミングを制御してみましょう。
1831 flip()命令の引数を省略すると、flip()命令は、描画の計算が終了した時点からもっとも早い同期タイミングで画面の切り替えを行います。
1832
1833 {{{
1834 &lt;例4&gt;
1835 #include &lt;psychlops.h&gt;
1836 using namespace Psychlops;
1837
1838 void psychlops_main() {
1839         Canvas sampleA(Canvas::fullscreen);
1840         sampleA.clear(Color::blue,Canvas::BACK);
1841         sampleA.flip(60);
1842         sampleA.clear(Color::cyan,Canvas::BACK);
1843         sampleA.flip(60);
1844         while(!Input::get(Keyboard::spc));
1845 }
1846 }}}
1847 青→シアンに画面の色が塗りつぶされるのを確認できたでしょうか。
1848 次はflipAfter()で同様のソースを実行します。
1849 {{{
1850 &lt;例5&gt;
1851 #include &lt;psychlops.h&gt;
1852 using namespace Psychlops;
1853
1854 void psychlops_main() {
1855         Canvas sampleA(Canvas::fullscreen);
1856         sampleA.clear(Color::blue,Canvas::BACK);
1857         sampleA.flipAfter(60);
1858         sampleA.clear(Color::cyan,Canvas::BACK);
1859         sampleA.flipAfter(60);
1860         while(!Input::get(Keyboard::spc));
1861 }
1862 }}}
1863 &lt;例4&gt;と&lt;例5&gt;の結果の差が見えたでしょうか。
1864 &lt;例4&gt;は60フレームのリフレッシュ分描画内容を表示したのに対し
1865 &lt;例5&gt;は60フレームのリフレッシュ分後、描画内容を表示しました。
1866 &lt;例6&gt;はflipAndWait()による画面入替えの確認をします。
1867 こちらはflip();と同様の結果を返します。
1868 &lt;例4&gt;と同じ結果が返るか確認してください。
1869 {{{
1870 &lt;例6&gt;
1871 #include &lt;psychlops.h&gt;
1872 using namespace Psychlops;
1873
1874 void psychlops_main() {
1875         Canvas sampleA(Canvas::fullscreen);
1876    sampleA.clear(Color::blue,Canvas::BACK);
1877         sampleA.flipAndWait(60);
1878         sampleA.clear(Color::cyan,Canvas::BACK);
1879         sampleA.flipAndWait(60);
1880         while(!Input::get(Keyboard::spc));
1881 }
1882 }}}</pre>
1883 </div>
1884 <div title="2.1.3 Canvasのメンバ取得と変数の表示" modifier="Psychlops_DevelopperG" modified="200910080827" created="200708240242" changecount="29">
1885 <pre>!Canvas::getXXXX()
1886 Psychlopsではより複雑な描画に必要なCanvasの様々な画面情報を、Canvas::getXXXX()命令を使って取得します(XXXXにはそれぞれの情報に対応した名前が入ります)。
1887 |!Canvas::getXXXX()|getCenter()|画面の中心座標(x,y)を取得します|
1888 |~|getHcenter()|横方向の中心座標(x)を取得します|
1889 |~|getVcenter()|縦方向の中心座標(y)を取得します|
1890 |~|getHeight()|画面の高さ(ピクセル)を取得します|
1891 |~|getWidth()|画面の幅(ピクセル)を取得します|
1892 |~|getColorDepth()|カラーモード(ビット)を取得します|
1893 |~|getRefreshRate()|リフレッシュレート(Hz)を取得します|
1894
1895 [img[image/canvasget.png]]
1896
1897 !!Canvas::getXXXX()の書き方
1898 実際に取得してみます。
1899 {{{
1900 &lt;例1&gt;
1901 #include &lt;psychlops.h&gt;
1902 using namespace Psychlops;
1903
1904 void psychlops_main() {
1905
1906         Canvas sampleA(Canvas::fullscreen);
1907         sampleA.getCenter();
1908         sampleA.getHcenter();
1909         sampleA.getVcenter();
1910         sampleA.getHeight();
1911         sampleA.getWidth();
1912         sampleA.getColorDepth();
1913         sampleA.getRefreshRate();
1914
1915                 
1916         sampleA.flip();
1917         while(!Input::get(Keyboard::spc));
1918 }
1919 }}}
1920 このコードを実行するだけでは、取得結果を確認できません。
1921 次に取得結果を確認するため、画面に取得した変数の値を表示してみましょう。
1922
1923 !Canvas::var()とCanvas::msg()
1924 取得した変数の値を画面に表示するにはCanvas::var()命令を使います。
1925 |!Canvas::var()|var(string str, double x, double y,[[Psychlops::Color]] col, bool fillForward)|
1926 |~|~|&gt;|&gt;|string str|画面表示をする変数を指定|
1927 |~|~|&gt;|&gt;|double x|画面表示をする座標(x)を指定|
1928 |~|~|&gt;|&gt;|double y|画面表示をする座標(y)を指定|
1929 |~|~|&gt;|&gt;|[[Psychlops::Color]] col|描画する点の色を指定(設定しない場合、白が設定される)|
1930 |~|~|~|bool fillForward|表示位置の指定|true |指定座標(x,y)から左部分に表示する|
1931 |~|~|~|~|~|false |指定座標(x,y)から右部分に表示する|
1932 |~|~|~|~|&gt;|&gt;|(指定しない場合、falseが設定される)|
1933
1934
1935 さらに、わかりやすくするためにラベルをつけましょう。
1936 定型の短い文章を画面上に表示する方法としてCanvas::msg()命令があります。
1937 |!Canvas::msg()|msg(char* string,double x,double y,[[Psychlops::Color]] col)|char* stringを座標(x,y)に指定色で表示します|
1938 |~|~|schar* string:画面表示をする文字列を指定|
1939 |~|~|double x:画面表示をする座標(x)を指定|
1940 |~|~|double y:画面表示をする座標(y)を指定|
1941 |~|~|[[Psychlops::Color]] col:描画する点の色を指定(設定しない場合、白が設定される)|
1942 |~|msg(string str,double x,double y,[[Psychlops::Color]] col)|string strを座標(x,y)に指定色で表示します|
1943 |~|~|string str:画面表示をする文字列を指定|
1944 |~|~|double x:画面表示をする座標(x)を指定|
1945 |~|~|double y:画面表示をする座標(y)を指定|
1946 |~|~|[[Psychlops::Color]] col:描画する点の色を指定(設定しない場合、白が設定される)|
1947
1948 |[img[image/canvasvar.png]]|[img[image/canvasmsg.png]]
1949
1950 !!Canvas::var()の書き方
1951 {{{
1952 &lt;例2&gt;
1953 #include &lt;psychlops.h&gt;
1954 using namespace Psychlops;
1955
1956 int a1,a2,a3,a4,a5;
1957 double d1,d2,d3,x,y;
1958 Psychlops::Point point1;
1959
1960 void psychlops_main() {
1961
1962         Canvas sampleA(Canvas::fullscreen);
1963         
1964         point1=sampleA.getCenter();
1965         d1=sampleA.getHcenter();
1966         d2=sampleA.getVcenter();
1967         a1=sampleA.getHeight();
1968         a2=sampleA.getWidth();
1969         a3=sampleA.getColorDepth();
1970         d3=sampleA.getRefreshRate();
1971         
1972         //get命令の内容を画面表示する
1973         x=point1.getX();
1974         y=point1.getY();
1975         sampleA.msg(&quot;getcenter_x:&quot;,50,200,Color::yellow);
1976         sampleA.var(x,200,200,Color::yellow,true);//getCenter:X
1977         sampleA.msg(&quot;getcenter_y:&quot;,50,250,Color::yellow);
1978         sampleA.var(y,200,250,Color::yellow,true);//getCenter:Y         
1979         sampleA.msg(&quot;getHcenter:&quot;,50,300,Color::green);
1980         sampleA.var(d1,200,300,Color::green,true);//getHcenter
1981         sampleA.msg(&quot;getVcenter:&quot;,50,350,Color::green);
1982         sampleA.var(d2,200,350,Color::green,true);//getVcenter
1983         sampleA.msg(&quot;gettHeight:&quot;,50,400,Color::red);
1984         sampleA.var(a1,200,400,Color::red,false);//getHeight
1985         sampleA.msg(&quot;getWidth:&quot;,50,450,Color::red);
1986         sampleA.var(a2,200,450,Color::red,true);//getWidth
1987
1988         sampleA.msg(&quot;getColorDepth:&quot;,250,200);
1989         sampleA.var(a3,500,200);//getColorDepth
1990         sampleA.msg(&quot;getRefreshRate:&quot;,250,250);
1991         sampleA.var(d3,500,250);//getRefreshRate
1992         
1993         sampleA.flip();
1994         while(!Input::get(Keyboard::spc));
1995 }
1996 }}}
1997 このコードを実行すると、宣言されたCanvasの各属性値が画面上に表示されます。</pre>
1998 </div>
1999 <div title="2.2 デモ環境の表示" modifier="Psychlops_DevelopperG" modified="200908190221" created="200710220134" changecount="3">
2000 <pre>Independentクラスを使って、デモ環境を自動的に構成することが出来ます。
2001 以下では具体例を挙げて簡単なデモ環境の構築を説明します。
2002
2003 1章で述べたやり方に従って、新規プロジェクトを作成すると、cppファイルの中に自動的にPsychlopsのデモプログラムが
2004 書き込まれています。このデモプログラムはIndependentクラスを用いた簡単なデモンストレーションのソースファイルになっています。ここでは、このデモプログラムを題材にIndependentを用いたデモ環境の構築について説明します。
2005 [[Psychlopsプログラムのデフォルトテンプレート]]
2006
2007 [[2.2.1 プログラムの基本構造]]
2008 [[2.2.2 デモ環境に使う描画命令の設定]]
2009 [[2.2.3 デモ環境の実行]]</pre>
2010 </div>
2011 <div title="2.2 図形描画のコマンド" modifier="Psychlops_DevelopperG" modified="200908190227" created="200708212305" changecount="14">
2012 <pre>[[2.1|2.1 描画領域の宣言]]節はCanvasそのものについて説明しました。
2013 本節ではCanvas上に点や線、図形を描画します。
2014
2015 [[2.2.1 画面上に点を描画する-Pointクラス1-]]           
2016 [[2.2.2 画面上に線を描画する]]           
2017 [[2.2.3 画面上に四角形を描画する-Rectangleクラス1-]]            
2018 [[2.2.4 画面上に円を描画する]]            
2019 [[2.2.5 描画する図形の色を指定する-Colorクラス-]]           
2020 [[2.2.6 画面上に縞を描画する-Rectangleクラス2-]]
2021 [[2.2.7 画面上に縞を描画する-Pointクラス2-]] 
2022 [[2.2.8 運動する図形を描画する-Rectangleクラス3-]] </pre>
2023 </div>
2024 <div title="2.2.1 プログラムの基本構造" modifier="Psychlops_DevelopperG" modified="200908190218" created="200711072356" changecount="1">
2025 <pre>このデモプログラムは大きく分けて2つの部分からなっています。
2026 {{{
2027 void RectLuminance() {
2028         double rect_size = 100;
2029         double rect_lum  = 0.5;
2030         double bg_lum    = 0.2;
2031
2032         Psychlops::Rectangle rect(rect_size,rect_size);
2033         rect.centering();
2034
2035         Range rng;
2036         Independent &lt;&lt; rect_size | &quot;Rect Size&quot;      |   1&lt; rng&lt; 500 | 10.0 | 2.0 ;
2037         Independent &lt;&lt; rect_lum  | &quot;Rect Luminance&quot; | 0.0&lt;=rng&lt;=1.0 |  0.1 | 0.05;
2038         Independent &lt;&lt; bg_lum    | &quot;BG Luminance&quot;   | 0.0&lt;=rng&lt;=1.0 |  0.1 | 0.05;
2039
2040         while(!Input::get(Keyboard::esc)) {
2041                 Display::clear(Color(bg_lum));
2042                 rect.resize(rect_size,rect_size);
2043                 rect.display(rect_lum);
2044                 Display::flip();
2045         }
2046 }
2047 }}}
2048 の部分と
2049 {{{
2050 void psychlops_main() {
2051         Canvas display(Canvas::fullscreen);
2052
2053         Procedure p;
2054         p.setDesign(Procedure::DEMO);
2055         p.setProcedure(RectLuminance);
2056         p.run();
2057 }
2058 }}}
2059 の部分です。
2060
2061 ぱっと見てわかることは、Psychlops_mainの中に描画命令が存在しないことでしょう。実際の描画命令はPsychlops_main()の上にある~RectLuminance()という関数の中にかかれています。
2062
2063 代わりに個々まで一度も触れられていないようなProcedureと言うクラスのインスタンスが宣言されて、これに対してsetDesign(), setProcedure(), run()といったメソッドがかかれています。
2064 実はデモを構築する時には、このProcedureクラスの詳細については理解する必要はありません。
2065 setProcedure()の引数としてかかれている文字列が、実際の描画を行う関数としてプログラム中にかかれていれば、後の部分は変更しなくてもデモ環境を構築することができます。
2066 つまり、もし実際の描画命令を書く関数の名前が~DemoDraw()であったとすると、この描画内容を使ったデモンストレーションを構築するためには、
2067 {{{
2068 void DemoDraw(){
2069        ......
2070 }
2071 void psychlops_main() {
2072         Canvas display(Canvas::fullscreen);
2073
2074         Procedure p;
2075         p.setDesign(Procedure::DEMO);
2076         p.setProcedure(DemoDraw);
2077         p.run();
2078 }
2079 }}}
2080 と書けば良いと言うことです。デフォルトのテンプレートと比較して、p.setProcedure()の行だけが異なっていることに注意してください。</pre>
2081 </div>
2082 <div title="2.2.1 画面上に点を描画する-Pointクラス1-" modifier="Psychlops_DevelopperG" modified="200910080836" created="200708212310" changecount="29">
2083 <pre>!Canvas::pix()
2084 Canvas上に点を描画するための命令です。
2085 Canvasに点や線等を描画するときに必要なのは「どこに」「何色」のものを描画するか、です。
2086 ここでの「どこが」が点を描画する「座標」、「何色」が「色の指定」となります。
2087 座標はCanvasの左上端が座標の基点(x=0,y=0)となっています。
2088 色の指定についての詳細は[[2.2.5 描画する図形の色を指定する-Colorクラス-]]を参照してください。ここでは、必要最小限の命令のみを使用・説明します。
2089
2090 |!Canvas::pix()|pix(double x,double y,[[Psychlops::Color]] col) |&gt;|!座標(x,y)に指定色の点を描画します&lt;例1&gt;|
2091 |~|~|double x|描画する点の座標軸xを指定|
2092 |~|~|double y|描画する点の座標軸yを指定|
2093 |~|~|[[Psychlops::Color]] col|描画する点の色を指定|
2094 |~|pix([[Psychlops::Point]] point,[[Psychlops::Color]] col) |&gt;|!Point座標(x,y)に指定色の点を描画します^^*1^^&lt;例4&gt;|
2095 |~|~|[[Psychlops::Point]] point |座標(x,y)を指定|
2096 |~|~|[[Psychlops::Color]] col|描画する点の色を指定|
2097 |~|pix(int dotsCnt,double* xArray,double* yArray),[[Psychlops::Color]]* colArray)|&gt;|!配列の座標(x[],y[])に指定色(配列を用いて複数色指定)の点を描画します&lt;例2&gt;|
2098 |~|~|int dotsCnt|描画する点の数を指定(後ろの引数の配列の個数を超えてはいけない)|
2099 |~|~|double* xArray|描画する点の座標軸xが格納された配列名(ポインタ)を指定|
2100 |~|~|double* yArray|描画する点の座標軸yが格納された配列名(ポインタ)を指定|
2101 |~|~|[[Psychlops::Color]]* colArray|描画する点の色が格納された配列名(ポインタ)を指定|
2102 |~|pix(int dotsCnt,double* xArray,double* yArray,[[Psychlops::Color]] col)|&gt;|!配列の座標(x[],y[])に指定色(一色のみ)の点を描画します&lt;例3&gt;|
2103 |~|~|int dotsCnt|描画する点の数(配列数)を指定|
2104 |~|~|double* xArray|描画する点の座標軸xが格納された配列名(ポインタ)を指定|
2105 |~|~|double* yArray|描画する点の座標軸yが格納された配列名(ポインタ)を指定|
2106 |~|~|[[Psychlops::Color]] col|描画する点の色を指定|
2107 |~|pix(int dotsCnt,[[Psychlops::Point]]* pointArray,[[Psychlops::Color]]* colArray)|&gt;|!配列Point座標(x[],y[])に指定色の点を描画します^^*1^^&lt;例5&gt;|
2108 |~|~|int dotsCnt|描画する点の数(配列数)を指定|
2109 |~|~|[[Psychlops::Point]]* pointArray |配列の座標(x[],y[])が格納された配列名(ポインタ)を指定|
2110 |~|~|[[Psychlops::Color]]* colArray|描画する点の色が格納された配列名(ポインタ)を指定|
2111 ^^*1 次のPoint::set()で説明します。^^
2112
2113 [img[image/canvaspix.png]]
2114
2115 !!Canvas::pix()の書き方
2116 座標(50,50)に青の点を描画します。
2117 {{{
2118 &lt;例1&gt;
2119 #include &lt;psychlops.h&gt;
2120 using namespace Psychlops;
2121
2122 void psychlops_main() {
2123
2124         Canvas sampleA(Canvas::fullscreen);
2125         while(!Input::get(Keyboard::spc)){
2126                 sampleA.pix(50,50,Color::blue);
2127                 sampleA.flip();
2128         }
2129 }
2130 }}}
2131 次は一命令で複数の異なる輝度の点を描画します。一つの命令を繰り返し実行する[[for文|1.6.4 for命令]]を使用しなくても複数個の点が描画される点に注目してください。
2132 {{{
2133 &lt;例2&gt;
2134 #include &lt;psychlops.h&gt;
2135 using namespace Psychlops;
2136
2137 //色の配列を宣言. 輝度値は0.0-1.0の範囲で指定
2138 Psychlops::Color colArray[3]={1.0,0.5,0.25};
2139 //X座標の配列を宣言
2140 double xArray[3] = {50,100,150};
2141 //Y座標の配列を宣言
2142 double yArray[3] = {50,50,50};
2143
2144 void psychlops_main() {
2145
2146         Canvas sampleA(Canvas::fullscreen);
2147         sampleA.pix(3,xArray,yArray,colArray);
2148         sampleA.flip();
2149         while(!Input::get(Keyboard::spc));
2150 }
2151 }}}
2152
2153 次は&lt;例2&gt;と同様に一命令で複数の点を描画しますが、全点の色は同一です。
2154 {{{
2155 &lt;例3&gt;
2156 #include &lt;psychlops.h&gt;
2157 using namespace Psychlops;
2158
2159 //X座標の配列を宣言
2160 double xArray[3] = {50,100,150};
2161 //Y座標の配列を宣言
2162 double yArray[3] = {50,50,50};
2163
2164 void psychlops_main() {
2165
2166         Canvas sampleA(Canvas::fullscreen);
2167         sampleA.pix(3,xArray,yArray,Psychlops::Color(1.0,0.0,0.0));//3つめの引数は描画色に赤を指定
2168         sampleA.flip();
2169         while(!Input::get(Keyboard::spc));
2170 }
2171 }}}
2172
2173 @@◎カラーについて@@
2174 &lt;例1&gt;~&lt;例3&gt;で使用している[[Psychlops::Color]]型について簡単に説明します。
2175 PsychlopsではRGB,GRAY(輝度)の色を描画することが可能です。
2176 &lt;例1&gt;で青の点を描画するため「Color::blue」という命令を使用しています。
2177 Psychlopsではいくつかのよく使用する色をあらかじめ予約語として設定してあります。
2178 これらのプリセットされた色は「Color::色名」と記述することで、使用することができます。
2179 あらかじめプリセットされた色については[[Color::XXXX]]を参照してください。
2180 &lt;例2&gt;では輝度値を使用しています。
2181 輝度値は0.0(黒)~1.0(白)の範囲で指定されています。
2182 Psychlops::Color colArray[3]={1.0,0.5,0.25};
2183 の命令でそれぞれの点に輝度値が設定されました。
2184 &lt;例3&gt;では赤の点を描画しています。
2185 Psychlops::Color型の変数に3つの引数を指定したときは、それぞれの値が順に赤、緑、青の各画素の輝度の指定として解釈されます。
2186 red,green,blueはそれぞれ0.0~1.0の範囲で指定されます。
2187 この例では[[Psychlops::Color]](red=1.0,green=0.0,blue=0.0)と指定することで赤が設定されました。
2188
2189 !Point::set()
2190 今まで座標はそれぞれx,y座標の変数を作成して値を設定していました。
2191 今回はPointクラスを使用して座標(x,y),ないし座標X,Yを指定し点を描画します。
2192 Point::set()を使用し座標を取得することでソースの簡略化がなされ
2193 また、修正がしやすくなります。
2194
2195 |!Point::set()|set(double x, double y)|座標(x,y)の値を設定します|
2196 |~|~|double x:座標xを指定|
2197 |~|~|double y:座標yを指定|
2198 |~|setX(double val)|X座標の値を設定します|
2199 |~|~|double val :座標xを指定|
2200 |~|setY(double val)|Y座標の値を設定します|
2201 |~|~|double val :座標yを指定|
2202
2203 !!Point::set()の書き方
2204 座標(50,50)に青の点を描画します。
2205 &lt;例1&gt;との差分は、直接座標を記述するかPoint::set()で設定するか、だけです。
2206 {{{
2207 &lt;例4&gt;
2208 #include &lt;psychlops.h&gt;
2209 using namespace Psychlops;
2210
2211 //Point型を宣言
2212 Psychlops::Point point1;
2213
2214 void psychlops_main() {
2215
2216         Canvas sampleA(Canvas::fullscreen);
2217         //座標(X,Y)を設定
2218         point1.set(50,50);
2219         sampleA.pix(point1,Psychlops::Color(0.0,0.0,1.0));
2220         sampleA.flip();
2221         while(!Input::get(Keyboard::spc));
2222 }
2223 }}}
2224 次はX,Y座標それぞれ座標を取得し、一命令で複数の点を描画します。
2225 &lt;例2&gt;との差分は、直接座標を記述するかPoint::set()で設定するか、だけです。
2226 {{{
2227 &lt;例5&gt;
2228 #include &lt;psychlops.h&gt;
2229 using namespace Psychlops;
2230
2231 //色の配列を宣言
2232 Psychlops::Color colArray[3]={1.0,0.5,0.25};
2233 //座標(X,Y)の配列を宣言
2234 Psychlops::Point pointArray[3];
2235
2236 void psychlops_main() {
2237
2238         Canvas sampleA(Canvas::fullscreen);
2239         //座標(X[],Y[])にそれぞれ値((50,50),(100,50),(150,50)を設定
2240         for(int i=0; i&lt;3; i++){
2241                 pointArray[i].setX((i+1)*50);
2242                 pointArray[i].setY(50);
2243         }
2244
2245         sampleA.pix(3,pointArray,colArray);
2246         sampleA.flip();
2247         while(!Input::get(Keyboard::spc));
2248 }
2249 }}}
2250
2251 !Point::centering()
2252 今度は点を画面中心に描画してみましょう。
2253 画面の大きさは環境により変化します。
2254 今までの命令では画面中心に点を描画することは難しいです。
2255 そこでPoint::centering()を使用します。
2256 この命令を使用することで、点を自動的に画面中心に移動します。
2257
2258 |!Point::centering()|centering()|座標(x,y)の点を画面の中心に移動します|
2259 |~|centering(double h, double v)|任意の座標(h,v)に点を移動します|
2260 |~|~|double h:移動するx軸の値を指定|
2261 |~|~|double v:移動するy軸の値を指定|
2262
2263 !Point::centering()の書き方
2264 {{{
2265 &lt;例6&gt;
2266 #include &lt;psychlops.h&gt;
2267 using namespace Psychlops;
2268
2269 //座標(X,Y)を宣言
2270 Psychlops::Point point1;
2271
2272 void psychlops_main() {
2273
2274         Canvas sampleA(Canvas::fullscreen);
2275         //座標(X,Y)を画面中心に移動
2276         point1.centering();
2277
2278         //点を描画する
2279         sampleA.pix(point1,Color::red);
2280         sampleA.flip();
2281         while(!Input::get(Keyboard::spc));
2282 }
2283 }}}
2284
2285 !Point::shift()
2286 この命令もPoint::centering()と同様に点に対する移動命令です。
2287 元の座標(x,y)を基点に入力値(h,v)だけ移動します。
2288
2289 |!Point::shift()|shift(double h, double v)|座標(X,Y)を(h,v)だけ移動します|
2290 |~|~|double h:右水平方向に移動する値を指定|
2291 |~|~|double v:下垂直方向に移動する値を指定|
2292
2293 [img[image/pointshift.png]]
2294
2295 !!Point::shift()の書き方
2296 &lt;例6&gt;を元に画面中央から(100,100)移動した青の点を描画します。
2297 {{{
2298 &lt;例7&gt;
2299 #include &lt;psychlops.h&gt;
2300 using namespace Psychlops;
2301
2302 //座標(X,Y)を宣言
2303 Psychlops::Point point1,point2;
2304
2305 void psychlops_main() {
2306
2307         Canvas sampleA(Canvas::fullscreen);
2308         //座標(X,Y)を画面中心に移動
2309         point1.centering();
2310         //座標(X,Y)を画面中心に移動し、更に(100,100)移動する
2311         point2.centering().shift(100,100);
2312
2313         //点を描画する
2314         sampleA.pix(point1,Color::red);
2315         sampleA.pix(point2,Color::blue);
2316         sampleA.flip();
2317         while(!Input::get(Keyboard::spc));
2318 }
2319 }}}
2320 画面中央の赤色の点(point1)と画面中央の右下に描画された青色の点(point2)が確認できたでしょうか。
2321 </pre>
2322 </div>
2323 <div title="2.2.2 デモ環境に使う描画命令の設定" modifier="Psychlops_DevelopperG" modified="200908190219" created="200711080010" changecount="2">
2324 <pre>さて、次に実際に描画を行っている関数~RectLuminance()について見てみましょう。[[2.2.1節|2.2.1 プログラムの基本構造]]で触れたとおり、デフォルトのデモプログラムでは、デモをする本体がpsychlops_mainとは別の関数になっています。
2325
2326 {{{
2327 void RectLuminance() {
2328         double rect_size = 100;
2329         double rect_lum  = 0.5;
2330         double bg_lum    = 0.2;
2331
2332         Psychlops::Rectangle rect(rect_size,rect_size);
2333         rect.centering();
2334
2335         Range rng;
2336         Independent &lt;&lt; rect_size | &quot;Rect Size&quot;      |   1&lt; rng&lt; 500 | 10.0 | 2.0 ;
2337         Independent &lt;&lt; rect_lum  | &quot;Rect Luminance&quot; | 0.0&lt;=rng&lt;=1.0 |  0.1 | 0.05;
2338         Independent &lt;&lt; bg_lum    | &quot;BG Luminance&quot;   | 0.0&lt;=rng&lt;=1.0 |  0.1 | 0.05;
2339
2340
2341         while(!Input::get(Keyboard::esc)) {
2342                 Display::clear(Color(bg_lum));
2343                 rect.resize(rect_size,rect_size);
2344                 rect.display(rect_lum);
2345                 Display::flip();
2346         }
2347
2348 }
2349 }}}
2350
2351 現段階ではこのデモ環境に使う描画関数は引数をとることができません。同様に値を返すこともできないので、かならずvoid XXXXXXX()という形で宣言が行われることになります。[[2.2.1節|2.2.1 プログラムの基本構造]]でも触れたとおり、名前は何でもかまいません。
2352
2353 次に中身を見てみると、はじめに変数の宣言とIndependentクラスへの変数の登録が行われています。
2354 この部分は、[[前節2.1|2.1 独立変数Independent]]で上げた例と全く同じです。良くわからなくなったら[[2.1節|2.1 独立変数Independent]]も参照してみてください。
2355 {{{
2356         double rect_size = 100;
2357         double rect_lum  = 0.5;
2358         double bg_lum    = 0.2;
2359
2360         Range rng;
2361         Independent &lt;&lt; rect_size | &quot;Rect Size&quot;      |   1&lt; rng&lt; 500 | 10.0 | 2.0 ;
2362         Independent &lt;&lt; rect_lum  | &quot;Rect Luminance&quot; | 0.0&lt;=rng&lt;=1.0 |  0.1 | 0.05;
2363         Independent &lt;&lt; bg_lum    | &quot;BG Luminance&quot;   | 0.0&lt;=rng&lt;=1.0 |  0.1 | 0.05;
2364 }}}
2365
2366
2367 次の部分では、これまでにも見てきたようにwhile文を使ってループが設定され、その中に描画命令がかかれています。デモ実行は永久ループさせておきます。ここではESCキーを押すと終了するように設定されています。
2368 {{{
2369 while(!Input::get(Keyboard::esc)) {
2370                 Display::clear(Color(bg_lum));
2371                 rect.resize(rect_size,rect_size);
2372                 rect.display(rect_lum);
2373                 Display::flip();
2374         }
2375 }}}
2376
2377 ここでは、描画時に&quot;Display::&quot;命令が使われていることに注意してください。
2378 Canvasクラスの宣言はPsychlops_main()の中で行われるので、ここで宣言されたCanvas名displayを使うことはできません。たとえば、1行目はdisplay.clear(Color(bg_lum));と書いてしまうとコンパイル時にエラーとなってしまいます。そこでデフォルトのCanvasへの描画をするためのクラス&quot;Display::&quot;を用いて宣言されたCanvasへの描画を行っています。</pre>
2379 </div>
2380 <div title="2.2.2 画面上に線を描画する" modifier="Psychlops_DevelopperG" modified="200910080837" created="200708230440" changecount="11">
2381 <pre>!Canvas::line()
2382 Canvas上に線を描画するためにはCanvas::line()命令を使います。
2383
2384 |!Canvas::line()|line(double x1, double y1, double x2, double y2, [[Psychlops::Color]] col)|&gt;|!開始座標(X1,Y1)から終端座標(X2,Y2)まで指定色の線を描画します|
2385 |~|~|double x1|描画する線の開始座標x1を指定|
2386 |~|~|double y1|描画する線の開始座標y1を指定|
2387 |~|~|double x2|描画する線の終端座標x2を指定|
2388 |~|~|double y2|描画する線の終端座標y2を指定|
2389 |~|~|[[Psychlops::Color]] col|描画する点の色を指定|
2390 |~|line([[Psychlops::Point]] point1, [[Psychlops::Point]] point2,[[Psychlops::Color]] col)|&gt;|!開始Point座標(x1,x1)から終端Point座標(X2,Y2)まで指定色の線を描画します|
2391 |~|~|[[Psychlops::Point]] point1|開始座標(x1,y1)を指定|
2392 |~|~|[[Psychlops::Point]] point2|終端座標(x2,y2)を指定|
2393 |~|~|[[Psychlops::Color]] col|描画する点の色を指定|
2394 画面上に線を描画するためには、「どこから」「どこに」「何色」の線を引くのかの指定が必要です。
2395 「どこから」は開始座標(x1,y1)、「どこに」は終端座標(x2,y2)、「何色」は[[Psychlops::Color]]型で指定します。
2396 座標はCanvasの左上端が座標の基点(x=0,y=0)となっています。
2397 これらの座標はPoint型の変数を使用して指定することもできます。
2398
2399 [img[image/canvasline.png]]
2400
2401 !!Canvas::line()の書き方
2402 座標(100,100)から(300,300)まで赤い線を描画します。
2403 {{{
2404 &lt;例1&gt;
2405 #include &lt;psychlops.h&gt;
2406 using namespace Psychlops;
2407
2408 void psychlops_main() {
2409
2410         Canvas sampleA(Canvas::fullscreen);
2411         sampleA.line(100, 100,300,300,Color::red);
2412         sampleA.flip();
2413         while(!Input::get(Keyboard::spc));
2414 }
2415 }}}
2416 次に[[Psychlops::Point]]型を使用して黄色の線を描画します。
2417 {{{
2418 &lt;例2&gt;
2419 #include &lt;psychlops.h&gt;
2420 using namespace Psychlops;
2421
2422 Psychlops::Point point1,point2;
2423
2424 void psychlops_main() {
2425
2426         Canvas sampleA(Canvas::fullscreen);
2427         //座標(x,y)を設定
2428         point1.set(100,100);
2429         point2.set(300,300);
2430         //線を描画
2431         sampleA.line(point1,point2,Color::yellow);;
2432         sampleA.flip();
2433         while(!Input::get(Keyboard::spc));
2434 }
2435 }}}</pre>
2436 </div>
2437 <div title="2.2.3 デモ環境の実行" modifier="Psychlops_DevelopperG" modified="200908190221" created="200712061912" changecount="1">
2438 <pre>[[2.2.1節|2.2.1 プログラムの基本構造]]、[[2.2.2節|2.2.2 デモ環境に使う描画命令の設定]]で作成したプログラムを実行すると、デモ環境を使用したデモを使うことができます。
2439
2440 画面左上にデモ環境コンソールが表示されています。コンソールには、Independentで指定した変数が列挙されています。
2441
2442 [img[部分行列|image/console_1.png]]
2443
2444 コンソールで明るく表示されている変数が変更対象となる(アクティブな)変数です。アクティブな変数は上下(↓↑)キーで切り替えることができます。
2445
2446 [img[部分行列|image/console_2.png]]
2447
2448
2449 コンソールでは、左右キーで変数の値を変更することができます(減←、→増)。shiftキーを押しながら←→キーを押すと、変量が変わります。
2450 [img[部分行列|image/console_3.png]]
2451
2452 [img[部分行列|image/console_4.png]]</pre>
2453 </div>
2454 <div title="2.2.3 画面上に四角形を描画する-Rectangleクラス1-" modifier="Psychlops_DevelopperG" modified="200912010832" created="200708231915" changecount="24">
2455 <pre>PsychlopsでCanvasと並んで重要なクラスにRectangleクラスがあります。
2456 RectangleはCanvas上に四角形の領域を設定する命令です。
2457 この領域の設定はコピーやペーストのために使用することもできますし、領域自体を塗りつぶすことで四角形を表示することもできます。
2458 PsychlopsにおけるCanvas上の描画はほとんどこのRectangleクラスを使用して行うことになります。
2459
2460 !Rectangle::set()
2461 Rectangle::set()は四角形の領域を設定します。
2462 領域の設定は、四角形の左上座標(x1,y1)と四角形の右下座標(x2,y2)を指定することで行います。
2463 また色指定は[[Psychlops::Color]]型で指定します。
2464
2465 |!Rectangle::set()|set(double width, double height)|&gt;|!width×heightの四角形を設定します|
2466 |~|~|double width|描画する四角形の横幅|
2467 |~|~|double height|描画する四角形の縦幅|
2468 |~|set(double l, double t, double r, double b)|&gt;|!左上座標(l,t)から右下座標(r,b)までの四角形を設定します|
2469 |~|~|double l|描画する四角形の左上座標x1を指定|
2470 |~|~|double t|描画する四角形の左上座標y1を指定|
2471 |~|~|double r|描画する四角形の右下座標x2を指定|
2472 |~|~|double b|描画する四角形の右下座標y2を指定|
2473 |~|set([[Psychlops::Point]] point1, [[Psychlops::Point]] point2)|&gt;|!左上Point座標(x1,x1)から右下Point座標(X2,Y2)までの四角形を設定します|
2474 |~|~|[[Psychlops::Point]] point1|左上座標(x1,y1)を指定|
2475 |~|~|[[Psychlops::Point]] point2|右下座標(x2,y2)を指定|
2476
2477 [img[image/rectset.png]]
2478
2479 !Canvas::rect()
2480 画面上に四角形を描画するにはCanvas::rect()命令を使用します。
2481 Rectangle::set命令で四角形の領域設定が終わったらCanvas::rect命令で描画処理を行います。
2482 この二つの命令は四角形の描画において対になっているので併せて覚えてください。
2483
2484 |!Canvas::rect()|rect([[Psychlops::Rectangle]] rect, [[Psychlops::Color]] col)|&gt;|!四角形を指定色で描画します|
2485 |~|~|([[Psychlops::Rectangle]] rect|描画する四角形を指定|
2486 |~|~|[[Psychlops::Color]] col|描画する四角形の色を指定|
2487
2488 [img[image/canvasrect.png]]
2489
2490 !!Rectangle::set()とCanvas::rect()の書き方
2491 Rectangleを宣言して、画面上に四角形を描画して見ましょう。
2492 Rectangleを縦横のサイズのみを用いて宣言したときには、左上の座標は(0,0)に設定されています。
2493 下の例では、200×100ピクセルの白色の四角形を画面左上隅に描画します。
2494 {{{
2495 &lt;例1&gt;
2496 #include &lt;psychlops.h&gt;
2497 using namespace Psychlops;
2498 //Rectangleの宣言
2499 Psychlops::Rectangle rect1;
2500
2501 void psychlops_main() {
2502
2503         Canvas sampleA(Canvas::fullscreen);
2504         rect1.set(200,100);
2505         sampleA.rect(rect1,Color::white);
2506         sampleA.flip();
2507         while(!Input::get(Keyboard::spc));
2508 }
2509 }}}
2510 次に(50,100)と(100,150)を頂点とする大きさ50×50ピクセルの四角形を描画します。
2511 描画命令にはCanvas::rect()を使用し、色に黄色を指定しています。
2512 {{{
2513 &lt;例2&gt;
2514 #include &lt;psychlops.h&gt;
2515 using namespace Psychlops;
2516
2517 Psychlops::Rectangle rect1;
2518
2519 void psychlops_main() {
2520
2521         Canvas sampleA(Canvas::fullscreen);
2522         rect1.set(50,100,100,150);
2523         sampleA.rect(rect1,Color::yellow);
2524         sampleA.flip();
2525         while(!Input::get(Keyboard::spc));
2526 }
2527 }}}
2528 次に[[Psychlops::Point]]型を使用して頂点の座標を設定し、四角形を描画します。
2529 描画命令にはCanvas::rect()を使用し、色に緑を指定しています。
2530 {{{
2531 &lt;例3&gt;
2532 #include &lt;psychlops.h&gt;
2533 using namespace Psychlops;
2534
2535 Psychlops::Rectangle rect1;
2536 Psychlops::Point point1,point2;
2537
2538 void psychlops_main() {
2539
2540         Canvas sampleA(Canvas::fullscreen);
2541         //座標(x,y)を設定
2542         point1.set(100,100);
2543         point2.set(300,300);
2544         rect1.set(point1,point2);
2545         sampleA.rect(rect1,Color::green);
2546         sampleA.flip();
2547         while(!Input::get(Keyboard::spc));
2548 }
2549 }}}
2550
2551 ここまでは、基本的な四角形の設定について実行してきました。次に複数の同一四角形を描画します。
2552 複数の同一四角形を描画するのにそれぞれに対応したRectangleを宣言することもできますが、よりよい方法があります。
2553 一つだけRectangleを宣言して、これをシフトしながら描画する方法です。一つのはんこを紙の上に何度も押すようなものです。
2554 Psychlopsでは、Canvas::flip()が起こるまでは、画面上に描画内容が表示されませんから、このような方法を用いても複数の四角形が同時に画面に表示されます。
2555 &lt;例1&gt;の四角形を座標(0,0),画面中心,画面中心から(200,200)移動したところに四角形を描画してみましょう。
2556 {{{
2557 &lt;例4&gt;
2558 #include &lt;psychlops.h&gt;
2559 using namespace Psychlops;
2560
2561 Psychlops::Rectangle rect1,rect2,rect3;
2562
2563 void psychlops_main() {
2564
2565         Canvas sampleA(Canvas::fullscreen);
2566         rect1.set(200,100);
2567         rect2.set(rect1);
2568         rect3.set(rect1);
2569         rect2.centering();
2570         rect3.centering().shift(200,200);
2571         sampleA.rect(rect1,Color::white);
2572         sampleA.rect(rect2,Color::white);
2573         sampleA.rect(rect3,Color::white);
2574         sampleA.flip();
2575         while(!Input::get(Keyboard::spc));
2576 }
2577 }}}
2578 </pre>
2579 </div>
2580 <div title="2.2.4 画面上に円を描画する" modifier="Psychlops_DevelopperG" modified="200908190229" created="200708232150" changecount="15">
2581 <pre>Canvas上に円を描画します。
2582 座標はCanvasの左上端が座標の基点(x=0,y=0)となっています。
2583
2584 !Canvas::oval()
2585 Rectangleを使用してこれに内接するような円を描画することもできます。
2586 [[Rectangle.set()|2.2.3 画面上に四角形を描画する-Rectangleクラス1-]]命令で横軸と縦軸の直径を設定し、Canvas::oval()命令で円を描画します。
2587 色指定は[[Psychlops::Color]]型で指定します。
2588
2589 |!Canvas::oval()|oval([[Psychlops::Rectangle]] rect, [[Psychlops::Color]] col)|[[Psychlops::Rectangle]] rectで指定された直径の円を指定色で塗りつぶし描画します|
2590 |~|~|[[Psychlops::Rectangle]] rect:描画する円の直径|
2591 |~|~|[[Psychlops::Color]] col:描画する点の円を指定|
2592
2593 [img[image/canvasoval.png]]
2594
2595 !!Canvas::oval()の書き方
2596 Rectangleを宣言し、直径100ピクセルの白い円を描画します。
2597 {{{
2598 &lt;例1&gt;
2599 #include &lt;psychlops.h&gt;
2600 using namespace Psychlops;
2601
2602 Psychlops::Rectangle rect1;
2603
2604 void psychlops_main() {
2605
2606         Canvas sampleA(Canvas::fullscreen);
2607         rect1.set(100,100);
2608         sampleA.oval(rect1,Color::white);
2609         sampleA.flip();
2610         while(!Input::get(Keyboard::spc));
2611 }
2612 }}}</pre>
2613 </div>
2614 <div title="2.2.5 描画する図形の色を指定する-Colorクラス-" modifier="YourName" modified="200803100611" created="200708232340" changecount="24">
2615 <pre>今までCanvas、描画図形に色を塗りつぶしてきましたが、色の設定については詳しくは触れませんでした。
2616 ここでは色設定について詳しく説明します。
2617
2618 !Color::set()
2619 描画対象に対して色を設定します。
2620 描画対象はCanvas,点・線・四角形・多角形・円等多岐にわたります。
2621 色の設定はおおまかに2パターンあります。
2622 RGBと~GRAYLevel(輝度)です。
2623 RGBは色の三原色に基づいた赤・緑・青で構成されています。
2624 引数が3つ(4つ)あるColor::set()命令はRGB(A)指定になります。
2625 それぞれ値を0.0~1.0の範囲で持ち、3色のバランスで色を構成します。
2626 赤・緑・青の全ての値が0.0の場合は黒、全ての値が1.0の場合は白になります。
2627 4つめの引数(A)は色の透明度を決定します。0.0の場合は完全透過、1.0の場合は完全に不透明になります。省略したときはA=1.0(完全不透過)と解釈されます
2628 ~GRAYLevel(輝度)はグレースケールの輝度値を決定します。
2629 引数は1つで0.0~1.0の範囲を持ち、0.0の場合は黒、1.0の場合は白になります。
2630
2631 カラーで描画したい場合はRGB(A)
2632 白~黒を描画したい場合は~GRAYLevel
2633 と覚えておくとよいでしょう。
2634 |!Color::set()|set(double r, double g, double b, double a)|r,g,bと透明度aで構成されるカラー情報を設定します。|
2635 |~|~|double r:赤の輝度を指定。範囲は0.0~1.0|
2636 |~|~|double g:緑の輝度を指定。範囲は0.0~1.0|
2637 |~|~|double b:青の輝度を指定。範囲は0.0~1.0|
2638 |~|~|double a:透明度を指定。範囲は0.0~1.0 (0.0に近いほど透明度が高い)|
2639 |~|set(double r, double g, double b)|r,g,bで構成されるカラー情報を設定します。|
2640 |~|~|double r:赤の輝度を指定。範囲は0.0~1.0|
2641 |~|~|double g:緑の輝度を指定。範囲は0.0~1.0|
2642 |~|~|double b:青の輝度を指定。範囲は0.0~1.0|
2643 |~|set(double gray)|グレースケールの輝度を設定します。|
2644 |~|~|double gray: 輝度を指定。範囲は0.0~1.0|
2645
2646
2647 !!Color::set()の書き方
2648 RBGで様々な色を描画してみましょう。
2649 {{{
2650 &lt;例1&gt;
2651 #include &lt;psychlops.h&gt;
2652 using namespace Psychlops;
2653
2654 Psychlops::Rectangle rect1,rect2,rect3;
2655 Psychlops::Color col1,col2,col3;
2656
2657 void psychlops_main() {
2658
2659         Canvas sampleA(Canvas::fullscreen);
2660         sampleA.clear(Color(0.25,0.25,0.25));
2661         rect1.set(50,50);
2662         rect2.set(rect1);
2663         rect3.set(rect1);
2664         rect1.centering().shift(-100,0);
2665         rect2.centering();
2666         rect3.centering().shift(100,0);
2667         //色の設定
2668         col1.set(0.7,0.2,0.4);
2669         col2.set(0.5,0.8,1.0);
2670         col3.set(0.2,0.2,0.5);
2671         sampleA.rect(rect1,col1);
2672         sampleA.rect(rect2,col2);
2673         sampleA.rect(rect3,col3);
2674         sampleA.flip();
2675         while(!Input::get(Keyboard::spc));
2676 }
2677 }}}
2678 では次に透明度の値を変えたときにどう描画されるか確認しましょう。
2679 色は赤(固定)で透明度だけ0.0,0.5,1.0と変化させてみます。
2680 {{{
2681 &lt;例2&gt;
2682 #include &lt;psychlops.h&gt;
2683 using namespace Psychlops;
2684
2685 Psychlops::Rectangle rect1,rect2,rect3;
2686 Psychlops::Color col1,col2,col3;
2687
2688 void psychlops_main() {
2689
2690         Canvas sampleA(Canvas::fullscreen);
2691         sampleA.clear(Color(0.25,0.25,0.25));
2692         rect1.set(50,50);
2693         rect2.set(rect1);
2694         rect3.set(rect1);
2695         rect1.centering().shift(-100,0);
2696         rect2.centering();
2697         rect3.centering().shift(100,0);
2698         //色の設定
2699         col1.set(1.0,0.0,0.0,0.25);
2700         col2.set(1.0,0.0,0.0,0.5);
2701         col3.set(1.0,0.0,0.0,1.0);
2702         sampleA.rect(rect1,col1);
2703         sampleA.rect(rect2,col2);
2704         sampleA.rect(rect3,col3);
2705         sampleA.flip();
2706         while(!Input::get(Keyboard::spc));
2707 }
2708 }}}
2709 今度はグレースケールで描画してみましょう。
2710 {{{
2711 &lt;例3&gt;
2712 #include &lt;psychlops.h&gt;
2713 using namespace Psychlops;
2714
2715 Psychlops::Rectangle rect1,rect2,rect3;
2716 Psychlops::Color col1,col2,col3;
2717
2718 void psychlops_main() {
2719
2720         Canvas sampleA(Canvas::fullscreen);
2721         rect1.set(50,50);
2722         rect2.set(rect1);
2723         rect3.set(rect1);
2724         rect1.centering().shift(-100,0);
2725         rect2.centering();
2726         rect3.centering().shift(100,0);
2727         //色の設定
2728         col1.set(0.1);
2729         col2.set(0.5);
2730         col3.set(1.0);
2731         sampleA.rect(rect1,col1);
2732         sampleA.rect(rect2,col2);
2733         sampleA.rect(rect3,col3);
2734         sampleA.flip();
2735         while(!Input::get(Keyboard::spc));
2736 }
2737 }}}
2738
2739 !Canvas::setGammaValue()
2740 他の多くの描画ライブラリ同様、CRTの各画素における輝度レベルとPsychlopsで指定する輝度レベルは線形の関係を維持しません。
2741 これを簡易的に補正するために、R,G,Bの各色チャンネルについてGamma関数を用いた補正を行う方法が広く用いられています。
2742 Psychlopsでは、このGamma関数の係数を指定することで、Gamma補正された色を表示することが可能です。
2743 このための命令として、setGammaValue()命令があります。
2744
2745 |!Canvas::setGammaValue()|setGammaValue(double Cr, double Cg, double Cb)|この命令以降実行されるPsychlops::Colorに対してGamma補正を自動的に行います|
2746 |~|~|Cr: R(赤)チャンネルのガンマ係数|
2747 |~|~|Cg: G(緑)チャンネルのガンマ係数|
2748 |~|~|Cb: B(青)チャンネルのガンマ係数|
2749
2750 コード例は[[ガンマ補正|Tips: ガンマ補正]]を参照ください。
2751 </pre>
2752 </div>
2753 <div title="2.2.6 画面上に縞を描画する-Rectangleクラス2-" modifier="Psychlops_Admin" modified="200709262314" created="200708242023" changecount="82">
2754 <pre>2.2.1節から2.2.5節まで基本的な描画記法を説明してきました。
2755 この節ではここまでのまとめとして、[[Psychlops::Rectangle]]型を使って「縞」を描画します。
2756
2757 ^^ここは、ステップが多いので、かなりかみ砕いた解説をしています。縞の描画になれた方は使用する関数の説明とプログラム例のみをざっとご覧になられることをおすすめいたします。^^
2758
2759 !縞を描いてみようStep1~グラデーションの四角形の描画~
2760 これから[[Psychlops::Rectangle]]型を用いて黒(0.0)と白(1.0)の縞を作ります。
2761
2762 どういう風に縞を作成するか想像してみてください。
2763 白と黒の四角形を並べて描画するのでしょうか。
2764 [img[image/縞1.png]]
2765 しかし、これでは、単純な白と黒の繰り返しの縞しか描くことができません。
2766
2767 もう少し複雑な縞を描くためにできそうなことを順に整理してみましょう。
2768 #四角形(w × h)を描画する
2769 #四角形内の色を変化させる
2770 #四角形の位置を移動させる
2771
2772 前の2つはこれまでに説明した[[Canvas::rect()]]命令を使えばできそうです。実際にはこの節では[[Canvas::rect()]]命令の代わりに''Rectangle::draw()''命令を用います。この命令は[[Canvas::rect()]]命令と全く同じ効果を持っているので、あなたの好みでどちらを使用してもかまいません。
2773 四角形の位置を移動させるには、''Rectangle::centering()'',''Rectangle::shift()''命令を使います。
2774 以下でまずこれらの命令の書式と使い方を簡単な例とともに説明します。
2775
2776 !!Rectangle::centering()
2777 [[Psychlops::Rectangle]]型の図形を移動します。
2778 [img[image/rect_centering().png]]
2779 座標を指定する場合、その座標に図形の中心が設定されます。
2780 |!Rectangle::centering()|centering()|[[Psychlops::Rectangle]] rectを画面中心に移動します|
2781 |~|centering(double x, double y)|[[Psychlops::Rectangle]] rectを指定した座標(x,y)に移動します|
2782 |~|~|double x:移動する図形のx座標を指定|
2783 |~|~|double y:移動する図形のy座標を指定|
2784 |~|centering([[Psychlops::Point]] po)|[[Psychlops::Rectangle]] rectを指定したpoint座標(x,y)に移動します|
2785 |~|~|[[Psychlops::Point]] po:移動する図形の(x,y)座標を指定|
2786 |~|centering(Rectangle rect)|[[Psychlops::Rectangle]] rectを指定した[[Psychlops::Rectangle]] rectの中心座標(x,y)に移動します|
2787 |~|~|[[Psychlops::Rectangle]] rect :移動する図形の中心座標となる[[Psychlops::Rectangle]] rectを指定|
2788
2789 !!Rectangle::shift()
2790 [[Psychlops::Rectangle]]型の図形を移動します。
2791 元の座標(x,y)を基点に入力値(h,v)だけ移動します。
2792 |!Rectangle::shift()|shift(double h, double v)|座標(X,Y)を(h,v)だけ移動します|
2793 |~|~|double h:入力値分、右水平方向に移動|
2794 |~|~|double v:入力値分、下垂直方向に移動|
2795 [img[image/rect_shift().png]]
2796
2797 !!!Rectangle::centering()とRectangle::shift()の書き方
2798 200×100の四角形を中心座標から(-100,-200)だけ移動します。
2799 {{{
2800 &lt;例1&gt;
2801 #include &lt;psychlops.h&gt;
2802 using namespace Psychlops;
2803
2804 Psychlops::Rectangle rect1;
2805
2806 void psychlops_main() {
2807
2808         Canvas sampleA(Canvas::fullscreen);
2809         rect1.set(200,100);
2810         rect1.centering().shift(-100,-200);
2811         sampleA.rect(rect1,Color::white);
2812         sampleA.flip();
2813         while(!Input::get(Keyboard::spc));
2814 }
2815 }}}
2816
2817 !!Rectangle::draw()
2818 今まで図を描画する時、[[Psychlops::Canvas]]型の命令を使用しました。
2819 今回は描画命令にRectangle::draw()命令を使用します。
2820 |!Rectangle::draw()|draw()|対象の[[Psychlops::Rectangle]]型を描画します|
2821 |~|draw([[Psychlops::Color]] col)|対象の[[Psychlops::Rectangle]]型を[[Psychlops::Color]] colで塗りつぶし描画します|
2822 |~|~|[[Psychlops::Color]] col:描画する四角形の色を指定|
2823
2824 !!Rectangle::draw()の書き方
2825 &lt;例1&gt;の四角形の色をRectangle::draw()命令を使用して「cyan」に塗りつぶします。
2826 {{{
2827 &lt;例2&gt;
2828 #include &lt;psychlops.h&gt;
2829 using namespace Psychlops;
2830
2831 Psychlops::Rectangle rect1;
2832
2833 void psychlops_main() {
2834
2835         Canvas sampleA(Canvas::fullscreen);
2836         rect1.set(200,100);
2837         rect1.centering().shift(-100,-200);
2838         rect1.draw(Color::cyan);
2839         sampleA.flip();
2840         while(!Input::get(Keyboard::spc));
2841 }
2842 }}}
2843
2844 !!実際に縞を描いてみましょう
2845 とりあえず四角形(w × h)を(200 × 100)の大きさに決めます。
2846 まず黒(0.0)から白(1.0)へ左から右にグラデーションする縞を描画してみましょう。
2847
2848 {{{
2849 &lt;例3&gt;
2850 #include &lt;psychlops.h&gt;
2851 using namespace Psychlops;
2852
2853 Psychlops::Rectangle rect1;
2854 Psychlops::Color col1;
2855
2856 void psychlops_main() {
2857
2858         Canvas sampleA(Canvas::fullscreen);
2859         rect1.set(1,100);
2860         for(int i=0;i&lt;200;i++){
2861                 col1.set((double)i/200);
2862                 rect1.centering().shift((double)-1/2*200+i,0);
2863                 rect1.draw(col1);
2864         }
2865         sampleA.flip();
2866         while(!Input::get(Keyboard::spc));
2867 }  
2868 }}}
2869
2870 このプログラムでは、200個の1 x 100ピクセルのRectangleを描画しています。
2871 左から数えてi個目の四角形の色をi / 200とすると、一番左i = 0では色は0.0(黒)となって、右に行くに従ってRectangleの輝度が大きくなるように設定できます。
2872 描画する場所は最終的な縞の中心が画面中央(Xc,Yc)に来るように計算します。
2873 [img[image/縞2.png]]
2874 描画する縞は200 × 100ピクセルなので、一番左のRectangleの中心位置は(Xc - 100,Yc)、一番右の位置は(Xc + 100,Yc)です。
2875 一般に幅wの縞を描くときには、一番左のRectangleの位置は(Xc - 1 / 2 * w ,Yc)、一番右の位置は(Xc + 1/2 * w ,Yc)になります。
2876
2877 次にこの一番左のRectangleの位置を使って、i番目のRectangleの座標位置を考えてみます。
2878 i番目のRectangleは一番左のRectangleから見て+ i だけx座標を移動した位置にあるはずです。
2879 従って、i番目の座標は(Xc -1 / 2 * w + i ,Yc)となります。
2880
2881 !!縞を描いてみようStep2~正弦波で縞を作成する~
2882 Step1で黒から白のグラデーション(色変化)の縞を作成しました。
2883 次にもう少し複雑な正弦縞を作成してみましょう。
2884 Step1から使えそうな要素を抜き出しましょう。
2885 *描画する四角形(w × h):(1 × hピクセルの四角形をw回描画する)
2886 *座標:Xc - 1 / 2 * w + i ,Yc
2887 [img[image/正弦波1.png]]
2888 {{{
2889 * 正弦波の一般式
2890 }}}
2891 [img[image/正弦波の一般式.png]]
2892 A:振幅,λ:波長,θ:位相,x:i
2893
2894 但しy&gt;0であるので(扱うのが色のため)
2895 [img[image/正弦波2.png]]
2896 [img[image/色の式.png]]
2897 となる。
2898
2899 上記式に値をあてはめていきます。
2900 A:振動をC:コントラストに置き換えます。
2901 Michelsonコントラストの定義式を用います。
2902 [img[image/contrastEq.png]]
2903 C=2A / 2Lmean = A / Lmean
2904 A=C * Lmean
2905 今回は位相0の縞を描画するのでθ = 0
2906 コントラスト:1.0
2907 波長λ:60
2908 平均輝度(Lmean): 0.5の縞を描画します。
2909
2910 y=Asin{ ( 2π * x / λ ) + θ } + Lmean
2911 y=( C * Lmean ) sin ( 2π * i / λ ) + Lmean
2912 y=Lmean { C sin ( 2π * i / λ ) + 1 }
2913 i番目の色=Lmean { C sin ( 2π * i / λ ) + 1 } 
2914 {{{
2915 i番目の色=1.0 { 0.5 sin ( ( 2π * i / 60 ) + 1 }
2916 }}}
2917 これで色が求まりました。
2918 実際にコードを書いてみましょう。  
2919                                                                                                                            
2920 {{{
2921 &lt;例4&gt;
2922 #include &lt;psychlops.h&gt;
2923 using namespace Psychlops;
2924
2925 Psychlops::Rectangle rect1;
2926 Psychlops::Color col1;
2927 double width=200;
2928 double height=100;
2929 double lambda=60;
2930 double lmean=0.5, contrast=1.0, theta=0;
2931
2932 void psychlops_main() {
2933
2934         Canvas sampleA(Canvas::fullscreen);
2935         rect1.set(1,height);
2936         for(int i=0;i&lt;width;i++){
2937                 col1.set(lmean*((contrast*sin((2*PI*i/lambda)+theta))+1));
2938                 rect1.centering().shift((double)-1/2*width+i,0);
2939                 rect1.draw(col1);
2940         }
2941         sampleA.flip();
2942         while(!Input::get(Keyboard::spc));
2943
2944 }}}
2945
2946 !!縞を描いてみようStep3~位相が異なる縞~
2947 Step2では位相0の縞を描画しました。
2948 位相に値を入れて位相90の縞を描画しましょう。
2949 わかりやすいように位相0の下に並べて描画します。
2950 {{{
2951 &lt;例5&gt;
2952 #include &lt;psychlops.h&gt;
2953 using namespace Psychlops;
2954
2955 Psychlops::Rectangle rect1,rect2;
2956 Psychlops::Color col1,col2;
2957 double width=200;
2958 double height=100;
2959 double lambda=60;
2960 double lmean=0.5, contrast=1.0, theta1=0, theta2=90;
2961
2962 void psychlops_main() {
2963
2964         Canvas sampleA(Canvas::fullscreen);
2965         rect1.set(1,height);
2966         rect2.set(rect1);
2967         for(int i=0;i&lt;width;i++){
2968                 //位相=0の縞
2969                 col1.set(lmean*((contrast*sin((2*PI*i/lambda)+theta1))+1));
2970                 rect1.centering().shift((double)-1/2*width+i,0);
2971                 rect1.draw(col1);
2972                 //位相=90の縞
2973                 col2.set(lmean*((contrast*sin((2*PI*i/lambda)+theta2))+1));
2974                 rect2.centering().shift((double)-1/2*width+i,150);
2975                 rect2.draw(col2);
2976         }
2977         sampleA.flip();
2978         while(!Input::get(Keyboard::spc));
2979
2980 }}}
2981 !縞を描いてみようStep4~矩形波の縞を作成する~
2982 正弦波の縞の場合、白と黒の境目が曖昧です。
2983 境目をはっきり描画したい場合、矩形波の縞を描画します。
2984 ですが実際には正弦波の式を使います。
2985 {{{
2986 * 正弦波の一般式
2987 }}}
2988 [img[image/正弦波の一般式.png]]
2989 y = 0を軸に正弦波が展開します。
2990 なのでこれを利用し、y &gt; 0の場合はLmax , y &lt; 0の場合はLminを描画するという判定分を追加することで矩形波の縞が描画できます。
2991
2992 コントラスト、Lmeanを使ってLmax,Lmeanの値を求めておくと、
2993 Lmax = Lmean + Lmean * コントラストが求まります。
2994 Lmin = Lmean - Lmean * コントラストが求まります。
2995 これらを使用してコードを書いてみましょう。
2996 {{{
2997 &lt;例6&gt;
2998 #include &lt;psychlops.h&gt;
2999 using namespace Psychlops;
3000
3001 Psychlops::Rectangle rect1;
3002 double col1;
3003 double width=200;
3004 double height=100;
3005 double lambda=60;
3006 double lmean=0.5, contrast=1.0, theta=0;
3007
3008 void psychlops_main() {
3009
3010         Canvas sampleA(Canvas::fullscreen);
3011         rect1.set(1,height);
3012         for(int i=0;i&lt;width;i++){
3013                 col1=(lmean*contrast*sin((2*PI*i/lambda)+theta));
3014                 if(col1&gt;0) col1=lmean+contrast*lmean;
3015                         else   col1=lmean-contrast*lmean;
3016                 rect1.centering().shift((double)-1/2*width+i,0);
3017                 rect1.draw(col1);
3018         }
3019         sampleA.flip();
3020         while(!Input::get(Keyboard::spc));
3021
3022 }}}
3023 !!縞を描いてみようStep5~縞の角度を変えてみる~
3024 今まで縦(90°)の縞を描画してきました。
3025 次は横(0°)の縞を描画してみましょう。
3026 w × hの縦の縞を描画するために1 × hの四角形をw個並べました。
3027 横の縞を描画するにはどうしたらいいか。
3028 w × hの横の縞を描画するためにw × 1の四角形をh個並べたら作れないでしょうか。
3029
3030 まずわかっていることを書いてみます。
3031 *描画する四角形( w × h ) : ( w × 1の四角形をh回描画する)
3032 *座標X:Xc - 1 / 2 * w + i 
3033 *色:y = A sin { ( 2π * x / λ ) + θ } + Lmean
3034
3035 今回はX座標ではなくY座標を指定する必要があります。
3036 よって以下の通りになります。
3037 *座標Y:Yc - 1 / 2 * h + j (考え方はX座標と同じなので[[Step1|2.2.6 画面上に縞を描画する-Rectangleクラス2-]]参照のこと)
3038
3039 これらから実際にコードを書いて試してみましょう。
3040 {{{
3041 &lt;例7&gt;
3042 #include &lt;psychlops.h&gt;
3043 using namespace Psychlops;
3044
3045 Psychlops::Rectangle rect1;
3046 Psychlops::Color col1;
3047 double width=200;
3048 double height=100;
3049 double lambda=60;
3050 double lmean=0.5, contrast=1.0, theta=0;
3051
3052 void psychlops_main() {
3053
3054         Canvas sampleA(Canvas::fullscreen);
3055         rect1.set(width,1);
3056         for(int j=0;j&lt;height;j++){
3057                 col1.set(lmean*((contrast*sin((2*PI*j/lambda)+theta))+1));
3058                 rect1.centering().shift(0,(double)-1/2*height+j);
3059                 rect1.draw(col1);
3060         }
3061         sampleA.flip();
3062         while(!Input::get(Keyboard::spc));
3063
3064 }}}
3065 確認できたでしょうか。
3066 矩形波の横の縞はStep4を参考に自分で試してみてください。</pre>
3067 </div>
3068 <div title="2.2.7 画面上に縞を描画する-Pointクラス2-" modifier="YourName" modified="200802191327" created="200709252135" changecount="11">
3069 <pre>[[2.2.6節|2.2.6 画面上に縞を描画する-Rectangleクラス2-]]では[[Psychlops::Rectangle]]型を用いて縞を描画しました。
3070 この節では[[Psychlops::Point]]型を用いて縞を描画します。
3071
3072 四角形(w × h)を描画する際、[[Psychlops::Rectangle]]型では1 × hの四角形をw個
3073 もしくはw × 1の四角形を h個並べて描画しました。
3074 [[Psychlops::Point]]型ではw × hの点を並べて描画します。
3075
3076 !縞を描いてみようStep1~正弦縞を描画する~
3077 はじめに一番簡単な垂直方位の縞を書いてみましょう。
3078
3079 まずわかっていることを書いてみます。
3080 *描画する縞のサイズ:w × h (1ピクセルの点をw×h回描画する)
3081 *縞の中心座標X:Xc - 1 / 2 * w + i
3082 *縞の中心座標Y:Yc - 1 / 2 * h + j
3083 *縞の式:L(x,y) = Lmean*(1+contrast*sin { ( 2π * x / λ ) + θ } )
3084
3085 式のパラメータは以下の通りとします。
3086 |位相θ:0|
3087 |コントラスト:0.5|
3088 |波長λ:60|
3089 |平均輝度(Lmean): 0.25|
3090
3091 では以上をふまえてコードを書いてみましょう。
3092 {{{
3093 &lt;例1&gt;
3094 #include &lt;psychlops.h&gt;
3095 using namespace Psychlops;
3096
3097 Psychlops::Point p1;
3098 Psychlops::Color col1;
3099 double width=200;
3100 double height=100;
3101 double lambda=60;
3102 double lmean=0.25, contrast=0.5, theta=0;
3103
3104 void psychlops_main() {
3105
3106         Canvas sampleA(Canvas::fullscreen);
3107         for(int i=0;i&lt;width;i++){
3108                 for(int j=0;j&lt;height;j++){
3109                         col1.set(lmean*((contrast*sin((2*PI*i/lambda)+theta))+1));
3110                         p1.centering().shift((double)-1/2*width+i,(double)-1/2*height+j);
3111                         sampleA.pix(p1,col1);
3112                 }
3113         }
3114         sampleA.flip();
3115         while(!Input::get(Keyboard::spc));
3116 }
3117 }}}
3118
3119 [img[image/grating01.png]]
3120
3121 !縞を描いてみようStep2~斜めの縞を描画する~
3122 [[Psychlops::Rectangle]]型を使った書き方では方位90°の縞(縦)と0°の縞(横)のみを描画しました。
3123 Pointクラスを使った縞の描画では、これ以外の方位の縞も描画することができます。
3124 ここでは回転を使って方位45°の縞(斜め)を描画します。
3125
3126 !!図形の回転
3127 点(i, j)をθラジアン回転させて(xx,yy)に変換する式は以下の通りです。:
3128 {{{
3129 xx = cosθ  * i - sinθ * j
3130 yy = sinθ * i + cosθ * j
3131 }}}
3132 この式を用いて、斜めの方位を持つ縞を描画してみましょう。
3133 θはラジアン単位です。度数法d(°)で値を指定したいときは下の式で変換した値を用います。
3134 {{{
3135 θ ( rad ) = d° * π / 180°
3136 }}}
3137
3138 以上をから&lt;例1&gt;を修正して方位が45°の縞を描画してみましょう。
3139 {{{
3140 &lt;例2&gt;
3141 #include &lt;psychlops.h&gt;
3142 using namespace Psychlops;
3143
3144 Psychlops::Point p1;
3145 Psychlops::Color col1;
3146 double width=200;
3147 double height=100;
3148 double lambda=60;
3149 double lmean=0.5, contrast=0.5, theta=0;
3150 double xx,yy;
3151 double orientation;
3152
3153 void psychlops_main() {
3154
3155         Canvas sampleA(Canvas::fullscreen);
3156         orientation=45*PI/180;
3157         for(int i=0;i&lt;width;i++){
3158                 for(int j=0;j&lt;height;j++){
3159                         //回転
3160                         xx=cos(orientation)*i-sin(orientation)*j;
3161                         yy=sin(orientation)*i+cos(orientation)*j;
3162                         
3163                         col1.set(lmean*((contrast*sin((2*PI*yy/lambda)+theta))+1));
3164                         p1.centering().shift((double)-1/2*width+i,(double)-1/2*height+j);
3165                         sampleA.pix(p1,col1);
3166                 }
3167         }
3168         sampleA.flip();
3169         while(!Input::get(Keyboard::spc));
3170 }
3171 }}}
3172
3173 [img[image/grating45.png]]
3174
3175 !縞を描いてみようStep3~交差した縞(Plaid)を描画する~
3176 では次にStep2で作成した45°の縞に135°の縞を交差させた縞を描画してみましょう。
3177
3178 |位相θ:0|
3179 |コントラスト:0.5|
3180 |波長λ:60|
3181 |平均輝度(Lmean): 0.25|
3182 |方位:45°|
3183
3184 交差する縞の情報を設定します。
3185 |位相θ:0|
3186 |コントラスト:0.25|
3187 |波長λ:45|
3188 |平均輝度(Lmean): 0.25|
3189 |方位:135°|
3190
3191 {{{
3192 &lt;例3&gt;
3193 #include &lt;psychlops.h&gt;
3194 using namespace Psychlops;
3195
3196 Psychlops::Point p1;
3197 Psychlops::Color col1;
3198 double width=200;
3199 double height=100;
3200 double lambda=60;
3201 double lmean=0.25, contrast=0.5, theta=0;
3202 double xx,yy;
3203 double orientation=45*PI/180;
3204         
3205 //交差する縞の情報
3206 double lambda2=45;
3207 double lmean2=0.25, contrast2=0.25, theta2=0;
3208 double xx2,yy2;
3209 double orientation2=135*PI/180;
3210
3211 void psychlops_main() {
3212
3213         Canvas sampleA(Canvas::fullscreen);
3214         
3215         for(int i=0;i&lt;width;i++){
3216                 for(int j=0;j&lt;height;j++){
3217                         //回転
3218                         xx=cos(orientation)*i-sin(orientation)*j;
3219                         yy=sin(orientation)*i+cos(orientation)*j;
3220                         xx2=cos(orientation2)*i-sin(orientation2)*j;
3221                         yy2=sin(orientation2)*i+cos(orientation2)*j;
3222                         
3223                         col1.set( lmean*(1+contrast*sin(theta+(2*PI*yy/lambda)))+
3224                                                  lmean2*(1+contrast2*sin(theta2+(2*PI*yy2/lambda2)))
3225                                           );
3226                         p1.centering().shift((double)-1/2*width+i,(double)-1/2*height+j);
3227                         sampleA.pix(p1,col1);
3228                 }
3229         }
3230         sampleA.flip();
3231         while(!Input::get(Keyboard::spc));
3232 }
3233 }}}
3234
3235 [img[image/plaid45_135.png]]
3236
3237 難しいと思われた方は、orientationやcontrast等のパラメタを変えて実行してみるとわかりやすいかもしれません。</pre>
3238 </div>
3239 <div title="2.2.8 運動する図形を描画する-Rectangleクラス3-" modifier="Psychlops_DevelopperG" modified="200908190231" created="200709252136" changecount="4">
3240 <pre>この節では[[2.2.6節|2.2.6 画面上に縞を描画する-Rectangleクラス2-]]で作成した縞を動かします。
3241
3242 [[2.2.6節|2.2.6 画面上に縞を描画する-Rectangleクラス2-]]では、位相を変えてみることで縞の位置を移動させました。
3243 「縞を動かす」ことは一見難しそうですが位相を変化させて描画することで
3244 縞が運動しているように見えます。
3245
3246 ちょっと復習しましょう。
3247 下のコードを実行すると位相 = 0と位相 = 90の四角形がそれぞれ描画されます。
3248 {{{
3249 #include &lt;psychlops.h&gt;
3250 using namespace Psychlops;
3251
3252 Psychlops::Rectangle rect1,rect2;
3253 Psychlops::Color col1,col2;
3254 double width=200;
3255 double height=100;
3256 double lambda=60;
3257 double lmean=0.5, contrast=1.0, theta1=0, theta2=90;
3258
3259 void psychlops_main() {
3260
3261         Canvas sampleA(Canvas::fullscreen);
3262         rect1.set(1,height);
3263         rect2.set(rect1);
3264         for(int i=0;i&lt;width;i++){
3265                 //位相=0の縞
3266                 col1.set(lmean*((contrast*sin((2*PI*i/lambda)+theta1))+1));
3267                 rect1.centering().shift((double)-1/2*width+i,0);
3268                 rect1.draw(col1);
3269                 //位相=90の縞
3270                 col2.set(lmean*((contrast*sin((2*PI*i/lambda)+theta2))+1));
3271                 rect2.centering().shift((double)-1/2*width+i,150);
3272                 rect2.draw(col2);
3273         }
3274         sampleA.flip();
3275         while(!Input::get(Keyboard::spc));
3276
3277 }}}
3278 ずれているのを確認できたでしょうか。
3279 ここで、描画する毎にθの値を変更して入力すれば四角形が常に移動しているように見えます。
3280 実際に試してみましょう。
3281 {{{
3282 &lt;例1&gt;
3283 #include &lt;psychlops.h&gt;
3284 using namespace Psychlops;
3285
3286 Psychlops::Rectangle rect1;
3287 Psychlops::Color col1;
3288 double width=200;
3289 double height=100;
3290 double lambda=60;
3291 double lmean=0.5, contrast=1.0, theta=0;
3292 double speed=PI/90;
3293
3294 void psychlops_main() {
3295
3296         Canvas sampleA(Canvas::fullscreen);
3297         while(!Input::get(Keyboard::spc)){
3298                 rect1.set(1,height);
3299                 for(int i=0;i&lt;width;i++){
3300                         col1.set(lmean*((contrast*sin((2*PI*i/lambda)+theta))+1));
3301                         rect1.centering().shift((double)-1/2*width+i,0);
3302                         rect1.draw(col1);
3303                 }
3304         sampleA.flip();
3305         theta+=speed;
3306         }
3307
3308 }}}
3309 &lt;例2&gt;は&lt;例1&gt;の1箇所だけを変えたコードです。
3310 どこが変わっているのか見つけてみてください。
3311 {{{
3312 &lt;例2&gt;
3313 #include &lt;psychlops.h&gt;
3314 using namespace Psychlops;
3315
3316 Psychlops::Rectangle rect1;
3317 Psychlops::Color col1;
3318 double width=200;
3319 double height=100;
3320 double lambda=60;
3321 double lmean=0.5, contrast=1.0, theta=0;
3322 double speed=PI/90;
3323
3324 void psychlops_main() {
3325
3326         Canvas sampleA(Canvas::fullscreen);
3327         while(!Input::get(Keyboard::spc)){
3328                 rect1.set(1,height);
3329                 for(int i=0;i&lt;width;i++){
3330                         col1.set(lmean*((contrast*sin((2*PI*i/lambda)+theta))+1));
3331                         rect1.centering().shift((double)-1/2*width+i,0);
3332                         rect1.draw(col1);
3333                 }
3334         sampleA.flip(6);
3335         theta+=speed;
3336         }
3337
3338 }}}
3339 答えはflip()です。&lt;例1&gt;ではflip(),&lt;例2&gt;ではflip(6)になっています。
3340 実行すると運動の速度が随分変化していることに気づくでしょう。
3341 flip(6)では6回リフレッシュが行われるまで描画したままの状態で待ち、次の垂直同期で画面が入れ替わります。
3342 60Hzの画面でflip(6)を実行する場合、100ミリsec描画したままの状態を維持できます。
3343 このようにして、flip(int waitframe)命令を活用すると運動刺激のリフレッシュレート(各フレームの長さ)や細かい描画の待ち時間を設定できます。
3344
3345 flip(int waitframe)について詳しい説明は[[2.1.2節|2.1.2 Canvasの基本構造と操作]]を参照してください。</pre>
3346 </div>
3347 <div title="2.3 文字列の描画" modifier="Psychlops_DevelopperG" modified="200912010809" created="200909300449" changecount="25">
3348 <pre>[[2.1.3節|2.1.3 Canvasのメンバ取得と変数の表示]]では簡単な文字列の表示について説明しました。
3349 この節ではより本格的な2バイト文字を用いた文字列の描画について取り扱います。
3350
3351 Psychlopsで2バイト文字を描画するには2つの方法があります。
3352 いずれの方法もCanvas::msg()命令を使用します。
3353
3354 |!Canvas::msg()|msg(char* string,double x,double y,[[Psychlops::Color]] col)|&gt;|char* stringを座標(x,y)に指定色で表示します|
3355 |~|~|schar* string|画面表示をする文字列を指定|
3356 |~|~|double x|画面表示をする座標(x)を指定|
3357 |~|~|double y|画面表示をする座標(y)を指定|
3358 |~|~|[[Psychlops::Color]] col|描画する点の色を指定(設定しない場合、白が設定される)|
3359 |~|msg(string str,double x,double y,[[Psychlops::Color]] col)|&gt;|string strを座標(x,y)に指定色で表示します|
3360 |~|~|string str|画面表示をする文字列を指定|
3361 |~|~|double x|画面表示をする座標(x)を指定|
3362 |~|~|double y|画面表示をする座標(y)を指定|
3363 |~|~|[[Psychlops::Color]] col|描画する点の色を指定(設定しない場合、白が設定される)|
3364
3365 2つの方法とはこの命令を単体で使う方法とLettersと呼ばれるクラスを用いた方法です。
3366 前者の方法は簡便ですが、フォント指定などはできず、それほど美しく表示することはできません。
3367 後者の方法はフォント指定など文字列表示の方法を細かく指定できますが、文字列を表示する前にメモリ領域に文字列を描画しておく必要があります。
3368 以下の節でそれぞれの方法を説明します。
3369
3370 [[2.3.1 Canvas::msg()命令単体での文字列描画]]
3371 [[2.3.2 Lettersクラスを用いた文字列描画]]
3372 また、これらのクラスの詳細については[[3.5節|3.5 文字列のレンダリングの仕様と関連情報]]をご覧ください。
3373
3374 </pre>
3375 </div>
3376 <div title="2.3.1 Canvas::msg()命令単体での文字列描画" modifier="Psychlops_DevelopperG" modified="200910260740" created="200910020306" changecount="13">
3377 <pre>!!Canvas::msg()命令単体での文字列描画
3378 まず、[[2.1.3節|2.1.3 Canvasのメンバ取得と変数の表示]]で説明したCanvas::msg()命令をそのまま用いるやり方について説明します。
3379 基本的には[[2.1.3節|2.1.3 Canvasのメンバ取得と変数の表示]]とほとんど同じですが、これまではここにアルファベットや記号、いわゆる1バイト文字でできた文字列を代入してきました。
3380 {{{
3381 &lt;例0&gt;
3382 #include &lt;psychlops.h&gt;
3383 using namespace Psychlops;
3384
3385 void psychlops_main() {
3386         Canvas sampleA(Canvas::fullscreen);
3387
3388         sampleA.msg(&quot;A test of Letter Drawing&quot;, SampleA.getCenter());
3389
3390         sampleA.flip();
3391         while(!Input::get(Keyboard::spc));
3392 }
3393 }}}
3394
3395 ここで、表示したい文字列がひらがな・カタカナなどの2バイト文字とか全角文字と呼ばれる文字でできている場合は「”」(ダブルクォーテーション)の前に大文字のLを入れます。以下の例を見てください。
3396
3397 {{{
3398 &lt;例1&gt;
3399 #include &lt;psychlops.h&gt;
3400 using namespace Psychlops;
3401
3402 void psychlops_main() {
3403         Canvas sampleA(Canvas::fullscreen);
3404
3405         sampleA.msg(L&quot;日本語の表示&quot;, SampleA.getCenter());
3406
3407         sampleA.flip();
3408         while(!Input::get(Keyboard::spc));
3409 }
3410 }}}
3411
3412 {{{
3413         sampleA.msg(L&quot;日本語の表示&quot;, SampleA.getCenter());
3414 }}}
3415 の命令を見てわかるとおり、&quot;日本語の表示&quot; という文字列のまえに大文字のLが入っています。
3416
3417 !!文字変数を使った代入
3418 さらにこの部分に文字変数を入れたい場合はどうすればよいでしょうか。
3419 普通のプログラムで文字列によく使うstring, char型を使ってもコンパイルできません。
3420 このような場合はstring型を拡張したwstring型の変数を宣言してCanvas::msg()命令に代入します。
3421
3422
3423 |!Canvas::msg()|msg(std::wstring string,double x,double y,[[Psychlops::Color]] col)|char* stringを座標(x,y)に指定色で表示します|
3424 |~|~|string str:画面表示をする文字列を指定|
3425 |~|~|double x:画面表示をする座標(x)を指定|
3426 |~|~|double y:画面表示をする座標(y)を指定|
3427 |~|~|[[Psychlops::Color]] col:描画する点の色を指定(設定しない場合、白が設定される)|
3428
3429 {{{
3430 &lt;例2&gt;
3431 #include &lt;psychlops.h&gt;
3432 using namespace Psychlops;
3433
3434 void psychlops_main() {
3435
3436         Canvas sampleA(Canvas::fullscreen);
3437         std::wstring wstr;
3438         wstr=L&quot;日本語の表示&quot;;
3439         sampleA.msg(wstr, SampleA.getCenter());
3440         sampleA.flip();
3441
3442         while(!Input::get(Keyboard::spc));
3443 }
3444 }}}
3445
3446 ワイド文字列についてはいくつか知っておいた方が便利なことがあります。これらの詳細な情報については[[3.5節|3.5 文字列のレンダリングの仕様と関連情報]]をご覧ください。
3447
3448 </pre>
3449 </div>
3450 <div title="2.3.2 Lettersクラスを用いた文字列描画" modifier="Psychlops_DevelopperG" modified="200910260748" created="200910020318" changecount="34">
3451 <pre>Canvas命令単体での文字列描画は簡単ですが、フォントがぎざぎざしていたりしてあまり美しくは表示されません。
3452 また、もう少し大きな文字を表示したい場合やイタリック体で文字を表示したい場合にはこの方法では対応できません。
3453 この様な複雑な指定をして文字列を描画したい場合には事前にメモリに文字列を描画して、これを画面にコピーするという方法を行います。本や大量の部数の文書を作るときに、活字を組んで版をつくり、インクで紙に刷るのと同じ事です。
3454 この事前のメモリ領域への文字列描画のためのクラスとして、[[Letters]],  [[Font]]クラスが用意されています。
3455
3456 この方法による文字列描画の流れを簡単に説明すると以下のようになります
3457 # [[Font]]クラスを用いてフォントを指定する
3458 # [[Letters]]クラスを用いてメモリに文字列を描画
3459 # [[Canvas::msg()命令を用いてメモリ内の文字列を画面に描画]]
3460 以下でそれぞれの段階の具体的な方法について説明します。
3461
3462 ![[Font]]クラスを用いたフォントの指定
3463 Psychlopsにおけるフォントの指定は、[[Font]]クラスの変数を宣言し、これを引数として代入することによって行います。
3464 Fontの宣言は以下の書式に従って行います。
3465 |!Fontの宣言|font(double size, int weight, Style style, std::wstring family);|!フォントを指定します。|&gt;|
3466 |~|~|double size|フォントサイズ(pixel)を指定|
3467 |~|~|int weight|線幅を指定 &lt;br&gt;  (normal_style (400) / bold (700) : 省略可能)|
3468 |~|~|Style style|斜体の有無を指定 &lt;br&gt; (normal_style / italic : 省略可能)|
3469 |~|~|std::wstring family|フォント名を指定 &lt;br&gt;      (省略可能)|
3470
3471 実際の例をいくつかあげてみましょう。
3472 {{{
3473 Font font1(24, Font::bold, Font::italic, L&quot;ArialMT&quot;); 
3474 }}}
3475 {{{
3476 Font font2(12, Font::bold, Font::normal_style, L&quot;メイリオ&quot;);
3477 }}}
3478 {{{
3479 Font font3(18, Font::normal_weight, Font::normal_style, L&quot;ヒラギノ角ゴ Pro&quot;);
3480 }}}
3481
3482 第2引数は文字の太さ、第3引数で斜体の有無を選択します。
3483 第4引数はフォントの名前を2重引用符(&quot;)でくくって指定します。
3484 これらは省略可能です。
3485
3486 Canvas.msg関数などで使われるデフォルトフォントを変更するには、Font::default_font変数に新しいフォント構造体を指定します。
3487 {{{
3488 Font::default_font = Font(L&quot;Arial&quot;, 20);
3489 }}}
3490
3491 フォントはインストールされていなければその環境標準のものになります。一般的にWindowsとMacOSXでは同じ名前のフォントはありませんので、注意してください。
3492
3493 ![[Letters]]クラスによる文字列のプリレンダリングと描画
3494 次のステップでは、上で用意したフォントを使って、文字列の元版(レンダリング済みの文字列、といいます)をメモリ内に作成します。
3495 これにはLettersクラスを使用します。
3496
3497 |!Lettersの宣言|Letters(std::wstring str, Font font );|!文字列を指定されたフォントでレンダリングします。|&gt;|
3498 |~|~|std::wstring str|描画する文字列を指定|
3499 |~|~|Font font|フォントを指定 &lt;br&gt;      (省略可能)|
3500
3501 ここでも実際の例をいくつかあげてみましょう。
3502 {{{
3503 Letters let1(L&quot;Space Barを押すと文字が変わります&quot;, Font(20, Font::bold));
3504 }}}
3505 {{{
3506 Letters let2(L&quot;矢印キーを押してください。配置が変わります。&quot;);
3507 }}}
3508 {{{
3509 Letters let3(L&quot;Font \&quot;Arial\&quot; have only Latain characters. 日本語フォントが必要です.&quot;, font1);
3510 }}}
3511
3512 文字列の内容をプログラム内で変更したいときなど、宣言とは違うところで文字列をレンダリングしたいときには、Letters::string()命令を使います。
3513
3514 |!Letters::string()|string(std::wstring str);|!文字列を指定されたフォントでレンダリングします。|&gt;|
3515 |~|~|std::wstring str|描画する文字列を指定|
3516
3517 このとき、font指定はLetters::font()命令を使用して別に行う事になりますが、何も指定せずデフォルトのフォントでレンダリングする事もできます。
3518
3519 |!Letters::font()|font(Font font);|!文字列を指定されたフォントでレンダリングします。|&gt;|
3520 |~|~|std::wstring str|描画する文字列を指定|
3521 |~|~|Font font|フォントを指定 |
3522
3523 例えば上にあげた一つ目の例
3524 {{{
3525 Letters let1(L&quot;Space Barを押すと文字が変わります&quot;, Font(20, Font::bold));
3526 }}}
3527 をこれらの命令を使って書き直してみるとこの様になるでしょう。
3528 {{{
3529 Letters let1;
3530 let1.string(L&quot;Space Barを押すと文字が変わります&quot;);
3531 let1.font(Font(20, Font::bold));
3532 }}}
3533
3534
3535
3536 ! Canvas::msg()命令を用いてメモリ内の文字列を画面に描画
3537 ここまでの段階で、指定されたフォントを使って表示したい文字列の元版ができあがりました。実際に画面にこれを表示するにはCanvas::msg()命令を使います。
3538
3539 |!Canvas::msg()|msg(Letters let, double x, double y, Color col, align, algn );|!文字列を指定されたフォントでレンダリングします。|&gt;|
3540 |~|~|Letters let|描画するレンダリング済み文字列を指定|
3541 |~|~|double x|文字列描画の水平位置を指定|
3542 |~|~|double y|文字列描画の垂直位置を指定|
3543 |~|~|Color col|文字列の描画色を指定 &lt;br&gt;      (省略可能)|
3544 |~|~|align, algn|文字詰めの方法を指定 &lt;br&gt;      (省略可能)|
3545
3546 {{{
3547 Letters let1(L&quot;Space Barを押すと文字が変わります&quot;, Font(20, Font::bold));
3548 Display::msg(let1, 100, 100);
3549 }}}
3550
3551
3552 ! Letters::draw()命令を用いてメモリ内の文字列を画面に描画
3553 もう一つ同じ目的の命令としてLetters::draw()命令があります。
3554 LettersクラスにはRectangle型に対して使用できるcentering(), shift()などの命令が使えます。
3555 これらの命令を使って同じ文字列を異なる位置に簡単に描画することができます。
3556 |!Letters::centering()|centering(double x, double y)|!レンダリング済みの文字列の描画位置をその中央の座標で指定する。|&gt;|
3557 |~|~|double x|センタリングの水平位置を指定&lt;br&gt;      (省略可能)|
3558 |~|~|double y|センタリングの垂直位置を指定&lt;br&gt;      (省略可能)|
3559 |~|~|&gt;|&gt;|これらの値が省略されるときは、画面中央に描画位置が設定される|
3560
3561 |!Letters::shift()|shift(double x, double y)|!レンダリング済みの文字列の描画位置を現在の指定位置から移動する。|&gt;|
3562 |~|~|double x|水平方向の移動量を指定|
3563 |~|~|double y|垂直方向の移動量を指定|
3564
3565 |!Letters::draw()|draw(Color col)|!レンダリング済みの文字列を描画する|&gt;|
3566 |~|~|double x|水平方向の移動量を指定|
3567 |~|~|double y|垂直方向の移動量を指定|
3568 |~|~|Color col|文字列の描画色を指定 &lt;br&gt;      (省略可能)|
3569
3570 以下のように使います。
3571 {{{
3572 Letters let1(L&quot;Space Barを押すと文字が変わります&quot;, Font(20, Font::bold));
3573 let1.centering(Mouse::x, Mouse::y+30).draw(Color::green);
3574 }}}
3575
3576
3577 !まとめ
3578
3579 ここまでをまとめて、文字列描画の簡単なプログラム例を作ってみましょう。
3580 {{{
3581 #include &lt;psychlops.h&gt;
3582 using namespace Psychlops;
3583
3584 void psychlops_main() {
3585
3586         Canvas sampleA(Canvas::fullscreen); //描画用windowの取得
3587
3588         Letters let1(L&quot;Space Barを押すと文字が変わります&quot;, Font(20, Font::bold));
3589         Letters let2;
3590         let2.string(L&quot;文字が変わりました&quot;);
3591         let2.font(Font(20, Font::bold));
3592
3593         sampleA.clear(Color(0.0));
3594         sampleA.msg(let1, 100, 100);
3595         sampleA.flip();
3596         while(!Input::get(Keyboard::spc));
3597         
3598         sampleA.clear(Color(0.5));
3599         let2.centering().draw(Color::red);
3600         while(!Input::get(Keyboard::spc));
3601 }
3602
3603 }}}
3604 </pre>
3605 </div>
3606 <div title="3. 複雑な描画を行う1(オフスクリーンへの描画)" modifier="Psychlops_DevelopperG" modified="200910260736" created="200708211945" changecount="2">
3607 <pre>[[3.1 オフスクリーン描画の基本]]
3608   [[3.1.1 オフスクリーン領域の宣言]]
3609   [[3.1.2 オフスクリーン上に点・四角形を描画する]] 
3610   [[3.1.3 オフスクリーン上の任意の位置に描画する]]
3611   [[3.1.4 発展的な内容]] 
3612 [[3.2 オフスクリーンを用いた描画例-ランダムドットパターン-]]
3613 [[3.3 画像ファイルの取り扱い]]
3614   [[3.3.1 画像ファイルを保存する]] 
3615   [[3.3.2 画像ファイルを読み込み表示する]] 
3616   [[3.3.3 複数の画像ファイルからアニメーションを作成する]]
3617 [[3.4 Canvasのオフスクリーンへの取り込み]]
3618 [[3.5 文字列のレンダリングの仕様と関連情報]]</pre>
3619 </div>
3620 <div title="3.1 オフスクリーン描画の基本" modifier="Psychlops_DevelopperG" modified="200908190234" created="200708292225" changecount="7">
3621 <pre>[[3.1.1 オフスクリーン領域の宣言]]
3622 [[3.1.2 オフスクリーン上に点・四角形を描画する]] 
3623 [[3.1.3 オフスクリーン上の任意の位置に描画する]] 
3624 [[3.1.4 発展的な内容]] </pre>
3625 </div>
3626 <div title="3.1.1 オフスクリーン領域の宣言" modifier="Psychlops_Admin" modified="200709142108" created="200708292249" changecount="31">
3627 <pre>!Imageクラスの概要
3628 !!オフスクリーン領域とは?
3629 第2章では直にCanvasに描画してきました。第3章ではオフスクリーンへ領域の描画ないし取扱いをします。
3630 Canvasは裏表2枚しかないので、複雑な動画を描画するには不便です。
3631 裏画面に描画が終わるまでflip()を実行できないので(もし強引にflip()を実行してしまったら、描画途中の中途半端なイメージが表示されてしまいます)、複雑な動画ではアニメーションの質が下がってしまいます。
3632 このような動画の描画や、とても時間のかかる内容の描画を行うときにはオフスクリーン領域(描画バッファともいいます)と呼ばれる特殊な描画領域を用います。
3633 このオフスクリーン領域はメモリ上に確保された画像ファイルのようなものだと考えるとよいかもしれません。
3634 この画像ファイルに複雑な描画内容を保存しておいて、後で紙芝居のように画面に表示することができれば、複雑な内容の描画も素早く行えるわけです。^^*1^^
3635 Psychlopsではこのオフスクリーン領域をImageというクラスを用いて操作します。
3636
3637 ^^ *1もう少し正しく表現するなら、オフスクリーン領域とはコンピュータのメインメモリ(RAM 1GBなどという”あれ”のことです)内に確保された描画内容の保存場所(描画用バッファ)のことです。^^
3638
3639 !!ImageとCanvasの違い
3640 ここではImageとCanvasの違いについて詳しく説明することはしませんが、これら2つのクラスは物理的に保管場所が違うということは頭に置いておいてください。
3641 Canvasの内容は画面上に素早く提示することのできる位置に保管されています。flip()命令を実行すると正確なタイミングで描画が起こるわけです。
3642 これに対して、ImageはCanvasよりも遙かに大量のデータを保管することができますが、画面上に描画内容を提示するには、ある程度の時間が必要です。
3643 これは、Canvas(の保管場所)にImageの内容を転送する必要があるからです。
3644 より詳しい内容については[[Tips: Image使用上の注意]]を参考にしてください。
3645 [img[image/オフスクリーン領域.png]]
3646
3647 !オフスクリーン領域の宣言文
3648 オフスクリーン領域は画像を取り扱うので
3649 宣言時には取り扱いたい画像の大きさやカラーモード、透明度の指定が必要となってきます。
3650 Image()のように宣言のみの場合は''Image::set()''命令で指定します。
3651
3652 |!Imageの宣言|Image()|オフスクリーン領域を宣言します|
3653 |~|Image([[Psychlops::Rectangle]] rec,PixelComponentsCnt pixcompcntval)|指定された[[Psychlops::Rectangle]] 型と同じ大きさのオフスクリーン領域を宣言します|
3654 |~|~|[[Psychlops::Rectangle]] rect :[[Psychlops::Rectangle]] 型を指定|
3655 |~|~|PixelComponentsCnt pixcompcntval :[[カラーモード]]を指定。省略した場合はRGBが指定される|
3656 |~|Image(long x, long y, PixelComponentsCnt pixcompcntval)|指定されたx×yと同じ大きさのオフスクリーン領域を宣言します|
3657 |~|~|long x :オフスクリーン領域の横幅を指定|
3658 |~|~|long y :オフスクリーン領域の縦幅を指定|
3659 |~|~|PixelComponentsCnt pixcompcntval :[[カラーモード]]を指定。省略した場合はRGBが指定される|
3660
3661 !Image::set()
3662 オフスクリーン領域の宣言時に何の指定もしなかった場合、Image::set()命令を使用します。
3663 |!Image::set()|set([[Psychlops::Rectangle]] rect, PixelComponentsCnt pixcompcntval)|指定された[[Psychlops::Rectangle]] 型と同じ大きさのオフスクリーン領域を設定します|
3664 |~|~|[[Psychlops::Rectangle]] rect :[[Psychlops::Rectangle]] 型を指定|
3665 |~|~|PixelComponentsCnt pixcompcntval :[[カラーモード]]を指定。省略した場合はRGBが指定される|
3666 |~|set(long x, long y, PixelComponentsCnt pixcompcntval)|指定されたx×yと同じ大きさのオフスクリーン領域を設定します|
3667 |~|~|long x :オフスクリーン領域の横幅を指定|
3668 |~|~|long y :オフスクリーン領域の縦幅を指定|
3669 |~|~|PixelComponentsCnt pixcompcntval :[[カラーモード]]を指定。省略した場合はRGBが指定される|
3670
3671 !!宣言文の書き方
3672 {{{
3673 &lt;例1&gt;
3674 #include &lt;psychlops.h&gt;
3675 using namespace Psychlops;
3676
3677 Psychlops::Rectangle rect1(200,100);
3678 Psychlops::Image Noise1(rect1,Image::GRAY);
3679
3680 void psychlops_main() {
3681
3682         Canvas sampleA(Canvas::fullscreen);
3683         sampleA.flip();
3684         while(!Input::get(Keyboard::spc));
3685
3686 }
3687 }}}
3688 次はImageの宣言と[[Image::set()]]命令を組み合わせたコードです。
3689 {{{
3690 &lt;例2&gt;
3691 #include &lt;psychlops.h&gt;
3692 using namespace Psychlops;
3693
3694 Psychlops::Image Noise1;
3695 long Width=200, Height=100;
3696
3697 void psychlops_main() {
3698
3699         Canvas sampleA(Canvas::fullscreen);
3700         Noise1.set(Width,Height,Image::RGBA);
3701         sampleA.flip();
3702         while(!Input::get(Keyboard::spc));
3703 }
3704 }}}
3705 これらのコードは実行しても黒い画面が表示されるだけでした。
3706 次の3.1.2節で実際にImage上に描画を行います。</pre>
3707 </div>
3708 <div title="3.1.2 オフスクリーン上に点・四角形を描画する" modifier="Psychlops_DevelopperG" modified="200908190233" created="200709032203" changecount="8">
3709 <pre>[[3.1.1|3.1.1 オフスクリーン領域の宣言]]節ではImageの宣言(領域確保)のみを行っていたので、コードを実行しても黒い画面が表示されるだけでした。
3710 本節では実際にImage上に点・四角形を描画して、これを画面に表示してみます。
3711 Imageクラスの命令(メソッド)はCanvasに対する命令とほとんど変わりません。
3712
3713 !Image::pix()
3714 オフスクリーン領域に点を描画するための命令です。
3715 [[Canvas::pix()|2.2.1 画面上に点を描画する-Pointクラス1-]]命令同様に点を描画するために「座標」と「色の指定」を行います。
3716 色の指定についての詳細は[[2.2.5 描画する図形の色を指定する-Colorクラス-]]を参照してください。
3717
3718 |!Image::pix()|pix(int x, int y, [[Psychlops::Color]] col)|座標(x,y)に[[Psychlops::Color]] col色の点を描画します|
3719 |~|~|int x: 描画する点の座標軸xを指定|
3720 |~|~|int y: 描画する点の座標軸yを指定|
3721 |~|~|[[Psychlops::Color]] col:描画する点の色を指定|
3722 |~|pix(double x, double y, [[Psychlops::Color]] col)|座標(x,y)に[[Psychlops::Color]] col色の点を描画します|
3723 |~|~|double x: 描画する点の座標軸xを指定|
3724 |~|~|double y: 描画する点の座標軸yを指定|
3725 |~|~|[[Psychlops::Color]] col:描画する点の色を指定|
3726 |~|pix([[Psychlops::Point]] po, [[Psychlops::Color]] col)|Point座標(x,y)に[[Psychlops::Color]] col色の点を描画します|
3727 |~|~|[[Psychlops::Point]] po :描画する点の座標(x,y)を指定|
3728 |~|~|[[Psychlops::Color]] col:描画する点の色を指定|
3729
3730 !Image::draw()
3731 実際にオフスクリーン領域に描画する際の命令です。
3732 この命令を記述しないといくら''Image::pix()''命令やこの後出てくる''Image::rect()''命令を記述しても描画されませんので注意してください。
3733
3734 |!Image::draw()|draw()|指定したオフスクリーンを描画します|
3735 |~|draw(double left, double top)|指定したオフスクリーン領域を(left,top)を中心とした座標に設定します|
3736 |~|~|double left: 描画するオフスクリーン領域の座標軸xを指定|
3737 |~|~|double top: 描画するオフスクリーン領域の座標軸yを指定|
3738
3739 !!Image::pix(),Image::draw()の書き方
3740 100×100のImageに点を描画します。
3741 {{{
3742 &lt;例1&gt;
3743 #include &lt;psychlops.h&gt;
3744 using namespace Psychlops;
3745
3746 double width=100,height=100;
3747 Psychlops::Rectangle rect1(width,height);
3748 Psychlops::Color col;
3749 Psychlops::Image Noise1(rect1,Image::GRAY);
3750
3751 void psychlops_main() {
3752
3753         Canvas sampleA(Canvas::fullscreen);
3754         //水平方向に2,垂直方向に5間隔の点を描画する。
3755         for(int x=0; x&lt;width; x+=2){
3756                 for(int y=0; y&lt;height; y+=5){
3757                         col.set(0.5);
3758                         Noise1.pix(x,y,col);
3759                 }
3760         }
3761         Noise1.draw();
3762         sampleA.flip();
3763         while(!Input::get(Keyboard::spc));
3764 }
3765 }}}
3766
3767 !Image::rect()
3768 オフスクリーン領域に四角形を描画するための命令です。
3769 実際の描画処理は''Image::draw()''命令で行われます。
3770 色の指定についての詳細は[[2.2.5 描画する図形の色を指定する-Colorクラス-]]を参照してください。
3771
3772 |!Image::rect()|rect([[Psychlops::Rectangle]] rect, [[Psychlops::Color]] col)|[[Psychlops::Rectangle]]型の四角形を[[Psychlops::Color]] col色で描画します|
3773 |~|~|[[Psychlops::Rectangle]] rect : 描画する四角形を指定|
3774 |~|~|[[Psychlops::Color]] col:描画する四角形の色を指定|
3775
3776 !!Image::rect()の書き方
3777 100×100のImageに青の四角形を描画します。
3778 {{{
3779 &lt;例2&gt;
3780 #include &lt;psychlops.h&gt;
3781 using namespace Psychlops;
3782
3783 Psychlops::Rectangle rect1(100,100);
3784 Psychlops::Image Noise1(rect1);
3785
3786 void psychlops_main() {
3787
3788         Canvas sampleA(Canvas::fullscreen);
3789         Noise1.rect(rect1,Color::blue);
3790         Noise1.draw();
3791         sampleA.flip();
3792         while(!Input::get(Keyboard::spc));
3793 }
3794 }}}</pre>
3795 </div>
3796 <div title="3.1.3 オフスクリーン上の任意の位置に描画する" modifier="Psychlops_DevelopperG" modified="200908190258" created="200709042020" changecount="16">
3797 <pre>前節でオフスクリーン上に図形の描画を行いました。
3798 描画位置の指定がない場合、オフスクリーン領域は常に画面の左上に設定されます。
3799 この節ではオフスクリーン領域を任意の場所に移動します。
3800 2.2節ではそれぞれ[[Psychlops::Point型の移動|2.2.1 画面上に点を描画する-Pointクラス1-]]と[[Psychlops::Rectangle型の移動|2.2.6 画面上に縞を描画する-Rectangleクラス2-]]の移動について説明しました。
3801 オフスクリーン領域の移動も上記の型と同様の命令文を使用します。
3802
3803 !Image::centering()
3804 [[Psychlops::Image]]型の領域を移動します。
3805 座標を指定する場合、その座標に領域の中心が設定されます。
3806 |!Image::centering()|centering()|[[Psychlops::Image]]型の領域を画面中心に移動します|
3807 |~|centering(double x, double y)|[[Psychlops::Image]]型の領域の中心を指定した座標(x,y)に移動します|
3808 |~|~|double x:移動する領域のx座標を指定|
3809 |~|~|double y:移動する領域のy座標を指定|
3810
3811 !!Image::centering()の書き方
3812 前節の&lt;例1&gt;で作成したImageを画面中心に移動します。
3813 {{{
3814 &lt;例1&gt;
3815 #include &lt;psychlops.h&gt;
3816 using namespace Psychlops;
3817
3818 double width=100,height=100;
3819 Psychlops::Rectangle rect1(width,height);
3820 Psychlops::Color col;
3821 Psychlops::Image Noise1(rect1,Image::GRAY);
3822
3823 void psychlops_main() {
3824
3825         Canvas sampleA(Canvas::fullscreen);
3826         Noise1.centering();
3827         
3828         //水平方向に2,垂直方向に5間隔の点を描画する。
3829         for(int x=0; x&lt;width; x+=2){
3830                 for(int y=0; y&lt;height; y+=5){
3831                         col.set(0.5);
3832                         Noise1.pix(x,y,col);
3833                 }
3834         }
3835         Noise1.draw();
3836         sampleA.flip();
3837         while(!Input::get(Keyboard::spc));
3838 }
3839 }}}
3840
3841 !Image::shift()
3842 [[Psychlops::Image]]型の領域を移動します。
3843 元の座標(x1,y1)を基点に入力値(x,y)だけ移動します。
3844 |!Image::shift()|shift(double h, double v)|座標(X,Y)を(h,v)だけ移動します|
3845 |~|~|double x:入力値分、右水平方向に移動|
3846 |~|~|double y:入力値分、下垂直方向に移動|
3847
3848 !!Image::shift()の書き方
3849 前節の&lt;例2&gt;で作成したImageを座標(100,50)分移動します。
3850 {{{
3851 &lt;例2&gt;
3852 #include &lt;psychlops.h&gt;
3853 using namespace Psychlops;
3854
3855 Psychlops::Rectangle rect1(100,100);
3856 Psychlops::Image Noise1(rect1);
3857
3858 void psychlops_main() {
3859
3860         Canvas sampleA(Canvas::fullscreen);
3861         Noise1.shift(100,50);
3862         Noise1.rect(rect1,Color::blue);
3863         Noise1.draw();
3864         sampleA.flip();
3865         while(!Input::get(Keyboard::spc));
3866 }
3867 }}}</pre>
3868 </div>
3869 <div title="3.1.4 発展的な内容" modifier="Psychlops_Admin" created="200709252141" changecount="1">
3870 <pre>この節ではは少し発展的で込み入った内容を取り扱います。ここまでの命令を使ってとりあえず自分の目的が達成できている場合は無視しても構いません。
3871 複雑で大きなサイズのイメージを高速でどうしても表示したい時にだけ、読んでみてください。
3872
3873 !Image::quicken()
3874 3.1.1節でサイズが大きなImageをCanvasに転送するには時間がかかるという説明をしました。
3875 この問題を解決する方法が一つあります。
3876 転送するのにかかる時間の総和を短縮することはできませんが、PsychlopsはImageをCanvasへより早く転送することのできるメモリ領域へ転送することができます。
3877
3878 Canvasに描画する場合はCanvasの裏に描画して[[Canvas::flip()]]命令で画面に表示しています。
3879 Imageはコードの記述はCanvasと大差ありませんが、Canvasの裏ではなくメインメモリ(主記憶装置)に描画しています。
3880 [[Image::draw()]]命令を実行することで初めてメインメモリ(主記憶装置)にある図形がVRAMに転送され、flip()とともに画面に表示されます。
3881 &lt;[[Image::draw()]]命令実行時のイメージ図&gt;
3882 [img[image/draw()命令.png]]
3883
3884 つまり普通であればImageからCanvasへ直接転送が起こります。
3885 ここで、この2つの中間地点にあらかじめ転送しておくことで、Canvasへの転送時間を見かけ上小さくすることができるのです。
3886 この中間地点はVRAMと呼ばれるビデオボード上のメモリチップの中に作られます。
3887 実は、VRAM内でのメモリの転送速度に比べてメインメモリからVRAMへの転送はとても時間がかかるのです。
3888 この時間がかかる部分を事前に行っておくことで、見かけ上の転送時間を大きく縮めることができます。
3889 この転送を事前に一部行っておく命令がImage::quicken()命令です。
3890 <<図>>
3891 [img[image/quicken()命令.png]]
3892 &lt;quickenモード&gt;
3893
3894 通常、[[Image::draw()]]命令を使用すると上図のようにメインメモリ(主記憶装置)から画面まで一気に転送し描画します。
3895 quicken()を実行すると、Imageの図形をVRAMに転送し、quickenモードに切り替えます。
3896 そして[[Image::draw()]]命令でVRAMにある図形を画面に転送し表示します。
3897
3898 |!Image::quicken()|quicken()|メインメモリ(主記憶装置)にあるImageの内容をあらかじめVRAMに送ります|
3899 |~|~|解除する場合はquicken(false)と記述|
3900
3901 !!Image::quicken()の書き方
3902 [[Image::draw()]]命令の直前に記述します。
3903 {{{
3904 &lt;例1&gt;
3905 #include &lt;psychlops.h&gt;
3906 using namespace Psychlops;
3907
3908 double width=100,height=100;
3909 Psychlops::Rectangle rect1(width,height);
3910 Psychlops::Color col;
3911 Psychlops::Image Noise1(rect1);
3912
3913 void psychlops_main() {
3914
3915         Canvas sampleA(Canvas::fullscreen);
3916         Noise1.centering();
3917         for(int x=0; x&lt;width; x+=3){
3918                 for(int y=0; y&lt;height; y+=3){
3919                         col.set(0.5,0.2,0.8);
3920                         Noise1.pix(x,y,col);
3921                 }
3922         }
3923         Noise1.quicken();
3924         Noise1.draw();
3925         sampleA.flip();
3926         while(!Input::get(Keyboard::spc));
3927 }
3928 }}}
3929
3930 !!Image::quicken()の使用上の注意
3931 ただし、この転送を行うためには
3932 # ImageがRGB/RGBAモードで宣言されていること
3933 # Imageへの描画がすでに完了していること
3934 が必要です。
3935
3936 特に2番目については注意が必要です。
3937 Imageに対する描画命令は全てメインメモリ上にあるImageに対して行われます。
3938 したがって、すでにquicken()してしまったimageに対して描画を行っても、変更が反映されません。
3939 {{{
3940 &lt;例2&gt;
3941 #include &lt;psychlops.h&gt;
3942 using namespace Psychlops;
3943
3944 double width=100,height=100;
3945 Psychlops::Rectangle rect1(width,height);
3946 Psychlops::Color col;
3947 Psychlops::Image Noise1(rect1);
3948
3949 void psychlops_main() {
3950
3951         Canvas sampleA(Canvas::fullscreen);
3952         Noise1.quicken();
3953         Noise1.centering();
3954         while(!Input::get(Keyboard::spc)){
3955                 for(int x=0; x&lt;width; x+=3){
3956                         for(int y=0; y&lt;height; y+=3){
3957                                 col.set(0.5,0.2,0.8);
3958                                 Noise1.pix(x,y,col);
3959                         }
3960                 }
3961         Noise1.draw();
3962         sampleA.flip();
3963         }
3964 }
3965 }}}
3966 この新しい描画内容を画面に反映させるには、quicken(false)命令を使ってquicken()モードを解除するか、再度Image::quicken()命令を実行してもう一度転送を行う必要があります。&lt;プログラム例&gt;
3967 (転送を行ってもメインメモリ上にコピーは残っているので、単にこれをもう一度転送し直すことで描画を反映させることができます)
3968 {{{
3969 &lt;例3&gt;
3970 #include &lt;psychlops.h&gt;
3971 using namespace Psychlops;
3972
3973 double width=100,height=100;
3974 Psychlops::Rectangle rect1(width,height);
3975 Psychlops::Color col;
3976 Psychlops::Image Noise1(rect1);
3977
3978 void psychlops_main() {
3979
3980         Canvas sampleA(Canvas::fullscreen);
3981         Noise1.quicken();
3982         Noise1.centering();
3983         while(!Input::get(Keyboard::spc)){
3984                 for(int x=0; x&lt;width; x+=3){
3985                         for(int y=0; y&lt;height; y+=3){
3986                                 col.set(0.5,0.2,0.8);
3987                                 Noise1.pix(x,y,col);
3988                         }
3989                 }
3990         Noise1.draw();
3991         sampleA.flip();
3992         Noise1.quicken(false);
3993         }
3994 }
3995 }}}
3996 ※2007年9月現在、quicken()命令はRGB/RGBAでのみ稼働</pre>
3997 </div>
3998 <div title="3.2 オフスクリーンを用いた描画例-ランダムドットパターン-" modifier="YourName" modified="200802191418" created="200709052137" changecount="18">
3999 <pre>この節ではImageクラスを用いたオフスクリーンへの描画の利用例として、下図に示した砂嵐のような図、ランダムドットパターンを描画してみます。
4000 [img[image/randomdot.png]]
4001 はじめにドットサイズが1ピクセル四方の基本的なパターンを描画する例を説明した後、ドットサイズがもっと大きいものについて説明します。
4002
4003 [[3.2.1 基本的なランダムドットを描画する]]
4004 [[3.2.2 ドットサイズの異なるランダムドットパターンの描画]]
4005 [[3.2.3 ランダムドットキネマトグラムの描画]]
4006
4007 この節では、複数の細いImageをshift()命令を使って並べることによって、ランダムドットを描画しています。この方法は簡単ですが、ドット密度やドットの運動方向を精密に操作したい場合は、Rectangleの配列を用いた方が描画がスムーズな場合があります。これについては、[[Tips: Rectangleの配列を用いたドットパターンの描画例]]を参照してください。</pre>
4008 </div>
4009 <div title="3.2.1 基本的なランダムドットを描画する" modifier="YourName" modified="200802232254" created="200709130524" changecount="7">
4010 <pre>PsychlopsではImageを用いてランダムドットパターンが比較的簡単に描画できます。
4011 ランダムドットを描画するためには''Psychlops::random()''命令を使用します。
4012 色の返値を''Psychlops::random()''命令を使用しランダムに値を返すことでランダムドットパターンが描画できます。
4013
4014 !!Psychlops::random()
4015 0~1,0~任意の数等好きに設定した値の中でランダムに数を返します。
4016
4017 |!Psychlops::random()|random()|0~1の値をランダムに返します。|
4018 |~|random(double N)|0~Nの値をランダムに返します。|
4019 |~|~|double N: ランダムで返す値の最大値を指定|
4020 |~|random(int N)|0~Nの整数値をランダムに返します。|
4021 |~|~|int N: ランダムで返す整数値の最大値を指定|
4022 |~|random(double min, double max)|min~maxの値をランダムに返します。|
4023 |~|~|double min: ランダムで返す値の最小値を指定|
4024 |~|~|double max: ランダムで返す値の最大値を指定|
4025
4026 引数が実数(double)か整数(int)かで動作が異なりますので、実数で返したい場合は最大値が整数でも必ず1.0などのように小数点をつけて指定してください。整数変数を指定する場合は(double)でキャストしてください。
4027 {{{
4028 random(5)   ← [0,1,2,3,4] のいずれかの数がint型で返る
4029 random(5.0) ← [0, 5)の間の浮動小数点値が返る
4030
4031 int i = 5;
4032 random(i)         ← [0,1,2,3,4] のいずれかの数がint型で返る
4033 random((double)i) ← [0, 5)の間の浮動小数点値が返る
4034 }}}
4035
4036 !!!Psychlops::random()の書き方
4037 3.1.3節の&lt;例1&gt;では画面中央のImage上に点を描画しました。
4038 このコードを応用してランダムドットを描画します。
4039 3.1.3節の&lt;例1&gt;では水平方向に2 ピクセル,垂直方向に5 ピクセル間隔に色:0.5の点を描画しました。
4040 ここではグレースケールのランダムドットを描画するために、色(輝度)が0 ~ 1の間でランダムに設定された、100 x 100 ピクセル四方の隙間のないドット列を描画します。
4041 {{{
4042 &lt;例1&gt;
4043 #include &lt;psychlops.h&gt;
4044 using namespace Psychlops;
4045
4046 double width=100,height=100;
4047 double temp1;
4048 Psychlops::Rectangle rect1(width,height);
4049 Psychlops::Color col;
4050 Psychlops::Image Noise1(rect1,Image::GRAY);
4051
4052 void psychlops_main() {
4053
4054         Canvas sampleA(Canvas::fullscreen);
4055         Noise1.centering();
4056         for(int x=0; x&lt;width; x++){
4057                 for(int y=0; y&lt;height; y++){
4058                         //0~1の値をランダムに返す
4059                         temp1=Psychlops::random();
4060                         col.set(temp1);
4061                         Noise1.pix(x,y,col);
4062                 }
4063         }
4064         Noise1.draw();
4065         sampleA.flip();
4066         while(!Input::get(Keyboard::spc));
4067 }
4068 }}}
4069 &lt;例1&gt;では[[カラーモード]]をGRAYで描画しましたがRGB,RBGAでも描画可能です。
4070 &lt;例2&gt;ではRGBAでランダムドットを描画します。
4071 {{{
4072 &lt;例2&gt;
4073 #include &lt;psychlops.h&gt;
4074 using namespace Psychlops;
4075
4076 double width=100,height=100;
4077 double temp1,temp2,temp3,temp4;
4078 Psychlops::Rectangle rect1(width,height);
4079 Psychlops::Color col;
4080 Psychlops::Image Noise1(rect1,Image::RGBA);
4081
4082 void psychlops_main() {
4083
4084         Canvas sampleA(Canvas::fullscreen);
4085         Noise1.centering();
4086         for(int x=0; x&lt;width; x++){
4087                 for(int y=0; y&lt;height; y++){
4088                         //R,G,B,Aそれぞれに0~1の値をランダムに返します
4089                         temp1=Psychlops::random();
4090                         temp2=Psychlops::random();
4091                         temp3=Psychlops::random();
4092                         temp4=Psychlops::random();
4093                         col.set(temp1,temp2,temp3,temp4);
4094                         Noise1.pix(x,y,col);
4095                 }
4096         }
4097         Noise1.draw();
4098         sampleA.flip();
4099         while(!Input::get(Keyboard::spc));
4100 }
4101 }}}
4102
4103 [img[image/colornoise.png]]
4104
4105 </pre>
4106 </div>
4107 <div title="3.2.2 ドットサイズの異なるランダムドットパターンの描画" modifier="Psychlops_Admin" modified="200709131828" created="200709130500" changecount="4">
4108 <pre>今まで描画したランダムドットパターンのドットサイズは1 ピクセル四方でした。
4109 次はピクセルサイズがn( n &gt; 1 )のランダムドットパターンを描画します。
4110 1 ピクセルのドットを描画する時[[Image::pix()]]命令を使用していましたが
4111 n ピクセルのドットを描画するためには[[Image::rect()]]命令を使用します。
4112 横幅×高さの領域にn ピクセルのランダムドットパターンを描画する場合、以下の要領で描画します。
4113 {{{
4114 1.ドットサイズ × ( 高さ × ドットサイズ )のImageをN個(横幅/ドットサイズ)用意する。
4115 2.ドットサイズ × ドットサイズのrectをN個( 横幅 / ドットサイズ )用意する。
4116 3.1で作成したImage(x)に2で作成したrect(x)をセットする。
4117 4.2で作成したrect(x)にランダムドットパターンを描画する。
4118 5.rectをドットサイズ縦方向に移動し、Image(x)全てに描画されるまで3~4を行う。
4119 6.Imageを横幅の長さまで並べる。
4120 }}}
4121 [img[image/dotsize.png]]
4122
4123 下の例では、横幅がwドット分、縦幅がhドット分のランダムドットパターンを描画しています。ここで指定した単位がドット単位でピクセル単位でないことに注意して、例を見てみてください。
4124 {{{
4125 &lt;例3&gt;
4126 #include &lt;psychlops.h&gt;
4127 using namespace Psychlops;
4128
4129 const int dotsize=4;
4130 const int width=100;
4131 double height=50;
4132 double temp1;
4133 Psychlops::Rectangle rect1[width];
4134 Psychlops::Color col;
4135 Psychlops::Image Noise1[width];
4136
4137 void psychlops_main() {
4138
4139         Canvas sampleA(Canvas::fullscreen);
4140         
4141         for(int x=0; x&lt;width; x++){
4142                 Noise1[x].set(dotsize,height*dotsize);
4143                 rect1[x].set(dotsize, dotsize);
4144                 for(int y=0; y&lt;height; y++){
4145                         temp1=Psychlops::random();
4146                         col.set(temp1);
4147                         Noise1[x].rect(rect1[x],col);
4148                         //ドットサイズ分下に移動する                       
4149                         rect1[x].shift(0, dotsize);
4150                 }       
4151                 Noise1[x].centering().shift((double)-1/2*(width*dotsize)+x*dotsize,0);
4152                 Noise1[x].draw();
4153         }
4154         sampleA.flip();
4155         while(!Input::get(Keyboard::spc));
4156 }
4157 }}}
4158 ドットサイズを他の値に変えて実行したい場合は『const int dotsize=4;』の値を変更してください。
4159 この例では、画面中央にランダムドットパターンを描画しました。画面中央に描画するための式は[[2.2.6節|2.2.6 画面上に縞を描画する-Rectangleクラス2-]]を参照してください。
4160
4161 ここでは( Xc - 1 / 2 * w + i , Yc )のwとiにドットサイズ dotsize を乗算しています。
4162 ドットサイズを乗算することでImageが重ならないように移動して描画しています。
4163 試しにドットサイズの乗算を外すと、1ピクセル分のみ移動されて最後の列のみ正常に、他の列は細長いドットが表示されます。これはImageが重なって描画されてしまっているためです。
4164 [img[image/dotsize2.png]]
4165 </pre>
4166 </div>
4167 <div title="3.2.3 ランダムドットキネマトグラムの描画" modifier="Psychlops_Admin" created="200709252142" changecount="1">
4168 <pre>!ランダムドットキネマトグラムを描画しよう
4169 次は上記のランダムドットを水平方向に動かしてみましょう。
4170 このような運動するランダムドットパターンをランダムドットキネマトグラムといいます。
4171
4172 今回は水平方向に移動するのでX座標( Xc - 1 / 2 * w + i )について考慮します。
4173 ( Xc - 1 / 2 * w + i ) + α と記述すると α 分移動します。
4174 しかし既存の横幅内での運動を行いたいので
4175 下図のように右に飛び出たα を前に移動します。
4176 移動要素である i + α に対しwで除算し余りを算出することで求まります。
4177 {{{
4178 ( Xc - 1 / 2 * w ) + ( i + α ) % w
4179 }}}
4180 [img[image/rdk.png]]
4181 &lt;例3&gt;ではドットサイズを変更しているので、それぞれの値をドットサイズ倍する必要があります。
4182 {{{
4183 ( Xc - 1 / 2 * w*dotsize ) + ( i *dotsize+ α*dotsize ) % w*dotsize
4184 }}}
4185
4186 次に α の内容について考えてみましょう。
4187 1フレームあたりの移動量をnドット分とすると、mフレーム目での0フレーム目の位置からのピクセル単位での移動量αは、 n* dotsize * m となります。
4188 つまりi番目のimageの水平方向の移動量は、以下のようになります。
4189 {{{
4190  ( Xc - 1 / 2 * w * dotsize ) + ( i *dotsize+ frame*speed*dotsize ) % w*dotsize
4191 }}}
4192 第1項(+の前)はImageをXcを中心として並べるための移動量、第2項が運動するパターンを実現するための移動量です。
4193
4194 早速上記式を用いて描画します。
4195 {{{
4196 &lt;例4&gt;
4197 #include &lt;psychlops.h&gt;
4198 using namespace Psychlops;
4199
4200 const int dotsize=4;
4201 const int width=100;
4202 double height=50;
4203 double temp1;
4204 double speed=1;
4205 double phase=0;
4206 int frame=0;
4207 Psychlops::Rectangle rect1[width];
4208 Psychlops::Color col;
4209 Psychlops::Image Noise1[width];
4210
4211 void psychlops_main() {
4212
4213         Canvas sampleA(Canvas::fullscreen);
4214         
4215         for(int x=0; x&lt;width; x++){
4216                 Noise1[x].set(dotsize,height*dotsize);
4217                 rect1[x].set(dotsize, dotsize);
4218                 for(int y=0; y&lt;height; y++){
4219                         temp1=Psychlops::random();
4220                         col.set(temp1);
4221                         Noise1[x].rect(rect1[x],col);
4222                         rect1[x].shift(0, dotsize);
4223                 }       
4224         }
4225         while(!Input::get(Keyboard::spc)){
4226                 for(int x=0; x&lt;width; x++){
4227                         phase=(double)-1/2*(width*dotsize)+(int)(x*dotsize+frame*speed*dotsize)%(int)(width*dotsize);
4228                         Noise1[x].centering().shift(phase,0);
4229                         Noise1[x].draw();
4230                 }
4231                 sampleA.flip();
4232                 frame++;
4233         }
4234 }
4235 }}}
4236
4237 水平方向へ移動するドットパターンが描画されたでしょうか?
4238 これと同様にしてランダムドットステレオグラムを書くこともできます。[[Tips: ランダムドットステレオグラムの描画コード]]を参照してみてください。</pre>
4239 </div>
4240 <div title="3.3 画像ファイルの取り扱い" modifier="Psychlops_Admin" modified="200709121657" created="200708292225" changecount="4">
4241 <pre>この節では、画像ファイルの取扱いについて説明します。
4242 現バージョンのPsychlopsではpng形式のファイルの読込と保存が可能です。
4243 この読込と保存はImage型を通して行われます。
4244 読み込まれた画像ファイルはPsychlops内部ではImage型の変数(インスタンス)として扱われます。
4245 同様に保存はImage型の描画内容をPng形式でファイルに書き出すことによって行われます。
4246 以下では、この2つについて説明します。
4247
4248 [[3.3.1 画像ファイルを保存する]] 
4249 [[3.3.2 画像ファイルを読み込み表示する]] 
4250 </pre>
4251 </div>
4252 <div title="3.3.1 画像ファイルを保存する" modifier="Psychlops_DevelopperG" modified="200910080846" created="200709111925" changecount="34">
4253 <pre>ディスプレイに提示した画像をプログラムが終了した後でも使えるように保存したい時がよくあります。
4254 この節では、画面上に描画した図形を外部ファイルとして保存する方法について説明します。
4255 PsychlopsではImageの描画内容を、PNG形式の画像ファイルとして保存することができます。
4256 これにはImage::save()命令を使用します。はじめにこのImage::save()命令の使い方と例を見てみましょう。
4257
4258 !Image::save()
4259 画像ファイルをpng形式で保存します。
4260
4261 |!Image::save()|save(filename)|!指定したImageを保存します|&gt;|&gt;|
4262 |~|~|filename| 保存するImageの画像ファイル名称(拡張子まで)を指定|&gt;|&gt;|
4263 |~|~|保存先を指定しない場合のパス|Mac:Macintosh HD/ユーザ/ユーザ名(任意)/書類/Psychlops/ ^^*1^^|&gt;|
4264 |~|~|~|Win:実行ファイルと同じパス|&gt;|
4265 |~|~|保存をする場合のパス指定|%APP%|Mac:実行バンドル(.app)と同じパス|
4266 |~|~|~|~|Win:実行ファイルと同じパス|
4267 |~|~|~|%RESOURCES%|Mac:実行バンドルの内部のResourcesフォルダ|
4268 |~|~|~|~|Win:実行ファイルのパスの直下のrscという名前のフォルダ|
4269 |~|~|~|%~USER_HOME%|Mac:&quot;~/&quot;と同じ(/Users/(user)/)|
4270 |~|~|~|~|Win:ユーザープロファイルフォルダ(C:\Documents and Settings\(user)\)|
4271 |~|~|~|%~USER_SETTING%|Mac:&quot;~/Library/Psychlops/&quot;|
4272 |~|~|~|~|Win:&quot;%~AppData%Psychlops&quot;|
4273 |~|~|~|%~USER_DOCUMENTS%|Mac:&quot;~/Documents/Psychlops&quot;|
4274 |~|~|~|~|Win:ユーザープロファイルフォルダ\My Documents\Psychlops|
4275 |~|~|~|%~USER_DOCUMENTS_ROOT%|Mac:&quot;~/Documents&quot;|
4276 |~|~|~|~|Win:ユーザープロファイルフォルダMy Documents\|
4277 *1 Finderでの表示は上記のとおりですが、Psychlopsで指定する場合は下記のUnix形式でパスを指定してください
4278 ** &quot;~/Documents/Psychlops/&quot;
4279 *2 MacOSX版では、日本語パス名は使用できません。強制終了してしまいます。
4280 *3 VISTA環境の場合、パス指定が正常に動作しません。VISTA環境の方はお手数ですが保存先を指定しないでお使いください。
4281
4282 !Canvas::to()
4283 この命令は[[Psychlops::Rectangle]]型の情報を[[Psychlops::Image]]型に移動する命令です。
4284 この命令と''Image::save()''命令を組み合わせることによってImage上以外で描画した画像が保存できます。
4285 |!Canvas::to()|to([[Psychlops::Image]] img, [[Psychlops::Rectangle]] rect) |&gt;|![[Psychlops::Rectangle]]型の情報を[[Psychlops::Image]]型に移動する|
4286 |~|~|[[Psychlops::Image]] img |移動先のImageを指定|
4287 |~|~|[[Psychlops::Rectangle]] rect |移動元のRectangleを指定|
4288
4289 ここで注意する点が一つあります。
4290 Canvas::to()命令は[[Psychlops::Rectangle]]型の引数で指定された大きさを取得して移動先のImageのサイズに指定します。
4291 このときに移動先のImageのサイズがあらかじめセットされていると、プログラムが予期しない動作をすることがあります。
4292 変数の宣言だけが行われていて、まだセットをされていないImage型を使用するか、
4293 Image::release()命令を使って一度Imageの中身をクリアしてからCanvas::to()命令を使うようにして下さい。
4294
4295 |!Image::release()|release(void) |メモリ内のイメージの内容を解放する|
4296
4297 !!Image::save()の書き方
4298 [[Canvas::pix()|2.2.1 画面上に点を描画する-Pointクラス1-]]命令でCanvas上に描画したランダムドットをImageに移動し保存します。
4299 ''Canvas::to()''命令は[[Psychlops::Rectangle]] 型が移動元になるので
4300 描画した点と同じ場所に空の[[Psychlops::Rectangle]] 型を設定する必要があります。
4301
4302 &lt;例1&gt;では座標(400,500)に100 × 100のランダムドットを描画し保存します。
4303 centerX,centerYの値を変更することで描画する座標位置が変わります。
4304 0,0を代入もしくは削除すると左上に描画されます。
4305 {{{
4306 &lt;例1&gt;
4307 #include &lt;psychlops.h&gt;
4308 using namespace Psychlops;
4309
4310 double width=100,height=100;
4311 double temp1;
4312 double centerX=700,centerY=500;
4313 Psychlops::Rectangle rect1(width,height);
4314 Psychlops::Color col;
4315 Psychlops::Image Noise1;
4316
4317 void psychlops_main() {
4318
4319         Canvas sampleA(Canvas::fullscreen);
4320         rect1.shift(centerX,centerY);
4321         for(int x=0; x&lt;width; x++){
4322                 for(int y=0; y&lt;height; y++){
4323                         temp1=Psychlops::random();
4324                         col.set(temp1);
4325                         sampleA.pix(x+centerX,y+centerY,col);
4326                 }
4327         }
4328         sampleA.to(Noise1,rect1);
4329         sampleA.flip();
4330         while(!Input::get(Keyboard::spc));
4331         Noise1.save(&quot;test.png&quot;);
4332 }
4333 }}}
4334 次は保存するパスを指定して保存します。
4335 {{{
4336 &lt;例2&gt;
4337 #include &lt;psychlops.h&gt;
4338 using namespace Psychlops;
4339
4340 double width=100,height=100;
4341 double temp1;
4342 double centerX=700,centerY=500;
4343 Psychlops::Rectangle rect1(width,height);
4344 Psychlops::Color col;
4345 Psychlops::Image Noise1;
4346
4347 void psychlops_main() {
4348
4349  Canvas sampleA(Canvas::fullscreen);
4350  rect1.shift(centerX,centerY);
4351  for(int x=0; x&lt;width; x++){
4352   for(int y=0; y&lt;height; y++){
4353    temp1=Psychlops::random();
4354    col.set(temp1);
4355    sampleA.pix(x+centerX,y+centerY,col);
4356   }
4357  }
4358  sampleA.to(Noise1,rect1);
4359  sampleA.flip();
4360  while(!Input::get(Keyboard::spc));
4361  Noise1.save(&quot;%APP%test.png&quot;);
4362 }
4363 }}}
4364 指定のパスに保存されたか確認してください。</pre>
4365 </div>
4366 <div title="3.3.2 画像ファイルを読み込み表示する" modifier="YourName" modified="200802131204" created="200709111918" changecount="13">
4367 <pre>今までの節では自分でオフスクリーン上に図形を描画しました。
4368 この節では既存の画像ファイルを読み込みます。
4369
4370 !Image::load()
4371 既存の任意の画像ファイルを読み込みます。
4372
4373 |!Image::load()|load(filename)|指定した画像ファイルを読み込みます|&gt;|&gt;|
4374 |~|~|filename: 読み込む画像ファイルの名前を指定|&gt;|&gt;|
4375 |~|~|保存先を指定しない場合のパス|Mac:Macintosh HD/ユーザ/ユーザ名(任意)/書類/Psychlops/ ^^*1^^|&gt;|
4376 |~|~|~|Win:実行ファイルと同じパス|&gt;|
4377 |~|~|読込みをする場合のパス指定|%APP%|Mac:実行バンドル(.app)と同じパス|
4378 |~|~|~|~|Win:実行ファイルと同じパス|
4379 |~|~|~|%RESOURCES%|Mac:実行バンドルの内部のResourcesフォルダ|
4380 |~|~|~|~|Win:実行ファイルのパスの直下のrscという名前のフォルダ|
4381 |~|~|~|%~USER_HOME%|Mac:&quot;~/&quot;と同じ(/Users/(user)/)|
4382 |~|~|~|~|Win:ユーザープロファイルフォルダ(C:\Documents and Settings\(user)\)|
4383 |~|~|~|%~USER_SETTING%|Mac:&quot;~/Library/Psychlops/&quot;|
4384 |~|~|~|~|Win:&quot;%~AppData%Psychlops&quot;|
4385 |~|~|~|%~USER_DOCUMENTS%|Mac:&quot;~/Documents/Psychlops&quot;|
4386 |~|~|~|~|Win:ユーザープロファイルフォルダ\My Documents\Psychlops|
4387 |~|~|~|%~USER_DOCUMENTS_ROOT%|Mac:&quot;~/Documents&quot;|
4388 |~|~|~|~|Win:ユーザープロファイルフォルダMy Documents\|
4389 *1 Finderでの表示は上記のとおりですが、Psychlopsで指定する場合は下記のUnix形式でパスを指定してください
4390 ** &quot;~/Documents/Psychlops/&quot;
4391 *2 MacOSX版では、日本語パス名は使用できません。強制終了してしまいます。
4392 *3 VISTA環境の場合、パス指定が正常に動作しません。VISTA環境の方はお手数ですが保存先を指定しないでお使いください。
4393
4394
4395 !Image::load()の書き方
4396 [[3.3.1節|3.3.1 画像ファイルを保存する]]の&lt;例1&gt;で保存したランダムドットを読み込みます。
4397 表示は[[Image::draw()]]で行います。
4398 {{{
4399 &lt;例1&gt;
4400 #include &lt;psychlops.h&gt;
4401 using namespace Psychlops;
4402
4403 Psychlops::Image Noise1;
4404
4405 void psychlops_main() {
4406
4407         Canvas sampleA(Canvas::fullscreen);
4408         Noise1.load(&quot;test.png&quot;);
4409         Noise1.centering();
4410         Noise1.draw();
4411         sampleA.flip();
4412         while(!Input::get(Keyboard::spc));
4413 }
4414 }}}
4415 次にパス指定を行い、画像ファイルを読み込みます。
4416 {{{
4417 &lt;例2&gt;
4418 #include &lt;psychlops.h&gt;
4419 using namespace Psychlops;
4420
4421 Psychlops::Image Noise1;
4422
4423 void psychlops_main() {
4424
4425         Canvas sampleA(Canvas::fullscreen);
4426         Noise1.load(&quot;%APP%test.png&quot;);
4427         Noise1.centering();
4428         Noise1.draw();
4429         sampleA.flip();
4430         while(!Input::get(Keyboard::spc));
4431 }
4432 }}}</pre>
4433 </div>
4434 <div title="3.4 Canvasのオフスクリーンへの取り込み" modifier="YourName" modified="200802191336" created="200710240512" changecount="3">
4435 <pre>スクリーンキャプチャのように、画面上に描画したイメージをオフスクリーンに取り込むことができます。
4436 |!Canvas::to()|to(Image target, Rectangle source)|VRAM上にある画面イメージをメインメモリ(主記憶装置)にあるImageに転送します|
4437 |~|~|Image target: 転送先のImageを指定します|
4438 |~|~|Rectangle source: 転送する画面上の範囲を指定します|
4439 この際、転送先のImageがsetされていなければ自動的にsetします。setされていた場合、破棄されて上書きされます。
4440
4441 {{{
4442 #include &lt;psychlops.h&gt;
4443 using namespace Psychlops;
4444
4445 void psychlops_main() {
4446
4447         Canvas d(Canvas::fullscreen);
4448
4449         Psychlops::Rectangle rect(100,100);
4450         rect.centering();
4451
4452         // check rect external bordar and Rectangle.resize validity
4453         rect.resize(102,102).draw(Color::red);
4454
4455         // check rect internal bordar and Rectangle.resize validity
4456         rect.resize(100,100).draw(Color::green);
4457
4458         // rect reference
4459         rect.resize(98,98).draw(Color::gray);
4460
4461
4462         rect.resize(100,100);
4463
4464         // check Canvas.to(Image, Rectangle) validity
4465         Image img;
4466         d.to(img, rect);
4467         img.centering().shift(0,-200).draw();
4468         d.msg(&quot;Canvas.to(Image)&quot;, d.getHcenter(), d.getVcenter()-200);
4469
4470         // check Canvas.copy(Rectangle, Rectangle) validity
4471         d.copy(rect, rect.dup().shift(200,0));
4472         d.msg(&quot;Canvas.copy&quot;, d.getHcenter()+200, d.getVcenter());
4473         d.flip();
4474
4475         // Wating roop
4476         while(!Input::get(Keyboard::esc)) {
4477         }
4478
4479 }
4480 }}}
4481 </pre>
4482 </div>
4483 <div title="3.5 文字列のレンダリングの仕様と関連情報" modifier="Psychlops_DevelopperG" modified="200910260739" created="200910260730" changecount="1">
4484 <pre>[[2.3節|2.3 文字列の描画]]ではLettersクラスを使った2バイト文字列の描画について最低限の事だけを説明しました。実はLettersクラスはImageクラスを応用して作成されています。
4485 ここでは、やや応用的ですが、本節で説明したImageクラスの仕様を踏まえてより詳細なLettersクラスの仕様と関連情報について説明します。
4486 * [[レンダリングのタイミング]]
4487 * [[ワイド文字列の入出力]]</pre>
4488 </div>
4489 <div title="4. 複雑な描画を行う2(複数オブジェクトのグループ化と回転・拡大縮小)" modifier="Psychlops_DevelopperG" modified="200912010726" created="200909300419" changecount="4">
4490 <pre>この章ではここまで用いてきたいくつかの描画のためのクラスをまとめて一つのグループとして取り扱う方法を紹介します。
4491 たとえば、いくつかのRectangleやImageをまとめて動かしたい時に、それぞれに対してshiftメソッドを使うというやり方は少し面倒です。
4492 こんな時に、これらを一つのグループにまとめてしまって、そのグループ全体に対して移動の操作をすることができれば、プログラムはかなり見通しがよくなるでしょう。
4493 また、描画したものをある点を中心に回転させたいというような時に、回転のための数学操作を考えるのはかなり面倒です。
4494 PsychlopsはOpenGLを用いて描画を行っているのですが、OpenGLは描画内容の回転や拡大・縮小をグラフィックチップに計算させることが簡単にできます。Psychlopsではグループ化したオブジェクトに対してこれらの変換を行うことが可能です。
4495
4496 [[4.1 複数の描画オブジェクトをグループ化するーGroupクラスー]]
4497 [[4.2 描画内容の回転および拡大縮小]]
4498
4499
4500 </pre>
4501 </div>
4502 <div title="4.1 複数の描画オブジェクトをグループ化するーGroupクラスー" modifier="Psychlops_DevelopperG" modified="200912010828" created="200912010750" changecount="27">
4503 <pre>!Groupクラスの構造
4504
4505 1. Groupクラスの変数を宣言する
4506 2. GroupクラスにShapeを追加する
4507 3. Groupクラスに対して操作を行う
4508 4.(必要に応じて)ShapeをGroupから外す
4509
4510 ! 1. Groupクラスの宣言
4511 グループクラスの変数宣言は特に引数などは必要ではありません。
4512 Canvasの取得後である必要がある?
4513 {{{
4514 code sample
4515 }}}
4516
4517 ! 2. GroupクラスにShapeを追加する
4518  ここまで取り扱ってきたRectangleやImageといったクラスはすべてShapeという大きなクラスを元に構成されています(クラスの継承)。Shapeクラスに属するクラスは以下の通りです。
4519 |Line|線領域|
4520 |Rectangle|四角形の領域|
4521 |Polygon|多角形の領域|
4522 |Ellipse|円を含む楕円形の領域|
4523 |Letters|文字列を格納するための領域(テキストボックス)|
4524
4525 Shapeクラスの詳細については[[]]
4526 これらのクラスはここまででも扱ってきたようなcentering(), shift(), draw()などの共通する命令セットを持っています。これらの命令が実際にどのように振る舞うかはクラスの内容に合わせて異なっていますが、引数などの命令の形はどれも共通になっています。これらの共通の命令セットの一つとしてadd()命令があります。
4527
4528 |!Shape::add()|add(Group gp)|&gt;|Shapeをグループクラスに追加します。|
4529 |~|~|Group gp|追加先のグループを指定|
4530
4531 コードの説明
4532 {{{
4533 code sample
4534 }}}
4535
4536 またGroupに対してappend()命令を使用しても同じことができます。
4537 使いやすい方を使うとよいでしょう。
4538 |!Group::append()|append(Shape shp)|&gt;|Shapeをグループクラスに追加します。|
4539 |~|~|Shape shp|追加するShapeを指定|
4540
4541 コードの説明
4542 {{{
4543 code sample
4544 }}}</pre>
4545 </div>
4546 <div title="5. 入出力を行う" modifier="Psychlops_DevelopperG" modified="200908190158" created="200708211945" changecount="3">
4547 <pre>[[5.1 外部装置(キーボード/マウス)の操作内容を取得する]] 
4548 [[5.2 ファイルの入出力]]</pre>
4549 </div>
4550 <div title="5.1 外部装置(キーボード/マウス)の操作内容を取得する" modifier="Psychlops_DevelopperG" modified="200908190201" created="200708232250" changecount="2">
4551 <pre>!Inputクラスとは
4552 コンピュータにあるキーボードやマウスから反応を取得するためのクラスとしてInputクラスがあります。
4553 現バージョンでPsychlopsはキーボードとマウスの反応を取得することが可能です。Psychlopsでは、キーボードのそれぞれのキーやマウスのボタン、カーソル位置をそれぞれKeyboard, Mouseクラスのメンバとして取り扱っています。具体的な使い方を以下で説明します。
4554
4555 !Input::get() -反応を取得する-
4556 これまでのソースの中でwhile(!Input::get(Keyboard::spc))という命令をスペースキー待ちの命令として使ってきました。これは、&quot;スペースキーが押されるまでループを繰り返しなさい&quot;という命令です。この文でも使っているようにInput::get()命令は引数として指定された入力機器の反応があれば1(TRUE)を返します。入力が無ければは0(FALSE)を返します。Input::get()命令の第2引数は省略可能です。この場合には、Input::get()命令は~KeyStateにはpushedが指定されたとみなします。
4557 |!Input::get()|Input::get([[Keyboard::KeyCode|キーコード表]] code, [[Keyboard::KeyState]] state)|キーボードの反応を取得します|
4558 |~|~|code 反応を取得するキーの名前 [[キーコード表]] 参照|
4559 |~|~|state 反応状態のオプション (省略時はpushed) [[Keyboard::KeyState]]参照|
4560 |~|Input::get([[Mouse::ButtonCode|キーコード表]] code, [[Mouse::ButtonState]] state)|マウスの反応を取得します|
4561 |~|~|code 反応を取得するマウスボタンの名前 [[キーコード表]] 参照|
4562 |~|~|state 反応状態のオプション (省略時はpushed) [[Mouse::ButtonState]]参照|
4563
4564 !! ~KeyState(~MouseState)の使い方
4565 第2引数のの&quot;~KeyState&quot;・&quot;~MouseState&quot;の使い方は少し難しいかもしれません。以下でこれらの&quot;~KeyState&quot;の使い方を見てみましょう。
4566 ~KeyStateには&quot;pushed&quot;, &quot;pressed&quot;, &quot;released&quot;の3つのモードが存在します。これら3つのモードの意味を実例とともに見ていきましょう。
4567
4568 はじめに&quot;pushed&quot;を使った例を見てみましょう。ここでは、Input::get()命令の第2引数として&quot;pushed&quot;を明示的に指示しましたが、省略しても、挙動は変わりません。
4569 [[例1: pushedを使ったソース]]
4570 このソースを実行すると中央に表示された四角形がカーソルの左右キーで左右に動きます。キーを押し続けても一度しかシフトが起こらないことに注目してください。&quot;pushed&quot;を指定したときにInput::get()命令がTRUEを返すのは、キーが押し込まれたフレームより後に実行されたうちのはじめの一回だけです。キーを離すと元の状態に戻って、またキーが押し込まれたフレーム以降に一度だけTRUEが返されます。
4571
4572 次に、&quot;pressed&quot;を指定した例です。例1とソースはほとんど変わりませんが、Input::get()命令の第2引数だけが&quot;pressed&quot;に変わっています。
4573 [[例2: pressedを使ったソース]]
4574 このソースを実行すると、やはりカーソルキーにしたがって四角形が左右に動きますが、例1と違って押している間、常に動き続けています。&quot;pressed&quot;を指定すると、キーが押し込まれている間ずっとInput::get()命令はTRUEを返します。
4575
4576 次に&quot;released&quot;を使った例について見てみましょう
4577 [[例3: releasedを使ったソース]]
4578 この例では、例2に&quot;released&quot;を指定したInput::get()命令の文が付け加わっています。実行してみると、四角形はやはりキーを押している間、常に動き続けますが、キーを離すと中央に戻ってしまいます。キーが離されたことが&quot;released&quot;を指定したInput::get()命令によって検出されると、四角形がcentering()命令を使って中央に戻るようにコードが組まれています。このように&quot;released&quot;は指定したキーが離されたことを検出するためのオプションです。
4579 ここで、例の中にInput::refresh()という命令が使われています。これは、キーボードやマウスのボタン検出を初期化する命令です。複雑な入出力動作をするプログラムを書くときにはこの命令でキーボード・マウスの初期化をしておくことをおすすめします。
4580 |!Input::refresh()|refresh(void)|キーボード・マウスの入出力ルーチンを初期化します|
4581 参考[[Tips: Psychlopsにおけるキーボードマウスの入出力検出ルーチン]]
4582
4583
4584 最後に、これらをまとめて比較するためのデモプログラム例を載せておきます。a/sでpushedのデモ, z/xでpressed, q/wでpressed &amp; released, cで四角形の位置初期化です。
4585 [[例4: pushed,pressed,releasedを使ったソース]]
4586
4587 以上では、キーボードの入力取得オプションである&quot;~KeyState&quot;を例として扱いましたが、&quot;~MouseState&quot;の使い方もまったく同じです。以下に&quot;~MouseState&quot;を使った簡単なプログラム例を挙げます。上の例2とほとんど変わりません。右クリックで右方向、左クリックで左方向へ移動します。
4588 [[例5: Mouse::pressedを使ったソース]]
4589
4590 ! カーソル座標の取得 とマウスカーソルの表示切替
4591 マウスの座標はMouseクラスのメンバMouse::x(水平座標), Mouse::y(垂直座標)に記録されています。このメンバに対して代入を行うとマウスの位置も移動します。
4592 |!Mouseのメンバ|Mouse::x|マウスの水平座標(代入可能)|
4593 |~|Mouse::y|マウスの水平座標(垂直座標)|
4594 マウスカーソルの表示切替はMouse::show(), Mouse::hide()命令を使用してください。
4595 |!Mouse::show()|show()|マウスカーソルを表示する|
4596 |!Mouse::hide()|hide()|マウスカーソルを非表示にする|
4597 次の例では、マウスカーソルを最初に画面中央に移動した後に、ユーザーのマウスの動きに合わせてマウスカーソルを動かします。左クリックすると、マウスカーソルの下にあるオブジェクトを選択して、ドラッグできます。また、右クリックでマウスカーソルを描画しないようにします。
4598 [[例6: マウスのデモ]]
4599 </pre>
4600 </div>
4601 <div title="5.2 ファイルの入出力" modifier="Psychlops_DevelopperG" modified="200908190200" created="200709170703" changecount="2">
4602 <pre>この節では簡易的なファイルの入出力命令について説明します。
4603 PsychlopsはC, C++の標準命令と混在が可能なので、そちらの方法になれている方は、標準の入出力関数を用いてデータの保存、読み込みを行っていただいても問題ありません。
4604 しかし、単にプログラム中で用いた配列に格納されたデータを保存するだけの時に標準の入出力関数を使うのが面倒な時があります。
4605 Psychlopsではファイルの入出力を簡易的に行う命令Data::savearray(), Data::loadarray()が準備されています。ただし、これらの命令を使用してデータを保存するには以下の条件が満たされている必要があります。
4606
4607 # 保存する複数の配列のサイズが同じ
4608 # 保存する配列の数が20個以下
4609
4610 Data::loadarray()も同様にサイズが同じ20個以下の配列を読み込むことができます。
4611
4612 [[5.2.1 ファイルへの出力]]
4613 [[5.2.2 ファイルからの入力]]</pre>
4614 </div>
4615 <div title="5.2.1 ファイルへの出力" modifier="Psychlops_DevelopperG" modified="200908190159" created="200709170712" changecount="1">
4616 <pre>!ファイルへの出力
4617 はじめに配列を保存するための命令Data::savearray()から説明を行います。
4618
4619 |!Data::savearray()|savearray(filename, header, length, ...)|指定した配列を保存します|&gt;|&gt;|
4620 |~|~|filename: 保存する配列のファイル名称(拡張子まで)を指定|&gt;|&gt;|
4621 |~|~|保存先を指定しない場合のパス|Mac:Macintosh HD/ユーザ/ユーザ名(任意)/書類/Psychlops/ ^^*1^^|&gt;|
4622 |~|~|~|Win:実行ファイルと同じパス|&gt;|
4623 |~|~|保存をする場合のパス指定|%APP%|Mac:実行バンドル(.app)と同じパス|
4624 |~|~|~|~|Win:実行ファイルと同じパス|
4625 |~|~|~|%RESOURCES%|Mac:実行バンドルの内部のResourcesフォルダ|
4626 |~|~|~|~|Win:実行ファイルのパスの直下のrscという名前のフォルダ|
4627 |~|~|~|%~USER_HOME%|Mac:&quot;~/&quot;と同じ(/Users/(user)/)|
4628 |~|~|~|~|Win:ユーザープロファイルフォルダ(C:\Documents and Settings\(user)\)|
4629 |~|~|~|%~USER_SETTING%|Mac:&quot;~/Library/Psychlops/&quot;|
4630 |~|~|~|~|Win:&quot;%~AppData%Psychlops&quot;|
4631 |~|~|~|%~USER_DOCUMENTS%|Mac:&quot;~/Documents/Psychlops&quot;|
4632 |~|~|~|~|Win:ユーザープロファイルフォルダ\My Documents\Psychlops|
4633 |~|~|~|%~USER_DOCUMENTS_ROOT%|Mac:&quot;~/Documents&quot;|
4634 |~|~|~|~|Win:ユーザープロファイルフォルダMy Documents\|
4635 |~|~|ファイル名に%TIME_という文字列があると、日時分秒に自動変換されます|&gt;|
4636 |~|~|header: 配列の冒頭に加える説明などを記述します|&gt;|&gt;|
4637 |~|~|length: 保存する配列の大きさを指定します|&gt;|&gt;|
4638 |~|~|...: 保存する配列を指定します。最大20個まで指定できます|&gt;|&gt;|
4639 *1 Finderでの表示は上記のとおりですが、Psychlopsで指定する場合は下記のUnix形式でパスを指定してください
4640 ** &quot;~/Documents/Psychlops/&quot;
4641 *2 MacOSX版では、日本語パス名は使用できません。強制終了してしまいます。
4642 *3 VISTA環境の場合、パス指定が正常に動作しません。VISTA環境の方はお手数ですが保存先を指定しないでお使いください。
4643
4644
4645 以下でこの命令の使い方の例を見てみましょう。
4646 {{{
4647 &lt;例1&gt;
4648 #include &lt;psychlops.h&gt;
4649 using namespace Psychlops;
4650
4651 const int MaxTrials = 100;
4652 int condition[MaxTrials], result[MaxTrials]
4653
4654 void psychlops_main() {
4655
4656         for(int CurrentTrial=0; CurrentTrial&lt;MaxTrials; CurrentTrial++) {
4657
4658                 condition[CurrentTrial] = Psychlops::random(2);
4659
4660                 // ...実験の描画処理
4661
4662                 if(Input::get(Keyboard::left))
4663                         result[CurrentTrial] = 0;
4664                 if(Input::get(Keyboard::right))
4665                         result[CurrentTrial] = 1;
4666
4667         }
4668
4669 }
4670
4671 }}}
4672 上のプログラム例では、変数配列resultには、被験者のキー押しによって0か1かの数値が入ります。この配列を配列conditionに保存されている実験条件とともにsavearray()命令を用いて、”sample_保存時間.txt&quot;という名前のファイルに保存するには以下のように命令を書きます。
4673 {{{
4674 Data::savearray(&quot;sample_%TIME_.txt&quot;, &quot;conditions\tresults&quot;, MaxTrials, condition, result);
4675 }}}
4676
4677 第1引数はファイル名の指定、第2引数はファイルのヘッダ(1行目の内容)です。conditionおよび、resultの配列の個数はMaxTrials(100)個なので、第3引数はMaxTrials, それ以下に実際の配列の名前であるcondition, resultが続きます。
4678
4679 実際のプログラムは以下の様になります。
4680
4681 {{{
4682 &lt;例2&gt;
4683 #include &lt;psychlops.h&gt;
4684 using namespace Psychlops;
4685
4686 const int MaxTrials = 100;
4687 int condition[MaxTrials], result[MaxTrials]
4688
4689 void psychlops_main() {
4690
4691         for(int CurrentTrial=0; CurrentTrial&lt;MaxTrials; CurrentTrial++) {
4692
4693                 condition[CurrentTrial] = Psychlops::random(2);
4694
4695                 // ...実験の描画処理
4696
4697                 if(Input::get(Keyboard::left))
4698                         result[CurrentTrial] = 0;
4699                 if(Input::get(Keyboard::right))
4700                         result[CurrentTrial] = 1;
4701
4702         }
4703
4704         Data::savearray(&quot;sample_%TIME_.txt&quot;, &quot;conditions\tresults&quot;, MaxTrials, condition, result);
4705
4706 }
4707
4708 }}}
4709 標準の出力パスに「sample_(日時).txt」というファイルが出力されたでしょうか?
4710
4711 ファイルは、配列を縦に並べたタブ区切り形式となります。上記の例を適用すると
4712 | ヘッダ| |
4713 | Condition[0] | Result[0] |
4714 | Condition[1] | Result[1] |
4715 | Condition[2] | Result[2] |
4716 | ...          | ...       |
4717 の順番で記録されています。このファイルを実際に開くと、以下の例のようになっています。
4718 {{{
4719 &lt;例3&gt;
4720
4721 conditions results
4722 0       1
4723 1       1
4724 0       0
4725 0       1
4726 1       0
4727 ...
4728 }}}
4729 この結果ファイルはそのままExcelなどに読み込むことが出来ます。
4730 </pre>
4731 </div>
4732 <div title="5.2.2 ファイルからの入力" modifier="Psychlops_DevelopperG" modified="200908190200" created="200709170714" changecount="1">
4733 <pre>Data::savearray()とは逆に、ファイルからデータを読み込むことも出来ます。
4734 データは配列に読み込まれるため、savearrayで書き込んだものをそのまま読み込むことが出来ます。
4735
4736
4737 !Data::savearray()
4738 テキストファイルから配列に読み込みます。
4739
4740 |!Data::savearray()|savearray(filename, skipped_lines, length, ...)|指定した配列を読み込みます|&gt;|&gt;|
4741 |~|~|filename: 読み込むファイル名称(拡張子まで)を指定|&gt;|&gt;|
4742 |~|~|保存先を指定しない場合のパス|Mac:Macintosh HD/ユーザ/ユーザ名(任意)/書類/Psychlops/ ^^*1^^|&gt;|
4743 |~|~|~|Win:実行ファイルと同じパス|&gt;|
4744 |~|~|読み込む場合のパス指定|%APP%|Mac:実行バンドル(.app)と同じパス|
4745 |~|~|~|~|Win:実行ファイルと同じパス|
4746 |~|~|~|%RESOURCES%|Mac:実行バンドルの内部のResourcesフォルダ|
4747 |~|~|~|~|Win:実行ファイルのパスの直下のrscという名前のフォルダ|
4748 |~|~|~|%~USER_HOME%|Mac:&quot;~/&quot;と同じ(/Users/(user)/)|
4749 |~|~|~|~|Win:ユーザープロファイルフォルダ(C:\Documents and Settings\(user)\)|
4750 |~|~|~|%~USER_SETTING%|Mac:&quot;~/Library/Psychlops/&quot;|
4751 |~|~|~|~|Win:&quot;%~AppData%Psychlops&quot;|
4752 |~|~|~|%~USER_DOCUMENTS%|Mac:&quot;~/Documents/Psychlops&quot;|
4753 |~|~|~|~|Win:ユーザープロファイルフォルダ\My Documents\Psychlops|
4754 |~|~|~|%~USER_DOCUMENTS_ROOT%|Mac:&quot;~/Documents&quot;|
4755 |~|~|~|~|Win:ユーザープロファイルフォルダMy Documents\|
4756 |~|~|skipped_lines: 冒頭から読み込みを飛ばす行数を指定します|&gt;|&gt;|
4757 |~|~|length: 読み込む配列の大きさを指定します|&gt;|&gt;|
4758 |~|~|...: 読み込む配列を指定します。最大20個まで指定できます|&gt;|&gt;|
4759 *1 Finderでの表示は上記のとおりですが、Psychlopsで指定する場合は下記のUnix形式でパスを指定してください
4760 ** &quot;~/Documents/Psychlops/&quot;
4761 *2 MacOSX版では、日本語パス名は使用できません。強制終了してしまいます。
4762 *3 VISTA環境の場合、パス指定が正常に動作しません。VISTA環境の方はお手数ですが保存先を指定しないでお使いください。
4763
4764
4765
4766 savearrayで保存したファイルを読み込んでみましょう。savearrayでは通常冒頭2行にファイルの説明を加えるため、skipped_linesには2を指定します。ファイル名中で%TIME_を指定すると現在時刻に変換されてしまうため、読み込むファイルはあらかじめ別の名前にしておきます。ここではsample.txtとしました。
4767 {{{
4768 &lt;例1&gt;
4769 #include &lt;psychlops.h&gt;
4770 using namespace Psychlops;
4771
4772 const int MaxTrials = 100;
4773 int condition[MaxTrials], result[MaxTrials]
4774
4775 void psychlops_main() {
4776
4777         Data::loadarray(&quot;sample.txt&quot;, 2, MaxTrials, condition, result)
4778
4779 }
4780
4781 }}}
4782 これでsample.txtの中身が配列に読み込まれました</pre>
4783 </div>
4784 <div title="6. 時間制御と各種ツールを使用する" modifier="Psychlops_DevelopperG" modified="200908190210" created="200709132309" changecount="6">
4785 <pre>[[6.1 時間を計測する]] 
4786 [[6.2 各種ツールを使ってみる]] 
4787   [[6.2.1 描画の時間精度を確認する-FPSチェッカー]]
4788   [[6.2.2 画面に反映されない描画の進行度を表示する-プログレスバー]]
4789 [[6.3 行列演算を行う]]
4790   [[6.3.1 行列の宣言]]
4791   [[6.3.2 行列の要素の操作]]
4792   [[6.3.3 行列全体の操作]]
4793   [[6.3.4 行列の加減乗除]]
4794   [[6.3.5 行列の中身をオフスクリーンに描画する]]
4795   [[6.3.6 メッシュグリッドの作成]]</pre>
4796 </div>
4797 <div title="6.1 時間を計測する" modifier="Psychlops_DevelopperG" modified="200908190204" created="200708242225" changecount="1">
4798 <pre>!Clockクラスの概要
4799 Psychlopsで時間を計測する方法は2種類あります。
4800 画面のリフレッシュを基準として、リフレッシュされたフレーム数をカウントすることです。この方法は比較的簡単ですが、1フレームの時間以上の精度で時間を制御することができません。
4801 そこで、より高い精度で時間を計測するためのクラスとしてPsychlopsにはClockクラスが用意されています。このClockクラスはCPUタイマーを利用することによって高い精度の時間間隔の計測を実現しています。
4802
4803 !Clockクラスの使い方
4804 Clockの宣言は特に難しくありません。[[Psychlops::Clock]]型のインスタンスを以下のような形式で単に宣言するだけです。
4805 {{{
4806 Psychlops::clock Timer;
4807 }}}
4808 このTimerに現在のCPUタイマーの値を入力するにはClock::update()命令を使用します。
4809 |!Clock::update()| Clock::update(void)| 現在のCPUタイマーの値を入力する|
4810 このままでは、保持されている時刻はCPUタイマーのカウントなので、実際にプログラム内でこのクラスを使うときにはClock::at_msec()命令を使用してタイマーのカウントをmsec単位に変換して使用します。
4811 |!Clock::at_msec()| double Clock::at_msec(void)| カウントをmsec単位に変換(double)して返す|
4812
4813 下にClockクラスを用いた簡単な時間計測例を挙げて見ました。スペースキーを押している時間が画面に表示されます。
4814 {{{
4815 * Clockクラスの使用例
4816 #include &lt;psychlops.h&gt;
4817 using namespace Psychlops;
4818
4819 void psychlops_main() {
4820  
4821  Canvas sampleA(Canvas::fullscreen);
4822  double elapsed=0.0, dcx, dcy;
4823  Clock Start, End;
4824  
4825  dcx=sampleA.getHcenter();
4826  dcy=sampleA.getVcenter();
4827  sampleA.clear(Color(0.5));
4828  sampleA.flip();
4829  
4830  while(!Input::get(Keyboard::esc)) {
4831  
4832         if(Input::get(Keyboard::spc, Keyboard::released)){
4833                 End.update();
4834                 elapsed=(End-Start).at_msec();
4835                 sampleA.clear(Color(0.5));
4836                 sampleA.flip();
4837                 sampleA.clear(Color(0.5));
4838                 sampleA.msg(&quot;Elapsed Time(msec)&quot;, dcx-100,dcy-30, Color::white);
4839                 sampleA.var((int)elapsed,dcx ,dcy, Color::white);
4840                 sampleA.flip();
4841                 }
4842   
4843         if(Input::get(Keyboard::spc, Keyboard::pushed)){
4844                 Start.update();
4845                 Input::refresh();
4846                 } 
4847         }
4848 }
4849 }}}
4850 ^^ごく稀にWindows環境下でこのプログラムを実行すると、キー押し時間がマイナスになることがあるようです。このような状態が起こったら、並行して動いているプログラムを終了させて、再起動をしてからこのプログラムを実行してみてください。参考[[Tips: 時間精度が必要なプログラムを実行するとき]]^^</pre>
4851 </div>
4852 <div title="6.2 各種ツールを使ってみる" modifier="Psychlops_DevelopperG" modified="200908190206" created="200709120042" changecount="3">
4853 <pre>Psychlopsにはここまで扱ったような簡単な描画命令だけではなく、描画内容をチェックするためのツールが実装されています。ここでは、これらのツールについて説明します。
4854 [[6.2.1 描画の時間精度を確認する-FPSチェッカー]]
4855 [[6.2.2 画面に反映されない描画の進行度を表示する-プログレスバー]]</pre>
4856 </div>
4857 <div title="6.2.1 描画の時間精度を確認する-FPSチェッカー" modifier="Psychlops_DevelopperG" modified="200908190205" created="200709120043" changecount="1">
4858 <pre>!!FPSチェッカの概要
4859 Psychlopsには、現在描画している画面がプログラムで指定した時間通りに実行されているかどうかをチェックするツールがあります。
4860 たとえば、以下のようなコードを考えて見ます。
4861 {{{
4862 void psychlops_main() {
4863         Canvas sampleA(Canvas::fullscreen);
4864         while(true){
4865         非常に時間のかかる描画コード
4866         sampleA.flip(); 
4867 }
4868 }}}
4869 このプログラムを書いたときの意図は、描画を毎フレームごとに更新するというものでしょう。(でなければ、なんらかの「待ち」処理がsampleA.flip()の前後に入っているはずです。)
4870 しかし、非常に複雑な描画内容をCanvasの裏画面に書いたり、大きなImageを転送するときは、描画に1フレーム分以上の時間かかることがあります。この場合、psychlopsは描画が完了するまで待ってから[[Canvas::flip()]]命令を実行するので実際に描画が更新されるのは2フレーム以上たってからということになります。
4871 問題は、このような&quot;こま落ち&quot;はただ見ているだけではわからないことが多いことです(複雑な刺激呈示の途中に16.7 msと33.3msの区別がつくでしょうか?)。
4872 psychlopsにはこのような&quot;こま落ち&quot;を検出するためのFPSチェッカと呼ばれるツールが準備されています。
4873 このFPSチェッカを使うには、まず時間計測を行いたい部分の前にFPSチェッカを起動する命令を書きます。
4874 これには、Canvas::watchFPS()命令を使用します。
4875 |!Canvas::watchFPS()|void watchFPS(void)|FPSチェッカをonにします|
4876 このままでは、FPSチェッカが起動しているだけで、その内容を見ることはできません。
4877 FPSチェッカの内容を表示するためには、[[Canvas::flip()]]命令の直前にCanvas::showFPS()命令を書いてFPSチェッカのウィンドウを表示させます。
4878 |!Canvas::showFPS()|void showFPS(void)|FPSチェッカのレポートを画面左上に表示します|
4879 Canvas::showFPS()命令を実行すると、半透明のレポートウィンドウが画面右上に表示されます。このレポートウィンドウは、すべての描画内容の上に重ねて表示されます(以下を参照してください)
4880
4881 !!FPSチェッカの使い方
4882 FPSチェッカを実際に使ってみましょう
4883 以下のコードは5フレームに一度描画内容を更新して、コマ落ちが発生しているかどうかを表示します。
4884 {{{
4885 #include &lt;psychlops.h&gt;
4886 using namespace Psychlops;
4887
4888 void psychlops_main() {
4889         Canvas sampleA(Canvas::fullscreen);
4890         sampleA.watchFPS();
4891         while(!Input::get(Keyboard::spc)){  
4892                 sampleA.showFPS();
4893                 sampleA.clear(0.5);
4894                 sampleA.flip(5); 
4895         }
4896 }
4897 }}}
4898
4899 このコードを60Hzのディスプレイで実行すると、以下のような画面が表示されます。
4900
4901 [img[image/FPSchecker.png]]
4902
4903 showFPS()命令を実行した後に、Canvas::clear()命令を実行しているにもかかわらずレポートウィンドウが表示されていることに注意してください。このように、実際にはレポートウィンドウは全ての描画が終了した後に描画されます。
4904 このレポートウィンドウを拡大したものが下図です。
4905
4906 [img[image/FPSwindow.png]]
4907
4908 レポートウィンドウは3行から成り立っています。一番上の赤い数字は、これまでにコマ落ちしたフレーム数、真ん中の白い数字は表示された総フレーム数、一番下の青い数字は前回のflip()から今回のflip()までにかかった時間(msec単位)です。
4909 この例では、5フレームに一度描画が更新されているので、60Hzだと画面更新間の時間は1/12 sec = 83.3 msecで、一番下の青い数字と一致していることに注目してください。</pre>
4910 </div>
4911 <div title="6.2.2 画面に反映されない描画の進行度を表示する-プログレスバー" modifier="Psychlops_DevelopperG" modified="200908190206" created="200709252142" changecount="1">
4912 <pre>!プログレスバーの概要
4913 大量の計算をコンピュータに行わせているとき、画面には何の変化も起きずにプログラムが暴走しているのか、単に計算に時間がかかっているだけなのかの区別がつかないときが良くあります(本当は、暴走をさせないようなプログラム構造にするべきですが・・・)。進行度を文字で画面に表示する方法もありますが、文字表示のために[[Canvas::flip()]]命令を挟んでしまうと計算速度が落ちてしまいます。
4914 このような状況で、画面に計算の総量に対して今どのくらいの割合が終了したかを表示するためのツールにプログレスバーがあります。このプログレスバーは強制的にフロントバッファ(現在表示中の画面)に表示されるため、[[Canvas::flip()]]命令は必要ありません。
4915
4916 このプログレスバーを表示するにはCanvas::progressbar()命令を使用します。
4917 |!Canvas::progressbar()|void progressbar(X now, X max)|現在のnowの値のmaxに対する比率をプログレスバーとして表示します。now, maxは数値型であればどの型でもかまいません|&gt;|
4918 |~|~|X now|現在の進行度を示す変数値(型は数値型)|
4919 |~|~|X max|nowの最終的な到達値(型は数値型)|
4920 |~|void progressbar(double ratio)|ratioで示される比率をプログレスバーとして表示します。|&gt;|
4921 |~|~|double ratio|比率の値(0.0~1.0)|
4922 この命令を使用すると、画面左上に灰色の四角のなかに時間とともに伸びる青い四角形が表示されます。この青い四角形が灰色の四角いっぱいまで伸びると、nowの値がmaxまで到達した(ratioが1.0になる)ことを示しています。
4923
4924
4925 !!プログレスバーの使い方
4926 実際の使い方の例を見てみましょう。
4927 下の例では100 msecと50 msecの&quot;待ち&quot;を100回繰り返します。
4928 時間経過とともに左上に提示されるプログレスバーが伸びていく様子に注目してください。
4929
4930 {{{
4931 #include &lt;psychlops.h&gt;
4932 using namespace Psychlops;
4933
4934 void psychlops_main() {
4935
4936         Canvas sampleA(Canvas::fullscreen);
4937         Clock timer1, timer2; 
4938         int temp;
4939
4940         sampleA.watchFPS();
4941
4942         sampleA.clear(0.5);
4943         sampleA.message(&quot;calculating...&quot;, sampleA.getHcenter(), sampleA.getVcenter());
4944         sampleA.flip();
4945
4946         temp=0;
4947         for(int i=0; i&lt;100; i++){
4948                 timer1.update();
4949                 sampleA.progressbar(i,100);
4950                 while((timer2-timer1).at_msec()&lt;100){timer2.update();}
4951                 if(Input::get(Keyboard::esc))break;
4952         }
4953  
4954         sampleA.clear(0.5);
4955         sampleA.message(&quot;10 sec elapsed&quot;, sampleA.getHcenter(), sampleA.getVcenter());
4956         sampleA.flip();
4957         while(!Input::get(Keyboard::spc));
4958         sampleA.clear(0.5);
4959         sampleA.message(&quot;calculating...&quot;, sampleA.getHcenter(), sampleA.getVcenter());
4960         sampleA.flip();
4961
4962
4963         temp=0;
4964         for(int i=0; i&lt;100; i++){
4965                 timer1.update();
4966                 sampleA.progressbar((double)i/100.0);
4967                 while((timer2-timer1).at_msec()&lt;50){timer2.update();}
4968                 if(Input::get(Keyboard::esc))break;
4969         }
4970
4971         sampleA.clear(0.5);
4972         sampleA.message(&quot;5 sec elapsed&quot;, sampleA.getHcenter(), sampleA.getVcenter());
4973         sampleA.flip();
4974         while(!Input::get(Keyboard::spc));
4975 }
4976 }}}</pre>
4977 </div>
4978 <div title="6.3 行列演算を行う" modifier="Psychlops_DevelopperG" modified="200908190211" created="200709252143" changecount="3">
4979 <pre>!Matrixクラスの概要
4980 !!行列とは?
4981 Matrixクラスは行列を取り扱います。線形代数としての取り扱いもできますが、Matlabの行列のように数値を2次元的に配置した表や画像(Image)のようなものとしても利用できます。Imageは精度が低いため、Imageに保存した色データをもとに再計算するのはお勧めできませんが、Matrixではそのような計算をしても十分な精度があります。
4982
4983 !!MatrixとImageの違い
4984 MatrixとImageは基本的に2次元のよく似たデータ構造を持っており、相互に変換が可能です。(実際にはImageは下で述べるようなより複雑なデータ構造を持っています。)ただし、両者にはいくつかの相違点があります。
4985 [Img[Image/Image_Matrix.png]]
4986
4987 * Matrixの要素アクセスは行(縦)列(横)の順で指定し、起点は1です。ImageはX(横)Y(縦)の順で指定し、起点は0です。
4988 [Img[Image/ImagetoMatrix.png]]
4989
4990
4991 * Imageは一つのx,y値に対してRGB 3値、RGBA 4値を取り扱うことが出来ます(数学的にはテンソルの構造を持っています)。Matrixは行列の要素として一つの値しか取り扱えないため、3値または4値のImageに変換する場合、それぞれの座標におけるR,G,B,(A)の値を示す同じ次元を持った3つまたは4つの行列を用意する必要があります。
4992 [Img[Image/ColImagetoMatrix.png]]
4993
4994
4995 * Matrixは精度を重視して64bit浮動小数点(double型)を取り扱います。Imageは速度を重視して8bit整数(unsigned char型)を基本として取り扱います(32bit浮動小数点 float型にすることもできます)
4996
4997 [[6.3.1 行列の宣言]]
4998 [[6.3.2 行列の要素の操作]]
4999 [[6.3.3 行列全体の操作]]
5000 [[6.3.4 行列の加減乗除]]
5001 [[6.3.5 行列の中身をオフスクリーンに描画する]]
5002 [[6.3.6 メッシュグリッドの作成]]</pre>
5003 </div>
5004 <div title="6.3.1 行列の宣言" modifier="Psychlops_DevelopperG" modified="200908190207" created="200710171011" changecount="1">
5005 <pre>!Matrixクラスの概要
5006 !!行列とは?
5007 Matrixクラスは行列を取り扱います。線形代数としての取り扱いもできますが、Matlabの行列のように数値を2次元的に配置した表や画像(Image)のようなものとしても利用できます。Imageは精度が低いため、Imageに保存した色データをもとに再計算するのはお勧めできませんが、Matrixではそのような計算をしても十分な精度があります。
5008
5009 !!MatrixとImageの違い
5010 MatrixとImageはよく似ており、相互に変換が可能です。
5011
5012 * Matrixは精度を重視して64bit浮動小数点(double型)を取り扱います。Imageは速度を重視して8bit整数(unsigned char型)を基本として取り扱います(32bit浮動小数点 float型にすることもできます)
5013 * ImageはRGB 3値、RGBA 4値を取り扱うことが出来ます。Matrixは1値しか取り扱えないため、3値または4値のImageに変換する場合、3つまたは4つの行列を用意する必要があります。
5014 * Matrixの要素アクセスは行(縦)列(横)の順で指定し、起点は1です。ImageはX(横)Y(縦)の順で指定し、起点は0です。
5015
5016 !行列の宣言文
5017 行列を確保するには、行数と列数を指定します。
5018 Matrix()のように宣言のみの場合は''Matrix::set()''命令で指定します。
5019
5020 |!Matrixの宣言|Matrix()|行列の使用を宣言します|
5021 |~|Matrix(rows, cols)|指定されたrows行cols列の行列を宣言します|
5022 |~|~|long rows :行数を指定|
5023 |~|~|long cols :列数を指定|
5024
5025
5026 !Matrix::set()
5027 行列の宣言時に何の指定もしなかった場合、Matrix::set()命令を使用します。
5028 |!Matrix::set()|Matrix(rows, cols)|指定されたrows行cols列の行列を設定します|
5029 |~|~|long rows :行数を指定|
5030 |~|~|long cols :列数を指定|
5031
5032
5033 !!宣言文の書き方
5034 {{{
5035 &lt;例1&gt;
5036 #include &lt;fstream&gt;
5037 #include &lt;psychlops.h&gt;
5038 using namespace Psychlops;
5039
5040 Matrix LuminanceMap1(200, 100);
5041
5042 void psychlops_main() {
5043         std::ofstream result(&quot;dump.txt&quot;);
5044
5045         result &lt;&lt; LuminanceMap1;
5046 }
5047 }}}
5048 次はMatrixの宣言と[[Matrix::set()]]命令を組み合わせたコードです。
5049 {{{
5050 &lt;例2&gt;
5051 #include &lt;fstream&gt;
5052 #include &lt;psychlops.h&gt;
5053 using namespace Psychlops;
5054
5055 Matrix LuminanceMap1;
5056 long Width=10, Height=10;
5057
5058 void psychlops_main() {
5059         std::ofstream result(&quot;dump.txt&quot;);
5060
5061         LuminanceMap1.set(Height, Width);
5062         result &lt;&lt; LuminanceMap1;
5063
5064 }
5065 }}}
5066 これらのコードを実行すると、プログラムと同じ場所に作成されるファイルdump.txtにMatrixの中身が保存されます。</pre>
5067 </div>
5068 <div title="6.3.2 行列の要素の操作" modifier="Psychlops_DevelopperG" modified="200908190204" created="200710171131" changecount="2">
5069 <pre>6.3.1節ではMatrixの宣言(領域確保)のみを行っていたので、Matrixの要素に0が代入されるだけでした。
5070 この節では実際にMatrix中に数値を代入していきます。
5071
5072 !Matrix全体への代入
5073 Matrix全体をひとつの値で埋め尽くすには、Matrixに対して数値の代入を行います。
5074 {{{
5075 &lt;例1&gt;
5076 #include &lt;fstream&gt;
5077 #include &lt;psychlops.h&gt;
5078 using namespace Psychlops;
5079
5080 void psychlops_main() {
5081         std::ofstream result(&quot;dump.txt&quot;);
5082
5083         Matrix LuminanceMap1(5, 5);
5084         LuminanceMap1 = 7.0;
5085         result &lt;&lt; LuminanceMap1;
5086 }
5087 }}}
5088 これで全体に7が代入されました。
5089
5090
5091 !Matrix(row, col) 単独要素
5092 Matrix中の要素にアクセスするための命令です。値を取り出すことも代入することも出来ます。二次元の配列に似た扱い方になりますが、要素の指定が行(縦)列(横)の順番であること、起点が0ではなく1であることに注意してください。
5093
5094 |!Matrix()|Matrix(row, col) |要素を取り出します。ちょうど配列のように、値を取り出すことも代入することもできます。|
5095 |~|~|int row: 要素の行を指定します。|
5096 |~|~|int col: 要素の列を指定します。|
5097 行列の範囲外の値が指定された場合はエラーになります。エラーメッセージを表示して強制終了します。
5098
5099 !!Matrixの要素へのアクセスの仕方
5100 2行2列目の要素に数値を代入し、さらにその値を取り出します。
5101 {{{
5102 &lt;例2&gt;
5103 #include &lt;fstream&gt;
5104 #include &lt;psychlops.h&gt;
5105 using namespace Psychlops;
5106
5107 const int Width = 10, Height = 10;
5108 Matrix LuminanceMap1(Height, Width);
5109
5110 void psychlops_main() {
5111         std::ofstream result(&quot;dump.txt&quot;);
5112
5113         LuminanceMap1(2,2) = 1.0;
5114         std::cout &lt;&lt; LuminanceMap1;
5115
5116         double a = LuminanceMap1(2,2);
5117         result &lt;&lt; a;
5118
5119 }
5120 }}}
5121 このプログラムを実行すると、行列の中身が10行10列で出力されます。次の行に、行列の(2,2)の値である1.0が出力されます。
5122
5123 !!Matrixの全要素へのアクセスの仕方
5124 全要素にアクセスするには、forループを使って順次アクセスしていきます。
5125 {{{
5126 &lt;例3&gt;
5127 #include &lt;fstream&gt;
5128 #include &lt;psychlops.h&gt;
5129 using namespace Psychlops;
5130
5131 const int Width = 10, Height = 10;
5132 Matrix LuminanceMap1(Height, Width);
5133
5134 void psychlops_main() {
5135         std::ofstream result(&quot;dump.txt&quot;);
5136
5137         for(int row; row&lt;Width; row++) {
5138                 for(int col; col&lt;Width; col++) {
5139                         LuminanceMap1(row, col) = row+col;
5140                 }
5141         }
5142         result &lt;&lt; LuminanceMap1;
5143
5144 }
5145 }}}
5146
5147 !Matrix(row, col) 部分行列
5148 要素アクセスで整数を指定すると単一の要素にアクセスできました。行列のある範囲をまとめて取り扱いたい場合は、Range型を使って範囲を指定し、部分行列にアクセスします。
5149
5150
5151 [img[部分行列|image/MatrixPartial.png]]
5152 部分行列は、行列の一部分を切り取ってサイズの異なる行列であるかのように振る舞わせます。部分行列は元の行列の窓のように振る舞うため、部分行列に代入すると元の行列の一部分に代入するのと同じように作用します。
5153
5154 |!Matrix()|Matrix(Range row, Range col)|行列の一部分から部分行列を作ります。|
5155 |~|~|Range row: 要素の行を指定します。|
5156 |~|~|Range col: 要素の列を指定します。|
5157
5158 !!部分行列の書き方
5159 10×10のMatrixの一部に数値を代入します。部分行列のさらに一部の要素にもアクセスできます。
5160 {{{
5161 &lt;例4&gt;
5162 #include &lt;fstream&gt;
5163 #include &lt;psychlops.h&gt;
5164 using namespace Psychlops;
5165
5166 const int Width = 10, Height = 10;
5167 Matrix LuminanceMap1(Width, Height);
5168
5169 void psychlops_main() {
5170         std::ofstream result(&quot;dump.txt&quot;);
5171
5172         Range row, col;
5173         LuminanceMap1(2&lt;=row&lt;=5, 3&lt;=col&lt;=6) = 1.0;
5174         LuminanceMap1(2&lt;=row&lt;=5, 3&lt;=col&lt;=6)(2,2) = 2.0;
5175         result &lt;&lt; LuminanceMap1;
5176
5177 }
5178 }}}
5179
5180
5181 !!部分行列のコピー
5182 部分行列は元の行列の窓として機能するので、部分行列への代入をすると元の行列にも代入されます。元の行列を壊したくない場合は、明示的にコピーをとっておく必要があります。
5183 {{{
5184 &lt;例4&gt;
5185 #include &lt;fstream&gt;
5186 #include &lt;psychlops.h&gt;
5187 using namespace Psychlops;
5188
5189 const int Width = 10, Height = 10;
5190 Matrix LuminanceMap1(Width, Height), Copy;
5191
5192 void psychlops_main() {
5193         std::ofstream result(&quot;dump.txt&quot;);
5194
5195         Range row, col;
5196         LuminanceMap1(2&lt;=row&lt;=5, 3&lt;=col&lt;=6) = 1.0;
5197         Copy = LuminanceMap1(2&lt;=row&lt;=5, 3&lt;=col&lt;=6);
5198         Copy = 2.0;
5199         result &lt;&lt; LuminanceMap1 &lt;&lt; Copy;
5200
5201 }
5202 }}}</pre>
5203 </div>
5204 <div title="6.3.3 行列全体の操作" modifier="Psychlops_DevelopperG" modified="200908190207" created="200710171157" changecount="1">
5205 <pre>!要素の再配置
5206 ここでは、行列全体に対して回転、列のスライドなどの操作を行う命令について解説します。
5207 Psychlopsが行列全体に対して行える操作は大まかに以下の表に示した5つです。ここではこれらの命令について解説します。
5208
5209 |slide|行列のスライド|
5210 |transpose|行列の転置|
5211 |rotation|行列全体の回転|
5212 |cat|行列の結合|
5213 |reshape|行列要素の再配置|
5214
5215 上記命令はすべて自己破壊的に作用します。
5216 つまり、命令を実行した段階で、元の行列が命令が適用された行列に置き換わってしまいます。
5217 これには、行列の次元の変換も含まれるので、上記命令を式の中に書いた場合には注意が必要です。
5218 いかに注意した方がよい命令例を挙げてみます。
5219 {{{
5220 Matrix a(5, 3), b(3, 5);
5221 b = a.transpose() + a;  
5222 b = b.transpose(); 
5223 }}}
5224
5225 一行目では、 左側のa.transpose()でaが自己破壊的に転置されてしまうため、右側のaも転置されたものとして扱われてしまいます。つまり、このような命令を実行すると、
5226 {{{
5227 b=2*a.transpose();
5228 }}}
5229 と同じ結果がえられます。
5230 2行目では、左辺のbは3行5列、右辺のbは5行3列で不正な代入にはならず、右辺bが自己破壊的に転置されて代入されます。
5231
5232 !slide(行・列のスライド)
5233
5234 |!Matrix::slide|slide(row, col)|要素を行方向、列方向にずらします。あふれた分は、反対側に移動します。|
5235 |~|~|int row 行方向にずらす量を指定します。|
5236 |~|~|int col 列方向にずらす量を指定します。|
5237
5238 !!slide操作の内容
5239 {{{
5240 1 2 3
5241 4 5 6
5242 をslide(1,0)すると
5243 4 5 6
5244 1 2 3
5245
5246 1 2 3
5247 4 5 6
5248 をslide(0,1)すると
5249 3 1 2
5250 6 4 5
5251
5252 1 2 3
5253 4 5 6
5254 をslide(1,1)すると
5255 6 4 5
5256 3 1 2
5257 }}}
5258
5259 !!slideの書き方
5260 {{{
5261 #include &lt;fstream&gt;
5262 #include &lt;psychlops.h&gt;
5263 using namespace Psychlops;
5264
5265 Psychlops::Matrix LuminanceMap1;
5266 long Width=10, Height=10;
5267
5268 void psychlops_main() {
5269         std::ofstream result(&quot;dump.txt&quot;);
5270
5271
5272         LuminanceMap1.set(Height, Width);
5273         for(int row; row&lt;Width; row++) {
5274                 for(int col; col&lt;Width; col++) {
5275                         LuminanceMap1(row, col) = row*Height + col;
5276                 }
5277         }
5278         result &lt;&lt; LuminanceMap1.slide(1,0);
5279 }
5280 }}}
5281
5282
5283 !転置
5284
5285 |!Matrix::transpose|transpose()|行列を転置(行と列の読み替え)します。自分自身を書き換えるので注意してください。|
5286
5287 !!転置操作の内容
5288 {{{
5289 1 2 3
5290 4 5 6
5291 をtransposeすると
5292 1 4
5293 2 5
5294 3 6
5295 }}}
5296
5297 !!transposeの書き方
5298 {{{
5299 #include &lt;fstream&gt;
5300 #include &lt;psychlops.h&gt;
5301 using namespace Psychlops;
5302
5303 Psychlops::Matrix LuminanceMap1;
5304 long Width=10, Height=10;
5305
5306 void psychlops_main() {
5307         std::ofstream result(&quot;dump.txt&quot;);
5308
5309
5310         LuminanceMap1.set(Height, Width);
5311         for(int row; row&lt;Width; row++) {
5312                 for(int col; col&lt;Width; col++) {
5313                         LuminanceMap1(row, col) = row*Height + col;
5314                 }
5315         }
5316         result &lt;&lt; LuminanceMap1.transpose();
5317 }
5318 }}}
5319
5320 !行列の回転
5321
5322 |!Matrix::rot90|rot90(count)|行列を90度単位で反時計回りに回転します。|
5323 |~|~|int count 回転角度を90度単位で指定します。-3,1,5のとき90度、-2,2,6のとき180度、-1,3,7のとき270度回転します。|
5324
5325 !!回転操作の内容
5326 {{{
5327 1 2 3
5328 4 5 6
5329 をrot90(1)すると
5330 3 6
5331 2 5
5332 1 4
5333 }}}
5334
5335 !!rot90()の書き方
5336 {{{
5337 #include &lt;fstream&gt;
5338 #include &lt;psychlops.h&gt;
5339 using namespace Psychlops;
5340
5341 Psychlops::Matrix LuminanceMap1;
5342 long Width=16, Height=1;
5343
5344 void psychlops_main() {
5345         std::ofstream result(&quot;dump.txt&quot;);
5346
5347
5348         LuminanceMap1.set(Height, Width);
5349         for(int col; col&lt;Width; col++) {
5350                 LuminanceMap1(1, col) = col;
5351         }
5352         result &lt;&lt; LuminanceMap1.rot90(1);
5353 }
5354 }}}
5355
5356
5357 !行列の結合
5358
5359 |!Matrix::catRow|catRow(other)|この行列の次の行から別の行列を継ぎ足します。|
5360 |~|~|Matrix other: 付け足す行列を指定します。付け足してもこれ自身は消されません。|
5361
5362 {{{
5363 #include &lt;fstream&gt;
5364 #include &lt;psychlops.h&gt;
5365 using namespace Psychlops;
5366
5367 Psychlops::Matrix LuminanceMap1, LuminanceMap2;
5368 long Width=5, Height=1;
5369
5370 void psychlops_main() {
5371         std::ofstream result(&quot;dump.txt&quot;);
5372
5373
5374         LuminanceMap1.set(Height, Width);
5375         LuminanceMap1 = 1;
5376         LuminanceMap2.set(Height, Width);
5377         LuminanceMap2 = 2;
5378
5379         LuminanceMap1.catRows(LuminanceMap2);
5380         result &lt;&lt; LuminanceMap1;
5381 }
5382 }}}
5383
5384
5385 !!reshape(行列配置の変換)
5386
5387 |!Matrix::reshape|reshape(row, col)|要素を変えずに、行と列の数のみを変更します。要素数(行*列)は変更前と変更後で同じでなければなりません。|
5388 |~|~|int row 新しい行数を指定します。|
5389 |~|~|int col 新しい列数を指定します。|
5390
5391 !!reshape操作の内容
5392 {{{
5393 1 2 3
5394 4 5 6
5395 をreshape(3, 2)すると
5396 1 2
5397 3 4
5398 5 6
5399 }}}
5400
5401 !!reshapeの書き方
5402 {{{
5403 #include &lt;fstream&gt;
5404 #include &lt;psychlops.h&gt;
5405 using namespace Psychlops;
5406
5407 Psychlops::Matrix LuminanceMap1;
5408 long Width=16, Height=1;
5409
5410 void psychlops_main() {
5411         std::ofstream result(&quot;dump.txt&quot;);
5412
5413
5414         LuminanceMap1.set(Height, Width);
5415         for(int col; col&lt;Width; col++) {
5416                 LuminanceMap1(1, col) = col;
5417         }
5418         result &lt;&lt; LuminanceMap1.reshape(4, 4);
5419 }
5420 }}}
5421
5422 !その他
5423 |!Matrix::min|min()|全要素中の最小値を得ます。|
5424 |!Matrix::max|max()|全要素中の最大値を得ます。|
5425 {{{
5426 // 行列の要素に乱数を代入し、その中の最大値と最小値を抽出します。
5427 #include &lt;fstream&gt;
5428 #include &lt;psychlops.h&gt;
5429 using namespace Psychlops;
5430
5431 Psychlops::Matrix LuminanceMap1;
5432 long Width=10, Height=10;
5433
5434 void psychlops_main() {
5435         std::ofstream result(&quot;dump.txt&quot;);
5436
5437
5438         LuminanceMap1.set(Height, Width);
5439         for(int row; row&lt;Width; row++) {
5440                 for(int col; col&lt;Width; col++) {
5441                         LuminanceMap1(row, col) = Psychlops::random();
5442                 }
5443         }
5444         result &lt;&lt; LuminanceMap1;
5445         result &lt;&lt; LuminanceMap1.max() &lt;&lt; &quot; &quot; &lt;&lt; LuminanceMap1.min();
5446
5447 }
5448 }}}
5449
5450 |!Matrix::getRows|getRows()|行数を得ます。|
5451 |!Matrix::getCols|getCols()|列数を得ます。|
5452 {{{
5453 #include &lt;fstream&gt;
5454 #include &lt;psychlops.h&gt;
5455 using namespace Psychlops;
5456
5457 Psychlops::Matrix LuminanceMap1;
5458 long Width=5, Height=3;
5459
5460 void psychlops_main() {
5461         std::ofstream result(&quot;dump.txt&quot;);
5462
5463
5464         LuminanceMap1.set(Height, Width);
5465
5466         result &lt;&lt; LuminanceMap1.getRows() &lt;&lt; &quot; &quot; &lt;&lt; LuminanceMap1.getCols();
5467 }
5468 }}}</pre>
5469 </div>
5470 <div title="6.3.4 行列の加減乗除" modifier="Psychlops_DevelopperG" modified="200908190207" created="200710171242" changecount="1">
5471 <pre>!実数との演算子
5472
5473 実数と行列の四則演算は、行列の各要素と実数の演算として定義されています。例えば、matrix + 1; というコードは、行列のすべての要素に1を足すことを意味しています。演算子の優先順位はC++の仕様に従います。
5474 {{{
5475  matrix * 10 + 1;
5476  ( matrix * 10 ) + 1; // 上の式と同じ
5477
5478 matrix + 1 * 10;
5479 matrix + ( 1 * 10 ); // 上の式と同じ
5480 }}}
5481
5482 *現在の仕様では、実数は必ず演算子の後ろに書いてください。演算子の前に実数があるとコンパイルエラーになります。
5483 {{{
5484 matrix + 1; // OK
5485 1 + matrix; // NG
5486 }}}
5487
5488 |!Matrix 演算子 実数|&gt;|
5489 |+|行列の要素すべてに実数を加算します|
5490 |-|行列の要素すべてに実数を減算します|
5491 |*|行列の要素すべてに実数を乗算します|
5492 |/|行列の要素すべてに実数を除算します|
5493
5494 !! プログラム例
5495 {{{
5496 #include &lt;fstream&gt;
5497 #include &lt;psychlops.h&gt;
5498 using namespace Psychlops;
5499
5500 void psychlops_main() {
5501         std::ofstream result(&quot;dump.txt&quot;);
5502
5503         Psychlops::Matrix a(5, 5),  b(5, 5);
5504         a = 3;
5505         b = a + 5;
5506         result &lt;&lt; b;
5507
5508         b(2,2) = 0;
5509         b = b * 10 - 6;
5510         result &lt;&lt; b;
5511 }
5512 }}}
5513
5514
5515 !行列との演算子
5516
5517 行列同士の演算は一般的な線形代数の定義に従います。行列の要素同士の乗算(Matlabでのドット演算子)は *~ 演算子でサポートしています。行列同士の除算は定義されていません。
5518
5519 演算の実行前には、行列どうしの行数・列数が適切かどうかチェックし、不適切であればC++の例外機構によってプログラムが強制終了します。コンパイル時にはチェックされません。
5520
5521 実数との演算と行列どうしの演算は同じ式中に混在させることができます。演算子の優先順位はC++の仕様に従います。
5522
5523 |!Matrix 演算子 線形代数演算|&gt;|
5524 |+|行列の加算をします(可換)。両辺の行数・列数は等しくなければなりません。|
5525 |-|行列の減算をします(不可換)。両辺の行数・列数は等しくなければなりません。|
5526 |*|行列の乗算をします(不可換)。左辺項の行数と右辺項の列数は等しくなければなりません。|
5527 |*~|行列の要素どうしを乗算します。Matlabでの.演算子に相当します(可換)。両辺の行数・列数は等しくなければなりません。|
5528
5529 不可換な演算子は、右と左のMatrixを入れ替えると演算結果が変わります。
5530 {{{
5531 Matrix A, B;
5532 A+B == B+A;
5533 A-B != B-A;
5534 A*B != B*A;
5535 A*~B == B*~A;
5536 }}}
5537
5538 !! プログラム例
5539 {{{
5540 #include &lt;fstream&gt;
5541 #include &lt;psychlops.h&gt;
5542 using namespace Psychlops;
5543
5544 void psychlops_main() {
5545         std::ofstream result(&quot;dump.txt&quot;);
5546
5547         Psychlops::Matrix a(5, 5),  b(5, 5), c(5, 5);
5548         a = 3;
5549         b = 2;
5550         c = a + b;
5551         result &lt;&lt; c;
5552
5553         b(2,2) = 0;
5554         c = b * a - 6;
5555         result &lt;&lt; c;
5556 }
5557 }}}
5558
5559
5560 !演算時の制限
5561 Psychlopsの行列は、四則演算において最低限の最適化のために、= 演算子で代入を行ったときにすべての計算を行うよう定義されています。このため、=演算子による代入が行われないと機能しないことがあります。
5562
5563 以下のコードは機能します。
5564 {{{
5565 Matrix a, b, c, d;
5566 c = a - b;
5567 d = max(c, d);
5568 }}}
5569 以下のコードは機能しません
5570 {{{
5571 Matrix a, b, c;
5572 d = max(a - b, c);
5573 }}}
5574 また、再帰的代入(代入先の左辺の変数が右辺にも出てくること)を行う場合は、当該行列が右辺に2回以上出現すると問題が生じる可能性があります。なるべく再帰的代入は行わないでください。
5575 {{{
5576 Matrix a, b;
5577 a = a + a * a;  // 右辺にaが2回以上出てくるのは危険
5578 b = a + a * a;  // 問題なし
5579 }}}
5580 </pre>
5581 </div>
5582 <div title="6.3.5 行列の中身をオフスクリーンに描画する" modifier="Psychlops_DevelopperG" modified="200908190208" created="200710171234" changecount="1">
5583 <pre>MatrixとImageは両方とも2次元に並べられた値(輝度や色)で、相互に変換が可能です。ImageをMatrixの計算機能を使って生成・加工するときに役立ちます。また、Matrixに変換すると各色チャネルが独立して制御しやすいため、各色チャネルを別々に操作する場合などにも役立ちます。
5584 ただし、[[この節の最初で述べたように|6.3 行列演算を行う]]、Matrixクラスの書式とImageクラスの書式がずれていることは注意して下さい。
5585
5586 Matrix / Imageの変換命令はImage型の一部になっています。
5587
5588
5589 !MatrixからImageへの変換
5590
5591 |!Image::from|from( luminance )|Matrixを輝度としてImageに読み込み、グレースケール画像を作成します。|
5592 |~|~|Matrix luminance: |
5593 |~|from( r, g, b )|Matrixを赤、緑、青チャネルとして読み込み、RGB画像を作成します。|
5594 |~|~|Matrix r: |
5595 |~|~|Matrix g: |
5596 |~|~|Matrix b: |
5597 |~|from( r, g, b, a) |Matrixを赤、緑、青、透明度チャネルとして読み込み、RGBA画像を作成します。|
5598 |~|~|Matrix r: |
5599 |~|~|Matrix g: |
5600 |~|~|Matrix b: |
5601 |~|~|Matrix a: |
5602 Imageの中身がすでに確保されていた場合は、破棄されます。
5603
5604
5605 !ImageからMatrixへの変換
5606
5607 |!Image::to|to( luminance )|グレースケール画像の輝度値をMatrixに書き込みます。|
5608 |~|~|Matrix luminance: |
5609 |~|to( r, g, b ) |RGB画像のMatrixを赤、緑、青チャネル値をMatrixに書き込みます。|
5610 |~|~|Matrix r: |
5611 |~|~|Matrix g: |
5612 |~|~|Matrix b: |
5613 |~|to( r, g, b, a ) |RGBA画像の赤、緑、青、透明度チャネルをMatrixに書き込みます。|
5614 |~|~|Matrix r: |
5615 |~|~|Matrix g: |
5616 |~|~|Matrix b: |
5617 |~|~|Matrix a: |
5618 書き込み先のMatrixは空である必要があります。大きさ等がImageと等しくなるように自動的に調整されます。
5619
5620
5621 !Image/Matrix変換の使用例
5622 以下のプログラムは、&quot;sample.png&quot;という名前のPNG画像を読み込み、赤チャネルと緑チャネルの差分を計算して&quot;diff_sample.png&quot;という名前で保存します。
5623
5624 &lt;注&gt;:場合によってはプログラムが正常終了せず、CPUを使い切ってしまうことがあります。そのときは、タスクマネージャ等で強制終了させてください。
5625 {{{
5626 &lt;例1&gt;
5627 #include &lt;psychlops.h&gt;
5628 using namespace Psychlops;
5629
5630 void psychlops_main() {
5631
5632         Image img;
5633         Matrix R, G, B;
5634         Matrix dR, dG;
5635
5636         img.load(&quot;sample.png&quot;);
5637
5638         img.to(R, G, B);
5639
5640         dR = R - G;
5641         dG = G - R;
5642
5643         B = 0;
5644         dR = max(dR, B);
5645         dG = max(dG, B);
5646
5647         img.from(dR, dG, B);
5648
5649         img.save(&quot;diff_sample.png&quot;);
5650
5651 }
5652 }}}</pre>
5653 </div>
5654 <div title="6.3.6 メッシュグリッドの作成" modifier="Psychlops_DevelopperG" modified="200908190210" created="200712061907" changecount="1">
5655 <pre>!メッシュグリッドとは
5656 メッシュグリッドとは、行列の各要素への代入を、ループを使わず普通の数式に見える形式で書くテクニックです。Matlabでよく使用されています。
5657
5658 5 x 5 の大きさの行列に対する操作を行う場合、まず以下のような行列(メッシュグリッド)を作成します。
5659 {{{
5660 X:
5661 0 1 2 3 4
5662 0 1 2 3 4
5663 0 1 2 3 4
5664 0 1 2 3 4
5665 0 1 2 3 4
5666
5667 Y:
5668 0 0 0 0 0
5669 1 1 1 1 1
5670 2 2 2 2 2
5671 3 3 3 3 3
5672 4 4 4 4 4
5673 }}}
5674
5675 この行列を座標値とみなし、各要素について演算する関数を適用します。
5676 {{{
5677 Patch = cos(X) * sin(Y);
5678 }}}
5679 これは、以下のプログラムと同等の計算を行います。
5680 {{{
5681 Matrix Patch(5,5);
5682 for(int Y = 0; Y&lt;5; Y++) {
5683         for(int X = 0; X&lt;5; X++) {
5684                 Patch(X, Y) = cos(X) * sin(Y);
5685         }
5686 }
5687 }}}
5688
5689
5690 !メッシュグリッドの生成
5691 |!Matrix::mesh|mesh(Range row, int col)|行を変動値とするmeshgridを生成します|
5692 |~|~|row: 行(Imageではy座標に相当)の範囲を指定します。行数は範囲から自動決定されます。|
5693 |~|~|col: 列(Imageではx座標に相当)の列数を指定します。|
5694 |~|mesh(int row, Range col)|列を変動値とするmeshgridを生成します|
5695 |~|~|row: 行(Imageではy座標に相当)の行数を指定します。|
5696 |~|~|col: 列(Imageではx座標に相当)の範囲を指定します。行数は範囲から自動決定されます。|
5697 {{{
5698 &lt;使用例&gt;
5699 Range row, col;
5700 Matrix m, y, x;
5701 y = Matrix::mesh(-2&lt;=row&lt;=2, 5);
5702 x = Matrix::mesh(5, -2&lt;=col&lt;=2);
5703
5704 =&gt;
5705 Y;
5706 -2 -2 -2 -2 -2
5707 -1 -1 -1 -1 -1
5708  0  0  0  0  0
5709  1  1  1  1  1
5710  2  2  2  2  2
5711 X;
5712 -2 -1  0  1  2 
5713 -2 -1  0  1  2 
5714 -2 -1  0  1  2 
5715 -2 -1  0  1  2 
5716 -2 -1  0  1  2 
5717 }}}
5718
5719 行の範囲、列の範囲を同時に指定する方法もあります。引数で指定した行列にメッシュグリッドの内容が入ります。
5720 |!Matrix::mesh|mesh(Range row, Matrix r, Range col, Matrix c)|行、列を変動値とするmeshgridを生成します|
5721 |~|~|row: 行(Imageではy座標に相当)の範囲を指定します。行数は範囲から自動決定されます。|
5722 |~|~|r: 行用のメッシュグリッドを指定します。|
5723 |~|~|col: 列(Imageではx座標に相当)の範囲を指定します。列数は範囲から自動決定されます。|
5724 |~|~|c: 列用のメッシュグリッドを指定します。|
5725 {{{
5726 &lt;使用例&gt;
5727 Range row, col;
5728 Matrix m, y, x;
5729 Matrix::mesh(-2&lt;=row&lt;=2, y,-2&lt;=col&lt;=2,x);
5730
5731 =&gt;
5732 Y;
5733 -2 -2 -2 -2 -2
5734 -1 -1 -1 -1 -1
5735  0  0  0  0  0
5736  1  1  1  1  1
5737  2  2  2  2  2
5738 X;
5739 -2 -1  0  1  2 
5740 -2 -1  0  1  2 
5741 -2 -1  0  1  2 
5742 -2 -1  0  1  2 
5743 -2 -1  0  1  2 
5744 }}}
5745
5746 !!Meshgridに使用可能な関数
5747 ここに記述されている関数はすべて自己破壊せず、新たな行列を生成して返します。
5748 |!Matrix::sin|sin(Matrix m)|mの各要素に対してsin関数の返り値を代入した行列を返します。|
5749 |!Matrix::cos|cos(Matrix m)|mの各要素にcos関数の返り値を代入した行列を返します。|
5750 |!Matrix::tan|tan(Matrix m)|mの各要素にtan関数の返り値を代入した行列を返します。|
5751 |!Matrix::exp|exp(Matrix m)|mの各要素にexp関数の返り値を代入した行列を返します。|
5752 |!Matrix::sqrt|sqrt(Matrix m)|mの各要素にsqrt関数の返り値を代入した行列を返します。|
5753 |!Matrix::pow|pow(Matrix m, double ex)|mの各要素を引数としてex乗した値を代入した行列を返します。|
5754
5755
5756 {{{
5757 #include &lt;psychlops.h&gt;
5758 using namespace Psychlops;
5759
5760 void psychlops_main()
5761 {
5762         double wave_length;
5763
5764         Canvas display(Canvas::fullscreen);
5765         Range row, col;
5766
5767         Matrix m, y, x;
5768         y = Matrix::mesh(-100&lt;=row&lt;=100, 201);
5769         x = Matrix::mesh(201, -100&lt;=col&lt;=100);
5770
5771         m = pow(sin(x/wave_length * 2*PI) + cos(y/wave_length * 2*PI), 4.0);
5772
5773         Image img;
5774         img.from(m);
5775
5776         while(!Input::get(Keyboard::esc)) {
5777                 img.draw();
5778                 display.flip();
5779         }
5780 }
5781 }}}
5782
5783
5784
5785 !その他
5786 |!Matrix::min|min(Matrix a, Matrix b)|aとbの各要素を比較し、小さいほうを要素とする行列を返します。両Matrixの大きさは同じでなければなりません。|
5787 |!Matrix::max|max(Matrix a, Matrix b)|aとbの各要素を比較し、大きいほうを要素とする行列を返します。両Matrixの大きさは同じでなければなりません。|
5788
5789 {{{
5790 A = 
5791 1 2 3
5792 0 0 2
5793
5794 B =
5795 5 3 0
5796 1 1 1
5797
5798 C = max(A, B);
5799
5800 C =&gt;
5801 5 3 3
5802 1 1 2
5803
5804 }}}
5805 </pre>
5806 </div>
5807 <div title="CSSEdit_Code" modifier="Psychlops_Admin" modified="200708280258" created="200708280257" tags="systemConfig" changecount="1">
5808 <pre>config.cssEdit ={};
5809 config.cssEdit.settings = {
5810     tags:&quot;&quot;,                         //記事のタグ
5811     user:&quot;CSSEditBackup&quot;,            //作成者名
5812     backupname:&quot;CSSEditBackup&quot;       //バックアップタイトル名
5813 }
5814
5815 config.macros.cssEdit = {};
5816 config.macros.cssEdit.handler = function(place,macroName,params){
5817  if(readOnly){return;}
5818
5819     var s =  '&lt;form mime=&quot;text/plain&quot; name=&quot;CSSEditForm&quot;&gt;'
5820            + '&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;'
5821            + '&lt;p&gt;'
5822            + '&lt;input type=&quot;button&quot; name=&quot;go&quot; value=&quot;GO&quot; onclick=&quot;config.macros.cssEdit.go();&quot;&gt;&amp;nbsp;'
5823            + '&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;' 
5824            + '&lt;input type=&quot;button&quot; name=&quot;Backup&quot; value=&quot;Backup&quot; onclick=&quot;config.macros.cssEdit.Backup();&quot;&gt;&amp;nbsp;'
5825            + '&lt;input type=&quot;button&quot; name=&quot;Restore&quot; value=&quot;Restore&quot; onclick=&quot;config.macros.cssEdit.restore();&quot;&gt;&amp;nbsp;'
5826            + '&lt;input type=&quot;reset&quot; value=&quot;reset&quot;&gt;&amp;nbsp;'
5827            + '&lt;/p&gt;'
5828            + '&lt;/form&gt;'
5829            + '&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;';
5830
5831
5832     var e = createTiddlyElement(place,&quot;div&quot;,null,null,&quot;&quot;);
5833
5834     e.innerHTML = s;
5835  
5836 }
5837 config.macros.cssEdit.go=function(){
5838   //setStylesheet(document.CSSEditForm.i.value,&quot;CSSEdit&quot;);
5839   var s =document.CSSEditForm.i.value;
5840   var title = &quot;CSSEdit_temp&quot;;
5841    config.macros.cssEdit.save(title,s,&quot;temp&quot;,&quot;&quot;);
5842    var html   = store.getRecursiveTiddlerText(title,null,10);
5843    store.deleteTiddler(title);
5844
5845    setStylesheet(html,&quot;CSSEdit&quot;);
5846 };
5847
5848 config.macros.cssEdit.readStyleSheet=function(){
5849     if (! window.confirm(&quot;Read StyleSheet OK ?&quot;))
5850         return;
5851     document.CSSEditForm.i.value = store.getTiddlerText(&quot;StyleSheet&quot;);
5852 };
5853
5854 config.macros.cssEdit.Backup=function(){
5855     if (! window.confirm(&quot;Backup OK?&quot;))
5856          return;
5857
5858     var p = config.cssEdit.settings;
5859     var s = document.CSSEditForm.i.value;
5860     var title = p.backupname;
5861     config.macros.cssEdit.save(title,s,p.user,p.tags)
5862     saveChanges();
5863 };
5864
5865 config.macros.cssEdit.restore=function(){
5866     if (! window.confirm(&quot;Restore OK ?&quot;))
5867         return;
5868
5869     var p = config.cssEdit.settings;
5870     document.CSSEditForm.i.value = store.getTiddlerText(p.backupname);
5871 };
5872
5873
5874 config.macros.cssEdit.save = function(title,s,user,tags){
5875     var p = config.HTMLEdit.settings;
5876     var now = new Date();
5877     var tiddler = store.fetchTiddler(title);
5878     var created;
5879     if(tiddler){
5880          created = tiddler.created;
5881         store.deleteTiddler(title);
5882     }else{
5883          tiddler = new Tiddler();
5884          created = now;
5885      }
5886     tiddler.set(title,s,user,now,tags,created);
5887     store.addTiddler(tiddler);
5888 };</pre>
5889 </div>
5890 <div title="Canvas::clear()" modifier="Psychlops_DevelopperG" modified="200910080819" created="200712131650" changecount="4">
5891 <pre>|!Canvas::clear()|clear([[Psychlops::Color]] col,TargetSurface)|&gt;|指定したCanvasを指定色で塗りつぶします|
5892 |~|~|[[Psychlops::Color]] col |塗りつぶす色を指定|
5893 |~|~|TargetSurface|塗りつぶす画面を指定|
5894 |~|clear(TargetSurface)|指定したCanvasを塗りつぶします。色の指定には[[Canvas::setClearColor()]]命令を使用します|
5895 |~|~|TargetSurface|塗りつぶす画面を指定|
5896
5897 [[2.1.2 Canvasの基本構造と操作]]</pre>
5898 </div>
5899 <div title="Canvas::copy()" modifier="Psychlops_DevelopperG" modified="200910080847" created="200709170159" changecount="24">
5900 <pre>[[Psychlops::Rectangle]]型(source)で指定される範囲内の情報を[[Psychlops::Rectangle]]型(target)の範囲に複写または移動します。移動元と移動先の矩形の大きさは同じにする必要があります。
5901 |!Canvas::copy()|copy([[Psychlops::Rectangle]] source, [[Psychlops::Rectangle]] target, bool copymode, [[Psychlops::Color]] col)|&gt;|! [[Psychlops::Rectangle]]型の情報を[[Psychlops::Image]]型に複写または移動する|&gt;|&gt;|
5902 |~|~|[[Psychlops::Rectangle]] source|移動(複写)元のRectangleを指定|&gt;|
5903 |~|~|[[Psychlops::Rectangle]] target|移動(複写)先のRectangleを指定|&gt;|
5904 |~|~|bool copymode| trueを指定する場合は移動処理(カット), falseを指定する場合は複写処理(コピー)|指定しない場合、false(複写)が指定される|
5905 |~|~|[[Psychlops::Color]] col|移動処理時の移動元の塗りつぶし色を指定|指定しない場合、黒色が指定される|
5906 |~|~|~|~|複写の場合、指定不要|
5907
5908 !!Canvas::copy()の書き方
5909 rect1の内部にある描画内容ををrect2の内部に複写します。
5910 {{{
5911 &lt;例&gt;
5912 #include &lt;psychlops.h&gt;
5913 using namespace Psychlops;
5914
5915 Psychlops::Rectangle rect1(100,100);
5916 Psychlops::Rectangle rect2(100,100);
5917
5918 void psychlops_main() {
5919
5920         Canvas sampleA(Canvas::fullscreen);
5921         rect1.centering();
5922         while(!Input::get(Keyboard::spc)){
5923         sampleA.rect(rect1,Color::yellow); 
5924         sampleA.copy(rect1,rect2);
5925         sampleA.flip();
5926         }
5927 }
5928 }}}</pre>
5929 </div>
5930 <div title="Canvas::flip()" modifier="Psychlops_DevelopperG" modified="200910080820" created="200709130046" changecount="6">
5931 <pre>表と裏の2つの画面の入れ替え処理です。
5932 描画内容はCanvas::flip() 処理で画面を入れ替えることで初めて画面上に表示されます。
5933 この命令は画面の垂直同期信号(=ビデオカードが1枚の画面を書ききったことを示す信号; Vsync)に同期して、画面の切り替えを行います。
5934 * 参考 [[Tips: Canvas::flip()とコマ落ち]]
5935
5936 |!Canvas::flip()|flip(int)|int入力値のリフレッシュ分描画内容を表示します|
5937 |~|~|引数を入力しない場合(flip())、flip(1)が指定されます|
5938 |~|flipAfter(int wait)|int wait回のリフレッシュ後まで待ってからflip()を実行して描画内容を画面に反映させます|
5939 |~|flipAndWait(int wait)|もっとも近い垂直同期のタイミングでflip()を実行した後に、int wait回ののリフレッシュ分の表示を予約します。次にflip()命令がコールされても、int wait回ののリフレッシュ分が経過するまでは何も起こりません(flip()と全く同じ効果ですが、引数は省略不可)|
5940
5941 flip()系の命令は画面描画の時間制御をする上でもっとも重要な命令です。上の表だけではわかりにくいでしょうから、図と実例を用いて各命令の働きをもう少し詳しく見ていきましょう。
5942
5943 flipAfter(), flipAndWait()命令の動きを図解すると以下のようになります。以下の図ではflipAfter(3)とflip(3)(あるいはflipAndWait(3))の場合を例としてあげました。
5944 [img[image/psychlops_flip.png]]
5945
5946 コード例は[[2.1.3節|2.1.3 Canvasのメンバ取得と変数の表示]]参照</pre>
5947 </div>
5948 <div title="Canvas::getXXXX()" modifier="Kazushi Maruya" modified="200712131657" created="200712131652" changecount="2">
5949 <pre>|!Canvas::getXXXX()|getCenter()|画面の中心座標(x,y)を取得します|
5950 |~|getHcenter()|横方向の中心座標(x)を取得します|
5951 |~|getVcenter()|縦方向の中心座標(y)を取得します|
5952 |~|getHeight()|画面の高さ(ピクセル)を取得します|
5953 |~|getWidth()|画面の幅(ピクセル)を取得します|
5954 |~|getColorDepth()|カラーモード(ビット)を取得します|
5955 |~|getRefreshRate()|リフレッシュレート(Hz)を取得します|
5956
5957 [[2.1.3 Canvasのメンバ取得と変数の表示]]</pre>
5958 </div>
5959 <div title="Canvas::lastFailedFrames()" modifier="Psychlops_DevelopperG" modified="200910080852" created="200709170302" changecount="6">
5960 <pre>[[Canvas::flip()]]命令で画面の切り替えをする時にコマ落ちする場合があります。
5961 その時に落ちたフレーム数を取得します。
5962 * 参考 [[Tips: Canvas::flip()とコマ落ち]]
5963 |!Canvas::lastFailedFrames()|lastFailedFrames()|!1秒間で落ちたフレーム数を取得します|
5964
5965 !!Canvas::lastFailedFrames()の書き方
5966 フレームが落ちたときにグレーで画面をクリアしています。
5967 (フレームが落ちるのが一瞬のため[[Canvas::var()]]命令で落ちた値を確認することは困難なため)
5968 何枚落ちているかは右上の赤い数字が落ちたフレーム数の和算となっているので確認してください。
5969 Display::~の書き方については [[Tips: CanvasクラスとDisplayクラス]]を参照してください。
5970
5971 {{{
5972 &lt;例&gt;
5973 #include &lt;psychlops.h&gt;
5974 using namespace Psychlops;
5975
5976 void ullmancylinder() {
5977         const int DOTCNT = 15000;
5978
5979         int dotcnt = 100;
5980         double bg_lum = 0.0;
5981         double velocity = 1.0;
5982         double SOA_pp;
5983         int SIZE = 3;
5984         int SOA = 1;
5985         double CylinderRadius=200;
5986         int a1=0;
5987         
5988         Range rng;
5989         Independent &lt;&lt; dotcnt   | &quot;Number of Dots&quot; |   1&lt; rng&lt; DOTCNT |  10   | 1000 ;
5990         Independent &lt;&lt; velocity | &quot;Dot Velocity&quot;   | 0.0&lt;=rng&lt;=10.0   |  0.1 | 0.05;
5991         Independent &lt;&lt; SOA      | &quot;SOA Frames&quot;     |   1.0&lt;=rng&lt;=60.0     |  1   | 1;
5992         Independent &lt;&lt; bg_lum   | &quot;Luminance Inv.&quot; | 0.0&lt;=rng&lt;=1.0    |  1   | 1;
5993         Independent &lt;&lt; SIZE     | &quot;Dot Size&quot;       |   0&lt; rng&lt;=10    |  1   | 1;
5994         Independent &lt;&lt; CylinderRadius|  &quot;Cylinder Size&quot;  |  0&lt; rng&lt;= 400   |  1   | 50;
5995
5996
5997         Canvas canvas(Canvas::fullscreen);
5998         Psychlops::Rectangle rect(SIZE,SIZE);
5999         Psychlops::Color bgcolor(0), dotcolor(1.0-bg_lum);
6000
6001         double phase=0.0;
6002         double x[DOTCNT], dx[DOTCNT], y[DOTCNT];
6003
6004         for(int i=0; i&lt;DOTCNT; i++) {
6005                 x[i] = Psychlops::random(CylinderRadius*2*PI);
6006                 y[i] = Psychlops::random(CylinderRadius*2)+200;
6007         }
6008         Display::watchFPS();
6009         while(!Input::get(Keyboard::spc)) {
6010                 bgcolor.set(bg_lum);
6011                 dotcolor.set(1.0-bg_lum);
6012                 canvas.clear(bgcolor);
6013                 rect.resize(SIZE,SIZE);
6014
6015                 phase += velocity;      
6016                 SOA_pp =SOA+1;
6017
6018                 for(int i=0; i&lt;DOTCNT; i++)  dx[i] = (int)(CylinderRadius*sin(x[i]+SOA_pp*phase/100.0))+canvas.getHcenter();//各ドット位置の計算
6019                 for(int i=0; i&lt;dotcnt; i++)  rect.centering(dx[i], y[i]).draw(dotcolor);
6020                 //落ちたフレーム数を返します
6021                 a1=Display::lastFailedFrames();
6022                 if(a1)Display::clear(Color(0.25,0.25,0.25));
6023                 Display::showFPS();
6024                 canvas.flip(SOA);
6025         }
6026 }
6027
6028
6029 void psychlops_main()
6030 {
6031
6032         Procedure p;
6033         p.setDesign(Procedure::DEMO);
6034         p.setProcedure(ullmancylinder);
6035         p.run();
6036
6037 }
6038 }}}
6039
6040 </pre>
6041 </div>
6042 <div title="Canvas::line()" modifier="Psychlops_DevelopperG" modified="200910080837" created="200712131651" changecount="4">
6043 <pre>|!Canvas::line()|line(double x1, double y1, double x2, double y2, [[Psychlops::Color]] col)|&gt;|!開始座標(X1,Y1)から終端座標(X2,Y2)まで指定色の線を描画します|
6044 |~|~|double x1|描画する線の開始座標x1を指定|
6045 |~|~|double y1|描画する線の開始座標y1を指定|
6046 |~|~|double x2|描画する線の終端座標x2を指定|
6047 |~|~|double y2|描画する線の終端座標y2を指定|
6048 |~|~|[[Psychlops::Color]] col|描画する点の色を指定|
6049 |~|line([[Psychlops::Point]] point1, [[Psychlops::Point]] point2,[[Psychlops::Color]] col)|&gt;|!開始Point座標(x1,x1)から終端Point座標(X2,Y2)まで指定色の線を描画します|
6050 |~|~|[[Psychlops::Point]] point1|開始座標(x1,y1)を指定|
6051 |~|~|[[Psychlops::Point]] point2|終端座標(x2,y2)を指定|
6052 |~|~|[[Psychlops::Color]] col|描画する点の色を指定|
6053 画面上に線を描画するためには、「どこから」「どこに」「何色」の線を引くのかの指定が必要です。
6054 「どこから」は開始座標(x1,y1)、「どこに」は終端座標(x2,y2)、「何色」は[[Psychlops::Color]]型で指定します。
6055 座標はCanvasの左上端が座標の基点(x=0,y=0)となっています。
6056 これらの座標はPoint型の変数を使用して指定することもできます。
6057
6058 [img[image/canvasline.png]]
6059
6060 [[2.2.2 画面上に線を描画する]]</pre>
6061 </div>
6062 <div title="Canvas::msg()" modifier="Psychlops_DevelopperG" modified="200910080829" created="200709130031" changecount="8">
6063 <pre>画面上に任意の文字列を表示します。
6064 |!Canvas::msg()|msg(char* string,double x,double y,[[Psychlops::Color]] col)|&gt;|!char* stringを座標(x,y)に指定色で表示します|
6065 |~|~|schar* string|画面表示をする文字列を指定|
6066 |~|~|double x|画面表示をする座標(x)を指定|
6067 |~|~|double y|画面表示をする座標(y)を指定|
6068 |~|~|[[Psychlops::Color]] col|描画する点の色を指定&lt;br&gt;(設定しない場合、白が設定される)|
6069 |~|msg(string str,double x,double y,[[Psychlops::Color]] col)|&gt;|!string strを座標(x,y)に指定色で表示します|
6070 |~|~|string str|画面表示をする文字列を指定|
6071 |~|~|double x|画面表示をする座標(x)を指定|
6072 |~|~|double y|画面表示をする座標(y)を指定|
6073 |~|~|[[Psychlops::Color]] col|描画する点の色を指定&lt;br&gt;(設定しない場合、白が設定される)|
6074
6075 [[2.1.3 Canvasのメンバ取得と変数の表示]]</pre>
6076 </div>
6077 <div title="Canvas::oval()" modifier="Psychlops_DevelopperG" modified="200910080839" created="200712131653" changecount="2">
6078 <pre>|!Canvas::oval()|oval([[Psychlops::Rectangle]] rect, [[Psychlops::Color]] col)|&gt;|![[Psychlops::Rectangle]] rectで指定された直径の円を指定色で塗りつぶし描画します|
6079 |~|~|[[Psychlops::Rectangle]] rect|描画する円の直径|
6080 |~|~|[[Psychlops::Color]] col|描画する点の円を指定|
6081
6082 [img[image/canvasoval.png]]</pre>
6083 </div>
6084 <div title="Canvas::pix()" modifier="Psychlops_DevelopperG" modified="200910080833" created="200712131650" changecount="6">
6085 <pre>|!Canvas::pix()|pix(double x,double y,[[Psychlops::Color]] col) |&gt;|!座標(x,y)に指定色の点を描画します|
6086 |~|~|double x|描画する点の座標軸xを指定|
6087 |~|~|double y|描画する点の座標軸yを指定|
6088 |~|~|[[Psychlops::Color]] col|描画する点の色を指定|
6089 |~|pix([[Psychlops::Point]] point,[[Psychlops::Color]] col) |&gt;|!Point座標(x,y)に指定色の点を描画します|
6090 |~|~|[[Psychlops::Point]] point |座標(x,y)を指定|
6091 |~|~|[[Psychlops::Color]] col|描画する点の色を指定|
6092 |~|pix(int dotsCnt,double* xArray,double* yArray),[[Psychlops::Color]]* colArray )|&gt;|!配列の座標(x[],y[])に指定色(配列を用いて複数色指定)の点を描画します|
6093 |~|~|int dotsCnt|描画する点の数を指定(後ろの引数の配列の個数を超えてはいけない)|
6094 |~|~|double* xArray| 描画する点の座標軸xが格納された配列名(ポインタ)を指定|
6095 |~|~|double* yArray| 描画する点の座標軸yが格納された配列名(ポインタ)を指定|
6096 |~|~|[[Psychlops::Color]]* colArray|描画する点の色が格納された配列名(ポインタ)を指定|
6097 |~|pix(int dotsCnt,double* xArray,double* yArray,[[Psychlops::Color]] col)|&gt;|!配列の座標(x[],y[])に指定色(一色のみ)の点を描画します|
6098 |~|~|int dotsCnt|描画する点の数(配列数)を指定|
6099 |~|~|double* xArray|描画する点の座標軸xが格納された配列名(ポインタ)を指定|
6100 |~|~|double* yArray|描画する点の座標軸yが格納された配列名(ポインタ)を指定|
6101 |~|~|[[Psychlops::Color]] col|描画する点の色を指定|
6102 |~|pix(int dotsCnt,[[Psychlops::Point]]* pointArray,[[Psychlops::Color]]* colArray)|&gt;|!配列Point座標(x[],y[])に指定色の点を描画します|
6103 |~|~|int dotsCnt|描画する点の数(配列数)を指定|
6104 |~|~|[[Psychlops::Point]]* pointArray|配列の座標(x[],y[])が格納された配列名(ポインタ)を指定|
6105 |~|~|[[Psychlops::Color]]* colArray|描画する点の色が格納された配列名(ポインタ)を指定|
6106
6107 [img[image/canvaspix.png]]
6108
6109 [[2.2.1 画面上に点を描画する-Pointクラス1-]]</pre>
6110 </div>
6111 <div title="Canvas::pix(double, double, double)という命令はコンパイルできないのですか?" modifier="Psychlops_DevelopperG" created="200908100457" changecount="1">
6112 <pre>Canvas::pix(double, double, double)という命令はコンパイルできないのですか?
6113
6114 できません。最後の色の引数をColor(double)としてColor型にキャスト(変換)してください。
6115 {{{
6116 (正) display.pix(100,100,Color(0.5));
6117 (誤) display.pix(100,100,0.5);
6118 }}}</pre>
6119 </div>
6120 <div title="Canvas::progressbar()" modifier="Psychlops_DevelopperG" modified="200910080852" created="200712131654" changecount="4">
6121 <pre>|!Canvas::progressbar()|void progressbar(X now, X max)|!現在のnowの値のmaxに対する比率をプログレスバーとして表示します。now, maxは数値型であればどの型でもかまいません|&gt;|
6122 |~|~|X now|現在の進行度を示す変数値(型は数値型)|
6123 |~|~|X max|nowの最終的な到達値(型は数値型)|
6124 |~|void progressbar(double ratio)|!ratioで示される比率をプログレスバーとして表示します。|&gt;|
6125 |~|~|double ratio|比率の値(0.0~1.0)|
6126
6127 [[5.2.2 画面に反映されない描画の進行度を表示する-プログレスバー]]</pre>
6128 </div>
6129 <div title="Canvas::rect()" modifier="Psychlops_DevelopperG" modified="200910080838" created="200709122308" changecount="13">
6130 <pre>画面上に四角形を描画します。
6131
6132 |!Canvas::rect()|rect([[Psychlops::Rectangle]] rect, [[Psychlops::Color]] col)|&gt;|!四角形を指定色で描画します|
6133 |~|~|([[Psychlops::Rectangle]] rect|描画する四角形を指定|
6134 |~|~|[[Psychlops::Color]] col|描画する四角形の色を指定|
6135 [[2.2.3 画面上に四角形を描画する-Rectangleクラス1-]]</pre>
6136 </div>
6137 <div title="Canvas::setClearColor()" modifier="Psychlops_DevelopperG" modified="200910080852" created="200709170246" changecount="6">
6138 <pre>[[Canvas::clear()|2.1.2 Canvasの基本構造と操作]]命令使用時に塗りつぶす色の設定を行います。
6139 setClearColor(Color)命令使用時には必ず[[Canvas::clear()|2.1.2 Canvasの基本構造と操作]]命令を使用してください。
6140 |!Canvas::setClearColor()|setClearColor([[Psychlops::Color]] col)|&gt;|!指定したCanvasを塗りつぶすための色を設定します|
6141 |~|~|[[Psychlops::Color]] col |塗りつぶす色を指定|
6142
6143 !!Canvas::setClearColor()の書き方
6144 今ディスプレイに表示されている画面(紙の表)を白く塗りつぶします。
6145 {{{
6146 &lt;例&gt;
6147 #include &lt;psychlops.h&gt;
6148 using namespace Psychlops;
6149
6150 void psychlops_main() {
6151
6152         Canvas sampleA(Canvas::fullscreen);
6153         sampleA.setClearColor(Color::white);
6154         sampleA.clear(Canvas::FRONT);
6155         while(!Input::get(Keyboard::spc));
6156 }
6157 }}}</pre>
6158 </div>
6159 <div title="Canvas::setGammaTable()" modifier="Psychlops_DevelopperG" modified="200910080850" created="200802130702" changecount="5">
6160 <pre>他の多くの描画ライブラリ同様、CRTの各画素における輝度レベルとPsychlopsで指定する輝度レベルは線形の関係を維持しません。これを簡易的に補正するために、R,G,Bの各色チャンネルについて変換表(LUTテーブル)を用いた補正を行う方法が広く用いられています。
6161 Psychlopsでは、この変換表を指定することで、[[ガンマ補正|Tips: ガンマ補正]]された色を表示することが可能です。このための命令として、setGammaTable()命令があります。
6162
6163 通常は[[Color::setGammaTable()]]を用いますが、将来的にCanvasごとに別の補正値が必要なときのためにこの関数が予約されています。
6164
6165 |!Canvas::setGammaTable()|setGammaTable(std::vector&lt;double&gt; Cr, std::vector&lt;double&gt; Cg, std::vector&lt;double&gt; Cb)|&gt;|!Canvasが確保している表示装置のDACテーブルを直接書き換え、ガンマ補正を行います。|
6166 |~|~|std::vector&lt;double&gt; Cr|R(赤)チャンネルの変換表|
6167 |~|~|std::vector&lt;double&gt; Cg|G(緑)チャンネルの変換表|
6168 |~|~|std::vector&lt;double&gt; Cb|B(青)チャンネルの変換表|
6169 *2 256個以下でも自動的に線形補完されますが、正確な補完は256個すべて指定することで実現されます。
6170 *3 値は配列またはC++STLの動的配列(std::vector&lt;double&gt;)で指定し、8ビット環境なら256段階の理論値を配列の要素番号とし、それぞれの段階の補償値を配列要素に書き込みます。
6171
6172 コード例は[[ガンマ補正|Tips: ガンマ補正]]参照</pre>
6173 </div>
6174 <div title="Canvas::setGammaValue()" modifier="Psychlops_DevelopperG" modified="200910080849" created="200709170234" changecount="11">
6175 <pre>他の多くの描画ライブラリ同様、CRTの各画素における輝度レベルとPsychlopsで指定する輝度レベルは線形の関係を維持しません。これを簡易的に補正するために、R,G,Bの各色チャンネルについてGamma関数を用いた補正を行う方法が広く用いられています。
6176 Psychlopsでは、このGamma関数の係数を指定することで、[[ガンマ補正|Tips: ガンマ補正]]された色を表示することが可能です。このための命令として、setGammaValue()命令があります。
6177
6178 通常は[[Color::setGammaValue()]]を用いますが、将来的にCanvasごとに別の補正値が必要なときのためにこの関数が予約されています。
6179
6180
6181 |!Canvas::setGammaValue()|setGammaValue(double Cr, double Cg, double Cb)|&gt;|!Canvasが確保している表示装置のDACテーブルを直接書き換え、ガンマ補正を行います。|
6182 |~|~|double Cr|表示装置のR(赤)チャンネルのガンマ係数|
6183 |~|~|double Cg|表示装置のG(緑)チャンネルのガンマ係数|
6184 |~|~|double Cb|表示装置のB(青)チャンネルのガンマ係数|
6185 * 指定する値は表示装置のガンマ値そのものです。Psychlops側で補償指数を自動決定します。
6186
6187 コード例は[[ガンマ補正|Tips: ガンマ補正]]参照</pre>
6188 </div>
6189 <div title="Canvas::showFPS()" modifier="Psychlops_DevelopperG" modified="200910080852" created="200712131656" changecount="2">
6190 <pre>|!Canvas::showFPS()|void showFPS(void)|!FPSチェッカのレポートを画面左上に表示します|
6191
6192 [[5.2.1 描画の時間精度を確認する-FPSチェッカー]]
6193 </pre>
6194 </div>
6195 <div title="Canvas::to()" modifier="Psychlops_DevelopperG" modified="200910080843" created="200709130159" changecount="6">
6196 <pre>[[Psychlops::Rectangle]]型で指定される範囲内の情報を[[Psychlops::Image]]型に複写します。
6197 この命令と''Image::save()''命令を組み合わせることによってImage上以外で描画した画像が保存できます。
6198 |!Canvas::to()|to([[Psychlops::Image]] img, [[Psychlops::Rectangle]] rect)|&gt;|![[Psychlops::Rectangle]]型の情報を[[Psychlops::Image]]型に複写する|
6199 |~|~|[[Psychlops::Image]] img |複写先のImage|
6200 |~|~|[[Psychlops::Rectangle]] rect |複写元のRectangle|
6201
6202 コード例は[[3.3.1節|3.3.1 画像ファイルを保存する]]参照</pre>
6203 </div>
6204 <div title="Canvas::var()" modifier="Psychlops_DevelopperG" modified="200910080830" created="200709130030" changecount="26">
6205 <pre>取得した変数の値を画面に表示します。
6206 |!Canvas::var()|var(string str, double x, double y,[[Psychlops::Color]] col, bool fillForward)|&gt;|&gt;|&gt;|!string strで指定された変数の値を取得して座標(x,y)に指定色で表示します|
6207 |~|~|&gt;|&gt;|string str|画面表示をする変数を指定|
6208 |~|~|&gt;|&gt;|double x|画面表示をする座標(x)を指定|
6209 |~|~|&gt;|&gt;|double y|画面表示をする座標(y)を指定|
6210 |~|~|&gt;|&gt;|[[Psychlops::Color]] col|描画する点の色を指定(設定しない場合、白が設定される)|
6211 |~|~|~|bool fillForward|表示位置の指定|true |指定座標(x,y)から左部分に表示する|
6212 |~|~|~|~|~|false |指定座標(x,y)から右部分に表示する|
6213 |~|~|~|~|&gt;|&gt;|(指定しない場合、falseが設定される)|
6214
6215 [[2.1.3 Canvasのメンバ取得と変数の表示]]</pre>
6216 </div>
6217 <div title="Canvas::watchFPS()" modifier="Psychlops_DevelopperG" modified="200910080852" created="200712131655" changecount="2">
6218 <pre>|!Canvas::watchFPS()|void watchFPS(void)|!FPSチェッカをonにします|
6219
6220 [[5.2.1 描画の時間精度を確認する-FPSチェッカー]]</pre>
6221 </div>
6222 <div title="CanvasMode" modifier="Psychlops_DevelopperG" modified="200910080814" created="200708212026" changecount="7">
6223 <pre>描画モード
6224 |Canvas::fullscreen()|画面いっぱいに描画領域を確保する|
6225 |Canvas::window()|指定サイズの描画領域を確保する|</pre>
6226 </div>
6227 <div title="Canvasの宣言" modifier="Psychlops_DevelopperG" modified="200910080808" created="200712131704" changecount="1">
6228 <pre>|!Canvasの宣言|
6229 |~|Canvas(int width,int height,int colordepth,double refreshrate)|&gt;|!全画面表示をします|
6230 |~|~|int width|水平方向の解像度(画面の横幅)を指定|
6231 |~|~|int height|垂直方向の解像度(画面の縦幅)を指定|
6232 |~|~|int colordepth|カラーモード(ビット)を指定|
6233 |~|~|double refreshrate|リフレッシュレート( Hz )を指定|
6234 |~|Canvas(Canvasmode mode, const Display disp)|&gt;|!現在の画面モードで画面を確保する場合に使います|
6235 |~|~|Canvasmode mode|[[表示モード|CanvasMode]](フルスクリーンorウィンドウ)を選択|
6236 |~|~|const [[Display|Displayの指定値]] disp|[[表示するディスプレイ|Displayの指定値]]を選択(省略可能)|
6237 |~|~|&gt;|マルチディスプレイ環境では、最後の引数でどのディスプレイに表示するか指定できます。省略時はプライマリディスプレイになります。詳細は [[マルチディスプレイの選択]]を参照ください。|
6238 |~|Canvas(int width, int height, int colordepth, double refreshrate, const Display disp)|&gt;|!画面モードを指定してフルスクリーン画面を確保します|
6239 |~|~|int width|水平方向の解像度(画面の横幅)を指定|
6240 |~|~|int height|垂直方向の解像度(画面の縦幅)を指定|
6241 |~|~|int colordepth|カラーモード(ビット)を指定|
6242 |~|~|double refreshrate|リフレッシュレート( Hz )を指定|
6243 |~|~|const [[Display|Displayの指定値]] disp|[[表示するディスプレイ|Displayの指定値]]を選択(省略可能)|
6244 |~|~|&gt;|指定した画面モードが選べなかった場合は、例外が送出されプログラムは強制終了します。|
6245 |~|Canvas(int width, int height, CanvasMode mode, const Display disp)|&gt;|!指定の幅と高さでウィンドウを確保します。|
6246 |~|~|int width|水平方向の解像度(画面の横幅)を指定|
6247 |~|~|int height|垂直方向の解像度(画面の縦幅)を指定|
6248 |~|~|Canvasmode mode|[[表示モード|CanvasMode]](フルスクリーンorウィンドウ)を選択|
6249 |~|~|&gt;|指定した画面モードが選べなかった場合は、例外が送出されプログラムは強制終了します。|
6250 |~|Canvas(Rectangle rect, CanvasMode mode , const Display disp)|&gt;|!バーチャルスクリーン上の指定の矩形でウィンドウを確保します。|
6251 |~|~|Rectangle rect|ディスプレイの大きさ、ディスプレイの位置を指定|
6252 |~|~|Canvasmode mode|[[表示モード|CanvasMode]](フルスクリーンorウィンドウ)を選択|
6253 |~|~|const [[Display|Displayの指定値]] disp|[[表示するディスプレイ|Displayの指定値]]を選択(省略可能)|
6254 |~|~|&gt;|指定した画面モードが選べなかった場合は、例外が送出されプログラムは強制終了します。|
6255
6256 * ここではコンストラクタ(宣言)の書式のみ記述していますが、同じ引数セットのset関数がすべて用意されています。
6257 * CRT は機器によってリフレッシュレートが小数点以下の桁でわずかに異なります(60Hzと表示されていても、厳密には60.2Hzなど小数点以下の桁があります)。初期化時には小数点以下の桁は無視して確保しますが、正確なタイミングでflipするには、ディスプレイ装置の設定等で小数点以下の桁をあらかじめ調べて、正確な値を指定することをお勧めします。
6258 * 1.3以降でではウィンドウモードが実装されました。
6259
6260 [[2.1.1 Canvasの宣言]]</pre>
6261 </div>
6262 <div title="Canvas型に関して" modifier="Psychlops_DevelopperG" modified="200908100506" created="200712131716" changecount="9">
6263 <pre>* [[描画内容を画面に反映させたい|Canvas::flip()]]
6264 * [[描画内容を画面に反映させて、一定時間表示したい|Canvas::flip()]]
6265 * [[Canvas型の変数のメンバを取得したい|Canvas::getXXXX()]]
6266
6267 * [[プログラム内の変数の値を表示したい|Canvas::var()]]
6268 * [[簡単なメッセージを表示したい|Canvas::msg()]]
6269
6270 * [[画面領域全てを塗りつぶしたい|Canvas::clear()]]
6271 * [[点を打ちたい|Canvas::pix()]]
6272 * [[四角形を描画したい|Canvas::rect()]]
6273 * [[円・楕円を描画したい|Canvas::oval()]]
6274
6275 * [[描画内容を画面上で複写したい|Canvas::copy()]]
6276 * [[描画内容をオフスクリーンへ複写したい|Canvas::to()]]
6277
6278 * [[FPSチェッカを使用したい|5.2.1 描画の時間精度を確認する-FPSチェッカー]]
6279
6280 * [[Canvas::pix(double, double, double)という命令はコンパイルできないのですか?]]
6281 * [[止まった絵を描画するときCanvas.flip()を単独でwhileループの中に書くと、画面がちらつきます。]]</pre>
6282 </div>
6283 <div title="Clock::at_msec()" modifier="Psychlops_Admin" created="200709142037" changecount="1">
6284 <pre>タイマーのカウントをmsec単位に変換します。
6285 |!Clock::at_msec()| double Clock::at_msec(void)| カウントをmsec単位に変換(double)して返す|
6286
6287 詳細とコード例は[[5.1節|5.1 時間を計測する]]参照</pre>
6288 </div>
6289 <div title="Clock::update()" modifier="Psychlops_Admin" created="200709142036" changecount="1">
6290 <pre>Timerに現在のCPUタイマーの値を入力します。
6291 |!Clock::update()| Clock::update(void)| 現在のCPUタイマーの値を入力する|
6292
6293 詳細とコード例は[[5.1節|5.1 時間を計測する]]参照</pre>
6294 </div>
6295 <div title="Color()の宣言" modifier="Psychlops_Admin" modified="200709132234" created="200709131904" changecount="4">
6296 <pre>描画対象に色をつけたい場合、Color()の宣言を行い色の設定をします。
6297 宣言のみの場合、[[Color::set()|2.2.5 描画する図形の色を指定する-Colorクラス-]]で色の詳細情報を設定しますが
6298 宣言文のみでも色の情報の設定が可能です。
6299
6300 |!Color()の宣言|Color()|[[Psychlops::Color]]型の宣言を行います|
6301 |~|Color(int r, int g, int b, int a)|[[Psychlops::Color]]型の宣言を行いr,g,bと透明度aで構成されるカラー情報を設定します|
6302 |~|~|int r:赤の輝度を指定。範囲は0~1|
6303 |~|~|int g:緑の輝度を指定。範囲は0~1|
6304 |~|~|int b:青の輝度を指定。範囲は0~1|
6305 |~|~|int a:透明度を指定。範囲は0~1。0に近いほど透明度が高い|
6306 |~|Color(double r, double g, double b, double a)|[[Psychlops::Color]]型の宣言を行いr,g,bと透明度aで構成されるカラー情報を設定します|
6307 |~|~|double r:赤の輝度を指定。範囲は0.0~1.0|
6308 |~|~|double g:緑の輝度を指定。範囲は0.0~1.0|
6309 |~|~|double b:青の輝度を指定。範囲は0.0~1.0|
6310 |~|~|double a:透明度を指定。範囲は0.0~1.0。0.0に近いほど透明度が高い|
6311 |~|Color(int r, int g, int b)|[[Psychlops::Color]]型の宣言を行いr,g,bで構成されるカラー情報を設定します|
6312 |~|~|int r:赤の輝度を指定。範囲は0.0~1.0|
6313 |~|~|int g:緑の輝度を指定。範囲は0.0~1.0|
6314 |~|~|int b:青の輝度を指定。範囲は0.0~1.0|
6315 |~|~|透明度は1.0が固定で指定されている|
6316 |~|Color(long r, long g, long b)|[[Psychlops::Color]]型の宣言を行いr,g,bで構成されるカラー情報を設定します|
6317 |~|~|long r:赤の輝度を指定。範囲は0.0~1.0|
6318 |~|~|long g:緑の輝度を指定。範囲は0.0~1.0|
6319 |~|~|long b:青の輝度を指定。範囲は0.0~1.0|
6320 |~|~|透明度は1.0が固定で指定されている|
6321 |~|Color(float r, float g, float b)|[[Psychlops::Color]]型の宣言を行いr,g,bで構成されるカラー情報を設定します|
6322 |~|~|float r:赤の輝度を指定。範囲は0.0~1.0|
6323 |~|~|float g:緑の輝度を指定。範囲は0.0~1.0|
6324 |~|~|float b:青の輝度を指定。範囲は0.0~1.0|
6325 |~|~|透明度は1.0が固定で指定されている|
6326 |~|Color(double r, double g, double b)|[[Psychlops::Color]]型の宣言を行いr,g,bで構成されるカラー情報を設定します|
6327 |~|~|double r:赤の輝度を指定。範囲は0.0~1.0|
6328 |~|~|double g:緑の輝度を指定。範囲は0.0~1.0|
6329 |~|~|double b:青の輝度を指定。範囲は0.0~1.0|
6330 |~|~|透明度は1.0が固定で指定されている|
6331 |~|Color(int gray)|[[Psychlops::Color]]型の宣言を行いグレースケールの輝度を設定します|
6332 |~|~|int gray: 輝度を指定。範囲は0.0~1.0|
6333 |~|~|透明度は1.0が固定で指定されている|
6334 |~|Color(long gray)|[[Psychlops::Color]]型の宣言を行いグレースケールの輝度を設定します|
6335 |~|~|long gray: 輝度を指定。範囲は0.0~1.0|
6336 |~|~|透明度は1.0が固定で指定されている|
6337 |~|Color(float gray)|[[Psychlops::Color]]型の宣言を行いグレースケールの輝度を設定します|
6338 |~|~|透明度は1.0が固定で指定されている|
6339 |~|~|float gray: 輝度を指定。範囲は0.0~1.0|
6340 |~|~|透明度は1.0が固定で指定されている|
6341 |~|Color(double gray)|[[Psychlops::Color]]型の宣言を行いグレースケールの輝度を設定します|
6342 |~|~|double gray: 輝度を指定。範囲は0.0~1.0|
6343 |~|~|透明度は1.0が固定で指定されている|
6344 </pre>
6345 </div>
6346 <div title="Color::XXXX" modifier="YourName" modified="200803100613" created="200708231845" changecount="2">
6347 <pre>あらかじめ用意された基本的な色指定を呼び出すことが可能です。
6348 呼び出せる色は以下の通りです。
6349 |!Color::white|白(1.0, 1.0, 1.0)|
6350 |!Color::black|黒(0.0, 0.0, 0.0)|
6351 |!Color::gray|灰色(0.5, 0.5, 0.5)|
6352 |!Color::red|赤(1.0, 0.0, 0.0)|
6353 |!Color::green|緑( 0.0, 1.0, 0.0)|
6354 |!Color::blue|青(0.0, 0.0, 1.0)|
6355 |!Color::cyan|シアン(0.0, 1.0, 1.0)|
6356 |!Color::magenta|マゼンタ(1.0, 1.0, 0.0)|
6357 |!Color::yellow|黄(1.0, 1.0, 0.0)|</pre>
6358 </div>
6359 <div title="Color::get()" modifier="YourName" modified="200803140530" created="200802130630" changecount="5">
6360 <pre>Color型に格納されている各値を得ます。
6361
6362 |!Color::get()|get(double r, double g, double b, double a)|r,g,bと透明度aで構成されるカラー情報を取得します。|
6363 |~|~|double r:赤の輝度(0.0~1.0)|
6364 |~|~|double g:緑の輝度(0.0~1.0)|
6365 |~|~|double b:青の輝度(0.0~1.0)|
6366 |~|~|double a:透明度(0.0~1.0)。0に近いほど透明度が高い|
6367
6368 |!Color::getX()|getR()|赤チャネルの値を返します|
6369 |~|getG()|緑チャネルの値を返します|
6370 |~|getB()|青チャネルの値を返します|
6371 |~|getA()|透明チャネルの値を返します|
6372
6373 {{{
6374 double r, g, b, a;
6375 Color col
6376
6377 col.set(0.5);
6378 col.get(r, g, b, a);
6379 // r =&gt; 0.5, g =&gt; 0.5, b =&gt; 0.5, a =&gt; 1
6380
6381 col.set(1.0, 0.5, 0.0, 0.5);
6382 r = col.getR();
6383
6384 // r =&gt; 1.0
6385
6386 }}}
6387 </pre>
6388 </div>
6389 <div title="Color::set()" modifier="YourName" modified="200803100609" created="200712131736" changecount="4">
6390 <pre>|!Color::set()|set(double r, double g, double b, double a)|r,g,bと透明度aで構成されるカラー情報を設定します。|
6391 |~|~|double r:赤の輝度を指定。範囲は0.0~1.0|
6392 |~|~|double g:緑の輝度を指定。範囲は0.0~1.0|
6393 |~|~|double b:青の輝度を指定。範囲は0.0~1.0|
6394 |~|~|double a:透明度を指定。範囲は0.0~1.0 (0.0に近いほど透明度が高い)|
6395 |~|set(double r, double g, double b)|r,g,bで構成されるカラー情報を設定します。|
6396 |~|~|double r:赤の輝度を指定。範囲は0.0~1.0|
6397 |~|~|double g:緑の輝度を指定。範囲は0.0~1.0|
6398 |~|~|double b:青の輝度を指定。範囲は0.0~1.0|
6399 |~|set(double gray)|グレースケールの輝度を設定します。|
6400 |~|~|double gray: 輝度を指定。範囲は0.0~1.0|
6401
6402 [[2.2.5 描画する図形の色を指定する-Colorクラス-]]</pre>
6403 </div>
6404 <div title="Color::setGammaTable()" modifier="YourName" modified="200802131125" created="200802130701" changecount="3">
6405 <pre>他の多くの描画ライブラリ同様、CRTの各画素における輝度レベルとPsychlopsで指定する輝度レベルは線形の関係を維持しません。
6406 これを簡易的に補正するために、R,G,Bの各色チャンネルについて変換表(LUTテーブル)を用いた補正を行う方法が広く用いられています。
6407 Psychlopsでは、この変換表を指定することで、[[ガンマ補正|Tips: ガンマ補正]]された色を表示することが可能です。
6408 このための命令として、setGammaTable()命令があります。
6409
6410 |!Canvas::setGammaTable()|setGammaTable(double Cr[], double Cg[], double Cb[], int table_size)|この命令以降実行されるPsychlops::Colorに対して変換表によるを自動的に行います^^*1^^|
6411 |~|~|Cr: R(赤)チャンネルの変換表|
6412 |~|~|Cg: G(緑)チャンネルの変換表|
6413 |~|~|Cb: B(青)チャンネルの変換表|
6414 |~|~|table_size: 表の項目数。8ビット環境下ではできれば256個の項目を用意してください。^^*2^^|
6415 |~|setGammaTable(std::vector&lt;double&gt; Cr, std::vector&lt;double&gt; Cg, std::vector&lt;double&gt; Cb)|この命令以降実行されるPsychlops::Colorに対してGamma補正を自動的に行います^^*1^^|
6416 |~|~|Cr: R(赤)チャンネルの変換表|
6417 |~|~|Cg: G(緑)チャンネルの変換表|
6418 |~|~|Cb: B(青)チャンネルの変換表|
6419 *1 Psychlopsはまずハードウェアによるガンマ補正([[Canvas::setGammaTable()]])を試みます。失敗すると、ソフトウェアによるエミュレーションを試みます。
6420 *2 256個以下でも自動的に線形補完されますが、正確な補完は256個すべて指定することで実現されます。
6421 *3 値は配列またはC++STLの動的配列(std::vector&lt;double&gt;)で指定し、8ビット環境なら256段階の理論値を配列の要素番号とし、それぞれの段階の補償値を配列要素に書き込みます。
6422
6423 コード例は[[ガンマ補正|Tips: ガンマ補正]]参照
6424 </pre>
6425 </div>
6426 <div title="Color::setGammaValue" modifier="YourName" modified="200802130652" created="200802130648" changecount="4">
6427 <pre></pre>
6428 </div>
6429 <div title="Color::setGammaValue()" modifier="YourName" modified="200802131124" created="200802130652" changecount="3">
6430 <pre>他の多くの描画ライブラリ同様、CRTの各画素における輝度レベルとPsychlopsで指定する輝度レベルは線形の関係を維持しません。これを簡易的に補正するために、R,G,Bの各色チャンネルについてGamma関数を用いた補正を行う方法が広く用いられています。
6431 Psychlopsでは、このGamma関数の係数を指定することで、[[ガンマ補正|Tips: ガンマ補正]]された色を表示することが可能です。
6432 このための命令として、setGammaValue()命令があります。
6433
6434 |!Canvas::setGammaValue()|setGammaValue(double Cr, double Cg, double Cb)|この命令以降実行されるPsychlops::Colorに対してGamma補正を自動的に行います^^*1^^|
6435 |~|~|Cr: R(赤)チャンネルのガンマ係数|
6436 |~|~|Cg: G(緑)チャンネルのガンマ係数|
6437 |~|~|Cb: B(青)チャンネルのガンマ係数|
6438 *1 Psychlopsはまずハードウェアによるガンマ補正([[Canvas::setGammaValue()]])を試みます。失敗すると、ソフトウェアによるエミュレーションを試みます。
6439 *2 指定する値は表示装置のガンマ値そのものです。Psychlops側で補償指数を自動決定します。
6440
6441 コード例は[[ガンマ補正|Tips: ガンマ補正]]参照
6442 </pre>
6443 </div>
6444 <div title="Color::strict_match" modifier="YourName" modified="200802201822" created="200802130704" changecount="3">
6445 <pre>実験時に色指定を厳密に行わせるよう強制させることができます。具体的には、ガンマ補正や色指定で値が0以上1以下にならないケースで警告する、ソフトウェアエミュレーション(後述)を禁止して警告を出す、という規制が行われます。以下のコードをpsychlops_main()の冒頭に記述してください。
6446 {{{
6447  Color::strict_match = true;
6448 }}}
6449
6450 なお、警告はC++の例外機能を使って送出されます。警告を受け入れる構文を書かない限り強制終了します。警告を受け入れるtry..catch構文については、C++の参考書をお読みください。
6451 </pre>
6452 </div>
6453 <div title="Color型に関して" modifier="Kazushi Maruya" created="200712131735" changecount="1">
6454 <pre>* [[描画色を設定したい|Color::set()]]
6455 * [[白、黒などのデフォルトの描画色を使用したい|Color::XXXX]]</pre>
6456 </div>
6457 <div title="DefaultTiddlers" modifier="PsychlopsAdmin" created="200709161806" changecount="1">
6458 <pre>MainMenu</pre>
6459 </div>
6460 <div title="Displayの指定値" modifier="Psychlops_DevelopperG" modified="200910080812" created="200910080753" changecount="5">
6461 <pre>クラス定数として以下のものが用意されています。
6462 |Display::secondary|その時点でのプライマリディスプレイを取得します|
6463 |Display::primary|その時点でのセカンダリディスプレイを取得します|
6464
6465 より複雑な環境では以下の命令を使ってリストを取得してください。
6466 |!Display::list()|ディスプレイのリストを動的配列(std::vector&lt;Display&gt;)で取得します。|
6467
6468
6469 </pre>
6470 </div>
6471 <div title="GaborImage" modifier="Kazushi Maruya" modified="200712061920" created="200710070016" changecount="3">
6472 <pre>{{{
6473 class GaborImage {
6474 private:
6475 public:
6476         virtual void draw(Image &amp;area,  double ori, double freq, double phase, double Lmean, double *contrast) {
6477                 int center_x = area.getCenter().getX(), center_y = area.getCenter().getY();
6478                 int width = area.getWidth()/2, height = area.getHeight()/2;
6479                 Color tmpcol;
6480                 double sigma = (width)/3;
6481                 double SINORI, COSORI, _x, gr, ga, ph, R, G, B;
6482                 
6483
6484                 COSORI=cos(-ori*PI/180);
6485                 SINORI=sin(-ori*PI/180);
6486                 
6487                 ph=phase*PI/180;
6488                 
6489                 for(int y=-height; y&lt;height; y++) {
6490                         for(int x=-width; x&lt;width; x++) {
6491                                 _x=     COSORI * x - SINORI * y;
6492                                 ga=exp(-0.5*(pow(x/(sigma),2))-0.5*(pow(y/(sigma), 2)));
6493                                 gr=(1+cos(2*PI*freq*_x+ph))*0.5;
6494                                 R=Lmean+Lmean*contrast[0]*(gr*ga);
6495                                 G=Lmean+Lmean*contrast[1]*(gr*ga);
6496                                 B=Lmean+Lmean*contrast[2]*(gr*ga);
6497                                 tmpcol.set(R, G, B);
6498                                 area.pix(x+width,y+height,tmpcol); 
6499                         }
6500                 }
6501         }
6502
6503         virtual void draw(Image &amp;area,  double ori, double freq, double phase, double Lmean, double contrast) {
6504                 int center_x = area.getCenter().getX(), center_y = area.getCenter().getY();
6505                 int width = area.getWidth()/2, height = area.getHeight()/2;
6506                 Color tmpcol;
6507                 double sigma = (width)/3;
6508                 
6509                 double SINORI, COSORI, _y, gr, ga, ph;
6510                 
6511                 COSORI=cos(ori*PI/180);
6512                 SINORI=sin(ori*PI/180);
6513                 
6514                 ph=phase*PI/180;
6515                 
6516                 for(int y=-height; y&lt;height; y++) {
6517                         for(int x=-width; x&lt;width; x++) {
6518                                 _y=     SINORI * x + COSORI * y;
6519                                 ga=exp(-0.5*(pow(x/(sigma),2))-0.5*(pow(y/(sigma), 2)));
6520                                 gr=(1+cos(2*PI*freq*_y+ph))*0.5;
6521                                 tmpcol.set(Lmean+Lmean*contrast*(gr*ga));
6522                                 area.pix(x+width,y+height,tmpcol);
6523                         }
6524                 }
6525         }
6526 };
6527 }}}</pre>
6528 </div>
6529 <div title="Gabor方位判断の実験プログラム例" modifier="Kazushi Maruya" modified="200712061920" created="200709162000" changecount="6">
6530 <pre>{{{
6531 #include &lt;psychlops.h&gt;
6532 #include &lt;stdio.h&gt;
6533 using namespace Psychlops;
6534
6535 class IndependentVariables
6536 {
6537         int MaxVarNum, MaxStepNum;
6538         double *VariableData;
6539         int *VariableStepNumber;
6540         int conditionNumber;
6541         int repeatNumber;
6542         
6543         Psychlops::Matrix CondMtx;
6544
6545         public:
6546         int trialNumber;
6547         
6548         IndependentVariables(int n, int m, int repeat): MaxVarNum(n), MaxStepNum(m){
6549                 VariableData = (double *)malloc(sizeof(double)*(n+1)*m);
6550                 VariableStepNumber = (int *)malloc(sizeof(double)*m);
6551                 repeatNumber=repeat;
6552                 trialNumber=-1;
6553         }
6554         
6555         virtual ~IndependentVariables(void){
6556                 free(VariableData);
6557                 free(VariableStepNumber);
6558         };
6559         
6560         virtual int set(int vnum, int arraynum, double *array){
6561                 if(vnum&lt;MaxVarNum+1 &amp;&amp; arraynum&lt;MaxStepNum+1){
6562                                   VariableStepNumber[vnum]=arraynum;
6563                                   for(int i=0; i&lt;arraynum; i++){*(VariableData+(vnum+1)*MaxStepNum+i)=*(array+i);}
6564                                   return arraynum;
6565                 }
6566                 else return -1; 
6567         };
6568         
6569         
6570         virtual int randomize(char* dataname=NULL){
6571                 trialNumber=repeatNumber;
6572                 for(int i=0; i&lt;MaxVarNum; i++)trialNumber*=VariableStepNumber[i];
6573                 if(trialNumber&lt;1){return -1;}
6574                 CondMtx.set(trialNumber, MaxVarNum);
6575                 
6576                 int i,j,a;
6577                 double temp=1, temp2;
6578
6579                 //Make Output Matrix
6580                 
6581                 for(i=0; i&lt;MaxVarNum; i++){
6582                         for(j=0; j&lt;i; j++){temp*=(double)VariableStepNumber[j];}
6583                         for(j=0; j&lt;trialNumber; j++){CondMtx(j+1,i+1)=(int)ceil(j/temp)% VariableStepNumber[i];}
6584                         temp=1;
6585                 }
6586         
6587                 //Randomize Output Matrix
6588                 for(i=1; i&lt;trialNumber;i++){
6589                         a = Psychlops::random(i-1)+1;
6590                         for(j=1;j&lt;MaxVarNum+1;j++){
6591                                 temp2=CondMtx(a,j); 
6592                                 CondMtx(a,j)=CondMtx(i,j); 
6593                                 CondMtx(i,j)=temp2;
6594                         }
6595                 }
6596                 
6597                 //swtich of the condition matrix save
6598                 if(dataname){   
6599                         std::ofstream output;
6600                         output.open(dataname);
6601                         output &lt;&lt; CondMtx &lt;&lt; std::endl;
6602                         output.close();
6603                 }
6604                 
6605         }
6606         
6607         virtual double get(int vnum, int trial_now){
6608                 if(vnum&lt;MaxVarNum &amp;&amp; (int)trial_now&lt;trialNumber)
6609                         return *(VariableData+(vnum+1)*MaxStepNum+getCondMtx(vnum, trial_now));
6610                 else return -1;
6611         };
6612         
6613         virtual int getCondMtx(int vnum, unsigned int trial_now){
6614                 if(vnum&lt;MaxVarNum &amp;&amp; (int)trial_now&lt;trialNumber)
6615                         return CondMtx( trial_now+1, vnum+1);
6616                 else return -1;
6617         };
6618
6619         virtual double getIndependent(int vnum, int stepnum){
6620                 if(vnum&lt;MaxVarNum &amp;&amp; stepnum&lt;VariableStepNumber[vnum])
6621                         return *(VariableData+(vnum)*MaxStepNum+stepnum);
6622                 else return -1;
6623         };
6624         
6625         
6626         virtual int getMStep(int vnum){
6627                 if(vnum&lt;MaxVarNum)return VariableStepNumber[vnum];
6628                         else return -1;
6629         };
6630 }; 
6631
6632 class GaborImage {
6633 private:
6634 public:
6635         virtual void draw(Image &amp;area,  double ori, double freq, double phase, double Lmean, double *contrast) {
6636                 int center_x = area.getCenter().getX(), center_y = area.getCenter().getY();
6637                 int width = area.getWidth()/2, height = area.getHeight()/2;
6638                 Color tmpcol;
6639                 double sigma = (width)/3;
6640                 double SINORI, COSORI, _x, gr, ga, ph, R, G, B;
6641                 
6642
6643                 COSORI=cos(-ori*PI/180);
6644                 SINORI=sin(-ori*PI/180);
6645                 
6646                 ph=phase*PI/180;
6647                 
6648                 for(int y=-height; y&lt;height; y++) {
6649                         for(int x=-width; x&lt;width; x++) {
6650                                 _x=     COSORI * x - SINORI * y;
6651                                 ga=exp(-0.5*(pow(x/(sigma),2))-0.5*(pow(y/(sigma), 2)));
6652                                 gr=(1+cos(2*PI*freq*_x+ph))*0.5;
6653                                 R=Lmean+Lmean*contrast[0]*(gr*ga);
6654                                 G=Lmean+Lmean*contrast[1]*(gr*ga);
6655                                 B=Lmean+Lmean*contrast[2]*(gr*ga);
6656                                 tmpcol.set(R, G, B);
6657                                 area.pix(x+width,y+height,tmpcol); 
6658                         }
6659                 }
6660         }
6661
6662         virtual void draw(Image &amp;area,  double ori, double freq, double phase, double Lmean, double contrast) {
6663                 int center_x = area.getCenter().getX(), center_y = area.getCenter().getY();
6664                 int width = area.getWidth()/2, height = area.getHeight()/2;
6665                 Color tmpcol;
6666                 double sigma = (width)/3;
6667                 
6668                 double SINORI, COSORI, _y, gr, ga, ph;
6669                 
6670                 COSORI=cos(ori*PI/180);
6671                 SINORI=sin(ori*PI/180);
6672                 
6673                 ph=phase*PI/180;
6674                 
6675                 for(int y=-height; y&lt;height; y++) {
6676                         for(int x=-width; x&lt;width; x++) {
6677                                 _y=     SINORI * x + COSORI * y;
6678                                 ga=exp(-0.5*(pow(x/(sigma),2))-0.5*(pow(y/(sigma), 2)));
6679                                 gr=(1+cos(2*PI*freq*_y+ph))*0.5;
6680                                 tmpcol.set(Lmean+Lmean*contrast*(gr*ga));
6681                                 area.pix(x+width,y+height,tmpcol);
6682                         }
6683                 }
6684         }
6685 };
6686
6687 void psychlops_main() {
6688
6689         Canvas sampleA(Canvas::fullscreen);
6690         IndependentVariables invar(2,5,10);
6691
6692         double gaborOrientation[5]={0,1,2,3,4}; //Gaborの方位
6693         double duration[3]={100,250,500}; //刺激の提示時間
6694         
6695         invar.set(0, 5, gaborOrientation);
6696         invar.set(1, 3, duration);
6697         invar.randomize(&quot;ConditionMatrix.dat&quot;);
6698
6699         GaborImage gaborIMG;
6700         Psychlops::Image gIMG[10];
6701         double gIMGsize=100, ori;
6702         for(int i=0; i&lt;10; i++){
6703                 if(i&lt;5){ ori=90.0-pow(2.0, i);}
6704                         else{ ori=90.0+pow(2.0,i-4);}
6705                 gIMG[i].set(gIMGsize, gIMGsize);
6706                 gaborIMG.draw(gIMG[i], ori, 1.0/30.0, 0.0, 0.5, 0.2);
6707                 gIMG[i].centering();
6708         }
6709
6710
6711         const int trialNum=2*5*10;
6712         int answer[trialNum], orientationCondition[trialNum], durationCondition[trialNum], ans;
6713         int ori_now, dura_now;
6714         int direction;
6715         char trial_header[64];
6716
6717         for(int trial=0; trial&lt;trialNum; trial++){
6718         //各試行に対する変数の取得
6719                 direction=Psychlops::random(2); //0:right 1:left
6720                 ori_now=invar.get(0,trial)+5*direction;
6721                 dura_now=sampleA.getRefreshRate()*(invar.get(1,trial)/1000);
6722
6723         //被験者のキー入力待ち
6724                 sampleA.clear(0.5);
6725                 for(int i=0; i&lt;64; i++) trial_header[i] = 0;// 文字列の初期化
6726                 sprintf(trial_header, &quot;%s%d%s%d&quot;,&quot;Trial: &quot;,  trial, &quot; /&quot;, trialNum );//試行番号の埋め込み
6727                 sampleA.message(trial_header, sampleA.getHcenter(), sampleA.getVcenter()-100, Color::green);//キー待ちメッセージの描画
6728                 sampleA.flip();
6729                 while(!Input::get(Keyboard::spc));
6730
6731         //刺激描画
6732                 sampleA.clear(0.5);
6733                 gIMG[ori_now].draw();
6734                 sampleA.flip(dura_now);
6735                 sampleA.clear(0.5);
6736                 sampleA.flip();
6737
6738         //反応の取得と格納
6739                 while(1){
6740                         if(Input::get(Keyboard::j)){ans=0;break;} //&quot;j&quot;なら右(0)
6741                         if(Input::get(Keyboard::f)){ans=1;break;}//&quot;f&quot;なら左(1)
6742                         if(Input::get(Keyboard::esc)){exit(0);}
6743                 }
6744                 if(ans==direction)answer[trial]=1;
6745                         else answer[trial]=0;
6746                 orientationCondition[trial]=invar.get(0, trial);
6747                 durationCondition[trial]=invar.get(1, trial);
6748         }
6749
6750         sampleA.clear(0.5);
6751         sampleA.message(&quot;Press space key to exit.&quot;, sampleA.getHcenter(), sampleA.getVcenter()-100, Color::green);
6752         Data::savearray(&quot;ExptData.dat&quot;, &quot;Duration /t Orientation /t Answer&quot;, trialNum, durationCondition, orientationCondition, answer );
6753 }
6754
6755 }}}</pre>
6756 </div>
6757 <div title="GettingStarted" modifier="PsychlopsAdmin" created="200707220303" changecount="1">
6758 <pre>To get started with this blank TiddlyWiki, you'll need to modify the following tiddlers:
6759 * SiteTitle &amp; SiteSubtitle: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)
6760 * MainMenu: The menu (usually on the left)
6761 * DefaultTiddlers: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened
6762 You'll also need to enter your username for signing your edits: &lt;&lt;option txtUserName&gt;&gt;</pre>
6763 </div>
6764 <div title="Image()の宣言" modifier="Psychlops_Admin" modified="200709142109" created="200709142045" changecount="2">
6765 <pre>[[Psychlops::Image]]型は主に画像を取り扱います。
6766 [[Psychlops::Image]]型の命令を使用する前に必ず宣言が必要です。
6767 宣言のみの場合、[[Image::set()]]でImageの詳細情報を設定しますが
6768 宣言文のみでも情報の設定が可能です。
6769
6770 |!Imageの宣言|Image()|オフスクリーン領域を宣言します|
6771 |~|Image([[Psychlops::Rectangle]] rec,PixelComponentsCnt pixcompcntval)|指定された[[Psychlops::Rectangle]] 型と同じ大きさのオフスクリーン領域を宣言します|
6772 |~|~|[[Psychlops::Rectangle]] rect :[[Psychlops::Rectangle]] 型を指定|
6773 |~|~|PixelComponentsCnt pixcompcntval :[[カラーモード]]を指定。省略した場合はRGBが指定される|
6774 |~|Image(long x, long y, PixelComponentsCnt pixcompcntval)|指定されたx×yと同じ大きさのオフスクリーン領域を宣言します|
6775 |~|~|long x :オフスクリーン領域の横幅を指定|
6776 |~|~|long y :オフスクリーン領域の縦幅を指定|
6777 |~|~|PixelComponentsCnt pixcompcntval :[[カラーモード]]を指定。省略した場合はRGBが指定される|
6778
6779 コード例は[[3.1.1節|3.1.1 オフスクリーン領域の宣言]]参照</pre>
6780 </div>
6781 <div title="Image::centering()" modifier="Kazushi Maruya" modified="200712131731" created="200712131730" changecount="2">
6782 <pre>!Image::centering()
6783 [[Psychlops::Image]]型の領域を移動します。
6784 座標を指定する場合、その座標に領域の中心が設定されます。
6785 |!Image::centering()|centering()|[[Psychlops::Image]]型の領域を画面中心に移動します|
6786 |~|centering(double x, double y)|[[Psychlops::Image]]型の領域の中心を指定した座標(x,y)に移動します|
6787 |~|~|double x:移動する領域のx座標を指定|
6788 |~|~|double y:移動する領域のy座標を指定|
6789
6790 [[3.1.3 オフスクリーン上の任意の位置に描画する]]</pre>
6791 </div>
6792 <div title="Image::clear()" modifier="Psychlops_Admin" modified="200709142113" created="200709142111" changecount="2">
6793 <pre>オフスクリーン領域をクリアします。
6794 |!Image::clear()|clear([[Psychlops::Color]] col)|指定したオフスクリーン領域を指定色でクリアします|
6795 |~|~|[[Psychlops::Color]] col:クリアする色を指定|
6796
6797 !!Image::clear()の書き方
6798 Imageをマゼンダでクリアします。
6799 {{{
6800 &lt;例&gt;
6801 #include &lt;psychlops.h&gt;
6802 using namespace Psychlops;
6803
6804 double width=100,height=100;
6805 Psychlops::Rectangle rect1(width,height);
6806 Psychlops::Image Noise1(rect1);
6807
6808 void psychlops_main() {
6809
6810         Canvas sampleA(Canvas::fullscreen);
6811         Noise1.clear(Color::magenta);
6812         Noise1.draw();
6813         sampleA.flip();
6814         while(!Input::get(Keyboard::spc));
6815 }
6816 }}}</pre>
6817 </div>
6818 <div title="Image::draw()" modifier="YourName" modified="200803100605" created="200709142049" changecount="5">
6819 <pre>!Image::draw()
6820 実際にオフスクリーン領域に描画する際の命令です。
6821 この命令を記述しないといくら[[Image::pix()]]命令や[[Image::rect()]]命令を記述しても描画されませんので注意してください。
6822
6823 |!Image::draw()|draw()|指定したオフスクリーンを描画します|
6824 |~|draw(double left, double top)|指定したオフスクリーン領域を(left,top)を中心とした座標に設定します|
6825 |~|~|double left: 描画するオフスクリーン領域の水平座標値|
6826 |~|~|double top: 描画するオフスクリーン領域の垂直座標値|
6827
6828 コード例は[[3.1.2節|3.1.2 オフスクリーン上に点・四角形を描画する]]参照</pre>
6829 </div>
6830 <div title="Image::from()" modifier="Kazushi Maruya" created="200712131721" changecount="1">
6831 <pre>|!Image::from|from( luminance )|Matrixを輝度としてImageに読み込み、グレースケール画像を作成します。|
6832 |~|~|Matrix luminance: |
6833 |~|from( r, g, b )|Matrixを赤、緑、青チャネルとして読み込み、RGB画像を作成します。|
6834 |~|~|Matrix r: |
6835 |~|~|Matrix g: |
6836 |~|~|Matrix b: |
6837 |~|from( r, g, b, a) |Matrixを赤、緑、青、透明度チャネルとして読み込み、RGBA画像を作成します。|
6838 |~|~|Matrix r: |
6839 |~|~|Matrix g: |
6840 |~|~|Matrix b: |
6841 |~|~|Matrix a: |
6842 Imageの中身がすでに確保されていた場合は、破棄されます。
6843
6844 [[5.3.5 行列の中身をオフスクリーンに描画する]]</pre>
6845 </div>
6846 <div title="Image::getXXXX()" modifier="Psychlops_Admin" modified="200709142210" created="200709142100" changecount="7">
6847 <pre>Imageの様々な情報を、Image::getXXXX()命令を使って取得します(XXXXにはそれぞれの情報に対応した名前が入ります)。
6848 |!Image::getXXXX()|getCenter()|オフスクリーン領域の中心座標(x,y)を取得します|
6849 |~|getHcenter()|オフスクリーン領域の横方向の中心座標(x)を取得します|
6850 |~|getVcenter()|オフスクリーン領域の縦方向の中心座標(y)を取得します|
6851 |~|getHeight()|オフスクリーン領域の高さ(ピクセル)を取得します|
6852 |~|getWidth()|オフスクリーン領域の幅(ピクセル)を取得します|
6853 |~|getPix(int x, int y)|オフスクリーン領域上の座標(x,y)の色を取得します|
6854 |~|~|int x :オフスクリーン領域上の座標xを指定|
6855 |~|~|int y :オフスクリーン領域上の座標yを指定|
6856 |~|~|注:オフスクリーン領域の左上を座標(0,0)とする。オフスクリーン領域外の座標は指定しないこと|
6857
6858 !!getXXXX()の書き方
6859 [[Psychlops::Image]]型の情報を取得し画面表示を行います。
6860 画面表示は[[Canvas::var()]]命令と[[Canvas::msg()]]命令を使用します。
6861 {{{
6862 &lt;例&gt;
6863 #include &lt;psychlops.h&gt;
6864 using namespace Psychlops;
6865
6866 Psychlops::Image Noise1(300,300);
6867 Psychlops::Point point1;
6868 Psychlops::Color col1,col2;
6869 Psychlops::Rectangle rect1(50,50);
6870 double d1,d2,d3,d4,x,y;
6871
6872 void psychlops_main() {
6873
6874         Canvas sampleA(Canvas::fullscreen);
6875         Noise1.centering();
6876         col1.set(0.8,0.4,0.1);
6877         Noise1.clear(col1);
6878         
6879         point1=Noise1.getCenter();
6880         d1=Noise1.getHcenter();
6881         d2=Noise1.getVcenter();
6882         d3=Noise1.getHeight();
6883         d4=Noise1.getWidth();
6884         col2=Noise1.getPix(100,100);
6885         
6886         //get命令の内容を画面表示する
6887         x=point1.getX();
6888         y=point1.getY();
6889         sampleA.msg(&quot;getCenter_x:&quot;,50,200);
6890         sampleA.var(x,200,200);//getCenter:X
6891         sampleA.msg(&quot;getCenter_y:&quot;,50,250);
6892         sampleA.var(y,200,250);//getCenter:Y            
6893         sampleA.msg(&quot;getHcenter:&quot;,50,300);
6894         sampleA.var(d1,200,300);//getHcenter
6895         sampleA.msg(&quot;getVcenter:&quot;,50,350);
6896         sampleA.var(d2,200,350);//getVcenter
6897         sampleA.msg(&quot;gettHeight:&quot;,50,400);
6898         sampleA.var(d3,200,400);//getHeight
6899         sampleA.msg(&quot;getWidth:&quot;,50,450);
6900         sampleA.var(d4,200,450);//getWidth
6901         //取得した色で円を描画する
6902         sampleA.oval(rect1,col2);
6903
6904         Noise1.draw();
6905         sampleA.flip();
6906         while(!Input::get(Keyboard::spc));
6907 }
6908 }}}</pre>
6909 </div>
6910 <div title="Image::line()" modifier="YourName" modified="200803100604" created="200709142118" changecount="4">
6911 <pre>オフスクリーン領域に線を描画します。
6912 指定する座標は画面上左上を座標(0,0)にするのではなく
6913 オフスクリーン領域の左上を座標(0,0)とします。
6914 |!Image::line()|line(double x1, double y1, double x2, double y2, [[Psychlops::Color]] col)|座標(x1,y1)から座標(x2,y2)に指定色で線を描画します|
6915 |~|~|double x1 :描画する線の開始水平座標値|
6916 |~|~|double y1 :描画する線の開始垂直座標値|
6917 |~|~|double x2 :描画する線の終端水平座標値|
6918 |~|~|double y2 :描画する線の終端垂直座標値|
6919 |~|~|[[Psychlops::Color]] col:描画する線の描画色|
6920
6921 !!Image::line()の書き方
6922 座標(100,100)から座標(200,200)に赤線を描画します。
6923 指定Image外の座標に設定すると描画されないのでオフスクリーンの位置に注意すること。
6924 {{{
6925 &lt;例&gt;
6926 #include &lt;psychlops.h&gt;
6927 using namespace Psychlops;
6928
6929 Psychlops::Image Noise1(500,500);
6930
6931 void psychlops_main() {
6932
6933         Canvas sampleA(Canvas::fullscreen);
6934         Noise1.line(100,100,200,200,Color::red);
6935         Noise1.draw();
6936         sampleA.flip();
6937         while(!Input::get(Keyboard::spc));
6938 }
6939 }}}</pre>
6940 </div>
6941 <div title="Image::load()" modifier="Kazushi Maruya" modified="200712131733" created="200712131731" changecount="2">
6942 <pre>!Image::load()
6943 既存の任意の画像ファイルを読み込みます。
6944
6945 |!Image::load()|load(filename)|指定した画像ファイルを読み込みます|&gt;|&gt;|
6946 |~|~|filename: 読み込む画像ファイルの名前を指定|&gt;|&gt;|
6947 |~|~|読込み先を指定しない場合のパス|Mac:Macintosh HD¥ユーザ¥ユーザ名(任意)¥書類¥Psychlops¥|&gt;|
6948 |~|~|~|Win:実行ファイルと同じパス|&gt;|
6949 |~|~|読込みをする場合のパス指定|%APP%|Mac:実行バンドル(.app)と同じパス|
6950 |~|~|~|~|Win:実行ファイルと同じパス|
6951 |~|~|~|%RESOURCES%|Mac:実行バンドルの内部のResourcesフォルダ|
6952 |~|~|~|~|Win:実行ファイルのパスの直下のrscという名前のフォルダ|
6953 |~|~|~|%~USER_HOME%|Mac:&quot;~/&quot;と同じ(/Users/(user)/)|
6954 |~|~|~|~|Win:ユーザープロファイルフォルダ(C:\Documents and Settings\(user)\)|
6955 |~|~|~|%~USER_SETTING%|Mac:&quot;~/Library/Psychlops/&quot;|
6956 |~|~|~|~|Win:&quot;%~AppData%Psychlops&quot;|
6957 |~|~|~|%~USER_DOCUMENTS%|Mac:&quot;~/Documents/Psychlops&quot;|
6958 |~|~|~|~|Win:ユーザープロファイルフォルダ\My Documents\Psychlops|
6959 |~|~|~|%~USER_DOCUMENTS_ROOT%|Mac:&quot;~/Documents&quot;|
6960 |~|~|~|~|Win:ユーザープロファイルフォルダMy Documents\|
6961 **VISTA環境の場合、パス指定が正常に動作しません。VISTA環境の方はお手数ですが保存先を指定しないでお使いください。
6962
6963 [[3.3.2 画像ファイルを読み込み表示する]]</pre>
6964 </div>
6965 <div title="Image::oval()" modifier="Psychlops_Admin" modified="200709142129" created="200709142125" changecount="2">
6966 <pre>オフスクリーン領域に円を描画します。
6967 円の描画位置はオフスクリーン領域の座標(0,0)に描画されます。
6968 |!Image::oval()|([[Psychlops::Rectangle]] rect, [[Psychlops::Color]] col)|[[Psychlops::Rectangle]] rectで指定された直径の円を指定色で塗りつぶし描画します|
6969 |~|~|[[Psychlops::Rectangle]] rect:描画する円の直径|
6970 |~|~|[[Psychlops::Color]] col:描画する円の色を指定|
6971
6972 !!Image::oval()の書き方
6973 座標(300,300)からはじまるオフスクリーン領域に直径(100,100)の赤円を描画します。
6974 {{{
6975 &lt;例&gt;
6976 #include &lt;psychlops.h&gt;
6977 using namespace Psychlops;
6978
6979 Psychlops::Image Noise1(300,300);
6980 Psychlops::Rectangle rect1(100,100);
6981
6982 void psychlops_main() {
6983
6984         Canvas sampleA(Canvas::fullscreen);
6985         Noise1.centering();
6986         Noise1.oval(rect1,Color::red);
6987         Noise1.draw();
6988         sampleA.flip();
6989         while(!Input::get(Keyboard::spc));
6990 }
6991 }}}</pre>
6992 </div>
6993 <div title="Image::pix()" modifier="YourName" modified="200803100603" created="200709142049" changecount="4">
6994 <pre>!Image::pix()
6995 オフスクリーン領域に点を描画するための命令です。
6996 [[Canvas::pix()|2.2.1 画面上に点を描画する-Pointクラス1-]]命令同様に点を描画するために「座標」と「色の指定」を行います。
6997 色の指定についての詳細は[[2.2.5 描画する図形の色を指定する-Colorクラス-]]を参照してください。
6998
6999 |!Image::pix()|pix(double x, double y, [[Psychlops::Color]] col)|座標(x,y)に[[Psychlops::Color]] col色の点を描画します。|
7000 |~|~|double x: 描画する点の水平座標値|
7001 |~|~|double y: 描画する点の垂直座標値|
7002 |~|~|[[Psychlops::Color]] col:描画する点の描画色|
7003 |~|pix([[Psychlops::Point]] po, [[Psychlops::Color]] col)|Point座標(x,y)に[[Psychlops::Color]] col色の点を描画します|
7004 |~|~|[[Psychlops::Point]] po :描画する点の座標|
7005 |~|~|[[Psychlops::Color]] col:描画する点の色|
7006
7007 コード例は[[3.1.2節|3.1.2 オフスクリーン上に点・四角形を描画する]]参照</pre>
7008 </div>
7009 <div title="Image::rect()" modifier="YourName" modified="200803100619" created="200709142050" changecount="5">
7010 <pre>オフスクリーン領域に四角形を描画するための命令です。
7011 実際の描画処理は[[Image::draw()]]命令で行われます。
7012 色の指定についての詳細は[[2.2.5 描画する図形の色を指定する-Colorクラス-]]を参照してください。
7013
7014 |!Image::rect()|rect([[Psychlops::Rectangle]] rect, [[Psychlops::Color]] col)|[[Psychlops::Rectangle]]型の四角形を[[Psychlops::Color]] col色で描画します|
7015 |~|~|[[Psychlops::Rectangle]] rect : 矩形の描画範囲|
7016 |~|~|[[Psychlops::Color]] col:描画する四角形の描画色|
7017
7018 コード例は[[3.1.2節|3.1.2 オフスクリーン上に点・四角形を描画する]]参照</pre>
7019 </div>
7020 <div title="Image::save()" modifier="Kazushi Maruya" created="200712131732" changecount="1">
7021 <pre>|!Image::save()|save(filename)|指定したImageを保存します|&gt;|&gt;|
7022 |~|~|filename: 保存するImageの画像ファイル名称(拡張子まで)を指定|&gt;|&gt;|
7023 |~|~|保存先を指定しない場合のパス|Mac:Macintosh HD¥ユーザ¥ユーザ名(任意)¥書類¥Psychlops¥|&gt;|
7024 |~|~|~|Win:実行ファイルと同じパス|&gt;|
7025 |~|~|保存をする場合のパス指定|%APP%|Mac:実行バンドル(.app)と同じパス|
7026 |~|~|~|~|Win:実行ファイルと同じパス|
7027 |~|~|~|%RESOURCES%|Mac:実行バンドルの内部のResourcesフォルダ|
7028 |~|~|~|~|Win:実行ファイルのパスの直下のrscという名前のフォルダ|
7029 |~|~|~|%~USER_HOME%|Mac:&quot;~/&quot;と同じ(/Users/(user)/)|
7030 |~|~|~|~|Win:ユーザープロファイルフォルダ(C:\Documents and Settings\(user)\)|
7031 |~|~|~|%~USER_SETTING%|Mac:&quot;~/Library/Psychlops/&quot;|
7032 |~|~|~|~|Win:&quot;%~AppData%Psychlops&quot;|
7033 |~|~|~|%~USER_DOCUMENTS%|Mac:&quot;~/Documents/Psychlops&quot;|
7034 |~|~|~|~|Win:ユーザープロファイルフォルダ\My Documents\Psychlops|
7035 |~|~|~|%~USER_DOCUMENTS_ROOT%|Mac:&quot;~/Documents&quot;|
7036 |~|~|~|~|Win:ユーザープロファイルフォルダMy Documents\|
7037 **VISTA環境の場合、パス指定が正常に動作しません。VISTA環境の方はお手数ですが保存先を指定しないでお使いください。
7038
7039 [[3.3.1 画像ファイルを保存する]]</pre>
7040 </div>
7041 <div title="Image::set()" modifier="YourName" modified="200803100559" created="200709142046" changecount="4">
7042 <pre>宣言時に何の指定もしなかった場合、Image::set()命令を使用します。
7043 |!Image::set()|set([[Psychlops::Rectangle]] rect, PixelComponentsCnt pixcompcntval)|指定された[[Psychlops::Rectangle]] 型と同じ大きさのオフスクリーン領域を設定します|
7044 |~|~|[[Psychlops::Rectangle]] rect :オフスクリーンの大きさを与える[[Psychlops::Rectangle]] 型を指定(座標は引き継がれない)|
7045 |~|~|PixelComponentsCnt pixcompcntval :[[カラーモード]]を指定。省略した場合はRGBが指定される|
7046 |~|set(long x, long y, PixelComponentsCnt pixcompcntval)|指定されたx×yと同じ大きさのオフスクリーン領域を設定します|
7047 |~|~|long x :オフスクリーン領域の横幅|
7048 |~|~|long y :オフスクリーン領域の縦幅|
7049 |~|~|PixelComponentsCnt pixcompcntval :[[カラーモード]]を指定。省略した場合はRGBが指定される|
7050
7051 コード例は[[3.1.1節|3.1.1 オフスクリーン領域の宣言]]参照</pre>
7052 </div>
7053 <div title="Image::shift()" modifier="YourName" modified="200803100601" created="200709142053" changecount="2">
7054 <pre>[[Psychlops::Image]]型の領域を移動します。
7055 元の座標(x1,y1)を基点に入力値(x,y)だけ移動します。
7056 |!Image::shift()|shift(double h, double v)|座標(X,Y)を(h,v)だけ移動します|
7057 |~|~|double x:右水平方向の移動量|
7058 |~|~|double y:下垂直方向の移動量|
7059
7060 コード例は[[3.1.3節|3.1.3 オフスクリーン上の任意の位置に描画する]]参照</pre>
7061 </div>
7062 <div title="Image::to()" modifier="Psychlops_DevelopperG" modified="200910070830" created="200712131722" changecount="31">
7063 <pre>|!Image::to|&gt;|&gt;|!グレースケール画像の輝度値をMatrixに書き込みます|
7064 |~|to( Matrix  luminance )|
7065 |~|~|Matrix luminance|輝度値の行列|
7066 |~|to( Point po, double width, double height, Matrix  luminance)|
7067 |~|~|Point po |コピー元領域の左上座標&lt;br&gt;(省略時は画像左上を起点とします)|
7068 |~|~|double width |コピー元領域の幅(pixel)|
7069 |~|~|double height |コピー元領域の高さ(pixel)|
7070 |~|~|Matrix luminance |輝度値の行列|
7071 |~|to( Point po, Rectangle rect, Matrix lumiannce)  |
7072 |~|~|Point po |コピー元領域の左上座標&lt;br&gt;(省略時は画像左上を起点とします)|
7073 |~|~|Rectangle rect |コピー元領域のサイズを持つ矩形|
7074 |~|~|Matrix luminance|輝度値の行列|
7075 |~|to( Interval h_intvl, Interval v_intvl, Matrix luminance) |
7076 |~|~|Interval h_intvl |コピー元領域の横座標の範囲(pixel)|
7077 |~|~|Interval v_intvl |コピー元領域の縦座標の範囲(pixel)|
7078 |~|~|Matrix luminance |輝度値の行列|
7079
7080 |!Image::to|&gt;|&gt;|!RGB画像の赤、緑、青チャネル値をMatrixに書き込みます|
7081 |~|to( r, g, b ) |
7082 |~|~|Matrix r |赤チャンネルの輝度値の行列|
7083 |~|~|Matrix g |緑チャンネルの輝度値の行列|
7084 |~|~|Matrix b |青チャンネルの輝度値の行列|
7085 |~|to( Point po, double width, double height, Matrix r, Matrix g, Matrix b) |
7086 |~|~|Point po |コピー元領域の左上座標&lt;br&gt;(省略時は画像左上を起点とします)|
7087 |~|~|double width |コピー元領域の幅(pixel)|
7088 |~|~|double height |コピー元領域の高さ(pixel)|
7089 |~|~|Matrix r |赤チャネルの輝度値の行列|
7090 |~|~|Matrix g |緑チャネルの輝度値の行列|
7091 |~|~|Matrix b |青チャネルの輝度値の行列|
7092 |~|to( Point po, Rectangle rect, Matrix r, Matrix g, Matrix b)  |
7093 |~|~|Point po |コピー元領域の左上座標&lt;br&gt;(省略時は画像左上を起点とします)|
7094 |~|~|Rectangle rect |コピー元領域のサイズを持つ矩形|
7095 |~|~|Matrix r |赤チャンネルの輝度値の行列|
7096 |~|~|Matrix g |緑チャンネルの輝度値の行列|
7097 |~|~|Matrix b |青チャンネルの輝度値の行列|
7098 |~|to( Interval h_intvl, Interval v_intvl, Matrix r, Matrix g, Matrix b) |
7099 |~|~|Interval h_intvl |コピー元領域の横座標の範囲(pixel)|
7100 |~|~|Interval v_intvl |コピー元領域の縦座標の範囲(pixel)|
7101 |~|~|Matrix r |赤チャネルの輝度値の行列|
7102 |~|~|Matrix g |緑チャネルの輝度値の行列|
7103 |~|~|Matrix b |青チャネルの輝度値の行列|
7104
7105
7106 |!Image::to|&gt;|&gt;|!RGBA画像の各チャネルの輝度値・透明度チャネルの値をMatrixに書き込みます|
7107 |~|to( Matrix r, Matrix g, Matrix b, Matrix a) |
7108 |~|~|Matrix r |赤チャネルの輝度値の行列|
7109 |~|~|Matrix g |緑チャネルの輝度値の行列|
7110 |~|~|Matrix b |青チャネルの輝度値の行列|
7111 |~|~|Matrix a |透明度チャネルの行列|
7112 |~|to( Point po, double width, double height, Matrix r, Matrix g, Matrix b, Matrix a ) |
7113 |~|~|Point po |コピー元領域の左上座標&lt;br&gt;(省略時は画像左上を起点とします)|
7114 |~|~|double width |コピー元領域の幅(pixel)|
7115 |~|~|double height |コピー元領域の高さ(pixel)|
7116 |~|~|Matrix r |赤チャネルの輝度値の行列|
7117 |~|~|Matrix g |緑チャネルの輝度値の行列|
7118 |~|~|Matrix b |青チャネルの輝度値の行列|
7119 |~|~|Matrix a |透明度チャネルの行列|
7120 |~|to( Point po, Rectangle rect, Matrix r, Matrix g, Matrix b, Matrix a)  |
7121 |~|~|Point po |コピー元領域の左上座標&lt;br&gt;(省略時は画像左上を起点とします)|
7122 |~|~|Rectangle rect |コピー元領域のサイズを持つ矩形|
7123 |~|~|Matrix r |赤チャンネルの輝度値の行列|
7124 |~|~|Matrix g |緑チャンネルの輝度値の行列|
7125 |~|~|Matrix b |青チャンネルの輝度値の行列|
7126 |~|~|Matrix a |透明度チャネルの行列|
7127 |~|to( Interval h_intvl, Interval v_intvl, Matrix r, Matrix g, Matrix b, Matrix a ) |
7128 |~|~|Interval h_intvl |コピー元領域の横座標の範囲(pixel)|
7129 |~|~|Interval v_intvl |コピー元領域の縦座標の範囲(pixel)|
7130 |~|~|Matrix r |赤チャネルの輝度値の行列|
7131 |~|~|Matrix g |緑チャネルの輝度値の行列|
7132 |~|~|Matrix b |青チャネルの輝度値の行列|
7133 |~|~|Matrix a |透明度チャネルの行列|
7134
7135 サンプル
7136 {{{
7137 Img.to(1&lt;=rng&lt;=100, 1&lt;=rng&lt;=50, luminance);
7138 }}}
7139
7140 書き込み先のMatrixはいったん破棄されて作り直されます。
7141 大きさ等はImageのサイズと等しくなるように自動的に調整されます。
7142
7143 [[5.3.5 行列の中身をオフスクリーンに描画する]]</pre>
7144 </div>
7145 <div title="Image型に関して" modifier="Kazushi Maruya" modified="200712131731" created="200712131730" changecount="2">
7146 <pre>*[[オフスクリーンのサイズを指定したい|Image::set()]]
7147 *[[オフスクリーンを特定の描画色で塗りつぶしたい|Image::clear()]]
7148 *[[オフスクリーン上に点を打ちたい|Image::pix()]]
7149 *[[オフスクリーン上に線を引きたい|Image::line()]]
7150 *[[オフスクリーン上に四角形を描画したい|Image::rect()]]
7151 *[[オフスクリーン上に円を描画したい|Image::oval()]]
7152 *[[オフスクリーンの内容を画面に描画したい|Image::draw()]]
7153 *[[オフスクリーンの描画位置を画面中央や特定の座標にセンタリングしたい|Image::centering()]]
7154 *[[オフスクリーンの描画位置を移動させたい|Image::shift()]]
7155 *[[オフスクリーンのメンバ(情報)を取得したい|Image::getXXXX()]]
7156 *[[オフスクリーンに画像を読み込みたい|Image::load()]]
7157 *[[オフスクリーンの描画内容をファイルとして保存したい|Image::save()]]
7158 *[[Matrix型を用いて計算した描画内容をオフスクリーンへ読み込みたい|Image::to()]]
7159 *[[オフスクリーンの描画内容をMatrix型へ変換したい|Image::to()]]</pre>
7160 </div>
7161 <div title="IndependentVariables" modifier="Psychlops_Admin" modified="200710070017" created="200709162126" changecount="3">
7162 <pre>{{{
7163 class IndependentVariables
7164 {
7165         int MaxVarNum, MaxStepNum;
7166         double *VariableData;
7167         int *VariableStepNumber;
7168         int conditionNumber;
7169         int repeatNumber;
7170         
7171         Psychlops::Matrix CondMtx;
7172
7173         public:
7174         int trialNumber;
7175         
7176         IndependentVariables(int n, int m, int repeat): MaxVarNum(n), MaxStepNum(m){
7177                 VariableData = (double *)malloc(sizeof(double)*(n+1)*m);
7178                 VariableStepNumber = (int *)malloc(sizeof(double)*m);
7179                 repeatNumber=repeat;
7180                 trialNumber=-1;
7181         }
7182         
7183         virtual ~IndependentVariables(void){
7184                 free(VariableData);
7185                 free(VariableStepNumber);
7186         };
7187         
7188         virtual int set(int vnum, int arraynum, double *array){
7189                 if(vnum&lt;MaxVarNum+1 &amp;&amp; arraynum&lt;MaxStepNum+1){
7190                                   VariableStepNumber[vnum]=arraynum;
7191                                   for(int i=0; i&lt;arraynum; i++){*(VariableData+(vnum+1)*MaxStepNum+i)=*(array+i);}
7192                                   return arraynum;
7193                 }
7194                 else return -1; 
7195         };
7196         
7197         
7198         virtual int randomize(char* dataname=NULL){
7199                 trialNumber=repeatNumber;
7200                 for(int i=0; i&lt;MaxVarNum; i++)trialNumber*=VariableStepNumber[i];
7201                 if(trialNumber&lt;1){return -1;}
7202                 CondMtx.set(trialNumber, MaxVarNum);
7203                 
7204                 int i,j,a;
7205                 double temp=1, temp2;
7206
7207                 //Make Output Matrix
7208                 
7209                 for(i=0; i&lt;MaxVarNum; i++){
7210                         for(j=0; j&lt;i; j++){temp*=(double)VariableStepNumber[j];}
7211                         for(j=0; j&lt;trialNumber; j++){CondMtx(j+1,i+1)=(int)ceil(j/temp)% VariableStepNumber[i];}
7212                         temp=1;
7213                 }
7214         
7215                 //Randomize Output Matrix
7216                 for(i=1; i&lt;trialNumber;i++){
7217                         a = Psychlops::random(i-1)+1;
7218                         for(j=1;j&lt;MaxVarNum+1;j++){
7219                                 temp2=CondMtx(a,j); 
7220                                 CondMtx(a,j)=CondMtx(i,j); 
7221                                 CondMtx(i,j)=temp2;
7222                         }
7223                 }
7224                 
7225                 //swtich of the condition matrix save
7226                 if(dataname){   
7227                         std::ofstream output;
7228                         output.open(dataname);
7229                         output &lt;&lt; CondMtx &lt;&lt; std::endl;
7230                         output.close();
7231                 }
7232                 
7233         }
7234         
7235         virtual double get(int vnum, int trial_now){
7236                 if(vnum&lt;MaxVarNum &amp;&amp; (int)trial_now&lt;trialNumber)
7237                         return *(VariableData+(vnum+1)*MaxStepNum+getCondMtx(vnum, trial_now));
7238                 else return -1;
7239         };
7240         
7241         virtual int getCondMtx(int vnum, unsigned int trial_now){
7242                 if(vnum&lt;MaxVarNum &amp;&amp; (int)trial_now&lt;trialNumber)
7243                         return CondMtx( trial_now+1, vnum+1);
7244                 else return -1;
7245         };
7246
7247         virtual double getIndependent(int vnum, int stepnum){
7248                 if(vnum&lt;MaxVarNum &amp;&amp; stepnum&lt;VariableStepNumber[vnum])
7249                         return *(VariableData+(vnum)*MaxStepNum+stepnum);
7250                 else return -1;
7251         };
7252         
7253         
7254         virtual int getMStep(int vnum){
7255                 if(vnum&lt;MaxVarNum)return VariableStepNumber[vnum];
7256                         else return -1;
7257         };
7258 }; 
7259  
7260 }}}</pre>
7261 </div>
7262 <div title="Input::get()" modifier="Kazushi Maruya" created="200712131738" changecount="1">
7263 <pre>|!Input::get()|Input::get([[Keyboard::KeyCode|キーコード表]] code, [[Keyboard::KeyState]] state)|キーボードの反応を取得します|
7264 |~|~|code 反応を取得するキーの名前 [[キーコード表]] 参照|
7265 |~|~|state 反応状態のオプション (省略時はpushed) [[Keyboard::KeyState]]参照|
7266 |~|Input::get([[Mouse::ButtonCode|キーコード表]] code, [[Mouse::ButtonState]] state)|マウスの反応を取得します|
7267 |~|~|code 反応を取得するマウスボタンの名前 [[キーコード表]] 参照|
7268 |~|~|state 反応状態のオプション (省略時はpushed) [[Mouse::ButtonState]]参照|
7269
7270 [[4.1 外部装置(キーボード/マウス)の操作内容を取得する]]</pre>
7271 </div>
7272 <div title="Input::refresh()" modifier="PsychlopsAdmin" modified="200709171526" created="200709171520" changecount="2">
7273 <pre>キーボードやマウスといった外部入力装置の入出力ルーチンの初期化処理
7274 |!Input::refresh()|refresh(void)|キーボード・マウスの入出力ルーチンを初期化します|
7275
7276 コード例は[[Tips: Psychlopsにおけるキーボードマウスの入出力検出ルーチン]]参照</pre>
7277 </div>
7278 <div title="Keyboard::KeyState" modifier="Psychlops_Admin" modified="200709142243" created="200708240330" changecount="3">
7279 <pre>| !~ButtonState | pressed |ボタンが押し続けられている |
7280 |~| pushed | 判定が行われるまでにボタンが押された |
7281 |~| released | 判定が行われるまでにボタンが離された |</pre>
7282 </div>
7283 <div title="MainMenu" modifier="Psychlops_DevelopperG" modified="200909300421" created="200707220305" changecount="64">
7284 <pre>[[このマニュアルの見方について(ここをクリックしてください)]]
7285
7286 [[0. Psyclopsとは]]
7287
7288 [[第I部 Psychlopsの基本]]
7289 [[1. インストールを行う]]
7290 [[2. 基本的な描画を行う]]
7291 [[3. 複雑な描画を行う1(オフスクリーンへの描画)]]
7292 [[4. 複雑な描画を行う2(複数オブジェクトのグループ化と回転・拡大縮小)]]
7293 [[5. 入出力を行う]]
7294 [[6. 時間制御と各種ツールを使用する]]
7295
7296 [[第II部 Psychlopsの応用]]
7297 [[1. 既成クラスを用いた簡単な実験プログラムを作成する]]
7298 [[2. デモを作成する]]
7299 [[3. 透明度を使って刺激描画を高速化する]]
7300 [[4. アナログ入出力を行う]]
7301
7302 付録
7303 [[関数一覧_FunctionList]]
7304 [[関数逆引きとQ&amp;A]]
7305 [[Tips]]
7306
7307 [[MainMenu]]</pre>
7308 </div>
7309 <div title="Math::mod" modifier="Kazushi Maruya" modified="200712180137" created="200712100223" changecount="5">
7310 <pre>剰余の計算を行います。
7311
7312 負の数を割ったときの剰余は多くの言語で未定義とされていますが、C++(%演算子やfmod関数)では負の数になります。また、C++の剰余演算子は整数型のみ定義されています。
7313 Math::modは剰余の計算を浮動小数点型に拡張するとともに、負の数を割ったときの挙動を周期関数的にしてあります。視覚実験では周期関数の位相を値とすることが多くありますが、この関数を使うと位相が負でも正でも一定の範囲の剰余が得られます。ifやswitchで値ごとに条件分岐している場合には特に役立ちます。
7314
7315 たとえば、-450を360で割った剰余をとる場合、
7316 {{{
7317  -90 == -450 % 360
7318  270 == Math::mod(-450, 360)
7319 }}}
7320 %演算子では剰余が負の数になってしまいますが、Math::modを使うと正の数で得ることができます。両関数をグラフで描画すると下図のようになります。
7321
7322 [img[image/Math.mod.png]]
7323
7324
7325 |!Math::mod()|mod(double lhs, double rhs)|剰余を計算します。|
7326 |~|~|double lhs: 左辺項(割られる数)を指定します。|
7327 |~|~|double rhs: 右辺項(割る数)を指定します。|
7328
7329 Math::mod()の使用例
7330 以下の例では%演算子とMath::mod()関数の返り値をプロットしています。
7331 上と同様の図が画面に表示されます
7332
7333 {{{
7334 #include &lt;psychlops.h&gt;
7335 using namespace Psychlops;
7336
7337 void psychlops_main() {
7338         Canvas display(Canvas::fullscreen);
7339
7340         double upper_v = display.getVcenter()-100, lower_v = display.getVcenter()+100;
7341
7342         while(!Input::get(Keyboard::esc)) {
7343                 //Draw Axis
7344                 display.line(display.getHcenter()-300, upper_v, display.getHcenter()+300, upper_v, Color::white);
7345                 display.line(display.getHcenter()-300, lower_v, display.getHcenter()+300, lower_v, Color::white);
7346                 display.line(display.getHcenter(), upper_v+60, display.getHcenter(), upper_v-60, Color::white);
7347                 display.line(display.getHcenter(), lower_v+60, display.getHcenter(), lower_v-60, Color::white);
7348                 display.msg(&quot;C++ %&quot;, display.getHcenter()-100, upper_v -20, Color::white);
7349                 display.msg(&quot;Math::mod&quot;, display.getHcenter()-100, lower_v -20, Color::white);
7350
7351                 //Plot Mod Results
7352                 for(int i=-300; i&lt;300; i++) {
7353                         display.pix(i+display.getHcenter(), upper_v - i%50, Color::red);
7354                         display.pix(i+display.getHcenter(), lower_v - Math::mod(i, 50), Color::green);
7355                 }
7356                 display.flip();
7357         }
7358 }
7359 }}}</pre>
7360 </div>
7361 <div title="Math::shuffle" modifier="YourName" modified="200712100209" created="200712100206" changecount="2">
7362 <pre>配列の中身をシャッフルします。
7363
7364 |!Psychlops::shuffle()|shuffle(X array, int num)|配列の中身をシャッフルします。|
7365 |~|~|X array: 配列変数を指定します。型=代入が可能であればなんでもかまいません。|
7366 |~|~|int num: 配列の要素数を指定します。|
7367 |~|shuffle(X array, int num, X init)|配列を初期化してから中身をシャッフルします。|
7368 |~|~|X array: 配列変数を指定します。型はint, double等で有効です。|
7369 |~|~|int num: 配列の要素数を指定します。|
7370 |~|~|X init: 配列の初期値です。0を指定すると、配列が0~num-1の数で埋められてから、シャッフルを行います。|
7371
7372 !!shuffleの使い方
7373
7374 {{{
7375 #include &lt;psychlops.h&gt;
7376 using namespace Psychlops;
7377
7378 void psychlops_main() {
7379         Canvas display(Canvas::fullscreen);
7380
7381         const int NUM = 10;
7382         int array[NUM];
7383         Math::shuffle(array, NUM, 0);
7384
7385         while(!Input::get(Keyboard::esc)) {
7386                 for(int i=0; i&lt;NUM; i++) {
7387                         display.var(array[i], 100, 20*i + 100);
7388                 }
7389                 display.flip();
7390         }
7391 }
7392 }}}</pre>
7393 </div>
7394 <div title="Matrix::catRow()" modifier="YourName" created="200712100125" changecount="1">
7395 <pre>この行列の下に他の行列を継ぎ足します。
7396
7397 |!Matrix::catRow|catRow(other)|この行列の次の行から別の行列を継ぎ足します。|
7398 |~|~|Matrix other: 付け足す行列を指定します。付け足してもこれ自身は消されません。|
7399
7400 {{{
7401 #include &lt;fstream&gt;
7402 #include &lt;psychlops.h&gt;
7403 using namespace Psychlops;
7404
7405 Psychlops::Matrix LuminanceMap1, LuminanceMap2;
7406 long Width=5, Height=1;
7407
7408 void psychlops_main() {
7409         std::ofstream result(&quot;dump.txt&quot;);
7410
7411
7412         LuminanceMap1.set(Height, Width);
7413         LuminanceMap1 = 1;
7414         LuminanceMap2.set(Height, Width);
7415         LuminanceMap2 = 2;
7416
7417         LuminanceMap1.catRows(LuminanceMap2);
7418         result &lt;&lt; LuminanceMap1;
7419 }
7420 }}}
7421
7422
7423 </pre>
7424 </div>
7425 <div title="Matrix::getXXXX()" modifier="YourName" created="200712100131" changecount="2">
7426 <pre>[[Psychlops::Matrix]]型の情報を、Matrix::getXXXX()命令を使って取得します(XXXXにはそれぞれの情報に対応した
7427
7428 名前が入ります)。
7429 |!Matrix::getXXXX()|getRows()|行列の行数を取得します|
7430 |~|getCols()|行列の列数を取得します|
7431
7432 !!Matrix::getXXXX()の書き方
7433 [[Psychlops::Matrix]]型の情報を取得しファイルに出力します。
7434
7435 {{{
7436 #include &lt;fstream&gt;
7437 #include &lt;psychlops.h&gt;
7438 using namespace Psychlops;
7439
7440 Psychlops::Matrix LuminanceMap1;
7441 long Width=5, Height=3;
7442
7443 void psychlops_main() {
7444         std::ofstream result(&quot;dump.txt&quot;);
7445
7446
7447         LuminanceMap1.set(Height, Width);
7448
7449         result &lt;&lt; LuminanceMap1.getRows() &lt;&lt; &quot; &quot; &lt;&lt; LuminanceMap1.getCols();
7450 }
7451 }}}
7452 </pre>
7453 </div>
7454 <div title="Matrix::min() / max()" modifier="YourName" created="200712100135" changecount="1">
7455 <pre>行列の全要素中の最大値・最小値を得ます
7456
7457 |!Matrix::min|min()|全要素中の最小値を得ます。|
7458 |!Matrix::max|max()|全要素中の最大値を得ます。|
7459 {{{
7460 // 行列の要素に乱数を代入し、その中の最大値と最小値を抽出します。
7461 #include &lt;fstream&gt;
7462 #include &lt;psychlops.h&gt;
7463 using namespace Psychlops;
7464
7465 Psychlops::Matrix LuminanceMap1;
7466 long Width=10, Height=10;
7467
7468 void psychlops_main() {
7469         std::ofstream result(&quot;dump.txt&quot;);
7470
7471
7472         LuminanceMap1.set(Height, Width);
7473         for(int row; row&lt;Width; row++) {
7474                 for(int col; col&lt;Width; col++) {
7475                         LuminanceMap1(row, col) = Psychlops::random();
7476                 }
7477         }
7478         result &lt;&lt; LuminanceMap1;
7479         result &lt;&lt; LuminanceMap1.max() &lt;&lt; &quot; &quot; &lt;&lt; LuminanceMap1.min();
7480
7481 }
7482 }}}
7483
7484
7485
7486 要素どうしで大きいものをまとめた行列、小さいものをまとめた行列を返すmax/minもあります。
7487
7488 |!Matrix::min|min(Matrix a, Matrix b)|aとbの各要素を比較し、小さいほうを要素とする行列を返します。両Matrixの大きさは同じでなければなりません。|
7489 |!Matrix::max|max(Matrix a, Matrix b)|aとbの各要素を比較し、大きいほうを要素とする行列を返します。両Matrixの大きさは同じでなければなりません。|
7490
7491 {{{
7492 A = 
7493 1 2 3
7494 0 0 2
7495
7496 B =
7497 5 3 0
7498 1 1 1
7499
7500 C = max(A, B);
7501
7502 C =&gt;
7503 5 3 3
7504 1 1 2
7505
7506 }}}
7507 </pre>
7508 </div>
7509 <div title="Matrix::reshape()" modifier="YourName" created="200712100127" changecount="1">
7510 <pre>行列をいったん1行の配列に並べ、新たに行を仕切りなおします。
7511
7512 |!Matrix::reshape|reshape(row, col)|要素を変えずに、行と列の数のみを変更します。要素数(行*列)は変更前と変更後で同じでなければなりません。|
7513 |~|~|int row 新しい行数を指定します。|
7514 |~|~|int col 新しい列数を指定します。|
7515
7516 !!reshape操作の内容
7517 {{{
7518 1 2 3
7519 4 5 6
7520 をreshape(3, 2)すると
7521 1 2
7522 3 4
7523 5 6
7524 }}}
7525
7526 !!reshapeの書き方
7527 {{{
7528 #include &lt;fstream&gt;
7529 #include &lt;psychlops.h&gt;
7530 using namespace Psychlops;
7531
7532 Psychlops::Matrix LuminanceMap1;
7533 long Width=16, Height=1;
7534
7535 void psychlops_main() {
7536         std::ofstream result(&quot;dump.txt&quot;);
7537
7538
7539         LuminanceMap1.set(Height, Width);
7540         for(int col; col&lt;Width; col++) {
7541                 LuminanceMap1(1, col) = col;
7542         }
7543         result &lt;&lt; LuminanceMap1.reshape(4, 4);
7544 }
7545 }}}
7546
7547 </pre>
7548 </div>
7549 <div title="Matrix::rot90()" modifier="YourName" created="200712100103" changecount="1">
7550 <pre>行列を90度単位で回転させます。
7551
7552 |!Matrix::rot90|rot90(count)|行列を90度単位で反時計回りに回転します。|
7553 |~|~|int count 回転角度を90度単位で指定します。-3,1,5のとき90度、-2,2,6のとき180度、-1,3,7のとき270度回転します。|
7554
7555 !!回転操作の内容
7556 {{{
7557 1 2 3
7558 4 5 6
7559 をrot90(1)すると
7560 3 6
7561 2 5
7562 1 4
7563 }}}
7564
7565 !!rot90()の書き方
7566 {{{
7567 #include &lt;fstream&gt;
7568 #include &lt;psychlops.h&gt;
7569 using namespace Psychlops;
7570
7571 Psychlops::Matrix LuminanceMap1;
7572 long Width=16, Height=1;
7573
7574 void psychlops_main() {
7575         std::ofstream result(&quot;dump.txt&quot;);
7576
7577
7578         LuminanceMap1.set(Height, Width);
7579         for(int col; col&lt;Width; col++) {
7580                 LuminanceMap1(1, col) = col;
7581         }
7582         result &lt;&lt; LuminanceMap1.rot90(1);
7583 }
7584 }}}
7585 </pre>
7586 </div>
7587 <div title="Matrix::set()" modifier="YourName" modified="200803100614" created="200712131744" changecount="3">
7588 <pre>!Matrix::set()
7589 行列の宣言時に何の指定もしなかった場合、Matrix::set()命令を使用します。
7590 |!Matrix::set()|Matrix(rows, cols)|指定されたrows行cols列の行列を設定します|
7591 |~|~|long rows :行列の行数|
7592 |~|~|long cols :行列の列数|
7593
7594 [[5.3.1 行列の宣言]]</pre>
7595 </div>
7596 <div title="Matrix::slide()" modifier="YourName" created="200712100100" changecount="1">
7597 <pre>各要素を行・列各方向にずらす命令です。
7598
7599 |!Matrix::slide|slide(row, col)|要素を行方向、列方向にずらします。あふれた分は、反対側に移動します。|
7600 |~|~|int row 行方向にずらす量を指定します。|
7601 |~|~|int col 列方向にずらす量を指定します。|
7602
7603 !!slide操作の内容
7604 {{{
7605 1 2 3
7606 4 5 6
7607 をslide(1,0)すると
7608 4 5 6
7609 1 2 3
7610
7611 1 2 3
7612 4 5 6
7613 をslide(0,1)すると
7614 3 1 2
7615 6 4 5
7616
7617 1 2 3
7618 4 5 6
7619 をslide(1,1)すると
7620 6 4 5
7621 3 1 2
7622 }}}
7623
7624 !!slideの書き方
7625 {{{
7626 #include &lt;fstream&gt;
7627 #include &lt;psychlops.h&gt;
7628 using namespace Psychlops;
7629
7630 Psychlops::Matrix LuminanceMap1;
7631 long Width=10, Height=10;
7632
7633 void psychlops_main() {
7634         std::ofstream result(&quot;dump.txt&quot;);
7635
7636
7637         LuminanceMap1.set(Height, Width);
7638         for(int row; row&lt;Width; row++) {
7639                 for(int col; col&lt;Width; col++) {
7640                         LuminanceMap1(row, col) = row*Height + col;
7641                 }
7642         }
7643         result &lt;&lt; LuminanceMap1.slide(1,0);
7644 }
7645 }}}
7646 </pre>
7647 </div>
7648 <div title="Matrix::transpose()" modifier="YourName" modified="200803140532" created="200712100101" changecount="2">
7649 <pre>行列を転置します。
7650
7651 |!Matrix::transpose|Matrix transpose(void)|行列を転置(行と列の読み替え)します。自分自身を書き換えるので注意してください。|
7652
7653 !!転置操作の内容
7654 {{{
7655 1 2 3
7656 4 5 6
7657 をtransposeすると
7658 1 4
7659 2 5
7660 3 6
7661 }}}
7662
7663 !!transposeの書き方
7664 {{{
7665 #include &lt;fstream&gt;
7666 #include &lt;psychlops.h&gt;
7667 using namespace Psychlops;
7668
7669 Psychlops::Matrix LuminanceMap1;
7670 long Width=10, Height=10;
7671
7672 void psychlops_main() {
7673         std::ofstream result(&quot;dump.txt&quot;);
7674
7675
7676         LuminanceMap1.set(Height, Width);
7677         for(int row; row&lt;Width; row++) {
7678                 for(int col; col&lt;Width; col++) {
7679                         LuminanceMap1(row, col) = row*Height + col;
7680                 }
7681         }
7682         result &lt;&lt; LuminanceMap1.transpose();
7683 }
7684 }}}
7685 </pre>
7686 </div>
7687 <div title="Matrix::要素アクセス・部分行列" modifier="Psychlops_DevelopperG" modified="200908190257" created="200712131744" changecount="2">
7688 <pre>[[6.3.2 行列の要素の操作]]</pre>
7689 </div>
7690 <div title="Mouse::ButtonState" modifier="Psychlops_Admin" created="200708240330" changecount="1">
7691 <pre>| ~KeyState | pressed | キーが押し続けられている |
7692 |~| pushed | 判定が行われるまでにキーが押された |
7693 |~| released | 判定が行われるまでにキーが離された |</pre>
7694 </div>
7695 <div title="Mouse::hide()" modifier="PsychlopsAdmin" created="200709171527" changecount="1">
7696 <pre>マウスカーソルの表示切り替え。
7697 [[Mouse::show()]]命令と対になっている命令です。
7698 |!Mouse::hide()|hide()|マウスカーソルを非表示にする|
7699
7700 コード例は[[例6: マウスのデモ]]参照</pre>
7701 </div>
7702 <div title="Mouse::show()" modifier="PsychlopsAdmin" created="200709171527" changecount="2">
7703 <pre>マウスカーソルの表示切り替え。
7704 [[Mouse::hide()]]命令と対になっている命令です。
7705 |!Mouse::show()|show()|マウスカーソルを表示する|
7706
7707 コード例は[[例6: マウスのデモ]]参照</pre>
7708 </div>
7709 <div title="PixelComponentsCnt" modifier="Psychlops_Admin" modified="200709041934" created="200709032052" changecount="5">
7710 <pre>PixelComponentsCntの指定方法
7711 |GRAY|グレースケールの輝度値|
7712 |RGB|色の三原色に基づいた赤・緑・青で構成された色|
7713 |RGBA|RGBに透明度が追加された色|
7714 ※省略時にはRGBが指定される
7715 詳しくは[[2.2.5 描画する図形の色を指定する-Colorクラス-]]を参照</pre>
7716 </div>
7717 <div title="PluginManager" modifier="PsychlopsAdmin" created="200707220245" changecount="1">
7718 <pre>&lt;&lt;plugins&gt;&gt;</pre>
7719 </div>
7720 <div title="Point()の宣言" modifier="Psychlops_Admin" modified="200709140117" created="200709132353" changecount="3">
7721 <pre>点を設定します。
7722 [[Psychlops::Point]]型の命令を使用する前に必ず宣言が必要です。
7723 宣言のみの場合、[[Point::set()]]で点の詳細情報を設定しますが
7724 宣言文のみでも情報の設定が可能です。
7725
7726 |!Pointの宣言|Point()|[[Psychlops::Point]]型の宣言を行います|
7727 |~|Point(double x, double y)|[[Psychlops::Point]]型の宣言を行い座標(x,y)に値を指定します|
7728 |~|~|double x:座標xを指定|
7729 |~|~|double y:座標yを指定|
7730
7731 !!Point()の宣言の書き方
7732 座標(100 × 100)に赤の点を描画します。
7733 {{{
7734 &lt;例&gt;
7735 #include &lt;psychlops.h&gt;
7736 using namespace Psychlops;
7737
7738 Psychlops::Point point1;
7739 void psychlops_main() {
7740
7741         Canvas sampleA(Canvas::fullscreen);
7742         point1.set(100,100);
7743         sampleA.pix(point1,Color::red);
7744         sampleA.flip();
7745         while(!Input::get(Keyboard::spc));
7746 }
7747 }}}</pre>
7748 </div>
7749 <div title="Point::centering()" modifier="YourName" modified="200803100601" created="200709132303" changecount="4">
7750 <pre>点を画面中心に描画します。
7751
7752 |!Point::centering()|centering()|座標(x,y)の点を画面の中心に移動します|
7753 |~|centering(Psychlops::Rectangle rect)|rectの中心に点を移動します|
7754
7755
7756 コード例は[[2.2.1節|2.2.1 画面上に点を描画する-Pointクラス1-]]参照</pre>
7757 </div>
7758 <div title="Point::get()" modifier="Psychlops_Admin" modified="200709142232" created="200709140110" changecount="4">
7759 <pre>対象の点の座標を取得します。
7760
7761 |!Point::get()|getX()|対象の点のX座標の値を取得します|
7762 |~|getY()|対象の点のY座標の値を取得します|
7763
7764 !!Point::get()の書き方
7765 {{{
7766 &lt;例&gt;
7767 #include &lt;psychlops.h&gt;
7768 using namespace Psychlops;
7769
7770 Psychlops::Point point1(100,100);
7771 double d1,d2;
7772
7773 void psychlops_main() {
7774
7775         Canvas sampleA(Canvas::fullscreen);
7776         point1.centering();
7777         sampleA.pix(point1,Color::red);
7778         
7779         d1=point1.getX();
7780         d2=point1.getY();
7781         
7782         //get命令の内容を画面表示する
7783         sampleA.msg(&quot;getX:&quot;,50,200);
7784         sampleA.var(d1,200,200);//getX
7785         sampleA.msg(&quot;getY:&quot;,50,250);
7786         sampleA.var(d2,200,250);//getY
7787         
7788         sampleA.flip();
7789         while(!Input::get(Keyboard::spc));
7790 }
7791 }}}</pre>
7792 </div>
7793 <div title="Point::set()" modifier="YourName" modified="200803100556" created="200709132300" changecount="6">
7794 <pre>座標(x,y),ないし座標X,Yを指定し点を描画します。
7795 Point::set()を使用し座標を取得することでソースの簡略化がなされ
7796 また、修正がしやすくなります。
7797
7798 |!Point::set()|set(double x, double y)|座標(x,y)の値を設定します|
7799 |~|~|double x:水平座標|
7800 |~|~|double y:垂直座標|
7801 |~|setX(double val)|水平座標の値を設定します|
7802 |~|~|double val :水平座標値|
7803 |~|setY(double val)|垂直座標の値を設定します|
7804 |~|~|double val :垂直座標値|
7805
7806 コード例は[[2.2.1節|2.2.1 画面上に点を描画する-Pointクラス1-]]参照</pre>
7807 </div>
7808 <div title="Point::shift()" modifier="YourName" modified="200803100557" created="200709132303" changecount="3">
7809 <pre>点の移動命令です。
7810 元の座標(x,y)を基点に入力値(h,v)だけ移動します。
7811
7812 |!Point::shift()|shift(double h, double v)|座標(X,Y)を(h,v)だけ移動します|
7813 |~|~|double h:右水平方向の移動量|
7814 |~|~|double v:下垂直方向の移動量|
7815
7816 コード例は[[2.2.1節|2.2.1 画面上に点を描画する-Pointクラス1-]]参照</pre>
7817 </div>
7818 <div title="Point型に関して" modifier="Kazushi Maruya" created="200712131720" changecount="1">
7819 <pre>* [[Point型の変数の座標を設定したい|Point::set()]]
7820 * [[Point型の変数を画面中央やRectangle型の変数の中央に移動したい|Point::centering()]]
7821 * [[Point型の変数の座標を移動させたい|Point::shift()]]
7822 * [[Point型の現在の座標を取得したい|Point::get()]]</pre>
7823 </div>
7824 <div title="Psychlops::Canvas" modifier="Psychlops_DevelopperG" modified="200910080816" created="200707220311" changecount="40">
7825 <pre>*メソッド
7826 **[[Canvasの宣言]]
7827 **[[Canvas::clear()]]
7828 **[[Canvas::flip()]]
7829 **[[Canvas::flipAfter()|Canvas::flip()]]
7830 **[[Canvas::flipAndWait()|Canvas::flip()]]
7831 **[[Canvas::getXXXX()]]
7832 **[[Canvas::var()]]
7833 **[[Canvas::msg()]]
7834 **[[Canvas::pix()]]
7835 **[[Canvas::line()]]
7836 **[[Canvas::rect()]]
7837 **[[Canvas::oval()]]
7838 **[[Canvas::to()]]
7839 **[[Canvas::copy()]]
7840 **[[Canvas::setGammaValue()]]
7841 **[[Canvas::setGammaTable()]]
7842 **[[Canvas::setClearColor()]]
7843 **[[Canvas::progressbar()]]
7844 **[[Canvas::watchFPS()]]
7845 **[[Canvas::showFPS()]]
7846 **[[Canvas::lastFailedFrames()]]
7847 *メンバ
7848 **TargetSurface</pre>
7849 </div>
7850 <div title="Psychlops::Clock" modifier="Psychlops_Admin" modified="200709142035" created="200709142034" changecount="2">
7851 <pre>*メソッド
7852 **[[Clock::update()]]
7853 **[[Clock::at_msec()]]</pre>
7854 </div>
7855 <div title="Psychlops::Color" modifier="YourName" modified="200802131229" created="200708212259" changecount="17">
7856 <pre>*メソッド
7857 ** [[Color()の宣言]]
7858 ** [[Color::set()]]
7859 ** [[Color::get()]]
7860 ** [[Color::setGammaValue()]]
7861 ** [[Color::strict_match]]
7862 ** [[Color::XXXX]]</pre>
7863 </div>
7864 <div title="Psychlops::Image" modifier="YourName" modified="200803050504" created="200709032052" changecount="15">
7865 <pre>*メソッド
7866 **[[Image()の宣言]]
7867 **[[Image::set()]]
7868 **[[Image::clear()]]
7869 **[[Image::pix()]]
7870 **[[Image::line()]]
7871 **[[Image::rect()]]
7872 **[[Image::oval()]]
7873 **[[Image::draw()]]
7874 **[[Image::quicken()|3.1.4 発展的な内容]]
7875 **[[Image::centering()]]
7876 **[[Image::shift()]]
7877 **[[Image::getXXXX()]]
7878 **[[Image::load()]]
7879 **[[Image::save()]]
7880 **[[Image::from()]]
7881 **[[Image::to()]]</pre>
7882 </div>
7883 <div title="Psychlops::Input" modifier="Kazushi Maruya" modified="200712131738" created="200709142237" changecount="6">
7884 <pre>*メソッド
7885 **[[Input::get()]]
7886 **[[Input::refresh()]]</pre>
7887 </div>
7888 <div title="Psychlops::Keyboad" modifier="PsychlopsAdmin" modified="200709170639" created="200709142246" changecount="2">
7889 <pre>*メソッド
7890 **[[Keyboad::get()]]
7891 </pre>
7892 </div>
7893 <div title="Psychlops::Math" modifier="Kazushi Maruya" modified="200712131628" created="200712100137" changecount="4">
7894 <pre>*メソッド
7895 ** [[Psychlops::random]]
7896 ** [[Math::shuffle]]
7897 ** [[Math::mod]]</pre>
7898 </div>
7899 <div title="Psychlops::Matrix" modifier="Psychlops_DevelopperG" modified="200910070546" created="200712100024" changecount="9">
7900 <pre>*メソッド
7901 **[[Matrix()の宣言|6.3.1 行列の宣言]]
7902 **[[Matrix::set()]]
7903 **[[Matrix::要素アクセス・部分行列|6.3.2 行列の要素の操作]]
7904 **[[Matrix::四則演算|6.3.4 行列の加減乗除]]
7905 **[[Matrix::slide()]]
7906 **[[Matrix::transpose()]]
7907 **[[Matrix::rot90()]]
7908 **[[Matrix::catRow()]]
7909 **[[Matrix::reshape()]]
7910 **[[Matrix::getXXXX()]]
7911 **[[Matrix::min() / max()]]
7912 **[[Matrix::mesh()|6.3.6 メッシュグリッドの作成]]</pre>
7913 </div>
7914 <div title="Psychlops::Mouse" modifier="PsychlopsAdmin" modified="200709170736" created="200709142247" changecount="3">
7915 <pre>*メソッド
7916 **[[Mouse::hide()]]
7917 **[[Mouse::show()]]</pre>
7918 </div>
7919 <div title="Psychlops::Point" modifier="Psychlops_Admin" modified="200709140121" created="200709122119" changecount="8">
7920 <pre>*メソッド
7921 **[[Point()の宣言]]
7922 **[[Point::set()]]
7923 **[[Point::centering()]]
7924 **[[Point::shift()]]
7925 **[[Point::get()]]</pre>
7926 </div>
7927 <div title="Psychlops::Rectangle" modifier="Psychlops_DevelopperG" modified="200910080843" created="200708230127" changecount="15">
7928 <pre>*メソッド
7929 **[[Rectangleの宣言]]
7930 **[[Rectangle::set()]]
7931 **[[Rectangle::centering()]]
7932 **[[Rectangle::shift()]]
7933 **[[Rectangle::draw()]]
7934 **[[Rectangle::resize()]]
7935 **[[Rectangle::setColor()]]
7936 **[[Rectangle::getXXXX()]]
7937 **[[Rectangle::include()]]
7938 **[[Rectangle::dup()]]</pre>
7939 </div>
7940 <div title="Psychlops::random" modifier="YourName" modified="200802232256" created="200712100149" changecount="11">
7941 <pre>乱数を返します。標準の擬似乱数生成アルゴリズムは[[dSFMT|http://www.math.sci.hiroshima-u.ac.jp/%7Em-mat/MT/SFMT/index-jp.html]]を利用しています。
7942 |!Psychlops::random()|random()|[0.0, 1.0)の範囲の一様乱数を生成します。|
7943 |~|random(double max)|[0.0, max)の範囲の一様乱数を生成します。|
7944 |~|random(int levels)|[0, levels)の範囲の離散的一様乱数を生成します。|
7945 |~|random(double min, double max)|[min, max)の値をランダムに返します。|
7946 |~|~|double min: ランダムで返す値の最小値を指定|
7947 |~|~|double max: ランダムで返す値の最大値を指定|
7948
7949 引数が実数(double)か整数(int)かで動作が異なりますので、実数で返したい場合は最大値が整数でも必ず1.0などのように小数点をつけて指定してください。整数変数を指定する場合は(double)でキャストしてください。
7950 {{{
7951 random(5)   ← [0,1,2,3,4] のいずれかの数がint型で返る
7952 random(5.0) ← [0, 5)の間の浮動小数点値が返る
7953
7954 int i = 5;
7955 random(i)         ← [0,1,2,3,4] のいずれかの数がint型で返る
7956 random((double)i) ← [0, 5)の間の浮動小数点値が返る
7957 }}}
7958
7959
7960 以下の例では100 ms毎にrandom命令を使用して作成したランダム配列を元に10000個のドットを画面に表示します。
7961
7962 {{{
7963 #include &lt;psychlops.h&gt;
7964 using namespace Psychlops;
7965
7966 void psychlops_main() {
7967         Canvas display(Canvas::fullscreen);
7968         double wid = 200, hei = 200;
7969         int refresh=display.getRefreshRate()/10;
7970         const int DOTNUM = 1000;
7971         double x[DOTNUM], y[DOTNUM];
7972         
7973
7974         while(!Input::get(Keyboard::esc)) {
7975                 display.clear(0.0);
7976                 for(int i=0; i&lt;DOTNUM; i++) {
7977                         x[i] = random(wid)+display.getHcenter()-wid*0.5;
7978                         y[i] = random(hei)+display.getVcenter()-hei*0.5;
7979                 }
7980                 display.pix(DOTNUM, x, y, Color::white);
7981                 display.flip(refresh);
7982         }
7983 }
7984 }}}</pre>
7985 </div>
7986 <div title="Psychlopsについて開発者に質問するには?" modifier="Kazushi Maruya" modified="200712180142" created="200712131755" changecount="2">
7987 <pre>直接の質問には(開発者と面識がある場合を除いて)お答えしておりません。
7988 Psychlops Authors Mailingリストに入っていただくのが便利です。
7989 このメーリングリストはGoogleグループサービスを利用しております。
7990 このメーリングリストに参加なされたい場合は、[[ここ|http://groups.google.com/group/psychlops/post?sendowner=1]]から申請してください。</pre>
7991 </div>
7992 <div title="Psychlopsを既存・自作のC/C++ライブラリと併用することができますか?" modifier="Kazushi Maruya" created="200712180146" changecount="1">
7993 <pre>可能です。
7994 OpenGL命令に関しては、Psychlopsライブラリのインクルード時に既にOpenGLライブラリのインクルードが行われていますので、そのまま書いていただいてコンパイル・実行可能となっています。
7995 自作ライブラリについては、関数名の衝突が無ければそのまま書いていただいて結構です。関数名が衝突する場合は、スコープ演算子などを用いて衝突を回避すれば利用が可能です。</pre>
7996 </div>
7997 <div title="Psychlopsプログラムのデフォルトテンプレート" modifier="YourName" created="200802191428" changecount="1">
7998 <pre>{{{
7999 void RectLuminance() {
8000         double rect_size = 100;
8001         double rect_lum  = 0.5;
8002         double bg_lum    = 0.2;
8003
8004         Psychlops::Rectangle rect(rect_size,rect_size);
8005         rect.centering();
8006
8007         Range rng;
8008         Independent &lt;&lt; rect_size | &quot;Rect Size&quot;      |   1&lt; rng&lt; 500 | 10.0 | 2.0 ;
8009         Independent &lt;&lt; rect_lum  | &quot;Rect Luminance&quot; | 0.0&lt;=rng&lt;=1.0 |  0.1 | 0.05;
8010         Independent &lt;&lt; bg_lum    | &quot;BG Luminance&quot;   | 0.0&lt;=rng&lt;=1.0 |  0.1 | 0.05;
8011
8012         while(!Input::get(Keyboard::esc)) {
8013                 Display::clear(Color(bg_lum));
8014                 rect.resize(rect_size,rect_size);
8015                 rect.display(rect_lum);
8016                 Display::flip();
8017         }
8018 }
8019
8020 void psychlops_main() {
8021         Canvas display(Canvas::fullscreen);
8022
8023         Procedure p;
8024         p.setDesign(Procedure::DEMO);
8025         p.setProcedure(RectLuminance);
8026         p.run();
8027 }
8028 }}}</pre>
8029 </div>
8030 <div title="Rectangle::centering()" modifier="Psychlops_DevelopperG" modified="200910080843" created="200709131913" changecount="8">
8031 <pre>[[Psychlops::Rectangle]]型の図形を移動します。
8032 [img[image/rect_centering().png]]
8033 座標を指定する場合、その座標に図形の中心が設定されます。
8034 |!Rectangle::centering()|centering()|[[Psychlops::Rectangle]] を画面中心に移動します|
8035 |~|centering(double x, double y)|[[Psychlops::Rectangle]] を指定した座標(x,y)に移動します|
8036 |~|~|double x:移動する図形のx座標|
8037 |~|~|double y:移動する図形のy座標|
8038 |~|centering([[Psychlops::Point]] po)|[[Psychlops::Rectangle]]を指定したpoint座標(x,y)に移動します|
8039 |~|~|[[Psychlops::Point]] po:移動する図形の(x,y)座標|
8040 |~|centering(Rectangle rect)|[[Psychlops::Rectangle]] を指定した[[Psychlops::Rectangle]] rectの中心座標(x,y)に移動します(2つのRectangleを中央ぞろえします)|
8041 |~|~|[[Psychlops::Rectangle]] rect :センタリングの中心座標を与える[[Psychlops::Rectangle]]|
8042
8043 コード例は[[2.2.6節|2.2.6 画面上に縞を描画する-Rectangleクラス2-]]参照</pre>
8044 </div>
8045 <div title="Rectangle::draw()" modifier="YourName" modified="200803100553" created="200709131911" changecount="7">
8046 <pre>[[Psychlops::Rectangle]]型の図形の描画命令です。
8047 |!Rectangle::draw()|draw()|対象の[[Psychlops::Rectangle]]型を描画します|
8048 |~|draw([[Psychlops::Color]] col)|対象の[[Psychlops::Rectangle]]型を[[Psychlops::Color]] colで塗りつぶし描画します|
8049 |~|~|[[Psychlops::Color]] col:描画する四角形の描画色|
8050
8051 コード例は[[2.2.6節|2.2.6 画面上に縞を描画する-Rectangleクラス2-]]参照</pre>
8052 </div>
8053 <div title="Rectangle::dup()" modifier="YourName" modified="200803100555" created="200802281432" changecount="2">
8054 <pre>[[Psychlops::Rectangle]]型を複製した一時オブジェクトを作成します。
8055
8056 |!Rectangle::dup()|矩形領域の複製を行います|
8057
8058 {{{
8059  Psychlops::Rectangle rect1(10,10), rect2(10,10);
8060  rect1.centering();             // rect1の中心位置は変わっている
8061  rect2.dup().centering()     // rect2の中心位置は変わっていないが、複製がセンタリングされている
8062 }}}</pre>
8063 </div>
8064 <div title="Rectangle::getXXXX()" modifier="Psychlops_Admin" modified="200709132236" created="200709131915" changecount="12">
8065 <pre>[[Psychlops::Rectangle]]型の情報を、Rectangle::getXXXX()命令を使って取得します(XXXXにはそれぞれの情報に対応した名前が入ります)。
8066 |!Rectangle::getXXXX()|getCenter()|四角形の中心座標(x,y)を取得します|
8067 |~|getHcenter()|横方向の中心座標(x)を取得します|
8068 |~|getVcenter()|縦方向の中心座標(y)を取得します|
8069 |~|getHeight()|四角形の高さ(ピクセル)を取得します|
8070 |~|getWidth()|四角形の幅(ピクセル)を取得します|
8071 |~|getTop()|四角形上辺のX座標(x)を取得します|
8072 |~|getLeft()|四角形左辺のX座標(x)を取得します|
8073 |~|getBottom()|四角形下辺のY座標(y)を取得します|
8074 |~|getRight()|四角形右辺のY座標(y)を取得します|
8075 [img[image/RectangleGet.png]]
8076
8077 !!Rectangle::getXXXX()の書き方
8078 [[Psychlops::Rectangle]]型の情報を取得し画面表示を行います。
8079 画面表示は[[Canvas::var()]]命令と[[Canvas::msg()]]命令を使用します。
8080 {{{
8081 &lt;例&gt;
8082 #include &lt;psychlops.h&gt;
8083 using namespace Psychlops;
8084
8085 double d1,d2,d3,d4,d5,d6,d7,d8,x,y;
8086 Psychlops::Point point1;
8087 Psychlops::Rectangle rect1;
8088
8089 void psychlops_main() {
8090
8091         Canvas sampleA(Canvas::fullscreen);
8092         rect1.set(100,100);
8093         rect1.centering();
8094         sampleA.rect(rect1,Color::cyan);
8095
8096         point1=rect1.getCenter();
8097         d1=rect1.getHcenter();
8098         d2=rect1.getVcenter();
8099         d3=rect1.getHeight();
8100         d4=rect1.getWidth();
8101         d5=rect1.getTop();
8102         d6=rect1.getLeft();
8103         d7=rect1.getBottom();
8104         d8=rect1.getRight();
8105         
8106         //get命令の内容を画面表示する
8107         x=point1.getX();
8108         y=point1.getY();
8109         sampleA.msg(&quot;getCenter_x:&quot;,50,200);
8110         sampleA.var(x,200,200);//getCenter:X
8111         sampleA.msg(&quot;getCenter_y:&quot;,50,250);
8112         sampleA.var(y,200,250);//getCenter:Y            
8113         sampleA.msg(&quot;getHcenter:&quot;,50,300);
8114         sampleA.var(d1,200,300);//getHcenter
8115         sampleA.msg(&quot;getVcenter:&quot;,50,350);
8116         sampleA.var(d2,200,350);//getVcenter
8117         sampleA.msg(&quot;gettHeight:&quot;,50,400);
8118         sampleA.var(d3,200,400);//getHeight
8119         sampleA.msg(&quot;getWidth:&quot;,50,450);
8120         sampleA.var(d4,200,450);//getWidth
8121
8122         sampleA.msg(&quot;getTop:&quot;,250,200);
8123         sampleA.var(d5,400,200);//getTop
8124         sampleA.msg(&quot;getLeft:&quot;,250,250);
8125         sampleA.var(d6,400,250);//getLeft
8126         sampleA.msg(&quot;getBottom:&quot;,250,300);
8127         sampleA.var(d7,400,300);//getBottom
8128         sampleA.msg(&quot;getRight:&quot;,250,350);
8129         sampleA.var(d8,400,350);//getRight
8130                 
8131         sampleA.flip();
8132         while(!Input::get(Keyboard::spc));
8133 }
8134 }}}</pre>
8135 </div>
8136 <div title="Rectangle::include()" modifier="YourName" modified="200803100554" created="200712100008" changecount="2">
8137 <pre>[[Psychlops::Rectangle]]型の座標の中にに他のRectangleや点が含まれるかどうかを判定します。
8138
8139 |!Rectangle::include()|include(double x, double y)|対象の座標を指定|
8140 |~|include(Point point)|[[Psychlops::Point]] point:判定対象の点|
8141 |~|include(Rectangle rect)|[[Psychlops::Rectangle]] rect:判定対象の四角形|
8142
8143
8144 !!Rectangle::include()の書き方
8145
8146 [[例6: マウスのデモ]]をRectangle::include()を使って判定してみます。
8147 実行すると画面左上に黒い四角形が、中央に白色の四角形が表示されます。
8148
8149 以下の2行でマウスのポインタ位置が白色の四角形の上にあるかどうかを判定しています。
8150 もし、マウスポインタが四角形の上にあって、マウスの左ボタンが押され続けていれば、変数draggingに1が代入されます。
8151 {{{
8152 if(Input::get(Mouse::left, Mouse::pressed) &amp;&amp; rect.include(Mouse::x, Mouse::y)) dragging = 1;
8153 if(Input::get(Mouse::left, Mouse::released)) dragging = 0;
8154 }}}
8155 マウスで白色の四角形をドラッグアンドドロップして、黒い四角形に重ねてみましょう。
8156
8157 {{{
8158 #include &lt;psychlops.h&gt;
8159 using namespace Psychlops;
8160
8161
8162 void psychlops_main() {
8163
8164        Canvas display(Canvas::fullscreen);
8165        Mouse::show();
8166
8167        double rectsize=80;
8168        Psychlops::Rectangle rect(rectsize,rectsize);
8169                rect.centering();
8170        Psychlops::Rectangle drop_here(rectsize*2,rectsize*2);
8171                drop_here.shift(10,10);
8172        Color rectcol, dropareacol;
8173        int dragging = 0;
8174        Input::refresh();
8175
8176        while(!Input::get(Keyboard::spc)){
8177                display.clear(Color::gray);
8178
8179                //      Checking rects
8180                if(Input::get(Mouse::left, Mouse::pressed) &amp;&amp; rect.include(Mouse::x,
8181 Mouse::y)) dragging = 1;
8182                if(Input::get(Mouse::left, Mouse::released)) dragging = 0;
8183                if(dragging) {
8184                        rect.centering(Mouse::x, Mouse::y);
8185                        rectcol.set(1.0);
8186                } else {
8187                        rectcol.set(0.25);
8188                }
8189
8190                if(drop_here.include(rect)) {
8191                        dropareacol.set(1.0,0.0,0.0);
8192                } else {
8193                        dropareacol.set(0.0,0.0,0.0);
8194                }
8195
8196                drop_here.draw(dropareacol);
8197                display.msg(&quot;Drop a rect here!&quot;, drop_here.getLeft()+10,
8198 drop_here.getTop()+10);
8199                rect.draw(rectcol);
8200                display.flip();
8201        }
8202 }
8203
8204 }}}
8205 </pre>
8206 </div>
8207 <div title="Rectangle::resize()" modifier="YourName" modified="200803100553" created="200709132255" changecount="2">
8208 <pre>[[Psychlops::Rectangle]]型のサイズの再設定を行います。
8209
8210 |!Rectangle::resize()|resize(double w, double h)|対象の[[Psychlops::Rectangle]]型のサイズをw×hに変更します|
8211 |~|~|double w:変更後の横幅|
8212 |~|~|double h:変更後の高さ|
8213
8214 !!Rectangle::resize()の書き方
8215 200 × 200の四角形を50 × 100に変更します。
8216 {{{
8217 &lt;例&gt;
8218 #include &lt;psychlops.h&gt;
8219 using namespace Psychlops;
8220
8221 Psychlops::Rectangle rect1(200,200);
8222
8223 void psychlops_main() {
8224
8225         Canvas sampleA(Canvas::fullscreen);
8226         rect1.setColor(Color::green);
8227         rect1.resize(50,100);
8228         rect1.centering();
8229         rect1.draw();
8230
8231         sampleA.flip();
8232         while(!Input::get(Keyboard::spc));
8233 }
8234 }}}</pre>
8235 </div>
8236 <div title="Rectangle::set()" modifier="Psychlops_DevelopperG" created="200910080842" changecount="1">
8237 <pre>|!Rectangle::set()|set(double width, double height)|&gt;|!width×heightの四角形を設定します|
8238 |~|~|double width|描画する四角形の横幅|
8239 |~|~|double height|描画する四角形の縦幅|
8240 |~|set(double l, double t, double r, double b)|&gt;|!左上座標(l,t)から右下座標(r,b)までの四角形を設定します|
8241 |~|~|double l|描画する四角形の左上座標x1を指定|
8242 |~|~|double t|描画する四角形の左上座標y1を指定|
8243 |~|~|double r|描画する四角形の右下座標x2を指定|
8244 |~|~|double b|描画する四角形の右下座標y2を指定|
8245 |~|set([[Psychlops::Point]] point1, [[Psychlops::Point]] point2)|&gt;|!左上Point座標(x1,x1)から右下Point座標(X2,Y2)までの四角形を設定します|
8246 |~|~|[[Psychlops::Point]] point1|左上座標(x1,y1)を指定|
8247 |~|~|[[Psychlops::Point]] point2|右下座標(x2,y2)を指定|</pre>
8248 </div>
8249 <div title="Rectangle::setColor()" modifier="YourName" modified="200803100554" created="200709132244" changecount="5">
8250 <pre>[[Psychlops::Rectangle]]型の色設定を行います。
8251 この命令を使用する場合は、[[Rectangle::draw()]]命令を使用してください。
8252
8253 |!Rectangle::setColor()|setColor([[Psychlops::Color]] col)|対象の[[Psychlops::Rectangle]]型を[[Psychlops::Color]] colで塗りつぶします|
8254 |~|~|[[Psychlops::Color]] col:四角形の描画色|
8255
8256 !!Rectangle::setColor()の書き方
8257 {{{
8258 &lt;例&gt;
8259 #include &lt;psychlops.h&gt;
8260 using namespace Psychlops;
8261
8262 Psychlops::Rectangle rect1;
8263
8264 void psychlops_main() {
8265
8266         Canvas sampleA(Canvas::fullscreen);
8267         rect1.set(100,100);
8268         rect1.setColor(Color::red);
8269         rect1.centering();
8270         rect1.draw();
8271
8272         sampleA.flip();
8273         while(!Input::get(Keyboard::spc));
8274 }
8275 }}}</pre>
8276 </div>
8277 <div title="Rectangle::shift()" modifier="YourName" modified="200803100552" created="200709131914" changecount="3">
8278 <pre>[[Psychlops::Rectangle]]型の図形を移動します。
8279 元の座標(x,y)を基点に入力値(h,v)だけ移動します。
8280 |!Rectangle::shift()|shift(double h, double v)|座標(X,Y)を(h,v)だけ移動します|
8281 |~|~|double h:右水平方向の移動量|
8282 |~|~|double v:下垂直方向の移動量|
8283 [img[image/rect_shift().png]]
8284
8285 コード例は[[2.2.6節|2.2.6 画面上に縞を描画する-Rectangleクラス2-]]参照</pre>
8286 </div>
8287 <div title="Rectangleの宣言" modifier="Psychlops_DevelopperG" modified="200910080842" created="200709132233" changecount="1">
8288 <pre>RectangleはCanvas上に四角形の領域を設定する命令です。
8289 [[Psychlops::Rectangle]]型の命令を使用する前に必ず宣言が必要です。
8290 宣言のみの場合、[[Rectangle::set()|2.2.3 画面上に四角形を描画する-Rectangleクラス1-]]で四角形の詳細情報を設定しますが
8291 宣言文のみでも情報の設定が可能です。
8292
8293 |!Rectangleの宣言|Rectangle()|[[Psychlops::Rectangle]]型の宣言を行います|
8294 |~|Rectangle(double width, double height)|[[Psychlops::Rectangle]]型の宣言を行いwidth×heightの四角形を設定します|
8295 |~|~|double width:描画する四角形の横幅|
8296 |~|~|double height:描画する四角形の縦幅|
8297 |~|Rectangle(double l, double t, double r, double b)|[[Psychlops::Rectangle]]型の宣言を行い左上座標(l,t)から右下座標(r,b)までの四角形を設定します|
8298 |~|~|double l:描画する四角形の左上座標x1を指定|
8299 |~|~|double t:描画する四角形の左上座標y1を指定|
8300 |~|~|double r:描画する四角形の右下座標x2を指定|
8301 |~|~|double b:描画する四角形の右下座標y2を指定|
8302
8303 !!Rectangle()の宣言の書き方
8304 100 × 100の四角形を描画します。
8305 {{{
8306 &lt;例&gt;
8307 #include &lt;psychlops.h&gt;
8308 using namespace Psychlops;
8309
8310 Psychlops::Rectangle rect1(100,100);
8311
8312 void psychlops_main() {
8313
8314         Canvas sampleA(Canvas::fullscreen);
8315         sampleA.rect(rect1,Color::cyan);
8316
8317         sampleA.flip();
8318         while(!Input::get(Keyboard::spc));
8319 }
8320 }}}</pre>
8321 </div>
8322 <div title="Rectangle型に関して" modifier="Kazushi Maruya" modified="200712131648" created="200712131647" changecount="2">
8323 <pre>* [[Rectangle型の変数を画面中央に持って行きたい|Rectangle::centering()]]
8324 * [[Rectangle型の変数を任意の位置に持って行きたい|Rectangle::shift()]]
8325 * [[Rectangle型の領域を塗りつぶしたい|Rectangle::draw()]]
8326 * [[Rectangle型の変数の画面上のサイズを再設定したい|Rectangle::resize()]]
8327 * [[Rectangle型の変数のメンバを取得したい|Rectangle::getXXXX()]]
8328 * [[Rectanble型の変数に対して包含関係を判定したい|Reectangle::include()]]</pre>
8329 </div>
8330 <div title="SideBarTabs" modifier="PsychlopsAdmin" created="200709170655" changecount="1">
8331 <pre>&lt;&lt;tabs TabTimeline 'タグ' 'タグ一覧' TabTags '詳細' '詳細' TabMore&gt;&gt;</pre>
8332 </div>
8333 <div title="SiteSubtitle" modifier="YourName" modified="200908100431" created="200707220301" changecount="9">
8334 <pre>Version 1.3.5 (2009/September)
8335 Generated by TiddlyWiki
8336 Authored by Psychlops Admin Group</pre>
8337 </div>
8338 <div title="SiteTitle" modifier="YourName" modified="200908100430" created="200707220259" changecount="19">
8339 <pre>Psychlops Manual
8340 </pre>
8341 </div>
8342 <div title="StyleSheet" modifier="Psychlops_DevelopperG" modified="200910020133" created="200708280415" changecount="169">
8343 <pre>/***
8344 CSSカスタマイズ
8345 ***/
8346 body {
8347         background-color: #f0f8f0;
8348         margin: 0 auto;
8349         padding:0;
8350         font-family: arial,helvetica, centurygothic;
8351 }
8352
8353 #displayArea {
8354       color: #1c311c;
8355       background-color: #e0f8e0;
8356       position: relative;  
8357       width: 70%;
8358       top : 12px;
8359       margin-left:235px;
8360       font-size: 1.1em;
8361 }
8362
8363 #mainMenu{
8364         color: #1c311c;
8365         font-family: arial,helvetica, centurygothic;
8366         font-size:100%;
8367         background: #ffffcc;
8368         border: 1px dotted #54c354;
8369         position: absolute;
8370         top : 165px;
8371         left: 10px;
8372         width:200px;
8373         text-align: left
8374 }
8375
8376 #sidebar {
8377  position: absolute;
8378  top: 750px; 
8379  left: 10px;
8380  width: 200px;
8381  font-size: .9em;
8382  }
8383
8384
8385 /*{{{*/
8386 h1,h2,h3,h4,h5 {
8387         padding-top: 0.3em;
8388 }
8389
8390 .headerShadow {
8391         padding: 2.0em 0em 0.7em 1em;
8392 }
8393
8394 .headerForeground {
8395         padding: 2.0em 0em 0.7em 1em;
8396 }
8397
8398
8399 .siteTitle {
8400         font-size: 3.0em;
8401 }
8402
8403 .siteSubtitle {
8404         font-size: 1.2em;
8405 }
8406
8407 .subtitle {
8408         font-size: 0.9em;
8409 }
8410 /*}}}*/
8411
8412 /*{{{*/
8413
8414
8415 h1,h2,h3,h4,h5 {
8416         color: #1c411c;
8417         background: #8bd78b;
8418 }
8419
8420 .button {
8421         border: 0px;
8422 }
8423
8424 .header {
8425         background: #8bd78b;
8426 }
8427
8428 .shadow .title {
8429         color: #fff;
8430 }
8431
8432 .title {
8433         color: #1c411c;
8434         background: #8bd78b;
8435 }
8436
8437 .viewer th {
8438         background: #996;
8439 }
8440
8441 .viewer pre {
8442         border: 1px solid #6a6;
8443         background: #cfc;
8444         font-size:1.1em;
8445         font-family: Helvetica, Arial;
8446 }
8447
8448 .viewer thead td {
8449         background: #8d8;
8450 }
8451 /*}}}*/
8452
8453 /*{{{*/
8454 .viewer thead td {
8455         background: #8d8;
8456 }
8457
8458 .viewer tfoot td {
8459         background: #bd6;
8460 }
8461
8462 .MyStyle {
8463         background: #aff;
8464 }
8465 /*}}}*/
8466 </pre>
8467 </div>
8468 <div title="TargetSurface" modifier="YourName" modified="200803140518" created="200708212223" changecount="4">
8469 <pre>Canvasの指定方法
8470 |!ALL|すべてのCanvas|
8471 |!BACK|バックグラウンドのCanvas|
8472 |!FRONT|表示中のCanvas|
8473 ※省略時にはBACKが指定される</pre>
8474 </div>
8475 <div title="TiddlyWiki" modifier="YourName" created="200707220242" changecount="1">
8476 <pre>Type the text for 'Psychlops Manual'</pre>
8477 </div>
8478 <div title="Tips" modifier="Psychlops_DevelopperG" modified="200912010831" created="200708240236" changecount="41">
8479 <pre>!! Psychlopsの基本
8480 * [[Tips: OpenGL 1.4に対応した一般向けビデオチップ]]
8481 * [[Tips: OS環境による関数名の衝突について]]
8482 * [[Tips: ガンマ補正]]
8483 * [[Tips: Canvasとリフレッシュレートの詳細]]
8484 * [[Tips: Canvas::flip()とコマ落ち]]
8485 * [[Tips: CanvasクラスとDisplayクラス]]
8486 * [[Tips: Canvasを宣言しないと動作しない命令群]]
8487 * [[Tips: Image使用上の注意]]
8488 * [[Tips: 時間精度が必要なプログラムを実行するとき]]
8489 * [[Tips: プログラムが暴走したとき]]
8490 * [[Tips: Xcodeで実行時エラーが起きるとフリーズする]]
8491 * [[Tips: intどうしの比率の計算がおかしい]]
8492 * [[Tips: 機器間の同期]]
8493 * [[Tips: Psychlopsにおけるキーボードマウスの入出力検出ルーチン]]
8494 * [[Tips: トリガーとアナログ入力]]
8495
8496 !! Psychlopsの応用
8497 * [[Tips: Shapeクラスの概要]]
8498 * [[Tips: Strokeクラスの概要]]
8499 * [[Tips: ランダムドットステレオグラムの描画コード]]
8500 * [[Tips: Rectangleの配列を用いたドットパターンの描画例]]
8501 * [[Tips: アルファ混色の活用]]
8502 * [[Tips: プログラムを複数のファイルに分割する]]
8503 * [[Tips: QUESTの実装]]
8504 * [[Tips: 自分の機器の描画性能を確かめる]]
8505 * [[Tips: 独自のクラスを作る]]
8506
8507 !! 他ライブラリの命令を使用した機能の補完
8508 * [[Tips: ビープを出す]]
8509 * [[Tips: OpenGL命令の混ぜ方]]
8510 * [[Tips: 画像のフィルタリング処理]]
8511 * [[Tips: トリガーとアナログ入力2-外部機器を利用した例-]]
8512 * [[Tips: Bits++の利用]]</pre>
8513 </div>
8514 <div title="Tips: Bits++の利用" modifier="YourName" modified="200803050448" created="200802201839" changecount="5">
8515 <pre>Cambridge Research Systems社Bits++は14bit DACを備えるアナログ画像信号出力用機器で、DVIとUSBにつなぐだけで機能し、特殊な関数などを使う必要はありません。従って、Psychlopsの命令系と問題なく結合することが可能です^^*1^^。 Bits++の動作モード別に説明します。
8516
8517 公式サイト: [[http://www.crsltd.com/catalog/bits++/modes.html]]
8518
8519 なお、Bits++を利用する場合、Psychlops側の一切の[[ガンマ補正]]機能は以下の命令で無効化し、LUTテーブルを管理するなどの方法で自力で補正をかけてください。
8520 {{{
8521 Color::setGammaValue(1,1,1);
8522 }}}
8523
8524 *1 現時点(2008/3/1現在)でBits++で扱える3つのモード(basic/mono/color)のうちで、実際に動作することを確認しているのはmonoモード(下記参照)だけですが、他の2つのモードについても、仕様上は問題なく動作をすると考えられます。この2モードでプログラムコードを実装された方がいらっしゃいましたら、ぜひPsychlops開発フォーラム(http://psychlops.l.u-tokyo.ac.jp/?forum)までご一報ください。
8525
8526
8527 ! BITS++ mono
8528 DVI出力のうち、赤8bit+緑6bitを合成して14bitの輝度値として解釈します。 → 全色14bitで、連動します。青8bitについては別に指定したパレットオーバーレイとして動作すると記述されていますが、詳細についてはまだわかりません。使用法としては、Imageであれば、RGB, BYTEで確保した上で値を16bit整数に変換してビットシフトでRとGに書き込む方法があります(確認済)。
8529 {{{
8530 Color luminance_to_Color(double luminance) {
8531     unsigned short bits_mono = (unsigned short)(65535 * luminance);
8532     unsigned char upper = (unsigned char)(bits_mono &gt;&gt; 8), (unsigned char)(lower = bits_mono &amp;&amp; 255) ;
8533     return Color(upper/255.0, lower/255.0, 0);
8534 }
8535
8536 Display::pix(1,1,luminance_to_Color(0.567));
8537 }}}
8538
8539 ! BITS++ basic(未確認)
8540 単純にDVI出力をトラップし、RGB各色をLUTに従って変換して14bitアナログ出力します。 → 各色14 bit中8 bitの色を使うことができます。 Bits++では、(0,0)から横に数ピクセルが特定の値であるとき、 y軸が0のラインのデジタル値をLUT配列として解釈する、というシステムになっています。従って、パレットとなるImageを(0,0)起点で描画すればLUTを書き換えることができます。パレットは配列アクセス演算子 [] を備えるクラスでくるめば扱いやすいかもしれません。このモードを使用するとLUTアニメーションが使えます。
8541
8542 ! BITS++ color(未確認)
8543 DVI出力の2ピクセル分の値を合成して各色16bitの値を得ます(実効14bit)。従って、解像度は半分になります。
8544 </pre>
8545 </div>
8546 <div title="Tips: Canvas::flip()とコマ落ち" modifier="YourName" modified="200802201734" created="200709171713" changecount="1">
8547 <pre>実験やデモには、各フレーム(コマ)がCanvas.flip(duration)で指定した正しい時間にに出ている必要があります。しかし、実際には正しい時間にコマが表示されないことがあります。この状態をコマ落ちと呼びます。
8548
8549 フレームが正しく表示されない原因は、フレームを提示する時間内に計算が終わらないためです。
8550 Psychlopsでは指定された時間よりも描画の計算時間が大きくなったときは描画終了時以降でもっとも近い垂直同期にあわせて描画内容を更新します。描画途中のちぎれた画像が表示される可能性はありませんが、その代わり指定された時間に表示されない可能性があります。
8551 現在のコンピュータは同時に多数のソフトが起動しており、その計算に処理時間をとられてフレーム落ちする場合があります。これを回避するには、[[Tips: 時間精度が必要なプログラムを実行するとき]]をご覧ください。</pre>
8552 </div>
8553 <div title="Tips: Canvasとリフレッシュレートの詳細" modifier="Psychlops_Admin" created="200708240232" changecount="1">
8554 <pre>CRTは機器によってリフレッシュレートが小数点以下の桁でわずかに異なります(60Hzと表示されていても、厳密には60.2Hzなど小数点以下の桁があります)。
8555 Psychlopsでは、初期化時には小数点以下の桁は無視して確保しますが、正確なタイミングでflipするには、ディスプレイ装置の設定等で小数点以下の桁をあらかじめ調べて、正確な値を指定することをお勧めします。</pre>
8556 </div>
8557 <div title="Tips: Canvasを宣言しないと動作しない命令群" modifier="YourName" modified="200908100434" created="200908100433" changecount="4">
8558 <pre>下記命令群はCanvasの実体が確保されていることを前提に動作します。
8559
8560 !!! Display::に属するすべての命令
8561 事前に確保されたCanvas実体への抜け道命令群です。
8562 事前にCanvas実体を確保することが必要です。
8563
8564 !!! Figure.draw() / Shape.draw(Color)
8565 Image.draw()やRectangle.draw(Color)はDisplay::に属する命令と同様に、事前に確保されたCanvas実体を探索して実行する抜け道命令です。
8566 事前にCanvas実体を確保することが必要です。
8567
8568 !!! Color::setGammaValue()
8569 この命令はハードウェアガンマを設定するためにCanvas実体を探索します。
8570 Canvas確保前にこの命令を実行しようとすると失敗します。
8571
8572 !!! Independent
8573 デモ用の変数操作コンソールをCanvas上に作ろうとするためにCanvasを探索します。
8574 Canvas確保前にこの命令を実行しようとすると失敗します。
8575
8576 !!! ExperimentalMethods::Demo
8577 Independentと同様ですが、initialize時にCanvasが確保されている必要があります
8578
8579 !!! Widgets::に属するクラスの大半
8580 自身をCanvas上に自動登録しようとするためにCanvasを探索します。
8581 Canvas確保前にこの命令を実行しようとすると失敗します。</pre>
8582 </div>
8583 <div title="Tips: CanvasクラスとDisplayクラス" modifier="PsychlopsAdmin" created="200709171711" changecount="1">
8584 <pre>Canvasクラスと同様に使えるクラスとして、Displayクラスがあります。
8585
8586 Cの変数は、その変数が定義されている関数やクラス以外からは操作することができません。
8587 {{{
8588 void psychlops_main() {
8589         Canvas display( Canvas::fullscreen );
8590 }
8591 void func() {
8592         display.flip(); // エラー、displayはこの関数からは操作できない
8593 }
8594 }}}
8595
8596 Displayクラスは、最後に宣言したCanvasクラスの別名として機能します。このクラスはどこからでもアクセスすることができますが、Canvasの宣言の代わりはできません。
8597 {{{
8598 void psychlops_main() {
8599         Canvas display( Canvas::fullscreen );
8600 }
8601 void func() {
8602         Display::flip();        // OK
8603 }
8604 }}}
8605 </pre>
8606 </div>
8607 <div title="Tips: Image使用上の注意" modifier="YourName" modified="200803050504" created="200709040623" changecount="4">
8608 <pre>また、この転送はグラフィックボードやマザーボードなどの規格やOSの種類によって速度が違いますが、一般にある程度の時間がかかります。この転送はCanvasの描画内容を画面上へ反映させるよりはずっと大きいもので、かつ信号転送量が大きくなればなるほど、時間が必要になります。
8609 2008年現在の平均的なマシン(iMacやメーカー製のWindowsデスクトップマシン)で、100 Hzの描画に間に合う(つまり10ms以内に転送と描画反映が完了する)Imageのサイズはおおよそ300 x 300 pixel程度でしょう。以上のようなことを考えて、Imageを用いたオフスクリーン描画とCanvasに対する直接の描画を上手く使い分ける必要があります。
8610 また、高速のオフスクリーン転送を実行したい場合には[[Image::quicken()|3.1.4 発展的な内容]]命令が有効な場合があります。詳しくは、関数一覧の[[Image::quicken()|3.1.4 発展的な内容]]の項を参照してください。</pre>
8611 </div>
8612 <div title="Tips: OS環境による関数名の衝突について" modifier="Psychlops_DevelopperG" modified="200908100455" created="200908100437" changecount="1">
8613 <pre>ほかのライブラリ、特にOS・コンパイラ標準のライブラリにあるクラスとこの特定のクラスが同じ名前を持っているために、名前が衝突してしまってコンパイルができなくなっていることが考えられます。
8614 このような衝突を起こすことがある命令は以下の通りです。
8615
8616 !! windows.hで問題が生じるシンボル
8617
8618 | !Rectangle | Psychlops::Rectangle |
8619 | !Ellipse | Psychlops::Ellipse |
8620 | !round | Math::round |
8621 | !random | Math::random |
8622
8623 !! MacOSXで問題が生じるシンボル
8624
8625 | !Point | Psychlops::Point |
8626 | !round | Math::round |
8627 | !random | Math::random |
8628 </pre>
8629 </div>
8630 <div title="Tips: OpenGL 1.4に対応した一般向けビデオチップ" modifier="YourName" modified="200803141457" created="200709131950" changecount="15">
8631 <pre>OpenGL 1.4仕様は2002年7月24日に発行されており、基本的にはそれ以降に設計されたビデオチップはこれに対応しています。
8632 http://www.opengl.org/documentation/specs/version1.4/glspec14.pdf
8633
8634 現在対応を確認しておりますのは、
8635 |!開発元|!汎用カード|!OpenGL特殊化カード|
8636 |nVidia|GeForce 6000(NV30)系、FX5000(NV40)系 以降|Quadro FX系|
8637 |AMD(旧ATI)|RADEON 8000(R200)以降、X系|FireGL 8000以降、X, T, Z, V系|
8638 |Intel|945チップセット(GMA950)以降(915でも一応の動作確認はあり)| |
8639 |VIA(S3 Graphics)|DeltaChrome(S8)以降| |
8640 |SiS|SiS 671以降| |
8641
8642 ただし2008年3月現在で以下のマシンにおける動作不良を確認しております。
8643 これらのマシンについてはドライバレベルでのOpenGL動作の改変が行われているようです。
8644 現在のところ対応は未定です。また、今後の改良のため、動作不良等がございましたら、ぜひ[[Psychlops WIKI Trouble Report|http://psychlops.l.u-tokyo.ac.jp/?Trouble+Report]]までご報告をお願いいたします。
8645
8646 |!製造会社名|!機種名|!搭載ビデオチップ|!動作不良の内容|!暫定的な回避策|
8647 |SONY|Vaio Type-T VGN-TZ92S|Intel 945GM|垂直同期を待たずに画面の描画が行われてしまう|未発見|
8648 |Apple|Mac Pro MA463J/A |Radeon X1900XT(他ビデオボードのものについては未検証) |一定以下の負荷しかない描画内容が画面に反映されないことがある|各フレームの描画命令の前に大きなサイズ(全画面)のRectangleを1~数枚表示する|
8649
8650 より新しい情報は、 [[Psychlops WIKI 動作環境 |http://psychlops.l.u-tokyo.ac.jp/?Environment]]に随時反映されます。
8651 また、これらの動作改変はOpenGLを用いたソフトウェア全般で起こっている可能性があります。これらの他のアプリケーションの情報についてもよろしければ[[Psychlops WIKI Trouble Report|http://psychlops.l.u-tokyo.ac.jp/?Trouble+Report]]に情報をあげていただきますよう、お願い申し上げます。
8652 </pre>
8653 </div>
8654 <div title="Tips: OpenGL命令の混ぜ方" modifier="YourName" created="200802201824" changecount="1">
8655 <pre>現在Psychlopsの描画命令形はOpenGLのみで行われており、OpenGLの描画命令を混ぜることができます。この際、いくつかの注意が必要です。
8656
8657 * 回転(glRotate)と拡大縮小(glScale)はほとんどのビデオカードで近似値を使うよう実装されており、ピクセル単位での正しい計算ができなくなりジャギやモアレの原因になります。これを了解した上でお使いください。
8658 * glRotateとglScaleはPsychlops命令形の原点座標を中心に行われます。これらの命令を使った上で表示位置を指定する場合は、回転中心からの相対座標に描画した後、glTranslateをご使用ください。
8659 * Image型はquicken()を適用しないと使うことができません。
8660
8661 Imageを回転させるサンプル(モアレが発生していることをお確かめください)
8662 {{{
8663 #include &lt;psychlops.h&gt;
8664 using namespace Psychlops;      // Initially developed with Psychlops Win32 20070313a
8665
8666
8667 void psychlops_main() {
8668
8669         Canvas display(Canvas::fullscreen);
8670
8671         // Internal Image
8672         Image img(100, 100);
8673         Color col;
8674         for(int i=0; i&lt;100; i++) {
8675                 for(int j=0; j&lt;100; j++) {
8676                         col.set(0.25+sin(i/10.0)/4 + 0.25+sin(j/10.0)/4);
8677                         img.pix(i,j,col);
8678                 }
8679         }
8680
8681         // External Image File
8682         /*
8683         Image img;
8684         img.load(&quot;sample.png&quot;);
8685         */
8686
8687         img.quicken();
8688
8689         double orientation = 0.0;
8690
8691
8692         while(!Input::get(Keyboard::esc)) {
8693                 display.clear(0.5);
8694
8695                 orientation += PI/10;
8696
8697                 glPushMatrix();
8698                 glTranslated(display.getHcenter(),display.getVcenter(),0);
8699                 glRotated(orientation,0,0,1);
8700                 img.draw();
8701                 glPopMatrix();
8702
8703                 display.flip();
8704         }
8705
8706 }
8707 }}}
8708
8709 </pre>
8710 </div>
8711 <div title="Tips: Psychlopsにおけるキーボードマウスの入出力検出ルーチン" modifier="YourName" modified="200802201727" created="200709150147" changecount="2">
8712 <pre>Psychlopsのキーボード・マウスの入力監視は、主のプログラムとは独立に自動的に一定の間隔(マシンに依存しますが、通常0.1ミリ秒程度)で行われています。
8713
8714 監視ルーチンは各キーについてPushed, Pressed, Releasedのイベントがあったかどうかの表を内部に保持しています。
8715
8716 監視ルーチンではまず入力デバイスから送られてくるPushed, Releasedのイベントを監視し、イベントがあれば当該キーの当該イベントの表に記録します。この記録はInput::refresh()をするまで残ります。次に、PushedされてからReleasedされるまでの間、Pressedであると表に書き込みます。これはrefreshしなくても常時更新されています。
8717
8718 本来はPressedのデータを基本に取得すべきなのですが、近年のコンピュータではUSB化やOSのセキュリティ強化のためにイベント依存のデータしか取得できないようになっており、このような形式になっています。
8719
8720 マウスの座標系に関してはMac OS X、Windowsともに常時取得・設定が可能です。
8721
8722 現在、多くのUSBキーボード・マウスでは、コンピュータに転送する時間精度は1 msより悪いものとなっています。このため、PsychlopsのKeyboardやMouse命令は、精密な反応時間の取得(数 ms以下のオーダーの精度が必要な反応時間取得)には向きません(ただし、この転送の時間ノイズは数多くの試行を平均加算することで除去できる可能性はあります)。ただし、この値は表示装置の更新間隔(フレームレート)よりは十分に短いため、キーボードやマウスの反応を表示に反映する場合には最悪でも1フレーム遅れる程度にとどまります</pre>
8723 </div>
8724 <div title="Tips: QUESTの実装" modifier="YourName" modified="200803140630" created="200803100446" changecount="11">
8725 <pre>QUESTは上下法の一種です。何らかの閾値を測定する場合に恒常法よりも効率的に測定することができます。最尤法とベイズ推定を用いて閾値を推定するのが特徴です。今回は、Psychlopsに追加で組み込めるQUESTのサンプルを実行してみます。
8726
8727 ! ダウンロード
8728
8729 まず最初に、[[QUEST Sample|http://satolab.l.u-tokyo.ac.jp/~psychlops_site/download/BasicSamples/QUEST_Sample.zip]]をダウンロードしてください。
8730
8731 圧縮ファイルを展開すると、3つのファイルが生成されます。
8732
8733 * QUEST.h
8734 * QUEST.cpp
8735 QUESTの本体が書かれています。XcodeやReloで新規プロジェクトを作成した後、以上の2ファイルをプロジェクトと同じフォルダにおいてください。次に、[[プロジェクトにこのプログラムを追加|Tips: プログラムを複数のファイルに分割する]]してください
8736
8737 * ~QUESTDemo.cpp
8738 上記QUESTを呼び出してコントラスト閾値測定を行うデモプログラムです。新しいプロジェクトのプログラムにコピーして貼り付けてください。
8739
8740 ! QUEST使用の大まかな流れ
8741
8742 このQUESTの実装では、QUEST用の確率表を内部で作成し、それをユーザプログラムからメソッドを呼び出して更新していくモデルをとります。
8743
8744 [img[image/tips/QUEST.png]]
8745
8746
8747 ! 初期化
8748
8749 QUESTでは様々な事前情報が必要です。
8750 * 閾値は大体どのくらいにあるか
8751 * その閾値の推測の確からしさはどの程度か
8752 * QUESTで求める閾値は何%に設定するのか
8753 * 被験者がボタンを押し間違える確率はどの程度ぐらいか
8754 * チャンスレベルは何%か
8755 といった情報が必要です。これを実験を始める前に設定します。
8756
8757 {{{
8758         QUEST(double priorEstimatedThreshold, double logPriorSD, double thresholdProbability,
8759                   double slope, double errorRate, double chanceLevel,
8760                   Range thresholdRange, double logStep = 0.005, Measure placementMeasure=MEAN);
8761 }}}
8762
8763 |!QUEST()|QUEST(double priorEstimatedThreshold, double logPriorSD, double thresholdProbability, double slope, double errorRate, double chanceLevel, Range thresholdRange, double logStep, Measure placementMeasure);|QUEST進行用のクラスを作ります|&gt;|
8764 |~|~|double priorEstimatedThreshold|閾値が大体どのくらいにあるかという推測値です。|
8765 |~|~|double priorSD|推測値の確かさ。閾値が推測値とあまり離れていない場合は小さな値を、離れているかもしれない場合は大きめの値を代入してください。|
8766 |~|~|double thresholdProbability|閾値とみなす正答率。たとえば2肢強制選択(チャンスレベル0.5)の場合は、正答率0.75が閾値とみなされることが多いです。|
8767 |~|~|double slope|心理物理関数の傾き。傾きが既知の場合はその値を代入してください。傾きがわからない場合は効率は落ちますが大きめ値を代入しておいたほうがよいでしょう。|
8768 |~|~|double errorRate|被験者がボタン押しを間違える確率|&gt;|
8769 |~|~|double chanceLevel|チャンスレベル。2肢強制選択なら0.5を代入してください。|
8770 |~|~|Range thresholdRange|閾値の存在範囲を0以上の範囲で指定|
8771 |~|~|double logStep|ステップの値|
8772 |~|~|Measure placementMeasure|推定値を確率分布の最頻値か平均値にしたがって推定します(MODEかMEAN)|
8773
8774
8775 ! 実行
8776
8777 初期化が終われば試行を40-100回ほど繰り返します。
8778 各試行は次のステップで行います。
8779
8780 # ループ開始前に刺激の初期値を得ます。
8781 ** 初期値はdouble QUEST::nextTrial()を実行することで最適な値を求めることができます。
8782 ** 通常このようにして求められた値をそのまま使います(手を加える例もあります)。
8783 # 刺激を提示し、被験者からの反応を得ます。
8784 # 実験結果をQUESTに与え、次の試行で使う値を決定します。
8785 ** 2で提示するとき実際に用いた刺激強度と、被験者が成功したか失敗したかをvoid QUEST::next(double intensity, int result)でQUESTに通知します。成功した場合はresultに1を、失敗した場合は0を渡してください。
8786 # 1へと戻り所定の試行数が終わるまでこれを繰り返します
8787 # 所定の試行数が終了したら最後にfinalEstimate()を呼び出すことで実験の結果、推定された閾値を得ることができます。
8788
8789 |!next()|試行の結果をQUESTに通知し、次の試行で用いる値を返します(下記2関数の合成)|&gt;|
8790 |~|double intensity|前回の試行で使われた刺激強度|
8791 |~|int result|前回の試行の被験者の回答。成功した場合はresultに1を、失敗した場合は0を渡してください。|
8792
8793 |!QUEST::nextTrial()|double nextTrial(void)|次の試行で用いる値を返します|&gt;|
8794
8795 |!QUEST::update()|void update(double intencity, int result)|試行の結果をQUESTに通知し、状態をアップデートします。|&gt;|
8796 |~|~|double intensity|前回の試行で使われた刺激強度|
8797 |~|~|int result|前回の試行の被験者の回答。成功した場合はresultに1を、失敗した場合は0を渡してください。|
8798
8799 |!QUEST::finalEstimate()|double finalEstimate(void)|最終結果を返します|&gt;|
8800
8801 {{{
8802 #include &lt;psychlops.h&gt;
8803 using namespace Psychlops;
8804
8805 #include &quot;QUEST.h&quot;
8806
8807 void psychlops_main() {
8808
8809         //initialize QUEST method
8810         const double estimatedThreshold = 0.05; //estimated threshold, mean of prior pdf
8811         const double pdfSD = 2.0; //standard deviation of prior pdf
8812         const double thresholdProbability = 0.75; //the probability defined as threshold
8813         const double slope = 3.5; //slope of psychometric function
8814         const double errorRate = 0.01; //error rate
8815         const double chanceLevel = 0.5; //chance level
8816         Range  contrastRange;
8817         0&lt; contrastRange &lt;=1.0;
8818         
8819         QUEST quest(estimatedThreshold, pdfSD, thresholdProbability, slope, errorRate, chanceLevel, contrastRange);
8820
8821         //other variable
8822         double contrast = quest.nextTrial(); //contrast of stimuli at each trial
8823         int result; //result of trial
8824         int trial = 50; //number of trial
8825         
8826
8827         // Drawing Tools
8828         Canvas canvas(Canvas::fullscreen);
8829         QuickGabor gabor;
8830         gabor.set(50, 50, 1.0, 0, 0).centering();
8831         int display_frames = 1*canvas.getRefreshRate(); // display gabor for 1 second. 
8832
8833         //trial loop
8834         for (int i = 0; i&lt;trial; i++) {
8835
8836                 //replace this section to your experiment
8837                         // displaying stimuli
8838                         for(int i=0; i&lt;display_frames; i++) {
8839                                 canvas.clear(0.5);
8840                                 gabor.draw();
8841                                 canvas.flip();
8842                         }
8843         
8844                         // waiting response
8845                         canvas.clear(0.5);
8846                         canvas.flip();
8847                         while(true) {
8848                                 if(Input::get(Keyboard::left))  { result = 1; break; }  // positive reaction
8849                                 if(Input::get(Keyboard::right)) { result = 0; break; }  // negative reaction
8850                         }
8851                 
8852                 //update QUEST
8853                 gabor.contrast = quest.next(contrast, result);
8854         }
8855
8856         //final estimate
8857         double threshold = quest.finalEstimate();
8858         double sd = quest.getSD();
8859         
8860 }
8861 }}}
8862
8863 ! 注意点
8864
8865 QUESTでは心理物理関数にWeibull分布を仮定しています。そのため、心理物理関数がWeibull分布で近似できない場合は使えませんのでご注意ください。
8866
8867 </pre>
8868 </div>
8869 <div title="Tips: Rectangleの配列を用いたドットパターンの描画例" modifier="YourName" modified="200802201227" created="200802191422" changecount="5">
8870 <pre>ランダムドットパターンなどの複数のドットを使ったパターンの描画で、
8871 ドット密度や各ドットの運動方向を精密に操作したいときは、
8872 [[3.2節|3.2 オフスクリーンを用いた描画例-ランダムドットパターン-]]で紹介した方法よりは、
8873 Rectangleの配列を用いた方法のほうがプログラミングが容易です。
8874 いかにRectangleの配列を用いたドットパターンの描画の典型的なプログラム例をしめします。
8875 ここでは、Rectangleの配列のうちの一部をランダムな方向に運動させ(Noise)、残りを右方向へ運動させています(Signal)。
8876
8877 {{{
8878 #include &lt;psychlops.h&gt;
8879 using namespace Psychlops;
8880
8881 void psychlops_main() {
8882
8883         Canvas display(Canvas::fullscreen);
8884
8885         //      Generic Random Dot Parameters
8886         const int DOT_SIZE = 3;
8887         const int MAX_NUM_DOTS = 1000;
8888         const double speed=3.0;
8889         const double noise_dots_ratio = 0.5;
8890         
8891         // Parameters of each dots
8892         Psychlops::Rectangle dot[MAX_NUM_DOTS];
8893         Psychlops::Rectangle canvas_rect(display.getWidth(), display.getHeight());
8894         double direction;
8895
8896         // Randomize Dots' Position
8897         for(int i=0; i&lt;MAX_NUM_DOTS; i++) {
8898                 dot[i].set(DOT_SIZE, DOT_SIZE);
8899                 dot[i].centering(
8900                 Psychlops::random(display.getWidth()), 
8901                 Psychlops::random(display.getHeight())
8902       );
8903         }
8904
8905         while(!Input::get(Keyboard::esc)) {
8906                 display.clear();
8907
8908                 //      Update noise dots' position
8909                 for(int i=0; i&lt;MAX_NUM_DOTS*noise_dots_ratio; i++) {
8910                         direction=Psychlops::random(2*PI);
8911                         dot[i].shift(speed*cos(direction), speed*sin(direction));
8912                         if(!canvas_rect.include(dot[i]))
8913                                 dot[i].centering(
8914                 Psychlops::random(display.getWidth()), 
8915                 Psychlops::random(display.getHeight())
8916              );
8917
8918                         dot[i].draw(Color::white);
8919                 }
8920
8921                 //      Update signal dots' position
8922                 for(int i=MAX_NUM_DOTS*noise_dots_ratio; i&lt;MAX_NUM_DOTS; i++) {
8923                         dot[i].shift(speed, 0.0);
8924                         if(!canvas_rect.include(dot[i])) dot[i].shift(-display.getWidth(), 0.0);
8925                         dot[i].draw(Color::white);
8926                 }
8927
8928                 display.flip(3);
8929         }
8930 }
8931 }}}
8932 </pre>
8933 </div>
8934 <div title="Tips: Shapeクラスの概要" modifier="Psychlops_DevelopperG" modified="200912010828" created="200912010822" changecount="1">
8935 <pre>Shapeクラスに属するクラスは、デフォルトの描画色や枠線を指定することができます。
8936 このデフォルトの色と線Groupでまとめたりする際にとくに有用です。
8937 これらの指定はShapeクラスのメンバ fill, strokeに対して代入を行うことで実行します。
8938
8939 |!Shape::fill|デフォルトの塗り色です。Colorが代入できます。初期状態では無色です。|
8940 |!Shape::stroke|デフォルトの枠線をStroke型で指定します。初期状態では太さ0の線です。|
8941
8942 枠線を指定ためにはstrokeクラスの変数を宣言して、書式指定を行っておく必要があります。
8943 書式指定には宣言時に指定をする方法とset()命令を使用する方法があります。
8944 これらの詳細については[[Tips: Strokeクラスの概要]]をご覧ください。
8945
8946 {{{
8947 #include &lt;psychlops.h&gt;
8948 using namespace Psychlops;
8949
8950 void psychlops_main() {
8951         Canvas canvas(Canvas::window);
8952
8953         Psychlops::Rectangle rect(100,100);
8954         rect.centering().shift( 100, 0);
8955         rect.stroke.set(Color::red, 5, Stroke::DOTTED);
8956
8957         Shape::Colored&lt;Psychlops::Ellipse&gt; ellipse;
8958         ellipse.set(rect);
8959         ellipse.centering().shift(-100, 0);
8960         ellipse.fill = Color::white;
8961
8962         while(!Keyboard::esc.pushed()) {
8963                 canvas.clear();
8964                 ellipse.draw();
8965                 rect.draw();
8966                 canvas.flip();
8967         }
8968 }
8969
8970 }}}
8971
8972 この命令セットのうちでここまで扱わなかったものとしてgetDatum()があります。
8973
8974 |!Point Shape::getDatum()|getDatum(void)|Figureの「基準座標」をPoint型で返します. &lt;br&gt; 基準座標の定義は各クラスによりますが、原則として中心点や起点(図形の左上の点)です|</pre>
8975 </div>
8976 <div title="Tips: Strokeクラスの概要" modifier="Psychlops_DevelopperG" modified="200912010835" created="200912010830" changecount="5">
8977 <pre>Strokeクラスは、線の書式を表現するためのクラスです。
8978 描画系の関数にColorの代わりに Stroke構造体を与えることで線を描画します。
8979
8980 !現状の制限
8981     * ImageにStrokeを描画することはできません。
8982     * 線が角ごとに途切れるうえ、円が多角形近似のため、ある程度以上太くしたり破線を指定したりするとアラが目に見えてわかります。
8983
8984 !関数
8985 コンストラクタ, set関数 (Color color, double thick, Stroke::Pattern pattern)
8986
8987 color
8988     線の色です。
8989 thick
8990     線の太さです。現状ピクセル単位になります。
8991 pattern
8992     バターンの種類はSOLID, DASHED, DOTTEDです
8993
8994 コード例
8995 {{{
8996    Stroke solid(Color::red, 1, Stroke::SOLID); 
8997   // 宣言と設定を同時に行う方法は関数内でのみ使用できます。
8998    Stroke dashed, dotted;
8999    dashed.set(Color::green, 3, Stroke::DASHED);
9000    dotted.color = Color::blue;
9001    dotted.width = 5;
9002    dotted.pattern = Stroke::DOTTED;
9003
9004    Psychlops::Rectangle rect(100,100);
9005
9006    while(!Keyboard::esc.pushed()) {
9007        canvas.clear();
9008        rect.centering();
9009        rect.shift(-150,0).draw(solid);
9010        rect.shift( 150,0).draw(dashed);
9011        rect.shift( 150,0).draw(dotted);
9012        canvas.flip();
9013    }
9014 }}}
9015
9016 実行結果
9017 [img[image/Stroke_Sample.png]]
9018
9019 </pre>
9020 </div>
9021 <div title="Tips: Vistaにおけるインストール" modifier="YourName" modified="200802210940" created="200802191504" changecount="3">
9022 <pre>Windows VistaでPsychlopsを利用する場合、いくつかの注意が必要です。
9023
9024 !! ビデオカードのドライバがOpenGLをサポートしているか確かめる
9025 Windows Vistaでは、DirectXがOpenGLより優先されたため、OpenGLへのサポートが十分でないビデオカードがあります。2008/2/14現在でWindows VistaでOpenGLが十分にサポートされているビデオカードは以下のとおりです。
9026
9027 * nVidia GeForce 7000番台以降
9028 * AMD(ATI) Radeon Xシリーズ
9029
9030
9031 !! インストーラの実行時に管理者権限で実行する
9032
9033 BCC、SetBCC、Psychlopsのインストーラをダブルクリックしても、何も起きない(ように見える)、あるいは明らかに実行に失敗しているように見える場合があります。その場合、実行ファイルを右クリックして「管理者として実行」をクリックし、管理者のパスワードを入力して実行してください。
9034 [img[Vista|image/Win/VistaBCC_1.png]]
9035
9036
9037 !! Borland C++ Compilerの環境変数を設定する
9038
9039 [[BCCのインストール|1.3 インストール作業(Windows)]]を実行した後、追加で以下の作業を行います。
9040
9041 0. スタートボタン → コントロールパネルを開きます
9042 1. 「環境変数」を検索します
9043 2. 「環境変数を編集」をクリックします
9044 3. 開いたウインドウで、「変数」の中に「PATH」があるかどうかを調べます
9045 4.1. 「PATH」があった場合、それを選んで「編集」ボタンを押します。開いたウィンドウで変数値の末尾に以下のテキストを''追加します''。セミコロンは必要なので落とさないようにしてください。
9046 {{{
9047  ; C:\borland\bcc55\Bin;
9048 }}}
9049 4.2. 「PATH」がなかった場合、「新規」ボタンを押します。開いたウィンドウで、変数名に「PATH」、変数値に以下のテキストを書き込みます
9050 {{{
9051  C:\borland\bcc55\Bin;
9052 }}}
9053 [img[Vista|image/Win/VistaBCC_2.png]]
9054
9055 </pre>
9056 </div>
9057 <div title="Tips: Xcodeで実行時エラーが起きるとフリーズする" modifier="YourName" modified="200802130846" created="200709171710" changecount="5">
9058 <pre>Xcodeで実行時エラーが起きると操作を受け付けなくなる現象があります。これは実際は実行時エラー発生時にデバッガが起動しており、フルスクリーンのために見えないだけです。フリーズはしていません。この現象は以下に例示する方法で回避できます。
9059
9060 ! 複数ディスプレイを使用する
9061 複数ディスプレイを使用した場合、Psychlopsは主ディスプレイ(メニューバーのあるディスプレイ)に表示され、副ディスプレイを拘束しません。副ディスプレイにデバッガウィンドウを表示しておくことで、デバッガからのプログラム終了やエラー追跡ができます。
9062
9063 ! デバッガの起動を防ぐ
9064 デバッガの起動をとめるには、プロジェクト中の「実行可能ファイル」タブから、
9065 * (プロジェクト名) static
9066 * (プロジェクト名) dynamic
9067 * (プロジェクト名) 10.3
9068 と並んでいるうちの一番上のstaticを右クリックまたはctrl+クリックして、「情報を見る」を選びます。
9069
9070 [img[実行ファイルの情報|image/OSX/Psychlops_OSX_dbg1.png]]
9071
9072 開いたダイアログで「デバッガ」タブを選び、すべてのチェックボックスをオフにします。
9073 [img[実行ファイルの情報|image/OSX/Psychlops_OSX_dbg2.png]]
9074
9075 ! 待つ
9076 演算性能によりますが、数分間待つと回復することがあります
9077
9078 </pre>
9079 </div>
9080 <div title="Tips: intどうしの比率の計算がおかしい" modifier="YourName" modified="200803050457" created="200802202143" changecount="2">
9081 <pre>C++では、intどうしの割り算を行うと小数点切捨ての整数で帰ってきます。このため、特にfor文のカウント変数と最大値から比率を求めようとするときなどで精度差が出ることがあります。これを回避するには型キャストしなければなりません。また、定数項はなるべく「.0」をつけて強制的に浮動小数点値と解釈させると、その項の演算結果を利用する演算子の結果はすべてdouble型にキャストされます。これを覚えておけば「式はあっているはずなのに何も表示されない」といったバグはずいぶんと減るはずです。
9082
9083 {{{
9084 for(int i=0; i&lt;max; i++) {
9085     Display.pix(i, 0, Color( (double)(i)/max ));
9086     Display.pix(i, 2, Color( i*2.0 / max));
9087 }
9088 }}}
9089
9090 </pre>
9091 </div>
9092 <div title="Tips: アルファ混色の活用" modifier="YourName" modified="200803140631" created="200802201822" changecount="7">
9093 <pre>Psychlopsが標準で扱うアルファ値(透明度)は、以下のように定義されています。
9094
9095 {{{
9096  結果 = α×上書き色 + (1-α)×元の色
9097 }}}
9098
9099 したがって、アルファ値を0.5とおくと元の色と上書き色の平均値になります。アルファ合成は合成結果が重み付け平均として表せるときに有効です。これ以外の計算方法でアルファ合成を行いたい場合には[[glBlendFunc命令を使用して直接指定をおこなってください|Tips: OpenGL命令の混ぜ方]]。
9100
9101 アルファ合成はビデオカード上で行われるために高速ですが、[[ガンマ補正|Tips: ガンマ補正]]がハードウェア合成の場合にのみ正常に機能し、ソフトウェアエミュレーションである場合には補正済みの値どうしの平均を行ってしまうために誤った値が表示されます。これを防ぐためには、[[Color::strict_match = true;|Color::strict_match]]を指定してソフトウェアエミュレーションを機能させないようにしてください。
9102
9103
9104 以下のコードを実行すると、横縞のガボール刺激に縦縞のダイナミックノイズがかかった刺激が描画されます。ここでは、縦縞のダイナミックノイズをアルファ合成を用いてガボール刺激に重ね合わせています。[[Tips: 独自のクラスを作る]]で例示されている~QuickGaborもアルファ合成を用いています。
9105
9106 [img[image/tips/Sample_alpha.png]]
9107
9108 {{{
9109 #include &lt;psychlops.h&gt;
9110 using namespace Psychlops;
9111
9112 void psychlops_main() {
9113
9114         Canvas display(Canvas::fullscreen);
9115
9116         Psychlops::Rectangle noise;
9117         double noise_lum;
9118
9119         QuickGabor gabor;
9120         gabor.set(30,30).centering();
9121         gabor.orientation = 90.0; // don't
9122
9123         while(!Input::get(Keyboard::esc)) {
9124                 display.clear(0.5);
9125
9126                 // draw gabor
9127                 gabor.phase += 6.0;
9128                 gabor.contrast = sin(PI*gabor.phase/360.0);
9129                 gabor.draw();
9130
9131                 // draw noise
9132                 noise.set(1, display.getHeight()).centering(-1,display.getVcenter());
9133                 for(int i=0; i&lt;display.getWidth(); i++) {
9134                         noise_lum = Psychlops::random();
9135
9136                         // **************draw with alpha-blending*****************************
9137                         noise.shift(1,0).draw(Color(noise_lum, noise_lum, noise_lum, 0.5));
9138                 }
9139
9140                 display.flip();
9141         }
9142
9143 }
9144 }}}
9145 </pre>
9146 </div>
9147 <div title="Tips: ガンマ補正" modifier="Psychlops_DevelopperG" modified="200910080850" created="200802130705" changecount="15">
9148 <pre>ディスプレイやプリンタなど、コンピュータから利用できる画像表示機器では、明るさが指定した値通りに表示されず、歪みが生じます。多くの場合はこの歪みは指数関数に従います。その指数を'''ガンマ値'''と呼び、表示装置の特性を記述するパラメタになります。CRTの場合、ガンマ値は赤、緑、青の蛍光体の間で独立です。
9149
9150 [img[輝度歪み|image/DisplayGamma.png]]
9151
9152 PsychlopsのColor型はガンマ補正機能を備えています。ガンマ補正にはガンマ値による指定のほかに、テーブル(補正表)による指定方法があります。CRT以外の表示装置の特性は必ずしもガンマ値型指数関数に従うとは限りませんので、液晶やプラズマ、DLPプロジェクタなどではテーブルによる指定を試みる必要があります(番、山本、江島、2006)。
9153
9154 表示装置のガンマ値を指定する
9155 {{{
9156  double gamma_r=1.7, gamma_g=1.6, gamma_b = 0.6;
9157  Color::setGammaValue(gamma_r, gamma_g, gamma_b);
9158 }}}
9159
9160
9161 ! ハードウェア補正とソフトウェア補正
9162 Psychlopsのガンマ補正は、標準でハードウェアの調整機能を起動し、それに失敗した場合ソフトウェアによるエミュレーションを試みます。
9163
9164 [img[ソフト補正とハード補正|image/ColorCalibrationModel.png]]
9165
9166 !! ハードウェア補正
9167 最近のビデオ表示機器は、理論値と電気出力値の変換テーブルを内部に保持しており、このテーブルに補正値を書き込むことで補正機能を有効にすることができます。書き込みはDVI接続時でも成功しますが、有効に機能するかはハードウェアに依存します。確認するためには実測が必要です。
9168
9169 ハードウェア補正では、以下のOSのAPI関数を呼び出します。
9170 {{{
9171  * Windows
9172  SetDeviceGammaRamp
9173  * Mac OS X
9174  CGSetDisplayTransferByFormula
9175  CGSetDisplayTransferByTable
9176 }}}
9177
9178 !! ソフトウェア補正
9179 ソフトウェア補正では、Color型とImage型の入出力時に変換関数を適用することで、ハードウェア補正と同様の機能を実現します。Color型とImage型の入出力関数で補正を行い、補正後の値を保存することで実現しています。
9180
9181 ソフトウェア補正はハードウェア補正に比べいくつかの点で劣ります。
9182 * 理論値の変化に対して鋭敏に輝度が変化する部分とそうでない部分が生じます。鋭敏でない部分では輝度変化がつぶれてしまいます(下図)
9183 * ガンマ値をプログラム中で変更した場合、それ以前までにsetされたColor情報は古いガンマ値のエミュレーション結果に従った値が保存されており、新しいガンマ値に従わない誤った色が表示されます。いっさいの警告が出ませんのでご注意ください。
9184 * 変換をCPUで計算するため、速度が低下します。
9185 * ソフトウェア補正で補正テーブルを使用した場合、さらに問題が生じます
9186 ** 色指定は通常0~1の実数ですが、補正テーブル適用時は入力値がテーブルの行数にあわせて離散化されます。このため、Color.set()で入力した値とget()で取得した値が異なることがあります。
9187 ** 複数の理論値に対して同一の補正値が割り当てられている場合、get()で復元する場合に入力理論値と異なることがあります。
9188
9189 [img[ソフトウェア補正の鋭敏さ|image/SoftwareColorCalibration.png]]
9190
9191
9192 !! 表示装置固有の輝度特性測定時の注意
9193 ハードウェア補正が適用されたPsychlopsを用いて表示装置の特性を測定する場合には、OSが標準で行っている補正(ほとんどの場合非線形ですを外すために、以下の関数を実行してください。
9194 {{{
9195  Color::setGammaValue(1,1,1);
9196 }}}
9197
9198 *この記述はver1.0 (Psychlops20080214)以降での注意です。これ以前のバージョンに使用されているsetGammaValue命令は常にソフトウェア補正を用いているために、OSによる標準の補正を気にする必要はありません。
9199
9200 !! 厳密なガンマ補正
9201 特に厳密な色補正を保証したい時に、ガンマ補正や色指定で値が0以上1以下にならないケースやソフトウェア補正が適用される環境下ではプログラムが停止されるように強制させることができます。以下のコードをpsychlops_main()の冒頭に記述してください。
9202 {{{
9203  Color::strict_match = true;
9204 }}}
9205
9206
9207 ! 脚注
9208
9209 番浩志、山本洋紀、江島義道 (2006) &quot;Mcalibrator: MATLAB言語を用いたディスプレイ較正のための統合型GUIソフトウェアの開発&quot; 基礎心理学研究、第24巻、149-161頁
9210 </pre>
9211 </div>
9212 <div title="Tips: トリガーとアナログ入力" modifier="YourName" modified="200802201728" created="200802201438" changecount="4">
9213 <pre>脳波やfMRI研究では電気的トリガを出す必要がありますが、[[Tips: 機器間の同期]]で説明したとおり、機器間の同期を取ることが非常に困難であり、視覚刺激提示装置と電気パルスを同期制御することは難しくなっています。
9214
9215 この問題を確実に解決する方法としては、画面の輝度をフォトトランジスタで電気信号に変換する方法があります。フォトトランジスタの照度に対する反応はきわめて高い時間精度を持つため、輝度の状態を得るには適しているといえます。
9216
9217 一般的な電気信号入出力装置は電流ではなく電圧を信号として扱いますので、フォトトランジスタの照度への反応を電圧の変化として取り出す回路が必要です。一般的な回路図を以下に示します。
9218
9219 [img[image/Tips/phototrigger.png]]
9220
9221 フォトトランジスタを吸盤などに埋め込んで画面に設置できるようにして、フォトトランジスタに直列につないだ抵抗器の両端の電圧を電圧計に入力します。
9222
9223 * たとえばCRTの場合、陰極線が画面を走査するには一定の時間が必要です。したがって、画面上部と画面下部では輝度の変化する時間に数ミリ秒の差があります。
9224 * 電圧の範囲は、測定照度、フォトトランジスタの特性、抵抗器の抵抗値、によって決まります。一般的には、抵抗を変えることで必要な電圧範囲になるようにします。
9225 * 刺激提示画面上に電圧計を設置するのが難しい場合、マルチディスプレイを用いて2画面に同じ画像を提示し、実験に使わないほうの画面でトリガーをとる方法があります。この方法では、画面間の同期はとれていませんが、垂直同期間隔は一定です。あらかじめ両画面の同期の時間差を測定しておくことで、正確な同期タイミングを算出することが可能です。
9226 </pre>
9227 </div>
9228 <div title="Tips: トリガーとアナログ入力2-外部機器を利用した例-" modifier="YourName" modified="200803062207" created="200803062201" changecount="6">
9229 <pre>脳波実験等では参照トリガ電圧が必要になることがありますが、近年のコンピュータでは、電気的外部入出力端子(~RS-232Cなど)を持つものが少なく利用しにくくなっています。そこで今回は、比較的容易に入手可能なNational Instruments社製[[USB-6009|http://sine.ni.com/nips/cds/view/p/lang/en/nid/14605]]を用いてアナログ入出力をしてみます。
9230
9231 ~USB-6009は電圧データ入出力用機器です。
9232 * アナログ入力 4 ch、アナログ出力 1 ch
9233 * 時間分解能:入力 48 kHz 出力 150 kHz
9234 * 電圧分解能:入力 14 bit 出力  12 bit
9235 と、心理物理学には十分な性能を持っています。価格は35000円程度で、電圧入出力機器としては比較的安価に入手できます。
9236
9237 実際に実験に使う上では、他の入出力機器との時間同期を気にする必要があります。
9238
9239 * 一般的注意
9240 ** USBにはキーボード・マウス等、最低限のものしかつながないでください。USBメモリ・ハードディスク・USBオーディオ・Webカメラ等、大量のデータを転送するものとの併用は危険です。
9241 * 入力
9242 ** データ取得までの時間遅れは、少なくとも10ms以下程度に抑えられています。したがって、入力を元に画像を制御する用途には用いることができます。
9243 ** 入力は4ch同時に取ることができるので、画面のトリガと反応ボックス等を組み合わせて入力することで、入力した機器間で厳密な同期をとることができます。
9244 * 出力
9245 ** データを~NI-6009に送信した後、~USB-6009のデジタル入力端子に外部から別のトリガ信号を入力することで、そのトリガに同期してデータを出力することができます。
9246 *** たとえば、VGAの垂直同期信号をデジタル入力端子に誘導しておくと、任意の時点で送信したアナログ出力データを次のflipに同期して出す、といったことが可能になります。
9247
9248
9249 今回は、もっとも簡単なサンプルとして、~USB-6009をひとつ接続し、10ms程度の精度で「電圧値を得る」「電圧を出す」を単発で行う拡張サンプルを実行してみます。高度な時間制御は行っておりません。なお、このサンプルを応用したプログラムを配布する場合は、配布先の環境でも同様の装置が必要です。
9250
9251
9252 ! セットアップ
9253
9254
9255 !! Mac OS XでXcodeを使用する場合
9256 * [[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]]ドライバの最新版をダウンロードします。
9257 * ダウンロードしたファイルを実行し、ドライバをインストールします(要再起動)。
9258
9259 * [[NIDAQmxBase_Sample_OSX.zip|http://psychlops.l.u-tokyo.ac.jp/download/BasicSamples/NIDAQmxBase_Sample_OSX.zip]]をダウンロードし、解凍します
9260 * 解凍されてできたフォルダから
9261 ** 「~NIDAQmxBase_Ext_Installer」をダブルクリックして実行します(下記動作を行うシェルスクリプトです)
9262 *** 失敗した場合、手動で「nidaqmxbase_ex_sample.cpp」を/Library/Frameworks/Psychlops.framework/Headers/Extentionsにコピーします。
9263
9264 新規プロジェクトを作成し、プロジェクトのプロパティでNI-DAQmx Baseドライバを読み込む設定を行います。
9265 * 「External Frameworks and Libraries」をCtrl+クリックし、追加→既存のフレームワークから「nidaqmxbase.framework」を選択
9266 [img[image/OSX/nidacmxbase_OSX_1.png]]
9267
9268 !! WindowsでBCC5.5 + Reloを使用する場合
9269
9270 まず最初に、必要なファイルのセットアップを行います。
9271
9272 * [[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]]ドライバの最新版をダウンロードします。
9273 * ダウンロードしたファイルを実行し、ドライバをインストールします(要再起動)。
9274
9275 * [[NIDAQmxBase_Sample_win32.zip|http://psychlops.l.u-tokyo.ac.jp/download/BasicSamples/NIDAQmxBase_Sample_win32.zip]]をダウンロードし、解凍します
9276 * 解凍されてできたフォルダから、
9277 ** 「~NIDAQmxBase対応Psychlops拡張のインストーラ.bat」をダブルクリックして実行します(nidaqmxbase_ex_sample.cppをExtentionフォルダにコピーします。
9278 ** 「~NIDAQmxBaseをBCCに対応させる.bat」をダブルクリックして実行します(bccのツールを呼び出します・要管理者権限)
9279
9280
9281 新規プロジェクトを作成し、プロジェクトのOptionsダイアログで、NI-DAQmx Baseドライバを読み込む設定を行います。
9282 *「Linker」タブの下部にある「Library」に「nidaqmxbase_bcc.lib」を追加(末尾にスペースをひとつ空けて追加します)
9283 * DirectoriesタブにNI-DAQmx Baseを追加する
9284 ** 「Extra Include Paths」に「C:\Program Files\National Instruments\NI-DAQmx Base\Include\」を追加
9285 ** 「Extra Library Paths」に「C:\Program Files\National Instruments\NI-DAQmx Base\Lib\」を追加
9286 [img[image/win/nidaqmxbase_Win32_1.png]]
9287
9288
9289 ! プログラミング
9290
9291 !! アナログ入力
9292
9293 * NI ~USB-6009 の2番(+)、3番(-)ポートに入力線を接続する
9294 * #include &lt;Extentions/Samples/nidaqmxbase_ex_sample.cpp&gt;を追加
9295 * ~NIDAQmxBase_AnalogInput_Dev1 クラスの変数を作成し、ポートを初期化する
9296 * 変数のget()メソッドを呼び、ポート間の電位差を得る
9297 これで、0Vから5Vまでの電位差を得ることが可能になります。
9298
9299 以下のサンプルでは、中心に長方形が表示され、その縦幅が電圧値(V) * 100 ピクセルとしてリアルタイムに表示されます。
9300
9301 {{{
9302 #include &lt;psychlops.h&gt;
9303 using namespace Psychlops;
9304 #include &lt;Extentions/Samples/nidaqmxbase_ex_sample.cpp&gt;
9305
9306 void psychlops_main() {
9307         Canvas canvas(Canvas::fullscreen);
9308
9309         double rect_size = 100;
9310         Psychlops::Rectangle rect;
9311         rect.centering();
9312
9313         NIDAQmxBase_AnalogInput_Dev1 analog_in;
9314
9315         while(!Input::get(Keyboard::esc)) {
9316                 canvas.clear();
9317                 rect.resize(rect_size, rect_size * analog_in.get()).draw(Color::white);
9318                 canvas.flip();
9319         }
9320 }
9321 }}}
9322
9323 このサンプルをフォトトランジスタと接続して照度に連動する実行例が[[Cliplife|http://cliplife.jp/clip/?content_id=usqdz3cv]]にアップロードされています。
9324
9325
9326 !! アナログ出力
9327
9328 * NI USB-6009 の14番、15番ポートに出力線を接続する
9329 * ~NIDAQmxBase_AnalogOutput_Dev1 クラスの変数を作成し、ポートを初期化する
9330 * 変数のput(double voltage)メソッドを呼び、ポート間の電位差を渡す
9331 これで、0Vから5Vまでの電位差の出力が可能になります。
9332
9333 以下のサンプルでは、マウスが画面最上部にあるとき1V、最下部にあるとき0Vになります。
9334
9335 {{{
9336 #include &lt;psychlops.h&gt;
9337 using namespace Psychlops;
9338 #include &lt;Extentions/nidaqmxbase_ex_sample.cpp&gt;
9339
9340 void psychlops_main() {
9341         Canvas canvas(Canvas::fullscreen);
9342
9343         NIDAQmxBase_AnalogOutput_Dev1 analog_out;
9344         Mouse::show();
9345
9346         while(!Input::get(Keyboard::esc)) {
9347                 canvas.clear();
9348                 analog_out.put(1.0-1.0*Mouse::y/Display::getHeight());
9349                 canvas.flip();
9350         }
9351 }
9352 }}}
9353
9354 このサンプルを電圧計で計測した実行例が[[cliplife|http://cliplife.jp/clip/?content_id=opt044zr]]にアップロードされています。
9355 出力を直接ヘッドホンにつなぐと音が鳴りますが、解像度が150 Hzしかないので精度には期待しないほうがいいでしょう。
9356 </pre>
9357 </div>
9358 <div title="Tips: ビープを出す" modifier="YourName" modified="200908100433" created="200802201253" changecount="4">
9359 <pre>精度をまったく気にせず単に音がなればいい場合、システムのビープ音を利用する方法があります。
9360
9361
9362 Windowsの場合、以下の命令でPCのビープ音を発生させます。ただし、PCによっては機械ビープが省略されているものがあります。
9363 {{{
9364   Beep(int 持続時間, int 周波数);
9365 }}}
9366
9367
9368 Mac OS Xの場合、以下の命令でOSの警告音を発生させることができます
9369 {{{
9370   SysBeep(int 持続時間);
9371 }}}
9372 この命令にはいくつかの問題があります
9373 * Apple社より、この命令は将来的に廃止されることが予告されています。
9374 * OSの警告音が1度なるだけで、持続時間の指定にはまったく効果はありません。
9375
9376
9377 将来的には、Psychlopsで機種非依存の命令を追加する予定です。
9378 </pre>
9379 </div>
9380 <div title="Tips: プログラムが暴走したとき" modifier="PsychlopsAdmin" created="200709171720" changecount="1">
9381 <pre>プログラムの実行中に強制的に終了したい場合があります。このような場合はOSの強制終了コマンドを使ってください。
9382
9383 * MacOSX
9384 ** ESC + Option + Command(花記号)キーを同時に押す
9385 * Windows
9386 ** Alt + F4キーを同時に押す
9387 ** 上記方法で失敗した場合、Ctrl + Alt + Delを同時に押して、タスクマネージャから強制終了する</pre>
9388 </div>
9389 <div title="Tips: プログラムを複数のファイルに分割する" modifier="YourName" modified="200803140500" created="200803110823" changecount="4">
9390 <pre>実験間で共通の処理がある場合、管理を容易にするためにその部分のプログラムを別ファイルに分けて使いまわすことができます。
9391
9392 ! 既存のファイルの追加
9393 既存のファイルをプロジェクトに追加する場合は、以下の操作を行います。
9394
9395 !! Xcodeの場合
9396
9397 # 「ループとファイル」の「Sources」を右クリックし、「追加」「既存のファイル」と選ぶ
9398 # 開いたウィンドウでファイルを選択し、追加する
9399
9400 [img[image/osx/Xcode_addFiles.png]]
9401
9402 !! Reloの場合
9403
9404 # プロジェクトのファイル一覧を右クリックし、「Add Files」を押す
9405 # 開いたウィンドウでファイルを選択し、追加する
9406
9407 [img[image/win/Relo_addFiles.png]]
9408
9409 ! 新規追加
9410 新規にファイルを作ってプロジェクトに追加する場合は、以下の操作を行います。
9411
9412 !! Xcodeの場合
9413
9414 # 「ループとファイル」の「Sources」を右クリックし、「追加」「新規ファイル」と選ぶ
9415 # ファイル名等を決めて新規ファイルを作成
9416
9417 !! Reloの場合
9418
9419 # プロジェクトのファイル一覧を右クリックし、「New Souce File」を押す
9420 # 新しいタブ「untitled」が開かれているので、保存ボタンを押し、名前と保存場所を決める
9421
9422 </pre>
9423 </div>
9424 <div title="Tips: ランダムドットステレオグラムの描画コード" modifier="Psychlops_Admin" created="200709130536" changecount="1">
9425 <pre>下の例ではダイナミックランダムドットステレオグラムを描画します。
9426 スペースキーを押すと、ポーズがかかります。
9427 リターンキーを押すと、対応領域の可視化モードが切り替わります。
9428 エスケープキーを押すと、プログラムが終了します。
9429
9430 {{{
9431 #include &lt;psychlops.h&gt;
9432 using namespace Psychlops;
9433
9434 void psychlops_main()
9435 {
9436
9437         Canvas display(Canvas::fullscreen);
9438         const int BUFFER = 10;
9439         const int whole_size = 200;
9440         const int stereo_size = 100;
9441         double disprity = 10.0;
9442         bool pause = false;
9443         int dotsurface = 0;
9444         int SOA = 5;
9445
9446         Image base[2][BUFFER], stereo[2][BUFFER];
9447         for(int i=0; i&lt;BUFFER; i++) {
9448                 for(int j=0; j&lt;2; j++) {
9449                         base[j][i].set(whole_size, whole_size);
9450                         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())); }}
9451                 }
9452                 stereo[0][i].set(stereo_size, stereo_size);
9453                 stereo[1][i].set(stereo_size, stereo_size);
9454                         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)); }}
9455                         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())); }}
9456         }
9457
9458         int loop = 0;
9459         while(!Input::get(Keyboard::esc)) {
9460                 if(Input::get(Keyboard::spc)) pause = !pause;
9461                 if(Input::get(Keyboard::rtn)) dotsurface = 1-dotsurface;
9462                 if(!pause){
9463                     loop++;
9464                          loop %= BUFFER;
9465                         }
9466                 Display::clear();
9467                         base[0][loop].centering().shift(-whole_size, 0).display();
9468                         stereo[dotsurface][loop].centering().shift(-whole_size, 0).display();
9469                         base[1][loop].centering().shift(whole_size, 0).display();
9470                         stereo[dotsurface][loop].centering().shift(whole_size+disprity, 0).display();
9471                 Display::msg(&quot;Press space key to pause.&quot;, 400, 100);
9472                 Display::msg(&quot;Press return key to switch visualize mode.&quot;, 400, 120);
9473                 Display::msg(&quot;Press esc key to exit.&quot;, 400, 140);
9474                 Display::flip(SOA);
9475         }
9476 }
9477
9478
9479 }}}</pre>
9480 </div>
9481 <div title="Tips: 時間精度が必要なプログラムを実行するとき" modifier="PsychlopsAdmin" created="200709171719" changecount="1">
9482 <pre>現在のコンピュータは同時に多数のソフトが起動しており、その計算に処理時間をとられてフレーム落ちする場合があります。これを回避するにはいくつかの方法があります。
9483
9484 * いったん再起動する(余計なソフトウェアを強制終了させることができます)
9485 * AppState::setThreadPriority( AppState::HIGH );関数を実行する(今後改名するかもしれません)
9486
9487 それでもコマ落ちする場合は、
9488 * 計算量の少ない等価な計算を行う
9489 ** forやwhile内で不必要な繰り返し計算を除去する
9490 ** pixの配列を書き込む計算の場合、for文をやめてCanvas.pix(ドット数, 配列)の呼び出しを行う
9491 * Imageが原因ならquicken()の使用を検討する
9492 * より処理速度の速いコンピュータを使用する
9493 などの方法が考えられます。</pre>
9494 </div>
9495 <div title="Tips: 機器間の同期" modifier="YourName" modified="200802201725" created="200802201301" changecount="4">
9496 <pre>現代のコンピュータはOSやハードウェア規格のレベルで機器間の同期を保証できないシステムに置き換えられています。このTipsでは、各機器の同期精度について説明します。
9497
9498 ! 一般論
9499 現代のコンピュータは、互換性の保持、失敗からの回復、暴走の回避を目的として、さまざまなインターフェースが仮想化され、パケット通信化されています。このため、コンピュータのクロックのみに依存した同期確保方法が利用できなくなっています。
9500
9501 ただし時間管理が必要な機器、たとえばCPUのクロック、ビデオカードの垂直同期信号、サウンドカードの連続的音声出力などはそれぞれ独自のクロック信号により精確な時間管理を行うことができます。これらの機器内部で精確なインターバルを得ることはできます。
9502
9503 しかし、機器間で同期を取ることは困難です。視聴覚時間同期といった信号は生成するのが非常に困難です。機器間で同期を取る方法としては、同期を制御する装置をひとつに絞り、その装置の外部にアナログ的な方法で同期装置を設ける方法が推奨されます。この方法は非常に精確です。
9504
9505
9506 ! OS
9507 最近のOS(Windows 2000以降やMac OS X)はプリエンプティブマルチタスク方式と呼ばれる、ひとつのアプリケーションが中央演算装置(CPU)を独占できない方式を採用しています。このため、あるアプリケーションが処理される速度は不定で、周辺機器の割り込みを実時間で拾えるとは限りません。
9508
9509
9510 ! ビデオカード
9511 近年のビデオカードはダブルバッファリングとバッファ切り替えの垂直同期信号との同期を行うことが標準化されており、ドライバの設定によって(Windowsでは画面の設定で指定、Mac OS Xでは標準)でその機能を利用できるようになり、描画途中で描画内容の転送が起こることのないように設計されています。Psychlopsではこれらのビデオボードに指示を行い、各描画フレームの垂直同期信号との同期を実現しています。
9512
9513 しかし、バッファ切り替えの行われた正確な時間を知ることはできません。
9514
9515
9516 ! キーボード、マウス
9517 キーボード、マウスなどHID(Human Interface Devices)は、一般的にはインタラプト転送と呼ばれる、一定間隔で行われる転送を用いて入力されます。これは比較的よい時間精度を持っていますが、一般的なGUIを持つOSでは、ウィンドウの配置を加味してカーネル等から各アプリケーションにプロセス間通信(時間がかかり、同期が維持されない)を用いてHIDイベント(キーを押した、離したなど)の情報が送られます。このため、アプリケーション側がHIDイベントを受け取るタイミングは同期が保証されません。
9518
9519 多くのOSでは、HIDイベントにそれが発生した時間情報が付加されます。アプリケーションが知ることができるHIDの時間情報はそれに制限されます。
9520
9521 HIDイベントに付加された時間情報が十分に正確であるならば、たとえば押し続けた時間は比較的正確に得ることができます。しかし視覚刺激提示からの反応時間をとることは、視覚刺激提示装置とHIDの同期が必要であり、そのままではできません。
9522
9523
9524 ! サウンドカード
9525 一般的なサウンドカードは、同期信号出力に用いるには向いていません。
9526 * 音声信号が途切れないように大き目のバッファを設けているため、数10ms以上のレイテンシがあります。
9527 ** ASIOとよばれる規格に対応したドライバ、サウンドカードをセットで使用すると、低レイテンシでの通信ができることがあります。一般に、5ms以下のレイテンシで動きます。
9528 * 信号の正確性があまり求められていないため、エラー訂正が不十分である場合や、通信失敗時に再送出をあきらめる場合があります。
9529
9530
9531 </pre>
9532 </div>
9533 <div title="Tips: 独自のクラスを作る" modifier="YourName" modified="200803140504" created="200803062209" changecount="6">
9534 <pre>自分で作成した機能(コード)を多数のプロジェクトで使いまわす場合、ユーザ独自の拡張としてクラス化し、Psychlopsのライブラリフォルダ追加すると簡単に利用することができます。
9535
9536 ここでは、例として扇形(パックマン図形)を多角形近似したものをクラス化してみます。このクラスは、以下の動作を行います。
9537 * 扇の半径、開口角、開口方向を指定して、多角形として作成する。
9538 * Rectangleと同じようにshift, centering, drawできるようにする。
9539
9540 クラスの中身はひとまず置いておき、ひとまずこのクラスを拡張として登録してみましょう。テキストエディタ(Windows: メモ帳、MacOSX: Xcode→メニュバー→ファイル→新規ファイル)に下記コードをコピー&ペーストし、「pacman.cpp」という名前をつけて保存します。
9541
9542 {{{
9543 #include &lt;psychlops_lib.h&gt;
9544 using namespace Psychlops;
9545
9546
9547 // Polygonally approximated Pacman(sector) shape
9548 class Pacman {
9549
9550         //      Sector Elements
9551         Psychlops::Point center;
9552         Psychlops::Point *vertices;
9553         int num_vertices;
9554
9555         public:
9556         //      Initialize
9557         Pacman(double radius, double open_angle_radian, double orientation_radian) {
9558                 int end = floor(radius * (1 - Math::mod(open_angle_radian, 2*PI)/(2*PI)));
9559                 num_vertices = end+2;
9560                 vertices = new Psychlops::Point[num_vertices];
9561
9562                 double offset = Math::mod(orientation_radian+open_angle_radian/2.0, 2*PI);
9563                 vertices[0].set(0,0);
9564                 for(int i=0; i&lt;end; i++) {
9565                         vertices[i+1].set(
9566                                 radius*cos(offset+2.0*((double)i/radius)*PI),
9567                                 radius*sin(offset+2.0*((double)i/radius)*PI)
9568                         );
9569                 }
9570                 vertices[num_vertices-1].set(
9571                                 radius*cos(offset+2*PI-open_angle_radian),
9572                                 radius*sin(offset+2*PI-open_angle_radian)
9573                         );
9574         }
9575         ~Pacman() {
9576                 delete [] vertices;
9577         }
9578
9579         //      Other Functions
9580         Pacman&amp; shift(double x, double y) {
9581                 center.set(x, y);
9582                 for(int i=0; i&lt;num_vertices; i++) vertices[i].shift(x,y);
9583                 return *this;
9584         }
9585         Pacman&amp; centering() {
9586                 shift(Display::getHcenter()-center.getX(), Display::getVcenter()-center.getY());
9587                 return *this;
9588         }
9589         Pacman&amp; draw(Color col) {
9590                 Display::polygon(vertices, num_vertices, col);
9591                 return *this;
9592         }
9593 };
9594 }}}
9595
9596
9597 次に、pacman.cppをPsychlops拡張フォルダ(以下のフォルダの中の適当な場所)にコピーします。この際、MacOSXやWindows Vistaでは管理者パスワードを求められることがあります。
9598
9599 * C:\Library\Frameworks\Psychlops.framework\Headers\Extentions
9600 * /Library/Frameworks/Psychlops.framework/Headers/Extentions
9601
9602 ここでは「Samples」というフォルダをつくり、ここに置いてみます。
9603
9604 次に、呼び出し側コードで今回作った独自拡張を#incluide指令を使って呼び出します。#include指令のオプションには、/Library/Frameworks/Psychlops.framework/Headers/より後の部分のファイルパスを指定します。
9605 {{{
9606  #include &lt;Extentions/Samples/pacman.cpp&gt;
9607 }}}
9608
9609
9610 {{{
9611 #include &lt;psychlops.h&gt;
9612 using namespace Psychlops;
9613 #include &lt;Extentions/Samples/pacman.cpp&gt;
9614
9615 void psychlops_main() {
9616
9617         Canvas display(Canvas::fullscreen);
9618         Pacman pacman(100, PI/2, 0); // radius: 100 px, opening: 90 deg, orientation: 0 deg (right)
9619         pacman.centering();
9620
9621         while(!Input::get(Keyboard::esc)) {
9622                 display.clear();
9623                 pacman.draw(Color::red);
9624                 display.flip();
9625         }
9626
9627 }
9628 }}}
9629
9630
9631  #includeでPacman拡張を呼び出すことで、いつでもPacman図形を使用することができるようになりました。
9632
9633
9634
9635 !! 上級者向け:ソース配布時の注意
9636 Extentionとして別ファイルに分離したものを配布する際は、必要なすべてのファイルを配布する必要があります。もしExtentionフォルダに入れることが前提のファイルを配布するのであれば、その旨を添付テキスト等に記述し、登録するよう指示しましょう。拡張ではない通常の複数ファイル使用の場合は、プロジェクトへの追加が必要な旨を書きましょう。
9637
9638
9639 !! 上級者向け:複数ファイルから拡張を呼び出す場合
9640 この解説は、ユーザ側のプログラムが単一のファイルになるという初心者向けの状況を想定して書かれています。単一バイナリを生成するのに複数のファイルを使い、それらが同時にひとつの拡張を#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]]を参照ください。
9641
9642
9643 !! 上級者向け:クラス設計の解説
9644
9645 クラスを作る際には、
9646 * 保持するデータ
9647 * 自動的な初期化処理と廃棄処理
9648 * 各種命令(メソッド)
9649 の3つを用意します。
9650 簡単な解説については、下記サイトが参考になります。
9651 [[C++入門:クラス|http://wisdom.sakura.ne.jp/programming/cpp/cpp5.html]]
9652
9653 また、psychlops.hの中には真のmain / WinMain関数が入っているため、これを2回以上includeするとコンパイラは本体にも拡張側にもmain関数があるかのようにみなし、衝突してしまいます。これを防ぐために、拡張側ではpsychlops_lib.hをインクルードしてください。
9654
9655 {{{
9656  #include &lt;psychlops_lib.h&gt;
9657 }}}
9658
9659 </pre>
9660 </div>
9661 <div title="Tips: 画像のフィルタリング処理" modifier="YourName" modified="200803140632" created="200803100447" changecount="27">
9662 <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拡張化して利用してみます。
9663
9664
9665 * FFTWはGPLと有料ライセンスの選択制です。GPLを選択される場合、GPLの通称「感染条項」によりこのライブラリを使用したブログラムはすべてGPLとしなければなりません。このプログラムの配布に当たっては、下記の添付ファイルを必ず添付し、パッケージ作成に必要なソースコードをすべて公開しなければなりません。
9666
9667
9668 ! FFTWのPsychlops拡張のインストール
9669
9670 !! Mac OS X向け
9671
9672 # [[FFTW_demo_osx.zip|http://psychlops.l.u-tokyo.ac.jp/download/BasicSamples/FFTW_demo_osx.zip]]をダウンロードし、解答します。
9673 # 「~FFTW-Installer」をダブルクリックして拡張ををインストールします(要管理者権限)。
9674 #* インストール時にはTerminalが開き、ユーザのパスワードを聞かれますので、入力してください。
9675
9676 次に、拡張を使ったプロジェクトを作成します。
9677 # 新しくプロジェクトを作成します。
9678 # 「External Frameworks and Libraries」を右クリックし、「追加」→「既存のファイルの追加」をクリックします
9679 ## ファイル選択ウィンドウが開いたら、右上の検索ボックスで以下のパスを''手打ちしてください''(コピー&amp;ペースト不可)
9680 ##* /usr/local/lib/
9681 ## 開いたフォルダで以下のファイルを選択してください。リンクするライブラリ一覧に追加されます。
9682 ##* libfftw3.a
9683 # デモを実行する場合、~FilterDemo.cppの内容をコピー&ペーストします
9684 # ビルドして実行してみましょう。
9685 [img[Image/OSX/Xcode_addLib1.png]]
9686 [img[Image/OSX/Xcode_addLib2.png]]
9687
9688 !! Windows向け
9689
9690 # [[FFTW_demo_win.zip|http://psychlops.l.u-tokyo.ac.jp/download/BasicSamples/FFTW_demo_win.zip]]をダウンロードし、解凍します。
9691 # 「FFTWのPsychlops拡張インストーラ.bat」を使って拡張ををインストールします(要管理者権限)。
9692
9693 次に、拡張を使ったプロジェクトを作成します。
9694 # 新しくプロジェクトを作成します。
9695 # いったんテンプレートをビルドします。
9696 # 作成された実行ファイルと同じフォルダに「libfftw3-3.dll」をコピーします
9697 # プロジェクトのオプションを開き、リンクするライブラリに「libfftw3-3_bcc.lib」を加えます(Codeblocks+gccであれば「libfftw3-3.def」を加えます)
9698 # デモを実行する場合、~FilterDemo.cppの内容をコピー&ペーストします
9699 # ビルドして実行してみましょう。
9700 [img[Image/Win/Relo_addLib_1.png]]
9701 [img[Image/Win/Relo_addLib_2.png]]
9702
9703
9704 ! 使用法
9705
9706 このサンプルでは、FFTWを用いてローパスフィルタを作成するための関数群が用意されています。配列の大きさが奇数の配列に対しては適用しないでください。
9707
9708 !関数一覧
9709
9710 !! フィルタリング関数
9711
9712 |!Matrix makeLowPassFilter()|makeLowPassFilter(int height, int width, double frequency)|高さheight, 幅width, カットオフ周波数がfrequencyのローパスフィルターとなるMatrixを作成します。|
9713 |~|~|int height: ローパスフィルタの高さ|
9714 |~|~|int width: ローパスフィルタの幅|
9715 |~|~|double frequency: カットオフ周波数|
9716
9717 内部では、カットオフより内側が1、カットオフより外側が0の行列を作成します。
9718
9719
9720 |!Matrix filterImage()|filterImage(Matrix image, Matrix filter)|imageに対してfilterで指定した行列を元にフィルタリングを適用します。|
9721 |~|~|Matrix image: フィルタをかける元画像|
9722 |~|~|Matrix filter: 適用するフィルタ|
9723
9724 内部では、
9725 # imageにFFTをかけ、周波数表現を作る
9726 # 周波数表現でフィルタを要素ごとに乗算する
9727 # フィルタリング後の周波数表現を逆FFTする
9728 という作業が行われています。
9729
9730 [img[image/tips/sample_FFTW.png]]
9731
9732
9733 !! FFT・逆FFT
9734
9735 2次元のFFTを実行します。ただし、fft2やifft2で使う配列は行および列が偶数である必要があります。奇数の場合は正常に動作しない可能性があります。
9736
9737 fft2を用いてFFTを行った後の行列は中心に直流成分が存在し、周辺部に高周波数成分が存在します。行列の大きさがMatrix(rows, cols)の場合、直流成分はMatrix(rows / 2 + 1, cols / 2 + 1)に存在します。
9738
9739 周波数分解能Δf(Matrixの要素が1つずれた場合にどれだけ周波数が変化するか)は、通常の視覚実験においてΔfは1 / 画像の大きさ(視角)となります。
9740
9741 FFTにおけるΔfはサンプリング周波数 / データ数です。視覚実験においてはサンプリング周波数は視角1度が何ピクセルかに相当します。データ数は画像の高さもしくは幅のピクセル数となります。画像の高さや幅(ピクセル数)はサンプリング周波数に画像の高さや幅(視角)をかけたものに等しくなります。このことから通常の視覚実験においてΔfは1 / 画像の大きさ(視角)となります。
9742
9743 |!void fft2()|fft2(Matrix src, Matrix dstReal, Matrix dstImaginary)|2次元のフーリエ変換を実行します。|
9744 |~|~|Matrix src:フーリエ変換を適用する配列。|
9745 |~|~|Matrix dstReal:フーリエ変換後の配列の実部が代入されます。|
9746 |~|~|Matrix dstImaginary:フーリエ変換後の配列の虚部が代入されます。|
9747 |!void fft2()|fft2(Matrix srcReal, Matrix srcImaginary, Matrix dstReal, Matrix dstImaginary)|2次元のフーリエ変換を実行します。|
9748 |~|~|Matrix srcReal:フーリエ変換を適用する配列の実部。|
9749 |~|~|Matrix srcImaginary:フーリエ変換を適用する配列の虚部。|
9750 |~|~|Matrix dstReal:フーリエ変換後の配列の実部が代入されます。|
9751 |~|~|Matrix dstImaginary:フーリエ変換後の配列の虚部が代入されます。|
9752
9753 実行後の配列は中心部に低周波数領域があります。
9754
9755 |!void ifft2()|ifft2(Matrix srcReal, const Matrix srcImaginary, Matrix dst)|2次元の逆フーリエ変換を実行します。|
9756 |~|~|Matrix srcReal:逆フーリエ変換を適用する配列の実部。|
9757 |~|~|Matrix srcImaginary:逆フーリエ変換後の配列の虚部。|
9758 |~|~|Matrix dst:逆フーリエ変換後の配列の絶対値が代入されます。|
9759 |!void ifft2()|ifft2(Matrix srcReal, Matrix srcImaginary, Matrix dstReal, Matrix dstImaginary)|2次元の逆フーリエ変換を実行します。|
9760 |~|~|Matrix srcReal:逆フーリエ変換を適用する配列の実部。|
9761 |~|~|Matrix srcImaginary:逆フーリエ変換後の配列の虚部。|
9762 |~|~|Matrix dstReal:逆フーリエ変換後の配列の実部が代入されます。|
9763 |~|~|Matrix dstImaginary:逆フーリエ変換後の配列の虚が代入されます。|
9764
9765
9766 !! 調整用関数
9767
9768 |!Matrix normalize()|normalize(Matrix matrix)|行列の各要素の平均値が0、標準偏差が1となるよう正規化します。|
9769 |~|~|Matrix matrix: 正規化対象の行列|
9770
9771 フィルター処理をしたあとの画像は[0, 1]の範囲に収まらず、表示可能な領域からはみ出してしまいます。そこで表示可能な範囲に収める処理が必要となります。フィルター処理をした後の画像の輝度値の分布は正規分布に従うことが知られています。そのため、一度正規化をします。normalizeはその正規化を行うための関数です。
9772
9773 正規化を行った後はコントラストをかけ、平均輝度を足します。このときのコントラストはマイケルソンコントラストではなく、RMSコントラストです。RMSコントラストが1/3を超えると表示可能な領域からはみ出してしまう点が多くなるのでなるべくこの値を最大値としてください。
9774
9775 最後に、それでも表示可能な領域が残る可能性は存在しますので、それを表示可能な領域に納めます。
9776
9777
9778 |!Matrix makeNoise()|makeNoise(int height, int width)|高さheight、幅widthのホワイトノイズ行列を作成します。|
9779 |~|~|int height: 縦の大きさ|
9780 |~|~|int width: 横の大きさ|
9781
9782
9783
9784 !! 内部用調整関数
9785
9786 以下の関数はfft2やifft2内部で呼び出される関数です。通常ユーザーがこの関数を実行する必要はありません。
9787
9788 |!Matrix fftShift()|fftShift(const Matrix &amp;matrix)|FFTをかけた後の周波数ごとの分布を移動させます。|
9789 |~|~|Matrix matrix: シフトを行う対象を指定します|
9790 FFTWではFFTを行った後の行列は低周波成分が4隅にあり、中心部には高周波成分が存在します。このままではフィルターを作成する際に計算しづらいので、中心部に低周波数成分を、周辺部に高周波数成分が並ぶようにします。そのための関数です。
9791
9792 逆に中心にある低周波数成分を周辺部に、周辺にある高周波数成分を中心にするように並べ替えることもできます。
9793
9794 |!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)|フーリエ変換を実行します。|
9795 |~|~|Matrix srcReal:フーリエ変換を適用する配列の実部。|
9796 |~|~|Matrix srcImaginary:フーリエ変換を適用する配列の虚部。|
9797 |~|~|Matrix dstReal:フーリエ変換後の配列の実部が代入されます。|
9798 |~|~|Matrix dstImaginary:フーリエ変換後の配列の虚部が代入されます。|
9799 |~|~|fftw_complex *input: FFTW作業用の変数|
9800 |~|~|fftw_complex *output: FFTW作業用の変数|
9801 |~|~|fftw_plan plan: FFTW作業用の変数|
9802 </pre>
9803 </div>
9804 <div title="Tips: 自分の機器の描画性能を確かめる" modifier="YourName" modified="200803062209" created="200803062208" changecount="2">
9805 <pre>Psychlopsの実行速度は環境によって異なります。現在の実行環境での実行速度を知りたい場合は、簡易に計測するプログラムを用意しましたので、これを実行して調べることができます。下記ファイルをダウンロードし、コンパイルして実行してみてください。
9806
9807 [[Benchmark.cpp|http://psychlops.l.u-tokyo.ac.jp/download/BasicSamples/Benchmark.cpp]]
9808
9809 2~5分程度のテストを行い、各種命令の「フレームが落ちずに実行できる最大処理能力」を測ります。結果は終了後に画面に表示されるほか、実行ファイルと同じ場所に結果ファイルを出力します(Mac OS Xの場合、プロジェクトフォルダ/build/Release/)。
9810
9811 現在のバージョンでは4つのテストを行います。各テストの項目のうち、
9812 * 上「今回のテストで使われたリフレッシュレートで、1フレームで表示できる最大数」
9813 * 下「100 Hzで正規化した場合に、1フレームで表示できる最大サイズ」(この値が1000の場合、100 Hz時に1000x1000ピクセルの画像が1枚表示可能ということを示します。)
9814
9815 !!! Pixel test
9816 Canvas.pix命令の処理能力を計測します。
9817 GPUの頂点処理能力と、CPUからGPUに命令を出す帯域幅が主な律速要因になります。
9818
9819 !!! Rectangle test
9820 Canvas.rect命令の処理能力を計測します。
9821 GPUのピクセル処理能力がが主な律速要因になります。
9822
9823 !!! Image test
9824 オフスクリーン(Image.draw)の処理能力を調べます。
9825 メインメモリからグラフィックカードへImageを転送する帯域幅が主な律速要因になります。
9826
9827 !!! VRAM Image test
9828 Image.quicken()されたオフスクリーン画像の処理能力を計測します。
9829 GPUのピクセル処理能力がが主な律速要因になります。
9830
9831
9832 実験を行ううえでの目安としてください。
9833 また、計測結果には10%前後の誤差があります。</pre>
9834 </div>
9835 <div title="Tips::CanvasクラスとDisplayクラス" modifier="Psychlops_Admin" modified="200708230528" created="200708230500" changecount="1">
9836 <pre>PsychlopsではデフォルトのCanvasに対して(インスタンス名を無視して)描画を強制的に行うDisplayクラスが用意されています。
9837 現バージョンでのPsychlopsはCanvasを同時に1つしか確保できない仕様となっているので(つまり宣言したCanvasインスタンスは常にデフォルトになる)、Displayクラスを使って描画を行っても、インスタンス名を使って描画を行った場合と同じ結果が得られます。たとえば、以下の2つのプログラムは同じ結果になります。
9838 {{{
9839 &lt;インスタンス名を使用したソース&gt;
9840 #include &lt;psychlops.h&gt;
9841 using namespace Psychlops;
9842 void psychlops_main() {
9843         Canvas sampleB(Canvas::fullscreen);
9844         sampleB.clear(Color::blue);
9845         sampleB.flip();
9846         while(!Input::get(Keyboard::spc));
9847 }
9848 }}}
9849
9850 {{{
9851 &lt;Displayクラスを使用したソース&gt;
9852 #include &lt;psychlops.h&gt;
9853 using namespace Psychlops;
9854 void psychlops_main() {
9855         Canvas sampleB(Canvas::fullscreen);
9856         Display::clear(Color::blue);
9857         Display::flip();
9858         while(!Input::get(Keyboard::spc));
9859 }
9860 }}}
9861
9862 また、psychlops_main外の関数で描画を行うときには、Canvasのポインタを渡す必要のないDisplayクラスを使った方法のほうが便利かもしれません。下の例を比べて見てください。
9863
9864 {{{
9865 &lt;インスタンス名を使用したソース&gt;
9866 #include &lt;psychlops.h&gt;
9867 using namespace Psychlops;
9868
9869 void stimulusDraw(Canvas &amp;sample){
9870         sample.clear(Color::blue);
9871         sample.flip();
9872         while(!Input::get(Keyboard::spc));
9873 }
9874
9875 void psychlops_main() {
9876         Canvas sampleB(Canvas::fullscreen);
9877         stimulusDraw(sampleB);
9878 }
9879
9880 }}}
9881
9882 {{{
9883 &lt;Displayクラスを使用したソース&gt;
9884 #include &lt;psychlops.h&gt;
9885 using namespace Psychlops;
9886 void stimulusDraw(){
9887         Display::clear(Color::blue);
9888         Display::flip();
9889         while(!Input::get(Keyboard::spc));
9890 }
9891 void psychlops_main() {
9892         Canvas sampleB(Canvas::fullscreen);
9893         stimulusDraw();
9894 }
9895 }}}
9896
9897
9898 </pre>
9899 </div>
9900 <div title="Update from 1.0 to 1.3" modifier="Psychlops_DevelopperG" modified="200912010831" created="200908100506" changecount="18">
9901 <pre>* 2.1.1節のCanvasの宣言をマルチスクリーンに対応したバージョンに書き換え
9902
9903 [[2.3 文字列の描画]]
9904 * [[2.3.1 Canvas::msg()命令単体での文字列描画]]
9905 * [[2.3.2 Lettersクラスを用いた文字列描画]]
9906 * [[3.5 文字列のレンダリングの仕様と関連情報]]
9907
9908 * 3.3.1節にCanvas::toの注意事項とImage::release()についての説明を追加。
9909
9910 [[3.3.3 複数の画像ファイルからアニメーションを作成する]]
9911
9912 [[4. 複雑な描画を行う2(複数オブジェクトのグループ化と回転・拡大縮小)]]
9913 [[8. 透明度を使って刺激描画を高速化する]]
9914 [[9. アナログ入出力を行う]]
9915
9916 ! [[Tips]]
9917 * [[Tips: Shapeクラスの概要]]
9918 *  [[Tips: Strokeクラスの概要]]
9919 * [[Tips: Canvasを宣言しないと動作しない命令群]]
9920 * [[Tips: OS環境による関数名の衝突について]]
9921 * [[Tips: ガンマ補正]]
9922 setGammaTable(std::vector&lt;double&gt; Cr, std::vector&lt;double&gt; Cg, std::vector&lt;double&gt; Cb)のコード例
9923 ![[関数一覧]]
9924 * [[Canvasの宣言]]
9925 * [[Image::to()]]
9926 * [[Displayの指定値]]
9927 * [[CanvasMode]]
9928
9929 ! [[関数逆引きとQ&amp;A]]
9930
9931 * [[Canvas::pix(double, double, double)という命令はコンパイルできないのですか?]]
9932 * [[止まった絵を描画するときCanvas.flip()を単独でwhileループの中に書くと、画面がちらつきます。]]
9933 * [[特定の命令に対してコンパイル時にエラーが出る]]
9934
9935
9936 </pre>
9937 </div>
9938 <div title="memo" modifier="Psychlops_Admin" modified="200708280427" created="200708280335" changecount="2">
9939 <pre>/***
9940 CSSカスタマイズ
9941 ***/
9942 body {
9943         background: #f0efc0;
9944 }
9945
9946 #mainMenu{
9947         background: #f0efc0;
9948         border: 2px dotted #6a6;
9949 }
9950
9951 /*{{{*/
9952 h1,h2,h3,h4,h5 {
9953         padding-top: 0.3em;
9954 }
9955
9956 .headerShadow {
9957         padding: 2.0em 0em 0.7em 1em;
9958 }
9959
9960 .headerForeground {
9961         padding: 2.0em 0em 0.7em 1em;
9962 }
9963
9964
9965 .siteTitle {
9966         font-size: 3.0em;
9967 }
9968
9969 .siteSubtitle {
9970         font-size: 1.2em;
9971 }
9972
9973 .subtitle {
9974         font-size: 0.9em;
9975 }
9976 /*}}}*/
9977
9978 /*{{{*/
9979
9980
9981 h1,h2,h3,h4,h5 {
9982         color: #fff;
9983         background: #4a7;
9984 }
9985
9986 .button {
9987         border: 0px;
9988 }
9989
9990 .header {
9991         background: #084;
9992 }
9993
9994 .shadow .title {
9995         color: #fff;
9996 }
9997
9998 .title {
9999         color: #fff;
10000         background: #084;
10001 }
10002
10003 .viewer th {
10004         background: #996;
10005 }
10006
10007 .viewer pre {
10008         border: 1px solid #6a6;
10009         background: #cfc;
10010         font-size:0.9em
10011 }
10012
10013 .viewer thead td {
10014         background: #8d8;
10015 }
10016 /*}}}*/
10017
10018 /*{{{*/
10019 .viewer thead td {
10020         background: #8d8;
10021 }
10022
10023 .viewer tfoot td {
10024         background: #bd6;
10025 }
10026
10027 .MyStyle {
10028         background: #aff;
10029 }
10030 /*}}}*/
10031
10032
10033
10034 body{
10035  margin: 0 auto;
10036  padding:0;
10037  width: 100%;
10038  font: 80% arial,sans-serif;
10039 }
10040
10041 #mainMenu {
10042  position: absolute;
10043  width: 16%;
10044  text-align:left;
10045  padding: 0em 1em 0em 1em;
10046
10047  background-color: #eee;
10048  color:#cccccc;
10049 font-size: .9em;
10050  }
10051
10052 #sidebar {
10053  position: absolute;
10054  right: 10px;
10055  width: 10%;
10056  font-size: .9em;
10057  }
10058
10059 .headerShadow,
10060 .headerForeground {
10061  padding: 1em;
10062  }
10063
10064 .viewer blockquote {
10065  font-size: 8pt;
10066  line-height: 150%;
10067  border-left: 3px solid #666666;
10068  padding-left: 1.0em;
10069  margin-left: 2.5em;
10070 }
10071
10072 .siteTitle {
10073  font-size: 24pt;
10074 }
10075 .siteSubtitle {
10076  font-size: 12pt;
10077 }
10078
10079 .title {
10080  color:#55221f;
10081  font-size: 1.5 em;
10082  padding-left:0.1em;
10083
10084  margin-bottom:10px;
10085  margin-top:4px;
10086
10087  border-bottom:2px solid #ccc;
10088 }
10089 </pre>
10090 </div>
10091 <div title="wiki文法" modifier="Psychlops_DevelopperG" modified="200912010832" created="200707220320" changecount="6">
10092 <pre>http://hsj.jp/junknews/archives/tiddlywiki_susume.html
10093
10094 図の引用
10095 {{{
10096 [img[image/canvasrect.png]]
10097 }}}</pre>
10098 </div>
10099 <div title="このマニュアルの見方について(ここをクリックしてください)" modifier="PsychlopsAdmin" created="200709171623" changecount="1">
10100 <pre>このマニュアルはTiddelyWikiによって作成されています。
10101 今、このウィンドウが開いたように、見出しをダブルクリックすると、該当する記事のウィンドウが自動的に開きます。必要なくなった(見終わった)ウィンドウは、見出しの左上のコマンドから&quot;close&quot;をクリックすれば閉じることができます。
10102 Editを押すと内容を改変して、ご自分でコメントをメモすることができますが、改変した部分は元には戻せないのでご注意ください。
10103 また、画面左端にメニューバーから各記事にアクセスしたり記事の内容を検索することもできます。</pre>
10104 </div>
10105 <div title="その他" modifier="Kazushi Maruya" modified="200712180151" created="200712131752" changecount="4">
10106 <pre>* [[Psychlopsについて開発者に質問するには?]]
10107 * [[素晴らしいデモプログラムを作成したので、公開したい]]
10108 * [[Psychlopsを既存・自作のC/C++ライブラリと併用することができますか?]]</pre>
10109 </div>
10110 <div title="カラーモード" modifier="Psychlops_Admin" created="200709041935" changecount="1">
10111 <pre>|GRAY|グレースケールの輝度値|
10112 |RGB|色の三原色に基づいた赤・緑・青で構成された色|
10113 |RGBA|RGBに透明度が追加された色|
10114 詳しくは[[2.2.5 描画する図形の色を指定する-Colorクラス-]]を参照</pre>
10115 </div>
10116 <div title="ガンマ補正" modifier="YourName" modified="200802130705" created="200802130649" changecount="4">
10117 <pre></pre>
10118 </div>
10119 <div title="キーコード表" modifier="Psychlops_DevelopperG" modified="200908100437" created="200708240330" changecount="3">
10120 <pre>!キーボード
10121
10122 |アルファベットキー (a~z)|a~zのまま(小文字)|
10123
10124 |キーボード最上段(1~10) |1|2|3|4|5|6|7|8|9|0|
10125 |~|one|two|three|four|five|six|seven|eight|nine|zero|
10126
10127 |特殊キー|Return|Space|Escape|カーソル上|カーソル下|カーソル左|カーソル右|Shift|Control|Alt|
10128 |~| rtn | spc | esc | up | down | left | right | shift | ctrl | alt |
10129
10130 |テンキー|0|1|2|3|4|5|6|7|8|9|
10131 |~|pad0|pad1|pad2|pad3|pad4|pad5|pad6|pad7|pad8|pad9|
10132 |~|ー|&gt;|+|&gt;|*|&gt;|/|&gt;|Enter|&gt;|.|
10133 |~|padminus|&gt;|padpuls|&gt;|padasterisk|&gt;|padslash|&gt;|padenter|&gt;|padperiod|&gt;|
10134
10135 !マウス
10136 |左ボタン|右ボタン|中ボタン|すべてのボタンのいずれか|
10137 | left | right | middle | any |
10138 </pre>
10139 </div>
10140 <div title="例1: pushedを使ったソース" modifier="Psychlops_Admin" modified="200708272051" created="200708272048" changecount="2">
10141 <pre>{{{
10142 *例1: pushedを使ったソース
10143 #include &lt;psychlops.h&gt;
10144 using namespace Psychlops;
10145
10146 int code, oldcode;
10147 Psychlops::Rectangle rect(60,60), rect2(60,60);
10148
10149 void psychlops_main() {
10150         
10151         Canvas display(Canvas::fullscreen);
10152         display.clear(Color::gray);
10153         rect.centering();
10154         
10155         Input::refresh();
10156         
10157         while(!Input::get(Keyboard::spc)){
10158                 oldcode=code;
10159                 code=-1;
10160                 display.clear(Color::gray);
10161                 
10162                 if(Input::get(Keyboard::c, Keyboard::pushed))code=0; //centering
10163                 if(Input::get(Keyboard::left, Keyboard::pushed))code=1;
10164                 else if(Input::get(Keyboard::right, Keyboard::pushed))code=2;
10165
10166                 switch(code){
10167                         case -1: break;
10168                         case 0: rect.centering(); break;
10169                         case 1: rect.shift(-1,0); break;
10170                         case 2: rect.shift(1,0); break;
10171                 }
10172                         
10173                 rect2.centering().draw(Psychlops::Color(0.75,0.75,0.75)); 
10174                 rect.draw(Psychlops::Color(1.0,1.0,1.0,0.25));
10175                 display.flip();
10176         }
10177 }
10178 }}}
10179 </pre>
10180 </div>
10181 <div title="例2: pressedを使ったソース" modifier="Psychlops_Admin" modified="200708272052" created="200708272051" changecount="2">
10182 <pre>{{{
10183 *例2: pressedを使ったソース
10184 #include &lt;psychlops.h&gt;
10185 using namespace Psychlops;      
10186
10187 int code, oldcode;
10188 Psychlops::Rectangle rect(60,60), rect2(60,60);
10189
10190 void psychlops_main() {
10191         
10192         Canvas display(Canvas::fullscreen);
10193         display.clear(Color::gray);
10194         rect.centering();
10195         
10196         Input::refresh();
10197         
10198         while(!Input::get(Keyboard::spc)){
10199                 oldcode=code;
10200                 code=-1;
10201                 display.clear(Color::gray);
10202                 
10203                 if(Input::get(Keyboard::c, Keyboard::pushed))code=0; //centering
10204                 if(Input::get(Keyboard::left, Keyboard::pressed))code=1;
10205                 else if(Input::get(Keyboard::right, Keyboard::pressed))code=2;
10206
10207                 switch(code){
10208                         case -1: break;
10209                         case 0: rect.centering(); break;
10210                         case 1: rect.shift(-1,0); break;
10211                         case 2: rect.shift(1,0); break;
10212                 }
10213                         
10214                 rect2.centering().draw(Psychlops::Color(0.75,0.75,0.75)); 
10215                 rect.draw(Psychlops::Color(1.0,1.0,1.0,0.25));
10216                 display.flip();
10217         }
10218 }
10219 }}}</pre>
10220 </div>
10221 <div title="例3: releasedを使ったソース" modifier="Psychlops_Admin" created="200708272053" changecount="1">
10222 <pre>{{{
10223 *例3: releasedを使ったソース
10224 #include &lt;psychlops.h&gt;
10225 using namespace Psychlops;
10226
10227 int code, oldcode;
10228 Psychlops::Rectangle rect(60,60), rect2(60,60);
10229
10230 void psychlops_main() {
10231         
10232         Canvas display(Canvas::fullscreen);
10233         display.clear(Color::gray);
10234         rect.centering();
10235         
10236         Input::refresh();
10237         
10238         while(!Input::get(Keyboard::spc)){
10239                 oldcode=code;
10240                 code=-1;
10241                 display.clear(Color::gray);
10242                 
10243                 if(Input::get(Keyboard::c, Keyboard::pushed))code=0; //centering
10244                 
10245                 //state &quot;pressed&quot; and &quot;released&quot; Demo
10246                 if(Input::get(Keyboard::left, Keyboard::pressed))code=1;
10247                 else if(Input::get(Keyboard::right, Keyboard::pressed))code=2;
10248                    
10249                 if(Input::get(Keyboard::left, Keyboard::released))code=3;
10250                 else if(Input::get(Keyboard::right, Keyboard::released))code=3;
10251                 
10252                 switch(code){
10253                         case -1: break;
10254                         case 0: rect.centering(); break;
10255                         case 1: rect.shift(-1,0); break;
10256                         case 2: rect.shift(1,0); break;
10257                         case 3: rect.centering(); break;
10258                 }
10259                 
10260                 rect2.centering().draw(Psychlops::Color(0.75,0.75,0.75)); 
10261                 rect.draw(Psychlops::Color(1.0,1.0,1.0,0.25));
10262                 display.flip();
10263         }
10264 }
10265 }}}</pre>
10266 </div>
10267 <div title="例4: pushed,pressed,releasedを使ったソース" modifier="Psychlops_Admin" modified="200708272058" created="200708272055" changecount="1">
10268 <pre>{{{
10269 *例4 KeyStateの使用例
10270 #include &lt;psychlops.h&gt;
10271 using namespace Psychlops;      
10272
10273 double x=0,y=0, dX,dY;
10274 int code, oldcode;
10275 Psychlops::Rectangle rect(60,60), rect2(60,60);
10276
10277 void psychlops_main() {
10278         
10279         Canvas display(Canvas::fullscreen);
10280         display.clear(Color::gray);
10281         rect.centering();
10282         dX= display.getHcenter();
10283         dY= display.getVcenter();
10284         x=dX;
10285         y=dY;
10286         
10287         Input::refresh();
10288         
10289         while(!Input::get(Keyboard::spc)){
10290                 oldcode=code;
10291                 code=-1;
10292                 
10293                 //state &quot;pushed&quot; Demo
10294                 if(Input::get(Keyboard::c, Keyboard::pushed))code=0; //centering
10295                 
10296                 if(Input::get(Keyboard::a, Keyboard::pushed))code=1;
10297                 else if(Input::get(Keyboard::s, Keyboard::pushed))code=2;
10298         
10299                 //state &quot;pressed&quot; Demo
10300                 if(Input::get(Keyboard::z, Keyboard::pressed))code=3;
10301                 else if(Input::get(Keyboard::x, Keyboard::pressed))code=4;
10302                 
10303                 //state &quot;pressed&quot; and &quot;released&quot; Demo
10304                 if(Input::get(Keyboard::q, Keyboard::pressed)){
10305                         code=5;
10306                    if(oldcode==5||oldcode==7)code=7;
10307                 }
10308                 else if(Input::get(Keyboard::w, Keyboard::pressed)){
10309                    code=6;
10310                    if(oldcode==6||oldcode==8)code=8;
10311                 }
10312                 if(Input::get(Keyboard::q, Keyboard::released))code=9;
10313                 else if(Input::get(Keyboard::w, Keyboard::released))code=9;
10314                 
10315                 switch(code){
10316                         case -1: break;
10317                         case 0: rect.centering(); break;
10318                         case 1: rect.shift(-1,0); break;
10319                         case 2: rect.shift(1,0); break;
10320                         case 3: rect.shift(-1,0); break;
10321                         case 4: rect.shift(1,0); break;
10322                         case 5: x=ceil(rect.getHcenter()); y=ceil(rect.getVcenter()); rect.shift(-1,0);break;
10323                         case 6: x=ceil(rect.getHcenter()); y=ceil(rect.getVcenter()); rect.shift(1,0);break;
10324                         case 7: rect.shift(-1,0); break;
10325                         case 8: rect.shift(1,0); break;
10326                         case 9: rect.centering(x,y); break;
10327                 }
10328                 display.clear(Color::gray);
10329                 
10330                 //Show variables 
10331                 display.message(&quot;KeyState:&quot;, dX-100, dY-120, Color::green);
10332                 display.var(code, dX,dY-120, Color::green);
10333                 display.message(&quot;X:&quot;, dX-100, dY-100, Color::white); 
10334                 display.var(ceil(rect.getHcenter()),dX,dY-100, Color::white);
10335                 display.message(&quot;Y:&quot;, dX-100, dY-80, Color::white);
10336                 display.var(ceil(rect.getVcenter()),dX,dY-80, Color::white);
10337                 
10338                 if(code&gt;5){ 
10339                                         rect2.centering(x,y).draw(Psychlops::Color(0.75,0.75,0.75)); 
10340                                         rect.draw(Psychlops::Color(1.0,1.0,1.0,0.25));
10341                         }
10342                 else rect.draw(1.0);
10343                 display.flip();
10344         }
10345 }
10346 }}}
10347 このプログラム内で使用されているCanvas::message, Canvas::var命令については[[6.1 時間を計測する]]を参照してください。
10348 </pre>
10349 </div>
10350 <div title="例5: Mouse::pressedを使ったソース" modifier="Psychlops_Admin" created="200708272055" changecount="1">
10351 <pre>{{{
10352 *例5 MouseStateの使用例
10353 #include &lt;psychlops.h&gt;
10354 using namespace Psychlops;
10355
10356 double x=0,y=0;
10357 int code, oldcode;
10358 Psychlops::Rectangle rect(60,60), rect2(60,60);
10359
10360 void psychlops_main() {
10361         
10362         Canvas display(Canvas::fullscreen);
10363         display.clear(Color::gray);
10364         rect.centering();
10365         
10366         Input::refresh();
10367         
10368         while(!Input::get(Keyboard::spc)){
10369                 oldcode=code;
10370                 code=-1;
10371                 display.clear(Color::gray);
10372                 
10373                 if(Input::get(Keyboard::c, Keyboard::pushed))code=0; //centering
10374                 
10375                 //state &quot;pressed&quot; and &quot;released&quot; Demo
10376                 if(Input::get(Mouse::left, Mouse::pressed))code=1;
10377                 else if(Input::get(Mouse::right, Mouse::pressed))code=2;
10378                 
10379                 switch(code){
10380                         case -1: break;
10381                         case 0: rect.centering(); break;
10382                         case 1: rect.shift(-1,0); break;
10383                         case 2: rect.shift(1,0); break;
10384                 }
10385                 
10386                 rect2.centering().draw(Psychlops::Color(0.75,0.75,0.75)); 
10387                 rect.draw(Psychlops::Color(1.0,1.0,1.0,0.25));
10388                 display.flip();
10389         }
10390 }
10391 }}}
10392 </pre>
10393 </div>
10394 <div title="例6: マウスのデモ" modifier="Psychlops_Admin" created="200708272056" changecount="1">
10395 <pre>{{{
10396 *例6 マウスデモ
10397 #include &lt;psychlops.h&gt;
10398 using namespace Psychlops;
10399
10400 double dcX,dcY, rectcol;
10401 double rectsize=80;
10402 int display_cursor=1, in_rect=0;
10403 Psychlops::Rectangle rect(rectsize*2,rectsize*2);
10404 Psychlops::Range rngx,rngy;
10405
10406 void psychlops_main() {
10407         
10408         Canvas display(Canvas::fullscreen);
10409         display.clear(Color::gray);
10410         AppState::setThreadPriority(AppState::HIGH);
10411         rect.centering();
10412         dcX= display.getHcenter();
10413         dcY= display.getVcenter();
10414         Input::refresh();
10415         
10416         //Move mouse cursor to the center 
10417         Mouse::x=dcX;
10418         Mouse::y=dcY;
10419         
10420         while(!Input::get(Keyboard::spc)){
10421                 display.clear(Color::gray);
10422                 rectcol=0.25;
10423                 //set current rect area
10424                 rect.getHcenter()-rectsize&lt;rngx&lt;rect.getHcenter()+rectsize;
10425                 rect.getVcenter()-rectsize&lt;rngy&lt;rect.getVcenter()+rectsize;
10426         
10427                 if(display_cursor)Mouse::show();
10428                         else Mouse::hide();
10429                 
10430                 if(Input::get(Mouse::right, Mouse::pushed))display_cursor=1-display_cursor;     
10431                 if(in_rect){
10432                         rect.centering(Mouse::x, Mouse::y);
10433                         rectcol=1.0;
10434                 }
10435                         
10436                 rect.draw(rectcol);
10437                 display.flip();
10438                 if(Input::get(Mouse::left, Mouse::pushed)){
10439                                 if (rngx.includes(Mouse::x) &amp;&amp; rngy.includes(Mouse::y))in_rect=1;
10440                                         else in_rect=0;
10441                 }
10442                 if(Input::get(Mouse::left, Mouse::released))    {
10443                                 if (rngx.includes(Mouse::x) &amp;&amp; rngy.includes(Mouse::y))in_rect=0;
10444                 }
10445         }
10446 }
10447 }}}</pre>
10448 </div>
10449 <div title="入力系のクラスに関して" modifier="Kazushi Maruya" modified="200712131742" created="200712131740" changecount="2">
10450 <pre>* [[キーボード/マウスの反応を取得したい|Input::get()]]
10451 * [[キーボード/マウスの反応をリセットしたい|Input::refresh()]]
10452 * [[マウスカーソルを表示したい|Mouse::show()]]
10453 * [[マウスカーソルを非表示にしたい|Mouse::hide()]]</pre>
10454 </div>
10455 <div title="基本的な命令の使用" modifier="Psychlops_DevelopperG" modified="200908100506" created="200712131619" changecount="5">
10456 <pre>* [[描画画面を取得したい|2.1 描画領域の宣言]]
10457 * [[取得した画面の情報を取得したい|2.1.3 Canvasのメンバ取得と変数の表示]]
10458 * [[画面を特定の色で塗りつぶしたい|2.1.2 Canvasの基本構造と操作]]
10459 * [[画面に点を打ちたい|2.2.1 画面上に点を描画する-Pointクラス1-]]
10460 * [[画面に四角形を書きたい|2.2.3 画面上に四角形を描画する-Rectangleクラス1-]]
10461
10462 * [[画面に円を描きたい|2.2.4 画面上に円を描画する]]
10463 * [[描画色を指定したい|2.2.5 描画する図形の色を指定する-Colorクラス-]]
10464 * [[リアルタイムでは描画できない複雑な図形描画を事前に計算したい|3. 複雑な描画を行う(オフスクリーンへの描画)]]
10465 * [[自然画像を読み込み・表示したい|3.3 画像ファイルの取り扱い]]
10466
10467 * [[図形をXX msecだけ提示したい|2.1.2 Canvasの基本構造と操作]]
10468 * [[時間を正確に計測したい|5.1 時間を計測する]]
10469
10470 * [[キーボードの入力を取得したい|4.1 外部装置(キーボード/マウス)の操作内容を取得する]]
10471 * [[マウス入力を取得したい|4.1 外部装置(キーボード/マウス)の操作内容を取得する]]
10472 * [[配列の中身をファイルに記録したい|4.2 ファイルの入出力]]
10473
10474 * [[特定の命令に対してコンパイル時にエラーが出る]]
10475 </pre>
10476 </div>
10477 <div title="数値計算" modifier="Kazushi Maruya" modified="200712131748" created="200712131747" changecount="2">
10478 <pre>*[[簡単な行列演算がしたい|5.3 行列演算を行う]]
10479 *[[配列の要素をランダムに並べ替えたい|Math::shuffle]]
10480 *[[負の値、浮動小数点型に対して剰余を求めたい|Math::mod]]</pre>
10481 </div>
10482 <div title="日本語化" modifier="Psychlops_Admin" modified="200709121644" created="200707220238" tags="systemConfig systemConfigForce" changecount="19">
10483 <pre>// Shadow tiddlers for emergencies
10484 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;;
10485 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;;
10486 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;;
10487 config.shadowTiddlers.SideBarTabs = &quot;&lt;&lt;tabs txtMainTab '更新順' '更新順に表示する' TabTimeline 'タグ' 'タグ一覧' TabTags '詳細' '詳細' TabMore&gt;&gt;&quot;;
10488 config.shadowTiddlers.TabTimeline = &quot;&lt;&lt;timeline&gt;&gt;&quot;;
10489 config.shadowTiddlers.TabTags = &quot;&lt;&lt;allTags&gt;&gt;&quot;;
10490 config.shadowTiddlers.TabMore = &quot;&lt;&lt;tabs txtMoreTab '全部' 'すべてのTiddler' TabMoreAll '定義なし' '定義されていないTiddler一覧' TabMoreMissing 'リンク切れ' 'リンク切れしている単独のTiddler' TabMoreOrphans&gt;&gt;&quot;;
10491 config.shadowTiddlers.TabMoreAll = &quot;&lt;&lt;list all&gt;&gt;&quot;;
10492 config.shadowTiddlers.TabMoreMissing = &quot;&lt;&lt;list missing&gt;&gt;&quot;;
10493 config.shadowTiddlers.TabMoreOrphans = &quot;&lt;&lt;list orphans&gt;&gt;&quot;;
10494 // メッセージ
10495 config.messages.customConfigError = &quot;カスタムコンフィグにてエラー発生。 - %0&quot;;
10496 config.messages.savedSnapshotError = &quot;保存に失敗しました。&quot;;
10497 config.messages.subtitleUnknown = &quot;(不明)&quot;;
10498 config.messages.undefinedTiddlerToolTip = &quot;'%0'というTiddlerはまだ存在しません。&quot;;
10499 config.messages.externalLinkTooltip = &quot;(外部リンク) %0&quot;;
10500 config.messages.noTags = &quot;タグの設定されていないTiddler&quot;;
10501 config.messages.notFileUrlError = &quot;変更を保存したい場合、このTiddlyWikiをファイルに保存(ダウンロード)する必要があります。&quot;;
10502 config.messages.cantSaveError = &quot;このブラウザでは保存することができません。できればFirefoxを使ってください。&quot;;
10503 config.messages.invalidFileError = &quot;元のファイル '%0' は妥当なTiddlyWikiのファイルではありません。&quot;;
10504 config.messages.backupSaved = &quot;バックアップファイルを保存しました。&quot;;
10505 config.messages.backupFailed = &quot;バックアップファイルの保存に失敗しました。&quot;;
10506 config.messages.rssSaved = &quot;RSSファイルを保存しました。&quot;;
10507 config.messages.rssFailed = &quot;RSSファイルの保存に失敗しました。&quot;;
10508 config.messages.emptySaved = &quot;空のテンプレートファイルを保存しました。&quot;;
10509 config.messages.emptyFailed = &quot;空のテンプレートファイルの保存に失敗しました。&quot;;
10510 config.messages.mainSaved = &quot;TiddlyWikiファイルを保存しました。&quot;;
10511 config.messages.mainFailed = &quot;TiddlyWikiファイルの保存に失敗しました。修正内容は保存されていません。&quot;;
10512 config.messages.macroError = &quot;マクロ実行時エラー: '%0'&quot;;
10513 config.messages.overwriteWarning = &quot;'%0'というTiddlerはすでに存在します。OKを選択すると上書きします。&quot;;
10514 config.messages.unsavedChangesWarning = &quot;警告! 保存されていない変更が存在します。\n\nOKを選択:保存\nCancelを選択:編集内容を破棄&quot;;
10515 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;];
10516 config.messages.dates.days   = [&quot;日&quot;, &quot;月&quot;, &quot;火&quot;, &quot;水&quot;, &quot;木&quot;, &quot;金&quot;, &quot;土&quot;];
10517 //  Tiddler表示時ツールバーなど
10518 config.views.wikified.tag.labelNoTags           = &quot;no tags&quot;;
10519 config.views.wikified.tag.labelTags             = &quot;タグ = &quot;;
10520 config.views.wikified.tag.tooltip               = &quot;タグ'%0'が設定されたTiddlerを閲覧する。&quot;;
10521 config.views.wikified.tag.openAllText           = &quot;タグ'%0'のTiddlerをすべて開く。&quot;;
10522 config.views.wikified.tag.openAllTooltip        = &quot;このTiddlerをすべて開く。&quot;;
10523 config.views.wikified.tag.popupNone             = &quot;タグ'%0'はこれ以外のTiddlerに設定されていません。&quot;;
10524 config.views.wikified.toolbarEdit          = &quot;編集&quot;;
10525 config.views.wikified.toolbarPermalink    = &quot;permalink&quot;;
10526 config.views.wikified.toolbarReferences   = &quot;参照一覧&quot;;
10527 config.views.wikified.toolbarReferences.popupNone = &quot;参照されていません。&quot;;
10528 config.views.wikified.defaultText               = &quot;'%0'はまだ存在していません。ダブルクリックで作成できます。&quot;;
10529 //  Tiddler編集時ツールバーなど
10530 config.views.editor.tagPrompt             = &quot;[[tags]]のスタイルでtagsをスペース区切りに入力します。&quot;;
10531 config.views.editor.tagChooser.text       = &quot;タグ&quot;;
10532 config.views.editor.tagChooser.tooltip    = &quot;既存のタグから追加するものを選択してください。&quot;;
10533 config.views.editor.tagChooser.popupNone  = &quot;タグが設定されていません。&quot;;
10534 config.views.editor.toolbarDone     = &quot;確定&quot;;
10535 config.views.editor.toolbarCancel   = &quot;編集中止&quot;;
10536 config.views.editor.toolbarDelete    = &quot;削除&quot;;
10537
10538 config.views.editor.defaultText           = &quot;'%0'の内容を入力してください。&quot;;
10539 // Each has a 'handler' member that is inserted later
10540 config.macros.search.label        = &quot;Wiki内検索&quot;;
10541 config.macros.search.prompt       = &quot;このTiddlyWiki内を検索します。&quot;
10542 config.macros.search.successMsg   = &quot;%0 件見つかりました。- %1&quot;;
10543 config.macros.search.failureMsg   = &quot;%0 に該当するデータはありません。&quot;;
10544 config.macros.timeline.dateFormat = &quot;YYYY年MM月DD日&quot;;
10545 config.macros.allTags.tooltip     = &quot;タグ'%0'のデータをすべて表示します。&quot;;
10546 config.macros.allTags.noTags      = &quot;タグが設定されていません。&quot;;
10547 config.macros.list.all.prompt     = &quot;アルファベット順のTiddler一覧&quot;;
10548 config.macros.list.missing.prompt = &quot;リンクはされているが定義されていないTiddler一覧&quot;;
10549 config.macros.list.orphans.prompt = &quot;他のどこからもリンクされていないTiddler一覧&quot;;
10550 config.macros.closeAll.label      = &quot;すべて閉じる&quot;;
10551 config.macros.closeAll.prompt     = &quot;編集されているもの以外の表示されているすべてTiddlerを閉じます。&quot;;
10552 config.macros.permaview.label     = &quot;permaview&quot;;
10553 config.macros.permaview.prompt    = &quot;現在表示されているTiddlerの状態を表示するURLです。&quot;
10554 config.macros.saveChanges.label   = &quot;保存&quot;;
10555 config.macros.saveChanges.prompt  = &quot;すべてのTiddlerを保存します。&quot;;
10556 config.macros.newTiddler.label    = &quot;新規作成&quot;;
10557 config.macros.newTiddler.Title    = &quot;新規Tiddler&quot;;
10558 config.macros.newTiddler.prompt   = &quot;新しいTiddlerを作成します。&quot;;
10559 config.macros.newJournal.label    = &quot;新規日報&quot;;
10560 config.macros.newJournal.prompt   = &quot;新しいTiddlerを現在の日時をタイトルとして作成します&quot;;</pre>
10561 </div>
10562 <div title="時間計測系のクラスに関して" modifier="Kazushi Maruya" created="200712131743" changecount="1">
10563 <pre>* [[現在のCPU時間を取得したい|Clock::update()]]
10564 * [[clock型の値をmsec単位で変数に代入したい|Clock::at_msec()]]</pre>
10565 </div>
10566 <div title="止まった絵を描画するときCanvas.flip()を単独でwhileループの中に書くと、画面がちらつきます。" modifier="Psychlops_DevelopperG" created="200908100500" changecount="1">
10567 <pre>Psychlopsの仕様として、描画はすべてダブルバッファリングの裏画面に行っています。
10568 このため、描画命令を書いた後に、単にCanvas.flip()命令だけを繰り返すと、2枚に1枚は何も描画されていない画面が表示されます。
10569 止まった絵を表示させたいときにこれを回避するには、2枚の画面の両方に同じ刺激を描画してください。</pre>
10570 </div>
10571 <div title="特定の命令に対してコンパイル時にエラーが出る" modifier="Psychlops_DevelopperG" modified="200908100456" created="200908100444" changecount="6">
10572 <pre>! 名前空間に関する問題
10573 Psychlopsで使っている命令をコンパイルするときに、ある特定のクラスについて大量のエラーが出ることがあります。
10574 とくに、変数の宣言自体についてエラーが出る場合のの原因の一つとして、ほかのライブラリ、特にOS・コンパイラ標準のライブラリにあるクラスとこの特定のクラスが同じ名前を持っているために、名前が衝突してしまってコンパイルができなくなっていることが考えられます。
10575 このような場合、以下のように スコープ演算子 &quot;Psychlops::&quot; をクラス名の前に付け加えることで問題を解決できます。
10576
10577
10578 !!! (1-1) Macintoshでコンパイル時に、Point型を使った命令すべてに対してエラーが出ます。
10579
10580 Macintoshでは標準ライブラリにPoint型がすでにあるために、名前が衝突してしまい、コンパイルができません。スコープ演算子Psychlops::を使って、衝突を回避してください。
10581 {{{
10582  (コンパイル不可)Point A[n]
10583   (コンパイル可) Psychlops::Point A[n]
10584 }}}
10585
10586 !!! (1-2) Windowsでコンパイル時に、Rectangle型を使った命令すべてに対してエラーが出ます。
10587
10588 (1-1)と同様にWindowsでは標準ライブラリにRectangle型がすでにあるために、名前が衝突してしまい、コンパイルができません。スコープ演算子Psychlops::を使って、衝突を回避してください。
10589 {{{
10590  (コンパイル不可)Rectangle A[n]
10591   (コンパイル可) Psychlops::Rectangle A[n]
10592 }}}
10593
10594 !!! (1-3) random()命令に対してエラーが出ます。
10595
10596 スコープ演算子Psychlops::を使って、衝突を回避してください。
10597 {{{
10598 (コンパイル不可)random()
10599 (コンパイル可) Psychlops::random()
10600 }}}
10601
10602 名前空間の問題についての詳細は、[[OS環境による関数名の衝突について]]をご覧ください。
10603 </pre>
10604 </div>
10605 <div title="特定の種類の刺激描画" modifier="YourName" modified="200802191333" created="200712131629" changecount="3">
10606 <pre>[[ランダムドットを書きたい|3.2 オフスクリーンを用いた描画例-ランダムドットパターン-]]
10607 [[ダイナミックランダムドットを書きたい|Psychlops::random]]
10608 [[一定方向に運動するランダムドットを書きたい|3.2.3 ランダムドットキネマトグラムの描画]]
10609 [[ランダムドットステレオグラムを書きたい|Tips: ランダムドットステレオグラムの描画コード]]
10610
10611 [[垂直・水平方位の正弦縞を書きたい|2.2.6 画面上に縞を描画する-Rectangleクラス2-]]
10612 [[任意の方位の正弦縞を書きたい|2.2.7 画面上に縞を描画する-Pointクラス2-]]
10613 [[運動する縞を書きたい|2.2.8 運動する図形を描画する-Rectangleクラス3-]]
10614 [[Plaid刺激を書きたい|2.2.7 画面上に縞を描画する-Pointクラス2-]]
10615 [[Gabor Patchを書きたい|6.2 Gaborパッチの事前描画]]
10616
10617 [[インタラクティブなデモプログラムを作成したい|7. デモを作成する]]</pre>
10618 </div>
10619 <div title="第II部 Psychlopsの応用" modifier="Psychlops_DevelopperG" modified="200909150151" created="200908100512" changecount="2">
10620 <pre>[[1. 既成クラスを用いた簡単な実験プログラムを作成する]]
10621   [[1.1 実験計画のプログラミング]] 
10622   [[1.2 Gaborパッチの事前描画]] 
10623   [[1.3 各試行部分のプログラミング]] 
10624   [[1.4 終了処理とまとめ]]
10625
10626 [[2. デモを作成する]]
10627   [[2.1 独立変数Independent]] 
10628   [[2.2 デモ環境の表示]] </pre>
10629 </div>
10630 <div title="第I部 Psychlopsの基本" modifier="Psychlops_DevelopperG" modified="200908190223" created="200908100511" changecount="3">
10631 <pre>[[1. インストールを行う]]
10632   [[1.1 必要な環境]] 
10633     [[1.1.1 ハードウェア環境]]
10634     [[1.1.2 ソフトウェア環境]]
10635   [[1.2 インストール作業(Mac) ]]
10636   [[1.3 インストール作業(Windows) ]]
10637   [[1.4 更新とアンインストール]] 
10638   [[1.5 Psychlopsの基本構造]] 
10639   [[1.6 Psychlopsを使ったプログラミング]]
10640     [[1.6.1 プログラミングとは?]]
10641     [[1.6.2 Psychlopsを使ったプログラムの形]]
10642     [[1.6.3 配列]]
10643     [[1.6.4 for命令]]
10644     [[1.6.5 変数型]]
10645
10646 [[2. 基本的な描画を行う]]
10647   [[2.1 描画領域の宣言]]
10648     [[2.1.1 Canvasの宣言]]
10649     [[2.1.2 Canvasの基本構造と操作]]
10650     [[2.1.3 Canvasのメンバ取得と変数の表示]]         
10651   [[2.2 図形描画のコマンド]]
10652     [[2.2.1 画面上に点を描画する-Pointクラス1-]]           
10653     [[2.2.2 画面上に線を描画する]]           
10654     [[2.2.3 画面上に四角形を描画する-Rectangleクラス1-]]            
10655     [[2.2.4 画面上に円を描画する]]            
10656     [[2.2.5 描画する図形の色を指定する-Colorクラス-]]           
10657     [[2.2.6 画面上に縞を描画する-Rectangleクラス2-]]
10658     [[2.2.7 画面上に縞を描画する-Pointクラス2-]] 
10659     [[2.2.8 運動する図形を描画する-Rectangleクラス3-]] 
10660            
10661 [[3. 複雑な描画を行う1(オフスクリーンへの描画)]]
10662   [[3.1 オフスクリーン描画の基本]]
10663     [[3.1.1 オフスクリーン領域の宣言]]
10664     [[3.1.2 オフスクリーン上に点・四角形を描画する]] 
10665     [[3.1.3 オフスクリーン上の任意の位置に描画する]] 
10666     [[3.1.4 発展的な内容]]
10667   [[3.2 オフスクリーンを用いた描画例-ランダムドットパターン-]]
10668     [[3.2.1 基本的なランダムドットを描画する]]
10669     [[3.2.2 ドットサイズの異なるランダムドットパターンの描画]]
10670     [[3.2.3 ランダムドットキネマトグラムの描画]]
10671   [[3.3 画像ファイルの取り扱い]]
10672     [[3.3.1 画像ファイルを保存する]]
10673     [[3.3.2 画像ファイルを読み込み表示する]] 
10674   [[3.4 Canvasのオフスクリーンへの取り込み]]
10675
10676 [[5. 入出力を行う]]
10677   [[5.1 外部装置(キーボード/マウス)の操作内容を取得する]] 
10678   [[5.2 ファイルの入出力]]
10679
10680 [[6. 時間制御と各種ツールを使用する]]
10681   [[6.1 時間を計測する]] 
10682   [[6.2 各種ツールを使ってみる]] 
10683     [[6.2.1 描画の時間精度を確認する-FPSチェッカー]]
10684     [[6.2.2 画面に反映されない描画の進行度を表示する-プログレスバー]]
10685   [[6.3 行列演算を行う]]
10686     [[6.3.1 行列の宣言]]
10687     [[6.3.2 行列の要素の操作]]
10688     [[6.3.3 行列全体の操作]]
10689     [[6.3.4 行列の加減乗除]]
10690     [[6.3.5 行列の中身をオフスクリーンに描画する]]
10691     [[6.3.6 メッシュグリッドの作成]]</pre>
10692 </div>
10693 <div title="素晴らしいデモプログラムを作成したので、公開したい" modifier="Kazushi Maruya" modified="200712180151" created="200712131803" changecount="3">
10694 <pre>[[Visiome Platform|http://platform.visiome.neuroinf.jp]]には様々なデモンストレーションが公開されています。
10695 Psychlopsによるデモプログラムも多数公開されておりますので、是非ご参加ください。
10696 (現在の所このサイトにアップロードするためのアカウント取得には制限があります。詳しくはVisiomeのホームページをご覧下さい。)</pre>
10697 </div>
10698 <div title="関数一覧_FunctionList" modifier="Psychlops_DevelopperG" modified="200910080818" created="200707220316" changecount="22">
10699 <pre>*Graphic
10700 **[[Psychlops::Canvas]]
10701 **[[Psychlops::Rectangle]]
10702 **[[Psychlops::Point]]
10703 **[[Psychlops::Image]]
10704 **[[Psychlops::Color]]
10705 *Devices
10706 **[[Psychlops::Input]]
10707 **[[Psychlops::Mouse]]
10708 **[[Psychlops::Clock]]
10709 *Matrix
10710 **[[Psychlops::Matrix]]
10711 **[[Psychlops::Math]]</pre>
10712 </div>
10713 <div title="関数逆引きとQ&amp;A" modifier="Kazushi Maruya" modified="200712131749" created="200712131608" changecount="1">
10714 <pre>*[[Canvas型に関して]] 
10715 *[[Rectangle型に関して]] 
10716 *[[Point型に関して]]
10717 *[[Image型に関して]]
10718 *[[Color型に関して]]
10719 *[[入力系のクラスに関して]]
10720 *[[時間計測系のクラスに関して]]
10721
10722 [[基本的な命令の使用]]
10723 [[特定の種類の刺激描画]]
10724 [[数値計算]]
10725 [[その他]]</pre>
10726 </div>
10727 </div>
10728 <!--POST-STOREAREA-->
10729 <!--POST-BODY-START-->
10730 <!--POST-BODY-END-->
10731 <script type="text/javascript">
10732 //<![CDATA[
10733 //
10734 // Please note:
10735 // 
10736 // * This code is designed to be readable but for compactness it only includes brief comments. You can see fuller comments
10737 //   in the project Subversion repository at http://svn.tiddlywiki.org/Trunk/core/
10738 //
10739 // * You should never need to modify this source code directly. TiddlyWiki is carefully designed to allow deep customisation
10740 //   without changing the core code. Please consult the development group at http://groups.google.com/group/TiddlyWikiDev
10741 // 
10742
10743 //--
10744 //-- Configuration repository
10745 //--
10746
10747 // Miscellaneous options
10748 var config = {
10749         numRssItems: 20, // Number of items in the RSS feed
10750         animDuration: 400, // Duration of UI animations in milliseconds
10751         cascadeFast: 20, // Speed for cascade animations (higher == slower)
10752         cascadeSlow: 60, // Speed for EasterEgg cascade animations
10753         cascadeDepth: 5 // Depth of cascade animation
10754 };
10755
10756 // Adaptors
10757 config.adaptors = {};
10758
10759 // Backstage tasks
10760 config.tasks = {};
10761
10762 // Annotations
10763 config.annotations = {};
10764
10765 // Custom fields to be automatically added to new tiddlers
10766 config.defaultCustomFields = {};
10767
10768 // Messages
10769 config.messages = {
10770         messageClose: {},
10771         dates: {},
10772         tiddlerPopup: {}
10773 };
10774
10775 // Options that can be set in the options panel and/or cookies
10776 config.options = {
10777         chkRegExpSearch: false,
10778         chkCaseSensitiveSearch: false,
10779         chkAnimate: true,
10780         chkSaveBackups: true,
10781         chkAutoSave: false,
10782         chkGenerateAnRssFeed: false,
10783         chkSaveEmptyTemplate: false,
10784         chkOpenInNewWindow: true,
10785         chkToggleLinks: false,
10786         chkHttpReadOnly: true,
10787         chkForceMinorUpdate: false,
10788         chkConfirmDelete: true,
10789         chkInsertTabs: false,
10790         chkUsePreForStorage: true, // Whether to use <pre> format for storage
10791         chkDisplayStartupTime: false,
10792         txtBackupFolder: "",
10793         txtMainTab: "tabTimeline",
10794         txtMoreTab: "moreTabAll",
10795         txtMaxEditRows: "30",
10796         txtFileSystemCharSet: "UTF-8"
10797         };
10798 config.optionsDesc = {};
10799         
10800 // List of notification functions to be called when certain tiddlers are changed or deleted
10801 config.notifyTiddlers = [
10802         {name: "StyleSheetLayout", notify: refreshStyles},
10803         {name: "StyleSheetColors", notify: refreshStyles},
10804         {name: "StyleSheet", notify: refreshStyles},
10805         {name: "StyleSheetPrint", notify: refreshStyles},
10806         {name: "PageTemplate", notify: refreshPageTemplate},
10807         {name: "SiteTitle", notify: refreshPageTitle},
10808         {name: "SiteSubtitle", notify: refreshPageTitle},
10809         {name: "ColorPalette", notify: refreshColorPalette},
10810         {name: null, notify: refreshDisplay}
10811 ];
10812
10813 // Default tiddler templates
10814 var DEFAULT_VIEW_TEMPLATE = 1;
10815 var DEFAULT_EDIT_TEMPLATE = 2;
10816 config.tiddlerTemplates = {
10817         1: "ViewTemplate",
10818         2: "EditTemplate"
10819 };
10820
10821 // More messages (rather a legacy layout that shouldn't really be like this)
10822 config.views = {
10823         wikified: {
10824                 tag: {}
10825         },
10826         editor: {
10827                 tagChooser: {}
10828         }
10829 };
10830
10831 // Backstage tasks
10832 config.backstageTasks = ["save","sync","importTask","tweak","plugins"];
10833
10834 // Macros; each has a 'handler' member that is inserted later
10835 config.macros = {
10836         today: {},
10837         version: {},
10838         search: {sizeTextbox: 15},
10839         tiddler: {},
10840         tag: {},
10841         tags: {},
10842         tagging: {},
10843         timeline: {},
10844         allTags: {},
10845         list: {
10846                 all: {},
10847                 missing: {},
10848                 orphans: {},
10849                 shadowed: {},
10850                 touched: {}
10851         },
10852         closeAll: {},
10853         permaview: {},
10854         saveChanges: {},
10855         slider: {},
10856         option: {},
10857         options: {},
10858         newTiddler: {},
10859         newJournal: {},
10860         sparkline: {},
10861         tabs: {},
10862         gradient: {},
10863         message: {},
10864         view: {},
10865         edit: {},
10866         tagChooser: {},
10867         toolbar: {},
10868         br: {},
10869         plugins: {},
10870         refreshDisplay: {},
10871         importTiddlers: {},
10872         sync: {},
10873         annotations: {}
10874 };
10875
10876 // Commands supported by the toolbar macro
10877 config.commands = {
10878         closeTiddler: {},
10879         closeOthers: {},
10880         editTiddler: {},
10881         saveTiddler: {hideReadOnly: true},
10882         cancelTiddler: {},
10883         deleteTiddler: {hideReadOnly: true},
10884         permalink: {},
10885         references: {type: "popup"},
10886         jump: {type: "popup"},
10887         syncing: {type: "popup"},
10888         fields: {type: "popup"}
10889 };
10890
10891 // Browser detection... In a very few places, there's nothing else for it but to know what browser we're using.
10892 config.userAgent = navigator.userAgent.toLowerCase();
10893 config.browser = {
10894         isIE: config.userAgent.indexOf("msie") != -1 && config.userAgent.indexOf("opera") == -1,
10895         isGecko: config.userAgent.indexOf("gecko") != -1,
10896         ieVersion: /MSIE (\d.\d)/i.exec(config.userAgent), // config.browser.ieVersion[1], if it exists, will be the IE version string, eg "6.0"
10897         isSafari: config.userAgent.indexOf("applewebkit") != -1,
10898         isBadSafari: !((new RegExp("[\u0150\u0170]","g")).test("\u0150")),
10899         firefoxDate: /gecko\/(\d{8})/i.exec(config.userAgent), // config.browser.firefoxDate[1], if it exists, will be Firefox release date as "YYYYMMDD"
10900         isOpera: config.userAgent.indexOf("opera") != -1,
10901         isLinux: config.userAgent.indexOf("linux") != -1,
10902         isUnix: config.userAgent.indexOf("x11") != -1,
10903         isMac: config.userAgent.indexOf("mac") != -1,
10904         isWindows: config.userAgent.indexOf("win") != -1
10905 };
10906
10907 // Basic regular expressions
10908 config.textPrimitives = {
10909         upperLetter: "[A-Z\u00c0-\u00de\u0150\u0170]",
10910         lowerLetter: "[a-z0-9_\\-\u00df-\u00ff\u0151\u0171]",
10911         anyLetter:   "[A-Za-z0-9_\\-\u00c0-\u00de\u00df-\u00ff\u0150\u0170\u0151\u0171]",
10912         anyLetterStrict: "[A-Za-z0-9\u00c0-\u00de\u00df-\u00ff\u0150\u0170\u0151\u0171]"
10913 };
10914 if(config.browser.isBadSafari) {
10915         config.textPrimitives = {
10916                 upperLetter: "[A-Z\u00c0-\u00de]",
10917                 lowerLetter: "[a-z0-9_\\-\u00df-\u00ff]",
10918                 anyLetter:   "[A-Za-z0-9_\\-\u00c0-\u00de\u00df-\u00ff]",
10919                 anyLetterStrict: "[A-Za-z0-9\u00c0-\u00de\u00df-\u00ff]"
10920         };
10921 }
10922 config.textPrimitives.sliceSeparator = "::";
10923 config.textPrimitives.urlPattern = "[a-z]{3,8}:[^\\s:'\"][^\\s'\"]*(?:/|\\b)";
10924 config.textPrimitives.unWikiLink = "~";
10925 config.textPrimitives.wikiLink = "(?:(?:" + config.textPrimitives.upperLetter + "+" +
10926         config.textPrimitives.lowerLetter + "+" +
10927         config.textPrimitives.upperLetter +
10928         config.textPrimitives.anyLetter + "*)|(?:" +
10929         config.textPrimitives.upperLetter + "{2,}" +
10930         config.textPrimitives.lowerLetter + "+))";
10931
10932 config.textPrimitives.cssLookahead = "(?:(" + config.textPrimitives.anyLetter + "+)\\(([^\\)\\|\\n]+)(?:\\):))|(?:(" + config.textPrimitives.anyLetter + "+):([^;\\|\\n]+);)";
10933 config.textPrimitives.cssLookaheadRegExp = new RegExp(config.textPrimitives.cssLookahead,"mg");
10934
10935 config.textPrimitives.brackettedLink = "\\[\\[([^\\]]+)\\]\\]";
10936 config.textPrimitives.titledBrackettedLink = "\\[\\[([^\\[\\]\\|]+)\\|([^\\[\\]\\|]+)\\]\\]";
10937 config.textPrimitives.tiddlerForcedLinkRegExp = new RegExp("(?:" + config.textPrimitives.titledBrackettedLink + ")|(?:" +
10938         config.textPrimitives.brackettedLink + ")|(?:" + 
10939         config.textPrimitives.urlPattern + ")","mg");
10940 config.textPrimitives.tiddlerAnyLinkRegExp = new RegExp("("+ config.textPrimitives.wikiLink + ")|(?:" +
10941         config.textPrimitives.titledBrackettedLink + ")|(?:" +
10942         config.textPrimitives.brackettedLink + ")|(?:" +
10943         config.textPrimitives.urlPattern + ")","mg");
10944
10945 config.glyphs = {
10946         browsers: [
10947                 function() {return config.browser.isIE;},
10948                 function() {return true}
10949         ],
10950         currBrowser: null,
10951         codes: {
10952                 downTriangle: ["\u25BC","\u25BE"],
10953                 downArrow: ["\u2193","\u2193"],
10954                 bentArrowLeft: ["\u2190","\u21A9"],
10955                 bentArrowRight: ["\u2192","\u21AA"]
10956         }
10957 };
10958
10959 //--
10960 //-- Shadow tiddlers
10961 //--
10962
10963 config.shadowTiddlers = {
10964         StyleSheet: "",
10965         MarkupPreHead: "<!--{{{-->\n<link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml'/>\n<!--}}}-->",
10966         MarkupPostHead: "",
10967         MarkupPreBody: "",
10968         MarkupPostBody: "",
10969         TabTimeline: '<<timeline>>',
10970         TabAll: '<<list all>>',
10971         TabTags: '<<allTags excludeLists>>',
10972         TabMoreMissing: '<<list missing>>',
10973         TabMoreOrphans: '<<list orphans>>',
10974         TabMoreShadowed: '<<list shadowed>>',
10975         AdvancedOptions: '<<options>>',
10976         PluginManager: '<<plugins>>',
10977         ImportTiddlers: '<<importTiddlers>>'
10978 };
10979
10980 //--
10981 //-- Translateable strings
10982 //--
10983
10984 // Strings in "double quotes" should be translated; strings in 'single quotes' should be left alone
10985
10986 merge(config.options,{
10987         txtUserName: "YourName"});
10988
10989 merge(config.tasks,{
10990         save: {text: "save", tooltip: "Save your changes to this TiddlyWiki", action: saveChanges},
10991         sync: {text: "sync", tooltip: "Synchronise changes with other TiddlyWiki files and servers", content: '<<sync>>'},
10992         importTask: {text: "import", tooltip: "Import tiddlers and plugins from other TiddlyWiki files and servers", content: '<<importTiddlers>>'},
10993         tweak: {text: "tweak", tooltip: "Tweak the appearance and behaviour of TiddlyWiki", content: '<<options>>'},
10994         plugins: {text: "plugins", tooltip: "Manage installed plugins", content: '<<plugins>>'}
10995 });
10996
10997 // Options that can be set in the options panel and/or cookies
10998 merge(config.optionsDesc,{
10999         txtUserName: "Username for signing your edits",
11000         chkRegExpSearch: "Enable regular expressions for searches",
11001         chkCaseSensitiveSearch: "Case-sensitive searching",
11002         chkAnimate: "Enable animations",
11003         chkSaveBackups: "Keep backup file when saving changes",
11004         chkAutoSave: "Automatically save changes",
11005         chkGenerateAnRssFeed: "Generate an RSS feed when saving changes",
11006         chkSaveEmptyTemplate: "Generate an empty template when saving changes",
11007         chkOpenInNewWindow: "Open external links in a new window",
11008         chkToggleLinks: "Clicking on links to open tiddlers causes them to close",
11009         chkHttpReadOnly: "Hide editing features when viewed over HTTP",
11010         chkForceMinorUpdate: "Don't update modifier username and date when editing tiddlers",
11011         chkConfirmDelete: "Require confirmation before deleting tiddlers",
11012         chkInsertTabs: "Use the tab key to insert tab characters instead of moving between fields",
11013         txtBackupFolder: "Name of folder to use for backups",
11014         txtMaxEditRows: "Maximum number of rows in edit boxes",
11015         txtFileSystemCharSet: "Default character set for saving changes (Firefox/Mozilla only)"});
11016
11017 merge(config.messages,{
11018         customConfigError: "Problems were encountered loading plugins. See PluginManager for details",
11019         pluginError: "Error: %0",
11020         pluginDisabled: "Not executed because disabled via 'systemConfigDisable' tag",
11021         pluginForced: "Executed because forced via 'systemConfigForce' tag",
11022         pluginVersionError: "Not executed because this plugin needs a newer version of TiddlyWiki",
11023         nothingSelected: "Nothing is selected. You must select one or more items first",
11024         savedSnapshotError: "It appears that this TiddlyWiki has been incorrectly saved. Please see http://www.tiddlywiki.com/#DownloadSoftware for details",
11025         subtitleUnknown: "(unknown)",
11026         undefinedTiddlerToolTip: "The tiddler '%0' doesn't yet exist",
11027         shadowedTiddlerToolTip: "The tiddler '%0' doesn't yet exist, but has a pre-defined shadow value",
11028         tiddlerLinkTooltip: "%0 - %1, %2",
11029         externalLinkTooltip: "External link to %0",
11030         noTags: "There are no tagged tiddlers",
11031         notFileUrlError: "You need to save this TiddlyWiki to a file before you can save changes",
11032         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",
11033         invalidFileError: "The original file '%0' does not appear to be a valid TiddlyWiki",
11034         backupSaved: "Backup saved",
11035         backupFailed: "Failed to save backup file",
11036         rssSaved: "RSS feed saved",
11037         rssFailed: "Failed to save RSS feed file",
11038         emptySaved: "Empty template saved",
11039         emptyFailed: "Failed to save empty template file",
11040         mainSaved: "Main TiddlyWiki file saved",
11041         mainFailed: "Failed to save main TiddlyWiki file. Your changes have not been saved",
11042         macroError: "Error in macro <<\%0>>",
11043         macroErrorDetails: "Error while executing macro <<\%0>>:\n%1",
11044         missingMacro: "No such macro",
11045         overwriteWarning: "A tiddler named '%0' already exists. Choose OK to overwrite it",
11046         unsavedChangesWarning: "WARNING! There are unsaved changes in TiddlyWiki\n\nChoose OK to save\nChoose CANCEL to discard",
11047         confirmExit: "--------------------------------\n\nThere are unsaved changes in TiddlyWiki. If you continue you will lose those changes\n\n--------------------------------",
11048         saveInstructions: "SaveChanges",
11049         unsupportedTWFormat: "Unsupported TiddlyWiki format '%0'",
11050         tiddlerSaveError: "Error when saving tiddler '%0'",
11051         tiddlerLoadError: "Error when loading tiddler '%0'",
11052         wrongSaveFormat: "Cannot save with storage format '%0'. Using standard format for save.",
11053         invalidFieldName: "Invalid field name %0",
11054         fieldCannotBeChanged: "Field '%0' cannot be changed",
11055         loadingMissingTiddler: "Attempting to retrieve the tiddler '%0' from the '%1' server at:\n\n'%2' in the workspace '%3'"});
11056
11057 merge(config.messages.messageClose,{
11058         text: "close",
11059         tooltip: "close this message area"});
11060
11061 config.messages.backstage = {
11062         open: {text: "backstage", tooltip: "Open the backstage area to perform authoring and editing tasks"},
11063         close: {text: "close", tooltip: "Close the backstage area"},
11064         prompt: "backstage: ",
11065         decal: {
11066                 edit: {text: "edit", tooltip: "Edit the tiddler '%0'"}
11067         }
11068 };
11069
11070 config.messages.listView = {
11071         tiddlerTooltip: "Click for the full text of this tiddler",
11072         previewUnavailable: "(preview not available)"
11073 };
11074
11075 config.messages.dates.months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November","December"];
11076 config.messages.dates.days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
11077 config.messages.dates.shortMonths = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
11078 config.messages.dates.shortDays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
11079 // suffixes for dates, eg "1st","2nd","3rd"..."30th","31st"
11080 config.messages.dates.daySuffixes = ["st","nd","rd","th","th","th","th","th","th","th",
11081                 "th","th","th","th","th","th","th","th","th","th",
11082                 "st","nd","rd","th","th","th","th","th","th","th",
11083                 "st"];
11084 config.messages.dates.am = "am";
11085 config.messages.dates.pm = "pm";
11086
11087 merge(config.messages.tiddlerPopup,{
11088         });
11089
11090 merge(config.views.wikified.tag,{
11091         labelNoTags: "no tags",
11092         labelTags: "tags: ",
11093         openTag: "Open tag '%0'",
11094         tooltip: "Show tiddlers tagged with '%0'",
11095         openAllText: "Open all",
11096         openAllTooltip: "Open all of these tiddlers",
11097         popupNone: "No other tiddlers tagged with '%0'"});
11098
11099 merge(config.views.wikified,{
11100         defaultText: "The tiddler '%0' doesn't yet exist. Double-click to create it",
11101         defaultModifier: "(missing)",
11102         shadowModifier: "(built-in shadow tiddler)",
11103         dateFormat: "DD MMM YYYY",
11104         createdPrompt: "created"});
11105
11106 merge(config.views.editor,{
11107         tagPrompt: "Type tags separated with spaces, [[use double square brackets]] if necessary, or add existing",
11108         defaultText: "Type the text for '%0'"});
11109
11110 merge(config.views.editor.tagChooser,{
11111         text: "tags",
11112         tooltip: "Choose existing tags to add to this tiddler",
11113         popupNone: "There are no tags defined",
11114         tagTooltip: "Add the tag '%0'"});
11115
11116 merge(config.messages,{
11117         sizeTemplates:
11118                 [
11119                 {unit: 1024*1024*1024, template: "%0\u00a0GB"},
11120                 {unit: 1024*1024, template: "%0\u00a0MB"},
11121                 {unit: 1024, template: "%0\u00a0KB"},
11122                 {unit: 1, template: "%0\u00a0B"}
11123                 ]});
11124
11125 merge(config.macros.search,{
11126         label: "search",
11127         prompt: "Search this TiddlyWiki",
11128         accessKey: "F",
11129         successMsg: "%0 tiddlers found matching %1",
11130         failureMsg: "No tiddlers found matching %0"});
11131
11132 merge(config.macros.tagging,{
11133         label: "tagging: ",
11134         labelNotTag: "not tagging",
11135         tooltip: "List of tiddlers tagged with '%0'"});
11136
11137 merge(config.macros.timeline,{
11138         dateFormat: "DD MMM YYYY"});
11139
11140 merge(config.macros.allTags,{
11141         tooltip: "Show tiddlers tagged with '%0'",
11142         noTags: "There are no tagged tiddlers"});
11143
11144 config.macros.list.all.prompt = "All tiddlers in alphabetical order";
11145 config.macros.list.missing.prompt = "Tiddlers that have links to them but are not defined";
11146 config.macros.list.orphans.prompt = "Tiddlers that are not linked to from any other tiddlers";
11147 config.macros.list.shadowed.prompt = "Tiddlers shadowed with default contents";
11148 config.macros.list.touched.prompt = "Tiddlers that have been modified locally";
11149
11150 merge(config.macros.closeAll,{
11151         label: "close all",
11152         prompt: "Close all displayed tiddlers (except any that are being edited)"});
11153
11154 merge(config.macros.permaview,{
11155         label: "permaview",
11156         prompt: "Link to an URL that retrieves all the currently displayed tiddlers"});
11157
11158 merge(config.macros.saveChanges,{
11159         label: "save changes",
11160         prompt: "Save all tiddlers to create a new TiddlyWiki",
11161         accessKey: "S"});
11162
11163 merge(config.macros.newTiddler,{
11164         label: "new tiddler",
11165         prompt: "Create a new tiddler",
11166         title: "New Tiddler",
11167         accessKey: "N"});
11168
11169 merge(config.macros.newJournal,{
11170         label: "new journal",
11171         prompt: "Create a new tiddler from the current date and time",
11172         accessKey: "J"});
11173
11174 merge(config.macros.options,{
11175         wizardTitle: "Tweak advanced options",
11176         step1Title: "These options are saved in cookies in your browser",
11177         step1Html: "<input type='hidden' name='markList'></input><br><input type='checkbox' checked='false' name='chkUnknown'>Show unknown options</input>",
11178         unknownDescription: "//(unknown)//",
11179         listViewTemplate: {
11180                 columns: [
11181                         {name: 'Option', field: 'option', title: "Option", type: 'String'},
11182                         {name: 'Description', field: 'description', title: "Description", type: 'WikiText'},
11183                         {name: 'Name', field: 'name', title: "Name", type: 'String'}
11184                         ],
11185                 rowClasses: [
11186                         {className: 'lowlight', field: 'lowlight'} 
11187                         ]}
11188         });
11189
11190 merge(config.macros.plugins,{
11191         wizardTitle: "Manage plugins",
11192         step1Title: "Currently loaded plugins",
11193         step1Html: "<input type='hidden' name='markList'></input>", // DO NOT TRANSLATE
11194         skippedText: "(This plugin has not been executed because it was added since startup)",
11195         noPluginText: "There are no plugins installed",
11196         confirmDeleteText: "Are you sure you want to delete these plugins:\n\n%0",
11197         removeLabel: "remove systemConfig tag",
11198         removePrompt: "Remove systemConfig tag",
11199         deleteLabel: "delete",
11200         deletePrompt: "Delete these tiddlers forever",
11201         listViewTemplate: {
11202                 columns: [
11203                         {name: 'Selected', field: 'Selected', rowName: 'title', type: 'Selector'},
11204                         {name: 'Tiddler', field: 'tiddler', title: "Tiddler", type: 'Tiddler'},
11205                         {name: 'Size', field: 'size', tiddlerLink: 'size', title: "Size", type: 'Size'},
11206                         {name: 'Forced', field: 'forced', title: "Forced", tag: 'systemConfigForce', type: 'TagCheckbox'},
11207                         {name: 'Disabled', field: 'disabled', title: "Disabled", tag: 'systemConfigDisable', type: 'TagCheckbox'},
11208                         {name: 'Executed', field: 'executed', title: "Loaded", type: 'Boolean', trueText: "Yes", falseText: "No"},
11209                         {name: 'Startup Time', field: 'startupTime', title: "Startup Time", type: 'String'},
11210                         {name: 'Error', field: 'error', title: "Status", type: 'Boolean', trueText: "Error", falseText: "OK"},
11211                         {name: 'Log', field: 'log', title: "Log", type: 'StringList'}
11212                         ],
11213                 rowClasses: [
11214                         {className: 'error', field: 'error'},
11215                         {className: 'warning', field: 'warning'}
11216                         ]}
11217         });
11218
11219 merge(config.macros.toolbar,{
11220         moreLabel: "more",
11221         morePrompt: "Reveal further commands"
11222         });
11223
11224 merge(config.macros.refreshDisplay,{
11225         label: "refresh",
11226         prompt: "Redraw the entire TiddlyWiki display"
11227         });
11228
11229 merge(config.macros.importTiddlers,{
11230         readOnlyWarning: "You cannot import into a read-only TiddlyWiki file. Try opening it from a file:// URL",
11231         wizardTitle: "Import tiddlers from another file or server",
11232         step1Title: "Step 1: Locate the server or TiddlyWiki file",
11233         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>",
11234         openLabel: "open",
11235         openPrompt: "Open the connection to this file or server",
11236         openError: "There were problems fetching the tiddlywiki file",
11237         statusOpenHost: "Opening the host",
11238         statusGetWorkspaceList: "Getting the list of available workspaces",
11239         step2Title: "Step 2: Choose the workspace",
11240         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>",
11241         cancelLabel: "cancel",
11242         cancelPrompt: "Cancel this import",
11243         statusOpenWorkspace: "Opening the workspace",
11244         statusGetTiddlerList: "Getting the list of available tiddlers",
11245         step3Title: "Step 3: Choose the tiddlers to import",
11246         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'>",
11247         importLabel: "import",
11248         importPrompt: "Import these tiddlers",
11249         confirmOverwriteText: "Are you sure you want to overwrite these tiddlers:\n\n%0",
11250         step4Title: "Step 4: Importing %0 tiddler(s)",
11251         step4Html: "<input type='hidden' name='markReport'></input>", // DO NOT TRANSLATE
11252         doneLabel: "done",
11253         donePrompt: "Close this wizard",
11254         statusDoingImport: "Importing tiddlers",
11255         statusDoneImport: "All tiddlers imported",
11256         systemServerNamePattern: "%2 on %1",
11257         systemServerNamePatternNoWorkspace: "%1",
11258         confirmOverwriteSaveTiddler: "The tiddler '%0' already exists. Click 'OK' to overwrite it with the details of this server, or 'Cancel' to leave it unchanged",
11259         serverSaveTemplate: "|''Type:''|%0|\n|''URL:''|%1|\n|''Workspace:''|%2|\n\nThis tiddler was automatically created to record the details of this server",
11260         serverSaveModifier: "(System)",
11261         listViewTemplate: {
11262                 columns: [
11263                         {name: 'Selected', field: 'Selected', rowName: 'title', type: 'Selector'},
11264                         {name: 'Tiddler', field: 'tiddler', title: "Tiddler", type: 'Tiddler'},
11265                         {name: 'Size', field: 'size', tiddlerLink: 'size', title: "Size", type: 'Size'},
11266                         {name: 'Tags', field: 'tags', title: "Tags", type: 'Tags'}
11267                         ],
11268                 rowClasses: [
11269                         ]}
11270         });
11271
11272 merge(config.macros.sync,{
11273         listViewTemplate: {
11274                 columns: [
11275                         {name: 'Selected', field: 'selected', rowName: 'title', type: 'Selector'},
11276                         {name: 'Tiddler', field: 'tiddler', title: "Tiddler", type: 'Tiddler'},
11277                         {name: 'Server Type', field: 'serverType', title: "Server type", type: 'String'},
11278                         {name: 'Server Host', field: 'serverHost', title: "Server host", type: 'String'},
11279                         {name: 'Server Workspace', field: 'serverWorkspace', title: "Server workspace", type: 'String'},
11280                         {name: 'Status', field: 'status', title: "Synchronisation status", type: 'String'},
11281                         {name: 'Server URL', field: 'serverUrl', title: "Server URL", text: "View", type: 'Link'}
11282                         ],
11283                 rowClasses: [
11284                         ],
11285                 buttons: [
11286                         {caption: "Sync these tiddlers", name: 'sync'}
11287                         ]},
11288         wizardTitle: "Synchronize with external servers and files",
11289         step1Title: "Choose the tiddlers you want to synchronize",
11290         step1Html: "<input type='hidden' name='markList'></input>", // DO NOT TRANSLATE
11291         syncLabel: "sync",
11292         syncPrompt: "Sync these tiddlers",
11293         hasChanged: "Changed while unplugged",
11294         hasNotChanged: "Unchanged while unplugged",
11295         syncStatusList: {
11296                 none: {text: "...", color: "none"},
11297                 changedServer: {text: "Changed on server", color: '#80ff80'},
11298                 changedLocally: {text: "Changed while unplugged", color: '#80ff80'},
11299                 changedBoth: {text: "Changed while unplugged and on server", color: '#ff8080'},
11300                 notFound: {text: "Not found on server", color: '#ffff80'},
11301                 putToServer: {text: "Saved update on server", color: '#ff80ff'},
11302                 gotFromServer: {text: "Retrieved update from server", color: '#80ffff'}
11303                 }
11304         });
11305
11306 merge(config.macros.annotations,{
11307         });
11308
11309 merge(config.commands.closeTiddler,{
11310         text: "close",
11311         tooltip: "Close this tiddler"});
11312
11313 merge(config.commands.closeOthers,{
11314         text: "close others",
11315         tooltip: "Close all other tiddlers"});
11316
11317 merge(config.commands.editTiddler,{
11318         text: "edit",
11319         tooltip: "Edit this tiddler",
11320         readOnlyText: "view",
11321         readOnlyTooltip: "View the source of this tiddler"});
11322
11323 merge(config.commands.saveTiddler,{
11324         text: "done",
11325         tooltip: "Save changes to this tiddler"});
11326
11327 merge(config.commands.cancelTiddler,{
11328         text: "cancel",
11329         tooltip: "Undo changes to this tiddler",
11330         warning: "Are you sure you want to abandon your changes to '%0'?",
11331         readOnlyText: "done",
11332         readOnlyTooltip: "View this tiddler normally"});
11333
11334 merge(config.commands.deleteTiddler,{
11335         text: "delete",
11336         tooltip: "Delete this tiddler",
11337         warning: "Are you sure you want to delete '%0'?"});
11338
11339 merge(config.commands.permalink,{
11340         text: "permalink",
11341         tooltip: "Permalink for this tiddler"});
11342
11343 merge(config.commands.references,{
11344         text: "references",
11345         tooltip: "Show tiddlers that link to this one",
11346         popupNone: "No references"});
11347
11348 merge(config.commands.jump,{
11349         text: "jump",
11350         tooltip: "Jump to another open tiddler"});
11351
11352 merge(config.commands.syncing,{
11353         text: "syncing",
11354         tooltip: "Control synchronisation of this tiddler with a server or external file",
11355         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
11356         notCurrentlySyncing: "Not currently syncing",
11357         captionUnSync: "Stop synchronising this tiddler",
11358         chooseServer: "Synchronise this tiddler with another server:",
11359         currServerMarker: "\u25cf ",
11360         notCurrServerMarker: "  "});
11361
11362 merge(config.commands.fields,{
11363         text: "fields",
11364         tooltip: "Show the extended fields of this tiddler",
11365         emptyText: "There are no extended fields for this tiddler",
11366         listViewTemplate: {
11367                 columns: [
11368                         {name: 'Field', field: 'field', title: "Field", type: 'String'},
11369                         {name: 'Value', field: 'value', title: "Value", type: 'String'}
11370                         ],
11371                 rowClasses: [
11372                         ],
11373                 buttons: [
11374                         ]}});
11375
11376 merge(config.shadowTiddlers,{
11377         DefaultTiddlers: "GettingStarted",
11378         MainMenu: "GettingStarted",
11379         SiteTitle: "My TiddlyWiki",
11380         SiteSubtitle: "a reusable non-linear personal web notebook",
11381         SiteUrl: "http://www.tiddlywiki.com/",
11382         SideBarOptions: '<<search>><<closeAll>><<permaview>><<newTiddler>><<newJournal "DD MMM YYYY">><<saveChanges>><<slider chkSliderOptionsPanel OptionsPanel "options »" "Change TiddlyWiki advanced options">>',
11383         SideBarTabs: '<<tabs txtMainTab "Timeline" "Timeline" TabTimeline "All" "All tiddlers" TabAll "Tags" "All tags" TabTags "More" "More lists" TabMore>>',
11384         TabMore: '<<tabs txtMoreTab "Missing" "Missing tiddlers" TabMoreMissing "Orphans" "Orphaned tiddlers" TabMoreOrphans "Shadowed" "Shadowed tiddlers" TabMoreShadowed>>'});
11385
11386 merge(config.annotations,{
11387         AdvancedOptions: "This shadow tiddler provides access to several advanced options",
11388         ColorPalette: "These values in this shadow tiddler determine the colour scheme of the ~TiddlyWiki user interface",
11389         DefaultTiddlers: "The tiddlers listed in this shadow tiddler will be automatically displayed when ~TiddlyWiki starts up",
11390         EditTemplate: "The HTML template in this shadow tiddler determines how tiddlers look while they are being edited",
11391         GettingStarted: "This shadow tiddler provides basic usage instructions",
11392         ImportTiddlers: "This shadow tiddler provides access to importing tiddlers",
11393         MainMenu: "This shadow tiddler is used as the contents of the main menu in the left-hand column of the screen",
11394         MarkupPreHead: "This tiddler is inserted at the top of the <head> section of the TiddlyWiki HTML file",
11395         MarkupPostHead: "This tiddler is inserted at the bottom of the <head> section of the TiddlyWiki HTML file",
11396         MarkupPreBody: "This tiddler is inserted at the top of the <body> section of the TiddlyWiki HTML file",
11397         MarkupPostBody: "This tiddler is inserted at the end of the <body> section of the TiddlyWiki HTML file immediately before the script block",
11398         OptionsPanel: "This shadow tiddler is used as the contents of the options panel slider in the right-hand sidebar",
11399         PageTemplate: "The HTML template in this shadow tiddler determines the overall ~TiddlyWiki layout",
11400         PluginManager: "This shadow tiddler provides access to the plugin manager",
11401         SideBarOptions: "This shadow tiddler is used as the contents of the option panel in the right-hand sidebar",
11402         SideBarTabs: "This shadow tiddler is used as the contents of the tabs panel in the right-hand sidebar",
11403         SiteSubtitle: "This shadow tiddler is used as the second part of the page title",
11404         SiteTitle: "This shadow tiddler is used as the first part of the page title",
11405         SiteUrl: "This shadow tiddler should be set to the full target URL for publication",
11406         StyleSheetColours: "This shadow tiddler contains CSS definitions related to the color of page elements",
11407         StyleSheet: "This tiddler can contain custom CSS definitions",
11408         StyleSheetLayout: "This shadow tiddler contains CSS definitions related to the layout of page elements",
11409         StyleSheetLocale: "This shadow tiddler contains CSS definitions related to the translation locale",
11410         StyleSheetPrint: "This shadow tiddler contains CSS definitions for printing",
11411         TabAll: "This shadow tiddler contains the contents of the 'All' tab in the right-hand sidebar",
11412         TabMore: "This shadow tiddler contains the contents of the 'More' tab in the right-hand sidebar",
11413         TabMoreMissing: "This shadow tiddler contains the contents of the 'Missing' tab in the right-hand sidebar",
11414         TabMoreOrphans: "This shadow tiddler contains the contents of the 'Orphans' tab in the right-hand sidebar",
11415         TabMoreShadowed: "This shadow tiddler contains the contents of the 'Shadowed' tab in the right-hand sidebar",
11416         TabTags: "This shadow tiddler contains the contents of the 'Tags' tab in the right-hand sidebar",
11417         TabTimeline: "This shadow tiddler contains the contents of the 'Timeline' tab in the right-hand sidebar",
11418         ViewTemplate: "The HTML template in this shadow tiddler determines how tiddlers look"
11419         });
11420
11421 //--
11422 //-- Main
11423 //--
11424
11425 var params = null; // Command line parameters
11426 var store = null; // TiddlyWiki storage
11427 var story = null; // Main story
11428 var formatter = null; // Default formatters for the wikifier
11429 config.parsers = {}; // Hashmap of alternative parsers for the wikifier
11430 var anim = new Animator(); // Animation engine
11431 var readOnly = false; // Whether we're in readonly mode
11432 var highlightHack = null; // Embarrassing hack department...
11433 var hadConfirmExit = false; // Don't warn more than once
11434 var safeMode = false; // Disable all plugins and cookies
11435 var installedPlugins = []; // Information filled in when plugins are executed
11436 var startingUp = false; // Whether we're in the process of starting up
11437 var pluginInfo,tiddler; // Used to pass information to plugins in loadPlugins()
11438
11439 // Whether to use the JavaSaver applet
11440 var useJavaSaver = config.browser.isSafari || config.browser.isOpera;
11441
11442 // Starting up
11443 function main()
11444 {
11445         var t9,t8,t7,t6,t5,t4,t3,t2,t1,t0 = new Date();
11446         startingUp = true;
11447         window.onbeforeunload = function(e) {if(window.confirmExit) return confirmExit();};
11448         params = getParameters();
11449         if(params)
11450                 params = params.parseParams("open",null,false);
11451         store = new TiddlyWiki();
11452         invokeParamifier(params,"oninit");
11453         story = new Story("tiddlerDisplay","tiddler");
11454         addEvent(document,"click",Popup.onDocumentClick);
11455         saveTest();
11456         loadOptionsCookie();
11457         for(var s=0; s<config.notifyTiddlers.length; s++)
11458                 store.addNotification(config.notifyTiddlers[s].name,config.notifyTiddlers[s].notify);
11459         t1 = new Date();
11460         store.loadFromDiv("storeArea","store",true);
11461         t2 = new Date();
11462         loadShadowTiddlers();
11463         t3 = new Date();
11464         invokeParamifier(params,"onload");
11465         t4 = new Date();
11466         readOnly = (window.location.protocol == "file:") ? false : config.options.chkHttpReadOnly;
11467         var pluginProblem = loadPlugins();
11468         t5 = new Date();
11469         formatter = new Formatter(config.formatters);
11470         invokeParamifier(params,"onconfig");
11471         t6 = new Date();
11472         store.notifyAll();
11473         t7 = new Date();
11474         restart();
11475         t8 = new Date();
11476         if(pluginProblem) {
11477                 story.displayTiddler(null,"PluginManager");
11478                 displayMessage(config.messages.customConfigError);
11479         }
11480         for(var m in config.macros) {
11481                 if(config.macros[m].init)
11482                         config.macros[m].init();
11483         }
11484         if(!readOnly)
11485                 backstage.init();
11486         t9 = new Date();
11487         if(config.options.chkDisplayStartupTime) {
11488                 displayMessage("Load in " + (t2-t1) + " ms");
11489                 displayMessage("Loadshadows in " + (t3-t2) + " ms");
11490                 displayMessage("Loadplugins in " + (t5-t4) + " ms");
11491                 displayMessage("Notify in " + (t7-t6) + " ms");
11492                 displayMessage("Restart in " + (t8-t7) + " ms");
11493                 displayMessage("Total startup in " + (t9-t0) + " ms");
11494         }
11495         startingUp = false;
11496 }
11497
11498 // Restarting
11499 function restart()
11500 {
11501         invokeParamifier(params,"onstart");
11502         if(story.isEmpty()) {
11503                 var defaultParams = store.getTiddlerText("DefaultTiddlers").parseParams("open",null,false);
11504                 invokeParamifier(defaultParams,"onstart");
11505         }
11506         window.scrollTo(0,0);
11507 }
11508
11509 function saveTest()
11510 {
11511         var s = document.getElementById("saveTest");
11512         if(s.hasChildNodes())
11513                 alert(config.messages.savedSnapshotError);
11514         s.appendChild(document.createTextNode("savetest"));
11515 }
11516
11517 function loadShadowTiddlers()
11518 {
11519         var shadows = new TiddlyWiki();
11520         shadows.loadFromDiv("shadowArea","shadows",true);
11521         shadows.forEachTiddler(function(title,tiddler){config.shadowTiddlers[title] = tiddler.text;});
11522         delete shadows;
11523 }
11524
11525 function loadPlugins()
11526 {
11527         if(safeMode)
11528                 return false;
11529         var tiddlers = store.getTaggedTiddlers("systemConfig");
11530         var toLoad = [];
11531         var nLoaded = 0;
11532         var map = {};
11533         var nPlugins = tiddlers.length;
11534         installedPlugins = [];
11535         for(var i=0; i<nPlugins; i++) {
11536                 var p = getPluginInfo(tiddlers[i]);
11537                 installedPlugins[i] = p;
11538                 var n = p.Name;
11539                 if(n) 
11540                         map[n] = p;
11541                 if(n = p.Source) 
11542                         map[n] = p;
11543         }
11544         var visit = function(p) {
11545                 if(!p || p.done)
11546                         return;
11547                 p.done = 1;
11548                 var reqs = p.Requires;
11549                 if(reqs) {
11550                         reqs = reqs.readBracketedList();
11551                         for(var i=0; i<reqs.length; i++)
11552                                 visit(map[reqs[i]]);
11553                 }
11554                 toLoad.push(p);
11555         };
11556         for(i=0; i<nPlugins; i++) 
11557                 visit(installedPlugins[i]);     
11558         for(i=0; i<toLoad.length; i++) {
11559                 p = toLoad[i];
11560                 pluginInfo = p;
11561                 tiddler = p.tiddler;
11562                 if(isPluginExecutable(p)) {
11563                         if(isPluginEnabled(p)) {
11564                                 p.executed = true;
11565                                 var startTime = new Date();
11566                                 try {
11567                                         if(tiddler.text)
11568                                                 window.eval(tiddler.text);
11569                                         nLoaded++;
11570                                 } catch(ex) {
11571                                         p.log.push(config.messages.pluginError.format([exceptionText(ex)]));
11572                                         p.error = true;
11573                                 }
11574                                 pluginInfo.startupTime = String((new Date()) - startTime) + "ms"; 
11575                         } else {
11576                                 nPlugins--;
11577                         }
11578                 } else {
11579                         p.warning = true;
11580                 }
11581         }
11582         return nLoaded != nPlugins;
11583 }
11584
11585 function getPluginInfo(tiddler)
11586 {
11587         var p = store.getTiddlerSlices(tiddler.title,["Name","Description","Version","Requires","CoreVersion","Date","Source","Author","License","Browsers"]);
11588         p.tiddler = tiddler;
11589         p.title = tiddler.title;
11590         p.log = [];
11591         return p;
11592 }
11593
11594 // Check that a particular plugin is valid for execution
11595 function isPluginExecutable(plugin)
11596 {
11597         if(plugin.tiddler.isTagged("systemConfigForce"))
11598                 return verifyTail(plugin,true,config.messages.pluginForced);
11599         if(plugin["CoreVersion"]) {
11600                 var coreVersion = plugin["CoreVersion"].split(".");
11601                 var w = parseInt(coreVersion[0]) - version.major;
11602                 if(w == 0 && coreVersion[1])
11603                         w = parseInt(coreVersion[1]) - version.minor;
11604                 if(w == 0 && coreVersion[2])
11605                         w = parseInt(coreVersion[2]) - version.revision;
11606                 if(w > 0)
11607                         return verifyTail(plugin,false,config.messages.pluginVersionError);
11608                 }
11609         return true;
11610 }
11611
11612 function isPluginEnabled(plugin)
11613 {
11614         if(plugin.tiddler.isTagged("systemConfigDisable"))
11615                 return verifyTail(plugin,false,config.messages.pluginDisabled);
11616         return true;
11617 }
11618
11619 function verifyTail(plugin,result,message)
11620 {
11621         plugin.log.push(message);
11622         return result;
11623 }
11624
11625 function invokeMacro(place,macro,params,wikifier,tiddler)
11626 {
11627         try {
11628                 var m = config.macros[macro];
11629                 if(m && m.handler)
11630                         m.handler(place,macro,params.readMacroParams(),wikifier,params,tiddler);
11631                 else
11632                         createTiddlyError(place,config.messages.macroError.format([macro]),config.messages.macroErrorDetails.format([macro,config.messages.missingMacro]));
11633         } catch(ex) {
11634                 createTiddlyError(place,config.messages.macroError.format([macro]),config.messages.macroErrorDetails.format([macro,ex.toString()]));
11635         }
11636 }
11637
11638 //--
11639 //-- Paramifiers
11640 //--
11641
11642 function getParameters()
11643 {
11644         var p = null;
11645         if(window.location.hash) {
11646                 p = decodeURI(window.location.hash.substr(1));
11647                 if(config.browser.firefoxDate != null && config.browser.firefoxDate[1] < "20051111")
11648                         p = convertUTF8ToUnicode(p);
11649         }
11650         return p;
11651 }
11652
11653 function invokeParamifier(params,handler)
11654 {
11655         if(!params || params.length == undefined || params.length <= 1)
11656                 return;
11657         for(var t=1; t<params.length; t++) {
11658                 var p = config.paramifiers[params[t].name];
11659                 if(p && p[handler] instanceof Function)
11660                         p[handler](params[t].value);
11661         }
11662 }
11663
11664 config.paramifiers = {};
11665
11666 config.paramifiers.start = {
11667         oninit: function(v) {
11668                 safeMode = v.toLowerCase() == "safe";
11669         }
11670 };
11671
11672 config.paramifiers.open = {
11673         onstart: function(v) {
11674                 story.displayTiddler("bottom",v,null,false,null);
11675         }
11676 };
11677
11678 config.paramifiers.story = {
11679         onstart: function(v) {
11680                 var list = store.getTiddlerText(v,"").parseParams("open",null,false);
11681                 invokeParamifier(list,"onstart");
11682         }
11683 };
11684
11685 config.paramifiers.search = {
11686         onstart: function(v) {
11687                 story.search(v,false,false);
11688         }
11689 };
11690
11691 config.paramifiers.searchRegExp = {
11692         onstart: function(v) {
11693                 story.prototype.search(v,false,true);
11694         }
11695 };
11696
11697 config.paramifiers.tag = {
11698         onstart: function(v) {
11699                 var tagged = store.getTaggedTiddlers(v,"title");
11700                 for(var t=0; t<tagged.length; t++)
11701                         story.displayTiddler("bottom",tagged[t].title,null,false,null);
11702         }
11703 };
11704
11705 config.paramifiers.newTiddler = {
11706         onstart: function(v) {
11707                 if(!readOnly) {
11708                         story.displayTiddler(null,v,DEFAULT_EDIT_TEMPLATE);
11709                         story.focusTiddler(v,"text");
11710                 }
11711         }
11712 };
11713
11714 config.paramifiers.newJournal = {
11715         onstart: function(v) {
11716                 if(!readOnly) {
11717                         var now = new Date();
11718                         var title = now.formatString(v.trim());
11719                         story.displayTiddler(null,title,DEFAULT_EDIT_TEMPLATE);
11720                         story.focusTiddler(title,"text");
11721                 }
11722         }
11723 };
11724
11725 config.paramifiers.readOnly = {
11726         onconfig: function(v) {
11727                 var p = v.toLowerCase();
11728                 readOnly = p == "yes" ? true : (p == "no" ? false : readOnly);
11729         }
11730 };
11731
11732 //--
11733 //-- Formatter helpers
11734 //--
11735
11736 function Formatter(formatters)
11737 {
11738         this.formatters = [];
11739         var pattern = [];
11740         for(var n=0; n<formatters.length; n++) {
11741                 pattern.push("(" + formatters[n].match + ")");
11742                 this.formatters.push(formatters[n]);
11743         }
11744         this.formatterRegExp = new RegExp(pattern.join("|"),"mg");
11745 }
11746
11747 config.formatterHelpers = {
11748
11749         createElementAndWikify: function(w)
11750         {
11751                 w.subWikifyTerm(createTiddlyElement(w.output,this.element),this.termRegExp);
11752         },
11753         
11754         inlineCssHelper: function(w)
11755         {
11756                 var styles = [];
11757                 config.textPrimitives.cssLookaheadRegExp.lastIndex = w.nextMatch;
11758                 var lookaheadMatch = config.textPrimitives.cssLookaheadRegExp.exec(w.source);
11759                 while(lookaheadMatch && lookaheadMatch.index == w.nextMatch) {
11760                         var s,v;
11761                         if(lookaheadMatch[1]) {
11762                                 s = lookaheadMatch[1].unDash();
11763                                 v = lookaheadMatch[2];
11764                         } else {
11765                                 s = lookaheadMatch[3].unDash();
11766                                 v = lookaheadMatch[4];
11767                         }
11768                         if (s=="bgcolor")
11769                                 s = "backgroundColor";
11770                         styles.push({style: s, value: v});
11771                         w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
11772                         config.textPrimitives.cssLookaheadRegExp.lastIndex = w.nextMatch;
11773                         lookaheadMatch = config.textPrimitives.cssLookaheadRegExp.exec(w.source);
11774                 }
11775                 return styles;
11776         },
11777
11778         applyCssHelper: function(e,styles)
11779         {
11780                 for(var t=0; t< styles.length; t++) {
11781                         try {
11782                                 e.style[styles[t].style] = styles[t].value;
11783                         } catch (ex) {
11784                         }
11785                 }
11786         },
11787
11788         enclosedTextHelper: function(w)
11789         {
11790                 this.lookaheadRegExp.lastIndex = w.matchStart;
11791                 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
11792                 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
11793                         var text = lookaheadMatch[1];
11794                         if(config.browser.isIE)
11795                                 text = text.replace(/\n/g,"\r");
11796                         createTiddlyElement(w.output,this.element,null,null,text);
11797                         w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
11798                 }
11799         },
11800
11801         isExternalLink: function(link)
11802         {
11803                 if(store.tiddlerExists(link) || store.isShadowTiddler(link)) {
11804                         return false;
11805                 }
11806                 var urlRegExp = new RegExp(config.textPrimitives.urlPattern,"mg");
11807                 if(urlRegExp.exec(link)) {
11808                         return true;
11809                 }
11810                 if (link.indexOf(".")!=-1 || link.indexOf("\\")!=-1 || link.indexOf("/")!=-1){
11811                         return true;
11812                 }
11813                 return false;
11814         }
11815
11816 };
11817
11818 //--
11819 //-- Standard formatters
11820 //--
11821
11822 config.formatters = [
11823 {
11824         name: "table",
11825         match: "^\\|(?:[^\\n]*)\\|(?:[fhck]?)$",
11826         lookaheadRegExp: /^\|([^\n]*)\|([fhck]?)$/mg,
11827         rowTermRegExp: /(\|(?:[fhck]?)$\n?)/mg,
11828         cellRegExp: /(?:\|([^\n\|]*)\|)|(\|[fhck]?$\n?)/mg,
11829         cellTermRegExp: /((?:\x20*)\|)/mg,
11830         rowTypes: {"c":"caption", "h":"thead", "":"tbody", "f":"tfoot"},
11831
11832         handler: function(w)
11833         {
11834                 var table = createTiddlyElement(w.output,"table",null,"twtable");
11835                 var prevColumns = [];
11836                 var currRowType = null;
11837                 var rowContainer;
11838                 var rowCount = 0;
11839                 w.nextMatch = w.matchStart;
11840                 this.lookaheadRegExp.lastIndex = w.nextMatch;
11841                 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
11842                 while(lookaheadMatch && lookaheadMatch.index == w.nextMatch) {
11843                         var nextRowType = lookaheadMatch[2];
11844                         if(nextRowType == "k") {
11845                                 table.className = lookaheadMatch[1];
11846                                 w.nextMatch += lookaheadMatch[0].length+1;
11847                         } else {
11848                                 if(nextRowType != currRowType) {
11849                                         rowContainer = createTiddlyElement(table,this.rowTypes[nextRowType]);
11850                                         currRowType = nextRowType;
11851                                 }
11852                                 if(currRowType == "c") {
11853                                         // Caption
11854                                         w.nextMatch++;
11855                                         if(rowContainer != table.firstChild)
11856                                                 table.insertBefore(rowContainer,table.firstChild);
11857                                         rowContainer.setAttribute("align",rowCount == 0?"top":"bottom");
11858                                         w.subWikifyTerm(rowContainer,this.rowTermRegExp);
11859                                 } else {
11860                                         this.rowHandler(w,createTiddlyElement(rowContainer,"tr",null,(rowCount&1)?"oddRow":"evenRow"),prevColumns);
11861                                         rowCount++;
11862                                 }
11863                         }
11864                         this.lookaheadRegExp.lastIndex = w.nextMatch;
11865                         lookaheadMatch = this.lookaheadRegExp.exec(w.source);
11866                 }
11867         },
11868         rowHandler: function(w,e,prevColumns)
11869         {
11870                 var col = 0;
11871                 var colSpanCount = 1;
11872                 var prevCell = null;
11873                 this.cellRegExp.lastIndex = w.nextMatch;
11874                 var cellMatch = this.cellRegExp.exec(w.source);
11875                 while(cellMatch && cellMatch.index == w.nextMatch) {
11876                         if(cellMatch[1] == "~") {
11877                                 // Rowspan
11878                                 var last = prevColumns[col];
11879                                 if(last) {
11880                                         last.rowSpanCount++;
11881                                         last.element.setAttribute("rowspan",last.rowSpanCount);
11882                                         last.element.setAttribute("rowSpan",last.rowSpanCount); // Needed for IE
11883                                         last.element.valign = "center";
11884                                 }
11885                                 w.nextMatch = this.cellRegExp.lastIndex-1;
11886                         } else if(cellMatch[1] == ">") {
11887                                 // Colspan
11888                                 colSpanCount++;
11889                                 w.nextMatch = this.cellRegExp.lastIndex-1;
11890                         } else if(cellMatch[2]) {
11891                                 // End of row
11892                                 if(prevCell && colSpanCount > 1) {
11893                                         prevCell.setAttribute("colspan",colSpanCount);
11894                                         prevCell.setAttribute("colSpan",colSpanCount); // Needed for IE
11895                                 }
11896                                 w.nextMatch = this.cellRegExp.lastIndex;
11897                                 break;
11898                         } else {
11899                                 // Cell
11900                                 w.nextMatch++;
11901                                 var styles = config.formatterHelpers.inlineCssHelper(w);
11902                                 var spaceLeft = false;
11903                                 var chr = w.source.substr(w.nextMatch,1);
11904                                 while(chr == " ") {
11905                                         spaceLeft = true;
11906                                         w.nextMatch++;
11907                                         chr = w.source.substr(w.nextMatch,1);
11908                                 }
11909                                 var cell;
11910                                 if(chr == "!") {
11911                                         cell = createTiddlyElement(e,"th");
11912                                         w.nextMatch++;
11913                                 } else {
11914                                         cell = createTiddlyElement(e,"td");
11915                                 }
11916                                 prevCell = cell;
11917                                 prevColumns[col] = {rowSpanCount:1,element:cell};
11918                                 if(colSpanCount > 1) {
11919                                         cell.setAttribute("colspan",colSpanCount);
11920                                         cell.setAttribute("colSpan",colSpanCount); // Needed for IE
11921                                         colSpanCount = 1;
11922                                 }
11923                                 config.formatterHelpers.applyCssHelper(cell,styles);
11924                                 w.subWikifyTerm(cell,this.cellTermRegExp);
11925                                 if(w.matchText.substr(w.matchText.length-2,1) == " ") // spaceRight
11926                                         cell.align = spaceLeft ? "center" : "left";
11927                                 else if(spaceLeft)
11928                                         cell.align = "right";
11929                                 w.nextMatch--;
11930                         }
11931                         col++;
11932                         this.cellRegExp.lastIndex = w.nextMatch;
11933                         cellMatch = this.cellRegExp.exec(w.source);
11934                 }
11935         }
11936 },
11937
11938 {
11939         name: "heading",
11940         match: "^!{1,6}",
11941         termRegExp: /(\n)/mg,
11942         handler: function(w)
11943         {
11944                 w.subWikifyTerm(createTiddlyElement(w.output,"h" + w.matchLength),this.termRegExp);
11945         }
11946 },
11947
11948 {
11949         name: "list",
11950         match: "^(?:[\\*#;:]+)",
11951         lookaheadRegExp: /^(?:(?:(\*)|(#)|(;)|(:))+)/mg,
11952         termRegExp: /(\n)/mg,
11953         handler: function(w)
11954         {
11955                 var stack = [w.output];
11956                 var currLevel = 0, currType = null;
11957                 var listLevel, listType, itemType;
11958                 w.nextMatch = w.matchStart;
11959                 this.lookaheadRegExp.lastIndex = w.nextMatch;
11960                 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
11961                 while(lookaheadMatch && lookaheadMatch.index == w.nextMatch) {
11962                         if(lookaheadMatch[1]) {
11963                                 listType = "ul";
11964                                 itemType = "li";
11965                         } else if(lookaheadMatch[2]) {
11966                                 listType = "ol";
11967                                 itemType = "li";
11968                         } else if(lookaheadMatch[3]) {
11969                                 listType = "dl";
11970                                 itemType = "dt";
11971                         } else if(lookaheadMatch[4]) {
11972                                 listType = "dl";
11973                                 itemType = "dd";
11974                         }
11975                         listLevel = lookaheadMatch[0].length;
11976                         w.nextMatch += lookaheadMatch[0].length;
11977                         var t;
11978                         if(listLevel > currLevel) {
11979                                 for(t=currLevel; t<listLevel; t++) {
11980                                         var target = (currLevel == 0) ? stack[stack.length-1] : stack[stack.length-1].lastChild;
11981                                         stack.push(createTiddlyElement(target,listType));
11982                                 }
11983                         } else if(listLevel < currLevel) {
11984                                 for(t=currLevel; t>listLevel; t--)
11985                                         stack.pop();
11986                         } else if(listLevel == currLevel && listType != currType) {
11987                                 stack.pop();
11988                                 stack.push(createTiddlyElement(stack[stack.length-1].lastChild,listType));
11989                         }
11990                         currLevel = listLevel;
11991                         currType = listType;
11992                         var e = createTiddlyElement(stack[stack.length-1],itemType);
11993                         w.subWikifyTerm(e,this.termRegExp);
11994                         this.lookaheadRegExp.lastIndex = w.nextMatch;
11995                         lookaheadMatch = this.lookaheadRegExp.exec(w.source);
11996                 }
11997         }
11998 },
11999
12000 {
12001         name: "quoteByBlock",
12002         match: "^<<<\\n",
12003         termRegExp: /(^<<<(\n|$))/mg,
12004         element: "blockquote",
12005         handler: config.formatterHelpers.createElementAndWikify
12006 },
12007
12008 {
12009         name: "quoteByLine",
12010         match: "^>+",
12011         lookaheadRegExp: /^>+/mg,
12012         termRegExp: /(\n)/mg,
12013         element: "blockquote",
12014         handler: function(w)
12015         {
12016                 var stack = [w.output];
12017                 var currLevel = 0;
12018                 var newLevel = w.matchLength;
12019                 var t;
12020                 do {
12021                         if(newLevel > currLevel) {
12022                                 for(t=currLevel; t<newLevel; t++)
12023                                         stack.push(createTiddlyElement(stack[stack.length-1],this.element));
12024                         } else if(newLevel < currLevel) {
12025                                 for(t=currLevel; t>newLevel; t--)
12026                                         stack.pop();
12027                         }
12028                         currLevel = newLevel;
12029                         w.subWikifyTerm(stack[stack.length-1],this.termRegExp);
12030                         createTiddlyElement(stack[stack.length-1],"br");
12031                         this.lookaheadRegExp.lastIndex = w.nextMatch;
12032                         var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
12033                         var matched = lookaheadMatch && lookaheadMatch.index == w.nextMatch;
12034                         if(matched) {
12035                                 newLevel = lookaheadMatch[0].length;
12036                                 w.nextMatch += lookaheadMatch[0].length;
12037                         }
12038                 } while(matched);
12039         }
12040 },
12041
12042 {
12043         name: "rule",
12044         match: "^----+$\\n?",
12045         handler: function(w)
12046         {
12047                 createTiddlyElement(w.output,"hr");
12048         }
12049 },
12050
12051 {
12052         name: "monospacedByLine",
12053         match: "^\\{\\{\\{\\n",
12054         lookaheadRegExp: /^\{\{\{\n((?:^[^\n]*\n)+?)(^\}\}\}$\n?)/mg,
12055         element: "pre",
12056         handler: config.formatterHelpers.enclosedTextHelper
12057 },
12058
12059 {
12060         name: "monospacedByLineForCSS",
12061         match: "^/\\*\\{\\{\\{\\*/\\n",
12062         lookaheadRegExp: /\/\*\{\{\{\*\/\n*((?:^[^\n]*\n)+?)(\n*^\/\*\}\}\}\*\/$\n?)/mg,
12063         element: "pre",
12064         handler: config.formatterHelpers.enclosedTextHelper
12065 },
12066
12067 {
12068         name: "monospacedByLineForPlugin",
12069         match: "^//\\{\\{\\{\\n",
12070         lookaheadRegExp: /^\/\/\{\{\{\n\n*((?:^[^\n]*\n)+?)(\n*^\/\/\}\}\}$\n?)/mg,
12071         element: "pre",
12072         handler: config.formatterHelpers.enclosedTextHelper
12073 },
12074
12075 {
12076         name: "monospacedByLineForTemplate",
12077         match: "^<!--\\{\\{\\{-->\\n",
12078         lookaheadRegExp: /<!--\{\{\{-->\n*((?:^[^\n]*\n)+?)(\n*^<!--\}\}\}-->$\n?)/mg,
12079         element: "pre",
12080         handler: config.formatterHelpers.enclosedTextHelper
12081 },
12082
12083 {
12084         name: "wikifyCommentForPlugin",
12085         match: "^/\\*\\*\\*\\n",
12086         termRegExp: /(^\*\*\*\/\n)/mg,
12087         handler: function(w)
12088         {
12089                 w.subWikifyTerm(w.output,this.termRegExp);
12090         }
12091 },
12092
12093 {
12094         name: "wikifyCommentForTemplate",
12095         match: "^<!---\\n",
12096         termRegExp: /(^--->\n)/mg,
12097         handler: function(w) 
12098         {
12099                 w.subWikifyTerm(w.output,this.termRegExp);
12100         }
12101 },
12102
12103 {
12104         name: "macro",
12105         match: "<<",
12106         lookaheadRegExp: /<<([^>\s]+)(?:\s*)((?:[^>]|(?:>(?!>)))*)>>/mg,
12107         handler: function(w)
12108         {
12109                 this.lookaheadRegExp.lastIndex = w.matchStart;
12110                 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
12111                 if(lookaheadMatch && lookaheadMatch.index == w.matchStart && lookaheadMatch[1]) {
12112                         w.nextMatch = this.lookaheadRegExp.lastIndex;
12113                         invokeMacro(w.output,lookaheadMatch[1],lookaheadMatch[2],w,w.tiddler);
12114                 }
12115         }
12116 },
12117
12118 {
12119         name: "prettyLink",
12120         match: "\\[\\[",
12121         lookaheadRegExp: /\[\[(.*?)(?:\|(~)?(.*?))?\]\]/mg,
12122         handler: function(w)
12123         {
12124                 this.lookaheadRegExp.lastIndex = w.matchStart;
12125                 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
12126                 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
12127                         var e;
12128                         var text = lookaheadMatch[1];
12129                         if(lookaheadMatch[3]) {
12130                                 // Pretty bracketted link
12131                                 var link = lookaheadMatch[3];
12132                                 e = (!lookaheadMatch[2] && config.formatterHelpers.isExternalLink(link)) ?
12133                                                 createExternalLink(w.output,link) : createTiddlyLink(w.output,link,false,null,w.isStatic,w.tiddler);
12134                         } else {
12135                                 // Simple bracketted link
12136                                 e = createTiddlyLink(w.output,text,false,null,w.isStatic,w.tiddler);
12137                         }
12138                         createTiddlyText(e,text);
12139                         w.nextMatch = this.lookaheadRegExp.lastIndex;
12140                 }
12141         }
12142 },
12143
12144 {
12145         name: "unWikiLink",
12146         match: config.textPrimitives.unWikiLink+config.textPrimitives.wikiLink,
12147         handler: function(w)
12148         {
12149                 w.outputText(w.output,w.matchStart+1,w.nextMatch);
12150         }
12151 },
12152
12153 {
12154         name: "wikiLink",
12155         match: config.textPrimitives.wikiLink,
12156         handler: function(w)
12157         {
12158                 if(w.matchStart > 0) {
12159                         var preRegExp = new RegExp(config.textPrimitives.anyLetterStrict,"mg");
12160                         preRegExp.lastIndex = w.matchStart-1;
12161                         var preMatch = preRegExp.exec(w.source);
12162                         if(preMatch.index == w.matchStart-1) {
12163                                 w.outputText(w.output,w.matchStart,w.nextMatch);
12164                                 return;
12165                         }
12166                 }
12167                 if(w.autoLinkWikiWords == true || store.isShadowTiddler(w.matchText)) {
12168                         var link = createTiddlyLink(w.output,w.matchText,false,null,w.isStatic,w.tiddler);
12169                         w.outputText(link,w.matchStart,w.nextMatch);
12170                 } else {
12171                         w.outputText(w.output,w.matchStart,w.nextMatch);
12172                 }
12173         }
12174 },
12175
12176 {
12177         name: "urlLink",
12178         match: config.textPrimitives.urlPattern,
12179         handler: function(w)
12180         {
12181                 w.outputText(createExternalLink(w.output,w.matchText),w.matchStart,w.nextMatch);
12182         }
12183 },
12184
12185 {
12186         name: "image",
12187         match: "\\[[<>]?[Ii][Mm][Gg]\\[",
12188         lookaheadRegExp: /\[([<]?)(>?)[Ii][Mm][Gg]\[(?:([^\|\]]+)\|)?([^\[\]\|]+)\](?:\[([^\]]*)\])?\]/mg,
12189         handler: function(w)
12190         {
12191                 this.lookaheadRegExp.lastIndex = w.matchStart;
12192                 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
12193                 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
12194                         var e = w.output;
12195                         if(lookaheadMatch[5]) {
12196                                 var link = lookaheadMatch[5];
12197                                 e = config.formatterHelpers.isExternalLink(link) ? createExternalLink(w.output,link) : createTiddlyLink(w.output,link,false,null,w.isStatic,w.tiddler);
12198                                 addClass(e,"imageLink");
12199                         }
12200                         var img = createTiddlyElement(e,"img");
12201                         if(lookaheadMatch[1])
12202                                 img.align = "left";
12203                         else if(lookaheadMatch[2])
12204                                 img.align = "right";
12205                         if(lookaheadMatch[3])
12206                                 img.title = lookaheadMatch[3];
12207                         img.src = lookaheadMatch[4];
12208                         w.nextMatch = this.lookaheadRegExp.lastIndex;
12209                 }
12210         }
12211 },
12212
12213 {
12214         name: "html",
12215         match: "<[Hh][Tt][Mm][Ll]>",
12216         lookaheadRegExp: /<[Hh][Tt][Mm][Ll]>((?:.|\n)*?)<\/[Hh][Tt][Mm][Ll]>/mg,
12217         handler: function(w)
12218         {
12219                 this.lookaheadRegExp.lastIndex = w.matchStart;
12220                 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
12221                 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
12222                         createTiddlyElement(w.output,"span").innerHTML = lookaheadMatch[1];
12223                         w.nextMatch = this.lookaheadRegExp.lastIndex;
12224                 }
12225         }
12226 },
12227
12228 {
12229         name: "commentByBlock",
12230         match: "/%",
12231         lookaheadRegExp: /\/%((?:.|\n)*?)%\//mg,
12232         handler: function(w)
12233         {
12234                 this.lookaheadRegExp.lastIndex = w.matchStart;
12235                 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
12236                 if(lookaheadMatch && lookaheadMatch.index == w.matchStart)
12237                         w.nextMatch = this.lookaheadRegExp.lastIndex;
12238         }
12239 },
12240
12241 {
12242         name: "boldByChar",
12243         match: "''",
12244         termRegExp: /('')/mg,
12245         element: "strong",
12246         handler: config.formatterHelpers.createElementAndWikify
12247 },
12248
12249 {
12250         name: "italicByChar",
12251         match: "//",
12252         termRegExp: /(\/\/)/mg,
12253         element: "em",
12254         handler: config.formatterHelpers.createElementAndWikify
12255 },
12256
12257 {
12258         name: "underlineByChar",
12259         match: "__",
12260         termRegExp: /(__)/mg,
12261         element: "u",
12262         handler: config.formatterHelpers.createElementAndWikify
12263 },
12264
12265 {
12266         name: "strikeByChar",
12267         match: "--(?!\\s|$)",
12268         termRegExp: /((?!\s)--|(?=\n\n))/mg,
12269         element: "strike",
12270         handler: config.formatterHelpers.createElementAndWikify
12271 },
12272
12273 {
12274         name: "superscriptByChar",
12275         match: "\\^\\^",
12276         termRegExp: /(\^\^)/mg,
12277         element: "sup",
12278         handler: config.formatterHelpers.createElementAndWikify
12279 },
12280
12281 {
12282         name: "subscriptByChar",
12283         match: "~~",
12284         termRegExp: /(~~)/mg,
12285         element: "sub",
12286         handler: config.formatterHelpers.createElementAndWikify
12287 },
12288
12289 {
12290         name: "monospacedByChar",
12291         match: "\\{\\{\\{",
12292         lookaheadRegExp: /\{\{\{((?:.|\n)*?)\}\}\}/mg,
12293         handler: function(w)
12294         {
12295                 this.lookaheadRegExp.lastIndex = w.matchStart;
12296                 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
12297                 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
12298                         createTiddlyElement(w.output,"code",null,null,lookaheadMatch[1]);
12299                         w.nextMatch = this.lookaheadRegExp.lastIndex;
12300                 }
12301         }
12302 },
12303
12304 {
12305         name: "styleByChar",
12306         match: "@@",
12307         termRegExp: /(@@)/mg,
12308         handler: function(w)
12309         {
12310                 var e = createTiddlyElement(w.output,"span");
12311                 var styles = config.formatterHelpers.inlineCssHelper(w);
12312                 if(styles.length == 0)
12313                         e.className = "marked";
12314                 else
12315                         config.formatterHelpers.applyCssHelper(e,styles);
12316                 w.subWikifyTerm(e,this.termRegExp);
12317         }
12318 },
12319
12320 {
12321         name: "lineBreak",
12322         match: "\\n|<br ?/?>",
12323         handler: function(w)
12324         {
12325                 createTiddlyElement(w.output,"br");
12326         }
12327 },
12328
12329 {
12330         name: "rawText",
12331         match: "\\\"{3}|<nowiki>",
12332         lookaheadRegExp: /(?:\"{3}|<nowiki>)((?:.|\n)*?)(?:\"{3}|<\/nowiki>)/mg,
12333         handler: function(w)
12334         {
12335                 this.lookaheadRegExp.lastIndex = w.matchStart;
12336                 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
12337                 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
12338                         createTiddlyElement(w.output,"span",null,null,lookaheadMatch[1]);
12339                         w.nextMatch = this.lookaheadRegExp.lastIndex;
12340                 }
12341         }
12342 },
12343
12344 {
12345         name: "mdash",
12346         match: "--",
12347         handler: function(w)
12348         {
12349                 createTiddlyElement(w.output,"span").innerHTML = "&mdash;";
12350         }
12351 },
12352
12353 {
12354         name: "htmlEntitiesEncoding",
12355         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};)",
12356         handler: function(w)
12357         {
12358                 createTiddlyElement(w.output,"span").innerHTML = w.matchText;
12359         }
12360 },
12361
12362 {
12363         name: "customClasses",
12364         match: "\\{\\{",
12365         termRegExp: /(\}\}\})/mg,
12366         lookaheadRegExp: /\{\{[\s]*([\w]+[\s\w]*)[\s]*\{(\n?)/mg,
12367         handler: function(w)
12368         {
12369                 this.lookaheadRegExp.lastIndex = w.matchStart;
12370                 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
12371                 if(lookaheadMatch) {
12372                         var e = createTiddlyElement(w.output,lookaheadMatch[2] == "\n" ? "div" : "span",null,lookaheadMatch[1]);
12373                         w.nextMatch = this.lookaheadRegExp.lastIndex;
12374                         w.subWikifyTerm(e,this.termRegExp);
12375                 }
12376         }
12377 }
12378
12379 ];
12380
12381 //--
12382 //-- Wikifier
12383 //--
12384
12385 function getParser(tiddler,format)
12386 {
12387         if(tiddler) {
12388                 if(!format)
12389                         format = tiddler.fields["wikiformat"];
12390                 if(format) {
12391                         for(var i in config.parsers) {
12392                                 if(format == config.parsers[i].format)
12393                                         return config.parsers[i];
12394                         }
12395                 } else {
12396                         for(var i in config.parsers) {
12397                                 if(tiddler.isTagged(config.parsers[i].formatTag))
12398                                         return config.parsers[i];
12399                         }
12400                 }
12401         }
12402         return formatter;
12403 }
12404
12405 function wikify(source,output,highlightRegExp,tiddler)
12406 {
12407         if(source && source != "") {
12408                 var wikifier = new Wikifier(source,getParser(tiddler),highlightRegExp,tiddler);
12409                 wikifier.subWikifyUnterm(output);
12410         }
12411 }
12412
12413 function wikifyStatic(source,highlightRegExp,tiddler,format)
12414 {
12415         var e = createTiddlyElement(document.body,"div");
12416         e.style.display = "none";
12417         var html = "";
12418         if(source && source != "") {
12419                 var wikifier = new Wikifier(source,getParser(tiddler,format),highlightRegExp,tiddler);
12420                 wikifier.isStatic = true;
12421                 wikifier.subWikifyUnterm(e);
12422                 html = e.innerHTML;
12423                 removeNode(e);
12424         }
12425         return html;
12426 }
12427
12428 function wikifyPlain(title,theStore,limit)
12429 {
12430         if(!theStore)
12431                 theStore = store;
12432         if(theStore.tiddlerExists(title) || theStore.isShadowTiddler(title)) {
12433                 return wikifyPlainText(theStore.getTiddlerText(title),limit,tiddler);
12434         } else {
12435                 return "";
12436         }
12437 }
12438
12439 function wikifyPlainText(text,limit,tiddler)
12440 {
12441         if(limit > 0)
12442                 text = text.substr(0,limit);
12443         var wikifier = new Wikifier(text,formatter,null,tiddler);
12444         return wikifier.wikifyPlain();
12445 }
12446
12447 function highlightify(source,output,highlightRegExp,tiddler)
12448 {
12449         if(source && source != "") {
12450                 var wikifier = new Wikifier(source,formatter,highlightRegExp,tiddler);
12451                 wikifier.outputText(output,0,source.length);
12452         }
12453 }
12454
12455 function Wikifier(source,formatter,highlightRegExp,tiddler)
12456 {
12457         this.source = source;
12458         this.output = null;
12459         this.formatter = formatter;
12460         this.nextMatch = 0;
12461         this.autoLinkWikiWords = tiddler && tiddler.autoLinkWikiWords() == false ? false : true;
12462         this.highlightRegExp = highlightRegExp;
12463         this.highlightMatch = null;
12464         this.isStatic = false;
12465         if(highlightRegExp) {
12466                 highlightRegExp.lastIndex = 0;
12467                 this.highlightMatch = highlightRegExp.exec(source);
12468         }
12469         this.tiddler = tiddler;
12470 }
12471
12472 Wikifier.prototype.wikifyPlain = function()
12473 {
12474         var e = createTiddlyElement(document.body,"div");
12475         this.subWikify(e);
12476         var text = getPlainText(e);
12477         removeNode(e);
12478         return text;
12479 };
12480
12481 Wikifier.prototype.subWikify = function(output,terminator)
12482 {
12483         if(terminator)
12484                 this.subWikifyTerm(output,new RegExp("(" + terminator + ")","mg"));
12485         else
12486                 this.subWikifyUnterm(output);
12487 };
12488
12489 Wikifier.prototype.subWikifyUnterm = function(output)
12490 {
12491         // subWikify() can be indirectly recursive, so we need to save the old output pointer
12492         var oldOutput = this.output;
12493         this.output = output;
12494         this.formatter.formatterRegExp.lastIndex = this.nextMatch;
12495         var formatterMatch = this.formatter.formatterRegExp.exec(this.source);
12496         while(formatterMatch) {
12497                 // Output any text before the match
12498                 if(formatterMatch.index > this.nextMatch)
12499                         this.outputText(this.output,this.nextMatch,formatterMatch.index);
12500                 // Set the match parameters for the handler
12501                 this.matchStart = formatterMatch.index;
12502                 this.matchLength = formatterMatch[0].length;
12503                 this.matchText = formatterMatch[0];
12504                 this.nextMatch = this.formatter.formatterRegExp.lastIndex;
12505                 for(var t=1; t<formatterMatch.length; t++) {
12506                         if(formatterMatch[t]) {
12507                                 this.formatter.formatters[t-1].handler(this);
12508                                 this.formatter.formatterRegExp.lastIndex = this.nextMatch;
12509                                 break;
12510                         }
12511                 }
12512                 formatterMatch = this.formatter.formatterRegExp.exec(this.source);
12513         }
12514         if(this.nextMatch < this.source.length) {
12515                 this.outputText(this.output,this.nextMatch,this.source.length);
12516                 this.nextMatch = this.source.length;
12517         }
12518         this.output = oldOutput;
12519 };
12520
12521 Wikifier.prototype.subWikifyTerm = function(output,terminatorRegExp)
12522 {
12523         // subWikify() can be indirectly recursive, so we need to save the old output pointer
12524         var oldOutput = this.output;
12525         this.output = output;
12526         // Get the first matches for the formatter and terminator RegExps
12527         terminatorRegExp.lastIndex = this.nextMatch;
12528         var terminatorMatch = terminatorRegExp.exec(this.source);
12529         this.formatter.formatterRegExp.lastIndex = this.nextMatch;
12530         var formatterMatch = this.formatter.formatterRegExp.exec(terminatorMatch ? this.source.substr(0,terminatorMatch.index) : this.source);
12531         while(terminatorMatch || formatterMatch) {
12532                 if(terminatorMatch && (!formatterMatch || terminatorMatch.index <= formatterMatch.index)) {
12533                         if(terminatorMatch.index > this.nextMatch)
12534                                 this.outputText(this.output,this.nextMatch,terminatorMatch.index);
12535                         this.matchText = terminatorMatch[1];
12536                         this.matchLength = terminatorMatch[1].length;
12537                         this.matchStart = terminatorMatch.index;
12538                         this.nextMatch = this.matchStart + this.matchLength;
12539                         this.output = oldOutput;
12540                         return;
12541                 }
12542                 if(formatterMatch.index > this.nextMatch)
12543                         this.outputText(this.output,this.nextMatch,formatterMatch.index);
12544                 this.matchStart = formatterMatch.index;
12545                 this.matchLength = formatterMatch[0].length;
12546                 this.matchText = formatterMatch[0];
12547                 this.nextMatch = this.formatter.formatterRegExp.lastIndex;
12548                 for(var t=1; t<formatterMatch.length; t++) {
12549                         if(formatterMatch[t]) {
12550                                 this.formatter.formatters[t-1].handler(this);
12551                                 this.formatter.formatterRegExp.lastIndex = this.nextMatch;
12552                                 break;
12553                         }
12554                 }
12555                 terminatorRegExp.lastIndex = this.nextMatch;
12556                 terminatorMatch = terminatorRegExp.exec(this.source);
12557                 formatterMatch = this.formatter.formatterRegExp.exec(terminatorMatch ? this.source.substr(0,terminatorMatch.index) : this.source);
12558         }
12559         if(this.nextMatch < this.source.length) {
12560                 this.outputText(this.output,this.nextMatch,this.source.length);
12561                 this.nextMatch = this.source.length;
12562         }
12563         this.output = oldOutput;
12564 };
12565
12566 Wikifier.prototype.outputText = function(place,startPos,endPos)
12567 {
12568         while(this.highlightMatch && (this.highlightRegExp.lastIndex > startPos) && (this.highlightMatch.index < endPos) && (startPos < endPos)) {
12569                 if(this.highlightMatch.index > startPos) {
12570                         createTiddlyText(place,this.source.substring(startPos,this.highlightMatch.index));
12571                         startPos = this.highlightMatch.index;
12572                 }
12573                 var highlightEnd = Math.min(this.highlightRegExp.lastIndex,endPos);
12574                 var theHighlight = createTiddlyElement(place,"span",null,"highlight",this.source.substring(startPos,highlightEnd));
12575                 startPos = highlightEnd;
12576                 if(startPos >= this.highlightRegExp.lastIndex)
12577                         this.highlightMatch = this.highlightRegExp.exec(this.source);
12578         }
12579         if(startPos < endPos) {
12580                 createTiddlyText(place,this.source.substring(startPos,endPos));
12581         }
12582 };
12583
12584 //--
12585 //-- Macro definitions
12586 //--
12587
12588 config.macros.today.handler = function(place,macroName,params)
12589 {
12590         var now = new Date();
12591         var text;
12592         if(params[0])
12593                 text = now.formatString(params[0].trim());
12594         else
12595                 text = now.toLocaleString();
12596         createTiddlyElement(place,"span",null,null,text);
12597 };
12598
12599 config.macros.version.handler = function(place)
12600 {
12601         createTiddlyElement(place,"span",null,null,version.major + "." + version.minor + "." + version.revision + (version.beta ? " (beta " + version.beta + ")" : ""));
12602 };
12603
12604 config.macros.list.handler = function(place,macroName,params)
12605 {
12606         var type = params[0] ? params[0] : "all";
12607         var list = document.createElement("ul");
12608         place.appendChild(list);
12609         if(this[type].prompt)
12610                 createTiddlyElement(list,"li",null,"listTitle",this[type].prompt);
12611         var results;
12612         if(this[type].handler)
12613                 results = this[type].handler(params);
12614         for(var t = 0; t < results.length; t++) {
12615                 var li = document.createElement("li");
12616                 list.appendChild(li);
12617                 createTiddlyLink(li,typeof results[t] == "string" ? results[t] : results[t].title,true);
12618         }
12619 };
12620
12621 config.macros.list.all.handler = function(params)
12622 {
12623         return store.reverseLookup("tags","excludeLists",false,"title");
12624 };
12625
12626 config.macros.list.missing.handler = function(params)
12627 {
12628         return store.getMissingLinks();
12629 };
12630
12631 config.macros.list.orphans.handler = function(params)
12632 {
12633         return store.getOrphans();
12634 };
12635
12636 config.macros.list.shadowed.handler = function(params)
12637 {
12638         return store.getShadowed();
12639 };
12640
12641 config.macros.list.touched.handler = function(params)
12642 {
12643         return store.getTouched();
12644 };
12645
12646 config.macros.allTags.handler = function(place,macroName,params)
12647 {
12648         var tags = store.getTags(params[0]);
12649         var ul = createTiddlyElement(place,"ul");
12650         if(tags.length == 0)
12651                 createTiddlyElement(ul,"li",null,"listTitle",this.noTags);
12652         for(var t=0; t<tags.length; t++) {
12653                 var title = tags[t][0];
12654                 var info = getTiddlyLinkInfo(title);
12655                 var li =createTiddlyElement(ul,"li");
12656                 var btn = createTiddlyButton(li,title + " (" + tags[t][1] + ")",this.tooltip.format([title]),onClickTag,info.classes);
12657                 btn.setAttribute("tag",title);
12658                 btn.setAttribute("refresh","link");
12659                 btn.setAttribute("tiddlyLink",title);
12660         }
12661 };
12662
12663 config.macros.timeline.handler = function(place,macroName,params)
12664 {
12665         var field = params[0] ? params[0] : "modified";
12666         var tiddlers = store.reverseLookup("tags","excludeLists",false,field);
12667         var lastDay = "";
12668         var last = params[1] ? tiddlers.length-Math.min(tiddlers.length,parseInt(params[1])) : 0;
12669         for(var t=tiddlers.length-1; t>=last; t--) {
12670                 var tiddler = tiddlers[t];
12671                 var theDay = tiddler[field].convertToLocalYYYYMMDDHHMM().substr(0,8);
12672                 if(theDay != lastDay) {
12673                         var theDateList = document.createElement("ul");
12674                         place.appendChild(theDateList);
12675                         createTiddlyElement(theDateList,"li",null,"listTitle",tiddler[field].formatString(this.dateFormat));
12676                         lastDay = theDay;
12677                 }
12678                 var theDateListItem = createTiddlyElement(theDateList,"li",null,"listLink");
12679                 theDateListItem.appendChild(createTiddlyLink(place,tiddler.title,true));
12680         }
12681 };
12682
12683 config.macros.search.handler = function(place,macroName,params)
12684 {
12685         var searchTimeout = null;
12686         var btn = createTiddlyButton(place,this.label,this.prompt,this.onClick);
12687         var txt = createTiddlyElement(place,"input",null,"txtOptionInput");
12688         if(params[0])
12689                 txt.value = params[0];
12690         txt.onkeyup = this.onKeyPress;
12691         txt.onfocus = this.onFocus;
12692         txt.setAttribute("size",this.sizeTextbox);
12693         txt.setAttribute("accessKey",this.accessKey);
12694         txt.setAttribute("autocomplete","off");
12695         txt.setAttribute("lastSearchText","");
12696         if(config.browser.isSafari) {
12697                 txt.setAttribute("type","search");
12698                 txt.setAttribute("results","5");
12699         } else {
12700                 txt.setAttribute("type","text");
12701         }
12702 };
12703
12704 // Global because there's only ever one outstanding incremental search timer
12705 config.macros.search.timeout = null;
12706
12707 config.macros.search.doSearch = function(txt)
12708 {
12709         if(txt.value.length > 0) {
12710                 story.search(txt.value,config.options.chkCaseSensitiveSearch,config.options.chkRegExpSearch);
12711                 txt.setAttribute("lastSearchText",txt.value);
12712         }
12713 };
12714
12715 config.macros.search.onClick = function(e)
12716 {
12717         config.macros.search.doSearch(this.nextSibling);
12718         return false;
12719 };
12720
12721 config.macros.search.onKeyPress = function(e)
12722 {
12723         if(!e) var e = window.event;
12724         switch(e.keyCode) {
12725                 case 13: // Ctrl-Enter
12726                 case 10: // Ctrl-Enter on IE PC
12727                         config.macros.search.doSearch(this);
12728                         break;
12729                 case 27: // Escape
12730                         this.value = "";
12731                         clearMessage();
12732                         break;
12733         }
12734         if(this.value.length > 2) {
12735                 if(this.value != this.getAttribute("lastSearchText")) {
12736                         if(config.macros.search.timeout)
12737                                 clearTimeout(config.macros.search.timeout);
12738                         var txt = this;
12739                         config.macros.search.timeout = setTimeout(function() {config.macros.search.doSearch(txt);},500);
12740                 }
12741         } else {
12742                 if(config.macros.search.timeout)
12743                         clearTimeout(config.macros.search.timeout);
12744         }
12745 };
12746
12747 config.macros.search.onFocus = function(e)
12748 {
12749         this.select();
12750 };
12751
12752 config.macros.tiddler.handler = function(place,macroName,params,wikifier,paramString,tiddler)
12753 {
12754         params = paramString.parseParams("name",null,true,false,true);
12755         var names = params[0]["name"];
12756         var tiddlerName = names[0];
12757         var className = names[1] ? names[1] : null;
12758         var args = params[0]["with"];
12759         var wrapper = createTiddlyElement(place,"span",null,className);
12760         if(!args) {
12761                 wrapper.setAttribute("refresh","content");
12762                 wrapper.setAttribute("tiddler",tiddlerName);
12763         }
12764         var text = store.getTiddlerText(tiddlerName);
12765         if(text) {
12766                 var stack = config.macros.tiddler.tiddlerStack;
12767                 if(stack.indexOf(tiddlerName) !== -1)
12768                         return;
12769                 stack.push(tiddlerName);
12770                 try {
12771                         var n = args ? Math.min(args.length,9) : 0;
12772                         for(var i=0; i<n; i++) {
12773                                 var placeholderRE = new RegExp("\\$" + (i + 1),"mg");
12774                                 text = text.replace(placeholderRE,args[i]);
12775                         }
12776                         config.macros.tiddler.renderText(wrapper,text,tiddlerName,params);
12777                 } finally {
12778                         stack.pop();
12779                 }
12780         }
12781 };
12782
12783 config.macros.tiddler.renderText = function(place,text,tiddlerName,params) 
12784 {
12785         wikify(text,place,null,store.getTiddler(tiddlerName));
12786 };
12787
12788 config.macros.tiddler.tiddlerStack = [];
12789
12790 config.macros.tag.handler = function(place,macroName,params)
12791 {
12792         createTagButton(place,params[0]);
12793 };
12794
12795 config.macros.tags.handler = function(place,macroName,params,wikifier,paramString,tiddler)
12796 {
12797         params = paramString.parseParams("anon",null,true,false,false);
12798         var theList = createTiddlyElement(place,"ul");
12799         var title = getParam(params,"anon","");
12800         if(title && store.tiddlerExists(title))
12801                 tiddler = store.getTiddler(title);
12802         var sep = getParam(params,"sep"," ");
12803         var lingo = config.views.wikified.tag;
12804         var prompt = tiddler.tags.length == 0 ? lingo.labelNoTags : lingo.labelTags;
12805         createTiddlyElement(theList,"li",null,"listTitle",prompt.format([tiddler.title]));
12806         for(var t=0; t<tiddler.tags.length; t++) {
12807                 createTagButton(createTiddlyElement(theList,"li"),tiddler.tags[t],tiddler.title);
12808                 if(t<tiddler.tags.length-1)
12809                         createTiddlyText(theList,sep);
12810         }
12811 };
12812
12813 config.macros.tagging.handler = function(place,macroName,params,wikifier,paramString,tiddler)
12814 {
12815         params = paramString.parseParams("anon",null,true,false,false);
12816         var theList = createTiddlyElement(place,"ul");
12817         var title = getParam(params,"anon","");
12818         if(title == "" && tiddler instanceof Tiddler)
12819                 title = tiddler.title;
12820         var sep = getParam(params,"sep"," ");
12821         theList.setAttribute("title",this.tooltip.format([title]));
12822         var tagged = store.getTaggedTiddlers(title);
12823         var prompt = tagged.length == 0 ? this.labelNotTag : this.label;
12824         createTiddlyElement(theList,"li",null,"listTitle",prompt.format([title,tagged.length]));
12825         for(var t=0; t<tagged.length; t++) {
12826                 createTiddlyLink(createTiddlyElement(theList,"li"),tagged[t].title,true);
12827                 if(t<tagged.length-1)
12828                         createTiddlyText(theList,sep);
12829         }
12830 };
12831
12832 config.macros.closeAll.handler = function(place)
12833 {
12834         createTiddlyButton(place,this.label,this.prompt,this.onClick);
12835 };
12836
12837 config.macros.closeAll.onClick = function(e)
12838 {
12839         story.closeAllTiddlers();
12840         return false;
12841 };
12842
12843 config.macros.permaview.handler = function(place)
12844 {
12845         createTiddlyButton(place,this.label,this.prompt,this.onClick);
12846 };
12847
12848 config.macros.permaview.onClick = function(e)
12849 {
12850         story.permaView();
12851         return false;
12852 };
12853
12854 config.macros.saveChanges.handler = function(place)
12855 {
12856         if(!readOnly)
12857                 createTiddlyButton(place,this.label,this.prompt,this.onClick,null,null,this.accessKey);
12858 };
12859
12860 config.macros.saveChanges.onClick = function(e)
12861 {
12862         saveChanges();
12863         return false;
12864 };
12865
12866 config.macros.slider.onClickSlider = function(e)
12867 {
12868         if(!e) var e = window.event;
12869         var n = this.nextSibling;
12870         var cookie = n.getAttribute("cookie");
12871         var isOpen = n.style.display != "none";
12872         if(config.options.chkAnimate && anim && typeof Slider == "function")
12873                 anim.startAnimating(new Slider(n,!isOpen,null,"none"));
12874         else
12875                 n.style.display = isOpen ? "none" : "block";
12876         config.options[cookie] = !isOpen;
12877         saveOptionCookie(cookie);
12878         return false;
12879 };
12880
12881 config.macros.slider.createSlider = function(place,cookie,title,tooltip)
12882 {
12883         var cookie = cookie ? cookie : "";
12884         var btn = createTiddlyButton(place,title,tooltip,this.onClickSlider);
12885         var panel = createTiddlyElement(null,"div",null,"sliderPanel");
12886         panel.setAttribute("cookie",cookie);
12887         panel.style.display = config.options[cookie] ? "block" : "none";
12888         place.appendChild(panel);
12889         return panel;
12890 };
12891
12892 config.macros.slider.handler = function(place,macroName,params)
12893 {
12894         var panel = this.createSlider(place,params[0],params[2],params[3]);
12895         var text = store.getTiddlerText(params[1]);
12896         panel.setAttribute("refresh","content");
12897         panel.setAttribute("tiddler",params[1]);
12898         if(text)
12899                 wikify(text,panel,null,store.getTiddler(params[1]));
12900 };
12901
12902 config.macros.option.genericCreate = function(place,type,opt,className,desc)
12903 {
12904         var typeInfo = config.macros.option.types[type];
12905         var c = document.createElement(typeInfo.elementType);
12906         if(typeInfo.typeValue)
12907                 c.setAttribute("type",typeInfo.typeValue);
12908         c[typeInfo.eventName] = typeInfo.onChange;
12909         c.setAttribute("option",opt);
12910         if(className)
12911                 c.className = className;
12912         else
12913                 c.className = typeInfo.className;
12914         if(config.optionsDesc[opt])
12915                 c.setAttribute("title",config.optionsDesc[opt]);
12916         place.appendChild(c);
12917         if(desc != "no")
12918                 createTiddlyText(place,config.optionsDesc[opt] ? config.optionsDesc[opt] : opt);
12919         c[typeInfo.valueField] = config.options[opt];
12920         return c;
12921 };
12922
12923 config.macros.option.genericOnChange = function(e)
12924 {
12925         var opt = this.getAttribute("option");
12926         if(opt) {
12927                 var optType = opt.substr(0,3);
12928                 var handler = config.macros.option.types[optType];
12929                 if (handler.elementType && handler.valueField)
12930                         config.macros.option.propagateOption(opt,handler.valueField,this[handler.valueField],handler.elementType);
12931                 }
12932         return true;
12933 };
12934
12935 config.macros.option.types = {
12936         'txt': {
12937                 elementType: "input",
12938                 valueField: "value",
12939                 eventName: "onkeyup",
12940                 className: "txtOptionInput",
12941                 create: config.macros.option.genericCreate,
12942                 onChange: config.macros.option.genericOnChange
12943         },
12944         'chk': {
12945                 elementType: "input",
12946                 valueField: "checked",
12947                 eventName: "onclick",
12948                 className: "chkOptionInput",
12949                 typeValue: "checkbox",
12950                 create: config.macros.option.genericCreate,
12951                 onChange: config.macros.option.genericOnChange
12952         }
12953 };
12954
12955 config.macros.option.propagateOption = function(opt,valueField,value,elementType)
12956 {
12957         config.options[opt] = value;
12958         saveOptionCookie(opt);
12959         var nodes = document.getElementsByTagName(elementType);
12960         for(var t=0; t<nodes.length; t++) {
12961                 var optNode = nodes[t].getAttribute("option");
12962                 if(opt == optNode)
12963                         nodes[t][valueField] = value;
12964                 }
12965 };
12966
12967 config.macros.option.handler = function(place,macroName,params,wikifier,paramString,tiddler)
12968 {
12969         params = paramString.parseParams("anon",null,true,false,false);
12970         var opt = (params[1] && params[1].name == "anon") ? params[1].value : getParam(params,"name",null);
12971         var className = (params[2] && params[2].name == "anon") ? params[2].value : getParam(params,"class",null);
12972         var desc = getParam(params,"desc","no");
12973         var type = opt.substr(0,3);
12974         var h = config.macros.option.types[type];
12975         if (h && h.create)
12976                 h.create(place,type,opt,className,desc);
12977 };
12978
12979 config.macros.options.handler = function(place,macroName,params,wikifier,paramString,tiddler)
12980 {
12981         params = paramString.parseParams("anon",null,true,false,false);
12982         var showUnknown = getParam(params,"showUnknown","no");
12983         var wizard = new Wizard();
12984         wizard.createWizard(place,this.wizardTitle);
12985         wizard.addStep(this.step1Title,this.step1Html);
12986         var markList = wizard.getElement("markList");
12987         var chkUnknown = wizard.getElement("chkUnknown");
12988         chkUnknown.checked = showUnknown == "yes";
12989         chkUnknown.onchange = this.onChangeUnknown;
12990         var listWrapper = document.createElement("div");
12991         markList.parentNode.insertBefore(listWrapper,markList);
12992         wizard.setValue("listWrapper",listWrapper);
12993         this.refreshOptions(listWrapper,showUnknown == "yes");
12994 };
12995
12996 config.macros.options.refreshOptions = function(listWrapper,showUnknown)
12997 {       
12998         var opts = [];
12999         for(var n in config.options) {
13000                 var opt = {};
13001                 opt.option = "";
13002                 opt.name = n;
13003                 opt.lowlight = !config.optionsDesc[n];
13004                 opt.description = opt.lowlight ? this.unknownDescription : config.optionsDesc[n];
13005                 if(!opt.lowlight || showUnknown)
13006                         opts.push(opt);
13007         }
13008         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);});
13009         var listview = ListView.create(listWrapper,opts,this.listViewTemplate);
13010         for(n=0; n<opts.length; n++) {
13011                 var type = opts[n].name.substr(0,3);
13012                 var h = config.macros.option.types[type];
13013                 if (h && h.create) {
13014                         h.create(opts[n].colElements['option'],type,opts[n].name,null,"no");
13015                 }
13016         }
13017 };
13018
13019 config.macros.options.onChangeUnknown = function(e)
13020 {
13021         var wizard = new Wizard(this);
13022         var listWrapper = wizard.getValue("listWrapper");
13023         removeChildren(listWrapper);
13024         config.macros.options.refreshOptions(listWrapper,this.checked);
13025         return false;
13026 };
13027
13028 config.macros.newTiddler.createNewTiddlerButton = function(place,title,params,label,prompt,accessKey,newFocus,isJournal)
13029 {
13030         var tags = [];
13031         for(var t=1; t<params.length; t++) {
13032                 if((params[t].name == "anon" && t != 1) || (params[t].name == "tag"))
13033                         tags.push(params[t].value);
13034         }
13035         label = getParam(params,"label",label);
13036         prompt = getParam(params,"prompt",prompt);
13037         accessKey = getParam(params,"accessKey",accessKey);
13038         newFocus = getParam(params,"focus",newFocus);
13039         var customFields = getParam(params,"fields","");
13040         if(!customFields && !store.isShadowTiddler(title))
13041                 customFields = String.encodeHashMap(config.defaultCustomFields);
13042         var btn = createTiddlyButton(place,label,prompt,this.onClickNewTiddler,null,null,accessKey);
13043         btn.setAttribute("newTitle",title);
13044         btn.setAttribute("isJournal",isJournal ? "true" : "false");
13045         if(tags.length > 0)
13046                 btn.setAttribute("params",tags.join("|"));
13047         btn.setAttribute("newFocus",newFocus);
13048         btn.setAttribute("newTemplate",getParam(params,"template",DEFAULT_EDIT_TEMPLATE));
13049         if(customFields !== "")
13050                 btn.setAttribute("customFields",customFields);
13051         var text = getParam(params,"text");
13052         if(text !== undefined) 
13053                 btn.setAttribute("newText",text);
13054         return btn;
13055 };
13056
13057 config.macros.newTiddler.onClickNewTiddler = function()
13058 {
13059         var title = this.getAttribute("newTitle");
13060         if(this.getAttribute("isJournal") == "true") {
13061                 var now = new Date();
13062                 title = now.formatString(title.trim());
13063         }
13064         var params = this.getAttribute("params");
13065         var tags = params ? params.split("|") : [];
13066         var focus = this.getAttribute("newFocus");
13067         var template = this.getAttribute("newTemplate");
13068         var customFields = this.getAttribute("customFields");
13069         story.displayTiddler(null,title,template,false,null,null);
13070         var tiddlerElem = document.getElementById(story.idPrefix + title);
13071         if(customFields)
13072                 story.addCustomFields(tiddlerElem,customFields);
13073         var text = this.getAttribute("newText");
13074         if(typeof text == "string")
13075                 story.getTiddlerField(title,"text").value = text.format([title]);
13076         for(var t=0;t<tags.length;t++)
13077                 story.setTiddlerTag(title,tags[t],+1);
13078         story.focusTiddler(title,focus);
13079         return false;
13080 };
13081
13082 config.macros.newTiddler.handler = function(place,macroName,params,wikifier,paramString,tiddler)
13083 {
13084         if(!readOnly) {
13085                 params = paramString.parseParams("anon",null,true,false,false);
13086                 var title = params[1] && params[1].name == "anon" ? params[1].value : this.title;
13087                 title = getParam(params,"title",title);
13088                 this.createNewTiddlerButton(place,title,params,this.label,this.prompt,this.accessKey,"title",false);
13089         }
13090 };
13091
13092 config.macros.newJournal.handler = function(place,macroName,params,wikifier,paramString,tiddler)
13093 {
13094         if(!readOnly) {
13095                 params = paramString.parseParams("anon",null,true,false,false);
13096                 var title = params[1] && params[1].name == "anon" ? params[1].value : "";
13097                 title = getParam(params,"title",title);
13098                 config.macros.newTiddler.createNewTiddlerButton(place,title,params,this.label,this.prompt,this.accessKey,"text",true);
13099         }
13100 };
13101
13102 config.macros.sparkline.handler = function(place,macroName,params)
13103 {
13104         var data = [];
13105         var min = 0;
13106         var max = 0;
13107         for(var t=0; t<params.length; t++) {
13108                 var v = parseInt(params[t]);
13109                 if(v < min)
13110                         min = v;
13111                 if(v > max)
13112                         max = v;
13113                 data.push(v);
13114         }
13115         if(data.length < 1)
13116                 return;
13117         var box = createTiddlyElement(place,"span",null,"sparkline",String.fromCharCode(160));
13118         box.title = data.join(",");
13119         var w = box.offsetWidth;
13120         var h = box.offsetHeight;
13121         box.style.paddingRight = (data.length * 2 - w) + "px";
13122         box.style.position = "relative";
13123         for(var d=0; d<data.length; d++) {
13124                 var tick = document.createElement("img");
13125                 tick.border = 0;
13126                 tick.className = "sparktick";
13127                 tick.style.position = "absolute";
13128                 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";
13129                 tick.style.left = d*2 + "px";
13130                 tick.style.width = "2px";
13131                 var v = Math.floor(((data[d] - min)/(max-min)) * h);
13132                 tick.style.top = (h-v) + "px";
13133                 tick.style.height = v + "px";
13134                 box.appendChild(tick);
13135         }
13136 };
13137
13138 config.macros.tabs.handler = function(place,macroName,params)
13139 {
13140         var cookie = params[0];
13141         var numTabs = (params.length-1)/3;
13142         var wrapper = createTiddlyElement(null,"div",null,cookie);
13143         var tabset = createTiddlyElement(wrapper,"div",null,"tabset");
13144         tabset.setAttribute("cookie",cookie);
13145         var validTab = false;
13146         for(var t=0; t<numTabs; t++) {
13147                 var label = params[t*3+1];
13148                 var prompt = params[t*3+2];
13149                 var content = params[t*3+3];
13150                 var tab = createTiddlyButton(tabset,label,prompt,this.onClickTab,"tab tabUnselected");
13151                 tab.setAttribute("tab",label);
13152                 tab.setAttribute("content",content);
13153                 tab.title = prompt;
13154                 if(config.options[cookie] == label)
13155                         validTab = true;
13156         }
13157         if(!validTab)
13158                 config.options[cookie] = params[1];
13159         place.appendChild(wrapper);
13160         this.switchTab(tabset,config.options[cookie]);
13161 };
13162
13163 config.macros.tabs.onClickTab = function(e)
13164 {
13165         config.macros.tabs.switchTab(this.parentNode,this.getAttribute("tab"));
13166         return false;
13167 };
13168
13169 config.macros.tabs.switchTab = function(tabset,tab)
13170 {
13171         var cookie = tabset.getAttribute("cookie");
13172         var theTab = null;
13173         var nodes = tabset.childNodes;
13174         for(var t=0; t<nodes.length; t++) {
13175                 if(nodes[t].getAttribute && nodes[t].getAttribute("tab") == tab) {
13176                         theTab = nodes[t];
13177                         theTab.className = "tab tabSelected";
13178                 } else {
13179                         nodes[t].className = "tab tabUnselected";
13180                 }
13181         }
13182         if(theTab) {
13183                 if(tabset.nextSibling && tabset.nextSibling.className == "tabContents")
13184                         removeNode(tabset.nextSibling);
13185                 var tabContent = createTiddlyElement(null,"div",null,"tabContents");
13186                 tabset.parentNode.insertBefore(tabContent,tabset.nextSibling);
13187                 var contentTitle = theTab.getAttribute("content");
13188                 wikify(store.getTiddlerText(contentTitle),tabContent,null,store.getTiddler(contentTitle));
13189                 if(cookie) {
13190                         config.options[cookie] = tab;
13191                         saveOptionCookie(cookie);
13192                 }
13193         }
13194 };
13195
13196 // <<gradient [[tiddler name]] vert|horiz rgb rgb rgb rgb... >>
13197 config.macros.gradient.handler = function(place,macroName,params,wikifier)
13198 {
13199         var terminator = ">>";
13200         var panel;
13201         if(wikifier)
13202                 panel = createTiddlyElement(place,"div",null,"gradient");
13203         else
13204                 panel = place;
13205         panel.style.position = "relative";
13206         panel.style.overflow = "hidden";
13207         panel.style.zIndex = "0";
13208         var t;
13209         if(wikifier) {
13210                 var styles = config.formatterHelpers.inlineCssHelper(wikifier);
13211                 config.formatterHelpers.applyCssHelper(panel,styles);
13212         }
13213         var colours = [];
13214         for(t=1; t<params.length; t++) {
13215                 var c = new RGB(params[t]);
13216                 if(c)
13217                         colours.push(c);
13218         }
13219         drawGradient(panel,params[0] != "vert",colours);
13220         if(wikifier)
13221                 wikifier.subWikify(panel,terminator);
13222         if(document.all) {
13223                 panel.style.height = "100%";
13224                 panel.style.width = "100%";
13225         }
13226 };
13227
13228 config.macros.message.handler = function(place,macroName,params)
13229 {
13230         if(params[0]) {
13231                 var m = config;
13232                 var p = params[0].split(".");
13233                 for(var t=0; t<p.length; t++) {
13234                         if(p[t] in m)
13235                                 m = m[p[t]];
13236                         else
13237                                 break;
13238                 }
13239                 createTiddlyText(place,m.toString().format(params.splice(1)));
13240         }
13241 };
13242
13243 config.macros.view.handler = function(place,macroName,params,wikifier,paramString,tiddler)
13244 {
13245         if((tiddler instanceof Tiddler) && params[0]) {
13246                 var value = store.getValue(tiddler,params[0]);
13247                 if(value != undefined) {
13248                         switch(params[1]) {
13249                                 case undefined:
13250                                         highlightify(value,place,highlightHack,tiddler);
13251                                         break;
13252                                 case "link":
13253                                         createTiddlyLink(place,value,true);
13254                                         break;
13255                                 case "wikified":
13256                                         wikify(value,place,highlightHack,tiddler);
13257                                         break;
13258                                 case "date":
13259                                         value = Date.convertFromYYYYMMDDHHMM(value);
13260                                         createTiddlyText(place,value.formatString(params[2] ? params[2] : config.views.wikified.dateFormat));
13261                                         break;
13262                         }
13263                 }
13264         }
13265 };
13266
13267 config.macros.edit.handler = function(place,macroName,params,wikifier,paramString,tiddler)
13268 {
13269         var field = params[0];
13270         var rows = params[1];
13271         if((tiddler instanceof Tiddler) && field) {
13272                 story.setDirty(tiddler.title,true);
13273                 if(field != "text" && !rows) {
13274                         var e = createTiddlyElement(null,"input");
13275                         if(tiddler.isReadOnly())
13276                                 e.setAttribute("readOnly","readOnly");
13277                         e.setAttribute("edit",field);
13278                         e.setAttribute("type","text");
13279                         var v = store.getValue(tiddler,field);
13280                         if(!v) 
13281                                 v = "";
13282                         e.value = v;
13283                         e.setAttribute("size","40");
13284                         e.setAttribute("autocomplete","off");
13285                         place.appendChild(e);
13286                 } else {
13287                         var wrapper1 = createTiddlyElement(null,"fieldset",null,"fieldsetFix");
13288                         var wrapper2 = createTiddlyElement(wrapper1,"div");
13289                         var e = createTiddlyElement(wrapper2,"textarea");
13290                         if(tiddler.isReadOnly())
13291                                 e.setAttribute("readOnly","readOnly");
13292                         var v = store.getValue(tiddler,field);
13293                         if(!v) 
13294                                 v = "";
13295                         e.value = v;
13296                         var rows = rows ? rows : 10;
13297                         var lines = v.match(/\n/mg);
13298                         var maxLines = Math.max(parseInt(config.options.txtMaxEditRows),5);
13299                         if(lines != null && lines.length > rows)
13300                                 rows = lines.length + 5;
13301                         rows = Math.min(rows,maxLines);
13302                         e.setAttribute("rows",rows);
13303                         e.setAttribute("edit",field);
13304                         place.appendChild(wrapper1);
13305                 }
13306         }
13307 };
13308
13309 config.macros.tagChooser.onClick = function(e)
13310 {
13311         if(!e) var e = window.event;
13312         var lingo = config.views.editor.tagChooser;
13313         var popup = Popup.create(this);
13314         var tags = store.getTags();
13315         if(tags.length == 0)
13316                 createTiddlyText(createTiddlyElement(popup,"li"),lingo.popupNone);
13317         for(var t=0; t<tags.length; t++) {
13318                 var theTag = createTiddlyButton(createTiddlyElement(popup,"li"),tags[t][0],lingo.tagTooltip.format([tags[t][0]]),config.macros.tagChooser.onTagClick);
13319                 theTag.setAttribute("tag",tags[t][0]);
13320                 theTag.setAttribute("tiddler",this.getAttribute("tiddler"));
13321         }
13322         Popup.show();
13323         e.cancelBubble = true;
13324         if(e.stopPropagation) e.stopPropagation();
13325         return false;
13326 };
13327
13328 config.macros.tagChooser.onTagClick = function(e)
13329 {
13330         if(!e) var e = window.event;
13331         var tag = this.getAttribute("tag");
13332         var title = this.getAttribute("tiddler");
13333         if(!readOnly)
13334                 story.setTiddlerTag(title,tag,0);
13335         return false;
13336 };
13337
13338 config.macros.tagChooser.handler = function(place,macroName,params,wikifier,paramString,tiddler)
13339 {
13340         if(tiddler instanceof Tiddler) {
13341                 var title = tiddler.title;
13342                 var lingo = config.views.editor.tagChooser;
13343                 var btn = createTiddlyButton(place,lingo.text,lingo.tooltip,this.onClick);
13344                 btn.setAttribute("tiddler",title);
13345         }
13346 };
13347
13348 // Create a toolbar command button
13349 config.macros.toolbar.createCommand = function(place,commandName,tiddler,theClass)
13350 {
13351         if(typeof commandName != "string") {
13352                 var c = null;
13353                 for(var t in config.commands) {
13354                         if(config.commands[t] == commandName)
13355                                 c = t;
13356                 }
13357                 commandName = c;
13358         }
13359         if((tiddler instanceof Tiddler) && (typeof commandName == "string")) {
13360                 var command = config.commands[commandName];
13361                 if(command.isEnabled ? command.isEnabled(tiddler) : this.isCommandEnabled(command,tiddler)) {
13362                         var text = command.getText ? command.getText(tiddler) : this.getCommandText(command,tiddler);
13363                         var tooltip = command.getTooltip ? command.getTooltip(tiddler) : this.getCommandTooltip(command,tiddler);
13364                         var cmd;
13365                         switch(command.type) {
13366                                 case "popup":
13367                                         cmd = this.onClickPopup;
13368                                         break;
13369                                 case "command":
13370                                 default:
13371                                         cmd = this.onClickCommand;
13372                                         break;
13373                         }
13374                         var btn = createTiddlyButton(null,text,tooltip,cmd);
13375                         btn.setAttribute("commandName",commandName);
13376                         btn.setAttribute("tiddler",tiddler.title);
13377                         if(theClass)
13378                                 addClass(btn,theClass);
13379                         place.appendChild(btn);
13380                 }
13381         }
13382 };
13383
13384 config.macros.toolbar.isCommandEnabled = function(command,tiddler)
13385 {
13386         var title = tiddler.title;
13387         var ro = tiddler.isReadOnly();
13388         var shadow = store.isShadowTiddler(title) && !store.tiddlerExists(title);
13389         return (!ro || (ro && !command.hideReadOnly)) && !(shadow && command.hideShadow);
13390 };
13391
13392 config.macros.toolbar.getCommandText = function(command,tiddler)
13393 {
13394         return tiddler.isReadOnly() && command.readOnlyText ? command.readOnlyText : command.text;
13395 };
13396
13397 config.macros.toolbar.getCommandTooltip = function(command,tiddler)
13398 {
13399         return tiddler.isReadOnly() && command.readOnlyTooltip ? command.readOnlyTooltip : command.tooltip;
13400 };
13401
13402 config.macros.toolbar.onClickCommand = function(e)
13403 {
13404         if(!e) var e = window.event;
13405         e.cancelBubble = true;
13406         if (e.stopPropagation) e.stopPropagation();
13407         var command = config.commands[this.getAttribute("commandName")];
13408         return command.handler(e,this,this.getAttribute("tiddler"));
13409 };
13410
13411 config.macros.toolbar.onClickPopup = function(e)
13412 {
13413         if(!e) var e = window.event;
13414         e.cancelBubble = true;
13415         if (e.stopPropagation) e.stopPropagation();
13416         var popup = Popup.create(this);
13417         var command = config.commands[this.getAttribute("commandName")];
13418         var title = this.getAttribute("tiddler");
13419         var tiddler = store.fetchTiddler(title);
13420         popup.setAttribute("tiddler",title);
13421         command.handlePopup(popup,title);
13422         Popup.show();
13423         return false;
13424 };
13425
13426 // Invoke the first command encountered from a given place that is tagged with a specified class
13427 config.macros.toolbar.invokeCommand = function(place,theClass,event)
13428 {
13429         var children = place.getElementsByTagName("a");
13430         for(var t=0; t<children.length; t++) {
13431                 var c = children[t];
13432                 if(hasClass(c,theClass) && c.getAttribute && c.getAttribute("commandName")) {
13433                         if(c.onclick instanceof Function)
13434                                 c.onclick.call(c,event);
13435                         break;
13436                 }
13437         }
13438 };
13439
13440 config.macros.toolbar.onClickMore = function(e)
13441 {
13442         var e = this.nextSibling;
13443         e.style.display = "inline";
13444         removeNode(this);
13445         return false;
13446 };
13447
13448 config.macros.toolbar.handler = function(place,macroName,params,wikifier,paramString,tiddler)
13449 {
13450         for(var t=0; t<params.length; t++) {
13451                 var c = params[t];
13452                 switch(c) {
13453                         case '>':
13454                                 var btn = createTiddlyButton(place,this.moreLabel,this.morePrompt,config.macros.toolbar.onClickMore);
13455                                 addClass(btn,"moreCommand");
13456                                 var e = createTiddlyElement(place,"span",null,"moreCommand");
13457                                 e.style.display = "none";
13458                                 place = e;
13459                                 break;
13460                         default:
13461                                 var theClass = "";
13462                                 switch(c.substr(0,1)) {
13463                                         case "+":
13464                                                 theClass = "defaultCommand";
13465                                                 c = c.substr(1);
13466                                                 break;
13467                                         case "-":
13468                                                 theClass = "cancelCommand";
13469                                                 c = c.substr(1);
13470                                                 break;
13471                                 }
13472                                 if(c in config.commands)
13473                                         this.createCommand(place,c,tiddler,theClass);
13474                                 break;
13475                 }
13476         }
13477 };
13478
13479 config.macros.refreshDisplay.handler = function(place)
13480 {
13481         createTiddlyButton(place,this.label,this.prompt,this.onClick);
13482 };
13483
13484 config.macros.refreshDisplay.onClick = function(e)
13485 {
13486         refreshAll();
13487         return false;
13488 };
13489
13490 config.macros.annotations.handler = function(place,macroName,params,wikifier,paramString,tiddler)
13491 {
13492         var title = tiddler ? tiddler.title : null;
13493         var a = title ? config.annotations[title] : null;
13494         if(!tiddler || !title || !a)
13495                 return;
13496         var text = a.format([title]);
13497         wikify(text,createTiddlyElement(place,"div",null,"annotation"),null,tiddler);
13498 };
13499
13500 //--
13501 //-- Menu and toolbar commands
13502 //--
13503
13504 config.commands.closeTiddler.handler = function(event,src,title)
13505 {
13506         story.closeTiddler(title,true);
13507         return false;
13508 };
13509
13510 config.commands.closeOthers.handler = function(event,src,title)
13511 {
13512         story.closeAllTiddlers(title);
13513         return false;
13514 };
13515
13516 config.commands.editTiddler.handler = function(event,src,title)
13517 {
13518         clearMessage();
13519         var tiddlerElem = document.getElementById(story.idPrefix + title);
13520         var fields = tiddlerElem.getAttribute("tiddlyFields");
13521         story.displayTiddler(null,title,DEFAULT_EDIT_TEMPLATE,false,null,fields);
13522         story.focusTiddler(title,"text");
13523         return false;
13524 };
13525
13526 config.commands.saveTiddler.handler = function(event,src,title)
13527 {
13528         var newTitle = story.saveTiddler(title,event.shiftKey);
13529         if(newTitle)
13530                 story.displayTiddler(null,newTitle);
13531         return false;
13532 };
13533
13534 config.commands.cancelTiddler.handler = function(event,src,title)
13535 {
13536         if(story.hasChanges(title) && !readOnly) {
13537                 if(!confirm(this.warning.format([title])))
13538                         return false;
13539         }
13540         story.setDirty(title,false);
13541         story.displayTiddler(null,title);
13542         return false;
13543 };
13544
13545 config.commands.deleteTiddler.handler = function(event,src,title)
13546 {
13547         var deleteIt = true;
13548         if (config.options.chkConfirmDelete)
13549                 deleteIt = confirm(this.warning.format([title]));
13550         if (deleteIt) {
13551                 store.removeTiddler(title);
13552                 story.closeTiddler(title,true);
13553                 autoSaveChanges();
13554         }
13555         return false;
13556 };
13557
13558 config.commands.permalink.handler = function(event,src,title)
13559 {
13560         var t = encodeURIComponent(String.encodeTiddlyLink(title));
13561         if(window.location.hash != t)
13562                 window.location.hash = t;
13563         return false;
13564 };
13565
13566 config.commands.references.handlePopup = function(popup,title)
13567 {
13568         var references = store.getReferringTiddlers(title);
13569         var c = false;
13570         for(var r=0; r<references.length; r++) {
13571                 if(references[r].title != title && !references[r].isTagged("excludeLists")) {
13572                         createTiddlyLink(createTiddlyElement(popup,"li"),references[r].title,true);
13573                         c = true;
13574                 }
13575         }
13576         if(!c)
13577                 createTiddlyText(createTiddlyElement(popup,"li",null,"disabled"),this.popupNone);
13578 };
13579
13580 config.commands.jump.handlePopup = function(popup,title)
13581 {
13582         story.forEachTiddler(function(title,element) {
13583                 createTiddlyLink(createTiddlyElement(popup,"li"),title,true,null,false,null,true);
13584                 });
13585 };
13586
13587 config.commands.syncing.handlePopup = function(popup,title)
13588 {
13589         var tiddler = store.fetchTiddler(title);
13590         if(!tiddler)
13591                 return;
13592         var serverType = tiddler.getServerType();
13593         var serverHost = tiddler.fields['server.host'];
13594         var serverWorkspace = tiddler.fields['server.workspace'];
13595         if(!serverWorkspace)
13596                 serverWorkspace = "";
13597         if(serverType) {
13598                 var e = createTiddlyElement(popup,"li",null,"popupMessage");
13599                 e.innerHTML = config.commands.syncing.currentlySyncing.format([serverType,serverHost,serverWorkspace]);
13600         } else {
13601                 createTiddlyElement(popup,"li",null,"popupMessage",config.commands.syncing.notCurrentlySyncing);
13602         }
13603         if(serverType) {
13604                 createTiddlyElement(createTiddlyElement(popup,"li",null,"listBreak"),"div");
13605                 var btn = createTiddlyButton(createTiddlyElement(popup,"li"),this.captionUnSync,null,config.commands.syncing.onChooseServer);
13606                 btn.setAttribute("tiddler",title);
13607                 btn.setAttribute("server.type","");
13608         }
13609         createTiddlyElement(createTiddlyElement(popup,"li",null,"listBreak"),"div");
13610         createTiddlyElement(popup,"li",null,"popupMessage",config.commands.syncing.chooseServer);
13611         var feeds = store.getTaggedTiddlers("systemServer","title");
13612         for(var t=0; t<feeds.length; t++) {
13613                 var f = feeds[t];
13614                 var feedServerType = store.getTiddlerSlice(f.title,"Type");
13615                 if(!feedServerType)
13616                         feedServerType = "file";
13617                 var feedServerHost = store.getTiddlerSlice(f.title,"URL");
13618                 if(!feedServerHost)
13619                         feedServerHost = "";
13620                 var feedServerWorkspace = store.getTiddlerSlice(f.title,"Workspace");
13621                 if(!feedServerWorkspace)
13622                         feedServerWorkspace = "";
13623                 var caption = f.title;
13624                 if(serverType == feedServerType && serverHost == feedServerHost && serverWorkspace == feedServerWorkspace) {
13625                         caption = config.commands.syncing.currServerMarker + caption;
13626                 } else {
13627                         caption = config.commands.syncing.notCurrServerMarker + caption;
13628                 }
13629                 btn = createTiddlyButton(createTiddlyElement(popup,"li"),caption,null,config.commands.syncing.onChooseServer);
13630                 btn.setAttribute("tiddler",title);
13631                 btn.setAttribute("server.type",feedServerType);
13632                 btn.setAttribute("server.host",feedServerHost);
13633                 btn.setAttribute("server.workspace",feedServerWorkspace);
13634         }
13635 };
13636
13637 config.commands.syncing.onChooseServer = function(e)
13638 {
13639         var tiddler = this.getAttribute("tiddler");
13640         var serverType = this.getAttribute("server.type");
13641         if(serverType) {
13642                 store.addTiddlerFields(tiddler,{
13643                         'server.type': serverType,
13644                         'server.host': this.getAttribute("server.host"),
13645                         'server.workspace': this.getAttribute("server.workspace")
13646                         });
13647         } else {
13648                 store.setValue(tiddler,'server',null);
13649         }
13650         return false;
13651 };
13652
13653 config.commands.fields.handlePopup = function(popup,title)
13654 {
13655         var tiddler = store.fetchTiddler(title);
13656         if(!tiddler)
13657                 return;
13658         var fields = {};
13659         store.forEachField(tiddler,function(tiddler,fieldName,value) {fields[fieldName] = value;},true);
13660         var items = [];
13661         for(var t in fields) {
13662                 items.push({field: t,value: fields[t]});
13663         }
13664         items.sort(function(a,b) {return a.field < b.field ? -1 : (a.field == b.field ? 0 : +1);});
13665         if(items.length > 0)
13666                 ListView.create(popup,items,this.listViewTemplate);
13667         else
13668                 createTiddlyElement(popup,"div",null,null,this.emptyText);
13669 };
13670
13671 //--
13672 //-- Tiddler() object
13673 //--
13674
13675 function Tiddler(title)
13676 {
13677         this.title = title;
13678         this.text = null;
13679         this.modifier = null;
13680         this.modified = new Date();
13681         this.created = new Date();
13682         this.links = [];
13683         this.linksUpdated = false;
13684         this.tags = [];
13685         this.fields = {};
13686         return this;
13687 }
13688
13689 Tiddler.prototype.getLinks = function()
13690 {
13691         if(this.linksUpdated==false)
13692                 this.changed();
13693         return this.links;
13694 };
13695
13696 // Returns the fields that are inherited in string field:"value" field2:"value2" format
13697 Tiddler.prototype.getInheritedFields = function()
13698 {
13699         var f = {};
13700         for(i in this.fields) {
13701                 if(i=="server.host" || i=="server.workspace" || i=="wikiformat"|| i=="server.type") {
13702                         f[i] = this.fields[i];
13703                 }
13704         }
13705         return String.encodeHashMap(f);
13706 };
13707
13708 // Increment the changeCount of a tiddler
13709 Tiddler.prototype.incChangeCount = function()
13710 {
13711         var c = this.fields['changecount'];
13712         c = c ? parseInt(c) : 0;
13713         this.fields['changecount'] = String(c+1);
13714 };
13715
13716 // Clear the changeCount of a tiddler
13717 Tiddler.prototype.clearChangeCount = function()
13718 {
13719         if(this.fields['changecount']) {
13720                 delete this.fields['changecount'];
13721         }
13722 };
13723
13724 // Returns true if the tiddler has been updated since the tiddler was created or downloaded
13725 Tiddler.prototype.isTouched = function()
13726 {
13727         var changeCount = this.fields['changecount'];
13728         if(changeCount === undefined)
13729                 changeCount = 0;
13730         return changeCount > 0;
13731 };
13732
13733 // Format the text for storage in an RSS item
13734 Tiddler.prototype.saveToRss = function(url)
13735 {
13736         var s = [];
13737         s.push("<item>");
13738         s.push("<title" + ">" + this.title.htmlEncode() + "</title" + ">");
13739         s.push("<description>" + wikifyStatic(this.text,null,this).htmlEncode() + "</description>");
13740         for(var t=0; t<this.tags.length; t++)
13741                 s.push("<category>" + this.tags[t] + "</category>");
13742         s.push("<link>" + url + "#" + encodeURIComponent(String.encodeTiddlyLink(this.title)) + "</link>");
13743         s.push("<pubDate>" + this.modified.toGMTString() + "</pubDate>");
13744         s.push("</item>");
13745         return s.join("\n");
13746 };
13747
13748 // Change the text and other attributes of a tiddler
13749 Tiddler.prototype.set = function(title,text,modifier,modified,tags,created,fields)
13750 {
13751         this.assign(title,text,modifier,modified,tags,created,fields);
13752         this.changed();
13753         return this;
13754 };
13755
13756 // Change the text and other attributes of a tiddler without triggered a tiddler.changed() call
13757 Tiddler.prototype.assign = function(title,text,modifier,modified,tags,created,fields)
13758 {
13759         if(title != undefined)
13760                 this.title = title;
13761         if(text != undefined)
13762                 this.text = text;
13763         if(modifier != undefined)
13764                 this.modifier = modifier;
13765         if(modified != undefined)
13766                 this.modified = modified;
13767         if(created != undefined)
13768                 this.created = created;
13769         if(fields != undefined)
13770                 this.fields = fields;
13771         if(tags != undefined)
13772                 this.tags = (typeof tags == "string") ? tags.readBracketedList() : tags;
13773         else if(this.tags == undefined)
13774                 this.tags = [];
13775         return this;
13776 };
13777
13778 // Get the tags for a tiddler as a string (space delimited, using [[brackets]] for tags containing spaces)
13779 Tiddler.prototype.getTags = function()
13780 {
13781         return String.encodeTiddlyLinkList(this.tags);
13782 };
13783
13784 // Test if a tiddler carries a tag
13785 Tiddler.prototype.isTagged = function(tag)
13786 {
13787         return this.tags.indexOf(tag) != -1;
13788 };
13789
13790 // Static method to convert "\n" to newlines, "\s" to "\"
13791 Tiddler.unescapeLineBreaks = function(text)
13792 {
13793         return text ? text.unescapeLineBreaks() : "";
13794 };
13795
13796 // Convert newlines to "\n", "\" to "\s"
13797 Tiddler.prototype.escapeLineBreaks = function()
13798 {
13799         return this.text.escapeLineBreaks();
13800 };
13801
13802 // Updates the secondary information (like links[] array) after a change to a tiddler
13803 Tiddler.prototype.changed = function()
13804 {
13805         this.links = [];
13806         var t = this.autoLinkWikiWords() ? 0 : 1;
13807         var tiddlerLinkRegExp = t==0 ? config.textPrimitives.tiddlerAnyLinkRegExp : config.textPrimitives.tiddlerForcedLinkRegExp;
13808         tiddlerLinkRegExp.lastIndex = 0;
13809         var formatMatch = tiddlerLinkRegExp.exec(this.text);
13810         while(formatMatch) {
13811                 var lastIndex = tiddlerLinkRegExp.lastIndex;
13812                 if(t==0 && formatMatch[1] && formatMatch[1] != this.title) {
13813                         // wikiWordLink
13814                         if(formatMatch.index > 0) {
13815                                 var preRegExp = new RegExp(config.textPrimitives.unWikiLink+"|"+config.textPrimitives.anyLetter,"mg");
13816                                 preRegExp.lastIndex = formatMatch.index-1;
13817                                 var preMatch = preRegExp.exec(this.text);
13818                                 if(preMatch.index != formatMatch.index-1)
13819                                         this.links.pushUnique(formatMatch[1]);
13820                         } else {
13821                                 this.links.pushUnique(formatMatch[1]);
13822                         }
13823                 }
13824                 else if(formatMatch[2-t] && !config.formatterHelpers.isExternalLink(formatMatch[3-t])) // titledBrackettedLink
13825                         this.links.pushUnique(formatMatch[3-t]);
13826                 else if(formatMatch[4-t] && formatMatch[4-t] != this.title) // brackettedLink
13827                         this.links.pushUnique(formatMatch[4-t]);
13828                 tiddlerLinkRegExp.lastIndex = lastIndex;
13829                 formatMatch = tiddlerLinkRegExp.exec(this.text);
13830         }
13831         this.linksUpdated = true;
13832 };
13833
13834 Tiddler.prototype.getSubtitle = function()
13835 {
13836         var theModifier = this.modifier;
13837         if(!theModifier)
13838                 theModifier = config.messages.subtitleUnknown;
13839         var theModified = this.modified;
13840         if(theModified)
13841                 theModified = theModified.toLocaleString();
13842         else
13843                 theModified = config.messages.subtitleUnknown;
13844         return config.messages.tiddlerLinkTooltip.format([this.title,theModifier,theModified]);
13845 };
13846
13847 Tiddler.prototype.isReadOnly = function()
13848 {
13849         return readOnly;
13850 };
13851
13852 Tiddler.prototype.autoLinkWikiWords = function()
13853 {
13854         return !(this.isTagged("systemConfig") || this.isTagged("excludeMissing"));
13855 };
13856
13857 Tiddler.prototype.generateFingerprint = function()
13858 {
13859         return "0x" + Crypto.hexSha1Str(this.text);
13860 };
13861
13862 Tiddler.prototype.getServerType = function()
13863 {
13864         var serverType = null;
13865         if(this.fields && this.fields['server.type'])
13866                 serverType = this.fields['server.type'];
13867         if(!serverType)
13868                 serverType = this.fields['wikiformat'];
13869         if(serverType && !config.adaptors[serverType])
13870                 serverType = null;
13871         return serverType;
13872 };
13873
13874 Tiddler.prototype.getAdaptor = function()
13875 {
13876         var serverType = this.getServerType();
13877         if(serverType)
13878                 return new config.adaptors[serverType];
13879         else
13880                 return null;
13881 };
13882
13883 //--
13884 //-- TiddlyWiki() object contains Tiddler()s
13885 //--
13886
13887 function TiddlyWiki()
13888 {
13889         var tiddlers = {}; // Hashmap by name of tiddlers
13890         this.tiddlersUpdated = false;
13891         this.namedNotifications = []; // Array of {name:,notify:} of notification functions
13892         this.notificationLevel = 0;
13893         this.slices = {}; // map tiddlerName->(map sliceName->sliceValue). Lazy.
13894         this.clear = function() {
13895                 tiddlers = {};
13896                 this.setDirty(false);
13897         };
13898         this.fetchTiddler = function(title) {
13899                 return tiddlers[title];
13900         };
13901         this.deleteTiddler = function(title) {
13902                 delete this.slices[title];
13903                 delete tiddlers[title];
13904         };
13905         this.addTiddler = function(tiddler) {
13906                 delete this.slices[tiddler.title];
13907                 tiddlers[tiddler.title] = tiddler;
13908         };
13909         this.forEachTiddler = function(callback) {
13910                 for(var t in tiddlers) {
13911                         var tiddler = tiddlers[t];
13912                         if(tiddler instanceof Tiddler)
13913                                 callback.call(this,t,tiddler);
13914                 }
13915         };
13916 }
13917
13918 TiddlyWiki.prototype.setDirty = function(dirty)
13919 {
13920         this.dirty = dirty;
13921 };
13922
13923 TiddlyWiki.prototype.isDirty = function()
13924 {
13925         return this.dirty;
13926 };
13927
13928 TiddlyWiki.prototype.suspendNotifications = function()
13929 {
13930         this.notificationLevel--;
13931 };
13932
13933 TiddlyWiki.prototype.resumeNotifications = function()
13934 {
13935         this.notificationLevel++;
13936 };
13937
13938 // Invoke the notification handlers for a particular tiddler
13939 TiddlyWiki.prototype.notify = function(title,doBlanket)
13940 {
13941         if(!this.notificationLevel) {
13942                 for(var t=0; t<this.namedNotifications.length; t++) {
13943                         var n = this.namedNotifications[t];
13944                         if((n.name == null && doBlanket) || (n.name == title))
13945                                 n.notify(title);
13946                 }
13947         }
13948 };
13949
13950 // Invoke the notification handlers for all tiddlers
13951 TiddlyWiki.prototype.notifyAll = function()
13952 {
13953         if(!this.notificationLevel) {
13954                 for(var t=0; t<this.namedNotifications.length; t++) {
13955                         var n = this.namedNotifications[t];
13956                         if(n.name)
13957                                 n.notify(n.name);
13958                 }
13959         }
13960 };
13961
13962 // Add a notification handler to a tiddler
13963 TiddlyWiki.prototype.addNotification = function(title,fn)
13964 {
13965         for(var i=0; i<this.namedNotifications.length; i++) {
13966                 if((this.namedNotifications[i].name == title) && (this.namedNotifications[i].notify == fn))
13967                         return this;
13968         }
13969         this.namedNotifications.push({name: title, notify: fn});
13970         return this;
13971 };
13972
13973 TiddlyWiki.prototype.removeTiddler = function(title)
13974 {
13975         var tiddler = this.fetchTiddler(title);
13976         if(tiddler) {
13977                 this.deleteTiddler(title);
13978                 this.notify(title,true);
13979                 this.setDirty(true);
13980         }
13981 };
13982
13983 TiddlyWiki.prototype.tiddlerExists = function(title)
13984 {
13985         var t = this.fetchTiddler(title);
13986         return t != undefined;
13987 };
13988
13989 TiddlyWiki.prototype.isShadowTiddler = function(title)
13990 {
13991         return typeof config.shadowTiddlers[title] == "string";
13992 };
13993
13994 TiddlyWiki.prototype.getTiddler = function(title)
13995 {
13996         var t = this.fetchTiddler(title);
13997         if(t != undefined)
13998                 return t;
13999         else
14000                 return null;
14001 };
14002
14003 TiddlyWiki.prototype.getTiddlerText = function(title,defaultText)
14004 {
14005         var tiddler = this.fetchTiddler(title);
14006         if(tiddler)
14007                 return tiddler.text;
14008         if(!title)
14009                 return defaultText;
14010         var pos = title.indexOf(config.textPrimitives.sliceSeparator);
14011         if(pos != -1) {
14012                 var slice = this.getTiddlerSlice(title.substr(0,pos),title.substr(pos + config.textPrimitives.sliceSeparator.length));
14013                 if(slice)
14014                         return slice;
14015         }
14016         if(this.isShadowTiddler(title))
14017                 return config.shadowTiddlers[title];
14018         if(defaultText != undefined)
14019                 return defaultText;
14020         return null;
14021 };
14022
14023 TiddlyWiki.prototype.slicesRE = /(?:[\'\/]*~?([\.\w]+)[\'\/]*\:[\'\/]*\s*(.*?)\s*$)|(?:\|[\'\/]*~?([\.\w]+)\:?[\'\/]*\|\s*(.*?)\s*\|)/gm;
14024
14025 // @internal
14026 TiddlyWiki.prototype.calcAllSlices = function(title)
14027 {
14028         var slices = {};
14029         var text = this.getTiddlerText(title,"");
14030         this.slicesRE.lastIndex = 0;
14031         do {
14032                 var m = this.slicesRE.exec(text);
14033                 if(m) {
14034                         if(m[1])
14035                                 slices[m[1]] = m[2];
14036                         else
14037                                 slices[m[3]] = m[4];
14038                 }
14039         } while(m);
14040         return slices;
14041 };
14042
14043 // Returns the slice of text of the given name
14044 TiddlyWiki.prototype.getTiddlerSlice = function(title,sliceName)
14045 {
14046         var slices = this.slices[title];
14047         if(!slices) {
14048                 slices = this.calcAllSlices(title);
14049                 this.slices[title] = slices;
14050         }
14051         return slices[sliceName];
14052 };
14053
14054 // Build an hashmap of the specified named slices of a tiddler
14055 TiddlyWiki.prototype.getTiddlerSlices = function(title,sliceNames)
14056 {
14057         var r = {};
14058         for(var t=0; t<sliceNames.length; t++) {
14059                 var slice = this.getTiddlerSlice(title,sliceNames[t]);
14060                 if(slice)
14061                         r[sliceNames[t]] = slice;
14062         }
14063         return r;
14064 };
14065
14066 TiddlyWiki.prototype.getRecursiveTiddlerText = function(title,defaultText,depth)
14067 {
14068         var bracketRegExp = new RegExp("(?:\\[\\[([^\\]]+)\\]\\])","mg");
14069         var text = this.getTiddlerText(title,null);
14070         if(text == null)
14071                 return defaultText;
14072         var textOut = [];
14073         var lastPos = 0;
14074         do {
14075                 var match = bracketRegExp.exec(text);
14076                 if(match) {
14077                         textOut.push(text.substr(lastPos,match.index-lastPos));
14078                         if(match[1]) {
14079                                 if(depth <= 0)
14080                                         textOut.push(match[1]);
14081                                 else
14082                                         textOut.push(this.getRecursiveTiddlerText(match[1],"[[" + match[1] + "]]",depth-1));
14083                         }
14084                         lastPos = match.index + match[0].length;
14085                 } else {
14086                         textOut.push(text.substr(lastPos));
14087                 }
14088         } while(match);
14089         return textOut.join("");
14090 };
14091
14092 TiddlyWiki.prototype.setTiddlerTag = function(title,status,tag)
14093 {
14094         var tiddler = this.fetchTiddler(title);
14095         if(tiddler) {
14096                 var t = tiddler.tags.indexOf(tag);
14097                 if(t != -1)
14098                         tiddler.tags.splice(t,1);
14099                 if(status)
14100                         tiddler.tags.push(tag);
14101                 tiddler.changed();
14102                 this.incChangeCount(title);
14103                 this.notify(title,true);
14104                 this.setDirty(true);
14105         }
14106 };
14107
14108 TiddlyWiki.prototype.addTiddlerFields = function(title,fields)
14109 {
14110         var tiddler = this.fetchTiddler(title);
14111         if(!tiddler)
14112                 return;
14113         merge(tiddler.fields,fields);
14114         tiddler.changed();
14115         this.incChangeCount(title);
14116         this.notify(title,true);
14117         this.setDirty(true);
14118 };
14119
14120 TiddlyWiki.prototype.saveTiddler = function(title,newTitle,newBody,modifier,modified,tags,fields,clearChangeCount,created)
14121 {
14122         var tiddler = this.fetchTiddler(title);
14123         if(tiddler) {
14124                 created = created ? created : tiddler.created; // Preserve created date
14125                 this.deleteTiddler(title);
14126         } else {
14127                 created = created ? created : modified;
14128                 tiddler = new Tiddler();
14129         }
14130         tiddler.set(newTitle,newBody,modifier,modified,tags,created,fields);
14131         this.addTiddler(tiddler);
14132         if(clearChangeCount)
14133                 tiddler.clearChangeCount();
14134         else
14135                 tiddler.incChangeCount();
14136         if(title != newTitle)
14137                 this.notify(title,true);
14138         this.notify(newTitle,true);
14139         this.setDirty(true);
14140         return tiddler;
14141 };
14142
14143 // Reset the sync status of a freshly synced tiddler
14144 TiddlyWiki.prototype.resetTiddler = function(title)
14145 {
14146         var tiddler = this.fetchTiddler(title);
14147         if(tiddler) {
14148                 tiddler.clearChangeCount();
14149                 this.notify(title,true);
14150                 this.setDirty(true);
14151         }
14152 };
14153
14154 TiddlyWiki.prototype.incChangeCount = function(title)
14155 {
14156         var tiddler = this.fetchTiddler(title);
14157         if(tiddler)
14158                 tiddler.incChangeCount();
14159 };
14160
14161 TiddlyWiki.prototype.createTiddler = function(title)
14162 {
14163         var tiddler = this.fetchTiddler(title);
14164         if(!tiddler) {
14165                 tiddler = new Tiddler();
14166                 tiddler.title = title;
14167                 this.addTiddler(tiddler);
14168                 this.setDirty(true);
14169         }
14170         return tiddler;
14171 };
14172
14173 // Load contents of a TiddlyWiki from an HTML DIV
14174 TiddlyWiki.prototype.loadFromDiv = function(src,idPrefix,noUpdate)
14175 {
14176         this.idPrefix = idPrefix;
14177         var storeElem = (typeof src == "string") ? document.getElementById(src) : src;
14178         if(!storeElem)
14179                 return;
14180         var tiddlers = this.getLoader().loadTiddlers(this,storeElem.childNodes);
14181         this.setDirty(false);
14182         if(!noUpdate) {
14183                 for(var i = 0;i<tiddlers.length; i++)
14184                         tiddlers[i].changed();
14185         }
14186 };
14187
14188 // Load contents of a TiddlyWiki from a string
14189 // Returns null if there's an error
14190 TiddlyWiki.prototype.importTiddlyWiki = function(text)
14191 {
14192         var posDiv = locateStoreArea(text);
14193         if(!posDiv)
14194                 return null;
14195         var content = "<" + "html><" + "body>" + text.substring(posDiv[0],posDiv[1] + endSaveArea.length) + "<" + "/body><" + "/html>";
14196         // Create the iframe
14197         var iframe = document.createElement("iframe");
14198         iframe.style.display = "none";
14199         document.body.appendChild(iframe);
14200         var doc = iframe.document;
14201         if(iframe.contentDocument)
14202                 doc = iframe.contentDocument; // For NS6
14203         else if(iframe.contentWindow)
14204                 doc = iframe.contentWindow.document; // For IE5.5 and IE6
14205         // Put the content in the iframe
14206         doc.open();
14207         doc.writeln(content);
14208         doc.close();
14209         // Load the content into a TiddlyWiki() object
14210         var storeArea = doc.getElementById("storeArea");
14211         this.loadFromDiv(storeArea,"store");
14212         // Get rid of the iframe
14213         iframe.parentNode.removeChild(iframe);
14214         return this;
14215 };
14216
14217 TiddlyWiki.prototype.updateTiddlers = function()
14218 {
14219         this.tiddlersUpdated = true;
14220         this.forEachTiddler(function(title,tiddler) {
14221                 tiddler.changed();
14222         });
14223 };
14224
14225 // Return all tiddlers formatted as an HTML string
14226 TiddlyWiki.prototype.allTiddlersAsHtml = function()
14227 {
14228         return store.getSaver().externalize(store);
14229 };
14230
14231 // Return an array of tiddlers matching a search regular expression
14232 TiddlyWiki.prototype.search = function(searchRegExp,sortField,excludeTag)
14233 {
14234         var candidates = this.reverseLookup("tags",excludeTag,false);
14235         var results = [];
14236         for(var t=0; t<candidates.length; t++) {
14237                 if((candidates[t].title.search(searchRegExp) != -1) || (candidates[t].text.search(searchRegExp) != -1))
14238                         results.push(candidates[t]);
14239         }
14240         if(!sortField)
14241                 sortField = "title";
14242         results.sort(function(a,b) {return a[sortField] < b[sortField] ? -1 : (a[sortField] == b[sortField] ? 0 : +1);});
14243         return results;
14244 };
14245
14246 // 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
14247 TiddlyWiki.prototype.getTags = function(excludeTag)
14248 {
14249         var results = [];
14250         this.forEachTiddler(function(title,tiddler) {
14251                 for(var g=0; g<tiddler.tags.length; g++) {
14252                         var tag = tiddler.tags[g];
14253                         if(excludeTag) {
14254                                 var t = store.fetchTiddler(tag);
14255                                 if(t && t.isTagged(excludeTag))
14256                                         return false;
14257                         }
14258                         var f = false;
14259                         for(var c=0; c<results.length; c++) {
14260                                 if(results[c][0] == tag) {
14261                                         f = true;
14262                                         results[c][1]++;
14263                                 }
14264                         }
14265                         if(!f)
14266                                 results.push([tag,1]);
14267                 }
14268         });
14269         results.sort(function(a,b) {return a[0].toLowerCase() < b[0].toLowerCase() ? -1 : (a[0].toLowerCase() == b[0].toLowerCase() ? 0 : +1);});
14270         return results;
14271 };
14272
14273 // Return an array of the tiddlers that are tagged with a given tag
14274 TiddlyWiki.prototype.getTaggedTiddlers = function(tag,sortField)
14275 {
14276         return this.reverseLookup("tags",tag,true,sortField);
14277 };
14278
14279 // Return an array of the tiddlers that link to a given tiddler
14280 TiddlyWiki.prototype.getReferringTiddlers = function(title,unusedParameter,sortField)
14281 {
14282         if(!this.tiddlersUpdated)
14283                 this.updateTiddlers();
14284         return this.reverseLookup("links",title,true,sortField);
14285 };
14286
14287 // Return an array of the tiddlers that do or do not have a specified entry in the specified storage array (ie, "links" or "tags")
14288 // lookupMatch == true to match tiddlers, false to exclude tiddlers
14289 TiddlyWiki.prototype.reverseLookup = function(lookupField,lookupValue,lookupMatch,sortField)
14290 {
14291         var results = [];
14292         this.forEachTiddler(function(title,tiddler) {
14293                 var f = !lookupMatch;
14294                 for(var lookup=0; lookup<tiddler[lookupField].length; lookup++) {
14295                         if(tiddler[lookupField][lookup] == lookupValue)
14296                                 f = lookupMatch;
14297                 }
14298                 if(f)
14299                         results.push(tiddler);
14300         });
14301         if(!sortField)
14302                 sortField = "title";
14303         results.sort(function(a,b) {return a[sortField] < b[sortField] ? -1 : (a[sortField] == b[sortField] ? 0 : +1);});
14304         return results;
14305 };
14306
14307 // Return the tiddlers as a sorted array
14308 TiddlyWiki.prototype.getTiddlers = function(field,excludeTag)
14309 {
14310         var results = [];
14311         this.forEachTiddler(function(title,tiddler) {
14312                 if(excludeTag == undefined || !tiddler.isTagged(excludeTag))
14313                         results.push(tiddler);
14314         });
14315         if(field)
14316                 results.sort(function(a,b) {return a[field] < b[field] ? -1 : (a[field] == b[field] ? 0 : +1);});
14317         return results;
14318 };
14319
14320 // Return array of names of tiddlers that are referred to but not defined
14321 TiddlyWiki.prototype.getMissingLinks = function(sortField)
14322 {
14323         if(!this.tiddlersUpdated)
14324                 this.updateTiddlers();
14325         var results = [];
14326         this.forEachTiddler(function (title,tiddler) {
14327                 if(tiddler.isTagged("excludeMissing") || tiddler.isTagged("systemConfig"))
14328                         return;
14329                 for(var n=0; n<tiddler.links.length;n++) {
14330                         var link = tiddler.links[n];
14331                         if(this.fetchTiddler(link) == null && !this.isShadowTiddler(link))
14332                                 results.pushUnique(link);
14333                 }
14334         });
14335         results.sort();
14336         return results;
14337 };
14338
14339 // Return an array of names of tiddlers that are defined but not referred to
14340 TiddlyWiki.prototype.getOrphans = function()
14341 {
14342         var results = [];
14343         this.forEachTiddler(function (title,tiddler) {
14344                 if(this.getReferringTiddlers(title).length == 0 && !tiddler.isTagged("excludeLists"))
14345                         results.push(title);
14346         });
14347         results.sort();
14348         return results;
14349 };
14350
14351 // Return an array of names of all the shadow tiddlers
14352 TiddlyWiki.prototype.getShadowed = function()
14353 {
14354         var results = [];
14355         for(var t in config.shadowTiddlers) {
14356                 if(typeof config.shadowTiddlers[t] == "string")
14357                         results.push(t);
14358         }
14359         results.sort();
14360         return results;
14361 };
14362
14363 // Return an array of tiddlers that have been touched since they were downloaded or created
14364 TiddlyWiki.prototype.getTouched = function()
14365 {
14366         var results = [];
14367         this.forEachTiddler(function(title,tiddler) {
14368                 if(tiddler.isTouched())
14369                         results.push(tiddler);
14370                 });
14371         results.sort();
14372         return results;
14373 };
14374
14375 // Resolves a Tiddler reference or tiddler title into a Tiddler object, or null if it doesn't exist
14376 TiddlyWiki.prototype.resolveTiddler = function(tiddler)
14377 {
14378         var t = (typeof tiddler == 'string') ? this.getTiddler(tiddler) : tiddler;
14379         return t instanceof Tiddler ? t : null;
14380 };
14381
14382 TiddlyWiki.prototype.getLoader = function()
14383 {
14384         if(!this.loader)
14385                 this.loader = new TW21Loader();
14386         return this.loader;
14387 };
14388
14389 TiddlyWiki.prototype.getSaver = function()
14390 {
14391         if(!this.saver)
14392                 this.saver = new TW21Saver();
14393         return this.saver;
14394 };
14395
14396 // Returns true if path is a valid field name (path),
14397 // i.e. a sequence of identifiers, separated by '.'
14398 TiddlyWiki.isValidFieldName = function(name)
14399 {
14400         var match = /[a-zA-Z_]\w*(\.[a-zA-Z_]\w*)*/.exec(name);
14401         return match && (match[0] == name);
14402 };
14403
14404 // Throws an exception when name is not a valid field name.
14405 TiddlyWiki.checkFieldName = function(name)
14406 {
14407         if(!TiddlyWiki.isValidFieldName(name))
14408                 throw config.messages.invalidFieldName.format([name]);
14409 };
14410
14411 function StringFieldAccess(n,readOnly)
14412 {
14413         this.set = readOnly ?
14414                         function(t,v) {if(v != t[n]) throw config.messages.fieldCannotBeChanged.format([n]);} :
14415                         function(t,v) {if(v != t[n]) {t[n] = v; return true;}};
14416         this.get = function(t) {return t[n];};
14417 }
14418
14419 function DateFieldAccess(n)
14420 {
14421         this.set = function(t,v) {
14422                 var d = v instanceof Date ? v : Date.convertFromYYYYMMDDHHMM(v);
14423                 if(d != t[n]) {
14424                         t[n] = d; return true;
14425                 }
14426         };
14427         this.get = function(t) {return t[n].convertToYYYYMMDDHHMM();};
14428 }
14429
14430 function LinksFieldAccess(n)
14431 {
14432         this.set = function(t,v) {
14433                 var s = (typeof v == "string") ? v.readBracketedList() : v;
14434                 if(s.toString() != t[n].toString()) {
14435                         t[n] = s; return true;
14436                 }
14437         };
14438         this.get = function(t) {return String.encodeTiddlyLinkList(t[n]);};
14439 }
14440
14441 TiddlyWiki.standardFieldAccess = {
14442         // The set functions return true when setting the data has changed the value.
14443         "title":    new StringFieldAccess("title",true),
14444         // Handle the "tiddler" field name as the title
14445         "tiddler":  new StringFieldAccess("title",true),
14446         "text":     new StringFieldAccess("text"),
14447         "modifier": new StringFieldAccess("modifier"),
14448         "modified": new DateFieldAccess("modified"),
14449         "created":  new DateFieldAccess("created"),
14450         "tags":     new LinksFieldAccess("tags")
14451 };
14452
14453 TiddlyWiki.isStandardField = function(name)
14454 {
14455         return TiddlyWiki.standardFieldAccess[name] != undefined;
14456 };
14457
14458 // Sets the value of the given field of the tiddler to the value.
14459 // Setting an ExtendedField's value to null or undefined removes the field.
14460 // Setting a namespace to undefined removes all fields of that namespace.
14461 // The fieldName is case-insensitive.
14462 // All values will be converted to a string value.
14463 TiddlyWiki.prototype.setValue = function(tiddler,fieldName,value)
14464 {
14465         TiddlyWiki.checkFieldName(fieldName);
14466         var t = this.resolveTiddler(tiddler);
14467         if(!t)
14468                 return;
14469         fieldName = fieldName.toLowerCase();
14470         var isRemove = (value === undefined) || (value === null);
14471         var accessor = TiddlyWiki.standardFieldAccess[fieldName];
14472         if(accessor) {
14473                 if(isRemove)
14474                         // don't remove StandardFields
14475                         return;
14476                 var h = TiddlyWiki.standardFieldAccess[fieldName];
14477                 if(!h.set(t,value))
14478                         return;
14479         } else {
14480                 var oldValue = t.fields[fieldName];
14481                 if(isRemove) {
14482                         if(oldValue !== undefined) {
14483                                 // deletes a single field
14484                                 delete t.fields[fieldName];
14485                         } else {
14486                                 // no concrete value is defined for the fieldName
14487                                 // so we guess this is a namespace path.
14488                                 // delete all fields in a namespace
14489                                 var re = new RegExp('^'+fieldName+'\\.');
14490                                 var dirty = false;
14491                                 for(var n in t.fields) {
14492                                         if(n.match(re)) {
14493                                                 delete t.fields[n];
14494                                                 dirty = true;
14495                                         }
14496                                 }
14497                                 if(!dirty)
14498                                         return;
14499                         }
14500                 } else {
14501                         // the "normal" set case. value is defined (not null/undefined)
14502                         // For convenience provide a nicer conversion Date->String
14503                         value = value instanceof Date ? value.convertToYYYYMMDDHHMMSSMMM() : String(value);
14504                         if(oldValue == value)
14505                                 return;
14506                         t.fields[fieldName] = value;
14507                 }
14508         }
14509         // When we are here the tiddler/store really was changed.
14510         this.notify(t.title,true);
14511         if(!fieldName.match(/^temp\./))
14512                 this.setDirty(true);
14513 };
14514
14515 // Returns the value of the given field of the tiddler.
14516 // The fieldName is case-insensitive.
14517 // Will only return String values (or undefined).
14518 TiddlyWiki.prototype.getValue = function(tiddler,fieldName)
14519 {
14520         var t = this.resolveTiddler(tiddler);
14521         if(!t)
14522                 return undefined;
14523         fieldName = fieldName.toLowerCase();
14524         var accessor = TiddlyWiki.standardFieldAccess[fieldName];
14525         if(accessor) {
14526                 return accessor.get(t);
14527         }
14528         return t.fields[fieldName];
14529 };
14530
14531 // Calls the callback function for every field in the tiddler.
14532 // When callback function returns a non-false value the iteration stops
14533 // and that value is returned.
14534 // The order of the fields is not defined.
14535 // @param callback a function(tiddler,fieldName,value).
14536 TiddlyWiki.prototype.forEachField = function(tiddler,callback,onlyExtendedFields)
14537 {
14538         var t = this.resolveTiddler(tiddler);
14539         if(!t)
14540                 return undefined;
14541         for(var n in t.fields) {
14542                 var result = callback(t,n,t.fields[n]);
14543                 if(result)
14544                         return result;
14545                 }
14546         if(onlyExtendedFields)
14547                 return undefined;
14548         for(var n in TiddlyWiki.standardFieldAccess) {
14549                 if(n == "tiddler")
14550                         // even though the "title" field can also be referenced through the name "tiddler"
14551                         // we only visit this field once.
14552                         continue;
14553                 var result = callback(t,n,TiddlyWiki.standardFieldAccess[n].get(t));
14554                 if(result)
14555                         return result;
14556         }
14557         return undefined;
14558 };
14559
14560 //--
14561 //-- Story functions
14562 //--
14563
14564 function Story(container,idPrefix)
14565 {
14566         this.container = container;
14567         this.idPrefix = idPrefix;
14568         this.highlightRegExp = null;
14569 }
14570
14571 Story.prototype.forEachTiddler = function(fn)
14572 {
14573         var place = document.getElementById(this.container);
14574         if(!place)
14575                 return;
14576         var e = place.firstChild;
14577         while(e) {
14578                 var n = e.nextSibling;
14579                 var title = e.getAttribute("tiddler");
14580                 fn.call(this,title,e);
14581                 e = n;
14582         }
14583 };
14584
14585 Story.prototype.displayTiddlers = function(srcElement,titles,template,animate,unused,customFields,toggle)
14586 {
14587         for(var t = titles.length-1;t>=0;t--)
14588                 this.displayTiddler(srcElement,titles[t],template,animate,unused,customFields);
14589 };
14590
14591 Story.prototype.displayTiddler = function(srcElement,title,template,animate,unused,customFields,toggle)
14592 {
14593         var place = document.getElementById(this.container);
14594         var tiddlerElem = document.getElementById(this.idPrefix + title);
14595         if(tiddlerElem) {
14596                 if(toggle)
14597                         this.closeTiddler(title,true);
14598                 else
14599                         this.refreshTiddler(title,template,false,customFields);
14600         } else {
14601                 var before = this.positionTiddler(srcElement);
14602                 tiddlerElem = this.createTiddler(place,before,title,template,customFields);
14603         }
14604         if(srcElement && typeof srcElement !== "string") {
14605                 if(config.options.chkAnimate && (animate == undefined || animate == true) && anim && typeof Zoomer == "function" && typeof Scroller == "function")
14606                         anim.startAnimating(new Zoomer(title,srcElement,tiddlerElem),new Scroller(tiddlerElem));
14607                 else
14608                         window.scrollTo(0,ensureVisible(tiddlerElem));
14609         }
14610 };
14611
14612 Story.prototype.positionTiddler = function(srcElement)
14613 {
14614         var place = document.getElementById(this.container);
14615         var before = null;
14616         if(typeof srcElement == "string") {
14617                 switch(srcElement) {
14618                         case "top":
14619                                 before = place.firstChild;
14620                                 break;
14621                         case "bottom":
14622                                 before = null;
14623                                 break;
14624                 }
14625         } else {
14626                 var after = this.findContainingTiddler(srcElement);
14627                 if(after == null) {
14628                         before = place.firstChild;
14629                 } else if(after.nextSibling) {
14630                         before = after.nextSibling;
14631                         if(before.nodeType != 1)
14632                                 before = null;
14633                 }
14634         }
14635         return before;
14636 };
14637
14638 Story.prototype.createTiddler = function(place,before,title,template,customFields)
14639 {
14640         var tiddlerElem = createTiddlyElement(null,"div",this.idPrefix + title,"tiddler");
14641         tiddlerElem.setAttribute("refresh","tiddler");
14642         if(customFields)
14643                 tiddlerElem.setAttribute("tiddlyFields",customFields);
14644         place.insertBefore(tiddlerElem,before);
14645         var defaultText = null;
14646         if(!store.tiddlerExists(title) && !store.isShadowTiddler(title))
14647                 defaultText = this.loadMissingTiddler(title,customFields,tiddlerElem);
14648         this.refreshTiddler(title,template,false,customFields,defaultText);
14649         return tiddlerElem;
14650 };
14651
14652 Story.prototype.loadMissingTiddler = function(title,fields,tiddlerElem)
14653 {
14654         var tiddler = new Tiddler(title);
14655         tiddler.fields = typeof fields == "string" ?  fields.decodeHashMap() : (fields ? fields : {});
14656         var serverType = tiddler.getServerType();
14657         var host = tiddler.fields['server.host'];
14658         var workspace = tiddler.fields['server.workspace'];
14659         if(!serverType | !host)
14660                 return null;
14661         var sm = new SyncMachine(serverType,{
14662                         start: function() {
14663                                 return this.openHost(host,"openWorkspace");
14664                         },
14665                         openWorkspace: function() {
14666                                 return this.openWorkspace(workspace,"getTiddler");
14667                         },
14668                         getTiddler: function() {
14669                                 return this.getTiddler(title,"gotTiddler");
14670                         },
14671                         gotTiddler: function(tiddler) {
14672                                 if(tiddler && tiddler.text) {
14673                                         var downloaded = new Date();
14674                                         if(!tiddler.created)
14675                                                 tiddler.created = downloaded;
14676                                         if(!tiddler.modified)
14677                                                 tiddler.modified = tiddler.created;
14678                                         store.saveTiddler(tiddler.title,tiddler.title,tiddler.text,tiddler.modifier,tiddler.modified,tiddler.tags,tiddler.fields,true,tiddler.created);
14679                                         autoSaveChanges();
14680                                 }
14681                                 delete this;
14682                                 return true;
14683                         },
14684                         error: function(message) {
14685                                 displayMessage("Error loading missing tiddler from %0: %1".format([host,message]));
14686                         }
14687                 });
14688         sm.go();
14689         return config.messages.loadingMissingTiddler.format([title,serverType,host,workspace]);
14690 };
14691
14692 Story.prototype.chooseTemplateForTiddler = function(title,template)
14693 {
14694         if(!template)
14695                 template = DEFAULT_VIEW_TEMPLATE;
14696         if(template == DEFAULT_VIEW_TEMPLATE || template == DEFAULT_EDIT_TEMPLATE)
14697                 template = config.tiddlerTemplates[template];
14698         return template;
14699 };
14700
14701 Story.prototype.getTemplateForTiddler = function(title,template,tiddler)
14702 {
14703         return store.getRecursiveTiddlerText(template,null,10);
14704 };
14705
14706 Story.prototype.refreshTiddler = function(title,template,force,customFields,defaultText)
14707 {
14708         var tiddlerElem = document.getElementById(this.idPrefix + title);
14709         if(tiddlerElem) {
14710                 if(tiddlerElem.getAttribute("dirty") == "true" && !force)
14711                         return tiddlerElem;
14712                 template = this.chooseTemplateForTiddler(title,template);
14713                 var currTemplate = tiddlerElem.getAttribute("template");
14714                 if((template != currTemplate) || force) {
14715                         var tiddler = store.getTiddler(title);
14716                         if(!tiddler) {
14717                                 tiddler = new Tiddler();
14718                                 if(store.isShadowTiddler(title)) {
14719                                         tiddler.set(title,store.getTiddlerText(title),config.views.wikified.shadowModifier,version.date,[],version.date);
14720                                 } else {
14721                                         var text = template=="EditTemplate" ?
14722                                                                 config.views.editor.defaultText.format([title]) :
14723                                                                 config.views.wikified.defaultText.format([title]);
14724                                         text = defaultText ? defaultText : text;
14725                                         var fields = customFields ? customFields.decodeHashMap() : null;
14726                                         tiddler.set(title,text,config.views.wikified.defaultModifier,version.date,[],version.date,fields);
14727                                 }
14728                         }
14729                         tiddlerElem.setAttribute("tags",tiddler.tags.join(" "));
14730                         tiddlerElem.setAttribute("tiddler",title);
14731                         tiddlerElem.setAttribute("template",template);
14732                         var me = this;
14733                         tiddlerElem.onmouseover = this.onTiddlerMouseOver;
14734                         tiddlerElem.onmouseout = this.onTiddlerMouseOut;
14735                         tiddlerElem.ondblclick = this.onTiddlerDblClick;
14736                         tiddlerElem[window.event?"onkeydown":"onkeypress"] = this.onTiddlerKeyPress;
14737                         var html = this.getTemplateForTiddler(title,template,tiddler);
14738                         tiddlerElem.innerHTML = html;
14739                         applyHtmlMacros(tiddlerElem,tiddler);
14740                         if(store.getTaggedTiddlers(title).length > 0)
14741                                 addClass(tiddlerElem,"isTag");
14742                         else
14743                                 removeClass(tiddlerElem,"isTag");
14744                         if(!store.tiddlerExists(title)) {
14745                                 if(store.isShadowTiddler(title))
14746                                         addClass(tiddlerElem,"shadow");
14747                                 else
14748                                         addClass(tiddlerElem,"missing");
14749                         } else {
14750                                 removeClass(tiddlerElem,"shadow");
14751                                 removeClass(tiddlerElem,"missing");
14752                         }
14753                         if(customFields)
14754                                 this.addCustomFields(tiddlerElem,customFields);
14755                         forceReflow();
14756                 }
14757         }
14758         return tiddlerElem;
14759 };
14760
14761 Story.prototype.addCustomFields = function(place,customFields)
14762 {
14763         var fields = customFields.decodeHashMap();
14764         var w = document.createElement("div");
14765         w.style.display = "none";
14766         place.appendChild(w);
14767         for(var t in fields) {
14768                 var e = document.createElement("input");
14769                 e.setAttribute("type","text");
14770                 e.setAttribute("value",fields[t]);
14771                 w.appendChild(e);
14772                 e.setAttribute("edit",t);
14773         }
14774 };
14775
14776 Story.prototype.refreshAllTiddlers = function() 
14777 {
14778         var place = document.getElementById(this.container);
14779         var e = place.firstChild;
14780         if(!e)
14781                 return;
14782         this.refreshTiddler(e.getAttribute("tiddler"),e.getAttribute("template"),true);
14783         while((e = e.nextSibling) != null) 
14784                 this.refreshTiddler(e.getAttribute("tiddler"),e.getAttribute("template"),true);
14785 };
14786
14787 Story.prototype.onTiddlerMouseOver = function(e)
14788 {
14789         if(window.addClass instanceof Function)
14790                 addClass(this,"selected");
14791 };
14792
14793 Story.prototype.onTiddlerMouseOut = function(e)
14794 {
14795         if(window.removeClass instanceof Function)
14796                 removeClass(this,"selected");
14797 };
14798
14799 Story.prototype.onTiddlerDblClick = function(e)
14800 {
14801         if(!e) var e = window.event;
14802         var theTarget = resolveTarget(e);
14803         if(theTarget && theTarget.nodeName.toLowerCase() != "input" && theTarget.nodeName.toLowerCase() != "textarea") {
14804                 if(document.selection && document.selection.empty)
14805                         document.selection.empty();
14806                 config.macros.toolbar.invokeCommand(this,"defaultCommand",e);
14807                 e.cancelBubble = true;
14808                 if(e.stopPropagation) e.stopPropagation();
14809                 return true;
14810         } else {
14811                 return false;
14812         }
14813 };
14814
14815 Story.prototype.onTiddlerKeyPress = function(e)
14816 {
14817         if(!e) var e = window.event;
14818         clearMessage();
14819         var consume = false; 
14820         var title = this.getAttribute("tiddler");
14821         var target = resolveTarget(e);
14822         switch(e.keyCode) {
14823                 case 9: // Tab
14824                         if(config.options.chkInsertTabs && target.tagName.toLowerCase() == "textarea") {
14825                                 replaceSelection(target,String.fromCharCode(9));
14826                                 consume = true; 
14827                         }
14828                         if(config.isOpera) {
14829                                 target.onblur = function() {
14830                                         this.focus();
14831                                         this.onblur = null;
14832                                 };
14833                         }
14834                         break;
14835                 case 13: // Ctrl-Enter
14836                 case 10: // Ctrl-Enter on IE PC
14837                 case 77: // Ctrl-Enter is "M" on some platforms
14838                         if(e.ctrlKey) {
14839                                 blurElement(this);
14840                                 config.macros.toolbar.invokeCommand(this,"defaultCommand",e);
14841                                 consume = true;
14842                         }
14843                         break; 
14844                 case 27: // Escape
14845                         blurElement(this);
14846                         config.macros.toolbar.invokeCommand(this,"cancelCommand",e);
14847                         consume = true;
14848                         break;
14849         }
14850         e.cancelBubble = consume;
14851         if(consume) {
14852                 if(e.stopPropagation) e.stopPropagation(); // Stop Propagation
14853                 e.returnValue = true; // Cancel The Event in IE
14854                 if(e.preventDefault ) e.preventDefault(); // Cancel The Event in Moz
14855         }
14856         return !consume;
14857 };
14858
14859 Story.prototype.getTiddlerField = function(title,field)
14860 {
14861         var tiddlerElem = document.getElementById(this.idPrefix + title);
14862         var e = null;
14863         if(tiddlerElem != null) {
14864                 var children = tiddlerElem.getElementsByTagName("*");
14865                 for(var t=0; t<children.length; t++) {
14866                         var c = children[t];
14867                         if(c.tagName.toLowerCase() == "input" || c.tagName.toLowerCase() == "textarea") {
14868                                 if(!e)
14869                                         e = c;
14870                                 if(c.getAttribute("edit") == field)
14871                                         e = c;
14872                         }
14873                 }
14874         }
14875         return e;
14876 };
14877
14878 Story.prototype.focusTiddler = function(title,field)
14879 {
14880         var e = this.getTiddlerField(title,field);
14881         if(e) {
14882                 e.focus();
14883                 e.select();
14884         }
14885 };
14886
14887 Story.prototype.blurTiddler = function(title)
14888 {
14889         var tiddlerElem = document.getElementById(this.idPrefix + title);
14890         if(tiddlerElem != null && tiddlerElem.focus && tiddlerElem.blur) {
14891                 tiddlerElem.focus();
14892                 tiddlerElem.blur();
14893         }
14894 };
14895
14896 Story.prototype.setTiddlerField = function(title,tag,mode,field)
14897 {
14898         var c = story.getTiddlerField(title,field);
14899
14900         var tags = c.value.readBracketedList();
14901         tags.setItem(tag,mode);
14902         c.value = String.encodeTiddlyLinkList(tags);
14903 };
14904
14905 Story.prototype.setTiddlerTag = function(title,tag,mode)
14906 {
14907         Story.prototype.setTiddlerField(title,tag,mode,"tags");
14908 };
14909
14910 Story.prototype.closeTiddler = function(title,animate,unused)
14911 {
14912         var tiddlerElem = document.getElementById(this.idPrefix + title);
14913         if(tiddlerElem != null) {
14914                 clearMessage();
14915                 this.scrubTiddler(tiddlerElem);
14916                 if(config.options.chkAnimate && animate && anim && typeof Slider == "function")
14917                         anim.startAnimating(new Slider(tiddlerElem,false,null,"all"));
14918                 else {
14919                         removeNode(tiddlerElem);
14920                         forceReflow();
14921                 }
14922         }
14923 };
14924
14925 Story.prototype.scrubTiddler = function(tiddlerElem)
14926 {
14927         tiddlerElem.id = null;
14928 };
14929
14930 Story.prototype.setDirty = function(title,dirty)
14931 {
14932         var tiddlerElem = document.getElementById(this.idPrefix + title);
14933         if(tiddlerElem != null)
14934                 tiddlerElem.setAttribute("dirty",dirty ? "true" : "false");
14935 };
14936
14937 Story.prototype.isDirty = function(title)
14938 {
14939         var tiddlerElem = document.getElementById(this.idPrefix + title);
14940         if(tiddlerElem != null)
14941                 return tiddlerElem.getAttribute("dirty") == "true";
14942         return null;
14943 };
14944
14945 Story.prototype.areAnyDirty = function()
14946 {
14947         var r = false;
14948         this.forEachTiddler(function(title,element) {
14949                 if(this.isDirty(title))
14950                         r = true;
14951         });
14952         return r;
14953 };
14954
14955 Story.prototype.closeAllTiddlers = function(exclude)
14956 {
14957         clearMessage();
14958         this.forEachTiddler(function(title,element) {
14959                 if((title != exclude) && element.getAttribute("dirty") != "true")
14960                         this.closeTiddler(title);
14961         });
14962         window.scrollTo(0,ensureVisible(this.container));
14963 };
14964
14965 Story.prototype.isEmpty = function()
14966 {
14967         var place = document.getElementById(this.container);
14968         return place && place.firstChild == null;
14969 };
14970
14971 Story.prototype.search = function(text,useCaseSensitive,useRegExp)
14972 {
14973         this.closeAllTiddlers();
14974         highlightHack = new RegExp(useRegExp ?   text : text.escapeRegExp(),useCaseSensitive ? "mg" : "img");
14975         var matches = store.search(highlightHack,"title","excludeSearch");
14976         var titles = [];
14977         for(var t=0;t<matches.length;t++)
14978                 titles.push(matches[t].title);
14979         this.displayTiddlers(null,titles);
14980         highlightHack = null;
14981         var q = useRegExp ? "/" : "'";
14982         if(matches.length > 0)
14983                 displayMessage(config.macros.search.successMsg.format([titles.length.toString(),q + text + q]));
14984         else
14985                 displayMessage(config.macros.search.failureMsg.format([q + text + q]));
14986 };
14987
14988 Story.prototype.findContainingTiddler = function(e)
14989 {
14990         while(e && !hasClass(e,"tiddler"))
14991                 e = e.parentNode;
14992         return e;
14993 };
14994
14995 Story.prototype.gatherSaveFields = function(e,fields)
14996 {
14997         if(e && e.getAttribute) {
14998                 var f = e.getAttribute("edit");
14999                 if(f)
15000                         fields[f] = e.value.replace(/\r/mg,"");
15001                 if(e.hasChildNodes()) {
15002                         var c = e.childNodes;
15003                         for(var t=0; t<c.length; t++)
15004                                 this.gatherSaveFields(c[t],fields);
15005                 }
15006         }
15007 };
15008
15009 Story.prototype.hasChanges = function(title)
15010 {
15011         var e = document.getElementById(this.idPrefix + title);
15012         if(e != null) {
15013                 var fields = {};
15014                 this.gatherSaveFields(e,fields);
15015                 var tiddler = store.fetchTiddler(title);
15016                 if(!tiddler)
15017                         return false;
15018                 for(var n in fields) {
15019                         if(store.getValue(title,n) != fields[n])
15020                                 return true;
15021                 }
15022         }
15023         return false;
15024 };
15025
15026 Story.prototype.saveTiddler = function(title,minorUpdate)
15027 {
15028         var tiddlerElem = document.getElementById(this.idPrefix + title);
15029         if(tiddlerElem != null) {
15030                 var fields = {};
15031                 this.gatherSaveFields(tiddlerElem,fields);
15032                 var newTitle = fields.title ? fields.title : title;
15033                 if(store.tiddlerExists(newTitle) && newTitle != title) {
15034                         if(!confirm(config.messages.overwriteWarning.format([newTitle.toString()])))
15035                                 return null;
15036                 }
15037                 if(newTitle != title)
15038                         this.closeTiddler(newTitle,false);
15039                 tiddlerElem.id = this.idPrefix + newTitle;
15040                 tiddlerElem.setAttribute("tiddler",newTitle);
15041                 tiddlerElem.setAttribute("template",DEFAULT_VIEW_TEMPLATE);
15042                 tiddlerElem.setAttribute("dirty","false");
15043                 if(config.options.chkForceMinorUpdate)
15044                         minorUpdate = !minorUpdate;
15045                 if(!store.tiddlerExists(newTitle))
15046                         minorUpdate = false;
15047                 var newDate = new Date();
15048                 var extendedFields = store.tiddlerExists(newTitle) ? store.fetchTiddler(newTitle).fields : {};
15049                 for(var n in fields) {
15050                         if(!TiddlyWiki.isStandardField(n))
15051                                 extendedFields[n] = fields[n];
15052                         }
15053                 var tiddler = store.saveTiddler(title,newTitle,fields.text,minorUpdate ? undefined : config.options.txtUserName,minorUpdate ? undefined : newDate,fields.tags,extendedFields);
15054                 autoSaveChanges(null,[tiddler]);
15055                 return newTitle;
15056         }
15057         return null;
15058 };
15059
15060 Story.prototype.permaView = function()
15061 {
15062         var links = [];
15063         this.forEachTiddler(function(title,element) {
15064                 links.push(String.encodeTiddlyLink(title));
15065         });
15066         var t = encodeURIComponent(links.join(" "));
15067         if(t == "")
15068                 t = "#";
15069         if(window.location.hash != t)
15070                 window.location.hash = t;
15071 };
15072
15073 //--
15074 //-- Backstage
15075 //--
15076
15077 var backstage = {
15078         area: null,
15079         toolbar: null,
15080         button: null,
15081         showButton: null,
15082         hideButton: null,
15083         cloak: null,
15084         panel: null,
15085         panelBody: null,
15086         panelFooter: null,
15087         currTabName: null,
15088         currTabElem: null,
15089         content: null,
15090
15091         init: function() {
15092                 var cmb = config.messages.backstage;
15093                 this.area = document.getElementById("backstageArea");
15094                 this.toolbar = document.getElementById("backstageToolbar");
15095                 this.button = document.getElementById("backstageButton");
15096                 this.button.style.display = "block";
15097                 var t = cmb.open.text + " " + glyph("bentArrowLeft");
15098                 this.showButton = createTiddlyButton(this.button,t,cmb.open.tooltip,
15099                                                 function (e) {backstage.show(); return false;},null,"backstageShow");
15100                 t = glyph("bentArrowRight") + " " + cmb.close.text;
15101                 this.hideButton = createTiddlyButton(this.button,t,cmb.close.tooltip,
15102                                                 function (e) {backstage.hide(); return false;},null,"backstageHide");
15103                 this.cloak = document.getElementById("backstageCloak");
15104                 this.panel = document.getElementById("backstagePanel");
15105                 this.panelFooter = createTiddlyElement(this.panel,"div",null,"backstagePanelFooter");
15106                 this.panelBody = createTiddlyElement(this.panel,"div",null,"backstagePanelBody");
15107                 this.cloak.onmousedown = function(e) {
15108                         backstage.switchTab(null);
15109                 };
15110                 createTiddlyText(this.toolbar,cmb.prompt);
15111                 for(t=0; t<config.backstageTasks.length; t++) {
15112                         var taskName = config.backstageTasks[t];
15113                         var task = config.tasks[taskName];
15114                         var handler = task.action ? this.onClickCommand : this.onClickTab;
15115                         var text = task.text + (task.action ? "" : glyph("downTriangle"));
15116                         var btn = createTiddlyButton(this.toolbar,text,task.tooltip,handler,"backstageTab");
15117                         btn.setAttribute("task",taskName);
15118                         addClass(btn,task.action ? "backstageAction" : "backstageTask");
15119                         }
15120                 this.content = document.getElementById("contentWrapper");
15121                 if(config.options.chkBackstage)
15122                         this.show();
15123                 else
15124                         this.hide();
15125         },
15126         
15127         isVisible: function () {
15128                 return this.area ? this.area.style.display == "block" : false;
15129         },
15130         
15131         show: function() {
15132                 this.area.style.display = "block";
15133                 if(anim && config.options.chkAnimate) {
15134                         backstage.toolbar.style.left = findWindowWidth() + "px";
15135                         var p = [
15136                                 {style: "left", start: findWindowWidth(), end: 0, template: "%0px"}
15137                         ];
15138                         anim.startAnimating(new Morpher(backstage.toolbar,config.animDuration,p));
15139                 } else {
15140                         backstage.area.style.left = "0px";
15141                 }
15142                 this.showButton.style.display = "none";
15143                 this.hideButton.style.display = "block";
15144                 config.options.chkBackstage = true;
15145                 saveOptionCookie("chkBackstage");
15146                 addClass(this.content,"backstageVisible");
15147         },
15148
15149         hide: function() {
15150                 if(this.currTabElem) {
15151                         this.switchTab(null);
15152                 } else {
15153                         backstage.toolbar.style.left = "0px";
15154                         if(anim && config.options.chkAnimate) {
15155                                 var p = [
15156                                         {style: "left", start: 0, end: findWindowWidth(), template: "%0px"}
15157                                 ];
15158                                 var c = function(element,properties) {backstage.area.style.display = "none";};
15159                                 anim.startAnimating(new Morpher(backstage.toolbar,config.animDuration,p,c));
15160                         } else {
15161                                 this.area.style.display = "none";
15162                         }
15163                         this.showButton.style.display = "block";
15164                         this.hideButton.style.display = "none";
15165                         config.options.chkBackstage = false;
15166                         saveOptionCookie("chkBackstage");
15167                         removeClass(this.content,"backstageVisible");
15168                 }
15169         },
15170
15171         onClickCommand: function(e) {
15172                 var task = config.tasks[this.getAttribute("task")];
15173                 displayMessage(task);
15174                 if(task.action) {
15175                         backstage.switchTab(null);
15176                         task.action();
15177                 }
15178                 return false;
15179         },
15180
15181         onClickTab: function(e) {
15182                 backstage.switchTab(this.getAttribute("task"));
15183                 return false;
15184         },
15185
15186         // Switch to a given tab, or none if null is passed
15187         switchTab: function(tabName) {
15188                 var tabElem = null;
15189                 var e = this.toolbar.firstChild;
15190                 while(e)
15191                         {
15192                         if(e.getAttribute && e.getAttribute("task") == tabName)
15193                                 tabElem = e;
15194                         e = e.nextSibling;
15195                         }
15196                 if(tabName == backstage.currTabName)
15197                         return;
15198                 if(backstage.currTabElem) {
15199                         removeClass(this.currTabElem,"backstageSelTab");
15200                 }
15201                 if(tabElem && tabName) {
15202                         backstage.preparePanel();
15203                         addClass(tabElem,"backstageSelTab");
15204                         var task = config.tasks[tabName];
15205                         wikify(task.content,backstage.panelBody,null,null);
15206                         backstage.showPanel();
15207                 } else if(backstage.currTabElem) {
15208                         backstage.hidePanel();
15209                 }
15210                 backstage.currTabName = tabName;
15211                 backstage.currTabElem = tabElem;
15212         },
15213
15214         isPanelVisible: function() {
15215                 return backstage.panel ? backstage.panel.style.display == "block" : false;
15216         },
15217
15218         preparePanel: function() {
15219                 backstage.cloak.style.height = findWindowHeight() + "px";
15220                 backstage.cloak.style.display = "block";
15221                 removeChildren(backstage.panelBody);
15222                 return backstage.panelBody;
15223         },
15224         
15225         showPanel: function() {
15226                 backstage.panel.style.display = "block";
15227                 if(anim && config.options.chkAnimate) {
15228                         backstage.panel.style.top = (-backstage.panel.offsetHeight) + "px";
15229                         var p = [
15230                                 {style: "top", start: -backstage.panel.offsetHeight, end: 0, template: "%0px"}
15231                         ];
15232                         anim.startAnimating(new Morpher(backstage.panel,config.animDuration,p),new Scroller(backstage.panel,false));
15233                 } else {
15234                         backstage.panel.style.top = "0px";
15235                 }
15236                 return backstage.panelBody;
15237         },
15238         
15239         hidePanel: function() {
15240                 backstage.currTabName = null;
15241                 backstage.currTabElem = null;
15242                 if(anim && config.options.chkAnimate) {
15243                         var p = [
15244                                 {style: "top", start: 0, end: -(backstage.panel.offsetHeight), template: "%0px"},
15245                                 {style: "display", atEnd: "none"}
15246                         ];
15247                         var c = function(element,properties) {backstage.cloak.style.display = "none";};
15248                         anim.startAnimating(new Morpher(backstage.panel,config.animDuration,p,c));
15249                  } else {
15250                         backstage.panel.style.display = "none";
15251                         backstage.cloak.style.display = "none";
15252                 }
15253         }
15254 };
15255
15256 config.macros.backstage = {};
15257
15258 config.macros.backstage.handler = function(place,macroName,params,wikifier,paramString,tiddler)
15259 {
15260         var backstageTask = config.tasks[params[0]];
15261         if(backstageTask)
15262                 createTiddlyButton(place,backstageTask.text,backstageTask.tooltip,function(e) {backstage.switchTab(params[0]); return false;});
15263 };
15264
15265 //--
15266 //-- ImportTiddlers macro
15267 //--
15268
15269 config.macros.importTiddlers.handler = function(place,macroName,params,wikifier,paramString,tiddler)
15270 {
15271         if(readOnly) {
15272                 createTiddlyElement(place,"div",null,"marked",this.readOnlyWarning);
15273                 return;
15274         }
15275         var w = new Wizard();
15276         w.createWizard(place,this.wizardTitle);
15277         this.restart(w);
15278 };
15279
15280 config.macros.importTiddlers.onCancel = function(e)
15281 {
15282         var wizard = new Wizard(this);
15283         var place = wizard.clear();
15284         config.macros.importTiddlers.restart(wizard);
15285         return false;
15286 };
15287
15288 config.macros.importTiddlers.restart = function(wizard)
15289 {
15290         wizard.addStep(this.step1Title,this.step1Html);
15291         var s = wizard.getElement("selTypes");
15292         for(var t in config.adaptors) {
15293                 var e = createTiddlyElement(s,"option",null,null,t);
15294                 e.value = t;
15295         }
15296         s = wizard.getElement("selFeeds");
15297         var feeds = this.getFeeds();
15298         for(t in feeds) {
15299                 e = createTiddlyElement(s,"option",null,null,t);
15300                 e.value = t;
15301         }
15302         wizard.setValue("feeds",feeds);
15303         s.onchange = config.macros.importTiddlers.onFeedChange;
15304         var fileInput = wizard.getElement("txtBrowse");
15305         fileInput.onchange = config.macros.importTiddlers.onBrowseChange;
15306         fileInput.onkeyup = config.macros.importTiddlers.onBrowseChange;
15307         wizard.setButtons([{caption: this.openLabel, tooltip: this.openPrompt, onClick: config.macros.importTiddlers.onOpen}]);
15308 };
15309
15310 config.macros.importTiddlers.getFeeds = function()
15311 {
15312         var feeds = {};
15313         var tagged = store.getTaggedTiddlers("systemServer","title");
15314         for(var t=0; t<tagged.length; t++) {
15315                 var title = tagged[t].title;
15316                 var serverType = store.getTiddlerSlice(title,"Type");
15317                 if(!serverType)
15318                         serverType = "file";
15319                 feeds[title] = {title: title,
15320                                                 url: store.getTiddlerSlice(title,"URL"),
15321                                                 workspace: store.getTiddlerSlice(title,"Workspace"),
15322                                                 workspaceList: store.getTiddlerSlice(title,"WorkspaceList"),
15323                                                 tiddlerFilter: store.getTiddlerSlice(title,"TiddlerFilter"),
15324                                                 serverType: serverType,
15325                                                 description: store.getTiddlerSlice(title,"Description")};
15326         }
15327         return feeds;
15328 };
15329
15330 config.macros.importTiddlers.onFeedChange = function(e)
15331 {
15332         var wizard = new Wizard(this);
15333         var selTypes = wizard.getElement("selTypes");
15334         var fileInput = wizard.getElement("txtPath");
15335         var feeds = wizard.getValue("feeds");
15336         var f = feeds[this.value];
15337         if(f) {
15338                 selTypes.value = f.serverType;
15339                 fileInput.value = f.url;
15340                 this.selectedIndex = 0;
15341                 wizard.setValue("feedName",f.serverType);
15342                 wizard.setValue("feedHost",f.url);
15343                 wizard.setValue("feedWorkspace",f.workspace);
15344                 wizard.setValue("feedWorkspaceList",f.workspaceList);
15345                 wizard.setValue("feedTiddlerFilter",f.tiddlerFilter);
15346         }
15347         return false;
15348 };
15349
15350 config.macros.importTiddlers.onBrowseChange = function(e)
15351 {
15352         var wizard = new Wizard(this);
15353         var fileInput = wizard.getElement("txtPath");
15354         fileInput.value = "file://" + this.value;
15355         var serverType = wizard.getElement("selTypes");
15356         serverType.value = "file";
15357         return false;
15358 };
15359
15360 config.macros.importTiddlers.onOpen = function(e)
15361 {
15362         var wizard = new Wizard(this);
15363         var fileInput = wizard.getElement("txtPath");
15364         var url = fileInput.value;
15365         var serverType = wizard.getElement("selTypes").value;
15366         var adaptor = new config.adaptors[serverType];
15367         wizard.setValue("adaptor",adaptor);
15368         wizard.setValue("serverType",serverType);
15369         wizard.setValue("host",url);
15370         var context = {};
15371         var ret = adaptor.openHost(url,context,wizard,config.macros.importTiddlers.onOpenHost);
15372         if(ret !== true)
15373                 displayMessage(ret);
15374         wizard.setButtons([{caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}],config.macros.importTiddlers.statusOpenHost);
15375         return false;
15376 };
15377
15378 config.macros.importTiddlers.onOpenHost = function(context,wizard)
15379 {
15380         var adaptor = wizard.getValue("adaptor");
15381         if(context.status !== true)
15382                 displayMessage("Error in importTiddlers.onOpenHost: " + context.statusText);
15383         var ret = adaptor.getWorkspaceList(context,wizard,config.macros.importTiddlers.onGetWorkspaceList);
15384         if(ret !== true)
15385                 displayMessage(ret);
15386         wizard.setButtons([{caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}],config.macros.importTiddlers.statusGetWorkspaceList);
15387 };
15388
15389 config.macros.importTiddlers.onGetWorkspaceList = function(context,wizard)
15390 {
15391         if(context.status !== true)
15392                 displayMessage("Error in importTiddlers.onGetWorkspaceList: " + context.statusText);
15393         wizard.addStep(config.macros.importTiddlers.step2Title,config.macros.importTiddlers.step2Html);
15394         var s = wizard.getElement("selWorkspace");
15395         s.onchange = config.macros.importTiddlers.onWorkspaceChange;
15396         for(var t=0; t<context.workspaces.length; t++) {
15397                 var e = createTiddlyElement(s,"option",null,null,context.workspaces[t].title);
15398                 e.value = context.workspaces[t].title;
15399         }
15400         var workspaceList = wizard.getValue("feedWorkspaceList");
15401         if(workspaceList) {
15402                 var list = workspaceList.parseParams("workspace",null,false,true);
15403                 for(var n=1; n<list.length; n++) {
15404                         if(context.workspaces.findByField("title",list[n].value) == null) {
15405                                 e = createTiddlyElement(s,"option",null,null,list[n].value);
15406                                 e.value = list[n].value;
15407                         }
15408                 }
15409         }
15410         var workspace = wizard.getValue("feedWorkspace");
15411         if(workspace) {
15412                 t = wizard.getElement("txtWorkspace");
15413                 t.value = workspace;
15414         }
15415         wizard.setButtons([{caption: config.macros.importTiddlers.openLabel, tooltip: config.macros.importTiddlers.openPrompt, onClick: config.macros.importTiddlers.onChooseWorkspace}]);
15416 };
15417
15418 config.macros.importTiddlers.onWorkspaceChange = function(e)
15419 {
15420         var wizard = new Wizard(this);
15421         var t = wizard.getElement("txtWorkspace");
15422         t.value  = this.value;
15423         this.selectedIndex = 0;
15424         return false;
15425 };
15426
15427 config.macros.importTiddlers.onChooseWorkspace = function(e)
15428 {
15429         var wizard = new Wizard(this);
15430         var adaptor = wizard.getValue("adaptor");
15431         var workspace = wizard.getElement("txtWorkspace").value;
15432         wizard.setValue("workspace",workspace);
15433         var context = {};
15434         var ret = adaptor.openWorkspace(workspace,context,wizard,config.macros.importTiddlers.onOpenWorkspace);
15435         if(ret !== true)
15436                 displayMessage(ret);
15437         wizard.setButtons([{caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}],config.macros.importTiddlers.statusOpenWorkspace);
15438         return false;
15439 };
15440
15441 config.macros.importTiddlers.onOpenWorkspace = function(context,wizard)
15442 {
15443         if(context.status !== true)
15444                 displayMessage("Error in importTiddlers.onOpenWorkspace: " + context.statusText);
15445         var adaptor = wizard.getValue("adaptor");
15446         var ret = adaptor.getTiddlerList(context,wizard,config.macros.importTiddlers.onGetTiddlerList,wizard.getValue("feedTiddlerFilter"));
15447         if(ret !== true)
15448                 displayMessage(ret);
15449         wizard.setButtons([{caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}],config.macros.importTiddlers.statusGetTiddlerList);
15450 };
15451
15452 config.macros.importTiddlers.onGetTiddlerList = function(context,wizard)
15453 {
15454         if(context.status !== true)
15455                 displayMessage("Error in importTiddlers.onGetTiddlerList: " + context.statusText);
15456         // Extract data for the listview
15457         var listedTiddlers = [];
15458         if(context.tiddlers) {
15459                 for(var n=0; n<context.tiddlers.length; n++) {
15460                         var tiddler = context.tiddlers[n];
15461                         listedTiddlers.push({
15462                                 title: tiddler.title,
15463                                 modified: tiddler.modified,
15464                                 modifier: tiddler.modifier,
15465                                 text: tiddler.text ? wikifyPlainText(tiddler.text,100) : "",
15466                                 tags: tiddler.tags,
15467                                 size: tiddler.text ? tiddler.text.length : 0,
15468                                 tiddler: tiddler
15469                         });
15470                 }
15471         }
15472         listedTiddlers.sort(function(a,b) {return a.title < b.title ? -1 : (a.title == b.title ? 0 : +1);});
15473         // Display the listview
15474         wizard.addStep(config.macros.importTiddlers.step3Title,config.macros.importTiddlers.step3Html);
15475         var markList = wizard.getElement("markList");
15476         var listWrapper = document.createElement("div");
15477         markList.parentNode.insertBefore(listWrapper,markList);
15478         var listView = ListView.create(listWrapper,listedTiddlers,config.macros.importTiddlers.listViewTemplate);
15479         wizard.setValue("listView",listView);
15480         var txtSaveTiddler = wizard.getElement("txtSaveTiddler");
15481         txtSaveTiddler.value = config.macros.importTiddlers.generateSystemServerName(wizard);
15482         wizard.setButtons([
15483                         {caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel},
15484                         {caption: config.macros.importTiddlers.importLabel, tooltip: config.macros.importTiddlers.importPrompt, onClick:  config.macros.importTiddlers.doImport}
15485                 ]);
15486 };
15487
15488 config.macros.importTiddlers.generateSystemServerName = function(wizard)
15489 {
15490         var serverType = wizard.getValue("serverType");
15491         var host = wizard.getValue("host");
15492         var workspace = wizard.getValue("workspace");
15493         var pattern = config.macros.importTiddlers[workspace ? "systemServerNamePattern" : "systemServerNamePatternNoWorkspace"];
15494         return pattern.format([serverType,host,workspace]);
15495 };
15496
15497 config.macros.importTiddlers.saveServerTiddler = function(wizard)
15498 {
15499         var txtSaveTiddler = wizard.getElement("txtSaveTiddler").value;
15500         if(store.tiddlerExists(txtSaveTiddler)) {
15501                 if(!confirm(config.macros.importTiddlers.confirmOverwriteSaveTiddler.format([txtSaveTiddler])))
15502                         return;
15503                 store.suspendNotifications();
15504                 store.removeTiddler(txtSaveTiddler);
15505                 store.resumeNotifications();
15506         }
15507         var serverType = wizard.getValue("serverType");
15508         var host = wizard.getValue("host");
15509         var workspace = wizard.getValue("workspace");
15510         var text = config.macros.importTiddlers.serverSaveTemplate.format([serverType,host,workspace]);
15511         store.saveTiddler(txtSaveTiddler,txtSaveTiddler,text,config.macros.importTiddlers.serverSaveModifier,new Date(),["systemServer"]);
15512 };
15513
15514 config.macros.importTiddlers.doImport = function(e)
15515 {
15516         var wizard = new Wizard(this);
15517         if(wizard.getElement("chkSave").checked)
15518                 config.macros.importTiddlers.saveServerTiddler(wizard);
15519         var chkSync = wizard.getElement("chkSync").checked;
15520         wizard.setValue("sync",chkSync);
15521         var listView = wizard.getValue("listView");
15522         var rowNames = ListView.getSelectedRows(listView);
15523         var adaptor = wizard.getValue("adaptor");
15524         var overwrite = new Array();
15525         var t;
15526         for(t=0; t<rowNames.length; t++) {
15527                 if(store.tiddlerExists(rowNames[t]))
15528                         overwrite.push(rowNames[t]);
15529         }
15530         if(overwrite.length > 0) {
15531                 if(!confirm(config.macros.importTiddlers.confirmOverwriteText.format([overwrite.join(", ")])))
15532                         return false;
15533         }
15534         wizard.addStep(config.macros.importTiddlers.step4Title.format([rowNames.length]),config.macros.importTiddlers.step4Html);
15535         for(t=0; t<rowNames.length; t++) {
15536                 var link = document.createElement("div");
15537                 createTiddlyLink(link,rowNames[t],true);
15538                 var place = wizard.getElement("markReport");
15539                 place.parentNode.insertBefore(link,place);
15540         }
15541         wizard.setValue("remainingImports",rowNames.length);
15542         wizard.setButtons([
15543                         {caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}
15544                 ],config.macros.importTiddlers.statusDoingImport);
15545         for(t=0; t<rowNames.length; t++) {
15546                 var context = {};
15547                 context.allowSynchronous = true;
15548                 var inbound = adaptor.getTiddler(rowNames[t],context,wizard,config.macros.importTiddlers.onGetTiddler);
15549         }
15550         return false;
15551 };
15552
15553 config.macros.importTiddlers.onGetTiddler = function(context,wizard)
15554 {
15555         if(!context.status)
15556                 displayMessage("Error in importTiddlers.onGetTiddler: " + context.statusText);
15557         var tiddler = context.tiddler;
15558         store.suspendNotifications();
15559         store.saveTiddler(tiddler.title, tiddler.title, tiddler.text, tiddler.modifier, tiddler.modified, tiddler.tags, tiddler.fields, true, tiddler.created);
15560         if(!wizard.getValue("sync")) {
15561                 store.setValue(tiddler.title,'server',null);
15562         }
15563         store.resumeNotifications();
15564         if(!context.isSynchronous) 
15565                 store.notify(tiddler.title,true);
15566         var remainingImports = wizard.getValue("remainingImports")-1;
15567         wizard.setValue("remainingImports",remainingImports);
15568         if(remainingImports == 0) {
15569                 if(context.isSynchronous) {
15570                         store.notifyAll();
15571                         refreshDisplay();
15572                 }
15573                 wizard.setButtons([
15574                                 {caption: config.macros.importTiddlers.doneLabel, tooltip: config.macros.importTiddlers.donePrompt, onClick: config.macros.importTiddlers.onCancel}
15575                         ],config.macros.importTiddlers.statusDoneImport);
15576                 autoSaveChanges();
15577         }
15578 };
15579
15580 //--
15581 //-- Sync macro
15582 //--
15583
15584 // Synchronisation handlers
15585 config.syncers = {};
15586
15587 // Sync state.
15588 var currSync = null;
15589
15590 // sync macro
15591 config.macros.sync.handler = function(place,macroName,params,wikifier,paramString,tiddler)
15592 {
15593         if(!wikifier.isStatic)
15594                 this.startSync(place);
15595 };
15596
15597 config.macros.sync.startSync = function(place)
15598 {
15599         if(currSync)
15600                 config.macros.sync.cancelSync();
15601         currSync = {};
15602         currSync.syncList = this.getSyncableTiddlers();
15603         this.createSyncTasks();
15604         this.preProcessSyncableTiddlers();
15605         var wizard = new Wizard();
15606         currSync.wizard = wizard;
15607         wizard.createWizard(place,this.wizardTitle);
15608         wizard.addStep(this.step1Title,this.step1Html);
15609         var markList = wizard.getElement("markList");
15610         var listWrapper = document.createElement("div");
15611         markList.parentNode.insertBefore(listWrapper,markList);
15612         currSync.listView = ListView.create(listWrapper,currSync.syncList,this.listViewTemplate);
15613         this.processSyncableTiddlers();
15614         wizard.setButtons([
15615                         {caption: this.syncLabel, tooltip: this.syncPrompt, onClick: this.doSync}
15616                 ]);
15617 };
15618
15619 config.macros.sync.getSyncableTiddlers = function ()
15620 {
15621         var list = [];
15622         store.forEachTiddler(function(title,tiddler) {
15623                 var syncItem = {};
15624                 syncItem.serverType = tiddler.getServerType();
15625                 syncItem.serverHost = tiddler.fields['server.host'];
15626                 syncItem.serverWorkspace = tiddler.fields['server.workspace'];
15627                 syncItem.tiddler = tiddler;
15628                 syncItem.title = tiddler.title;
15629                 syncItem.isTouched = tiddler.isTouched();
15630                 syncItem.selected = syncItem.isTouched;
15631                 syncItem.syncStatus = config.macros.sync.syncStatusList[syncItem.isTouched ? "changedLocally" : "none"];
15632                 syncItem.status = syncItem.syncStatus.text;
15633                 if(syncItem.serverType && syncItem.serverHost)
15634                         list.push(syncItem);
15635                 });
15636         list.sort(function(a,b) {return a.title < b.title ? -1 : (a.title == b.title ? 0 : +1);});
15637         return list;
15638 };
15639
15640 config.macros.sync.preProcessSyncableTiddlers = function()
15641 {
15642         for(var t=0; t<currSync.syncList.length; t++) {
15643                 si = currSync.syncList[t];
15644                 var ti = si.syncTask.syncMachine.generateTiddlerInfo(si.tiddler);
15645                 si.serverUrl = ti.uri;
15646         }
15647 };
15648
15649 config.macros.sync.processSyncableTiddlers = function()
15650 {
15651         for(var t=0; t<currSync.syncList.length; t++) {
15652                 si = currSync.syncList[t];
15653                 si.rowElement.style.backgroundColor = si.syncStatus.color;
15654         }
15655 };
15656
15657 config.macros.sync.createSyncTasks = function()
15658 {
15659         currSync.syncTasks = [];
15660         for(var t=0; t<currSync.syncList.length; t++) {
15661                 var si = currSync.syncList[t];
15662                 var r = null;
15663                 for(var st=0; st<currSync.syncTasks.length; st++) {
15664                         var cst = currSync.syncTasks[st];
15665                         if(si.serverType == cst.serverType && si.serverHost == cst.serverHost && si.serverWorkspace == cst.serverWorkspace)
15666                                 r = cst;
15667                 }
15668                 if(r == null) {
15669                         si.syncTask = this.createSyncTask(si);
15670                         currSync.syncTasks.push(si.syncTask);
15671                 } else {
15672                         si.syncTask = r;
15673                         r.syncItems.push(si);
15674                 }
15675         }
15676 };
15677
15678 config.macros.sync.createSyncTask = function(syncItem)
15679 {
15680         var st = {};
15681         st.serverType = syncItem.serverType;
15682         st.serverHost = syncItem.serverHost;
15683         st.serverWorkspace = syncItem.serverWorkspace;
15684         st.syncItems = [syncItem];
15685         st.syncMachine = new SyncMachine(st.serverType,{
15686                 start: function() {
15687                         return this.openHost(st.serverHost,"openWorkspace");
15688                 },
15689                 openWorkspace: function() {
15690                         return this.openWorkspace(st.serverWorkspace,"getTiddlerList");
15691                 },
15692                 getTiddlerList: function() {
15693                         return this.getTiddlerList("gotTiddlerList");
15694                 },
15695                 gotTiddlerList: function(tiddlers) {
15696                         for(var t=0; t<st.syncItems.length; t++) {
15697                                 var si = st.syncItems[t];
15698                                 var f = tiddlers.findByField("title",si.title);
15699                                 if(f !== null) {
15700                                         if(tiddlers[f].fields['server.page.revision'] > si.tiddler.fields['server.page.revision']) {
15701                                                 si.syncStatus = config.macros.sync.syncStatusList[si.isTouched ? 'changedBoth' : 'changedServer'];
15702                                         }
15703                                 } else {
15704                                         si.syncStatus = config.macros.sync.syncStatusList.notFound;
15705                                 }
15706                                 config.macros.sync.updateSyncStatus(si);
15707                         }
15708                 },
15709                 getTiddler: function(title) {
15710                         return this.getTiddler(title,"onGetTiddler");
15711                 },
15712                 onGetTiddler: function(tiddler) {
15713                         var syncItem = st.syncItems.findByField("title",tiddler.title);
15714                         if(syncItem !== null) {
15715                                 syncItem = st.syncItems[syncItem];
15716                                 store.saveTiddler(tiddler.title, tiddler.title, tiddler.text, tiddler.modifier, tiddler.modified, tiddler.tags, tiddler.fields, true, tiddler.created);
15717                                 syncItem.syncStatus = config.macros.sync.syncStatusList.gotFromServer;
15718                                 config.macros.sync.updateSyncStatus(syncItem);
15719                         }
15720                 },
15721                 putTiddler: function(tiddler) {
15722                         return this.putTiddler(tiddler,"onPutTiddler");
15723                 },
15724                 onPutTiddler: function(tiddler) {
15725                         var syncItem = st.syncItems.findByField("title",tiddler.title);
15726                         if(syncItem !== null) {
15727                                 syncItem = st.syncItems[syncItem];
15728                                 store.resetTiddler(tiddler.title);
15729                                 syncItem.syncStatus = config.macros.sync.syncStatusList.putToServer;
15730                                 config.macros.sync.updateSyncStatus(syncItem);
15731                         }
15732                 }
15733         });
15734         st.syncMachine.go();
15735         return st;
15736 };
15737
15738 config.macros.sync.updateSyncStatus = function(syncItem)
15739 {
15740         var e = syncItem.colElements["status"];
15741         removeChildren(e);
15742         createTiddlyText(e,syncItem.syncStatus.text);
15743         syncItem.rowElement.style.backgroundColor = syncItem.syncStatus.color;
15744 };
15745
15746 config.macros.sync.doSync = function(e)
15747 {
15748         var rowNames = ListView.getSelectedRows(currSync.listView);
15749         for(var t=0; t<currSync.syncList.length; t++) {
15750                 var si = currSync.syncList[t];
15751                 if(rowNames.indexOf(si.title) != -1) {
15752                         config.macros.sync.doSyncItem(si);
15753                 }
15754         }
15755         return false;
15756 };
15757
15758 config.macros.sync.doSyncItem = function(syncItem)
15759 {
15760         var r = true;
15761         var sl = config.macros.sync.syncStatusList;
15762         switch(syncItem.syncStatus) {
15763                 case sl.changedServer:
15764                         r = syncItem.syncTask.syncMachine.go("getTiddler",syncItem.title);
15765                         break;
15766                 case sl.notFound:
15767                 case sl.changedLocally:
15768                 case sl.changedBoth:
15769                         r = syncItem.syncTask.syncMachine.go("putTiddler",syncItem.tiddler);
15770                         break;
15771                 default:
15772                         break;
15773         }
15774         if(r !== true)
15775                 displayMessage("Error in doSyncItem: " + r);
15776 };
15777
15778 config.macros.sync.cancelSync = function()
15779 {
15780         currSync = null;
15781 };
15782
15783 function SyncMachine(serverType,steps)
15784 {
15785         this.serverType = serverType;
15786         this.adaptor = new config.adaptors[serverType];
15787         this.steps = steps;
15788 }
15789
15790 SyncMachine.prototype.go = function(step,varargs)
15791 {
15792         if(!step)
15793                 step = "start";
15794         var h = this.steps[step];
15795         if(!h)
15796                 return null;
15797         var a = [];
15798         for(var t=1; t<arguments.length; t++)
15799                 a.push(arguments[t]);
15800         var r = h.apply(this,a);
15801         if(typeof r == "string")
15802                 this.invokeError(r);
15803         return r;
15804 };
15805
15806 SyncMachine.prototype.invokeError = function(message)
15807 {
15808         if(this.steps.error)
15809                 this.steps.error(message);
15810 };
15811
15812 SyncMachine.prototype.openHost = function(host,nextStep)
15813 {
15814         var me = this;
15815         return me.adaptor.openHost(host,null,null,function(context) {
15816                 if(typeof context.status == "string")
15817                         me.invokeError(context.status);
15818                 else
15819                         me.go(nextStep);
15820         });
15821 };
15822
15823 SyncMachine.prototype.getWorkspaceList = function(nextStep)
15824 {
15825         var me = this;
15826         return me.adaptor.getWorkspaceList(null,null,function(context) {
15827                 if(typeof context.status == "string")
15828                         me.invokeError(context.status);
15829                 else
15830                         me.go(nextStep,context.workspaces);
15831         });
15832 };
15833
15834 SyncMachine.prototype.openWorkspace = function(workspace,nextStep)
15835 {
15836         var me = this;
15837         return me.adaptor.openWorkspace(workspace,null,null,function(context) {
15838                 if(typeof context.status == "string")
15839                         me.invokeError(context.status);
15840                 else
15841                         me.go(nextStep);
15842         });
15843 };
15844
15845 SyncMachine.prototype.getTiddlerList = function(nextStep)
15846 {
15847         var me = this;
15848         return me.adaptor.getTiddlerList(null,null,function(context) {
15849                 if(typeof context.status == "string")
15850                         me.invokeError(context.status);
15851                 else
15852                         me.go(nextStep,context.tiddlers);
15853         });
15854 };
15855
15856 SyncMachine.prototype.generateTiddlerInfo = function(tiddler)
15857 {
15858         return this.adaptor.generateTiddlerInfo(tiddler);
15859 };
15860
15861 SyncMachine.prototype.getTiddler = function(title,nextStep)
15862 {
15863         var me = this;
15864         return me.adaptor.getTiddler(title,null,null,function(context) {
15865                 if(typeof context.status == "string")
15866                         me.invokeError(context.status);
15867                 else
15868                         me.go(nextStep,context.tiddler);
15869         });
15870 };
15871
15872 SyncMachine.prototype.putTiddler = function(tiddler,nextStep)
15873 {
15874         var me = this;
15875         return me.adaptor.putTiddler(tiddler,null,null,function(context) {
15876                 if(typeof context.status == "string")
15877                         me.invokeError(context.status);
15878                 else
15879                         me.go(nextStep,tiddler);
15880         });
15881 };
15882
15883 //--
15884 //-- Manager UI for groups of tiddlers
15885 //--
15886
15887 config.macros.plugins.handler = function(place,macroName,params,wikifier,paramString,tiddler)
15888 {
15889         var wizard = new Wizard();
15890         wizard.createWizard(place,this.wizardTitle);
15891         wizard.addStep(this.step1Title,this.step1Html);
15892         var markList = wizard.getElement("markList");
15893         var listWrapper = document.createElement("div");
15894         markList.parentNode.insertBefore(listWrapper,markList);
15895         listWrapper.setAttribute("refresh","macro");
15896         listWrapper.setAttribute("macroName","plugins");
15897         listWrapper.setAttribute("params",paramString);
15898         this.refresh(listWrapper,paramString);
15899 };
15900
15901 config.macros.plugins.refresh = function(listWrapper,params)
15902 {
15903         var wizard = new Wizard(listWrapper);
15904         var selectedRows = [];
15905         ListView.forEachSelector(listWrapper,function(e,rowName) {
15906                         if(e.checked)
15907                                 selectedRows.push(e.getAttribute("rowName"));
15908                 });
15909         removeChildren(listWrapper);
15910         params = params.parseParams("anon");
15911         var plugins = installedPlugins.slice(0);
15912         var t,tiddler,p;
15913         var configTiddlers = store.getTaggedTiddlers("systemConfig");
15914         for(t=0; t<configTiddlers.length; t++) {
15915                 tiddler = configTiddlers[t];
15916                 if(plugins.findByField("title",tiddler.title) == null) {
15917                         p = getPluginInfo(tiddler);
15918                         p.executed = false;
15919                         p.log.splice(0,0,this.skippedText);
15920                         plugins.push(p);
15921                 }
15922         }
15923         for(t=0; t<plugins.length; t++) {
15924                 p = plugins[t];
15925                 p.size = p.tiddler.text ? p.tiddler.text.length : 0;
15926                 p.forced = p.tiddler.isTagged("systemConfigForce");
15927                 p.disabled = p.tiddler.isTagged("systemConfigDisable");
15928                 p.Selected = selectedRows.indexOf(plugins[t].title) != -1;
15929         }
15930         if(plugins.length == 0) {
15931                 createTiddlyElement(listWrapper,"em",null,null,this.noPluginText);
15932                 wizard.setButtons([]);
15933         } else {
15934                 var listView = ListView.create(listWrapper,plugins,this.listViewTemplate,this.onSelectCommand);
15935                 wizard.setValue("listView",listView);
15936                 wizard.setButtons([
15937                                 {caption: config.macros.plugins.removeLabel, tooltip: config.macros.plugins.removePrompt, onClick:  config.macros.plugins.doRemoveTag},
15938                                 {caption: config.macros.plugins.deleteLabel, tooltip: config.macros.plugins.deletePrompt, onClick:  config.macros.plugins.doDelete}
15939                         ]);
15940         }
15941 };
15942
15943 config.macros.plugins.doRemoveTag = function(e)
15944 {
15945         var wizard = new Wizard(this);
15946         var listView = wizard.getValue("listView");
15947         var rowNames = ListView.getSelectedRows(listView);
15948         if(rowNames.length == 0) {
15949                 alert(config.messages.nothingSelected);
15950         } else {
15951                 for(var t=0; t<rowNames.length; t++)
15952                         store.setTiddlerTag(rowNames[t],false,"systemConfig");
15953         }
15954 };
15955
15956 config.macros.plugins.doDelete = function(e)
15957 {
15958         var wizard = new Wizard(this);
15959         var listView = wizard.getValue("listView");
15960         var rowNames = ListView.getSelectedRows(listView);
15961         if(rowNames.length == 0) {
15962                 alert(config.messages.nothingSelected);
15963         } else {
15964                 if(confirm(config.macros.plugins.confirmDeleteText.format([rowNames.join(", ")]))) {
15965                         for(t=0; t<rowNames.length; t++) {
15966                                 store.removeTiddler(rowNames[t]);
15967                                 story.closeTiddler(rowNames[t],true);
15968                         }
15969                 }
15970         }
15971 };
15972
15973 //--
15974 //-- Message area
15975 //--
15976
15977 function getMessageDiv()
15978 {
15979         var msgArea = document.getElementById("messageArea");
15980         if(!msgArea)
15981                 return null;
15982         if(!msgArea.hasChildNodes())
15983                 createTiddlyButton(createTiddlyElement(msgArea,"div",null,"messageToolbar"),
15984                         config.messages.messageClose.text,
15985                         config.messages.messageClose.tooltip,
15986                         clearMessage);
15987         msgArea.style.display = "block";
15988         return createTiddlyElement(msgArea,"div");
15989 }
15990
15991 function displayMessage(text,linkText)
15992 {
15993         var e = getMessageDiv();
15994         if(!e) {
15995                 alert(text);
15996                 return;
15997         }
15998         if(linkText) {
15999                 var link = createTiddlyElement(e,"a",null,null,text);
16000                 link.href = linkText;
16001                 link.target = "_blank";
16002         } else {
16003                 e.appendChild(document.createTextNode(text));
16004         }
16005 }
16006
16007 function clearMessage()
16008 {
16009         var msgArea = document.getElementById("messageArea");
16010         if(msgArea) {
16011                 removeChildren(msgArea);
16012                 msgArea.style.display = "none";
16013         }
16014         return false;
16015 }
16016
16017 //--
16018 //-- Refresh mechanism
16019 //--
16020
16021 config.refreshers = {
16022         link: function(e,changeList)
16023                 {
16024                 var title = e.getAttribute("tiddlyLink");
16025                 refreshTiddlyLink(e,title);
16026                 return true;
16027                 },
16028         
16029         tiddler: function(e,changeList)
16030                 {
16031                 var title = e.getAttribute("tiddler");
16032                 var template = e.getAttribute("template");
16033                 if(changeList && changeList.indexOf(title) != -1 && !story.isDirty(title))
16034                         story.refreshTiddler(title,template,true);
16035                 else
16036                         refreshElements(e,changeList);
16037                 return true;
16038                 },
16039
16040         content: function(e,changeList)
16041                 {
16042                 var title = e.getAttribute("tiddler");
16043                 var force = e.getAttribute("force");
16044                 if(force != null || changeList == null || changeList.indexOf(title) != -1) {
16045                         removeChildren(e);
16046                         wikify(store.getTiddlerText(title,title),e,null);
16047                         return true;
16048                 } else
16049                         return false;
16050                 },
16051
16052         macro: function(e,changeList)
16053                 {
16054                 var macro = e.getAttribute("macroName");
16055                 var params = e.getAttribute("params");
16056                 if(macro)
16057                         macro = config.macros[macro];
16058                 if(macro && macro.refresh)
16059                         macro.refresh(e,params);
16060                 return true;
16061                 }
16062 };
16063
16064 function refreshElements(root,changeList)
16065 {
16066         var nodes = root.childNodes;
16067         for(var c=0; c<nodes.length; c++) {
16068                 var e = nodes[c], type = null;
16069                 if(e.getAttribute  && (e.tagName ? e.tagName != "IFRAME" : true))
16070                         type = e.getAttribute("refresh");
16071                 var refresher = config.refreshers[type];
16072                 var refreshed = false;
16073                 if(refresher != undefined)
16074                         refreshed = refresher(e,changeList);
16075                 if(e.hasChildNodes() && !refreshed)
16076                         refreshElements(e,changeList);
16077         }
16078 }
16079
16080 function applyHtmlMacros(root,tiddler)
16081 {
16082         var e = root.firstChild;
16083         while(e) {
16084                 var nextChild = e.nextSibling;
16085                 if(e.getAttribute) {
16086                         var macro = e.getAttribute("macro");
16087                         if(macro) {
16088                                 var params = "";
16089                                 var p = macro.indexOf(" ");
16090                                 if(p != -1) {
16091                                         params = macro.substr(p+1);
16092                                         macro = macro.substr(0,p);
16093                                 }
16094                                 invokeMacro(e,macro,params,null,tiddler);
16095                         }
16096                 }
16097                 if(e.hasChildNodes())
16098                         applyHtmlMacros(e,tiddler);
16099                 e = nextChild;
16100         }
16101 }
16102
16103 function refreshPageTemplate(title)
16104 {
16105         var stash = createTiddlyElement(document.body,"div");
16106         stash.style.display = "none";
16107         var display = document.getElementById("tiddlerDisplay");
16108         var nodes,t;
16109         if(display) {
16110                 nodes = display.childNodes;
16111                 for(t=nodes.length-1; t>=0; t--)
16112                         stash.appendChild(nodes[t]);
16113         }
16114         var wrapper = document.getElementById("contentWrapper");
16115         if(!title)
16116                 title = "PageTemplate";
16117         var html = store.getRecursiveTiddlerText(title,null,10);
16118         wrapper.innerHTML = html;
16119         applyHtmlMacros(wrapper);
16120         refreshElements(wrapper);
16121         display = document.getElementById("tiddlerDisplay");
16122         removeChildren(display);
16123         if(!display)
16124                 display = createTiddlyElement(wrapper,"div","tiddlerDisplay");
16125         nodes = stash.childNodes;
16126         for(t=nodes.length-1; t>=0; t--)
16127                 display.appendChild(nodes[t]);
16128         removeNode(stash);
16129 }
16130
16131 function refreshDisplay(hint)
16132 {
16133         if(typeof hint == "string")
16134                 hint = [hint];
16135         var e = document.getElementById("contentWrapper");
16136         refreshElements(e,hint);
16137         if(backstage.isPanelVisible()) {
16138                 e = document.getElementById("backstage");
16139                 refreshElements(e,hint);
16140         }
16141 }
16142
16143 function refreshPageTitle()
16144 {
16145         document.title = getPageTitle();
16146 }
16147
16148 function getPageTitle()
16149 {
16150         var st = wikifyPlain("SiteTitle");
16151         var ss = wikifyPlain("SiteSubtitle");
16152         return st + ((st == "" || ss == "") ? "" : " - ") + ss;
16153 }
16154
16155 function refreshStyles(title,doc)
16156 {
16157         if(!doc)
16158                 doc = document;
16159         setStylesheet(title == null ? "" : store.getRecursiveTiddlerText(title,"",10),title,doc);
16160 }
16161
16162 function refreshColorPalette(title)
16163 {
16164         if(!startingUp)
16165                 refreshAll();
16166 }
16167
16168 function refreshAll()
16169 {
16170         refreshPageTemplate();
16171         refreshDisplay();
16172         refreshStyles("StyleSheetLayout");
16173         refreshStyles("StyleSheetColors");
16174         refreshStyles("StyleSheet");
16175         refreshStyles("StyleSheetPrint");
16176 }
16177
16178 //--
16179 //-- Options cookie stuff
16180 //--
16181
16182 config.optionHandlers = {
16183         'txt': {
16184                 get: function(name) {return encodeCookie(config.options[name].toString());},
16185                 set: function(name,value) {config.options[name] = decodeCookie(value);}
16186         },
16187         'chk': {
16188                 get: function(name) {return config.options[name] ? "true" : "false";},
16189                 set: function(name,value) {config.options[name] = value == "true";}
16190         }
16191 };
16192
16193 function loadOptionsCookie()
16194 {
16195         if(safeMode)
16196                 return;
16197         var cookies = document.cookie.split(";");
16198         for(var c=0; c<cookies.length; c++) {
16199                 var p = cookies[c].indexOf("=");
16200                 if(p != -1) {
16201                         var name = cookies[c].substr(0,p).trim();
16202                         var value = cookies[c].substr(p+1).trim();
16203                         var optType = name.substr(0,3);
16204                         if(config.optionHandlers[optType] && config.optionHandlers[optType].set)
16205                                 config.optionHandlers[optType].set(name,value);
16206                 }
16207         }
16208 }
16209
16210 function saveOptionCookie(name)
16211 {
16212         if(safeMode)
16213                 return;
16214         var c = name + "=";
16215         var optType = name.substr(0,3);
16216         if(config.optionHandlers[optType] && config.optionHandlers[optType].get)
16217                 c += config.optionHandlers[optType].get(name);
16218         c += "; expires=Fri, 1 Jan 2038 12:00:00 UTC; path=/";
16219         document.cookie = c;
16220 }
16221
16222 function encodeCookie(s)
16223 {
16224         return escape(manualConvertUnicodeToUTF8(s));
16225 }
16226
16227 function decodeCookie(s)
16228 {
16229         s = unescape(s);
16230         var re = /&#[0-9]{1,5};/g;
16231         return s.replace(re,function($0) {return String.fromCharCode(eval($0.replace(/[&#;]/g,"")));});
16232 }
16233
16234 //--
16235 //-- Saving
16236 //--
16237
16238 var saveUsingSafari = false;
16239
16240 var startSaveArea = '<div id="' + 'storeArea">'; // Split up into two so that indexOf() of this source doesn't find it
16241 var endSaveArea = '</d' + 'iv>';
16242
16243 // If there are unsaved changes, force the user to confirm before exitting
16244 function confirmExit()
16245 {
16246         hadConfirmExit = true;
16247         if((store && store.isDirty && store.isDirty()) || (story && story.areAnyDirty && story.areAnyDirty()))
16248                 return config.messages.confirmExit;
16249 }
16250
16251 // Give the user a chance to save changes before exitting
16252 function checkUnsavedChanges()
16253 {
16254         if(store && store.isDirty && store.isDirty() && window.hadConfirmExit === false) {
16255                 if(confirm(config.messages.unsavedChangesWarning))
16256                         saveChanges();
16257         }
16258 }
16259
16260 function updateLanguageAttribute(s)
16261 {
16262         if(config.locale) {
16263                 var mRE = /(<html(?:.*?)?)(?: xml:lang\="([a-z]+)")?(?: lang\="([a-z]+)")?>/;
16264                 var m = mRE.exec(s);
16265                 if(m) {
16266                         var t = m[1];
16267                         if(m[2])
16268                                 t += ' xml:lang="' + config.locale + '"';
16269                         if(m[3])
16270                                 t += ' lang="' + config.locale + '"';
16271                         t += ">";
16272                         s = s.substr(0,m.index) + t + s.substr(m.index+m[0].length);
16273                 }
16274         }
16275         return s;
16276 }
16277
16278 function updateMarkupBlock(s,blockName,tiddlerName)
16279 {
16280         return s.replaceChunk(
16281                         "<!--%0-START-->".format([blockName]),
16282                         "<!--%0-END-->".format([blockName]),
16283                         "\n" + store.getRecursiveTiddlerText(tiddlerName,"") + "\n");
16284 }
16285
16286 function updateOriginal(original,posDiv)
16287 {
16288         if(!posDiv)
16289                 posDiv = locateStoreArea(original);
16290         if(!posDiv) {
16291                 alert(config.messages.invalidFileError.format([localPath]));
16292                 return null;
16293         }
16294         var revised = original.substr(0,posDiv[0] + startSaveArea.length) + "\n" +
16295                                 convertUnicodeToUTF8(store.allTiddlersAsHtml()) + "\n" +
16296                                 original.substr(posDiv[1]);
16297         var newSiteTitle = convertUnicodeToUTF8(getPageTitle()).htmlEncode();
16298         revised = revised.replaceChunk("<title"+">","</title"+">"," " + newSiteTitle + " ");
16299         revised = updateLanguageAttribute(revised);
16300         revised = updateMarkupBlock(revised,"PRE-HEAD","MarkupPreHead");
16301         revised = updateMarkupBlock(revised,"POST-HEAD","MarkupPostHead");
16302         revised = updateMarkupBlock(revised,"PRE-BODY","MarkupPreBody");
16303         revised = updateMarkupBlock(revised,"POST-SCRIPT","MarkupPostBody");
16304         return revised;
16305 }
16306
16307 function locateStoreArea(original)
16308 {
16309         // Locate the storeArea div's
16310         var posOpeningDiv = original.indexOf(startSaveArea);
16311         var limitClosingDiv = original.indexOf("<"+"!--POST-STOREAREA--"+">");
16312         if(limitClosingDiv == -1)
16313                 limitClosingDiv = original.indexOf("<"+"!--POST-BODY-START--"+">");
16314         var posClosingDiv = original.lastIndexOf(endSaveArea,limitClosingDiv == -1 ? original.length : limitClosingDiv);
16315         return (posOpeningDiv != -1 && posClosingDiv != -1) ? [posOpeningDiv,posClosingDiv] : null;
16316 }
16317
16318 function autoSaveChanges(onlyIfDirty,tiddlers)
16319 {
16320         if(config.options.chkAutoSave)
16321                 saveChanges(onlyIfDirty,tiddlers);
16322 }
16323
16324 // Save this tiddlywiki with the pending changes
16325 function saveChanges(onlyIfDirty,tiddlers)
16326 {
16327         if(onlyIfDirty && !store.isDirty())
16328                 return;
16329         clearMessage();
16330         // Get the URL of the document
16331         var originalPath = document.location.toString();
16332         // Check we were loaded from a file URL
16333         if(originalPath.substr(0,5) != "file:") {
16334                 alert(config.messages.notFileUrlError);
16335                 if(store.tiddlerExists(config.messages.saveInstructions))
16336                         story.displayTiddler(null,config.messages.saveInstructions);
16337                 return;
16338         }
16339         var localPath = getLocalPath(originalPath);
16340         // Load the original file
16341         var original = loadFile(localPath);
16342         if(original == null) {
16343                 alert(config.messages.cantSaveError);
16344                 if(store.tiddlerExists(config.messages.saveInstructions))
16345                         story.displayTiddler(null,config.messages.saveInstructions);
16346                 return;
16347         }
16348         // Locate the storeArea div's
16349         var posDiv = locateStoreArea(original);
16350         if(!posDiv) {
16351                 alert(config.messages.invalidFileError.format([localPath]));
16352                 return;
16353         }
16354         saveBackup(localPath,original);
16355         saveRss(localPath);
16356         saveEmpty(localPath,original,posDiv);
16357         saveMain(localPath,original,posDiv);
16358 }
16359
16360 function saveBackup(localPath,original)
16361 {
16362         // Save the backup
16363         if(config.options.chkSaveBackups) {
16364                 var backupPath = getBackupPath(localPath);
16365                 var backup = config.browser.isIE ? ieCopyFile(backupPath,localPath) : saveFile(backupPath,original);
16366                 if(backup)
16367                         displayMessage(config.messages.backupSaved,"file://" + backupPath);
16368                 else
16369                         alert(config.messages.backupFailed);
16370         }
16371 }
16372
16373 function saveRss(localPath)
16374 {
16375         if(config.options.chkGenerateAnRssFeed) {
16376                 var rssPath = localPath.substr(0,localPath.lastIndexOf(".")) + ".xml";
16377                 var rssSave = saveFile(rssPath,convertUnicodeToUTF8(generateRss()));
16378                 if(rssSave)
16379                         displayMessage(config.messages.rssSaved,"file://" + rssPath);
16380                 else
16381                         alert(config.messages.rssFailed);
16382         }
16383 }
16384
16385 function saveEmpty(localPath,original,posDiv)
16386 {
16387         if(config.options.chkSaveEmptyTemplate) {
16388                 var emptyPath,p;
16389                 if((p = localPath.lastIndexOf("/")) != -1)
16390                         emptyPath = localPath.substr(0,p) + "/empty.html";
16391                 else if((p = localPath.lastIndexOf("\\")) != -1)
16392                         emptyPath = localPath.substr(0,p) + "\\empty.html";
16393                 else
16394                         emptyPath = localPath + ".empty.html";
16395                 var empty = original.substr(0,posDiv[0] + startSaveArea.length) + original.substr(posDiv[1]);
16396                 var emptySave = saveFile(emptyPath,empty);
16397                 if(emptySave)
16398                         displayMessage(config.messages.emptySaved,"file://" + emptyPath);
16399                 else
16400                         alert(config.messages.emptyFailed);
16401         }
16402 }
16403
16404 function saveMain(localPath,original,posDiv)
16405 {
16406         var save;
16407         try {
16408                 var revised = updateOriginal(original,posDiv);
16409                 save = saveFile(localPath,revised);
16410         } catch (ex) {
16411                 showException(ex);
16412         }
16413         if(save) {
16414                 displayMessage(config.messages.mainSaved,"file://" + localPath);
16415                 store.setDirty(false);
16416         } else {
16417                 alert(config.messages.mainFailed);
16418         }
16419 }
16420
16421 function getLocalPath(origPath)
16422 {
16423         var originalPath = convertUriToUTF8(origPath,config.options.txtFileSystemCharSet);
16424         // Remove any location or query part of the URL
16425         var argPos = originalPath.indexOf("?");
16426         if(argPos != -1)
16427                 originalPath = originalPath.substr(0,argPos);
16428         var hashPos = originalPath.indexOf("#");
16429         if(hashPos != -1)
16430                 originalPath = originalPath.substr(0,hashPos);
16431         // Convert file://localhost/ to file:///
16432         if(originalPath.indexOf("file://localhost/") == 0)
16433                 originalPath = "file://" + originalPath.substr(16);
16434         // Convert to a native file format
16435         var localPath;
16436         if(originalPath.charAt(9) == ":") // pc local file
16437                 localPath = unescape(originalPath.substr(8)).replace(new RegExp("/","g"),"\\");
16438         else if(originalPath.indexOf("file://///") == 0) // FireFox pc network file
16439                 localPath = "\\\\" + unescape(originalPath.substr(10)).replace(new RegExp("/","g"),"\\");
16440         else if(originalPath.indexOf("file:///") == 0) // mac/unix local file
16441                 localPath = unescape(originalPath.substr(7));
16442         else if(originalPath.indexOf("file:/") == 0) // mac/unix local file
16443                 localPath = unescape(originalPath.substr(5));
16444         else // pc network file
16445                 localPath = "\\\\" + unescape(originalPath.substr(7)).replace(new RegExp("/","g"),"\\");
16446         return localPath;
16447 }
16448
16449 function getBackupPath(localPath)
16450 {
16451         var backSlash = true;
16452         var dirPathPos = localPath.lastIndexOf("\\");
16453         if(dirPathPos == -1) {
16454                 dirPathPos = localPath.lastIndexOf("/");
16455                 backSlash = false;
16456         }
16457         var backupFolder = config.options.txtBackupFolder;
16458         if(!backupFolder || backupFolder == "")
16459                 backupFolder = ".";
16460         var backupPath = localPath.substr(0,dirPathPos) + (backSlash ? "\\" : "/") + backupFolder + localPath.substr(dirPathPos);
16461         backupPath = backupPath.substr(0,backupPath.lastIndexOf(".")) + "." + (new Date()).convertToYYYYMMDDHHMMSSMMM() + ".html";
16462         return backupPath;
16463 }
16464
16465 function generateRss()
16466 {
16467         var s = [];
16468         var d = new Date();
16469         var u = store.getTiddlerText("SiteUrl");
16470         // Assemble the header
16471         s.push("<" + "?xml version=\"1.0\"?" + ">");
16472         s.push("<rss version=\"2.0\">");
16473         s.push("<channel>");
16474         s.push("<title" + ">" + wikifyPlain("SiteTitle").htmlEncode() + "</title" + ">");
16475         if(u)
16476                 s.push("<link>" + u.htmlEncode() + "</link>");
16477         s.push("<description>" + wikifyPlain("SiteSubtitle").htmlEncode() + "</description>");
16478         s.push("<language>en-us</language>");
16479         s.push("<copyright>Copyright " + d.getFullYear() + " " + config.options.txtUserName.htmlEncode() + "</copyright>");
16480         s.push("<pubDate>" + d.toGMTString() + "</pubDate>");
16481         s.push("<lastBuildDate>" + d.toGMTString() + "</lastBuildDate>");
16482         s.push("<docs>http://blogs.law.harvard.edu/tech/rss</docs>");
16483         s.push("<generator>TiddlyWiki " + version.major + "." + version.minor + "." + version.revision + "</generator>");
16484         // The body
16485         var tiddlers = store.getTiddlers("modified","excludeLists");
16486         var n = config.numRssItems > tiddlers.length ? 0 : tiddlers.length-config.numRssItems;
16487         for (var t=tiddlers.length-1; t>=n; t--)
16488                 s.push(tiddlers[t].saveToRss(u));
16489         // And footer
16490         s.push("</channel>");
16491         s.push("</rss>");
16492         // Save it all
16493         return s.join("\n");
16494 }
16495
16496 //--
16497 //-- Filesystem code
16498 //--
16499
16500 function convertUTF8ToUnicode(u)
16501 {
16502         if(window.netscape == undefined)
16503                 return manualConvertUTF8ToUnicode(u);
16504         else
16505                 return mozConvertUTF8ToUnicode(u);
16506 }
16507
16508 function manualConvertUTF8ToUnicode(utf)
16509 {
16510         var uni = utf;
16511         var src = 0;
16512         var dst = 0;
16513         var b1, b2, b3;
16514         var c;
16515         while(src < utf.length) {
16516                 b1 = utf.charCodeAt(src++);
16517                 if(b1 < 0x80) {
16518                         dst++;
16519                 } else if(b1 < 0xE0) {
16520                         b2 = utf.charCodeAt(src++);
16521                         c = String.fromCharCode(((b1 & 0x1F) << 6) | (b2 & 0x3F));
16522                         uni = uni.substring(0,dst++).concat(c,utf.substr(src));
16523                 } else {
16524                         b2 = utf.charCodeAt(src++);
16525                         b3 = utf.charCodeAt(src++);
16526                         c = String.fromCharCode(((b1 & 0xF) << 12) | ((b2 & 0x3F) << 6) | (b3 & 0x3F));
16527                         uni = uni.substring(0,dst++).concat(c,utf.substr(src));
16528                 }
16529         }
16530         return uni;
16531 }
16532
16533 function mozConvertUTF8ToUnicode(u)
16534 {
16535         try {
16536                 netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
16537                 var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
16538                 converter.charset = "UTF-8";
16539         } catch(ex) {
16540                 return manualConvertUTF8ToUnicode(u);
16541         } // fallback
16542         var s = converter.ConvertToUnicode(u);
16543         var fin = converter.Finish();
16544         return (fin.length > 0) ? s+fin : s;
16545 }
16546
16547 function convertUnicodeToUTF8(s)
16548 {
16549         if(window.netscape == undefined)
16550                 return manualConvertUnicodeToUTF8(s);
16551         else
16552                 return mozConvertUnicodeToUTF8(s);
16553 }
16554
16555 function manualConvertUnicodeToUTF8(s)
16556 {
16557         var re = /[^\u0000-\u007F]/g ;
16558         return s.replace(re,function($0) {return "&#" + $0.charCodeAt(0).toString() + ";";});
16559 }
16560
16561 function mozConvertUnicodeToUTF8(s)
16562 {
16563         try {
16564                 netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
16565                 var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
16566                 converter.charset = "UTF-8";
16567         } catch(ex) {
16568                 return manualConvertUnicodeToUTF8(s);
16569         } // fallback
16570         var u = converter.ConvertFromUnicode(s);
16571         var fin = converter.Finish();
16572         if(fin.length > 0)
16573                 return u + fin;
16574         else
16575                 return u;
16576 }
16577
16578 function convertUriToUTF8(uri,charSet)
16579 {
16580         if(window.netscape == undefined || charSet == undefined || charSet == "")
16581                 return uri;
16582         try {
16583                 netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
16584                 var converter = Components.classes["@mozilla.org/intl/utf8converterservice;1"].getService(Components.interfaces.nsIUTF8ConverterService);
16585         } catch(ex) {
16586                 return uri;
16587         }
16588         return converter.convertURISpecToUTF8(uri,charSet);
16589 }
16590
16591 function saveFile(fileUrl,content)
16592 {
16593         var r = null;
16594         if(!r)
16595                 r = mozillaSaveFile(fileUrl,content);
16596         if(!r)
16597                 r = ieSaveFile(fileUrl,content);
16598         if(!r)
16599                 r = javaSaveFile(fileUrl,content);
16600         return r;
16601 }
16602
16603 function loadFile(fileUrl)
16604 {
16605         var r = null;
16606         if((r == null) || (r == false))
16607                 r = mozillaLoadFile(fileUrl);
16608         if((r == null) || (r == false))
16609                 r = ieLoadFile(fileUrl);
16610         if((r == null) || (r == false))
16611                 r = javaLoadFile(fileUrl);
16612         return r;
16613 }
16614
16615 // Returns null if it can't do it, false if there's an error, true if it saved OK
16616 function ieSaveFile(filePath,content)
16617 {
16618         try {
16619                 var fso = new ActiveXObject("Scripting.FileSystemObject");
16620         } catch(ex) {
16621                 return null;
16622         }
16623         var file = fso.OpenTextFile(filePath,2,-1,0);
16624         file.Write(content);
16625         file.Close();
16626         return true;
16627 }
16628
16629 // Returns null if it can't do it, false if there's an error, or a string of the content if successful
16630 function ieLoadFile(filePath)
16631 {
16632         try {
16633                 var fso = new ActiveXObject("Scripting.FileSystemObject");
16634                 var file = fso.OpenTextFile(filePath,1);
16635                 var content = file.ReadAll();
16636                 file.Close();
16637         } catch(ex) {
16638                 return null;
16639         }
16640         return content;
16641 }
16642
16643 function ieCopyFile(dest,source)
16644 {
16645         try {
16646                 var fso = new ActiveXObject("Scripting.FileSystemObject");
16647                 fso.GetFile(source).Copy(dest);
16648         } catch(ex) {
16649                 return false;
16650         }
16651         return true;
16652 }
16653
16654 // Returns null if it can't do it, false if there's an error, true if it saved OK
16655 function mozillaSaveFile(filePath,content)
16656 {
16657         if(window.Components) {
16658                 try {
16659                         netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
16660                         var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
16661                         file.initWithPath(filePath);
16662                         if(!file.exists())
16663                                 file.create(0,0664);
16664                         var out = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream);
16665                         out.init(file,0x20|0x02,00004,null);
16666                         out.write(content,content.length);
16667                         out.flush();
16668                         out.close();
16669                         return true;
16670                 } catch(ex) {
16671                         return false;
16672                 }
16673         }
16674         return null;
16675 }
16676
16677 // Returns null if it can't do it, false if there's an error, or a string of the content if successful
16678 function mozillaLoadFile(filePath)
16679 {
16680         if(window.Components) {
16681                 try {
16682                         netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
16683                         var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
16684                         file.initWithPath(filePath);
16685                         if(!file.exists())
16686                                 return null;
16687                         var inputStream = Components.classes["@mozilla.org/network/file-input-stream;1"].createInstance(Components.interfaces.nsIFileInputStream);
16688                         inputStream.init(file,0x01,00004,null);
16689                         var sInputStream = Components.classes["@mozilla.org/scriptableinputstream;1"].createInstance(Components.interfaces.nsIScriptableInputStream);
16690                         sInputStream.init(inputStream);
16691                         return sInputStream.read(sInputStream.available());
16692                 } catch(ex) {
16693                         return false;
16694                 }
16695         }
16696         return null;
16697 }
16698
16699 function javaUrlToFilename(url)
16700 {
16701         var f = "//localhost";
16702         if(url.indexOf(f) == 0)
16703                 return url.substring(f.length);
16704         var i = url.indexOf(":");
16705         if(i > 0)
16706                 return url.substring(i-1);
16707         return url;
16708 }
16709
16710 function javaSaveFile(filePath,content)
16711 {
16712         try {
16713                 if(document.applets["TiddlySaver"])
16714                         return document.applets["TiddlySaver"].saveFile(javaUrlToFilename(filePath),"UTF-8",content);
16715         } catch(ex) {
16716         }
16717         try {
16718                 var s = new java.io.PrintStream(new java.io.FileOutputStream(javaUrlToFilename(filePath)));
16719                 s.print(content);
16720                 s.close();
16721         } catch(ex) {
16722                 return null;
16723         }
16724         return true;
16725 }
16726
16727 function javaLoadFile(filePath)
16728 {
16729         try {
16730                 if(document.applets["TiddlySaver"])
16731                         return String(document.applets["TiddlySaver"].loadFile(javaUrlToFilename(filePath),"UTF-8"));
16732         } catch(ex) {
16733         }
16734         var content = [];
16735         try {
16736                 var r = new java.io.BufferedReader(new java.io.FileReader(javaUrlToFilename(filePath)));
16737                 var line;
16738                 while((line = r.readLine()) != null)
16739                         content.push(new String(line));
16740                 r.close();
16741         } catch(ex) {
16742                 return null;
16743         }
16744         return content.join("\n");
16745 }
16746
16747 //--
16748 //-- Server adaptor for talking to static files
16749 //--
16750
16751 function FileAdaptor()
16752 {
16753         this.host = null;
16754         this.store = null;
16755         return this;
16756 }
16757
16758 FileAdaptor.NotLoadedError = "TiddlyWiki file has not been loaded";
16759 FileAdaptor.serverType = 'file';
16760
16761 // Open the specified host/server
16762 FileAdaptor.prototype.openHost = function(host,context,userParams,callback)
16763 {
16764         this.host = host;
16765         if(!context)
16766                 context = {};
16767         context.adaptor = this;
16768         context.callback = callback;
16769         context.userParams = userParams;
16770         var ret = loadRemoteFile(host,FileAdaptor.openHostCallback,context);
16771         return typeof(ret) == "string" ? ret : true;
16772 };
16773
16774 FileAdaptor.openHostCallback = function(status,context,responseText,url,xhr)
16775 {
16776         var adaptor = context.adaptor;
16777         context.status = status;
16778         if(!status) {
16779                 context.statusText = "Error reading file: " + xhr.statusText;
16780         } else {
16781                 // Load the content into a TiddlyWiki() object
16782                 adaptor.store = new TiddlyWiki();
16783                 if(!adaptor.store.importTiddlyWiki(responseText))
16784                         context.statusText = config.messages.invalidFileError.format([url]);
16785         }
16786         context.callback(context,context.userParams);
16787 };
16788
16789 // Gets the list of workspaces on a given server
16790 FileAdaptor.prototype.getWorkspaceList = function(context,userParams,callback)
16791 {
16792         if(!context)
16793                 context = {};
16794         context.workspaces = [{title:"(default)"}];
16795         context.status = true;
16796         window.setTimeout(function() {callback(context,userParams);},10);
16797         return true;
16798 };
16799
16800 // Open the specified workspace
16801 FileAdaptor.prototype.openWorkspace = function(workspace,context,userParams,callback)
16802 {
16803         if(!context)
16804                 context = {};
16805         context.status = true;
16806         window.setTimeout(function() {callback(context,userParams);},10);
16807         return true;
16808 };
16809
16810 // Gets the list of tiddlers within a given workspace
16811 FileAdaptor.prototype.getTiddlerList = function(context,userParams,callback)
16812 {
16813         if(!this.store)
16814                 return FileAdaptor.NotLoadedError;
16815         if(!context)
16816                 context = {};
16817         context.tiddlers = [];
16818         this.store.forEachTiddler(function(title,tiddler)
16819                 {
16820                 var t = new Tiddler(title);
16821                 t.text = tiddler.text;
16822                 t.modified = tiddler.modified;
16823                 t.modifier = tiddler.modifier;
16824                 t.fields['server.page.revision'] = tiddler.modified.convertToYYYYMMDDHHMM();
16825                 t.tags = tiddler.tags;
16826                 context.tiddlers.push(t);
16827                 });
16828         context.status = true;
16829         window.setTimeout(function() {callback(context,userParams);},10);
16830         return true;
16831 };
16832
16833 FileAdaptor.prototype.generateTiddlerInfo = function(tiddler)
16834 {
16835         var info = {};
16836         info.uri = tiddler.fields['server.host'] + "#" + tiddler.title;
16837         return info;
16838 };
16839
16840 // Retrieves a tiddler from a given workspace on a given server
16841 FileAdaptor.prototype.getTiddler = function(title,context,userParams,callback)
16842 {
16843         if(!this.store)
16844                 return FileAdaptor.NotLoadedError;
16845         if(!context)
16846                 context = {};
16847         context.tiddler = this.store.fetchTiddler(title);
16848         if(context.tiddler) {
16849                 context.tiddler.fields['server.type'] = FileAdaptor.serverType;
16850                 context.tiddler.fields['server.host'] = this.host;
16851                 context.tiddler.fields['server.page.revision'] = context.tiddler.modified.convertToYYYYMMDDHHMM();
16852         }
16853         context.status = true;
16854         if(context.allowSynchronous) {
16855                 context.isSynchronous = true;
16856                 callback(context,userParams);
16857         } else {
16858                 window.setTimeout(function() {callback(context,userParams);},10);
16859         }
16860         return true;
16861 };
16862
16863 FileAdaptor.prototype.close = function()
16864 {
16865         delete this.store;
16866         this.store = null;
16867 };
16868
16869 config.adaptors[FileAdaptor.serverType] = FileAdaptor;
16870
16871 //--
16872 //-- Remote HTTP requests
16873 //--
16874
16875 function loadRemoteFile(url,callback,params)
16876 {
16877         return doHttp("GET",url,null,null,null,null,callback,params,null);
16878 }
16879
16880 // HTTP status codes
16881 var httpStatus = {
16882         OK: 200,
16883         ContentCreated: 201,
16884         NoContent: 204,
16885         Unauthorized: 401,
16886         Forbidden: 403,
16887         NotFound: 404,
16888         MethodNotAllowed: 405
16889 };
16890
16891 function doHttp(type,url,data,contentType,username,password,callback,params,headers)
16892 {
16893         // Get an xhr object
16894         var x = getXMLHttpRequest();
16895         if(!x)
16896                 return "Can't create XMLHttpRequest object";
16897         // Install callback
16898         x.onreadystatechange = function() {
16899                 if (x.readyState == 4 && callback && (x.status !== undefined)) {
16900                         if([0, httpStatus.OK, httpStatus.ContentCreated, httpStatus.NoContent].contains(x.status))
16901                                 callback(true,params,x.responseText,url,x);
16902                         else
16903                                 callback(false,params,null,url,x);
16904                         x.onreadystatechange = function(){};
16905                         x = null;
16906                 }
16907         };
16908         // Send request
16909         if(window.Components && window.netscape && window.netscape.security && document.location.protocol.indexOf("http") == -1)
16910                 window.netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
16911         try {
16912                 url = url + (url.indexOf("?") < 0 ? "?" : "&") + "nocache=" + Math.random();
16913                 x.open(type,url,true,username,password);
16914                 if (data)
16915                         x.setRequestHeader("Content-Type", contentType ? contentType : "application/x-www-form-urlencoded");
16916                 if (x.overrideMimeType)
16917                         x.setRequestHeader("Connection", "close");
16918                 if(headers) {
16919                         for(n in headers)
16920                                 x.setRequestHeader(n,headers[n]);
16921                 }
16922                 x.setRequestHeader("X-Requested-With", "TiddlyWiki " + version.major + "." + version.minor + "." + version.revision + (version.beta ? " (beta " + version.beta + ")" : ""));
16923                 x.send(data);
16924         } catch (ex) {
16925                 return exceptionText(ex);
16926         }
16927         return x;
16928 }
16929
16930 function getXMLHttpRequest()
16931 {
16932         try {
16933                 var x = new XMLHttpRequest(); // Modern
16934         } catch(ex) {
16935                 try {
16936                         x = new ActiveXObject("Msxml2.XMLHTTP"); // IE 6
16937                 } catch (ex2) {
16938                         return null;
16939                 }
16940         }
16941         return x;
16942 }
16943
16944 //--
16945 //-- TiddlyWiki-specific utility functions
16946 //--
16947
16948 function createTiddlyButton(theParent,theText,theTooltip,theAction,theClass,theId,theAccessKey)
16949 {
16950         var theButton = document.createElement("a");
16951         if(theAction) {
16952                 theButton.onclick = theAction;
16953                 theButton.setAttribute("href","javascript:;");
16954         }
16955         if(theTooltip)
16956                 theButton.setAttribute("title",theTooltip);
16957         if(theText)
16958                 theButton.appendChild(document.createTextNode(theText));
16959         if(theClass)
16960                 theButton.className = theClass;
16961         else
16962                 theButton.className = "button";
16963         if(theId)
16964                 theButton.id = theId;
16965         if(theParent)
16966                 theParent.appendChild(theButton);
16967         if(theAccessKey)
16968                 theButton.setAttribute("accessKey",theAccessKey);
16969         return theButton;
16970 }
16971
16972 function createTiddlyLink(place,title,includeText,theClass,isStatic,linkedFromTiddler,noToggle)
16973 {
16974         var text = includeText ? title : null;
16975         var i = getTiddlyLinkInfo(title,theClass);
16976         var btn = isStatic ? createExternalLink(place,store.getTiddlerText("SiteUrl",null) + "#" + title) : createTiddlyButton(place,text,i.subTitle,onClickTiddlerLink,i.classes);
16977         btn.setAttribute("refresh","link");
16978         btn.setAttribute("tiddlyLink",title);
16979         if(noToggle)
16980                 btn.setAttribute("noToggle","true");
16981         if(linkedFromTiddler) {
16982                 var fields = linkedFromTiddler.getInheritedFields();
16983                 if(fields)
16984                         btn.setAttribute("tiddlyFields",fields);
16985         }
16986         return btn;
16987 }
16988
16989 function refreshTiddlyLink(e,title)
16990 {
16991         var i = getTiddlyLinkInfo(title,e.className);
16992         e.className = i.classes;
16993         e.title = i.subTitle;
16994 }
16995
16996 function getTiddlyLinkInfo(title,currClasses)
16997 {
16998         var classes = currClasses ? currClasses.split(" ") : [];
16999         classes.pushUnique("tiddlyLink");
17000         var tiddler = store.fetchTiddler(title);
17001         var subTitle;
17002         if(tiddler) {
17003                 subTitle = tiddler.getSubtitle();
17004                 classes.pushUnique("tiddlyLinkExisting");
17005                 classes.remove("tiddlyLinkNonExisting");
17006                 classes.remove("shadow");
17007         } else {
17008                 classes.remove("tiddlyLinkExisting");
17009                 classes.pushUnique("tiddlyLinkNonExisting");
17010                 if(store.isShadowTiddler(title)) {
17011                         subTitle = config.messages.shadowedTiddlerToolTip.format([title]);
17012                         classes.pushUnique("shadow");
17013                 } else {
17014                         subTitle = config.messages.undefinedTiddlerToolTip.format([title]);
17015                         classes.remove("shadow");
17016                 }
17017         }
17018         if(config.annotations[title])
17019                 subTitle = config.annotations[title];
17020         return {classes: classes.join(" "),subTitle: subTitle};
17021 }
17022
17023 function createExternalLink(place,url)
17024 {
17025         var theLink = document.createElement("a");
17026         theLink.className = "externalLink";
17027         theLink.href = url;
17028         theLink.title = config.messages.externalLinkTooltip.format([url]);
17029         if(config.options.chkOpenInNewWindow)
17030                 theLink.target = "_blank";
17031         place.appendChild(theLink);
17032         return theLink;
17033 }
17034
17035 // Event handler for clicking on a tiddly link
17036 function onClickTiddlerLink(e)
17037 {
17038         if(!e) e = window.event;
17039         var theTarget = resolveTarget(e);
17040         var theLink = theTarget;
17041         var title = null;
17042         var fields = null;
17043         var noToggle = null;
17044         do {
17045                 title = theLink.getAttribute("tiddlyLink");
17046                 fields = theLink.getAttribute("tiddlyFields");
17047                 noToggle = theLink.getAttribute("noToggle");
17048                 theLink = theLink.parentNode;
17049         } while(title == null && theLink != null);
17050         if(!fields && !store.isShadowTiddler(title))
17051                 fields = String.encodeHashMap(config.defaultCustomFields);
17052         if(title) {
17053                 var toggling = e.metaKey || e.ctrlKey;
17054                 if(config.options.chkToggleLinks)
17055                         toggling = !toggling;
17056                 if(noToggle)
17057                         toggling = false;
17058                 story.displayTiddler(theTarget,title,null,true,null,fields,toggling);
17059         }
17060         clearMessage();
17061         return false;
17062 }
17063
17064 // Create a button for a tag with a popup listing all the tiddlers that it tags
17065 function createTagButton(place,tag,excludeTiddler)
17066 {
17067         var theTag = createTiddlyButton(place,tag,config.views.wikified.tag.tooltip.format([tag]),onClickTag);
17068         theTag.setAttribute("tag",tag);
17069         if(excludeTiddler)
17070                 theTag.setAttribute("tiddler",excludeTiddler);
17071         return theTag;
17072 }
17073
17074 // Event handler for clicking on a tiddler tag
17075 function onClickTag(e)
17076 {
17077         if(!e) var e = window.event;
17078         var theTarget = resolveTarget(e);
17079         var popup = Popup.create(this);
17080         var tag = this.getAttribute("tag");
17081         var title = this.getAttribute("tiddler");
17082         if(popup && tag) {
17083                 var tagged = store.getTaggedTiddlers(tag);
17084                 var titles = [];
17085                 var li,r;
17086                 for(r=0;r<tagged.length;r++) {
17087                         if(tagged[r].title != title)
17088                                 titles.push(tagged[r].title);
17089                 }
17090                 var lingo = config.views.wikified.tag;
17091                 if(titles.length > 0) {
17092                         var openAll = createTiddlyButton(createTiddlyElement(popup,"li"),lingo.openAllText.format([tag]),lingo.openAllTooltip,onClickTagOpenAll);
17093                         openAll.setAttribute("tag",tag);
17094                         createTiddlyElement(createTiddlyElement(popup,"li",null,"listBreak"),"div");
17095                         for(r=0; r<titles.length; r++) {
17096                                 createTiddlyLink(createTiddlyElement(popup,"li"),titles[r],true);
17097                         }
17098                 } else {
17099                         createTiddlyText(createTiddlyElement(popup,"li",null,"disabled"),lingo.popupNone.format([tag]));
17100                 }
17101                 createTiddlyElement(createTiddlyElement(popup,"li",null,"listBreak"),"div");
17102                 var h = createTiddlyLink(createTiddlyElement(popup,"li"),tag,false);
17103                 createTiddlyText(h,lingo.openTag.format([tag]));
17104         }
17105         Popup.show();
17106         e.cancelBubble = true;
17107         if(e.stopPropagation) e.stopPropagation();
17108         return false;
17109 }
17110
17111 // Event handler for 'open all' on a tiddler popup
17112 function onClickTagOpenAll(e)
17113 {
17114         if(!e) var e = window.event;
17115         var tag = this.getAttribute("tag");
17116         var tagged = store.getTaggedTiddlers(tag);
17117         var titles = [];
17118         for(var t=0; t<tagged.length; t++)
17119                 titles.push(tagged[t].title);
17120         story.displayTiddlers(this,titles);
17121         return false;
17122 }
17123
17124 function onClickError(e)
17125 {
17126         if(!e) var e = window.event;
17127         var popup = Popup.create(this);
17128         var lines = this.getAttribute("errorText").split("\n");
17129         for(var t=0; t<lines.length; t++)
17130                 createTiddlyElement(popup,"li",null,null,lines[t]);
17131         Popup.show();
17132         e.cancelBubble = true;
17133         if(e.stopPropagation) e.stopPropagation();
17134         return false;
17135 }
17136
17137 function createTiddlyDropDown(place,onchange,options,defaultValue)
17138 {
17139         var sel = createTiddlyElement(place,"select");
17140         sel.onchange = onchange;
17141         for(var t=0; t<options.length; t++) {
17142                 var e = createTiddlyElement(sel,"option",null,null,options[t].caption);
17143                 e.value = options[t].name;
17144                 if(options[t].name == defaultValue)
17145                         e.selected = true;
17146         }
17147         return sel;
17148 }
17149
17150 function createTiddlyPopup(place,caption,tooltip,tiddler)
17151 {
17152         if(tiddler.text) {
17153                 createTiddlyLink(place,caption,true);
17154                 var btn = createTiddlyButton(place,glyph("downArrow"),tooltip,onClickTiddlyPopup,"tiddlerPopupButton");
17155                 btn.tiddler = tiddler;
17156         } else {
17157                 createTiddlyText(place,caption);
17158         }
17159 }
17160
17161 function onClickTiddlyPopup(e)
17162 {
17163         if(!e) var e = window.event;
17164         var tiddler = this.tiddler;
17165         if(tiddler.text) {
17166                 var popup = Popup.create(this,"div","popupTiddler");
17167                 wikify(tiddler.text,popup,null,tiddler);
17168                 Popup.show();
17169         }
17170         if(e) e.cancelBubble = true;
17171         if(e && e.stopPropagation) e.stopPropagation();
17172         return false;
17173 }
17174
17175 function createTiddlyError(place,title,text)
17176 {
17177         var btn = createTiddlyButton(place,title,null,onClickError,"errorButton");
17178         if(text) btn.setAttribute("errorText",text);
17179 }
17180
17181 function merge(dst,src,preserveExisting)
17182 {
17183         for(p in src) {
17184                 if(!preserveExisting || dst[p] === undefined)
17185                         dst[p] = src[p];
17186         }
17187         return dst;
17188 }
17189
17190 // Returns a string containing the description of an exception, optionally prepended by a message
17191 function exceptionText(e,message)
17192 {
17193         var s = e.description ? e.description : e.toString();
17194         return message ? "%0:\n%1".format([message,s]) : s;
17195 }
17196
17197 // Displays an alert of an exception description with optional message
17198 function showException(e,message)
17199 {
17200         alert(exceptionText(e,message));
17201 }
17202
17203 function alertAndThrow(m)
17204 {
17205         alert(m);
17206         throw(m);
17207 }
17208
17209 function glyph(name)
17210 {
17211         var g = config.glyphs;
17212         var b = g.currBrowser;
17213         if(b == null) {
17214                 b = 0;
17215                 while(!g.browsers[b]() && b < g.browsers.length-1)
17216                         b++;
17217                 g.currBrowser = b;
17218         }
17219         if(!g.codes[name])
17220                 return "";
17221         return g.codes[name][b];
17222 }
17223 //-
17224 //- Animation engine
17225 //-
17226
17227 function Animator()
17228 {
17229         this.running = 0; // Incremented at start of each animation, decremented afterwards. If zero, the interval timer is disabled
17230         this.timerID = 0; // ID of the timer used for animating
17231         this.animations = []; // List of animations in progress
17232         return this;
17233 }
17234
17235 // Start animation engine
17236 Animator.prototype.startAnimating = function() // Variable number of arguments
17237 {
17238         for(var t=0; t<arguments.length; t++)
17239                 this.animations.push(arguments[t]);
17240         if(this.running == 0) {
17241                 var me = this;
17242                 this.timerID = window.setInterval(function() {me.doAnimate(me);},10);
17243         }
17244         this.running += arguments.length;
17245 };
17246
17247 // Perform an animation engine tick, calling each of the known animation modules
17248 Animator.prototype.doAnimate = function(me)
17249 {
17250         var a = 0;
17251         while(a < me.animations.length) {
17252                 var animation = me.animations[a];
17253                 if(animation.tick()) {
17254                         a++;
17255                 } else {
17256                         me.animations.splice(a,1);
17257                         if(--me.running == 0)
17258                                 window.clearInterval(me.timerID);
17259                 }
17260         }
17261 };
17262
17263 // Map a 0..1 value to 0..1, but slow down at the start and end
17264 Animator.slowInSlowOut = function(progress)
17265 {
17266         return(1-((Math.cos(progress * Math.PI)+1)/2));
17267 };
17268
17269 //--
17270 //-- Morpher animation
17271 //--
17272
17273 // Animate a set of properties of an element
17274 function Morpher(element,duration,properties,callback)
17275 {
17276         this.element = element;
17277         this.duration = duration;
17278         this.properties = properties;
17279         this.startTime = new Date();
17280         this.endTime = Number(this.startTime) + duration;
17281         this.callback = callback;
17282         this.tick();
17283         return this;
17284 }
17285
17286 Morpher.prototype.assignStyle = function(element,style,value)
17287 {
17288         switch(style) {
17289                 case "-tw-vertScroll":
17290                         window.scrollTo(findScrollX(),value);
17291                         break;
17292                 case "-tw-horizScroll":
17293                         window.scrollTo(value,findScrollY());
17294                         break;
17295                 default:
17296                         element.style[style] = value;
17297                         break;
17298         }
17299 };
17300
17301 Morpher.prototype.stop = function()
17302 {
17303         for(var t=0; t<this.properties.length; t++) {
17304                 var p = this.properties[t];
17305                 if(p.atEnd !== undefined) {
17306                         this.assignStyle(this.element,p.style,p.atEnd);
17307                 }
17308         }
17309         if(this.callback)
17310                 this.callback(this.element,this.properties);
17311 };
17312
17313 Morpher.prototype.tick = function()
17314 {
17315         var currTime = Number(new Date());
17316         progress = Animator.slowInSlowOut(Math.min(1,(currTime-this.startTime)/this.duration));
17317         for(var t=0; t<this.properties.length; t++) {
17318                 var p = this.properties[t];
17319                 if(p.start !== undefined && p.end !== undefined) {
17320                         var template = p.template ? p.template : "%0";
17321                         switch(p.format) {
17322                                 case undefined:
17323                                 case "style":
17324                                         var v = p.start + (p.end-p.start) * progress;
17325                                         this.assignStyle(this.element,p.style,template.format([v]));
17326                                         break;
17327                                 case "color":
17328                                         break;
17329                         }
17330                 }
17331         }
17332         if(currTime >= this.endTime) {
17333                 this.stop();
17334                 return false;
17335         }
17336         return true;
17337 };
17338
17339 //--
17340 //-- Zoomer animation
17341 //--
17342
17343 function Zoomer(text,startElement,targetElement,unused)
17344 {
17345         var e = createTiddlyElement(document.body,"div",null,"zoomer");
17346         createTiddlyElement(e,"div",null,null,text);
17347         var winWidth = findWindowWidth();
17348         var winHeight = findWindowHeight();
17349         var p = [
17350                 {style: 'left', start: findPosX(startElement), end: findPosX(targetElement), template: '%0px'},
17351                 {style: 'top', start: findPosY(startElement), end: findPosY(targetElement), template: '%0px'},
17352                 {style: 'width', start: Math.min(startElement.scrollWidth,winWidth), end: Math.min(targetElement.scrollWidth,winWidth), template: '%0px', atEnd: 'auto'},
17353                 {style: 'height', start: Math.min(startElement.scrollHeight,winHeight), end: Math.min(targetElement.scrollHeight,winHeight), template: '%0px', atEnd: 'auto'},
17354                 {style: 'fontSize', start: 8, end: 24, template: '%0pt'}
17355         ];
17356         var c = function(element,properties) {removeNode(element);};
17357         return new Morpher(e,config.animDuration,p,c);
17358 }
17359
17360 //--
17361 //-- Scroller animation
17362 //--
17363
17364 function Scroller(targetElement,unused)
17365 {
17366         var p = [
17367                 {style: '-tw-vertScroll', start: findScrollY(), end: ensureVisible(targetElement)}
17368         ];
17369         return new Morpher(targetElement,config.animDuration,p);
17370 }
17371
17372 //--
17373 //-- Slider animation
17374 //--
17375
17376 // deleteMode - "none", "all" [delete target element and it's children], [only] "children" [but not the target element]
17377 function Slider(element,opening,unused,deleteMode)
17378 {
17379         element.style.overflow = 'hidden';
17380         if(opening)
17381                 element.style.height = '0px'; // Resolves a Firefox flashing bug
17382         element.style.display = 'block';
17383         var left = findPosX(element);
17384         var width = element.scrollWidth;
17385         var height = element.scrollHeight;
17386         var winWidth = findWindowWidth();
17387         var p = [];
17388         var c = null;
17389         if(opening) {
17390                 p.push({style: 'height', start: 0, end: height, template: '%0px', atEnd: 'auto'});
17391                 p.push({style: 'opacity', start: 0, end: 1, template: '%0'});
17392                 p.push({style: 'filter', start: 0, end: 100, template: 'alpha(opacity:%0)'});
17393         } else {
17394                 p.push({style: 'height', start: height, end: 0, template: '%0px'});
17395                 p.push({style: 'display', atEnd: 'none'});
17396                 p.push({style: 'opacity', start: 1, end: 0, template: '%0'});
17397                 p.push({style: 'filter', start: 100, end: 0, template: 'alpha(opacity:%0)'});
17398                 switch(deleteMode) {
17399                         case "all":
17400                                 c = function(element,properties) {removeNode(element);};
17401                                 break;
17402                         case "children":
17403                                 c = function(element,properties) {removeChildren(element);};
17404                                 break;
17405                 }
17406         }
17407         return new Morpher(element,config.animDuration,p,c);
17408 }
17409
17410 //--
17411 //-- Popup menu
17412 //--
17413
17414 var Popup = {
17415         stack: [] // Array of objects with members root: and popup:
17416         };
17417
17418 Popup.create = function(root,elem,theClass)
17419 {
17420         Popup.remove();
17421         var popup = createTiddlyElement(document.body,elem ? elem : "ol","popup",theClass ? theClass : "popup");
17422         Popup.stack.push({root: root, popup: popup});
17423         return popup;
17424 };
17425
17426 Popup.onDocumentClick = function(e)
17427 {
17428         if (!e) var e = window.event;
17429         var target = resolveTarget(e);
17430         if(e.eventPhase == undefined)
17431                 Popup.remove();
17432         else if(e.eventPhase == Event.BUBBLING_PHASE || e.eventPhase == Event.AT_TARGET)
17433                 Popup.remove();
17434         return true;
17435 };
17436
17437 Popup.show = function(unused1,unused2)
17438 {
17439         var curr = Popup.stack[Popup.stack.length-1];
17440         this.place(curr.root,curr.popup);
17441         addClass(curr.root,"highlight");
17442         if(config.options.chkAnimate && anim && typeof Scroller == "function")
17443                 anim.startAnimating(new Scroller(curr.popup));
17444         else
17445                 window.scrollTo(0,ensureVisible(curr.popup));
17446 };
17447
17448 Popup.place = function(root,popup,offset)
17449 {
17450         if(!offset) var offset = {x:0, y:0};
17451         var rootLeft = findPosX(root);
17452         var rootTop = findPosY(root);
17453         var rootHeight = root.offsetHeight;
17454         var popupLeft = rootLeft + offset.x;
17455         var popupTop = rootTop + rootHeight + offset.y;
17456         var winWidth = findWindowWidth();
17457         if(popup.offsetWidth > winWidth*0.75)
17458                 popup.style.width = winWidth*0.75 + "px";
17459         var popupWidth = popup.offsetWidth;
17460         if(popupLeft + popupWidth > winWidth)
17461                 popupLeft = winWidth - popupWidth;
17462         popup.style.left = popupLeft + "px";
17463         popup.style.top = popupTop + "px";
17464         popup.style.display = "block";
17465 }
17466
17467 Popup.remove = function()
17468 {
17469         if(Popup.stack.length > 0) {
17470                 Popup.removeFrom(0);
17471         }
17472 };
17473
17474 Popup.removeFrom = function(from)
17475 {
17476         for(var t=Popup.stack.length-1; t>=from; t--) {
17477                 var p = Popup.stack[t];
17478                 removeClass(p.root,"highlight");
17479                 removeNode(p.popup);
17480         }
17481         Popup.stack = Popup.stack.slice(0,from);
17482 };
17483
17484 //--
17485 //-- Wizard support
17486 //--
17487
17488 function Wizard(elem)
17489 {
17490         if(elem) {
17491                 this.formElem = findRelated(elem,"wizard","className");
17492                 this.bodyElem = findRelated(this.formElem.firstChild,"wizardBody","className","nextSibling");
17493                 this.footElem = findRelated(this.formElem.firstChild,"wizardFooter","className","nextSibling");
17494         } else {
17495                 this.formElem = null;
17496                 this.bodyElem = null;
17497                 this.footElem = null;
17498         }
17499 }
17500
17501 Wizard.prototype.setValue = function(name,value)
17502 {
17503         if(this.formElem)
17504                 this.formElem[name] = value;
17505 };
17506
17507 Wizard.prototype.getValue = function(name)
17508 {
17509         return this.formElem ? this.formElem[name] : null;
17510 };
17511
17512 Wizard.prototype.createWizard = function(place,title)
17513 {
17514         this.formElem = createTiddlyElement(place,"form",null,"wizard");
17515         createTiddlyElement(this.formElem,"h1",null,null,title);
17516         this.bodyElem = createTiddlyElement(this.formElem,"div",null,"wizardBody");
17517         this.footElem = createTiddlyElement(this.formElem,"div",null,"wizardFooter");
17518 };
17519
17520 Wizard.prototype.clear = function()
17521 {
17522         removeChildren(this.bodyElem);
17523 };
17524
17525 Wizard.prototype.setButtons = function(buttonInfo,status)
17526 {
17527         removeChildren(this.footElem);
17528         for(var t=0; t<buttonInfo.length; t++) {
17529                 createTiddlyButton(this.footElem,buttonInfo[t].caption,buttonInfo[t].tooltip,buttonInfo[t].onClick);
17530                 insertSpacer(this.footElem);
17531                 }
17532         if(typeof status == "string") {
17533                 createTiddlyElement(this.footElem,"span",null,"status",status);
17534         }
17535 };
17536
17537 Wizard.prototype.addStep = function(stepTitle,html)
17538 {
17539         removeChildren(this.bodyElem);
17540         var w = createTiddlyElement(this.bodyElem,"div");
17541         createTiddlyElement(w,"h2",null,null,stepTitle);
17542         var step = createTiddlyElement(w,"div",null,"wizardStep");
17543         step.innerHTML = html;
17544         applyHtmlMacros(step,tiddler);
17545 };
17546
17547 Wizard.prototype.getElement = function(name)
17548 {
17549         return this.formElem.elements[name];
17550 };
17551
17552 //--
17553 //-- ListView gadget
17554 //--
17555
17556 var ListView = {};
17557
17558 // Create a listview
17559 ListView.create = function(place,listObject,listTemplate,callback,className)
17560 {
17561         var table = createTiddlyElement(place,"table",null,className ? className : "listView twtable");
17562         var thead = createTiddlyElement(table,"thead");
17563         var r = createTiddlyElement(thead,"tr");
17564         for(var t=0; t<listTemplate.columns.length; t++) {
17565                 var columnTemplate = listTemplate.columns[t];
17566                 var c = createTiddlyElement(r,"th");
17567                 var colType = ListView.columnTypes[columnTemplate.type];
17568                 if(colType && colType.createHeader)
17569                         colType.createHeader(c,columnTemplate,t);
17570         }
17571         var tbody = createTiddlyElement(table,"tbody");
17572         for(var rc=0; rc<listObject.length; rc++) {
17573                 rowObject = listObject[rc];
17574                 r = createTiddlyElement(tbody,"tr");
17575                 for(c=0; c<listTemplate.rowClasses.length; c++) {
17576                         if(rowObject[listTemplate.rowClasses[c].field])
17577                                 addClass(r,listTemplate.rowClasses[c].className);
17578                 }
17579                 rowObject.rowElement = r;
17580                 rowObject.colElements = {};
17581                 for(var cc=0; cc<listTemplate.columns.length; cc++) {
17582                         c = createTiddlyElement(r,"td");
17583                         columnTemplate = listTemplate.columns[cc];
17584                         var field = columnTemplate.field;
17585                         colType = ListView.columnTypes[columnTemplate.type];
17586                         if(colType && colType.createItem)
17587                                 colType.createItem(c,rowObject,field,columnTemplate,cc,rc);
17588                         rowObject.colElements[field] = c;
17589                 }
17590         }
17591         if(callback && listTemplate.actions)
17592                 createTiddlyDropDown(place,ListView.getCommandHandler(callback),listTemplate.actions);
17593         if(callback && listTemplate.buttons) {
17594                 for(t=0; t<listTemplate.buttons.length; t++) {
17595                         var a = listTemplate.buttons[t];
17596                         if(a && a.name != "")
17597                                 createTiddlyButton(place,a.caption,null,ListView.getCommandHandler(callback,a.name,a.allowEmptySelection));
17598                 }
17599         }
17600         return table;
17601 };
17602
17603 ListView.getCommandHandler = function(callback,name,allowEmptySelection)
17604 {
17605         return function(e) {
17606                 var view = findRelated(this,"TABLE",null,"previousSibling");
17607                 var tiddlers = [];
17608                 ListView.forEachSelector(view,function(e,rowName) {
17609                                         if(e.checked)
17610                                                 tiddlers.push(rowName);
17611                                         });
17612                 if(tiddlers.length == 0 && !allowEmptySelection) {
17613                         alert(config.messages.nothingSelected);
17614                 } else {
17615                         if(this.nodeName.toLowerCase() == "select") {
17616                                 callback(view,this.value,tiddlers);
17617                                 this.selectedIndex = 0;
17618                         } else {
17619                                 callback(view,name,tiddlers);
17620                         }
17621                 }
17622         };
17623 };
17624
17625 // Invoke a callback for each selector checkbox in the listview
17626 ListView.forEachSelector = function(view,callback)
17627 {
17628         var checkboxes = view.getElementsByTagName("input");
17629         var hadOne = false;
17630         for(var t=0; t<checkboxes.length; t++) {
17631                 var cb = checkboxes[t];
17632                 if(cb.getAttribute("type") == "checkbox") {
17633                         var rn = cb.getAttribute("rowName");
17634                         if(rn) {
17635                                 callback(cb,rn);
17636                                 hadOne = true;
17637                         }
17638                 }
17639         }
17640         return hadOne;
17641 };
17642
17643 ListView.getSelectedRows = function(view)
17644 {
17645         var rowNames = [];
17646         ListView.forEachSelector(view,function(e,rowName) {
17647                                 if(e.checked)
17648                                         rowNames.push(rowName);
17649                                 });
17650         return rowNames;
17651 };
17652
17653 ListView.columnTypes = {};
17654
17655 ListView.columnTypes.String = {
17656         createHeader: function(place,columnTemplate,col)
17657                 {
17658                         createTiddlyText(place,columnTemplate.title);
17659                 },
17660         createItem: function(place,listObject,field,columnTemplate,col,row)
17661                 {
17662                         var v = listObject[field];
17663                         if(v != undefined)
17664                                 createTiddlyText(place,v);
17665                 }
17666 };
17667
17668 ListView.columnTypes.WikiText = {
17669         createHeader: ListView.columnTypes.String.createHeader,
17670         createItem: function(place,listObject,field,columnTemplate,col,row)
17671                 {
17672                         var v = listObject[field];
17673                         if(v != undefined)
17674                                 wikify(v,place,null,null);
17675                 }
17676 };
17677
17678 ListView.columnTypes.Tiddler = {
17679         createHeader: ListView.columnTypes.String.createHeader,
17680         createItem: function(place,listObject,field,columnTemplate,col,row)
17681                 {
17682                         var v = listObject[field];
17683                         if(v != undefined && v.title)
17684                                 createTiddlyPopup(place,v.title,config.messages.listView.tiddlerTooltip,v);
17685                 }
17686 };
17687
17688 ListView.columnTypes.Size = {
17689         createHeader: ListView.columnTypes.String.createHeader,
17690         createItem: function(place,listObject,field,columnTemplate,col,row)
17691                 {
17692                         var v = listObject[field];
17693                         if(v != undefined) {
17694                                 var t = 0;
17695                                 while(t<config.messages.sizeTemplates.length-1 && v<config.messages.sizeTemplates[t].unit)
17696                                         t++;
17697                                 createTiddlyText(place,config.messages.sizeTemplates[t].template.format([Math.round(v/config.messages.sizeTemplates[t].unit)]));
17698                         }
17699                 }
17700 };
17701
17702 ListView.columnTypes.Link = {
17703         createHeader: ListView.columnTypes.String.createHeader,
17704         createItem: function(place,listObject,field,columnTemplate,col,row)
17705                 {
17706                         var v = listObject[field];
17707                         var c = columnTemplate.text;
17708                         if(v != undefined)
17709                                 createTiddlyText(createExternalLink(place,v),c ? c : v);
17710                 }
17711 };
17712
17713 ListView.columnTypes.Date = {
17714         createHeader: ListView.columnTypes.String.createHeader,
17715         createItem: function(place,listObject,field,columnTemplate,col,row)
17716                 {
17717                         var v = listObject[field];
17718                         if(v != undefined)
17719                                 createTiddlyText(place,v.formatString(columnTemplate.dateFormat));
17720                 }
17721 };
17722
17723 ListView.columnTypes.StringList = {
17724         createHeader: ListView.columnTypes.String.createHeader,
17725         createItem: function(place,listObject,field,columnTemplate,col,row)
17726                 {
17727                         var v = listObject[field];
17728                         if(v != undefined) {
17729                                 for(var t=0; t<v.length; t++) {
17730                                         createTiddlyText(place,v[t]);
17731                                         createTiddlyElement(place,"br");
17732                                 }
17733                         }
17734                 }
17735 };
17736
17737 ListView.columnTypes.Selector = {
17738         createHeader: function(place,columnTemplate,col)
17739                 {
17740                         createTiddlyCheckbox(place,null,false,this.onHeaderChange);
17741                 },
17742         createItem: function(place,listObject,field,columnTemplate,col,row)
17743                 {
17744                         var e = createTiddlyCheckbox(place,null,listObject[field],null);
17745                         e.setAttribute("rowName",listObject[columnTemplate.rowName]);
17746                 },
17747         onHeaderChange: function(e)
17748                 {
17749                         var state = this.checked;
17750                         var view = findRelated(this,"TABLE");
17751                         if(!view)
17752                                 return;
17753                         ListView.forEachSelector(view,function(e,rowName) {
17754                                                                 e.checked = state;
17755                                                         });
17756                 }
17757 };
17758
17759 ListView.columnTypes.Tags = {
17760         createHeader: ListView.columnTypes.String.createHeader,
17761         createItem: function(place,listObject,field,columnTemplate,col,row)
17762                 {
17763                         var tags = listObject[field];
17764                         createTiddlyText(place,String.encodeTiddlyLinkList(tags));
17765                 }
17766 };
17767
17768 ListView.columnTypes.Boolean = {
17769         createHeader: ListView.columnTypes.String.createHeader,
17770         createItem: function(place,listObject,field,columnTemplate,col,row)
17771                 {
17772                         if(listObject[field] == true)
17773                                 createTiddlyText(place,columnTemplate.trueText);
17774                         if(listObject[field] == false)
17775                                 createTiddlyText(place,columnTemplate.falseText);
17776                 }
17777 };
17778
17779 ListView.columnTypes.TagCheckbox = {
17780         createHeader: ListView.columnTypes.String.createHeader,
17781         createItem: function(place,listObject,field,columnTemplate,col,row)
17782                 {
17783                         var e = createTiddlyCheckbox(place,null,listObject[field],this.onChange);
17784                         e.setAttribute("tiddler",listObject.title);
17785                         e.setAttribute("tag",columnTemplate.tag);
17786                 },
17787         onChange : function(e)
17788                 {
17789                         var tag = this.getAttribute("tag");
17790                         var tiddler = this.getAttribute("tiddler");
17791                         store.setTiddlerTag(tiddler,this.checked,tag);
17792                 }
17793 };
17794
17795 ListView.columnTypes.TiddlerLink = {
17796         createHeader: ListView.columnTypes.String.createHeader,
17797         createItem: function(place,listObject,field,columnTemplate,col,row)
17798                 {
17799                         var v = listObject[field];
17800                         if(v != undefined) {
17801                                 var link = createTiddlyLink(place,listObject[columnTemplate.tiddlerLink],false,null);
17802                                 createTiddlyText(link,listObject[field]);
17803                         }
17804                 }
17805 };
17806
17807 //--
17808 //-- Augmented methods for the JavaScript Number(), Array(), String() and Date() objects
17809 //--
17810
17811 // Clamp a number to a range
17812 Number.prototype.clamp = function(min,max)
17813 {
17814         var c = this;
17815         if(c < min)
17816                 c = min;
17817         if(c > max)
17818                 c = max;
17819         return c;
17820 };
17821
17822 // Add indexOf function if browser does not support it
17823 if(!Array.indexOf) {
17824 Array.prototype.indexOf = function(item,from)
17825 {
17826         if(!from)
17827                 from = 0;
17828         for(var i=from; i<this.length; i++) {
17829                 if(this[i] === item)
17830                         return i;
17831         }
17832         return -1;
17833 };}
17834
17835 // Find an entry in a given field of the members of an array
17836 Array.prototype.findByField = function(field,value)
17837 {
17838         for(var t=0; t<this.length; t++) {
17839                 if(this[t][field] == value)
17840                         return t;
17841         }
17842         return null;
17843 };
17844
17845 // Return whether an entry exists in an array
17846 Array.prototype.contains = function(item)
17847 {
17848         return this.indexOf(item) != -1;
17849 };
17850
17851 // Adds, removes or toggles a particular value within an array
17852 //  value - value to add
17853 //  mode - +1 to add value, -1 to remove value, 0 to toggle it
17854 Array.prototype.setItem = function(value,mode)
17855 {
17856         var p = this.indexOf(value);
17857         if(mode == 0)
17858                 mode = (p == -1) ? +1 : -1;
17859         if(mode == +1) {
17860                 if(p == -1)
17861                         this.push(value);
17862         } else if(mode == -1) {
17863                 if(p != -1)
17864                         this.splice(p,1);
17865         }
17866 };
17867
17868 // Return whether one of a list of values exists in an array
17869 Array.prototype.containsAny = function(items)
17870 {
17871         for(var i=0; i<items.length; i++) {
17872                 if (this.indexOf(items[i]) != -1)
17873                         return true;
17874         }
17875         return false;
17876 };
17877
17878 // Return whether all of a list of values exists in an array
17879 Array.prototype.containsAll = function(items)
17880 {
17881         for (var i = 0; i<items.length; i++) {
17882                 if (this.indexOf(items[i]) == -1)
17883                         return false;
17884         }
17885         return true;
17886 };
17887
17888 // 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
17889 Array.prototype.pushUnique = function(item,unique)
17890 {
17891         if(unique === false) {
17892                 this.push(item);
17893         } else {
17894                 if(this.indexOf(item) == -1)
17895                         this.push(item);
17896         }
17897 };
17898
17899 Array.prototype.remove = function(item)
17900 {
17901         var p = this.indexOf(item);
17902         if(p != -1)
17903                 this.splice(p,1);
17904 };
17905
17906 // Get characters from the right end of a string
17907 String.prototype.right = function(n)
17908 {
17909         return n < this.length ? this.slice(this.length-n) : this;
17910 };
17911
17912 // Trim whitespace from both ends of a string
17913 String.prototype.trim = function()
17914 {
17915         return this.replace(/^\s*|\s*$/g,"");
17916 };
17917
17918 // Convert a string from a CSS style property name to a JavaScript style name ("background-color" -> "backgroundColor")
17919 String.prototype.unDash = function()
17920 {
17921         var s = this.split("-");
17922         if(s.length > 1) {
17923                 for(var t=1; t<s.length; t++)
17924                         s[t] = s[t].substr(0,1).toUpperCase() + s[t].substr(1);
17925         }
17926         return s.join("");
17927 };
17928
17929 // Substitute substrings from an array into a format string that includes '%1'-type specifiers
17930 String.prototype.format = function(substrings)
17931 {
17932         var subRegExp = /(?:%(\d+))/mg;
17933         var currPos = 0;
17934         var r = [];
17935         do {
17936                 var match = subRegExp.exec(this);
17937                 if(match && match[1]) {
17938                         if(match.index > currPos)
17939                                 r.push(this.substring(currPos,match.index));
17940                         r.push(substrings[parseInt(match[1])]);
17941                         currPos = subRegExp.lastIndex;
17942                 }
17943         } while(match);
17944         if(currPos < this.length)
17945                 r.push(this.substring(currPos,this.length));
17946         return r.join("");
17947 };
17948
17949 // Escape any special RegExp characters with that character preceded by a backslash
17950 String.prototype.escapeRegExp = function()
17951 {
17952         var s = "\\^$*+?()=!|,{}[].";
17953         var c = this;
17954         for(var t=0; t<s.length; t++)
17955                 c = c.replace(new RegExp("\\" + s.substr(t,1),"g"),"\\" + s.substr(t,1));
17956         return c;
17957 };
17958
17959 // Convert "\" to "\s", newlines to "\n" (and remove carriage returns)
17960 String.prototype.escapeLineBreaks = function()
17961 {
17962         return this.replace(/\\/mg,"\\s").replace(/\n/mg,"\\n").replace(/\r/mg,"");
17963 };
17964
17965 // Convert "\n" to newlines, "\b" to " ", "\s" to "\" (and remove carriage returns)
17966 String.prototype.unescapeLineBreaks = function()
17967 {
17968         return this.replace(/\\n/mg,"\n").replace(/\\b/mg," ").replace(/\\s/mg,"\\").replace(/\r/mg,"");
17969 };
17970
17971 // Convert & to "&amp;", < to "&lt;", > to "&gt;" and " to "&quot;"
17972 String.prototype.htmlEncode = function()
17973 {
17974         return this.replace(/&/mg,"&amp;").replace(/</mg,"&lt;").replace(/>/mg,"&gt;").replace(/\"/mg,"&quot;");
17975 };
17976
17977 // Convert "&amp;" to &, "&lt;" to <, "&gt;" to > and "&quot;" to "
17978 String.prototype.htmlDecode = function()
17979 {
17980         return this.replace(/&lt;/mg,"<").replace(/&gt;/mg,">").replace(/&quot;/mg,"\"").replace(/&amp;/mg,"&");
17981 };
17982
17983 // Convert a string to it's JSON representation by encoding control characters, double quotes and backslash. See json.org
17984 String.prototype.toJSONString = function()
17985 {
17986         var m = {
17987                 '\b': '\\b',
17988                 '\f': '\\f',
17989                 '\n': '\\n',
17990                 '\r': '\\r',
17991                 '\t': '\\t',
17992                 '"' : '\\"',
17993                 '\\': '\\\\'
17994                 };
17995         var replaceFn = function(a,b) {
17996                 var c = m[b];
17997                 if(c)
17998                         return c;
17999                 c = b.charCodeAt();
18000                 return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
18001                 };
18002         if(/["\\\x00-\x1f]/.test(this))
18003                 return '"' + this.replace(/([\x00-\x1f\\"])/g,replaceFn) + '"';
18004         return '"' + this + '"';
18005 };
18006
18007 // Parse a space-separated string of name:value parameters
18008 // The result is an array of objects:
18009 //   result[0] = object with a member for each parameter name, value of that member being an array of values
18010 //   result[1..n] = one object for each parameter, with 'name' and 'value' members
18011 String.prototype.parseParams = function(defaultName,defaultValue,allowEval,noNames,cascadeDefaults)
18012 {
18013         var parseToken = function(match,p) {
18014                 var n;
18015                 if(match[p]) // Double quoted
18016                         n = match[p];
18017                 else if(match[p+1]) // Single quoted
18018                         n = match[p+1];
18019                 else if(match[p+2]) // Double-square-bracket quoted
18020                         n = match[p+2];
18021                 else if(match[p+3]) // Double-brace quoted
18022                         try {
18023                                 n = match[p+3];
18024                                 if(allowEval)
18025                                         n = window.eval(n);
18026                         } catch(ex) {
18027                                 throw "Unable to evaluate {{" + match[p+3] + "}}: " + exceptionText(ex);
18028                         }
18029                 else if(match[p+4]) // Unquoted
18030                         n = match[p+4];
18031                 else if(match[p+5]) // empty quote
18032                         n = "";
18033                 return n;
18034         };
18035         var r = [{}];
18036         var dblQuote = "(?:\"((?:(?:\\\\\")|[^\"])+)\")";
18037         var sngQuote = "(?:'((?:(?:\\\\\')|[^'])+)')";
18038         var dblSquare = "(?:\\[\\[((?:\\s|\\S)*?)\\]\\])";
18039         var dblBrace = "(?:\\{\\{((?:\\s|\\S)*?)\\}\\})";
18040         var unQuoted = noNames ? "([^\"'\\s]\\S*)" : "([^\"':\\s][^\\s:]*)";
18041         var emptyQuote = "((?:\"\")|(?:''))";
18042         var skipSpace = "(?:\\s*)";
18043         var token = "(?:" + dblQuote + "|" + sngQuote + "|" + dblSquare + "|" + dblBrace + "|" + unQuoted + "|" + emptyQuote + ")";
18044         var re = noNames ? new RegExp(token,"mg") : new RegExp(skipSpace + token + skipSpace + "(?:(\\:)" + skipSpace + token + ")?","mg");
18045         var params = [];
18046         do {
18047                 var match = re.exec(this);
18048                 if(match) {
18049                         var n = parseToken(match,1);
18050                         if(noNames) {
18051                                 r.push({name:"",value:n});
18052                         } else {
18053                                 var v = parseToken(match,8);
18054                                 if(v == null && defaultName) {
18055                                         v = n;
18056                                         n = defaultName;
18057                                 } else if(v == null && defaultValue) {
18058                                         v = defaultValue;
18059                                 }
18060                                 r.push({name:n,value:v});
18061                                 if(cascadeDefaults) {
18062                                         defaultName = n;
18063                                         defaultValue = v;
18064                                 }
18065                         }
18066                 }
18067         } while(match);
18068         // Summarise parameters into first element
18069         for(var t=1; t<r.length; t++) {
18070                 if(r[0][r[t].name])
18071                         r[0][r[t].name].push(r[t].value);
18072                 else
18073                         r[0][r[t].name] = [r[t].value];
18074         }
18075         return r;
18076 };
18077
18078 // Process a string list of macro parameters into an array. Parameters can be quoted with "", '',
18079 // [[]], {{ }} or left unquoted (and therefore space-separated). Double-braces {{}} results in
18080 // an *evaluated* parameter: e.g. {{config.options.txtUserName}} results in the current user's name.
18081 String.prototype.readMacroParams = function()
18082 {
18083         var p = this.parseParams("list",null,true,true);
18084         var n = [];
18085         for(var t=1; t<p.length; t++)
18086                 n.push(p[t].value);
18087         return n;
18088 };
18089
18090 // Process a string list of unique tiddler names into an array. Tiddler names that have spaces in them must be [[bracketed]]
18091 String.prototype.readBracketedList = function(unique)
18092 {
18093         var p = this.parseParams("list",null,false,true);
18094         var n = [];
18095         for(var t=1; t<p.length; t++)
18096                 n.pushUnique(p[t].value,unique);
18097         return n;
18098 };
18099
18100 // Returns array with start and end index of chunk between given start and end marker, or undefined.
18101 String.prototype.getChunkRange = function(start,end) 
18102 {
18103         var s = this.indexOf(start);
18104         if(s != -1) {
18105                 s += start.length;
18106                 var e = this.indexOf(end,s);
18107                 if(e != -1)
18108                         return [s,e];
18109         }
18110 };
18111
18112 // Replace a chunk of a string given start and end markers
18113 String.prototype.replaceChunk = function(start,end,sub)
18114 {
18115         var r = this.getChunkRange(start,end);
18116         return r ? this.substring(0,r[0]) + sub + this.substring(r[1]) : this;
18117 };
18118
18119 // Returns a chunk of a string between start and end markers, or undefined
18120 String.prototype.getChunk = function(start,end)
18121 {
18122         var r = this.getChunkRange(start,end);
18123         if(r)
18124                 return this.substring(r[0],r[1]);
18125 };
18126
18127
18128 // Static method to bracket a string with double square brackets if it contains a space
18129 String.encodeTiddlyLink = function(title)
18130 {
18131         return title.indexOf(" ") == -1 ? title : "[[" + title + "]]";
18132 };
18133
18134 // Static method to encodeTiddlyLink for every item in an array and join them with spaces
18135 String.encodeTiddlyLinkList = function(list)
18136 {
18137         if(list) {
18138                 var results = [];
18139                 for(var t=0; t<list.length; t++)
18140                         results.push(String.encodeTiddlyLink(list[t]));
18141                 return results.join(" ");
18142         } else {
18143                 return "";
18144         }
18145 };
18146
18147 // Convert a string as a sequence of name:"value" pairs into a hashmap
18148 String.prototype.decodeHashMap = function()
18149 {
18150         var fields = this.parseParams("anon","",false);
18151         var r = {};
18152         for(var t=1; t<fields.length; t++)
18153                 r[fields[t].name] = fields[t].value;
18154         return r;
18155 };
18156
18157 // Static method to encode a hashmap into a name:"value"... string
18158 String.encodeHashMap = function(hashmap)
18159 {
18160         var r = [];
18161         for(var t in hashmap)
18162                 r.push(t + ':"' + hashmap[t] + '"');
18163         return r.join(" ");
18164 };
18165
18166 // Static method to left-pad a string with 0s to a certain width
18167 String.zeroPad = function(n,d)
18168 {
18169         var s = n.toString();
18170         if(s.length < d)
18171                 s = "000000000000000000000000000".substr(0,d-s.length) + s;
18172         return s;
18173 };
18174
18175 String.prototype.startsWith = function(prefix) 
18176 {
18177         return !prefix || this.substring(0,prefix.length) == prefix;
18178 };
18179
18180 // Returns the first value of the given named parameter.
18181 function getParam(params,name,defaultValue)
18182 {
18183         if(!params)
18184                 return defaultValue;
18185         var p = params[0][name];
18186         return p ? p[0] : defaultValue;
18187 }
18188
18189 // Returns the first value of the given boolean named parameter.
18190 function getFlag(params,name,defaultValue)
18191 {
18192         return !!getParam(params,name,defaultValue);
18193 }
18194
18195 // Substitute date components into a string
18196 Date.prototype.formatString = function(template)
18197 {
18198         var t = template.replace(/0hh12/g,String.zeroPad(this.getHours12(),2));
18199         t = t.replace(/hh12/g,this.getHours12());
18200         t = t.replace(/0hh/g,String.zeroPad(this.getHours(),2));
18201         t = t.replace(/hh/g,this.getHours());
18202         t = t.replace(/mmm/g,config.messages.dates.shortMonths[this.getMonth()]);
18203         t = t.replace(/0mm/g,String.zeroPad(this.getMinutes(),2));
18204         t = t.replace(/mm/g,this.getMinutes());
18205         t = t.replace(/0ss/g,String.zeroPad(this.getSeconds(),2));
18206         t = t.replace(/ss/g,this.getSeconds());
18207         t = t.replace(/[ap]m/g,this.getAmPm().toLowerCase());
18208         t = t.replace(/[AP]M/g,this.getAmPm().toUpperCase());
18209         t = t.replace(/wYYYY/g,this.getYearForWeekNo());
18210         t = t.replace(/wYY/g,String.zeroPad(this.getYearForWeekNo()-2000,2));
18211         t = t.replace(/YYYY/g,this.getFullYear());
18212         t = t.replace(/YY/g,String.zeroPad(this.getFullYear()-2000,2));
18213         t = t.replace(/MMM/g,config.messages.dates.months[this.getMonth()]);
18214         t = t.replace(/0MM/g,String.zeroPad(this.getMonth()+1,2));
18215         t = t.replace(/MM/g,this.getMonth()+1);
18216         t = t.replace(/0WW/g,String.zeroPad(this.getWeek(),2));
18217         t = t.replace(/WW/g,this.getWeek());
18218         t = t.replace(/DDD/g,config.messages.dates.days[this.getDay()]);
18219         t = t.replace(/ddd/g,config.messages.dates.shortDays[this.getDay()]);
18220         t = t.replace(/0DD/g,String.zeroPad(this.getDate(),2));
18221         t = t.replace(/DDth/g,this.getDate()+this.daySuffix());
18222         t = t.replace(/DD/g,this.getDate());
18223         return t;
18224 };
18225
18226 Date.prototype.getWeek = function()
18227 {
18228         var dt = new Date(this.getTime());
18229         var d = dt.getDay();
18230         if (d==0) d=7;// JavaScript Sun=0, ISO Sun=7
18231         dt.setTime(dt.getTime()+(4-d)*86400000);// shift day to Thurs of same week to calculate weekNo
18232         var n = Math.floor((dt.getTime()-new Date(dt.getFullYear(),0,1)+3600000)/86400000); 
18233         return Math.floor(n/7)+1;
18234 };
18235
18236 Date.prototype.getYearForWeekNo = function()
18237 {
18238         var dt = new Date(this.getTime());
18239         var d = dt.getDay();
18240         if (d==0) d=7;// JavaScript Sun=0, ISO Sun=7
18241         dt.setTime(dt.getTime()+(4-d)*86400000);// shift day to Thurs of same week
18242         return dt.getFullYear();
18243 };
18244
18245 Date.prototype.getHours12 = function()
18246 {
18247         var h = this.getHours();
18248         return h > 12 ? h-12 : ( h > 0 ? h : 12 );
18249 };
18250
18251 Date.prototype.getAmPm = function()
18252 {
18253         return this.getHours() >= 12 ? config.messages.dates.pm : config.messages.dates.am;
18254 };
18255
18256 Date.prototype.daySuffix = function()
18257 {
18258         return config.messages.dates.daySuffixes[this.getDate()-1];
18259 };
18260
18261 // Convert a date to local YYYYMMDDHHMM string format
18262 Date.prototype.convertToLocalYYYYMMDDHHMM = function()
18263 {
18264         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);
18265 };
18266
18267 // Convert a date to UTC YYYYMMDDHHMM string format
18268 Date.prototype.convertToYYYYMMDDHHMM = function()
18269 {
18270         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);
18271 };
18272
18273 // Convert a date to UTC YYYYMMDD.HHMMSSMMM string format
18274 Date.prototype.convertToYYYYMMDDHHMMSSMMM = function()
18275 {
18276         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);
18277 };
18278
18279 // Static method to create a date from a UTC YYYYMMDDHHMM format string
18280 Date.convertFromYYYYMMDDHHMM = function(d)
18281 {
18282         return new Date(Date.UTC(parseInt(d.substr(0,4),10),
18283                         parseInt(d.substr(4,2),10)-1,
18284                         parseInt(d.substr(6,2),10),
18285                         parseInt(d.substr(8,2),10),
18286                         parseInt(d.substr(10,2),10),0,0));
18287 };
18288
18289 //--
18290 //-- Crypto functions and associated conversion routines
18291 //--
18292
18293 // Crypto "namespace"
18294 function Crypto() {}
18295
18296 // Convert a string to an array of big-endian 32-bit words
18297 Crypto.strToBe32s = function(str)
18298 {
18299         var be = Array();
18300         var len = Math.floor(str.length/4);
18301         var i, j;
18302         for(i=0, j=0; i<len; i++, j+=4) {
18303                 be[i] = ((str.charCodeAt(j)&0xff) << 24)|((str.charCodeAt(j+1)&0xff) << 16)|((str.charCodeAt(j+2)&0xff) << 8)|(str.charCodeAt(j+3)&0xff);
18304         }
18305         while (j<str.length) {
18306                 be[j>>2] |= (str.charCodeAt(j)&0xff)<<(24-(j*8)%32);
18307                 j++;
18308         }
18309         return be;
18310 };
18311
18312 // Convert an array of big-endian 32-bit words to a string
18313 Crypto.be32sToStr = function(be)
18314 {
18315         var str = "";
18316         for(var i=0;i<be.length*32;i+=8)
18317                 str += String.fromCharCode((be[i>>5]>>>(24-i%32)) & 0xff);
18318         return str;
18319 };
18320
18321 // Convert an array of big-endian 32-bit words to a hex string
18322 Crypto.be32sToHex = function(be)
18323 {
18324         var hex = "0123456789ABCDEF";
18325         var str = "";
18326         for(var i=0;i<be.length*4;i++)
18327                 str += hex.charAt((be[i>>2]>>((3-i%4)*8+4))&0xF) + hex.charAt((be[i>>2]>>((3-i%4)*8))&0xF);
18328         return str;
18329 };
18330
18331 // Return, in hex, the SHA-1 hash of a string
18332 Crypto.hexSha1Str = function(str)
18333 {
18334         return Crypto.be32sToHex(Crypto.sha1Str(str));
18335 };
18336
18337 // Return the SHA-1 hash of a string
18338 Crypto.sha1Str = function(str)
18339 {
18340         return Crypto.sha1(Crypto.strToBe32s(str),str.length);
18341 };
18342
18343 // Calculate the SHA-1 hash of an array of blen bytes of big-endian 32-bit words
18344 Crypto.sha1 = function(x,blen)
18345 {
18346         // Add 32-bit integers, wrapping at 32 bits
18347         add32 = function(a,b)
18348         {
18349                 var lsw = (a&0xFFFF)+(b&0xFFFF);
18350                 var msw = (a>>16)+(b>>16)+(lsw>>16);
18351                 return (msw<<16)|(lsw&0xFFFF);
18352         };
18353         // Add five 32-bit integers, wrapping at 32 bits
18354         add32x5 = function(a,b,c,d,e)
18355         {
18356                 var lsw = (a&0xFFFF)+(b&0xFFFF)+(c&0xFFFF)+(d&0xFFFF)+(e&0xFFFF);
18357                 var msw = (a>>16)+(b>>16)+(c>>16)+(d>>16)+(e>>16)+(lsw>>16);
18358                 return (msw<<16)|(lsw&0xFFFF);
18359         };
18360         // Bitwise rotate left a 32-bit integer by 1 bit
18361         rol32 = function(n)
18362         {
18363                 return (n>>>31)|(n<<1);
18364         };
18365
18366         var len = blen*8;
18367         // Append padding so length in bits is 448 mod 512
18368         x[len>>5] |= 0x80 << (24-len%32);
18369         // Append length
18370         x[((len+64>>9)<<4)+15] = len;
18371         var w = Array(80);
18372
18373         var k1 = 0x5A827999;
18374         var k2 = 0x6ED9EBA1;
18375         var k3 = 0x8F1BBCDC;
18376         var k4 = 0xCA62C1D6;
18377
18378         var h0 = 0x67452301;
18379         var h1 = 0xEFCDAB89;
18380         var h2 = 0x98BADCFE;
18381         var h3 = 0x10325476;
18382         var h4 = 0xC3D2E1F0;
18383
18384         for(var i=0;i<x.length;i+=16) {
18385                 var j,t;
18386                 var a = h0;
18387                 var b = h1;
18388                 var c = h2;
18389                 var d = h3;
18390                 var e = h4;
18391                 for(j = 0;j<16;j++) {
18392                         w[j] = x[i+j];
18393                         t = add32x5(e,(a>>>27)|(a<<5),d^(b&(c^d)),w[j],k1);
18394                         e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
18395                 }
18396                 for(j=16;j<20;j++) {
18397                         w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
18398                         t = add32x5(e,(a>>>27)|(a<<5),d^(b&(c^d)),w[j],k1);
18399                         e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
18400                 }
18401                 for(j=20;j<40;j++) {
18402                         w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
18403                         t = add32x5(e,(a>>>27)|(a<<5),b^c^d,w[j],k2);
18404                         e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
18405                 }
18406                 for(j=40;j<60;j++) {
18407                         w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
18408                         t = add32x5(e,(a>>>27)|(a<<5),(b&c)|(d&(b|c)),w[j],k3);
18409                         e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
18410                 }
18411                 for(j=60;j<80;j++) {
18412                         w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
18413                         t = add32x5(e,(a>>>27)|(a<<5),b^c^d,w[j],k4);
18414                         e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
18415                 }
18416
18417                 h0 = add32(h0,a);
18418                 h1 = add32(h1,b);
18419                 h2 = add32(h2,c);
18420                 h3 = add32(h3,d);
18421                 h4 = add32(h4,e);
18422         }
18423         return Array(h0,h1,h2,h3,h4);
18424 };
18425
18426 //--
18427 //-- RGB colour object
18428 //--
18429
18430 // Construct an RGB colour object from a '#rrggbb', '#rgb' or 'rgb(n,n,n)' string or from separate r,g,b values
18431 function RGB(r,g,b)
18432 {
18433         this.r = 0;
18434         this.g = 0;
18435         this.b = 0;
18436         if(typeof r == "string") {
18437                 if(r.substr(0,1) == "#") {
18438                         if(r.length == 7) {
18439                                 this.r = parseInt(r.substr(1,2),16)/255;
18440                                 this.g = parseInt(r.substr(3,2),16)/255;
18441                                 this.b = parseInt(r.substr(5,2),16)/255;
18442                         } else {
18443                                 this.r = parseInt(r.substr(1,1),16)/15;
18444                                 this.g = parseInt(r.substr(2,1),16)/15;
18445                                 this.b = parseInt(r.substr(3,1),16)/15;
18446                         }
18447                 } else {
18448                         var rgbPattern = /rgb\s*\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)/;
18449                         var c = r.match(rgbPattern);
18450                         if(c) {
18451                                 this.r = parseInt(c[1],10)/255;
18452                                 this.g = parseInt(c[2],10)/255;
18453                                 this.b = parseInt(c[3],10)/255;
18454                         }
18455                 }
18456         } else {
18457                 this.r = r;
18458                 this.g = g;
18459                 this.b = b;
18460         }
18461         return this;
18462 }
18463
18464 // Mixes this colour with another in a specified proportion
18465 // c = other colour to mix
18466 // f = 0..1 where 0 is this colour and 1 is the new colour
18467 // Returns an RGB object
18468 RGB.prototype.mix = function(c,f)
18469 {
18470         return new RGB(this.r + (c.r-this.r) * f,this.g + (c.g-this.g) * f,this.b + (c.b-this.b) * f);
18471 };
18472
18473 // Return an rgb colour as a #rrggbb format hex string
18474 RGB.prototype.toString = function()
18475 {
18476         return "#" + ("0" + Math.floor(this.r.clamp(0,1) * 255).toString(16)).right(2) +
18477                                  ("0" + Math.floor(this.g.clamp(0,1) * 255).toString(16)).right(2) +
18478                                  ("0" + Math.floor(this.b.clamp(0,1) * 255).toString(16)).right(2);
18479 };
18480
18481 //--
18482 //-- DOM utilities - many derived from www.quirksmode.org
18483 //--
18484
18485 function drawGradient(place,horiz,colours)
18486 {
18487         for(var t=0; t<= 100; t+=2) {
18488                 var bar = document.createElement("div");
18489                 place.appendChild(bar);
18490                 bar.style.position = "absolute";
18491                 bar.style.left = horiz ? t + "%" : 0;
18492                 bar.style.top = horiz ? 0 : t + "%";
18493                 bar.style.width = horiz ? (101-t) + "%" : "100%";
18494                 bar.style.height = horiz ? "100%" : (101-t) + "%";
18495                 bar.style.zIndex = -1;
18496                 var f = t/100;
18497                 var p = f*(colours.length-1);
18498                 bar.style.backgroundColor = colours[Math.floor(p)].mix(colours[Math.ceil(p)],p-Math.floor(p)).toString();
18499         }
18500 }
18501
18502 function createTiddlyText(theParent,theText)
18503 {
18504         return theParent.appendChild(document.createTextNode(theText));
18505 }
18506
18507 function createTiddlyCheckbox(theParent,caption,checked,onChange)
18508 {
18509         var cb = document.createElement("input");
18510         cb.setAttribute("type","checkbox");
18511         cb.onclick = onChange;
18512         theParent.appendChild(cb);
18513         cb.checked = checked;
18514         cb.className = "chkOptionInput";
18515         if(caption)
18516                 wikify(caption,theParent);
18517         return cb;
18518 }
18519
18520 function createTiddlyElement(theParent,theElement,theID,theClass,theText)
18521 {
18522         var e = document.createElement(theElement);
18523         if(theClass != null)
18524                 e.className = theClass;
18525         if(theID != null)
18526                 e.setAttribute("id",theID);
18527         if(theText != null)
18528                 e.appendChild(document.createTextNode(theText));
18529         if(theParent != null)
18530                 theParent.appendChild(e);
18531         return e;
18532 }
18533
18534 function addEvent(obj,type,fn)
18535 {
18536         if(obj.attachEvent) {
18537                 obj['e'+type+fn] = fn;
18538                 obj[type+fn] = function(){obj['e'+type+fn](window.event);};
18539                 obj.attachEvent('on'+type,obj[type+fn]);
18540         } else {
18541                 obj.addEventListener(type,fn,false);
18542         }
18543 }
18544
18545 function removeEvent(obj,type,fn)
18546 {
18547         if(obj.detachEvent) {
18548                 obj.detachEvent('on'+type,obj[type+fn]);
18549                 obj[type+fn] = null;
18550         } else {
18551                 obj.removeEventListener(type,fn,false);
18552         }
18553 }
18554
18555 function addClass(e,theClass)
18556 {
18557         var currClass = e.className.split(" ");
18558         if(currClass.indexOf(theClass) == -1)
18559                 e.className += " " + theClass;
18560 }
18561
18562 function removeClass(e,theClass)
18563 {
18564         var currClass = e.className.split(" ");
18565         var i = currClass.indexOf(theClass);
18566         while(i != -1) {
18567                 currClass.splice(i,1);
18568                 i = currClass.indexOf(theClass);
18569         }
18570         e.className = currClass.join(" ");
18571 }
18572
18573 function hasClass(e,theClass)
18574 {
18575         if(e.className) {
18576                 if(e.className.split(" ").indexOf(theClass) != -1)
18577                         return true;
18578         }
18579         return false;
18580 }
18581
18582 // Find the closest relative with a given property value (property defaults to tagName, relative defaults to parentNode)
18583 function findRelated(e,value,name,relative)
18584 {
18585         name = name ? name : "tagName";
18586         relative = relative ? relative : "parentNode";
18587         if(name == "className") {
18588                 while(e && !hasClass(e,value)) {
18589                         e = e[relative];
18590                 }
18591         } else {
18592                 while(e && e[name] != value) {
18593                         e = e[relative];
18594                 }
18595         }
18596         return e;
18597 }
18598
18599 // Resolve the target object of an event
18600 function resolveTarget(e)
18601 {
18602         var obj;
18603         if(e.target)
18604                 obj = e.target;
18605         else if(e.srcElement)
18606                 obj = e.srcElement;
18607         if(obj.nodeType == 3) // defeat Safari bug
18608                 obj = obj.parentNode;
18609         return obj;
18610 }
18611
18612 // Return the content of an element as plain text with no formatting
18613 function getPlainText(e)
18614 {
18615         var text = "";
18616         if(e.innerText)
18617                 text = e.innerText;
18618         else if(e.textContent)
18619                 text = e.textContent;
18620         return text;
18621 }
18622
18623 // Get the scroll position for window.scrollTo necessary to scroll a given element into view
18624 function ensureVisible(e)
18625 {
18626         var posTop = findPosY(e);
18627         var posBot = posTop + e.offsetHeight;
18628         var winTop = findScrollY();
18629         var winHeight = findWindowHeight();
18630         var winBot = winTop + winHeight;
18631         if(posTop < winTop) {
18632                 return posTop;
18633         } else if(posBot > winBot) {
18634                 if(e.offsetHeight < winHeight)
18635                         return posTop - (winHeight - e.offsetHeight);
18636                 else
18637                         return posTop;
18638         } else {
18639                 return winTop;
18640         }
18641 }
18642
18643 // Get the current width of the display window
18644 function findWindowWidth()
18645 {
18646         return window.innerWidth ? window.innerWidth : document.documentElement.clientWidth;
18647 }
18648
18649 // Get the current height of the display window
18650 function findWindowHeight()
18651 {
18652         return window.innerHeight ? window.innerHeight : document.documentElement.clientHeight;
18653 }
18654
18655 // Get the current horizontal page scroll position
18656 function findScrollX()
18657 {
18658         return window.scrollX ? window.scrollX : document.documentElement.scrollLeft;
18659 }
18660
18661 // Get the current vertical page scroll position
18662 function findScrollY()
18663 {
18664         return window.scrollY ? window.scrollY : document.documentElement.scrollTop;
18665 }
18666
18667 function findPosX(obj)
18668 {
18669         var curleft = 0;
18670         while(obj.offsetParent) {
18671                 curleft += obj.offsetLeft;
18672                 obj = obj.offsetParent;
18673         }
18674         return curleft;
18675 }
18676
18677 function findPosY(obj)
18678 {
18679         var curtop = 0;
18680         while(obj.offsetParent) {
18681                 curtop += obj.offsetTop;
18682                 obj = obj.offsetParent;
18683         }
18684         return curtop;
18685 }
18686
18687 // Blur a particular element
18688 function blurElement(e)
18689 {
18690         if(e != null && e.focus && e.blur) {
18691                 e.focus();
18692                 e.blur();
18693         }
18694 }
18695
18696 // Create a non-breaking space
18697 function insertSpacer(place)
18698 {
18699         var e = document.createTextNode(String.fromCharCode(160));
18700         if(place)
18701                 place.appendChild(e);
18702         return e;
18703 }
18704
18705 // Remove all children of a node
18706 function removeChildren(e)
18707 {
18708         while(e && e.hasChildNodes())
18709                 removeNode(e.firstChild);
18710 }
18711
18712 // Remove a node and all it's children
18713 function removeNode(e)
18714 {
18715         scrubNode(e);
18716         e.parentNode.removeChild(e);
18717 }
18718
18719 // Remove any event handlers or non-primitve custom attributes
18720 function scrubNode(e)
18721 {
18722         var att = e.attributes;
18723         if(att) {
18724                 for(var t=0; t<att.length; t++) {
18725                         var n = att[t].name;
18726                         if(n !== 'style' && (typeof e[n] === 'function' || (typeof e[n] === 'object' && e[n] != null))) {
18727                                 try {
18728                                         e[n] = null;
18729                                 } catch(ex) {
18730                                 }
18731                         }
18732                 }
18733         }
18734         var c = e.firstChild;
18735         while(c) {
18736                 scrubNode(c);
18737                 c = c.nextSibling;
18738         }
18739 }
18740
18741 // Add a stylesheet, replacing any previous custom stylesheet
18742 function setStylesheet(s,id,doc)
18743 {
18744         if(!id)
18745                 id = "customStyleSheet";
18746         if(!doc)
18747                 doc = document;
18748         var n = doc.getElementById(id);
18749         if(doc.createStyleSheet) {
18750                 // Test for IE's non-standard createStyleSheet method
18751                 if(n)
18752                         n.parentNode.removeChild(n);
18753                 // This failed without the &nbsp;
18754                 doc.getElementsByTagName("head")[0].insertAdjacentHTML("beforeEnd","&nbsp;<style id='" + id + "'>" + s + "</style>");
18755         } else {
18756                 if(n) {
18757                         n.replaceChild(doc.createTextNode(s),n.firstChild);
18758                 } else {
18759                         n = doc.createElement("style");
18760                         n.type = "text/css";
18761                         n.id = id;
18762                         n.appendChild(doc.createTextNode(s));
18763                         doc.getElementsByTagName("head")[0].appendChild(n);
18764                 }
18765         }
18766 }
18767
18768 // Force the browser to do a document reflow when needed to workaround browser bugs
18769 function forceReflow()
18770 {
18771         if(config.browser.isGecko) {
18772                 setStylesheet("body {top:-1em;margin-top:1em;}");
18773                 setStylesheet("");
18774         }
18775 }
18776
18777 // Replace the current selection of a textarea or text input and scroll it into view
18778 function replaceSelection(e,text)
18779 {
18780         if(e.setSelectionRange) {
18781                 var oldpos = e.selectionStart;
18782                 var isRange = e.selectionEnd > e.selectionStart;
18783                 e.value = e.value.substr(0,e.selectionStart) + text + e.value.substr(e.selectionEnd);
18784                 e.setSelectionRange(isRange ? oldpos : oldpos + text.length,oldpos + text.length);
18785                 var linecount = e.value.split('\n').length;
18786                 var thisline = e.value.substr(0,e.selectionStart).split('\n').length-1;
18787                 e.scrollTop = Math.floor((thisline - e.rows / 2) * e.scrollHeight / linecount);
18788         } else if(document.selection) {
18789                 var range = document.selection.createRange();
18790                 if(range.parentElement() == e) {
18791                         var isCollapsed = range.text == "";
18792                         range.text = text;
18793                         if(!isCollapsed) {
18794                                 range.moveStart('character', -text.length);
18795                                 range.select();
18796                         }
18797                 }
18798         }
18799 }
18800
18801 // Returns the text of the given (text) node, possibly merging subsequent text nodes
18802 function getNodeText(e)
18803 {
18804         var t = ""; 
18805         while(e && e.nodeName == "#text") {
18806                 t += e.nodeValue;
18807                 e = e.nextSibling;
18808         }
18809         return t;
18810 }
18811
18812 //--
18813 //-- LoaderBase and SaverBase
18814 //--
18815
18816 function LoaderBase() {}
18817
18818 LoaderBase.prototype.loadTiddler = function(store,node,tiddlers)
18819 {
18820         var title = this.getTitle(store,node);
18821         if(title) {
18822                 var tiddler = store.createTiddler(title);
18823                 this.internalizeTiddler(store,tiddler,title,node);
18824                 tiddlers.push(tiddler);
18825         }
18826 };
18827
18828 LoaderBase.prototype.loadTiddlers = function(store,nodes)
18829 {
18830         var tiddlers = [];
18831         for(var t = 0; t < nodes.length; t++) {
18832                 try {
18833                         this.loadTiddler(store,nodes[t],tiddlers);
18834                 } catch(ex) {
18835                         showException(ex,config.messages.tiddlerLoadError.format([this.getTitle(store,nodes[t])]));
18836                 }
18837         }
18838         return tiddlers;
18839 };
18840
18841 function SaverBase() {}
18842
18843 SaverBase.prototype.externalize = function(store)
18844 {
18845         var results = [];
18846         var tiddlers = store.getTiddlers("title");
18847         for(var t = 0; t < tiddlers.length; t++)
18848                 results.push(this.externalizeTiddler(store,tiddlers[t]));
18849         return results.join("\n");
18850 };
18851
18852 //--
18853 //-- TW21Loader (inherits from LoaderBase)
18854 //--
18855
18856 function TW21Loader() {}
18857
18858 TW21Loader.prototype = new LoaderBase();
18859
18860 TW21Loader.prototype.getTitle = function(store,node)
18861 {
18862         var title = null;
18863         if(node.getAttribute) {
18864                 title = node.getAttribute("title");
18865                 if(!title)
18866                         title = node.getAttribute("tiddler");
18867         }
18868         if(!title && node.id) {
18869                 var lenPrefix = store.idPrefix.length;
18870                 if (node.id.substr(0,lenPrefix) == store.idPrefix)
18871                         title = node.id.substr(lenPrefix);
18872         }
18873         return title;
18874 };
18875
18876 TW21Loader.prototype.internalizeTiddler = function(store,tiddler,title,node)
18877 {
18878         var e = node.firstChild;
18879         var text = null;
18880         if(node.getAttribute("tiddler")) {
18881                 text = getNodeText(e).unescapeLineBreaks();
18882         } else {
18883                 while(e.nodeName!="PRE" && e.nodeName!="pre") {
18884                         e = e.nextSibling;
18885                 }
18886                 text = e.innerHTML.replace(/\r/mg,"").htmlDecode();
18887         }
18888         var modifier = node.getAttribute("modifier");
18889         var c = node.getAttribute("created");
18890         var m = node.getAttribute("modified");
18891         var created = c ? Date.convertFromYYYYMMDDHHMM(c) : version.date;
18892         var modified = m ? Date.convertFromYYYYMMDDHHMM(m) : created;
18893         var tags = node.getAttribute("tags");
18894         var fields = {};
18895         var attrs = node.attributes;
18896         for(var i = attrs.length-1; i >= 0; i--) {
18897                 var name = attrs[i].name;
18898                 if (attrs[i].specified && !TiddlyWiki.isStandardField(name)) {
18899                         fields[name] = attrs[i].value.unescapeLineBreaks();
18900                 }
18901         }
18902         tiddler.assign(title,text,modifier,modified,tags,created,fields);
18903         return tiddler;
18904 };
18905
18906 //--
18907 //-- TW21Saver (inherits from SaverBase)
18908 //--
18909
18910 function TW21Saver() {}
18911
18912 TW21Saver.prototype = new SaverBase();
18913
18914 TW21Saver.prototype.externalizeTiddler = function(store,tiddler)
18915 {
18916         try {
18917                 var extendedAttributes = "";
18918                 var usePre = config.options.chkUsePreForStorage;
18919                 store.forEachField(tiddler,
18920                         function(tiddler,fieldName,value) {
18921                                 // don't store stuff from the temp namespace
18922                                 if(typeof value != "string")
18923                                         value = "";
18924                                 if (!fieldName.match(/^temp\./))
18925                                         extendedAttributes += ' %0="%1"'.format([fieldName,value.escapeLineBreaks().htmlEncode()]);
18926                         },true);
18927                 var created = tiddler.created.convertToYYYYMMDDHHMM();
18928                 var modified = tiddler.modified.convertToYYYYMMDDHHMM();
18929                 var vdate = version.date.convertToYYYYMMDDHHMM();
18930                 var attributes = tiddler.modifier ? ' modifier="' + tiddler.modifier.htmlEncode() + '"' : "";
18931                 attributes += (usePre && modified == created) ? "" : ' modified="' + modified +'"';
18932                 attributes += (usePre && created == vdate) ? "" :' created="' + created + '"';
18933                 var tags = tiddler.getTags();
18934                 if(!usePre || tags)
18935                         attributes += ' tags="' + tags.htmlEncode() + '"';
18936                 return ('<div %0="%1"%2%3>%4</'+'div>').format([
18937                                 usePre ? "title" : "tiddler",
18938                                 tiddler.title.htmlEncode(),
18939                                 attributes,
18940                                 extendedAttributes,
18941                                 usePre ? "\n<pre>" + tiddler.text.htmlEncode() + "</pre>\n" : tiddler.text.escapeLineBreaks().htmlEncode()
18942                         ]);
18943         } catch (ex) {
18944                 throw exceptionText(ex,config.messages.tiddlerSaveError.format([tiddler.title]));
18945         }
18946 };
18947
18948 //--
18949 //-- Deprecated code
18950 //--
18951
18952 // @Deprecated: Use createElementAndWikify and this.termRegExp instead
18953 config.formatterHelpers.charFormatHelper = function(w)
18954 {
18955         w.subWikify(createTiddlyElement(w.output,this.element),this.terminator);
18956 };
18957
18958 // @Deprecated: Use enclosedTextHelper and this.lookaheadRegExp instead
18959 config.formatterHelpers.monospacedByLineHelper = function(w)
18960 {
18961         var lookaheadRegExp = new RegExp(this.lookahead,"mg");
18962         lookaheadRegExp.lastIndex = w.matchStart;
18963         var lookaheadMatch = lookaheadRegExp.exec(w.source);
18964         if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
18965                 var text = lookaheadMatch[1];
18966                 if(config.browser.isIE)
18967                         text = text.replace(/\n/g,"\r");
18968                 createTiddlyElement(w.output,"pre",null,null,text);
18969                 w.nextMatch = lookaheadRegExp.lastIndex;
18970         }
18971 };
18972
18973 // @Deprecated: Use <br> or <br /> instead of <<br>>
18974 config.macros.br.handler = function(place)
18975 {
18976         createTiddlyElement(place,"br");
18977 };
18978
18979 // Find an entry in an array. Returns the array index or null
18980 // @Deprecated: Use indexOf instead
18981 Array.prototype.find = function(item)
18982 {
18983         var i = this.indexOf(item);
18984         return i == -1 ? null : i;
18985 };
18986
18987 // Load a tiddler from an HTML DIV. The caller should make sure to later call Tiddler.changed()
18988 // @Deprecated: Use store.getLoader().internalizeTiddler instead
18989 Tiddler.prototype.loadFromDiv = function(divRef,title)
18990 {
18991         return store.getLoader().internalizeTiddler(store,this,title,divRef);
18992 };
18993
18994 // Format the text for storage in an HTML DIV
18995 // @Deprecated Use store.getSaver().externalizeTiddler instead.
18996 Tiddler.prototype.saveToDiv = function()
18997 {
18998         return store.getSaver().externalizeTiddler(store,this);
18999 };
19000
19001 // @Deprecated: Use store.allTiddlersAsHtml() instead
19002 function allTiddlersAsHtml()
19003 {
19004         return store.allTiddlersAsHtml();
19005 }
19006
19007 // @Deprecated: Use refreshPageTemplate instead
19008 function applyPageTemplate(title)
19009 {
19010         refreshPageTemplate(title);
19011 }
19012
19013 // @Deprecated: Use story.displayTiddlers instead
19014 function displayTiddlers(srcElement,titles,template,unused1,unused2,animate,unused3)
19015 {
19016         story.displayTiddlers(srcElement,titles,template,animate);
19017 }
19018
19019 // @Deprecated: Use story.displayTiddler instead
19020 function displayTiddler(srcElement,title,template,unused1,unused2,animate,unused3)
19021 {
19022         story.displayTiddler(srcElement,title,template,animate);
19023 }
19024
19025 // @Deprecated: Use functions on right hand side directly instead
19026 var createTiddlerPopup = Popup.create;
19027 var scrollToTiddlerPopup = Popup.show;
19028 var hideTiddlerPopup = Popup.remove;
19029
19030 // @Deprecated: Use right hand side directly instead
19031 var regexpBackSlashEn = new RegExp("\\\\n","mg");
19032 var regexpBackSlash = new RegExp("\\\\","mg");
19033 var regexpBackSlashEss = new RegExp("\\\\s","mg");
19034 var regexpNewLine = new RegExp("\n","mg");
19035 var regexpCarriageReturn = new RegExp("\r","mg");
19036 //--
19037 //-- End of scripts
19038 //--
19039 //]]>
19040 </script>
19041 <script type="text/javascript">
19042 //<![CDATA[
19043 if(useJavaSaver)
19044         document.write("<applet style='position:absolute;left:-1px' name='TiddlySaver' code='TiddlySaver.class' archive='TiddlySaver.jar' width='1' height='1'></applet>");
19045 //]]>
19046 </script>
19047 <!--POST-SCRIPT-START-->
19048
19049 <!--POST-SCRIPT-END-->
19050 </body>
19051 </html>