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">
4 <script type="text/javascript">
6 var version = {title: "TiddlyWiki", major: 2, minor: 2, revision: 4, date: new Date("Jun 19, 2007"), extensions: {}};
10 TiddlyWiki created by Jeremy Ruston, (jeremy [at] osmosoft [dot] com)
12 Copyright (c) UnaMesa Association 2004-2007
14 Redistribution and use in source and binary forms, with or without modification,
15 are permitted provided that the following conditions are met:
17 Redistributions of source code must retain the above copyright notice, this
18 list of conditions and the following disclaimer.
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.
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.
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
39 <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
42 <link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml'/>
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;}
55 <!--POST-HEAD-START-->
59 <body onload="main();" onunload="if(window.checkUnsavedChanges) checkUnsavedChanges(); if(window.scrubNodes) scrubNodes(document.body);">
64 Welcome to TiddlyWiki created by Jeremy Ruston, Copyright © 2007 UnaMesa Association
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>
69 <div id="saveTest"></div>
70 <div id="backstageCloak"></div>
71 <div id="backstageButton"></div>
72 <div id="backstageArea"><div id="backstageToolbar"></div></div>
74 <div id="backstagePanel"></div>
76 <div id="contentWrapper"></div>
77 <div id="contentStash"></div>
79 <div title="ColorPalette">
96 <div title="StyleSheetColors">
98 body {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
100 a {color:[[ColorPalette::PrimaryMid]];}
101 a:hover {background-color:[[ColorPalette::PrimaryMid]]; color:[[ColorPalette::Background]];}
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]];}
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]];}
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]];}
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]];
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;}
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]];}
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]];}
149 #messageArea {border:1px solid [[ColorPalette::SecondaryMid]]; background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]];}
150 #messageArea .button {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::SecondaryPale]]; border:none;}
152 .popupTiddler {background:[[ColorPalette::TertiaryPale]]; border:2px solid [[ColorPalette::TertiaryMid]];}
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]];}
163 .tiddler .defaultCommand {font-weight:bold;}
165 .shadow .title {color:[[ColorPalette::TertiaryDark]];}
167 .title {color:[[ColorPalette::SecondaryDark]];}
168 .subtitle {color:[[ColorPalette::TertiaryDark]];}
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]];}
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;}
180 .footer {color:[[ColorPalette::TertiaryLight]];}
181 .selected .footer {color:[[ColorPalette::TertiaryMid]];}
183 .sparkline {background:[[ColorPalette::PrimaryPale]]; border:0;}
184 .sparktick {background:[[ColorPalette::PrimaryDark]];}
186 .error, .errorButton {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Error]];}
187 .warning {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryPale]];}
188 .lowlight {background:[[ColorPalette::TertiaryLight]];}
190 .zoomer {background:none; color:[[ColorPalette::TertiaryMid]]; border:3px solid [[ColorPalette::TertiaryMid]];}
192 .imageLink, #displayArea .imageLink {background:transparent;}
194 .annotation {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border:2px solid [[ColorPalette::SecondaryMid]];}
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]];}
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]];}
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]];}
208 .highlight, .marked {background:[[ColorPalette::SecondaryLight]];}
210 .editor input {border:1px solid [[ColorPalette::PrimaryMid]];}
211 .editor textarea {border:1px solid [[ColorPalette::PrimaryMid]]; width:100%;}
212 .editorFooter {color:[[ColorPalette::TertiaryMid]];}
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)';}
226 <div title="StyleSheetLayout">
228 * html .tiddler {height:1%;}
230 body {font-size:.75em; font-family:arial,helvetica; margin:0; padding:0;}
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;}
243 a {text-decoration:none;}
245 dt {font-weight:bold;}
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;}
255 .txtOptionInput {width:11em;}
257 #contentWrapper .chkOptionInput {border:0;}
259 .externalLink {text-decoration:underline;}
261 .indent {margin-left:3em;}
262 .outdent {margin-left:3em; text-indent:-3em;}
263 code.escaped {white-space:nowrap;}
265 .tiddlyLinkExisting {font-weight:bold;}
266 .tiddlyLinkNonExisting {font-style:italic;}
268 /* the 'a' is required for IE, otherwise it renders the whole tiddler in bold */
269 a.tiddlyLinkNonExisting.shadow {font-weight:bold;}
271 #mainMenu .tiddlyLinkExisting,
272 #mainMenu .tiddlyLinkNonExisting,
273 #sidebarTabs .tiddlyLinkNonExisting {font-weight:normal; font-style:normal;}
274 #sidebarTabs .tiddlyLinkExisting {font-weight:bold; font-style:normal;}
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;}
281 .siteTitle {font-size:3em;}
282 .siteSubtitle {font-size:1.2em;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
326 #contentWrapper {display:block;}
327 #splashScreen {display:none;}
329 #displayArea {margin:1em 17em 0em 14em;}
331 .toolbar {text-align:right; font-size:.9em;}
333 .tiddler {padding:1em 1em 0em 1em;}
335 .missing .viewer,.missing .title {font-style:italic;}
337 .title {font-size:1.6em; font-weight:bold;}
339 .missing .subtitle {display:none;}
340 .subtitle {font-size:1.1em;}
342 .tiddler .button {padding:0.2em 0.4em;}
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;}
351 .footer {font-size:.9em;}
352 .footer li {display:inline;}
354 .annotation {padding:0.5em; margin:0.5em;}
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;}
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;}
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;}
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;}
375 .fieldsetFix {border:0; padding:0; margin:1px 0px 1px 0px;}
377 .sparkline {line-height:1em;}
378 .sparktick {outline:0;}
380 .zoomer {font-size:1.1em; position:absolute; overflow:hidden;}
381 .zoomer div {padding:1em;}
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;}
396 .whenBackstage {display:none;}
397 .backstageVisible .whenBackstage {display:block;}
400 <div title="StyleSheetLocale">
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.
407 body {font-size:0.8em;}
409 #sidebarOptions {font-size:1.05em;}
410 #sidebarOptions a {font-style:normal;}
411 #sidebarOptions .sliderPanel {font-size:0.95em;}
413 .subtitle {font-size:0.8em;}
415 .viewer table.listView {font-size:0.95em;}
417 .htmlarea .toolbarHA table {border:1px solid ButtonFace; margin:0em 0em;}
420 <div title="StyleSheetPrint">
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;}
430 <div title="PageTemplate">
431 <pre><!--{{{-->
432 <div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'>
433 <div class='headerShadow'>
434 <span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
435 <span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
437 <div class='headerForeground'>
438 <span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
439 <span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
442 <div id='mainMenu' refresh='content' tiddler='MainMenu'></div>
443 <div id='sidebar'>
444 <div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
445 <div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
447 <div id='displayArea'>
448 <div id='messageArea'></div>
449 <div id='tiddlerDisplay'></div>
451 <!--}}}--></pre>
453 <div title="ViewTemplate">
454 <pre><!--{{{-->
455 <div class='toolbar' macro='toolbar closeTiddler closeOthers +editTiddler > fields syncing permalink references jump'></div>
456 <div class='title' macro='view title'></div>
457 <div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date'></span> (<span macro='message views.wikified.createdPrompt'></span> <span macro='view created date'></span>)</div>
458 <div class='tagging' macro='tagging'></div>
459 <div class='tagged' macro='tags'></div>
460 <div class='viewer' macro='view text wikified'></div>
461 <div class='tagClear'></div>
462 <!--}}}--></pre>
464 <div title="EditTemplate">
465 <pre><!--{{{-->
466 <div class='toolbar' macro='toolbar +saveTiddler -cancelTiddler deleteTiddler'></div>
467 <div class='title' macro='view title'></div>
468 <div class='editor' macro='edit title'></div>
469 <div macro='annotations'></div>
470 <div class='editor' macro='edit text'></div>
471 <div class='editor' macro='edit tags'></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser'></span></div>
472 <!--}}}--></pre>
474 <div title="GettingStarted">
475 <pre>To get started with this blank TiddlyWiki, you'll need to modify the following tiddlers:
476 * SiteTitle & 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: <<option txtUserName>></pre>
481 <div title="OptionsPanel">
482 <pre>These InterfaceOptions for customising TiddlyWiki are saved in your browser
484 Your username for signing your edits. Write it as a WikiWord (eg JoeBloggs)
486 <<option txtUserName>>
487 <<option chkSaveBackups>> SaveBackups
488 <<option chkAutoSave>> AutoSave
489 <<option chkRegExpSearch>> RegExpSearch
490 <<option chkCaseSensitiveSearch>> CaseSensitiveSearch
491 <<option chkAnimate>> EnableAnimations
494 Also see AdvancedOptions</pre>
497 <!--POST-SHADOWAREA-->
499 <div title="0. Psyclopsとは" modifier="Kazushi Maruya" modified="200712061845" created="200712061745" changecount="4">
500 <pre>Psychlopsは、できるだけ簡単に正確でフレキシブルな画面描画をするためのツールです。
501 Psychlopsを使うと、Windows/MacのCGアプリケーションを作成することができます。
503 "C++言語ライブラリ"の形で提供されていますが、C++言語の複雑な構造の知識はほとんど必要ありません。
504 C++プログラミングの事前の知識がなくても、できる限り簡単に使用できるように設計されています。C++の文法については[[1.6節|1.6 Psychlopsを使ったプログラミング]]にまとめてある程度の知識があれば一通りの機能を利用することができるでしょう。
506 以下の項目ではPsychlopsの概要についてもう少し詳しく説明します。とにかく使ってみたい方は[[1節|1. インストールを行う]]に進んでください。
509 * 複雑な操作を正確に行えることをコンセプトとしています。
510 * 刺激提示に特化し、視覚実験での使いやすさを追求しています。
511 * Mac OS XでもWindowsでも使える汎用性があります。
513 * 他のC/C++コードと混在させることができます。
517 Psychlopsはプログラミング初心者でも取り扱いやすいライブラリをコンセプトとしています。習得難易度は各種ソフト付属のスクリプト程度を目標としています。
518 C/C++言語で刺激を記述しなければならない場合、どうしても初期化処理が煩雑になり、制御の正確さまで手が回りません。例えば、OpenGL標準の拡張ライブラリGLUTは、時間の制御精度は全く保証されていません。Psychlopsは初期化処理を全自動で行いますが、その制御処理は逐一検証して作成しています。
520 * PCの機能の最小単位で視覚刺激を操作できます
522 ** 垂直同期ひとつひとつにフレームを埋め込むことができます。
523 * プログラム言語を直接うつことで、パラメタをシステマティックに変えるなどの複雑な実験パラダイムを組むことも可能です。
527 刺激提示の準備処理を自動化し、刺激に必要な部分だけを書けば動かすことができます。以下の例は、画像ファイルを読み込んで提示する最短のプログラム例です。
530 #include <psychlops.h>
531 using namespace Psychlops; // Psychlopsの呼び出し
533 void psychlops_main() {
534 Canvas display( Canvas::fullscreen ); // フルスクリーン表示領域の確保
536 natural_iamge.load(“Natural Image.png”) // 自然画像の読み込み
538 while(Input::get(Keyboard::esc)) { // ESCキーが押されるまで刺激提示
539 natural_iamge.centering().draw(); // 自然画像の位置あわせと提示
540 display.flip(); // フレームの提示
546 Psychlopsの基本コンセプトは、OpenGL等のOSやドライバに直結した機能を用い、汎用ビデオカードを正確に制御することを目標としています。このため、特定のソフトや機械に依存せず、様々な環境で実行することが可能です。
548 * Mac OS XとWindowsで同一コードで動作可能です。
549 * 特定のソフトウェア・機器に依存しません。
550 ** 一定以上古い環境はサポートしておりません。[[1.1 必要な環境]]をご確認ください。
555 Psychloplsプログラムの作成実行は、OS以外はすべて無償のソフトを利用して行うことができます。
557 * Psychlops自体はオープンソースで公開されています。
558 * Mac OS X PPC mac, intel mac
559 ** Apple社より無償提供のXcode上でPsychlopsプログラムを作成できます
560 * Windows 2000, XP, Vista
561 ** 他ベンダ製無償の開発ツール上でPsychlopsプログラムを作成できます
565 <div title="1. インストールを行う" modifier="YourName" modified="201002040400" created="200708211944" changecount="12">
566 <pre>Psychlopsによるアプリケーション作成には以下の2つのステップがあります。
567 * プログラム本体(cppの拡張子がついたテキストファイル)の作成
568 * 作成されたプログラムの実行(アプリケーション)ファイルへの変換(コンパイル)
569 これを実現するには、Psychlops本体のインストールとは別に、変換を行うためのコンパイラと呼ばれるプログラムのインストールが必要です。さらに、この2つのステップをスムーズに行うための支援プログラム(開発環境)をインストールすることを強く推奨します。
571 以下では、Macintosh, Windowsの各環境でこれらのアプリケーションをインストールしてPsychlopsによるC++アプリケーションの作成が可能な環境の準備を行う方法を説明します。
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) ]]
581 [[1.5 Psychlopsの基本構造]] </pre>
583 <div title="1. 既成クラスを用いた簡単な実験プログラムを作成する" modifier="Psychlops_DevelopperG" modified="200908190217" created="200709252143" changecount="5">
584 <pre>この節では、ここまでのまとめとして、これまで説明してきた関数群を用いて簡単な恒常法の実験プログラム作成例について説明します。
585 一般的な恒常法を用いた心理学実験のプログラム上の大まかな流れは以下のようになります。
588 ## 実験計画のプログラミング:独立変数の範囲設定と、ラテン格子法に基づいた各試行への割り振り
589 ## 実験刺激の事前描画(ある場合のみ):リアルタイム描画が不可能な精密な刺激の描画とバッファリング
591 ## 各試行における描画(刺激の提示):各試行における独立変数の読み込みと、それらを用いた実際の描画
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セッションが終わりです。
602 この実験計画を上の一般的なプログラムの流れにあわせてみると以下のようになります。
605 ## 実験計画のプログラミング:Gabor方位の範囲設定と、ラテン格子法に基づいた各試行への割り振り
606 ## 実験刺激の事前描画:各方位のGaborパターンのオフスクリーン(Image)への描画
608 ## 各試行における描画(刺激の提示):該当するGaborが描画されたImageインスタンスを用いた描画
610 # 終了処理:反応が記録された配列の保存:各試行の独立変数条件と被験者の反応(正答: 1, 誤答: 0)をファイルに記録
612 このそれぞれのパーツをC++のプログラムとして実装していきます。
613 [[1.1 実験計画のプログラミング]] (1-a)
614 [[1.2 Gaborパッチの事前描画]] (1-b)
615 [[1.3 各試行部分のプログラミング]] (2)
616 [[1.4 終了処理とまとめ]](3)</pre>
618 <div title="1.1 実験計画のプログラミング" modifier="Psychlops_DevelopperG" modified="200908190212" created="200709252144" changecount="1">
621 ここはPsychlopsの使用とは直接関係ありませんが、初心者が実験プログラムを書くときに最も戸惑うところかもしれません。
622 このマニュアルでは、汎用性を意識したラテン格子法を実行する独立変数のクラスIndependentVariablesを追加して、使用してみます。
623 ここでは、このクラスの詳細な解説は行いません。
624 C++のプログラミングに慣れていない方はここで作成したクラスを単にプログラムに追加して使ってみることをお勧めします。
626 !!~IndependentVariablesクラスの概要
627 このクラスには、基本的な情報(メンバ)として
628 # 独立変数の数(int ~MaxVarNum)と、独立変数の水準数の最大値(int ~MaxStepNum)
629 #それぞれの水準の値(double * ~VariableStepNumber)
630 # 各水準ごとの繰り返しの数(int repeatNumber)
631 # ある特定の試行時の各独立変数の情報(Matrix ~CondMtx)
634 次に実際に恒常法の実験プログラムで使用する命令(メソッド)としては、
635 * これらの値を設定するためのset()命令
636 * 各試行において必要な独立変数を取り出すためのget()命令
637 * ラテン格子法を実行するrandomize()命令
640 [[IndependentVariablesクラスのソースコード|IndependentVariables]]
641 上のリンクのソースコードを実際の宣言が起こる前(普通は、グローバル変数の宣言直後)にコピーアンドペーストして、プログラムに追加してください。さらに、このクラスは標準の入出力ライブラリを使用しているので、プログラムの一番始めに
643 #include <stdio.h>
645 と書いて、標準の入出力ライブラリを使用できるようにしてください。
646 以上の処理を行うと、このクラスが使用できるようになります。
648 !!IndependentVariablesクラス変数の宣言
649 このクラスのコードをプログラムに追加したら、次はこのクラスのインスタンス(変数)を宣言します。
651 |~IndependentVariablesの宣言|~IndependentVariables(int varnumber, int maxstep, int iteration)|
652 |~|~|int ~varnumber:独立変数の数を指定|
653 |~|~|int maxstep:独立変数の水準の最大値を指定|
654 |~|~|int iteration:各水準の繰り返し回数|
657 # 独立変数は2個だったので、第1引数は2
658 # 水準数の最大値はGaborの方位の5水準なので、第2引数は5
659 # 繰り返しは各10回なので、第3引数は10
661 インスタンス名をinvarにして宣言すると、以下のようになります。
663 IndependentVariables invar(2,5,10) ;
667 次に宣言したインスタンスinvarに各変数の水準を設定していきます。
669 はじめに各水準の値をあらかじめ1次元のdouble型配列に格納しておきます。配列名は適当でかまいません。
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
676 Gaborの方位については実際の値を入れても良いのですが、後々のために各方位に番号を振ってその番号を格納する形になっています。
678 次に、これらの配列名を使ってinvarに変数の水準数と各水準の値を登録します。
680 |int ~IndependentVariables::set()|set(int vnum, int arraynum, double *array)|
681 |~|~|int vnum:独立変数の識別番号|
682 |~|~|int arraynum:独立変数の水準数|
683 |~|~|double *array:登録する値が格納された配列の名前(ポインタ)|
685 独立変数の識別番号は適当でかまいませんが、各独立変数に異なる値を割り振る必要があります。さらに、この識別番号の値の範囲は0~varnumber(インスタンスの宣言時に設定した独立変数の数)でなくてはいけません。この関数の返り値は登録が正常に行われた時は設定された独立変数の水準数、失敗したときは-1です。
687 Gaborの方位に0,提示時間に1を割り振ると、以下のようになります。
689 invar.set(0, 5, gaborOrientation);
690 invar.set(1, 3, duration);
694 最後に登録した値を使って、ラテン格子を組みます。これにはrandomize()命令を使用します。
695 |int ~IndependentVariables::randomize()|randomize([char *dataname])|char *dataname:組み上がったラテン格子の出力名 <br> (省略可能。省略時は出力されません)|
696 ここでは、"~ConditionMatrix.dat"というデータファイル(タブ区切りテキスト形式)を出力させることにしましょう。
698 invar.randomize("ConditionMatrix.dat");
701 !!計画部のプログラムのまとめと組み上がったラテン格子の利用
702 これで、実験計画のプログラミングは完了です。ここまでの所をまとめてみましょう。
704 IndependentVaribales invar(2,5,10) ;
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
710 invar.set(0, 5, gaborOrientation);
711 invar.set(1, 3, duration);
712 invar.randomize("ConditionMatrix.dat");
715 この手続きによって計算されたラテン格子から各試行における独立変数の値を取得するにはget()命令を使用します。
716 |int ~IndependentVariables::get()|set(int vnum, int trial_now)|
717 |~|~|int vnum: 独立変数の識別番号|
718 |~|~|int trial_now: 試行番号|
720 例えば第5試行における刺激の提示時間を取得してdouble型の変数dura_nowに格納するには以下の様に書きます。
723 dura_now=invar.get(1,5);
727 <div title="1.1 必要な環境" modifier="Psychlops_Admin" created="200709131944" changecount="1">
728 <pre>Psychlopsの実行には、Mac OS X 10.4またはWindows 2000以降がインストールされているコンピュータが必要です。なるべく最近のコンピュータの使用をお勧めしますが、2003年以降に製造されたものであれば概ね動作します。
731 [[1.1.2 ソフトウェア環境]]</pre>
733 <div title="1.1.1 ハードウェア環境" modifier="Psychlops_Admin" modified="200709131949" created="200709131947" changecount="3">
738 * 1GHz以上の~PowerPC G4を搭載した以降の世代のMacintosh
739 * (Power Macの場合)~OpenGL 1.4以降に対応したビデオカード
740 ** Mac OS X10.4の動作環境は[[アップル社のサイト|<a class="linkification-ext" href="<a class="linkification-ext" href="<a class="linkification-ext" href="<a class="linkification-ext" href="<a class="linkification-ext" href="<a class="linkification-ext" href="<a class="linkification-ext" href="<a class="linkification-ext" href="<a class="linkification-ext" href="<a class="linkification-ext" href="<a class="linkification-ext" href="<a class="linkification-ext" href="http://www.apple.com/jp/macosx/upgrade/requirements.html" title="Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html">http://www.apple.com/jp/macosx/upgrade/requirements.html</a>" title="Linkification: <a class="linkification-ext" href="http://www.apple.com/jp/macosx/upgrade/requirements.html" title="Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html">http://www.apple.com/jp/macosx/upgrade/requirements.html</a>"><a class="linkification-ext" href="http://www.apple.com/jp/macosx/upgrade/requirements.html" title="Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html">http://www.apple.com/jp/macosx/upgrade/requirements.html</a></a>" title="Linkification: <a class="linkification-ext" href="http://www.apple.com/jp/macosx/upgrade/requirements.html" title="Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html">http://www.apple.com/jp/macosx/upgrade/requirements.html</a>"><a class="linkification-ext" href="http://www.apple.com/jp/macosx/upgrade/requirements.html" title="Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html">http://www.apple.com/jp/macosx/upgrade/requirements.html</a></a>" title="Linkification: <a class="linkification-ext" href="http://www.apple.com/jp/macosx/upgrade/requirements.html" title="Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html">http://www.apple.com/jp/macosx/upgrade/requirements.html</a>"><a class="linkification-ext" href="http://www.apple.com/jp/macosx/upgrade/requirements.html" title="Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html">http://www.apple.com/jp/macosx/upgrade/requirements.html</a></a>" title="Linkification: <a class="linkification-ext" href="http://www.apple.com/jp/macosx/upgrade/requirements.html" title="Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html">http://www.apple.com/jp/macosx/upgrade/requirements.html</a>"><a class="linkification-ext" href="http://www.apple.com/jp/macosx/upgrade/requirements.html" title="Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html">http://www.apple.com/jp/macosx/upgrade/requirements.html</a></a>" title="Linkification: <a class="linkification-ext" href="http://www.apple.com/jp/macosx/upgrade/requirements.html" title="Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html">http://www.apple.com/jp/macosx/upgrade/requirements.html</a>"><a class="linkification-ext" href="http://www.apple.com/jp/macosx/upgrade/requirements.html" title="Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html">http://www.apple.com/jp/macosx/upgrade/requirements.html</a></a>" title="Linkification: <a class="linkification-ext" href="http://www.apple.com/jp/macosx/upgrade/requirements.html" title="Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html">http://www.apple.com/jp/macosx/upgrade/requirements.html</a>"><a class="linkification-ext" href="http://www.apple.com/jp/macosx/upgrade/requirements.html" title="Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html">http://www.apple.com/jp/macosx/upgrade/requirements.html</a></a>" title="Linkification: <a class="linkification-ext" href="http://www.apple.com/jp/macosx/upgrade/requirements.html" title="Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html">http://www.apple.com/jp/macosx/upgrade/requirements.html</a>"><a class="linkification-ext" href="http://www.apple.com/jp/macosx/upgrade/requirements.html" title="Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html">http://www.apple.com/jp/macosx/upgrade/requirements.html</a></a>" title="Linkification: <a class="linkification-ext" href="http://www.apple.com/jp/macosx/upgrade/requirements.html" title="Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html">http://www.apple.com/jp/macosx/upgrade/requirements.html</a>"><a class="linkification-ext" href="http://www.apple.com/jp/macosx/upgrade/requirements.html" title="Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html">http://www.apple.com/jp/macosx/upgrade/requirements.html</a></a>" title="Linkification: <a class="linkification-ext" href="http://www.apple.com/jp/macosx/upgrade/requirements.html" title="Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html">http://www.apple.com/jp/macosx/upgrade/requirements.html</a>"><a class="linkification-ext" href="http://www.apple.com/jp/macosx/upgrade/requirements.html" title="Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html">http://www.apple.com/jp/macosx/upgrade/requirements.html</a></a>" title="Linkification: <a class="linkification-ext" href="http://www.apple.com/jp/macosx/upgrade/requirements.html" title="Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html">http://www.apple.com/jp/macosx/upgrade/requirements.html</a>"><a class="linkification-ext" href="http://www.apple.com/jp/macosx/upgrade/requirements.html" title="Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html">http://www.apple.com/jp/macosx/upgrade/requirements.html</a></a>" title="Linkification: <a class="linkification-ext" href="http://www.apple.com/jp/macosx/upgrade/requirements.html" title="Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html">http://www.apple.com/jp/macosx/upgrade/requirements.html</a>"><a class="linkification-ext" href="http://www.apple.com/jp/macosx/upgrade/requirements.html" title="Linkification: http://www.apple.com/jp/macosx/upgrade/requirements.html">http://www.apple.com/jp/macosx/upgrade/requirements.html</a></a>]]よりご確認ください
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社の製品は対応していることを確認しています。
754 * なるべく最新のビデオカード、なるべく多めのVRAM</pre>
756 <div title="1.1.2 ソフトウェア環境" modifier="YourName" modified="201003111850" created="200709131950" changecount="8">
757 <pre>PsychlopsはC++のライブラリとして提供されます。つまり、Psychlopsを使ったプログラミングには、C++の開発環境が必要です。これには様々なものがありますが、以下のものの使用を強くおすすめします。入手・インストール方法の詳細は1.2以降で説明します。
761 ** [[Apple社が提供している純正開発環境|<a class="linkification-ext" href="<a class="linkification-ext" href="<a class="linkification-ext" href="<a class="linkification-ext" href="http://www.apple.com/jp/macosx/developertools/" title="Linkification: http://www.apple.com/jp/macosx/developertools/">http://www.apple.com/jp/macosx/developertools/</a>" title="Linkification: <a class="linkification-ext" href="http://www.apple.com/jp/macosx/developertools/" title="Linkification: http://www.apple.com/jp/macosx/developertools/">http://www.apple.com/jp/macosx/developertools/</a>"><a class="linkification-ext" href="http://www.apple.com/jp/macosx/developertools/" title="Linkification: http://www.apple.com/jp/macosx/developertools/">http://www.apple.com/jp/macosx/developertools/</a></a>" title="Linkification: <a class="linkification-ext" href="http://www.apple.com/jp/macosx/developertools/" title="Linkification: http://www.apple.com/jp/macosx/developertools/">http://www.apple.com/jp/macosx/developertools/</a>"><a class="linkification-ext" href="http://www.apple.com/jp/macosx/developertools/" title="Linkification: http://www.apple.com/jp/macosx/developertools/">http://www.apple.com/jp/macosx/developertools/</a></a>" title="Linkification: <a class="linkification-ext" href="http://www.apple.com/jp/macosx/developertools/" title="Linkification: http://www.apple.com/jp/macosx/developertools/">http://www.apple.com/jp/macosx/developertools/</a>"><a class="linkification-ext" href="http://www.apple.com/jp/macosx/developertools/" title="Linkification: http://www.apple.com/jp/macosx/developertools/">http://www.apple.com/jp/macosx/developertools/</a></a>]]を参考にApple社のサイトからダウンロードしてください。
762 ** ファイルサイズがとても大きいので、注意してください。
765 * コンパイラ本体:~MinGW 3.4 or later, VC Toolkit 2003 with Platform SDK, ~BCC5.5など
766 * フリーの開発環境である[[Code::blocks|<a class="linkification-ext" href="http://www.codeblocks.org/" title="Linkification: http://www.codeblocks.org/">http://www.codeblocks.org/</a>]], [[Relo|<a class="linkification-ext" href="http://www.fifsoft.com/relo/" title="Linkification: http://www.fifsoft.com/relo/">http://www.fifsoft.com/relo/</a>]] を使用することをおすすめします。これら2つのためのプロジェクトテンプレート(psychlopsを利用した実行ファイルを作成するためのテンプレート)がPsychlopsのインストールキットに含まれています。
767 ** Code::blocksを使用する場合は、~MinGWが同時にインストールされます。</pre>
769 <div title="1.2 Gaborパッチの事前描画" modifier="Psychlops_DevelopperG" modified="200908190214" created="200709252145" changecount="2">
770 <pre>この節では実験刺激のGaborパッチを事前描画するプログラムを作ります。
771 Gaborパッチとは、以下の図のような正弦波にGauss関数によるエンベロープをかけて、刺激中心から周辺に離れるに従って正弦波のコントラストが低くなっていく様な刺激です。
772 |[Img[image/gabor.png]]| [Img[image/gabor3d.png]]|
773 この刺激は複雑な2次元パターンなので、Canvas::pix()を使ってリアルタイム描画をするよりは、Imageにあらかじめ描画を行った方がいいでしょう。
775 Imageを使った描画については[[3章|3. 複雑な描画を行う1(オフスクリーンへの描画)]]で既に触れましたが、この節では[[1.1節|1.1 実験計画のプログラミング]]と同様に汎用性を意識してGaborパッチを描画するためのクラス~GaborImageを作成して、これを利用した刺激描画を行います。
778 [[GaborImageクラスのソースコード|GaborImage]]
779 [[1.1節|1.1 実験計画のプログラミング]]のIndependentVariablesクラス同様上のリンクのソースコードを実際の宣言が起こる前にコピーアンドペーストして、プログラムに追加するとこのクラスを使用することができます。
781 このクラスはdraw()命令のみから構成されています。この命令は指定したImageにImageサイズの1/6をσとするエンベロープのGaborパッチを描画します。
782 |!~GaborImage::draw()|draw(Image &area, double ori, double freq, double phase, double Lmean, double *contrast)|カラーのGaborパッチをImageに描画します。|
783 |~|draw(Image &area, double ori, double freq, double phase, double Lmean, double contrast)|グレースケールのGaborパッチをImage上に描画します。|
785 |~|Image &area: Gaborパッチを描画するImageの名前|>|
786 |~|double freq: キャリアの空間周波数(pixel)|>|
787 |~|double phase: キャリアの位相(度)|>|
788 |~|double Lmean: パッチの平均輝度 |>|
789 |~|double *contrast: <br> キャリアのR,G,B各チャンネルのコントラストの配列名 (double[3]/範囲0.0-1.0)|double contrast: <br> キャリアのコントラスト <br> (範囲0.0-1.0)|
791 !!~GaborImageクラスを用いた刺激の事前描画
792 ここでは、上で説明した~GaborImageクラスを使って刺激の事前描画を行います。
793 まず、ここで追加したクラスのインスタンスを作成することにします。インスタンス名はgaborIMGにします。
797 次にGaborパッチを描画するImageを配列gIMGとして宣言します。
799 Psychlops::Image gIMG[10];
802 このImageの10枚分の配列に(傾き量5水準) x (傾きの方向:右/左の2水準)の10枚のGaborパッチを書けばよいのですが、どの配列にどの方位のGaborパッチを書けば良いかを考えなくてはいけません。6.1でGaborパッチの傾き量の水準は0~4の番号を振られていたことを思い出してください。各試行ではこの番号を元にで、各試行でランダムに与えられた傾きの方向を考慮して、呼び出すgIMGの番号を決める必要があります。
804 少し複雑になってきたので、与えられた傾き量の番号と傾きの方向に対して、どの方位のGaborパッチが呼び出されるべきかを表にしておきましょう。垂直方位は90度なのでこれに対して傾き量が加減されていることに気をつけてください。
806 |>|!傾き量の番号| 0 | 1 | 2 | 3 | 4 |
807 |!傾きの方向|右|90-2|90-4|90-8|90-16|90-32|
808 |~|左|90+2|90+4|90+8|90+16|90+32|
811 ここでは、以下の表の様にgIMGの番号とGaborパッチの方位を対応させることにします。ついでに、傾きの方向に対しても番号を振っておきます。右を0左を1としてみましょう。
813 |>|!傾き量の番号|>| 0 |>| 1 |>| 2 |>| 3 |>| 4 |>|
814 |!傾きの方向|右(0)|90-2|bgcolor(#a0ffa0):gIMG[0]|90-4|bgcolor(#a0ffa0):gIMG[1]|90-8|bgcolor(#a0ffa0):gIMG[2]|90-16|bgcolor(#a0ffa0):gIMG[3]|90-32|bgcolor(#a0ffa0):gIMG[4]|
815 |~|左(1)|90+2|bgcolor(#a0ffa0):gIMG[5]|90+4|bgcolor(#a0ffa0):gIMG[6]|90+8|bgcolor(#a0ffa0):gIMG[7]|90+16|bgcolor(#a0ffa0):gIMG[8]|90+32|bgcolor(#a0ffa0):gIMG[9]|
818 さらに、これをgIMGの番号順に並べ替えて、各番号に対する計算式を考えます。以下のようになるでしょう。
820 |!gIMGの配列番号|gIMG[0]|gIMG[1]|gIMG[2]|gIMG[3]|gIMG[4]|gIMG[5]|gIMG[6]|gIMG[7]|gIMG[8]|gIMG[9]|
821 |!(傾き量, 方向) 番号|(0,0)|(1,0)|(2,0)|(3,0)|(4,0)|(0,1)|(1,1)|(2,1)|(3,1)|(4,1)|
822 |!Gaborパッチの方位(度)|90-2|90-4|90-8|90-16|90-32|90+2|90+4|90+8|90+16|90+32|
823 |!配列番号iに対する方位の計算式|>|>|>|>| 90-pow(2, i) |>|>|>|>| 90+pow(2,i-4) |
824 | ^^Pow(a,b)はaのb乗を示す^^|c
826 これで、10枚のGaborパッチを各準備が整いました。~GaborImage::draw()命令を使って実際のプログラムを組んでみます。ここでは最終引数に単なる数値を入れて、グレースケールのGaborパッチを描画することにします。
828 最後にdraw()命令を使ってGaborパッチの描画を行います。刺激サイズは100 pixel四方、キャリアの波長は30 pix (空間周波数は1.0/30.0), キャリアのコントラストは0.2 (20%), 位相は0, パッチの平均輝度は0.5にします。
829 また、あらかじめ刺激提示位置(画面中央)にイメージをシフトしておきます。
831 double gIMGsize=100, ori;
832 for(int i=0; i<10; i++){
833 if(i<5){ ori=90.0-pow(2.0, i);}
834 else{ ori=90.0+pow(2.0,i-4);}
835 gIMG[i].set(gIMGsize, gIMGsize);
836 gaborIMG.draw(gIMG[i], ori, 1.0/30.0, 0.0, 0.5, 0.2);
841 この節のコードをまとめると以下のようになります。
844 Psychlops::Image gIMG[10];
845 double gIMGsize=100, ori;
846 for(int i=0; i<10; i++){
847 if(i<5){ ori=90.0-pow(2.0, i);}
848 else{ ori=90.0+pow(2.0,i-4);}
849 gIMG[i].set(gIMGsize, gIMGsize);
850 gaborIMG.draw(gIMG[i], ori, 1.0/30.0, 0.0, 0.5, 0.2);
855 <div title="1.2 インストール作業(Mac OS X 10.4) " modifier="YourName" modified="201002040347" created="200709131951" changecount="3">
857 PsychlopsのOSXでのインストールには、まずXcodeをインストールする必要があります。XcodeはApple社が提供する標準の開発環境で、無償で利用できます(ダウンロードにはApple Developers Connectionへの登録が必要です)。
859 * [[ダウンロード|http://developer.apple.com/tools/xcode/]]
860 * [[機能紹介|http://www.apple.com/jp/macosx/features/xcode/]]
862 ''Mac OS X 10.4向けのXcode 2.x系はApple社よりの配布がすでに終了しています。OSX 10.4のインストール/リカバリディスク中に開発用ツールキットがない場合、10.6へのアップグレードをご検討ください''
864 ''xcode.mpkg''をダブルクリックしてインストールを始めます。
865 [img[Xcodeインストール|image/OSX/Xcode2_10.4.png]]
867 インストーラの指示に従ってインストールを完了します。
868 [img[Xcodeインストール|image/OSX/Xcode3_10.4.png]]
870 !! Psychlopsの本体をインストール
871 * [[Psychlops Download|http://psychlops.l.u-tokyo.ac.jp/?Download]]
873 ダウンロードページへ行き、Xcode版の最新版をクリックします。
874 [img[Psychlopsダウンロード|image/OSX/Psychlops_OSX1png]]
876 zipファイルを解凍するとパッケージファイルが現れますので、これをダブルクリックしてインストールを開始します。
877 [img[Psychlopsインストール|image/OSX/Psychlops_OSX2.png]]
879 インストーラの指示に従ってインストールを完了します。
880 [img[Psychlopsインストール|image/OSX/Psychlops_OSX3.png]]
883 Xcodeを起動し、''メニューパー → ファイル → 新しいプロジェクト''を選択します。
884 [img[新しいプロジェクト|image/OSX/Psychlops_OSX_10.4_New1.png]]
886 新しいプロジェクトのウィザードが開いたら、Applicationカテゴリにある''Psychlops C++ Application''を選択します。
887 [img[新しいプロジェクト|image/OSX/Psychlops_OSX_10.4_New2.png]]
889 新しいプロジェクトを作成する場所と名前を選びます。作成後に場所は変更可能ですが、名前は作り直す以外に変更できませんのでご注意ください。また、パスに日本語を含んでいると実行に失敗することがありますので、ASCII文字のみのフォルダの中に作成してください。
890 [img[新しいプロジェクト|image/OSX/Psychlops_OSX_10.4_New3.png]]
892 プログラムはSourcesカテゴリの中にある(プロジェクト名).cppにあります。標準のサンプルプログラムが入っていますので、まずはダブルクリックしてエディタを開き、中身を見てみます(最初から開いていることもあります)。
893 [img[新しいプロジェクト|image/OSX/Psychlops_OSX_10.4_New5.png]]
896 [img[ウインドウ説明|image/OSX/Psychlops_OSX_10.4_New6.png]]
897 プログラム編集エディタでブログラムを編集することができます。実際にはこのサンプルを消して、新しく自分のプログラムを書いていくわけですが、まず最初は正しくインストールされているかどうかを確かめるために、標準のサンプルをそのまま実行してみましょう。ツールバーの実行ボタンをおすと、プログラムが機械語に翻訳されて実行ファイルの作成・実行が行われます。
899 出来上がった実行ファイルは、''プロジェクトのあるフォルダ > buildフォルダ > Releaseフォルダ '' の中にあります。この実行ファイルをコピーすることで頒布が可能ですが、メールなどに添付する際は必ずzipファイル等に圧縮してください。(ここで見えている実行プログラムは実際はフォルダです。Macのプログラムの仕様で、実際の実行プログラムは複数存在して、実行プログラムに見えるフォルダに格納されています。これを他のマシンに頒布するためにアップロードしたり、メールに添付したりするときには圧縮して実際に一つのファイルにまとめる必要があります。)
900 [img[ウインドウ説明|image/OSX/Psychlops_OSX_10.4_New7.png]]
903 <div title="1.2 インストール作業(Mac) " modifier="YourName" created="201002040339" changecount="1">
905 PsychlopsのOSXでのインストールには、まずXcodeをインストールする必要があります。XcodeはApple社が提供する標準の開発環境で、無償で利用できます(ダウンロードにはApple Developers Connectionへの登録が必要です)。対応バージョンについては、2010/01/31現在、Xcode2.5と3.x系に対応しておりますが、最新の対応状況につきましては[[http://psychlops.l.u-tokyo.ac.jp/?Environment]]からご確認ください。スクリーンショットはマニュアル作成時のものですので、バージョンが更新されていると異なる場合があります。ご了承ください。
907 * [[ダウンロード|http://developer.apple.com/technology/xcode.html]]
908 * [[機能紹介|http://www.apple.com/jp/macosx/features/xcode/]]
911 !!! OSXインストールディスクを利用する場合
913 OSXインストールディスクに開発環境のインストーラがあります。ディスクの内容は購入時期によって異なる可能性があります。ここでは10.5を例に説明いたします。
915 [img[Xcodeインストール|image/OSX/Xcode_DVD_Screen.png]]
917 OSXインストールディスク中をFinderで開くと、「オプションインストール」フォルダがあります。このなかの「Xcode Tools」フォルダを開き、「XcodeTools.mpkg」(拡張子は見えない場合があります)をダブルクリックすると、Xcodeインストーラが実行されます。
922 Xcodeをダウンロードするには、まず[[Appleのダウンロードサイト|http://developer.apple.com/technology/xcode.html]]へ行きます。その際、初めての方はまずApple Developers Connectionへの登録をする必要があります。
924 [img[Xcodeインストール|image/OSX/Xcode_01.png]]
926 「ADC membership」と書かれたリンクをクリックすると「Join Now」というボタンがありますので、それをクリックして
928 [img[Xcodeインストール|image/OSX/Xcode_02.png]]
930 もう一度[[ダウンロードページ|http://developer.apple.com/technology/xcode.html]]へ行き下側の「Xcode for Mac-only Development」の「Download now」をクリックしてください。その後のページでアカウントを入力すると、ダウンロードリンクが現れますので、本体のdmgファイルをダウンロードしてください。
932 [img[Xcodeインストール|image/OSX/Xcode_03.png]]
934 ダウンロードが終わると自動的にディスクイメージが展開されますので、''XcodeTools.mpkg''をダブルクリックしてインストールを始めます。
936 [img[Xcodeインストール|image/OSX/Xcode2.png]]
938 インストーラの指示に従ってインストールを完了します。
939 [img[Xcodeインストール|image/OSX/Xcode3.png]]
942 !! Psychlopsの本体をインストール
943 * [[Psychlops Download|http://psychlops.l.u-tokyo.ac.jp/?Download]]
945 ダウンロードページへ行き、Xcode版の最新版をクリックします。
946 [img[Psychlopsダウンロード|image/OSX/Psychlops_OSX1png]]
948 zipファイルを解凍するとパッケージファイルが現れますので、これをダブルクリックしてインストールを開始します。
949 [img[Psychlopsインストール|image/OSX/Psychlops_OSX2.png]]
951 インストーラの指示に従ってインストールを完了します。
952 [img[Psychlopsインストール|image/OSX/Psychlops_OSX3.png]]
955 Xcodeを起動し、''メニューパー → ファイル → 新しいプロジェクト''を選択します。
956 [img[新しいプロジェクト|image/OSX/Psychlops_OSX_New1.png]]
958 新しいプロジェクトのウィザードが開いたら、Applicationカテゴリにある''Psychlops C++ Application''を選択します。
959 [img[新しいプロジェクト|image/OSX/Psychlops_OSX_New2.png]]
961 新しいプロジェクトを作成する場所と名前を選びます。作成後に場所は変更可能ですが、名前は作り直す以外に変更できませんのでご注意ください。また、パスに日本語を含んでいると実行に失敗することがありますので、ASCII文字のみのフォルダの中に作成してください。
962 [img[新しいプロジェクト|image/OSX/Psychlops_OSX_New3.png]]
964 プログラムはSourcesカテゴリの中にある(プロジェクト名).cppにあります。標準のサンプルプログラムが入っていますので、まずはダブルクリックしてエディタを開き、中身を見てみます(最初から開いていることもあります)。
965 [img[新しいプロジェクト|image/OSX/Psychlops_OSX_New5.png]]
968 [img[ウインドウ説明|image/OSX/Psychlops_OSX_New6.png]]
969 プログラム編集エディタでブログラムを編集することができます。実際にはこのサンプルを消して、新しく自分のプログラムを書いていくわけですが、まず最初は正しくインストールされているかどうかを確かめるために、標準のサンプルをそのまま実行してみましょう。ツールバーの実行ボタンをおすと、プログラムが機械語に翻訳されて実行ファイルの作成・実行が行われます。
971 出来上がった実行ファイルは、''プロジェクトのあるフォルダ > buildフォルダ > Releaseフォルダ または Debugフォルダ'' の中にあります。この実行ファイルをコピーすることで頒布が可能ですが、メールなどに添付する際は必ずzipファイル等に圧縮してください。(ここで見えている実行プログラムは実際はフォルダです。Macのプログラムの仕様で、実際の実行プログラムは複数存在して、実行プログラムに見えるフォルダに格納されています。これを他のマシンに頒布するためにアップロードしたり、メールに添付したりするときには圧縮して実際に一つのファイルにまとめる必要があります。)
972 [img[ウインドウ説明|image/OSX/Psychlops_OSX_New7.png]]
975 <div title="1.3 インストール作業(Windows + BCC) " modifier="YourName" modified="201002040332" created="200709131955" changecount="2">
976 <pre>インストールには管理者権限のあるユーザである必要があります。
977 Windows Vistaに関しては、インストールが自動化されていませんがお使いいただくことは可能です。
978 [[Tips: Vistaにおけるインストール]]をご覧ください。
981 Psychlopsは画面の垂直同期信号に合わせて画面を更新しますが、この機能を正常に動作させるには、ディスプレイドライバのOpenGL描画設定で垂直同期にあわせるオプションを常にオンにする必要があります。
982 一般的には''コントロールパネル > 画面''の設定の''モニタ''タブを選び、''詳細設定''ボタンから設定します。詳しくはご使用のビデオカードのマニュアルをご覧ください。
985 PsychlopsのWindowsでのインストールには、まずコンパイラをインストールする必要があります。ここではBorland社が無償で提供するBorland C++ Compiler 5.5をインストールしてみます。Cマガジンから提供されている設定ツールも一緒にダウンロードしましょう。
987 * [[BCCダウンロード|http://www.codegear.com/jp/downloads/free/cppbuilder]]
988 * [[設定ツールダウンロード|http://www.cmagazine.jp/setbcc.html]]
990 まずダウンロードページへ行き、''Borland C++Compiler / Turbo Debugger''をクリックします。この後個人情報を入力してダウンロードを開始します。
991 [img[BCCダウンロード|image/Win/BCC0.png]]
993 ダウンロードが終わるとインストーラの実行ファイルがありますので、''freecommandlinetools2.exe''をダブルクリックしてインストールを始めます。インストーラの指示に従ってインストールを完了します。
994 [img[BCCインストール|image/Win/BCC1.png]]
996 次に設定ツールである''setbcc15b.exe''をダウンロードし、実行します。
997 [img[BCCインストール|image/Win/BCC2.png]]
999 特に細かい設定をする必要はありません。''進む>>''をクリックし続けて実行すれば実行可能になります。
1000 [img[BCCインストール|image/Win/BCC4.png]]
1003 次に開発環境をインストールします。ここではオープンソースのRelo2を利用します。
1005 * [[Reloダウンロード|http://www.fifsoft.com/relo/download.php]]
1007 まずダウンロードページへ行き、''RELO v2.0 INSTALLER''をクリックし、ダウンロードします。
1008 [img[Reloダウンロード|image/Win/Psychlops_Relo1.png]]
1010 ダウンロードが終わるとインストーラの実行ファイルがありますので、''relosetup20.exe''をダブルクリックしてインストールを始めます。インストーラの指示に従ってインストールを完了します。
1011 [img[Reloインストール|image/Win/Psychlops_Relo2.png]]
1013 インストール終了後、まず最初に環境設定を行うためにRelo2を起動します。起動したらメニューバーのTools から Compilers を選択します。
1014 [img[Reloインストール|image/Win/Psychlops_Relo_New5.png]]
1016 開いたウィンドウで、Newボタンを押し、そのまま進め、New CompilerをBCCにする設定をします。各フィールドを以下のように指定します。
1019 * Path : C:\borland\bcc55\Bin\
1021 [img[Reloインストール|image/Win/Psychlops_Relo4.png]]
1024 !! Psychlopsの本体をインストール
1025 * [[Psychlops Download|http://psychlops.l.u-tokyo.ac.jp/?Download]]
1027 ダウンロードページへ行き、BCC版の最新版をクリックします。
1028 [img[Psychlopsダウンロード|image/Win/Psychlops_WinGL1.png]]
1030 zipファイルを解凍するとインストーラフォルダが現れますので、この中の''PsychlopsLib''フォルダ内にある''PsychlopsLib_xxxx.exe''をダブルクリックしてインストールを開始します。
1031 [img[Psychlopsインストール|image/Win/Psychlops_WinGL3.png]]
1033 インストーラの指示に従ってインストールを完了します。
1034 [img[Psychlopsインストール|image/Win/Psychlops_WinGL3.png]]
1036 !! Relo用のテンプレートをインストール
1037 zipファイルを解凍してできたインストーラフォルダの中の''ReloTemplate''フォルダ内にある''Psychlops_Relo2_Template_xxxxxxxx.exe''をダブルクリックしてインストールを開始します。インストーラの指示に従ってインストールを完了します。
1038 [img[Psychlopsインストール|image/Win/Psychlops_Relo3.png]]
1042 Reloを起動し、''メニューパー → File → New''を選択します。
1043 [img[新しいプロジェクト|image/Win/Psychlops_Relo_New1.png]]
1045 新しいプロジェクトのウィザードが開いたら、''Psychlops Application''を選択します。
1046 [img[新しいプロジェクト|image/Win/Psychlops_Relo_New2.png]]
1048 新しいプロジェクトの名前を選びます。作成後に場所は変更可能ですが、名前は作り直す以外に変更できませんのでご注意ください。
1049 [img[新しいプロジェクト|image/Win/Psychlops_Relo_New3.png]]
1051 Reloはプロジェクトの作成直後は保存場所が決まっておりませんので、まずはプロジェクトを保存します。パスにスペースまたは日本語を含んでいると実行に失敗することがありますので、ASCII文字のみのフォルダの中に作成してください。
1052 [img[新しいプロジェクト|image/Win/Psychlops_Relo_New4.png]]
1054 プログラムはSourcesカテゴリの中にある(プロジェクト名).cppにあります。標準のサンプルプログラムが入っていますので、まずはダブルクリックしてエディタを開き、中身を見てみます(最初から開いていることもあります)。
1056 下のようなウィンドウが開いたでしょうか?
1057 [img[ウインドウ説明|image/Win/Psychlops_Relo_New6.png]]
1058 プログラム編集エディタでブログラムを編集することができます。実際にはこのサンプルを消して、新しく自分のプログラムを書いていくわけですが、まず最初は正しくインストールされているかどうかを確かめるために、標準のサンプルをそのまま実行してみましょう。ツールバーの実行ボタンをおすと、プログラムが機械語に翻訳されて実行ファイルの作成・実行が行われます。
1060 Reloは現在のバージョンでは、起動直後にコンパイラの設定ウィンドウを開いてOKボタンを押す必要があります。''メニューパー → Tools → Compilers''を選択して、開いたダイアログでOKを押します。
1061 [img[新しいプロジェクト|image/Win/Psychlops_Relo_New5.png]]
1063 出来上がった実行ファイルはプロジェクトと同じフォルダにあります。
1064 [img[新しいプロジェクト|image/Win/Psychlops_Relo_New7.png]]
1067 <div title="1.3 インストール作業(Windows) " modifier="YourName" created="201002040143" changecount="1">
1068 <pre>!! OpenGLのドライバ設定の確認
1069 Psychlopsは画面の垂直同期信号に合わせて画面を更新しますが、この機能を正常に動作させるには、ディスプレイドライバのOpenGL描画設定で垂直同期にあわせるオプションを常にオンにする必要があります。
1070 一般的には''コントロールパネル > 画面''の設定の''モニタ''タブを選び、''詳細設定''ボタンから設定します。詳しくはご使用のビデオカードのマニュアルをご覧ください。
1072 !! CodeBlocks + GCCのインストール
1073 PsychlopsのWindowsで利用するには、まずコンパイラと開発環境をインストールする必要があります。ここでは両者がセットになっているCodeBlocks(MinGW gccコンパイラつき)をダウンロードして利用する例を紹介します。
1075 2010/01/31現在、CodeBlocks8.02 / Windows XP, Vista, 7に対応しておりますが、最新の対応状況につきましてはhttp://psychlops.l.u-tokyo.ac.jp/?Environmentからご確認ください。スクリーンショットはマニュアル作成時のものですので、バージョンが更新されていると異なる場合があります。ご了承ください。
1077 * [[CodeBlocks + GCCダウンロード|http://www.codeblocks.org/downloads/5]]
1079 まずダウンロードページへ行き、''codeblocks-8.02mingw-setup.exe''をSourceforgeまたはBerliOSのサーバからダウンロードします。どちらのサーバからダウンロードしても中身は同じです。
1080 [img[CodeBlocksダウンロード|image/Win/CodeBlocks_Win01.png]]
1082 ダウンロードが終わるとインストーラの実行ファイルがありますので、''codeblocks-8.02mingw-setup.exe''をダブルクリックしてインストールを始めます。インストーラの指示に従ってインストールを完了します。
1083 [img[BCCインストール|image/Win/CodeBlocks_Win02.png]]
1087 !! Psychlopsの本体をインストール
1088 * [[Psychlops Download|http://psychlops.l.u-tokyo.ac.jp/?Download]]
1090 ダウンロードページへ行き、Win32GL版の最新版をクリックします。
1091 [img[Psychlopsダウンロード|image/Win/Psychlops_WinGL1.png]]
1093 zipファイルを解凍するとインストーラフォルダが現れますので、この中の''PsychlopsLib''フォルダ内にある''PsychlopsFramework_Win32_x.x.x.exe''をダブルクリックしてインストールを開始します。
1094 [img[Psychlopsインストール|image/Win/Psychlops_WinGL3.png]]
1096 インストーラの指示に従ってインストールを完了します。
1097 [img[Psychlopsインストール|image/Win/Psychlops_WinGL5.png]]
1100 !! CodeBlocks用のテンプレートをインストール
1101 zipファイルを解凍してできたインストーラフォルダの中の''CodeblocksTemplate''フォルダ内にある''Psychlops_Codeblocks_Template_xxxxxxxx.exe''をダブルクリックしてインストールを開始します。インストーラの指示に従ってインストールを完了します。
1102 [img[Psychlopsインストール|image/Win/Psychlops_WinGL4.png]]
1106 CodeBlocksを起動し、''メニューパー → File → New → Project''を選択します。
1107 [img[新しいプロジェクト|image/Win/CodeBlocks_Win10.png]]
1109 新しいプロジェクトのウィザードが開いたら、''Psychlops GL Ploject''を選択します(見つからなければ選択ボックスをスクロールしてください)。
1110 [img[新しいプロジェクト|image/Win/CodeBlocks_Win11.png]]
1112 新しいプロジェクトの名前を決め、ファイルを保存する場所を選びます。作成後にフォルダごと移動することで場所は変更可能ですが、名前は作り直す以外に変更できませんのでご注意ください。
1113 [img[新しいプロジェクト|image/Win/CodeBlocks_Win13.png]]
1115 プログラムはSourcesカテゴリの中にあるmain.cppにあります。標準のサンプルプログラムが入っていますので、まずはダブルクリックしてエディタを開き、中身を見てみます(最初から開いていることもあります)。
1117 下のようなウィンドウが開いたでしょうか?
1118 [img[ウインドウ説明|image/Win/CodeBlocks_Win14.png]]
1119 プログラム編集エディタでブログラムを編集することができます。実際にはこのサンプルを消して、新しく自分のプログラムを書いていくわけですが、まず最初は正しくインストールされているかどうかを確かめるために、標準のサンプルをそのまま実行してみましょう。ツールバーの実行ボタンをおすと、プログラムが機械語に翻訳されて実行ファイルの作成・実行が行われます。
1122 出来上がった実行ファイルは、''プロジェクトのあるフォルダ > binフォルダ > Releaseフォルダ または Debugフォルダ'' の中にあります。この実行ファイルをコピーすることで頒布が可能ですが、メールなどに添付する際は必ずzipファイル等に圧縮してください。。
1125 <div title="1.3 各試行部分のプログラミング" modifier="Psychlops_DevelopperG" modified="200908190216" created="200709252145" changecount="2">
1126 <pre>[[1.1|1.1 実験計画のプログラミング]], [[1.2|1.2 Gaborパッチの事前描画]]節で実際の試行に先立つ計算は終了したことになります。
1127 ここでは実際の試行部分のプログラミングを行います。
1128 各試行の実行時はここで作成している実験の場合大きく分けて2つに分かれるのでした。
1129 a. 各試行における描画(刺激の提示):該当するGaborが描画されたImageインスタンスを用いた描画
1131 まずa.の部分についてもう少し詳しく考えてみると、以下のような手順をループさせれば良いことがわかります。
1134 # 試行開始待ち(被験者のキー入力待ち)
1139 この流れに従って試行番号"trial"番の試行をプログラムしてみます。
1140 ここで使う変数はあとでwhileループの外で宣言を行うので、とりあえず、宣言なしで変数を使っていきます。
1143 まず、この試行におけるGaborパッチの傾きの方向"direction"をPsychlops::random()命令を使って0か1にランダムに決定します。
1144 次に、試行番号trialから6.1で作成したクラスインスタンスinvarを用いて各独立変数を取得します。
1145 各試行における傾き量の番号("ori_now")と提示時間("dura_now")を求めるには~IndependentVariables::get()を使います。
1146 |!int ~IndependentVariables::get()|set(int vnum, int trial_now)|
1147 |~|~|int vnum: 独立変数の識別番号|
1148 |~|~|int trial_now: 試行番号|
1150 単に設定したinvarから各試行に対応した変数を取得するのであれば、以下のコードの様になるでしょう。
1152 ori_now=invar.get(0, trial);
1153 dura_now=invar.get(1, trial);
1156 しかしここでは実際に欲しい以下の2つの値を計算します
1157 * [[1.2|1.2 Gaborパッチの事前描画]]節で事前描画したImageの配列gIMGの中でこの試行でCanvasに転送されるImageの番号
1160 転送番号については、[[1.2|1.2 Gaborパッチの事前描画]]節で作った表をみると、呼び出す配列番号が(傾き量番号)+5 x (方向)であることがわかります。
1161 |!gIMGの配列番号|gIMG[0]|gIMG[1]|gIMG[2]|gIMG[3]|gIMG[4]|gIMG[5]|gIMG[6]|gIMG[7]|gIMG[8]|gIMG[9]|
1162 |!(傾き量, 方向) 番号|(0,0)|(1,0)|(2,0)|(3,0)|(4,0)|(0,1)|(1,1)|(2,1)|(3,1)|(4,1)|
1164 次に、刺激提示のフレーム数ですが、これはCanvasクラスのリフレッシュレートを取得する関数getRefreshRate()を使えばCanvasの宣言に依存せずに提示時間を制御できます。^^*1^^
1166 これらをまとめると変数の取得部は、以下の様なコードになります。
1168 direction=Psychlops::random(2); //0:right 1:left
1169 ori_now=invar.get(0,trial)+5*direction;
1170 dura_now=sampleA.getRefreshRate()*(invar.get(1,trial)/1000);
1172 ^^*1 この実験では必ず計算結果が整数フレームとなるようにあらかじめ変数を設定してあることに注意してください。^^
1175 次にユーザー(被験者)の準備が整って、キーを押すまでプログラムを"待ち"の状態にする部分をコーディングします。
1176 ここでは、試行開始のキーにはスペースキーを用います。さらに、被験者がわかりやすいように、プログラム側の準備が整って「待ち」の状態になっている間は試行が何番目に当たるかを表示するようにしてみます。
1177 キー入力待ちのコードは今まで何度も使ってきたとおりに、以下のように書きます。
1179 while(!Input::get(Keyboard::spc));
1181 試行番号を表示するにはCanvas::message()命令を使えばよいのですが、試行番号は試行毎に変化するので少し工夫が必要です。
1182 ここでは、Cの標準関数であるsprintf()関数を使って、試行番号を文字列に埋め込みます。この部分はあまりプログラムの流れには関係ないので、良くわからない方はとりあえず無視して先に進んでください。以上をまとめると以下のようなコードになります。
1186 for(int i=0; i<64; i++) trial_header[i] = 0; // 文字列の初期化
1187 sprintf(trial_header, "%s%d%s%d","Trial: ", trial, " /", trialNum ); //試行番号の埋め込み
1188 sampleA.message(trial_header, sampleA.getHcenter(), sampleA.getVcenter()-100, Color::green);//キー待ちメッセージの描画
1191 while(!Input::get(Keyboard::spc));
1195 いよいよ実際の刺激の描画ですが、これは特に難しくありません。Image::draw()命令を使って裏画面にori_now番のイメージを転送し、Canvas::flip(int)命令を使って、dura_nowフレーム分の描画時間を予約して描画を反映させます。この後に、Canvas::clear()命令を使えば、一定時間たつと自動的に刺激が消えるプログラムになります。
1198 gIMG[ori_now].draw();
1199 sampleA.flip(dura_now);
1204 ! 反応待ち(被験者のキー入力待ち)とデータの格納
1206 刺激描画が終わったら被験者のキー入力(f: 左/j: 右)を待ちます。プログラムは"f"か"j"が押されるまでは待ち、押されたら押されたキーに従って、0(j), 1(f)のいずれかの値を変数ansに格納します。[[6.1|6.1 Gaborパッチの事前描画]]節においてgIMGを描画する際、傾きの方向番号にふられた番号が0が右、1が左であったことに注意してください。また、ここでescを押すと、プログラムを途中終了するコードも入れておきます。
1210 if(Input::get(Keyboard::j)){ans=0;break;} //"j"なら右(0)
1211 if(Input::get(Keyboard::f)){ans=1;break;}//"f"なら左(1)
1212 if(Input::get(Keyboard::esc)){exit(0);}
1216 最後はデータの格納です。上の方でランダムに決定したdirectionとキー入力によって得られた値ansが同じならば正答(1),異なれば誤答(0)を配列answer のtrial番目に格納します。ついでに、後でデータを見やすくするためにここで使った2つ独立変数の値も配列orientationConditionと配列durationConditionに格納しておきます。
1218 if(ans==direction) answer[trial]=1;
1219 else answer[trial]=0;
1220 orientationCondition[trial]=invar.get(0, trial);
1221 durationCondition[trial]=invar.get(1, trial);
1225 以上をまとめて、ループの中に入れると、各試行部分のプログラミングは完成です。
1226 これまで保留していた変数の宣言がループの前にきちんと記述されていることやそれぞれの型がどのようになっているかにも注目してください。特に、answer, orientationCondition, durationConditionの配列を確保するためにはtrialNumをconst int型として宣言する必要があることには注意が必要です。
1229 const int trialNum=2*5*10;
1230 int answer[trialNum], orientationCondition[trialNum], durationCondition[trialNum], ans;
1231 int ori_now, dura_now;
1233 char trial_header[64];
1235 for(int trial=0; trial<trialNum; trial++){
1237 direction=Psychlops::random(2); //0:right 1:left
1238 ori_now=invar.get(0,trial)+5*direction;
1239 dura_now=sampleA.getRefreshRate()*(invar.get(1,trial)/1000);
1243 for(int i=0; i<64; i++) trial_header[i] = 0; // 文字列の初期化
1244 sprintf(trial_header, "%s%d%s%d","Trial: ", trial, " /", trialNum ); //試行番号の埋め込み
1245 sampleA.message(trial_header, sampleA.getHcenter(), sampleA.getVcenter()-100, Color::green);//キー待ちメッセージの描画
1247 while(!Input::get(Keyboard::spc));
1251 gIMG[ori_now].draw();
1252 sampleA.flip(dura_now);
1258 if(Input::get(Keyboard::j)){ans=0;break;}
1259 if(Input::get(Keyboard::f)){ans=1;break;}
1260 if(Input::get(Keyboard::esc)){exit(0);}
1262 if(ans==direction)answer[trial]=1;
1263 else answer[trial]=0;
1264 orientationCondition[trial]=invar.get(0, trial);
1265 durationCondition[trial]=invar.get(1, trial);
1269 <div title="1.4 更新とアンインストール" modifier="YourName" modified="200802191314" created="200709131956" changecount="3">
1270 <pre>2007/8/13以降のバージョンを使用していた場合は、Psychlopsの更新は通常のインストールとまったく同じです。ダウングレードする場合も同様です(つまり、単純に上書きしてしまってかまいません)。それ以前のバージョンが既にインストールされたシステムについては、一度アンインストールをしてからもう一度インストールを行ってください(下記参照)。
1272 Psychlopsは特にレジストリ等の設定を行いませんので、アンインストール時はインストールされたフォルダごと削除してください。Psychlopsは単なるC++ライブラリで、常駐等はしませんので、削除しない場合でもシステムに影響を与えることはありません。
1274 また、C++開発環境を自分で既に運用していて、デフォルトの場所以外の所にライブラリを置くことも可能です。この場合は、インストールを行った後に、自分でFrameworks以下にあるファイル・フォルダを目的の場所に移動して、ご自分でパスの設定をなさってください。
1277 ** /Library/Frameworks/Psychlops.framework
1280 ** (システムドライブ)\Library\Frameworks\Psychlops.framework
1281 *** Windowsには通常Libraryフォルダがありませんので、このフォルダ内にPsychlopsしか入っていなければLibraryフォルダごと削除してかまいません。
1282 ** Relo、Code::Blocksのテンプレートについては、テンプレート一覧ファイルを上書きしています。元に戻す必要がある場合は、再インストールする必要があります。</pre>
1284 <div title="1.4 終了処理とまとめ" modifier="Psychlops_DevelopperG" modified="200908190216" created="200709252145" changecount="1">
1285 <pre>最後にデータをファイルにセーブして、実験の終了メッセージを記録すれば、プログラミングは完了です。
1286 被験者の反応を記録した配列answerと実験条件を記録した配列orientationCondition, durationConditionをData::savearray()命令を使用して"~ExptData.dat"という名前のテキストファイル(tab区切り)にセーブします。
1290 sampleA.message("Press space key to exit.",
1291 sampleA.getHcenter(), sampleA.getVcenter()-100, Color::green);
1292 Data::savearray("ExptData.dat",
1293 "Duration /t Orientation /t Answer", trialNum,
1294 durationCondition, orientationCondition, answer );
1297 これで、すべてのパーツが完成しました。一つにまとめると以下の様になります。
1298 [[Gabor方位判断の実験プログラム例]]
1299 ぱっと見たときには長そうなプログラムですが、よく見ると実際にはクラスの宣言部分がほとんどを占めていて、実験に必要な部分のプログラム(つまりあなたが理解して、書かなければいけない部分!) は比較的短いコードであることがわかります。
1300 コンパイルして1つ1つの部分が正しく実行されているかどうか確かめてみてください。</pre>
1302 <div title="1.5 Psychlopsの基本構造" modifier="Psychlops_DevelopperG" modified="200908190224" created="200711081840" changecount="3">
1303 <pre>次節で詳しく述べるように、PsychlopsはC++のライブラリになっています。このライブラリは、いくつかの命令のグループから成り立っています。それぞれの命令のことをメソッド、グループのことをクラスと呼びます。Psychlopsを用いた描画は、ある変数がどのクラスに属する変数なのかを宣言し、この変数に対してメソッドを指定することで行います。より詳細な具体例は次節や、このマニュアルの中で随時見ていくと理解できるでしょう。今は、このようなグループがいくつかPsychlopsの中にあると言うことだけを理解しておけば十分です。以下に代表的なPsychlopsのクラスをまとめておきます。
1305 |!Psychlops::Canvas|描画ウィンドウの確保と基本的な描画の命令セット|
1306 |!Psychlops::Point|ドット描画と座標指定のための命令セット|
1307 |!Psychlops::Rectangle|矩形描画と矩形範囲指定のための命令セット|
1308 |!Psychlops::Color|描画色指定のための命令セット|
1309 |!Psychlops::Image|オフスクリーン描画のための命令セット|
1310 |!Psychlops::Matrix|行列演算のための命令セット|
1311 |!Psychlops::Clock|時間計測のための命令セット|
1312 |!Psychlops::Input|入出力取得のための命令セット|
1314 それぞれのクラスの先頭についているPsychlops::はこれらのクラスがPsychlopsというライブラリの中のクラスであることを示しています。たとえば、Colorと言うクラスは、別のライブラリの中にも存在するかも知れません。その場合、プログラムを解釈する側は、このクラスがどのライブラリのクラスであるかを判断できなくなってしまいます。これを避けるために、スコープ演算子"::"を使って、あるクラスがどのライブラリに所属するかを示すのです。名字のようなものだと思うとわかりやすいかも知れません。
1316 このほかにもPsychlopsにはData, File, Range等の特殊なクラスが存在しますが、これらについては具体的な使い方とともに解説します。
1320 <div title="1.6 Psychlopsを使ったプログラミング" modifier="Kazushi Maruya" modified="200711081830" created="200709170801" changecount="11">
1321 <pre>Psychlopsを使ったプログラミングはC++のプログラミングと同様ですが、必ずしもその全てを理解しておく必要はありません。(もちろん、理解しているほうがより柔軟なプログラミングができますが。)
1322 ここでは、psychlopsを用いたプログラミングの際に、必要不可欠だと思われる基本要素についてごく簡単な説明を行います。より詳細なC++プログラミングを学びたい方は入門書・専門書を参考にされることをおすすめしますが、ここで説明するいくつかのことに対する大まかなイメージをもっておけばPsychlopsのプログラミングにとっては十分なはずです。より細かなことは実際のプログラミングを通して理解することをおすすめします。
1324 [[1.6.1 プログラミングとは?]]
1325 [[1.6.2 Psychlopsを使ったプログラムの形]]
1329 [[1.6.6 クラスと関数]]</pre>
1331 <div title="1.6.1 プログラミングとは?" modifier="YourName" modified="200802191315" created="200709170805" changecount="4">
1332 <pre>まず最初に、プログラムとは何かについて説明します。
1334 プログラムとは、画像や数値などのデータを、一定の手続きに従って操作する過程を、文字で表記したものです。たとえば、Excelなどの表計算でセルに書き込む式も、一種のプログラムということができます。
1335 ここでは、Psychlopsのプログラムを例に考えてみます。
1337 プログラムでまず最初に必要なことは、取り扱うデータや装置を準備することです。
1338 コンピュータを使わない実験でも、画像や画像を配置する画面などをあらかじめ準備しなければなりませんが、
1339 これと同じことをコンピューター上でも行います。
1345 Image natural_image;
1347 この1行は、画像(Iamge)にnatural_imageという名前をつけて取り扱う準備をしたことになります。
1349 次に、データを操作する手続きを記述します。手続きは
1350 ''操作が行われる対象.操作方法(関係するデータ)''
1351 の方法で記述します。英語のSVO文法に近い記述法ですね。
1354 natural_image.load("Natural Image.png");
1356 この一行は、natural_imageという画像として、「Natural Image.png」という名前の画像ファイルを読み込む手続きを表します。
1359 natural_image.centering().draw();
1361 この一行は、先ほど読み込んだ画像を、画面の中央に配置(センタリング)し、画面に描画する手続きを表します。
1363 プログラムとは、「データを準備する」「データを操作する」ことについて、行う順番どおりに書いていくことなのです。
1364 準備や操作に区切りをつけるには「;」(セミコロン)を記述します。</pre>
1366 <div title="1.6.2 Psychlopsを使ったプログラムの形" modifier="YourName" modified="200802191316" created="200709170816" changecount="5">
1367 <pre>では、Psychlopsで動く簡単なプログラムを例にとって最も簡単なPsychlopsのプログラムの形を見てみます。
1370 #include <psychlops.h>
1371 using namespace Psychlops;
1373 void psychlops_main() {
1374 Canvas display( Canvas::fullscreen );
1375 Image natural_image;
1376 natural_image.load("Natural Image.png");
1377 natural_image.centering().draw();
1382 先頭の2行はPsychlopsを使うための命令です。
1383 意味はわからなくてもかまいませんが、Psychlopsの命令をコンピュータに理解させるためには必ずこの2行をプログラムの先頭に書いておく必要があります。
1386 #include <psychlops.h> // このプログラムにPsychlopsを含める
1387 using namespace Psychlops; // このプログラムはPsychlopsを使う
1390 次の行は、Psychlopsを用いたプログラム本体を書く場所(メインルーチン)を示す行です。
1392 void psychlops_main() { // psychlopsが主に扱うブロック
1394 Psychlopsは、ここから最後の行の閉じ括弧(})までのブロックをまず最初に実行します。
1397 Canvas display( Canvas::fullscreen );
1398 Image natural_image;
1399 natural_image.load("Natural Image.png");
1400 natural_image.centering().draw();
1403 この5行が実際にコンピュータに対して与える命令になります。
1404 今はこの5行の具体的な内容については述べませんが、ユーザーは基本的にはこの部分を書き換えて描画を実行させることになります。</pre>
1406 <div title="1.6.3 配列" modifier="PsychlopsAdmin" modified="200709170821" created="200709170817" changecount="1">
1407 <pre>これまでの例では画像はひとつだけでしたが、何枚かの画像を選んで出したい場合があります。
1408 いくつかのデータに似たような操作をしたい場合、「配列」が役に立ちます。配列とは、データがまとめて並んでいるもののことを言います。内部にいくつも区切りがある箱のようなものを想像するとよいかもしれません。
1410 たとえば、画像を3枚まとめて扱いたい場合、以下のようにデータを準備します。
1412 Image natural_images[3];
1415 これで3枚の画像配列が用意されました。次に、データの並びの中のそれぞれの画像を操作してみましょう。
1418 natural_images[0].load("Natural Image0.png");
1419 natural_images[1].load("Natural Image1.png");
1420 natural_images[2].load("Natural Image2.png");
1424 と書くことで、配列のうち数字で指定した順番の要素(この場合は画像)を操作することができます。
1425 順番は0から始まることに注意してください。画像3枚を用意した場合、0,1,2になります。</pre>
1427 <div title="1.6.4 for命令" modifier="Kazushi Maruya" modified="200711082123" created="200709170819" changecount="7">
1428 <pre>この画像配列を順番に表示してみましょう。ここでは、繰り返し操作を記述するfor文を使うことにします。
1430 for(int i=0; i<3; i++) {
1431 natural_images[i].centering().draw();
1435 for文は、{ } で囲まれたブロックを複数回実行することを示します。
1437 [img[image/formethod.png]]
1438 この命令の後に続く{}で囲まれた部分のブロックは繰り返し、カウンタ(i)の値を0,1,2の順番で変更しながら実行されます。
1441 natural_images[i].centering().draw();
1448 for(int i=0; i<3; i++) {
1449 natural_images[i].centering().draw();
1455 natural_images[1].centering().draw();
1457 natural_images[2].centering().draw();
1459 natural_images[3].centering().draw();
1464 image.centering().draw()は画像をセンタリングして描画する操作なので、以下のプログラムをNatural Image0.png, Natural Image1.png, Natural Image2.pngという3つのファイルがあるディレクトリで実行すると、3つの画像が順番に表示されます。
1467 #include <psychlops.h>
1468 using namespace Psychlops;
1470 void psychlops_main() {
1471 Canvas display( Canvas::fullscreen );
1472 Image natural_image;
1474 natural_images[0].load("Natural Image0.png");
1475 natural_images[1].load("Natural Image1.png");
1476 natural_images[2].load("Natural Image2.png");
1478 for(int i=0; i<3; i++) {
1479 natural_images[i].centering().draw();
1487 <div title="1.6.5 変数型" modifier="PsychlopsAdmin" modified="200709170825" created="200709170821" changecount="3">
1488 <pre>ここまではPsychlopsで扱えるデータを題材に解説してきましたが、最後に、C++言語で取り扱うことのできる基本的なデータについて説明します。
1489 これらのデータの中でもっとも基本的なものは数値です。数値は、主に整数型と小数点型に分かれます。
1491 ** 整数(''int''eger)の略で、整数を取り扱います。小数点以下は切り捨てられますので、注意してください。
1493 ** 浮動小数点を取り扱います(倍精度浮動小数点 ''double'' precision floating point number)
1501 のように使います。データに特定の数値を代入する(格納する)場合は、
1507 C++にはこのほかにも文字列型(char *やstd::string)、真偽値型(bool)などがありますが、ここでは詳しくは説明しません。これらの型の使い方については、これ以降のプログラミングの実例のなかで必要なところだけを説明していきます。詳細を知りたい型は、CやC++の入門書をご覧ください。</pre>
1509 <div title="1.6.6 クラスと関数" modifier="YourName" modified="200802191319" created="200709171609" changecount="14">
1511 クラスとは、関係する変数(データ)や関数(メソッド)をまとめたものです。
1512 [[1.5節|1.5 Psychlopsの基本構造]]で概観したように、Psychlopsでは、画面(Canvas)や画像(Image)、図形(Ractangle)など、描画に関するさまざまなクラスを用意しています。各クラスには、その機能に関連した固有の関数があります。
1515 * Image.pix(x, y, color)
1516 * Image.rect(rect, color)
1517 * Image.save(filename)
1518 *Image. load(filename)
1522 関数はには''返り値''と''引数''があります。たとえば累乗関数(power)を例に見てみましょう。
1526 この式を実行すると、変数aには10の3乗である1000が代入されます。累乗関数が必要とする値である10と3を引数といい、返される計算結果1000を返り値と呼びます。
1528 pow関数の引数のうち、1番目は累乗される数、2番目は累乗する数です。順番や型は固定されていますのでご注意ください。順番を間違えると、計算結果も変わってきてしまいます。たとえば、
1532 を実行すると、3の10乗である59049がaに代入されます。
1536 Psychclopsでは、一部の関数を.でつなぎながら連続で書くことができます。
1538 rect.centering().shift(x, y).draw(Color::red);
1540 この書き方は他のC言語のプログラムではあまり見かけませんが、Psychlopsでは多用されています。プログラムの長さを短くし読みやすくすることができます。
1542 この書き方ができるものは、関数の返り値として変数自身が返されるものに限ります。
1547 このマニュアルでは、各クラスの関数について表で説明が出てきます。この表の読み方を、Imageを表示する関数を題材に説明します。
1549 |!Image::draw()|draw()|指定したオフスクリーンを描画します|
1550 |~|draw(double left, double top)|指定したオフスクリーン領域を(left,top)を中心とした座標に設定します|
1551 |~|~|double left: 描画するオフスクリーン領域の座標軸xを指定|
1552 |~|~|double top: 描画するオフスクリーン領域の座標軸yを指定|
1554 一番左の列は関数の名前を示します。ここでまたスコープ演算子が関数名の名前についていますが、これは1.5節と同様にその関数がどのクラスに所属しているかを示すためのものです。
1559 と書く様な気がしてしまいますが、いくつかのクラスをのぞいて^^*1^^このような書き方をすることはありません。なぜなら、実際のプログラムの中は、あるクラスに属する変数(インスタンス、と呼ばれます)に対しての命令しか行わないからです。Image型として宣言されたインスタンスに対してメソッドを実行するときには"."演算子を用います。たとえば、Image::draw()命令を実際に使うときには以下のような形になるでしょう。
1561 Psychlops::Image img;
1565 *1 Input, Display等のハードウェアと直結したクラスはインスタンスを宣言せずに使います。これは、これらのクラスがPsychlopsのイニシャライズ時にあらかじめ宣言されたデフォルトのインスタンスを持っていて、これに対するメソッドの実行を行うように設計されているためです。
1567 上記の表は、Image::draw()というメソッドに
1569 * Image::draw(double left, double top)
1570 という二つの使い方があることを示しています。
1573 二番目の列は、引数の違ういくつかのバージョンが列挙されています。
1574 2つめの使い方Image::draw(double left, double top)では、括弧の中にコンマで区切られた2つの文字列があります。
1575 これは、この使い方では関数の引数として2つの変数を代入することができることを示しています。
1576 [img[image/FunctionNotation.png]]
1577 一つめの引数はdouble leftと書かれています。これは、一つめの引数がdouble型の変数でなくてはいけないことを示しています。(ただし、後で書くようにleftと言う名前にする必要はありません。)このマニュアルの中ではそれぞれのメソッド型名(スペース)名前という形でそれぞれの使い方に必要な引数の形式を示しています。
1579 実際に使用するときは、型名を書く必要はありません。left,topという2つの引数をこの関数に渡すときには以下のように書きます。
1581 Psychlops::Image img;
1582 double left=10,top=10;
1586 一番右の列は、各バージョンでの引数の説明になっています。Image::draw(double left, double top)の右側の欄を見ると、各引数について、一番目が表示したいx座標、二番目がy座標であることがわかります。
1588 各引数には名前がついていますが、関数を呼び出す際に引き渡す変数はこの名前である必要はありません。マニュアルでは
1589 * image.draw(left, top)
1591 * image.draw(100, 200)
1592 * image.draw( x, y )
1593 など任意の値を渡すことが可能です。</pre>
1595 <div title="2. デモを作成する" modifier="Psychlops_DevelopperG" modified="200908190221" created="200712061915" changecount="3">
1596 <pre>Psychlopsには、プログラム内の変数をプログラムのユーザがインタラクティブに操作できるデモアプリケーションを簡単に作成するためのクラスIndependentが用意されています。
1597 この節では、Independentクラスについて簡単に説明して、これを用いたプログラムの作成例について解説します。
1599 [[2.1 独立変数Independent]]
1603 <div title="2. 基本的な描画を行う" modifier="Psychlops_DevelopperG" modified="200909300724" created="200708211944" changecount="16">
1604 <pre>[[2.1 描画領域の宣言]]
1606 [[2.1.2 Canvasの基本構造と操作]]
1607 [[2.1.3 Canvasのメンバ取得と変数の表示]]
1609 [[2.2.1 画面上に点を描画する-Pointクラス1-]]
1610 [[2.2.2 画面上に線を描画する]]
1611 [[2.2.3 画面上に四角形を描画する-Rectangleクラス1-]]
1612 [[2.2.4 画面上に円を描画する]]
1613 [[2.2.5 描画する図形の色を指定する-Colorクラス-]]
1614 [[2.2.6 画面上に縞を描画する-Rectangleクラス2-]]
1615 [[2.2.7 画面上に縞を描画する-Pointクラス2-]]
1616 [[2.2.8 運動する図形を描画する-Rectangleクラス3-]]
1617 [[2.3 文字列の描画]]</pre>
1619 <div title="2.1 描画領域の宣言" modifier="Psychlops_DevelopperG" modified="200908190225" created="200708212010" changecount="6">
1620 <pre>この節ではPsychlopsのもっとも基本的なクラスであるCanvasクラスの宣言方法と初歩的な操作を説明します。
1622 [[2.1.2 Canvasの基本構造と操作]]
1623 [[2.1.3 Canvasのメンバ取得と変数の表示]]</pre>
1625 <div title="2.1 独立変数Independent" modifier="Psychlops_DevelopperG" modified="200908190217" created="200710220054" changecount="1">
1628 Independentクラスは、プログラム中で独立変数に当たる変数を制御するためのオプション機能です。現在はデモ用のコンソールで使用することが出来ます。
1630 通常、プログラムは画面を描画するなどの作業を行っています。プログラムの中にはたくさんの変数や関数が入っています。
1631 [img[Independentなし|image/Independent1.png]]
1633 プログラム中の変数をIndependentに登録すると、もともとのプログラムの動作を変えずにデモ用のコンソール機能などを追加することが出来ます。
1634 [img[Independentあり|image/Independent2.png]]
1639 (Tiddly Wikiの機能上の都合、記号が全角で表示されていますが、実際は半角です)
1640 |!Independent|<< 変数|Independentクラスに登録する変数を指定します。|
1641 |~|| 文字列|変数につけるラベルを指定します|
1642 |~|| Range|Independenクラスtに指定する変数の変動範囲を指定します。変動範囲チェックはデモコンソールなどからの書き換え時にしか行われないため、元のプログラムで変動範囲外に変化する場合は検出できません。|
1643 |~|| 数値|デモコンソールでのキー押しあたり変化量を設定します。2つ続けて書いた場合、一方がShiftキーとの同時押し時の変化量になります。|
1644 現在のところ(2007年11月現在)、Independentクラスに登録できる変数は数値型(int, double等)のみです。
1646 !!Independentクラスへの登録
1647 以下では、例として3つの変数をIndependentクラスに登録する書き方例を示します。
1649 double rect_size = 100;
1650 double rect_lum = 0.5;
1651 double bg_lum = 0.2;
1654 Independent << rect_size | "Rect Size" | 1< rng< 500 | 10.0 | 2.0 ;
1655 Independent << rect_lum | "Rect Luminance" | 0.0<=rng<=1.0 | 0.1 | 0.05;
1656 Independent << bg_lum | "BG Luminance" | 0.0<=rng<=1.0 | 0.1 | 0.05;
1659 ここで注意することは、変数に適正な初期値を与えておくことです。もしも、Indpendentで設定される範囲の外に初期値が設定されてしまうと、実行時に変更することができなくなります。変数の宣言の時に忘れずに初期値の設定をしておくことをおすすめします。
1662 <div title="2.1.1 Canvasの宣言" modifier="Psychlops_DevelopperG" modified="200910080812" created="200708240239" changecount="25">
1665 絵を書くときには描く筆や絵の具のほかに紙などのキャンバスが必要です。
1666 この処理はコンピュータ上に絵を描くための"Canvas(描画のためのウィンドウ)"を用意します。
1668 Canvasが確保されていないと描画処理を記述しても処理されません。
1669 描画処理を行う前に必ずCanvasの宣言が必要となります。
1672 キャンバスを用意する命令には様々な書式があるのですが、その基本は以下の2つです。
1674 一つは解像度、リフレッシュレートを変更せずに、画面いっぱいに領域を確保する
1675 Canvas(Canvas::fullscreen)
1677 もう一つは解像度、リフレッシュレートを変更して、画面いっぱいに領域を確保する
1678 Canvas(int,int,int,double)
1681 後者で指定した画面モードが選べなかった場合は、例外が送出されプログラムは強制終了します。
1683 そのほかに Canvas::windowスイッチを使ってウインドウを開いて画面を取得する方法があります。
1684 この方法では描画の垂直同期信号への同期が保証されないので(くわしくは[[2.1.2 Canvasの基本構造と操作]]で説明します)、デモやプログラムのデバッグ時に使うとよいでしょう。
1685 詳しくは[[関数一覧|Canvasの宣言]]をご覧ください。
1686 * 参考[[Tips: Canvasとリフレッシュレートの詳細]]
1689 |~|Canvas(Canvas::fullscreen, const Display disp)|>|!現在の画面モードで画面を確保する場合に使います|
1690 |~|~|Canvasmode mode|[[表示モード|CanvasMode]](フルスクリーンorウィンドウ)を選択|
1691 |~|~|const [[Display|Displayの指定値]] disp|[[表示するディスプレイ|Displayの指定値]]を選択(省略可能)|
1692 |~|~|>|マルチディスプレイ環境では、最後の引数でどのディスプレイに表示するか指定できます。省略時はプライマリディスプレイになります。|
1693 |~|Canvas(int width, int height, int colordepth, double refreshrate, const Display disp)|>|!画面モードを指定してフルスクリーン画面を確保します|
1694 |~|~|int width|水平方向の解像度(画面の横幅)を指定|
1695 |~|~|int height|垂直方向の解像度(画面の縦幅)を指定|
1696 |~|~|int colordepth|カラーモード(ビット)を指定|
1697 |~|~|double refreshrate|リフレッシュレート( Hz )を指定|
1698 |~|~|const [[Display|Displayの指定値]] disp|[[表示するディスプレイ|Displayの指定値]]を選択(省略可能)|
1699 |~|~|>|指定した画面モードが選べなかった場合は、例外が送出されプログラムは強制終了します。|
1703 <例1: Canvas::fullscreenオプションを使った宣言例>
1704 #include <psychlops.h>
1705 using namespace Psychlops;
1707 void psychlops_main() {
1708 Canvas sampleA(Canvas::fullscreen);
1712 <例2: 各属性値を指定した宣言例>
1713 #include <psychlops.h>
1714 using namespace Psychlops;
1716 void psychlops_main() {
1717 Canvas sampleB(1024,768,32,60.0);
1722 <例2: 各属性値を指定した宣言例>
1723 #include <psychlops.h>
1724 using namespace Psychlops;
1726 void psychlops_main() {
1727 Canvas sampleC(Canvas1024,768,32,60.0);
1732 上記のソースでは「psychlops_main()」クラスの中で
1733 sampleA,sampleBという名前のCanvasを宣言しています。
1734 これらのプログラム内でCanvas型(クラス)として宣言された変数はインスタンスと呼ばれます。
1735 マニュアル上では、ある命令hoge()の形式を説明するときにCanvas::hoge()と書きますが、実際にプログラム内で宣言されたCanvasに対して命令を実行したいときは、このインスタンス名を使って『インスタンス名.命令文()』という形式で記述します。たとえば、Sample.hoge(hoge)といった形です。より具体的な例はこれ以降に何回も現れます。
1736 ここで注意しなくてはならない点として、ふつうCanvasを宣言する場所はpsychlops_main() 内であることです。つまり、この宣言名はpsychlops_main() 内でしか有効ではありません。
1737 psychlops_main()で呼び出す他の関数内でCanvasを使用したいときは、その関数にCanvasのポインタを引き渡すかDisplayクラスを使って描画する必要があります。
1738 * 参考[[Tips::CanvasクラスとDisplayクラス]]</pre>
1740 <div title="2.1.2 Canvasの基本構造と操作" modifier="Psychlops_DevelopperG" modified="200910080810" created="200708240240" changecount="7">
1741 <pre>Psychlopsでは2つの画面を交互にディスプレイに提示します。
1742 この2つの画面が対となって、1つのCanvasを構成しています。たとえば、紙に裏表があるようなものだと思ってください。
1743 この2枚の画面に描画をすることとflip()メソッドを使って表画面と裏画面を切り替えることがCanvasクラスに対する主な操作です。
1745 [img[image/canvasstructure.png]]
1747 !Canvas::clear() -画面を塗りつぶす-
1748 はじめにCanvasを何かの色で塗りつぶしてみましょう。
1749 そのための処理がCanvas::clear()命令です。
1750 書式はCanvas::clear([[Psychlops::Color]] col,TargetSurface)です。いずれの引数も省略可能です。
1751 |!Canvas::clear()|clear([[Psychlops::Color]] col,TargetSurface)|指定したCanvasを指定色で塗りつぶします|
1752 |~|~|[[Psychlops::Color]] col :塗りつぶす色を指定|
1753 |~|~|TargetSurface:塗りつぶす画面を指定|
1754 |~|clear(TargetSurface)|指定したCanvasを塗りつぶします。色の指定には[[Canvas::setClearColor()]]命令を使用します|
1755 |~|~|TargetSurface:塗りつぶす画面を指定|
1757 2つめの引数は少しわかりにくいかもしれません。
1758 clear()命令は2つめの引数を指定することで、今ディスプレイに表示されている画面(紙の表)と、表示されていない画面(紙の裏)のいずれを(あるいは両方を)塗りつぶすのかを指定することができます。
1759 省略した場合は表示されていない画面を塗りつぶします。
1762 !!Canvas::clear()の書き方
1765 #include <psychlops.h>
1766 using namespace Psychlops;
1768 void psychlops_main() {
1770 Canvas sampleA(Canvas::fullscreen);
1771 sampleA.clear(Color::white,Canvas::FRONT);
1774 今ディスプレイに表示されている画面(紙の表)を白く塗りつぶせ、という命令文ですが
1775 これでは描画が一瞬で結果がわかりにくいですね。
1776 「while(!Input::get(Keyboard::spc));」を追加してスペースキーが押されるまで今ディスプレイに表示されている画面(紙の表)が白く表示されるかを確認できるようにしましょう。
1777 この命令の詳細については、[[4.1節|4.1 外部装置(キーボード/マウス)の操作内容を取得する]] で詳しく説明します。今はこの文がスペースキーの入力待ちをする文であると思っていてください。
1780 #include <psychlops.h>
1781 using namespace Psychlops;
1783 void psychlops_main() {
1785 Canvas sampleA(Canvas::fullscreen);
1786 sampleA.clear(Color::white,Canvas::FRONT);
1787 while(!Input::get(Keyboard::spc));
1791 !Canvas::flip() -画面を入れ替える-
1792 上の例では、オプションを使って現在表示されている画面に対して描画を実行しました。
1793 しかし、これは例外的な描画で、Psychlopsでは描画処理は表示されていない画面(裏画面)に対して行われます。
1794 従って、描画内容を画面に反映させるためには、表示画面と裏画面を入れ替える必要があります。
1795 この表と裏の2つの画面を入れ替え処理を行う命令がCanvas::flip()命令です。
1796 つまり、描画内容はCanvas::flip() 処理で画面を入れ替えることで初めて画面上に表示されます。
1797 この命令は画面の垂直同期信号(=ビデオカードが1枚の画面を書ききったことを示す信号; Vsync)に同期して、画面の切り替えを行います。
1798 * 参考 [[Tips: Canvas::flip()とコマ落ち]]
1800 |!Canvas::flip()|flip(int)|int:入力値のリフレッシュ分描画内容を表示します|
1801 |~|~|引数を入力しない場合(flip())、flip(1)が指定されます|
1802 |~|flipAfter(int wait)|int wait回のリフレッシュ後まで待ってからflip()を実行して描画内容を画面に反映させます|
1803 |~|flipAndWait(int wait)|もっとも近い垂直同期のタイミングでflip()を実行した後に、int wait回ののリフレッシュ分の表示を予約します。次にflip()命令がコールされても、int wait回ののリフレッシュ分が経過するまでは何も起こりません(flip()と全く同じ効果ですが、引数は省略不可)|
1805 flip()系の命令は画面描画の時間制御をする上でもっとも重要な命令です。上の表だけではわかりにくいでしょうから、図と実例を用いて各命令の働きをもう少し詳しく見ていきましょう。
1807 flipAfter(), flipAndWait()命令の動きを図解すると以下のようになります。以下の図ではflipAfter(3)とflip(3)(あるいはflipAndWait(3))の場合を例としてあげました。
1808 [img[image/psychlops_flip.png]]
1810 !!Canvas::flip()の書き方1
1813 #include <psychlops.h>
1814 using namespace Psychlops;
1816 void psychlops_main() {
1818 Canvas sampleA(Canvas::fullscreen);
1819 sampleA.clear(Color::blue,Canvas::BACK);
1820 while(!Input::get(Keyboard::spc));
1824 例2では今ディスプレイに表示されている画面(紙の表)の色を塗りつぶしましたが
1825 今回は表示されていない画面(紙の裏)を塗りつぶし、それをflip()命令で表画面に入れ替えました。
1827 !!Canvas::flip()の書き方2
1828 次は、flip()が実行されるタイミングを制御してみましょう。
1829 flip()命令の引数を省略すると、flip()命令は、描画の計算が終了した時点からもっとも早い同期タイミングで画面の切り替えを行います。
1833 #include <psychlops.h>
1834 using namespace Psychlops;
1836 void psychlops_main() {
1837 Canvas sampleA(Canvas::fullscreen);
1838 sampleA.clear(Color::blue,Canvas::BACK);
1840 sampleA.clear(Color::cyan,Canvas::BACK);
1842 while(!Input::get(Keyboard::spc));
1845 青→シアンに画面の色が塗りつぶされるのを確認できたでしょうか。
1846 次はflipAfter()で同様のソースを実行します。
1849 #include <psychlops.h>
1850 using namespace Psychlops;
1852 void psychlops_main() {
1853 Canvas sampleA(Canvas::fullscreen);
1854 sampleA.clear(Color::blue,Canvas::BACK);
1855 sampleA.flipAfter(60);
1856 sampleA.clear(Color::cyan,Canvas::BACK);
1857 sampleA.flipAfter(60);
1858 while(!Input::get(Keyboard::spc));
1861 <例4>と<例5>の結果の差が見えたでしょうか。
1862 <例4>は60フレームのリフレッシュ分描画内容を表示したのに対し
1863 <例5>は60フレームのリフレッシュ分後、描画内容を表示しました。
1864 <例6>はflipAndWait()による画面入替えの確認をします。
1865 こちらはflip();と同様の結果を返します。
1866 <例4>と同じ結果が返るか確認してください。
1869 #include <psychlops.h>
1870 using namespace Psychlops;
1872 void psychlops_main() {
1873 Canvas sampleA(Canvas::fullscreen);
1874 sampleA.clear(Color::blue,Canvas::BACK);
1875 sampleA.flipAndWait(60);
1876 sampleA.clear(Color::cyan,Canvas::BACK);
1877 sampleA.flipAndWait(60);
1878 while(!Input::get(Keyboard::spc));
1882 <div title="2.1.3 Canvasのメンバ取得と変数の表示" modifier="Psychlops_DevelopperG" modified="200910080827" created="200708240242" changecount="29">
1883 <pre>!Canvas::getXXXX()
1884 Psychlopsではより複雑な描画に必要なCanvasの様々な画面情報を、Canvas::getXXXX()命令を使って取得します(XXXXにはそれぞれの情報に対応した名前が入ります)。
1885 |!Canvas::getXXXX()|getCenter()|画面の中心座標(x,y)を取得します|
1886 |~|getHcenter()|横方向の中心座標(x)を取得します|
1887 |~|getVcenter()|縦方向の中心座標(y)を取得します|
1888 |~|getHeight()|画面の高さ(ピクセル)を取得します|
1889 |~|getWidth()|画面の幅(ピクセル)を取得します|
1890 |~|getColorDepth()|カラーモード(ビット)を取得します|
1891 |~|getRefreshRate()|リフレッシュレート(Hz)を取得します|
1893 [img[image/canvasget.png]]
1895 !!Canvas::getXXXX()の書き方
1899 #include <psychlops.h>
1900 using namespace Psychlops;
1902 void psychlops_main() {
1904 Canvas sampleA(Canvas::fullscreen);
1905 sampleA.getCenter();
1906 sampleA.getHcenter();
1907 sampleA.getVcenter();
1908 sampleA.getHeight();
1910 sampleA.getColorDepth();
1911 sampleA.getRefreshRate();
1915 while(!Input::get(Keyboard::spc));
1918 このコードを実行するだけでは、取得結果を確認できません。
1919 次に取得結果を確認するため、画面に取得した変数の値を表示してみましょう。
1921 !Canvas::var()とCanvas::msg()
1922 取得した変数の値を画面に表示するにはCanvas::var()命令を使います。
1923 |!Canvas::var()|var(string str, double x, double y,[[Psychlops::Color]] col, bool fillForward)|
1924 |~|~|>|>|string str|画面表示をする変数を指定|
1925 |~|~|>|>|double x|画面表示をする座標(x)を指定|
1926 |~|~|>|>|double y|画面表示をする座標(y)を指定|
1927 |~|~|>|>|[[Psychlops::Color]] col|描画する点の色を指定(設定しない場合、白が設定される)|
1928 |~|~|~|bool fillForward|表示位置の指定|true |指定座標(x,y)から左部分に表示する|
1929 |~|~|~|~|~|false |指定座標(x,y)から右部分に表示する|
1930 |~|~|~|~|>|>|(指定しない場合、falseが設定される)|
1933 さらに、わかりやすくするためにラベルをつけましょう。
1934 定型の短い文章を画面上に表示する方法としてCanvas::msg()命令があります。
1935 |!Canvas::msg()|msg(char* string,double x,double y,[[Psychlops::Color]] col)|char* stringを座標(x,y)に指定色で表示します|
1936 |~|~|schar* string:画面表示をする文字列を指定|
1937 |~|~|double x:画面表示をする座標(x)を指定|
1938 |~|~|double y:画面表示をする座標(y)を指定|
1939 |~|~|[[Psychlops::Color]] col:描画する点の色を指定(設定しない場合、白が設定される)|
1940 |~|msg(string str,double x,double y,[[Psychlops::Color]] col)|string strを座標(x,y)に指定色で表示します|
1941 |~|~|string str:画面表示をする文字列を指定|
1942 |~|~|double x:画面表示をする座標(x)を指定|
1943 |~|~|double y:画面表示をする座標(y)を指定|
1944 |~|~|[[Psychlops::Color]] col:描画する点の色を指定(設定しない場合、白が設定される)|
1946 |[img[image/canvasvar.png]]|[img[image/canvasmsg.png]]
1951 #include <psychlops.h>
1952 using namespace Psychlops;
1955 double d1,d2,d3,x,y;
1956 Psychlops::Point point1;
1958 void psychlops_main() {
1960 Canvas sampleA(Canvas::fullscreen);
1962 point1=sampleA.getCenter();
1963 d1=sampleA.getHcenter();
1964 d2=sampleA.getVcenter();
1965 a1=sampleA.getHeight();
1966 a2=sampleA.getWidth();
1967 a3=sampleA.getColorDepth();
1968 d3=sampleA.getRefreshRate();
1973 sampleA.msg("getcenter_x:",50,200,Color::yellow);
1974 sampleA.var(x,200,200,Color::yellow,true);//getCenter:X
1975 sampleA.msg("getcenter_y:",50,250,Color::yellow);
1976 sampleA.var(y,200,250,Color::yellow,true);//getCenter:Y
1977 sampleA.msg("getHcenter:",50,300,Color::green);
1978 sampleA.var(d1,200,300,Color::green,true);//getHcenter
1979 sampleA.msg("getVcenter:",50,350,Color::green);
1980 sampleA.var(d2,200,350,Color::green,true);//getVcenter
1981 sampleA.msg("gettHeight:",50,400,Color::red);
1982 sampleA.var(a1,200,400,Color::red,false);//getHeight
1983 sampleA.msg("getWidth:",50,450,Color::red);
1984 sampleA.var(a2,200,450,Color::red,true);//getWidth
1986 sampleA.msg("getColorDepth:",250,200);
1987 sampleA.var(a3,500,200);//getColorDepth
1988 sampleA.msg("getRefreshRate:",250,250);
1989 sampleA.var(d3,500,250);//getRefreshRate
1992 while(!Input::get(Keyboard::spc));
1995 このコードを実行すると、宣言されたCanvasの各属性値が画面上に表示されます。</pre>
1997 <div title="2.2 デモ環境の表示" modifier="Psychlops_DevelopperG" modified="200908190221" created="200710220134" changecount="3">
1998 <pre>Independentクラスを使って、デモ環境を自動的に構成することが出来ます。
1999 以下では具体例を挙げて簡単なデモ環境の構築を説明します。
2001 1章で述べたやり方に従って、新規プロジェクトを作成すると、cppファイルの中に自動的にPsychlopsのデモプログラムが
2002 書き込まれています。このデモプログラムはIndependentクラスを用いた簡単なデモンストレーションのソースファイルになっています。ここでは、このデモプログラムを題材にIndependentを用いたデモ環境の構築について説明します。
2003 [[Psychlopsプログラムのデフォルトテンプレート]]
2005 [[2.2.1 プログラムの基本構造]]
2006 [[2.2.2 デモ環境に使う描画命令の設定]]
2007 [[2.2.3 デモ環境の実行]]</pre>
2009 <div title="2.2 図形描画のコマンド" modifier="Psychlops_DevelopperG" modified="200908190227" created="200708212305" changecount="14">
2010 <pre>[[2.1|2.1 描画領域の宣言]]節はCanvasそのものについて説明しました。
2011 本節ではCanvas上に点や線、図形を描画します。
2013 [[2.2.1 画面上に点を描画する-Pointクラス1-]]
2014 [[2.2.2 画面上に線を描画する]]
2015 [[2.2.3 画面上に四角形を描画する-Rectangleクラス1-]]
2016 [[2.2.4 画面上に円を描画する]]
2017 [[2.2.5 描画する図形の色を指定する-Colorクラス-]]
2018 [[2.2.6 画面上に縞を描画する-Rectangleクラス2-]]
2019 [[2.2.7 画面上に縞を描画する-Pointクラス2-]]
2020 [[2.2.8 運動する図形を描画する-Rectangleクラス3-]] </pre>
2022 <div title="2.2.1 プログラムの基本構造" modifier="Psychlops_DevelopperG" modified="200908190218" created="200711072356" changecount="1">
2023 <pre>このデモプログラムは大きく分けて2つの部分からなっています。
2025 void RectLuminance() {
2026 double rect_size = 100;
2027 double rect_lum = 0.5;
2028 double bg_lum = 0.2;
2030 Psychlops::Rectangle rect(rect_size,rect_size);
2034 Independent << rect_size | "Rect Size" | 1< rng< 500 | 10.0 | 2.0 ;
2035 Independent << rect_lum | "Rect Luminance" | 0.0<=rng<=1.0 | 0.1 | 0.05;
2036 Independent << bg_lum | "BG Luminance" | 0.0<=rng<=1.0 | 0.1 | 0.05;
2038 while(!Input::get(Keyboard::esc)) {
2039 Display::clear(Color(bg_lum));
2040 rect.resize(rect_size,rect_size);
2041 rect.display(rect_lum);
2048 void psychlops_main() {
2049 Canvas display(Canvas::fullscreen);
2052 p.setDesign(Procedure::DEMO);
2053 p.setProcedure(RectLuminance);
2059 ぱっと見てわかることは、Psychlops_mainの中に描画命令が存在しないことでしょう。実際の描画命令はPsychlops_main()の上にある~RectLuminance()という関数の中にかかれています。
2061 代わりに個々まで一度も触れられていないようなProcedureと言うクラスのインスタンスが宣言されて、これに対してsetDesign(), setProcedure(), run()といったメソッドがかかれています。
2062 実はデモを構築する時には、このProcedureクラスの詳細については理解する必要はありません。
2063 setProcedure()の引数としてかかれている文字列が、実際の描画を行う関数としてプログラム中にかかれていれば、後の部分は変更しなくてもデモ環境を構築することができます。
2064 つまり、もし実際の描画命令を書く関数の名前が~DemoDraw()であったとすると、この描画内容を使ったデモンストレーションを構築するためには、
2069 void psychlops_main() {
2070 Canvas display(Canvas::fullscreen);
2073 p.setDesign(Procedure::DEMO);
2074 p.setProcedure(DemoDraw);
2078 と書けば良いと言うことです。デフォルトのテンプレートと比較して、p.setProcedure()の行だけが異なっていることに注意してください。</pre>
2080 <div title="2.2.1 画面上に点を描画する-Pointクラス1-" modifier="Psychlops_DevelopperG" modified="200910080836" created="200708212310" changecount="29">
2082 Canvas上に点を描画するための命令です。
2083 Canvasに点や線等を描画するときに必要なのは「どこに」「何色」のものを描画するか、です。
2084 ここでの「どこが」が点を描画する「座標」、「何色」が「色の指定」となります。
2085 座標はCanvasの左上端が座標の基点(x=0,y=0)となっています。
2086 色の指定についての詳細は[[2.2.5 描画する図形の色を指定する-Colorクラス-]]を参照してください。ここでは、必要最小限の命令のみを使用・説明します。
2088 |!Canvas::pix()|pix(double x,double y,[[Psychlops::Color]] col) |>|!座標(x,y)に指定色の点を描画します<例1>|
2089 |~|~|double x|描画する点の座標軸xを指定|
2090 |~|~|double y|描画する点の座標軸yを指定|
2091 |~|~|[[Psychlops::Color]] col|描画する点の色を指定|
2092 |~|pix([[Psychlops::Point]] point,[[Psychlops::Color]] col) |>|!Point座標(x,y)に指定色の点を描画します^^*1^^<例4>|
2093 |~|~|[[Psychlops::Point]] point |座標(x,y)を指定|
2094 |~|~|[[Psychlops::Color]] col|描画する点の色を指定|
2095 |~|pix(int dotsCnt,double* xArray,double* yArray),[[Psychlops::Color]]* colArray)|>|!配列の座標(x[],y[])に指定色(配列を用いて複数色指定)の点を描画します<例2>|
2096 |~|~|int dotsCnt|描画する点の数を指定(後ろの引数の配列の個数を超えてはいけない)|
2097 |~|~|double* xArray|描画する点の座標軸xが格納された配列名(ポインタ)を指定|
2098 |~|~|double* yArray|描画する点の座標軸yが格納された配列名(ポインタ)を指定|
2099 |~|~|[[Psychlops::Color]]* colArray|描画する点の色が格納された配列名(ポインタ)を指定|
2100 |~|pix(int dotsCnt,double* xArray,double* yArray,[[Psychlops::Color]] col)|>|!配列の座標(x[],y[])に指定色(一色のみ)の点を描画します<例3>|
2101 |~|~|int dotsCnt|描画する点の数(配列数)を指定|
2102 |~|~|double* xArray|描画する点の座標軸xが格納された配列名(ポインタ)を指定|
2103 |~|~|double* yArray|描画する点の座標軸yが格納された配列名(ポインタ)を指定|
2104 |~|~|[[Psychlops::Color]] col|描画する点の色を指定|
2105 |~|pix(int dotsCnt,[[Psychlops::Point]]* pointArray,[[Psychlops::Color]]* colArray)|>|!配列Point座標(x[],y[])に指定色の点を描画します^^*1^^<例5>|
2106 |~|~|int dotsCnt|描画する点の数(配列数)を指定|
2107 |~|~|[[Psychlops::Point]]* pointArray |配列の座標(x[],y[])が格納された配列名(ポインタ)を指定|
2108 |~|~|[[Psychlops::Color]]* colArray|描画する点の色が格納された配列名(ポインタ)を指定|
2109 ^^*1 次のPoint::set()で説明します。^^
2111 [img[image/canvaspix.png]]
2114 座標(50,50)に青の点を描画します。
2117 #include <psychlops.h>
2118 using namespace Psychlops;
2120 void psychlops_main() {
2122 Canvas sampleA(Canvas::fullscreen);
2123 while(!Input::get(Keyboard::spc)){
2124 sampleA.pix(50,50,Color::blue);
2129 次は一命令で複数の異なる輝度の点を描画します。一つの命令を繰り返し実行する[[for文|1.6.4 for命令]]を使用しなくても複数個の点が描画される点に注目してください。
2132 #include <psychlops.h>
2133 using namespace Psychlops;
2135 //色の配列を宣言. 輝度値は0.0-1.0の範囲で指定
2136 Psychlops::Color colArray[3]={1.0,0.5,0.25};
2138 double xArray[3] = {50,100,150};
2140 double yArray[3] = {50,50,50};
2142 void psychlops_main() {
2144 Canvas sampleA(Canvas::fullscreen);
2145 sampleA.pix(3,xArray,yArray,colArray);
2147 while(!Input::get(Keyboard::spc));
2151 次は<例2>と同様に一命令で複数の点を描画しますが、全点の色は同一です。
2154 #include <psychlops.h>
2155 using namespace Psychlops;
2158 double xArray[3] = {50,100,150};
2160 double yArray[3] = {50,50,50};
2162 void psychlops_main() {
2164 Canvas sampleA(Canvas::fullscreen);
2165 sampleA.pix(3,xArray,yArray,Psychlops::Color(1.0,0.0,0.0));//3つめの引数は描画色に赤を指定
2167 while(!Input::get(Keyboard::spc));
2172 <例1>~<例3>で使用している[[Psychlops::Color]]型について簡単に説明します。
2173 PsychlopsではRGB,GRAY(輝度)の色を描画することが可能です。
2174 <例1>で青の点を描画するため「Color::blue」という命令を使用しています。
2175 Psychlopsではいくつかのよく使用する色をあらかじめ予約語として設定してあります。
2176 これらのプリセットされた色は「Color::色名」と記述することで、使用することができます。
2177 あらかじめプリセットされた色については[[Color::XXXX]]を参照してください。
2178 <例2>では輝度値を使用しています。
2179 輝度値は0.0(黒)~1.0(白)の範囲で指定されています。
2180 Psychlops::Color colArray[3]={1.0,0.5,0.25};
2181 の命令でそれぞれの点に輝度値が設定されました。
2182 <例3>では赤の点を描画しています。
2183 Psychlops::Color型の変数に3つの引数を指定したときは、それぞれの値が順に赤、緑、青の各画素の輝度の指定として解釈されます。
2184 red,green,blueはそれぞれ0.0~1.0の範囲で指定されます。
2185 この例では[[Psychlops::Color]](red=1.0,green=0.0,blue=0.0)と指定することで赤が設定されました。
2188 今まで座標はそれぞれx,y座標の変数を作成して値を設定していました。
2189 今回はPointクラスを使用して座標(x,y),ないし座標X,Yを指定し点を描画します。
2190 Point::set()を使用し座標を取得することでソースの簡略化がなされ
2193 |!Point::set()|set(double x, double y)|座標(x,y)の値を設定します|
2194 |~|~|double x:座標xを指定|
2195 |~|~|double y:座標yを指定|
2196 |~|setX(double val)|X座標の値を設定します|
2197 |~|~|double val :座標xを指定|
2198 |~|setY(double val)|Y座標の値を設定します|
2199 |~|~|double val :座標yを指定|
2202 座標(50,50)に青の点を描画します。
2203 <例1>との差分は、直接座標を記述するかPoint::set()で設定するか、だけです。
2206 #include <psychlops.h>
2207 using namespace Psychlops;
2210 Psychlops::Point point1;
2212 void psychlops_main() {
2214 Canvas sampleA(Canvas::fullscreen);
2217 sampleA.pix(point1,Psychlops::Color(0.0,0.0,1.0));
2219 while(!Input::get(Keyboard::spc));
2222 次はX,Y座標それぞれ座標を取得し、一命令で複数の点を描画します。
2223 <例2>との差分は、直接座標を記述するかPoint::set()で設定するか、だけです。
2226 #include <psychlops.h>
2227 using namespace Psychlops;
2230 Psychlops::Color colArray[3]={1.0,0.5,0.25};
2232 Psychlops::Point pointArray[3];
2234 void psychlops_main() {
2236 Canvas sampleA(Canvas::fullscreen);
2237 //座標(X[],Y[])にそれぞれ値((50,50),(100,50),(150,50)を設定
2238 for(int i=0; i<3; i++){
2239 pointArray[i].setX((i+1)*50);
2240 pointArray[i].setY(50);
2243 sampleA.pix(3,pointArray,colArray);
2245 while(!Input::get(Keyboard::spc));
2250 今度は点を画面中心に描画してみましょう。
2252 今までの命令では画面中心に点を描画することは難しいです。
2253 そこでPoint::centering()を使用します。
2254 この命令を使用することで、点を自動的に画面中心に移動します。
2256 |!Point::centering()|centering()|座標(x,y)の点を画面の中心に移動します|
2257 |~|centering(double h, double v)|任意の座標(h,v)に点を移動します|
2258 |~|~|double h:移動するx軸の値を指定|
2259 |~|~|double v:移動するy軸の値を指定|
2261 !Point::centering()の書き方
2264 #include <psychlops.h>
2265 using namespace Psychlops;
2268 Psychlops::Point point1;
2270 void psychlops_main() {
2272 Canvas sampleA(Canvas::fullscreen);
2277 sampleA.pix(point1,Color::red);
2279 while(!Input::get(Keyboard::spc));
2284 この命令もPoint::centering()と同様に点に対する移動命令です。
2285 元の座標(x,y)を基点に入力値(h,v)だけ移動します。
2287 |!Point::shift()|shift(double h, double v)|座標(X,Y)を(h,v)だけ移動します|
2288 |~|~|double h:右水平方向に移動する値を指定|
2289 |~|~|double v:下垂直方向に移動する値を指定|
2291 [img[image/pointshift.png]]
2293 !!Point::shift()の書き方
2294 <例6>を元に画面中央から(100,100)移動した青の点を描画します。
2297 #include <psychlops.h>
2298 using namespace Psychlops;
2301 Psychlops::Point point1,point2;
2303 void psychlops_main() {
2305 Canvas sampleA(Canvas::fullscreen);
2308 //座標(X,Y)を画面中心に移動し、更に(100,100)移動する
2309 point2.centering().shift(100,100);
2312 sampleA.pix(point1,Color::red);
2313 sampleA.pix(point2,Color::blue);
2315 while(!Input::get(Keyboard::spc));
2318 画面中央の赤色の点(point1)と画面中央の右下に描画された青色の点(point2)が確認できたでしょうか。
2321 <div title="2.2.2 デモ環境に使う描画命令の設定" modifier="Psychlops_DevelopperG" modified="200908190219" created="200711080010" changecount="2">
2322 <pre>さて、次に実際に描画を行っている関数~RectLuminance()について見てみましょう。[[2.2.1節|2.2.1 プログラムの基本構造]]で触れたとおり、デフォルトのデモプログラムでは、デモをする本体がpsychlops_mainとは別の関数になっています。
2325 void RectLuminance() {
2326 double rect_size = 100;
2327 double rect_lum = 0.5;
2328 double bg_lum = 0.2;
2330 Psychlops::Rectangle rect(rect_size,rect_size);
2334 Independent << rect_size | "Rect Size" | 1< rng< 500 | 10.0 | 2.0 ;
2335 Independent << rect_lum | "Rect Luminance" | 0.0<=rng<=1.0 | 0.1 | 0.05;
2336 Independent << bg_lum | "BG Luminance" | 0.0<=rng<=1.0 | 0.1 | 0.05;
2339 while(!Input::get(Keyboard::esc)) {
2340 Display::clear(Color(bg_lum));
2341 rect.resize(rect_size,rect_size);
2342 rect.display(rect_lum);
2349 現段階ではこのデモ環境に使う描画関数は引数をとることができません。同様に値を返すこともできないので、かならずvoid XXXXXXX()という形で宣言が行われることになります。[[2.2.1節|2.2.1 プログラムの基本構造]]でも触れたとおり、名前は何でもかまいません。
2351 次に中身を見てみると、はじめに変数の宣言とIndependentクラスへの変数の登録が行われています。
2352 この部分は、[[前節2.1|2.1 独立変数Independent]]で上げた例と全く同じです。良くわからなくなったら[[2.1節|2.1 独立変数Independent]]も参照してみてください。
2354 double rect_size = 100;
2355 double rect_lum = 0.5;
2356 double bg_lum = 0.2;
2359 Independent << rect_size | "Rect Size" | 1< rng< 500 | 10.0 | 2.0 ;
2360 Independent << rect_lum | "Rect Luminance" | 0.0<=rng<=1.0 | 0.1 | 0.05;
2361 Independent << bg_lum | "BG Luminance" | 0.0<=rng<=1.0 | 0.1 | 0.05;
2365 次の部分では、これまでにも見てきたようにwhile文を使ってループが設定され、その中に描画命令がかかれています。デモ実行は永久ループさせておきます。ここではESCキーを押すと終了するように設定されています。
2367 while(!Input::get(Keyboard::esc)) {
2368 Display::clear(Color(bg_lum));
2369 rect.resize(rect_size,rect_size);
2370 rect.display(rect_lum);
2375 ここでは、描画時に"Display::"命令が使われていることに注意してください。
2376 Canvasクラスの宣言はPsychlops_main()の中で行われるので、ここで宣言されたCanvas名displayを使うことはできません。たとえば、1行目はdisplay.clear(Color(bg_lum));と書いてしまうとコンパイル時にエラーとなってしまいます。そこでデフォルトのCanvasへの描画をするためのクラス"Display::"を用いて宣言されたCanvasへの描画を行っています。</pre>
2378 <div title="2.2.2 画面上に線を描画する" modifier="Psychlops_DevelopperG" modified="200910080837" created="200708230440" changecount="11">
2379 <pre>!Canvas::line()
2380 Canvas上に線を描画するためにはCanvas::line()命令を使います。
2382 |!Canvas::line()|line(double x1, double y1, double x2, double y2, [[Psychlops::Color]] col)|>|!開始座標(X1,Y1)から終端座標(X2,Y2)まで指定色の線を描画します|
2383 |~|~|double x1|描画する線の開始座標x1を指定|
2384 |~|~|double y1|描画する線の開始座標y1を指定|
2385 |~|~|double x2|描画する線の終端座標x2を指定|
2386 |~|~|double y2|描画する線の終端座標y2を指定|
2387 |~|~|[[Psychlops::Color]] col|描画する点の色を指定|
2388 |~|line([[Psychlops::Point]] point1, [[Psychlops::Point]] point2,[[Psychlops::Color]] col)|>|!開始Point座標(x1,x1)から終端Point座標(X2,Y2)まで指定色の線を描画します|
2389 |~|~|[[Psychlops::Point]] point1|開始座標(x1,y1)を指定|
2390 |~|~|[[Psychlops::Point]] point2|終端座標(x2,y2)を指定|
2391 |~|~|[[Psychlops::Color]] col|描画する点の色を指定|
2392 画面上に線を描画するためには、「どこから」「どこに」「何色」の線を引くのかの指定が必要です。
2393 「どこから」は開始座標(x1,y1)、「どこに」は終端座標(x2,y2)、「何色」は[[Psychlops::Color]]型で指定します。
2394 座標はCanvasの左上端が座標の基点(x=0,y=0)となっています。
2395 これらの座標はPoint型の変数を使用して指定することもできます。
2397 [img[image/canvasline.png]]
2399 !!Canvas::line()の書き方
2400 座標(100,100)から(300,300)まで赤い線を描画します。
2403 #include <psychlops.h>
2404 using namespace Psychlops;
2406 void psychlops_main() {
2408 Canvas sampleA(Canvas::fullscreen);
2409 sampleA.line(100, 100,300,300,Color::red);
2411 while(!Input::get(Keyboard::spc));
2414 次に[[Psychlops::Point]]型を使用して黄色の線を描画します。
2417 #include <psychlops.h>
2418 using namespace Psychlops;
2420 Psychlops::Point point1,point2;
2422 void psychlops_main() {
2424 Canvas sampleA(Canvas::fullscreen);
2426 point1.set(100,100);
2427 point2.set(300,300);
2429 sampleA.line(point1,point2,Color::yellow);;
2431 while(!Input::get(Keyboard::spc));
2435 <div title="2.2.3 デモ環境の実行" modifier="Psychlops_DevelopperG" modified="200908190221" created="200712061912" changecount="1">
2436 <pre>[[2.2.1節|2.2.1 プログラムの基本構造]]、[[2.2.2節|2.2.2 デモ環境に使う描画命令の設定]]で作成したプログラムを実行すると、デモ環境を使用したデモを使うことができます。
2438 画面左上にデモ環境コンソールが表示されています。コンソールには、Independentで指定した変数が列挙されています。
2440 [img[部分行列|image/console_1.png]]
2442 コンソールで明るく表示されている変数が変更対象となる(アクティブな)変数です。アクティブな変数は上下(↓↑)キーで切り替えることができます。
2444 [img[部分行列|image/console_2.png]]
2447 コンソールでは、左右キーで変数の値を変更することができます(減←、→増)。shiftキーを押しながら←→キーを押すと、変量が変わります。
2448 [img[部分行列|image/console_3.png]]
2450 [img[部分行列|image/console_4.png]]</pre>
2452 <div title="2.2.3 画面上に四角形を描画する-Rectangleクラス1-" modifier="Psychlops_DevelopperG" modified="200912010832" created="200708231915" changecount="24">
2453 <pre>PsychlopsでCanvasと並んで重要なクラスにRectangleクラスがあります。
2454 RectangleはCanvas上に四角形の領域を設定する命令です。
2455 この領域の設定はコピーやペーストのために使用することもできますし、領域自体を塗りつぶすことで四角形を表示することもできます。
2456 PsychlopsにおけるCanvas上の描画はほとんどこのRectangleクラスを使用して行うことになります。
2459 Rectangle::set()は四角形の領域を設定します。
2460 領域の設定は、四角形の左上座標(x1,y1)と四角形の右下座標(x2,y2)を指定することで行います。
2461 また色指定は[[Psychlops::Color]]型で指定します。
2463 |!Rectangle::set()|set(double width, double height)|>|!width×heightの四角形を設定します|
2464 |~|~|double width|描画する四角形の横幅|
2465 |~|~|double height|描画する四角形の縦幅|
2466 |~|set(double l, double t, double r, double b)|>|!左上座標(l,t)から右下座標(r,b)までの四角形を設定します|
2467 |~|~|double l|描画する四角形の左上座標x1を指定|
2468 |~|~|double t|描画する四角形の左上座標y1を指定|
2469 |~|~|double r|描画する四角形の右下座標x2を指定|
2470 |~|~|double b|描画する四角形の右下座標y2を指定|
2471 |~|set([[Psychlops::Point]] point1, [[Psychlops::Point]] point2)|>|!左上Point座標(x1,x1)から右下Point座標(X2,Y2)までの四角形を設定します|
2472 |~|~|[[Psychlops::Point]] point1|左上座標(x1,y1)を指定|
2473 |~|~|[[Psychlops::Point]] point2|右下座標(x2,y2)を指定|
2475 [img[image/rectset.png]]
2478 画面上に四角形を描画するにはCanvas::rect()命令を使用します。
2479 Rectangle::set命令で四角形の領域設定が終わったらCanvas::rect命令で描画処理を行います。
2480 この二つの命令は四角形の描画において対になっているので併せて覚えてください。
2482 |!Canvas::rect()|rect([[Psychlops::Rectangle]] rect, [[Psychlops::Color]] col)|>|!四角形を指定色で描画します|
2483 |~|~|([[Psychlops::Rectangle]] rect|描画する四角形を指定|
2484 |~|~|[[Psychlops::Color]] col|描画する四角形の色を指定|
2486 [img[image/canvasrect.png]]
2488 !!Rectangle::set()とCanvas::rect()の書き方
2489 Rectangleを宣言して、画面上に四角形を描画して見ましょう。
2490 Rectangleを縦横のサイズのみを用いて宣言したときには、左上の座標は(0,0)に設定されています。
2491 下の例では、200×100ピクセルの白色の四角形を画面左上隅に描画します。
2494 #include <psychlops.h>
2495 using namespace Psychlops;
2497 Psychlops::Rectangle rect1;
2499 void psychlops_main() {
2501 Canvas sampleA(Canvas::fullscreen);
2503 sampleA.rect(rect1,Color::white);
2505 while(!Input::get(Keyboard::spc));
2508 次に(50,100)と(100,150)を頂点とする大きさ50×50ピクセルの四角形を描画します。
2509 描画命令にはCanvas::rect()を使用し、色に黄色を指定しています。
2512 #include <psychlops.h>
2513 using namespace Psychlops;
2515 Psychlops::Rectangle rect1;
2517 void psychlops_main() {
2519 Canvas sampleA(Canvas::fullscreen);
2520 rect1.set(50,100,100,150);
2521 sampleA.rect(rect1,Color::yellow);
2523 while(!Input::get(Keyboard::spc));
2526 次に[[Psychlops::Point]]型を使用して頂点の座標を設定し、四角形を描画します。
2527 描画命令にはCanvas::rect()を使用し、色に緑を指定しています。
2530 #include <psychlops.h>
2531 using namespace Psychlops;
2533 Psychlops::Rectangle rect1;
2534 Psychlops::Point point1,point2;
2536 void psychlops_main() {
2538 Canvas sampleA(Canvas::fullscreen);
2540 point1.set(100,100);
2541 point2.set(300,300);
2542 rect1.set(point1,point2);
2543 sampleA.rect(rect1,Color::green);
2545 while(!Input::get(Keyboard::spc));
2549 ここまでは、基本的な四角形の設定について実行してきました。次に複数の同一四角形を描画します。
2550 複数の同一四角形を描画するのにそれぞれに対応したRectangleを宣言することもできますが、よりよい方法があります。
2551 一つだけRectangleを宣言して、これをシフトしながら描画する方法です。一つのはんこを紙の上に何度も押すようなものです。
2552 Psychlopsでは、Canvas::flip()が起こるまでは、画面上に描画内容が表示されませんから、このような方法を用いても複数の四角形が同時に画面に表示されます。
2553 <例1>の四角形を座標(0,0),画面中心,画面中心から(200,200)移動したところに四角形を描画してみましょう。
2556 #include <psychlops.h>
2557 using namespace Psychlops;
2559 Psychlops::Rectangle rect1,rect2,rect3;
2561 void psychlops_main() {
2563 Canvas sampleA(Canvas::fullscreen);
2568 rect3.centering().shift(200,200);
2569 sampleA.rect(rect1,Color::white);
2570 sampleA.rect(rect2,Color::white);
2571 sampleA.rect(rect3,Color::white);
2573 while(!Input::get(Keyboard::spc));
2578 <div title="2.2.4 画面上に円を描画する" modifier="Psychlops_DevelopperG" modified="200908190229" created="200708232150" changecount="15">
2579 <pre>Canvas上に円を描画します。
2580 座標はCanvasの左上端が座標の基点(x=0,y=0)となっています。
2583 Rectangleを使用してこれに内接するような円を描画することもできます。
2584 [[Rectangle.set()|2.2.3 画面上に四角形を描画する-Rectangleクラス1-]]命令で横軸と縦軸の直径を設定し、Canvas::oval()命令で円を描画します。
2585 色指定は[[Psychlops::Color]]型で指定します。
2587 |!Canvas::oval()|oval([[Psychlops::Rectangle]] rect, [[Psychlops::Color]] col)|[[Psychlops::Rectangle]] rectで指定された直径の円を指定色で塗りつぶし描画します|
2588 |~|~|[[Psychlops::Rectangle]] rect:描画する円の直径|
2589 |~|~|[[Psychlops::Color]] col:描画する点の円を指定|
2591 [img[image/canvasoval.png]]
2593 !!Canvas::oval()の書き方
2594 Rectangleを宣言し、直径100ピクセルの白い円を描画します。
2597 #include <psychlops.h>
2598 using namespace Psychlops;
2600 Psychlops::Rectangle rect1;
2602 void psychlops_main() {
2604 Canvas sampleA(Canvas::fullscreen);
2606 sampleA.oval(rect1,Color::white);
2608 while(!Input::get(Keyboard::spc));
2612 <div title="2.2.5 描画する図形の色を指定する-Colorクラス-" modifier="YourName" modified="200803100611" created="200708232340" changecount="24">
2613 <pre>今までCanvas、描画図形に色を塗りつぶしてきましたが、色の設定については詳しくは触れませんでした。
2614 ここでは色設定について詳しく説明します。
2618 描画対象はCanvas,点・線・四角形・多角形・円等多岐にわたります。
2619 色の設定はおおまかに2パターンあります。
2620 RGBと~GRAYLevel(輝度)です。
2621 RGBは色の三原色に基づいた赤・緑・青で構成されています。
2622 引数が3つ(4つ)あるColor::set()命令はRGB(A)指定になります。
2623 それぞれ値を0.0~1.0の範囲で持ち、3色のバランスで色を構成します。
2624 赤・緑・青の全ての値が0.0の場合は黒、全ての値が1.0の場合は白になります。
2625 4つめの引数(A)は色の透明度を決定します。0.0の場合は完全透過、1.0の場合は完全に不透明になります。省略したときはA=1.0(完全不透過)と解釈されます
2626 ~GRAYLevel(輝度)はグレースケールの輝度値を決定します。
2627 引数は1つで0.0~1.0の範囲を持ち、0.0の場合は黒、1.0の場合は白になります。
2630 白~黒を描画したい場合は~GRAYLevel
2632 |!Color::set()|set(double r, double g, double b, double a)|r,g,bと透明度aで構成されるカラー情報を設定します。|
2633 |~|~|double r:赤の輝度を指定。範囲は0.0~1.0|
2634 |~|~|double g:緑の輝度を指定。範囲は0.0~1.0|
2635 |~|~|double b:青の輝度を指定。範囲は0.0~1.0|
2636 |~|~|double a:透明度を指定。範囲は0.0~1.0 (0.0に近いほど透明度が高い)|
2637 |~|set(double r, double g, double b)|r,g,bで構成されるカラー情報を設定します。|
2638 |~|~|double r:赤の輝度を指定。範囲は0.0~1.0|
2639 |~|~|double g:緑の輝度を指定。範囲は0.0~1.0|
2640 |~|~|double b:青の輝度を指定。範囲は0.0~1.0|
2641 |~|set(double gray)|グレースケールの輝度を設定します。|
2642 |~|~|double gray: 輝度を指定。範囲は0.0~1.0|
2649 #include <psychlops.h>
2650 using namespace Psychlops;
2652 Psychlops::Rectangle rect1,rect2,rect3;
2653 Psychlops::Color col1,col2,col3;
2655 void psychlops_main() {
2657 Canvas sampleA(Canvas::fullscreen);
2658 sampleA.clear(Color(0.25,0.25,0.25));
2662 rect1.centering().shift(-100,0);
2664 rect3.centering().shift(100,0);
2666 col1.set(0.7,0.2,0.4);
2667 col2.set(0.5,0.8,1.0);
2668 col3.set(0.2,0.2,0.5);
2669 sampleA.rect(rect1,col1);
2670 sampleA.rect(rect2,col2);
2671 sampleA.rect(rect3,col3);
2673 while(!Input::get(Keyboard::spc));
2676 では次に透明度の値を変えたときにどう描画されるか確認しましょう。
2677 色は赤(固定)で透明度だけ0.0,0.5,1.0と変化させてみます。
2680 #include <psychlops.h>
2681 using namespace Psychlops;
2683 Psychlops::Rectangle rect1,rect2,rect3;
2684 Psychlops::Color col1,col2,col3;
2686 void psychlops_main() {
2688 Canvas sampleA(Canvas::fullscreen);
2689 sampleA.clear(Color(0.25,0.25,0.25));
2693 rect1.centering().shift(-100,0);
2695 rect3.centering().shift(100,0);
2697 col1.set(1.0,0.0,0.0,0.25);
2698 col2.set(1.0,0.0,0.0,0.5);
2699 col3.set(1.0,0.0,0.0,1.0);
2700 sampleA.rect(rect1,col1);
2701 sampleA.rect(rect2,col2);
2702 sampleA.rect(rect3,col3);
2704 while(!Input::get(Keyboard::spc));
2707 今度はグレースケールで描画してみましょう。
2710 #include <psychlops.h>
2711 using namespace Psychlops;
2713 Psychlops::Rectangle rect1,rect2,rect3;
2714 Psychlops::Color col1,col2,col3;
2716 void psychlops_main() {
2718 Canvas sampleA(Canvas::fullscreen);
2722 rect1.centering().shift(-100,0);
2724 rect3.centering().shift(100,0);
2729 sampleA.rect(rect1,col1);
2730 sampleA.rect(rect2,col2);
2731 sampleA.rect(rect3,col3);
2733 while(!Input::get(Keyboard::spc));
2737 !Canvas::setGammaValue()
2738 他の多くの描画ライブラリ同様、CRTの各画素における輝度レベルとPsychlopsで指定する輝度レベルは線形の関係を維持しません。
2739 これを簡易的に補正するために、R,G,Bの各色チャンネルについてGamma関数を用いた補正を行う方法が広く用いられています。
2740 Psychlopsでは、このGamma関数の係数を指定することで、Gamma補正された色を表示することが可能です。
2741 このための命令として、setGammaValue()命令があります。
2743 |!Canvas::setGammaValue()|setGammaValue(double Cr, double Cg, double Cb)|この命令以降実行されるPsychlops::Colorに対してGamma補正を自動的に行います|
2744 |~|~|Cr: R(赤)チャンネルのガンマ係数|
2745 |~|~|Cg: G(緑)チャンネルのガンマ係数|
2746 |~|~|Cb: B(青)チャンネルのガンマ係数|
2748 コード例は[[ガンマ補正|Tips: ガンマ補正]]を参照ください。
2751 <div title="2.2.6 画面上に縞を描画する-Rectangleクラス2-" modifier="Psychlops_Admin" modified="200709262314" created="200708242023" changecount="82">
2752 <pre>2.2.1節から2.2.5節まで基本的な描画記法を説明してきました。
2753 この節ではここまでのまとめとして、[[Psychlops::Rectangle]]型を使って「縞」を描画します。
2755 ^^ここは、ステップが多いので、かなりかみ砕いた解説をしています。縞の描画になれた方は使用する関数の説明とプログラム例のみをざっとご覧になられることをおすすめいたします。^^
2757 !縞を描いてみようStep1~グラデーションの四角形の描画~
2758 これから[[Psychlops::Rectangle]]型を用いて黒(0.0)と白(1.0)の縞を作ります。
2760 どういう風に縞を作成するか想像してみてください。
2761 白と黒の四角形を並べて描画するのでしょうか。
2763 しかし、これでは、単純な白と黒の繰り返しの縞しか描くことができません。
2765 もう少し複雑な縞を描くためにできそうなことを順に整理してみましょう。
2770 前の2つはこれまでに説明した[[Canvas::rect()]]命令を使えばできそうです。実際にはこの節では[[Canvas::rect()]]命令の代わりに''Rectangle::draw()''命令を用います。この命令は[[Canvas::rect()]]命令と全く同じ効果を持っているので、あなたの好みでどちらを使用してもかまいません。
2771 四角形の位置を移動させるには、''Rectangle::centering()'',''Rectangle::shift()''命令を使います。
2772 以下でまずこれらの命令の書式と使い方を簡単な例とともに説明します。
2774 !!Rectangle::centering()
2775 [[Psychlops::Rectangle]]型の図形を移動します。
2776 [img[image/rect_centering().png]]
2777 座標を指定する場合、その座標に図形の中心が設定されます。
2778 |!Rectangle::centering()|centering()|[[Psychlops::Rectangle]] rectを画面中心に移動します|
2779 |~|centering(double x, double y)|[[Psychlops::Rectangle]] rectを指定した座標(x,y)に移動します|
2780 |~|~|double x:移動する図形のx座標を指定|
2781 |~|~|double y:移動する図形のy座標を指定|
2782 |~|centering([[Psychlops::Point]] po)|[[Psychlops::Rectangle]] rectを指定したpoint座標(x,y)に移動します|
2783 |~|~|[[Psychlops::Point]] po:移動する図形の(x,y)座標を指定|
2784 |~|centering(Rectangle rect)|[[Psychlops::Rectangle]] rectを指定した[[Psychlops::Rectangle]] rectの中心座標(x,y)に移動します|
2785 |~|~|[[Psychlops::Rectangle]] rect :移動する図形の中心座標となる[[Psychlops::Rectangle]] rectを指定|
2787 !!Rectangle::shift()
2788 [[Psychlops::Rectangle]]型の図形を移動します。
2789 元の座標(x,y)を基点に入力値(h,v)だけ移動します。
2790 |!Rectangle::shift()|shift(double h, double v)|座標(X,Y)を(h,v)だけ移動します|
2791 |~|~|double h:入力値分、右水平方向に移動|
2792 |~|~|double v:入力値分、下垂直方向に移動|
2793 [img[image/rect_shift().png]]
2795 !!!Rectangle::centering()とRectangle::shift()の書き方
2796 200×100の四角形を中心座標から(-100,-200)だけ移動します。
2799 #include <psychlops.h>
2800 using namespace Psychlops;
2802 Psychlops::Rectangle rect1;
2804 void psychlops_main() {
2806 Canvas sampleA(Canvas::fullscreen);
2808 rect1.centering().shift(-100,-200);
2809 sampleA.rect(rect1,Color::white);
2811 while(!Input::get(Keyboard::spc));
2816 今まで図を描画する時、[[Psychlops::Canvas]]型の命令を使用しました。
2817 今回は描画命令にRectangle::draw()命令を使用します。
2818 |!Rectangle::draw()|draw()|対象の[[Psychlops::Rectangle]]型を描画します|
2819 |~|draw([[Psychlops::Color]] col)|対象の[[Psychlops::Rectangle]]型を[[Psychlops::Color]] colで塗りつぶし描画します|
2820 |~|~|[[Psychlops::Color]] col:描画する四角形の色を指定|
2822 !!Rectangle::draw()の書き方
2823 <例1>の四角形の色をRectangle::draw()命令を使用して「cyan」に塗りつぶします。
2826 #include <psychlops.h>
2827 using namespace Psychlops;
2829 Psychlops::Rectangle rect1;
2831 void psychlops_main() {
2833 Canvas sampleA(Canvas::fullscreen);
2835 rect1.centering().shift(-100,-200);
2836 rect1.draw(Color::cyan);
2838 while(!Input::get(Keyboard::spc));
2843 とりあえず四角形(w × h)を(200 × 100)の大きさに決めます。
2844 まず黒(0.0)から白(1.0)へ左から右にグラデーションする縞を描画してみましょう。
2848 #include <psychlops.h>
2849 using namespace Psychlops;
2851 Psychlops::Rectangle rect1;
2852 Psychlops::Color col1;
2854 void psychlops_main() {
2856 Canvas sampleA(Canvas::fullscreen);
2858 for(int i=0;i<200;i++){
2859 col1.set((double)i/200);
2860 rect1.centering().shift((double)-1/2*200+i,0);
2864 while(!Input::get(Keyboard::spc));
2868 このプログラムでは、200個の1 x 100ピクセルのRectangleを描画しています。
2869 左から数えてi個目の四角形の色をi / 200とすると、一番左i = 0では色は0.0(黒)となって、右に行くに従ってRectangleの輝度が大きくなるように設定できます。
2870 描画する場所は最終的な縞の中心が画面中央(Xc,Yc)に来るように計算します。
2872 描画する縞は200 × 100ピクセルなので、一番左のRectangleの中心位置は(Xc - 100,Yc)、一番右の位置は(Xc + 100,Yc)です。
2873 一般に幅wの縞を描くときには、一番左のRectangleの位置は(Xc - 1 / 2 * w ,Yc)、一番右の位置は(Xc + 1/2 * w ,Yc)になります。
2875 次にこの一番左のRectangleの位置を使って、i番目のRectangleの座標位置を考えてみます。
2876 i番目のRectangleは一番左のRectangleから見て+ i だけx座標を移動した位置にあるはずです。
2877 従って、i番目の座標は(Xc -1 / 2 * w + i ,Yc)となります。
2879 !!縞を描いてみようStep2~正弦波で縞を作成する~
2880 Step1で黒から白のグラデーション(色変化)の縞を作成しました。
2881 次にもう少し複雑な正弦縞を作成してみましょう。
2882 Step1から使えそうな要素を抜き出しましょう。
2883 *描画する四角形(w × h):(1 × hピクセルの四角形をw回描画する)
2884 *座標:Xc - 1 / 2 * w + i ,Yc
2885 [img[image/正弦波1.png]]
2889 [img[image/正弦波の一般式.png]]
2892 但しy>0であるので(扱うのが色のため)
2893 [img[image/正弦波2.png]]
2894 [img[image/色の式.png]]
2898 A:振動をC:コントラストに置き換えます。
2899 Michelsonコントラストの定義式を用います。
2900 [img[image/contrastEq.png]]
2901 C=2A / 2Lmean = A / Lmean
2903 今回は位相0の縞を描画するのでθ = 0
2906 平均輝度(Lmean): 0.5の縞を描画します。
2908 y=Asin{ ( 2π * x / λ ) + θ } + Lmean
2909 y=( C * Lmean ) sin ( 2π * i / λ ) + Lmean
2910 y=Lmean { C sin ( 2π * i / λ ) + 1 }
2911 i番目の色=Lmean { C sin ( 2π * i / λ ) + 1 }
2913 i番目の色=1.0 { 0.5 sin ( ( 2π * i / 60 ) + 1 }
2920 #include <psychlops.h>
2921 using namespace Psychlops;
2923 Psychlops::Rectangle rect1;
2924 Psychlops::Color col1;
2928 double lmean=0.5, contrast=1.0, theta=0;
2930 void psychlops_main() {
2932 Canvas sampleA(Canvas::fullscreen);
2933 rect1.set(1,height);
2934 for(int i=0;i<width;i++){
2935 col1.set(lmean*((contrast*sin((2*PI*i/lambda)+theta))+1));
2936 rect1.centering().shift((double)-1/2*width+i,0);
2940 while(!Input::get(Keyboard::spc));
2944 !!縞を描いてみようStep3~位相が異なる縞~
2945 Step2では位相0の縞を描画しました。
2946 位相に値を入れて位相90の縞を描画しましょう。
2947 わかりやすいように位相0の下に並べて描画します。
2950 #include <psychlops.h>
2951 using namespace Psychlops;
2953 Psychlops::Rectangle rect1,rect2;
2954 Psychlops::Color col1,col2;
2958 double lmean=0.5, contrast=1.0, theta1=0, theta2=90;
2960 void psychlops_main() {
2962 Canvas sampleA(Canvas::fullscreen);
2963 rect1.set(1,height);
2965 for(int i=0;i<width;i++){
2967 col1.set(lmean*((contrast*sin((2*PI*i/lambda)+theta1))+1));
2968 rect1.centering().shift((double)-1/2*width+i,0);
2971 col2.set(lmean*((contrast*sin((2*PI*i/lambda)+theta2))+1));
2972 rect2.centering().shift((double)-1/2*width+i,150);
2976 while(!Input::get(Keyboard::spc));
2979 !縞を描いてみようStep4~矩形波の縞を作成する~
2980 正弦波の縞の場合、白と黒の境目が曖昧です。
2981 境目をはっきり描画したい場合、矩形波の縞を描画します。
2986 [img[image/正弦波の一般式.png]]
2988 なのでこれを利用し、y > 0の場合はLmax , y < 0の場合はLminを描画するという判定分を追加することで矩形波の縞が描画できます。
2990 コントラスト、Lmeanを使ってLmax,Lmeanの値を求めておくと、
2991 Lmax = Lmean + Lmean * コントラストが求まります。
2992 Lmin = Lmean - Lmean * コントラストが求まります。
2993 これらを使用してコードを書いてみましょう。
2996 #include <psychlops.h>
2997 using namespace Psychlops;
2999 Psychlops::Rectangle rect1;
3004 double lmean=0.5, contrast=1.0, theta=0;
3006 void psychlops_main() {
3008 Canvas sampleA(Canvas::fullscreen);
3009 rect1.set(1,height);
3010 for(int i=0;i<width;i++){
3011 col1=(lmean*contrast*sin((2*PI*i/lambda)+theta));
3012 if(col1>0) col1=lmean+contrast*lmean;
3013 else col1=lmean-contrast*lmean;
3014 rect1.centering().shift((double)-1/2*width+i,0);
3018 while(!Input::get(Keyboard::spc));
3021 !!縞を描いてみようStep5~縞の角度を変えてみる~
3022 今まで縦(90°)の縞を描画してきました。
3023 次は横(0°)の縞を描画してみましょう。
3024 w × hの縦の縞を描画するために1 × hの四角形をw個並べました。
3026 w × hの横の縞を描画するためにw × 1の四角形をh個並べたら作れないでしょうか。
3029 *描画する四角形( w × h ) : ( w × 1の四角形をh回描画する)
3030 *座標X:Xc - 1 / 2 * w + i
3031 *色:y = A sin { ( 2π * x / λ ) + θ } + Lmean
3033 今回はX座標ではなくY座標を指定する必要があります。
3035 *座標Y:Yc - 1 / 2 * h + j (考え方はX座標と同じなので[[Step1|2.2.6 画面上に縞を描画する-Rectangleクラス2-]]参照のこと)
3037 これらから実際にコードを書いて試してみましょう。
3040 #include <psychlops.h>
3041 using namespace Psychlops;
3043 Psychlops::Rectangle rect1;
3044 Psychlops::Color col1;
3048 double lmean=0.5, contrast=1.0, theta=0;
3050 void psychlops_main() {
3052 Canvas sampleA(Canvas::fullscreen);
3054 for(int j=0;j<height;j++){
3055 col1.set(lmean*((contrast*sin((2*PI*j/lambda)+theta))+1));
3056 rect1.centering().shift(0,(double)-1/2*height+j);
3060 while(!Input::get(Keyboard::spc));
3064 矩形波の横の縞はStep4を参考に自分で試してみてください。</pre>
3066 <div title="2.2.7 画面上に縞を描画する-Pointクラス2-" modifier="YourName" modified="200802191327" created="200709252135" changecount="11">
3067 <pre>[[2.2.6節|2.2.6 画面上に縞を描画する-Rectangleクラス2-]]では[[Psychlops::Rectangle]]型を用いて縞を描画しました。
3068 この節では[[Psychlops::Point]]型を用いて縞を描画します。
3070 四角形(w × h)を描画する際、[[Psychlops::Rectangle]]型では1 × hの四角形をw個
3071 もしくはw × 1の四角形を h個並べて描画しました。
3072 [[Psychlops::Point]]型ではw × hの点を並べて描画します。
3074 !縞を描いてみようStep1~正弦縞を描画する~
3075 はじめに一番簡単な垂直方位の縞を書いてみましょう。
3078 *描画する縞のサイズ:w × h (1ピクセルの点をw×h回描画する)
3079 *縞の中心座標X:Xc - 1 / 2 * w + i
3080 *縞の中心座標Y:Yc - 1 / 2 * h + j
3081 *縞の式:L(x,y) = Lmean*(1+contrast*sin { ( 2π * x / λ ) + θ } )
3089 では以上をふまえてコードを書いてみましょう。
3092 #include <psychlops.h>
3093 using namespace Psychlops;
3095 Psychlops::Point p1;
3096 Psychlops::Color col1;
3100 double lmean=0.25, contrast=0.5, theta=0;
3102 void psychlops_main() {
3104 Canvas sampleA(Canvas::fullscreen);
3105 for(int i=0;i<width;i++){
3106 for(int j=0;j<height;j++){
3107 col1.set(lmean*((contrast*sin((2*PI*i/lambda)+theta))+1));
3108 p1.centering().shift((double)-1/2*width+i,(double)-1/2*height+j);
3109 sampleA.pix(p1,col1);
3113 while(!Input::get(Keyboard::spc));
3117 [img[image/grating01.png]]
3119 !縞を描いてみようStep2~斜めの縞を描画する~
3120 [[Psychlops::Rectangle]]型を使った書き方では方位90°の縞(縦)と0°の縞(横)のみを描画しました。
3121 Pointクラスを使った縞の描画では、これ以外の方位の縞も描画することができます。
3122 ここでは回転を使って方位45°の縞(斜め)を描画します。
3125 点(i, j)をθラジアン回転させて(xx,yy)に変換する式は以下の通りです。:
3127 xx = cosθ * i - sinθ * j
3128 yy = sinθ * i + cosθ * j
3130 この式を用いて、斜めの方位を持つ縞を描画してみましょう。
3131 θはラジアン単位です。度数法d(°)で値を指定したいときは下の式で変換した値を用います。
3133 θ ( rad ) = d° * π / 180°
3136 以上をから<例1>を修正して方位が45°の縞を描画してみましょう。
3139 #include <psychlops.h>
3140 using namespace Psychlops;
3142 Psychlops::Point p1;
3143 Psychlops::Color col1;
3147 double lmean=0.5, contrast=0.5, theta=0;
3151 void psychlops_main() {
3153 Canvas sampleA(Canvas::fullscreen);
3154 orientation=45*PI/180;
3155 for(int i=0;i<width;i++){
3156 for(int j=0;j<height;j++){
3158 xx=cos(orientation)*i-sin(orientation)*j;
3159 yy=sin(orientation)*i+cos(orientation)*j;
3161 col1.set(lmean*((contrast*sin((2*PI*yy/lambda)+theta))+1));
3162 p1.centering().shift((double)-1/2*width+i,(double)-1/2*height+j);
3163 sampleA.pix(p1,col1);
3167 while(!Input::get(Keyboard::spc));
3171 [img[image/grating45.png]]
3173 !縞を描いてみようStep3~交差した縞(Plaid)を描画する~
3174 では次にStep2で作成した45°の縞に135°の縞を交差させた縞を描画してみましょう。
3191 #include <psychlops.h>
3192 using namespace Psychlops;
3194 Psychlops::Point p1;
3195 Psychlops::Color col1;
3199 double lmean=0.25, contrast=0.5, theta=0;
3201 double orientation=45*PI/180;
3205 double lmean2=0.25, contrast2=0.25, theta2=0;
3207 double orientation2=135*PI/180;
3209 void psychlops_main() {
3211 Canvas sampleA(Canvas::fullscreen);
3213 for(int i=0;i<width;i++){
3214 for(int j=0;j<height;j++){
3216 xx=cos(orientation)*i-sin(orientation)*j;
3217 yy=sin(orientation)*i+cos(orientation)*j;
3218 xx2=cos(orientation2)*i-sin(orientation2)*j;
3219 yy2=sin(orientation2)*i+cos(orientation2)*j;
3221 col1.set( lmean*(1+contrast*sin(theta+(2*PI*yy/lambda)))+
3222 lmean2*(1+contrast2*sin(theta2+(2*PI*yy2/lambda2)))
3224 p1.centering().shift((double)-1/2*width+i,(double)-1/2*height+j);
3225 sampleA.pix(p1,col1);
3229 while(!Input::get(Keyboard::spc));
3233 [img[image/plaid45_135.png]]
3235 難しいと思われた方は、orientationやcontrast等のパラメタを変えて実行してみるとわかりやすいかもしれません。</pre>
3237 <div title="2.2.8 運動する図形を描画する-Rectangleクラス3-" modifier="Psychlops_DevelopperG" modified="200908190231" created="200709252136" changecount="4">
3238 <pre>この節では[[2.2.6節|2.2.6 画面上に縞を描画する-Rectangleクラス2-]]で作成した縞を動かします。
3240 [[2.2.6節|2.2.6 画面上に縞を描画する-Rectangleクラス2-]]では、位相を変えてみることで縞の位置を移動させました。
3241 「縞を動かす」ことは一見難しそうですが位相を変化させて描画することで
3245 下のコードを実行すると位相 = 0と位相 = 90の四角形がそれぞれ描画されます。
3247 #include <psychlops.h>
3248 using namespace Psychlops;
3250 Psychlops::Rectangle rect1,rect2;
3251 Psychlops::Color col1,col2;
3255 double lmean=0.5, contrast=1.0, theta1=0, theta2=90;
3257 void psychlops_main() {
3259 Canvas sampleA(Canvas::fullscreen);
3260 rect1.set(1,height);
3262 for(int i=0;i<width;i++){
3264 col1.set(lmean*((contrast*sin((2*PI*i/lambda)+theta1))+1));
3265 rect1.centering().shift((double)-1/2*width+i,0);
3268 col2.set(lmean*((contrast*sin((2*PI*i/lambda)+theta2))+1));
3269 rect2.centering().shift((double)-1/2*width+i,150);
3273 while(!Input::get(Keyboard::spc));
3277 ここで、描画する毎にθの値を変更して入力すれば四角形が常に移動しているように見えます。
3281 #include <psychlops.h>
3282 using namespace Psychlops;
3284 Psychlops::Rectangle rect1;
3285 Psychlops::Color col1;
3289 double lmean=0.5, contrast=1.0, theta=0;
3292 void psychlops_main() {
3294 Canvas sampleA(Canvas::fullscreen);
3295 while(!Input::get(Keyboard::spc)){
3296 rect1.set(1,height);
3297 for(int i=0;i<width;i++){
3298 col1.set(lmean*((contrast*sin((2*PI*i/lambda)+theta))+1));
3299 rect1.centering().shift((double)-1/2*width+i,0);
3307 <例2>は<例1>の1箇所だけを変えたコードです。
3308 どこが変わっているのか見つけてみてください。
3311 #include <psychlops.h>
3312 using namespace Psychlops;
3314 Psychlops::Rectangle rect1;
3315 Psychlops::Color col1;
3319 double lmean=0.5, contrast=1.0, theta=0;
3322 void psychlops_main() {
3324 Canvas sampleA(Canvas::fullscreen);
3325 while(!Input::get(Keyboard::spc)){
3326 rect1.set(1,height);
3327 for(int i=0;i<width;i++){
3328 col1.set(lmean*((contrast*sin((2*PI*i/lambda)+theta))+1));
3329 rect1.centering().shift((double)-1/2*width+i,0);
3337 答えはflip()です。<例1>ではflip(),<例2>ではflip(6)になっています。
3338 実行すると運動の速度が随分変化していることに気づくでしょう。
3339 flip(6)では6回リフレッシュが行われるまで描画したままの状態で待ち、次の垂直同期で画面が入れ替わります。
3340 60Hzの画面でflip(6)を実行する場合、100ミリsec描画したままの状態を維持できます。
3341 このようにして、flip(int waitframe)命令を活用すると運動刺激のリフレッシュレート(各フレームの長さ)や細かい描画の待ち時間を設定できます。
3343 flip(int waitframe)について詳しい説明は[[2.1.2節|2.1.2 Canvasの基本構造と操作]]を参照してください。</pre>
3345 <div title="2.3 文字列の描画" modifier="Psychlops_DevelopperG" modified="200912010809" created="200909300449" changecount="25">
3346 <pre>[[2.1.3節|2.1.3 Canvasのメンバ取得と変数の表示]]では簡単な文字列の表示について説明しました。
3347 この節ではより本格的な2バイト文字を用いた文字列の描画について取り扱います。
3349 Psychlopsで2バイト文字を描画するには2つの方法があります。
3350 いずれの方法もCanvas::msg()命令を使用します。
3352 |!Canvas::msg()|msg(char* string,double x,double y,[[Psychlops::Color]] col)|>|char* stringを座標(x,y)に指定色で表示します|
3353 |~|~|schar* string|画面表示をする文字列を指定|
3354 |~|~|double x|画面表示をする座標(x)を指定|
3355 |~|~|double y|画面表示をする座標(y)を指定|
3356 |~|~|[[Psychlops::Color]] col|描画する点の色を指定(設定しない場合、白が設定される)|
3357 |~|msg(string str,double x,double y,[[Psychlops::Color]] col)|>|string strを座標(x,y)に指定色で表示します|
3358 |~|~|string str|画面表示をする文字列を指定|
3359 |~|~|double x|画面表示をする座標(x)を指定|
3360 |~|~|double y|画面表示をする座標(y)を指定|
3361 |~|~|[[Psychlops::Color]] col|描画する点の色を指定(設定しない場合、白が設定される)|
3363 2つの方法とはこの命令を単体で使う方法とLettersと呼ばれるクラスを用いた方法です。
3364 前者の方法は簡便ですが、フォント指定などはできず、それほど美しく表示することはできません。
3365 後者の方法はフォント指定など文字列表示の方法を細かく指定できますが、文字列を表示する前にメモリ領域に文字列を描画しておく必要があります。
3368 [[2.3.1 Canvas::msg()命令単体での文字列描画]]
3369 [[2.3.2 Lettersクラスを用いた文字列描画]]
3370 また、これらのクラスの詳細については[[3.5節|3.5 文字列のレンダリングの仕様と関連情報]]をご覧ください。
3374 <div title="2.3.1 Canvas::msg()命令単体での文字列描画" modifier="Psychlops_DevelopperG" modified="200910260740" created="200910020306" changecount="13">
3375 <pre>!!Canvas::msg()命令単体での文字列描画
3376 まず、[[2.1.3節|2.1.3 Canvasのメンバ取得と変数の表示]]で説明したCanvas::msg()命令をそのまま用いるやり方について説明します。
3377 基本的には[[2.1.3節|2.1.3 Canvasのメンバ取得と変数の表示]]とほとんど同じですが、これまではここにアルファベットや記号、いわゆる1バイト文字でできた文字列を代入してきました。
3380 #include <psychlops.h>
3381 using namespace Psychlops;
3383 void psychlops_main() {
3384 Canvas sampleA(Canvas::fullscreen);
3386 sampleA.msg("A test of Letter Drawing", SampleA.getCenter());
3389 while(!Input::get(Keyboard::spc));
3393 ここで、表示したい文字列がひらがな・カタカナなどの2バイト文字とか全角文字と呼ばれる文字でできている場合は「”」(ダブルクォーテーション)の前に大文字のLを入れます。以下の例を見てください。
3397 #include <psychlops.h>
3398 using namespace Psychlops;
3400 void psychlops_main() {
3401 Canvas sampleA(Canvas::fullscreen);
3403 sampleA.msg(L"日本語の表示", SampleA.getCenter());
3406 while(!Input::get(Keyboard::spc));
3411 sampleA.msg(L"日本語の表示", SampleA.getCenter());
3413 の命令を見てわかるとおり、"日本語の表示" という文字列のまえに大文字のLが入っています。
3416 さらにこの部分に文字変数を入れたい場合はどうすればよいでしょうか。
3417 普通のプログラムで文字列によく使うstring, char型を使ってもコンパイルできません。
3418 このような場合はstring型を拡張したwstring型の変数を宣言してCanvas::msg()命令に代入します。
3421 |!Canvas::msg()|msg(std::wstring string,double x,double y,[[Psychlops::Color]] col)|char* stringを座標(x,y)に指定色で表示します|
3422 |~|~|string str:画面表示をする文字列を指定|
3423 |~|~|double x:画面表示をする座標(x)を指定|
3424 |~|~|double y:画面表示をする座標(y)を指定|
3425 |~|~|[[Psychlops::Color]] col:描画する点の色を指定(設定しない場合、白が設定される)|
3429 #include <psychlops.h>
3430 using namespace Psychlops;
3432 void psychlops_main() {
3434 Canvas sampleA(Canvas::fullscreen);
3436 wstr=L"日本語の表示";
3437 sampleA.msg(wstr, SampleA.getCenter());
3440 while(!Input::get(Keyboard::spc));
3444 ワイド文字列についてはいくつか知っておいた方が便利なことがあります。これらの詳細な情報については[[3.5節|3.5 文字列のレンダリングの仕様と関連情報]]をご覧ください。
3448 <div title="2.3.2 Lettersクラスを用いた文字列描画" modifier="Psychlops_DevelopperG" modified="200910260748" created="200910020318" changecount="34">
3449 <pre>Canvas命令単体での文字列描画は簡単ですが、フォントがぎざぎざしていたりしてあまり美しくは表示されません。
3450 また、もう少し大きな文字を表示したい場合やイタリック体で文字を表示したい場合にはこの方法では対応できません。
3451 この様な複雑な指定をして文字列を描画したい場合には事前にメモリに文字列を描画して、これを画面にコピーするという方法を行います。本や大量の部数の文書を作るときに、活字を組んで版をつくり、インクで紙に刷るのと同じ事です。
3452 この事前のメモリ領域への文字列描画のためのクラスとして、[[Letters]], [[Font]]クラスが用意されています。
3454 この方法による文字列描画の流れを簡単に説明すると以下のようになります
3455 # [[Font]]クラスを用いてフォントを指定する
3456 # [[Letters]]クラスを用いてメモリに文字列を描画
3457 # [[Canvas::msg()命令を用いてメモリ内の文字列を画面に描画]]
3458 以下でそれぞれの段階の具体的な方法について説明します。
3460 ![[Font]]クラスを用いたフォントの指定
3461 Psychlopsにおけるフォントの指定は、[[Font]]クラスの変数を宣言し、これを引数として代入することによって行います。
3462 Fontの宣言は以下の書式に従って行います。
3463 |!Fontの宣言|font(double size, int weight, Style style, std::wstring family);|!フォントを指定します。|>|
3464 |~|~|double size|フォントサイズ(pixel)を指定|
3465 |~|~|int weight|線幅を指定 <br> (normal_style (400) / bold (700) : 省略可能)|
3466 |~|~|Style style|斜体の有無を指定 <br> (normal_style / italic : 省略可能)|
3467 |~|~|std::wstring family|フォント名を指定 <br> (省略可能)|
3471 Font font1(24, Font::bold, Font::italic, L"ArialMT");
3474 Font font2(12, Font::bold, Font::normal_style, L"メイリオ");
3477 Font font3(18, Font::normal_weight, Font::normal_style, L"ヒラギノ角ゴ Pro");
3480 第2引数は文字の太さ、第3引数で斜体の有無を選択します。
3481 第4引数はフォントの名前を2重引用符(")でくくって指定します。
3484 Canvas.msg関数などで使われるデフォルトフォントを変更するには、Font::default_font変数に新しいフォント構造体を指定します。
3486 Font::default_font = Font(L"Arial", 20);
3489 フォントはインストールされていなければその環境標準のものになります。一般的にWindowsとMacOSXでは同じ名前のフォントはありませんので、注意してください。
3491 ![[Letters]]クラスによる文字列のプリレンダリングと描画
3492 次のステップでは、上で用意したフォントを使って、文字列の元版(レンダリング済みの文字列、といいます)をメモリ内に作成します。
3493 これにはLettersクラスを使用します。
3495 |!Lettersの宣言|Letters(std::wstring str, Font font );|!文字列を指定されたフォントでレンダリングします。|>|
3496 |~|~|std::wstring str|描画する文字列を指定|
3497 |~|~|Font font|フォントを指定 <br> (省略可能)|
3499 ここでも実際の例をいくつかあげてみましょう。
3501 Letters let1(L"Space Barを押すと文字が変わります", Font(20, Font::bold));
3504 Letters let2(L"矢印キーを押してください。配置が変わります。");
3507 Letters let3(L"Font \"Arial\" have only Latain characters. 日本語フォントが必要です.", font1);
3510 文字列の内容をプログラム内で変更したいときなど、宣言とは違うところで文字列をレンダリングしたいときには、Letters::string()命令を使います。
3512 |!Letters::string()|string(std::wstring str);|!文字列を指定されたフォントでレンダリングします。|>|
3513 |~|~|std::wstring str|描画する文字列を指定|
3515 このとき、font指定はLetters::font()命令を使用して別に行う事になりますが、何も指定せずデフォルトのフォントでレンダリングする事もできます。
3517 |!Letters::font()|font(Font font);|!文字列を指定されたフォントでレンダリングします。|>|
3518 |~|~|std::wstring str|描画する文字列を指定|
3519 |~|~|Font font|フォントを指定 |
3523 Letters let1(L"Space Barを押すと文字が変わります", Font(20, Font::bold));
3525 をこれらの命令を使って書き直してみるとこの様になるでしょう。
3528 let1.string(L"Space Barを押すと文字が変わります");
3529 let1.font(Font(20, Font::bold));
3534 ! Canvas::msg()命令を用いてメモリ内の文字列を画面に描画
3535 ここまでの段階で、指定されたフォントを使って表示したい文字列の元版ができあがりました。実際に画面にこれを表示するにはCanvas::msg()命令を使います。
3537 |!Canvas::msg()|msg(Letters let, double x, double y, Color col, align, algn );|!文字列を指定されたフォントでレンダリングします。|>|
3538 |~|~|Letters let|描画するレンダリング済み文字列を指定|
3539 |~|~|double x|文字列描画の水平位置を指定|
3540 |~|~|double y|文字列描画の垂直位置を指定|
3541 |~|~|Color col|文字列の描画色を指定 <br> (省略可能)|
3542 |~|~|align, algn|文字詰めの方法を指定 <br> (省略可能)|
3545 Letters let1(L"Space Barを押すと文字が変わります", Font(20, Font::bold));
3546 Display::msg(let1, 100, 100);
3550 ! Letters::draw()命令を用いてメモリ内の文字列を画面に描画
3551 もう一つ同じ目的の命令としてLetters::draw()命令があります。
3552 LettersクラスにはRectangle型に対して使用できるcentering(), shift()などの命令が使えます。
3553 これらの命令を使って同じ文字列を異なる位置に簡単に描画することができます。
3554 |!Letters::centering()|centering(double x, double y)|!レンダリング済みの文字列の描画位置をその中央の座標で指定する。|>|
3555 |~|~|double x|センタリングの水平位置を指定<br> (省略可能)|
3556 |~|~|double y|センタリングの垂直位置を指定<br> (省略可能)|
3557 |~|~|>|>|これらの値が省略されるときは、画面中央に描画位置が設定される|
3559 |!Letters::shift()|shift(double x, double y)|!レンダリング済みの文字列の描画位置を現在の指定位置から移動する。|>|
3560 |~|~|double x|水平方向の移動量を指定|
3561 |~|~|double y|垂直方向の移動量を指定|
3563 |!Letters::draw()|draw(Color col)|!レンダリング済みの文字列を描画する|>|
3564 |~|~|double x|水平方向の移動量を指定|
3565 |~|~|double y|垂直方向の移動量を指定|
3566 |~|~|Color col|文字列の描画色を指定 <br> (省略可能)|
3570 Letters let1(L"Space Barを押すと文字が変わります", Font(20, Font::bold));
3571 let1.centering(Mouse::x, Mouse::y+30).draw(Color::green);
3577 ここまでをまとめて、文字列描画の簡単なプログラム例を作ってみましょう。
3579 #include <psychlops.h>
3580 using namespace Psychlops;
3582 void psychlops_main() {
3584 Canvas sampleA(Canvas::fullscreen); //描画用windowの取得
3586 Letters let1(L"Space Barを押すと文字が変わります", Font(20, Font::bold));
3588 let2.string(L"文字が変わりました");
3589 let2.font(Font(20, Font::bold));
3591 sampleA.clear(Color(0.0));
3592 sampleA.msg(let1, 100, 100);
3594 while(!Input::get(Keyboard::spc));
3596 sampleA.clear(Color(0.5));
3597 let2.centering().draw(Color::red);
3598 while(!Input::get(Keyboard::spc));
3604 <div title="3. 複雑な描画を行う1(オフスクリーンへの描画)" modifier="Psychlops_DevelopperG" modified="200910260736" created="200708211945" changecount="2">
3605 <pre>[[3.1 オフスクリーン描画の基本]]
3606 [[3.1.1 オフスクリーン領域の宣言]]
3607 [[3.1.2 オフスクリーン上に点・四角形を描画する]]
3608 [[3.1.3 オフスクリーン上の任意の位置に描画する]]
3610 [[3.2 オフスクリーンを用いた描画例-ランダムドットパターン-]]
3612 [[3.3.1 画像ファイルを保存する]]
3613 [[3.3.2 画像ファイルを読み込み表示する]]
3614 [[3.3.3 複数の画像ファイルからアニメーションを作成する]]
3615 [[3.4 Canvasのオフスクリーンへの取り込み]]
3616 [[3.5 文字列のレンダリングの仕様と関連情報]]</pre>
3618 <div title="3.1 オフスクリーン描画の基本" modifier="Psychlops_DevelopperG" modified="200908190234" created="200708292225" changecount="7">
3619 <pre>[[3.1.1 オフスクリーン領域の宣言]]
3620 [[3.1.2 オフスクリーン上に点・四角形を描画する]]
3621 [[3.1.3 オフスクリーン上の任意の位置に描画する]]
3622 [[3.1.4 発展的な内容]] </pre>
3624 <div title="3.1.1 オフスクリーン領域の宣言" modifier="Psychlops_Admin" modified="200709142108" created="200708292249" changecount="31">
3627 第2章では直にCanvasに描画してきました。第3章ではオフスクリーンへ領域の描画ないし取扱いをします。
3628 Canvasは裏表2枚しかないので、複雑な動画を描画するには不便です。
3629 裏画面に描画が終わるまでflip()を実行できないので(もし強引にflip()を実行してしまったら、描画途中の中途半端なイメージが表示されてしまいます)、複雑な動画ではアニメーションの質が下がってしまいます。
3630 このような動画の描画や、とても時間のかかる内容の描画を行うときにはオフスクリーン領域(描画バッファともいいます)と呼ばれる特殊な描画領域を用います。
3631 このオフスクリーン領域はメモリ上に確保された画像ファイルのようなものだと考えるとよいかもしれません。
3632 この画像ファイルに複雑な描画内容を保存しておいて、後で紙芝居のように画面に表示することができれば、複雑な内容の描画も素早く行えるわけです。^^*1^^
3633 Psychlopsではこのオフスクリーン領域をImageというクラスを用いて操作します。
3635 ^^ *1もう少し正しく表現するなら、オフスクリーン領域とはコンピュータのメインメモリ(RAM 1GBなどという”あれ”のことです)内に確保された描画内容の保存場所(描画用バッファ)のことです。^^
3638 ここではImageとCanvasの違いについて詳しく説明することはしませんが、これら2つのクラスは物理的に保管場所が違うということは頭に置いておいてください。
3639 Canvasの内容は画面上に素早く提示することのできる位置に保管されています。flip()命令を実行すると正確なタイミングで描画が起こるわけです。
3640 これに対して、ImageはCanvasよりも遙かに大量のデータを保管することができますが、画面上に描画内容を提示するには、ある程度の時間が必要です。
3641 これは、Canvas(の保管場所)にImageの内容を転送する必要があるからです。
3642 より詳しい内容については[[Tips: Image使用上の注意]]を参考にしてください。
3643 [img[image/オフスクリーン領域.png]]
3647 宣言時には取り扱いたい画像の大きさやカラーモード、透明度の指定が必要となってきます。
3648 Image()のように宣言のみの場合は''Image::set()''命令で指定します。
3650 |!Imageの宣言|Image()|オフスクリーン領域を宣言します|
3651 |~|Image([[Psychlops::Rectangle]] rec,PixelComponentsCnt pixcompcntval)|指定された[[Psychlops::Rectangle]] 型と同じ大きさのオフスクリーン領域を宣言します|
3652 |~|~|[[Psychlops::Rectangle]] rect :[[Psychlops::Rectangle]] 型を指定|
3653 |~|~|PixelComponentsCnt pixcompcntval :[[カラーモード]]を指定。省略した場合はRGBが指定される|
3654 |~|Image(long x, long y, PixelComponentsCnt pixcompcntval)|指定されたx×yと同じ大きさのオフスクリーン領域を宣言します|
3655 |~|~|long x :オフスクリーン領域の横幅を指定|
3656 |~|~|long y :オフスクリーン領域の縦幅を指定|
3657 |~|~|PixelComponentsCnt pixcompcntval :[[カラーモード]]を指定。省略した場合はRGBが指定される|
3660 オフスクリーン領域の宣言時に何の指定もしなかった場合、Image::set()命令を使用します。
3661 |!Image::set()|set([[Psychlops::Rectangle]] rect, PixelComponentsCnt pixcompcntval)|指定された[[Psychlops::Rectangle]] 型と同じ大きさのオフスクリーン領域を設定します|
3662 |~|~|[[Psychlops::Rectangle]] rect :[[Psychlops::Rectangle]] 型を指定|
3663 |~|~|PixelComponentsCnt pixcompcntval :[[カラーモード]]を指定。省略した場合はRGBが指定される|
3664 |~|set(long x, long y, PixelComponentsCnt pixcompcntval)|指定されたx×yと同じ大きさのオフスクリーン領域を設定します|
3665 |~|~|long x :オフスクリーン領域の横幅を指定|
3666 |~|~|long y :オフスクリーン領域の縦幅を指定|
3667 |~|~|PixelComponentsCnt pixcompcntval :[[カラーモード]]を指定。省略した場合はRGBが指定される|
3672 #include <psychlops.h>
3673 using namespace Psychlops;
3675 Psychlops::Rectangle rect1(200,100);
3676 Psychlops::Image Noise1(rect1,Image::GRAY);
3678 void psychlops_main() {
3680 Canvas sampleA(Canvas::fullscreen);
3682 while(!Input::get(Keyboard::spc));
3686 次はImageの宣言と[[Image::set()]]命令を組み合わせたコードです。
3689 #include <psychlops.h>
3690 using namespace Psychlops;
3692 Psychlops::Image Noise1;
3693 long Width=200, Height=100;
3695 void psychlops_main() {
3697 Canvas sampleA(Canvas::fullscreen);
3698 Noise1.set(Width,Height,Image::RGBA);
3700 while(!Input::get(Keyboard::spc));
3703 これらのコードは実行しても黒い画面が表示されるだけでした。
3704 次の3.1.2節で実際にImage上に描画を行います。</pre>
3706 <div title="3.1.2 オフスクリーン上に点・四角形を描画する" modifier="Psychlops_DevelopperG" modified="200908190233" created="200709032203" changecount="8">
3707 <pre>[[3.1.1|3.1.1 オフスクリーン領域の宣言]]節ではImageの宣言(領域確保)のみを行っていたので、コードを実行しても黒い画面が表示されるだけでした。
3708 本節では実際にImage上に点・四角形を描画して、これを画面に表示してみます。
3709 Imageクラスの命令(メソッド)はCanvasに対する命令とほとんど変わりません。
3712 オフスクリーン領域に点を描画するための命令です。
3713 [[Canvas::pix()|2.2.1 画面上に点を描画する-Pointクラス1-]]命令同様に点を描画するために「座標」と「色の指定」を行います。
3714 色の指定についての詳細は[[2.2.5 描画する図形の色を指定する-Colorクラス-]]を参照してください。
3716 |!Image::pix()|pix(int x, int y, [[Psychlops::Color]] col)|座標(x,y)に[[Psychlops::Color]] col色の点を描画します|
3717 |~|~|int x: 描画する点の座標軸xを指定|
3718 |~|~|int y: 描画する点の座標軸yを指定|
3719 |~|~|[[Psychlops::Color]] col:描画する点の色を指定|
3720 |~|pix(double x, double y, [[Psychlops::Color]] col)|座標(x,y)に[[Psychlops::Color]] col色の点を描画します|
3721 |~|~|double x: 描画する点の座標軸xを指定|
3722 |~|~|double y: 描画する点の座標軸yを指定|
3723 |~|~|[[Psychlops::Color]] col:描画する点の色を指定|
3724 |~|pix([[Psychlops::Point]] po, [[Psychlops::Color]] col)|Point座標(x,y)に[[Psychlops::Color]] col色の点を描画します|
3725 |~|~|[[Psychlops::Point]] po :描画する点の座標(x,y)を指定|
3726 |~|~|[[Psychlops::Color]] col:描画する点の色を指定|
3729 実際にオフスクリーン領域に描画する際の命令です。
3730 この命令を記述しないといくら''Image::pix()''命令やこの後出てくる''Image::rect()''命令を記述しても描画されませんので注意してください。
3732 |!Image::draw()|draw()|指定したオフスクリーンを描画します|
3733 |~|draw(double left, double top)|指定したオフスクリーン領域を(left,top)を中心とした座標に設定します|
3734 |~|~|double left: 描画するオフスクリーン領域の座標軸xを指定|
3735 |~|~|double top: 描画するオフスクリーン領域の座標軸yを指定|
3737 !!Image::pix(),Image::draw()の書き方
3738 100×100のImageに点を描画します。
3741 #include <psychlops.h>
3742 using namespace Psychlops;
3744 double width=100,height=100;
3745 Psychlops::Rectangle rect1(width,height);
3746 Psychlops::Color col;
3747 Psychlops::Image Noise1(rect1,Image::GRAY);
3749 void psychlops_main() {
3751 Canvas sampleA(Canvas::fullscreen);
3752 //水平方向に2,垂直方向に5間隔の点を描画する。
3753 for(int x=0; x<width; x+=2){
3754 for(int y=0; y<height; y+=5){
3756 Noise1.pix(x,y,col);
3761 while(!Input::get(Keyboard::spc));
3766 オフスクリーン領域に四角形を描画するための命令です。
3767 実際の描画処理は''Image::draw()''命令で行われます。
3768 色の指定についての詳細は[[2.2.5 描画する図形の色を指定する-Colorクラス-]]を参照してください。
3770 |!Image::rect()|rect([[Psychlops::Rectangle]] rect, [[Psychlops::Color]] col)|[[Psychlops::Rectangle]]型の四角形を[[Psychlops::Color]] col色で描画します|
3771 |~|~|[[Psychlops::Rectangle]] rect : 描画する四角形を指定|
3772 |~|~|[[Psychlops::Color]] col:描画する四角形の色を指定|
3775 100×100のImageに青の四角形を描画します。
3778 #include <psychlops.h>
3779 using namespace Psychlops;
3781 Psychlops::Rectangle rect1(100,100);
3782 Psychlops::Image Noise1(rect1);
3784 void psychlops_main() {
3786 Canvas sampleA(Canvas::fullscreen);
3787 Noise1.rect(rect1,Color::blue);
3790 while(!Input::get(Keyboard::spc));
3794 <div title="3.1.3 オフスクリーン上の任意の位置に描画する" modifier="Psychlops_DevelopperG" modified="200908190258" created="200709042020" changecount="16">
3795 <pre>前節でオフスクリーン上に図形の描画を行いました。
3796 描画位置の指定がない場合、オフスクリーン領域は常に画面の左上に設定されます。
3797 この節ではオフスクリーン領域を任意の場所に移動します。
3798 2.2節ではそれぞれ[[Psychlops::Point型の移動|2.2.1 画面上に点を描画する-Pointクラス1-]]と[[Psychlops::Rectangle型の移動|2.2.6 画面上に縞を描画する-Rectangleクラス2-]]の移動について説明しました。
3799 オフスクリーン領域の移動も上記の型と同様の命令文を使用します。
3802 [[Psychlops::Image]]型の領域を移動します。
3803 座標を指定する場合、その座標に領域の中心が設定されます。
3804 |!Image::centering()|centering()|[[Psychlops::Image]]型の領域を画面中心に移動します|
3805 |~|centering(double x, double y)|[[Psychlops::Image]]型の領域の中心を指定した座標(x,y)に移動します|
3806 |~|~|double x:移動する領域のx座標を指定|
3807 |~|~|double y:移動する領域のy座標を指定|
3809 !!Image::centering()の書き方
3810 前節の<例1>で作成したImageを画面中心に移動します。
3813 #include <psychlops.h>
3814 using namespace Psychlops;
3816 double width=100,height=100;
3817 Psychlops::Rectangle rect1(width,height);
3818 Psychlops::Color col;
3819 Psychlops::Image Noise1(rect1,Image::GRAY);
3821 void psychlops_main() {
3823 Canvas sampleA(Canvas::fullscreen);
3826 //水平方向に2,垂直方向に5間隔の点を描画する。
3827 for(int x=0; x<width; x+=2){
3828 for(int y=0; y<height; y+=5){
3830 Noise1.pix(x,y,col);
3835 while(!Input::get(Keyboard::spc));
3840 [[Psychlops::Image]]型の領域を移動します。
3841 元の座標(x1,y1)を基点に入力値(x,y)だけ移動します。
3842 |!Image::shift()|shift(double h, double v)|座標(X,Y)を(h,v)だけ移動します|
3843 |~|~|double x:入力値分、右水平方向に移動|
3844 |~|~|double y:入力値分、下垂直方向に移動|
3846 !!Image::shift()の書き方
3847 前節の<例2>で作成したImageを座標(100,50)分移動します。
3850 #include <psychlops.h>
3851 using namespace Psychlops;
3853 Psychlops::Rectangle rect1(100,100);
3854 Psychlops::Image Noise1(rect1);
3856 void psychlops_main() {
3858 Canvas sampleA(Canvas::fullscreen);
3859 Noise1.shift(100,50);
3860 Noise1.rect(rect1,Color::blue);
3863 while(!Input::get(Keyboard::spc));
3867 <div title="3.1.4 発展的な内容" modifier="Psychlops_Admin" created="200709252141" changecount="1">
3868 <pre>この節ではは少し発展的で込み入った内容を取り扱います。ここまでの命令を使ってとりあえず自分の目的が達成できている場合は無視しても構いません。
3869 複雑で大きなサイズのイメージを高速でどうしても表示したい時にだけ、読んでみてください。
3872 3.1.1節でサイズが大きなImageをCanvasに転送するには時間がかかるという説明をしました。
3874 転送するのにかかる時間の総和を短縮することはできませんが、PsychlopsはImageをCanvasへより早く転送することのできるメモリ領域へ転送することができます。
3876 Canvasに描画する場合はCanvasの裏に描画して[[Canvas::flip()]]命令で画面に表示しています。
3877 Imageはコードの記述はCanvasと大差ありませんが、Canvasの裏ではなくメインメモリ(主記憶装置)に描画しています。
3878 [[Image::draw()]]命令を実行することで初めてメインメモリ(主記憶装置)にある図形がVRAMに転送され、flip()とともに画面に表示されます。
3879 <[[Image::draw()]]命令実行時のイメージ図>
3880 [img[image/draw()命令.png]]
3882 つまり普通であればImageからCanvasへ直接転送が起こります。
3883 ここで、この2つの中間地点にあらかじめ転送しておくことで、Canvasへの転送時間を見かけ上小さくすることができるのです。
3884 この中間地点はVRAMと呼ばれるビデオボード上のメモリチップの中に作られます。
3885 実は、VRAM内でのメモリの転送速度に比べてメインメモリからVRAMへの転送はとても時間がかかるのです。
3886 この時間がかかる部分を事前に行っておくことで、見かけ上の転送時間を大きく縮めることができます。
3887 この転送を事前に一部行っておく命令がImage::quicken()命令です。
3889 [img[image/quicken()命令.png]]
3892 通常、[[Image::draw()]]命令を使用すると上図のようにメインメモリ(主記憶装置)から画面まで一気に転送し描画します。
3893 quicken()を実行すると、Imageの図形をVRAMに転送し、quickenモードに切り替えます。
3894 そして[[Image::draw()]]命令でVRAMにある図形を画面に転送し表示します。
3896 |!Image::quicken()|quicken()|メインメモリ(主記憶装置)にあるImageの内容をあらかじめVRAMに送ります|
3897 |~|~|解除する場合はquicken(false)と記述|
3899 !!Image::quicken()の書き方
3900 [[Image::draw()]]命令の直前に記述します。
3903 #include <psychlops.h>
3904 using namespace Psychlops;
3906 double width=100,height=100;
3907 Psychlops::Rectangle rect1(width,height);
3908 Psychlops::Color col;
3909 Psychlops::Image Noise1(rect1);
3911 void psychlops_main() {
3913 Canvas sampleA(Canvas::fullscreen);
3915 for(int x=0; x<width; x+=3){
3916 for(int y=0; y<height; y+=3){
3917 col.set(0.5,0.2,0.8);
3918 Noise1.pix(x,y,col);
3924 while(!Input::get(Keyboard::spc));
3928 !!Image::quicken()の使用上の注意
3930 # ImageがRGB/RGBAモードで宣言されていること
3931 # Imageへの描画がすでに完了していること
3935 Imageに対する描画命令は全てメインメモリ上にあるImageに対して行われます。
3936 したがって、すでにquicken()してしまったimageに対して描画を行っても、変更が反映されません。
3939 #include <psychlops.h>
3940 using namespace Psychlops;
3942 double width=100,height=100;
3943 Psychlops::Rectangle rect1(width,height);
3944 Psychlops::Color col;
3945 Psychlops::Image Noise1(rect1);
3947 void psychlops_main() {
3949 Canvas sampleA(Canvas::fullscreen);
3952 while(!Input::get(Keyboard::spc)){
3953 for(int x=0; x<width; x+=3){
3954 for(int y=0; y<height; y+=3){
3955 col.set(0.5,0.2,0.8);
3956 Noise1.pix(x,y,col);
3964 この新しい描画内容を画面に反映させるには、quicken(false)命令を使ってquicken()モードを解除するか、再度Image::quicken()命令を実行してもう一度転送を行う必要があります。<プログラム例>
3965 (転送を行ってもメインメモリ上にコピーは残っているので、単にこれをもう一度転送し直すことで描画を反映させることができます)
3968 #include <psychlops.h>
3969 using namespace Psychlops;
3971 double width=100,height=100;
3972 Psychlops::Rectangle rect1(width,height);
3973 Psychlops::Color col;
3974 Psychlops::Image Noise1(rect1);
3976 void psychlops_main() {
3978 Canvas sampleA(Canvas::fullscreen);
3981 while(!Input::get(Keyboard::spc)){
3982 for(int x=0; x<width; x+=3){
3983 for(int y=0; y<height; y+=3){
3984 col.set(0.5,0.2,0.8);
3985 Noise1.pix(x,y,col);
3990 Noise1.quicken(false);
3994 ※2007年9月現在、quicken()命令はRGB/RGBAでのみ稼働</pre>
3996 <div title="3.2 オフスクリーンを用いた描画例-ランダムドットパターン-" modifier="YourName" modified="200802191418" created="200709052137" changecount="18">
3997 <pre>この節ではImageクラスを用いたオフスクリーンへの描画の利用例として、下図に示した砂嵐のような図、ランダムドットパターンを描画してみます。
3998 [img[image/randomdot.png]]
3999 はじめにドットサイズが1ピクセル四方の基本的なパターンを描画する例を説明した後、ドットサイズがもっと大きいものについて説明します。
4001 [[3.2.1 基本的なランダムドットを描画する]]
4002 [[3.2.2 ドットサイズの異なるランダムドットパターンの描画]]
4003 [[3.2.3 ランダムドットキネマトグラムの描画]]
4005 この節では、複数の細いImageをshift()命令を使って並べることによって、ランダムドットを描画しています。この方法は簡単ですが、ドット密度やドットの運動方向を精密に操作したい場合は、Rectangleの配列を用いた方が描画がスムーズな場合があります。これについては、[[Tips: Rectangleの配列を用いたドットパターンの描画例]]を参照してください。</pre>
4007 <div title="3.2.1 基本的なランダムドットを描画する" modifier="YourName" modified="200802232254" created="200709130524" changecount="7">
4008 <pre>PsychlopsではImageを用いてランダムドットパターンが比較的簡単に描画できます。
4009 ランダムドットを描画するためには''Psychlops::random()''命令を使用します。
4010 色の返値を''Psychlops::random()''命令を使用しランダムに値を返すことでランダムドットパターンが描画できます。
4012 !!Psychlops::random()
4013 0~1,0~任意の数等好きに設定した値の中でランダムに数を返します。
4015 |!Psychlops::random()|random()|0~1の値をランダムに返します。|
4016 |~|random(double N)|0~Nの値をランダムに返します。|
4017 |~|~|double N: ランダムで返す値の最大値を指定|
4018 |~|random(int N)|0~Nの整数値をランダムに返します。|
4019 |~|~|int N: ランダムで返す整数値の最大値を指定|
4020 |~|random(double min, double max)|min~maxの値をランダムに返します。|
4021 |~|~|double min: ランダムで返す値の最小値を指定|
4022 |~|~|double max: ランダムで返す値の最大値を指定|
4024 引数が実数(double)か整数(int)かで動作が異なりますので、実数で返したい場合は最大値が整数でも必ず1.0などのように小数点をつけて指定してください。整数変数を指定する場合は(double)でキャストしてください。
4026 random(5) ← [0,1,2,3,4] のいずれかの数がint型で返る
4027 random(5.0) ← [0, 5)の間の浮動小数点値が返る
4030 random(i) ← [0,1,2,3,4] のいずれかの数がint型で返る
4031 random((double)i) ← [0, 5)の間の浮動小数点値が返る
4034 !!!Psychlops::random()の書き方
4035 3.1.3節の<例1>では画面中央のImage上に点を描画しました。
4036 このコードを応用してランダムドットを描画します。
4037 3.1.3節の<例1>では水平方向に2 ピクセル,垂直方向に5 ピクセル間隔に色:0.5の点を描画しました。
4038 ここではグレースケールのランダムドットを描画するために、色(輝度)が0 ~ 1の間でランダムに設定された、100 x 100 ピクセル四方の隙間のないドット列を描画します。
4041 #include <psychlops.h>
4042 using namespace Psychlops;
4044 double width=100,height=100;
4046 Psychlops::Rectangle rect1(width,height);
4047 Psychlops::Color col;
4048 Psychlops::Image Noise1(rect1,Image::GRAY);
4050 void psychlops_main() {
4052 Canvas sampleA(Canvas::fullscreen);
4054 for(int x=0; x<width; x++){
4055 for(int y=0; y<height; y++){
4057 temp1=Psychlops::random();
4059 Noise1.pix(x,y,col);
4064 while(!Input::get(Keyboard::spc));
4067 <例1>では[[カラーモード]]をGRAYで描画しましたがRGB,RBGAでも描画可能です。
4068 <例2>ではRGBAでランダムドットを描画します。
4071 #include <psychlops.h>
4072 using namespace Psychlops;
4074 double width=100,height=100;
4075 double temp1,temp2,temp3,temp4;
4076 Psychlops::Rectangle rect1(width,height);
4077 Psychlops::Color col;
4078 Psychlops::Image Noise1(rect1,Image::RGBA);
4080 void psychlops_main() {
4082 Canvas sampleA(Canvas::fullscreen);
4084 for(int x=0; x<width; x++){
4085 for(int y=0; y<height; y++){
4086 //R,G,B,Aそれぞれに0~1の値をランダムに返します
4087 temp1=Psychlops::random();
4088 temp2=Psychlops::random();
4089 temp3=Psychlops::random();
4090 temp4=Psychlops::random();
4091 col.set(temp1,temp2,temp3,temp4);
4092 Noise1.pix(x,y,col);
4097 while(!Input::get(Keyboard::spc));
4101 [img[image/colornoise.png]]
4105 <div title="3.2.2 ドットサイズの異なるランダムドットパターンの描画" modifier="Psychlops_Admin" modified="200709131828" created="200709130500" changecount="4">
4106 <pre>今まで描画したランダムドットパターンのドットサイズは1 ピクセル四方でした。
4107 次はピクセルサイズがn( n > 1 )のランダムドットパターンを描画します。
4108 1 ピクセルのドットを描画する時[[Image::pix()]]命令を使用していましたが
4109 n ピクセルのドットを描画するためには[[Image::rect()]]命令を使用します。
4110 横幅×高さの領域にn ピクセルのランダムドットパターンを描画する場合、以下の要領で描画します。
4112 1.ドットサイズ × ( 高さ × ドットサイズ )のImageをN個(横幅/ドットサイズ)用意する。
4113 2.ドットサイズ × ドットサイズのrectをN個( 横幅 / ドットサイズ )用意する。
4114 3.1で作成したImage(x)に2で作成したrect(x)をセットする。
4115 4.2で作成したrect(x)にランダムドットパターンを描画する。
4116 5.rectをドットサイズ縦方向に移動し、Image(x)全てに描画されるまで3~4を行う。
4119 [img[image/dotsize.png]]
4121 下の例では、横幅がwドット分、縦幅がhドット分のランダムドットパターンを描画しています。ここで指定した単位がドット単位でピクセル単位でないことに注意して、例を見てみてください。
4124 #include <psychlops.h>
4125 using namespace Psychlops;
4127 const int dotsize=4;
4128 const int width=100;
4131 Psychlops::Rectangle rect1[width];
4132 Psychlops::Color col;
4133 Psychlops::Image Noise1[width];
4135 void psychlops_main() {
4137 Canvas sampleA(Canvas::fullscreen);
4139 for(int x=0; x<width; x++){
4140 Noise1[x].set(dotsize,height*dotsize);
4141 rect1[x].set(dotsize, dotsize);
4142 for(int y=0; y<height; y++){
4143 temp1=Psychlops::random();
4145 Noise1[x].rect(rect1[x],col);
4147 rect1[x].shift(0, dotsize);
4149 Noise1[x].centering().shift((double)-1/2*(width*dotsize)+x*dotsize,0);
4153 while(!Input::get(Keyboard::spc));
4156 ドットサイズを他の値に変えて実行したい場合は『const int dotsize=4;』の値を変更してください。
4157 この例では、画面中央にランダムドットパターンを描画しました。画面中央に描画するための式は[[2.2.6節|2.2.6 画面上に縞を描画する-Rectangleクラス2-]]を参照してください。
4159 ここでは( Xc - 1 / 2 * w + i , Yc )のwとiにドットサイズ dotsize を乗算しています。
4160 ドットサイズを乗算することでImageが重ならないように移動して描画しています。
4161 試しにドットサイズの乗算を外すと、1ピクセル分のみ移動されて最後の列のみ正常に、他の列は細長いドットが表示されます。これはImageが重なって描画されてしまっているためです。
4162 [img[image/dotsize2.png]]
4165 <div title="3.2.3 ランダムドットキネマトグラムの描画" modifier="Psychlops_Admin" created="200709252142" changecount="1">
4166 <pre>!ランダムドットキネマトグラムを描画しよう
4167 次は上記のランダムドットを水平方向に動かしてみましょう。
4168 このような運動するランダムドットパターンをランダムドットキネマトグラムといいます。
4170 今回は水平方向に移動するのでX座標( Xc - 1 / 2 * w + i )について考慮します。
4171 ( Xc - 1 / 2 * w + i ) + α と記述すると α 分移動します。
4172 しかし既存の横幅内での運動を行いたいので
4173 下図のように右に飛び出たα を前に移動します。
4174 移動要素である i + α に対しwで除算し余りを算出することで求まります。
4176 ( Xc - 1 / 2 * w ) + ( i + α ) % w
4178 [img[image/rdk.png]]
4179 <例3>ではドットサイズを変更しているので、それぞれの値をドットサイズ倍する必要があります。
4181 ( Xc - 1 / 2 * w*dotsize ) + ( i *dotsize+ α*dotsize ) % w*dotsize
4184 次に α の内容について考えてみましょう。
4185 1フレームあたりの移動量をnドット分とすると、mフレーム目での0フレーム目の位置からのピクセル単位での移動量αは、 n* dotsize * m となります。
4186 つまりi番目のimageの水平方向の移動量は、以下のようになります。
4188 ( Xc - 1 / 2 * w * dotsize ) + ( i *dotsize+ frame*speed*dotsize ) % w*dotsize
4190 第1項(+の前)はImageをXcを中心として並べるための移動量、第2項が運動するパターンを実現するための移動量です。
4195 #include <psychlops.h>
4196 using namespace Psychlops;
4198 const int dotsize=4;
4199 const int width=100;
4205 Psychlops::Rectangle rect1[width];
4206 Psychlops::Color col;
4207 Psychlops::Image Noise1[width];
4209 void psychlops_main() {
4211 Canvas sampleA(Canvas::fullscreen);
4213 for(int x=0; x<width; x++){
4214 Noise1[x].set(dotsize,height*dotsize);
4215 rect1[x].set(dotsize, dotsize);
4216 for(int y=0; y<height; y++){
4217 temp1=Psychlops::random();
4219 Noise1[x].rect(rect1[x],col);
4220 rect1[x].shift(0, dotsize);
4223 while(!Input::get(Keyboard::spc)){
4224 for(int x=0; x<width; x++){
4225 phase=(double)-1/2*(width*dotsize)+(int)(x*dotsize+frame*speed*dotsize)%(int)(width*dotsize);
4226 Noise1[x].centering().shift(phase,0);
4235 水平方向へ移動するドットパターンが描画されたでしょうか?
4236 これと同様にしてランダムドットステレオグラムを書くこともできます。[[Tips: ランダムドットステレオグラムの描画コード]]を参照してみてください。</pre>
4238 <div title="3.3 画像ファイルの取り扱い" modifier="Psychlops_Admin" modified="200709121657" created="200708292225" changecount="4">
4239 <pre>この節では、画像ファイルの取扱いについて説明します。
4240 現バージョンのPsychlopsではpng形式のファイルの読込と保存が可能です。
4241 この読込と保存はImage型を通して行われます。
4242 読み込まれた画像ファイルはPsychlops内部ではImage型の変数(インスタンス)として扱われます。
4243 同様に保存はImage型の描画内容をPng形式でファイルに書き出すことによって行われます。
4246 [[3.3.1 画像ファイルを保存する]]
4247 [[3.3.2 画像ファイルを読み込み表示する]]
4250 <div title="3.3.1 画像ファイルを保存する" modifier="Psychlops_DevelopperG" modified="200910080846" created="200709111925" changecount="34">
4251 <pre>ディスプレイに提示した画像をプログラムが終了した後でも使えるように保存したい時がよくあります。
4252 この節では、画面上に描画した図形を外部ファイルとして保存する方法について説明します。
4253 PsychlopsではImageの描画内容を、PNG形式の画像ファイルとして保存することができます。
4254 これにはImage::save()命令を使用します。はじめにこのImage::save()命令の使い方と例を見てみましょう。
4259 |!Image::save()|save(filename)|!指定したImageを保存します|>|>|
4260 |~|~|filename| 保存するImageの画像ファイル名称(拡張子まで)を指定|>|>|
4261 |~|~|保存先を指定しない場合のパス|Mac:Macintosh HD/ユーザ/ユーザ名(任意)/書類/Psychlops/ ^^*1^^|>|
4262 |~|~|~|Win:実行ファイルと同じパス|>|
4263 |~|~|保存をする場合のパス指定|%APP%|Mac:実行バンドル(.app)と同じパス|
4264 |~|~|~|~|Win:実行ファイルと同じパス|
4265 |~|~|~|%RESOURCES%|Mac:実行バンドルの内部のResourcesフォルダ|
4266 |~|~|~|~|Win:実行ファイルのパスの直下のrscという名前のフォルダ|
4267 |~|~|~|%~USER_HOME%|Mac:"~/"と同じ(/Users/(user)/)|
4268 |~|~|~|~|Win:ユーザープロファイルフォルダ(C:\Documents and Settings\(user)\)|
4269 |~|~|~|%~USER_SETTING%|Mac:"~/Library/Psychlops/"|
4270 |~|~|~|~|Win:"%~AppData%Psychlops"|
4271 |~|~|~|%~USER_DOCUMENTS%|Mac:"~/Documents/Psychlops"|
4272 |~|~|~|~|Win:ユーザープロファイルフォルダ\My Documents\Psychlops|
4273 |~|~|~|%~USER_DOCUMENTS_ROOT%|Mac:"~/Documents"|
4274 |~|~|~|~|Win:ユーザープロファイルフォルダMy Documents\|
4275 *1 Finderでの表示は上記のとおりですが、Psychlopsで指定する場合は下記のUnix形式でパスを指定してください
4276 ** "~/Documents/Psychlops/"
4277 *2 MacOSX版では、日本語パス名は使用できません。強制終了してしまいます。
4278 *3 VISTA環境の場合、パス指定が正常に動作しません。VISTA環境の方はお手数ですが保存先を指定しないでお使いください。
4281 この命令は[[Psychlops::Rectangle]]型の情報を[[Psychlops::Image]]型に移動する命令です。
4282 この命令と''Image::save()''命令を組み合わせることによってImage上以外で描画した画像が保存できます。
4283 |!Canvas::to()|to([[Psychlops::Image]] img, [[Psychlops::Rectangle]] rect) |>|![[Psychlops::Rectangle]]型の情報を[[Psychlops::Image]]型に移動する|
4284 |~|~|[[Psychlops::Image]] img |移動先のImageを指定|
4285 |~|~|[[Psychlops::Rectangle]] rect |移動元のRectangleを指定|
4288 Canvas::to()命令は[[Psychlops::Rectangle]]型の引数で指定された大きさを取得して移動先のImageのサイズに指定します。
4289 このときに移動先のImageのサイズがあらかじめセットされていると、プログラムが予期しない動作をすることがあります。
4290 変数の宣言だけが行われていて、まだセットをされていないImage型を使用するか、
4291 Image::release()命令を使って一度Imageの中身をクリアしてからCanvas::to()命令を使うようにして下さい。
4293 |!Image::release()|release(void) |メモリ内のイメージの内容を解放する|
4296 [[Canvas::pix()|2.2.1 画面上に点を描画する-Pointクラス1-]]命令でCanvas上に描画したランダムドットをImageに移動し保存します。
4297 ''Canvas::to()''命令は[[Psychlops::Rectangle]] 型が移動元になるので
4298 描画した点と同じ場所に空の[[Psychlops::Rectangle]] 型を設定する必要があります。
4300 <例1>では座標(400,500)に100 × 100のランダムドットを描画し保存します。
4301 centerX,centerYの値を変更することで描画する座標位置が変わります。
4302 0,0を代入もしくは削除すると左上に描画されます。
4305 #include <psychlops.h>
4306 using namespace Psychlops;
4308 double width=100,height=100;
4310 double centerX=700,centerY=500;
4311 Psychlops::Rectangle rect1(width,height);
4312 Psychlops::Color col;
4313 Psychlops::Image Noise1;
4315 void psychlops_main() {
4317 Canvas sampleA(Canvas::fullscreen);
4318 rect1.shift(centerX,centerY);
4319 for(int x=0; x<width; x++){
4320 for(int y=0; y<height; y++){
4321 temp1=Psychlops::random();
4323 sampleA.pix(x+centerX,y+centerY,col);
4326 sampleA.to(Noise1,rect1);
4328 while(!Input::get(Keyboard::spc));
4329 Noise1.save("test.png");
4335 #include <psychlops.h>
4336 using namespace Psychlops;
4338 double width=100,height=100;
4340 double centerX=700,centerY=500;
4341 Psychlops::Rectangle rect1(width,height);
4342 Psychlops::Color col;
4343 Psychlops::Image Noise1;
4345 void psychlops_main() {
4347 Canvas sampleA(Canvas::fullscreen);
4348 rect1.shift(centerX,centerY);
4349 for(int x=0; x<width; x++){
4350 for(int y=0; y<height; y++){
4351 temp1=Psychlops::random();
4353 sampleA.pix(x+centerX,y+centerY,col);
4356 sampleA.to(Noise1,rect1);
4358 while(!Input::get(Keyboard::spc));
4359 Noise1.save("%APP%test.png");
4362 指定のパスに保存されたか確認してください。</pre>
4364 <div title="3.3.2 画像ファイルを読み込み表示する" modifier="YourName" modified="200802131204" created="200709111918" changecount="13">
4365 <pre>今までの節では自分でオフスクリーン上に図形を描画しました。
4366 この節では既存の画像ファイルを読み込みます。
4369 既存の任意の画像ファイルを読み込みます。
4371 |!Image::load()|load(filename)|指定した画像ファイルを読み込みます|>|>|
4372 |~|~|filename: 読み込む画像ファイルの名前を指定|>|>|
4373 |~|~|保存先を指定しない場合のパス|Mac:Macintosh HD/ユーザ/ユーザ名(任意)/書類/Psychlops/ ^^*1^^|>|
4374 |~|~|~|Win:実行ファイルと同じパス|>|
4375 |~|~|読込みをする場合のパス指定|%APP%|Mac:実行バンドル(.app)と同じパス|
4376 |~|~|~|~|Win:実行ファイルと同じパス|
4377 |~|~|~|%RESOURCES%|Mac:実行バンドルの内部のResourcesフォルダ|
4378 |~|~|~|~|Win:実行ファイルのパスの直下のrscという名前のフォルダ|
4379 |~|~|~|%~USER_HOME%|Mac:"~/"と同じ(/Users/(user)/)|
4380 |~|~|~|~|Win:ユーザープロファイルフォルダ(C:\Documents and Settings\(user)\)|
4381 |~|~|~|%~USER_SETTING%|Mac:"~/Library/Psychlops/"|
4382 |~|~|~|~|Win:"%~AppData%Psychlops"|
4383 |~|~|~|%~USER_DOCUMENTS%|Mac:"~/Documents/Psychlops"|
4384 |~|~|~|~|Win:ユーザープロファイルフォルダ\My Documents\Psychlops|
4385 |~|~|~|%~USER_DOCUMENTS_ROOT%|Mac:"~/Documents"|
4386 |~|~|~|~|Win:ユーザープロファイルフォルダMy Documents\|
4387 *1 Finderでの表示は上記のとおりですが、Psychlopsで指定する場合は下記のUnix形式でパスを指定してください
4388 ** "~/Documents/Psychlops/"
4389 *2 MacOSX版では、日本語パス名は使用できません。強制終了してしまいます。
4390 *3 VISTA環境の場合、パス指定が正常に動作しません。VISTA環境の方はお手数ですが保存先を指定しないでお使いください。
4394 [[3.3.1節|3.3.1 画像ファイルを保存する]]の<例1>で保存したランダムドットを読み込みます。
4395 表示は[[Image::draw()]]で行います。
4398 #include <psychlops.h>
4399 using namespace Psychlops;
4401 Psychlops::Image Noise1;
4403 void psychlops_main() {
4405 Canvas sampleA(Canvas::fullscreen);
4406 Noise1.load("test.png");
4410 while(!Input::get(Keyboard::spc));
4413 次にパス指定を行い、画像ファイルを読み込みます。
4416 #include <psychlops.h>
4417 using namespace Psychlops;
4419 Psychlops::Image Noise1;
4421 void psychlops_main() {
4423 Canvas sampleA(Canvas::fullscreen);
4424 Noise1.load("%APP%test.png");
4428 while(!Input::get(Keyboard::spc));
4432 <div title="3.4 Canvasのオフスクリーンへの取り込み" modifier="YourName" modified="200802191336" created="200710240512" changecount="3">
4433 <pre>スクリーンキャプチャのように、画面上に描画したイメージをオフスクリーンに取り込むことができます。
4434 |!Canvas::to()|to(Image target, Rectangle source)|VRAM上にある画面イメージをメインメモリ(主記憶装置)にあるImageに転送します|
4435 |~|~|Image target: 転送先のImageを指定します|
4436 |~|~|Rectangle source: 転送する画面上の範囲を指定します|
4437 この際、転送先のImageがsetされていなければ自動的にsetします。setされていた場合、破棄されて上書きされます。
4440 #include <psychlops.h>
4441 using namespace Psychlops;
4443 void psychlops_main() {
4445 Canvas d(Canvas::fullscreen);
4447 Psychlops::Rectangle rect(100,100);
4450 // check rect external bordar and Rectangle.resize validity
4451 rect.resize(102,102).draw(Color::red);
4453 // check rect internal bordar and Rectangle.resize validity
4454 rect.resize(100,100).draw(Color::green);
4457 rect.resize(98,98).draw(Color::gray);
4460 rect.resize(100,100);
4462 // check Canvas.to(Image, Rectangle) validity
4465 img.centering().shift(0,-200).draw();
4466 d.msg("Canvas.to(Image)", d.getHcenter(), d.getVcenter()-200);
4468 // check Canvas.copy(Rectangle, Rectangle) validity
4469 d.copy(rect, rect.dup().shift(200,0));
4470 d.msg("Canvas.copy", d.getHcenter()+200, d.getVcenter());
4474 while(!Input::get(Keyboard::esc)) {
4481 <div title="3.5 文字列のレンダリングの仕様と関連情報" modifier="Psychlops_DevelopperG" modified="200910260739" created="200910260730" changecount="1">
4482 <pre>[[2.3節|2.3 文字列の描画]]ではLettersクラスを使った2バイト文字列の描画について最低限の事だけを説明しました。実はLettersクラスはImageクラスを応用して作成されています。
4483 ここでは、やや応用的ですが、本節で説明したImageクラスの仕様を踏まえてより詳細なLettersクラスの仕様と関連情報について説明します。
4485 * [[ワイド文字列の入出力]]</pre>
4487 <div title="4. 複雑な描画を行う2(複数オブジェクトのグループ化と回転・拡大縮小)" modifier="Psychlops_DevelopperG" modified="200912010726" created="200909300419" changecount="4">
4488 <pre>この章ではここまで用いてきたいくつかの描画のためのクラスをまとめて一つのグループとして取り扱う方法を紹介します。
4489 たとえば、いくつかのRectangleやImageをまとめて動かしたい時に、それぞれに対してshiftメソッドを使うというやり方は少し面倒です。
4490 こんな時に、これらを一つのグループにまとめてしまって、そのグループ全体に対して移動の操作をすることができれば、プログラムはかなり見通しがよくなるでしょう。
4491 また、描画したものをある点を中心に回転させたいというような時に、回転のための数学操作を考えるのはかなり面倒です。
4492 PsychlopsはOpenGLを用いて描画を行っているのですが、OpenGLは描画内容の回転や拡大・縮小をグラフィックチップに計算させることが簡単にできます。Psychlopsではグループ化したオブジェクトに対してこれらの変換を行うことが可能です。
4494 [[4.1 複数の描画オブジェクトをグループ化するーGroupクラスー]]
4495 [[4.2 描画内容の回転および拡大縮小]]
4500 <div title="4.1 複数の描画オブジェクトをグループ化するーGroupクラスー" modifier="Psychlops_DevelopperG" modified="200912010828" created="200912010750" changecount="27">
4504 2. GroupクラスにShapeを追加する
4505 3. Groupクラスに対して操作を行う
4506 4.(必要に応じて)ShapeをGroupから外す
4509 グループクラスの変数宣言は特に引数などは必要ではありません。
4515 ! 2. GroupクラスにShapeを追加する
4516 ここまで取り扱ってきたRectangleやImageといったクラスはすべてShapeという大きなクラスを元に構成されています(クラスの継承)。Shapeクラスに属するクラスは以下の通りです。
4520 |Ellipse|円を含む楕円形の領域|
4521 |Letters|文字列を格納するための領域(テキストボックス)|
4523 Shapeクラスの詳細については[[]]
4524 これらのクラスはここまででも扱ってきたようなcentering(), shift(), draw()などの共通する命令セットを持っています。これらの命令が実際にどのように振る舞うかはクラスの内容に合わせて異なっていますが、引数などの命令の形はどれも共通になっています。これらの共通の命令セットの一つとしてadd()命令があります。
4526 |!Shape::add()|add(Group gp)|>|Shapeをグループクラスに追加します。|
4527 |~|~|Group gp|追加先のグループを指定|
4534 またGroupに対してappend()命令を使用しても同じことができます。
4536 |!Group::append()|append(Shape shp)|>|Shapeをグループクラスに追加します。|
4537 |~|~|Shape shp|追加するShapeを指定|
4544 <div title="5. 入出力を行う" modifier="Psychlops_DevelopperG" modified="200908190158" created="200708211945" changecount="3">
4545 <pre>[[5.1 外部装置(キーボード/マウス)の操作内容を取得する]]
4546 [[5.2 ファイルの入出力]]</pre>
4548 <div title="5.1 外部装置(キーボード/マウス)の操作内容を取得する" modifier="Psychlops_DevelopperG" modified="200908190201" created="200708232250" changecount="2">
4550 コンピュータにあるキーボードやマウスから反応を取得するためのクラスとしてInputクラスがあります。
4551 現バージョンでPsychlopsはキーボードとマウスの反応を取得することが可能です。Psychlopsでは、キーボードのそれぞれのキーやマウスのボタン、カーソル位置をそれぞれKeyboard, Mouseクラスのメンバとして取り扱っています。具体的な使い方を以下で説明します。
4553 !Input::get() -反応を取得する-
4554 これまでのソースの中でwhile(!Input::get(Keyboard::spc))という命令をスペースキー待ちの命令として使ってきました。これは、"スペースキーが押されるまでループを繰り返しなさい"という命令です。この文でも使っているようにInput::get()命令は引数として指定された入力機器の反応があれば1(TRUE)を返します。入力が無ければは0(FALSE)を返します。Input::get()命令の第2引数は省略可能です。この場合には、Input::get()命令は~KeyStateにはpushedが指定されたとみなします。
4555 |!Input::get()|Input::get([[Keyboard::KeyCode|キーコード表]] code, [[Keyboard::KeyState]] state)|キーボードの反応を取得します|
4556 |~|~|code 反応を取得するキーの名前 [[キーコード表]] 参照|
4557 |~|~|state 反応状態のオプション (省略時はpushed) [[Keyboard::KeyState]]参照|
4558 |~|Input::get([[Mouse::ButtonCode|キーコード表]] code, [[Mouse::ButtonState]] state)|マウスの反応を取得します|
4559 |~|~|code 反応を取得するマウスボタンの名前 [[キーコード表]] 参照|
4560 |~|~|state 反応状態のオプション (省略時はpushed) [[Mouse::ButtonState]]参照|
4562 !! ~KeyState(~MouseState)の使い方
4563 第2引数のの"~KeyState"・"~MouseState"の使い方は少し難しいかもしれません。以下でこれらの"~KeyState"の使い方を見てみましょう。
4564 ~KeyStateには"pushed", "pressed", "released"の3つのモードが存在します。これら3つのモードの意味を実例とともに見ていきましょう。
4566 はじめに"pushed"を使った例を見てみましょう。ここでは、Input::get()命令の第2引数として"pushed"を明示的に指示しましたが、省略しても、挙動は変わりません。
4567 [[例1: pushedを使ったソース]]
4568 このソースを実行すると中央に表示された四角形がカーソルの左右キーで左右に動きます。キーを押し続けても一度しかシフトが起こらないことに注目してください。"pushed"を指定したときにInput::get()命令がTRUEを返すのは、キーが押し込まれたフレームより後に実行されたうちのはじめの一回だけです。キーを離すと元の状態に戻って、またキーが押し込まれたフレーム以降に一度だけTRUEが返されます。
4570 次に、"pressed"を指定した例です。例1とソースはほとんど変わりませんが、Input::get()命令の第2引数だけが"pressed"に変わっています。
4571 [[例2: pressedを使ったソース]]
4572 このソースを実行すると、やはりカーソルキーにしたがって四角形が左右に動きますが、例1と違って押している間、常に動き続けています。"pressed"を指定すると、キーが押し込まれている間ずっとInput::get()命令はTRUEを返します。
4574 次に"released"を使った例について見てみましょう
4575 [[例3: releasedを使ったソース]]
4576 この例では、例2に"released"を指定したInput::get()命令の文が付け加わっています。実行してみると、四角形はやはりキーを押している間、常に動き続けますが、キーを離すと中央に戻ってしまいます。キーが離されたことが"released"を指定したInput::get()命令によって検出されると、四角形がcentering()命令を使って中央に戻るようにコードが組まれています。このように"released"は指定したキーが離されたことを検出するためのオプションです。
4577 ここで、例の中にInput::refresh()という命令が使われています。これは、キーボードやマウスのボタン検出を初期化する命令です。複雑な入出力動作をするプログラムを書くときにはこの命令でキーボード・マウスの初期化をしておくことをおすすめします。
4578 |!Input::refresh()|refresh(void)|キーボード・マウスの入出力ルーチンを初期化します|
4579 参考[[Tips: Psychlopsにおけるキーボードマウスの入出力検出ルーチン]]
4582 最後に、これらをまとめて比較するためのデモプログラム例を載せておきます。a/sでpushedのデモ, z/xでpressed, q/wでpressed & released, cで四角形の位置初期化です。
4583 [[例4: pushed,pressed,releasedを使ったソース]]
4585 以上では、キーボードの入力取得オプションである"~KeyState"を例として扱いましたが、"~MouseState"の使い方もまったく同じです。以下に"~MouseState"を使った簡単なプログラム例を挙げます。上の例2とほとんど変わりません。右クリックで右方向、左クリックで左方向へ移動します。
4586 [[例5: Mouse::pressedを使ったソース]]
4588 ! カーソル座標の取得 とマウスカーソルの表示切替
4589 マウスの座標はMouseクラスのメンバMouse::x(水平座標), Mouse::y(垂直座標)に記録されています。このメンバに対して代入を行うとマウスの位置も移動します。
4590 |!Mouseのメンバ|Mouse::x|マウスの水平座標(代入可能)|
4591 |~|Mouse::y|マウスの水平座標(垂直座標)|
4592 マウスカーソルの表示切替はMouse::show(), Mouse::hide()命令を使用してください。
4593 |!Mouse::show()|show()|マウスカーソルを表示する|
4594 |!Mouse::hide()|hide()|マウスカーソルを非表示にする|
4595 次の例では、マウスカーソルを最初に画面中央に移動した後に、ユーザーのマウスの動きに合わせてマウスカーソルを動かします。左クリックすると、マウスカーソルの下にあるオブジェクトを選択して、ドラッグできます。また、右クリックでマウスカーソルを描画しないようにします。
4599 <div title="5.2 ファイルの入出力" modifier="Psychlops_DevelopperG" modified="200908190200" created="200709170703" changecount="2">
4600 <pre>この節では簡易的なファイルの入出力命令について説明します。
4601 PsychlopsはC, C++の標準命令と混在が可能なので、そちらの方法になれている方は、標準の入出力関数を用いてデータの保存、読み込みを行っていただいても問題ありません。
4602 しかし、単にプログラム中で用いた配列に格納されたデータを保存するだけの時に標準の入出力関数を使うのが面倒な時があります。
4603 Psychlopsではファイルの入出力を簡易的に行う命令Data::savearray(), Data::loadarray()が準備されています。ただし、これらの命令を使用してデータを保存するには以下の条件が満たされている必要があります。
4608 Data::loadarray()も同様にサイズが同じ20個以下の配列を読み込むことができます。
4611 [[5.2.2 ファイルからの入力]]</pre>
4613 <div title="5.2.1 ファイルへの出力" modifier="Psychlops_DevelopperG" modified="200908190159" created="200709170712" changecount="1">
4615 はじめに配列を保存するための命令Data::savearray()から説明を行います。
4617 |!Data::savearray()|savearray(filename, header, length, ...)|指定した配列を保存します|>|>|
4618 |~|~|filename: 保存する配列のファイル名称(拡張子まで)を指定|>|>|
4619 |~|~|保存先を指定しない場合のパス|Mac:Macintosh HD/ユーザ/ユーザ名(任意)/書類/Psychlops/ ^^*1^^|>|
4620 |~|~|~|Win:実行ファイルと同じパス|>|
4621 |~|~|保存をする場合のパス指定|%APP%|Mac:実行バンドル(.app)と同じパス|
4622 |~|~|~|~|Win:実行ファイルと同じパス|
4623 |~|~|~|%RESOURCES%|Mac:実行バンドルの内部のResourcesフォルダ|
4624 |~|~|~|~|Win:実行ファイルのパスの直下のrscという名前のフォルダ|
4625 |~|~|~|%~USER_HOME%|Mac:"~/"と同じ(/Users/(user)/)|
4626 |~|~|~|~|Win:ユーザープロファイルフォルダ(C:\Documents and Settings\(user)\)|
4627 |~|~|~|%~USER_SETTING%|Mac:"~/Library/Psychlops/"|
4628 |~|~|~|~|Win:"%~AppData%Psychlops"|
4629 |~|~|~|%~USER_DOCUMENTS%|Mac:"~/Documents/Psychlops"|
4630 |~|~|~|~|Win:ユーザープロファイルフォルダ\My Documents\Psychlops|
4631 |~|~|~|%~USER_DOCUMENTS_ROOT%|Mac:"~/Documents"|
4632 |~|~|~|~|Win:ユーザープロファイルフォルダMy Documents\|
4633 |~|~|ファイル名に%TIME_という文字列があると、日時分秒に自動変換されます|>|
4634 |~|~|header: 配列の冒頭に加える説明などを記述します|>|>|
4635 |~|~|length: 保存する配列の大きさを指定します|>|>|
4636 |~|~|...: 保存する配列を指定します。最大20個まで指定できます|>|>|
4637 *1 Finderでの表示は上記のとおりですが、Psychlopsで指定する場合は下記のUnix形式でパスを指定してください
4638 ** "~/Documents/Psychlops/"
4639 *2 MacOSX版では、日本語パス名は使用できません。強制終了してしまいます。
4640 *3 VISTA環境の場合、パス指定が正常に動作しません。VISTA環境の方はお手数ですが保存先を指定しないでお使いください。
4643 以下でこの命令の使い方の例を見てみましょう。
4646 #include <psychlops.h>
4647 using namespace Psychlops;
4649 const int MaxTrials = 100;
4650 int condition[MaxTrials], result[MaxTrials]
4652 void psychlops_main() {
4654 for(int CurrentTrial=0; CurrentTrial<MaxTrials; CurrentTrial++) {
4656 condition[CurrentTrial] = Psychlops::random(2);
4660 if(Input::get(Keyboard::left))
4661 result[CurrentTrial] = 0;
4662 if(Input::get(Keyboard::right))
4663 result[CurrentTrial] = 1;
4670 上のプログラム例では、変数配列resultには、被験者のキー押しによって0か1かの数値が入ります。この配列を配列conditionに保存されている実験条件とともにsavearray()命令を用いて、”sample_保存時間.txt"という名前のファイルに保存するには以下のように命令を書きます。
4672 Data::savearray("sample_%TIME_.txt", "conditions\tresults", MaxTrials, condition, result);
4675 第1引数はファイル名の指定、第2引数はファイルのヘッダ(1行目の内容)です。conditionおよび、resultの配列の個数はMaxTrials(100)個なので、第3引数はMaxTrials, それ以下に実際の配列の名前であるcondition, resultが続きます。
4681 #include <psychlops.h>
4682 using namespace Psychlops;
4684 const int MaxTrials = 100;
4685 int condition[MaxTrials], result[MaxTrials]
4687 void psychlops_main() {
4689 for(int CurrentTrial=0; CurrentTrial<MaxTrials; CurrentTrial++) {
4691 condition[CurrentTrial] = Psychlops::random(2);
4695 if(Input::get(Keyboard::left))
4696 result[CurrentTrial] = 0;
4697 if(Input::get(Keyboard::right))
4698 result[CurrentTrial] = 1;
4702 Data::savearray("sample_%TIME_.txt", "conditions\tresults", MaxTrials, condition, result);
4707 標準の出力パスに「sample_(日時).txt」というファイルが出力されたでしょうか?
4709 ファイルは、配列を縦に並べたタブ区切り形式となります。上記の例を適用すると
4711 | Condition[0] | Result[0] |
4712 | Condition[1] | Result[1] |
4713 | Condition[2] | Result[2] |
4715 の順番で記録されています。このファイルを実際に開くと、以下の例のようになっています。
4727 この結果ファイルはそのままExcelなどに読み込むことが出来ます。
4730 <div title="5.2.2 ファイルからの入力" modifier="Psychlops_DevelopperG" modified="200908190200" created="200709170714" changecount="1">
4731 <pre>Data::savearray()とは逆に、ファイルからデータを読み込むことも出来ます。
4732 データは配列に読み込まれるため、savearrayで書き込んだものをそのまま読み込むことが出来ます。
4736 テキストファイルから配列に読み込みます。
4738 |!Data::savearray()|savearray(filename, skipped_lines, length, ...)|指定した配列を読み込みます|>|>|
4739 |~|~|filename: 読み込むファイル名称(拡張子まで)を指定|>|>|
4740 |~|~|保存先を指定しない場合のパス|Mac:Macintosh HD/ユーザ/ユーザ名(任意)/書類/Psychlops/ ^^*1^^|>|
4741 |~|~|~|Win:実行ファイルと同じパス|>|
4742 |~|~|読み込む場合のパス指定|%APP%|Mac:実行バンドル(.app)と同じパス|
4743 |~|~|~|~|Win:実行ファイルと同じパス|
4744 |~|~|~|%RESOURCES%|Mac:実行バンドルの内部のResourcesフォルダ|
4745 |~|~|~|~|Win:実行ファイルのパスの直下のrscという名前のフォルダ|
4746 |~|~|~|%~USER_HOME%|Mac:"~/"と同じ(/Users/(user)/)|
4747 |~|~|~|~|Win:ユーザープロファイルフォルダ(C:\Documents and Settings\(user)\)|
4748 |~|~|~|%~USER_SETTING%|Mac:"~/Library/Psychlops/"|
4749 |~|~|~|~|Win:"%~AppData%Psychlops"|
4750 |~|~|~|%~USER_DOCUMENTS%|Mac:"~/Documents/Psychlops"|
4751 |~|~|~|~|Win:ユーザープロファイルフォルダ\My Documents\Psychlops|
4752 |~|~|~|%~USER_DOCUMENTS_ROOT%|Mac:"~/Documents"|
4753 |~|~|~|~|Win:ユーザープロファイルフォルダMy Documents\|
4754 |~|~|skipped_lines: 冒頭から読み込みを飛ばす行数を指定します|>|>|
4755 |~|~|length: 読み込む配列の大きさを指定します|>|>|
4756 |~|~|...: 読み込む配列を指定します。最大20個まで指定できます|>|>|
4757 *1 Finderでの表示は上記のとおりですが、Psychlopsで指定する場合は下記のUnix形式でパスを指定してください
4758 ** "~/Documents/Psychlops/"
4759 *2 MacOSX版では、日本語パス名は使用できません。強制終了してしまいます。
4760 *3 VISTA環境の場合、パス指定が正常に動作しません。VISTA環境の方はお手数ですが保存先を指定しないでお使いください。
4764 savearrayで保存したファイルを読み込んでみましょう。savearrayでは通常冒頭2行にファイルの説明を加えるため、skipped_linesには2を指定します。ファイル名中で%TIME_を指定すると現在時刻に変換されてしまうため、読み込むファイルはあらかじめ別の名前にしておきます。ここではsample.txtとしました。
4767 #include <psychlops.h>
4768 using namespace Psychlops;
4770 const int MaxTrials = 100;
4771 int condition[MaxTrials], result[MaxTrials]
4773 void psychlops_main() {
4775 Data::loadarray("sample.txt", 2, MaxTrials, condition, result)
4780 これでsample.txtの中身が配列に読み込まれました</pre>
4782 <div title="6. 時間制御と各種ツールを使用する" modifier="Psychlops_DevelopperG" modified="200908190210" created="200709132309" changecount="6">
4783 <pre>[[6.1 時間を計測する]]
4785 [[6.2.1 描画の時間精度を確認する-FPSチェッカー]]
4786 [[6.2.2 画面に反映されない描画の進行度を表示する-プログレスバー]]
4792 [[6.3.5 行列の中身をオフスクリーンに描画する]]
4793 [[6.3.6 メッシュグリッドの作成]]</pre>
4795 <div title="6.1 時間を計測する" modifier="Psychlops_DevelopperG" modified="200908190204" created="200708242225" changecount="1">
4797 Psychlopsで時間を計測する方法は2種類あります。
4798 画面のリフレッシュを基準として、リフレッシュされたフレーム数をカウントすることです。この方法は比較的簡単ですが、1フレームの時間以上の精度で時間を制御することができません。
4799 そこで、より高い精度で時間を計測するためのクラスとしてPsychlopsにはClockクラスが用意されています。このClockクラスはCPUタイマーを利用することによって高い精度の時間間隔の計測を実現しています。
4802 Clockの宣言は特に難しくありません。[[Psychlops::Clock]]型のインスタンスを以下のような形式で単に宣言するだけです。
4804 Psychlops::clock Timer;
4806 このTimerに現在のCPUタイマーの値を入力するにはClock::update()命令を使用します。
4807 |!Clock::update()| Clock::update(void)| 現在のCPUタイマーの値を入力する|
4808 このままでは、保持されている時刻はCPUタイマーのカウントなので、実際にプログラム内でこのクラスを使うときにはClock::at_msec()命令を使用してタイマーのカウントをmsec単位に変換して使用します。
4809 |!Clock::at_msec()| double Clock::at_msec(void)| カウントをmsec単位に変換(double)して返す|
4811 下にClockクラスを用いた簡単な時間計測例を挙げて見ました。スペースキーを押している時間が画面に表示されます。
4814 #include <psychlops.h>
4815 using namespace Psychlops;
4817 void psychlops_main() {
4819 Canvas sampleA(Canvas::fullscreen);
4820 double elapsed=0.0, dcx, dcy;
4823 dcx=sampleA.getHcenter();
4824 dcy=sampleA.getVcenter();
4825 sampleA.clear(Color(0.5));
4828 while(!Input::get(Keyboard::esc)) {
4830 if(Input::get(Keyboard::spc, Keyboard::released)){
4832 elapsed=(End-Start).at_msec();
4833 sampleA.clear(Color(0.5));
4835 sampleA.clear(Color(0.5));
4836 sampleA.msg("Elapsed Time(msec)", dcx-100,dcy-30, Color::white);
4837 sampleA.var((int)elapsed,dcx ,dcy, Color::white);
4841 if(Input::get(Keyboard::spc, Keyboard::pushed)){
4848 ^^ごく稀にWindows環境下でこのプログラムを実行すると、キー押し時間がマイナスになることがあるようです。このような状態が起こったら、並行して動いているプログラムを終了させて、再起動をしてからこのプログラムを実行してみてください。参考[[Tips: 時間精度が必要なプログラムを実行するとき]]^^</pre>
4850 <div title="6.2 各種ツールを使ってみる" modifier="Psychlops_DevelopperG" modified="200908190206" created="200709120042" changecount="3">
4851 <pre>Psychlopsにはここまで扱ったような簡単な描画命令だけではなく、描画内容をチェックするためのツールが実装されています。ここでは、これらのツールについて説明します。
4852 [[6.2.1 描画の時間精度を確認する-FPSチェッカー]]
4853 [[6.2.2 画面に反映されない描画の進行度を表示する-プログレスバー]]</pre>
4855 <div title="6.2.1 描画の時間精度を確認する-FPSチェッカー" modifier="Psychlops_DevelopperG" modified="200908190205" created="200709120043" changecount="1">
4857 Psychlopsには、現在描画している画面がプログラムで指定した時間通りに実行されているかどうかをチェックするツールがあります。
4858 たとえば、以下のようなコードを考えて見ます。
4860 void psychlops_main() {
4861 Canvas sampleA(Canvas::fullscreen);
4867 このプログラムを書いたときの意図は、描画を毎フレームごとに更新するというものでしょう。(でなければ、なんらかの「待ち」処理がsampleA.flip()の前後に入っているはずです。)
4868 しかし、非常に複雑な描画内容をCanvasの裏画面に書いたり、大きなImageを転送するときは、描画に1フレーム分以上の時間かかることがあります。この場合、psychlopsは描画が完了するまで待ってから[[Canvas::flip()]]命令を実行するので実際に描画が更新されるのは2フレーム以上たってからということになります。
4869 問題は、このような"こま落ち"はただ見ているだけではわからないことが多いことです(複雑な刺激呈示の途中に16.7 msと33.3msの区別がつくでしょうか?)。
4870 psychlopsにはこのような"こま落ち"を検出するためのFPSチェッカと呼ばれるツールが準備されています。
4871 このFPSチェッカを使うには、まず時間計測を行いたい部分の前にFPSチェッカを起動する命令を書きます。
4872 これには、Canvas::watchFPS()命令を使用します。
4873 |!Canvas::watchFPS()|void watchFPS(void)|FPSチェッカをonにします|
4874 このままでは、FPSチェッカが起動しているだけで、その内容を見ることはできません。
4875 FPSチェッカの内容を表示するためには、[[Canvas::flip()]]命令の直前にCanvas::showFPS()命令を書いてFPSチェッカのウィンドウを表示させます。
4876 |!Canvas::showFPS()|void showFPS(void)|FPSチェッカのレポートを画面左上に表示します|
4877 Canvas::showFPS()命令を実行すると、半透明のレポートウィンドウが画面右上に表示されます。このレポートウィンドウは、すべての描画内容の上に重ねて表示されます(以下を参照してください)
4881 以下のコードは5フレームに一度描画内容を更新して、コマ落ちが発生しているかどうかを表示します。
4883 #include <psychlops.h>
4884 using namespace Psychlops;
4886 void psychlops_main() {
4887 Canvas sampleA(Canvas::fullscreen);
4889 while(!Input::get(Keyboard::spc)){
4897 このコードを60Hzのディスプレイで実行すると、以下のような画面が表示されます。
4899 [img[image/FPSchecker.png]]
4901 showFPS()命令を実行した後に、Canvas::clear()命令を実行しているにもかかわらずレポートウィンドウが表示されていることに注意してください。このように、実際にはレポートウィンドウは全ての描画が終了した後に描画されます。
4902 このレポートウィンドウを拡大したものが下図です。
4904 [img[image/FPSwindow.png]]
4906 レポートウィンドウは3行から成り立っています。一番上の赤い数字は、これまでにコマ落ちしたフレーム数、真ん中の白い数字は表示された総フレーム数、一番下の青い数字は前回のflip()から今回のflip()までにかかった時間(msec単位)です。
4907 この例では、5フレームに一度描画が更新されているので、60Hzだと画面更新間の時間は1/12 sec = 83.3 msecで、一番下の青い数字と一致していることに注目してください。</pre>
4909 <div title="6.2.2 画面に反映されない描画の進行度を表示する-プログレスバー" modifier="Psychlops_DevelopperG" modified="200908190206" created="200709252142" changecount="1">
4911 大量の計算をコンピュータに行わせているとき、画面には何の変化も起きずにプログラムが暴走しているのか、単に計算に時間がかかっているだけなのかの区別がつかないときが良くあります(本当は、暴走をさせないようなプログラム構造にするべきですが・・・)。進行度を文字で画面に表示する方法もありますが、文字表示のために[[Canvas::flip()]]命令を挟んでしまうと計算速度が落ちてしまいます。
4912 このような状況で、画面に計算の総量に対して今どのくらいの割合が終了したかを表示するためのツールにプログレスバーがあります。このプログレスバーは強制的にフロントバッファ(現在表示中の画面)に表示されるため、[[Canvas::flip()]]命令は必要ありません。
4914 このプログレスバーを表示するにはCanvas::progressbar()命令を使用します。
4915 |!Canvas::progressbar()|void progressbar(X now, X max)|現在のnowの値のmaxに対する比率をプログレスバーとして表示します。now, maxは数値型であればどの型でもかまいません|>|
4916 |~|~|X now|現在の進行度を示す変数値(型は数値型)|
4917 |~|~|X max|nowの最終的な到達値(型は数値型)|
4918 |~|void progressbar(double ratio)|ratioで示される比率をプログレスバーとして表示します。|>|
4919 |~|~|double ratio|比率の値(0.0~1.0)|
4920 この命令を使用すると、画面左上に灰色の四角のなかに時間とともに伸びる青い四角形が表示されます。この青い四角形が灰色の四角いっぱいまで伸びると、nowの値がmaxまで到達した(ratioが1.0になる)ことを示しています。
4925 下の例では100 msecと50 msecの"待ち"を100回繰り返します。
4926 時間経過とともに左上に提示されるプログレスバーが伸びていく様子に注目してください。
4929 #include <psychlops.h>
4930 using namespace Psychlops;
4932 void psychlops_main() {
4934 Canvas sampleA(Canvas::fullscreen);
4935 Clock timer1, timer2;
4941 sampleA.message("calculating...", sampleA.getHcenter(), sampleA.getVcenter());
4945 for(int i=0; i<100; i++){
4947 sampleA.progressbar(i,100);
4948 while((timer2-timer1).at_msec()<100){timer2.update();}
4949 if(Input::get(Keyboard::esc))break;
4953 sampleA.message("10 sec elapsed", sampleA.getHcenter(), sampleA.getVcenter());
4955 while(!Input::get(Keyboard::spc));
4957 sampleA.message("calculating...", sampleA.getHcenter(), sampleA.getVcenter());
4962 for(int i=0; i<100; i++){
4964 sampleA.progressbar((double)i/100.0);
4965 while((timer2-timer1).at_msec()<50){timer2.update();}
4966 if(Input::get(Keyboard::esc))break;
4970 sampleA.message("5 sec elapsed", sampleA.getHcenter(), sampleA.getVcenter());
4972 while(!Input::get(Keyboard::spc));
4976 <div title="6.3 行列演算を行う" modifier="Psychlops_DevelopperG" modified="200908190211" created="200709252143" changecount="3">
4979 Matrixクラスは行列を取り扱います。線形代数としての取り扱いもできますが、Matlabの行列のように数値を2次元的に配置した表や画像(Image)のようなものとしても利用できます。Imageは精度が低いため、Imageに保存した色データをもとに再計算するのはお勧めできませんが、Matrixではそのような計算をしても十分な精度があります。
4982 MatrixとImageは基本的に2次元のよく似たデータ構造を持っており、相互に変換が可能です。(実際にはImageは下で述べるようなより複雑なデータ構造を持っています。)ただし、両者にはいくつかの相違点があります。
4983 [Img[Image/Image_Matrix.png]]
4985 * Matrixの要素アクセスは行(縦)列(横)の順で指定し、起点は1です。ImageはX(横)Y(縦)の順で指定し、起点は0です。
4986 [Img[Image/ImagetoMatrix.png]]
4989 * Imageは一つのx,y値に対してRGB 3値、RGBA 4値を取り扱うことが出来ます(数学的にはテンソルの構造を持っています)。Matrixは行列の要素として一つの値しか取り扱えないため、3値または4値のImageに変換する場合、それぞれの座標におけるR,G,B,(A)の値を示す同じ次元を持った3つまたは4つの行列を用意する必要があります。
4990 [Img[Image/ColImagetoMatrix.png]]
4993 * Matrixは精度を重視して64bit浮動小数点(double型)を取り扱います。Imageは速度を重視して8bit整数(unsigned char型)を基本として取り扱います(32bit浮動小数点 float型にすることもできます)
4999 [[6.3.5 行列の中身をオフスクリーンに描画する]]
5000 [[6.3.6 メッシュグリッドの作成]]</pre>
5002 <div title="6.3.1 行列の宣言" modifier="Psychlops_DevelopperG" modified="200908190207" created="200710171011" changecount="1">
5005 Matrixクラスは行列を取り扱います。線形代数としての取り扱いもできますが、Matlabの行列のように数値を2次元的に配置した表や画像(Image)のようなものとしても利用できます。Imageは精度が低いため、Imageに保存した色データをもとに再計算するのはお勧めできませんが、Matrixではそのような計算をしても十分な精度があります。
5008 MatrixとImageはよく似ており、相互に変換が可能です。
5010 * Matrixは精度を重視して64bit浮動小数点(double型)を取り扱います。Imageは速度を重視して8bit整数(unsigned char型)を基本として取り扱います(32bit浮動小数点 float型にすることもできます)
5011 * ImageはRGB 3値、RGBA 4値を取り扱うことが出来ます。Matrixは1値しか取り扱えないため、3値または4値のImageに変換する場合、3つまたは4つの行列を用意する必要があります。
5012 * Matrixの要素アクセスは行(縦)列(横)の順で指定し、起点は1です。ImageはX(横)Y(縦)の順で指定し、起点は0です。
5015 行列を確保するには、行数と列数を指定します。
5016 Matrix()のように宣言のみの場合は''Matrix::set()''命令で指定します。
5018 |!Matrixの宣言|Matrix()|行列の使用を宣言します|
5019 |~|Matrix(rows, cols)|指定されたrows行cols列の行列を宣言します|
5020 |~|~|long rows :行数を指定|
5021 |~|~|long cols :列数を指定|
5025 行列の宣言時に何の指定もしなかった場合、Matrix::set()命令を使用します。
5026 |!Matrix::set()|Matrix(rows, cols)|指定されたrows行cols列の行列を設定します|
5027 |~|~|long rows :行数を指定|
5028 |~|~|long cols :列数を指定|
5034 #include <fstream>
5035 #include <psychlops.h>
5036 using namespace Psychlops;
5038 Matrix LuminanceMap1(200, 100);
5040 void psychlops_main() {
5041 std::ofstream result("dump.txt");
5043 result << LuminanceMap1;
5046 次はMatrixの宣言と[[Matrix::set()]]命令を組み合わせたコードです。
5049 #include <fstream>
5050 #include <psychlops.h>
5051 using namespace Psychlops;
5053 Matrix LuminanceMap1;
5054 long Width=10, Height=10;
5056 void psychlops_main() {
5057 std::ofstream result("dump.txt");
5059 LuminanceMap1.set(Height, Width);
5060 result << LuminanceMap1;
5064 これらのコードを実行すると、プログラムと同じ場所に作成されるファイルdump.txtにMatrixの中身が保存されます。</pre>
5066 <div title="6.3.2 行列の要素の操作" modifier="Psychlops_DevelopperG" modified="200908190204" created="200710171131" changecount="2">
5067 <pre>6.3.1節ではMatrixの宣言(領域確保)のみを行っていたので、Matrixの要素に0が代入されるだけでした。
5068 この節では実際にMatrix中に数値を代入していきます。
5071 Matrix全体をひとつの値で埋め尽くすには、Matrixに対して数値の代入を行います。
5074 #include <fstream>
5075 #include <psychlops.h>
5076 using namespace Psychlops;
5078 void psychlops_main() {
5079 std::ofstream result("dump.txt");
5081 Matrix LuminanceMap1(5, 5);
5082 LuminanceMap1 = 7.0;
5083 result << LuminanceMap1;
5089 !Matrix(row, col) 単独要素
5090 Matrix中の要素にアクセスするための命令です。値を取り出すことも代入することも出来ます。二次元の配列に似た扱い方になりますが、要素の指定が行(縦)列(横)の順番であること、起点が0ではなく1であることに注意してください。
5092 |!Matrix()|Matrix(row, col) |要素を取り出します。ちょうど配列のように、値を取り出すことも代入することもできます。|
5093 |~|~|int row: 要素の行を指定します。|
5094 |~|~|int col: 要素の列を指定します。|
5095 行列の範囲外の値が指定された場合はエラーになります。エラーメッセージを表示して強制終了します。
5097 !!Matrixの要素へのアクセスの仕方
5098 2行2列目の要素に数値を代入し、さらにその値を取り出します。
5101 #include <fstream>
5102 #include <psychlops.h>
5103 using namespace Psychlops;
5105 const int Width = 10, Height = 10;
5106 Matrix LuminanceMap1(Height, Width);
5108 void psychlops_main() {
5109 std::ofstream result("dump.txt");
5111 LuminanceMap1(2,2) = 1.0;
5112 std::cout << LuminanceMap1;
5114 double a = LuminanceMap1(2,2);
5119 このプログラムを実行すると、行列の中身が10行10列で出力されます。次の行に、行列の(2,2)の値である1.0が出力されます。
5121 !!Matrixの全要素へのアクセスの仕方
5122 全要素にアクセスするには、forループを使って順次アクセスしていきます。
5125 #include <fstream>
5126 #include <psychlops.h>
5127 using namespace Psychlops;
5129 const int Width = 10, Height = 10;
5130 Matrix LuminanceMap1(Height, Width);
5132 void psychlops_main() {
5133 std::ofstream result("dump.txt");
5135 for(int row; row<Width; row++) {
5136 for(int col; col<Width; col++) {
5137 LuminanceMap1(row, col) = row+col;
5140 result << LuminanceMap1;
5145 !Matrix(row, col) 部分行列
5146 要素アクセスで整数を指定すると単一の要素にアクセスできました。行列のある範囲をまとめて取り扱いたい場合は、Range型を使って範囲を指定し、部分行列にアクセスします。
5149 [img[部分行列|image/MatrixPartial.png]]
5150 部分行列は、行列の一部分を切り取ってサイズの異なる行列であるかのように振る舞わせます。部分行列は元の行列の窓のように振る舞うため、部分行列に代入すると元の行列の一部分に代入するのと同じように作用します。
5152 |!Matrix()|Matrix(Range row, Range col)|行列の一部分から部分行列を作ります。|
5153 |~|~|Range row: 要素の行を指定します。|
5154 |~|~|Range col: 要素の列を指定します。|
5157 10×10のMatrixの一部に数値を代入します。部分行列のさらに一部の要素にもアクセスできます。
5160 #include <fstream>
5161 #include <psychlops.h>
5162 using namespace Psychlops;
5164 const int Width = 10, Height = 10;
5165 Matrix LuminanceMap1(Width, Height);
5167 void psychlops_main() {
5168 std::ofstream result("dump.txt");
5171 LuminanceMap1(2<=row<=5, 3<=col<=6) = 1.0;
5172 LuminanceMap1(2<=row<=5, 3<=col<=6)(2,2) = 2.0;
5173 result << LuminanceMap1;
5180 部分行列は元の行列の窓として機能するので、部分行列への代入をすると元の行列にも代入されます。元の行列を壊したくない場合は、明示的にコピーをとっておく必要があります。
5183 #include <fstream>
5184 #include <psychlops.h>
5185 using namespace Psychlops;
5187 const int Width = 10, Height = 10;
5188 Matrix LuminanceMap1(Width, Height), Copy;
5190 void psychlops_main() {
5191 std::ofstream result("dump.txt");
5194 LuminanceMap1(2<=row<=5, 3<=col<=6) = 1.0;
5195 Copy = LuminanceMap1(2<=row<=5, 3<=col<=6);
5197 result << LuminanceMap1 << Copy;
5202 <div title="6.3.3 行列全体の操作" modifier="Psychlops_DevelopperG" modified="200908190207" created="200710171157" changecount="1">
5204 ここでは、行列全体に対して回転、列のスライドなどの操作を行う命令について解説します。
5205 Psychlopsが行列全体に対して行える操作は大まかに以下の表に示した5つです。ここではこれらの命令について解説します。
5213 上記命令はすべて自己破壊的に作用します。
5214 つまり、命令を実行した段階で、元の行列が命令が適用された行列に置き換わってしまいます。
5215 これには、行列の次元の変換も含まれるので、上記命令を式の中に書いた場合には注意が必要です。
5216 いかに注意した方がよい命令例を挙げてみます。
5218 Matrix a(5, 3), b(3, 5);
5219 b = a.transpose() + a;
5223 一行目では、 左側のa.transpose()でaが自己破壊的に転置されてしまうため、右側のaも転置されたものとして扱われてしまいます。つまり、このような命令を実行すると、
5228 2行目では、左辺のbは3行5列、右辺のbは5行3列で不正な代入にはならず、右辺bが自己破壊的に転置されて代入されます。
5232 |!Matrix::slide|slide(row, col)|要素を行方向、列方向にずらします。あふれた分は、反対側に移動します。|
5233 |~|~|int row 行方向にずらす量を指定します。|
5234 |~|~|int col 列方向にずらす量を指定します。|
5259 #include <fstream>
5260 #include <psychlops.h>
5261 using namespace Psychlops;
5263 Psychlops::Matrix LuminanceMap1;
5264 long Width=10, Height=10;
5266 void psychlops_main() {
5267 std::ofstream result("dump.txt");
5270 LuminanceMap1.set(Height, Width);
5271 for(int row; row<Width; row++) {
5272 for(int col; col<Width; col++) {
5273 LuminanceMap1(row, col) = row*Height + col;
5276 result << LuminanceMap1.slide(1,0);
5283 |!Matrix::transpose|transpose()|行列を転置(行と列の読み替え)します。自分自身を書き換えるので注意してください。|
5297 #include <fstream>
5298 #include <psychlops.h>
5299 using namespace Psychlops;
5301 Psychlops::Matrix LuminanceMap1;
5302 long Width=10, Height=10;
5304 void psychlops_main() {
5305 std::ofstream result("dump.txt");
5308 LuminanceMap1.set(Height, Width);
5309 for(int row; row<Width; row++) {
5310 for(int col; col<Width; col++) {
5311 LuminanceMap1(row, col) = row*Height + col;
5314 result << LuminanceMap1.transpose();
5320 |!Matrix::rot90|rot90(count)|行列を90度単位で反時計回りに回転します。|
5321 |~|~|int count 回転角度を90度単位で指定します。-3,1,5のとき90度、-2,2,6のとき180度、-1,3,7のとき270度回転します。|
5335 #include <fstream>
5336 #include <psychlops.h>
5337 using namespace Psychlops;
5339 Psychlops::Matrix LuminanceMap1;
5340 long Width=16, Height=1;
5342 void psychlops_main() {
5343 std::ofstream result("dump.txt");
5346 LuminanceMap1.set(Height, Width);
5347 for(int col; col<Width; col++) {
5348 LuminanceMap1(1, col) = col;
5350 result << LuminanceMap1.rot90(1);
5357 |!Matrix::catRow|catRow(other)|この行列の次の行から別の行列を継ぎ足します。|
5358 |~|~|Matrix other: 付け足す行列を指定します。付け足してもこれ自身は消されません。|
5361 #include <fstream>
5362 #include <psychlops.h>
5363 using namespace Psychlops;
5365 Psychlops::Matrix LuminanceMap1, LuminanceMap2;
5366 long Width=5, Height=1;
5368 void psychlops_main() {
5369 std::ofstream result("dump.txt");
5372 LuminanceMap1.set(Height, Width);
5374 LuminanceMap2.set(Height, Width);
5377 LuminanceMap1.catRows(LuminanceMap2);
5378 result << LuminanceMap1;
5385 |!Matrix::reshape|reshape(row, col)|要素を変えずに、行と列の数のみを変更します。要素数(行*列)は変更前と変更後で同じでなければなりません。|
5386 |~|~|int row 新しい行数を指定します。|
5387 |~|~|int col 新しい列数を指定します。|
5401 #include <fstream>
5402 #include <psychlops.h>
5403 using namespace Psychlops;
5405 Psychlops::Matrix LuminanceMap1;
5406 long Width=16, Height=1;
5408 void psychlops_main() {
5409 std::ofstream result("dump.txt");
5412 LuminanceMap1.set(Height, Width);
5413 for(int col; col<Width; col++) {
5414 LuminanceMap1(1, col) = col;
5416 result << LuminanceMap1.reshape(4, 4);
5421 |!Matrix::min|min()|全要素中の最小値を得ます。|
5422 |!Matrix::max|max()|全要素中の最大値を得ます。|
5424 // 行列の要素に乱数を代入し、その中の最大値と最小値を抽出します。
5425 #include <fstream>
5426 #include <psychlops.h>
5427 using namespace Psychlops;
5429 Psychlops::Matrix LuminanceMap1;
5430 long Width=10, Height=10;
5432 void psychlops_main() {
5433 std::ofstream result("dump.txt");
5436 LuminanceMap1.set(Height, Width);
5437 for(int row; row<Width; row++) {
5438 for(int col; col<Width; col++) {
5439 LuminanceMap1(row, col) = Psychlops::random();
5442 result << LuminanceMap1;
5443 result << LuminanceMap1.max() << " " << LuminanceMap1.min();
5448 |!Matrix::getRows|getRows()|行数を得ます。|
5449 |!Matrix::getCols|getCols()|列数を得ます。|
5451 #include <fstream>
5452 #include <psychlops.h>
5453 using namespace Psychlops;
5455 Psychlops::Matrix LuminanceMap1;
5456 long Width=5, Height=3;
5458 void psychlops_main() {
5459 std::ofstream result("dump.txt");
5462 LuminanceMap1.set(Height, Width);
5464 result << LuminanceMap1.getRows() << " " << LuminanceMap1.getCols();
5468 <div title="6.3.4 行列の加減乗除" modifier="Psychlops_DevelopperG" modified="200908190207" created="200710171242" changecount="1">
5471 実数と行列の四則演算は、行列の各要素と実数の演算として定義されています。例えば、matrix + 1; というコードは、行列のすべての要素に1を足すことを意味しています。演算子の優先順位はC++の仕様に従います。
5474 ( matrix * 10 ) + 1; // 上の式と同じ
5477 matrix + ( 1 * 10 ); // 上の式と同じ
5480 *現在の仕様では、実数は必ず演算子の後ろに書いてください。演算子の前に実数があるとコンパイルエラーになります。
5486 |!Matrix 演算子 実数|>|
5487 |+|行列の要素すべてに実数を加算します|
5488 |-|行列の要素すべてに実数を減算します|
5489 |*|行列の要素すべてに実数を乗算します|
5490 |/|行列の要素すべてに実数を除算します|
5494 #include <fstream>
5495 #include <psychlops.h>
5496 using namespace Psychlops;
5498 void psychlops_main() {
5499 std::ofstream result("dump.txt");
5501 Psychlops::Matrix a(5, 5), b(5, 5);
5515 行列同士の演算は一般的な線形代数の定義に従います。行列の要素同士の乗算(Matlabでのドット演算子)は *~ 演算子でサポートしています。行列同士の除算は定義されていません。
5517 演算の実行前には、行列どうしの行数・列数が適切かどうかチェックし、不適切であればC++の例外機構によってプログラムが強制終了します。コンパイル時にはチェックされません。
5519 実数との演算と行列どうしの演算は同じ式中に混在させることができます。演算子の優先順位はC++の仕様に従います。
5521 |!Matrix 演算子 線形代数演算|>|
5522 |+|行列の加算をします(可換)。両辺の行数・列数は等しくなければなりません。|
5523 |-|行列の減算をします(不可換)。両辺の行数・列数は等しくなければなりません。|
5524 |*|行列の乗算をします(不可換)。左辺項の行数と右辺項の列数は等しくなければなりません。|
5525 |*~|行列の要素どうしを乗算します。Matlabでの.演算子に相当します(可換)。両辺の行数・列数は等しくなければなりません。|
5527 不可換な演算子は、右と左のMatrixを入れ替えると演算結果が変わります。
5538 #include <fstream>
5539 #include <psychlops.h>
5540 using namespace Psychlops;
5542 void psychlops_main() {
5543 std::ofstream result("dump.txt");
5545 Psychlops::Matrix a(5, 5), b(5, 5), c(5, 5);
5559 Psychlopsの行列は、四則演算において最低限の最適化のために、= 演算子で代入を行ったときにすべての計算を行うよう定義されています。このため、=演算子による代入が行われないと機能しないことがあります。
5572 また、再帰的代入(代入先の左辺の変数が右辺にも出てくること)を行う場合は、当該行列が右辺に2回以上出現すると問題が生じる可能性があります。なるべく再帰的代入は行わないでください。
5575 a = a + a * a; // 右辺にaが2回以上出てくるのは危険
5576 b = a + a * a; // 問題なし
5580 <div title="6.3.5 行列の中身をオフスクリーンに描画する" modifier="Psychlops_DevelopperG" modified="200908190208" created="200710171234" changecount="1">
5581 <pre>MatrixとImageは両方とも2次元に並べられた値(輝度や色)で、相互に変換が可能です。ImageをMatrixの計算機能を使って生成・加工するときに役立ちます。また、Matrixに変換すると各色チャネルが独立して制御しやすいため、各色チャネルを別々に操作する場合などにも役立ちます。
5582 ただし、[[この節の最初で述べたように|6.3 行列演算を行う]]、Matrixクラスの書式とImageクラスの書式がずれていることは注意して下さい。
5584 Matrix / Imageの変換命令はImage型の一部になっています。
5589 |!Image::from|from( luminance )|Matrixを輝度としてImageに読み込み、グレースケール画像を作成します。|
5590 |~|~|Matrix luminance: |
5591 |~|from( r, g, b )|Matrixを赤、緑、青チャネルとして読み込み、RGB画像を作成します。|
5595 |~|from( r, g, b, a) |Matrixを赤、緑、青、透明度チャネルとして読み込み、RGBA画像を作成します。|
5600 Imageの中身がすでに確保されていた場合は、破棄されます。
5605 |!Image::to|to( luminance )|グレースケール画像の輝度値をMatrixに書き込みます。|
5606 |~|~|Matrix luminance: |
5607 |~|to( r, g, b ) |RGB画像のMatrixを赤、緑、青チャネル値をMatrixに書き込みます。|
5611 |~|to( r, g, b, a ) |RGBA画像の赤、緑、青、透明度チャネルをMatrixに書き込みます。|
5616 書き込み先のMatrixは空である必要があります。大きさ等がImageと等しくなるように自動的に調整されます。
5620 以下のプログラムは、"sample.png"という名前のPNG画像を読み込み、赤チャネルと緑チャネルの差分を計算して"diff_sample.png"という名前で保存します。
5622 <注>:場合によってはプログラムが正常終了せず、CPUを使い切ってしまうことがあります。そのときは、タスクマネージャ等で強制終了させてください。
5625 #include <psychlops.h>
5626 using namespace Psychlops;
5628 void psychlops_main() {
5634 img.load("sample.png");
5645 img.from(dR, dG, B);
5647 img.save("diff_sample.png");
5652 <div title="6.3.6 メッシュグリッドの作成" modifier="Psychlops_DevelopperG" modified="200908190210" created="200712061907" changecount="1">
5654 メッシュグリッドとは、行列の各要素への代入を、ループを使わず普通の数式に見える形式で書くテクニックです。Matlabでよく使用されています。
5656 5 x 5 の大きさの行列に対する操作を行う場合、まず以下のような行列(メッシュグリッド)を作成します。
5673 この行列を座標値とみなし、各要素について演算する関数を適用します。
5675 Patch = cos(X) * sin(Y);
5677 これは、以下のプログラムと同等の計算を行います。
5680 for(int Y = 0; Y<5; Y++) {
5681 for(int X = 0; X<5; X++) {
5682 Patch(X, Y) = cos(X) * sin(Y);
5689 |!Matrix::mesh|mesh(Range row, int col)|行を変動値とするmeshgridを生成します|
5690 |~|~|row: 行(Imageではy座標に相当)の範囲を指定します。行数は範囲から自動決定されます。|
5691 |~|~|col: 列(Imageではx座標に相当)の列数を指定します。|
5692 |~|mesh(int row, Range col)|列を変動値とするmeshgridを生成します|
5693 |~|~|row: 行(Imageではy座標に相当)の行数を指定します。|
5694 |~|~|col: 列(Imageではx座標に相当)の範囲を指定します。行数は範囲から自動決定されます。|
5699 y = Matrix::mesh(-2<=row<=2, 5);
5700 x = Matrix::mesh(5, -2<=col<=2);
5717 行の範囲、列の範囲を同時に指定する方法もあります。引数で指定した行列にメッシュグリッドの内容が入ります。
5718 |!Matrix::mesh|mesh(Range row, Matrix r, Range col, Matrix c)|行、列を変動値とするmeshgridを生成します|
5719 |~|~|row: 行(Imageではy座標に相当)の範囲を指定します。行数は範囲から自動決定されます。|
5720 |~|~|r: 行用のメッシュグリッドを指定します。|
5721 |~|~|col: 列(Imageではx座標に相当)の範囲を指定します。列数は範囲から自動決定されます。|
5722 |~|~|c: 列用のメッシュグリッドを指定します。|
5727 Matrix::mesh(-2<=row<=2, y,-2<=col<=2,x);
5745 ここに記述されている関数はすべて自己破壊せず、新たな行列を生成して返します。
5746 |!Matrix::sin|sin(Matrix m)|mの各要素に対してsin関数の返り値を代入した行列を返します。|
5747 |!Matrix::cos|cos(Matrix m)|mの各要素にcos関数の返り値を代入した行列を返します。|
5748 |!Matrix::tan|tan(Matrix m)|mの各要素にtan関数の返り値を代入した行列を返します。|
5749 |!Matrix::exp|exp(Matrix m)|mの各要素にexp関数の返り値を代入した行列を返します。|
5750 |!Matrix::sqrt|sqrt(Matrix m)|mの各要素にsqrt関数の返り値を代入した行列を返します。|
5751 |!Matrix::pow|pow(Matrix m, double ex)|mの各要素を引数としてex乗した値を代入した行列を返します。|
5755 #include <psychlops.h>
5756 using namespace Psychlops;
5758 void psychlops_main()
5762 Canvas display(Canvas::fullscreen);
5766 y = Matrix::mesh(-100<=row<=100, 201);
5767 x = Matrix::mesh(201, -100<=col<=100);
5769 m = pow(sin(x/wave_length * 2*PI) + cos(y/wave_length * 2*PI), 4.0);
5774 while(!Input::get(Keyboard::esc)) {
5784 |!Matrix::min|min(Matrix a, Matrix b)|aとbの各要素を比較し、小さいほうを要素とする行列を返します。両Matrixの大きさは同じでなければなりません。|
5785 |!Matrix::max|max(Matrix a, Matrix b)|aとbの各要素を比較し、大きいほうを要素とする行列を返します。両Matrixの大きさは同じでなければなりません。|
5805 <div title="CSSEdit_Code" modifier="Psychlops_Admin" modified="200708280258" created="200708280257" tags="systemConfig" changecount="1">
5806 <pre>config.cssEdit ={};
5807 config.cssEdit.settings = {
5808 tags:"", //記事のタグ
5809 user:"CSSEditBackup", //作成者名
5810 backupname:"CSSEditBackup" //バックアップタイトル名
5813 config.macros.cssEdit = {};
5814 config.macros.cssEdit.handler = function(place,macroName,params){
5815 if(readOnly){return;}
5817 var s = '<form mime="text/plain" name="CSSEditForm">'
5818 + '<p><textarea name="i" rows="20" style="width:90%;"></textarea></p>'
5820 + '<input type="button" name="go" value="GO" onclick="config.macros.cssEdit.go();">&nbsp;'
5821 + '<input type="button" name="ReadStyleSheet" value="Read StyleSheet" onclick="config.macros.cssEdit.readStyleSheet();">&nbsp;'
5822 + '<input type="button" name="Backup" value="Backup" onclick="config.macros.cssEdit.Backup();">&nbsp;'
5823 + '<input type="button" name="Restore" value="Restore" onclick="config.macros.cssEdit.restore();">&nbsp;'
5824 + '<input type="reset" value="reset">&nbsp;'
5827 + '<p style="font-size:small;">CSSEdit Ver 0.1.0 Copyright &copy; 2006 <a href="<a class="linkification-ext" href="<a class="linkification-ext" href="<a class="linkification-ext" href="<a class="linkification-ext" href="<a class="linkification-ext" href="<a class="linkification-ext" href="<a class="linkification-ext" href="<a class="linkification-ext" href="<a class="linkification-ext" href="<a class="linkification-ext" href="<a class="linkification-ext" href="<a class="linkification-ext" href="http://www.potto.client.jp/" title="Linkification: http://www.potto.client.jp/">http://www.potto.client.jp/</a>" title="Linkification: <a class="linkification-ext" href="http://www.potto.client.jp/" title="Linkification: http://www.potto.client.jp/">http://www.potto.client.jp/</a>"><a class="linkification-ext" href="http://www.potto.client.jp/" title="Linkification: http://www.potto.client.jp/">http://www.potto.client.jp/</a></a>" title="Linkification: <a class="linkification-ext" href="http://www.potto.client.jp/" title="Linkification: http://www.potto.client.jp/">http://www.potto.client.jp/</a>"><a class="linkification-ext" href="http://www.potto.client.jp/" title="Linkification: http://www.potto.client.jp/">http://www.potto.client.jp/</a></a>" title="Linkification: <a class="linkification-ext" href="http://www.potto.client.jp/" title="Linkification: http://www.potto.client.jp/">http://www.potto.client.jp/</a>"><a class="linkification-ext" href="http://www.potto.client.jp/" title="Linkification: http://www.potto.client.jp/">http://www.potto.client.jp/</a></a>" title="Linkification: <a class="linkification-ext" href="http://www.potto.client.jp/" title="Linkification: http://www.potto.client.jp/">http://www.potto.client.jp/</a>"><a class="linkification-ext" href="http://www.potto.client.jp/" title="Linkification: http://www.potto.client.jp/">http://www.potto.client.jp/</a></a>" title="Linkification: <a class="linkification-ext" href="http://www.potto.client.jp/" title="Linkification: http://www.potto.client.jp/">http://www.potto.client.jp/</a>"><a class="linkification-ext" href="http://www.potto.client.jp/" title="Linkification: http://www.potto.client.jp/">http://www.potto.client.jp/</a></a>" title="Linkification: <a class="linkification-ext" href="http://www.potto.client.jp/" title="Linkification: http://www.potto.client.jp/">http://www.potto.client.jp/</a>"><a class="linkification-ext" href="http://www.potto.client.jp/" title="Linkification: http://www.potto.client.jp/">http://www.potto.client.jp/</a></a>" title="Linkification: <a class="linkification-ext" href="http://www.potto.client.jp/" title="Linkification: http://www.potto.client.jp/">http://www.potto.client.jp/</a>"><a class="linkification-ext" href="http://www.potto.client.jp/" title="Linkification: http://www.potto.client.jp/">http://www.potto.client.jp/</a></a>" title="Linkification: <a class="linkification-ext" href="http://www.potto.client.jp/" title="Linkification: http://www.potto.client.jp/">http://www.potto.client.jp/</a>"><a class="linkification-ext" href="http://www.potto.client.jp/" title="Linkification: http://www.potto.client.jp/">http://www.potto.client.jp/</a></a>" title="Linkification: <a class="linkification-ext" href="http://www.potto.client.jp/" title="Linkification: http://www.potto.client.jp/">http://www.potto.client.jp/</a>"><a class="linkification-ext" href="http://www.potto.client.jp/" title="Linkification: http://www.potto.client.jp/">http://www.potto.client.jp/</a></a>" title="Linkification: <a class="linkification-ext" href="http://www.potto.client.jp/" title="Linkification: http://www.potto.client.jp/">http://www.potto.client.jp/</a>"><a class="linkification-ext" href="http://www.potto.client.jp/" title="Linkification: http://www.potto.client.jp/">http://www.potto.client.jp/</a></a>" title="Linkification: <a class="linkification-ext" href="http://www.potto.client.jp/" title="Linkification: http://www.potto.client.jp/">http://www.potto.client.jp/</a>"><a class="linkification-ext" href="http://www.potto.client.jp/" title="Linkification: http://www.potto.client.jp/">http://www.potto.client.jp/</a></a>">potto</a></p>';
5830 var e = createTiddlyElement(place,"div",null,null,"");
5835 config.macros.cssEdit.go=function(){
5836 //setStylesheet(document.CSSEditForm.i.value,"CSSEdit");
5837 var s =document.CSSEditForm.i.value;
5838 var title = "CSSEdit_temp";
5839 config.macros.cssEdit.save(title,s,"temp","");
5840 var html = store.getRecursiveTiddlerText(title,null,10);
5841 store.deleteTiddler(title);
5843 setStylesheet(html,"CSSEdit");
5846 config.macros.cssEdit.readStyleSheet=function(){
5847 if (! window.confirm("Read StyleSheet OK ?"))
5849 document.CSSEditForm.i.value = store.getTiddlerText("StyleSheet");
5852 config.macros.cssEdit.Backup=function(){
5853 if (! window.confirm("Backup OK?"))
5856 var p = config.cssEdit.settings;
5857 var s = document.CSSEditForm.i.value;
5858 var title = p.backupname;
5859 config.macros.cssEdit.save(title,s,p.user,p.tags)
5863 config.macros.cssEdit.restore=function(){
5864 if (! window.confirm("Restore OK ?"))
5867 var p = config.cssEdit.settings;
5868 document.CSSEditForm.i.value = store.getTiddlerText(p.backupname);
5872 config.macros.cssEdit.save = function(title,s,user,tags){
5873 var p = config.HTMLEdit.settings;
5874 var now = new Date();
5875 var tiddler = store.fetchTiddler(title);
5878 created = tiddler.created;
5879 store.deleteTiddler(title);
5881 tiddler = new Tiddler();
5884 tiddler.set(title,s,user,now,tags,created);
5885 store.addTiddler(tiddler);
5888 <div title="Canvas::clear()" modifier="Psychlops_DevelopperG" modified="200910080819" created="200712131650" changecount="4">
5889 <pre>|!Canvas::clear()|clear([[Psychlops::Color]] col,TargetSurface)|>|指定したCanvasを指定色で塗りつぶします|
5890 |~|~|[[Psychlops::Color]] col |塗りつぶす色を指定|
5891 |~|~|TargetSurface|塗りつぶす画面を指定|
5892 |~|clear(TargetSurface)|指定したCanvasを塗りつぶします。色の指定には[[Canvas::setClearColor()]]命令を使用します|
5893 |~|~|TargetSurface|塗りつぶす画面を指定|
5895 [[2.1.2 Canvasの基本構造と操作]]</pre>
5897 <div title="Canvas::copy()" modifier="Psychlops_DevelopperG" modified="200910080847" created="200709170159" changecount="24">
5898 <pre>[[Psychlops::Rectangle]]型(source)で指定される範囲内の情報を[[Psychlops::Rectangle]]型(target)の範囲に複写または移動します。移動元と移動先の矩形の大きさは同じにする必要があります。
5899 |!Canvas::copy()|copy([[Psychlops::Rectangle]] source, [[Psychlops::Rectangle]] target, bool copymode, [[Psychlops::Color]] col)|>|! [[Psychlops::Rectangle]]型の情報を[[Psychlops::Image]]型に複写または移動する|>|>|
5900 |~|~|[[Psychlops::Rectangle]] source|移動(複写)元のRectangleを指定|>|
5901 |~|~|[[Psychlops::Rectangle]] target|移動(複写)先のRectangleを指定|>|
5902 |~|~|bool copymode| trueを指定する場合は移動処理(カット), falseを指定する場合は複写処理(コピー)|指定しない場合、false(複写)が指定される|
5903 |~|~|[[Psychlops::Color]] col|移動処理時の移動元の塗りつぶし色を指定|指定しない場合、黒色が指定される|
5904 |~|~|~|~|複写の場合、指定不要|
5906 !!Canvas::copy()の書き方
5907 rect1の内部にある描画内容ををrect2の内部に複写します。
5910 #include <psychlops.h>
5911 using namespace Psychlops;
5913 Psychlops::Rectangle rect1(100,100);
5914 Psychlops::Rectangle rect2(100,100);
5916 void psychlops_main() {
5918 Canvas sampleA(Canvas::fullscreen);
5920 while(!Input::get(Keyboard::spc)){
5921 sampleA.rect(rect1,Color::yellow);
5922 sampleA.copy(rect1,rect2);
5928 <div title="Canvas::flip()" modifier="Psychlops_DevelopperG" modified="200910080820" created="200709130046" changecount="6">
5929 <pre>表と裏の2つの画面の入れ替え処理です。
5930 描画内容はCanvas::flip() 処理で画面を入れ替えることで初めて画面上に表示されます。
5931 この命令は画面の垂直同期信号(=ビデオカードが1枚の画面を書ききったことを示す信号; Vsync)に同期して、画面の切り替えを行います。
5932 * 参考 [[Tips: Canvas::flip()とコマ落ち]]
5934 |!Canvas::flip()|flip(int)|int入力値のリフレッシュ分描画内容を表示します|
5935 |~|~|引数を入力しない場合(flip())、flip(1)が指定されます|
5936 |~|flipAfter(int wait)|int wait回のリフレッシュ後まで待ってからflip()を実行して描画内容を画面に反映させます|
5937 |~|flipAndWait(int wait)|もっとも近い垂直同期のタイミングでflip()を実行した後に、int wait回ののリフレッシュ分の表示を予約します。次にflip()命令がコールされても、int wait回ののリフレッシュ分が経過するまでは何も起こりません(flip()と全く同じ効果ですが、引数は省略不可)|
5939 flip()系の命令は画面描画の時間制御をする上でもっとも重要な命令です。上の表だけではわかりにくいでしょうから、図と実例を用いて各命令の働きをもう少し詳しく見ていきましょう。
5941 flipAfter(), flipAndWait()命令の動きを図解すると以下のようになります。以下の図ではflipAfter(3)とflip(3)(あるいはflipAndWait(3))の場合を例としてあげました。
5942 [img[image/psychlops_flip.png]]
5944 コード例は[[2.1.3節|2.1.3 Canvasのメンバ取得と変数の表示]]参照</pre>
5946 <div title="Canvas::getXXXX()" modifier="Kazushi Maruya" modified="200712131657" created="200712131652" changecount="2">
5947 <pre>|!Canvas::getXXXX()|getCenter()|画面の中心座標(x,y)を取得します|
5948 |~|getHcenter()|横方向の中心座標(x)を取得します|
5949 |~|getVcenter()|縦方向の中心座標(y)を取得します|
5950 |~|getHeight()|画面の高さ(ピクセル)を取得します|
5951 |~|getWidth()|画面の幅(ピクセル)を取得します|
5952 |~|getColorDepth()|カラーモード(ビット)を取得します|
5953 |~|getRefreshRate()|リフレッシュレート(Hz)を取得します|
5955 [[2.1.3 Canvasのメンバ取得と変数の表示]]</pre>
5957 <div title="Canvas::lastFailedFrames()" modifier="Psychlops_DevelopperG" modified="200910080852" created="200709170302" changecount="6">
5958 <pre>[[Canvas::flip()]]命令で画面の切り替えをする時にコマ落ちする場合があります。
5960 * 参考 [[Tips: Canvas::flip()とコマ落ち]]
5961 |!Canvas::lastFailedFrames()|lastFailedFrames()|!1秒間で落ちたフレーム数を取得します|
5963 !!Canvas::lastFailedFrames()の書き方
5964 フレームが落ちたときにグレーで画面をクリアしています。
5965 (フレームが落ちるのが一瞬のため[[Canvas::var()]]命令で落ちた値を確認することは困難なため)
5966 何枚落ちているかは右上の赤い数字が落ちたフレーム数の和算となっているので確認してください。
5967 Display::~の書き方については [[Tips: CanvasクラスとDisplayクラス]]を参照してください。
5971 #include <psychlops.h>
5972 using namespace Psychlops;
5974 void ullmancylinder() {
5975 const int DOTCNT = 15000;
5978 double bg_lum = 0.0;
5979 double velocity = 1.0;
5983 double CylinderRadius=200;
5987 Independent << dotcnt | "Number of Dots" | 1< rng< DOTCNT | 10 | 1000 ;
5988 Independent << velocity | "Dot Velocity" | 0.0<=rng<=10.0 | 0.1 | 0.05;
5989 Independent << SOA | "SOA Frames" | 1.0<=rng<=60.0 | 1 | 1;
5990 Independent << bg_lum | "Luminance Inv." | 0.0<=rng<=1.0 | 1 | 1;
5991 Independent << SIZE | "Dot Size" | 0< rng<=10 | 1 | 1;
5992 Independent << CylinderRadius| "Cylinder Size" | 0< rng<= 400 | 1 | 50;
5995 Canvas canvas(Canvas::fullscreen);
5996 Psychlops::Rectangle rect(SIZE,SIZE);
5997 Psychlops::Color bgcolor(0), dotcolor(1.0-bg_lum);
6000 double x[DOTCNT], dx[DOTCNT], y[DOTCNT];
6002 for(int i=0; i<DOTCNT; i++) {
6003 x[i] = Psychlops::random(CylinderRadius*2*PI);
6004 y[i] = Psychlops::random(CylinderRadius*2)+200;
6006 Display::watchFPS();
6007 while(!Input::get(Keyboard::spc)) {
6008 bgcolor.set(bg_lum);
6009 dotcolor.set(1.0-bg_lum);
6010 canvas.clear(bgcolor);
6011 rect.resize(SIZE,SIZE);
6016 for(int i=0; i<DOTCNT; i++) dx[i] = (int)(CylinderRadius*sin(x[i]+SOA_pp*phase/100.0))+canvas.getHcenter();//各ドット位置の計算
6017 for(int i=0; i<dotcnt; i++) rect.centering(dx[i], y[i]).draw(dotcolor);
6019 a1=Display::lastFailedFrames();
6020 if(a1)Display::clear(Color(0.25,0.25,0.25));
6027 void psychlops_main()
6031 p.setDesign(Procedure::DEMO);
6032 p.setProcedure(ullmancylinder);
6040 <div title="Canvas::line()" modifier="Psychlops_DevelopperG" modified="200910080837" created="200712131651" changecount="4">
6041 <pre>|!Canvas::line()|line(double x1, double y1, double x2, double y2, [[Psychlops::Color]] col)|>|!開始座標(X1,Y1)から終端座標(X2,Y2)まで指定色の線を描画します|
6042 |~|~|double x1|描画する線の開始座標x1を指定|
6043 |~|~|double y1|描画する線の開始座標y1を指定|
6044 |~|~|double x2|描画する線の終端座標x2を指定|
6045 |~|~|double y2|描画する線の終端座標y2を指定|
6046 |~|~|[[Psychlops::Color]] col|描画する点の色を指定|
6047 |~|line([[Psychlops::Point]] point1, [[Psychlops::Point]] point2,[[Psychlops::Color]] col)|>|!開始Point座標(x1,x1)から終端Point座標(X2,Y2)まで指定色の線を描画します|
6048 |~|~|[[Psychlops::Point]] point1|開始座標(x1,y1)を指定|
6049 |~|~|[[Psychlops::Point]] point2|終端座標(x2,y2)を指定|
6050 |~|~|[[Psychlops::Color]] col|描画する点の色を指定|
6051 画面上に線を描画するためには、「どこから」「どこに」「何色」の線を引くのかの指定が必要です。
6052 「どこから」は開始座標(x1,y1)、「どこに」は終端座標(x2,y2)、「何色」は[[Psychlops::Color]]型で指定します。
6053 座標はCanvasの左上端が座標の基点(x=0,y=0)となっています。
6054 これらの座標はPoint型の変数を使用して指定することもできます。
6056 [img[image/canvasline.png]]
6058 [[2.2.2 画面上に線を描画する]]</pre>
6060 <div title="Canvas::msg()" modifier="Psychlops_DevelopperG" modified="200910080829" created="200709130031" changecount="8">
6061 <pre>画面上に任意の文字列を表示します。
6062 |!Canvas::msg()|msg(char* string,double x,double y,[[Psychlops::Color]] col)|>|!char* stringを座標(x,y)に指定色で表示します|
6063 |~|~|schar* string|画面表示をする文字列を指定|
6064 |~|~|double x|画面表示をする座標(x)を指定|
6065 |~|~|double y|画面表示をする座標(y)を指定|
6066 |~|~|[[Psychlops::Color]] col|描画する点の色を指定<br>(設定しない場合、白が設定される)|
6067 |~|msg(string str,double x,double y,[[Psychlops::Color]] col)|>|!string strを座標(x,y)に指定色で表示します|
6068 |~|~|string str|画面表示をする文字列を指定|
6069 |~|~|double x|画面表示をする座標(x)を指定|
6070 |~|~|double y|画面表示をする座標(y)を指定|
6071 |~|~|[[Psychlops::Color]] col|描画する点の色を指定<br>(設定しない場合、白が設定される)|
6073 [[2.1.3 Canvasのメンバ取得と変数の表示]]</pre>
6075 <div title="Canvas::oval()" modifier="Psychlops_DevelopperG" modified="200910080839" created="200712131653" changecount="2">
6076 <pre>|!Canvas::oval()|oval([[Psychlops::Rectangle]] rect, [[Psychlops::Color]] col)|>|![[Psychlops::Rectangle]] rectで指定された直径の円を指定色で塗りつぶし描画します|
6077 |~|~|[[Psychlops::Rectangle]] rect|描画する円の直径|
6078 |~|~|[[Psychlops::Color]] col|描画する点の円を指定|
6080 [img[image/canvasoval.png]]</pre>
6082 <div title="Canvas::pix()" modifier="Psychlops_DevelopperG" modified="200910080833" created="200712131650" changecount="6">
6083 <pre>|!Canvas::pix()|pix(double x,double y,[[Psychlops::Color]] col) |>|!座標(x,y)に指定色の点を描画します|
6084 |~|~|double x|描画する点の座標軸xを指定|
6085 |~|~|double y|描画する点の座標軸yを指定|
6086 |~|~|[[Psychlops::Color]] col|描画する点の色を指定|
6087 |~|pix([[Psychlops::Point]] point,[[Psychlops::Color]] col) |>|!Point座標(x,y)に指定色の点を描画します|
6088 |~|~|[[Psychlops::Point]] point |座標(x,y)を指定|
6089 |~|~|[[Psychlops::Color]] col|描画する点の色を指定|
6090 |~|pix(int dotsCnt,double* xArray,double* yArray),[[Psychlops::Color]]* colArray )|>|!配列の座標(x[],y[])に指定色(配列を用いて複数色指定)の点を描画します|
6091 |~|~|int dotsCnt|描画する点の数を指定(後ろの引数の配列の個数を超えてはいけない)|
6092 |~|~|double* xArray| 描画する点の座標軸xが格納された配列名(ポインタ)を指定|
6093 |~|~|double* yArray| 描画する点の座標軸yが格納された配列名(ポインタ)を指定|
6094 |~|~|[[Psychlops::Color]]* colArray|描画する点の色が格納された配列名(ポインタ)を指定|
6095 |~|pix(int dotsCnt,double* xArray,double* yArray,[[Psychlops::Color]] col)|>|!配列の座標(x[],y[])に指定色(一色のみ)の点を描画します|
6096 |~|~|int dotsCnt|描画する点の数(配列数)を指定|
6097 |~|~|double* xArray|描画する点の座標軸xが格納された配列名(ポインタ)を指定|
6098 |~|~|double* yArray|描画する点の座標軸yが格納された配列名(ポインタ)を指定|
6099 |~|~|[[Psychlops::Color]] col|描画する点の色を指定|
6100 |~|pix(int dotsCnt,[[Psychlops::Point]]* pointArray,[[Psychlops::Color]]* colArray)|>|!配列Point座標(x[],y[])に指定色の点を描画します|
6101 |~|~|int dotsCnt|描画する点の数(配列数)を指定|
6102 |~|~|[[Psychlops::Point]]* pointArray|配列の座標(x[],y[])が格納された配列名(ポインタ)を指定|
6103 |~|~|[[Psychlops::Color]]* colArray|描画する点の色が格納された配列名(ポインタ)を指定|
6105 [img[image/canvaspix.png]]
6107 [[2.2.1 画面上に点を描画する-Pointクラス1-]]</pre>
6109 <div title="Canvas::pix(double, double, double)という命令はコンパイルできないのですか?" modifier="Psychlops_DevelopperG" created="200908100457" changecount="1">
6110 <pre>Canvas::pix(double, double, double)という命令はコンパイルできないのですか?
6112 できません。最後の色の引数をColor(double)としてColor型にキャスト(変換)してください。
6114 (正) display.pix(100,100,Color(0.5));
6115 (誤) display.pix(100,100,0.5);
6118 <div title="Canvas::progressbar()" modifier="Psychlops_DevelopperG" modified="200910080852" created="200712131654" changecount="4">
6119 <pre>|!Canvas::progressbar()|void progressbar(X now, X max)|!現在のnowの値のmaxに対する比率をプログレスバーとして表示します。now, maxは数値型であればどの型でもかまいません|>|
6120 |~|~|X now|現在の進行度を示す変数値(型は数値型)|
6121 |~|~|X max|nowの最終的な到達値(型は数値型)|
6122 |~|void progressbar(double ratio)|!ratioで示される比率をプログレスバーとして表示します。|>|
6123 |~|~|double ratio|比率の値(0.0~1.0)|
6125 [[5.2.2 画面に反映されない描画の進行度を表示する-プログレスバー]]</pre>
6127 <div title="Canvas::rect()" modifier="Psychlops_DevelopperG" modified="200910080838" created="200709122308" changecount="13">
6130 |!Canvas::rect()|rect([[Psychlops::Rectangle]] rect, [[Psychlops::Color]] col)|>|!四角形を指定色で描画します|
6131 |~|~|([[Psychlops::Rectangle]] rect|描画する四角形を指定|
6132 |~|~|[[Psychlops::Color]] col|描画する四角形の色を指定|
6133 [[2.2.3 画面上に四角形を描画する-Rectangleクラス1-]]</pre>
6135 <div title="Canvas::setClearColor()" modifier="Psychlops_DevelopperG" modified="200910080852" created="200709170246" changecount="6">
6136 <pre>[[Canvas::clear()|2.1.2 Canvasの基本構造と操作]]命令使用時に塗りつぶす色の設定を行います。
6137 setClearColor(Color)命令使用時には必ず[[Canvas::clear()|2.1.2 Canvasの基本構造と操作]]命令を使用してください。
6138 |!Canvas::setClearColor()|setClearColor([[Psychlops::Color]] col)|>|!指定したCanvasを塗りつぶすための色を設定します|
6139 |~|~|[[Psychlops::Color]] col |塗りつぶす色を指定|
6141 !!Canvas::setClearColor()の書き方
6142 今ディスプレイに表示されている画面(紙の表)を白く塗りつぶします。
6145 #include <psychlops.h>
6146 using namespace Psychlops;
6148 void psychlops_main() {
6150 Canvas sampleA(Canvas::fullscreen);
6151 sampleA.setClearColor(Color::white);
6152 sampleA.clear(Canvas::FRONT);
6153 while(!Input::get(Keyboard::spc));
6157 <div title="Canvas::setGammaTable()" modifier="Psychlops_DevelopperG" modified="200910080850" created="200802130702" changecount="5">
6158 <pre>他の多くの描画ライブラリ同様、CRTの各画素における輝度レベルとPsychlopsで指定する輝度レベルは線形の関係を維持しません。これを簡易的に補正するために、R,G,Bの各色チャンネルについて変換表(LUTテーブル)を用いた補正を行う方法が広く用いられています。
6159 Psychlopsでは、この変換表を指定することで、[[ガンマ補正|Tips: ガンマ補正]]された色を表示することが可能です。このための命令として、setGammaTable()命令があります。
6161 通常は[[Color::setGammaTable()]]を用いますが、将来的にCanvasごとに別の補正値が必要なときのためにこの関数が予約されています。
6163 |!Canvas::setGammaTable()|setGammaTable(std::vector<double> Cr, std::vector<double> Cg, std::vector<double> Cb)|>|!Canvasが確保している表示装置のDACテーブルを直接書き換え、ガンマ補正を行います。|
6164 |~|~|std::vector<double> Cr|R(赤)チャンネルの変換表|
6165 |~|~|std::vector<double> Cg|G(緑)チャンネルの変換表|
6166 |~|~|std::vector<double> Cb|B(青)チャンネルの変換表|
6167 *2 256個以下でも自動的に線形補完されますが、正確な補完は256個すべて指定することで実現されます。
6168 *3 値は配列またはC++STLの動的配列(std::vector<double>)で指定し、8ビット環境なら256段階の理論値を配列の要素番号とし、それぞれの段階の補償値を配列要素に書き込みます。
6170 コード例は[[ガンマ補正|Tips: ガンマ補正]]参照</pre>
6172 <div title="Canvas::setGammaValue()" modifier="Psychlops_DevelopperG" modified="200910080849" created="200709170234" changecount="11">
6173 <pre>他の多くの描画ライブラリ同様、CRTの各画素における輝度レベルとPsychlopsで指定する輝度レベルは線形の関係を維持しません。これを簡易的に補正するために、R,G,Bの各色チャンネルについてGamma関数を用いた補正を行う方法が広く用いられています。
6174 Psychlopsでは、このGamma関数の係数を指定することで、[[ガンマ補正|Tips: ガンマ補正]]された色を表示することが可能です。このための命令として、setGammaValue()命令があります。
6176 通常は[[Color::setGammaValue()]]を用いますが、将来的にCanvasごとに別の補正値が必要なときのためにこの関数が予約されています。
6179 |!Canvas::setGammaValue()|setGammaValue(double Cr, double Cg, double Cb)|>|!Canvasが確保している表示装置のDACテーブルを直接書き換え、ガンマ補正を行います。|
6180 |~|~|double Cr|表示装置のR(赤)チャンネルのガンマ係数|
6181 |~|~|double Cg|表示装置のG(緑)チャンネルのガンマ係数|
6182 |~|~|double Cb|表示装置のB(青)チャンネルのガンマ係数|
6183 * 指定する値は表示装置のガンマ値そのものです。Psychlops側で補償指数を自動決定します。
6185 コード例は[[ガンマ補正|Tips: ガンマ補正]]参照</pre>
6187 <div title="Canvas::showFPS()" modifier="Psychlops_DevelopperG" modified="200910080852" created="200712131656" changecount="2">
6188 <pre>|!Canvas::showFPS()|void showFPS(void)|!FPSチェッカのレポートを画面左上に表示します|
6190 [[5.2.1 描画の時間精度を確認する-FPSチェッカー]]
6193 <div title="Canvas::to()" modifier="Psychlops_DevelopperG" modified="200910080843" created="200709130159" changecount="6">
6194 <pre>[[Psychlops::Rectangle]]型で指定される範囲内の情報を[[Psychlops::Image]]型に複写します。
6195 この命令と''Image::save()''命令を組み合わせることによってImage上以外で描画した画像が保存できます。
6196 |!Canvas::to()|to([[Psychlops::Image]] img, [[Psychlops::Rectangle]] rect)|>|![[Psychlops::Rectangle]]型の情報を[[Psychlops::Image]]型に複写する|
6197 |~|~|[[Psychlops::Image]] img |複写先のImage|
6198 |~|~|[[Psychlops::Rectangle]] rect |複写元のRectangle|
6200 コード例は[[3.3.1節|3.3.1 画像ファイルを保存する]]参照</pre>
6202 <div title="Canvas::var()" modifier="Psychlops_DevelopperG" modified="200910080830" created="200709130030" changecount="26">
6203 <pre>取得した変数の値を画面に表示します。
6204 |!Canvas::var()|var(string str, double x, double y,[[Psychlops::Color]] col, bool fillForward)|>|>|>|!string strで指定された変数の値を取得して座標(x,y)に指定色で表示します|
6205 |~|~|>|>|string str|画面表示をする変数を指定|
6206 |~|~|>|>|double x|画面表示をする座標(x)を指定|
6207 |~|~|>|>|double y|画面表示をする座標(y)を指定|
6208 |~|~|>|>|[[Psychlops::Color]] col|描画する点の色を指定(設定しない場合、白が設定される)|
6209 |~|~|~|bool fillForward|表示位置の指定|true |指定座標(x,y)から左部分に表示する|
6210 |~|~|~|~|~|false |指定座標(x,y)から右部分に表示する|
6211 |~|~|~|~|>|>|(指定しない場合、falseが設定される)|
6213 [[2.1.3 Canvasのメンバ取得と変数の表示]]</pre>
6215 <div title="Canvas::watchFPS()" modifier="Psychlops_DevelopperG" modified="200910080852" created="200712131655" changecount="2">
6216 <pre>|!Canvas::watchFPS()|void watchFPS(void)|!FPSチェッカをonにします|
6218 [[5.2.1 描画の時間精度を確認する-FPSチェッカー]]</pre>
6220 <div title="CanvasMode" modifier="Psychlops_DevelopperG" modified="200910080814" created="200708212026" changecount="7">
6222 |Canvas::fullscreen()|画面いっぱいに描画領域を確保する|
6223 |Canvas::window()|指定サイズの描画領域を確保する|</pre>
6225 <div title="Canvasの宣言" modifier="Psychlops_DevelopperG" modified="200910080808" created="200712131704" changecount="1">
6227 |~|Canvas(int width,int height,int colordepth,double refreshrate)|>|!全画面表示をします|
6228 |~|~|int width|水平方向の解像度(画面の横幅)を指定|
6229 |~|~|int height|垂直方向の解像度(画面の縦幅)を指定|
6230 |~|~|int colordepth|カラーモード(ビット)を指定|
6231 |~|~|double refreshrate|リフレッシュレート( Hz )を指定|
6232 |~|Canvas(Canvasmode mode, const Display disp)|>|!現在の画面モードで画面を確保する場合に使います|
6233 |~|~|Canvasmode mode|[[表示モード|CanvasMode]](フルスクリーンorウィンドウ)を選択|
6234 |~|~|const [[Display|Displayの指定値]] disp|[[表示するディスプレイ|Displayの指定値]]を選択(省略可能)|
6235 |~|~|>|マルチディスプレイ環境では、最後の引数でどのディスプレイに表示するか指定できます。省略時はプライマリディスプレイになります。詳細は [[マルチディスプレイの選択]]を参照ください。|
6236 |~|Canvas(int width, int height, int colordepth, double refreshrate, const Display disp)|>|!画面モードを指定してフルスクリーン画面を確保します|
6237 |~|~|int width|水平方向の解像度(画面の横幅)を指定|
6238 |~|~|int height|垂直方向の解像度(画面の縦幅)を指定|
6239 |~|~|int colordepth|カラーモード(ビット)を指定|
6240 |~|~|double refreshrate|リフレッシュレート( Hz )を指定|
6241 |~|~|const [[Display|Displayの指定値]] disp|[[表示するディスプレイ|Displayの指定値]]を選択(省略可能)|
6242 |~|~|>|指定した画面モードが選べなかった場合は、例外が送出されプログラムは強制終了します。|
6243 |~|Canvas(int width, int height, CanvasMode mode, const Display disp)|>|!指定の幅と高さでウィンドウを確保します。|
6244 |~|~|int width|水平方向の解像度(画面の横幅)を指定|
6245 |~|~|int height|垂直方向の解像度(画面の縦幅)を指定|
6246 |~|~|Canvasmode mode|[[表示モード|CanvasMode]](フルスクリーンorウィンドウ)を選択|
6247 |~|~|>|指定した画面モードが選べなかった場合は、例外が送出されプログラムは強制終了します。|
6248 |~|Canvas(Rectangle rect, CanvasMode mode , const Display disp)|>|!バーチャルスクリーン上の指定の矩形でウィンドウを確保します。|
6249 |~|~|Rectangle rect|ディスプレイの大きさ、ディスプレイの位置を指定|
6250 |~|~|Canvasmode mode|[[表示モード|CanvasMode]](フルスクリーンorウィンドウ)を選択|
6251 |~|~|const [[Display|Displayの指定値]] disp|[[表示するディスプレイ|Displayの指定値]]を選択(省略可能)|
6252 |~|~|>|指定した画面モードが選べなかった場合は、例外が送出されプログラムは強制終了します。|
6254 * ここではコンストラクタ(宣言)の書式のみ記述していますが、同じ引数セットのset関数がすべて用意されています。
6255 * CRT は機器によってリフレッシュレートが小数点以下の桁でわずかに異なります(60Hzと表示されていても、厳密には60.2Hzなど小数点以下の桁があります)。初期化時には小数点以下の桁は無視して確保しますが、正確なタイミングでflipするには、ディスプレイ装置の設定等で小数点以下の桁をあらかじめ調べて、正確な値を指定することをお勧めします。
6256 * 1.3以降でではウィンドウモードが実装されました。
6258 [[2.1.1 Canvasの宣言]]</pre>
6260 <div title="Canvas型に関して" modifier="Psychlops_DevelopperG" modified="200908100506" created="200712131716" changecount="9">
6261 <pre>* [[描画内容を画面に反映させたい|Canvas::flip()]]
6262 * [[描画内容を画面に反映させて、一定時間表示したい|Canvas::flip()]]
6263 * [[Canvas型の変数のメンバを取得したい|Canvas::getXXXX()]]
6265 * [[プログラム内の変数の値を表示したい|Canvas::var()]]
6266 * [[簡単なメッセージを表示したい|Canvas::msg()]]
6268 * [[画面領域全てを塗りつぶしたい|Canvas::clear()]]
6269 * [[点を打ちたい|Canvas::pix()]]
6270 * [[四角形を描画したい|Canvas::rect()]]
6271 * [[円・楕円を描画したい|Canvas::oval()]]
6273 * [[描画内容を画面上で複写したい|Canvas::copy()]]
6274 * [[描画内容をオフスクリーンへ複写したい|Canvas::to()]]
6276 * [[FPSチェッカを使用したい|5.2.1 描画の時間精度を確認する-FPSチェッカー]]
6278 * [[Canvas::pix(double, double, double)という命令はコンパイルできないのですか?]]
6279 * [[止まった絵を描画するときCanvas.flip()を単独でwhileループの中に書くと、画面がちらつきます。]]</pre>
6281 <div title="Clock::at_msec()" modifier="Psychlops_Admin" created="200709142037" changecount="1">
6282 <pre>タイマーのカウントをmsec単位に変換します。
6283 |!Clock::at_msec()| double Clock::at_msec(void)| カウントをmsec単位に変換(double)して返す|
6285 詳細とコード例は[[5.1節|5.1 時間を計測する]]参照</pre>
6287 <div title="Clock::update()" modifier="Psychlops_Admin" created="200709142036" changecount="1">
6288 <pre>Timerに現在のCPUタイマーの値を入力します。
6289 |!Clock::update()| Clock::update(void)| 現在のCPUタイマーの値を入力する|
6291 詳細とコード例は[[5.1節|5.1 時間を計測する]]参照</pre>
6293 <div title="Color()の宣言" modifier="Psychlops_Admin" modified="200709132234" created="200709131904" changecount="4">
6294 <pre>描画対象に色をつけたい場合、Color()の宣言を行い色の設定をします。
6295 宣言のみの場合、[[Color::set()|2.2.5 描画する図形の色を指定する-Colorクラス-]]で色の詳細情報を設定しますが
6296 宣言文のみでも色の情報の設定が可能です。
6298 |!Color()の宣言|Color()|[[Psychlops::Color]]型の宣言を行います|
6299 |~|Color(int r, int g, int b, int a)|[[Psychlops::Color]]型の宣言を行いr,g,bと透明度aで構成されるカラー情報を設定します|
6300 |~|~|int r:赤の輝度を指定。範囲は0~1|
6301 |~|~|int g:緑の輝度を指定。範囲は0~1|
6302 |~|~|int b:青の輝度を指定。範囲は0~1|
6303 |~|~|int a:透明度を指定。範囲は0~1。0に近いほど透明度が高い|
6304 |~|Color(double r, double g, double b, double a)|[[Psychlops::Color]]型の宣言を行いr,g,bと透明度aで構成されるカラー情報を設定します|
6305 |~|~|double r:赤の輝度を指定。範囲は0.0~1.0|
6306 |~|~|double g:緑の輝度を指定。範囲は0.0~1.0|
6307 |~|~|double b:青の輝度を指定。範囲は0.0~1.0|
6308 |~|~|double a:透明度を指定。範囲は0.0~1.0。0.0に近いほど透明度が高い|
6309 |~|Color(int r, int g, int b)|[[Psychlops::Color]]型の宣言を行いr,g,bで構成されるカラー情報を設定します|
6310 |~|~|int r:赤の輝度を指定。範囲は0.0~1.0|
6311 |~|~|int g:緑の輝度を指定。範囲は0.0~1.0|
6312 |~|~|int b:青の輝度を指定。範囲は0.0~1.0|
6313 |~|~|透明度は1.0が固定で指定されている|
6314 |~|Color(long r, long g, long b)|[[Psychlops::Color]]型の宣言を行いr,g,bで構成されるカラー情報を設定します|
6315 |~|~|long r:赤の輝度を指定。範囲は0.0~1.0|
6316 |~|~|long g:緑の輝度を指定。範囲は0.0~1.0|
6317 |~|~|long b:青の輝度を指定。範囲は0.0~1.0|
6318 |~|~|透明度は1.0が固定で指定されている|
6319 |~|Color(float r, float g, float b)|[[Psychlops::Color]]型の宣言を行いr,g,bで構成されるカラー情報を設定します|
6320 |~|~|float r:赤の輝度を指定。範囲は0.0~1.0|
6321 |~|~|float g:緑の輝度を指定。範囲は0.0~1.0|
6322 |~|~|float b:青の輝度を指定。範囲は0.0~1.0|
6323 |~|~|透明度は1.0が固定で指定されている|
6324 |~|Color(double r, double g, double b)|[[Psychlops::Color]]型の宣言を行いr,g,bで構成されるカラー情報を設定します|
6325 |~|~|double r:赤の輝度を指定。範囲は0.0~1.0|
6326 |~|~|double g:緑の輝度を指定。範囲は0.0~1.0|
6327 |~|~|double b:青の輝度を指定。範囲は0.0~1.0|
6328 |~|~|透明度は1.0が固定で指定されている|
6329 |~|Color(int gray)|[[Psychlops::Color]]型の宣言を行いグレースケールの輝度を設定します|
6330 |~|~|int gray: 輝度を指定。範囲は0.0~1.0|
6331 |~|~|透明度は1.0が固定で指定されている|
6332 |~|Color(long gray)|[[Psychlops::Color]]型の宣言を行いグレースケールの輝度を設定します|
6333 |~|~|long gray: 輝度を指定。範囲は0.0~1.0|
6334 |~|~|透明度は1.0が固定で指定されている|
6335 |~|Color(float gray)|[[Psychlops::Color]]型の宣言を行いグレースケールの輝度を設定します|
6336 |~|~|透明度は1.0が固定で指定されている|
6337 |~|~|float gray: 輝度を指定。範囲は0.0~1.0|
6338 |~|~|透明度は1.0が固定で指定されている|
6339 |~|Color(double gray)|[[Psychlops::Color]]型の宣言を行いグレースケールの輝度を設定します|
6340 |~|~|double gray: 輝度を指定。範囲は0.0~1.0|
6341 |~|~|透明度は1.0が固定で指定されている|
6344 <div title="Color::XXXX" modifier="YourName" modified="200803100613" created="200708231845" changecount="2">
6345 <pre>あらかじめ用意された基本的な色指定を呼び出すことが可能です。
6347 |!Color::white|白(1.0, 1.0, 1.0)|
6348 |!Color::black|黒(0.0, 0.0, 0.0)|
6349 |!Color::gray|灰色(0.5, 0.5, 0.5)|
6350 |!Color::red|赤(1.0, 0.0, 0.0)|
6351 |!Color::green|緑( 0.0, 1.0, 0.0)|
6352 |!Color::blue|青(0.0, 0.0, 1.0)|
6353 |!Color::cyan|シアン(0.0, 1.0, 1.0)|
6354 |!Color::magenta|マゼンタ(1.0, 1.0, 0.0)|
6355 |!Color::yellow|黄(1.0, 1.0, 0.0)|</pre>
6357 <div title="Color::get()" modifier="YourName" modified="200803140530" created="200802130630" changecount="5">
6358 <pre>Color型に格納されている各値を得ます。
6360 |!Color::get()|get(double r, double g, double b, double a)|r,g,bと透明度aで構成されるカラー情報を取得します。|
6361 |~|~|double r:赤の輝度(0.0~1.0)|
6362 |~|~|double g:緑の輝度(0.0~1.0)|
6363 |~|~|double b:青の輝度(0.0~1.0)|
6364 |~|~|double a:透明度(0.0~1.0)。0に近いほど透明度が高い|
6366 |!Color::getX()|getR()|赤チャネルの値を返します|
6367 |~|getG()|緑チャネルの値を返します|
6368 |~|getB()|青チャネルの値を返します|
6369 |~|getA()|透明チャネルの値を返します|
6376 col.get(r, g, b, a);
6377 // r => 0.5, g => 0.5, b => 0.5, a => 1
6379 col.set(1.0, 0.5, 0.0, 0.5);
6387 <div title="Color::set()" modifier="YourName" modified="200803100609" created="200712131736" changecount="4">
6388 <pre>|!Color::set()|set(double r, double g, double b, double a)|r,g,bと透明度aで構成されるカラー情報を設定します。|
6389 |~|~|double r:赤の輝度を指定。範囲は0.0~1.0|
6390 |~|~|double g:緑の輝度を指定。範囲は0.0~1.0|
6391 |~|~|double b:青の輝度を指定。範囲は0.0~1.0|
6392 |~|~|double a:透明度を指定。範囲は0.0~1.0 (0.0に近いほど透明度が高い)|
6393 |~|set(double r, double g, double b)|r,g,bで構成されるカラー情報を設定します。|
6394 |~|~|double r:赤の輝度を指定。範囲は0.0~1.0|
6395 |~|~|double g:緑の輝度を指定。範囲は0.0~1.0|
6396 |~|~|double b:青の輝度を指定。範囲は0.0~1.0|
6397 |~|set(double gray)|グレースケールの輝度を設定します。|
6398 |~|~|double gray: 輝度を指定。範囲は0.0~1.0|
6400 [[2.2.5 描画する図形の色を指定する-Colorクラス-]]</pre>
6402 <div title="Color::setGammaTable()" modifier="YourName" modified="200802131125" created="200802130701" changecount="3">
6403 <pre>他の多くの描画ライブラリ同様、CRTの各画素における輝度レベルとPsychlopsで指定する輝度レベルは線形の関係を維持しません。
6404 これを簡易的に補正するために、R,G,Bの各色チャンネルについて変換表(LUTテーブル)を用いた補正を行う方法が広く用いられています。
6405 Psychlopsでは、この変換表を指定することで、[[ガンマ補正|Tips: ガンマ補正]]された色を表示することが可能です。
6406 このための命令として、setGammaTable()命令があります。
6408 |!Canvas::setGammaTable()|setGammaTable(double Cr[], double Cg[], double Cb[], int table_size)|この命令以降実行されるPsychlops::Colorに対して変換表によるを自動的に行います^^*1^^|
6409 |~|~|Cr: R(赤)チャンネルの変換表|
6410 |~|~|Cg: G(緑)チャンネルの変換表|
6411 |~|~|Cb: B(青)チャンネルの変換表|
6412 |~|~|table_size: 表の項目数。8ビット環境下ではできれば256個の項目を用意してください。^^*2^^|
6413 |~|setGammaTable(std::vector<double> Cr, std::vector<double> Cg, std::vector<double> Cb)|この命令以降実行されるPsychlops::Colorに対してGamma補正を自動的に行います^^*1^^|
6414 |~|~|Cr: R(赤)チャンネルの変換表|
6415 |~|~|Cg: G(緑)チャンネルの変換表|
6416 |~|~|Cb: B(青)チャンネルの変換表|
6417 *1 Psychlopsはまずハードウェアによるガンマ補正([[Canvas::setGammaTable()]])を試みます。失敗すると、ソフトウェアによるエミュレーションを試みます。
6418 *2 256個以下でも自動的に線形補完されますが、正確な補完は256個すべて指定することで実現されます。
6419 *3 値は配列またはC++STLの動的配列(std::vector<double>)で指定し、8ビット環境なら256段階の理論値を配列の要素番号とし、それぞれの段階の補償値を配列要素に書き込みます。
6421 コード例は[[ガンマ補正|Tips: ガンマ補正]]参照
6424 <div title="Color::setGammaValue" modifier="YourName" modified="200802130652" created="200802130648" changecount="4">
6427 <div title="Color::setGammaValue()" modifier="YourName" modified="200802131124" created="200802130652" changecount="3">
6428 <pre>他の多くの描画ライブラリ同様、CRTの各画素における輝度レベルとPsychlopsで指定する輝度レベルは線形の関係を維持しません。これを簡易的に補正するために、R,G,Bの各色チャンネルについてGamma関数を用いた補正を行う方法が広く用いられています。
6429 Psychlopsでは、このGamma関数の係数を指定することで、[[ガンマ補正|Tips: ガンマ補正]]された色を表示することが可能です。
6430 このための命令として、setGammaValue()命令があります。
6432 |!Canvas::setGammaValue()|setGammaValue(double Cr, double Cg, double Cb)|この命令以降実行されるPsychlops::Colorに対してGamma補正を自動的に行います^^*1^^|
6433 |~|~|Cr: R(赤)チャンネルのガンマ係数|
6434 |~|~|Cg: G(緑)チャンネルのガンマ係数|
6435 |~|~|Cb: B(青)チャンネルのガンマ係数|
6436 *1 Psychlopsはまずハードウェアによるガンマ補正([[Canvas::setGammaValue()]])を試みます。失敗すると、ソフトウェアによるエミュレーションを試みます。
6437 *2 指定する値は表示装置のガンマ値そのものです。Psychlops側で補償指数を自動決定します。
6439 コード例は[[ガンマ補正|Tips: ガンマ補正]]参照
6442 <div title="Color::strict_match" modifier="YourName" modified="200802201822" created="200802130704" changecount="3">
6443 <pre>実験時に色指定を厳密に行わせるよう強制させることができます。具体的には、ガンマ補正や色指定で値が0以上1以下にならないケースで警告する、ソフトウェアエミュレーション(後述)を禁止して警告を出す、という規制が行われます。以下のコードをpsychlops_main()の冒頭に記述してください。
6445 Color::strict_match = true;
6448 なお、警告はC++の例外機能を使って送出されます。警告を受け入れる構文を書かない限り強制終了します。警告を受け入れるtry..catch構文については、C++の参考書をお読みください。
6451 <div title="Color型に関して" modifier="Kazushi Maruya" created="200712131735" changecount="1">
6452 <pre>* [[描画色を設定したい|Color::set()]]
6453 * [[白、黒などのデフォルトの描画色を使用したい|Color::XXXX]]</pre>
6455 <div title="DefaultTiddlers" modifier="PsychlopsAdmin" created="200709161806" changecount="1">
6458 <div title="Displayの指定値" modifier="Psychlops_DevelopperG" modified="200910080812" created="200910080753" changecount="5">
6459 <pre>クラス定数として以下のものが用意されています。
6460 |Display::secondary|その時点でのプライマリディスプレイを取得します|
6461 |Display::primary|その時点でのセカンダリディスプレイを取得します|
6463 より複雑な環境では以下の命令を使ってリストを取得してください。
6464 |!Display::list()|ディスプレイのリストを動的配列(std::vector<Display>)で取得します。|
6469 <div title="GaborImage" modifier="Kazushi Maruya" modified="200712061920" created="200710070016" changecount="3">
6474 virtual void draw(Image &area, double ori, double freq, double phase, double Lmean, double *contrast) {
6475 int center_x = area.getCenter().getX(), center_y = area.getCenter().getY();
6476 int width = area.getWidth()/2, height = area.getHeight()/2;
6478 double sigma = (width)/3;
6479 double SINORI, COSORI, _x, gr, ga, ph, R, G, B;
6482 COSORI=cos(-ori*PI/180);
6483 SINORI=sin(-ori*PI/180);
6487 for(int y=-height; y<height; y++) {
6488 for(int x=-width; x<width; x++) {
6489 _x= COSORI * x - SINORI * y;
6490 ga=exp(-0.5*(pow(x/(sigma),2))-0.5*(pow(y/(sigma), 2)));
6491 gr=(1+cos(2*PI*freq*_x+ph))*0.5;
6492 R=Lmean+Lmean*contrast[0]*(gr*ga);
6493 G=Lmean+Lmean*contrast[1]*(gr*ga);
6494 B=Lmean+Lmean*contrast[2]*(gr*ga);
6495 tmpcol.set(R, G, B);
6496 area.pix(x+width,y+height,tmpcol);
6501 virtual void draw(Image &area, double ori, double freq, double phase, double Lmean, double contrast) {
6502 int center_x = area.getCenter().getX(), center_y = area.getCenter().getY();
6503 int width = area.getWidth()/2, height = area.getHeight()/2;
6505 double sigma = (width)/3;
6507 double SINORI, COSORI, _y, gr, ga, ph;
6509 COSORI=cos(ori*PI/180);
6510 SINORI=sin(ori*PI/180);
6514 for(int y=-height; y<height; y++) {
6515 for(int x=-width; x<width; x++) {
6516 _y= SINORI * x + COSORI * y;
6517 ga=exp(-0.5*(pow(x/(sigma),2))-0.5*(pow(y/(sigma), 2)));
6518 gr=(1+cos(2*PI*freq*_y+ph))*0.5;
6519 tmpcol.set(Lmean+Lmean*contrast*(gr*ga));
6520 area.pix(x+width,y+height,tmpcol);
6527 <div title="Gabor方位判断の実験プログラム例" modifier="Kazushi Maruya" modified="200712061920" created="200709162000" changecount="6">
6529 #include <psychlops.h>
6530 #include <stdio.h>
6531 using namespace Psychlops;
6533 class IndependentVariables
6535 int MaxVarNum, MaxStepNum;
6536 double *VariableData;
6537 int *VariableStepNumber;
6538 int conditionNumber;
6541 Psychlops::Matrix CondMtx;
6546 IndependentVariables(int n, int m, int repeat): MaxVarNum(n), MaxStepNum(m){
6547 VariableData = (double *)malloc(sizeof(double)*(n+1)*m);
6548 VariableStepNumber = (int *)malloc(sizeof(double)*m);
6549 repeatNumber=repeat;
6553 virtual ~IndependentVariables(void){
6555 free(VariableStepNumber);
6558 virtual int set(int vnum, int arraynum, double *array){
6559 if(vnum<MaxVarNum+1 && arraynum<MaxStepNum+1){
6560 VariableStepNumber[vnum]=arraynum;
6561 for(int i=0; i<arraynum; i++){*(VariableData+(vnum+1)*MaxStepNum+i)=*(array+i);}
6568 virtual int randomize(char* dataname=NULL){
6569 trialNumber=repeatNumber;
6570 for(int i=0; i<MaxVarNum; i++)trialNumber*=VariableStepNumber[i];
6571 if(trialNumber<1){return -1;}
6572 CondMtx.set(trialNumber, MaxVarNum);
6575 double temp=1, temp2;
6577 //Make Output Matrix
6579 for(i=0; i<MaxVarNum; i++){
6580 for(j=0; j<i; j++){temp*=(double)VariableStepNumber[j];}
6581 for(j=0; j<trialNumber; j++){CondMtx(j+1,i+1)=(int)ceil(j/temp)% VariableStepNumber[i];}
6585 //Randomize Output Matrix
6586 for(i=1; i<trialNumber;i++){
6587 a = Psychlops::random(i-1)+1;
6588 for(j=1;j<MaxVarNum+1;j++){
6590 CondMtx(a,j)=CondMtx(i,j);
6595 //swtich of the condition matrix save
6597 std::ofstream output;
6598 output.open(dataname);
6599 output << CondMtx << std::endl;
6605 virtual double get(int vnum, int trial_now){
6606 if(vnum<MaxVarNum && (int)trial_now<trialNumber)
6607 return *(VariableData+(vnum+1)*MaxStepNum+getCondMtx(vnum, trial_now));
6611 virtual int getCondMtx(int vnum, unsigned int trial_now){
6612 if(vnum<MaxVarNum && (int)trial_now<trialNumber)
6613 return CondMtx( trial_now+1, vnum+1);
6617 virtual double getIndependent(int vnum, int stepnum){
6618 if(vnum<MaxVarNum && stepnum<VariableStepNumber[vnum])
6619 return *(VariableData+(vnum)*MaxStepNum+stepnum);
6624 virtual int getMStep(int vnum){
6625 if(vnum<MaxVarNum)return VariableStepNumber[vnum];
6633 virtual void draw(Image &area, double ori, double freq, double phase, double Lmean, double *contrast) {
6634 int center_x = area.getCenter().getX(), center_y = area.getCenter().getY();
6635 int width = area.getWidth()/2, height = area.getHeight()/2;
6637 double sigma = (width)/3;
6638 double SINORI, COSORI, _x, gr, ga, ph, R, G, B;
6641 COSORI=cos(-ori*PI/180);
6642 SINORI=sin(-ori*PI/180);
6646 for(int y=-height; y<height; y++) {
6647 for(int x=-width; x<width; x++) {
6648 _x= COSORI * x - SINORI * y;
6649 ga=exp(-0.5*(pow(x/(sigma),2))-0.5*(pow(y/(sigma), 2)));
6650 gr=(1+cos(2*PI*freq*_x+ph))*0.5;
6651 R=Lmean+Lmean*contrast[0]*(gr*ga);
6652 G=Lmean+Lmean*contrast[1]*(gr*ga);
6653 B=Lmean+Lmean*contrast[2]*(gr*ga);
6654 tmpcol.set(R, G, B);
6655 area.pix(x+width,y+height,tmpcol);
6660 virtual void draw(Image &area, double ori, double freq, double phase, double Lmean, double contrast) {
6661 int center_x = area.getCenter().getX(), center_y = area.getCenter().getY();
6662 int width = area.getWidth()/2, height = area.getHeight()/2;
6664 double sigma = (width)/3;
6666 double SINORI, COSORI, _y, gr, ga, ph;
6668 COSORI=cos(ori*PI/180);
6669 SINORI=sin(ori*PI/180);
6673 for(int y=-height; y<height; y++) {
6674 for(int x=-width; x<width; x++) {
6675 _y= SINORI * x + COSORI * y;
6676 ga=exp(-0.5*(pow(x/(sigma),2))-0.5*(pow(y/(sigma), 2)));
6677 gr=(1+cos(2*PI*freq*_y+ph))*0.5;
6678 tmpcol.set(Lmean+Lmean*contrast*(gr*ga));
6679 area.pix(x+width,y+height,tmpcol);
6685 void psychlops_main() {
6687 Canvas sampleA(Canvas::fullscreen);
6688 IndependentVariables invar(2,5,10);
6690 double gaborOrientation[5]={0,1,2,3,4}; //Gaborの方位
6691 double duration[3]={100,250,500}; //刺激の提示時間
6693 invar.set(0, 5, gaborOrientation);
6694 invar.set(1, 3, duration);
6695 invar.randomize("ConditionMatrix.dat");
6697 GaborImage gaborIMG;
6698 Psychlops::Image gIMG[10];
6699 double gIMGsize=100, ori;
6700 for(int i=0; i<10; i++){
6701 if(i<5){ ori=90.0-pow(2.0, i);}
6702 else{ ori=90.0+pow(2.0,i-4);}
6703 gIMG[i].set(gIMGsize, gIMGsize);
6704 gaborIMG.draw(gIMG[i], ori, 1.0/30.0, 0.0, 0.5, 0.2);
6705 gIMG[i].centering();
6709 const int trialNum=2*5*10;
6710 int answer[trialNum], orientationCondition[trialNum], durationCondition[trialNum], ans;
6711 int ori_now, dura_now;
6713 char trial_header[64];
6715 for(int trial=0; trial<trialNum; trial++){
6717 direction=Psychlops::random(2); //0:right 1:left
6718 ori_now=invar.get(0,trial)+5*direction;
6719 dura_now=sampleA.getRefreshRate()*(invar.get(1,trial)/1000);
6723 for(int i=0; i<64; i++) trial_header[i] = 0;// 文字列の初期化
6724 sprintf(trial_header, "%s%d%s%d","Trial: ", trial, " /", trialNum );//試行番号の埋め込み
6725 sampleA.message(trial_header, sampleA.getHcenter(), sampleA.getVcenter()-100, Color::green);//キー待ちメッセージの描画
6727 while(!Input::get(Keyboard::spc));
6731 gIMG[ori_now].draw();
6732 sampleA.flip(dura_now);
6738 if(Input::get(Keyboard::j)){ans=0;break;} //"j"なら右(0)
6739 if(Input::get(Keyboard::f)){ans=1;break;}//"f"なら左(1)
6740 if(Input::get(Keyboard::esc)){exit(0);}
6742 if(ans==direction)answer[trial]=1;
6743 else answer[trial]=0;
6744 orientationCondition[trial]=invar.get(0, trial);
6745 durationCondition[trial]=invar.get(1, trial);
6749 sampleA.message("Press space key to exit.", sampleA.getHcenter(), sampleA.getVcenter()-100, Color::green);
6750 Data::savearray("ExptData.dat", "Duration /t Orientation /t Answer", trialNum, durationCondition, orientationCondition, answer );
6755 <div title="GettingStarted" modifier="PsychlopsAdmin" created="200707220303" changecount="1">
6756 <pre>To get started with this blank TiddlyWiki, you'll need to modify the following tiddlers:
6757 * SiteTitle & SiteSubtitle: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)
6758 * MainMenu: The menu (usually on the left)
6759 * DefaultTiddlers: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened
6760 You'll also need to enter your username for signing your edits: <<option txtUserName>></pre>
6762 <div title="Image()の宣言" modifier="Psychlops_Admin" modified="200709142109" created="200709142045" changecount="2">
6763 <pre>[[Psychlops::Image]]型は主に画像を取り扱います。
6764 [[Psychlops::Image]]型の命令を使用する前に必ず宣言が必要です。
6765 宣言のみの場合、[[Image::set()]]でImageの詳細情報を設定しますが
6768 |!Imageの宣言|Image()|オフスクリーン領域を宣言します|
6769 |~|Image([[Psychlops::Rectangle]] rec,PixelComponentsCnt pixcompcntval)|指定された[[Psychlops::Rectangle]] 型と同じ大きさのオフスクリーン領域を宣言します|
6770 |~|~|[[Psychlops::Rectangle]] rect :[[Psychlops::Rectangle]] 型を指定|
6771 |~|~|PixelComponentsCnt pixcompcntval :[[カラーモード]]を指定。省略した場合はRGBが指定される|
6772 |~|Image(long x, long y, PixelComponentsCnt pixcompcntval)|指定されたx×yと同じ大きさのオフスクリーン領域を宣言します|
6773 |~|~|long x :オフスクリーン領域の横幅を指定|
6774 |~|~|long y :オフスクリーン領域の縦幅を指定|
6775 |~|~|PixelComponentsCnt pixcompcntval :[[カラーモード]]を指定。省略した場合はRGBが指定される|
6777 コード例は[[3.1.1節|3.1.1 オフスクリーン領域の宣言]]参照</pre>
6779 <div title="Image::centering()" modifier="Kazushi Maruya" modified="200712131731" created="200712131730" changecount="2">
6780 <pre>!Image::centering()
6781 [[Psychlops::Image]]型の領域を移動します。
6782 座標を指定する場合、その座標に領域の中心が設定されます。
6783 |!Image::centering()|centering()|[[Psychlops::Image]]型の領域を画面中心に移動します|
6784 |~|centering(double x, double y)|[[Psychlops::Image]]型の領域の中心を指定した座標(x,y)に移動します|
6785 |~|~|double x:移動する領域のx座標を指定|
6786 |~|~|double y:移動する領域のy座標を指定|
6788 [[3.1.3 オフスクリーン上の任意の位置に描画する]]</pre>
6790 <div title="Image::clear()" modifier="Psychlops_Admin" modified="200709142113" created="200709142111" changecount="2">
6791 <pre>オフスクリーン領域をクリアします。
6792 |!Image::clear()|clear([[Psychlops::Color]] col)|指定したオフスクリーン領域を指定色でクリアします|
6793 |~|~|[[Psychlops::Color]] col:クリアする色を指定|
6795 !!Image::clear()の書き方
6799 #include <psychlops.h>
6800 using namespace Psychlops;
6802 double width=100,height=100;
6803 Psychlops::Rectangle rect1(width,height);
6804 Psychlops::Image Noise1(rect1);
6806 void psychlops_main() {
6808 Canvas sampleA(Canvas::fullscreen);
6809 Noise1.clear(Color::magenta);
6812 while(!Input::get(Keyboard::spc));
6816 <div title="Image::draw()" modifier="YourName" modified="200803100605" created="200709142049" changecount="5">
6818 実際にオフスクリーン領域に描画する際の命令です。
6819 この命令を記述しないといくら[[Image::pix()]]命令や[[Image::rect()]]命令を記述しても描画されませんので注意してください。
6821 |!Image::draw()|draw()|指定したオフスクリーンを描画します|
6822 |~|draw(double left, double top)|指定したオフスクリーン領域を(left,top)を中心とした座標に設定します|
6823 |~|~|double left: 描画するオフスクリーン領域の水平座標値|
6824 |~|~|double top: 描画するオフスクリーン領域の垂直座標値|
6826 コード例は[[3.1.2節|3.1.2 オフスクリーン上に点・四角形を描画する]]参照</pre>
6828 <div title="Image::from()" modifier="Kazushi Maruya" created="200712131721" changecount="1">
6829 <pre>|!Image::from|from( luminance )|Matrixを輝度としてImageに読み込み、グレースケール画像を作成します。|
6830 |~|~|Matrix luminance: |
6831 |~|from( r, g, b )|Matrixを赤、緑、青チャネルとして読み込み、RGB画像を作成します。|
6835 |~|from( r, g, b, a) |Matrixを赤、緑、青、透明度チャネルとして読み込み、RGBA画像を作成します。|
6840 Imageの中身がすでに確保されていた場合は、破棄されます。
6842 [[5.3.5 行列の中身をオフスクリーンに描画する]]</pre>
6844 <div title="Image::getXXXX()" modifier="Psychlops_Admin" modified="200709142210" created="200709142100" changecount="7">
6845 <pre>Imageの様々な情報を、Image::getXXXX()命令を使って取得します(XXXXにはそれぞれの情報に対応した名前が入ります)。
6846 |!Image::getXXXX()|getCenter()|オフスクリーン領域の中心座標(x,y)を取得します|
6847 |~|getHcenter()|オフスクリーン領域の横方向の中心座標(x)を取得します|
6848 |~|getVcenter()|オフスクリーン領域の縦方向の中心座標(y)を取得します|
6849 |~|getHeight()|オフスクリーン領域の高さ(ピクセル)を取得します|
6850 |~|getWidth()|オフスクリーン領域の幅(ピクセル)を取得します|
6851 |~|getPix(int x, int y)|オフスクリーン領域上の座標(x,y)の色を取得します|
6852 |~|~|int x :オフスクリーン領域上の座標xを指定|
6853 |~|~|int y :オフスクリーン領域上の座標yを指定|
6854 |~|~|注:オフスクリーン領域の左上を座標(0,0)とする。オフスクリーン領域外の座標は指定しないこと|
6857 [[Psychlops::Image]]型の情報を取得し画面表示を行います。
6858 画面表示は[[Canvas::var()]]命令と[[Canvas::msg()]]命令を使用します。
6861 #include <psychlops.h>
6862 using namespace Psychlops;
6864 Psychlops::Image Noise1(300,300);
6865 Psychlops::Point point1;
6866 Psychlops::Color col1,col2;
6867 Psychlops::Rectangle rect1(50,50);
6868 double d1,d2,d3,d4,x,y;
6870 void psychlops_main() {
6872 Canvas sampleA(Canvas::fullscreen);
6874 col1.set(0.8,0.4,0.1);
6877 point1=Noise1.getCenter();
6878 d1=Noise1.getHcenter();
6879 d2=Noise1.getVcenter();
6880 d3=Noise1.getHeight();
6881 d4=Noise1.getWidth();
6882 col2=Noise1.getPix(100,100);
6887 sampleA.msg("getCenter_x:",50,200);
6888 sampleA.var(x,200,200);//getCenter:X
6889 sampleA.msg("getCenter_y:",50,250);
6890 sampleA.var(y,200,250);//getCenter:Y
6891 sampleA.msg("getHcenter:",50,300);
6892 sampleA.var(d1,200,300);//getHcenter
6893 sampleA.msg("getVcenter:",50,350);
6894 sampleA.var(d2,200,350);//getVcenter
6895 sampleA.msg("gettHeight:",50,400);
6896 sampleA.var(d3,200,400);//getHeight
6897 sampleA.msg("getWidth:",50,450);
6898 sampleA.var(d4,200,450);//getWidth
6900 sampleA.oval(rect1,col2);
6904 while(!Input::get(Keyboard::spc));
6908 <div title="Image::line()" modifier="YourName" modified="200803100604" created="200709142118" changecount="4">
6909 <pre>オフスクリーン領域に線を描画します。
6910 指定する座標は画面上左上を座標(0,0)にするのではなく
6911 オフスクリーン領域の左上を座標(0,0)とします。
6912 |!Image::line()|line(double x1, double y1, double x2, double y2, [[Psychlops::Color]] col)|座標(x1,y1)から座標(x2,y2)に指定色で線を描画します|
6913 |~|~|double x1 :描画する線の開始水平座標値|
6914 |~|~|double y1 :描画する線の開始垂直座標値|
6915 |~|~|double x2 :描画する線の終端水平座標値|
6916 |~|~|double y2 :描画する線の終端垂直座標値|
6917 |~|~|[[Psychlops::Color]] col:描画する線の描画色|
6920 座標(100,100)から座標(200,200)に赤線を描画します。
6921 指定Image外の座標に設定すると描画されないのでオフスクリーンの位置に注意すること。
6924 #include <psychlops.h>
6925 using namespace Psychlops;
6927 Psychlops::Image Noise1(500,500);
6929 void psychlops_main() {
6931 Canvas sampleA(Canvas::fullscreen);
6932 Noise1.line(100,100,200,200,Color::red);
6935 while(!Input::get(Keyboard::spc));
6939 <div title="Image::load()" modifier="Kazushi Maruya" modified="200712131733" created="200712131731" changecount="2">
6941 既存の任意の画像ファイルを読み込みます。
6943 |!Image::load()|load(filename)|指定した画像ファイルを読み込みます|>|>|
6944 |~|~|filename: 読み込む画像ファイルの名前を指定|>|>|
6945 |~|~|読込み先を指定しない場合のパス|Mac:Macintosh HD¥ユーザ¥ユーザ名(任意)¥書類¥Psychlops¥|>|
6946 |~|~|~|Win:実行ファイルと同じパス|>|
6947 |~|~|読込みをする場合のパス指定|%APP%|Mac:実行バンドル(.app)と同じパス|
6948 |~|~|~|~|Win:実行ファイルと同じパス|
6949 |~|~|~|%RESOURCES%|Mac:実行バンドルの内部のResourcesフォルダ|
6950 |~|~|~|~|Win:実行ファイルのパスの直下のrscという名前のフォルダ|
6951 |~|~|~|%~USER_HOME%|Mac:"~/"と同じ(/Users/(user)/)|
6952 |~|~|~|~|Win:ユーザープロファイルフォルダ(C:\Documents and Settings\(user)\)|
6953 |~|~|~|%~USER_SETTING%|Mac:"~/Library/Psychlops/"|
6954 |~|~|~|~|Win:"%~AppData%Psychlops"|
6955 |~|~|~|%~USER_DOCUMENTS%|Mac:"~/Documents/Psychlops"|
6956 |~|~|~|~|Win:ユーザープロファイルフォルダ\My Documents\Psychlops|
6957 |~|~|~|%~USER_DOCUMENTS_ROOT%|Mac:"~/Documents"|
6958 |~|~|~|~|Win:ユーザープロファイルフォルダMy Documents\|
6959 **VISTA環境の場合、パス指定が正常に動作しません。VISTA環境の方はお手数ですが保存先を指定しないでお使いください。
6961 [[3.3.2 画像ファイルを読み込み表示する]]</pre>
6963 <div title="Image::oval()" modifier="Psychlops_Admin" modified="200709142129" created="200709142125" changecount="2">
6964 <pre>オフスクリーン領域に円を描画します。
6965 円の描画位置はオフスクリーン領域の座標(0,0)に描画されます。
6966 |!Image::oval()|([[Psychlops::Rectangle]] rect, [[Psychlops::Color]] col)|[[Psychlops::Rectangle]] rectで指定された直径の円を指定色で塗りつぶし描画します|
6967 |~|~|[[Psychlops::Rectangle]] rect:描画する円の直径|
6968 |~|~|[[Psychlops::Color]] col:描画する円の色を指定|
6971 座標(300,300)からはじまるオフスクリーン領域に直径(100,100)の赤円を描画します。
6974 #include <psychlops.h>
6975 using namespace Psychlops;
6977 Psychlops::Image Noise1(300,300);
6978 Psychlops::Rectangle rect1(100,100);
6980 void psychlops_main() {
6982 Canvas sampleA(Canvas::fullscreen);
6984 Noise1.oval(rect1,Color::red);
6987 while(!Input::get(Keyboard::spc));
6991 <div title="Image::pix()" modifier="YourName" modified="200803100603" created="200709142049" changecount="4">
6993 オフスクリーン領域に点を描画するための命令です。
6994 [[Canvas::pix()|2.2.1 画面上に点を描画する-Pointクラス1-]]命令同様に点を描画するために「座標」と「色の指定」を行います。
6995 色の指定についての詳細は[[2.2.5 描画する図形の色を指定する-Colorクラス-]]を参照してください。
6997 |!Image::pix()|pix(double x, double y, [[Psychlops::Color]] col)|座標(x,y)に[[Psychlops::Color]] col色の点を描画します。|
6998 |~|~|double x: 描画する点の水平座標値|
6999 |~|~|double y: 描画する点の垂直座標値|
7000 |~|~|[[Psychlops::Color]] col:描画する点の描画色|
7001 |~|pix([[Psychlops::Point]] po, [[Psychlops::Color]] col)|Point座標(x,y)に[[Psychlops::Color]] col色の点を描画します|
7002 |~|~|[[Psychlops::Point]] po :描画する点の座標|
7003 |~|~|[[Psychlops::Color]] col:描画する点の色|
7005 コード例は[[3.1.2節|3.1.2 オフスクリーン上に点・四角形を描画する]]参照</pre>
7007 <div title="Image::rect()" modifier="YourName" modified="200803100619" created="200709142050" changecount="5">
7008 <pre>オフスクリーン領域に四角形を描画するための命令です。
7009 実際の描画処理は[[Image::draw()]]命令で行われます。
7010 色の指定についての詳細は[[2.2.5 描画する図形の色を指定する-Colorクラス-]]を参照してください。
7012 |!Image::rect()|rect([[Psychlops::Rectangle]] rect, [[Psychlops::Color]] col)|[[Psychlops::Rectangle]]型の四角形を[[Psychlops::Color]] col色で描画します|
7013 |~|~|[[Psychlops::Rectangle]] rect : 矩形の描画範囲|
7014 |~|~|[[Psychlops::Color]] col:描画する四角形の描画色|
7016 コード例は[[3.1.2節|3.1.2 オフスクリーン上に点・四角形を描画する]]参照</pre>
7018 <div title="Image::save()" modifier="Kazushi Maruya" created="200712131732" changecount="1">
7019 <pre>|!Image::save()|save(filename)|指定したImageを保存します|>|>|
7020 |~|~|filename: 保存するImageの画像ファイル名称(拡張子まで)を指定|>|>|
7021 |~|~|保存先を指定しない場合のパス|Mac:Macintosh HD¥ユーザ¥ユーザ名(任意)¥書類¥Psychlops¥|>|
7022 |~|~|~|Win:実行ファイルと同じパス|>|
7023 |~|~|保存をする場合のパス指定|%APP%|Mac:実行バンドル(.app)と同じパス|
7024 |~|~|~|~|Win:実行ファイルと同じパス|
7025 |~|~|~|%RESOURCES%|Mac:実行バンドルの内部のResourcesフォルダ|
7026 |~|~|~|~|Win:実行ファイルのパスの直下のrscという名前のフォルダ|
7027 |~|~|~|%~USER_HOME%|Mac:"~/"と同じ(/Users/(user)/)|
7028 |~|~|~|~|Win:ユーザープロファイルフォルダ(C:\Documents and Settings\(user)\)|
7029 |~|~|~|%~USER_SETTING%|Mac:"~/Library/Psychlops/"|
7030 |~|~|~|~|Win:"%~AppData%Psychlops"|
7031 |~|~|~|%~USER_DOCUMENTS%|Mac:"~/Documents/Psychlops"|
7032 |~|~|~|~|Win:ユーザープロファイルフォルダ\My Documents\Psychlops|
7033 |~|~|~|%~USER_DOCUMENTS_ROOT%|Mac:"~/Documents"|
7034 |~|~|~|~|Win:ユーザープロファイルフォルダMy Documents\|
7035 **VISTA環境の場合、パス指定が正常に動作しません。VISTA環境の方はお手数ですが保存先を指定しないでお使いください。
7037 [[3.3.1 画像ファイルを保存する]]</pre>
7039 <div title="Image::set()" modifier="YourName" modified="200803100559" created="200709142046" changecount="4">
7040 <pre>宣言時に何の指定もしなかった場合、Image::set()命令を使用します。
7041 |!Image::set()|set([[Psychlops::Rectangle]] rect, PixelComponentsCnt pixcompcntval)|指定された[[Psychlops::Rectangle]] 型と同じ大きさのオフスクリーン領域を設定します|
7042 |~|~|[[Psychlops::Rectangle]] rect :オフスクリーンの大きさを与える[[Psychlops::Rectangle]] 型を指定(座標は引き継がれない)|
7043 |~|~|PixelComponentsCnt pixcompcntval :[[カラーモード]]を指定。省略した場合はRGBが指定される|
7044 |~|set(long x, long y, PixelComponentsCnt pixcompcntval)|指定されたx×yと同じ大きさのオフスクリーン領域を設定します|
7045 |~|~|long x :オフスクリーン領域の横幅|
7046 |~|~|long y :オフスクリーン領域の縦幅|
7047 |~|~|PixelComponentsCnt pixcompcntval :[[カラーモード]]を指定。省略した場合はRGBが指定される|
7049 コード例は[[3.1.1節|3.1.1 オフスクリーン領域の宣言]]参照</pre>
7051 <div title="Image::shift()" modifier="YourName" modified="200803100601" created="200709142053" changecount="2">
7052 <pre>[[Psychlops::Image]]型の領域を移動します。
7053 元の座標(x1,y1)を基点に入力値(x,y)だけ移動します。
7054 |!Image::shift()|shift(double h, double v)|座標(X,Y)を(h,v)だけ移動します|
7055 |~|~|double x:右水平方向の移動量|
7056 |~|~|double y:下垂直方向の移動量|
7058 コード例は[[3.1.3節|3.1.3 オフスクリーン上の任意の位置に描画する]]参照</pre>
7060 <div title="Image::to()" modifier="Psychlops_DevelopperG" modified="200910070830" created="200712131722" changecount="31">
7061 <pre>|!Image::to|>|>|!グレースケール画像の輝度値をMatrixに書き込みます|
7062 |~|to( Matrix luminance )|
7063 |~|~|Matrix luminance|輝度値の行列|
7064 |~|to( Point po, double width, double height, Matrix luminance)|
7065 |~|~|Point po |コピー元領域の左上座標<br>(省略時は画像左上を起点とします)|
7066 |~|~|double width |コピー元領域の幅(pixel)|
7067 |~|~|double height |コピー元領域の高さ(pixel)|
7068 |~|~|Matrix luminance |輝度値の行列|
7069 |~|to( Point po, Rectangle rect, Matrix lumiannce) |
7070 |~|~|Point po |コピー元領域の左上座標<br>(省略時は画像左上を起点とします)|
7071 |~|~|Rectangle rect |コピー元領域のサイズを持つ矩形|
7072 |~|~|Matrix luminance|輝度値の行列|
7073 |~|to( Interval h_intvl, Interval v_intvl, Matrix luminance) |
7074 |~|~|Interval h_intvl |コピー元領域の横座標の範囲(pixel)|
7075 |~|~|Interval v_intvl |コピー元領域の縦座標の範囲(pixel)|
7076 |~|~|Matrix luminance |輝度値の行列|
7078 |!Image::to|>|>|!RGB画像の赤、緑、青チャネル値をMatrixに書き込みます|
7080 |~|~|Matrix r |赤チャンネルの輝度値の行列|
7081 |~|~|Matrix g |緑チャンネルの輝度値の行列|
7082 |~|~|Matrix b |青チャンネルの輝度値の行列|
7083 |~|to( Point po, double width, double height, Matrix r, Matrix g, Matrix b) |
7084 |~|~|Point po |コピー元領域の左上座標<br>(省略時は画像左上を起点とします)|
7085 |~|~|double width |コピー元領域の幅(pixel)|
7086 |~|~|double height |コピー元領域の高さ(pixel)|
7087 |~|~|Matrix r |赤チャネルの輝度値の行列|
7088 |~|~|Matrix g |緑チャネルの輝度値の行列|
7089 |~|~|Matrix b |青チャネルの輝度値の行列|
7090 |~|to( Point po, Rectangle rect, Matrix r, Matrix g, Matrix b) |
7091 |~|~|Point po |コピー元領域の左上座標<br>(省略時は画像左上を起点とします)|
7092 |~|~|Rectangle rect |コピー元領域のサイズを持つ矩形|
7093 |~|~|Matrix r |赤チャンネルの輝度値の行列|
7094 |~|~|Matrix g |緑チャンネルの輝度値の行列|
7095 |~|~|Matrix b |青チャンネルの輝度値の行列|
7096 |~|to( Interval h_intvl, Interval v_intvl, Matrix r, Matrix g, Matrix b) |
7097 |~|~|Interval h_intvl |コピー元領域の横座標の範囲(pixel)|
7098 |~|~|Interval v_intvl |コピー元領域の縦座標の範囲(pixel)|
7099 |~|~|Matrix r |赤チャネルの輝度値の行列|
7100 |~|~|Matrix g |緑チャネルの輝度値の行列|
7101 |~|~|Matrix b |青チャネルの輝度値の行列|
7104 |!Image::to|>|>|!RGBA画像の各チャネルの輝度値・透明度チャネルの値をMatrixに書き込みます|
7105 |~|to( Matrix r, Matrix g, Matrix b, Matrix a) |
7106 |~|~|Matrix r |赤チャネルの輝度値の行列|
7107 |~|~|Matrix g |緑チャネルの輝度値の行列|
7108 |~|~|Matrix b |青チャネルの輝度値の行列|
7109 |~|~|Matrix a |透明度チャネルの行列|
7110 |~|to( Point po, double width, double height, Matrix r, Matrix g, Matrix b, Matrix a ) |
7111 |~|~|Point po |コピー元領域の左上座標<br>(省略時は画像左上を起点とします)|
7112 |~|~|double width |コピー元領域の幅(pixel)|
7113 |~|~|double height |コピー元領域の高さ(pixel)|
7114 |~|~|Matrix r |赤チャネルの輝度値の行列|
7115 |~|~|Matrix g |緑チャネルの輝度値の行列|
7116 |~|~|Matrix b |青チャネルの輝度値の行列|
7117 |~|~|Matrix a |透明度チャネルの行列|
7118 |~|to( Point po, Rectangle rect, Matrix r, Matrix g, Matrix b, Matrix a) |
7119 |~|~|Point po |コピー元領域の左上座標<br>(省略時は画像左上を起点とします)|
7120 |~|~|Rectangle rect |コピー元領域のサイズを持つ矩形|
7121 |~|~|Matrix r |赤チャンネルの輝度値の行列|
7122 |~|~|Matrix g |緑チャンネルの輝度値の行列|
7123 |~|~|Matrix b |青チャンネルの輝度値の行列|
7124 |~|~|Matrix a |透明度チャネルの行列|
7125 |~|to( Interval h_intvl, Interval v_intvl, Matrix r, Matrix g, Matrix b, Matrix a ) |
7126 |~|~|Interval h_intvl |コピー元領域の横座標の範囲(pixel)|
7127 |~|~|Interval v_intvl |コピー元領域の縦座標の範囲(pixel)|
7128 |~|~|Matrix r |赤チャネルの輝度値の行列|
7129 |~|~|Matrix g |緑チャネルの輝度値の行列|
7130 |~|~|Matrix b |青チャネルの輝度値の行列|
7131 |~|~|Matrix a |透明度チャネルの行列|
7135 Img.to(1<=rng<=100, 1<=rng<=50, luminance);
7138 書き込み先のMatrixはいったん破棄されて作り直されます。
7139 大きさ等はImageのサイズと等しくなるように自動的に調整されます。
7141 [[5.3.5 行列の中身をオフスクリーンに描画する]]</pre>
7143 <div title="Image型に関して" modifier="Kazushi Maruya" modified="200712131731" created="200712131730" changecount="2">
7144 <pre>*[[オフスクリーンのサイズを指定したい|Image::set()]]
7145 *[[オフスクリーンを特定の描画色で塗りつぶしたい|Image::clear()]]
7146 *[[オフスクリーン上に点を打ちたい|Image::pix()]]
7147 *[[オフスクリーン上に線を引きたい|Image::line()]]
7148 *[[オフスクリーン上に四角形を描画したい|Image::rect()]]
7149 *[[オフスクリーン上に円を描画したい|Image::oval()]]
7150 *[[オフスクリーンの内容を画面に描画したい|Image::draw()]]
7151 *[[オフスクリーンの描画位置を画面中央や特定の座標にセンタリングしたい|Image::centering()]]
7152 *[[オフスクリーンの描画位置を移動させたい|Image::shift()]]
7153 *[[オフスクリーンのメンバ(情報)を取得したい|Image::getXXXX()]]
7154 *[[オフスクリーンに画像を読み込みたい|Image::load()]]
7155 *[[オフスクリーンの描画内容をファイルとして保存したい|Image::save()]]
7156 *[[Matrix型を用いて計算した描画内容をオフスクリーンへ読み込みたい|Image::to()]]
7157 *[[オフスクリーンの描画内容をMatrix型へ変換したい|Image::to()]]</pre>
7159 <div title="IndependentVariables" modifier="Psychlops_Admin" modified="200710070017" created="200709162126" changecount="3">
7161 class IndependentVariables
7163 int MaxVarNum, MaxStepNum;
7164 double *VariableData;
7165 int *VariableStepNumber;
7166 int conditionNumber;
7169 Psychlops::Matrix CondMtx;
7174 IndependentVariables(int n, int m, int repeat): MaxVarNum(n), MaxStepNum(m){
7175 VariableData = (double *)malloc(sizeof(double)*(n+1)*m);
7176 VariableStepNumber = (int *)malloc(sizeof(double)*m);
7177 repeatNumber=repeat;
7181 virtual ~IndependentVariables(void){
7183 free(VariableStepNumber);
7186 virtual int set(int vnum, int arraynum, double *array){
7187 if(vnum<MaxVarNum+1 && arraynum<MaxStepNum+1){
7188 VariableStepNumber[vnum]=arraynum;
7189 for(int i=0; i<arraynum; i++){*(VariableData+(vnum+1)*MaxStepNum+i)=*(array+i);}
7196 virtual int randomize(char* dataname=NULL){
7197 trialNumber=repeatNumber;
7198 for(int i=0; i<MaxVarNum; i++)trialNumber*=VariableStepNumber[i];
7199 if(trialNumber<1){return -1;}
7200 CondMtx.set(trialNumber, MaxVarNum);
7203 double temp=1, temp2;
7205 //Make Output Matrix
7207 for(i=0; i<MaxVarNum; i++){
7208 for(j=0; j<i; j++){temp*=(double)VariableStepNumber[j];}
7209 for(j=0; j<trialNumber; j++){CondMtx(j+1,i+1)=(int)ceil(j/temp)% VariableStepNumber[i];}
7213 //Randomize Output Matrix
7214 for(i=1; i<trialNumber;i++){
7215 a = Psychlops::random(i-1)+1;
7216 for(j=1;j<MaxVarNum+1;j++){
7218 CondMtx(a,j)=CondMtx(i,j);
7223 //swtich of the condition matrix save
7225 std::ofstream output;
7226 output.open(dataname);
7227 output << CondMtx << std::endl;
7233 virtual double get(int vnum, int trial_now){
7234 if(vnum<MaxVarNum && (int)trial_now<trialNumber)
7235 return *(VariableData+(vnum+1)*MaxStepNum+getCondMtx(vnum, trial_now));
7239 virtual int getCondMtx(int vnum, unsigned int trial_now){
7240 if(vnum<MaxVarNum && (int)trial_now<trialNumber)
7241 return CondMtx( trial_now+1, vnum+1);
7245 virtual double getIndependent(int vnum, int stepnum){
7246 if(vnum<MaxVarNum && stepnum<VariableStepNumber[vnum])
7247 return *(VariableData+(vnum)*MaxStepNum+stepnum);
7252 virtual int getMStep(int vnum){
7253 if(vnum<MaxVarNum)return VariableStepNumber[vnum];
7260 <div title="Input::get()" modifier="Kazushi Maruya" created="200712131738" changecount="1">
7261 <pre>|!Input::get()|Input::get([[Keyboard::KeyCode|キーコード表]] code, [[Keyboard::KeyState]] state)|キーボードの反応を取得します|
7262 |~|~|code 反応を取得するキーの名前 [[キーコード表]] 参照|
7263 |~|~|state 反応状態のオプション (省略時はpushed) [[Keyboard::KeyState]]参照|
7264 |~|Input::get([[Mouse::ButtonCode|キーコード表]] code, [[Mouse::ButtonState]] state)|マウスの反応を取得します|
7265 |~|~|code 反応を取得するマウスボタンの名前 [[キーコード表]] 参照|
7266 |~|~|state 反応状態のオプション (省略時はpushed) [[Mouse::ButtonState]]参照|
7268 [[4.1 外部装置(キーボード/マウス)の操作内容を取得する]]</pre>
7270 <div title="Input::refresh()" modifier="PsychlopsAdmin" modified="200709171526" created="200709171520" changecount="2">
7271 <pre>キーボードやマウスといった外部入力装置の入出力ルーチンの初期化処理
7272 |!Input::refresh()|refresh(void)|キーボード・マウスの入出力ルーチンを初期化します|
7274 コード例は[[Tips: Psychlopsにおけるキーボードマウスの入出力検出ルーチン]]参照</pre>
7276 <div title="Keyboard::KeyState" modifier="Psychlops_Admin" modified="200709142243" created="200708240330" changecount="3">
7277 <pre>| !~ButtonState | pressed |ボタンが押し続けられている |
7278 |~| pushed | 判定が行われるまでにボタンが押された |
7279 |~| released | 判定が行われるまでにボタンが離された |</pre>
7281 <div title="MainMenu" modifier="Psychlops_DevelopperG" modified="200909300421" created="200707220305" changecount="64">
7282 <pre>[[このマニュアルの見方について(ここをクリックしてください)]]
7286 [[第I部 Psychlopsの基本]]
7289 [[3. 複雑な描画を行う1(オフスクリーンへの描画)]]
7290 [[4. 複雑な描画を行う2(複数オブジェクトのグループ化と回転・拡大縮小)]]
7292 [[6. 時間制御と各種ツールを使用する]]
7294 [[第II部 Psychlopsの応用]]
7295 [[1. 既成クラスを用いた簡単な実験プログラムを作成する]]
7297 [[3. 透明度を使って刺激描画を高速化する]]
7301 [[関数一覧_FunctionList]]
7307 <div title="Math::mod" modifier="Kazushi Maruya" modified="200712180137" created="200712100223" changecount="5">
7310 負の数を割ったときの剰余は多くの言語で未定義とされていますが、C++(%演算子やfmod関数)では負の数になります。また、C++の剰余演算子は整数型のみ定義されています。
7311 Math::modは剰余の計算を浮動小数点型に拡張するとともに、負の数を割ったときの挙動を周期関数的にしてあります。視覚実験では周期関数の位相を値とすることが多くありますが、この関数を使うと位相が負でも正でも一定の範囲の剰余が得られます。ifやswitchで値ごとに条件分岐している場合には特に役立ちます。
7313 たとえば、-450を360で割った剰余をとる場合、
7316 270 == Math::mod(-450, 360)
7318 %演算子では剰余が負の数になってしまいますが、Math::modを使うと正の数で得ることができます。両関数をグラフで描画すると下図のようになります。
7320 [img[image/Math.mod.png]]
7323 |!Math::mod()|mod(double lhs, double rhs)|剰余を計算します。|
7324 |~|~|double lhs: 左辺項(割られる数)を指定します。|
7325 |~|~|double rhs: 右辺項(割る数)を指定します。|
7328 以下の例では%演算子とMath::mod()関数の返り値をプロットしています。
7332 #include <psychlops.h>
7333 using namespace Psychlops;
7335 void psychlops_main() {
7336 Canvas display(Canvas::fullscreen);
7338 double upper_v = display.getVcenter()-100, lower_v = display.getVcenter()+100;
7340 while(!Input::get(Keyboard::esc)) {
7342 display.line(display.getHcenter()-300, upper_v, display.getHcenter()+300, upper_v, Color::white);
7343 display.line(display.getHcenter()-300, lower_v, display.getHcenter()+300, lower_v, Color::white);
7344 display.line(display.getHcenter(), upper_v+60, display.getHcenter(), upper_v-60, Color::white);
7345 display.line(display.getHcenter(), lower_v+60, display.getHcenter(), lower_v-60, Color::white);
7346 display.msg("C++ %", display.getHcenter()-100, upper_v -20, Color::white);
7347 display.msg("Math::mod", display.getHcenter()-100, lower_v -20, Color::white);
7350 for(int i=-300; i<300; i++) {
7351 display.pix(i+display.getHcenter(), upper_v - i%50, Color::red);
7352 display.pix(i+display.getHcenter(), lower_v - Math::mod(i, 50), Color::green);
7359 <div title="Math::shuffle" modifier="YourName" modified="200712100209" created="200712100206" changecount="2">
7360 <pre>配列の中身をシャッフルします。
7362 |!Psychlops::shuffle()|shuffle(X array, int num)|配列の中身をシャッフルします。|
7363 |~|~|X array: 配列変数を指定します。型=代入が可能であればなんでもかまいません。|
7364 |~|~|int num: 配列の要素数を指定します。|
7365 |~|shuffle(X array, int num, X init)|配列を初期化してから中身をシャッフルします。|
7366 |~|~|X array: 配列変数を指定します。型はint, double等で有効です。|
7367 |~|~|int num: 配列の要素数を指定します。|
7368 |~|~|X init: 配列の初期値です。0を指定すると、配列が0~num-1の数で埋められてから、シャッフルを行います。|
7373 #include <psychlops.h>
7374 using namespace Psychlops;
7376 void psychlops_main() {
7377 Canvas display(Canvas::fullscreen);
7381 Math::shuffle(array, NUM, 0);
7383 while(!Input::get(Keyboard::esc)) {
7384 for(int i=0; i<NUM; i++) {
7385 display.var(array[i], 100, 20*i + 100);
7392 <div title="Matrix::catRow()" modifier="YourName" created="200712100125" changecount="1">
7393 <pre>この行列の下に他の行列を継ぎ足します。
7395 |!Matrix::catRow|catRow(other)|この行列の次の行から別の行列を継ぎ足します。|
7396 |~|~|Matrix other: 付け足す行列を指定します。付け足してもこれ自身は消されません。|
7399 #include <fstream>
7400 #include <psychlops.h>
7401 using namespace Psychlops;
7403 Psychlops::Matrix LuminanceMap1, LuminanceMap2;
7404 long Width=5, Height=1;
7406 void psychlops_main() {
7407 std::ofstream result("dump.txt");
7410 LuminanceMap1.set(Height, Width);
7412 LuminanceMap2.set(Height, Width);
7415 LuminanceMap1.catRows(LuminanceMap2);
7416 result << LuminanceMap1;
7423 <div title="Matrix::getXXXX()" modifier="YourName" created="200712100131" changecount="2">
7424 <pre>[[Psychlops::Matrix]]型の情報を、Matrix::getXXXX()命令を使って取得します(XXXXにはそれぞれの情報に対応した
7427 |!Matrix::getXXXX()|getRows()|行列の行数を取得します|
7428 |~|getCols()|行列の列数を取得します|
7430 !!Matrix::getXXXX()の書き方
7431 [[Psychlops::Matrix]]型の情報を取得しファイルに出力します。
7434 #include <fstream>
7435 #include <psychlops.h>
7436 using namespace Psychlops;
7438 Psychlops::Matrix LuminanceMap1;
7439 long Width=5, Height=3;
7441 void psychlops_main() {
7442 std::ofstream result("dump.txt");
7445 LuminanceMap1.set(Height, Width);
7447 result << LuminanceMap1.getRows() << " " << LuminanceMap1.getCols();
7452 <div title="Matrix::min() / max()" modifier="YourName" created="200712100135" changecount="1">
7453 <pre>行列の全要素中の最大値・最小値を得ます
7455 |!Matrix::min|min()|全要素中の最小値を得ます。|
7456 |!Matrix::max|max()|全要素中の最大値を得ます。|
7458 // 行列の要素に乱数を代入し、その中の最大値と最小値を抽出します。
7459 #include <fstream>
7460 #include <psychlops.h>
7461 using namespace Psychlops;
7463 Psychlops::Matrix LuminanceMap1;
7464 long Width=10, Height=10;
7466 void psychlops_main() {
7467 std::ofstream result("dump.txt");
7470 LuminanceMap1.set(Height, Width);
7471 for(int row; row<Width; row++) {
7472 for(int col; col<Width; col++) {
7473 LuminanceMap1(row, col) = Psychlops::random();
7476 result << LuminanceMap1;
7477 result << LuminanceMap1.max() << " " << LuminanceMap1.min();
7484 要素どうしで大きいものをまとめた行列、小さいものをまとめた行列を返すmax/minもあります。
7486 |!Matrix::min|min(Matrix a, Matrix b)|aとbの各要素を比較し、小さいほうを要素とする行列を返します。両Matrixの大きさは同じでなければなりません。|
7487 |!Matrix::max|max(Matrix a, Matrix b)|aとbの各要素を比較し、大きいほうを要素とする行列を返します。両Matrixの大きさは同じでなければなりません。|
7507 <div title="Matrix::reshape()" modifier="YourName" created="200712100127" changecount="1">
7508 <pre>行列をいったん1行の配列に並べ、新たに行を仕切りなおします。
7510 |!Matrix::reshape|reshape(row, col)|要素を変えずに、行と列の数のみを変更します。要素数(行*列)は変更前と変更後で同じでなければなりません。|
7511 |~|~|int row 新しい行数を指定します。|
7512 |~|~|int col 新しい列数を指定します。|
7526 #include <fstream>
7527 #include <psychlops.h>
7528 using namespace Psychlops;
7530 Psychlops::Matrix LuminanceMap1;
7531 long Width=16, Height=1;
7533 void psychlops_main() {
7534 std::ofstream result("dump.txt");
7537 LuminanceMap1.set(Height, Width);
7538 for(int col; col<Width; col++) {
7539 LuminanceMap1(1, col) = col;
7541 result << LuminanceMap1.reshape(4, 4);
7547 <div title="Matrix::rot90()" modifier="YourName" created="200712100103" changecount="1">
7548 <pre>行列を90度単位で回転させます。
7550 |!Matrix::rot90|rot90(count)|行列を90度単位で反時計回りに回転します。|
7551 |~|~|int count 回転角度を90度単位で指定します。-3,1,5のとき90度、-2,2,6のとき180度、-1,3,7のとき270度回転します。|
7565 #include <fstream>
7566 #include <psychlops.h>
7567 using namespace Psychlops;
7569 Psychlops::Matrix LuminanceMap1;
7570 long Width=16, Height=1;
7572 void psychlops_main() {
7573 std::ofstream result("dump.txt");
7576 LuminanceMap1.set(Height, Width);
7577 for(int col; col<Width; col++) {
7578 LuminanceMap1(1, col) = col;
7580 result << LuminanceMap1.rot90(1);
7585 <div title="Matrix::set()" modifier="YourName" modified="200803100614" created="200712131744" changecount="3">
7587 行列の宣言時に何の指定もしなかった場合、Matrix::set()命令を使用します。
7588 |!Matrix::set()|Matrix(rows, cols)|指定されたrows行cols列の行列を設定します|
7589 |~|~|long rows :行列の行数|
7590 |~|~|long cols :行列の列数|
7592 [[5.3.1 行列の宣言]]</pre>
7594 <div title="Matrix::slide()" modifier="YourName" created="200712100100" changecount="1">
7595 <pre>各要素を行・列各方向にずらす命令です。
7597 |!Matrix::slide|slide(row, col)|要素を行方向、列方向にずらします。あふれた分は、反対側に移動します。|
7598 |~|~|int row 行方向にずらす量を指定します。|
7599 |~|~|int col 列方向にずらす量を指定します。|
7624 #include <fstream>
7625 #include <psychlops.h>
7626 using namespace Psychlops;
7628 Psychlops::Matrix LuminanceMap1;
7629 long Width=10, Height=10;
7631 void psychlops_main() {
7632 std::ofstream result("dump.txt");
7635 LuminanceMap1.set(Height, Width);
7636 for(int row; row<Width; row++) {
7637 for(int col; col<Width; col++) {
7638 LuminanceMap1(row, col) = row*Height + col;
7641 result << LuminanceMap1.slide(1,0);
7646 <div title="Matrix::transpose()" modifier="YourName" modified="200803140532" created="200712100101" changecount="2">
7649 |!Matrix::transpose|Matrix transpose(void)|行列を転置(行と列の読み替え)します。自分自身を書き換えるので注意してください。|
7663 #include <fstream>
7664 #include <psychlops.h>
7665 using namespace Psychlops;
7667 Psychlops::Matrix LuminanceMap1;
7668 long Width=10, Height=10;
7670 void psychlops_main() {
7671 std::ofstream result("dump.txt");
7674 LuminanceMap1.set(Height, Width);
7675 for(int row; row<Width; row++) {
7676 for(int col; col<Width; col++) {
7677 LuminanceMap1(row, col) = row*Height + col;
7680 result << LuminanceMap1.transpose();
7685 <div title="Matrix::要素アクセス・部分行列" modifier="Psychlops_DevelopperG" modified="200908190257" created="200712131744" changecount="2">
7686 <pre>[[6.3.2 行列の要素の操作]]</pre>
7688 <div title="Mouse::ButtonState" modifier="Psychlops_Admin" created="200708240330" changecount="1">
7689 <pre>| ~KeyState | pressed | キーが押し続けられている |
7690 |~| pushed | 判定が行われるまでにキーが押された |
7691 |~| released | 判定が行われるまでにキーが離された |</pre>
7693 <div title="Mouse::hide()" modifier="PsychlopsAdmin" created="200709171527" changecount="1">
7694 <pre>マウスカーソルの表示切り替え。
7695 [[Mouse::show()]]命令と対になっている命令です。
7696 |!Mouse::hide()|hide()|マウスカーソルを非表示にする|
7698 コード例は[[例6: マウスのデモ]]参照</pre>
7700 <div title="Mouse::show()" modifier="PsychlopsAdmin" created="200709171527" changecount="2">
7701 <pre>マウスカーソルの表示切り替え。
7702 [[Mouse::hide()]]命令と対になっている命令です。
7703 |!Mouse::show()|show()|マウスカーソルを表示する|
7705 コード例は[[例6: マウスのデモ]]参照</pre>
7707 <div title="PixelComponentsCnt" modifier="Psychlops_Admin" modified="200709041934" created="200709032052" changecount="5">
7708 <pre>PixelComponentsCntの指定方法
7710 |RGB|色の三原色に基づいた赤・緑・青で構成された色|
7711 |RGBA|RGBに透明度が追加された色|
7713 詳しくは[[2.2.5 描画する図形の色を指定する-Colorクラス-]]を参照</pre>
7715 <div title="PluginManager" modifier="PsychlopsAdmin" created="200707220245" changecount="1">
7716 <pre><<plugins>></pre>
7718 <div title="Point()の宣言" modifier="Psychlops_Admin" modified="200709140117" created="200709132353" changecount="3">
7720 [[Psychlops::Point]]型の命令を使用する前に必ず宣言が必要です。
7721 宣言のみの場合、[[Point::set()]]で点の詳細情報を設定しますが
7724 |!Pointの宣言|Point()|[[Psychlops::Point]]型の宣言を行います|
7725 |~|Point(double x, double y)|[[Psychlops::Point]]型の宣言を行い座標(x,y)に値を指定します|
7726 |~|~|double x:座標xを指定|
7727 |~|~|double y:座標yを指定|
7730 座標(100 × 100)に赤の点を描画します。
7733 #include <psychlops.h>
7734 using namespace Psychlops;
7736 Psychlops::Point point1;
7737 void psychlops_main() {
7739 Canvas sampleA(Canvas::fullscreen);
7740 point1.set(100,100);
7741 sampleA.pix(point1,Color::red);
7743 while(!Input::get(Keyboard::spc));
7747 <div title="Point::centering()" modifier="YourName" modified="200803100601" created="200709132303" changecount="4">
7750 |!Point::centering()|centering()|座標(x,y)の点を画面の中心に移動します|
7751 |~|centering(Psychlops::Rectangle rect)|rectの中心に点を移動します|
7754 コード例は[[2.2.1節|2.2.1 画面上に点を描画する-Pointクラス1-]]参照</pre>
7756 <div title="Point::get()" modifier="Psychlops_Admin" modified="200709142232" created="200709140110" changecount="4">
7759 |!Point::get()|getX()|対象の点のX座標の値を取得します|
7760 |~|getY()|対象の点のY座標の値を取得します|
7765 #include <psychlops.h>
7766 using namespace Psychlops;
7768 Psychlops::Point point1(100,100);
7771 void psychlops_main() {
7773 Canvas sampleA(Canvas::fullscreen);
7775 sampleA.pix(point1,Color::red);
7781 sampleA.msg("getX:",50,200);
7782 sampleA.var(d1,200,200);//getX
7783 sampleA.msg("getY:",50,250);
7784 sampleA.var(d2,200,250);//getY
7787 while(!Input::get(Keyboard::spc));
7791 <div title="Point::set()" modifier="YourName" modified="200803100556" created="200709132300" changecount="6">
7792 <pre>座標(x,y),ないし座標X,Yを指定し点を描画します。
7793 Point::set()を使用し座標を取得することでソースの簡略化がなされ
7796 |!Point::set()|set(double x, double y)|座標(x,y)の値を設定します|
7799 |~|setX(double val)|水平座標の値を設定します|
7800 |~|~|double val :水平座標値|
7801 |~|setY(double val)|垂直座標の値を設定します|
7802 |~|~|double val :垂直座標値|
7804 コード例は[[2.2.1節|2.2.1 画面上に点を描画する-Pointクラス1-]]参照</pre>
7806 <div title="Point::shift()" modifier="YourName" modified="200803100557" created="200709132303" changecount="3">
7808 元の座標(x,y)を基点に入力値(h,v)だけ移動します。
7810 |!Point::shift()|shift(double h, double v)|座標(X,Y)を(h,v)だけ移動します|
7811 |~|~|double h:右水平方向の移動量|
7812 |~|~|double v:下垂直方向の移動量|
7814 コード例は[[2.2.1節|2.2.1 画面上に点を描画する-Pointクラス1-]]参照</pre>
7816 <div title="Point型に関して" modifier="Kazushi Maruya" created="200712131720" changecount="1">
7817 <pre>* [[Point型の変数の座標を設定したい|Point::set()]]
7818 * [[Point型の変数を画面中央やRectangle型の変数の中央に移動したい|Point::centering()]]
7819 * [[Point型の変数の座標を移動させたい|Point::shift()]]
7820 * [[Point型の現在の座標を取得したい|Point::get()]]</pre>
7822 <div title="Psychlops::Canvas" modifier="Psychlops_DevelopperG" modified="200910080816" created="200707220311" changecount="40">
7825 **[[Canvas::clear()]]
7826 **[[Canvas::flip()]]
7827 **[[Canvas::flipAfter()|Canvas::flip()]]
7828 **[[Canvas::flipAndWait()|Canvas::flip()]]
7829 **[[Canvas::getXXXX()]]
7833 **[[Canvas::line()]]
7834 **[[Canvas::rect()]]
7835 **[[Canvas::oval()]]
7837 **[[Canvas::copy()]]
7838 **[[Canvas::setGammaValue()]]
7839 **[[Canvas::setGammaTable()]]
7840 **[[Canvas::setClearColor()]]
7841 **[[Canvas::progressbar()]]
7842 **[[Canvas::watchFPS()]]
7843 **[[Canvas::showFPS()]]
7844 **[[Canvas::lastFailedFrames()]]
7846 **TargetSurface</pre>
7848 <div title="Psychlops::Clock" modifier="Psychlops_Admin" modified="200709142035" created="200709142034" changecount="2">
7850 **[[Clock::update()]]
7851 **[[Clock::at_msec()]]</pre>
7853 <div title="Psychlops::Color" modifier="YourName" modified="200802131229" created="200708212259" changecount="17">
7858 ** [[Color::setGammaValue()]]
7859 ** [[Color::strict_match]]
7860 ** [[Color::XXXX]]</pre>
7862 <div title="Psychlops::Image" modifier="YourName" modified="200803050504" created="200709032052" changecount="15">
7866 **[[Image::clear()]]
7872 **[[Image::quicken()|3.1.4 発展的な内容]]
7873 **[[Image::centering()]]
7874 **[[Image::shift()]]
7875 **[[Image::getXXXX()]]
7879 **[[Image::to()]]</pre>
7881 <div title="Psychlops::Input" modifier="Kazushi Maruya" modified="200712131738" created="200709142237" changecount="6">
7884 **[[Input::refresh()]]</pre>
7886 <div title="Psychlops::Keyboad" modifier="PsychlopsAdmin" modified="200709170639" created="200709142246" changecount="2">
7888 **[[Keyboad::get()]]
7891 <div title="Psychlops::Math" modifier="Kazushi Maruya" modified="200712131628" created="200712100137" changecount="4">
7893 ** [[Psychlops::random]]
7894 ** [[Math::shuffle]]
7895 ** [[Math::mod]]</pre>
7897 <div title="Psychlops::Matrix" modifier="Psychlops_DevelopperG" modified="200910070546" created="200712100024" changecount="9">
7899 **[[Matrix()の宣言|6.3.1 行列の宣言]]
7901 **[[Matrix::要素アクセス・部分行列|6.3.2 行列の要素の操作]]
7902 **[[Matrix::四則演算|6.3.4 行列の加減乗除]]
7903 **[[Matrix::slide()]]
7904 **[[Matrix::transpose()]]
7905 **[[Matrix::rot90()]]
7906 **[[Matrix::catRow()]]
7907 **[[Matrix::reshape()]]
7908 **[[Matrix::getXXXX()]]
7909 **[[Matrix::min() / max()]]
7910 **[[Matrix::mesh()|6.3.6 メッシュグリッドの作成]]</pre>
7912 <div title="Psychlops::Mouse" modifier="PsychlopsAdmin" modified="200709170736" created="200709142247" changecount="3">
7915 **[[Mouse::show()]]</pre>
7917 <div title="Psychlops::Point" modifier="Psychlops_Admin" modified="200709140121" created="200709122119" changecount="8">
7921 **[[Point::centering()]]
7922 **[[Point::shift()]]
7923 **[[Point::get()]]</pre>
7925 <div title="Psychlops::Rectangle" modifier="Psychlops_DevelopperG" modified="200910080843" created="200708230127" changecount="15">
7928 **[[Rectangle::set()]]
7929 **[[Rectangle::centering()]]
7930 **[[Rectangle::shift()]]
7931 **[[Rectangle::draw()]]
7932 **[[Rectangle::resize()]]
7933 **[[Rectangle::setColor()]]
7934 **[[Rectangle::getXXXX()]]
7935 **[[Rectangle::include()]]
7936 **[[Rectangle::dup()]]</pre>
7938 <div title="Psychlops::random" modifier="YourName" modified="200802232256" created="200712100149" changecount="11">
7939 <pre>乱数を返します。標準の擬似乱数生成アルゴリズムは[[dSFMT|http://www.math.sci.hiroshima-u.ac.jp/%7Em-mat/MT/SFMT/index-jp.html]]を利用しています。
7940 |!Psychlops::random()|random()|[0.0, 1.0)の範囲の一様乱数を生成します。|
7941 |~|random(double max)|[0.0, max)の範囲の一様乱数を生成します。|
7942 |~|random(int levels)|[0, levels)の範囲の離散的一様乱数を生成します。|
7943 |~|random(double min, double max)|[min, max)の値をランダムに返します。|
7944 |~|~|double min: ランダムで返す値の最小値を指定|
7945 |~|~|double max: ランダムで返す値の最大値を指定|
7947 引数が実数(double)か整数(int)かで動作が異なりますので、実数で返したい場合は最大値が整数でも必ず1.0などのように小数点をつけて指定してください。整数変数を指定する場合は(double)でキャストしてください。
7949 random(5) ← [0,1,2,3,4] のいずれかの数がint型で返る
7950 random(5.0) ← [0, 5)の間の浮動小数点値が返る
7953 random(i) ← [0,1,2,3,4] のいずれかの数がint型で返る
7954 random((double)i) ← [0, 5)の間の浮動小数点値が返る
7958 以下の例では100 ms毎にrandom命令を使用して作成したランダム配列を元に10000個のドットを画面に表示します。
7961 #include <psychlops.h>
7962 using namespace Psychlops;
7964 void psychlops_main() {
7965 Canvas display(Canvas::fullscreen);
7966 double wid = 200, hei = 200;
7967 int refresh=display.getRefreshRate()/10;
7968 const int DOTNUM = 1000;
7969 double x[DOTNUM], y[DOTNUM];
7972 while(!Input::get(Keyboard::esc)) {
7974 for(int i=0; i<DOTNUM; i++) {
7975 x[i] = random(wid)+display.getHcenter()-wid*0.5;
7976 y[i] = random(hei)+display.getVcenter()-hei*0.5;
7978 display.pix(DOTNUM, x, y, Color::white);
7979 display.flip(refresh);
7984 <div title="Psychlopsについて開発者に質問するには?" modifier="Kazushi Maruya" modified="200712180142" created="200712131755" changecount="2">
7985 <pre>直接の質問には(開発者と面識がある場合を除いて)お答えしておりません。
7986 Psychlops Authors Mailingリストに入っていただくのが便利です。
7987 このメーリングリストはGoogleグループサービスを利用しております。
7988 このメーリングリストに参加なされたい場合は、[[ここ|http://groups.google.com/group/psychlops/post?sendowner=1]]から申請してください。</pre>
7990 <div title="Psychlopsを既存・自作のC/C++ライブラリと併用することができますか?" modifier="Kazushi Maruya" created="200712180146" changecount="1">
7992 OpenGL命令に関しては、Psychlopsライブラリのインクルード時に既にOpenGLライブラリのインクルードが行われていますので、そのまま書いていただいてコンパイル・実行可能となっています。
7993 自作ライブラリについては、関数名の衝突が無ければそのまま書いていただいて結構です。関数名が衝突する場合は、スコープ演算子などを用いて衝突を回避すれば利用が可能です。</pre>
7995 <div title="Psychlopsプログラムのデフォルトテンプレート" modifier="YourName" created="200802191428" changecount="1">
7997 void RectLuminance() {
7998 double rect_size = 100;
7999 double rect_lum = 0.5;
8000 double bg_lum = 0.2;
8002 Psychlops::Rectangle rect(rect_size,rect_size);
8006 Independent << rect_size | "Rect Size" | 1< rng< 500 | 10.0 | 2.0 ;
8007 Independent << rect_lum | "Rect Luminance" | 0.0<=rng<=1.0 | 0.1 | 0.05;
8008 Independent << bg_lum | "BG Luminance" | 0.0<=rng<=1.0 | 0.1 | 0.05;
8010 while(!Input::get(Keyboard::esc)) {
8011 Display::clear(Color(bg_lum));
8012 rect.resize(rect_size,rect_size);
8013 rect.display(rect_lum);
8018 void psychlops_main() {
8019 Canvas display(Canvas::fullscreen);
8022 p.setDesign(Procedure::DEMO);
8023 p.setProcedure(RectLuminance);
8028 <div title="Rectangle::centering()" modifier="Psychlops_DevelopperG" modified="200910080843" created="200709131913" changecount="8">
8029 <pre>[[Psychlops::Rectangle]]型の図形を移動します。
8030 [img[image/rect_centering().png]]
8031 座標を指定する場合、その座標に図形の中心が設定されます。
8032 |!Rectangle::centering()|centering()|[[Psychlops::Rectangle]] を画面中心に移動します|
8033 |~|centering(double x, double y)|[[Psychlops::Rectangle]] を指定した座標(x,y)に移動します|
8034 |~|~|double x:移動する図形のx座標|
8035 |~|~|double y:移動する図形のy座標|
8036 |~|centering([[Psychlops::Point]] po)|[[Psychlops::Rectangle]]を指定したpoint座標(x,y)に移動します|
8037 |~|~|[[Psychlops::Point]] po:移動する図形の(x,y)座標|
8038 |~|centering(Rectangle rect)|[[Psychlops::Rectangle]] を指定した[[Psychlops::Rectangle]] rectの中心座標(x,y)に移動します(2つのRectangleを中央ぞろえします)|
8039 |~|~|[[Psychlops::Rectangle]] rect :センタリングの中心座標を与える[[Psychlops::Rectangle]]|
8041 コード例は[[2.2.6節|2.2.6 画面上に縞を描画する-Rectangleクラス2-]]参照</pre>
8043 <div title="Rectangle::draw()" modifier="YourName" modified="200803100553" created="200709131911" changecount="7">
8044 <pre>[[Psychlops::Rectangle]]型の図形の描画命令です。
8045 |!Rectangle::draw()|draw()|対象の[[Psychlops::Rectangle]]型を描画します|
8046 |~|draw([[Psychlops::Color]] col)|対象の[[Psychlops::Rectangle]]型を[[Psychlops::Color]] colで塗りつぶし描画します|
8047 |~|~|[[Psychlops::Color]] col:描画する四角形の描画色|
8049 コード例は[[2.2.6節|2.2.6 画面上に縞を描画する-Rectangleクラス2-]]参照</pre>
8051 <div title="Rectangle::dup()" modifier="YourName" modified="200803100555" created="200802281432" changecount="2">
8052 <pre>[[Psychlops::Rectangle]]型を複製した一時オブジェクトを作成します。
8054 |!Rectangle::dup()|矩形領域の複製を行います|
8057 Psychlops::Rectangle rect1(10,10), rect2(10,10);
8058 rect1.centering(); // rect1の中心位置は変わっている
8059 rect2.dup().centering() // rect2の中心位置は変わっていないが、複製がセンタリングされている
8062 <div title="Rectangle::getXXXX()" modifier="Psychlops_Admin" modified="200709132236" created="200709131915" changecount="12">
8063 <pre>[[Psychlops::Rectangle]]型の情報を、Rectangle::getXXXX()命令を使って取得します(XXXXにはそれぞれの情報に対応した名前が入ります)。
8064 |!Rectangle::getXXXX()|getCenter()|四角形の中心座標(x,y)を取得します|
8065 |~|getHcenter()|横方向の中心座標(x)を取得します|
8066 |~|getVcenter()|縦方向の中心座標(y)を取得します|
8067 |~|getHeight()|四角形の高さ(ピクセル)を取得します|
8068 |~|getWidth()|四角形の幅(ピクセル)を取得します|
8069 |~|getTop()|四角形上辺のX座標(x)を取得します|
8070 |~|getLeft()|四角形左辺のX座標(x)を取得します|
8071 |~|getBottom()|四角形下辺のY座標(y)を取得します|
8072 |~|getRight()|四角形右辺のY座標(y)を取得します|
8073 [img[image/RectangleGet.png]]
8075 !!Rectangle::getXXXX()の書き方
8076 [[Psychlops::Rectangle]]型の情報を取得し画面表示を行います。
8077 画面表示は[[Canvas::var()]]命令と[[Canvas::msg()]]命令を使用します。
8080 #include <psychlops.h>
8081 using namespace Psychlops;
8083 double d1,d2,d3,d4,d5,d6,d7,d8,x,y;
8084 Psychlops::Point point1;
8085 Psychlops::Rectangle rect1;
8087 void psychlops_main() {
8089 Canvas sampleA(Canvas::fullscreen);
8092 sampleA.rect(rect1,Color::cyan);
8094 point1=rect1.getCenter();
8095 d1=rect1.getHcenter();
8096 d2=rect1.getVcenter();
8097 d3=rect1.getHeight();
8098 d4=rect1.getWidth();
8101 d7=rect1.getBottom();
8102 d8=rect1.getRight();
8107 sampleA.msg("getCenter_x:",50,200);
8108 sampleA.var(x,200,200);//getCenter:X
8109 sampleA.msg("getCenter_y:",50,250);
8110 sampleA.var(y,200,250);//getCenter:Y
8111 sampleA.msg("getHcenter:",50,300);
8112 sampleA.var(d1,200,300);//getHcenter
8113 sampleA.msg("getVcenter:",50,350);
8114 sampleA.var(d2,200,350);//getVcenter
8115 sampleA.msg("gettHeight:",50,400);
8116 sampleA.var(d3,200,400);//getHeight
8117 sampleA.msg("getWidth:",50,450);
8118 sampleA.var(d4,200,450);//getWidth
8120 sampleA.msg("getTop:",250,200);
8121 sampleA.var(d5,400,200);//getTop
8122 sampleA.msg("getLeft:",250,250);
8123 sampleA.var(d6,400,250);//getLeft
8124 sampleA.msg("getBottom:",250,300);
8125 sampleA.var(d7,400,300);//getBottom
8126 sampleA.msg("getRight:",250,350);
8127 sampleA.var(d8,400,350);//getRight
8130 while(!Input::get(Keyboard::spc));
8134 <div title="Rectangle::include()" modifier="YourName" modified="200803100554" created="200712100008" changecount="2">
8135 <pre>[[Psychlops::Rectangle]]型の座標の中にに他のRectangleや点が含まれるかどうかを判定します。
8137 |!Rectangle::include()|include(double x, double y)|対象の座標を指定|
8138 |~|include(Point point)|[[Psychlops::Point]] point:判定対象の点|
8139 |~|include(Rectangle rect)|[[Psychlops::Rectangle]] rect:判定対象の四角形|
8142 !!Rectangle::include()の書き方
8144 [[例6: マウスのデモ]]をRectangle::include()を使って判定してみます。
8145 実行すると画面左上に黒い四角形が、中央に白色の四角形が表示されます。
8147 以下の2行でマウスのポインタ位置が白色の四角形の上にあるかどうかを判定しています。
8148 もし、マウスポインタが四角形の上にあって、マウスの左ボタンが押され続けていれば、変数draggingに1が代入されます。
8150 if(Input::get(Mouse::left, Mouse::pressed) && rect.include(Mouse::x, Mouse::y)) dragging = 1;
8151 if(Input::get(Mouse::left, Mouse::released)) dragging = 0;
8153 マウスで白色の四角形をドラッグアンドドロップして、黒い四角形に重ねてみましょう。
8156 #include <psychlops.h>
8157 using namespace Psychlops;
8160 void psychlops_main() {
8162 Canvas display(Canvas::fullscreen);
8166 Psychlops::Rectangle rect(rectsize,rectsize);
8168 Psychlops::Rectangle drop_here(rectsize*2,rectsize*2);
8169 drop_here.shift(10,10);
8170 Color rectcol, dropareacol;
8174 while(!Input::get(Keyboard::spc)){
8175 display.clear(Color::gray);
8178 if(Input::get(Mouse::left, Mouse::pressed) && rect.include(Mouse::x,
8179 Mouse::y)) dragging = 1;
8180 if(Input::get(Mouse::left, Mouse::released)) dragging = 0;
8182 rect.centering(Mouse::x, Mouse::y);
8188 if(drop_here.include(rect)) {
8189 dropareacol.set(1.0,0.0,0.0);
8191 dropareacol.set(0.0,0.0,0.0);
8194 drop_here.draw(dropareacol);
8195 display.msg("Drop a rect here!", drop_here.getLeft()+10,
8196 drop_here.getTop()+10);
8205 <div title="Rectangle::resize()" modifier="YourName" modified="200803100553" created="200709132255" changecount="2">
8206 <pre>[[Psychlops::Rectangle]]型のサイズの再設定を行います。
8208 |!Rectangle::resize()|resize(double w, double h)|対象の[[Psychlops::Rectangle]]型のサイズをw×hに変更します|
8209 |~|~|double w:変更後の横幅|
8210 |~|~|double h:変更後の高さ|
8212 !!Rectangle::resize()の書き方
8213 200 × 200の四角形を50 × 100に変更します。
8216 #include <psychlops.h>
8217 using namespace Psychlops;
8219 Psychlops::Rectangle rect1(200,200);
8221 void psychlops_main() {
8223 Canvas sampleA(Canvas::fullscreen);
8224 rect1.setColor(Color::green);
8225 rect1.resize(50,100);
8230 while(!Input::get(Keyboard::spc));
8234 <div title="Rectangle::set()" modifier="Psychlops_DevelopperG" created="200910080842" changecount="1">
8235 <pre>|!Rectangle::set()|set(double width, double height)|>|!width×heightの四角形を設定します|
8236 |~|~|double width|描画する四角形の横幅|
8237 |~|~|double height|描画する四角形の縦幅|
8238 |~|set(double l, double t, double r, double b)|>|!左上座標(l,t)から右下座標(r,b)までの四角形を設定します|
8239 |~|~|double l|描画する四角形の左上座標x1を指定|
8240 |~|~|double t|描画する四角形の左上座標y1を指定|
8241 |~|~|double r|描画する四角形の右下座標x2を指定|
8242 |~|~|double b|描画する四角形の右下座標y2を指定|
8243 |~|set([[Psychlops::Point]] point1, [[Psychlops::Point]] point2)|>|!左上Point座標(x1,x1)から右下Point座標(X2,Y2)までの四角形を設定します|
8244 |~|~|[[Psychlops::Point]] point1|左上座標(x1,y1)を指定|
8245 |~|~|[[Psychlops::Point]] point2|右下座標(x2,y2)を指定|</pre>
8247 <div title="Rectangle::setColor()" modifier="YourName" modified="200803100554" created="200709132244" changecount="5">
8248 <pre>[[Psychlops::Rectangle]]型の色設定を行います。
8249 この命令を使用する場合は、[[Rectangle::draw()]]命令を使用してください。
8251 |!Rectangle::setColor()|setColor([[Psychlops::Color]] col)|対象の[[Psychlops::Rectangle]]型を[[Psychlops::Color]] colで塗りつぶします|
8252 |~|~|[[Psychlops::Color]] col:四角形の描画色|
8254 !!Rectangle::setColor()の書き方
8257 #include <psychlops.h>
8258 using namespace Psychlops;
8260 Psychlops::Rectangle rect1;
8262 void psychlops_main() {
8264 Canvas sampleA(Canvas::fullscreen);
8266 rect1.setColor(Color::red);
8271 while(!Input::get(Keyboard::spc));
8275 <div title="Rectangle::shift()" modifier="YourName" modified="200803100552" created="200709131914" changecount="3">
8276 <pre>[[Psychlops::Rectangle]]型の図形を移動します。
8277 元の座標(x,y)を基点に入力値(h,v)だけ移動します。
8278 |!Rectangle::shift()|shift(double h, double v)|座標(X,Y)を(h,v)だけ移動します|
8279 |~|~|double h:右水平方向の移動量|
8280 |~|~|double v:下垂直方向の移動量|
8281 [img[image/rect_shift().png]]
8283 コード例は[[2.2.6節|2.2.6 画面上に縞を描画する-Rectangleクラス2-]]参照</pre>
8285 <div title="Rectangleの宣言" modifier="Psychlops_DevelopperG" modified="200910080842" created="200709132233" changecount="1">
8286 <pre>RectangleはCanvas上に四角形の領域を設定する命令です。
8287 [[Psychlops::Rectangle]]型の命令を使用する前に必ず宣言が必要です。
8288 宣言のみの場合、[[Rectangle::set()|2.2.3 画面上に四角形を描画する-Rectangleクラス1-]]で四角形の詳細情報を設定しますが
8291 |!Rectangleの宣言|Rectangle()|[[Psychlops::Rectangle]]型の宣言を行います|
8292 |~|Rectangle(double width, double height)|[[Psychlops::Rectangle]]型の宣言を行いwidth×heightの四角形を設定します|
8293 |~|~|double width:描画する四角形の横幅|
8294 |~|~|double height:描画する四角形の縦幅|
8295 |~|Rectangle(double l, double t, double r, double b)|[[Psychlops::Rectangle]]型の宣言を行い左上座標(l,t)から右下座標(r,b)までの四角形を設定します|
8296 |~|~|double l:描画する四角形の左上座標x1を指定|
8297 |~|~|double t:描画する四角形の左上座標y1を指定|
8298 |~|~|double r:描画する四角形の右下座標x2を指定|
8299 |~|~|double b:描画する四角形の右下座標y2を指定|
8301 !!Rectangle()の宣言の書き方
8302 100 × 100の四角形を描画します。
8305 #include <psychlops.h>
8306 using namespace Psychlops;
8308 Psychlops::Rectangle rect1(100,100);
8310 void psychlops_main() {
8312 Canvas sampleA(Canvas::fullscreen);
8313 sampleA.rect(rect1,Color::cyan);
8316 while(!Input::get(Keyboard::spc));
8320 <div title="Rectangle型に関して" modifier="Kazushi Maruya" modified="200712131648" created="200712131647" changecount="2">
8321 <pre>* [[Rectangle型の変数を画面中央に持って行きたい|Rectangle::centering()]]
8322 * [[Rectangle型の変数を任意の位置に持って行きたい|Rectangle::shift()]]
8323 * [[Rectangle型の領域を塗りつぶしたい|Rectangle::draw()]]
8324 * [[Rectangle型の変数の画面上のサイズを再設定したい|Rectangle::resize()]]
8325 * [[Rectangle型の変数のメンバを取得したい|Rectangle::getXXXX()]]
8326 * [[Rectanble型の変数に対して包含関係を判定したい|Reectangle::include()]]</pre>
8328 <div title="SideBarTabs" modifier="PsychlopsAdmin" created="200709170655" changecount="1">
8329 <pre><<tabs TabTimeline 'タグ' 'タグ一覧' TabTags '詳細' '詳細' TabMore>></pre>
8331 <div title="SiteSubtitle" modifier="YourName" modified="200908100431" created="200707220301" changecount="9">
8332 <pre>Version 1.3.5 (2009/September)
8333 Generated by TiddlyWiki
8334 Authored by Psychlops Admin Group</pre>
8336 <div title="SiteTitle" modifier="YourName" modified="200908100430" created="200707220259" changecount="19">
8337 <pre>Psychlops Manual
8340 <div title="StyleSheet" modifier="Psychlops_DevelopperG" modified="200910020133" created="200708280415" changecount="169">
8345 background-color: #f0f8f0;
8348 font-family: arial,helvetica, centurygothic;
8353 background-color: #e0f8e0;
8363 font-family: arial,helvetica, centurygothic;
8365 background: #ffffcc;
8366 border: 1px dotted #54c354;
8389 padding: 2.0em 0em 0.7em 1em;
8393 padding: 2.0em 0em 0.7em 1em;
8415 background: #8bd78b;
8423 background: #8bd78b;
8432 background: #8bd78b;
8440 border: 1px solid #6a6;
8443 font-family: Helvetica, Arial;
8466 <div title="TargetSurface" modifier="YourName" modified="200803140518" created="200708212223" changecount="4">
8469 |!BACK|バックグラウンドのCanvas|
8471 ※省略時にはBACKが指定される</pre>
8473 <div title="TiddlyWiki" modifier="YourName" created="200707220242" changecount="1">
8474 <pre>Type the text for 'Psychlops Manual'</pre>
8476 <div title="Tips" modifier="Psychlops_DevelopperG" modified="200912010831" created="200708240236" changecount="41">
8477 <pre>!! Psychlopsの基本
8478 * [[Tips: OpenGL 1.4に対応した一般向けビデオチップ]]
8479 * [[Tips: OS環境による関数名の衝突について]]
8481 * [[Tips: Canvasとリフレッシュレートの詳細]]
8482 * [[Tips: Canvas::flip()とコマ落ち]]
8483 * [[Tips: CanvasクラスとDisplayクラス]]
8484 * [[Tips: Canvasを宣言しないと動作しない命令群]]
8485 * [[Tips: Image使用上の注意]]
8486 * [[Tips: 時間精度が必要なプログラムを実行するとき]]
8487 * [[Tips: プログラムが暴走したとき]]
8488 * [[Tips: Xcodeで実行時エラーが起きるとフリーズする]]
8489 * [[Tips: intどうしの比率の計算がおかしい]]
8491 * [[Tips: Psychlopsにおけるキーボードマウスの入出力検出ルーチン]]
8492 * [[Tips: トリガーとアナログ入力]]
8495 * [[Tips: Shapeクラスの概要]]
8496 * [[Tips: Strokeクラスの概要]]
8497 * [[Tips: ランダムドットステレオグラムの描画コード]]
8498 * [[Tips: Rectangleの配列を用いたドットパターンの描画例]]
8499 * [[Tips: アルファ混色の活用]]
8500 * [[Tips: プログラムを複数のファイルに分割する]]
8501 * [[Tips: QUESTの実装]]
8502 * [[Tips: 自分の機器の描画性能を確かめる]]
8503 * [[Tips: 独自のクラスを作る]]
8505 !! 他ライブラリの命令を使用した機能の補完
8507 * [[Tips: OpenGL命令の混ぜ方]]
8508 * [[Tips: 画像のフィルタリング処理]]
8509 * [[Tips: トリガーとアナログ入力2-外部機器を利用した例-]]
8510 * [[Tips: Bits++の利用]]</pre>
8512 <div title="Tips: Bits++の利用" modifier="YourName" modified="200803050448" created="200802201839" changecount="5">
8513 <pre>Cambridge Research Systems社Bits++は14bit DACを備えるアナログ画像信号出力用機器で、DVIとUSBにつなぐだけで機能し、特殊な関数などを使う必要はありません。従って、Psychlopsの命令系と問題なく結合することが可能です^^*1^^。 Bits++の動作モード別に説明します。
8515 公式サイト: [[http://www.crsltd.com/catalog/bits++/modes.html]]
8517 なお、Bits++を利用する場合、Psychlops側の一切の[[ガンマ補正]]機能は以下の命令で無効化し、LUTテーブルを管理するなどの方法で自力で補正をかけてください。
8519 Color::setGammaValue(1,1,1);
8522 *1 現時点(2008/3/1現在)でBits++で扱える3つのモード(basic/mono/color)のうちで、実際に動作することを確認しているのはmonoモード(下記参照)だけですが、他の2つのモードについても、仕様上は問題なく動作をすると考えられます。この2モードでプログラムコードを実装された方がいらっしゃいましたら、ぜひPsychlops開発フォーラム(http://psychlops.l.u-tokyo.ac.jp/?forum)までご一報ください。
8526 DVI出力のうち、赤8bit+緑6bitを合成して14bitの輝度値として解釈します。 → 全色14bitで、連動します。青8bitについては別に指定したパレットオーバーレイとして動作すると記述されていますが、詳細についてはまだわかりません。使用法としては、Imageであれば、RGB, BYTEで確保した上で値を16bit整数に変換してビットシフトでRとGに書き込む方法があります(確認済)。
8528 Color luminance_to_Color(double luminance) {
8529 unsigned short bits_mono = (unsigned short)(65535 * luminance);
8530 unsigned char upper = (unsigned char)(bits_mono >> 8), (unsigned char)(lower = bits_mono && 255) ;
8531 return Color(upper/255.0, lower/255.0, 0);
8534 Display::pix(1,1,luminance_to_Color(0.567));
8538 単純にDVI出力をトラップし、RGB各色をLUTに従って変換して14bitアナログ出力します。 → 各色14 bit中8 bitの色を使うことができます。 Bits++では、(0,0)から横に数ピクセルが特定の値であるとき、 y軸が0のラインのデジタル値をLUT配列として解釈する、というシステムになっています。従って、パレットとなるImageを(0,0)起点で描画すればLUTを書き換えることができます。パレットは配列アクセス演算子 [] を備えるクラスでくるめば扱いやすいかもしれません。このモードを使用するとLUTアニメーションが使えます。
8541 DVI出力の2ピクセル分の値を合成して各色16bitの値を得ます(実効14bit)。従って、解像度は半分になります。
8544 <div title="Tips: Canvas::flip()とコマ落ち" modifier="YourName" modified="200802201734" created="200709171713" changecount="1">
8545 <pre>実験やデモには、各フレーム(コマ)がCanvas.flip(duration)で指定した正しい時間にに出ている必要があります。しかし、実際には正しい時間にコマが表示されないことがあります。この状態をコマ落ちと呼びます。
8547 フレームが正しく表示されない原因は、フレームを提示する時間内に計算が終わらないためです。
8548 Psychlopsでは指定された時間よりも描画の計算時間が大きくなったときは描画終了時以降でもっとも近い垂直同期にあわせて描画内容を更新します。描画途中のちぎれた画像が表示される可能性はありませんが、その代わり指定された時間に表示されない可能性があります。
8549 現在のコンピュータは同時に多数のソフトが起動しており、その計算に処理時間をとられてフレーム落ちする場合があります。これを回避するには、[[Tips: 時間精度が必要なプログラムを実行するとき]]をご覧ください。</pre>
8551 <div title="Tips: Canvasとリフレッシュレートの詳細" modifier="Psychlops_Admin" created="200708240232" changecount="1">
8552 <pre>CRTは機器によってリフレッシュレートが小数点以下の桁でわずかに異なります(60Hzと表示されていても、厳密には60.2Hzなど小数点以下の桁があります)。
8553 Psychlopsでは、初期化時には小数点以下の桁は無視して確保しますが、正確なタイミングでflipするには、ディスプレイ装置の設定等で小数点以下の桁をあらかじめ調べて、正確な値を指定することをお勧めします。</pre>
8555 <div title="Tips: Canvasを宣言しないと動作しない命令群" modifier="YourName" modified="200908100434" created="200908100433" changecount="4">
8556 <pre>下記命令群はCanvasの実体が確保されていることを前提に動作します。
8558 !!! Display::に属するすべての命令
8559 事前に確保されたCanvas実体への抜け道命令群です。
8560 事前にCanvas実体を確保することが必要です。
8562 !!! Figure.draw() / Shape.draw(Color)
8563 Image.draw()やRectangle.draw(Color)はDisplay::に属する命令と同様に、事前に確保されたCanvas実体を探索して実行する抜け道命令です。
8564 事前にCanvas実体を確保することが必要です。
8566 !!! Color::setGammaValue()
8567 この命令はハードウェアガンマを設定するためにCanvas実体を探索します。
8568 Canvas確保前にこの命令を実行しようとすると失敗します。
8571 デモ用の変数操作コンソールをCanvas上に作ろうとするためにCanvasを探索します。
8572 Canvas確保前にこの命令を実行しようとすると失敗します。
8574 !!! ExperimentalMethods::Demo
8575 Independentと同様ですが、initialize時にCanvasが確保されている必要があります
8577 !!! Widgets::に属するクラスの大半
8578 自身をCanvas上に自動登録しようとするためにCanvasを探索します。
8579 Canvas確保前にこの命令を実行しようとすると失敗します。</pre>
8581 <div title="Tips: CanvasクラスとDisplayクラス" modifier="PsychlopsAdmin" created="200709171711" changecount="1">
8582 <pre>Canvasクラスと同様に使えるクラスとして、Displayクラスがあります。
8584 Cの変数は、その変数が定義されている関数やクラス以外からは操作することができません。
8586 void psychlops_main() {
8587 Canvas display( Canvas::fullscreen );
8590 display.flip(); // エラー、displayはこの関数からは操作できない
8594 Displayクラスは、最後に宣言したCanvasクラスの別名として機能します。このクラスはどこからでもアクセスすることができますが、Canvasの宣言の代わりはできません。
8596 void psychlops_main() {
8597 Canvas display( Canvas::fullscreen );
8600 Display::flip(); // OK
8605 <div title="Tips: Image使用上の注意" modifier="YourName" modified="200803050504" created="200709040623" changecount="4">
8606 <pre>また、この転送はグラフィックボードやマザーボードなどの規格やOSの種類によって速度が違いますが、一般にある程度の時間がかかります。この転送はCanvasの描画内容を画面上へ反映させるよりはずっと大きいもので、かつ信号転送量が大きくなればなるほど、時間が必要になります。
8607 2008年現在の平均的なマシン(iMacやメーカー製のWindowsデスクトップマシン)で、100 Hzの描画に間に合う(つまり10ms以内に転送と描画反映が完了する)Imageのサイズはおおよそ300 x 300 pixel程度でしょう。以上のようなことを考えて、Imageを用いたオフスクリーン描画とCanvasに対する直接の描画を上手く使い分ける必要があります。
8608 また、高速のオフスクリーン転送を実行したい場合には[[Image::quicken()|3.1.4 発展的な内容]]命令が有効な場合があります。詳しくは、関数一覧の[[Image::quicken()|3.1.4 発展的な内容]]の項を参照してください。</pre>
8610 <div title="Tips: OS環境による関数名の衝突について" modifier="Psychlops_DevelopperG" modified="200908100455" created="200908100437" changecount="1">
8611 <pre>ほかのライブラリ、特にOS・コンパイラ標準のライブラリにあるクラスとこの特定のクラスが同じ名前を持っているために、名前が衝突してしまってコンパイルができなくなっていることが考えられます。
8612 このような衝突を起こすことがある命令は以下の通りです。
8614 !! windows.hで問題が生じるシンボル
8616 | !Rectangle | Psychlops::Rectangle |
8617 | !Ellipse | Psychlops::Ellipse |
8618 | !round | Math::round |
8619 | !random | Math::random |
8621 !! MacOSXで問題が生じるシンボル
8623 | !Point | Psychlops::Point |
8624 | !round | Math::round |
8625 | !random | Math::random |
8628 <div title="Tips: OpenGL 1.4に対応した一般向けビデオチップ" modifier="YourName" modified="200803141457" created="200709131950" changecount="15">
8629 <pre>OpenGL 1.4仕様は2002年7月24日に発行されており、基本的にはそれ以降に設計されたビデオチップはこれに対応しています。
8630 http://www.opengl.org/documentation/specs/version1.4/glspec14.pdf
8633 |!開発元|!汎用カード|!OpenGL特殊化カード|
8634 |nVidia|GeForce 6000(NV30)系、FX5000(NV40)系 以降|Quadro FX系|
8635 |AMD(旧ATI)|RADEON 8000(R200)以降、X系|FireGL 8000以降、X, T, Z, V系|
8636 |Intel|945チップセット(GMA950)以降(915でも一応の動作確認はあり)| |
8637 |VIA(S3 Graphics)|DeltaChrome(S8)以降| |
8640 ただし2008年3月現在で以下のマシンにおける動作不良を確認しております。
8641 これらのマシンについてはドライバレベルでのOpenGL動作の改変が行われているようです。
8642 現在のところ対応は未定です。また、今後の改良のため、動作不良等がございましたら、ぜひ[[Psychlops WIKI Trouble Report|http://psychlops.l.u-tokyo.ac.jp/?Trouble+Report]]までご報告をお願いいたします。
8644 |!製造会社名|!機種名|!搭載ビデオチップ|!動作不良の内容|!暫定的な回避策|
8645 |SONY|Vaio Type-T VGN-TZ92S|Intel 945GM|垂直同期を待たずに画面の描画が行われてしまう|未発見|
8646 |Apple|Mac Pro MA463J/A |Radeon X1900XT(他ビデオボードのものについては未検証) |一定以下の負荷しかない描画内容が画面に反映されないことがある|各フレームの描画命令の前に大きなサイズ(全画面)のRectangleを1~数枚表示する|
8648 より新しい情報は、 [[Psychlops WIKI 動作環境 |http://psychlops.l.u-tokyo.ac.jp/?Environment]]に随時反映されます。
8649 また、これらの動作改変はOpenGLを用いたソフトウェア全般で起こっている可能性があります。これらの他のアプリケーションの情報についてもよろしければ[[Psychlops WIKI Trouble Report|http://psychlops.l.u-tokyo.ac.jp/?Trouble+Report]]に情報をあげていただきますよう、お願い申し上げます。
8652 <div title="Tips: OpenGL命令の混ぜ方" modifier="YourName" created="200802201824" changecount="1">
8653 <pre>現在Psychlopsの描画命令形はOpenGLのみで行われており、OpenGLの描画命令を混ぜることができます。この際、いくつかの注意が必要です。
8655 * 回転(glRotate)と拡大縮小(glScale)はほとんどのビデオカードで近似値を使うよう実装されており、ピクセル単位での正しい計算ができなくなりジャギやモアレの原因になります。これを了解した上でお使いください。
8656 * glRotateとglScaleはPsychlops命令形の原点座標を中心に行われます。これらの命令を使った上で表示位置を指定する場合は、回転中心からの相対座標に描画した後、glTranslateをご使用ください。
8657 * Image型はquicken()を適用しないと使うことができません。
8659 Imageを回転させるサンプル(モアレが発生していることをお確かめください)
8661 #include <psychlops.h>
8662 using namespace Psychlops; // Initially developed with Psychlops Win32 20070313a
8665 void psychlops_main() {
8667 Canvas display(Canvas::fullscreen);
8670 Image img(100, 100);
8672 for(int i=0; i<100; i++) {
8673 for(int j=0; j<100; j++) {
8674 col.set(0.25+sin(i/10.0)/4 + 0.25+sin(j/10.0)/4);
8679 // External Image File
8682 img.load("sample.png");
8687 double orientation = 0.0;
8690 while(!Input::get(Keyboard::esc)) {
8693 orientation += PI/10;
8696 glTranslated(display.getHcenter(),display.getVcenter(),0);
8697 glRotated(orientation,0,0,1);
8709 <div title="Tips: Psychlopsにおけるキーボードマウスの入出力検出ルーチン" modifier="YourName" modified="200802201727" created="200709150147" changecount="2">
8710 <pre>Psychlopsのキーボード・マウスの入力監視は、主のプログラムとは独立に自動的に一定の間隔(マシンに依存しますが、通常0.1ミリ秒程度)で行われています。
8712 監視ルーチンは各キーについてPushed, Pressed, Releasedのイベントがあったかどうかの表を内部に保持しています。
8714 監視ルーチンではまず入力デバイスから送られてくるPushed, Releasedのイベントを監視し、イベントがあれば当該キーの当該イベントの表に記録します。この記録はInput::refresh()をするまで残ります。次に、PushedされてからReleasedされるまでの間、Pressedであると表に書き込みます。これはrefreshしなくても常時更新されています。
8716 本来はPressedのデータを基本に取得すべきなのですが、近年のコンピュータではUSB化やOSのセキュリティ強化のためにイベント依存のデータしか取得できないようになっており、このような形式になっています。
8718 マウスの座標系に関してはMac OS X、Windowsともに常時取得・設定が可能です。
8720 現在、多くのUSBキーボード・マウスでは、コンピュータに転送する時間精度は1 msより悪いものとなっています。このため、PsychlopsのKeyboardやMouse命令は、精密な反応時間の取得(数 ms以下のオーダーの精度が必要な反応時間取得)には向きません(ただし、この転送の時間ノイズは数多くの試行を平均加算することで除去できる可能性はあります)。ただし、この値は表示装置の更新間隔(フレームレート)よりは十分に短いため、キーボードやマウスの反応を表示に反映する場合には最悪でも1フレーム遅れる程度にとどまります</pre>
8722 <div title="Tips: QUESTの実装" modifier="YourName" modified="200803140630" created="200803100446" changecount="11">
8723 <pre>QUESTは上下法の一種です。何らかの閾値を測定する場合に恒常法よりも効率的に測定することができます。最尤法とベイズ推定を用いて閾値を推定するのが特徴です。今回は、Psychlopsに追加で組み込めるQUESTのサンプルを実行してみます。
8727 まず最初に、[[QUEST Sample|http://satolab.l.u-tokyo.ac.jp/~psychlops_site/download/BasicSamples/QUEST_Sample.zip]]をダウンロードしてください。
8729 圧縮ファイルを展開すると、3つのファイルが生成されます。
8733 QUESTの本体が書かれています。XcodeやReloで新規プロジェクトを作成した後、以上の2ファイルをプロジェクトと同じフォルダにおいてください。次に、[[プロジェクトにこのプログラムを追加|Tips: プログラムを複数のファイルに分割する]]してください
8736 上記QUESTを呼び出してコントラスト閾値測定を行うデモプログラムです。新しいプロジェクトのプログラムにコピーして貼り付けてください。
8740 このQUESTの実装では、QUEST用の確率表を内部で作成し、それをユーザプログラムからメソッドを呼び出して更新していくモデルをとります。
8742 [img[image/tips/QUEST.png]]
8747 QUESTでは様々な事前情報が必要です。
8749 * その閾値の推測の確からしさはどの程度か
8750 * QUESTで求める閾値は何%に設定するのか
8751 * 被験者がボタンを押し間違える確率はどの程度ぐらいか
8753 といった情報が必要です。これを実験を始める前に設定します。
8756 QUEST(double priorEstimatedThreshold, double logPriorSD, double thresholdProbability,
8757 double slope, double errorRate, double chanceLevel,
8758 Range thresholdRange, double logStep = 0.005, Measure placementMeasure=MEAN);
8761 |!QUEST()|QUEST(double priorEstimatedThreshold, double logPriorSD, double thresholdProbability, double slope, double errorRate, double chanceLevel, Range thresholdRange, double logStep, Measure placementMeasure);|QUEST進行用のクラスを作ります|>|
8762 |~|~|double priorEstimatedThreshold|閾値が大体どのくらいにあるかという推測値です。|
8763 |~|~|double priorSD|推測値の確かさ。閾値が推測値とあまり離れていない場合は小さな値を、離れているかもしれない場合は大きめの値を代入してください。|
8764 |~|~|double thresholdProbability|閾値とみなす正答率。たとえば2肢強制選択(チャンスレベル0.5)の場合は、正答率0.75が閾値とみなされることが多いです。|
8765 |~|~|double slope|心理物理関数の傾き。傾きが既知の場合はその値を代入してください。傾きがわからない場合は効率は落ちますが大きめ値を代入しておいたほうがよいでしょう。|
8766 |~|~|double errorRate|被験者がボタン押しを間違える確率|>|
8767 |~|~|double chanceLevel|チャンスレベル。2肢強制選択なら0.5を代入してください。|
8768 |~|~|Range thresholdRange|閾値の存在範囲を0以上の範囲で指定|
8769 |~|~|double logStep|ステップの値|
8770 |~|~|Measure placementMeasure|推定値を確率分布の最頻値か平均値にしたがって推定します(MODEかMEAN)|
8775 初期化が終われば試行を40-100回ほど繰り返します。
8778 # ループ開始前に刺激の初期値を得ます。
8779 ** 初期値はdouble QUEST::nextTrial()を実行することで最適な値を求めることができます。
8780 ** 通常このようにして求められた値をそのまま使います(手を加える例もあります)。
8781 # 刺激を提示し、被験者からの反応を得ます。
8782 # 実験結果をQUESTに与え、次の試行で使う値を決定します。
8783 ** 2で提示するとき実際に用いた刺激強度と、被験者が成功したか失敗したかをvoid QUEST::next(double intensity, int result)でQUESTに通知します。成功した場合はresultに1を、失敗した場合は0を渡してください。
8784 # 1へと戻り所定の試行数が終わるまでこれを繰り返します
8785 # 所定の試行数が終了したら最後にfinalEstimate()を呼び出すことで実験の結果、推定された閾値を得ることができます。
8787 |!next()|試行の結果をQUESTに通知し、次の試行で用いる値を返します(下記2関数の合成)|>|
8788 |~|double intensity|前回の試行で使われた刺激強度|
8789 |~|int result|前回の試行の被験者の回答。成功した場合はresultに1を、失敗した場合は0を渡してください。|
8791 |!QUEST::nextTrial()|double nextTrial(void)|次の試行で用いる値を返します|>|
8793 |!QUEST::update()|void update(double intencity, int result)|試行の結果をQUESTに通知し、状態をアップデートします。|>|
8794 |~|~|double intensity|前回の試行で使われた刺激強度|
8795 |~|~|int result|前回の試行の被験者の回答。成功した場合はresultに1を、失敗した場合は0を渡してください。|
8797 |!QUEST::finalEstimate()|double finalEstimate(void)|最終結果を返します|>|
8800 #include <psychlops.h>
8801 using namespace Psychlops;
8803 #include "QUEST.h"
8805 void psychlops_main() {
8807 //initialize QUEST method
8808 const double estimatedThreshold = 0.05; //estimated threshold, mean of prior pdf
8809 const double pdfSD = 2.0; //standard deviation of prior pdf
8810 const double thresholdProbability = 0.75; //the probability defined as threshold
8811 const double slope = 3.5; //slope of psychometric function
8812 const double errorRate = 0.01; //error rate
8813 const double chanceLevel = 0.5; //chance level
8814 Range contrastRange;
8815 0< contrastRange <=1.0;
8817 QUEST quest(estimatedThreshold, pdfSD, thresholdProbability, slope, errorRate, chanceLevel, contrastRange);
8820 double contrast = quest.nextTrial(); //contrast of stimuli at each trial
8821 int result; //result of trial
8822 int trial = 50; //number of trial
8826 Canvas canvas(Canvas::fullscreen);
8828 gabor.set(50, 50, 1.0, 0, 0).centering();
8829 int display_frames = 1*canvas.getRefreshRate(); // display gabor for 1 second.
8832 for (int i = 0; i<trial; i++) {
8834 //replace this section to your experiment
8835 // displaying stimuli
8836 for(int i=0; i<display_frames; i++) {
8846 if(Input::get(Keyboard::left)) { result = 1; break; } // positive reaction
8847 if(Input::get(Keyboard::right)) { result = 0; break; } // negative reaction
8851 gabor.contrast = quest.next(contrast, result);
8855 double threshold = quest.finalEstimate();
8856 double sd = quest.getSD();
8863 QUESTでは心理物理関数にWeibull分布を仮定しています。そのため、心理物理関数がWeibull分布で近似できない場合は使えませんのでご注意ください。
8867 <div title="Tips: Rectangleの配列を用いたドットパターンの描画例" modifier="YourName" modified="200802201227" created="200802191422" changecount="5">
8868 <pre>ランダムドットパターンなどの複数のドットを使ったパターンの描画で、
8869 ドット密度や各ドットの運動方向を精密に操作したいときは、
8870 [[3.2節|3.2 オフスクリーンを用いた描画例-ランダムドットパターン-]]で紹介した方法よりは、
8871 Rectangleの配列を用いた方法のほうがプログラミングが容易です。
8872 いかにRectangleの配列を用いたドットパターンの描画の典型的なプログラム例をしめします。
8873 ここでは、Rectangleの配列のうちの一部をランダムな方向に運動させ(Noise)、残りを右方向へ運動させています(Signal)。
8876 #include <psychlops.h>
8877 using namespace Psychlops;
8879 void psychlops_main() {
8881 Canvas display(Canvas::fullscreen);
8883 // Generic Random Dot Parameters
8884 const int DOT_SIZE = 3;
8885 const int MAX_NUM_DOTS = 1000;
8886 const double speed=3.0;
8887 const double noise_dots_ratio = 0.5;
8889 // Parameters of each dots
8890 Psychlops::Rectangle dot[MAX_NUM_DOTS];
8891 Psychlops::Rectangle canvas_rect(display.getWidth(), display.getHeight());
8894 // Randomize Dots' Position
8895 for(int i=0; i<MAX_NUM_DOTS; i++) {
8896 dot[i].set(DOT_SIZE, DOT_SIZE);
8898 Psychlops::random(display.getWidth()),
8899 Psychlops::random(display.getHeight())
8903 while(!Input::get(Keyboard::esc)) {
8906 // Update noise dots' position
8907 for(int i=0; i<MAX_NUM_DOTS*noise_dots_ratio; i++) {
8908 direction=Psychlops::random(2*PI);
8909 dot[i].shift(speed*cos(direction), speed*sin(direction));
8910 if(!canvas_rect.include(dot[i]))
8912 Psychlops::random(display.getWidth()),
8913 Psychlops::random(display.getHeight())
8916 dot[i].draw(Color::white);
8919 // Update signal dots' position
8920 for(int i=MAX_NUM_DOTS*noise_dots_ratio; i<MAX_NUM_DOTS; i++) {
8921 dot[i].shift(speed, 0.0);
8922 if(!canvas_rect.include(dot[i])) dot[i].shift(-display.getWidth(), 0.0);
8923 dot[i].draw(Color::white);
8932 <div title="Tips: Shapeクラスの概要" modifier="Psychlops_DevelopperG" modified="200912010828" created="200912010822" changecount="1">
8933 <pre>Shapeクラスに属するクラスは、デフォルトの描画色や枠線を指定することができます。
8934 このデフォルトの色と線Groupでまとめたりする際にとくに有用です。
8935 これらの指定はShapeクラスのメンバ fill, strokeに対して代入を行うことで実行します。
8937 |!Shape::fill|デフォルトの塗り色です。Colorが代入できます。初期状態では無色です。|
8938 |!Shape::stroke|デフォルトの枠線をStroke型で指定します。初期状態では太さ0の線です。|
8940 枠線を指定ためにはstrokeクラスの変数を宣言して、書式指定を行っておく必要があります。
8941 書式指定には宣言時に指定をする方法とset()命令を使用する方法があります。
8942 これらの詳細については[[Tips: Strokeクラスの概要]]をご覧ください。
8945 #include <psychlops.h>
8946 using namespace Psychlops;
8948 void psychlops_main() {
8949 Canvas canvas(Canvas::window);
8951 Psychlops::Rectangle rect(100,100);
8952 rect.centering().shift( 100, 0);
8953 rect.stroke.set(Color::red, 5, Stroke::DOTTED);
8955 Shape::Colored<Psychlops::Ellipse> ellipse;
8957 ellipse.centering().shift(-100, 0);
8958 ellipse.fill = Color::white;
8960 while(!Keyboard::esc.pushed()) {
8970 この命令セットのうちでここまで扱わなかったものとしてgetDatum()があります。
8972 |!Point Shape::getDatum()|getDatum(void)|Figureの「基準座標」をPoint型で返します. <br> 基準座標の定義は各クラスによりますが、原則として中心点や起点(図形の左上の点)です|</pre>
8974 <div title="Tips: Strokeクラスの概要" modifier="Psychlops_DevelopperG" modified="200912010835" created="200912010830" changecount="5">
8975 <pre>Strokeクラスは、線の書式を表現するためのクラスです。
8976 描画系の関数にColorの代わりに Stroke構造体を与えることで線を描画します。
8979 * ImageにStrokeを描画することはできません。
8980 * 線が角ごとに途切れるうえ、円が多角形近似のため、ある程度以上太くしたり破線を指定したりするとアラが目に見えてわかります。
8983 コンストラクタ, set関数 (Color color, double thick, Stroke::Pattern pattern)
8988 線の太さです。現状ピクセル単位になります。
8990 バターンの種類はSOLID, DASHED, DOTTEDです
8994 Stroke solid(Color::red, 1, Stroke::SOLID);
8995 // 宣言と設定を同時に行う方法は関数内でのみ使用できます。
8996 Stroke dashed, dotted;
8997 dashed.set(Color::green, 3, Stroke::DASHED);
8998 dotted.color = Color::blue;
9000 dotted.pattern = Stroke::DOTTED;
9002 Psychlops::Rectangle rect(100,100);
9004 while(!Keyboard::esc.pushed()) {
9007 rect.shift(-150,0).draw(solid);
9008 rect.shift( 150,0).draw(dashed);
9009 rect.shift( 150,0).draw(dotted);
9015 [img[image/Stroke_Sample.png]]
9019 <div title="Tips: Vistaにおけるインストール" modifier="YourName" modified="200802210940" created="200802191504" changecount="3">
9020 <pre>Windows VistaでPsychlopsを利用する場合、いくつかの注意が必要です。
9022 !! ビデオカードのドライバがOpenGLをサポートしているか確かめる
9023 Windows Vistaでは、DirectXがOpenGLより優先されたため、OpenGLへのサポートが十分でないビデオカードがあります。2008/2/14現在でWindows VistaでOpenGLが十分にサポートされているビデオカードは以下のとおりです。
9025 * nVidia GeForce 7000番台以降
9026 * AMD(ATI) Radeon Xシリーズ
9029 !! インストーラの実行時に管理者権限で実行する
9031 BCC、SetBCC、Psychlopsのインストーラをダブルクリックしても、何も起きない(ように見える)、あるいは明らかに実行に失敗しているように見える場合があります。その場合、実行ファイルを右クリックして「管理者として実行」をクリックし、管理者のパスワードを入力して実行してください。
9032 [img[Vista|image/Win/VistaBCC_1.png]]
9035 !! Borland C++ Compilerの環境変数を設定する
9037 [[BCCのインストール|1.3 インストール作業(Windows)]]を実行した後、追加で以下の作業を行います。
9039 0. スタートボタン → コントロールパネルを開きます
9041 2. 「環境変数を編集」をクリックします
9042 3. 開いたウインドウで、「変数」の中に「PATH」があるかどうかを調べます
9043 4.1. 「PATH」があった場合、それを選んで「編集」ボタンを押します。開いたウィンドウで変数値の末尾に以下のテキストを''追加します''。セミコロンは必要なので落とさないようにしてください。
9045 ; C:\borland\bcc55\Bin;
9047 4.2. 「PATH」がなかった場合、「新規」ボタンを押します。開いたウィンドウで、変数名に「PATH」、変数値に以下のテキストを書き込みます
9049 C:\borland\bcc55\Bin;
9051 [img[Vista|image/Win/VistaBCC_2.png]]
9055 <div title="Tips: Xcodeで実行時エラーが起きるとフリーズする" modifier="YourName" modified="200802130846" created="200709171710" changecount="5">
9056 <pre>Xcodeで実行時エラーが起きると操作を受け付けなくなる現象があります。これは実際は実行時エラー発生時にデバッガが起動しており、フルスクリーンのために見えないだけです。フリーズはしていません。この現象は以下に例示する方法で回避できます。
9059 複数ディスプレイを使用した場合、Psychlopsは主ディスプレイ(メニューバーのあるディスプレイ)に表示され、副ディスプレイを拘束しません。副ディスプレイにデバッガウィンドウを表示しておくことで、デバッガからのプログラム終了やエラー追跡ができます。
9062 デバッガの起動をとめるには、プロジェクト中の「実行可能ファイル」タブから、
9066 と並んでいるうちの一番上のstaticを右クリックまたはctrl+クリックして、「情報を見る」を選びます。
9068 [img[実行ファイルの情報|image/OSX/Psychlops_OSX_dbg1.png]]
9070 開いたダイアログで「デバッガ」タブを選び、すべてのチェックボックスをオフにします。
9071 [img[実行ファイルの情報|image/OSX/Psychlops_OSX_dbg2.png]]
9074 演算性能によりますが、数分間待つと回復することがあります
9078 <div title="Tips: intどうしの比率の計算がおかしい" modifier="YourName" modified="200803050457" created="200802202143" changecount="2">
9079 <pre>C++では、intどうしの割り算を行うと小数点切捨ての整数で帰ってきます。このため、特にfor文のカウント変数と最大値から比率を求めようとするときなどで精度差が出ることがあります。これを回避するには型キャストしなければなりません。また、定数項はなるべく「.0」をつけて強制的に浮動小数点値と解釈させると、その項の演算結果を利用する演算子の結果はすべてdouble型にキャストされます。これを覚えておけば「式はあっているはずなのに何も表示されない」といったバグはずいぶんと減るはずです。
9082 for(int i=0; i<max; i++) {
9083 Display.pix(i, 0, Color( (double)(i)/max ));
9084 Display.pix(i, 2, Color( i*2.0 / max));
9090 <div title="Tips: アルファ混色の活用" modifier="YourName" modified="200803140631" created="200802201822" changecount="7">
9091 <pre>Psychlopsが標準で扱うアルファ値(透明度)は、以下のように定義されています。
9094 結果 = α×上書き色 + (1-α)×元の色
9097 したがって、アルファ値を0.5とおくと元の色と上書き色の平均値になります。アルファ合成は合成結果が重み付け平均として表せるときに有効です。これ以外の計算方法でアルファ合成を行いたい場合には[[glBlendFunc命令を使用して直接指定をおこなってください|Tips: OpenGL命令の混ぜ方]]。
9099 アルファ合成はビデオカード上で行われるために高速ですが、[[ガンマ補正|Tips: ガンマ補正]]がハードウェア合成の場合にのみ正常に機能し、ソフトウェアエミュレーションである場合には補正済みの値どうしの平均を行ってしまうために誤った値が表示されます。これを防ぐためには、[[Color::strict_match = true;|Color::strict_match]]を指定してソフトウェアエミュレーションを機能させないようにしてください。
9102 以下のコードを実行すると、横縞のガボール刺激に縦縞のダイナミックノイズがかかった刺激が描画されます。ここでは、縦縞のダイナミックノイズをアルファ合成を用いてガボール刺激に重ね合わせています。[[Tips: 独自のクラスを作る]]で例示されている~QuickGaborもアルファ合成を用いています。
9104 [img[image/tips/Sample_alpha.png]]
9107 #include <psychlops.h>
9108 using namespace Psychlops;
9110 void psychlops_main() {
9112 Canvas display(Canvas::fullscreen);
9114 Psychlops::Rectangle noise;
9118 gabor.set(30,30).centering();
9119 gabor.orientation = 90.0; // don't
9121 while(!Input::get(Keyboard::esc)) {
9126 gabor.contrast = sin(PI*gabor.phase/360.0);
9130 noise.set(1, display.getHeight()).centering(-1,display.getVcenter());
9131 for(int i=0; i<display.getWidth(); i++) {
9132 noise_lum = Psychlops::random();
9134 // **************draw with alpha-blending*****************************
9135 noise.shift(1,0).draw(Color(noise_lum, noise_lum, noise_lum, 0.5));
9145 <div title="Tips: ガンマ補正" modifier="Psychlops_DevelopperG" modified="200910080850" created="200802130705" changecount="15">
9146 <pre>ディスプレイやプリンタなど、コンピュータから利用できる画像表示機器では、明るさが指定した値通りに表示されず、歪みが生じます。多くの場合はこの歪みは指数関数に従います。その指数を'''ガンマ値'''と呼び、表示装置の特性を記述するパラメタになります。CRTの場合、ガンマ値は赤、緑、青の蛍光体の間で独立です。
9148 [img[輝度歪み|image/DisplayGamma.png]]
9150 PsychlopsのColor型はガンマ補正機能を備えています。ガンマ補正にはガンマ値による指定のほかに、テーブル(補正表)による指定方法があります。CRT以外の表示装置の特性は必ずしもガンマ値型指数関数に従うとは限りませんので、液晶やプラズマ、DLPプロジェクタなどではテーブルによる指定を試みる必要があります(番、山本、江島、2006)。
9154 double gamma_r=1.7, gamma_g=1.6, gamma_b = 0.6;
9155 Color::setGammaValue(gamma_r, gamma_g, gamma_b);
9160 Psychlopsのガンマ補正は、標準でハードウェアの調整機能を起動し、それに失敗した場合ソフトウェアによるエミュレーションを試みます。
9162 [img[ソフト補正とハード補正|image/ColorCalibrationModel.png]]
9165 最近のビデオ表示機器は、理論値と電気出力値の変換テーブルを内部に保持しており、このテーブルに補正値を書き込むことで補正機能を有効にすることができます。書き込みはDVI接続時でも成功しますが、有効に機能するかはハードウェアに依存します。確認するためには実測が必要です。
9167 ハードウェア補正では、以下のOSのAPI関数を呼び出します。
9172 CGSetDisplayTransferByFormula
9173 CGSetDisplayTransferByTable
9177 ソフトウェア補正では、Color型とImage型の入出力時に変換関数を適用することで、ハードウェア補正と同様の機能を実現します。Color型とImage型の入出力関数で補正を行い、補正後の値を保存することで実現しています。
9179 ソフトウェア補正はハードウェア補正に比べいくつかの点で劣ります。
9180 * 理論値の変化に対して鋭敏に輝度が変化する部分とそうでない部分が生じます。鋭敏でない部分では輝度変化がつぶれてしまいます(下図)
9181 * ガンマ値をプログラム中で変更した場合、それ以前までにsetされたColor情報は古いガンマ値のエミュレーション結果に従った値が保存されており、新しいガンマ値に従わない誤った色が表示されます。いっさいの警告が出ませんのでご注意ください。
9182 * 変換をCPUで計算するため、速度が低下します。
9183 * ソフトウェア補正で補正テーブルを使用した場合、さらに問題が生じます
9184 ** 色指定は通常0~1の実数ですが、補正テーブル適用時は入力値がテーブルの行数にあわせて離散化されます。このため、Color.set()で入力した値とget()で取得した値が異なることがあります。
9185 ** 複数の理論値に対して同一の補正値が割り当てられている場合、get()で復元する場合に入力理論値と異なることがあります。
9187 [img[ソフトウェア補正の鋭敏さ|image/SoftwareColorCalibration.png]]
9190 !! 表示装置固有の輝度特性測定時の注意
9191 ハードウェア補正が適用されたPsychlopsを用いて表示装置の特性を測定する場合には、OSが標準で行っている補正(ほとんどの場合非線形ですを外すために、以下の関数を実行してください。
9193 Color::setGammaValue(1,1,1);
9196 *この記述はver1.0 (Psychlops20080214)以降での注意です。これ以前のバージョンに使用されているsetGammaValue命令は常にソフトウェア補正を用いているために、OSによる標準の補正を気にする必要はありません。
9199 特に厳密な色補正を保証したい時に、ガンマ補正や色指定で値が0以上1以下にならないケースやソフトウェア補正が適用される環境下ではプログラムが停止されるように強制させることができます。以下のコードをpsychlops_main()の冒頭に記述してください。
9201 Color::strict_match = true;
9207 番浩志、山本洋紀、江島義道 (2006) "Mcalibrator: MATLAB言語を用いたディスプレイ較正のための統合型GUIソフトウェアの開発" 基礎心理学研究、第24巻、149-161頁
9210 <div title="Tips: トリガーとアナログ入力" modifier="YourName" modified="200802201728" created="200802201438" changecount="4">
9211 <pre>脳波やfMRI研究では電気的トリガを出す必要がありますが、[[Tips: 機器間の同期]]で説明したとおり、機器間の同期を取ることが非常に困難であり、視覚刺激提示装置と電気パルスを同期制御することは難しくなっています。
9213 この問題を確実に解決する方法としては、画面の輝度をフォトトランジスタで電気信号に変換する方法があります。フォトトランジスタの照度に対する反応はきわめて高い時間精度を持つため、輝度の状態を得るには適しているといえます。
9215 一般的な電気信号入出力装置は電流ではなく電圧を信号として扱いますので、フォトトランジスタの照度への反応を電圧の変化として取り出す回路が必要です。一般的な回路図を以下に示します。
9217 [img[image/Tips/phototrigger.png]]
9219 フォトトランジスタを吸盤などに埋め込んで画面に設置できるようにして、フォトトランジスタに直列につないだ抵抗器の両端の電圧を電圧計に入力します。
9221 * たとえばCRTの場合、陰極線が画面を走査するには一定の時間が必要です。したがって、画面上部と画面下部では輝度の変化する時間に数ミリ秒の差があります。
9222 * 電圧の範囲は、測定照度、フォトトランジスタの特性、抵抗器の抵抗値、によって決まります。一般的には、抵抗を変えることで必要な電圧範囲になるようにします。
9223 * 刺激提示画面上に電圧計を設置するのが難しい場合、マルチディスプレイを用いて2画面に同じ画像を提示し、実験に使わないほうの画面でトリガーをとる方法があります。この方法では、画面間の同期はとれていませんが、垂直同期間隔は一定です。あらかじめ両画面の同期の時間差を測定しておくことで、正確な同期タイミングを算出することが可能です。
9226 <div title="Tips: トリガーとアナログ入力2-外部機器を利用した例-" modifier="YourName" modified="200803062207" created="200803062201" changecount="6">
9227 <pre>脳波実験等では参照トリガ電圧が必要になることがありますが、近年のコンピュータでは、電気的外部入出力端子(~RS-232Cなど)を持つものが少なく利用しにくくなっています。そこで今回は、比較的容易に入手可能なNational Instruments社製[[USB-6009|http://sine.ni.com/nips/cds/view/p/lang/en/nid/14605]]を用いてアナログ入出力をしてみます。
9229 ~USB-6009は電圧データ入出力用機器です。
9230 * アナログ入力 4 ch、アナログ出力 1 ch
9231 * 時間分解能:入力 48 kHz 出力 150 kHz
9232 * 電圧分解能:入力 14 bit 出力 12 bit
9233 と、心理物理学には十分な性能を持っています。価格は35000円程度で、電圧入出力機器としては比較的安価に入手できます。
9235 実際に実験に使う上では、他の入出力機器との時間同期を気にする必要があります。
9238 ** USBにはキーボード・マウス等、最低限のものしかつながないでください。USBメモリ・ハードディスク・USBオーディオ・Webカメラ等、大量のデータを転送するものとの併用は危険です。
9240 ** データ取得までの時間遅れは、少なくとも10ms以下程度に抑えられています。したがって、入力を元に画像を制御する用途には用いることができます。
9241 ** 入力は4ch同時に取ることができるので、画面のトリガと反応ボックス等を組み合わせて入力することで、入力した機器間で厳密な同期をとることができます。
9243 ** データを~NI-6009に送信した後、~USB-6009のデジタル入力端子に外部から別のトリガ信号を入力することで、そのトリガに同期してデータを出力することができます。
9244 *** たとえば、VGAの垂直同期信号をデジタル入力端子に誘導しておくと、任意の時点で送信したアナログ出力データを次のflipに同期して出す、といったことが可能になります。
9247 今回は、もっとも簡単なサンプルとして、~USB-6009をひとつ接続し、10ms程度の精度で「電圧値を得る」「電圧を出す」を単発で行う拡張サンプルを実行してみます。高度な時間制御は行っておりません。なお、このサンプルを応用したプログラムを配布する場合は、配布先の環境でも同様の装置が必要です。
9253 !! Mac OS XでXcodeを使用する場合
9254 * [[NI-DAQmx Base|http://joule.ni.com/nidu/cds/fn/p/sn/n19:MacOS.MACOS10/sb/navsRel/lang/en?q=NI-DAQmx+Base&x=10&y=7]]ドライバの最新版をダウンロードします。
9255 * ダウンロードしたファイルを実行し、ドライバをインストールします(要再起動)。
9257 * [[NIDAQmxBase_Sample_OSX.zip|http://psychlops.l.u-tokyo.ac.jp/download/BasicSamples/NIDAQmxBase_Sample_OSX.zip]]をダウンロードし、解凍します
9259 ** 「~NIDAQmxBase_Ext_Installer」をダブルクリックして実行します(下記動作を行うシェルスクリプトです)
9260 *** 失敗した場合、手動で「nidaqmxbase_ex_sample.cpp」を/Library/Frameworks/Psychlops.framework/Headers/Extentionsにコピーします。
9262 新規プロジェクトを作成し、プロジェクトのプロパティでNI-DAQmx Baseドライバを読み込む設定を行います。
9263 * 「External Frameworks and Libraries」をCtrl+クリックし、追加→既存のフレームワークから「nidaqmxbase.framework」を選択
9264 [img[image/OSX/nidacmxbase_OSX_1.png]]
9266 !! WindowsでBCC5.5 + Reloを使用する場合
9268 まず最初に、必要なファイルのセットアップを行います。
9270 * [[NI-DAQmx Base|http://joule.ni.com/nidu/cds/fn/p/sn/n19:Windows,n23:3478/sb/navsRel/lang/en?q=NI-DAQmx+Base&x=0&y=0]]ドライバの最新版をダウンロードします。
9271 * ダウンロードしたファイルを実行し、ドライバをインストールします(要再起動)。
9273 * [[NIDAQmxBase_Sample_win32.zip|http://psychlops.l.u-tokyo.ac.jp/download/BasicSamples/NIDAQmxBase_Sample_win32.zip]]をダウンロードし、解凍します
9275 ** 「~NIDAQmxBase対応Psychlops拡張のインストーラ.bat」をダブルクリックして実行します(nidaqmxbase_ex_sample.cppをExtentionフォルダにコピーします。
9276 ** 「~NIDAQmxBaseをBCCに対応させる.bat」をダブルクリックして実行します(bccのツールを呼び出します・要管理者権限)
9279 新規プロジェクトを作成し、プロジェクトのOptionsダイアログで、NI-DAQmx Baseドライバを読み込む設定を行います。
9280 *「Linker」タブの下部にある「Library」に「nidaqmxbase_bcc.lib」を追加(末尾にスペースをひとつ空けて追加します)
9281 * DirectoriesタブにNI-DAQmx Baseを追加する
9282 ** 「Extra Include Paths」に「C:\Program Files\National Instruments\NI-DAQmx Base\Include\」を追加
9283 ** 「Extra Library Paths」に「C:\Program Files\National Instruments\NI-DAQmx Base\Lib\」を追加
9284 [img[image/win/nidaqmxbase_Win32_1.png]]
9291 * NI ~USB-6009 の2番(+)、3番(-)ポートに入力線を接続する
9292 * #include <Extentions/Samples/nidaqmxbase_ex_sample.cpp>を追加
9293 * ~NIDAQmxBase_AnalogInput_Dev1 クラスの変数を作成し、ポートを初期化する
9294 * 変数のget()メソッドを呼び、ポート間の電位差を得る
9295 これで、0Vから5Vまでの電位差を得ることが可能になります。
9297 以下のサンプルでは、中心に長方形が表示され、その縦幅が電圧値(V) * 100 ピクセルとしてリアルタイムに表示されます。
9300 #include <psychlops.h>
9301 using namespace Psychlops;
9302 #include <Extentions/Samples/nidaqmxbase_ex_sample.cpp>
9304 void psychlops_main() {
9305 Canvas canvas(Canvas::fullscreen);
9307 double rect_size = 100;
9308 Psychlops::Rectangle rect;
9311 NIDAQmxBase_AnalogInput_Dev1 analog_in;
9313 while(!Input::get(Keyboard::esc)) {
9315 rect.resize(rect_size, rect_size * analog_in.get()).draw(Color::white);
9321 このサンプルをフォトトランジスタと接続して照度に連動する実行例が[[Cliplife|http://cliplife.jp/clip/?content_id=usqdz3cv]]にアップロードされています。
9326 * NI USB-6009 の14番、15番ポートに出力線を接続する
9327 * ~NIDAQmxBase_AnalogOutput_Dev1 クラスの変数を作成し、ポートを初期化する
9328 * 変数のput(double voltage)メソッドを呼び、ポート間の電位差を渡す
9329 これで、0Vから5Vまでの電位差の出力が可能になります。
9331 以下のサンプルでは、マウスが画面最上部にあるとき1V、最下部にあるとき0Vになります。
9334 #include <psychlops.h>
9335 using namespace Psychlops;
9336 #include <Extentions/nidaqmxbase_ex_sample.cpp>
9338 void psychlops_main() {
9339 Canvas canvas(Canvas::fullscreen);
9341 NIDAQmxBase_AnalogOutput_Dev1 analog_out;
9344 while(!Input::get(Keyboard::esc)) {
9346 analog_out.put(1.0-1.0*Mouse::y/Display::getHeight());
9352 このサンプルを電圧計で計測した実行例が[[cliplife|http://cliplife.jp/clip/?content_id=opt044zr]]にアップロードされています。
9353 出力を直接ヘッドホンにつなぐと音が鳴りますが、解像度が150 Hzしかないので精度には期待しないほうがいいでしょう。
9356 <div title="Tips: ビープを出す" modifier="YourName" modified="201003111856" created="200802201253" changecount="5">
9357 <pre>精度をまったく気にせず単に音がなればいい場合、システムのビープ音を利用する方法があります。
9360 Windowsの場合、以下の命令でPCのビープ音を発生させます。ただし、PCによっては機械ビープが省略されているものがあります。
9362 Beep(int 持続時間, int 周波数);
9366 Mac OS Xの場合、以下の命令でOSの警告音を発生させることができます
9371 * Apple社より、この命令は将来的に廃止されることが予告されています。
9372 * OSの警告音が1度なるだけで、持続時間の指定にはまったく効果はありません。
9375 将来的には、Psychlopsで機種非依存の命令を追加する予定です。
9381 <div title="Tips: プログラムが暴走したとき" modifier="PsychlopsAdmin" created="200709171720" changecount="1">
9382 <pre>プログラムの実行中に強制的に終了したい場合があります。このような場合はOSの強制終了コマンドを使ってください。
9385 ** ESC + Option + Command(花記号)キーを同時に押す
9388 ** 上記方法で失敗した場合、Ctrl + Alt + Delを同時に押して、タスクマネージャから強制終了する</pre>
9390 <div title="Tips: プログラムを複数のファイルに分割する" modifier="YourName" modified="200803140500" created="200803110823" changecount="4">
9391 <pre>実験間で共通の処理がある場合、管理を容易にするためにその部分のプログラムを別ファイルに分けて使いまわすことができます。
9394 既存のファイルをプロジェクトに追加する場合は、以下の操作を行います。
9398 # 「ループとファイル」の「Sources」を右クリックし、「追加」「既存のファイル」と選ぶ
9399 # 開いたウィンドウでファイルを選択し、追加する
9401 [img[image/osx/Xcode_addFiles.png]]
9405 # プロジェクトのファイル一覧を右クリックし、「Add Files」を押す
9406 # 開いたウィンドウでファイルを選択し、追加する
9408 [img[image/win/Relo_addFiles.png]]
9411 新規にファイルを作ってプロジェクトに追加する場合は、以下の操作を行います。
9415 # 「ループとファイル」の「Sources」を右クリックし、「追加」「新規ファイル」と選ぶ
9416 # ファイル名等を決めて新規ファイルを作成
9420 # プロジェクトのファイル一覧を右クリックし、「New Souce File」を押す
9421 # 新しいタブ「untitled」が開かれているので、保存ボタンを押し、名前と保存場所を決める
9425 <div title="Tips: ランダムドットステレオグラムの描画コード" modifier="Psychlops_Admin" created="200709130536" changecount="1">
9426 <pre>下の例ではダイナミックランダムドットステレオグラムを描画します。
9427 スペースキーを押すと、ポーズがかかります。
9428 リターンキーを押すと、対応領域の可視化モードが切り替わります。
9429 エスケープキーを押すと、プログラムが終了します。
9432 #include <psychlops.h>
9433 using namespace Psychlops;
9435 void psychlops_main()
9438 Canvas display(Canvas::fullscreen);
9439 const int BUFFER = 10;
9440 const int whole_size = 200;
9441 const int stereo_size = 100;
9442 double disprity = 10.0;
9447 Image base[2][BUFFER], stereo[2][BUFFER];
9448 for(int i=0; i<BUFFER; i++) {
9449 for(int j=0; j<2; j++) {
9450 base[j][i].set(whole_size, whole_size);
9451 for(int y=0; y<whole_size; y++) { for(int x=0; x<whole_size; x++) { base[j][i].pix(x,y,Color(Psychlops::random())); }}
9453 stereo[0][i].set(stereo_size, stereo_size);
9454 stereo[1][i].set(stereo_size, stereo_size);
9455 for(int y=0; y<stereo_size; y++) { for(int x=0; x<stereo_size; x++) { stereo[0][i].pix(x,y,Color(Psychlops::random()*0.5,0.0,0.0)); }}
9456 for(int y=0; y<stereo_size; y++) { for(int x=0; x<stereo_size; x++) { stereo[1][i].pix(x,y,Color(Psychlops::random())); }}
9460 while(!Input::get(Keyboard::esc)) {
9461 if(Input::get(Keyboard::spc)) pause = !pause;
9462 if(Input::get(Keyboard::rtn)) dotsurface = 1-dotsurface;
9468 base[0][loop].centering().shift(-whole_size, 0).display();
9469 stereo[dotsurface][loop].centering().shift(-whole_size, 0).display();
9470 base[1][loop].centering().shift(whole_size, 0).display();
9471 stereo[dotsurface][loop].centering().shift(whole_size+disprity, 0).display();
9472 Display::msg("Press space key to pause.", 400, 100);
9473 Display::msg("Press return key to switch visualize mode.", 400, 120);
9474 Display::msg("Press esc key to exit.", 400, 140);
9482 <div title="Tips: 時間精度が必要なプログラムを実行するとき" modifier="PsychlopsAdmin" created="200709171719" changecount="1">
9483 <pre>現在のコンピュータは同時に多数のソフトが起動しており、その計算に処理時間をとられてフレーム落ちする場合があります。これを回避するにはいくつかの方法があります。
9485 * いったん再起動する(余計なソフトウェアを強制終了させることができます)
9486 * AppState::setThreadPriority( AppState::HIGH );関数を実行する(今後改名するかもしれません)
9490 ** forやwhile内で不必要な繰り返し計算を除去する
9491 ** pixの配列を書き込む計算の場合、for文をやめてCanvas.pix(ドット数, 配列)の呼び出しを行う
9492 * Imageが原因ならquicken()の使用を検討する
9493 * より処理速度の速いコンピュータを使用する
9496 <div title="Tips: 機器間の同期" modifier="YourName" modified="200802201725" created="200802201301" changecount="4">
9497 <pre>現代のコンピュータはOSやハードウェア規格のレベルで機器間の同期を保証できないシステムに置き換えられています。このTipsでは、各機器の同期精度について説明します。
9500 現代のコンピュータは、互換性の保持、失敗からの回復、暴走の回避を目的として、さまざまなインターフェースが仮想化され、パケット通信化されています。このため、コンピュータのクロックのみに依存した同期確保方法が利用できなくなっています。
9502 ただし時間管理が必要な機器、たとえばCPUのクロック、ビデオカードの垂直同期信号、サウンドカードの連続的音声出力などはそれぞれ独自のクロック信号により精確な時間管理を行うことができます。これらの機器内部で精確なインターバルを得ることはできます。
9504 しかし、機器間で同期を取ることは困難です。視聴覚時間同期といった信号は生成するのが非常に困難です。機器間で同期を取る方法としては、同期を制御する装置をひとつに絞り、その装置の外部にアナログ的な方法で同期装置を設ける方法が推奨されます。この方法は非常に精確です。
9508 最近のOS(Windows 2000以降やMac OS X)はプリエンプティブマルチタスク方式と呼ばれる、ひとつのアプリケーションが中央演算装置(CPU)を独占できない方式を採用しています。このため、あるアプリケーションが処理される速度は不定で、周辺機器の割り込みを実時間で拾えるとは限りません。
9512 近年のビデオカードはダブルバッファリングとバッファ切り替えの垂直同期信号との同期を行うことが標準化されており、ドライバの設定によって(Windowsでは画面の設定で指定、Mac OS Xでは標準)でその機能を利用できるようになり、描画途中で描画内容の転送が起こることのないように設計されています。Psychlopsではこれらのビデオボードに指示を行い、各描画フレームの垂直同期信号との同期を実現しています。
9514 しかし、バッファ切り替えの行われた正確な時間を知ることはできません。
9518 キーボード、マウスなどHID(Human Interface Devices)は、一般的にはインタラプト転送と呼ばれる、一定間隔で行われる転送を用いて入力されます。これは比較的よい時間精度を持っていますが、一般的なGUIを持つOSでは、ウィンドウの配置を加味してカーネル等から各アプリケーションにプロセス間通信(時間がかかり、同期が維持されない)を用いてHIDイベント(キーを押した、離したなど)の情報が送られます。このため、アプリケーション側がHIDイベントを受け取るタイミングは同期が保証されません。
9520 多くのOSでは、HIDイベントにそれが発生した時間情報が付加されます。アプリケーションが知ることができるHIDの時間情報はそれに制限されます。
9522 HIDイベントに付加された時間情報が十分に正確であるならば、たとえば押し続けた時間は比較的正確に得ることができます。しかし視覚刺激提示からの反応時間をとることは、視覚刺激提示装置とHIDの同期が必要であり、そのままではできません。
9526 一般的なサウンドカードは、同期信号出力に用いるには向いていません。
9527 * 音声信号が途切れないように大き目のバッファを設けているため、数10ms以上のレイテンシがあります。
9528 ** ASIOとよばれる規格に対応したドライバ、サウンドカードをセットで使用すると、低レイテンシでの通信ができることがあります。一般に、5ms以下のレイテンシで動きます。
9529 * 信号の正確性があまり求められていないため、エラー訂正が不十分である場合や、通信失敗時に再送出をあきらめる場合があります。
9534 <div title="Tips: 独自のクラスを作る" modifier="YourName" modified="200803140504" created="200803062209" changecount="6">
9535 <pre>自分で作成した機能(コード)を多数のプロジェクトで使いまわす場合、ユーザ独自の拡張としてクラス化し、Psychlopsのライブラリフォルダ追加すると簡単に利用することができます。
9537 ここでは、例として扇形(パックマン図形)を多角形近似したものをクラス化してみます。このクラスは、以下の動作を行います。
9538 * 扇の半径、開口角、開口方向を指定して、多角形として作成する。
9539 * Rectangleと同じようにshift, centering, drawできるようにする。
9541 クラスの中身はひとまず置いておき、ひとまずこのクラスを拡張として登録してみましょう。テキストエディタ(Windows: メモ帳、MacOSX: Xcode→メニュバー→ファイル→新規ファイル)に下記コードをコピー&ペーストし、「pacman.cpp」という名前をつけて保存します。
9544 #include <psychlops_lib.h>
9545 using namespace Psychlops;
9548 // Polygonally approximated Pacman(sector) shape
9552 Psychlops::Point center;
9553 Psychlops::Point *vertices;
9558 Pacman(double radius, double open_angle_radian, double orientation_radian) {
9559 int end = floor(radius * (1 - Math::mod(open_angle_radian, 2*PI)/(2*PI)));
9560 num_vertices = end+2;
9561 vertices = new Psychlops::Point[num_vertices];
9563 double offset = Math::mod(orientation_radian+open_angle_radian/2.0, 2*PI);
9564 vertices[0].set(0,0);
9565 for(int i=0; i<end; i++) {
9567 radius*cos(offset+2.0*((double)i/radius)*PI),
9568 radius*sin(offset+2.0*((double)i/radius)*PI)
9571 vertices[num_vertices-1].set(
9572 radius*cos(offset+2*PI-open_angle_radian),
9573 radius*sin(offset+2*PI-open_angle_radian)
9581 Pacman& shift(double x, double y) {
9583 for(int i=0; i<num_vertices; i++) vertices[i].shift(x,y);
9586 Pacman& centering() {
9587 shift(Display::getHcenter()-center.getX(), Display::getVcenter()-center.getY());
9590 Pacman& draw(Color col) {
9591 Display::polygon(vertices, num_vertices, col);
9598 次に、pacman.cppをPsychlops拡張フォルダ(以下のフォルダの中の適当な場所)にコピーします。この際、MacOSXやWindows Vistaでは管理者パスワードを求められることがあります。
9600 * C:\Library\Frameworks\Psychlops.framework\Headers\Extentions
9601 * /Library/Frameworks/Psychlops.framework/Headers/Extentions
9603 ここでは「Samples」というフォルダをつくり、ここに置いてみます。
9605 次に、呼び出し側コードで今回作った独自拡張を#incluide指令を使って呼び出します。#include指令のオプションには、/Library/Frameworks/Psychlops.framework/Headers/より後の部分のファイルパスを指定します。
9607 #include <Extentions/Samples/pacman.cpp>
9612 #include <psychlops.h>
9613 using namespace Psychlops;
9614 #include <Extentions/Samples/pacman.cpp>
9616 void psychlops_main() {
9618 Canvas display(Canvas::fullscreen);
9619 Pacman pacman(100, PI/2, 0); // radius: 100 px, opening: 90 deg, orientation: 0 deg (right)
9622 while(!Input::get(Keyboard::esc)) {
9624 pacman.draw(Color::red);
9632 #includeでPacman拡張を呼び出すことで、いつでもPacman図形を使用することができるようになりました。
9637 Extentionとして別ファイルに分離したものを配布する際は、必要なすべてのファイルを配布する必要があります。もしExtentionフォルダに入れることが前提のファイルを配布するのであれば、その旨を添付テキスト等に記述し、登録するよう指示しましょう。拡張ではない通常の複数ファイル使用の場合は、プロジェクトへの追加が必要な旨を書きましょう。
9640 !! 上級者向け:複数ファイルから拡張を呼び出す場合
9641 この解説は、ユーザ側のプログラムが単一のファイルになるという初心者向けの状況を想定して書かれています。単一バイナリを生成するのに複数のファイルを使い、それらが同時にひとつの拡張を#includeするような場合は、この方法は使うことができません。このような場合、ヘッダファイルと実装の分離をする必要がありますが、これについては[[Wikipediaの項目|http://ja.wikipedia.org/wiki/%E3%83%98%E3%83%83%E3%83%80%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB]]を参照ください。
9651 簡単な解説については、下記サイトが参考になります。
9652 [[C++入門:クラス|http://wisdom.sakura.ne.jp/programming/cpp/cpp5.html]]
9654 また、psychlops.hの中には真のmain / WinMain関数が入っているため、これを2回以上includeするとコンパイラは本体にも拡張側にもmain関数があるかのようにみなし、衝突してしまいます。これを防ぐために、拡張側ではpsychlops_lib.hをインクルードしてください。
9657 #include <psychlops_lib.h>
9662 <div title="Tips: 画像のフィルタリング処理" modifier="YourName" modified="200803140632" created="200803100447" changecount="27">
9663 <pre>視覚刺激の中には特定の空間周波数でローパス・ハイパスのフィルタをかけた画像を使用することがあります。現在Psychlops本体ではFFT([[高速フーリエ変換|http://ja.wikipedia.org/wiki/%E9%AB%98%E9%80%9F%E3%83%95%E3%83%BC%E3%83%AA%E3%82%A8%E5%A4%89%E6%8F%9B]])機能を持っていませんが、既存のライブラリと併用することで実現できます。ここでは、MATLABでも採用されている[[FFTW|http://www.fftw.org/]]をPsychlops拡張化して利用してみます。
9666 * FFTWはGPLと有料ライセンスの選択制です。GPLを選択される場合、GPLの通称「感染条項」によりこのライブラリを使用したブログラムはすべてGPLとしなければなりません。このプログラムの配布に当たっては、下記の添付ファイルを必ず添付し、パッケージ作成に必要なソースコードをすべて公開しなければなりません。
9669 ! FFTWのPsychlops拡張のインストール
9673 # [[FFTW_demo_osx.zip|http://psychlops.l.u-tokyo.ac.jp/download/BasicSamples/FFTW_demo_osx.zip]]をダウンロードし、解答します。
9674 # 「~FFTW-Installer」をダブルクリックして拡張ををインストールします(要管理者権限)。
9675 #* インストール時にはTerminalが開き、ユーザのパスワードを聞かれますので、入力してください。
9677 次に、拡張を使ったプロジェクトを作成します。
9679 # 「External Frameworks and Libraries」を右クリックし、「追加」→「既存のファイルの追加」をクリックします
9680 ## ファイル選択ウィンドウが開いたら、右上の検索ボックスで以下のパスを''手打ちしてください''(コピー&ペースト不可)
9682 ## 開いたフォルダで以下のファイルを選択してください。リンクするライブラリ一覧に追加されます。
9684 # デモを実行する場合、~FilterDemo.cppの内容をコピー&ペーストします
9686 [img[Image/OSX/Xcode_addLib1.png]]
9687 [img[Image/OSX/Xcode_addLib2.png]]
9691 # [[FFTW_demo_win.zip|http://psychlops.l.u-tokyo.ac.jp/download/BasicSamples/FFTW_demo_win.zip]]をダウンロードし、解凍します。
9692 # 「FFTWのPsychlops拡張インストーラ.bat」を使って拡張ををインストールします(要管理者権限)。
9694 次に、拡張を使ったプロジェクトを作成します。
9696 # いったんテンプレートをビルドします。
9697 # 作成された実行ファイルと同じフォルダに「libfftw3-3.dll」をコピーします
9698 # プロジェクトのオプションを開き、リンクするライブラリに「libfftw3-3_bcc.lib」を加えます(Codeblocks+gccであれば「libfftw3-3.def」を加えます)
9699 # デモを実行する場合、~FilterDemo.cppの内容をコピー&ペーストします
9701 [img[Image/Win/Relo_addLib_1.png]]
9702 [img[Image/Win/Relo_addLib_2.png]]
9707 このサンプルでは、FFTWを用いてローパスフィルタを作成するための関数群が用意されています。配列の大きさが奇数の配列に対しては適用しないでください。
9713 |!Matrix makeLowPassFilter()|makeLowPassFilter(int height, int width, double frequency)|高さheight, 幅width, カットオフ周波数がfrequencyのローパスフィルターとなるMatrixを作成します。|
9714 |~|~|int height: ローパスフィルタの高さ|
9715 |~|~|int width: ローパスフィルタの幅|
9716 |~|~|double frequency: カットオフ周波数|
9718 内部では、カットオフより内側が1、カットオフより外側が0の行列を作成します。
9721 |!Matrix filterImage()|filterImage(Matrix image, Matrix filter)|imageに対してfilterで指定した行列を元にフィルタリングを適用します。|
9722 |~|~|Matrix image: フィルタをかける元画像|
9723 |~|~|Matrix filter: 適用するフィルタ|
9726 # imageにFFTをかけ、周波数表現を作る
9727 # 周波数表現でフィルタを要素ごとに乗算する
9728 # フィルタリング後の周波数表現を逆FFTする
9731 [img[image/tips/sample_FFTW.png]]
9736 2次元のFFTを実行します。ただし、fft2やifft2で使う配列は行および列が偶数である必要があります。奇数の場合は正常に動作しない可能性があります。
9738 fft2を用いてFFTを行った後の行列は中心に直流成分が存在し、周辺部に高周波数成分が存在します。行列の大きさがMatrix(rows, cols)の場合、直流成分はMatrix(rows / 2 + 1, cols / 2 + 1)に存在します。
9740 周波数分解能Δf(Matrixの要素が1つずれた場合にどれだけ周波数が変化するか)は、通常の視覚実験においてΔfは1 / 画像の大きさ(視角)となります。
9742 FFTにおけるΔfはサンプリング周波数 / データ数です。視覚実験においてはサンプリング周波数は視角1度が何ピクセルかに相当します。データ数は画像の高さもしくは幅のピクセル数となります。画像の高さや幅(ピクセル数)はサンプリング周波数に画像の高さや幅(視角)をかけたものに等しくなります。このことから通常の視覚実験においてΔfは1 / 画像の大きさ(視角)となります。
9744 |!void fft2()|fft2(Matrix src, Matrix dstReal, Matrix dstImaginary)|2次元のフーリエ変換を実行します。|
9745 |~|~|Matrix src:フーリエ変換を適用する配列。|
9746 |~|~|Matrix dstReal:フーリエ変換後の配列の実部が代入されます。|
9747 |~|~|Matrix dstImaginary:フーリエ変換後の配列の虚部が代入されます。|
9748 |!void fft2()|fft2(Matrix srcReal, Matrix srcImaginary, Matrix dstReal, Matrix dstImaginary)|2次元のフーリエ変換を実行します。|
9749 |~|~|Matrix srcReal:フーリエ変換を適用する配列の実部。|
9750 |~|~|Matrix srcImaginary:フーリエ変換を適用する配列の虚部。|
9751 |~|~|Matrix dstReal:フーリエ変換後の配列の実部が代入されます。|
9752 |~|~|Matrix dstImaginary:フーリエ変換後の配列の虚部が代入されます。|
9754 実行後の配列は中心部に低周波数領域があります。
9756 |!void ifft2()|ifft2(Matrix srcReal, const Matrix srcImaginary, Matrix dst)|2次元の逆フーリエ変換を実行します。|
9757 |~|~|Matrix srcReal:逆フーリエ変換を適用する配列の実部。|
9758 |~|~|Matrix srcImaginary:逆フーリエ変換後の配列の虚部。|
9759 |~|~|Matrix dst:逆フーリエ変換後の配列の絶対値が代入されます。|
9760 |!void ifft2()|ifft2(Matrix srcReal, Matrix srcImaginary, Matrix dstReal, Matrix dstImaginary)|2次元の逆フーリエ変換を実行します。|
9761 |~|~|Matrix srcReal:逆フーリエ変換を適用する配列の実部。|
9762 |~|~|Matrix srcImaginary:逆フーリエ変換後の配列の虚部。|
9763 |~|~|Matrix dstReal:逆フーリエ変換後の配列の実部が代入されます。|
9764 |~|~|Matrix dstImaginary:逆フーリエ変換後の配列の虚が代入されます。|
9769 |!Matrix normalize()|normalize(Matrix matrix)|行列の各要素の平均値が0、標準偏差が1となるよう正規化します。|
9770 |~|~|Matrix matrix: 正規化対象の行列|
9772 フィルター処理をしたあとの画像は[0, 1]の範囲に収まらず、表示可能な領域からはみ出してしまいます。そこで表示可能な範囲に収める処理が必要となります。フィルター処理をした後の画像の輝度値の分布は正規分布に従うことが知られています。そのため、一度正規化をします。normalizeはその正規化を行うための関数です。
9774 正規化を行った後はコントラストをかけ、平均輝度を足します。このときのコントラストはマイケルソンコントラストではなく、RMSコントラストです。RMSコントラストが1/3を超えると表示可能な領域からはみ出してしまう点が多くなるのでなるべくこの値を最大値としてください。
9776 最後に、それでも表示可能な領域が残る可能性は存在しますので、それを表示可能な領域に納めます。
9779 |!Matrix makeNoise()|makeNoise(int height, int width)|高さheight、幅widthのホワイトノイズ行列を作成します。|
9780 |~|~|int height: 縦の大きさ|
9781 |~|~|int width: 横の大きさ|
9787 以下の関数はfft2やifft2内部で呼び出される関数です。通常ユーザーがこの関数を実行する必要はありません。
9789 |!Matrix fftShift()|fftShift(const Matrix &matrix)|FFTをかけた後の周波数ごとの分布を移動させます。|
9790 |~|~|Matrix matrix: シフトを行う対象を指定します|
9791 FFTWではFFTを行った後の行列は低周波成分が4隅にあり、中心部には高周波成分が存在します。このままではフィルターを作成する際に計算しづらいので、中心部に低周波数成分を、周辺部に高周波数成分が並ぶようにします。そのための関数です。
9793 逆に中心にある低周波数成分を周辺部に、周辺にある高周波数成分を中心にするように並べ替えることもできます。
9795 |!void fftExecute()|fftExecute(const Matrix &srcReal, const Matrix &srcImaginary, Matrix &dstReal, Matrix &dstImaginary, fftw_complex *input, fftw_complex *output, const fftw_plan &plan)|フーリエ変換を実行します。|
9796 |~|~|Matrix srcReal:フーリエ変換を適用する配列の実部。|
9797 |~|~|Matrix srcImaginary:フーリエ変換を適用する配列の虚部。|
9798 |~|~|Matrix dstReal:フーリエ変換後の配列の実部が代入されます。|
9799 |~|~|Matrix dstImaginary:フーリエ変換後の配列の虚部が代入されます。|
9800 |~|~|fftw_complex *input: FFTW作業用の変数|
9801 |~|~|fftw_complex *output: FFTW作業用の変数|
9802 |~|~|fftw_plan plan: FFTW作業用の変数|
9805 <div title="Tips: 自分の機器の描画性能を確かめる" modifier="YourName" modified="200803062209" created="200803062208" changecount="2">
9806 <pre>Psychlopsの実行速度は環境によって異なります。現在の実行環境での実行速度を知りたい場合は、簡易に計測するプログラムを用意しましたので、これを実行して調べることができます。下記ファイルをダウンロードし、コンパイルして実行してみてください。
9808 [[Benchmark.cpp|http://psychlops.l.u-tokyo.ac.jp/download/BasicSamples/Benchmark.cpp]]
9810 2~5分程度のテストを行い、各種命令の「フレームが落ちずに実行できる最大処理能力」を測ります。結果は終了後に画面に表示されるほか、実行ファイルと同じ場所に結果ファイルを出力します(Mac OS Xの場合、プロジェクトフォルダ/build/Release/)。
9812 現在のバージョンでは4つのテストを行います。各テストの項目のうち、
9813 * 上「今回のテストで使われたリフレッシュレートで、1フレームで表示できる最大数」
9814 * 下「100 Hzで正規化した場合に、1フレームで表示できる最大サイズ」(この値が1000の場合、100 Hz時に1000x1000ピクセルの画像が1枚表示可能ということを示します。)
9817 Canvas.pix命令の処理能力を計測します。
9818 GPUの頂点処理能力と、CPUからGPUに命令を出す帯域幅が主な律速要因になります。
9821 Canvas.rect命令の処理能力を計測します。
9822 GPUのピクセル処理能力がが主な律速要因になります。
9825 オフスクリーン(Image.draw)の処理能力を調べます。
9826 メインメモリからグラフィックカードへImageを転送する帯域幅が主な律速要因になります。
9829 Image.quicken()されたオフスクリーン画像の処理能力を計測します。
9830 GPUのピクセル処理能力がが主な律速要因になります。
9834 また、計測結果には10%前後の誤差があります。</pre>
9836 <div title="Tips::CanvasクラスとDisplayクラス" modifier="Psychlops_Admin" modified="200708230528" created="200708230500" changecount="1">
9837 <pre>PsychlopsではデフォルトのCanvasに対して(インスタンス名を無視して)描画を強制的に行うDisplayクラスが用意されています。
9838 現バージョンでのPsychlopsはCanvasを同時に1つしか確保できない仕様となっているので(つまり宣言したCanvasインスタンスは常にデフォルトになる)、Displayクラスを使って描画を行っても、インスタンス名を使って描画を行った場合と同じ結果が得られます。たとえば、以下の2つのプログラムは同じ結果になります。
9840 <インスタンス名を使用したソース>
9841 #include <psychlops.h>
9842 using namespace Psychlops;
9843 void psychlops_main() {
9844 Canvas sampleB(Canvas::fullscreen);
9845 sampleB.clear(Color::blue);
9847 while(!Input::get(Keyboard::spc));
9852 <Displayクラスを使用したソース>
9853 #include <psychlops.h>
9854 using namespace Psychlops;
9855 void psychlops_main() {
9856 Canvas sampleB(Canvas::fullscreen);
9857 Display::clear(Color::blue);
9859 while(!Input::get(Keyboard::spc));
9863 また、psychlops_main外の関数で描画を行うときには、Canvasのポインタを渡す必要のないDisplayクラスを使った方法のほうが便利かもしれません。下の例を比べて見てください。
9866 <インスタンス名を使用したソース>
9867 #include <psychlops.h>
9868 using namespace Psychlops;
9870 void stimulusDraw(Canvas &sample){
9871 sample.clear(Color::blue);
9873 while(!Input::get(Keyboard::spc));
9876 void psychlops_main() {
9877 Canvas sampleB(Canvas::fullscreen);
9878 stimulusDraw(sampleB);
9884 <Displayクラスを使用したソース>
9885 #include <psychlops.h>
9886 using namespace Psychlops;
9887 void stimulusDraw(){
9888 Display::clear(Color::blue);
9890 while(!Input::get(Keyboard::spc));
9892 void psychlops_main() {
9893 Canvas sampleB(Canvas::fullscreen);
9901 <div title="Update from 1.0 to 1.3" modifier="Psychlops_DevelopperG" modified="200912010831" created="200908100506" changecount="18">
9902 <pre>* 2.1.1節のCanvasの宣言をマルチスクリーンに対応したバージョンに書き換え
9905 * [[2.3.1 Canvas::msg()命令単体での文字列描画]]
9906 * [[2.3.2 Lettersクラスを用いた文字列描画]]
9907 * [[3.5 文字列のレンダリングの仕様と関連情報]]
9909 * 3.3.1節にCanvas::toの注意事項とImage::release()についての説明を追加。
9911 [[3.3.3 複数の画像ファイルからアニメーションを作成する]]
9913 [[4. 複雑な描画を行う2(複数オブジェクトのグループ化と回転・拡大縮小)]]
9914 [[8. 透明度を使って刺激描画を高速化する]]
9918 * [[Tips: Shapeクラスの概要]]
9919 * [[Tips: Strokeクラスの概要]]
9920 * [[Tips: Canvasを宣言しないと動作しない命令群]]
9921 * [[Tips: OS環境による関数名の衝突について]]
9923 setGammaTable(std::vector<double> Cr, std::vector<double> Cg, std::vector<double> Cb)のコード例
9932 * [[Canvas::pix(double, double, double)という命令はコンパイルできないのですか?]]
9933 * [[止まった絵を描画するときCanvas.flip()を単独でwhileループの中に書くと、画面がちらつきます。]]
9934 * [[特定の命令に対してコンパイル時にエラーが出る]]
9939 <div title="memo" modifier="Psychlops_Admin" modified="200708280427" created="200708280335" changecount="2">
9944 background: #f0efc0;
9948 background: #f0efc0;
9949 border: 2px dotted #6a6;
9958 padding: 2.0em 0em 0.7em 1em;
9962 padding: 2.0em 0em 0.7em 1em;
10009 border: 1px solid #6a6;
10039 font: 80% arial,sans-serif;
10043 position: absolute;
10046 padding: 0em 1em 0em 1em;
10048 background-color: #eee;
10054 position: absolute;
10061 .headerForeground {
10065 .viewer blockquote {
10068 border-left: 3px solid #666666;
10069 padding-left: 1.0em;
10070 margin-left: 2.5em;
10083 padding-left:0.1em;
10085 margin-bottom:10px;
10088 border-bottom:2px solid #ccc;
10092 <div title="wiki文法" modifier="Psychlops_DevelopperG" modified="200912010832" created="200707220320" changecount="6">
10093 <pre>http://hsj.jp/junknews/archives/tiddlywiki_susume.html
10097 [img[image/canvasrect.png]]
10100 <div title="このマニュアルの見方について(ここをクリックしてください)" modifier="PsychlopsAdmin" created="200709171623" changecount="1">
10101 <pre>このマニュアルはTiddelyWikiによって作成されています。
10102 今、このウィンドウが開いたように、見出しをダブルクリックすると、該当する記事のウィンドウが自動的に開きます。必要なくなった(見終わった)ウィンドウは、見出しの左上のコマンドから"close"をクリックすれば閉じることができます。
10103 Editを押すと内容を改変して、ご自分でコメントをメモすることができますが、改変した部分は元には戻せないのでご注意ください。
10104 また、画面左端にメニューバーから各記事にアクセスしたり記事の内容を検索することもできます。</pre>
10106 <div title="その他" modifier="Kazushi Maruya" modified="200712180151" created="200712131752" changecount="4">
10107 <pre>* [[Psychlopsについて開発者に質問するには?]]
10108 * [[素晴らしいデモプログラムを作成したので、公開したい]]
10109 * [[Psychlopsを既存・自作のC/C++ライブラリと併用することができますか?]]</pre>
10111 <div title="カラーモード" modifier="Psychlops_Admin" created="200709041935" changecount="1">
10112 <pre>|GRAY|グレースケールの輝度値|
10113 |RGB|色の三原色に基づいた赤・緑・青で構成された色|
10114 |RGBA|RGBに透明度が追加された色|
10115 詳しくは[[2.2.5 描画する図形の色を指定する-Colorクラス-]]を参照</pre>
10117 <div title="ガンマ補正" modifier="YourName" modified="200802130705" created="200802130649" changecount="4">
10120 <div title="キーコード表" modifier="Psychlops_DevelopperG" modified="200908100437" created="200708240330" changecount="3">
10123 |アルファベットキー (a~z)|a~zのまま(小文字)|
10125 |キーボード最上段(1~10) |1|2|3|4|5|6|7|8|9|0|
10126 |~|one|two|three|four|five|six|seven|eight|nine|zero|
10128 |特殊キー|Return|Space|Escape|カーソル上|カーソル下|カーソル左|カーソル右|Shift|Control|Alt|
10129 |~| rtn | spc | esc | up | down | left | right | shift | ctrl | alt |
10131 |テンキー|0|1|2|3|4|5|6|7|8|9|
10132 |~|pad0|pad1|pad2|pad3|pad4|pad5|pad6|pad7|pad8|pad9|
10133 |~|ー|>|+|>|*|>|/|>|Enter|>|.|
10134 |~|padminus|>|padpuls|>|padasterisk|>|padslash|>|padenter|>|padperiod|>|
10137 |左ボタン|右ボタン|中ボタン|すべてのボタンのいずれか|
10138 | left | right | middle | any |
10141 <div title="例1: pushedを使ったソース" modifier="Psychlops_Admin" modified="200708272051" created="200708272048" changecount="2">
10144 #include <psychlops.h>
10145 using namespace Psychlops;
10148 Psychlops::Rectangle rect(60,60), rect2(60,60);
10150 void psychlops_main() {
10152 Canvas display(Canvas::fullscreen);
10153 display.clear(Color::gray);
10158 while(!Input::get(Keyboard::spc)){
10161 display.clear(Color::gray);
10163 if(Input::get(Keyboard::c, Keyboard::pushed))code=0; //centering
10164 if(Input::get(Keyboard::left, Keyboard::pushed))code=1;
10165 else if(Input::get(Keyboard::right, Keyboard::pushed))code=2;
10169 case 0: rect.centering(); break;
10170 case 1: rect.shift(-1,0); break;
10171 case 2: rect.shift(1,0); break;
10174 rect2.centering().draw(Psychlops::Color(0.75,0.75,0.75));
10175 rect.draw(Psychlops::Color(1.0,1.0,1.0,0.25));
10182 <div title="例2: pressedを使ったソース" modifier="Psychlops_Admin" modified="200708272052" created="200708272051" changecount="2">
10184 *例2: pressedを使ったソース
10185 #include <psychlops.h>
10186 using namespace Psychlops;
10189 Psychlops::Rectangle rect(60,60), rect2(60,60);
10191 void psychlops_main() {
10193 Canvas display(Canvas::fullscreen);
10194 display.clear(Color::gray);
10199 while(!Input::get(Keyboard::spc)){
10202 display.clear(Color::gray);
10204 if(Input::get(Keyboard::c, Keyboard::pushed))code=0; //centering
10205 if(Input::get(Keyboard::left, Keyboard::pressed))code=1;
10206 else if(Input::get(Keyboard::right, Keyboard::pressed))code=2;
10210 case 0: rect.centering(); break;
10211 case 1: rect.shift(-1,0); break;
10212 case 2: rect.shift(1,0); break;
10215 rect2.centering().draw(Psychlops::Color(0.75,0.75,0.75));
10216 rect.draw(Psychlops::Color(1.0,1.0,1.0,0.25));
10222 <div title="例3: releasedを使ったソース" modifier="Psychlops_Admin" created="200708272053" changecount="1">
10224 *例3: releasedを使ったソース
10225 #include <psychlops.h>
10226 using namespace Psychlops;
10229 Psychlops::Rectangle rect(60,60), rect2(60,60);
10231 void psychlops_main() {
10233 Canvas display(Canvas::fullscreen);
10234 display.clear(Color::gray);
10239 while(!Input::get(Keyboard::spc)){
10242 display.clear(Color::gray);
10244 if(Input::get(Keyboard::c, Keyboard::pushed))code=0; //centering
10246 //state "pressed" and "released" Demo
10247 if(Input::get(Keyboard::left, Keyboard::pressed))code=1;
10248 else if(Input::get(Keyboard::right, Keyboard::pressed))code=2;
10250 if(Input::get(Keyboard::left, Keyboard::released))code=3;
10251 else if(Input::get(Keyboard::right, Keyboard::released))code=3;
10255 case 0: rect.centering(); break;
10256 case 1: rect.shift(-1,0); break;
10257 case 2: rect.shift(1,0); break;
10258 case 3: rect.centering(); break;
10261 rect2.centering().draw(Psychlops::Color(0.75,0.75,0.75));
10262 rect.draw(Psychlops::Color(1.0,1.0,1.0,0.25));
10268 <div title="例4: pushed,pressed,releasedを使ったソース" modifier="Psychlops_Admin" modified="200708272058" created="200708272055" changecount="1">
10271 #include <psychlops.h>
10272 using namespace Psychlops;
10274 double x=0,y=0, dX,dY;
10276 Psychlops::Rectangle rect(60,60), rect2(60,60);
10278 void psychlops_main() {
10280 Canvas display(Canvas::fullscreen);
10281 display.clear(Color::gray);
10283 dX= display.getHcenter();
10284 dY= display.getVcenter();
10290 while(!Input::get(Keyboard::spc)){
10294 //state "pushed" Demo
10295 if(Input::get(Keyboard::c, Keyboard::pushed))code=0; //centering
10297 if(Input::get(Keyboard::a, Keyboard::pushed))code=1;
10298 else if(Input::get(Keyboard::s, Keyboard::pushed))code=2;
10300 //state "pressed" Demo
10301 if(Input::get(Keyboard::z, Keyboard::pressed))code=3;
10302 else if(Input::get(Keyboard::x, Keyboard::pressed))code=4;
10304 //state "pressed" and "released" Demo
10305 if(Input::get(Keyboard::q, Keyboard::pressed)){
10307 if(oldcode==5||oldcode==7)code=7;
10309 else if(Input::get(Keyboard::w, Keyboard::pressed)){
10311 if(oldcode==6||oldcode==8)code=8;
10313 if(Input::get(Keyboard::q, Keyboard::released))code=9;
10314 else if(Input::get(Keyboard::w, Keyboard::released))code=9;
10318 case 0: rect.centering(); break;
10319 case 1: rect.shift(-1,0); break;
10320 case 2: rect.shift(1,0); break;
10321 case 3: rect.shift(-1,0); break;
10322 case 4: rect.shift(1,0); break;
10323 case 5: x=ceil(rect.getHcenter()); y=ceil(rect.getVcenter()); rect.shift(-1,0);break;
10324 case 6: x=ceil(rect.getHcenter()); y=ceil(rect.getVcenter()); rect.shift(1,0);break;
10325 case 7: rect.shift(-1,0); break;
10326 case 8: rect.shift(1,0); break;
10327 case 9: rect.centering(x,y); break;
10329 display.clear(Color::gray);
10332 display.message("KeyState:", dX-100, dY-120, Color::green);
10333 display.var(code, dX,dY-120, Color::green);
10334 display.message("X:", dX-100, dY-100, Color::white);
10335 display.var(ceil(rect.getHcenter()),dX,dY-100, Color::white);
10336 display.message("Y:", dX-100, dY-80, Color::white);
10337 display.var(ceil(rect.getVcenter()),dX,dY-80, Color::white);
10340 rect2.centering(x,y).draw(Psychlops::Color(0.75,0.75,0.75));
10341 rect.draw(Psychlops::Color(1.0,1.0,1.0,0.25));
10343 else rect.draw(1.0);
10348 このプログラム内で使用されているCanvas::message, Canvas::var命令については[[6.1 時間を計測する]]を参照してください。
10351 <div title="例5: Mouse::pressedを使ったソース" modifier="Psychlops_Admin" created="200708272055" changecount="1">
10354 #include <psychlops.h>
10355 using namespace Psychlops;
10359 Psychlops::Rectangle rect(60,60), rect2(60,60);
10361 void psychlops_main() {
10363 Canvas display(Canvas::fullscreen);
10364 display.clear(Color::gray);
10369 while(!Input::get(Keyboard::spc)){
10372 display.clear(Color::gray);
10374 if(Input::get(Keyboard::c, Keyboard::pushed))code=0; //centering
10376 //state "pressed" and "released" Demo
10377 if(Input::get(Mouse::left, Mouse::pressed))code=1;
10378 else if(Input::get(Mouse::right, Mouse::pressed))code=2;
10382 case 0: rect.centering(); break;
10383 case 1: rect.shift(-1,0); break;
10384 case 2: rect.shift(1,0); break;
10387 rect2.centering().draw(Psychlops::Color(0.75,0.75,0.75));
10388 rect.draw(Psychlops::Color(1.0,1.0,1.0,0.25));
10395 <div title="例6: マウスのデモ" modifier="Psychlops_Admin" created="200708272056" changecount="1">
10398 #include <psychlops.h>
10399 using namespace Psychlops;
10401 double dcX,dcY, rectcol;
10402 double rectsize=80;
10403 int display_cursor=1, in_rect=0;
10404 Psychlops::Rectangle rect(rectsize*2,rectsize*2);
10405 Psychlops::Range rngx,rngy;
10407 void psychlops_main() {
10409 Canvas display(Canvas::fullscreen);
10410 display.clear(Color::gray);
10411 AppState::setThreadPriority(AppState::HIGH);
10413 dcX= display.getHcenter();
10414 dcY= display.getVcenter();
10417 //Move mouse cursor to the center
10421 while(!Input::get(Keyboard::spc)){
10422 display.clear(Color::gray);
10424 //set current rect area
10425 rect.getHcenter()-rectsize<rngx<rect.getHcenter()+rectsize;
10426 rect.getVcenter()-rectsize<rngy<rect.getVcenter()+rectsize;
10428 if(display_cursor)Mouse::show();
10429 else Mouse::hide();
10431 if(Input::get(Mouse::right, Mouse::pushed))display_cursor=1-display_cursor;
10433 rect.centering(Mouse::x, Mouse::y);
10437 rect.draw(rectcol);
10439 if(Input::get(Mouse::left, Mouse::pushed)){
10440 if (rngx.includes(Mouse::x) && rngy.includes(Mouse::y))in_rect=1;
10443 if(Input::get(Mouse::left, Mouse::released)) {
10444 if (rngx.includes(Mouse::x) && rngy.includes(Mouse::y))in_rect=0;
10450 <div title="入力系のクラスに関して" modifier="Kazushi Maruya" modified="200712131742" created="200712131740" changecount="2">
10451 <pre>* [[キーボード/マウスの反応を取得したい|Input::get()]]
10452 * [[キーボード/マウスの反応をリセットしたい|Input::refresh()]]
10453 * [[マウスカーソルを表示したい|Mouse::show()]]
10454 * [[マウスカーソルを非表示にしたい|Mouse::hide()]]</pre>
10456 <div title="基本的な命令の使用" modifier="Psychlops_DevelopperG" modified="200908100506" created="200712131619" changecount="5">
10457 <pre>* [[描画画面を取得したい|2.1 描画領域の宣言]]
10458 * [[取得した画面の情報を取得したい|2.1.3 Canvasのメンバ取得と変数の表示]]
10459 * [[画面を特定の色で塗りつぶしたい|2.1.2 Canvasの基本構造と操作]]
10460 * [[画面に点を打ちたい|2.2.1 画面上に点を描画する-Pointクラス1-]]
10461 * [[画面に四角形を書きたい|2.2.3 画面上に四角形を描画する-Rectangleクラス1-]]
10463 * [[画面に円を描きたい|2.2.4 画面上に円を描画する]]
10464 * [[描画色を指定したい|2.2.5 描画する図形の色を指定する-Colorクラス-]]
10465 * [[リアルタイムでは描画できない複雑な図形描画を事前に計算したい|3. 複雑な描画を行う(オフスクリーンへの描画)]]
10466 * [[自然画像を読み込み・表示したい|3.3 画像ファイルの取り扱い]]
10468 * [[図形をXX msecだけ提示したい|2.1.2 Canvasの基本構造と操作]]
10469 * [[時間を正確に計測したい|5.1 時間を計測する]]
10471 * [[キーボードの入力を取得したい|4.1 外部装置(キーボード/マウス)の操作内容を取得する]]
10472 * [[マウス入力を取得したい|4.1 外部装置(キーボード/マウス)の操作内容を取得する]]
10473 * [[配列の中身をファイルに記録したい|4.2 ファイルの入出力]]
10475 * [[特定の命令に対してコンパイル時にエラーが出る]]
10478 <div title="数値計算" modifier="Kazushi Maruya" modified="200712131748" created="200712131747" changecount="2">
10479 <pre>*[[簡単な行列演算がしたい|5.3 行列演算を行う]]
10480 *[[配列の要素をランダムに並べ替えたい|Math::shuffle]]
10481 *[[負の値、浮動小数点型に対して剰余を求めたい|Math::mod]]</pre>
10483 <div title="日本語化" modifier="Psychlops_Admin" modified="200709121644" created="200707220238" tags="systemConfig systemConfigForce" changecount="19">
10484 <pre>// Shadow tiddlers for emergencies
10485 config.shadowTiddlers.SideBarOptions = "<<search>><<closeAll>><<permaview>><<newTiddler>><<saveChanges>><<slider chkSliderOptionsPanel OptionsPanel 設定 'TiddlyWikiの設定を変更する。'>>";
10486 config.shadowTiddlers.OptionsPanel = "これらの設定はご使用のブラウザ内に保存されます。\n\n署名として使用するあなたの名前をWikiWord(eg JoeBloggs)の形式で入力してください。\n\n<<option txtUserName>>\n<<option chkSaveBackups>> バックアップ取得\n<<option chkAutoSave>> 自動保存\n<<option chkGenerateAnRssFeed>> RSSファイル生成\n<<option chkRegExpSearch>> 正規表現による検索\n<<option chkCaseSensitiveSearch>> 英文字大小区別検索\n<<option chkAnimate>> アニメーション\n\n[[詳細な設定|AdvancedOptions]]";
10487 config.shadowTiddlers.AdvancedOptions = "<<option chkOpenInNewWindow>> 新しいウィンドウでリンクを開く\n<<option chkSaveEmptyTemplate>> 空のテンプレートファイル(empty.html)を保存する\n<<option chkToggleLinks>> 既に開いているTiddlerをクリックした時に閉じる\n^^(override with Control or other modifier key)^^";
10488 config.shadowTiddlers.SideBarTabs = "<<tabs txtMainTab '更新順' '更新順に表示する' TabTimeline 'タグ' 'タグ一覧' TabTags '詳細' '詳細' TabMore>>";
10489 config.shadowTiddlers.TabTimeline = "<<timeline>>";
10490 config.shadowTiddlers.TabTags = "<<allTags>>";
10491 config.shadowTiddlers.TabMore = "<<tabs txtMoreTab '全部' 'すべてのTiddler' TabMoreAll '定義なし' '定義されていないTiddler一覧' TabMoreMissing 'リンク切れ' 'リンク切れしている単独のTiddler' TabMoreOrphans>>";
10492 config.shadowTiddlers.TabMoreAll = "<<list all>>";
10493 config.shadowTiddlers.TabMoreMissing = "<<list missing>>";
10494 config.shadowTiddlers.TabMoreOrphans = "<<list orphans>>";
10496 config.messages.customConfigError = "カスタムコンフィグにてエラー発生。 - %0";
10497 config.messages.savedSnapshotError = "保存に失敗しました。";
10498 config.messages.subtitleUnknown = "(不明)";
10499 config.messages.undefinedTiddlerToolTip = "'%0'というTiddlerはまだ存在しません。";
10500 config.messages.externalLinkTooltip = "(外部リンク) %0";
10501 config.messages.noTags = "タグの設定されていないTiddler";
10502 config.messages.notFileUrlError = "変更を保存したい場合、このTiddlyWikiをファイルに保存(ダウンロード)する必要があります。";
10503 config.messages.cantSaveError = "このブラウザでは保存することができません。できればFirefoxを使ってください。";
10504 config.messages.invalidFileError = "元のファイル '%0' は妥当なTiddlyWikiのファイルではありません。";
10505 config.messages.backupSaved = "バックアップファイルを保存しました。";
10506 config.messages.backupFailed = "バックアップファイルの保存に失敗しました。";
10507 config.messages.rssSaved = "RSSファイルを保存しました。";
10508 config.messages.rssFailed = "RSSファイルの保存に失敗しました。";
10509 config.messages.emptySaved = "空のテンプレートファイルを保存しました。";
10510 config.messages.emptyFailed = "空のテンプレートファイルの保存に失敗しました。";
10511 config.messages.mainSaved = "TiddlyWikiファイルを保存しました。";
10512 config.messages.mainFailed = "TiddlyWikiファイルの保存に失敗しました。修正内容は保存されていません。";
10513 config.messages.macroError = "マクロ実行時エラー: '%0'";
10514 config.messages.overwriteWarning = "'%0'というTiddlerはすでに存在します。OKを選択すると上書きします。";
10515 config.messages.unsavedChangesWarning = "警告! 保存されていない変更が存在します。\n\nOKを選択:保存\nCancelを選択:編集内容を破棄";
10516 config.messages.months = ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月","12月"];
10517 config.messages.dates.days = ["日", "月", "火", "水", "木", "金", "土"];
10518 // Tiddler表示時ツールバーなど
10519 config.views.wikified.tag.labelNoTags = "no tags";
10520 config.views.wikified.tag.labelTags = "タグ = ";
10521 config.views.wikified.tag.tooltip = "タグ'%0'が設定されたTiddlerを閲覧する。";
10522 config.views.wikified.tag.openAllText = "タグ'%0'のTiddlerをすべて開く。";
10523 config.views.wikified.tag.openAllTooltip = "このTiddlerをすべて開く。";
10524 config.views.wikified.tag.popupNone = "タグ'%0'はこれ以外のTiddlerに設定されていません。";
10525 config.views.wikified.toolbarEdit = "編集";
10526 config.views.wikified.toolbarPermalink = "permalink";
10527 config.views.wikified.toolbarReferences = "参照一覧";
10528 config.views.wikified.toolbarReferences.popupNone = "参照されていません。";
10529 config.views.wikified.defaultText = "'%0'はまだ存在していません。ダブルクリックで作成できます。";
10530 // Tiddler編集時ツールバーなど
10531 config.views.editor.tagPrompt = "[[tags]]のスタイルでtagsをスペース区切りに入力します。";
10532 config.views.editor.tagChooser.text = "タグ";
10533 config.views.editor.tagChooser.tooltip = "既存のタグから追加するものを選択してください。";
10534 config.views.editor.tagChooser.popupNone = "タグが設定されていません。";
10535 config.views.editor.toolbarDone = "確定";
10536 config.views.editor.toolbarCancel = "編集中止";
10537 config.views.editor.toolbarDelete = "削除";
10539 config.views.editor.defaultText = "'%0'の内容を入力してください。";
10540 // Each has a 'handler' member that is inserted later
10541 config.macros.search.label = "Wiki内検索";
10542 config.macros.search.prompt = "このTiddlyWiki内を検索します。"
10543 config.macros.search.successMsg = "%0 件見つかりました。- %1";
10544 config.macros.search.failureMsg = "%0 に該当するデータはありません。";
10545 config.macros.timeline.dateFormat = "YYYY年MM月DD日";
10546 config.macros.allTags.tooltip = "タグ'%0'のデータをすべて表示します。";
10547 config.macros.allTags.noTags = "タグが設定されていません。";
10548 config.macros.list.all.prompt = "アルファベット順のTiddler一覧";
10549 config.macros.list.missing.prompt = "リンクはされているが定義されていないTiddler一覧";
10550 config.macros.list.orphans.prompt = "他のどこからもリンクされていないTiddler一覧";
10551 config.macros.closeAll.label = "すべて閉じる";
10552 config.macros.closeAll.prompt = "編集されているもの以外の表示されているすべてTiddlerを閉じます。";
10553 config.macros.permaview.label = "permaview";
10554 config.macros.permaview.prompt = "現在表示されているTiddlerの状態を表示するURLです。"
10555 config.macros.saveChanges.label = "保存";
10556 config.macros.saveChanges.prompt = "すべてのTiddlerを保存します。";
10557 config.macros.newTiddler.label = "新規作成";
10558 config.macros.newTiddler.Title = "新規Tiddler";
10559 config.macros.newTiddler.prompt = "新しいTiddlerを作成します。";
10560 config.macros.newJournal.label = "新規日報";
10561 config.macros.newJournal.prompt = "新しいTiddlerを現在の日時をタイトルとして作成します";</pre>
10563 <div title="時間計測系のクラスに関して" modifier="Kazushi Maruya" created="200712131743" changecount="1">
10564 <pre>* [[現在のCPU時間を取得したい|Clock::update()]]
10565 * [[clock型の値をmsec単位で変数に代入したい|Clock::at_msec()]]</pre>
10567 <div title="止まった絵を描画するときCanvas.flip()を単独でwhileループの中に書くと、画面がちらつきます。" modifier="Psychlops_DevelopperG" created="200908100500" changecount="1">
10568 <pre>Psychlopsの仕様として、描画はすべてダブルバッファリングの裏画面に行っています。
10569 このため、描画命令を書いた後に、単にCanvas.flip()命令だけを繰り返すと、2枚に1枚は何も描画されていない画面が表示されます。
10570 止まった絵を表示させたいときにこれを回避するには、2枚の画面の両方に同じ刺激を描画してください。</pre>
10572 <div title="特定の命令に対してコンパイル時にエラーが出る" modifier="Psychlops_DevelopperG" modified="200908100456" created="200908100444" changecount="6">
10574 Psychlopsで使っている命令をコンパイルするときに、ある特定のクラスについて大量のエラーが出ることがあります。
10575 とくに、変数の宣言自体についてエラーが出る場合のの原因の一つとして、ほかのライブラリ、特にOS・コンパイラ標準のライブラリにあるクラスとこの特定のクラスが同じ名前を持っているために、名前が衝突してしまってコンパイルができなくなっていることが考えられます。
10576 このような場合、以下のように スコープ演算子 "Psychlops::" をクラス名の前に付け加えることで問題を解決できます。
10579 !!! (1-1) Macintoshでコンパイル時に、Point型を使った命令すべてに対してエラーが出ます。
10581 Macintoshでは標準ライブラリにPoint型がすでにあるために、名前が衝突してしまい、コンパイルができません。スコープ演算子Psychlops::を使って、衝突を回避してください。
10583 (コンパイル不可)Point A[n]
10584 (コンパイル可) Psychlops::Point A[n]
10587 !!! (1-2) Windowsでコンパイル時に、Rectangle型を使った命令すべてに対してエラーが出ます。
10589 (1-1)と同様にWindowsでは標準ライブラリにRectangle型がすでにあるために、名前が衝突してしまい、コンパイルができません。スコープ演算子Psychlops::を使って、衝突を回避してください。
10591 (コンパイル不可)Rectangle A[n]
10592 (コンパイル可) Psychlops::Rectangle A[n]
10595 !!! (1-3) random()命令に対してエラーが出ます。
10597 スコープ演算子Psychlops::を使って、衝突を回避してください。
10600 (コンパイル可) Psychlops::random()
10603 名前空間の問題についての詳細は、[[OS環境による関数名の衝突について]]をご覧ください。
10606 <div title="特定の種類の刺激描画" modifier="YourName" modified="200802191333" created="200712131629" changecount="3">
10607 <pre>[[ランダムドットを書きたい|3.2 オフスクリーンを用いた描画例-ランダムドットパターン-]]
10608 [[ダイナミックランダムドットを書きたい|Psychlops::random]]
10609 [[一定方向に運動するランダムドットを書きたい|3.2.3 ランダムドットキネマトグラムの描画]]
10610 [[ランダムドットステレオグラムを書きたい|Tips: ランダムドットステレオグラムの描画コード]]
10612 [[垂直・水平方位の正弦縞を書きたい|2.2.6 画面上に縞を描画する-Rectangleクラス2-]]
10613 [[任意の方位の正弦縞を書きたい|2.2.7 画面上に縞を描画する-Pointクラス2-]]
10614 [[運動する縞を書きたい|2.2.8 運動する図形を描画する-Rectangleクラス3-]]
10615 [[Plaid刺激を書きたい|2.2.7 画面上に縞を描画する-Pointクラス2-]]
10616 [[Gabor Patchを書きたい|6.2 Gaborパッチの事前描画]]
10618 [[インタラクティブなデモプログラムを作成したい|7. デモを作成する]]</pre>
10620 <div title="第II部 Psychlopsの応用" modifier="Psychlops_DevelopperG" modified="200909150151" created="200908100512" changecount="2">
10621 <pre>[[1. 既成クラスを用いた簡単な実験プログラムを作成する]]
10622 [[1.1 実験計画のプログラミング]]
10623 [[1.2 Gaborパッチの事前描画]]
10624 [[1.3 各試行部分のプログラミング]]
10628 [[2.1 独立変数Independent]]
10629 [[2.2 デモ環境の表示]] </pre>
10631 <div title="第I部 Psychlopsの基本" modifier="Psychlops_DevelopperG" modified="200908190223" created="200908100511" changecount="3">
10632 <pre>[[1. インストールを行う]]
10636 [[1.2 インストール作業(Mac) ]]
10637 [[1.3 インストール作業(Windows) ]]
10638 [[1.4 更新とアンインストール]]
10639 [[1.5 Psychlopsの基本構造]]
10640 [[1.6 Psychlopsを使ったプログラミング]]
10641 [[1.6.1 プログラミングとは?]]
10642 [[1.6.2 Psychlopsを使ったプログラムの形]]
10649 [[2.1.1 Canvasの宣言]]
10650 [[2.1.2 Canvasの基本構造と操作]]
10651 [[2.1.3 Canvasのメンバ取得と変数の表示]]
10653 [[2.2.1 画面上に点を描画する-Pointクラス1-]]
10654 [[2.2.2 画面上に線を描画する]]
10655 [[2.2.3 画面上に四角形を描画する-Rectangleクラス1-]]
10656 [[2.2.4 画面上に円を描画する]]
10657 [[2.2.5 描画する図形の色を指定する-Colorクラス-]]
10658 [[2.2.6 画面上に縞を描画する-Rectangleクラス2-]]
10659 [[2.2.7 画面上に縞を描画する-Pointクラス2-]]
10660 [[2.2.8 運動する図形を描画する-Rectangleクラス3-]]
10662 [[3. 複雑な描画を行う1(オフスクリーンへの描画)]]
10663 [[3.1 オフスクリーン描画の基本]]
10664 [[3.1.1 オフスクリーン領域の宣言]]
10665 [[3.1.2 オフスクリーン上に点・四角形を描画する]]
10666 [[3.1.3 オフスクリーン上の任意の位置に描画する]]
10668 [[3.2 オフスクリーンを用いた描画例-ランダムドットパターン-]]
10669 [[3.2.1 基本的なランダムドットを描画する]]
10670 [[3.2.2 ドットサイズの異なるランダムドットパターンの描画]]
10671 [[3.2.3 ランダムドットキネマトグラムの描画]]
10672 [[3.3 画像ファイルの取り扱い]]
10673 [[3.3.1 画像ファイルを保存する]]
10674 [[3.3.2 画像ファイルを読み込み表示する]]
10675 [[3.4 Canvasのオフスクリーンへの取り込み]]
10678 [[5.1 外部装置(キーボード/マウス)の操作内容を取得する]]
10681 [[6. 時間制御と各種ツールを使用する]]
10683 [[6.2 各種ツールを使ってみる]]
10684 [[6.2.1 描画の時間精度を確認する-FPSチェッカー]]
10685 [[6.2.2 画面に反映されない描画の進行度を表示する-プログレスバー]]
10691 [[6.3.5 行列の中身をオフスクリーンに描画する]]
10692 [[6.3.6 メッシュグリッドの作成]]</pre>
10694 <div title="素晴らしいデモプログラムを作成したので、公開したい" modifier="Kazushi Maruya" modified="200712180151" created="200712131803" changecount="3">
10695 <pre>[[Visiome Platform|http://platform.visiome.neuroinf.jp]]には様々なデモンストレーションが公開されています。
10696 Psychlopsによるデモプログラムも多数公開されておりますので、是非ご参加ください。
10697 (現在の所このサイトにアップロードするためのアカウント取得には制限があります。詳しくはVisiomeのホームページをご覧下さい。)</pre>
10699 <div title="関数一覧_FunctionList" modifier="Psychlops_DevelopperG" modified="200910080818" created="200707220316" changecount="22">
10701 **[[Psychlops::Canvas]]
10702 **[[Psychlops::Rectangle]]
10703 **[[Psychlops::Point]]
10704 **[[Psychlops::Image]]
10705 **[[Psychlops::Color]]
10707 **[[Psychlops::Input]]
10708 **[[Psychlops::Mouse]]
10709 **[[Psychlops::Clock]]
10711 **[[Psychlops::Matrix]]
10712 **[[Psychlops::Math]]</pre>
10714 <div title="関数逆引きとQ&A" modifier="Kazushi Maruya" modified="200712131749" created="200712131608" changecount="1">
10715 <pre>*[[Canvas型に関して]]
10716 *[[Rectangle型に関して]]
10729 <!--POST-STOREAREA-->
10730 <!--POST-BODY-START-->
10731 <!--POST-BODY-END-->
10732 <script type="text/javascript">
10737 // * This code is designed to be readable but for compactness it only includes brief comments. You can see fuller comments
10738 // in the project Subversion repository at http://svn.tiddlywiki.org/Trunk/core/
10740 // * You should never need to modify this source code directly. TiddlyWiki is carefully designed to allow deep customisation
10741 // without changing the core code. Please consult the development group at http://groups.google.com/group/TiddlyWikiDev
10745 //-- Configuration repository
10748 // Miscellaneous options
10750 numRssItems: 20, // Number of items in the RSS feed
10751 animDuration: 400, // Duration of UI animations in milliseconds
10752 cascadeFast: 20, // Speed for cascade animations (higher == slower)
10753 cascadeSlow: 60, // Speed for EasterEgg cascade animations
10754 cascadeDepth: 5 // Depth of cascade animation
10758 config.adaptors = {};
10764 config.annotations = {};
10766 // Custom fields to be automatically added to new tiddlers
10767 config.defaultCustomFields = {};
10770 config.messages = {
10776 // Options that can be set in the options panel and/or cookies
10778 chkRegExpSearch: false,
10779 chkCaseSensitiveSearch: false,
10781 chkSaveBackups: true,
10782 chkAutoSave: false,
10783 chkGenerateAnRssFeed: false,
10784 chkSaveEmptyTemplate: false,
10785 chkOpenInNewWindow: true,
10786 chkToggleLinks: false,
10787 chkHttpReadOnly: true,
10788 chkForceMinorUpdate: false,
10789 chkConfirmDelete: true,
10790 chkInsertTabs: false,
10791 chkUsePreForStorage: true, // Whether to use <pre> format for storage
10792 chkDisplayStartupTime: false,
10793 txtBackupFolder: "",
10794 txtMainTab: "tabTimeline",
10795 txtMoreTab: "moreTabAll",
10796 txtMaxEditRows: "30",
10797 txtFileSystemCharSet: "UTF-8"
10799 config.optionsDesc = {};
10801 // List of notification functions to be called when certain tiddlers are changed or deleted
10802 config.notifyTiddlers = [
10803 {name: "StyleSheetLayout", notify: refreshStyles},
10804 {name: "StyleSheetColors", notify: refreshStyles},
10805 {name: "StyleSheet", notify: refreshStyles},
10806 {name: "StyleSheetPrint", notify: refreshStyles},
10807 {name: "PageTemplate", notify: refreshPageTemplate},
10808 {name: "SiteTitle", notify: refreshPageTitle},
10809 {name: "SiteSubtitle", notify: refreshPageTitle},
10810 {name: "ColorPalette", notify: refreshColorPalette},
10811 {name: null, notify: refreshDisplay}
10814 // Default tiddler templates
10815 var DEFAULT_VIEW_TEMPLATE = 1;
10816 var DEFAULT_EDIT_TEMPLATE = 2;
10817 config.tiddlerTemplates = {
10822 // More messages (rather a legacy layout that shouldn't really be like this)
10833 config.backstageTasks = ["save","sync","importTask","tweak","plugins"];
10835 // Macros; each has a 'handler' member that is inserted later
10839 search: {sizeTextbox: 15},
10871 refreshDisplay: {},
10872 importTiddlers: {},
10877 // Commands supported by the toolbar macro
10878 config.commands = {
10882 saveTiddler: {hideReadOnly: true},
10884 deleteTiddler: {hideReadOnly: true},
10886 references: {type: "popup"},
10887 jump: {type: "popup"},
10888 syncing: {type: "popup"},
10889 fields: {type: "popup"}
10892 // Browser detection... In a very few places, there's nothing else for it but to know what browser we're using.
10893 config.userAgent = navigator.userAgent.toLowerCase();
10895 isIE: config.userAgent.indexOf("msie") != -1 && config.userAgent.indexOf("opera") == -1,
10896 isGecko: config.userAgent.indexOf("gecko") != -1,
10897 ieVersion: /MSIE (\d.\d)/i.exec(config.userAgent), // config.browser.ieVersion[1], if it exists, will be the IE version string, eg "6.0"
10898 isSafari: config.userAgent.indexOf("applewebkit") != -1,
10899 isBadSafari: !((new RegExp("[\u0150\u0170]","g")).test("\u0150")),
10900 firefoxDate: /gecko\/(\d{8})/i.exec(config.userAgent), // config.browser.firefoxDate[1], if it exists, will be Firefox release date as "YYYYMMDD"
10901 isOpera: config.userAgent.indexOf("opera") != -1,
10902 isLinux: config.userAgent.indexOf("linux") != -1,
10903 isUnix: config.userAgent.indexOf("x11") != -1,
10904 isMac: config.userAgent.indexOf("mac") != -1,
10905 isWindows: config.userAgent.indexOf("win") != -1
10908 // Basic regular expressions
10909 config.textPrimitives = {
10910 upperLetter: "[A-Z\u00c0-\u00de\u0150\u0170]",
10911 lowerLetter: "[a-z0-9_\\-\u00df-\u00ff\u0151\u0171]",
10912 anyLetter: "[A-Za-z0-9_\\-\u00c0-\u00de\u00df-\u00ff\u0150\u0170\u0151\u0171]",
10913 anyLetterStrict: "[A-Za-z0-9\u00c0-\u00de\u00df-\u00ff\u0150\u0170\u0151\u0171]"
10915 if(config.browser.isBadSafari) {
10916 config.textPrimitives = {
10917 upperLetter: "[A-Z\u00c0-\u00de]",
10918 lowerLetter: "[a-z0-9_\\-\u00df-\u00ff]",
10919 anyLetter: "[A-Za-z0-9_\\-\u00c0-\u00de\u00df-\u00ff]",
10920 anyLetterStrict: "[A-Za-z0-9\u00c0-\u00de\u00df-\u00ff]"
10923 config.textPrimitives.sliceSeparator = "::";
10924 config.textPrimitives.urlPattern = "[a-z]{3,8}:[^\\s:'\"][^\\s'\"]*(?:/|\\b)";
10925 config.textPrimitives.unWikiLink = "~";
10926 config.textPrimitives.wikiLink = "(?:(?:" + config.textPrimitives.upperLetter + "+" +
10927 config.textPrimitives.lowerLetter + "+" +
10928 config.textPrimitives.upperLetter +
10929 config.textPrimitives.anyLetter + "*)|(?:" +
10930 config.textPrimitives.upperLetter + "{2,}" +
10931 config.textPrimitives.lowerLetter + "+))";
10933 config.textPrimitives.cssLookahead = "(?:(" + config.textPrimitives.anyLetter + "+)\\(([^\\)\\|\\n]+)(?:\\):))|(?:(" + config.textPrimitives.anyLetter + "+):([^;\\|\\n]+);)";
10934 config.textPrimitives.cssLookaheadRegExp = new RegExp(config.textPrimitives.cssLookahead,"mg");
10936 config.textPrimitives.brackettedLink = "\\[\\[([^\\]]+)\\]\\]";
10937 config.textPrimitives.titledBrackettedLink = "\\[\\[([^\\[\\]\\|]+)\\|([^\\[\\]\\|]+)\\]\\]";
10938 config.textPrimitives.tiddlerForcedLinkRegExp = new RegExp("(?:" + config.textPrimitives.titledBrackettedLink + ")|(?:" +
10939 config.textPrimitives.brackettedLink + ")|(?:" +
10940 config.textPrimitives.urlPattern + ")","mg");
10941 config.textPrimitives.tiddlerAnyLinkRegExp = new RegExp("("+ config.textPrimitives.wikiLink + ")|(?:" +
10942 config.textPrimitives.titledBrackettedLink + ")|(?:" +
10943 config.textPrimitives.brackettedLink + ")|(?:" +
10944 config.textPrimitives.urlPattern + ")","mg");
10948 function() {return config.browser.isIE;},
10949 function() {return true}
10953 downTriangle: ["\u25BC","\u25BE"],
10954 downArrow: ["\u2193","\u2193"],
10955 bentArrowLeft: ["\u2190","\u21A9"],
10956 bentArrowRight: ["\u2192","\u21AA"]
10961 //-- Shadow tiddlers
10964 config.shadowTiddlers = {
10966 MarkupPreHead: "<!--{{{-->\n<link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml'/>\n<!--}}}-->",
10967 MarkupPostHead: "",
10969 MarkupPostBody: "",
10970 TabTimeline: '<<timeline>>',
10971 TabAll: '<<list all>>',
10972 TabTags: '<<allTags excludeLists>>',
10973 TabMoreMissing: '<<list missing>>',
10974 TabMoreOrphans: '<<list orphans>>',
10975 TabMoreShadowed: '<<list shadowed>>',
10976 AdvancedOptions: '<<options>>',
10977 PluginManager: '<<plugins>>',
10978 ImportTiddlers: '<<importTiddlers>>'
10982 //-- Translateable strings
10985 // Strings in "double quotes" should be translated; strings in 'single quotes' should be left alone
10987 merge(config.options,{
10988 txtUserName: "YourName"});
10990 merge(config.tasks,{
10991 save: {text: "save", tooltip: "Save your changes to this TiddlyWiki", action: saveChanges},
10992 sync: {text: "sync", tooltip: "Synchronise changes with other TiddlyWiki files and servers", content: '<<sync>>'},
10993 importTask: {text: "import", tooltip: "Import tiddlers and plugins from other TiddlyWiki files and servers", content: '<<importTiddlers>>'},
10994 tweak: {text: "tweak", tooltip: "Tweak the appearance and behaviour of TiddlyWiki", content: '<<options>>'},
10995 plugins: {text: "plugins", tooltip: "Manage installed plugins", content: '<<plugins>>'}
10998 // Options that can be set in the options panel and/or cookies
10999 merge(config.optionsDesc,{
11000 txtUserName: "Username for signing your edits",
11001 chkRegExpSearch: "Enable regular expressions for searches",
11002 chkCaseSensitiveSearch: "Case-sensitive searching",
11003 chkAnimate: "Enable animations",
11004 chkSaveBackups: "Keep backup file when saving changes",
11005 chkAutoSave: "Automatically save changes",
11006 chkGenerateAnRssFeed: "Generate an RSS feed when saving changes",
11007 chkSaveEmptyTemplate: "Generate an empty template when saving changes",
11008 chkOpenInNewWindow: "Open external links in a new window",
11009 chkToggleLinks: "Clicking on links to open tiddlers causes them to close",
11010 chkHttpReadOnly: "Hide editing features when viewed over HTTP",
11011 chkForceMinorUpdate: "Don't update modifier username and date when editing tiddlers",
11012 chkConfirmDelete: "Require confirmation before deleting tiddlers",
11013 chkInsertTabs: "Use the tab key to insert tab characters instead of moving between fields",
11014 txtBackupFolder: "Name of folder to use for backups",
11015 txtMaxEditRows: "Maximum number of rows in edit boxes",
11016 txtFileSystemCharSet: "Default character set for saving changes (Firefox/Mozilla only)"});
11018 merge(config.messages,{
11019 customConfigError: "Problems were encountered loading plugins. See PluginManager for details",
11020 pluginError: "Error: %0",
11021 pluginDisabled: "Not executed because disabled via 'systemConfigDisable' tag",
11022 pluginForced: "Executed because forced via 'systemConfigForce' tag",
11023 pluginVersionError: "Not executed because this plugin needs a newer version of TiddlyWiki",
11024 nothingSelected: "Nothing is selected. You must select one or more items first",
11025 savedSnapshotError: "It appears that this TiddlyWiki has been incorrectly saved. Please see http://www.tiddlywiki.com/#DownloadSoftware for details",
11026 subtitleUnknown: "(unknown)",
11027 undefinedTiddlerToolTip: "The tiddler '%0' doesn't yet exist",
11028 shadowedTiddlerToolTip: "The tiddler '%0' doesn't yet exist, but has a pre-defined shadow value",
11029 tiddlerLinkTooltip: "%0 - %1, %2",
11030 externalLinkTooltip: "External link to %0",
11031 noTags: "There are no tagged tiddlers",
11032 notFileUrlError: "You need to save this TiddlyWiki to a file before you can save changes",
11033 cantSaveError: "It's not possible to save changes. Possible reasons include:\n- your browser doesn't support saving (Firefox, Internet Explorer, Safari and Opera all work if properly configured)\n- the pathname to your TiddlyWiki file contains illegal characters\n- the TiddlyWiki HTML file has been moved or renamed",
11034 invalidFileError: "The original file '%0' does not appear to be a valid TiddlyWiki",
11035 backupSaved: "Backup saved",
11036 backupFailed: "Failed to save backup file",
11037 rssSaved: "RSS feed saved",
11038 rssFailed: "Failed to save RSS feed file",
11039 emptySaved: "Empty template saved",
11040 emptyFailed: "Failed to save empty template file",
11041 mainSaved: "Main TiddlyWiki file saved",
11042 mainFailed: "Failed to save main TiddlyWiki file. Your changes have not been saved",
11043 macroError: "Error in macro <<\%0>>",
11044 macroErrorDetails: "Error while executing macro <<\%0>>:\n%1",
11045 missingMacro: "No such macro",
11046 overwriteWarning: "A tiddler named '%0' already exists. Choose OK to overwrite it",
11047 unsavedChangesWarning: "WARNING! There are unsaved changes in TiddlyWiki\n\nChoose OK to save\nChoose CANCEL to discard",
11048 confirmExit: "--------------------------------\n\nThere are unsaved changes in TiddlyWiki. If you continue you will lose those changes\n\n--------------------------------",
11049 saveInstructions: "SaveChanges",
11050 unsupportedTWFormat: "Unsupported TiddlyWiki format '%0'",
11051 tiddlerSaveError: "Error when saving tiddler '%0'",
11052 tiddlerLoadError: "Error when loading tiddler '%0'",
11053 wrongSaveFormat: "Cannot save with storage format '%0'. Using standard format for save.",
11054 invalidFieldName: "Invalid field name %0",
11055 fieldCannotBeChanged: "Field '%0' cannot be changed",
11056 loadingMissingTiddler: "Attempting to retrieve the tiddler '%0' from the '%1' server at:\n\n'%2' in the workspace '%3'"});
11058 merge(config.messages.messageClose,{
11060 tooltip: "close this message area"});
11062 config.messages.backstage = {
11063 open: {text: "backstage", tooltip: "Open the backstage area to perform authoring and editing tasks"},
11064 close: {text: "close", tooltip: "Close the backstage area"},
11065 prompt: "backstage: ",
11067 edit: {text: "edit", tooltip: "Edit the tiddler '%0'"}
11071 config.messages.listView = {
11072 tiddlerTooltip: "Click for the full text of this tiddler",
11073 previewUnavailable: "(preview not available)"
11076 config.messages.dates.months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November","December"];
11077 config.messages.dates.days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
11078 config.messages.dates.shortMonths = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
11079 config.messages.dates.shortDays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
11080 // suffixes for dates, eg "1st","2nd","3rd"..."30th","31st"
11081 config.messages.dates.daySuffixes = ["st","nd","rd","th","th","th","th","th","th","th",
11082 "th","th","th","th","th","th","th","th","th","th",
11083 "st","nd","rd","th","th","th","th","th","th","th",
11085 config.messages.dates.am = "am";
11086 config.messages.dates.pm = "pm";
11088 merge(config.messages.tiddlerPopup,{
11091 merge(config.views.wikified.tag,{
11092 labelNoTags: "no tags",
11093 labelTags: "tags: ",
11094 openTag: "Open tag '%0'",
11095 tooltip: "Show tiddlers tagged with '%0'",
11096 openAllText: "Open all",
11097 openAllTooltip: "Open all of these tiddlers",
11098 popupNone: "No other tiddlers tagged with '%0'"});
11100 merge(config.views.wikified,{
11101 defaultText: "The tiddler '%0' doesn't yet exist. Double-click to create it",
11102 defaultModifier: "(missing)",
11103 shadowModifier: "(built-in shadow tiddler)",
11104 dateFormat: "DD MMM YYYY",
11105 createdPrompt: "created"});
11107 merge(config.views.editor,{
11108 tagPrompt: "Type tags separated with spaces, [[use double square brackets]] if necessary, or add existing",
11109 defaultText: "Type the text for '%0'"});
11111 merge(config.views.editor.tagChooser,{
11113 tooltip: "Choose existing tags to add to this tiddler",
11114 popupNone: "There are no tags defined",
11115 tagTooltip: "Add the tag '%0'"});
11117 merge(config.messages,{
11120 {unit: 1024*1024*1024, template: "%0\u00a0GB"},
11121 {unit: 1024*1024, template: "%0\u00a0MB"},
11122 {unit: 1024, template: "%0\u00a0KB"},
11123 {unit: 1, template: "%0\u00a0B"}
11126 merge(config.macros.search,{
11128 prompt: "Search this TiddlyWiki",
11130 successMsg: "%0 tiddlers found matching %1",
11131 failureMsg: "No tiddlers found matching %0"});
11133 merge(config.macros.tagging,{
11134 label: "tagging: ",
11135 labelNotTag: "not tagging",
11136 tooltip: "List of tiddlers tagged with '%0'"});
11138 merge(config.macros.timeline,{
11139 dateFormat: "DD MMM YYYY"});
11141 merge(config.macros.allTags,{
11142 tooltip: "Show tiddlers tagged with '%0'",
11143 noTags: "There are no tagged tiddlers"});
11145 config.macros.list.all.prompt = "All tiddlers in alphabetical order";
11146 config.macros.list.missing.prompt = "Tiddlers that have links to them but are not defined";
11147 config.macros.list.orphans.prompt = "Tiddlers that are not linked to from any other tiddlers";
11148 config.macros.list.shadowed.prompt = "Tiddlers shadowed with default contents";
11149 config.macros.list.touched.prompt = "Tiddlers that have been modified locally";
11151 merge(config.macros.closeAll,{
11152 label: "close all",
11153 prompt: "Close all displayed tiddlers (except any that are being edited)"});
11155 merge(config.macros.permaview,{
11156 label: "permaview",
11157 prompt: "Link to an URL that retrieves all the currently displayed tiddlers"});
11159 merge(config.macros.saveChanges,{
11160 label: "save changes",
11161 prompt: "Save all tiddlers to create a new TiddlyWiki",
11164 merge(config.macros.newTiddler,{
11165 label: "new tiddler",
11166 prompt: "Create a new tiddler",
11167 title: "New Tiddler",
11170 merge(config.macros.newJournal,{
11171 label: "new journal",
11172 prompt: "Create a new tiddler from the current date and time",
11175 merge(config.macros.options,{
11176 wizardTitle: "Tweak advanced options",
11177 step1Title: "These options are saved in cookies in your browser",
11178 step1Html: "<input type='hidden' name='markList'></input><br><input type='checkbox' checked='false' name='chkUnknown'>Show unknown options</input>",
11179 unknownDescription: "//(unknown)//",
11180 listViewTemplate: {
11182 {name: 'Option', field: 'option', title: "Option", type: 'String'},
11183 {name: 'Description', field: 'description', title: "Description", type: 'WikiText'},
11184 {name: 'Name', field: 'name', title: "Name", type: 'String'}
11187 {className: 'lowlight', field: 'lowlight'}
11191 merge(config.macros.plugins,{
11192 wizardTitle: "Manage plugins",
11193 step1Title: "Currently loaded plugins",
11194 step1Html: "<input type='hidden' name='markList'></input>", // DO NOT TRANSLATE
11195 skippedText: "(This plugin has not been executed because it was added since startup)",
11196 noPluginText: "There are no plugins installed",
11197 confirmDeleteText: "Are you sure you want to delete these plugins:\n\n%0",
11198 removeLabel: "remove systemConfig tag",
11199 removePrompt: "Remove systemConfig tag",
11200 deleteLabel: "delete",
11201 deletePrompt: "Delete these tiddlers forever",
11202 listViewTemplate: {
11204 {name: 'Selected', field: 'Selected', rowName: 'title', type: 'Selector'},
11205 {name: 'Tiddler', field: 'tiddler', title: "Tiddler", type: 'Tiddler'},
11206 {name: 'Size', field: 'size', tiddlerLink: 'size', title: "Size", type: 'Size'},
11207 {name: 'Forced', field: 'forced', title: "Forced", tag: 'systemConfigForce', type: 'TagCheckbox'},
11208 {name: 'Disabled', field: 'disabled', title: "Disabled", tag: 'systemConfigDisable', type: 'TagCheckbox'},
11209 {name: 'Executed', field: 'executed', title: "Loaded", type: 'Boolean', trueText: "Yes", falseText: "No"},
11210 {name: 'Startup Time', field: 'startupTime', title: "Startup Time", type: 'String'},
11211 {name: 'Error', field: 'error', title: "Status", type: 'Boolean', trueText: "Error", falseText: "OK"},
11212 {name: 'Log', field: 'log', title: "Log", type: 'StringList'}
11215 {className: 'error', field: 'error'},
11216 {className: 'warning', field: 'warning'}
11220 merge(config.macros.toolbar,{
11222 morePrompt: "Reveal further commands"
11225 merge(config.macros.refreshDisplay,{
11227 prompt: "Redraw the entire TiddlyWiki display"
11230 merge(config.macros.importTiddlers,{
11231 readOnlyWarning: "You cannot import into a read-only TiddlyWiki file. Try opening it from a file:// URL",
11232 wizardTitle: "Import tiddlers from another file or server",
11233 step1Title: "Step 1: Locate the server or TiddlyWiki file",
11234 step1Html: "Specify the type of the server: <select name='selTypes'><option value=''>Choose...</option></select><br>Enter the URL or pathname here: <input type='text' size=50 name='txtPath'><br>...or browse for a file: <input type='file' size=50 name='txtBrowse'><br><hr>...or select a pre-defined feed: <select name='selFeeds'><option value=''>Choose...</option></select>",
11236 openPrompt: "Open the connection to this file or server",
11237 openError: "There were problems fetching the tiddlywiki file",
11238 statusOpenHost: "Opening the host",
11239 statusGetWorkspaceList: "Getting the list of available workspaces",
11240 step2Title: "Step 2: Choose the workspace",
11241 step2Html: "Enter a workspace name: <input type='text' size=50 name='txtWorkspace'><br>...or select a workspace: <select name='selWorkspace'><option value=''>Choose...</option></select>",
11242 cancelLabel: "cancel",
11243 cancelPrompt: "Cancel this import",
11244 statusOpenWorkspace: "Opening the workspace",
11245 statusGetTiddlerList: "Getting the list of available tiddlers",
11246 step3Title: "Step 3: Choose the tiddlers to import",
11247 step3Html: "<input type='hidden' name='markList'></input><br><input type='checkbox' checked='true' name='chkSync'>Keep these tiddlers linked to this server so that you can synchronise subsequent changes</input><br><input type='checkbox' name='chkSave'>Save the details of this server in a 'systemServer' tiddler called:</input> <input type='text' size=25 name='txtSaveTiddler'>",
11248 importLabel: "import",
11249 importPrompt: "Import these tiddlers",
11250 confirmOverwriteText: "Are you sure you want to overwrite these tiddlers:\n\n%0",
11251 step4Title: "Step 4: Importing %0 tiddler(s)",
11252 step4Html: "<input type='hidden' name='markReport'></input>", // DO NOT TRANSLATE
11254 donePrompt: "Close this wizard",
11255 statusDoingImport: "Importing tiddlers",
11256 statusDoneImport: "All tiddlers imported",
11257 systemServerNamePattern: "%2 on %1",
11258 systemServerNamePatternNoWorkspace: "%1",
11259 confirmOverwriteSaveTiddler: "The tiddler '%0' already exists. Click 'OK' to overwrite it with the details of this server, or 'Cancel' to leave it unchanged",
11260 serverSaveTemplate: "|''Type:''|%0|\n|''URL:''|%1|\n|''Workspace:''|%2|\n\nThis tiddler was automatically created to record the details of this server",
11261 serverSaveModifier: "(System)",
11262 listViewTemplate: {
11264 {name: 'Selected', field: 'Selected', rowName: 'title', type: 'Selector'},
11265 {name: 'Tiddler', field: 'tiddler', title: "Tiddler", type: 'Tiddler'},
11266 {name: 'Size', field: 'size', tiddlerLink: 'size', title: "Size", type: 'Size'},
11267 {name: 'Tags', field: 'tags', title: "Tags", type: 'Tags'}
11273 merge(config.macros.sync,{
11274 listViewTemplate: {
11276 {name: 'Selected', field: 'selected', rowName: 'title', type: 'Selector'},
11277 {name: 'Tiddler', field: 'tiddler', title: "Tiddler", type: 'Tiddler'},
11278 {name: 'Server Type', field: 'serverType', title: "Server type", type: 'String'},
11279 {name: 'Server Host', field: 'serverHost', title: "Server host", type: 'String'},
11280 {name: 'Server Workspace', field: 'serverWorkspace', title: "Server workspace", type: 'String'},
11281 {name: 'Status', field: 'status', title: "Synchronisation status", type: 'String'},
11282 {name: 'Server URL', field: 'serverUrl', title: "Server URL", text: "View", type: 'Link'}
11287 {caption: "Sync these tiddlers", name: 'sync'}
11289 wizardTitle: "Synchronize with external servers and files",
11290 step1Title: "Choose the tiddlers you want to synchronize",
11291 step1Html: "<input type='hidden' name='markList'></input>", // DO NOT TRANSLATE
11293 syncPrompt: "Sync these tiddlers",
11294 hasChanged: "Changed while unplugged",
11295 hasNotChanged: "Unchanged while unplugged",
11297 none: {text: "...", color: "none"},
11298 changedServer: {text: "Changed on server", color: '#80ff80'},
11299 changedLocally: {text: "Changed while unplugged", color: '#80ff80'},
11300 changedBoth: {text: "Changed while unplugged and on server", color: '#ff8080'},
11301 notFound: {text: "Not found on server", color: '#ffff80'},
11302 putToServer: {text: "Saved update on server", color: '#ff80ff'},
11303 gotFromServer: {text: "Retrieved update from server", color: '#80ffff'}
11307 merge(config.macros.annotations,{
11310 merge(config.commands.closeTiddler,{
11312 tooltip: "Close this tiddler"});
11314 merge(config.commands.closeOthers,{
11315 text: "close others",
11316 tooltip: "Close all other tiddlers"});
11318 merge(config.commands.editTiddler,{
11320 tooltip: "Edit this tiddler",
11321 readOnlyText: "view",
11322 readOnlyTooltip: "View the source of this tiddler"});
11324 merge(config.commands.saveTiddler,{
11326 tooltip: "Save changes to this tiddler"});
11328 merge(config.commands.cancelTiddler,{
11330 tooltip: "Undo changes to this tiddler",
11331 warning: "Are you sure you want to abandon your changes to '%0'?",
11332 readOnlyText: "done",
11333 readOnlyTooltip: "View this tiddler normally"});
11335 merge(config.commands.deleteTiddler,{
11337 tooltip: "Delete this tiddler",
11338 warning: "Are you sure you want to delete '%0'?"});
11340 merge(config.commands.permalink,{
11342 tooltip: "Permalink for this tiddler"});
11344 merge(config.commands.references,{
11345 text: "references",
11346 tooltip: "Show tiddlers that link to this one",
11347 popupNone: "No references"});
11349 merge(config.commands.jump,{
11351 tooltip: "Jump to another open tiddler"});
11353 merge(config.commands.syncing,{
11355 tooltip: "Control synchronisation of this tiddler with a server or external file",
11356 currentlySyncing: "<div>Currently syncing via <span class='popupHighlight'>'%0'</span> to:</"+"div><div>host: <span class='popupHighlight'>%1</span></"+"div><div>workspace: <span class='popupHighlight'>%2</span></"+"div>", // Note escaping of closing <div> tag
11357 notCurrentlySyncing: "Not currently syncing",
11358 captionUnSync: "Stop synchronising this tiddler",
11359 chooseServer: "Synchronise this tiddler with another server:",
11360 currServerMarker: "\u25cf ",
11361 notCurrServerMarker: " "});
11363 merge(config.commands.fields,{
11365 tooltip: "Show the extended fields of this tiddler",
11366 emptyText: "There are no extended fields for this tiddler",
11367 listViewTemplate: {
11369 {name: 'Field', field: 'field', title: "Field", type: 'String'},
11370 {name: 'Value', field: 'value', title: "Value", type: 'String'}
11377 merge(config.shadowTiddlers,{
11378 DefaultTiddlers: "GettingStarted",
11379 MainMenu: "GettingStarted",
11380 SiteTitle: "My TiddlyWiki",
11381 SiteSubtitle: "a reusable non-linear personal web notebook",
11382 SiteUrl: "http://www.tiddlywiki.com/",
11383 SideBarOptions: '<<search>><<closeAll>><<permaview>><<newTiddler>><<newJournal "DD MMM YYYY">><<saveChanges>><<slider chkSliderOptionsPanel OptionsPanel "options »" "Change TiddlyWiki advanced options">>',
11384 SideBarTabs: '<<tabs txtMainTab "Timeline" "Timeline" TabTimeline "All" "All tiddlers" TabAll "Tags" "All tags" TabTags "More" "More lists" TabMore>>',
11385 TabMore: '<<tabs txtMoreTab "Missing" "Missing tiddlers" TabMoreMissing "Orphans" "Orphaned tiddlers" TabMoreOrphans "Shadowed" "Shadowed tiddlers" TabMoreShadowed>>'});
11387 merge(config.annotations,{
11388 AdvancedOptions: "This shadow tiddler provides access to several advanced options",
11389 ColorPalette: "These values in this shadow tiddler determine the colour scheme of the ~TiddlyWiki user interface",
11390 DefaultTiddlers: "The tiddlers listed in this shadow tiddler will be automatically displayed when ~TiddlyWiki starts up",
11391 EditTemplate: "The HTML template in this shadow tiddler determines how tiddlers look while they are being edited",
11392 GettingStarted: "This shadow tiddler provides basic usage instructions",
11393 ImportTiddlers: "This shadow tiddler provides access to importing tiddlers",
11394 MainMenu: "This shadow tiddler is used as the contents of the main menu in the left-hand column of the screen",
11395 MarkupPreHead: "This tiddler is inserted at the top of the <head> section of the TiddlyWiki HTML file",
11396 MarkupPostHead: "This tiddler is inserted at the bottom of the <head> section of the TiddlyWiki HTML file",
11397 MarkupPreBody: "This tiddler is inserted at the top of the <body> section of the TiddlyWiki HTML file",
11398 MarkupPostBody: "This tiddler is inserted at the end of the <body> section of the TiddlyWiki HTML file immediately before the script block",
11399 OptionsPanel: "This shadow tiddler is used as the contents of the options panel slider in the right-hand sidebar",
11400 PageTemplate: "The HTML template in this shadow tiddler determines the overall ~TiddlyWiki layout",
11401 PluginManager: "This shadow tiddler provides access to the plugin manager",
11402 SideBarOptions: "This shadow tiddler is used as the contents of the option panel in the right-hand sidebar",
11403 SideBarTabs: "This shadow tiddler is used as the contents of the tabs panel in the right-hand sidebar",
11404 SiteSubtitle: "This shadow tiddler is used as the second part of the page title",
11405 SiteTitle: "This shadow tiddler is used as the first part of the page title",
11406 SiteUrl: "This shadow tiddler should be set to the full target URL for publication",
11407 StyleSheetColours: "This shadow tiddler contains CSS definitions related to the color of page elements",
11408 StyleSheet: "This tiddler can contain custom CSS definitions",
11409 StyleSheetLayout: "This shadow tiddler contains CSS definitions related to the layout of page elements",
11410 StyleSheetLocale: "This shadow tiddler contains CSS definitions related to the translation locale",
11411 StyleSheetPrint: "This shadow tiddler contains CSS definitions for printing",
11412 TabAll: "This shadow tiddler contains the contents of the 'All' tab in the right-hand sidebar",
11413 TabMore: "This shadow tiddler contains the contents of the 'More' tab in the right-hand sidebar",
11414 TabMoreMissing: "This shadow tiddler contains the contents of the 'Missing' tab in the right-hand sidebar",
11415 TabMoreOrphans: "This shadow tiddler contains the contents of the 'Orphans' tab in the right-hand sidebar",
11416 TabMoreShadowed: "This shadow tiddler contains the contents of the 'Shadowed' tab in the right-hand sidebar",
11417 TabTags: "This shadow tiddler contains the contents of the 'Tags' tab in the right-hand sidebar",
11418 TabTimeline: "This shadow tiddler contains the contents of the 'Timeline' tab in the right-hand sidebar",
11419 ViewTemplate: "The HTML template in this shadow tiddler determines how tiddlers look"
11426 var params = null; // Command line parameters
11427 var store = null; // TiddlyWiki storage
11428 var story = null; // Main story
11429 var formatter = null; // Default formatters for the wikifier
11430 config.parsers = {}; // Hashmap of alternative parsers for the wikifier
11431 var anim = new Animator(); // Animation engine
11432 var readOnly = false; // Whether we're in readonly mode
11433 var highlightHack = null; // Embarrassing hack department...
11434 var hadConfirmExit = false; // Don't warn more than once
11435 var safeMode = false; // Disable all plugins and cookies
11436 var installedPlugins = []; // Information filled in when plugins are executed
11437 var startingUp = false; // Whether we're in the process of starting up
11438 var pluginInfo,tiddler; // Used to pass information to plugins in loadPlugins()
11440 // Whether to use the JavaSaver applet
11441 var useJavaSaver = config.browser.isSafari || config.browser.isOpera;
11446 var t9,t8,t7,t6,t5,t4,t3,t2,t1,t0 = new Date();
11448 window.onbeforeunload = function(e) {if(window.confirmExit) return confirmExit();};
11449 params = getParameters();
11451 params = params.parseParams("open",null,false);
11452 store = new TiddlyWiki();
11453 invokeParamifier(params,"oninit");
11454 story = new Story("tiddlerDisplay","tiddler");
11455 addEvent(document,"click",Popup.onDocumentClick);
11457 loadOptionsCookie();
11458 for(var s=0; s<config.notifyTiddlers.length; s++)
11459 store.addNotification(config.notifyTiddlers[s].name,config.notifyTiddlers[s].notify);
11461 store.loadFromDiv("storeArea","store",true);
11463 loadShadowTiddlers();
11465 invokeParamifier(params,"onload");
11467 readOnly = (window.location.protocol == "file:") ? false : config.options.chkHttpReadOnly;
11468 var pluginProblem = loadPlugins();
11470 formatter = new Formatter(config.formatters);
11471 invokeParamifier(params,"onconfig");
11477 if(pluginProblem) {
11478 story.displayTiddler(null,"PluginManager");
11479 displayMessage(config.messages.customConfigError);
11481 for(var m in config.macros) {
11482 if(config.macros[m].init)
11483 config.macros[m].init();
11488 if(config.options.chkDisplayStartupTime) {
11489 displayMessage("Load in " + (t2-t1) + " ms");
11490 displayMessage("Loadshadows in " + (t3-t2) + " ms");
11491 displayMessage("Loadplugins in " + (t5-t4) + " ms");
11492 displayMessage("Notify in " + (t7-t6) + " ms");
11493 displayMessage("Restart in " + (t8-t7) + " ms");
11494 displayMessage("Total startup in " + (t9-t0) + " ms");
11496 startingUp = false;
11502 invokeParamifier(params,"onstart");
11503 if(story.isEmpty()) {
11504 var defaultParams = store.getTiddlerText("DefaultTiddlers").parseParams("open",null,false);
11505 invokeParamifier(defaultParams,"onstart");
11507 window.scrollTo(0,0);
11510 function saveTest()
11512 var s = document.getElementById("saveTest");
11513 if(s.hasChildNodes())
11514 alert(config.messages.savedSnapshotError);
11515 s.appendChild(document.createTextNode("savetest"));
11518 function loadShadowTiddlers()
11520 var shadows = new TiddlyWiki();
11521 shadows.loadFromDiv("shadowArea","shadows",true);
11522 shadows.forEachTiddler(function(title,tiddler){config.shadowTiddlers[title] = tiddler.text;});
11526 function loadPlugins()
11530 var tiddlers = store.getTaggedTiddlers("systemConfig");
11534 var nPlugins = tiddlers.length;
11535 installedPlugins = [];
11536 for(var i=0; i<nPlugins; i++) {
11537 var p = getPluginInfo(tiddlers[i]);
11538 installedPlugins[i] = p;
11545 var visit = function(p) {
11549 var reqs = p.Requires;
11551 reqs = reqs.readBracketedList();
11552 for(var i=0; i<reqs.length; i++)
11553 visit(map[reqs[i]]);
11557 for(i=0; i<nPlugins; i++)
11558 visit(installedPlugins[i]);
11559 for(i=0; i<toLoad.length; i++) {
11562 tiddler = p.tiddler;
11563 if(isPluginExecutable(p)) {
11564 if(isPluginEnabled(p)) {
11566 var startTime = new Date();
11569 window.eval(tiddler.text);
11572 p.log.push(config.messages.pluginError.format([exceptionText(ex)]));
11575 pluginInfo.startupTime = String((new Date()) - startTime) + "ms";
11583 return nLoaded != nPlugins;
11586 function getPluginInfo(tiddler)
11588 var p = store.getTiddlerSlices(tiddler.title,["Name","Description","Version","Requires","CoreVersion","Date","Source","Author","License","Browsers"]);
11589 p.tiddler = tiddler;
11590 p.title = tiddler.title;
11595 // Check that a particular plugin is valid for execution
11596 function isPluginExecutable(plugin)
11598 if(plugin.tiddler.isTagged("systemConfigForce"))
11599 return verifyTail(plugin,true,config.messages.pluginForced);
11600 if(plugin["CoreVersion"]) {
11601 var coreVersion = plugin["CoreVersion"].split(".");
11602 var w = parseInt(coreVersion[0]) - version.major;
11603 if(w == 0 && coreVersion[1])
11604 w = parseInt(coreVersion[1]) - version.minor;
11605 if(w == 0 && coreVersion[2])
11606 w = parseInt(coreVersion[2]) - version.revision;
11608 return verifyTail(plugin,false,config.messages.pluginVersionError);
11613 function isPluginEnabled(plugin)
11615 if(plugin.tiddler.isTagged("systemConfigDisable"))
11616 return verifyTail(plugin,false,config.messages.pluginDisabled);
11620 function verifyTail(plugin,result,message)
11622 plugin.log.push(message);
11626 function invokeMacro(place,macro,params,wikifier,tiddler)
11629 var m = config.macros[macro];
11631 m.handler(place,macro,params.readMacroParams(),wikifier,params,tiddler);
11633 createTiddlyError(place,config.messages.macroError.format([macro]),config.messages.macroErrorDetails.format([macro,config.messages.missingMacro]));
11635 createTiddlyError(place,config.messages.macroError.format([macro]),config.messages.macroErrorDetails.format([macro,ex.toString()]));
11643 function getParameters()
11646 if(window.location.hash) {
11647 p = decodeURI(window.location.hash.substr(1));
11648 if(config.browser.firefoxDate != null && config.browser.firefoxDate[1] < "20051111")
11649 p = convertUTF8ToUnicode(p);
11654 function invokeParamifier(params,handler)
11656 if(!params || params.length == undefined || params.length <= 1)
11658 for(var t=1; t<params.length; t++) {
11659 var p = config.paramifiers[params[t].name];
11660 if(p && p[handler] instanceof Function)
11661 p[handler](params[t].value);
11665 config.paramifiers = {};
11667 config.paramifiers.start = {
11668 oninit: function(v) {
11669 safeMode = v.toLowerCase() == "safe";
11673 config.paramifiers.open = {
11674 onstart: function(v) {
11675 story.displayTiddler("bottom",v,null,false,null);
11679 config.paramifiers.story = {
11680 onstart: function(v) {
11681 var list = store.getTiddlerText(v,"").parseParams("open",null,false);
11682 invokeParamifier(list,"onstart");
11686 config.paramifiers.search = {
11687 onstart: function(v) {
11688 story.search(v,false,false);
11692 config.paramifiers.searchRegExp = {
11693 onstart: function(v) {
11694 story.prototype.search(v,false,true);
11698 config.paramifiers.tag = {
11699 onstart: function(v) {
11700 var tagged = store.getTaggedTiddlers(v,"title");
11701 for(var t=0; t<tagged.length; t++)
11702 story.displayTiddler("bottom",tagged[t].title,null,false,null);
11706 config.paramifiers.newTiddler = {
11707 onstart: function(v) {
11709 story.displayTiddler(null,v,DEFAULT_EDIT_TEMPLATE);
11710 story.focusTiddler(v,"text");
11715 config.paramifiers.newJournal = {
11716 onstart: function(v) {
11718 var now = new Date();
11719 var title = now.formatString(v.trim());
11720 story.displayTiddler(null,title,DEFAULT_EDIT_TEMPLATE);
11721 story.focusTiddler(title,"text");
11726 config.paramifiers.readOnly = {
11727 onconfig: function(v) {
11728 var p = v.toLowerCase();
11729 readOnly = p == "yes" ? true : (p == "no" ? false : readOnly);
11734 //-- Formatter helpers
11737 function Formatter(formatters)
11739 this.formatters = [];
11741 for(var n=0; n<formatters.length; n++) {
11742 pattern.push("(" + formatters[n].match + ")");
11743 this.formatters.push(formatters[n]);
11745 this.formatterRegExp = new RegExp(pattern.join("|"),"mg");
11748 config.formatterHelpers = {
11750 createElementAndWikify: function(w)
11752 w.subWikifyTerm(createTiddlyElement(w.output,this.element),this.termRegExp);
11755 inlineCssHelper: function(w)
11758 config.textPrimitives.cssLookaheadRegExp.lastIndex = w.nextMatch;
11759 var lookaheadMatch = config.textPrimitives.cssLookaheadRegExp.exec(w.source);
11760 while(lookaheadMatch && lookaheadMatch.index == w.nextMatch) {
11762 if(lookaheadMatch[1]) {
11763 s = lookaheadMatch[1].unDash();
11764 v = lookaheadMatch[2];
11766 s = lookaheadMatch[3].unDash();
11767 v = lookaheadMatch[4];
11770 s = "backgroundColor";
11771 styles.push({style: s, value: v});
11772 w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
11773 config.textPrimitives.cssLookaheadRegExp.lastIndex = w.nextMatch;
11774 lookaheadMatch = config.textPrimitives.cssLookaheadRegExp.exec(w.source);
11779 applyCssHelper: function(e,styles)
11781 for(var t=0; t< styles.length; t++) {
11783 e.style[styles[t].style] = styles[t].value;
11789 enclosedTextHelper: function(w)
11791 this.lookaheadRegExp.lastIndex = w.matchStart;
11792 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
11793 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
11794 var text = lookaheadMatch[1];
11795 if(config.browser.isIE)
11796 text = text.replace(/\n/g,"\r");
11797 createTiddlyElement(w.output,this.element,null,null,text);
11798 w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
11802 isExternalLink: function(link)
11804 if(store.tiddlerExists(link) || store.isShadowTiddler(link)) {
11807 var urlRegExp = new RegExp(config.textPrimitives.urlPattern,"mg");
11808 if(urlRegExp.exec(link)) {
11811 if (link.indexOf(".")!=-1 || link.indexOf("\\")!=-1 || link.indexOf("/")!=-1){
11820 //-- Standard formatters
11823 config.formatters = [
11826 match: "^\\|(?:[^\\n]*)\\|(?:[fhck]?)$",
11827 lookaheadRegExp: /^\|([^\n]*)\|([fhck]?)$/mg,
11828 rowTermRegExp: /(\|(?:[fhck]?)$\n?)/mg,
11829 cellRegExp: /(?:\|([^\n\|]*)\|)|(\|[fhck]?$\n?)/mg,
11830 cellTermRegExp: /((?:\x20*)\|)/mg,
11831 rowTypes: {"c":"caption", "h":"thead", "":"tbody", "f":"tfoot"},
11833 handler: function(w)
11835 var table = createTiddlyElement(w.output,"table",null,"twtable");
11836 var prevColumns = [];
11837 var currRowType = null;
11840 w.nextMatch = w.matchStart;
11841 this.lookaheadRegExp.lastIndex = w.nextMatch;
11842 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
11843 while(lookaheadMatch && lookaheadMatch.index == w.nextMatch) {
11844 var nextRowType = lookaheadMatch[2];
11845 if(nextRowType == "k") {
11846 table.className = lookaheadMatch[1];
11847 w.nextMatch += lookaheadMatch[0].length+1;
11849 if(nextRowType != currRowType) {
11850 rowContainer = createTiddlyElement(table,this.rowTypes[nextRowType]);
11851 currRowType = nextRowType;
11853 if(currRowType == "c") {
11856 if(rowContainer != table.firstChild)
11857 table.insertBefore(rowContainer,table.firstChild);
11858 rowContainer.setAttribute("align",rowCount == 0?"top":"bottom");
11859 w.subWikifyTerm(rowContainer,this.rowTermRegExp);
11861 this.rowHandler(w,createTiddlyElement(rowContainer,"tr",null,(rowCount&1)?"oddRow":"evenRow"),prevColumns);
11865 this.lookaheadRegExp.lastIndex = w.nextMatch;
11866 lookaheadMatch = this.lookaheadRegExp.exec(w.source);
11869 rowHandler: function(w,e,prevColumns)
11872 var colSpanCount = 1;
11873 var prevCell = null;
11874 this.cellRegExp.lastIndex = w.nextMatch;
11875 var cellMatch = this.cellRegExp.exec(w.source);
11876 while(cellMatch && cellMatch.index == w.nextMatch) {
11877 if(cellMatch[1] == "~") {
11879 var last = prevColumns[col];
11881 last.rowSpanCount++;
11882 last.element.setAttribute("rowspan",last.rowSpanCount);
11883 last.element.setAttribute("rowSpan",last.rowSpanCount); // Needed for IE
11884 last.element.valign = "center";
11886 w.nextMatch = this.cellRegExp.lastIndex-1;
11887 } else if(cellMatch[1] == ">") {
11890 w.nextMatch = this.cellRegExp.lastIndex-1;
11891 } else if(cellMatch[2]) {
11893 if(prevCell && colSpanCount > 1) {
11894 prevCell.setAttribute("colspan",colSpanCount);
11895 prevCell.setAttribute("colSpan",colSpanCount); // Needed for IE
11897 w.nextMatch = this.cellRegExp.lastIndex;
11902 var styles = config.formatterHelpers.inlineCssHelper(w);
11903 var spaceLeft = false;
11904 var chr = w.source.substr(w.nextMatch,1);
11905 while(chr == " ") {
11908 chr = w.source.substr(w.nextMatch,1);
11912 cell = createTiddlyElement(e,"th");
11915 cell = createTiddlyElement(e,"td");
11918 prevColumns[col] = {rowSpanCount:1,element:cell};
11919 if(colSpanCount > 1) {
11920 cell.setAttribute("colspan",colSpanCount);
11921 cell.setAttribute("colSpan",colSpanCount); // Needed for IE
11924 config.formatterHelpers.applyCssHelper(cell,styles);
11925 w.subWikifyTerm(cell,this.cellTermRegExp);
11926 if(w.matchText.substr(w.matchText.length-2,1) == " ") // spaceRight
11927 cell.align = spaceLeft ? "center" : "left";
11929 cell.align = "right";
11933 this.cellRegExp.lastIndex = w.nextMatch;
11934 cellMatch = this.cellRegExp.exec(w.source);
11942 termRegExp: /(\n)/mg,
11943 handler: function(w)
11945 w.subWikifyTerm(createTiddlyElement(w.output,"h" + w.matchLength),this.termRegExp);
11951 match: "^(?:[\\*#;:]+)",
11952 lookaheadRegExp: /^(?:(?:(\*)|(#)|(;)|(:))+)/mg,
11953 termRegExp: /(\n)/mg,
11954 handler: function(w)
11956 var stack = [w.output];
11957 var currLevel = 0, currType = null;
11958 var listLevel, listType, itemType;
11959 w.nextMatch = w.matchStart;
11960 this.lookaheadRegExp.lastIndex = w.nextMatch;
11961 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
11962 while(lookaheadMatch && lookaheadMatch.index == w.nextMatch) {
11963 if(lookaheadMatch[1]) {
11966 } else if(lookaheadMatch[2]) {
11969 } else if(lookaheadMatch[3]) {
11972 } else if(lookaheadMatch[4]) {
11976 listLevel = lookaheadMatch[0].length;
11977 w.nextMatch += lookaheadMatch[0].length;
11979 if(listLevel > currLevel) {
11980 for(t=currLevel; t<listLevel; t++) {
11981 var target = (currLevel == 0) ? stack[stack.length-1] : stack[stack.length-1].lastChild;
11982 stack.push(createTiddlyElement(target,listType));
11984 } else if(listLevel < currLevel) {
11985 for(t=currLevel; t>listLevel; t--)
11987 } else if(listLevel == currLevel && listType != currType) {
11989 stack.push(createTiddlyElement(stack[stack.length-1].lastChild,listType));
11991 currLevel = listLevel;
11992 currType = listType;
11993 var e = createTiddlyElement(stack[stack.length-1],itemType);
11994 w.subWikifyTerm(e,this.termRegExp);
11995 this.lookaheadRegExp.lastIndex = w.nextMatch;
11996 lookaheadMatch = this.lookaheadRegExp.exec(w.source);
12002 name: "quoteByBlock",
12004 termRegExp: /(^<<<(\n|$))/mg,
12005 element: "blockquote",
12006 handler: config.formatterHelpers.createElementAndWikify
12010 name: "quoteByLine",
12012 lookaheadRegExp: /^>+/mg,
12013 termRegExp: /(\n)/mg,
12014 element: "blockquote",
12015 handler: function(w)
12017 var stack = [w.output];
12019 var newLevel = w.matchLength;
12022 if(newLevel > currLevel) {
12023 for(t=currLevel; t<newLevel; t++)
12024 stack.push(createTiddlyElement(stack[stack.length-1],this.element));
12025 } else if(newLevel < currLevel) {
12026 for(t=currLevel; t>newLevel; t--)
12029 currLevel = newLevel;
12030 w.subWikifyTerm(stack[stack.length-1],this.termRegExp);
12031 createTiddlyElement(stack[stack.length-1],"br");
12032 this.lookaheadRegExp.lastIndex = w.nextMatch;
12033 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
12034 var matched = lookaheadMatch && lookaheadMatch.index == w.nextMatch;
12036 newLevel = lookaheadMatch[0].length;
12037 w.nextMatch += lookaheadMatch[0].length;
12045 match: "^----+$\\n?",
12046 handler: function(w)
12048 createTiddlyElement(w.output,"hr");
12053 name: "monospacedByLine",
12054 match: "^\\{\\{\\{\\n",
12055 lookaheadRegExp: /^\{\{\{\n((?:^[^\n]*\n)+?)(^\}\}\}$\n?)/mg,
12057 handler: config.formatterHelpers.enclosedTextHelper
12061 name: "monospacedByLineForCSS",
12062 match: "^/\\*\\{\\{\\{\\*/\\n",
12063 lookaheadRegExp: /\/\*\{\{\{\*\/\n*((?:^[^\n]*\n)+?)(\n*^\/\*\}\}\}\*\/$\n?)/mg,
12065 handler: config.formatterHelpers.enclosedTextHelper
12069 name: "monospacedByLineForPlugin",
12070 match: "^//\\{\\{\\{\\n",
12071 lookaheadRegExp: /^\/\/\{\{\{\n\n*((?:^[^\n]*\n)+?)(\n*^\/\/\}\}\}$\n?)/mg,
12073 handler: config.formatterHelpers.enclosedTextHelper
12077 name: "monospacedByLineForTemplate",
12078 match: "^<!--\\{\\{\\{-->\\n",
12079 lookaheadRegExp: /<!--\{\{\{-->\n*((?:^[^\n]*\n)+?)(\n*^<!--\}\}\}-->$\n?)/mg,
12081 handler: config.formatterHelpers.enclosedTextHelper
12085 name: "wikifyCommentForPlugin",
12086 match: "^/\\*\\*\\*\\n",
12087 termRegExp: /(^\*\*\*\/\n)/mg,
12088 handler: function(w)
12090 w.subWikifyTerm(w.output,this.termRegExp);
12095 name: "wikifyCommentForTemplate",
12096 match: "^<!---\\n",
12097 termRegExp: /(^--->\n)/mg,
12098 handler: function(w)
12100 w.subWikifyTerm(w.output,this.termRegExp);
12107 lookaheadRegExp: /<<([^>\s]+)(?:\s*)((?:[^>]|(?:>(?!>)))*)>>/mg,
12108 handler: function(w)
12110 this.lookaheadRegExp.lastIndex = w.matchStart;
12111 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
12112 if(lookaheadMatch && lookaheadMatch.index == w.matchStart && lookaheadMatch[1]) {
12113 w.nextMatch = this.lookaheadRegExp.lastIndex;
12114 invokeMacro(w.output,lookaheadMatch[1],lookaheadMatch[2],w,w.tiddler);
12120 name: "prettyLink",
12122 lookaheadRegExp: /\[\[(.*?)(?:\|(~)?(.*?))?\]\]/mg,
12123 handler: function(w)
12125 this.lookaheadRegExp.lastIndex = w.matchStart;
12126 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
12127 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
12129 var text = lookaheadMatch[1];
12130 if(lookaheadMatch[3]) {
12131 // Pretty bracketted link
12132 var link = lookaheadMatch[3];
12133 e = (!lookaheadMatch[2] && config.formatterHelpers.isExternalLink(link)) ?
12134 createExternalLink(w.output,link) : createTiddlyLink(w.output,link,false,null,w.isStatic,w.tiddler);
12136 // Simple bracketted link
12137 e = createTiddlyLink(w.output,text,false,null,w.isStatic,w.tiddler);
12139 createTiddlyText(e,text);
12140 w.nextMatch = this.lookaheadRegExp.lastIndex;
12146 name: "unWikiLink",
12147 match: config.textPrimitives.unWikiLink+config.textPrimitives.wikiLink,
12148 handler: function(w)
12150 w.outputText(w.output,w.matchStart+1,w.nextMatch);
12156 match: config.textPrimitives.wikiLink,
12157 handler: function(w)
12159 if(w.matchStart > 0) {
12160 var preRegExp = new RegExp(config.textPrimitives.anyLetterStrict,"mg");
12161 preRegExp.lastIndex = w.matchStart-1;
12162 var preMatch = preRegExp.exec(w.source);
12163 if(preMatch.index == w.matchStart-1) {
12164 w.outputText(w.output,w.matchStart,w.nextMatch);
12168 if(w.autoLinkWikiWords == true || store.isShadowTiddler(w.matchText)) {
12169 var link = createTiddlyLink(w.output,w.matchText,false,null,w.isStatic,w.tiddler);
12170 w.outputText(link,w.matchStart,w.nextMatch);
12172 w.outputText(w.output,w.matchStart,w.nextMatch);
12179 match: config.textPrimitives.urlPattern,
12180 handler: function(w)
12182 w.outputText(createExternalLink(w.output,w.matchText),w.matchStart,w.nextMatch);
12188 match: "\\[[<>]?[Ii][Mm][Gg]\\[",
12189 lookaheadRegExp: /\[([<]?)(>?)[Ii][Mm][Gg]\[(?:([^\|\]]+)\|)?([^\[\]\|]+)\](?:\[([^\]]*)\])?\]/mg,
12190 handler: function(w)
12192 this.lookaheadRegExp.lastIndex = w.matchStart;
12193 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
12194 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
12196 if(lookaheadMatch[5]) {
12197 var link = lookaheadMatch[5];
12198 e = config.formatterHelpers.isExternalLink(link) ? createExternalLink(w.output,link) : createTiddlyLink(w.output,link,false,null,w.isStatic,w.tiddler);
12199 addClass(e,"imageLink");
12201 var img = createTiddlyElement(e,"img");
12202 if(lookaheadMatch[1])
12203 img.align = "left";
12204 else if(lookaheadMatch[2])
12205 img.align = "right";
12206 if(lookaheadMatch[3])
12207 img.title = lookaheadMatch[3];
12208 img.src = lookaheadMatch[4];
12209 w.nextMatch = this.lookaheadRegExp.lastIndex;
12216 match: "<[Hh][Tt][Mm][Ll]>",
12217 lookaheadRegExp: /<[Hh][Tt][Mm][Ll]>((?:.|\n)*?)<\/[Hh][Tt][Mm][Ll]>/mg,
12218 handler: function(w)
12220 this.lookaheadRegExp.lastIndex = w.matchStart;
12221 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
12222 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
12223 createTiddlyElement(w.output,"span").innerHTML = lookaheadMatch[1];
12224 w.nextMatch = this.lookaheadRegExp.lastIndex;
12230 name: "commentByBlock",
12232 lookaheadRegExp: /\/%((?:.|\n)*?)%\//mg,
12233 handler: function(w)
12235 this.lookaheadRegExp.lastIndex = w.matchStart;
12236 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
12237 if(lookaheadMatch && lookaheadMatch.index == w.matchStart)
12238 w.nextMatch = this.lookaheadRegExp.lastIndex;
12243 name: "boldByChar",
12245 termRegExp: /('')/mg,
12247 handler: config.formatterHelpers.createElementAndWikify
12251 name: "italicByChar",
12253 termRegExp: /(\/\/)/mg,
12255 handler: config.formatterHelpers.createElementAndWikify
12259 name: "underlineByChar",
12261 termRegExp: /(__)/mg,
12263 handler: config.formatterHelpers.createElementAndWikify
12267 name: "strikeByChar",
12268 match: "--(?!\\s|$)",
12269 termRegExp: /((?!\s)--|(?=\n\n))/mg,
12271 handler: config.formatterHelpers.createElementAndWikify
12275 name: "superscriptByChar",
12277 termRegExp: /(\^\^)/mg,
12279 handler: config.formatterHelpers.createElementAndWikify
12283 name: "subscriptByChar",
12285 termRegExp: /(~~)/mg,
12287 handler: config.formatterHelpers.createElementAndWikify
12291 name: "monospacedByChar",
12292 match: "\\{\\{\\{",
12293 lookaheadRegExp: /\{\{\{((?:.|\n)*?)\}\}\}/mg,
12294 handler: function(w)
12296 this.lookaheadRegExp.lastIndex = w.matchStart;
12297 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
12298 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
12299 createTiddlyElement(w.output,"code",null,null,lookaheadMatch[1]);
12300 w.nextMatch = this.lookaheadRegExp.lastIndex;
12306 name: "styleByChar",
12308 termRegExp: /(@@)/mg,
12309 handler: function(w)
12311 var e = createTiddlyElement(w.output,"span");
12312 var styles = config.formatterHelpers.inlineCssHelper(w);
12313 if(styles.length == 0)
12314 e.className = "marked";
12316 config.formatterHelpers.applyCssHelper(e,styles);
12317 w.subWikifyTerm(e,this.termRegExp);
12323 match: "\\n|<br ?/?>",
12324 handler: function(w)
12326 createTiddlyElement(w.output,"br");
12332 match: "\\\"{3}|<nowiki>",
12333 lookaheadRegExp: /(?:\"{3}|<nowiki>)((?:.|\n)*?)(?:\"{3}|<\/nowiki>)/mg,
12334 handler: function(w)
12336 this.lookaheadRegExp.lastIndex = w.matchStart;
12337 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
12338 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
12339 createTiddlyElement(w.output,"span",null,null,lookaheadMatch[1]);
12340 w.nextMatch = this.lookaheadRegExp.lastIndex;
12348 handler: function(w)
12350 createTiddlyElement(w.output,"span").innerHTML = "—";
12355 name: "htmlEntitiesEncoding",
12356 match: "(?:(?:&#?[a-zA-Z0-9]{2,8};|.)(?:&#?(?:x0*(?:3[0-6][0-9a-fA-F]|1D[c-fC-F][0-9a-fA-F]|20[d-fD-F][0-9a-fA-F]|FE2[0-9a-fA-F])|0*(?:76[89]|7[7-9][0-9]|8[0-7][0-9]|761[6-9]|76[2-7][0-9]|84[0-3][0-9]|844[0-7]|6505[6-9]|6506[0-9]|6507[0-1]));)+|&#?[a-zA-Z0-9]{2,8};)",
12357 handler: function(w)
12359 createTiddlyElement(w.output,"span").innerHTML = w.matchText;
12364 name: "customClasses",
12366 termRegExp: /(\}\}\})/mg,
12367 lookaheadRegExp: /\{\{[\s]*([\w]+[\s\w]*)[\s]*\{(\n?)/mg,
12368 handler: function(w)
12370 this.lookaheadRegExp.lastIndex = w.matchStart;
12371 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
12372 if(lookaheadMatch) {
12373 var e = createTiddlyElement(w.output,lookaheadMatch[2] == "\n" ? "div" : "span",null,lookaheadMatch[1]);
12374 w.nextMatch = this.lookaheadRegExp.lastIndex;
12375 w.subWikifyTerm(e,this.termRegExp);
12386 function getParser(tiddler,format)
12390 format = tiddler.fields["wikiformat"];
12392 for(var i in config.parsers) {
12393 if(format == config.parsers[i].format)
12394 return config.parsers[i];
12397 for(var i in config.parsers) {
12398 if(tiddler.isTagged(config.parsers[i].formatTag))
12399 return config.parsers[i];
12406 function wikify(source,output,highlightRegExp,tiddler)
12408 if(source && source != "") {
12409 var wikifier = new Wikifier(source,getParser(tiddler),highlightRegExp,tiddler);
12410 wikifier.subWikifyUnterm(output);
12414 function wikifyStatic(source,highlightRegExp,tiddler,format)
12416 var e = createTiddlyElement(document.body,"div");
12417 e.style.display = "none";
12419 if(source && source != "") {
12420 var wikifier = new Wikifier(source,getParser(tiddler,format),highlightRegExp,tiddler);
12421 wikifier.isStatic = true;
12422 wikifier.subWikifyUnterm(e);
12423 html = e.innerHTML;
12429 function wikifyPlain(title,theStore,limit)
12433 if(theStore.tiddlerExists(title) || theStore.isShadowTiddler(title)) {
12434 return wikifyPlainText(theStore.getTiddlerText(title),limit,tiddler);
12440 function wikifyPlainText(text,limit,tiddler)
12443 text = text.substr(0,limit);
12444 var wikifier = new Wikifier(text,formatter,null,tiddler);
12445 return wikifier.wikifyPlain();
12448 function highlightify(source,output,highlightRegExp,tiddler)
12450 if(source && source != "") {
12451 var wikifier = new Wikifier(source,formatter,highlightRegExp,tiddler);
12452 wikifier.outputText(output,0,source.length);
12456 function Wikifier(source,formatter,highlightRegExp,tiddler)
12458 this.source = source;
12459 this.output = null;
12460 this.formatter = formatter;
12461 this.nextMatch = 0;
12462 this.autoLinkWikiWords = tiddler && tiddler.autoLinkWikiWords() == false ? false : true;
12463 this.highlightRegExp = highlightRegExp;
12464 this.highlightMatch = null;
12465 this.isStatic = false;
12466 if(highlightRegExp) {
12467 highlightRegExp.lastIndex = 0;
12468 this.highlightMatch = highlightRegExp.exec(source);
12470 this.tiddler = tiddler;
12473 Wikifier.prototype.wikifyPlain = function()
12475 var e = createTiddlyElement(document.body,"div");
12477 var text = getPlainText(e);
12482 Wikifier.prototype.subWikify = function(output,terminator)
12485 this.subWikifyTerm(output,new RegExp("(" + terminator + ")","mg"));
12487 this.subWikifyUnterm(output);
12490 Wikifier.prototype.subWikifyUnterm = function(output)
12492 // subWikify() can be indirectly recursive, so we need to save the old output pointer
12493 var oldOutput = this.output;
12494 this.output = output;
12495 this.formatter.formatterRegExp.lastIndex = this.nextMatch;
12496 var formatterMatch = this.formatter.formatterRegExp.exec(this.source);
12497 while(formatterMatch) {
12498 // Output any text before the match
12499 if(formatterMatch.index > this.nextMatch)
12500 this.outputText(this.output,this.nextMatch,formatterMatch.index);
12501 // Set the match parameters for the handler
12502 this.matchStart = formatterMatch.index;
12503 this.matchLength = formatterMatch[0].length;
12504 this.matchText = formatterMatch[0];
12505 this.nextMatch = this.formatter.formatterRegExp.lastIndex;
12506 for(var t=1; t<formatterMatch.length; t++) {
12507 if(formatterMatch[t]) {
12508 this.formatter.formatters[t-1].handler(this);
12509 this.formatter.formatterRegExp.lastIndex = this.nextMatch;
12513 formatterMatch = this.formatter.formatterRegExp.exec(this.source);
12515 if(this.nextMatch < this.source.length) {
12516 this.outputText(this.output,this.nextMatch,this.source.length);
12517 this.nextMatch = this.source.length;
12519 this.output = oldOutput;
12522 Wikifier.prototype.subWikifyTerm = function(output,terminatorRegExp)
12524 // subWikify() can be indirectly recursive, so we need to save the old output pointer
12525 var oldOutput = this.output;
12526 this.output = output;
12527 // Get the first matches for the formatter and terminator RegExps
12528 terminatorRegExp.lastIndex = this.nextMatch;
12529 var terminatorMatch = terminatorRegExp.exec(this.source);
12530 this.formatter.formatterRegExp.lastIndex = this.nextMatch;
12531 var formatterMatch = this.formatter.formatterRegExp.exec(terminatorMatch ? this.source.substr(0,terminatorMatch.index) : this.source);
12532 while(terminatorMatch || formatterMatch) {
12533 if(terminatorMatch && (!formatterMatch || terminatorMatch.index <= formatterMatch.index)) {
12534 if(terminatorMatch.index > this.nextMatch)
12535 this.outputText(this.output,this.nextMatch,terminatorMatch.index);
12536 this.matchText = terminatorMatch[1];
12537 this.matchLength = terminatorMatch[1].length;
12538 this.matchStart = terminatorMatch.index;
12539 this.nextMatch = this.matchStart + this.matchLength;
12540 this.output = oldOutput;
12543 if(formatterMatch.index > this.nextMatch)
12544 this.outputText(this.output,this.nextMatch,formatterMatch.index);
12545 this.matchStart = formatterMatch.index;
12546 this.matchLength = formatterMatch[0].length;
12547 this.matchText = formatterMatch[0];
12548 this.nextMatch = this.formatter.formatterRegExp.lastIndex;
12549 for(var t=1; t<formatterMatch.length; t++) {
12550 if(formatterMatch[t]) {
12551 this.formatter.formatters[t-1].handler(this);
12552 this.formatter.formatterRegExp.lastIndex = this.nextMatch;
12556 terminatorRegExp.lastIndex = this.nextMatch;
12557 terminatorMatch = terminatorRegExp.exec(this.source);
12558 formatterMatch = this.formatter.formatterRegExp.exec(terminatorMatch ? this.source.substr(0,terminatorMatch.index) : this.source);
12560 if(this.nextMatch < this.source.length) {
12561 this.outputText(this.output,this.nextMatch,this.source.length);
12562 this.nextMatch = this.source.length;
12564 this.output = oldOutput;
12567 Wikifier.prototype.outputText = function(place,startPos,endPos)
12569 while(this.highlightMatch && (this.highlightRegExp.lastIndex > startPos) && (this.highlightMatch.index < endPos) && (startPos < endPos)) {
12570 if(this.highlightMatch.index > startPos) {
12571 createTiddlyText(place,this.source.substring(startPos,this.highlightMatch.index));
12572 startPos = this.highlightMatch.index;
12574 var highlightEnd = Math.min(this.highlightRegExp.lastIndex,endPos);
12575 var theHighlight = createTiddlyElement(place,"span",null,"highlight",this.source.substring(startPos,highlightEnd));
12576 startPos = highlightEnd;
12577 if(startPos >= this.highlightRegExp.lastIndex)
12578 this.highlightMatch = this.highlightRegExp.exec(this.source);
12580 if(startPos < endPos) {
12581 createTiddlyText(place,this.source.substring(startPos,endPos));
12586 //-- Macro definitions
12589 config.macros.today.handler = function(place,macroName,params)
12591 var now = new Date();
12594 text = now.formatString(params[0].trim());
12596 text = now.toLocaleString();
12597 createTiddlyElement(place,"span",null,null,text);
12600 config.macros.version.handler = function(place)
12602 createTiddlyElement(place,"span",null,null,version.major + "." + version.minor + "." + version.revision + (version.beta ? " (beta " + version.beta + ")" : ""));
12605 config.macros.list.handler = function(place,macroName,params)
12607 var type = params[0] ? params[0] : "all";
12608 var list = document.createElement("ul");
12609 place.appendChild(list);
12610 if(this[type].prompt)
12611 createTiddlyElement(list,"li",null,"listTitle",this[type].prompt);
12613 if(this[type].handler)
12614 results = this[type].handler(params);
12615 for(var t = 0; t < results.length; t++) {
12616 var li = document.createElement("li");
12617 list.appendChild(li);
12618 createTiddlyLink(li,typeof results[t] == "string" ? results[t] : results[t].title,true);
12622 config.macros.list.all.handler = function(params)
12624 return store.reverseLookup("tags","excludeLists",false,"title");
12627 config.macros.list.missing.handler = function(params)
12629 return store.getMissingLinks();
12632 config.macros.list.orphans.handler = function(params)
12634 return store.getOrphans();
12637 config.macros.list.shadowed.handler = function(params)
12639 return store.getShadowed();
12642 config.macros.list.touched.handler = function(params)
12644 return store.getTouched();
12647 config.macros.allTags.handler = function(place,macroName,params)
12649 var tags = store.getTags(params[0]);
12650 var ul = createTiddlyElement(place,"ul");
12651 if(tags.length == 0)
12652 createTiddlyElement(ul,"li",null,"listTitle",this.noTags);
12653 for(var t=0; t<tags.length; t++) {
12654 var title = tags[t][0];
12655 var info = getTiddlyLinkInfo(title);
12656 var li =createTiddlyElement(ul,"li");
12657 var btn = createTiddlyButton(li,title + " (" + tags[t][1] + ")",this.tooltip.format([title]),onClickTag,info.classes);
12658 btn.setAttribute("tag",title);
12659 btn.setAttribute("refresh","link");
12660 btn.setAttribute("tiddlyLink",title);
12664 config.macros.timeline.handler = function(place,macroName,params)
12666 var field = params[0] ? params[0] : "modified";
12667 var tiddlers = store.reverseLookup("tags","excludeLists",false,field);
12669 var last = params[1] ? tiddlers.length-Math.min(tiddlers.length,parseInt(params[1])) : 0;
12670 for(var t=tiddlers.length-1; t>=last; t--) {
12671 var tiddler = tiddlers[t];
12672 var theDay = tiddler[field].convertToLocalYYYYMMDDHHMM().substr(0,8);
12673 if(theDay != lastDay) {
12674 var theDateList = document.createElement("ul");
12675 place.appendChild(theDateList);
12676 createTiddlyElement(theDateList,"li",null,"listTitle",tiddler[field].formatString(this.dateFormat));
12679 var theDateListItem = createTiddlyElement(theDateList,"li",null,"listLink");
12680 theDateListItem.appendChild(createTiddlyLink(place,tiddler.title,true));
12684 config.macros.search.handler = function(place,macroName,params)
12686 var searchTimeout = null;
12687 var btn = createTiddlyButton(place,this.label,this.prompt,this.onClick);
12688 var txt = createTiddlyElement(place,"input",null,"txtOptionInput");
12690 txt.value = params[0];
12691 txt.onkeyup = this.onKeyPress;
12692 txt.onfocus = this.onFocus;
12693 txt.setAttribute("size",this.sizeTextbox);
12694 txt.setAttribute("accessKey",this.accessKey);
12695 txt.setAttribute("autocomplete","off");
12696 txt.setAttribute("lastSearchText","");
12697 if(config.browser.isSafari) {
12698 txt.setAttribute("type","search");
12699 txt.setAttribute("results","5");
12701 txt.setAttribute("type","text");
12705 // Global because there's only ever one outstanding incremental search timer
12706 config.macros.search.timeout = null;
12708 config.macros.search.doSearch = function(txt)
12710 if(txt.value.length > 0) {
12711 story.search(txt.value,config.options.chkCaseSensitiveSearch,config.options.chkRegExpSearch);
12712 txt.setAttribute("lastSearchText",txt.value);
12716 config.macros.search.onClick = function(e)
12718 config.macros.search.doSearch(this.nextSibling);
12722 config.macros.search.onKeyPress = function(e)
12724 if(!e) var e = window.event;
12725 switch(e.keyCode) {
12726 case 13: // Ctrl-Enter
12727 case 10: // Ctrl-Enter on IE PC
12728 config.macros.search.doSearch(this);
12735 if(this.value.length > 2) {
12736 if(this.value != this.getAttribute("lastSearchText")) {
12737 if(config.macros.search.timeout)
12738 clearTimeout(config.macros.search.timeout);
12740 config.macros.search.timeout = setTimeout(function() {config.macros.search.doSearch(txt);},500);
12743 if(config.macros.search.timeout)
12744 clearTimeout(config.macros.search.timeout);
12748 config.macros.search.onFocus = function(e)
12753 config.macros.tiddler.handler = function(place,macroName,params,wikifier,paramString,tiddler)
12755 params = paramString.parseParams("name",null,true,false,true);
12756 var names = params[0]["name"];
12757 var tiddlerName = names[0];
12758 var className = names[1] ? names[1] : null;
12759 var args = params[0]["with"];
12760 var wrapper = createTiddlyElement(place,"span",null,className);
12762 wrapper.setAttribute("refresh","content");
12763 wrapper.setAttribute("tiddler",tiddlerName);
12765 var text = store.getTiddlerText(tiddlerName);
12767 var stack = config.macros.tiddler.tiddlerStack;
12768 if(stack.indexOf(tiddlerName) !== -1)
12770 stack.push(tiddlerName);
12772 var n = args ? Math.min(args.length,9) : 0;
12773 for(var i=0; i<n; i++) {
12774 var placeholderRE = new RegExp("\\$" + (i + 1),"mg");
12775 text = text.replace(placeholderRE,args[i]);
12777 config.macros.tiddler.renderText(wrapper,text,tiddlerName,params);
12784 config.macros.tiddler.renderText = function(place,text,tiddlerName,params)
12786 wikify(text,place,null,store.getTiddler(tiddlerName));
12789 config.macros.tiddler.tiddlerStack = [];
12791 config.macros.tag.handler = function(place,macroName,params)
12793 createTagButton(place,params[0]);
12796 config.macros.tags.handler = function(place,macroName,params,wikifier,paramString,tiddler)
12798 params = paramString.parseParams("anon",null,true,false,false);
12799 var theList = createTiddlyElement(place,"ul");
12800 var title = getParam(params,"anon","");
12801 if(title && store.tiddlerExists(title))
12802 tiddler = store.getTiddler(title);
12803 var sep = getParam(params,"sep"," ");
12804 var lingo = config.views.wikified.tag;
12805 var prompt = tiddler.tags.length == 0 ? lingo.labelNoTags : lingo.labelTags;
12806 createTiddlyElement(theList,"li",null,"listTitle",prompt.format([tiddler.title]));
12807 for(var t=0; t<tiddler.tags.length; t++) {
12808 createTagButton(createTiddlyElement(theList,"li"),tiddler.tags[t],tiddler.title);
12809 if(t<tiddler.tags.length-1)
12810 createTiddlyText(theList,sep);
12814 config.macros.tagging.handler = function(place,macroName,params,wikifier,paramString,tiddler)
12816 params = paramString.parseParams("anon",null,true,false,false);
12817 var theList = createTiddlyElement(place,"ul");
12818 var title = getParam(params,"anon","");
12819 if(title == "" && tiddler instanceof Tiddler)
12820 title = tiddler.title;
12821 var sep = getParam(params,"sep"," ");
12822 theList.setAttribute("title",this.tooltip.format([title]));
12823 var tagged = store.getTaggedTiddlers(title);
12824 var prompt = tagged.length == 0 ? this.labelNotTag : this.label;
12825 createTiddlyElement(theList,"li",null,"listTitle",prompt.format([title,tagged.length]));
12826 for(var t=0; t<tagged.length; t++) {
12827 createTiddlyLink(createTiddlyElement(theList,"li"),tagged[t].title,true);
12828 if(t<tagged.length-1)
12829 createTiddlyText(theList,sep);
12833 config.macros.closeAll.handler = function(place)
12835 createTiddlyButton(place,this.label,this.prompt,this.onClick);
12838 config.macros.closeAll.onClick = function(e)
12840 story.closeAllTiddlers();
12844 config.macros.permaview.handler = function(place)
12846 createTiddlyButton(place,this.label,this.prompt,this.onClick);
12849 config.macros.permaview.onClick = function(e)
12855 config.macros.saveChanges.handler = function(place)
12858 createTiddlyButton(place,this.label,this.prompt,this.onClick,null,null,this.accessKey);
12861 config.macros.saveChanges.onClick = function(e)
12867 config.macros.slider.onClickSlider = function(e)
12869 if(!e) var e = window.event;
12870 var n = this.nextSibling;
12871 var cookie = n.getAttribute("cookie");
12872 var isOpen = n.style.display != "none";
12873 if(config.options.chkAnimate && anim && typeof Slider == "function")
12874 anim.startAnimating(new Slider(n,!isOpen,null,"none"));
12876 n.style.display = isOpen ? "none" : "block";
12877 config.options[cookie] = !isOpen;
12878 saveOptionCookie(cookie);
12882 config.macros.slider.createSlider = function(place,cookie,title,tooltip)
12884 var cookie = cookie ? cookie : "";
12885 var btn = createTiddlyButton(place,title,tooltip,this.onClickSlider);
12886 var panel = createTiddlyElement(null,"div",null,"sliderPanel");
12887 panel.setAttribute("cookie",cookie);
12888 panel.style.display = config.options[cookie] ? "block" : "none";
12889 place.appendChild(panel);
12893 config.macros.slider.handler = function(place,macroName,params)
12895 var panel = this.createSlider(place,params[0],params[2],params[3]);
12896 var text = store.getTiddlerText(params[1]);
12897 panel.setAttribute("refresh","content");
12898 panel.setAttribute("tiddler",params[1]);
12900 wikify(text,panel,null,store.getTiddler(params[1]));
12903 config.macros.option.genericCreate = function(place,type,opt,className,desc)
12905 var typeInfo = config.macros.option.types[type];
12906 var c = document.createElement(typeInfo.elementType);
12907 if(typeInfo.typeValue)
12908 c.setAttribute("type",typeInfo.typeValue);
12909 c[typeInfo.eventName] = typeInfo.onChange;
12910 c.setAttribute("option",opt);
12912 c.className = className;
12914 c.className = typeInfo.className;
12915 if(config.optionsDesc[opt])
12916 c.setAttribute("title",config.optionsDesc[opt]);
12917 place.appendChild(c);
12919 createTiddlyText(place,config.optionsDesc[opt] ? config.optionsDesc[opt] : opt);
12920 c[typeInfo.valueField] = config.options[opt];
12924 config.macros.option.genericOnChange = function(e)
12926 var opt = this.getAttribute("option");
12928 var optType = opt.substr(0,3);
12929 var handler = config.macros.option.types[optType];
12930 if (handler.elementType && handler.valueField)
12931 config.macros.option.propagateOption(opt,handler.valueField,this[handler.valueField],handler.elementType);
12936 config.macros.option.types = {
12938 elementType: "input",
12939 valueField: "value",
12940 eventName: "onkeyup",
12941 className: "txtOptionInput",
12942 create: config.macros.option.genericCreate,
12943 onChange: config.macros.option.genericOnChange
12946 elementType: "input",
12947 valueField: "checked",
12948 eventName: "onclick",
12949 className: "chkOptionInput",
12950 typeValue: "checkbox",
12951 create: config.macros.option.genericCreate,
12952 onChange: config.macros.option.genericOnChange
12956 config.macros.option.propagateOption = function(opt,valueField,value,elementType)
12958 config.options[opt] = value;
12959 saveOptionCookie(opt);
12960 var nodes = document.getElementsByTagName(elementType);
12961 for(var t=0; t<nodes.length; t++) {
12962 var optNode = nodes[t].getAttribute("option");
12964 nodes[t][valueField] = value;
12968 config.macros.option.handler = function(place,macroName,params,wikifier,paramString,tiddler)
12970 params = paramString.parseParams("anon",null,true,false,false);
12971 var opt = (params[1] && params[1].name == "anon") ? params[1].value : getParam(params,"name",null);
12972 var className = (params[2] && params[2].name == "anon") ? params[2].value : getParam(params,"class",null);
12973 var desc = getParam(params,"desc","no");
12974 var type = opt.substr(0,3);
12975 var h = config.macros.option.types[type];
12977 h.create(place,type,opt,className,desc);
12980 config.macros.options.handler = function(place,macroName,params,wikifier,paramString,tiddler)
12982 params = paramString.parseParams("anon",null,true,false,false);
12983 var showUnknown = getParam(params,"showUnknown","no");
12984 var wizard = new Wizard();
12985 wizard.createWizard(place,this.wizardTitle);
12986 wizard.addStep(this.step1Title,this.step1Html);
12987 var markList = wizard.getElement("markList");
12988 var chkUnknown = wizard.getElement("chkUnknown");
12989 chkUnknown.checked = showUnknown == "yes";
12990 chkUnknown.onchange = this.onChangeUnknown;
12991 var listWrapper = document.createElement("div");
12992 markList.parentNode.insertBefore(listWrapper,markList);
12993 wizard.setValue("listWrapper",listWrapper);
12994 this.refreshOptions(listWrapper,showUnknown == "yes");
12997 config.macros.options.refreshOptions = function(listWrapper,showUnknown)
13000 for(var n in config.options) {
13004 opt.lowlight = !config.optionsDesc[n];
13005 opt.description = opt.lowlight ? this.unknownDescription : config.optionsDesc[n];
13006 if(!opt.lowlight || showUnknown)
13009 opts.sort(function(a,b) {return a.name.substr(3) < b.name.substr(3) ? -1 : (a.name.substr(3) == b.name.substr(3) ? 0 : +1);});
13010 var listview = ListView.create(listWrapper,opts,this.listViewTemplate);
13011 for(n=0; n<opts.length; n++) {
13012 var type = opts[n].name.substr(0,3);
13013 var h = config.macros.option.types[type];
13014 if (h && h.create) {
13015 h.create(opts[n].colElements['option'],type,opts[n].name,null,"no");
13020 config.macros.options.onChangeUnknown = function(e)
13022 var wizard = new Wizard(this);
13023 var listWrapper = wizard.getValue("listWrapper");
13024 removeChildren(listWrapper);
13025 config.macros.options.refreshOptions(listWrapper,this.checked);
13029 config.macros.newTiddler.createNewTiddlerButton = function(place,title,params,label,prompt,accessKey,newFocus,isJournal)
13032 for(var t=1; t<params.length; t++) {
13033 if((params[t].name == "anon" && t != 1) || (params[t].name == "tag"))
13034 tags.push(params[t].value);
13036 label = getParam(params,"label",label);
13037 prompt = getParam(params,"prompt",prompt);
13038 accessKey = getParam(params,"accessKey",accessKey);
13039 newFocus = getParam(params,"focus",newFocus);
13040 var customFields = getParam(params,"fields","");
13041 if(!customFields && !store.isShadowTiddler(title))
13042 customFields = String.encodeHashMap(config.defaultCustomFields);
13043 var btn = createTiddlyButton(place,label,prompt,this.onClickNewTiddler,null,null,accessKey);
13044 btn.setAttribute("newTitle",title);
13045 btn.setAttribute("isJournal",isJournal ? "true" : "false");
13046 if(tags.length > 0)
13047 btn.setAttribute("params",tags.join("|"));
13048 btn.setAttribute("newFocus",newFocus);
13049 btn.setAttribute("newTemplate",getParam(params,"template",DEFAULT_EDIT_TEMPLATE));
13050 if(customFields !== "")
13051 btn.setAttribute("customFields",customFields);
13052 var text = getParam(params,"text");
13053 if(text !== undefined)
13054 btn.setAttribute("newText",text);
13058 config.macros.newTiddler.onClickNewTiddler = function()
13060 var title = this.getAttribute("newTitle");
13061 if(this.getAttribute("isJournal") == "true") {
13062 var now = new Date();
13063 title = now.formatString(title.trim());
13065 var params = this.getAttribute("params");
13066 var tags = params ? params.split("|") : [];
13067 var focus = this.getAttribute("newFocus");
13068 var template = this.getAttribute("newTemplate");
13069 var customFields = this.getAttribute("customFields");
13070 story.displayTiddler(null,title,template,false,null,null);
13071 var tiddlerElem = document.getElementById(story.idPrefix + title);
13073 story.addCustomFields(tiddlerElem,customFields);
13074 var text = this.getAttribute("newText");
13075 if(typeof text == "string")
13076 story.getTiddlerField(title,"text").value = text.format([title]);
13077 for(var t=0;t<tags.length;t++)
13078 story.setTiddlerTag(title,tags[t],+1);
13079 story.focusTiddler(title,focus);
13083 config.macros.newTiddler.handler = function(place,macroName,params,wikifier,paramString,tiddler)
13086 params = paramString.parseParams("anon",null,true,false,false);
13087 var title = params[1] && params[1].name == "anon" ? params[1].value : this.title;
13088 title = getParam(params,"title",title);
13089 this.createNewTiddlerButton(place,title,params,this.label,this.prompt,this.accessKey,"title",false);
13093 config.macros.newJournal.handler = function(place,macroName,params,wikifier,paramString,tiddler)
13096 params = paramString.parseParams("anon",null,true,false,false);
13097 var title = params[1] && params[1].name == "anon" ? params[1].value : "";
13098 title = getParam(params,"title",title);
13099 config.macros.newTiddler.createNewTiddlerButton(place,title,params,this.label,this.prompt,this.accessKey,"text",true);
13103 config.macros.sparkline.handler = function(place,macroName,params)
13108 for(var t=0; t<params.length; t++) {
13109 var v = parseInt(params[t]);
13116 if(data.length < 1)
13118 var box = createTiddlyElement(place,"span",null,"sparkline",String.fromCharCode(160));
13119 box.title = data.join(",");
13120 var w = box.offsetWidth;
13121 var h = box.offsetHeight;
13122 box.style.paddingRight = (data.length * 2 - w) + "px";
13123 box.style.position = "relative";
13124 for(var d=0; d<data.length; d++) {
13125 var tick = document.createElement("img");
13127 tick.className = "sparktick";
13128 tick.style.position = "absolute";
13129 tick.src = "data:image/gif,GIF89a%01%00%01%00%91%FF%00%FF%FF%FF%00%00%00%C0%C0%C0%00%00%00!%F9%04%01%00%00%02%00%2C%00%00%00%00%01%00%01%00%40%02%02T%01%00%3B";
13130 tick.style.left = d*2 + "px";
13131 tick.style.width = "2px";
13132 var v = Math.floor(((data[d] - min)/(max-min)) * h);
13133 tick.style.top = (h-v) + "px";
13134 tick.style.height = v + "px";
13135 box.appendChild(tick);
13139 config.macros.tabs.handler = function(place,macroName,params)
13141 var cookie = params[0];
13142 var numTabs = (params.length-1)/3;
13143 var wrapper = createTiddlyElement(null,"div",null,cookie);
13144 var tabset = createTiddlyElement(wrapper,"div",null,"tabset");
13145 tabset.setAttribute("cookie",cookie);
13146 var validTab = false;
13147 for(var t=0; t<numTabs; t++) {
13148 var label = params[t*3+1];
13149 var prompt = params[t*3+2];
13150 var content = params[t*3+3];
13151 var tab = createTiddlyButton(tabset,label,prompt,this.onClickTab,"tab tabUnselected");
13152 tab.setAttribute("tab",label);
13153 tab.setAttribute("content",content);
13154 tab.title = prompt;
13155 if(config.options[cookie] == label)
13159 config.options[cookie] = params[1];
13160 place.appendChild(wrapper);
13161 this.switchTab(tabset,config.options[cookie]);
13164 config.macros.tabs.onClickTab = function(e)
13166 config.macros.tabs.switchTab(this.parentNode,this.getAttribute("tab"));
13170 config.macros.tabs.switchTab = function(tabset,tab)
13172 var cookie = tabset.getAttribute("cookie");
13174 var nodes = tabset.childNodes;
13175 for(var t=0; t<nodes.length; t++) {
13176 if(nodes[t].getAttribute && nodes[t].getAttribute("tab") == tab) {
13178 theTab.className = "tab tabSelected";
13180 nodes[t].className = "tab tabUnselected";
13184 if(tabset.nextSibling && tabset.nextSibling.className == "tabContents")
13185 removeNode(tabset.nextSibling);
13186 var tabContent = createTiddlyElement(null,"div",null,"tabContents");
13187 tabset.parentNode.insertBefore(tabContent,tabset.nextSibling);
13188 var contentTitle = theTab.getAttribute("content");
13189 wikify(store.getTiddlerText(contentTitle),tabContent,null,store.getTiddler(contentTitle));
13191 config.options[cookie] = tab;
13192 saveOptionCookie(cookie);
13197 // <<gradient [[tiddler name]] vert|horiz rgb rgb rgb rgb... >>
13198 config.macros.gradient.handler = function(place,macroName,params,wikifier)
13200 var terminator = ">>";
13203 panel = createTiddlyElement(place,"div",null,"gradient");
13206 panel.style.position = "relative";
13207 panel.style.overflow = "hidden";
13208 panel.style.zIndex = "0";
13211 var styles = config.formatterHelpers.inlineCssHelper(wikifier);
13212 config.formatterHelpers.applyCssHelper(panel,styles);
13215 for(t=1; t<params.length; t++) {
13216 var c = new RGB(params[t]);
13220 drawGradient(panel,params[0] != "vert",colours);
13222 wikifier.subWikify(panel,terminator);
13224 panel.style.height = "100%";
13225 panel.style.width = "100%";
13229 config.macros.message.handler = function(place,macroName,params)
13233 var p = params[0].split(".");
13234 for(var t=0; t<p.length; t++) {
13240 createTiddlyText(place,m.toString().format(params.splice(1)));
13244 config.macros.view.handler = function(place,macroName,params,wikifier,paramString,tiddler)
13246 if((tiddler instanceof Tiddler) && params[0]) {
13247 var value = store.getValue(tiddler,params[0]);
13248 if(value != undefined) {
13249 switch(params[1]) {
13251 highlightify(value,place,highlightHack,tiddler);
13254 createTiddlyLink(place,value,true);
13257 wikify(value,place,highlightHack,tiddler);
13260 value = Date.convertFromYYYYMMDDHHMM(value);
13261 createTiddlyText(place,value.formatString(params[2] ? params[2] : config.views.wikified.dateFormat));
13268 config.macros.edit.handler = function(place,macroName,params,wikifier,paramString,tiddler)
13270 var field = params[0];
13271 var rows = params[1];
13272 if((tiddler instanceof Tiddler) && field) {
13273 story.setDirty(tiddler.title,true);
13274 if(field != "text" && !rows) {
13275 var e = createTiddlyElement(null,"input");
13276 if(tiddler.isReadOnly())
13277 e.setAttribute("readOnly","readOnly");
13278 e.setAttribute("edit",field);
13279 e.setAttribute("type","text");
13280 var v = store.getValue(tiddler,field);
13284 e.setAttribute("size","40");
13285 e.setAttribute("autocomplete","off");
13286 place.appendChild(e);
13288 var wrapper1 = createTiddlyElement(null,"fieldset",null,"fieldsetFix");
13289 var wrapper2 = createTiddlyElement(wrapper1,"div");
13290 var e = createTiddlyElement(wrapper2,"textarea");
13291 if(tiddler.isReadOnly())
13292 e.setAttribute("readOnly","readOnly");
13293 var v = store.getValue(tiddler,field);
13297 var rows = rows ? rows : 10;
13298 var lines = v.match(/\n/mg);
13299 var maxLines = Math.max(parseInt(config.options.txtMaxEditRows),5);
13300 if(lines != null && lines.length > rows)
13301 rows = lines.length + 5;
13302 rows = Math.min(rows,maxLines);
13303 e.setAttribute("rows",rows);
13304 e.setAttribute("edit",field);
13305 place.appendChild(wrapper1);
13310 config.macros.tagChooser.onClick = function(e)
13312 if(!e) var e = window.event;
13313 var lingo = config.views.editor.tagChooser;
13314 var popup = Popup.create(this);
13315 var tags = store.getTags();
13316 if(tags.length == 0)
13317 createTiddlyText(createTiddlyElement(popup,"li"),lingo.popupNone);
13318 for(var t=0; t<tags.length; t++) {
13319 var theTag = createTiddlyButton(createTiddlyElement(popup,"li"),tags[t][0],lingo.tagTooltip.format([tags[t][0]]),config.macros.tagChooser.onTagClick);
13320 theTag.setAttribute("tag",tags[t][0]);
13321 theTag.setAttribute("tiddler",this.getAttribute("tiddler"));
13324 e.cancelBubble = true;
13325 if(e.stopPropagation) e.stopPropagation();
13329 config.macros.tagChooser.onTagClick = function(e)
13331 if(!e) var e = window.event;
13332 var tag = this.getAttribute("tag");
13333 var title = this.getAttribute("tiddler");
13335 story.setTiddlerTag(title,tag,0);
13339 config.macros.tagChooser.handler = function(place,macroName,params,wikifier,paramString,tiddler)
13341 if(tiddler instanceof Tiddler) {
13342 var title = tiddler.title;
13343 var lingo = config.views.editor.tagChooser;
13344 var btn = createTiddlyButton(place,lingo.text,lingo.tooltip,this.onClick);
13345 btn.setAttribute("tiddler",title);
13349 // Create a toolbar command button
13350 config.macros.toolbar.createCommand = function(place,commandName,tiddler,theClass)
13352 if(typeof commandName != "string") {
13354 for(var t in config.commands) {
13355 if(config.commands[t] == commandName)
13360 if((tiddler instanceof Tiddler) && (typeof commandName == "string")) {
13361 var command = config.commands[commandName];
13362 if(command.isEnabled ? command.isEnabled(tiddler) : this.isCommandEnabled(command,tiddler)) {
13363 var text = command.getText ? command.getText(tiddler) : this.getCommandText(command,tiddler);
13364 var tooltip = command.getTooltip ? command.getTooltip(tiddler) : this.getCommandTooltip(command,tiddler);
13366 switch(command.type) {
13368 cmd = this.onClickPopup;
13372 cmd = this.onClickCommand;
13375 var btn = createTiddlyButton(null,text,tooltip,cmd);
13376 btn.setAttribute("commandName",commandName);
13377 btn.setAttribute("tiddler",tiddler.title);
13379 addClass(btn,theClass);
13380 place.appendChild(btn);
13385 config.macros.toolbar.isCommandEnabled = function(command,tiddler)
13387 var title = tiddler.title;
13388 var ro = tiddler.isReadOnly();
13389 var shadow = store.isShadowTiddler(title) && !store.tiddlerExists(title);
13390 return (!ro || (ro && !command.hideReadOnly)) && !(shadow && command.hideShadow);
13393 config.macros.toolbar.getCommandText = function(command,tiddler)
13395 return tiddler.isReadOnly() && command.readOnlyText ? command.readOnlyText : command.text;
13398 config.macros.toolbar.getCommandTooltip = function(command,tiddler)
13400 return tiddler.isReadOnly() && command.readOnlyTooltip ? command.readOnlyTooltip : command.tooltip;
13403 config.macros.toolbar.onClickCommand = function(e)
13405 if(!e) var e = window.event;
13406 e.cancelBubble = true;
13407 if (e.stopPropagation) e.stopPropagation();
13408 var command = config.commands[this.getAttribute("commandName")];
13409 return command.handler(e,this,this.getAttribute("tiddler"));
13412 config.macros.toolbar.onClickPopup = function(e)
13414 if(!e) var e = window.event;
13415 e.cancelBubble = true;
13416 if (e.stopPropagation) e.stopPropagation();
13417 var popup = Popup.create(this);
13418 var command = config.commands[this.getAttribute("commandName")];
13419 var title = this.getAttribute("tiddler");
13420 var tiddler = store.fetchTiddler(title);
13421 popup.setAttribute("tiddler",title);
13422 command.handlePopup(popup,title);
13427 // Invoke the first command encountered from a given place that is tagged with a specified class
13428 config.macros.toolbar.invokeCommand = function(place,theClass,event)
13430 var children = place.getElementsByTagName("a");
13431 for(var t=0; t<children.length; t++) {
13432 var c = children[t];
13433 if(hasClass(c,theClass) && c.getAttribute && c.getAttribute("commandName")) {
13434 if(c.onclick instanceof Function)
13435 c.onclick.call(c,event);
13441 config.macros.toolbar.onClickMore = function(e)
13443 var e = this.nextSibling;
13444 e.style.display = "inline";
13449 config.macros.toolbar.handler = function(place,macroName,params,wikifier,paramString,tiddler)
13451 for(var t=0; t<params.length; t++) {
13455 var btn = createTiddlyButton(place,this.moreLabel,this.morePrompt,config.macros.toolbar.onClickMore);
13456 addClass(btn,"moreCommand");
13457 var e = createTiddlyElement(place,"span",null,"moreCommand");
13458 e.style.display = "none";
13463 switch(c.substr(0,1)) {
13465 theClass = "defaultCommand";
13469 theClass = "cancelCommand";
13473 if(c in config.commands)
13474 this.createCommand(place,c,tiddler,theClass);
13480 config.macros.refreshDisplay.handler = function(place)
13482 createTiddlyButton(place,this.label,this.prompt,this.onClick);
13485 config.macros.refreshDisplay.onClick = function(e)
13491 config.macros.annotations.handler = function(place,macroName,params,wikifier,paramString,tiddler)
13493 var title = tiddler ? tiddler.title : null;
13494 var a = title ? config.annotations[title] : null;
13495 if(!tiddler || !title || !a)
13497 var text = a.format([title]);
13498 wikify(text,createTiddlyElement(place,"div",null,"annotation"),null,tiddler);
13502 //-- Menu and toolbar commands
13505 config.commands.closeTiddler.handler = function(event,src,title)
13507 story.closeTiddler(title,true);
13511 config.commands.closeOthers.handler = function(event,src,title)
13513 story.closeAllTiddlers(title);
13517 config.commands.editTiddler.handler = function(event,src,title)
13520 var tiddlerElem = document.getElementById(story.idPrefix + title);
13521 var fields = tiddlerElem.getAttribute("tiddlyFields");
13522 story.displayTiddler(null,title,DEFAULT_EDIT_TEMPLATE,false,null,fields);
13523 story.focusTiddler(title,"text");
13527 config.commands.saveTiddler.handler = function(event,src,title)
13529 var newTitle = story.saveTiddler(title,event.shiftKey);
13531 story.displayTiddler(null,newTitle);
13535 config.commands.cancelTiddler.handler = function(event,src,title)
13537 if(story.hasChanges(title) && !readOnly) {
13538 if(!confirm(this.warning.format([title])))
13541 story.setDirty(title,false);
13542 story.displayTiddler(null,title);
13546 config.commands.deleteTiddler.handler = function(event,src,title)
13548 var deleteIt = true;
13549 if (config.options.chkConfirmDelete)
13550 deleteIt = confirm(this.warning.format([title]));
13552 store.removeTiddler(title);
13553 story.closeTiddler(title,true);
13559 config.commands.permalink.handler = function(event,src,title)
13561 var t = encodeURIComponent(String.encodeTiddlyLink(title));
13562 if(window.location.hash != t)
13563 window.location.hash = t;
13567 config.commands.references.handlePopup = function(popup,title)
13569 var references = store.getReferringTiddlers(title);
13571 for(var r=0; r<references.length; r++) {
13572 if(references[r].title != title && !references[r].isTagged("excludeLists")) {
13573 createTiddlyLink(createTiddlyElement(popup,"li"),references[r].title,true);
13578 createTiddlyText(createTiddlyElement(popup,"li",null,"disabled"),this.popupNone);
13581 config.commands.jump.handlePopup = function(popup,title)
13583 story.forEachTiddler(function(title,element) {
13584 createTiddlyLink(createTiddlyElement(popup,"li"),title,true,null,false,null,true);
13588 config.commands.syncing.handlePopup = function(popup,title)
13590 var tiddler = store.fetchTiddler(title);
13593 var serverType = tiddler.getServerType();
13594 var serverHost = tiddler.fields['server.host'];
13595 var serverWorkspace = tiddler.fields['server.workspace'];
13596 if(!serverWorkspace)
13597 serverWorkspace = "";
13599 var e = createTiddlyElement(popup,"li",null,"popupMessage");
13600 e.innerHTML = config.commands.syncing.currentlySyncing.format([serverType,serverHost,serverWorkspace]);
13602 createTiddlyElement(popup,"li",null,"popupMessage",config.commands.syncing.notCurrentlySyncing);
13605 createTiddlyElement(createTiddlyElement(popup,"li",null,"listBreak"),"div");
13606 var btn = createTiddlyButton(createTiddlyElement(popup,"li"),this.captionUnSync,null,config.commands.syncing.onChooseServer);
13607 btn.setAttribute("tiddler",title);
13608 btn.setAttribute("server.type","");
13610 createTiddlyElement(createTiddlyElement(popup,"li",null,"listBreak"),"div");
13611 createTiddlyElement(popup,"li",null,"popupMessage",config.commands.syncing.chooseServer);
13612 var feeds = store.getTaggedTiddlers("systemServer","title");
13613 for(var t=0; t<feeds.length; t++) {
13615 var feedServerType = store.getTiddlerSlice(f.title,"Type");
13616 if(!feedServerType)
13617 feedServerType = "file";
13618 var feedServerHost = store.getTiddlerSlice(f.title,"URL");
13619 if(!feedServerHost)
13620 feedServerHost = "";
13621 var feedServerWorkspace = store.getTiddlerSlice(f.title,"Workspace");
13622 if(!feedServerWorkspace)
13623 feedServerWorkspace = "";
13624 var caption = f.title;
13625 if(serverType == feedServerType && serverHost == feedServerHost && serverWorkspace == feedServerWorkspace) {
13626 caption = config.commands.syncing.currServerMarker + caption;
13628 caption = config.commands.syncing.notCurrServerMarker + caption;
13630 btn = createTiddlyButton(createTiddlyElement(popup,"li"),caption,null,config.commands.syncing.onChooseServer);
13631 btn.setAttribute("tiddler",title);
13632 btn.setAttribute("server.type",feedServerType);
13633 btn.setAttribute("server.host",feedServerHost);
13634 btn.setAttribute("server.workspace",feedServerWorkspace);
13638 config.commands.syncing.onChooseServer = function(e)
13640 var tiddler = this.getAttribute("tiddler");
13641 var serverType = this.getAttribute("server.type");
13643 store.addTiddlerFields(tiddler,{
13644 'server.type': serverType,
13645 'server.host': this.getAttribute("server.host"),
13646 'server.workspace': this.getAttribute("server.workspace")
13649 store.setValue(tiddler,'server',null);
13654 config.commands.fields.handlePopup = function(popup,title)
13656 var tiddler = store.fetchTiddler(title);
13660 store.forEachField(tiddler,function(tiddler,fieldName,value) {fields[fieldName] = value;},true);
13662 for(var t in fields) {
13663 items.push({field: t,value: fields[t]});
13665 items.sort(function(a,b) {return a.field < b.field ? -1 : (a.field == b.field ? 0 : +1);});
13666 if(items.length > 0)
13667 ListView.create(popup,items,this.listViewTemplate);
13669 createTiddlyElement(popup,"div",null,null,this.emptyText);
13673 //-- Tiddler() object
13676 function Tiddler(title)
13678 this.title = title;
13680 this.modifier = null;
13681 this.modified = new Date();
13682 this.created = new Date();
13684 this.linksUpdated = false;
13690 Tiddler.prototype.getLinks = function()
13692 if(this.linksUpdated==false)
13697 // Returns the fields that are inherited in string field:"value" field2:"value2" format
13698 Tiddler.prototype.getInheritedFields = function()
13701 for(i in this.fields) {
13702 if(i=="server.host" || i=="server.workspace" || i=="wikiformat"|| i=="server.type") {
13703 f[i] = this.fields[i];
13706 return String.encodeHashMap(f);
13709 // Increment the changeCount of a tiddler
13710 Tiddler.prototype.incChangeCount = function()
13712 var c = this.fields['changecount'];
13713 c = c ? parseInt(c) : 0;
13714 this.fields['changecount'] = String(c+1);
13717 // Clear the changeCount of a tiddler
13718 Tiddler.prototype.clearChangeCount = function()
13720 if(this.fields['changecount']) {
13721 delete this.fields['changecount'];
13725 // Returns true if the tiddler has been updated since the tiddler was created or downloaded
13726 Tiddler.prototype.isTouched = function()
13728 var changeCount = this.fields['changecount'];
13729 if(changeCount === undefined)
13731 return changeCount > 0;
13734 // Format the text for storage in an RSS item
13735 Tiddler.prototype.saveToRss = function(url)
13739 s.push("<title" + ">" + this.title.htmlEncode() + "</title" + ">");
13740 s.push("<description>" + wikifyStatic(this.text,null,this).htmlEncode() + "</description>");
13741 for(var t=0; t<this.tags.length; t++)
13742 s.push("<category>" + this.tags[t] + "</category>");
13743 s.push("<link>" + url + "#" + encodeURIComponent(String.encodeTiddlyLink(this.title)) + "</link>");
13744 s.push("<pubDate>" + this.modified.toGMTString() + "</pubDate>");
13746 return s.join("\n");
13749 // Change the text and other attributes of a tiddler
13750 Tiddler.prototype.set = function(title,text,modifier,modified,tags,created,fields)
13752 this.assign(title,text,modifier,modified,tags,created,fields);
13757 // Change the text and other attributes of a tiddler without triggered a tiddler.changed() call
13758 Tiddler.prototype.assign = function(title,text,modifier,modified,tags,created,fields)
13760 if(title != undefined)
13761 this.title = title;
13762 if(text != undefined)
13764 if(modifier != undefined)
13765 this.modifier = modifier;
13766 if(modified != undefined)
13767 this.modified = modified;
13768 if(created != undefined)
13769 this.created = created;
13770 if(fields != undefined)
13771 this.fields = fields;
13772 if(tags != undefined)
13773 this.tags = (typeof tags == "string") ? tags.readBracketedList() : tags;
13774 else if(this.tags == undefined)
13779 // Get the tags for a tiddler as a string (space delimited, using [[brackets]] for tags containing spaces)
13780 Tiddler.prototype.getTags = function()
13782 return String.encodeTiddlyLinkList(this.tags);
13785 // Test if a tiddler carries a tag
13786 Tiddler.prototype.isTagged = function(tag)
13788 return this.tags.indexOf(tag) != -1;
13791 // Static method to convert "\n" to newlines, "\s" to "\"
13792 Tiddler.unescapeLineBreaks = function(text)
13794 return text ? text.unescapeLineBreaks() : "";
13797 // Convert newlines to "\n", "\" to "\s"
13798 Tiddler.prototype.escapeLineBreaks = function()
13800 return this.text.escapeLineBreaks();
13803 // Updates the secondary information (like links[] array) after a change to a tiddler
13804 Tiddler.prototype.changed = function()
13807 var t = this.autoLinkWikiWords() ? 0 : 1;
13808 var tiddlerLinkRegExp = t==0 ? config.textPrimitives.tiddlerAnyLinkRegExp : config.textPrimitives.tiddlerForcedLinkRegExp;
13809 tiddlerLinkRegExp.lastIndex = 0;
13810 var formatMatch = tiddlerLinkRegExp.exec(this.text);
13811 while(formatMatch) {
13812 var lastIndex = tiddlerLinkRegExp.lastIndex;
13813 if(t==0 && formatMatch[1] && formatMatch[1] != this.title) {
13815 if(formatMatch.index > 0) {
13816 var preRegExp = new RegExp(config.textPrimitives.unWikiLink+"|"+config.textPrimitives.anyLetter,"mg");
13817 preRegExp.lastIndex = formatMatch.index-1;
13818 var preMatch = preRegExp.exec(this.text);
13819 if(preMatch.index != formatMatch.index-1)
13820 this.links.pushUnique(formatMatch[1]);
13822 this.links.pushUnique(formatMatch[1]);
13825 else if(formatMatch[2-t] && !config.formatterHelpers.isExternalLink(formatMatch[3-t])) // titledBrackettedLink
13826 this.links.pushUnique(formatMatch[3-t]);
13827 else if(formatMatch[4-t] && formatMatch[4-t] != this.title) // brackettedLink
13828 this.links.pushUnique(formatMatch[4-t]);
13829 tiddlerLinkRegExp.lastIndex = lastIndex;
13830 formatMatch = tiddlerLinkRegExp.exec(this.text);
13832 this.linksUpdated = true;
13835 Tiddler.prototype.getSubtitle = function()
13837 var theModifier = this.modifier;
13839 theModifier = config.messages.subtitleUnknown;
13840 var theModified = this.modified;
13842 theModified = theModified.toLocaleString();
13844 theModified = config.messages.subtitleUnknown;
13845 return config.messages.tiddlerLinkTooltip.format([this.title,theModifier,theModified]);
13848 Tiddler.prototype.isReadOnly = function()
13853 Tiddler.prototype.autoLinkWikiWords = function()
13855 return !(this.isTagged("systemConfig") || this.isTagged("excludeMissing"));
13858 Tiddler.prototype.generateFingerprint = function()
13860 return "0x" + Crypto.hexSha1Str(this.text);
13863 Tiddler.prototype.getServerType = function()
13865 var serverType = null;
13866 if(this.fields && this.fields['server.type'])
13867 serverType = this.fields['server.type'];
13869 serverType = this.fields['wikiformat'];
13870 if(serverType && !config.adaptors[serverType])
13875 Tiddler.prototype.getAdaptor = function()
13877 var serverType = this.getServerType();
13879 return new config.adaptors[serverType];
13885 //-- TiddlyWiki() object contains Tiddler()s
13888 function TiddlyWiki()
13890 var tiddlers = {}; // Hashmap by name of tiddlers
13891 this.tiddlersUpdated = false;
13892 this.namedNotifications = []; // Array of {name:,notify:} of notification functions
13893 this.notificationLevel = 0;
13894 this.slices = {}; // map tiddlerName->(map sliceName->sliceValue). Lazy.
13895 this.clear = function() {
13897 this.setDirty(false);
13899 this.fetchTiddler = function(title) {
13900 return tiddlers[title];
13902 this.deleteTiddler = function(title) {
13903 delete this.slices[title];
13904 delete tiddlers[title];
13906 this.addTiddler = function(tiddler) {
13907 delete this.slices[tiddler.title];
13908 tiddlers[tiddler.title] = tiddler;
13910 this.forEachTiddler = function(callback) {
13911 for(var t in tiddlers) {
13912 var tiddler = tiddlers[t];
13913 if(tiddler instanceof Tiddler)
13914 callback.call(this,t,tiddler);
13919 TiddlyWiki.prototype.setDirty = function(dirty)
13921 this.dirty = dirty;
13924 TiddlyWiki.prototype.isDirty = function()
13929 TiddlyWiki.prototype.suspendNotifications = function()
13931 this.notificationLevel--;
13934 TiddlyWiki.prototype.resumeNotifications = function()
13936 this.notificationLevel++;
13939 // Invoke the notification handlers for a particular tiddler
13940 TiddlyWiki.prototype.notify = function(title,doBlanket)
13942 if(!this.notificationLevel) {
13943 for(var t=0; t<this.namedNotifications.length; t++) {
13944 var n = this.namedNotifications[t];
13945 if((n.name == null && doBlanket) || (n.name == title))
13951 // Invoke the notification handlers for all tiddlers
13952 TiddlyWiki.prototype.notifyAll = function()
13954 if(!this.notificationLevel) {
13955 for(var t=0; t<this.namedNotifications.length; t++) {
13956 var n = this.namedNotifications[t];
13963 // Add a notification handler to a tiddler
13964 TiddlyWiki.prototype.addNotification = function(title,fn)
13966 for(var i=0; i<this.namedNotifications.length; i++) {
13967 if((this.namedNotifications[i].name == title) && (this.namedNotifications[i].notify == fn))
13970 this.namedNotifications.push({name: title, notify: fn});
13974 TiddlyWiki.prototype.removeTiddler = function(title)
13976 var tiddler = this.fetchTiddler(title);
13978 this.deleteTiddler(title);
13979 this.notify(title,true);
13980 this.setDirty(true);
13984 TiddlyWiki.prototype.tiddlerExists = function(title)
13986 var t = this.fetchTiddler(title);
13987 return t != undefined;
13990 TiddlyWiki.prototype.isShadowTiddler = function(title)
13992 return typeof config.shadowTiddlers[title] == "string";
13995 TiddlyWiki.prototype.getTiddler = function(title)
13997 var t = this.fetchTiddler(title);
14004 TiddlyWiki.prototype.getTiddlerText = function(title,defaultText)
14006 var tiddler = this.fetchTiddler(title);
14008 return tiddler.text;
14010 return defaultText;
14011 var pos = title.indexOf(config.textPrimitives.sliceSeparator);
14013 var slice = this.getTiddlerSlice(title.substr(0,pos),title.substr(pos + config.textPrimitives.sliceSeparator.length));
14017 if(this.isShadowTiddler(title))
14018 return config.shadowTiddlers[title];
14019 if(defaultText != undefined)
14020 return defaultText;
14024 TiddlyWiki.prototype.slicesRE = /(?:[\'\/]*~?([\.\w]+)[\'\/]*\:[\'\/]*\s*(.*?)\s*$)|(?:\|[\'\/]*~?([\.\w]+)\:?[\'\/]*\|\s*(.*?)\s*\|)/gm;
14027 TiddlyWiki.prototype.calcAllSlices = function(title)
14030 var text = this.getTiddlerText(title,"");
14031 this.slicesRE.lastIndex = 0;
14033 var m = this.slicesRE.exec(text);
14036 slices[m[1]] = m[2];
14038 slices[m[3]] = m[4];
14044 // Returns the slice of text of the given name
14045 TiddlyWiki.prototype.getTiddlerSlice = function(title,sliceName)
14047 var slices = this.slices[title];
14049 slices = this.calcAllSlices(title);
14050 this.slices[title] = slices;
14052 return slices[sliceName];
14055 // Build an hashmap of the specified named slices of a tiddler
14056 TiddlyWiki.prototype.getTiddlerSlices = function(title,sliceNames)
14059 for(var t=0; t<sliceNames.length; t++) {
14060 var slice = this.getTiddlerSlice(title,sliceNames[t]);
14062 r[sliceNames[t]] = slice;
14067 TiddlyWiki.prototype.getRecursiveTiddlerText = function(title,defaultText,depth)
14069 var bracketRegExp = new RegExp("(?:\\[\\[([^\\]]+)\\]\\])","mg");
14070 var text = this.getTiddlerText(title,null);
14072 return defaultText;
14076 var match = bracketRegExp.exec(text);
14078 textOut.push(text.substr(lastPos,match.index-lastPos));
14081 textOut.push(match[1]);
14083 textOut.push(this.getRecursiveTiddlerText(match[1],"[[" + match[1] + "]]",depth-1));
14085 lastPos = match.index + match[0].length;
14087 textOut.push(text.substr(lastPos));
14090 return textOut.join("");
14093 TiddlyWiki.prototype.setTiddlerTag = function(title,status,tag)
14095 var tiddler = this.fetchTiddler(title);
14097 var t = tiddler.tags.indexOf(tag);
14099 tiddler.tags.splice(t,1);
14101 tiddler.tags.push(tag);
14103 this.incChangeCount(title);
14104 this.notify(title,true);
14105 this.setDirty(true);
14109 TiddlyWiki.prototype.addTiddlerFields = function(title,fields)
14111 var tiddler = this.fetchTiddler(title);
14114 merge(tiddler.fields,fields);
14116 this.incChangeCount(title);
14117 this.notify(title,true);
14118 this.setDirty(true);
14121 TiddlyWiki.prototype.saveTiddler = function(title,newTitle,newBody,modifier,modified,tags,fields,clearChangeCount,created)
14123 var tiddler = this.fetchTiddler(title);
14125 created = created ? created : tiddler.created; // Preserve created date
14126 this.deleteTiddler(title);
14128 created = created ? created : modified;
14129 tiddler = new Tiddler();
14131 tiddler.set(newTitle,newBody,modifier,modified,tags,created,fields);
14132 this.addTiddler(tiddler);
14133 if(clearChangeCount)
14134 tiddler.clearChangeCount();
14136 tiddler.incChangeCount();
14137 if(title != newTitle)
14138 this.notify(title,true);
14139 this.notify(newTitle,true);
14140 this.setDirty(true);
14144 // Reset the sync status of a freshly synced tiddler
14145 TiddlyWiki.prototype.resetTiddler = function(title)
14147 var tiddler = this.fetchTiddler(title);
14149 tiddler.clearChangeCount();
14150 this.notify(title,true);
14151 this.setDirty(true);
14155 TiddlyWiki.prototype.incChangeCount = function(title)
14157 var tiddler = this.fetchTiddler(title);
14159 tiddler.incChangeCount();
14162 TiddlyWiki.prototype.createTiddler = function(title)
14164 var tiddler = this.fetchTiddler(title);
14166 tiddler = new Tiddler();
14167 tiddler.title = title;
14168 this.addTiddler(tiddler);
14169 this.setDirty(true);
14174 // Load contents of a TiddlyWiki from an HTML DIV
14175 TiddlyWiki.prototype.loadFromDiv = function(src,idPrefix,noUpdate)
14177 this.idPrefix = idPrefix;
14178 var storeElem = (typeof src == "string") ? document.getElementById(src) : src;
14181 var tiddlers = this.getLoader().loadTiddlers(this,storeElem.childNodes);
14182 this.setDirty(false);
14184 for(var i = 0;i<tiddlers.length; i++)
14185 tiddlers[i].changed();
14189 // Load contents of a TiddlyWiki from a string
14190 // Returns null if there's an error
14191 TiddlyWiki.prototype.importTiddlyWiki = function(text)
14193 var posDiv = locateStoreArea(text);
14196 var content = "<" + "html><" + "body>" + text.substring(posDiv[0],posDiv[1] + endSaveArea.length) + "<" + "/body><" + "/html>";
14197 // Create the iframe
14198 var iframe = document.createElement("iframe");
14199 iframe.style.display = "none";
14200 document.body.appendChild(iframe);
14201 var doc = iframe.document;
14202 if(iframe.contentDocument)
14203 doc = iframe.contentDocument; // For NS6
14204 else if(iframe.contentWindow)
14205 doc = iframe.contentWindow.document; // For IE5.5 and IE6
14206 // Put the content in the iframe
14208 doc.writeln(content);
14210 // Load the content into a TiddlyWiki() object
14211 var storeArea = doc.getElementById("storeArea");
14212 this.loadFromDiv(storeArea,"store");
14213 // Get rid of the iframe
14214 iframe.parentNode.removeChild(iframe);
14218 TiddlyWiki.prototype.updateTiddlers = function()
14220 this.tiddlersUpdated = true;
14221 this.forEachTiddler(function(title,tiddler) {
14226 // Return all tiddlers formatted as an HTML string
14227 TiddlyWiki.prototype.allTiddlersAsHtml = function()
14229 return store.getSaver().externalize(store);
14232 // Return an array of tiddlers matching a search regular expression
14233 TiddlyWiki.prototype.search = function(searchRegExp,sortField,excludeTag)
14235 var candidates = this.reverseLookup("tags",excludeTag,false);
14237 for(var t=0; t<candidates.length; t++) {
14238 if((candidates[t].title.search(searchRegExp) != -1) || (candidates[t].text.search(searchRegExp) != -1))
14239 results.push(candidates[t]);
14242 sortField = "title";
14243 results.sort(function(a,b) {return a[sortField] < b[sortField] ? -1 : (a[sortField] == b[sortField] ? 0 : +1);});
14247 // Return an array of all the tags in use. Each member of the array is another array where [0] is the name of the tag and [1] is the number of occurances
14248 TiddlyWiki.prototype.getTags = function(excludeTag)
14251 this.forEachTiddler(function(title,tiddler) {
14252 for(var g=0; g<tiddler.tags.length; g++) {
14253 var tag = tiddler.tags[g];
14255 var t = store.fetchTiddler(tag);
14256 if(t && t.isTagged(excludeTag))
14260 for(var c=0; c<results.length; c++) {
14261 if(results[c][0] == tag) {
14267 results.push([tag,1]);
14270 results.sort(function(a,b) {return a[0].toLowerCase() < b[0].toLowerCase() ? -1 : (a[0].toLowerCase() == b[0].toLowerCase() ? 0 : +1);});
14274 // Return an array of the tiddlers that are tagged with a given tag
14275 TiddlyWiki.prototype.getTaggedTiddlers = function(tag,sortField)
14277 return this.reverseLookup("tags",tag,true,sortField);
14280 // Return an array of the tiddlers that link to a given tiddler
14281 TiddlyWiki.prototype.getReferringTiddlers = function(title,unusedParameter,sortField)
14283 if(!this.tiddlersUpdated)
14284 this.updateTiddlers();
14285 return this.reverseLookup("links",title,true,sortField);
14288 // Return an array of the tiddlers that do or do not have a specified entry in the specified storage array (ie, "links" or "tags")
14289 // lookupMatch == true to match tiddlers, false to exclude tiddlers
14290 TiddlyWiki.prototype.reverseLookup = function(lookupField,lookupValue,lookupMatch,sortField)
14293 this.forEachTiddler(function(title,tiddler) {
14294 var f = !lookupMatch;
14295 for(var lookup=0; lookup<tiddler[lookupField].length; lookup++) {
14296 if(tiddler[lookupField][lookup] == lookupValue)
14300 results.push(tiddler);
14303 sortField = "title";
14304 results.sort(function(a,b) {return a[sortField] < b[sortField] ? -1 : (a[sortField] == b[sortField] ? 0 : +1);});
14308 // Return the tiddlers as a sorted array
14309 TiddlyWiki.prototype.getTiddlers = function(field,excludeTag)
14312 this.forEachTiddler(function(title,tiddler) {
14313 if(excludeTag == undefined || !tiddler.isTagged(excludeTag))
14314 results.push(tiddler);
14317 results.sort(function(a,b) {return a[field] < b[field] ? -1 : (a[field] == b[field] ? 0 : +1);});
14321 // Return array of names of tiddlers that are referred to but not defined
14322 TiddlyWiki.prototype.getMissingLinks = function(sortField)
14324 if(!this.tiddlersUpdated)
14325 this.updateTiddlers();
14327 this.forEachTiddler(function (title,tiddler) {
14328 if(tiddler.isTagged("excludeMissing") || tiddler.isTagged("systemConfig"))
14330 for(var n=0; n<tiddler.links.length;n++) {
14331 var link = tiddler.links[n];
14332 if(this.fetchTiddler(link) == null && !this.isShadowTiddler(link))
14333 results.pushUnique(link);
14340 // Return an array of names of tiddlers that are defined but not referred to
14341 TiddlyWiki.prototype.getOrphans = function()
14344 this.forEachTiddler(function (title,tiddler) {
14345 if(this.getReferringTiddlers(title).length == 0 && !tiddler.isTagged("excludeLists"))
14346 results.push(title);
14352 // Return an array of names of all the shadow tiddlers
14353 TiddlyWiki.prototype.getShadowed = function()
14356 for(var t in config.shadowTiddlers) {
14357 if(typeof config.shadowTiddlers[t] == "string")
14364 // Return an array of tiddlers that have been touched since they were downloaded or created
14365 TiddlyWiki.prototype.getTouched = function()
14368 this.forEachTiddler(function(title,tiddler) {
14369 if(tiddler.isTouched())
14370 results.push(tiddler);
14376 // Resolves a Tiddler reference or tiddler title into a Tiddler object, or null if it doesn't exist
14377 TiddlyWiki.prototype.resolveTiddler = function(tiddler)
14379 var t = (typeof tiddler == 'string') ? this.getTiddler(tiddler) : tiddler;
14380 return t instanceof Tiddler ? t : null;
14383 TiddlyWiki.prototype.getLoader = function()
14386 this.loader = new TW21Loader();
14387 return this.loader;
14390 TiddlyWiki.prototype.getSaver = function()
14393 this.saver = new TW21Saver();
14397 // Returns true if path is a valid field name (path),
14398 // i.e. a sequence of identifiers, separated by '.'
14399 TiddlyWiki.isValidFieldName = function(name)
14401 var match = /[a-zA-Z_]\w*(\.[a-zA-Z_]\w*)*/.exec(name);
14402 return match && (match[0] == name);
14405 // Throws an exception when name is not a valid field name.
14406 TiddlyWiki.checkFieldName = function(name)
14408 if(!TiddlyWiki.isValidFieldName(name))
14409 throw config.messages.invalidFieldName.format([name]);
14412 function StringFieldAccess(n,readOnly)
14414 this.set = readOnly ?
14415 function(t,v) {if(v != t[n]) throw config.messages.fieldCannotBeChanged.format([n]);} :
14416 function(t,v) {if(v != t[n]) {t[n] = v; return true;}};
14417 this.get = function(t) {return t[n];};
14420 function DateFieldAccess(n)
14422 this.set = function(t,v) {
14423 var d = v instanceof Date ? v : Date.convertFromYYYYMMDDHHMM(v);
14425 t[n] = d; return true;
14428 this.get = function(t) {return t[n].convertToYYYYMMDDHHMM();};
14431 function LinksFieldAccess(n)
14433 this.set = function(t,v) {
14434 var s = (typeof v == "string") ? v.readBracketedList() : v;
14435 if(s.toString() != t[n].toString()) {
14436 t[n] = s; return true;
14439 this.get = function(t) {return String.encodeTiddlyLinkList(t[n]);};
14442 TiddlyWiki.standardFieldAccess = {
14443 // The set functions return true when setting the data has changed the value.
14444 "title": new StringFieldAccess("title",true),
14445 // Handle the "tiddler" field name as the title
14446 "tiddler": new StringFieldAccess("title",true),
14447 "text": new StringFieldAccess("text"),
14448 "modifier": new StringFieldAccess("modifier"),
14449 "modified": new DateFieldAccess("modified"),
14450 "created": new DateFieldAccess("created"),
14451 "tags": new LinksFieldAccess("tags")
14454 TiddlyWiki.isStandardField = function(name)
14456 return TiddlyWiki.standardFieldAccess[name] != undefined;
14459 // Sets the value of the given field of the tiddler to the value.
14460 // Setting an ExtendedField's value to null or undefined removes the field.
14461 // Setting a namespace to undefined removes all fields of that namespace.
14462 // The fieldName is case-insensitive.
14463 // All values will be converted to a string value.
14464 TiddlyWiki.prototype.setValue = function(tiddler,fieldName,value)
14466 TiddlyWiki.checkFieldName(fieldName);
14467 var t = this.resolveTiddler(tiddler);
14470 fieldName = fieldName.toLowerCase();
14471 var isRemove = (value === undefined) || (value === null);
14472 var accessor = TiddlyWiki.standardFieldAccess[fieldName];
14475 // don't remove StandardFields
14477 var h = TiddlyWiki.standardFieldAccess[fieldName];
14478 if(!h.set(t,value))
14481 var oldValue = t.fields[fieldName];
14483 if(oldValue !== undefined) {
14484 // deletes a single field
14485 delete t.fields[fieldName];
14487 // no concrete value is defined for the fieldName
14488 // so we guess this is a namespace path.
14489 // delete all fields in a namespace
14490 var re = new RegExp('^'+fieldName+'\\.');
14492 for(var n in t.fields) {
14494 delete t.fields[n];
14502 // the "normal" set case. value is defined (not null/undefined)
14503 // For convenience provide a nicer conversion Date->String
14504 value = value instanceof Date ? value.convertToYYYYMMDDHHMMSSMMM() : String(value);
14505 if(oldValue == value)
14507 t.fields[fieldName] = value;
14510 // When we are here the tiddler/store really was changed.
14511 this.notify(t.title,true);
14512 if(!fieldName.match(/^temp\./))
14513 this.setDirty(true);
14516 // Returns the value of the given field of the tiddler.
14517 // The fieldName is case-insensitive.
14518 // Will only return String values (or undefined).
14519 TiddlyWiki.prototype.getValue = function(tiddler,fieldName)
14521 var t = this.resolveTiddler(tiddler);
14524 fieldName = fieldName.toLowerCase();
14525 var accessor = TiddlyWiki.standardFieldAccess[fieldName];
14527 return accessor.get(t);
14529 return t.fields[fieldName];
14532 // Calls the callback function for every field in the tiddler.
14533 // When callback function returns a non-false value the iteration stops
14534 // and that value is returned.
14535 // The order of the fields is not defined.
14536 // @param callback a function(tiddler,fieldName,value).
14537 TiddlyWiki.prototype.forEachField = function(tiddler,callback,onlyExtendedFields)
14539 var t = this.resolveTiddler(tiddler);
14542 for(var n in t.fields) {
14543 var result = callback(t,n,t.fields[n]);
14547 if(onlyExtendedFields)
14549 for(var n in TiddlyWiki.standardFieldAccess) {
14551 // even though the "title" field can also be referenced through the name "tiddler"
14552 // we only visit this field once.
14554 var result = callback(t,n,TiddlyWiki.standardFieldAccess[n].get(t));
14562 //-- Story functions
14565 function Story(container,idPrefix)
14567 this.container = container;
14568 this.idPrefix = idPrefix;
14569 this.highlightRegExp = null;
14572 Story.prototype.forEachTiddler = function(fn)
14574 var place = document.getElementById(this.container);
14577 var e = place.firstChild;
14579 var n = e.nextSibling;
14580 var title = e.getAttribute("tiddler");
14581 fn.call(this,title,e);
14586 Story.prototype.displayTiddlers = function(srcElement,titles,template,animate,unused,customFields,toggle)
14588 for(var t = titles.length-1;t>=0;t--)
14589 this.displayTiddler(srcElement,titles[t],template,animate,unused,customFields);
14592 Story.prototype.displayTiddler = function(srcElement,title,template,animate,unused,customFields,toggle)
14594 var place = document.getElementById(this.container);
14595 var tiddlerElem = document.getElementById(this.idPrefix + title);
14598 this.closeTiddler(title,true);
14600 this.refreshTiddler(title,template,false,customFields);
14602 var before = this.positionTiddler(srcElement);
14603 tiddlerElem = this.createTiddler(place,before,title,template,customFields);
14605 if(srcElement && typeof srcElement !== "string") {
14606 if(config.options.chkAnimate && (animate == undefined || animate == true) && anim && typeof Zoomer == "function" && typeof Scroller == "function")
14607 anim.startAnimating(new Zoomer(title,srcElement,tiddlerElem),new Scroller(tiddlerElem));
14609 window.scrollTo(0,ensureVisible(tiddlerElem));
14613 Story.prototype.positionTiddler = function(srcElement)
14615 var place = document.getElementById(this.container);
14617 if(typeof srcElement == "string") {
14618 switch(srcElement) {
14620 before = place.firstChild;
14627 var after = this.findContainingTiddler(srcElement);
14628 if(after == null) {
14629 before = place.firstChild;
14630 } else if(after.nextSibling) {
14631 before = after.nextSibling;
14632 if(before.nodeType != 1)
14639 Story.prototype.createTiddler = function(place,before,title,template,customFields)
14641 var tiddlerElem = createTiddlyElement(null,"div",this.idPrefix + title,"tiddler");
14642 tiddlerElem.setAttribute("refresh","tiddler");
14644 tiddlerElem.setAttribute("tiddlyFields",customFields);
14645 place.insertBefore(tiddlerElem,before);
14646 var defaultText = null;
14647 if(!store.tiddlerExists(title) && !store.isShadowTiddler(title))
14648 defaultText = this.loadMissingTiddler(title,customFields,tiddlerElem);
14649 this.refreshTiddler(title,template,false,customFields,defaultText);
14650 return tiddlerElem;
14653 Story.prototype.loadMissingTiddler = function(title,fields,tiddlerElem)
14655 var tiddler = new Tiddler(title);
14656 tiddler.fields = typeof fields == "string" ? fields.decodeHashMap() : (fields ? fields : {});
14657 var serverType = tiddler.getServerType();
14658 var host = tiddler.fields['server.host'];
14659 var workspace = tiddler.fields['server.workspace'];
14660 if(!serverType | !host)
14662 var sm = new SyncMachine(serverType,{
14663 start: function() {
14664 return this.openHost(host,"openWorkspace");
14666 openWorkspace: function() {
14667 return this.openWorkspace(workspace,"getTiddler");
14669 getTiddler: function() {
14670 return this.getTiddler(title,"gotTiddler");
14672 gotTiddler: function(tiddler) {
14673 if(tiddler && tiddler.text) {
14674 var downloaded = new Date();
14675 if(!tiddler.created)
14676 tiddler.created = downloaded;
14677 if(!tiddler.modified)
14678 tiddler.modified = tiddler.created;
14679 store.saveTiddler(tiddler.title,tiddler.title,tiddler.text,tiddler.modifier,tiddler.modified,tiddler.tags,tiddler.fields,true,tiddler.created);
14685 error: function(message) {
14686 displayMessage("Error loading missing tiddler from %0: %1".format([host,message]));
14690 return config.messages.loadingMissingTiddler.format([title,serverType,host,workspace]);
14693 Story.prototype.chooseTemplateForTiddler = function(title,template)
14696 template = DEFAULT_VIEW_TEMPLATE;
14697 if(template == DEFAULT_VIEW_TEMPLATE || template == DEFAULT_EDIT_TEMPLATE)
14698 template = config.tiddlerTemplates[template];
14702 Story.prototype.getTemplateForTiddler = function(title,template,tiddler)
14704 return store.getRecursiveTiddlerText(template,null,10);
14707 Story.prototype.refreshTiddler = function(title,template,force,customFields,defaultText)
14709 var tiddlerElem = document.getElementById(this.idPrefix + title);
14711 if(tiddlerElem.getAttribute("dirty") == "true" && !force)
14712 return tiddlerElem;
14713 template = this.chooseTemplateForTiddler(title,template);
14714 var currTemplate = tiddlerElem.getAttribute("template");
14715 if((template != currTemplate) || force) {
14716 var tiddler = store.getTiddler(title);
14718 tiddler = new Tiddler();
14719 if(store.isShadowTiddler(title)) {
14720 tiddler.set(title,store.getTiddlerText(title),config.views.wikified.shadowModifier,version.date,[],version.date);
14722 var text = template=="EditTemplate" ?
14723 config.views.editor.defaultText.format([title]) :
14724 config.views.wikified.defaultText.format([title]);
14725 text = defaultText ? defaultText : text;
14726 var fields = customFields ? customFields.decodeHashMap() : null;
14727 tiddler.set(title,text,config.views.wikified.defaultModifier,version.date,[],version.date,fields);
14730 tiddlerElem.setAttribute("tags",tiddler.tags.join(" "));
14731 tiddlerElem.setAttribute("tiddler",title);
14732 tiddlerElem.setAttribute("template",template);
14734 tiddlerElem.onmouseover = this.onTiddlerMouseOver;
14735 tiddlerElem.onmouseout = this.onTiddlerMouseOut;
14736 tiddlerElem.ondblclick = this.onTiddlerDblClick;
14737 tiddlerElem[window.event?"onkeydown":"onkeypress"] = this.onTiddlerKeyPress;
14738 var html = this.getTemplateForTiddler(title,template,tiddler);
14739 tiddlerElem.innerHTML = html;
14740 applyHtmlMacros(tiddlerElem,tiddler);
14741 if(store.getTaggedTiddlers(title).length > 0)
14742 addClass(tiddlerElem,"isTag");
14744 removeClass(tiddlerElem,"isTag");
14745 if(!store.tiddlerExists(title)) {
14746 if(store.isShadowTiddler(title))
14747 addClass(tiddlerElem,"shadow");
14749 addClass(tiddlerElem,"missing");
14751 removeClass(tiddlerElem,"shadow");
14752 removeClass(tiddlerElem,"missing");
14755 this.addCustomFields(tiddlerElem,customFields);
14759 return tiddlerElem;
14762 Story.prototype.addCustomFields = function(place,customFields)
14764 var fields = customFields.decodeHashMap();
14765 var w = document.createElement("div");
14766 w.style.display = "none";
14767 place.appendChild(w);
14768 for(var t in fields) {
14769 var e = document.createElement("input");
14770 e.setAttribute("type","text");
14771 e.setAttribute("value",fields[t]);
14773 e.setAttribute("edit",t);
14777 Story.prototype.refreshAllTiddlers = function()
14779 var place = document.getElementById(this.container);
14780 var e = place.firstChild;
14783 this.refreshTiddler(e.getAttribute("tiddler"),e.getAttribute("template"),true);
14784 while((e = e.nextSibling) != null)
14785 this.refreshTiddler(e.getAttribute("tiddler"),e.getAttribute("template"),true);
14788 Story.prototype.onTiddlerMouseOver = function(e)
14790 if(window.addClass instanceof Function)
14791 addClass(this,"selected");
14794 Story.prototype.onTiddlerMouseOut = function(e)
14796 if(window.removeClass instanceof Function)
14797 removeClass(this,"selected");
14800 Story.prototype.onTiddlerDblClick = function(e)
14802 if(!e) var e = window.event;
14803 var theTarget = resolveTarget(e);
14804 if(theTarget && theTarget.nodeName.toLowerCase() != "input" && theTarget.nodeName.toLowerCase() != "textarea") {
14805 if(document.selection && document.selection.empty)
14806 document.selection.empty();
14807 config.macros.toolbar.invokeCommand(this,"defaultCommand",e);
14808 e.cancelBubble = true;
14809 if(e.stopPropagation) e.stopPropagation();
14816 Story.prototype.onTiddlerKeyPress = function(e)
14818 if(!e) var e = window.event;
14820 var consume = false;
14821 var title = this.getAttribute("tiddler");
14822 var target = resolveTarget(e);
14823 switch(e.keyCode) {
14825 if(config.options.chkInsertTabs && target.tagName.toLowerCase() == "textarea") {
14826 replaceSelection(target,String.fromCharCode(9));
14829 if(config.isOpera) {
14830 target.onblur = function() {
14832 this.onblur = null;
14836 case 13: // Ctrl-Enter
14837 case 10: // Ctrl-Enter on IE PC
14838 case 77: // Ctrl-Enter is "M" on some platforms
14841 config.macros.toolbar.invokeCommand(this,"defaultCommand",e);
14847 config.macros.toolbar.invokeCommand(this,"cancelCommand",e);
14851 e.cancelBubble = consume;
14853 if(e.stopPropagation) e.stopPropagation(); // Stop Propagation
14854 e.returnValue = true; // Cancel The Event in IE
14855 if(e.preventDefault ) e.preventDefault(); // Cancel The Event in Moz
14860 Story.prototype.getTiddlerField = function(title,field)
14862 var tiddlerElem = document.getElementById(this.idPrefix + title);
14864 if(tiddlerElem != null) {
14865 var children = tiddlerElem.getElementsByTagName("*");
14866 for(var t=0; t<children.length; t++) {
14867 var c = children[t];
14868 if(c.tagName.toLowerCase() == "input" || c.tagName.toLowerCase() == "textarea") {
14871 if(c.getAttribute("edit") == field)
14879 Story.prototype.focusTiddler = function(title,field)
14881 var e = this.getTiddlerField(title,field);
14888 Story.prototype.blurTiddler = function(title)
14890 var tiddlerElem = document.getElementById(this.idPrefix + title);
14891 if(tiddlerElem != null && tiddlerElem.focus && tiddlerElem.blur) {
14892 tiddlerElem.focus();
14893 tiddlerElem.blur();
14897 Story.prototype.setTiddlerField = function(title,tag,mode,field)
14899 var c = story.getTiddlerField(title,field);
14901 var tags = c.value.readBracketedList();
14902 tags.setItem(tag,mode);
14903 c.value = String.encodeTiddlyLinkList(tags);
14906 Story.prototype.setTiddlerTag = function(title,tag,mode)
14908 Story.prototype.setTiddlerField(title,tag,mode,"tags");
14911 Story.prototype.closeTiddler = function(title,animate,unused)
14913 var tiddlerElem = document.getElementById(this.idPrefix + title);
14914 if(tiddlerElem != null) {
14916 this.scrubTiddler(tiddlerElem);
14917 if(config.options.chkAnimate && animate && anim && typeof Slider == "function")
14918 anim.startAnimating(new Slider(tiddlerElem,false,null,"all"));
14920 removeNode(tiddlerElem);
14926 Story.prototype.scrubTiddler = function(tiddlerElem)
14928 tiddlerElem.id = null;
14931 Story.prototype.setDirty = function(title,dirty)
14933 var tiddlerElem = document.getElementById(this.idPrefix + title);
14934 if(tiddlerElem != null)
14935 tiddlerElem.setAttribute("dirty",dirty ? "true" : "false");
14938 Story.prototype.isDirty = function(title)
14940 var tiddlerElem = document.getElementById(this.idPrefix + title);
14941 if(tiddlerElem != null)
14942 return tiddlerElem.getAttribute("dirty") == "true";
14946 Story.prototype.areAnyDirty = function()
14949 this.forEachTiddler(function(title,element) {
14950 if(this.isDirty(title))
14956 Story.prototype.closeAllTiddlers = function(exclude)
14959 this.forEachTiddler(function(title,element) {
14960 if((title != exclude) && element.getAttribute("dirty") != "true")
14961 this.closeTiddler(title);
14963 window.scrollTo(0,ensureVisible(this.container));
14966 Story.prototype.isEmpty = function()
14968 var place = document.getElementById(this.container);
14969 return place && place.firstChild == null;
14972 Story.prototype.search = function(text,useCaseSensitive,useRegExp)
14974 this.closeAllTiddlers();
14975 highlightHack = new RegExp(useRegExp ? text : text.escapeRegExp(),useCaseSensitive ? "mg" : "img");
14976 var matches = store.search(highlightHack,"title","excludeSearch");
14978 for(var t=0;t<matches.length;t++)
14979 titles.push(matches[t].title);
14980 this.displayTiddlers(null,titles);
14981 highlightHack = null;
14982 var q = useRegExp ? "/" : "'";
14983 if(matches.length > 0)
14984 displayMessage(config.macros.search.successMsg.format([titles.length.toString(),q + text + q]));
14986 displayMessage(config.macros.search.failureMsg.format([q + text + q]));
14989 Story.prototype.findContainingTiddler = function(e)
14991 while(e && !hasClass(e,"tiddler"))
14996 Story.prototype.gatherSaveFields = function(e,fields)
14998 if(e && e.getAttribute) {
14999 var f = e.getAttribute("edit");
15001 fields[f] = e.value.replace(/\r/mg,"");
15002 if(e.hasChildNodes()) {
15003 var c = e.childNodes;
15004 for(var t=0; t<c.length; t++)
15005 this.gatherSaveFields(c[t],fields);
15010 Story.prototype.hasChanges = function(title)
15012 var e = document.getElementById(this.idPrefix + title);
15015 this.gatherSaveFields(e,fields);
15016 var tiddler = store.fetchTiddler(title);
15019 for(var n in fields) {
15020 if(store.getValue(title,n) != fields[n])
15027 Story.prototype.saveTiddler = function(title,minorUpdate)
15029 var tiddlerElem = document.getElementById(this.idPrefix + title);
15030 if(tiddlerElem != null) {
15032 this.gatherSaveFields(tiddlerElem,fields);
15033 var newTitle = fields.title ? fields.title : title;
15034 if(store.tiddlerExists(newTitle) && newTitle != title) {
15035 if(!confirm(config.messages.overwriteWarning.format([newTitle.toString()])))
15038 if(newTitle != title)
15039 this.closeTiddler(newTitle,false);
15040 tiddlerElem.id = this.idPrefix + newTitle;
15041 tiddlerElem.setAttribute("tiddler",newTitle);
15042 tiddlerElem.setAttribute("template",DEFAULT_VIEW_TEMPLATE);
15043 tiddlerElem.setAttribute("dirty","false");
15044 if(config.options.chkForceMinorUpdate)
15045 minorUpdate = !minorUpdate;
15046 if(!store.tiddlerExists(newTitle))
15047 minorUpdate = false;
15048 var newDate = new Date();
15049 var extendedFields = store.tiddlerExists(newTitle) ? store.fetchTiddler(newTitle).fields : {};
15050 for(var n in fields) {
15051 if(!TiddlyWiki.isStandardField(n))
15052 extendedFields[n] = fields[n];
15054 var tiddler = store.saveTiddler(title,newTitle,fields.text,minorUpdate ? undefined : config.options.txtUserName,minorUpdate ? undefined : newDate,fields.tags,extendedFields);
15055 autoSaveChanges(null,[tiddler]);
15061 Story.prototype.permaView = function()
15064 this.forEachTiddler(function(title,element) {
15065 links.push(String.encodeTiddlyLink(title));
15067 var t = encodeURIComponent(links.join(" "));
15070 if(window.location.hash != t)
15071 window.location.hash = t;
15093 var cmb = config.messages.backstage;
15094 this.area = document.getElementById("backstageArea");
15095 this.toolbar = document.getElementById("backstageToolbar");
15096 this.button = document.getElementById("backstageButton");
15097 this.button.style.display = "block";
15098 var t = cmb.open.text + " " + glyph("bentArrowLeft");
15099 this.showButton = createTiddlyButton(this.button,t,cmb.open.tooltip,
15100 function (e) {backstage.show(); return false;},null,"backstageShow");
15101 t = glyph("bentArrowRight") + " " + cmb.close.text;
15102 this.hideButton = createTiddlyButton(this.button,t,cmb.close.tooltip,
15103 function (e) {backstage.hide(); return false;},null,"backstageHide");
15104 this.cloak = document.getElementById("backstageCloak");
15105 this.panel = document.getElementById("backstagePanel");
15106 this.panelFooter = createTiddlyElement(this.panel,"div",null,"backstagePanelFooter");
15107 this.panelBody = createTiddlyElement(this.panel,"div",null,"backstagePanelBody");
15108 this.cloak.onmousedown = function(e) {
15109 backstage.switchTab(null);
15111 createTiddlyText(this.toolbar,cmb.prompt);
15112 for(t=0; t<config.backstageTasks.length; t++) {
15113 var taskName = config.backstageTasks[t];
15114 var task = config.tasks[taskName];
15115 var handler = task.action ? this.onClickCommand : this.onClickTab;
15116 var text = task.text + (task.action ? "" : glyph("downTriangle"));
15117 var btn = createTiddlyButton(this.toolbar,text,task.tooltip,handler,"backstageTab");
15118 btn.setAttribute("task",taskName);
15119 addClass(btn,task.action ? "backstageAction" : "backstageTask");
15121 this.content = document.getElementById("contentWrapper");
15122 if(config.options.chkBackstage)
15128 isVisible: function () {
15129 return this.area ? this.area.style.display == "block" : false;
15133 this.area.style.display = "block";
15134 if(anim && config.options.chkAnimate) {
15135 backstage.toolbar.style.left = findWindowWidth() + "px";
15137 {style: "left", start: findWindowWidth(), end: 0, template: "%0px"}
15139 anim.startAnimating(new Morpher(backstage.toolbar,config.animDuration,p));
15141 backstage.area.style.left = "0px";
15143 this.showButton.style.display = "none";
15144 this.hideButton.style.display = "block";
15145 config.options.chkBackstage = true;
15146 saveOptionCookie("chkBackstage");
15147 addClass(this.content,"backstageVisible");
15151 if(this.currTabElem) {
15152 this.switchTab(null);
15154 backstage.toolbar.style.left = "0px";
15155 if(anim && config.options.chkAnimate) {
15157 {style: "left", start: 0, end: findWindowWidth(), template: "%0px"}
15159 var c = function(element,properties) {backstage.area.style.display = "none";};
15160 anim.startAnimating(new Morpher(backstage.toolbar,config.animDuration,p,c));
15162 this.area.style.display = "none";
15164 this.showButton.style.display = "block";
15165 this.hideButton.style.display = "none";
15166 config.options.chkBackstage = false;
15167 saveOptionCookie("chkBackstage");
15168 removeClass(this.content,"backstageVisible");
15172 onClickCommand: function(e) {
15173 var task = config.tasks[this.getAttribute("task")];
15174 displayMessage(task);
15176 backstage.switchTab(null);
15182 onClickTab: function(e) {
15183 backstage.switchTab(this.getAttribute("task"));
15187 // Switch to a given tab, or none if null is passed
15188 switchTab: function(tabName) {
15189 var tabElem = null;
15190 var e = this.toolbar.firstChild;
15193 if(e.getAttribute && e.getAttribute("task") == tabName)
15197 if(tabName == backstage.currTabName)
15199 if(backstage.currTabElem) {
15200 removeClass(this.currTabElem,"backstageSelTab");
15202 if(tabElem && tabName) {
15203 backstage.preparePanel();
15204 addClass(tabElem,"backstageSelTab");
15205 var task = config.tasks[tabName];
15206 wikify(task.content,backstage.panelBody,null,null);
15207 backstage.showPanel();
15208 } else if(backstage.currTabElem) {
15209 backstage.hidePanel();
15211 backstage.currTabName = tabName;
15212 backstage.currTabElem = tabElem;
15215 isPanelVisible: function() {
15216 return backstage.panel ? backstage.panel.style.display == "block" : false;
15219 preparePanel: function() {
15220 backstage.cloak.style.height = findWindowHeight() + "px";
15221 backstage.cloak.style.display = "block";
15222 removeChildren(backstage.panelBody);
15223 return backstage.panelBody;
15226 showPanel: function() {
15227 backstage.panel.style.display = "block";
15228 if(anim && config.options.chkAnimate) {
15229 backstage.panel.style.top = (-backstage.panel.offsetHeight) + "px";
15231 {style: "top", start: -backstage.panel.offsetHeight, end: 0, template: "%0px"}
15233 anim.startAnimating(new Morpher(backstage.panel,config.animDuration,p),new Scroller(backstage.panel,false));
15235 backstage.panel.style.top = "0px";
15237 return backstage.panelBody;
15240 hidePanel: function() {
15241 backstage.currTabName = null;
15242 backstage.currTabElem = null;
15243 if(anim && config.options.chkAnimate) {
15245 {style: "top", start: 0, end: -(backstage.panel.offsetHeight), template: "%0px"},
15246 {style: "display", atEnd: "none"}
15248 var c = function(element,properties) {backstage.cloak.style.display = "none";};
15249 anim.startAnimating(new Morpher(backstage.panel,config.animDuration,p,c));
15251 backstage.panel.style.display = "none";
15252 backstage.cloak.style.display = "none";
15257 config.macros.backstage = {};
15259 config.macros.backstage.handler = function(place,macroName,params,wikifier,paramString,tiddler)
15261 var backstageTask = config.tasks[params[0]];
15263 createTiddlyButton(place,backstageTask.text,backstageTask.tooltip,function(e) {backstage.switchTab(params[0]); return false;});
15267 //-- ImportTiddlers macro
15270 config.macros.importTiddlers.handler = function(place,macroName,params,wikifier,paramString,tiddler)
15273 createTiddlyElement(place,"div",null,"marked",this.readOnlyWarning);
15276 var w = new Wizard();
15277 w.createWizard(place,this.wizardTitle);
15281 config.macros.importTiddlers.onCancel = function(e)
15283 var wizard = new Wizard(this);
15284 var place = wizard.clear();
15285 config.macros.importTiddlers.restart(wizard);
15289 config.macros.importTiddlers.restart = function(wizard)
15291 wizard.addStep(this.step1Title,this.step1Html);
15292 var s = wizard.getElement("selTypes");
15293 for(var t in config.adaptors) {
15294 var e = createTiddlyElement(s,"option",null,null,t);
15297 s = wizard.getElement("selFeeds");
15298 var feeds = this.getFeeds();
15300 e = createTiddlyElement(s,"option",null,null,t);
15303 wizard.setValue("feeds",feeds);
15304 s.onchange = config.macros.importTiddlers.onFeedChange;
15305 var fileInput = wizard.getElement("txtBrowse");
15306 fileInput.onchange = config.macros.importTiddlers.onBrowseChange;
15307 fileInput.onkeyup = config.macros.importTiddlers.onBrowseChange;
15308 wizard.setButtons([{caption: this.openLabel, tooltip: this.openPrompt, onClick: config.macros.importTiddlers.onOpen}]);
15311 config.macros.importTiddlers.getFeeds = function()
15314 var tagged = store.getTaggedTiddlers("systemServer","title");
15315 for(var t=0; t<tagged.length; t++) {
15316 var title = tagged[t].title;
15317 var serverType = store.getTiddlerSlice(title,"Type");
15319 serverType = "file";
15320 feeds[title] = {title: title,
15321 url: store.getTiddlerSlice(title,"URL"),
15322 workspace: store.getTiddlerSlice(title,"Workspace"),
15323 workspaceList: store.getTiddlerSlice(title,"WorkspaceList"),
15324 tiddlerFilter: store.getTiddlerSlice(title,"TiddlerFilter"),
15325 serverType: serverType,
15326 description: store.getTiddlerSlice(title,"Description")};
15331 config.macros.importTiddlers.onFeedChange = function(e)
15333 var wizard = new Wizard(this);
15334 var selTypes = wizard.getElement("selTypes");
15335 var fileInput = wizard.getElement("txtPath");
15336 var feeds = wizard.getValue("feeds");
15337 var f = feeds[this.value];
15339 selTypes.value = f.serverType;
15340 fileInput.value = f.url;
15341 this.selectedIndex = 0;
15342 wizard.setValue("feedName",f.serverType);
15343 wizard.setValue("feedHost",f.url);
15344 wizard.setValue("feedWorkspace",f.workspace);
15345 wizard.setValue("feedWorkspaceList",f.workspaceList);
15346 wizard.setValue("feedTiddlerFilter",f.tiddlerFilter);
15351 config.macros.importTiddlers.onBrowseChange = function(e)
15353 var wizard = new Wizard(this);
15354 var fileInput = wizard.getElement("txtPath");
15355 fileInput.value = "file://" + this.value;
15356 var serverType = wizard.getElement("selTypes");
15357 serverType.value = "file";
15361 config.macros.importTiddlers.onOpen = function(e)
15363 var wizard = new Wizard(this);
15364 var fileInput = wizard.getElement("txtPath");
15365 var url = fileInput.value;
15366 var serverType = wizard.getElement("selTypes").value;
15367 var adaptor = new config.adaptors[serverType];
15368 wizard.setValue("adaptor",adaptor);
15369 wizard.setValue("serverType",serverType);
15370 wizard.setValue("host",url);
15372 var ret = adaptor.openHost(url,context,wizard,config.macros.importTiddlers.onOpenHost);
15374 displayMessage(ret);
15375 wizard.setButtons([{caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}],config.macros.importTiddlers.statusOpenHost);
15379 config.macros.importTiddlers.onOpenHost = function(context,wizard)
15381 var adaptor = wizard.getValue("adaptor");
15382 if(context.status !== true)
15383 displayMessage("Error in importTiddlers.onOpenHost: " + context.statusText);
15384 var ret = adaptor.getWorkspaceList(context,wizard,config.macros.importTiddlers.onGetWorkspaceList);
15386 displayMessage(ret);
15387 wizard.setButtons([{caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}],config.macros.importTiddlers.statusGetWorkspaceList);
15390 config.macros.importTiddlers.onGetWorkspaceList = function(context,wizard)
15392 if(context.status !== true)
15393 displayMessage("Error in importTiddlers.onGetWorkspaceList: " + context.statusText);
15394 wizard.addStep(config.macros.importTiddlers.step2Title,config.macros.importTiddlers.step2Html);
15395 var s = wizard.getElement("selWorkspace");
15396 s.onchange = config.macros.importTiddlers.onWorkspaceChange;
15397 for(var t=0; t<context.workspaces.length; t++) {
15398 var e = createTiddlyElement(s,"option",null,null,context.workspaces[t].title);
15399 e.value = context.workspaces[t].title;
15401 var workspaceList = wizard.getValue("feedWorkspaceList");
15402 if(workspaceList) {
15403 var list = workspaceList.parseParams("workspace",null,false,true);
15404 for(var n=1; n<list.length; n++) {
15405 if(context.workspaces.findByField("title",list[n].value) == null) {
15406 e = createTiddlyElement(s,"option",null,null,list[n].value);
15407 e.value = list[n].value;
15411 var workspace = wizard.getValue("feedWorkspace");
15413 t = wizard.getElement("txtWorkspace");
15414 t.value = workspace;
15416 wizard.setButtons([{caption: config.macros.importTiddlers.openLabel, tooltip: config.macros.importTiddlers.openPrompt, onClick: config.macros.importTiddlers.onChooseWorkspace}]);
15419 config.macros.importTiddlers.onWorkspaceChange = function(e)
15421 var wizard = new Wizard(this);
15422 var t = wizard.getElement("txtWorkspace");
15423 t.value = this.value;
15424 this.selectedIndex = 0;
15428 config.macros.importTiddlers.onChooseWorkspace = function(e)
15430 var wizard = new Wizard(this);
15431 var adaptor = wizard.getValue("adaptor");
15432 var workspace = wizard.getElement("txtWorkspace").value;
15433 wizard.setValue("workspace",workspace);
15435 var ret = adaptor.openWorkspace(workspace,context,wizard,config.macros.importTiddlers.onOpenWorkspace);
15437 displayMessage(ret);
15438 wizard.setButtons([{caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}],config.macros.importTiddlers.statusOpenWorkspace);
15442 config.macros.importTiddlers.onOpenWorkspace = function(context,wizard)
15444 if(context.status !== true)
15445 displayMessage("Error in importTiddlers.onOpenWorkspace: " + context.statusText);
15446 var adaptor = wizard.getValue("adaptor");
15447 var ret = adaptor.getTiddlerList(context,wizard,config.macros.importTiddlers.onGetTiddlerList,wizard.getValue("feedTiddlerFilter"));
15449 displayMessage(ret);
15450 wizard.setButtons([{caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}],config.macros.importTiddlers.statusGetTiddlerList);
15453 config.macros.importTiddlers.onGetTiddlerList = function(context,wizard)
15455 if(context.status !== true)
15456 displayMessage("Error in importTiddlers.onGetTiddlerList: " + context.statusText);
15457 // Extract data for the listview
15458 var listedTiddlers = [];
15459 if(context.tiddlers) {
15460 for(var n=0; n<context.tiddlers.length; n++) {
15461 var tiddler = context.tiddlers[n];
15462 listedTiddlers.push({
15463 title: tiddler.title,
15464 modified: tiddler.modified,
15465 modifier: tiddler.modifier,
15466 text: tiddler.text ? wikifyPlainText(tiddler.text,100) : "",
15467 tags: tiddler.tags,
15468 size: tiddler.text ? tiddler.text.length : 0,
15473 listedTiddlers.sort(function(a,b) {return a.title < b.title ? -1 : (a.title == b.title ? 0 : +1);});
15474 // Display the listview
15475 wizard.addStep(config.macros.importTiddlers.step3Title,config.macros.importTiddlers.step3Html);
15476 var markList = wizard.getElement("markList");
15477 var listWrapper = document.createElement("div");
15478 markList.parentNode.insertBefore(listWrapper,markList);
15479 var listView = ListView.create(listWrapper,listedTiddlers,config.macros.importTiddlers.listViewTemplate);
15480 wizard.setValue("listView",listView);
15481 var txtSaveTiddler = wizard.getElement("txtSaveTiddler");
15482 txtSaveTiddler.value = config.macros.importTiddlers.generateSystemServerName(wizard);
15483 wizard.setButtons([
15484 {caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel},
15485 {caption: config.macros.importTiddlers.importLabel, tooltip: config.macros.importTiddlers.importPrompt, onClick: config.macros.importTiddlers.doImport}
15489 config.macros.importTiddlers.generateSystemServerName = function(wizard)
15491 var serverType = wizard.getValue("serverType");
15492 var host = wizard.getValue("host");
15493 var workspace = wizard.getValue("workspace");
15494 var pattern = config.macros.importTiddlers[workspace ? "systemServerNamePattern" : "systemServerNamePatternNoWorkspace"];
15495 return pattern.format([serverType,host,workspace]);
15498 config.macros.importTiddlers.saveServerTiddler = function(wizard)
15500 var txtSaveTiddler = wizard.getElement("txtSaveTiddler").value;
15501 if(store.tiddlerExists(txtSaveTiddler)) {
15502 if(!confirm(config.macros.importTiddlers.confirmOverwriteSaveTiddler.format([txtSaveTiddler])))
15504 store.suspendNotifications();
15505 store.removeTiddler(txtSaveTiddler);
15506 store.resumeNotifications();
15508 var serverType = wizard.getValue("serverType");
15509 var host = wizard.getValue("host");
15510 var workspace = wizard.getValue("workspace");
15511 var text = config.macros.importTiddlers.serverSaveTemplate.format([serverType,host,workspace]);
15512 store.saveTiddler(txtSaveTiddler,txtSaveTiddler,text,config.macros.importTiddlers.serverSaveModifier,new Date(),["systemServer"]);
15515 config.macros.importTiddlers.doImport = function(e)
15517 var wizard = new Wizard(this);
15518 if(wizard.getElement("chkSave").checked)
15519 config.macros.importTiddlers.saveServerTiddler(wizard);
15520 var chkSync = wizard.getElement("chkSync").checked;
15521 wizard.setValue("sync",chkSync);
15522 var listView = wizard.getValue("listView");
15523 var rowNames = ListView.getSelectedRows(listView);
15524 var adaptor = wizard.getValue("adaptor");
15525 var overwrite = new Array();
15527 for(t=0; t<rowNames.length; t++) {
15528 if(store.tiddlerExists(rowNames[t]))
15529 overwrite.push(rowNames[t]);
15531 if(overwrite.length > 0) {
15532 if(!confirm(config.macros.importTiddlers.confirmOverwriteText.format([overwrite.join(", ")])))
15535 wizard.addStep(config.macros.importTiddlers.step4Title.format([rowNames.length]),config.macros.importTiddlers.step4Html);
15536 for(t=0; t<rowNames.length; t++) {
15537 var link = document.createElement("div");
15538 createTiddlyLink(link,rowNames[t],true);
15539 var place = wizard.getElement("markReport");
15540 place.parentNode.insertBefore(link,place);
15542 wizard.setValue("remainingImports",rowNames.length);
15543 wizard.setButtons([
15544 {caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}
15545 ],config.macros.importTiddlers.statusDoingImport);
15546 for(t=0; t<rowNames.length; t++) {
15548 context.allowSynchronous = true;
15549 var inbound = adaptor.getTiddler(rowNames[t],context,wizard,config.macros.importTiddlers.onGetTiddler);
15554 config.macros.importTiddlers.onGetTiddler = function(context,wizard)
15556 if(!context.status)
15557 displayMessage("Error in importTiddlers.onGetTiddler: " + context.statusText);
15558 var tiddler = context.tiddler;
15559 store.suspendNotifications();
15560 store.saveTiddler(tiddler.title, tiddler.title, tiddler.text, tiddler.modifier, tiddler.modified, tiddler.tags, tiddler.fields, true, tiddler.created);
15561 if(!wizard.getValue("sync")) {
15562 store.setValue(tiddler.title,'server',null);
15564 store.resumeNotifications();
15565 if(!context.isSynchronous)
15566 store.notify(tiddler.title,true);
15567 var remainingImports = wizard.getValue("remainingImports")-1;
15568 wizard.setValue("remainingImports",remainingImports);
15569 if(remainingImports == 0) {
15570 if(context.isSynchronous) {
15574 wizard.setButtons([
15575 {caption: config.macros.importTiddlers.doneLabel, tooltip: config.macros.importTiddlers.donePrompt, onClick: config.macros.importTiddlers.onCancel}
15576 ],config.macros.importTiddlers.statusDoneImport);
15585 // Synchronisation handlers
15586 config.syncers = {};
15589 var currSync = null;
15592 config.macros.sync.handler = function(place,macroName,params,wikifier,paramString,tiddler)
15594 if(!wikifier.isStatic)
15595 this.startSync(place);
15598 config.macros.sync.startSync = function(place)
15601 config.macros.sync.cancelSync();
15603 currSync.syncList = this.getSyncableTiddlers();
15604 this.createSyncTasks();
15605 this.preProcessSyncableTiddlers();
15606 var wizard = new Wizard();
15607 currSync.wizard = wizard;
15608 wizard.createWizard(place,this.wizardTitle);
15609 wizard.addStep(this.step1Title,this.step1Html);
15610 var markList = wizard.getElement("markList");
15611 var listWrapper = document.createElement("div");
15612 markList.parentNode.insertBefore(listWrapper,markList);
15613 currSync.listView = ListView.create(listWrapper,currSync.syncList,this.listViewTemplate);
15614 this.processSyncableTiddlers();
15615 wizard.setButtons([
15616 {caption: this.syncLabel, tooltip: this.syncPrompt, onClick: this.doSync}
15620 config.macros.sync.getSyncableTiddlers = function ()
15623 store.forEachTiddler(function(title,tiddler) {
15625 syncItem.serverType = tiddler.getServerType();
15626 syncItem.serverHost = tiddler.fields['server.host'];
15627 syncItem.serverWorkspace = tiddler.fields['server.workspace'];
15628 syncItem.tiddler = tiddler;
15629 syncItem.title = tiddler.title;
15630 syncItem.isTouched = tiddler.isTouched();
15631 syncItem.selected = syncItem.isTouched;
15632 syncItem.syncStatus = config.macros.sync.syncStatusList[syncItem.isTouched ? "changedLocally" : "none"];
15633 syncItem.status = syncItem.syncStatus.text;
15634 if(syncItem.serverType && syncItem.serverHost)
15635 list.push(syncItem);
15637 list.sort(function(a,b) {return a.title < b.title ? -1 : (a.title == b.title ? 0 : +1);});
15641 config.macros.sync.preProcessSyncableTiddlers = function()
15643 for(var t=0; t<currSync.syncList.length; t++) {
15644 si = currSync.syncList[t];
15645 var ti = si.syncTask.syncMachine.generateTiddlerInfo(si.tiddler);
15646 si.serverUrl = ti.uri;
15650 config.macros.sync.processSyncableTiddlers = function()
15652 for(var t=0; t<currSync.syncList.length; t++) {
15653 si = currSync.syncList[t];
15654 si.rowElement.style.backgroundColor = si.syncStatus.color;
15658 config.macros.sync.createSyncTasks = function()
15660 currSync.syncTasks = [];
15661 for(var t=0; t<currSync.syncList.length; t++) {
15662 var si = currSync.syncList[t];
15664 for(var st=0; st<currSync.syncTasks.length; st++) {
15665 var cst = currSync.syncTasks[st];
15666 if(si.serverType == cst.serverType && si.serverHost == cst.serverHost && si.serverWorkspace == cst.serverWorkspace)
15670 si.syncTask = this.createSyncTask(si);
15671 currSync.syncTasks.push(si.syncTask);
15674 r.syncItems.push(si);
15679 config.macros.sync.createSyncTask = function(syncItem)
15682 st.serverType = syncItem.serverType;
15683 st.serverHost = syncItem.serverHost;
15684 st.serverWorkspace = syncItem.serverWorkspace;
15685 st.syncItems = [syncItem];
15686 st.syncMachine = new SyncMachine(st.serverType,{
15687 start: function() {
15688 return this.openHost(st.serverHost,"openWorkspace");
15690 openWorkspace: function() {
15691 return this.openWorkspace(st.serverWorkspace,"getTiddlerList");
15693 getTiddlerList: function() {
15694 return this.getTiddlerList("gotTiddlerList");
15696 gotTiddlerList: function(tiddlers) {
15697 for(var t=0; t<st.syncItems.length; t++) {
15698 var si = st.syncItems[t];
15699 var f = tiddlers.findByField("title",si.title);
15701 if(tiddlers[f].fields['server.page.revision'] > si.tiddler.fields['server.page.revision']) {
15702 si.syncStatus = config.macros.sync.syncStatusList[si.isTouched ? 'changedBoth' : 'changedServer'];
15705 si.syncStatus = config.macros.sync.syncStatusList.notFound;
15707 config.macros.sync.updateSyncStatus(si);
15710 getTiddler: function(title) {
15711 return this.getTiddler(title,"onGetTiddler");
15713 onGetTiddler: function(tiddler) {
15714 var syncItem = st.syncItems.findByField("title",tiddler.title);
15715 if(syncItem !== null) {
15716 syncItem = st.syncItems[syncItem];
15717 store.saveTiddler(tiddler.title, tiddler.title, tiddler.text, tiddler.modifier, tiddler.modified, tiddler.tags, tiddler.fields, true, tiddler.created);
15718 syncItem.syncStatus = config.macros.sync.syncStatusList.gotFromServer;
15719 config.macros.sync.updateSyncStatus(syncItem);
15722 putTiddler: function(tiddler) {
15723 return this.putTiddler(tiddler,"onPutTiddler");
15725 onPutTiddler: function(tiddler) {
15726 var syncItem = st.syncItems.findByField("title",tiddler.title);
15727 if(syncItem !== null) {
15728 syncItem = st.syncItems[syncItem];
15729 store.resetTiddler(tiddler.title);
15730 syncItem.syncStatus = config.macros.sync.syncStatusList.putToServer;
15731 config.macros.sync.updateSyncStatus(syncItem);
15735 st.syncMachine.go();
15739 config.macros.sync.updateSyncStatus = function(syncItem)
15741 var e = syncItem.colElements["status"];
15743 createTiddlyText(e,syncItem.syncStatus.text);
15744 syncItem.rowElement.style.backgroundColor = syncItem.syncStatus.color;
15747 config.macros.sync.doSync = function(e)
15749 var rowNames = ListView.getSelectedRows(currSync.listView);
15750 for(var t=0; t<currSync.syncList.length; t++) {
15751 var si = currSync.syncList[t];
15752 if(rowNames.indexOf(si.title) != -1) {
15753 config.macros.sync.doSyncItem(si);
15759 config.macros.sync.doSyncItem = function(syncItem)
15762 var sl = config.macros.sync.syncStatusList;
15763 switch(syncItem.syncStatus) {
15764 case sl.changedServer:
15765 r = syncItem.syncTask.syncMachine.go("getTiddler",syncItem.title);
15768 case sl.changedLocally:
15769 case sl.changedBoth:
15770 r = syncItem.syncTask.syncMachine.go("putTiddler",syncItem.tiddler);
15776 displayMessage("Error in doSyncItem: " + r);
15779 config.macros.sync.cancelSync = function()
15784 function SyncMachine(serverType,steps)
15786 this.serverType = serverType;
15787 this.adaptor = new config.adaptors[serverType];
15788 this.steps = steps;
15791 SyncMachine.prototype.go = function(step,varargs)
15795 var h = this.steps[step];
15799 for(var t=1; t<arguments.length; t++)
15800 a.push(arguments[t]);
15801 var r = h.apply(this,a);
15802 if(typeof r == "string")
15803 this.invokeError(r);
15807 SyncMachine.prototype.invokeError = function(message)
15809 if(this.steps.error)
15810 this.steps.error(message);
15813 SyncMachine.prototype.openHost = function(host,nextStep)
15816 return me.adaptor.openHost(host,null,null,function(context) {
15817 if(typeof context.status == "string")
15818 me.invokeError(context.status);
15824 SyncMachine.prototype.getWorkspaceList = function(nextStep)
15827 return me.adaptor.getWorkspaceList(null,null,function(context) {
15828 if(typeof context.status == "string")
15829 me.invokeError(context.status);
15831 me.go(nextStep,context.workspaces);
15835 SyncMachine.prototype.openWorkspace = function(workspace,nextStep)
15838 return me.adaptor.openWorkspace(workspace,null,null,function(context) {
15839 if(typeof context.status == "string")
15840 me.invokeError(context.status);
15846 SyncMachine.prototype.getTiddlerList = function(nextStep)
15849 return me.adaptor.getTiddlerList(null,null,function(context) {
15850 if(typeof context.status == "string")
15851 me.invokeError(context.status);
15853 me.go(nextStep,context.tiddlers);
15857 SyncMachine.prototype.generateTiddlerInfo = function(tiddler)
15859 return this.adaptor.generateTiddlerInfo(tiddler);
15862 SyncMachine.prototype.getTiddler = function(title,nextStep)
15865 return me.adaptor.getTiddler(title,null,null,function(context) {
15866 if(typeof context.status == "string")
15867 me.invokeError(context.status);
15869 me.go(nextStep,context.tiddler);
15873 SyncMachine.prototype.putTiddler = function(tiddler,nextStep)
15876 return me.adaptor.putTiddler(tiddler,null,null,function(context) {
15877 if(typeof context.status == "string")
15878 me.invokeError(context.status);
15880 me.go(nextStep,tiddler);
15885 //-- Manager UI for groups of tiddlers
15888 config.macros.plugins.handler = function(place,macroName,params,wikifier,paramString,tiddler)
15890 var wizard = new Wizard();
15891 wizard.createWizard(place,this.wizardTitle);
15892 wizard.addStep(this.step1Title,this.step1Html);
15893 var markList = wizard.getElement("markList");
15894 var listWrapper = document.createElement("div");
15895 markList.parentNode.insertBefore(listWrapper,markList);
15896 listWrapper.setAttribute("refresh","macro");
15897 listWrapper.setAttribute("macroName","plugins");
15898 listWrapper.setAttribute("params",paramString);
15899 this.refresh(listWrapper,paramString);
15902 config.macros.plugins.refresh = function(listWrapper,params)
15904 var wizard = new Wizard(listWrapper);
15905 var selectedRows = [];
15906 ListView.forEachSelector(listWrapper,function(e,rowName) {
15908 selectedRows.push(e.getAttribute("rowName"));
15910 removeChildren(listWrapper);
15911 params = params.parseParams("anon");
15912 var plugins = installedPlugins.slice(0);
15914 var configTiddlers = store.getTaggedTiddlers("systemConfig");
15915 for(t=0; t<configTiddlers.length; t++) {
15916 tiddler = configTiddlers[t];
15917 if(plugins.findByField("title",tiddler.title) == null) {
15918 p = getPluginInfo(tiddler);
15919 p.executed = false;
15920 p.log.splice(0,0,this.skippedText);
15924 for(t=0; t<plugins.length; t++) {
15926 p.size = p.tiddler.text ? p.tiddler.text.length : 0;
15927 p.forced = p.tiddler.isTagged("systemConfigForce");
15928 p.disabled = p.tiddler.isTagged("systemConfigDisable");
15929 p.Selected = selectedRows.indexOf(plugins[t].title) != -1;
15931 if(plugins.length == 0) {
15932 createTiddlyElement(listWrapper,"em",null,null,this.noPluginText);
15933 wizard.setButtons([]);
15935 var listView = ListView.create(listWrapper,plugins,this.listViewTemplate,this.onSelectCommand);
15936 wizard.setValue("listView",listView);
15937 wizard.setButtons([
15938 {caption: config.macros.plugins.removeLabel, tooltip: config.macros.plugins.removePrompt, onClick: config.macros.plugins.doRemoveTag},
15939 {caption: config.macros.plugins.deleteLabel, tooltip: config.macros.plugins.deletePrompt, onClick: config.macros.plugins.doDelete}
15944 config.macros.plugins.doRemoveTag = function(e)
15946 var wizard = new Wizard(this);
15947 var listView = wizard.getValue("listView");
15948 var rowNames = ListView.getSelectedRows(listView);
15949 if(rowNames.length == 0) {
15950 alert(config.messages.nothingSelected);
15952 for(var t=0; t<rowNames.length; t++)
15953 store.setTiddlerTag(rowNames[t],false,"systemConfig");
15957 config.macros.plugins.doDelete = function(e)
15959 var wizard = new Wizard(this);
15960 var listView = wizard.getValue("listView");
15961 var rowNames = ListView.getSelectedRows(listView);
15962 if(rowNames.length == 0) {
15963 alert(config.messages.nothingSelected);
15965 if(confirm(config.macros.plugins.confirmDeleteText.format([rowNames.join(", ")]))) {
15966 for(t=0; t<rowNames.length; t++) {
15967 store.removeTiddler(rowNames[t]);
15968 story.closeTiddler(rowNames[t],true);
15978 function getMessageDiv()
15980 var msgArea = document.getElementById("messageArea");
15983 if(!msgArea.hasChildNodes())
15984 createTiddlyButton(createTiddlyElement(msgArea,"div",null,"messageToolbar"),
15985 config.messages.messageClose.text,
15986 config.messages.messageClose.tooltip,
15988 msgArea.style.display = "block";
15989 return createTiddlyElement(msgArea,"div");
15992 function displayMessage(text,linkText)
15994 var e = getMessageDiv();
16000 var link = createTiddlyElement(e,"a",null,null,text);
16001 link.href = linkText;
16002 link.target = "_blank";
16004 e.appendChild(document.createTextNode(text));
16008 function clearMessage()
16010 var msgArea = document.getElementById("messageArea");
16012 removeChildren(msgArea);
16013 msgArea.style.display = "none";
16019 //-- Refresh mechanism
16022 config.refreshers = {
16023 link: function(e,changeList)
16025 var title = e.getAttribute("tiddlyLink");
16026 refreshTiddlyLink(e,title);
16030 tiddler: function(e,changeList)
16032 var title = e.getAttribute("tiddler");
16033 var template = e.getAttribute("template");
16034 if(changeList && changeList.indexOf(title) != -1 && !story.isDirty(title))
16035 story.refreshTiddler(title,template,true);
16037 refreshElements(e,changeList);
16041 content: function(e,changeList)
16043 var title = e.getAttribute("tiddler");
16044 var force = e.getAttribute("force");
16045 if(force != null || changeList == null || changeList.indexOf(title) != -1) {
16047 wikify(store.getTiddlerText(title,title),e,null);
16053 macro: function(e,changeList)
16055 var macro = e.getAttribute("macroName");
16056 var params = e.getAttribute("params");
16058 macro = config.macros[macro];
16059 if(macro && macro.refresh)
16060 macro.refresh(e,params);
16065 function refreshElements(root,changeList)
16067 var nodes = root.childNodes;
16068 for(var c=0; c<nodes.length; c++) {
16069 var e = nodes[c], type = null;
16070 if(e.getAttribute && (e.tagName ? e.tagName != "IFRAME" : true))
16071 type = e.getAttribute("refresh");
16072 var refresher = config.refreshers[type];
16073 var refreshed = false;
16074 if(refresher != undefined)
16075 refreshed = refresher(e,changeList);
16076 if(e.hasChildNodes() && !refreshed)
16077 refreshElements(e,changeList);
16081 function applyHtmlMacros(root,tiddler)
16083 var e = root.firstChild;
16085 var nextChild = e.nextSibling;
16086 if(e.getAttribute) {
16087 var macro = e.getAttribute("macro");
16090 var p = macro.indexOf(" ");
16092 params = macro.substr(p+1);
16093 macro = macro.substr(0,p);
16095 invokeMacro(e,macro,params,null,tiddler);
16098 if(e.hasChildNodes())
16099 applyHtmlMacros(e,tiddler);
16104 function refreshPageTemplate(title)
16106 var stash = createTiddlyElement(document.body,"div");
16107 stash.style.display = "none";
16108 var display = document.getElementById("tiddlerDisplay");
16111 nodes = display.childNodes;
16112 for(t=nodes.length-1; t>=0; t--)
16113 stash.appendChild(nodes[t]);
16115 var wrapper = document.getElementById("contentWrapper");
16117 title = "PageTemplate";
16118 var html = store.getRecursiveTiddlerText(title,null,10);
16119 wrapper.innerHTML = html;
16120 applyHtmlMacros(wrapper);
16121 refreshElements(wrapper);
16122 display = document.getElementById("tiddlerDisplay");
16123 removeChildren(display);
16125 display = createTiddlyElement(wrapper,"div","tiddlerDisplay");
16126 nodes = stash.childNodes;
16127 for(t=nodes.length-1; t>=0; t--)
16128 display.appendChild(nodes[t]);
16132 function refreshDisplay(hint)
16134 if(typeof hint == "string")
16136 var e = document.getElementById("contentWrapper");
16137 refreshElements(e,hint);
16138 if(backstage.isPanelVisible()) {
16139 e = document.getElementById("backstage");
16140 refreshElements(e,hint);
16144 function refreshPageTitle()
16146 document.title = getPageTitle();
16149 function getPageTitle()
16151 var st = wikifyPlain("SiteTitle");
16152 var ss = wikifyPlain("SiteSubtitle");
16153 return st + ((st == "" || ss == "") ? "" : " - ") + ss;
16156 function refreshStyles(title,doc)
16160 setStylesheet(title == null ? "" : store.getRecursiveTiddlerText(title,"",10),title,doc);
16163 function refreshColorPalette(title)
16169 function refreshAll()
16171 refreshPageTemplate();
16173 refreshStyles("StyleSheetLayout");
16174 refreshStyles("StyleSheetColors");
16175 refreshStyles("StyleSheet");
16176 refreshStyles("StyleSheetPrint");
16180 //-- Options cookie stuff
16183 config.optionHandlers = {
16185 get: function(name) {return encodeCookie(config.options[name].toString());},
16186 set: function(name,value) {config.options[name] = decodeCookie(value);}
16189 get: function(name) {return config.options[name] ? "true" : "false";},
16190 set: function(name,value) {config.options[name] = value == "true";}
16194 function loadOptionsCookie()
16198 var cookies = document.cookie.split(";");
16199 for(var c=0; c<cookies.length; c++) {
16200 var p = cookies[c].indexOf("=");
16202 var name = cookies[c].substr(0,p).trim();
16203 var value = cookies[c].substr(p+1).trim();
16204 var optType = name.substr(0,3);
16205 if(config.optionHandlers[optType] && config.optionHandlers[optType].set)
16206 config.optionHandlers[optType].set(name,value);
16211 function saveOptionCookie(name)
16215 var c = name + "=";
16216 var optType = name.substr(0,3);
16217 if(config.optionHandlers[optType] && config.optionHandlers[optType].get)
16218 c += config.optionHandlers[optType].get(name);
16219 c += "; expires=Fri, 1 Jan 2038 12:00:00 UTC; path=/";
16220 document.cookie = c;
16223 function encodeCookie(s)
16225 return escape(manualConvertUnicodeToUTF8(s));
16228 function decodeCookie(s)
16231 var re = /&#[0-9]{1,5};/g;
16232 return s.replace(re,function($0) {return String.fromCharCode(eval($0.replace(/[&#;]/g,"")));});
16239 var saveUsingSafari = false;
16241 var startSaveArea = '<div id="' + 'storeArea">'; // Split up into two so that indexOf() of this source doesn't find it
16242 var endSaveArea = '</d' + 'iv>';
16244 // If there are unsaved changes, force the user to confirm before exitting
16245 function confirmExit()
16247 hadConfirmExit = true;
16248 if((store && store.isDirty && store.isDirty()) || (story && story.areAnyDirty && story.areAnyDirty()))
16249 return config.messages.confirmExit;
16252 // Give the user a chance to save changes before exitting
16253 function checkUnsavedChanges()
16255 if(store && store.isDirty && store.isDirty() && window.hadConfirmExit === false) {
16256 if(confirm(config.messages.unsavedChangesWarning))
16261 function updateLanguageAttribute(s)
16263 if(config.locale) {
16264 var mRE = /(<html(?:.*?)?)(?: xml:lang\="([a-z]+)")?(?: lang\="([a-z]+)")?>/;
16265 var m = mRE.exec(s);
16269 t += ' xml:lang="' + config.locale + '"';
16271 t += ' lang="' + config.locale + '"';
16273 s = s.substr(0,m.index) + t + s.substr(m.index+m[0].length);
16279 function updateMarkupBlock(s,blockName,tiddlerName)
16281 return s.replaceChunk(
16282 "<!--%0-START-->".format([blockName]),
16283 "<!--%0-END-->".format([blockName]),
16284 "\n" + store.getRecursiveTiddlerText(tiddlerName,"") + "\n");
16287 function updateOriginal(original,posDiv)
16290 posDiv = locateStoreArea(original);
16292 alert(config.messages.invalidFileError.format([localPath]));
16295 var revised = original.substr(0,posDiv[0] + startSaveArea.length) + "\n" +
16296 convertUnicodeToUTF8(store.allTiddlersAsHtml()) + "\n" +
16297 original.substr(posDiv[1]);
16298 var newSiteTitle = convertUnicodeToUTF8(getPageTitle()).htmlEncode();
16299 revised = revised.replaceChunk("<title"+">","</title"+">"," " + newSiteTitle + " ");
16300 revised = updateLanguageAttribute(revised);
16301 revised = updateMarkupBlock(revised,"PRE-HEAD","MarkupPreHead");
16302 revised = updateMarkupBlock(revised,"POST-HEAD","MarkupPostHead");
16303 revised = updateMarkupBlock(revised,"PRE-BODY","MarkupPreBody");
16304 revised = updateMarkupBlock(revised,"POST-SCRIPT","MarkupPostBody");
16308 function locateStoreArea(original)
16310 // Locate the storeArea div's
16311 var posOpeningDiv = original.indexOf(startSaveArea);
16312 var limitClosingDiv = original.indexOf("<"+"!--POST-STOREAREA--"+">");
16313 if(limitClosingDiv == -1)
16314 limitClosingDiv = original.indexOf("<"+"!--POST-BODY-START--"+">");
16315 var posClosingDiv = original.lastIndexOf(endSaveArea,limitClosingDiv == -1 ? original.length : limitClosingDiv);
16316 return (posOpeningDiv != -1 && posClosingDiv != -1) ? [posOpeningDiv,posClosingDiv] : null;
16319 function autoSaveChanges(onlyIfDirty,tiddlers)
16321 if(config.options.chkAutoSave)
16322 saveChanges(onlyIfDirty,tiddlers);
16325 // Save this tiddlywiki with the pending changes
16326 function saveChanges(onlyIfDirty,tiddlers)
16328 if(onlyIfDirty && !store.isDirty())
16331 // Get the URL of the document
16332 var originalPath = document.location.toString();
16333 // Check we were loaded from a file URL
16334 if(originalPath.substr(0,5) != "file:") {
16335 alert(config.messages.notFileUrlError);
16336 if(store.tiddlerExists(config.messages.saveInstructions))
16337 story.displayTiddler(null,config.messages.saveInstructions);
16340 var localPath = getLocalPath(originalPath);
16341 // Load the original file
16342 var original = loadFile(localPath);
16343 if(original == null) {
16344 alert(config.messages.cantSaveError);
16345 if(store.tiddlerExists(config.messages.saveInstructions))
16346 story.displayTiddler(null,config.messages.saveInstructions);
16349 // Locate the storeArea div's
16350 var posDiv = locateStoreArea(original);
16352 alert(config.messages.invalidFileError.format([localPath]));
16355 saveBackup(localPath,original);
16356 saveRss(localPath);
16357 saveEmpty(localPath,original,posDiv);
16358 saveMain(localPath,original,posDiv);
16361 function saveBackup(localPath,original)
16364 if(config.options.chkSaveBackups) {
16365 var backupPath = getBackupPath(localPath);
16366 var backup = config.browser.isIE ? ieCopyFile(backupPath,localPath) : saveFile(backupPath,original);
16368 displayMessage(config.messages.backupSaved,"file://" + backupPath);
16370 alert(config.messages.backupFailed);
16374 function saveRss(localPath)
16376 if(config.options.chkGenerateAnRssFeed) {
16377 var rssPath = localPath.substr(0,localPath.lastIndexOf(".")) + ".xml";
16378 var rssSave = saveFile(rssPath,convertUnicodeToUTF8(generateRss()));
16380 displayMessage(config.messages.rssSaved,"file://" + rssPath);
16382 alert(config.messages.rssFailed);
16386 function saveEmpty(localPath,original,posDiv)
16388 if(config.options.chkSaveEmptyTemplate) {
16390 if((p = localPath.lastIndexOf("/")) != -1)
16391 emptyPath = localPath.substr(0,p) + "/empty.html";
16392 else if((p = localPath.lastIndexOf("\\")) != -1)
16393 emptyPath = localPath.substr(0,p) + "\\empty.html";
16395 emptyPath = localPath + ".empty.html";
16396 var empty = original.substr(0,posDiv[0] + startSaveArea.length) + original.substr(posDiv[1]);
16397 var emptySave = saveFile(emptyPath,empty);
16399 displayMessage(config.messages.emptySaved,"file://" + emptyPath);
16401 alert(config.messages.emptyFailed);
16405 function saveMain(localPath,original,posDiv)
16409 var revised = updateOriginal(original,posDiv);
16410 save = saveFile(localPath,revised);
16415 displayMessage(config.messages.mainSaved,"file://" + localPath);
16416 store.setDirty(false);
16418 alert(config.messages.mainFailed);
16422 function getLocalPath(origPath)
16424 var originalPath = convertUriToUTF8(origPath,config.options.txtFileSystemCharSet);
16425 // Remove any location or query part of the URL
16426 var argPos = originalPath.indexOf("?");
16428 originalPath = originalPath.substr(0,argPos);
16429 var hashPos = originalPath.indexOf("#");
16431 originalPath = originalPath.substr(0,hashPos);
16432 // Convert file://localhost/ to file:///
16433 if(originalPath.indexOf("file://localhost/") == 0)
16434 originalPath = "file://" + originalPath.substr(16);
16435 // Convert to a native file format
16437 if(originalPath.charAt(9) == ":") // pc local file
16438 localPath = unescape(originalPath.substr(8)).replace(new RegExp("/","g"),"\\");
16439 else if(originalPath.indexOf("file://///") == 0) // FireFox pc network file
16440 localPath = "\\\\" + unescape(originalPath.substr(10)).replace(new RegExp("/","g"),"\\");
16441 else if(originalPath.indexOf("file:///") == 0) // mac/unix local file
16442 localPath = unescape(originalPath.substr(7));
16443 else if(originalPath.indexOf("file:/") == 0) // mac/unix local file
16444 localPath = unescape(originalPath.substr(5));
16445 else // pc network file
16446 localPath = "\\\\" + unescape(originalPath.substr(7)).replace(new RegExp("/","g"),"\\");
16450 function getBackupPath(localPath)
16452 var backSlash = true;
16453 var dirPathPos = localPath.lastIndexOf("\\");
16454 if(dirPathPos == -1) {
16455 dirPathPos = localPath.lastIndexOf("/");
16458 var backupFolder = config.options.txtBackupFolder;
16459 if(!backupFolder || backupFolder == "")
16460 backupFolder = ".";
16461 var backupPath = localPath.substr(0,dirPathPos) + (backSlash ? "\\" : "/") + backupFolder + localPath.substr(dirPathPos);
16462 backupPath = backupPath.substr(0,backupPath.lastIndexOf(".")) + "." + (new Date()).convertToYYYYMMDDHHMMSSMMM() + ".html";
16466 function generateRss()
16469 var d = new Date();
16470 var u = store.getTiddlerText("SiteUrl");
16471 // Assemble the header
16472 s.push("<" + "?xml version=\"1.0\"?" + ">");
16473 s.push("<rss version=\"2.0\">");
16474 s.push("<channel>");
16475 s.push("<title" + ">" + wikifyPlain("SiteTitle").htmlEncode() + "</title" + ">");
16477 s.push("<link>" + u.htmlEncode() + "</link>");
16478 s.push("<description>" + wikifyPlain("SiteSubtitle").htmlEncode() + "</description>");
16479 s.push("<language>en-us</language>");
16480 s.push("<copyright>Copyright " + d.getFullYear() + " " + config.options.txtUserName.htmlEncode() + "</copyright>");
16481 s.push("<pubDate>" + d.toGMTString() + "</pubDate>");
16482 s.push("<lastBuildDate>" + d.toGMTString() + "</lastBuildDate>");
16483 s.push("<docs>http://blogs.law.harvard.edu/tech/rss</docs>");
16484 s.push("<generator>TiddlyWiki " + version.major + "." + version.minor + "." + version.revision + "</generator>");
16486 var tiddlers = store.getTiddlers("modified","excludeLists");
16487 var n = config.numRssItems > tiddlers.length ? 0 : tiddlers.length-config.numRssItems;
16488 for (var t=tiddlers.length-1; t>=n; t--)
16489 s.push(tiddlers[t].saveToRss(u));
16491 s.push("</channel>");
16494 return s.join("\n");
16498 //-- Filesystem code
16501 function convertUTF8ToUnicode(u)
16503 if(window.netscape == undefined)
16504 return manualConvertUTF8ToUnicode(u);
16506 return mozConvertUTF8ToUnicode(u);
16509 function manualConvertUTF8ToUnicode(utf)
16516 while(src < utf.length) {
16517 b1 = utf.charCodeAt(src++);
16520 } else if(b1 < 0xE0) {
16521 b2 = utf.charCodeAt(src++);
16522 c = String.fromCharCode(((b1 & 0x1F) << 6) | (b2 & 0x3F));
16523 uni = uni.substring(0,dst++).concat(c,utf.substr(src));
16525 b2 = utf.charCodeAt(src++);
16526 b3 = utf.charCodeAt(src++);
16527 c = String.fromCharCode(((b1 & 0xF) << 12) | ((b2 & 0x3F) << 6) | (b3 & 0x3F));
16528 uni = uni.substring(0,dst++).concat(c,utf.substr(src));
16534 function mozConvertUTF8ToUnicode(u)
16537 netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
16538 var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
16539 converter.charset = "UTF-8";
16541 return manualConvertUTF8ToUnicode(u);
16543 var s = converter.ConvertToUnicode(u);
16544 var fin = converter.Finish();
16545 return (fin.length > 0) ? s+fin : s;
16548 function convertUnicodeToUTF8(s)
16550 if(window.netscape == undefined)
16551 return manualConvertUnicodeToUTF8(s);
16553 return mozConvertUnicodeToUTF8(s);
16556 function manualConvertUnicodeToUTF8(s)
16558 var re = /[^\u0000-\u007F]/g ;
16559 return s.replace(re,function($0) {return "&#" + $0.charCodeAt(0).toString() + ";";});
16562 function mozConvertUnicodeToUTF8(s)
16565 netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
16566 var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
16567 converter.charset = "UTF-8";
16569 return manualConvertUnicodeToUTF8(s);
16571 var u = converter.ConvertFromUnicode(s);
16572 var fin = converter.Finish();
16579 function convertUriToUTF8(uri,charSet)
16581 if(window.netscape == undefined || charSet == undefined || charSet == "")
16584 netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
16585 var converter = Components.classes["@mozilla.org/intl/utf8converterservice;1"].getService(Components.interfaces.nsIUTF8ConverterService);
16589 return converter.convertURISpecToUTF8(uri,charSet);
16592 function saveFile(fileUrl,content)
16596 r = mozillaSaveFile(fileUrl,content);
16598 r = ieSaveFile(fileUrl,content);
16600 r = javaSaveFile(fileUrl,content);
16604 function loadFile(fileUrl)
16607 if((r == null) || (r == false))
16608 r = mozillaLoadFile(fileUrl);
16609 if((r == null) || (r == false))
16610 r = ieLoadFile(fileUrl);
16611 if((r == null) || (r == false))
16612 r = javaLoadFile(fileUrl);
16616 // Returns null if it can't do it, false if there's an error, true if it saved OK
16617 function ieSaveFile(filePath,content)
16620 var fso = new ActiveXObject("Scripting.FileSystemObject");
16624 var file = fso.OpenTextFile(filePath,2,-1,0);
16625 file.Write(content);
16630 // Returns null if it can't do it, false if there's an error, or a string of the content if successful
16631 function ieLoadFile(filePath)
16634 var fso = new ActiveXObject("Scripting.FileSystemObject");
16635 var file = fso.OpenTextFile(filePath,1);
16636 var content = file.ReadAll();
16644 function ieCopyFile(dest,source)
16647 var fso = new ActiveXObject("Scripting.FileSystemObject");
16648 fso.GetFile(source).Copy(dest);
16655 // Returns null if it can't do it, false if there's an error, true if it saved OK
16656 function mozillaSaveFile(filePath,content)
16658 if(window.Components) {
16660 netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
16661 var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
16662 file.initWithPath(filePath);
16664 file.create(0,0664);
16665 var out = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream);
16666 out.init(file,0x20|0x02,00004,null);
16667 out.write(content,content.length);
16678 // Returns null if it can't do it, false if there's an error, or a string of the content if successful
16679 function mozillaLoadFile(filePath)
16681 if(window.Components) {
16683 netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
16684 var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
16685 file.initWithPath(filePath);
16688 var inputStream = Components.classes["@mozilla.org/network/file-input-stream;1"].createInstance(Components.interfaces.nsIFileInputStream);
16689 inputStream.init(file,0x01,00004,null);
16690 var sInputStream = Components.classes["@mozilla.org/scriptableinputstream;1"].createInstance(Components.interfaces.nsIScriptableInputStream);
16691 sInputStream.init(inputStream);
16692 return sInputStream.read(sInputStream.available());
16700 function javaUrlToFilename(url)
16702 var f = "//localhost";
16703 if(url.indexOf(f) == 0)
16704 return url.substring(f.length);
16705 var i = url.indexOf(":");
16707 return url.substring(i-1);
16711 function javaSaveFile(filePath,content)
16714 if(document.applets["TiddlySaver"])
16715 return document.applets["TiddlySaver"].saveFile(javaUrlToFilename(filePath),"UTF-8",content);
16719 var s = new java.io.PrintStream(new java.io.FileOutputStream(javaUrlToFilename(filePath)));
16728 function javaLoadFile(filePath)
16731 if(document.applets["TiddlySaver"])
16732 return String(document.applets["TiddlySaver"].loadFile(javaUrlToFilename(filePath),"UTF-8"));
16737 var r = new java.io.BufferedReader(new java.io.FileReader(javaUrlToFilename(filePath)));
16739 while((line = r.readLine()) != null)
16740 content.push(new String(line));
16745 return content.join("\n");
16749 //-- Server adaptor for talking to static files
16752 function FileAdaptor()
16759 FileAdaptor.NotLoadedError = "TiddlyWiki file has not been loaded";
16760 FileAdaptor.serverType = 'file';
16762 // Open the specified host/server
16763 FileAdaptor.prototype.openHost = function(host,context,userParams,callback)
16768 context.adaptor = this;
16769 context.callback = callback;
16770 context.userParams = userParams;
16771 var ret = loadRemoteFile(host,FileAdaptor.openHostCallback,context);
16772 return typeof(ret) == "string" ? ret : true;
16775 FileAdaptor.openHostCallback = function(status,context,responseText,url,xhr)
16777 var adaptor = context.adaptor;
16778 context.status = status;
16780 context.statusText = "Error reading file: " + xhr.statusText;
16782 // Load the content into a TiddlyWiki() object
16783 adaptor.store = new TiddlyWiki();
16784 if(!adaptor.store.importTiddlyWiki(responseText))
16785 context.statusText = config.messages.invalidFileError.format([url]);
16787 context.callback(context,context.userParams);
16790 // Gets the list of workspaces on a given server
16791 FileAdaptor.prototype.getWorkspaceList = function(context,userParams,callback)
16795 context.workspaces = [{title:"(default)"}];
16796 context.status = true;
16797 window.setTimeout(function() {callback(context,userParams);},10);
16801 // Open the specified workspace
16802 FileAdaptor.prototype.openWorkspace = function(workspace,context,userParams,callback)
16806 context.status = true;
16807 window.setTimeout(function() {callback(context,userParams);},10);
16811 // Gets the list of tiddlers within a given workspace
16812 FileAdaptor.prototype.getTiddlerList = function(context,userParams,callback)
16815 return FileAdaptor.NotLoadedError;
16818 context.tiddlers = [];
16819 this.store.forEachTiddler(function(title,tiddler)
16821 var t = new Tiddler(title);
16822 t.text = tiddler.text;
16823 t.modified = tiddler.modified;
16824 t.modifier = tiddler.modifier;
16825 t.fields['server.page.revision'] = tiddler.modified.convertToYYYYMMDDHHMM();
16826 t.tags = tiddler.tags;
16827 context.tiddlers.push(t);
16829 context.status = true;
16830 window.setTimeout(function() {callback(context,userParams);},10);
16834 FileAdaptor.prototype.generateTiddlerInfo = function(tiddler)
16837 info.uri = tiddler.fields['server.host'] + "#" + tiddler.title;
16841 // Retrieves a tiddler from a given workspace on a given server
16842 FileAdaptor.prototype.getTiddler = function(title,context,userParams,callback)
16845 return FileAdaptor.NotLoadedError;
16848 context.tiddler = this.store.fetchTiddler(title);
16849 if(context.tiddler) {
16850 context.tiddler.fields['server.type'] = FileAdaptor.serverType;
16851 context.tiddler.fields['server.host'] = this.host;
16852 context.tiddler.fields['server.page.revision'] = context.tiddler.modified.convertToYYYYMMDDHHMM();
16854 context.status = true;
16855 if(context.allowSynchronous) {
16856 context.isSynchronous = true;
16857 callback(context,userParams);
16859 window.setTimeout(function() {callback(context,userParams);},10);
16864 FileAdaptor.prototype.close = function()
16870 config.adaptors[FileAdaptor.serverType] = FileAdaptor;
16873 //-- Remote HTTP requests
16876 function loadRemoteFile(url,callback,params)
16878 return doHttp("GET",url,null,null,null,null,callback,params,null);
16881 // HTTP status codes
16884 ContentCreated: 201,
16889 MethodNotAllowed: 405
16892 function doHttp(type,url,data,contentType,username,password,callback,params,headers)
16894 // Get an xhr object
16895 var x = getXMLHttpRequest();
16897 return "Can't create XMLHttpRequest object";
16898 // Install callback
16899 x.onreadystatechange = function() {
16900 if (x.readyState == 4 && callback && (x.status !== undefined)) {
16901 if([0, httpStatus.OK, httpStatus.ContentCreated, httpStatus.NoContent].contains(x.status))
16902 callback(true,params,x.responseText,url,x);
16904 callback(false,params,null,url,x);
16905 x.onreadystatechange = function(){};
16910 if(window.Components && window.netscape && window.netscape.security && document.location.protocol.indexOf("http") == -1)
16911 window.netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
16913 url = url + (url.indexOf("?") < 0 ? "?" : "&") + "nocache=" + Math.random();
16914 x.open(type,url,true,username,password);
16916 x.setRequestHeader("Content-Type", contentType ? contentType : "application/x-www-form-urlencoded");
16917 if (x.overrideMimeType)
16918 x.setRequestHeader("Connection", "close");
16921 x.setRequestHeader(n,headers[n]);
16923 x.setRequestHeader("X-Requested-With", "TiddlyWiki " + version.major + "." + version.minor + "." + version.revision + (version.beta ? " (beta " + version.beta + ")" : ""));
16926 return exceptionText(ex);
16931 function getXMLHttpRequest()
16934 var x = new XMLHttpRequest(); // Modern
16937 x = new ActiveXObject("Msxml2.XMLHTTP"); // IE 6
16946 //-- TiddlyWiki-specific utility functions
16949 function createTiddlyButton(theParent,theText,theTooltip,theAction,theClass,theId,theAccessKey)
16951 var theButton = document.createElement("a");
16953 theButton.onclick = theAction;
16954 theButton.setAttribute("href","javascript:;");
16957 theButton.setAttribute("title",theTooltip);
16959 theButton.appendChild(document.createTextNode(theText));
16961 theButton.className = theClass;
16963 theButton.className = "button";
16965 theButton.id = theId;
16967 theParent.appendChild(theButton);
16969 theButton.setAttribute("accessKey",theAccessKey);
16973 function createTiddlyLink(place,title,includeText,theClass,isStatic,linkedFromTiddler,noToggle)
16975 var text = includeText ? title : null;
16976 var i = getTiddlyLinkInfo(title,theClass);
16977 var btn = isStatic ? createExternalLink(place,store.getTiddlerText("SiteUrl",null) + "#" + title) : createTiddlyButton(place,text,i.subTitle,onClickTiddlerLink,i.classes);
16978 btn.setAttribute("refresh","link");
16979 btn.setAttribute("tiddlyLink",title);
16981 btn.setAttribute("noToggle","true");
16982 if(linkedFromTiddler) {
16983 var fields = linkedFromTiddler.getInheritedFields();
16985 btn.setAttribute("tiddlyFields",fields);
16990 function refreshTiddlyLink(e,title)
16992 var i = getTiddlyLinkInfo(title,e.className);
16993 e.className = i.classes;
16994 e.title = i.subTitle;
16997 function getTiddlyLinkInfo(title,currClasses)
16999 var classes = currClasses ? currClasses.split(" ") : [];
17000 classes.pushUnique("tiddlyLink");
17001 var tiddler = store.fetchTiddler(title);
17004 subTitle = tiddler.getSubtitle();
17005 classes.pushUnique("tiddlyLinkExisting");
17006 classes.remove("tiddlyLinkNonExisting");
17007 classes.remove("shadow");
17009 classes.remove("tiddlyLinkExisting");
17010 classes.pushUnique("tiddlyLinkNonExisting");
17011 if(store.isShadowTiddler(title)) {
17012 subTitle = config.messages.shadowedTiddlerToolTip.format([title]);
17013 classes.pushUnique("shadow");
17015 subTitle = config.messages.undefinedTiddlerToolTip.format([title]);
17016 classes.remove("shadow");
17019 if(config.annotations[title])
17020 subTitle = config.annotations[title];
17021 return {classes: classes.join(" "),subTitle: subTitle};
17024 function createExternalLink(place,url)
17026 var theLink = document.createElement("a");
17027 theLink.className = "externalLink";
17028 theLink.href = url;
17029 theLink.title = config.messages.externalLinkTooltip.format([url]);
17030 if(config.options.chkOpenInNewWindow)
17031 theLink.target = "_blank";
17032 place.appendChild(theLink);
17036 // Event handler for clicking on a tiddly link
17037 function onClickTiddlerLink(e)
17039 if(!e) e = window.event;
17040 var theTarget = resolveTarget(e);
17041 var theLink = theTarget;
17044 var noToggle = null;
17046 title = theLink.getAttribute("tiddlyLink");
17047 fields = theLink.getAttribute("tiddlyFields");
17048 noToggle = theLink.getAttribute("noToggle");
17049 theLink = theLink.parentNode;
17050 } while(title == null && theLink != null);
17051 if(!fields && !store.isShadowTiddler(title))
17052 fields = String.encodeHashMap(config.defaultCustomFields);
17054 var toggling = e.metaKey || e.ctrlKey;
17055 if(config.options.chkToggleLinks)
17056 toggling = !toggling;
17059 story.displayTiddler(theTarget,title,null,true,null,fields,toggling);
17065 // Create a button for a tag with a popup listing all the tiddlers that it tags
17066 function createTagButton(place,tag,excludeTiddler)
17068 var theTag = createTiddlyButton(place,tag,config.views.wikified.tag.tooltip.format([tag]),onClickTag);
17069 theTag.setAttribute("tag",tag);
17071 theTag.setAttribute("tiddler",excludeTiddler);
17075 // Event handler for clicking on a tiddler tag
17076 function onClickTag(e)
17078 if(!e) var e = window.event;
17079 var theTarget = resolveTarget(e);
17080 var popup = Popup.create(this);
17081 var tag = this.getAttribute("tag");
17082 var title = this.getAttribute("tiddler");
17084 var tagged = store.getTaggedTiddlers(tag);
17087 for(r=0;r<tagged.length;r++) {
17088 if(tagged[r].title != title)
17089 titles.push(tagged[r].title);
17091 var lingo = config.views.wikified.tag;
17092 if(titles.length > 0) {
17093 var openAll = createTiddlyButton(createTiddlyElement(popup,"li"),lingo.openAllText.format([tag]),lingo.openAllTooltip,onClickTagOpenAll);
17094 openAll.setAttribute("tag",tag);
17095 createTiddlyElement(createTiddlyElement(popup,"li",null,"listBreak"),"div");
17096 for(r=0; r<titles.length; r++) {
17097 createTiddlyLink(createTiddlyElement(popup,"li"),titles[r],true);
17100 createTiddlyText(createTiddlyElement(popup,"li",null,"disabled"),lingo.popupNone.format([tag]));
17102 createTiddlyElement(createTiddlyElement(popup,"li",null,"listBreak"),"div");
17103 var h = createTiddlyLink(createTiddlyElement(popup,"li"),tag,false);
17104 createTiddlyText(h,lingo.openTag.format([tag]));
17107 e.cancelBubble = true;
17108 if(e.stopPropagation) e.stopPropagation();
17112 // Event handler for 'open all' on a tiddler popup
17113 function onClickTagOpenAll(e)
17115 if(!e) var e = window.event;
17116 var tag = this.getAttribute("tag");
17117 var tagged = store.getTaggedTiddlers(tag);
17119 for(var t=0; t<tagged.length; t++)
17120 titles.push(tagged[t].title);
17121 story.displayTiddlers(this,titles);
17125 function onClickError(e)
17127 if(!e) var e = window.event;
17128 var popup = Popup.create(this);
17129 var lines = this.getAttribute("errorText").split("\n");
17130 for(var t=0; t<lines.length; t++)
17131 createTiddlyElement(popup,"li",null,null,lines[t]);
17133 e.cancelBubble = true;
17134 if(e.stopPropagation) e.stopPropagation();
17138 function createTiddlyDropDown(place,onchange,options,defaultValue)
17140 var sel = createTiddlyElement(place,"select");
17141 sel.onchange = onchange;
17142 for(var t=0; t<options.length; t++) {
17143 var e = createTiddlyElement(sel,"option",null,null,options[t].caption);
17144 e.value = options[t].name;
17145 if(options[t].name == defaultValue)
17151 function createTiddlyPopup(place,caption,tooltip,tiddler)
17154 createTiddlyLink(place,caption,true);
17155 var btn = createTiddlyButton(place,glyph("downArrow"),tooltip,onClickTiddlyPopup,"tiddlerPopupButton");
17156 btn.tiddler = tiddler;
17158 createTiddlyText(place,caption);
17162 function onClickTiddlyPopup(e)
17164 if(!e) var e = window.event;
17165 var tiddler = this.tiddler;
17167 var popup = Popup.create(this,"div","popupTiddler");
17168 wikify(tiddler.text,popup,null,tiddler);
17171 if(e) e.cancelBubble = true;
17172 if(e && e.stopPropagation) e.stopPropagation();
17176 function createTiddlyError(place,title,text)
17178 var btn = createTiddlyButton(place,title,null,onClickError,"errorButton");
17179 if(text) btn.setAttribute("errorText",text);
17182 function merge(dst,src,preserveExisting)
17185 if(!preserveExisting || dst[p] === undefined)
17191 // Returns a string containing the description of an exception, optionally prepended by a message
17192 function exceptionText(e,message)
17194 var s = e.description ? e.description : e.toString();
17195 return message ? "%0:\n%1".format([message,s]) : s;
17198 // Displays an alert of an exception description with optional message
17199 function showException(e,message)
17201 alert(exceptionText(e,message));
17204 function alertAndThrow(m)
17210 function glyph(name)
17212 var g = config.glyphs;
17213 var b = g.currBrowser;
17216 while(!g.browsers[b]() && b < g.browsers.length-1)
17222 return g.codes[name][b];
17225 //- Animation engine
17228 function Animator()
17230 this.running = 0; // Incremented at start of each animation, decremented afterwards. If zero, the interval timer is disabled
17231 this.timerID = 0; // ID of the timer used for animating
17232 this.animations = []; // List of animations in progress
17236 // Start animation engine
17237 Animator.prototype.startAnimating = function() // Variable number of arguments
17239 for(var t=0; t<arguments.length; t++)
17240 this.animations.push(arguments[t]);
17241 if(this.running == 0) {
17243 this.timerID = window.setInterval(function() {me.doAnimate(me);},10);
17245 this.running += arguments.length;
17248 // Perform an animation engine tick, calling each of the known animation modules
17249 Animator.prototype.doAnimate = function(me)
17252 while(a < me.animations.length) {
17253 var animation = me.animations[a];
17254 if(animation.tick()) {
17257 me.animations.splice(a,1);
17258 if(--me.running == 0)
17259 window.clearInterval(me.timerID);
17264 // Map a 0..1 value to 0..1, but slow down at the start and end
17265 Animator.slowInSlowOut = function(progress)
17267 return(1-((Math.cos(progress * Math.PI)+1)/2));
17271 //-- Morpher animation
17274 // Animate a set of properties of an element
17275 function Morpher(element,duration,properties,callback)
17277 this.element = element;
17278 this.duration = duration;
17279 this.properties = properties;
17280 this.startTime = new Date();
17281 this.endTime = Number(this.startTime) + duration;
17282 this.callback = callback;
17287 Morpher.prototype.assignStyle = function(element,style,value)
17290 case "-tw-vertScroll":
17291 window.scrollTo(findScrollX(),value);
17293 case "-tw-horizScroll":
17294 window.scrollTo(value,findScrollY());
17297 element.style[style] = value;
17302 Morpher.prototype.stop = function()
17304 for(var t=0; t<this.properties.length; t++) {
17305 var p = this.properties[t];
17306 if(p.atEnd !== undefined) {
17307 this.assignStyle(this.element,p.style,p.atEnd);
17311 this.callback(this.element,this.properties);
17314 Morpher.prototype.tick = function()
17316 var currTime = Number(new Date());
17317 progress = Animator.slowInSlowOut(Math.min(1,(currTime-this.startTime)/this.duration));
17318 for(var t=0; t<this.properties.length; t++) {
17319 var p = this.properties[t];
17320 if(p.start !== undefined && p.end !== undefined) {
17321 var template = p.template ? p.template : "%0";
17325 var v = p.start + (p.end-p.start) * progress;
17326 this.assignStyle(this.element,p.style,template.format([v]));
17333 if(currTime >= this.endTime) {
17341 //-- Zoomer animation
17344 function Zoomer(text,startElement,targetElement,unused)
17346 var e = createTiddlyElement(document.body,"div",null,"zoomer");
17347 createTiddlyElement(e,"div",null,null,text);
17348 var winWidth = findWindowWidth();
17349 var winHeight = findWindowHeight();
17351 {style: 'left', start: findPosX(startElement), end: findPosX(targetElement), template: '%0px'},
17352 {style: 'top', start: findPosY(startElement), end: findPosY(targetElement), template: '%0px'},
17353 {style: 'width', start: Math.min(startElement.scrollWidth,winWidth), end: Math.min(targetElement.scrollWidth,winWidth), template: '%0px', atEnd: 'auto'},
17354 {style: 'height', start: Math.min(startElement.scrollHeight,winHeight), end: Math.min(targetElement.scrollHeight,winHeight), template: '%0px', atEnd: 'auto'},
17355 {style: 'fontSize', start: 8, end: 24, template: '%0pt'}
17357 var c = function(element,properties) {removeNode(element);};
17358 return new Morpher(e,config.animDuration,p,c);
17362 //-- Scroller animation
17365 function Scroller(targetElement,unused)
17368 {style: '-tw-vertScroll', start: findScrollY(), end: ensureVisible(targetElement)}
17370 return new Morpher(targetElement,config.animDuration,p);
17374 //-- Slider animation
17377 // deleteMode - "none", "all" [delete target element and it's children], [only] "children" [but not the target element]
17378 function Slider(element,opening,unused,deleteMode)
17380 element.style.overflow = 'hidden';
17382 element.style.height = '0px'; // Resolves a Firefox flashing bug
17383 element.style.display = 'block';
17384 var left = findPosX(element);
17385 var width = element.scrollWidth;
17386 var height = element.scrollHeight;
17387 var winWidth = findWindowWidth();
17391 p.push({style: 'height', start: 0, end: height, template: '%0px', atEnd: 'auto'});
17392 p.push({style: 'opacity', start: 0, end: 1, template: '%0'});
17393 p.push({style: 'filter', start: 0, end: 100, template: 'alpha(opacity:%0)'});
17395 p.push({style: 'height', start: height, end: 0, template: '%0px'});
17396 p.push({style: 'display', atEnd: 'none'});
17397 p.push({style: 'opacity', start: 1, end: 0, template: '%0'});
17398 p.push({style: 'filter', start: 100, end: 0, template: 'alpha(opacity:%0)'});
17399 switch(deleteMode) {
17401 c = function(element,properties) {removeNode(element);};
17404 c = function(element,properties) {removeChildren(element);};
17408 return new Morpher(element,config.animDuration,p,c);
17416 stack: [] // Array of objects with members root: and popup:
17419 Popup.create = function(root,elem,theClass)
17422 var popup = createTiddlyElement(document.body,elem ? elem : "ol","popup",theClass ? theClass : "popup");
17423 Popup.stack.push({root: root, popup: popup});
17427 Popup.onDocumentClick = function(e)
17429 if (!e) var e = window.event;
17430 var target = resolveTarget(e);
17431 if(e.eventPhase == undefined)
17433 else if(e.eventPhase == Event.BUBBLING_PHASE || e.eventPhase == Event.AT_TARGET)
17438 Popup.show = function(unused1,unused2)
17440 var curr = Popup.stack[Popup.stack.length-1];
17441 this.place(curr.root,curr.popup);
17442 addClass(curr.root,"highlight");
17443 if(config.options.chkAnimate && anim && typeof Scroller == "function")
17444 anim.startAnimating(new Scroller(curr.popup));
17446 window.scrollTo(0,ensureVisible(curr.popup));
17449 Popup.place = function(root,popup,offset)
17451 if(!offset) var offset = {x:0, y:0};
17452 var rootLeft = findPosX(root);
17453 var rootTop = findPosY(root);
17454 var rootHeight = root.offsetHeight;
17455 var popupLeft = rootLeft + offset.x;
17456 var popupTop = rootTop + rootHeight + offset.y;
17457 var winWidth = findWindowWidth();
17458 if(popup.offsetWidth > winWidth*0.75)
17459 popup.style.width = winWidth*0.75 + "px";
17460 var popupWidth = popup.offsetWidth;
17461 if(popupLeft + popupWidth > winWidth)
17462 popupLeft = winWidth - popupWidth;
17463 popup.style.left = popupLeft + "px";
17464 popup.style.top = popupTop + "px";
17465 popup.style.display = "block";
17468 Popup.remove = function()
17470 if(Popup.stack.length > 0) {
17471 Popup.removeFrom(0);
17475 Popup.removeFrom = function(from)
17477 for(var t=Popup.stack.length-1; t>=from; t--) {
17478 var p = Popup.stack[t];
17479 removeClass(p.root,"highlight");
17480 removeNode(p.popup);
17482 Popup.stack = Popup.stack.slice(0,from);
17486 //-- Wizard support
17489 function Wizard(elem)
17492 this.formElem = findRelated(elem,"wizard","className");
17493 this.bodyElem = findRelated(this.formElem.firstChild,"wizardBody","className","nextSibling");
17494 this.footElem = findRelated(this.formElem.firstChild,"wizardFooter","className","nextSibling");
17496 this.formElem = null;
17497 this.bodyElem = null;
17498 this.footElem = null;
17502 Wizard.prototype.setValue = function(name,value)
17505 this.formElem[name] = value;
17508 Wizard.prototype.getValue = function(name)
17510 return this.formElem ? this.formElem[name] : null;
17513 Wizard.prototype.createWizard = function(place,title)
17515 this.formElem = createTiddlyElement(place,"form",null,"wizard");
17516 createTiddlyElement(this.formElem,"h1",null,null,title);
17517 this.bodyElem = createTiddlyElement(this.formElem,"div",null,"wizardBody");
17518 this.footElem = createTiddlyElement(this.formElem,"div",null,"wizardFooter");
17521 Wizard.prototype.clear = function()
17523 removeChildren(this.bodyElem);
17526 Wizard.prototype.setButtons = function(buttonInfo,status)
17528 removeChildren(this.footElem);
17529 for(var t=0; t<buttonInfo.length; t++) {
17530 createTiddlyButton(this.footElem,buttonInfo[t].caption,buttonInfo[t].tooltip,buttonInfo[t].onClick);
17531 insertSpacer(this.footElem);
17533 if(typeof status == "string") {
17534 createTiddlyElement(this.footElem,"span",null,"status",status);
17538 Wizard.prototype.addStep = function(stepTitle,html)
17540 removeChildren(this.bodyElem);
17541 var w = createTiddlyElement(this.bodyElem,"div");
17542 createTiddlyElement(w,"h2",null,null,stepTitle);
17543 var step = createTiddlyElement(w,"div",null,"wizardStep");
17544 step.innerHTML = html;
17545 applyHtmlMacros(step,tiddler);
17548 Wizard.prototype.getElement = function(name)
17550 return this.formElem.elements[name];
17554 //-- ListView gadget
17559 // Create a listview
17560 ListView.create = function(place,listObject,listTemplate,callback,className)
17562 var table = createTiddlyElement(place,"table",null,className ? className : "listView twtable");
17563 var thead = createTiddlyElement(table,"thead");
17564 var r = createTiddlyElement(thead,"tr");
17565 for(var t=0; t<listTemplate.columns.length; t++) {
17566 var columnTemplate = listTemplate.columns[t];
17567 var c = createTiddlyElement(r,"th");
17568 var colType = ListView.columnTypes[columnTemplate.type];
17569 if(colType && colType.createHeader)
17570 colType.createHeader(c,columnTemplate,t);
17572 var tbody = createTiddlyElement(table,"tbody");
17573 for(var rc=0; rc<listObject.length; rc++) {
17574 rowObject = listObject[rc];
17575 r = createTiddlyElement(tbody,"tr");
17576 for(c=0; c<listTemplate.rowClasses.length; c++) {
17577 if(rowObject[listTemplate.rowClasses[c].field])
17578 addClass(r,listTemplate.rowClasses[c].className);
17580 rowObject.rowElement = r;
17581 rowObject.colElements = {};
17582 for(var cc=0; cc<listTemplate.columns.length; cc++) {
17583 c = createTiddlyElement(r,"td");
17584 columnTemplate = listTemplate.columns[cc];
17585 var field = columnTemplate.field;
17586 colType = ListView.columnTypes[columnTemplate.type];
17587 if(colType && colType.createItem)
17588 colType.createItem(c,rowObject,field,columnTemplate,cc,rc);
17589 rowObject.colElements[field] = c;
17592 if(callback && listTemplate.actions)
17593 createTiddlyDropDown(place,ListView.getCommandHandler(callback),listTemplate.actions);
17594 if(callback && listTemplate.buttons) {
17595 for(t=0; t<listTemplate.buttons.length; t++) {
17596 var a = listTemplate.buttons[t];
17597 if(a && a.name != "")
17598 createTiddlyButton(place,a.caption,null,ListView.getCommandHandler(callback,a.name,a.allowEmptySelection));
17604 ListView.getCommandHandler = function(callback,name,allowEmptySelection)
17606 return function(e) {
17607 var view = findRelated(this,"TABLE",null,"previousSibling");
17609 ListView.forEachSelector(view,function(e,rowName) {
17611 tiddlers.push(rowName);
17613 if(tiddlers.length == 0 && !allowEmptySelection) {
17614 alert(config.messages.nothingSelected);
17616 if(this.nodeName.toLowerCase() == "select") {
17617 callback(view,this.value,tiddlers);
17618 this.selectedIndex = 0;
17620 callback(view,name,tiddlers);
17626 // Invoke a callback for each selector checkbox in the listview
17627 ListView.forEachSelector = function(view,callback)
17629 var checkboxes = view.getElementsByTagName("input");
17630 var hadOne = false;
17631 for(var t=0; t<checkboxes.length; t++) {
17632 var cb = checkboxes[t];
17633 if(cb.getAttribute("type") == "checkbox") {
17634 var rn = cb.getAttribute("rowName");
17644 ListView.getSelectedRows = function(view)
17647 ListView.forEachSelector(view,function(e,rowName) {
17649 rowNames.push(rowName);
17654 ListView.columnTypes = {};
17656 ListView.columnTypes.String = {
17657 createHeader: function(place,columnTemplate,col)
17659 createTiddlyText(place,columnTemplate.title);
17661 createItem: function(place,listObject,field,columnTemplate,col,row)
17663 var v = listObject[field];
17665 createTiddlyText(place,v);
17669 ListView.columnTypes.WikiText = {
17670 createHeader: ListView.columnTypes.String.createHeader,
17671 createItem: function(place,listObject,field,columnTemplate,col,row)
17673 var v = listObject[field];
17675 wikify(v,place,null,null);
17679 ListView.columnTypes.Tiddler = {
17680 createHeader: ListView.columnTypes.String.createHeader,
17681 createItem: function(place,listObject,field,columnTemplate,col,row)
17683 var v = listObject[field];
17684 if(v != undefined && v.title)
17685 createTiddlyPopup(place,v.title,config.messages.listView.tiddlerTooltip,v);
17689 ListView.columnTypes.Size = {
17690 createHeader: ListView.columnTypes.String.createHeader,
17691 createItem: function(place,listObject,field,columnTemplate,col,row)
17693 var v = listObject[field];
17694 if(v != undefined) {
17696 while(t<config.messages.sizeTemplates.length-1 && v<config.messages.sizeTemplates[t].unit)
17698 createTiddlyText(place,config.messages.sizeTemplates[t].template.format([Math.round(v/config.messages.sizeTemplates[t].unit)]));
17703 ListView.columnTypes.Link = {
17704 createHeader: ListView.columnTypes.String.createHeader,
17705 createItem: function(place,listObject,field,columnTemplate,col,row)
17707 var v = listObject[field];
17708 var c = columnTemplate.text;
17710 createTiddlyText(createExternalLink(place,v),c ? c : v);
17714 ListView.columnTypes.Date = {
17715 createHeader: ListView.columnTypes.String.createHeader,
17716 createItem: function(place,listObject,field,columnTemplate,col,row)
17718 var v = listObject[field];
17720 createTiddlyText(place,v.formatString(columnTemplate.dateFormat));
17724 ListView.columnTypes.StringList = {
17725 createHeader: ListView.columnTypes.String.createHeader,
17726 createItem: function(place,listObject,field,columnTemplate,col,row)
17728 var v = listObject[field];
17729 if(v != undefined) {
17730 for(var t=0; t<v.length; t++) {
17731 createTiddlyText(place,v[t]);
17732 createTiddlyElement(place,"br");
17738 ListView.columnTypes.Selector = {
17739 createHeader: function(place,columnTemplate,col)
17741 createTiddlyCheckbox(place,null,false,this.onHeaderChange);
17743 createItem: function(place,listObject,field,columnTemplate,col,row)
17745 var e = createTiddlyCheckbox(place,null,listObject[field],null);
17746 e.setAttribute("rowName",listObject[columnTemplate.rowName]);
17748 onHeaderChange: function(e)
17750 var state = this.checked;
17751 var view = findRelated(this,"TABLE");
17754 ListView.forEachSelector(view,function(e,rowName) {
17760 ListView.columnTypes.Tags = {
17761 createHeader: ListView.columnTypes.String.createHeader,
17762 createItem: function(place,listObject,field,columnTemplate,col,row)
17764 var tags = listObject[field];
17765 createTiddlyText(place,String.encodeTiddlyLinkList(tags));
17769 ListView.columnTypes.Boolean = {
17770 createHeader: ListView.columnTypes.String.createHeader,
17771 createItem: function(place,listObject,field,columnTemplate,col,row)
17773 if(listObject[field] == true)
17774 createTiddlyText(place,columnTemplate.trueText);
17775 if(listObject[field] == false)
17776 createTiddlyText(place,columnTemplate.falseText);
17780 ListView.columnTypes.TagCheckbox = {
17781 createHeader: ListView.columnTypes.String.createHeader,
17782 createItem: function(place,listObject,field,columnTemplate,col,row)
17784 var e = createTiddlyCheckbox(place,null,listObject[field],this.onChange);
17785 e.setAttribute("tiddler",listObject.title);
17786 e.setAttribute("tag",columnTemplate.tag);
17788 onChange : function(e)
17790 var tag = this.getAttribute("tag");
17791 var tiddler = this.getAttribute("tiddler");
17792 store.setTiddlerTag(tiddler,this.checked,tag);
17796 ListView.columnTypes.TiddlerLink = {
17797 createHeader: ListView.columnTypes.String.createHeader,
17798 createItem: function(place,listObject,field,columnTemplate,col,row)
17800 var v = listObject[field];
17801 if(v != undefined) {
17802 var link = createTiddlyLink(place,listObject[columnTemplate.tiddlerLink],false,null);
17803 createTiddlyText(link,listObject[field]);
17809 //-- Augmented methods for the JavaScript Number(), Array(), String() and Date() objects
17812 // Clamp a number to a range
17813 Number.prototype.clamp = function(min,max)
17823 // Add indexOf function if browser does not support it
17824 if(!Array.indexOf) {
17825 Array.prototype.indexOf = function(item,from)
17829 for(var i=from; i<this.length; i++) {
17830 if(this[i] === item)
17836 // Find an entry in a given field of the members of an array
17837 Array.prototype.findByField = function(field,value)
17839 for(var t=0; t<this.length; t++) {
17840 if(this[t][field] == value)
17846 // Return whether an entry exists in an array
17847 Array.prototype.contains = function(item)
17849 return this.indexOf(item) != -1;
17852 // Adds, removes or toggles a particular value within an array
17853 // value - value to add
17854 // mode - +1 to add value, -1 to remove value, 0 to toggle it
17855 Array.prototype.setItem = function(value,mode)
17857 var p = this.indexOf(value);
17859 mode = (p == -1) ? +1 : -1;
17863 } else if(mode == -1) {
17869 // Return whether one of a list of values exists in an array
17870 Array.prototype.containsAny = function(items)
17872 for(var i=0; i<items.length; i++) {
17873 if (this.indexOf(items[i]) != -1)
17879 // Return whether all of a list of values exists in an array
17880 Array.prototype.containsAll = function(items)
17882 for (var i = 0; i<items.length; i++) {
17883 if (this.indexOf(items[i]) == -1)
17889 // Push a new value into an array only if it is not already present in the array. If the optional unique parameter is false, it reverts to a normal push
17890 Array.prototype.pushUnique = function(item,unique)
17892 if(unique === false) {
17895 if(this.indexOf(item) == -1)
17900 Array.prototype.remove = function(item)
17902 var p = this.indexOf(item);
17907 // Get characters from the right end of a string
17908 String.prototype.right = function(n)
17910 return n < this.length ? this.slice(this.length-n) : this;
17913 // Trim whitespace from both ends of a string
17914 String.prototype.trim = function()
17916 return this.replace(/^\s*|\s*$/g,"");
17919 // Convert a string from a CSS style property name to a JavaScript style name ("background-color" -> "backgroundColor")
17920 String.prototype.unDash = function()
17922 var s = this.split("-");
17924 for(var t=1; t<s.length; t++)
17925 s[t] = s[t].substr(0,1).toUpperCase() + s[t].substr(1);
17930 // Substitute substrings from an array into a format string that includes '%1'-type specifiers
17931 String.prototype.format = function(substrings)
17933 var subRegExp = /(?:%(\d+))/mg;
17937 var match = subRegExp.exec(this);
17938 if(match && match[1]) {
17939 if(match.index > currPos)
17940 r.push(this.substring(currPos,match.index));
17941 r.push(substrings[parseInt(match[1])]);
17942 currPos = subRegExp.lastIndex;
17945 if(currPos < this.length)
17946 r.push(this.substring(currPos,this.length));
17950 // Escape any special RegExp characters with that character preceded by a backslash
17951 String.prototype.escapeRegExp = function()
17953 var s = "\\^$*+?()=!|,{}[].";
17955 for(var t=0; t<s.length; t++)
17956 c = c.replace(new RegExp("\\" + s.substr(t,1),"g"),"\\" + s.substr(t,1));
17960 // Convert "\" to "\s", newlines to "\n" (and remove carriage returns)
17961 String.prototype.escapeLineBreaks = function()
17963 return this.replace(/\\/mg,"\\s").replace(/\n/mg,"\\n").replace(/\r/mg,"");
17966 // Convert "\n" to newlines, "\b" to " ", "\s" to "\" (and remove carriage returns)
17967 String.prototype.unescapeLineBreaks = function()
17969 return this.replace(/\\n/mg,"\n").replace(/\\b/mg," ").replace(/\\s/mg,"\\").replace(/\r/mg,"");
17972 // Convert & to "&", < to "<", > to ">" and " to """
17973 String.prototype.htmlEncode = function()
17975 return this.replace(/&/mg,"&").replace(/</mg,"<").replace(/>/mg,">").replace(/\"/mg,""");
17978 // Convert "&" to &, "<" to <, ">" to > and """ to "
17979 String.prototype.htmlDecode = function()
17981 return this.replace(/</mg,"<").replace(/>/mg,">").replace(/"/mg,"\"").replace(/&/mg,"&");
17984 // Convert a string to it's JSON representation by encoding control characters, double quotes and backslash. See json.org
17985 String.prototype.toJSONString = function()
17996 var replaceFn = function(a,b) {
18000 c = b.charCodeAt();
18001 return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
18003 if(/["\\\x00-\x1f]/.test(this))
18004 return '"' + this.replace(/([\x00-\x1f\\"])/g,replaceFn) + '"';
18005 return '"' + this + '"';
18008 // Parse a space-separated string of name:value parameters
18009 // The result is an array of objects:
18010 // result[0] = object with a member for each parameter name, value of that member being an array of values
18011 // result[1..n] = one object for each parameter, with 'name' and 'value' members
18012 String.prototype.parseParams = function(defaultName,defaultValue,allowEval,noNames,cascadeDefaults)
18014 var parseToken = function(match,p) {
18016 if(match[p]) // Double quoted
18018 else if(match[p+1]) // Single quoted
18020 else if(match[p+2]) // Double-square-bracket quoted
18022 else if(match[p+3]) // Double-brace quoted
18026 n = window.eval(n);
18028 throw "Unable to evaluate {{" + match[p+3] + "}}: " + exceptionText(ex);
18030 else if(match[p+4]) // Unquoted
18032 else if(match[p+5]) // empty quote
18037 var dblQuote = "(?:\"((?:(?:\\\\\")|[^\"])+)\")";
18038 var sngQuote = "(?:'((?:(?:\\\\\')|[^'])+)')";
18039 var dblSquare = "(?:\\[\\[((?:\\s|\\S)*?)\\]\\])";
18040 var dblBrace = "(?:\\{\\{((?:\\s|\\S)*?)\\}\\})";
18041 var unQuoted = noNames ? "([^\"'\\s]\\S*)" : "([^\"':\\s][^\\s:]*)";
18042 var emptyQuote = "((?:\"\")|(?:''))";
18043 var skipSpace = "(?:\\s*)";
18044 var token = "(?:" + dblQuote + "|" + sngQuote + "|" + dblSquare + "|" + dblBrace + "|" + unQuoted + "|" + emptyQuote + ")";
18045 var re = noNames ? new RegExp(token,"mg") : new RegExp(skipSpace + token + skipSpace + "(?:(\\:)" + skipSpace + token + ")?","mg");
18048 var match = re.exec(this);
18050 var n = parseToken(match,1);
18052 r.push({name:"",value:n});
18054 var v = parseToken(match,8);
18055 if(v == null && defaultName) {
18058 } else if(v == null && defaultValue) {
18061 r.push({name:n,value:v});
18062 if(cascadeDefaults) {
18069 // Summarise parameters into first element
18070 for(var t=1; t<r.length; t++) {
18071 if(r[0][r[t].name])
18072 r[0][r[t].name].push(r[t].value);
18074 r[0][r[t].name] = [r[t].value];
18079 // Process a string list of macro parameters into an array. Parameters can be quoted with "", '',
18080 // [[]], {{ }} or left unquoted (and therefore space-separated). Double-braces {{}} results in
18081 // an *evaluated* parameter: e.g. {{config.options.txtUserName}} results in the current user's name.
18082 String.prototype.readMacroParams = function()
18084 var p = this.parseParams("list",null,true,true);
18086 for(var t=1; t<p.length; t++)
18087 n.push(p[t].value);
18091 // Process a string list of unique tiddler names into an array. Tiddler names that have spaces in them must be [[bracketed]]
18092 String.prototype.readBracketedList = function(unique)
18094 var p = this.parseParams("list",null,false,true);
18096 for(var t=1; t<p.length; t++)
18097 n.pushUnique(p[t].value,unique);
18101 // Returns array with start and end index of chunk between given start and end marker, or undefined.
18102 String.prototype.getChunkRange = function(start,end)
18104 var s = this.indexOf(start);
18107 var e = this.indexOf(end,s);
18113 // Replace a chunk of a string given start and end markers
18114 String.prototype.replaceChunk = function(start,end,sub)
18116 var r = this.getChunkRange(start,end);
18117 return r ? this.substring(0,r[0]) + sub + this.substring(r[1]) : this;
18120 // Returns a chunk of a string between start and end markers, or undefined
18121 String.prototype.getChunk = function(start,end)
18123 var r = this.getChunkRange(start,end);
18125 return this.substring(r[0],r[1]);
18129 // Static method to bracket a string with double square brackets if it contains a space
18130 String.encodeTiddlyLink = function(title)
18132 return title.indexOf(" ") == -1 ? title : "[[" + title + "]]";
18135 // Static method to encodeTiddlyLink for every item in an array and join them with spaces
18136 String.encodeTiddlyLinkList = function(list)
18140 for(var t=0; t<list.length; t++)
18141 results.push(String.encodeTiddlyLink(list[t]));
18142 return results.join(" ");
18148 // Convert a string as a sequence of name:"value" pairs into a hashmap
18149 String.prototype.decodeHashMap = function()
18151 var fields = this.parseParams("anon","",false);
18153 for(var t=1; t<fields.length; t++)
18154 r[fields[t].name] = fields[t].value;
18158 // Static method to encode a hashmap into a name:"value"... string
18159 String.encodeHashMap = function(hashmap)
18162 for(var t in hashmap)
18163 r.push(t + ':"' + hashmap[t] + '"');
18164 return r.join(" ");
18167 // Static method to left-pad a string with 0s to a certain width
18168 String.zeroPad = function(n,d)
18170 var s = n.toString();
18172 s = "000000000000000000000000000".substr(0,d-s.length) + s;
18176 String.prototype.startsWith = function(prefix)
18178 return !prefix || this.substring(0,prefix.length) == prefix;
18181 // Returns the first value of the given named parameter.
18182 function getParam(params,name,defaultValue)
18185 return defaultValue;
18186 var p = params[0][name];
18187 return p ? p[0] : defaultValue;
18190 // Returns the first value of the given boolean named parameter.
18191 function getFlag(params,name,defaultValue)
18193 return !!getParam(params,name,defaultValue);
18196 // Substitute date components into a string
18197 Date.prototype.formatString = function(template)
18199 var t = template.replace(/0hh12/g,String.zeroPad(this.getHours12(),2));
18200 t = t.replace(/hh12/g,this.getHours12());
18201 t = t.replace(/0hh/g,String.zeroPad(this.getHours(),2));
18202 t = t.replace(/hh/g,this.getHours());
18203 t = t.replace(/mmm/g,config.messages.dates.shortMonths[this.getMonth()]);
18204 t = t.replace(/0mm/g,String.zeroPad(this.getMinutes(),2));
18205 t = t.replace(/mm/g,this.getMinutes());
18206 t = t.replace(/0ss/g,String.zeroPad(this.getSeconds(),2));
18207 t = t.replace(/ss/g,this.getSeconds());
18208 t = t.replace(/[ap]m/g,this.getAmPm().toLowerCase());
18209 t = t.replace(/[AP]M/g,this.getAmPm().toUpperCase());
18210 t = t.replace(/wYYYY/g,this.getYearForWeekNo());
18211 t = t.replace(/wYY/g,String.zeroPad(this.getYearForWeekNo()-2000,2));
18212 t = t.replace(/YYYY/g,this.getFullYear());
18213 t = t.replace(/YY/g,String.zeroPad(this.getFullYear()-2000,2));
18214 t = t.replace(/MMM/g,config.messages.dates.months[this.getMonth()]);
18215 t = t.replace(/0MM/g,String.zeroPad(this.getMonth()+1,2));
18216 t = t.replace(/MM/g,this.getMonth()+1);
18217 t = t.replace(/0WW/g,String.zeroPad(this.getWeek(),2));
18218 t = t.replace(/WW/g,this.getWeek());
18219 t = t.replace(/DDD/g,config.messages.dates.days[this.getDay()]);
18220 t = t.replace(/ddd/g,config.messages.dates.shortDays[this.getDay()]);
18221 t = t.replace(/0DD/g,String.zeroPad(this.getDate(),2));
18222 t = t.replace(/DDth/g,this.getDate()+this.daySuffix());
18223 t = t.replace(/DD/g,this.getDate());
18227 Date.prototype.getWeek = function()
18229 var dt = new Date(this.getTime());
18230 var d = dt.getDay();
18231 if (d==0) d=7;// JavaScript Sun=0, ISO Sun=7
18232 dt.setTime(dt.getTime()+(4-d)*86400000);// shift day to Thurs of same week to calculate weekNo
18233 var n = Math.floor((dt.getTime()-new Date(dt.getFullYear(),0,1)+3600000)/86400000);
18234 return Math.floor(n/7)+1;
18237 Date.prototype.getYearForWeekNo = function()
18239 var dt = new Date(this.getTime());
18240 var d = dt.getDay();
18241 if (d==0) d=7;// JavaScript Sun=0, ISO Sun=7
18242 dt.setTime(dt.getTime()+(4-d)*86400000);// shift day to Thurs of same week
18243 return dt.getFullYear();
18246 Date.prototype.getHours12 = function()
18248 var h = this.getHours();
18249 return h > 12 ? h-12 : ( h > 0 ? h : 12 );
18252 Date.prototype.getAmPm = function()
18254 return this.getHours() >= 12 ? config.messages.dates.pm : config.messages.dates.am;
18257 Date.prototype.daySuffix = function()
18259 return config.messages.dates.daySuffixes[this.getDate()-1];
18262 // Convert a date to local YYYYMMDDHHMM string format
18263 Date.prototype.convertToLocalYYYYMMDDHHMM = function()
18265 return String.zeroPad(this.getFullYear(),4) + String.zeroPad(this.getMonth()+1,2) + String.zeroPad(this.getDate(),2) + String.zeroPad(this.getHours(),2) + String.zeroPad(this.getMinutes(),2);
18268 // Convert a date to UTC YYYYMMDDHHMM string format
18269 Date.prototype.convertToYYYYMMDDHHMM = function()
18271 return String.zeroPad(this.getUTCFullYear(),4) + String.zeroPad(this.getUTCMonth()+1,2) + String.zeroPad(this.getUTCDate(),2) + String.zeroPad(this.getUTCHours(),2) + String.zeroPad(this.getUTCMinutes(),2);
18274 // Convert a date to UTC YYYYMMDD.HHMMSSMMM string format
18275 Date.prototype.convertToYYYYMMDDHHMMSSMMM = function()
18277 return String.zeroPad(this.getUTCFullYear(),4) + String.zeroPad(this.getUTCMonth()+1,2) + String.zeroPad(this.getUTCDate(),2) + "." + String.zeroPad(this.getUTCHours(),2) + String.zeroPad(this.getUTCMinutes(),2) + String.zeroPad(this.getUTCSeconds(),2) + String.zeroPad(this.getUTCMilliseconds(),4);
18280 // Static method to create a date from a UTC YYYYMMDDHHMM format string
18281 Date.convertFromYYYYMMDDHHMM = function(d)
18283 return new Date(Date.UTC(parseInt(d.substr(0,4),10),
18284 parseInt(d.substr(4,2),10)-1,
18285 parseInt(d.substr(6,2),10),
18286 parseInt(d.substr(8,2),10),
18287 parseInt(d.substr(10,2),10),0,0));
18291 //-- Crypto functions and associated conversion routines
18294 // Crypto "namespace"
18295 function Crypto() {}
18297 // Convert a string to an array of big-endian 32-bit words
18298 Crypto.strToBe32s = function(str)
18301 var len = Math.floor(str.length/4);
18303 for(i=0, j=0; i<len; i++, j+=4) {
18304 be[i] = ((str.charCodeAt(j)&0xff) << 24)|((str.charCodeAt(j+1)&0xff) << 16)|((str.charCodeAt(j+2)&0xff) << 8)|(str.charCodeAt(j+3)&0xff);
18306 while (j<str.length) {
18307 be[j>>2] |= (str.charCodeAt(j)&0xff)<<(24-(j*8)%32);
18313 // Convert an array of big-endian 32-bit words to a string
18314 Crypto.be32sToStr = function(be)
18317 for(var i=0;i<be.length*32;i+=8)
18318 str += String.fromCharCode((be[i>>5]>>>(24-i%32)) & 0xff);
18322 // Convert an array of big-endian 32-bit words to a hex string
18323 Crypto.be32sToHex = function(be)
18325 var hex = "0123456789ABCDEF";
18327 for(var i=0;i<be.length*4;i++)
18328 str += hex.charAt((be[i>>2]>>((3-i%4)*8+4))&0xF) + hex.charAt((be[i>>2]>>((3-i%4)*8))&0xF);
18332 // Return, in hex, the SHA-1 hash of a string
18333 Crypto.hexSha1Str = function(str)
18335 return Crypto.be32sToHex(Crypto.sha1Str(str));
18338 // Return the SHA-1 hash of a string
18339 Crypto.sha1Str = function(str)
18341 return Crypto.sha1(Crypto.strToBe32s(str),str.length);
18344 // Calculate the SHA-1 hash of an array of blen bytes of big-endian 32-bit words
18345 Crypto.sha1 = function(x,blen)
18347 // Add 32-bit integers, wrapping at 32 bits
18348 add32 = function(a,b)
18350 var lsw = (a&0xFFFF)+(b&0xFFFF);
18351 var msw = (a>>16)+(b>>16)+(lsw>>16);
18352 return (msw<<16)|(lsw&0xFFFF);
18354 // Add five 32-bit integers, wrapping at 32 bits
18355 add32x5 = function(a,b,c,d,e)
18357 var lsw = (a&0xFFFF)+(b&0xFFFF)+(c&0xFFFF)+(d&0xFFFF)+(e&0xFFFF);
18358 var msw = (a>>16)+(b>>16)+(c>>16)+(d>>16)+(e>>16)+(lsw>>16);
18359 return (msw<<16)|(lsw&0xFFFF);
18361 // Bitwise rotate left a 32-bit integer by 1 bit
18362 rol32 = function(n)
18364 return (n>>>31)|(n<<1);
18368 // Append padding so length in bits is 448 mod 512
18369 x[len>>5] |= 0x80 << (24-len%32);
18371 x[((len+64>>9)<<4)+15] = len;
18374 var k1 = 0x5A827999;
18375 var k2 = 0x6ED9EBA1;
18376 var k3 = 0x8F1BBCDC;
18377 var k4 = 0xCA62C1D6;
18379 var h0 = 0x67452301;
18380 var h1 = 0xEFCDAB89;
18381 var h2 = 0x98BADCFE;
18382 var h3 = 0x10325476;
18383 var h4 = 0xC3D2E1F0;
18385 for(var i=0;i<x.length;i+=16) {
18392 for(j = 0;j<16;j++) {
18394 t = add32x5(e,(a>>>27)|(a<<5),d^(b&(c^d)),w[j],k1);
18395 e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
18397 for(j=16;j<20;j++) {
18398 w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
18399 t = add32x5(e,(a>>>27)|(a<<5),d^(b&(c^d)),w[j],k1);
18400 e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
18402 for(j=20;j<40;j++) {
18403 w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
18404 t = add32x5(e,(a>>>27)|(a<<5),b^c^d,w[j],k2);
18405 e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
18407 for(j=40;j<60;j++) {
18408 w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
18409 t = add32x5(e,(a>>>27)|(a<<5),(b&c)|(d&(b|c)),w[j],k3);
18410 e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
18412 for(j=60;j<80;j++) {
18413 w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
18414 t = add32x5(e,(a>>>27)|(a<<5),b^c^d,w[j],k4);
18415 e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
18424 return Array(h0,h1,h2,h3,h4);
18428 //-- RGB colour object
18431 // Construct an RGB colour object from a '#rrggbb', '#rgb' or 'rgb(n,n,n)' string or from separate r,g,b values
18432 function RGB(r,g,b)
18437 if(typeof r == "string") {
18438 if(r.substr(0,1) == "#") {
18439 if(r.length == 7) {
18440 this.r = parseInt(r.substr(1,2),16)/255;
18441 this.g = parseInt(r.substr(3,2),16)/255;
18442 this.b = parseInt(r.substr(5,2),16)/255;
18444 this.r = parseInt(r.substr(1,1),16)/15;
18445 this.g = parseInt(r.substr(2,1),16)/15;
18446 this.b = parseInt(r.substr(3,1),16)/15;
18449 var rgbPattern = /rgb\s*\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)/;
18450 var c = r.match(rgbPattern);
18452 this.r = parseInt(c[1],10)/255;
18453 this.g = parseInt(c[2],10)/255;
18454 this.b = parseInt(c[3],10)/255;
18465 // Mixes this colour with another in a specified proportion
18466 // c = other colour to mix
18467 // f = 0..1 where 0 is this colour and 1 is the new colour
18468 // Returns an RGB object
18469 RGB.prototype.mix = function(c,f)
18471 return new RGB(this.r + (c.r-this.r) * f,this.g + (c.g-this.g) * f,this.b + (c.b-this.b) * f);
18474 // Return an rgb colour as a #rrggbb format hex string
18475 RGB.prototype.toString = function()
18477 return "#" + ("0" + Math.floor(this.r.clamp(0,1) * 255).toString(16)).right(2) +
18478 ("0" + Math.floor(this.g.clamp(0,1) * 255).toString(16)).right(2) +
18479 ("0" + Math.floor(this.b.clamp(0,1) * 255).toString(16)).right(2);
18483 //-- DOM utilities - many derived from www.quirksmode.org
18486 function drawGradient(place,horiz,colours)
18488 for(var t=0; t<= 100; t+=2) {
18489 var bar = document.createElement("div");
18490 place.appendChild(bar);
18491 bar.style.position = "absolute";
18492 bar.style.left = horiz ? t + "%" : 0;
18493 bar.style.top = horiz ? 0 : t + "%";
18494 bar.style.width = horiz ? (101-t) + "%" : "100%";
18495 bar.style.height = horiz ? "100%" : (101-t) + "%";
18496 bar.style.zIndex = -1;
18498 var p = f*(colours.length-1);
18499 bar.style.backgroundColor = colours[Math.floor(p)].mix(colours[Math.ceil(p)],p-Math.floor(p)).toString();
18503 function createTiddlyText(theParent,theText)
18505 return theParent.appendChild(document.createTextNode(theText));
18508 function createTiddlyCheckbox(theParent,caption,checked,onChange)
18510 var cb = document.createElement("input");
18511 cb.setAttribute("type","checkbox");
18512 cb.onclick = onChange;
18513 theParent.appendChild(cb);
18514 cb.checked = checked;
18515 cb.className = "chkOptionInput";
18517 wikify(caption,theParent);
18521 function createTiddlyElement(theParent,theElement,theID,theClass,theText)
18523 var e = document.createElement(theElement);
18524 if(theClass != null)
18525 e.className = theClass;
18527 e.setAttribute("id",theID);
18528 if(theText != null)
18529 e.appendChild(document.createTextNode(theText));
18530 if(theParent != null)
18531 theParent.appendChild(e);
18535 function addEvent(obj,type,fn)
18537 if(obj.attachEvent) {
18538 obj['e'+type+fn] = fn;
18539 obj[type+fn] = function(){obj['e'+type+fn](window.event);};
18540 obj.attachEvent('on'+type,obj[type+fn]);
18542 obj.addEventListener(type,fn,false);
18546 function removeEvent(obj,type,fn)
18548 if(obj.detachEvent) {
18549 obj.detachEvent('on'+type,obj[type+fn]);
18550 obj[type+fn] = null;
18552 obj.removeEventListener(type,fn,false);
18556 function addClass(e,theClass)
18558 var currClass = e.className.split(" ");
18559 if(currClass.indexOf(theClass) == -1)
18560 e.className += " " + theClass;
18563 function removeClass(e,theClass)
18565 var currClass = e.className.split(" ");
18566 var i = currClass.indexOf(theClass);
18568 currClass.splice(i,1);
18569 i = currClass.indexOf(theClass);
18571 e.className = currClass.join(" ");
18574 function hasClass(e,theClass)
18577 if(e.className.split(" ").indexOf(theClass) != -1)
18583 // Find the closest relative with a given property value (property defaults to tagName, relative defaults to parentNode)
18584 function findRelated(e,value,name,relative)
18586 name = name ? name : "tagName";
18587 relative = relative ? relative : "parentNode";
18588 if(name == "className") {
18589 while(e && !hasClass(e,value)) {
18593 while(e && e[name] != value) {
18600 // Resolve the target object of an event
18601 function resolveTarget(e)
18606 else if(e.srcElement)
18607 obj = e.srcElement;
18608 if(obj.nodeType == 3) // defeat Safari bug
18609 obj = obj.parentNode;
18613 // Return the content of an element as plain text with no formatting
18614 function getPlainText(e)
18618 text = e.innerText;
18619 else if(e.textContent)
18620 text = e.textContent;
18624 // Get the scroll position for window.scrollTo necessary to scroll a given element into view
18625 function ensureVisible(e)
18627 var posTop = findPosY(e);
18628 var posBot = posTop + e.offsetHeight;
18629 var winTop = findScrollY();
18630 var winHeight = findWindowHeight();
18631 var winBot = winTop + winHeight;
18632 if(posTop < winTop) {
18634 } else if(posBot > winBot) {
18635 if(e.offsetHeight < winHeight)
18636 return posTop - (winHeight - e.offsetHeight);
18644 // Get the current width of the display window
18645 function findWindowWidth()
18647 return window.innerWidth ? window.innerWidth : document.documentElement.clientWidth;
18650 // Get the current height of the display window
18651 function findWindowHeight()
18653 return window.innerHeight ? window.innerHeight : document.documentElement.clientHeight;
18656 // Get the current horizontal page scroll position
18657 function findScrollX()
18659 return window.scrollX ? window.scrollX : document.documentElement.scrollLeft;
18662 // Get the current vertical page scroll position
18663 function findScrollY()
18665 return window.scrollY ? window.scrollY : document.documentElement.scrollTop;
18668 function findPosX(obj)
18671 while(obj.offsetParent) {
18672 curleft += obj.offsetLeft;
18673 obj = obj.offsetParent;
18678 function findPosY(obj)
18681 while(obj.offsetParent) {
18682 curtop += obj.offsetTop;
18683 obj = obj.offsetParent;
18688 // Blur a particular element
18689 function blurElement(e)
18691 if(e != null && e.focus && e.blur) {
18697 // Create a non-breaking space
18698 function insertSpacer(place)
18700 var e = document.createTextNode(String.fromCharCode(160));
18702 place.appendChild(e);
18706 // Remove all children of a node
18707 function removeChildren(e)
18709 while(e && e.hasChildNodes())
18710 removeNode(e.firstChild);
18713 // Remove a node and all it's children
18714 function removeNode(e)
18717 e.parentNode.removeChild(e);
18720 // Remove any event handlers or non-primitve custom attributes
18721 function scrubNode(e)
18723 var att = e.attributes;
18725 for(var t=0; t<att.length; t++) {
18726 var n = att[t].name;
18727 if(n !== 'style' && (typeof e[n] === 'function' || (typeof e[n] === 'object' && e[n] != null))) {
18735 var c = e.firstChild;
18742 // Add a stylesheet, replacing any previous custom stylesheet
18743 function setStylesheet(s,id,doc)
18746 id = "customStyleSheet";
18749 var n = doc.getElementById(id);
18750 if(doc.createStyleSheet) {
18751 // Test for IE's non-standard createStyleSheet method
18753 n.parentNode.removeChild(n);
18754 // This failed without the
18755 doc.getElementsByTagName("head")[0].insertAdjacentHTML("beforeEnd"," <style id='" + id + "'>" + s + "</style>");
18758 n.replaceChild(doc.createTextNode(s),n.firstChild);
18760 n = doc.createElement("style");
18761 n.type = "text/css";
18763 n.appendChild(doc.createTextNode(s));
18764 doc.getElementsByTagName("head")[0].appendChild(n);
18769 // Force the browser to do a document reflow when needed to workaround browser bugs
18770 function forceReflow()
18772 if(config.browser.isGecko) {
18773 setStylesheet("body {top:-1em;margin-top:1em;}");
18778 // Replace the current selection of a textarea or text input and scroll it into view
18779 function replaceSelection(e,text)
18781 if(e.setSelectionRange) {
18782 var oldpos = e.selectionStart;
18783 var isRange = e.selectionEnd > e.selectionStart;
18784 e.value = e.value.substr(0,e.selectionStart) + text + e.value.substr(e.selectionEnd);
18785 e.setSelectionRange(isRange ? oldpos : oldpos + text.length,oldpos + text.length);
18786 var linecount = e.value.split('\n').length;
18787 var thisline = e.value.substr(0,e.selectionStart).split('\n').length-1;
18788 e.scrollTop = Math.floor((thisline - e.rows / 2) * e.scrollHeight / linecount);
18789 } else if(document.selection) {
18790 var range = document.selection.createRange();
18791 if(range.parentElement() == e) {
18792 var isCollapsed = range.text == "";
18795 range.moveStart('character', -text.length);
18802 // Returns the text of the given (text) node, possibly merging subsequent text nodes
18803 function getNodeText(e)
18806 while(e && e.nodeName == "#text") {
18814 //-- LoaderBase and SaverBase
18817 function LoaderBase() {}
18819 LoaderBase.prototype.loadTiddler = function(store,node,tiddlers)
18821 var title = this.getTitle(store,node);
18823 var tiddler = store.createTiddler(title);
18824 this.internalizeTiddler(store,tiddler,title,node);
18825 tiddlers.push(tiddler);
18829 LoaderBase.prototype.loadTiddlers = function(store,nodes)
18832 for(var t = 0; t < nodes.length; t++) {
18834 this.loadTiddler(store,nodes[t],tiddlers);
18836 showException(ex,config.messages.tiddlerLoadError.format([this.getTitle(store,nodes[t])]));
18842 function SaverBase() {}
18844 SaverBase.prototype.externalize = function(store)
18847 var tiddlers = store.getTiddlers("title");
18848 for(var t = 0; t < tiddlers.length; t++)
18849 results.push(this.externalizeTiddler(store,tiddlers[t]));
18850 return results.join("\n");
18854 //-- TW21Loader (inherits from LoaderBase)
18857 function TW21Loader() {}
18859 TW21Loader.prototype = new LoaderBase();
18861 TW21Loader.prototype.getTitle = function(store,node)
18864 if(node.getAttribute) {
18865 title = node.getAttribute("title");
18867 title = node.getAttribute("tiddler");
18869 if(!title && node.id) {
18870 var lenPrefix = store.idPrefix.length;
18871 if (node.id.substr(0,lenPrefix) == store.idPrefix)
18872 title = node.id.substr(lenPrefix);
18877 TW21Loader.prototype.internalizeTiddler = function(store,tiddler,title,node)
18879 var e = node.firstChild;
18881 if(node.getAttribute("tiddler")) {
18882 text = getNodeText(e).unescapeLineBreaks();
18884 while(e.nodeName!="PRE" && e.nodeName!="pre") {
18887 text = e.innerHTML.replace(/\r/mg,"").htmlDecode();
18889 var modifier = node.getAttribute("modifier");
18890 var c = node.getAttribute("created");
18891 var m = node.getAttribute("modified");
18892 var created = c ? Date.convertFromYYYYMMDDHHMM(c) : version.date;
18893 var modified = m ? Date.convertFromYYYYMMDDHHMM(m) : created;
18894 var tags = node.getAttribute("tags");
18896 var attrs = node.attributes;
18897 for(var i = attrs.length-1; i >= 0; i--) {
18898 var name = attrs[i].name;
18899 if (attrs[i].specified && !TiddlyWiki.isStandardField(name)) {
18900 fields[name] = attrs[i].value.unescapeLineBreaks();
18903 tiddler.assign(title,text,modifier,modified,tags,created,fields);
18908 //-- TW21Saver (inherits from SaverBase)
18911 function TW21Saver() {}
18913 TW21Saver.prototype = new SaverBase();
18915 TW21Saver.prototype.externalizeTiddler = function(store,tiddler)
18918 var extendedAttributes = "";
18919 var usePre = config.options.chkUsePreForStorage;
18920 store.forEachField(tiddler,
18921 function(tiddler,fieldName,value) {
18922 // don't store stuff from the temp namespace
18923 if(typeof value != "string")
18925 if (!fieldName.match(/^temp\./))
18926 extendedAttributes += ' %0="%1"'.format([fieldName,value.escapeLineBreaks().htmlEncode()]);
18928 var created = tiddler.created.convertToYYYYMMDDHHMM();
18929 var modified = tiddler.modified.convertToYYYYMMDDHHMM();
18930 var vdate = version.date.convertToYYYYMMDDHHMM();
18931 var attributes = tiddler.modifier ? ' modifier="' + tiddler.modifier.htmlEncode() + '"' : "";
18932 attributes += (usePre && modified == created) ? "" : ' modified="' + modified +'"';
18933 attributes += (usePre && created == vdate) ? "" :' created="' + created + '"';
18934 var tags = tiddler.getTags();
18935 if(!usePre || tags)
18936 attributes += ' tags="' + tags.htmlEncode() + '"';
18937 return ('<div %0="%1"%2%3>%4</'+'div>').format([
18938 usePre ? "title" : "tiddler",
18939 tiddler.title.htmlEncode(),
18941 extendedAttributes,
18942 usePre ? "\n<pre>" + tiddler.text.htmlEncode() + "</pre>\n" : tiddler.text.escapeLineBreaks().htmlEncode()
18945 throw exceptionText(ex,config.messages.tiddlerSaveError.format([tiddler.title]));
18950 //-- Deprecated code
18953 // @Deprecated: Use createElementAndWikify and this.termRegExp instead
18954 config.formatterHelpers.charFormatHelper = function(w)
18956 w.subWikify(createTiddlyElement(w.output,this.element),this.terminator);
18959 // @Deprecated: Use enclosedTextHelper and this.lookaheadRegExp instead
18960 config.formatterHelpers.monospacedByLineHelper = function(w)
18962 var lookaheadRegExp = new RegExp(this.lookahead,"mg");
18963 lookaheadRegExp.lastIndex = w.matchStart;
18964 var lookaheadMatch = lookaheadRegExp.exec(w.source);
18965 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
18966 var text = lookaheadMatch[1];
18967 if(config.browser.isIE)
18968 text = text.replace(/\n/g,"\r");
18969 createTiddlyElement(w.output,"pre",null,null,text);
18970 w.nextMatch = lookaheadRegExp.lastIndex;
18974 // @Deprecated: Use <br> or <br /> instead of <<br>>
18975 config.macros.br.handler = function(place)
18977 createTiddlyElement(place,"br");
18980 // Find an entry in an array. Returns the array index or null
18981 // @Deprecated: Use indexOf instead
18982 Array.prototype.find = function(item)
18984 var i = this.indexOf(item);
18985 return i == -1 ? null : i;
18988 // Load a tiddler from an HTML DIV. The caller should make sure to later call Tiddler.changed()
18989 // @Deprecated: Use store.getLoader().internalizeTiddler instead
18990 Tiddler.prototype.loadFromDiv = function(divRef,title)
18992 return store.getLoader().internalizeTiddler(store,this,title,divRef);
18995 // Format the text for storage in an HTML DIV
18996 // @Deprecated Use store.getSaver().externalizeTiddler instead.
18997 Tiddler.prototype.saveToDiv = function()
18999 return store.getSaver().externalizeTiddler(store,this);
19002 // @Deprecated: Use store.allTiddlersAsHtml() instead
19003 function allTiddlersAsHtml()
19005 return store.allTiddlersAsHtml();
19008 // @Deprecated: Use refreshPageTemplate instead
19009 function applyPageTemplate(title)
19011 refreshPageTemplate(title);
19014 // @Deprecated: Use story.displayTiddlers instead
19015 function displayTiddlers(srcElement,titles,template,unused1,unused2,animate,unused3)
19017 story.displayTiddlers(srcElement,titles,template,animate);
19020 // @Deprecated: Use story.displayTiddler instead
19021 function displayTiddler(srcElement,title,template,unused1,unused2,animate,unused3)
19023 story.displayTiddler(srcElement,title,template,animate);
19026 // @Deprecated: Use functions on right hand side directly instead
19027 var createTiddlerPopup = Popup.create;
19028 var scrollToTiddlerPopup = Popup.show;
19029 var hideTiddlerPopup = Popup.remove;
19031 // @Deprecated: Use right hand side directly instead
19032 var regexpBackSlashEn = new RegExp("\\\\n","mg");
19033 var regexpBackSlash = new RegExp("\\\\","mg");
19034 var regexpBackSlashEss = new RegExp("\\\\s","mg");
19035 var regexpNewLine = new RegExp("\n","mg");
19036 var regexpCarriageReturn = new RegExp("\r","mg");
19038 //-- End of scripts
19042 <script type="text/javascript">
19045 document.write("<applet style='position:absolute;left:-1px' name='TiddlySaver' code='TiddlySaver.class' archive='TiddlySaver.jar' width='1' height='1'></applet>");
19048 <!--POST-SCRIPT-START-->
19050 <!--POST-SCRIPT-END-->