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="200802191305" created="200709131950" changecount="7">
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が同時にインストールされます。
768 ** ~BCC5.5 + Reloはインストールが比較的簡単で、初心者の型はこちらをお勧めします。
769 ** BCC 5.5 (Borland C++Compiler / Turbo Debugger) はhttp://www.codegear.com/jp/downloads/free/cppbuilderから入手してください。</pre>
771 <div title="1.2 Gaborパッチの事前描画" modifier="Psychlops_DevelopperG" modified="200908190214" created="200709252145" changecount="2">
772 <pre>この節では実験刺激のGaborパッチを事前描画するプログラムを作ります。
773 Gaborパッチとは、以下の図のような正弦波にGauss関数によるエンベロープをかけて、刺激中心から周辺に離れるに従って正弦波のコントラストが低くなっていく様な刺激です。
774 |[Img[image/gabor.png]]| [Img[image/gabor3d.png]]|
775 この刺激は複雑な2次元パターンなので、Canvas::pix()を使ってリアルタイム描画をするよりは、Imageにあらかじめ描画を行った方がいいでしょう。
777 Imageを使った描画については[[3章|3. 複雑な描画を行う1(オフスクリーンへの描画)]]で既に触れましたが、この節では[[1.1節|1.1 実験計画のプログラミング]]と同様に汎用性を意識してGaborパッチを描画するためのクラス~GaborImageを作成して、これを利用した刺激描画を行います。
780 [[GaborImageクラスのソースコード|GaborImage]]
781 [[1.1節|1.1 実験計画のプログラミング]]のIndependentVariablesクラス同様上のリンクのソースコードを実際の宣言が起こる前にコピーアンドペーストして、プログラムに追加するとこのクラスを使用することができます。
783 このクラスはdraw()命令のみから構成されています。この命令は指定したImageにImageサイズの1/6をσとするエンベロープのGaborパッチを描画します。
784 |!~GaborImage::draw()|draw(Image &area, double ori, double freq, double phase, double Lmean, double *contrast)|カラーのGaborパッチをImageに描画します。|
785 |~|draw(Image &area, double ori, double freq, double phase, double Lmean, double contrast)|グレースケールのGaborパッチをImage上に描画します。|
787 |~|Image &area: Gaborパッチを描画するImageの名前|>|
788 |~|double freq: キャリアの空間周波数(pixel)|>|
789 |~|double phase: キャリアの位相(度)|>|
790 |~|double Lmean: パッチの平均輝度 |>|
791 |~|double *contrast: <br> キャリアのR,G,B各チャンネルのコントラストの配列名 (double[3]/範囲0.0-1.0)|double contrast: <br> キャリアのコントラスト <br> (範囲0.0-1.0)|
793 !!~GaborImageクラスを用いた刺激の事前描画
794 ここでは、上で説明した~GaborImageクラスを使って刺激の事前描画を行います。
795 まず、ここで追加したクラスのインスタンスを作成することにします。インスタンス名はgaborIMGにします。
799 次にGaborパッチを描画するImageを配列gIMGとして宣言します。
801 Psychlops::Image gIMG[10];
804 このImageの10枚分の配列に(傾き量5水準) x (傾きの方向:右/左の2水準)の10枚のGaborパッチを書けばよいのですが、どの配列にどの方位のGaborパッチを書けば良いかを考えなくてはいけません。6.1でGaborパッチの傾き量の水準は0~4の番号を振られていたことを思い出してください。各試行ではこの番号を元にで、各試行でランダムに与えられた傾きの方向を考慮して、呼び出すgIMGの番号を決める必要があります。
806 少し複雑になってきたので、与えられた傾き量の番号と傾きの方向に対して、どの方位のGaborパッチが呼び出されるべきかを表にしておきましょう。垂直方位は90度なのでこれに対して傾き量が加減されていることに気をつけてください。
808 |>|!傾き量の番号| 0 | 1 | 2 | 3 | 4 |
809 |!傾きの方向|右|90-2|90-4|90-8|90-16|90-32|
810 |~|左|90+2|90+4|90+8|90+16|90+32|
813 ここでは、以下の表の様にgIMGの番号とGaborパッチの方位を対応させることにします。ついでに、傾きの方向に対しても番号を振っておきます。右を0左を1としてみましょう。
815 |>|!傾き量の番号|>| 0 |>| 1 |>| 2 |>| 3 |>| 4 |>|
816 |!傾きの方向|右(0)|90-2|bgcolor(#a0ffa0):gIMG[0]|90-4|bgcolor(#a0ffa0):gIMG[1]|90-8|bgcolor(#a0ffa0):gIMG[2]|90-16|bgcolor(#a0ffa0):gIMG[3]|90-32|bgcolor(#a0ffa0):gIMG[4]|
817 |~|左(1)|90+2|bgcolor(#a0ffa0):gIMG[5]|90+4|bgcolor(#a0ffa0):gIMG[6]|90+8|bgcolor(#a0ffa0):gIMG[7]|90+16|bgcolor(#a0ffa0):gIMG[8]|90+32|bgcolor(#a0ffa0):gIMG[9]|
820 さらに、これをgIMGの番号順に並べ替えて、各番号に対する計算式を考えます。以下のようになるでしょう。
822 |!gIMGの配列番号|gIMG[0]|gIMG[1]|gIMG[2]|gIMG[3]|gIMG[4]|gIMG[5]|gIMG[6]|gIMG[7]|gIMG[8]|gIMG[9]|
823 |!(傾き量, 方向) 番号|(0,0)|(1,0)|(2,0)|(3,0)|(4,0)|(0,1)|(1,1)|(2,1)|(3,1)|(4,1)|
824 |!Gaborパッチの方位(度)|90-2|90-4|90-8|90-16|90-32|90+2|90+4|90+8|90+16|90+32|
825 |!配列番号iに対する方位の計算式|>|>|>|>| 90-pow(2, i) |>|>|>|>| 90+pow(2,i-4) |
826 | ^^Pow(a,b)はaのb乗を示す^^|c
828 これで、10枚のGaborパッチを各準備が整いました。~GaborImage::draw()命令を使って実際のプログラムを組んでみます。ここでは最終引数に単なる数値を入れて、グレースケールのGaborパッチを描画することにします。
830 最後にdraw()命令を使ってGaborパッチの描画を行います。刺激サイズは100 pixel四方、キャリアの波長は30 pix (空間周波数は1.0/30.0), キャリアのコントラストは0.2 (20%), 位相は0, パッチの平均輝度は0.5にします。
831 また、あらかじめ刺激提示位置(画面中央)にイメージをシフトしておきます。
833 double gIMGsize=100, ori;
834 for(int i=0; i<10; i++){
835 if(i<5){ ori=90.0-pow(2.0, i);}
836 else{ ori=90.0+pow(2.0,i-4);}
837 gIMG[i].set(gIMGsize, gIMGsize);
838 gaborIMG.draw(gIMG[i], ori, 1.0/30.0, 0.0, 0.5, 0.2);
843 この節のコードをまとめると以下のようになります。
846 Psychlops::Image gIMG[10];
847 double gIMGsize=100, ori;
848 for(int i=0; i<10; i++){
849 if(i<5){ ori=90.0-pow(2.0, i);}
850 else{ ori=90.0+pow(2.0,i-4);}
851 gIMG[i].set(gIMGsize, gIMGsize);
852 gaborIMG.draw(gIMG[i], ori, 1.0/30.0, 0.0, 0.5, 0.2);
857 <div title="1.2 インストール作業(Mac OS X 10.4) " modifier="YourName" modified="201002040347" created="200709131951" changecount="3">
859 PsychlopsのOSXでのインストールには、まずXcodeをインストールする必要があります。XcodeはApple社が提供する標準の開発環境で、無償で利用できます(ダウンロードにはApple Developers Connectionへの登録が必要です)。
861 * [[ダウンロード|http://developer.apple.com/tools/xcode/]]
862 * [[機能紹介|http://www.apple.com/jp/macosx/features/xcode/]]
864 ''Mac OS X 10.4向けのXcode 2.x系はApple社よりの配布がすでに終了しています。OSX 10.4のインストール/リカバリディスク中に開発用ツールキットがない場合、10.6へのアップグレードをご検討ください''
866 ''xcode.mpkg''をダブルクリックしてインストールを始めます。
867 [img[Xcodeインストール|image/OSX/Xcode2_10.4.png]]
869 インストーラの指示に従ってインストールを完了します。
870 [img[Xcodeインストール|image/OSX/Xcode3_10.4.png]]
872 !! Psychlopsの本体をインストール
873 * [[Psychlops Download|http://psychlops.l.u-tokyo.ac.jp/?Download]]
875 ダウンロードページへ行き、Xcode版の最新版をクリックします。
876 [img[Psychlopsダウンロード|image/OSX/Psychlops_OSX1png]]
878 zipファイルを解凍するとパッケージファイルが現れますので、これをダブルクリックしてインストールを開始します。
879 [img[Psychlopsインストール|image/OSX/Psychlops_OSX2.png]]
881 インストーラの指示に従ってインストールを完了します。
882 [img[Psychlopsインストール|image/OSX/Psychlops_OSX3.png]]
885 Xcodeを起動し、''メニューパー → ファイル → 新しいプロジェクト''を選択します。
886 [img[新しいプロジェクト|image/OSX/Psychlops_OSX_10.4_New1.png]]
888 新しいプロジェクトのウィザードが開いたら、Applicationカテゴリにある''Psychlops C++ Application''を選択します。
889 [img[新しいプロジェクト|image/OSX/Psychlops_OSX_10.4_New2.png]]
891 新しいプロジェクトを作成する場所と名前を選びます。作成後に場所は変更可能ですが、名前は作り直す以外に変更できませんのでご注意ください。また、パスに日本語を含んでいると実行に失敗することがありますので、ASCII文字のみのフォルダの中に作成してください。
892 [img[新しいプロジェクト|image/OSX/Psychlops_OSX_10.4_New3.png]]
894 プログラムはSourcesカテゴリの中にある(プロジェクト名).cppにあります。標準のサンプルプログラムが入っていますので、まずはダブルクリックしてエディタを開き、中身を見てみます(最初から開いていることもあります)。
895 [img[新しいプロジェクト|image/OSX/Psychlops_OSX_10.4_New5.png]]
898 [img[ウインドウ説明|image/OSX/Psychlops_OSX_10.4_New6.png]]
899 プログラム編集エディタでブログラムを編集することができます。実際にはこのサンプルを消して、新しく自分のプログラムを書いていくわけですが、まず最初は正しくインストールされているかどうかを確かめるために、標準のサンプルをそのまま実行してみましょう。ツールバーの実行ボタンをおすと、プログラムが機械語に翻訳されて実行ファイルの作成・実行が行われます。
901 出来上がった実行ファイルは、''プロジェクトのあるフォルダ > buildフォルダ > Releaseフォルダ '' の中にあります。この実行ファイルをコピーすることで頒布が可能ですが、メールなどに添付する際は必ずzipファイル等に圧縮してください。(ここで見えている実行プログラムは実際はフォルダです。Macのプログラムの仕様で、実際の実行プログラムは複数存在して、実行プログラムに見えるフォルダに格納されています。これを他のマシンに頒布するためにアップロードしたり、メールに添付したりするときには圧縮して実際に一つのファイルにまとめる必要があります。)
902 [img[ウインドウ説明|image/OSX/Psychlops_OSX_10.4_New7.png]]
905 <div title="1.2 インストール作業(Mac) " modifier="YourName" created="201002040339" changecount="1">
907 PsychlopsのOSXでのインストールには、まずXcodeをインストールする必要があります。XcodeはApple社が提供する標準の開発環境で、無償で利用できます(ダウンロードにはApple Developers Connectionへの登録が必要です)。対応バージョンについては、2010/01/31現在、Xcode2.5と3.x系に対応しておりますが、最新の対応状況につきましては[[http://psychlops.l.u-tokyo.ac.jp/?Environment]]からご確認ください。スクリーンショットはマニュアル作成時のものですので、バージョンが更新されていると異なる場合があります。ご了承ください。
909 * [[ダウンロード|http://developer.apple.com/technology/xcode.html]]
910 * [[機能紹介|http://www.apple.com/jp/macosx/features/xcode/]]
913 !!! OSXインストールディスクを利用する場合
915 OSXインストールディスクに開発環境のインストーラがあります。ディスクの内容は購入時期によって異なる可能性があります。ここでは10.5を例に説明いたします。
917 [img[Xcodeインストール|image/OSX/Xcode_DVD_Screen.png]]
919 OSXインストールディスク中をFinderで開くと、「オプションインストール」フォルダがあります。このなかの「Xcode Tools」フォルダを開き、「XcodeTools.mpkg」(拡張子は見えない場合があります)をダブルクリックすると、Xcodeインストーラが実行されます。
924 Xcodeをダウンロードするには、まず[[Appleのダウンロードサイト|http://developer.apple.com/technology/xcode.html]]へ行きます。その際、初めての方はまずApple Developers Connectionへの登録をする必要があります。
926 [img[Xcodeインストール|image/OSX/Xcode_01.png]]
928 「ADC membership」と書かれたリンクをクリックすると「Join Now」というボタンがありますので、それをクリックして
930 [img[Xcodeインストール|image/OSX/Xcode_02.png]]
932 もう一度[[ダウンロードページ|http://developer.apple.com/technology/xcode.html]]へ行き下側の「Xcode for Mac-only Development」の「Download now」をクリックしてください。その後のページでアカウントを入力すると、ダウンロードリンクが現れますので、本体のdmgファイルをダウンロードしてください。
934 [img[Xcodeインストール|image/OSX/Xcode_03.png]]
936 ダウンロードが終わると自動的にディスクイメージが展開されますので、''XcodeTools.mpkg''をダブルクリックしてインストールを始めます。
938 [img[Xcodeインストール|image/OSX/Xcode2.png]]
940 インストーラの指示に従ってインストールを完了します。
941 [img[Xcodeインストール|image/OSX/Xcode3.png]]
944 !! Psychlopsの本体をインストール
945 * [[Psychlops Download|http://psychlops.l.u-tokyo.ac.jp/?Download]]
947 ダウンロードページへ行き、Xcode版の最新版をクリックします。
948 [img[Psychlopsダウンロード|image/OSX/Psychlops_OSX1png]]
950 zipファイルを解凍するとパッケージファイルが現れますので、これをダブルクリックしてインストールを開始します。
951 [img[Psychlopsインストール|image/OSX/Psychlops_OSX2.png]]
953 インストーラの指示に従ってインストールを完了します。
954 [img[Psychlopsインストール|image/OSX/Psychlops_OSX3.png]]
957 Xcodeを起動し、''メニューパー → ファイル → 新しいプロジェクト''を選択します。
958 [img[新しいプロジェクト|image/OSX/Psychlops_OSX_New1.png]]
960 新しいプロジェクトのウィザードが開いたら、Applicationカテゴリにある''Psychlops C++ Application''を選択します。
961 [img[新しいプロジェクト|image/OSX/Psychlops_OSX_New2.png]]
963 新しいプロジェクトを作成する場所と名前を選びます。作成後に場所は変更可能ですが、名前は作り直す以外に変更できませんのでご注意ください。また、パスに日本語を含んでいると実行に失敗することがありますので、ASCII文字のみのフォルダの中に作成してください。
964 [img[新しいプロジェクト|image/OSX/Psychlops_OSX_New3.png]]
966 プログラムはSourcesカテゴリの中にある(プロジェクト名).cppにあります。標準のサンプルプログラムが入っていますので、まずはダブルクリックしてエディタを開き、中身を見てみます(最初から開いていることもあります)。
967 [img[新しいプロジェクト|image/OSX/Psychlops_OSX_New5.png]]
970 [img[ウインドウ説明|image/OSX/Psychlops_OSX_New6.png]]
971 プログラム編集エディタでブログラムを編集することができます。実際にはこのサンプルを消して、新しく自分のプログラムを書いていくわけですが、まず最初は正しくインストールされているかどうかを確かめるために、標準のサンプルをそのまま実行してみましょう。ツールバーの実行ボタンをおすと、プログラムが機械語に翻訳されて実行ファイルの作成・実行が行われます。
973 出来上がった実行ファイルは、''プロジェクトのあるフォルダ > buildフォルダ > Releaseフォルダ または Debugフォルダ'' の中にあります。この実行ファイルをコピーすることで頒布が可能ですが、メールなどに添付する際は必ずzipファイル等に圧縮してください。(ここで見えている実行プログラムは実際はフォルダです。Macのプログラムの仕様で、実際の実行プログラムは複数存在して、実行プログラムに見えるフォルダに格納されています。これを他のマシンに頒布するためにアップロードしたり、メールに添付したりするときには圧縮して実際に一つのファイルにまとめる必要があります。)
974 [img[ウインドウ説明|image/OSX/Psychlops_OSX_New7.png]]
977 <div title="1.3 インストール作業(Windows + BCC) " modifier="YourName" modified="201002040332" created="200709131955" changecount="2">
978 <pre>インストールには管理者権限のあるユーザである必要があります。
979 Windows Vistaに関しては、インストールが自動化されていませんがお使いいただくことは可能です。
980 [[Tips: Vistaにおけるインストール]]をご覧ください。
983 Psychlopsは画面の垂直同期信号に合わせて画面を更新しますが、この機能を正常に動作させるには、ディスプレイドライバのOpenGL描画設定で垂直同期にあわせるオプションを常にオンにする必要があります。
984 一般的には''コントロールパネル > 画面''の設定の''モニタ''タブを選び、''詳細設定''ボタンから設定します。詳しくはご使用のビデオカードのマニュアルをご覧ください。
987 PsychlopsのWindowsでのインストールには、まずコンパイラをインストールする必要があります。ここではBorland社が無償で提供するBorland C++ Compiler 5.5をインストールしてみます。Cマガジンから提供されている設定ツールも一緒にダウンロードしましょう。
989 * [[BCCダウンロード|http://www.codegear.com/jp/downloads/free/cppbuilder]]
990 * [[設定ツールダウンロード|http://www.cmagazine.jp/setbcc.html]]
992 まずダウンロードページへ行き、''Borland C++Compiler / Turbo Debugger''をクリックします。この後個人情報を入力してダウンロードを開始します。
993 [img[BCCダウンロード|image/Win/BCC0.png]]
995 ダウンロードが終わるとインストーラの実行ファイルがありますので、''freecommandlinetools2.exe''をダブルクリックしてインストールを始めます。インストーラの指示に従ってインストールを完了します。
996 [img[BCCインストール|image/Win/BCC1.png]]
998 次に設定ツールである''setbcc15b.exe''をダウンロードし、実行します。
999 [img[BCCインストール|image/Win/BCC2.png]]
1001 特に細かい設定をする必要はありません。''進む>>''をクリックし続けて実行すれば実行可能になります。
1002 [img[BCCインストール|image/Win/BCC4.png]]
1005 次に開発環境をインストールします。ここではオープンソースのRelo2を利用します。
1007 * [[Reloダウンロード|http://www.fifsoft.com/relo/download.php]]
1009 まずダウンロードページへ行き、''RELO v2.0 INSTALLER''をクリックし、ダウンロードします。
1010 [img[Reloダウンロード|image/Win/Psychlops_Relo1.png]]
1012 ダウンロードが終わるとインストーラの実行ファイルがありますので、''relosetup20.exe''をダブルクリックしてインストールを始めます。インストーラの指示に従ってインストールを完了します。
1013 [img[Reloインストール|image/Win/Psychlops_Relo2.png]]
1015 インストール終了後、まず最初に環境設定を行うためにRelo2を起動します。起動したらメニューバーのTools から Compilers を選択します。
1016 [img[Reloインストール|image/Win/Psychlops_Relo_New5.png]]
1018 開いたウィンドウで、Newボタンを押し、そのまま進め、New CompilerをBCCにする設定をします。各フィールドを以下のように指定します。
1021 * Path : C:\borland\bcc55\Bin\
1023 [img[Reloインストール|image/Win/Psychlops_Relo4.png]]
1026 !! Psychlopsの本体をインストール
1027 * [[Psychlops Download|http://psychlops.l.u-tokyo.ac.jp/?Download]]
1029 ダウンロードページへ行き、BCC版の最新版をクリックします。
1030 [img[Psychlopsダウンロード|image/Win/Psychlops_WinGL1.png]]
1032 zipファイルを解凍するとインストーラフォルダが現れますので、この中の''PsychlopsLib''フォルダ内にある''PsychlopsLib_xxxx.exe''をダブルクリックしてインストールを開始します。
1033 [img[Psychlopsインストール|image/Win/Psychlops_WinGL3.png]]
1035 インストーラの指示に従ってインストールを完了します。
1036 [img[Psychlopsインストール|image/Win/Psychlops_WinGL3.png]]
1038 !! Relo用のテンプレートをインストール
1039 zipファイルを解凍してできたインストーラフォルダの中の''ReloTemplate''フォルダ内にある''Psychlops_Relo2_Template_xxxxxxxx.exe''をダブルクリックしてインストールを開始します。インストーラの指示に従ってインストールを完了します。
1040 [img[Psychlopsインストール|image/Win/Psychlops_Relo3.png]]
1044 Reloを起動し、''メニューパー → File → New''を選択します。
1045 [img[新しいプロジェクト|image/Win/Psychlops_Relo_New1.png]]
1047 新しいプロジェクトのウィザードが開いたら、''Psychlops Application''を選択します。
1048 [img[新しいプロジェクト|image/Win/Psychlops_Relo_New2.png]]
1050 新しいプロジェクトの名前を選びます。作成後に場所は変更可能ですが、名前は作り直す以外に変更できませんのでご注意ください。
1051 [img[新しいプロジェクト|image/Win/Psychlops_Relo_New3.png]]
1053 Reloはプロジェクトの作成直後は保存場所が決まっておりませんので、まずはプロジェクトを保存します。パスにスペースまたは日本語を含んでいると実行に失敗することがありますので、ASCII文字のみのフォルダの中に作成してください。
1054 [img[新しいプロジェクト|image/Win/Psychlops_Relo_New4.png]]
1056 プログラムはSourcesカテゴリの中にある(プロジェクト名).cppにあります。標準のサンプルプログラムが入っていますので、まずはダブルクリックしてエディタを開き、中身を見てみます(最初から開いていることもあります)。
1058 下のようなウィンドウが開いたでしょうか?
1059 [img[ウインドウ説明|image/Win/Psychlops_Relo_New6.png]]
1060 プログラム編集エディタでブログラムを編集することができます。実際にはこのサンプルを消して、新しく自分のプログラムを書いていくわけですが、まず最初は正しくインストールされているかどうかを確かめるために、標準のサンプルをそのまま実行してみましょう。ツールバーの実行ボタンをおすと、プログラムが機械語に翻訳されて実行ファイルの作成・実行が行われます。
1062 Reloは現在のバージョンでは、起動直後にコンパイラの設定ウィンドウを開いてOKボタンを押す必要があります。''メニューパー → Tools → Compilers''を選択して、開いたダイアログでOKを押します。
1063 [img[新しいプロジェクト|image/Win/Psychlops_Relo_New5.png]]
1065 出来上がった実行ファイルはプロジェクトと同じフォルダにあります。
1066 [img[新しいプロジェクト|image/Win/Psychlops_Relo_New7.png]]
1069 <div title="1.3 インストール作業(Windows) " modifier="YourName" created="201002040143" changecount="1">
1070 <pre>!! OpenGLのドライバ設定の確認
1071 Psychlopsは画面の垂直同期信号に合わせて画面を更新しますが、この機能を正常に動作させるには、ディスプレイドライバのOpenGL描画設定で垂直同期にあわせるオプションを常にオンにする必要があります。
1072 一般的には''コントロールパネル > 画面''の設定の''モニタ''タブを選び、''詳細設定''ボタンから設定します。詳しくはご使用のビデオカードのマニュアルをご覧ください。
1074 !! CodeBlocks + GCCのインストール
1075 PsychlopsのWindowsで利用するには、まずコンパイラと開発環境をインストールする必要があります。ここでは両者がセットになっているCodeBlocks(MinGW gccコンパイラつき)をダウンロードして利用する例を紹介します。
1077 2010/01/31現在、CodeBlocks8.02 / Windows XP, Vista, 7に対応しておりますが、最新の対応状況につきましてはhttp://psychlops.l.u-tokyo.ac.jp/?Environmentからご確認ください。スクリーンショットはマニュアル作成時のものですので、バージョンが更新されていると異なる場合があります。ご了承ください。
1079 * [[CodeBlocks + GCCダウンロード|http://www.codeblocks.org/downloads/5]]
1081 まずダウンロードページへ行き、''codeblocks-8.02mingw-setup.exe''をSourceforgeまたはBerliOSのサーバからダウンロードします。どちらのサーバからダウンロードしても中身は同じです。
1082 [img[CodeBlocksダウンロード|image/Win/CodeBlocks_Win01.png]]
1084 ダウンロードが終わるとインストーラの実行ファイルがありますので、''codeblocks-8.02mingw-setup.exe''をダブルクリックしてインストールを始めます。インストーラの指示に従ってインストールを完了します。
1085 [img[BCCインストール|image/Win/CodeBlocks_Win02.png]]
1089 !! Psychlopsの本体をインストール
1090 * [[Psychlops Download|http://psychlops.l.u-tokyo.ac.jp/?Download]]
1092 ダウンロードページへ行き、Win32GL版の最新版をクリックします。
1093 [img[Psychlopsダウンロード|image/Win/Psychlops_WinGL1.png]]
1095 zipファイルを解凍するとインストーラフォルダが現れますので、この中の''PsychlopsLib''フォルダ内にある''PsychlopsFramework_Win32_x.x.x.exe''をダブルクリックしてインストールを開始します。
1096 [img[Psychlopsインストール|image/Win/Psychlops_WinGL3.png]]
1098 インストーラの指示に従ってインストールを完了します。
1099 [img[Psychlopsインストール|image/Win/Psychlops_WinGL5.png]]
1102 !! CodeBlocks用のテンプレートをインストール
1103 zipファイルを解凍してできたインストーラフォルダの中の''CodeblocksTemplate''フォルダ内にある''Psychlops_Codeblocks_Template_xxxxxxxx.exe''をダブルクリックしてインストールを開始します。インストーラの指示に従ってインストールを完了します。
1104 [img[Psychlopsインストール|image/Win/Psychlops_WinGL4.png]]
1108 CodeBlocksを起動し、''メニューパー → File → New → Project''を選択します。
1109 [img[新しいプロジェクト|image/Win/CodeBlocks_Win10.png]]
1111 新しいプロジェクトのウィザードが開いたら、''Psychlops GL Ploject''を選択します(見つからなければ選択ボックスをスクロールしてください)。
1112 [img[新しいプロジェクト|image/Win/CodeBlocks_Win11.png]]
1114 新しいプロジェクトの名前を決め、ファイルを保存する場所を選びます。作成後にフォルダごと移動することで場所は変更可能ですが、名前は作り直す以外に変更できませんのでご注意ください。
1115 [img[新しいプロジェクト|image/Win/CodeBlocks_Win13.png]]
1117 プログラムはSourcesカテゴリの中にあるmain.cppにあります。標準のサンプルプログラムが入っていますので、まずはダブルクリックしてエディタを開き、中身を見てみます(最初から開いていることもあります)。
1119 下のようなウィンドウが開いたでしょうか?
1120 [img[ウインドウ説明|image/Win/CodeBlocks_Win14.png]]
1121 プログラム編集エディタでブログラムを編集することができます。実際にはこのサンプルを消して、新しく自分のプログラムを書いていくわけですが、まず最初は正しくインストールされているかどうかを確かめるために、標準のサンプルをそのまま実行してみましょう。ツールバーの実行ボタンをおすと、プログラムが機械語に翻訳されて実行ファイルの作成・実行が行われます。
1124 出来上がった実行ファイルは、''プロジェクトのあるフォルダ > binフォルダ > Releaseフォルダ または Debugフォルダ'' の中にあります。この実行ファイルをコピーすることで頒布が可能ですが、メールなどに添付する際は必ずzipファイル等に圧縮してください。。
1127 <div title="1.3 各試行部分のプログラミング" modifier="Psychlops_DevelopperG" modified="200908190216" created="200709252145" changecount="2">
1128 <pre>[[1.1|1.1 実験計画のプログラミング]], [[1.2|1.2 Gaborパッチの事前描画]]節で実際の試行に先立つ計算は終了したことになります。
1129 ここでは実際の試行部分のプログラミングを行います。
1130 各試行の実行時はここで作成している実験の場合大きく分けて2つに分かれるのでした。
1131 a. 各試行における描画(刺激の提示):該当するGaborが描画されたImageインスタンスを用いた描画
1133 まずa.の部分についてもう少し詳しく考えてみると、以下のような手順をループさせれば良いことがわかります。
1136 # 試行開始待ち(被験者のキー入力待ち)
1141 この流れに従って試行番号"trial"番の試行をプログラムしてみます。
1142 ここで使う変数はあとでwhileループの外で宣言を行うので、とりあえず、宣言なしで変数を使っていきます。
1145 まず、この試行におけるGaborパッチの傾きの方向"direction"をPsychlops::random()命令を使って0か1にランダムに決定します。
1146 次に、試行番号trialから6.1で作成したクラスインスタンスinvarを用いて各独立変数を取得します。
1147 各試行における傾き量の番号("ori_now")と提示時間("dura_now")を求めるには~IndependentVariables::get()を使います。
1148 |!int ~IndependentVariables::get()|set(int vnum, int trial_now)|
1149 |~|~|int vnum: 独立変数の識別番号|
1150 |~|~|int trial_now: 試行番号|
1152 単に設定したinvarから各試行に対応した変数を取得するのであれば、以下のコードの様になるでしょう。
1154 ori_now=invar.get(0, trial);
1155 dura_now=invar.get(1, trial);
1158 しかしここでは実際に欲しい以下の2つの値を計算します
1159 * [[1.2|1.2 Gaborパッチの事前描画]]節で事前描画したImageの配列gIMGの中でこの試行でCanvasに転送されるImageの番号
1162 転送番号については、[[1.2|1.2 Gaborパッチの事前描画]]節で作った表をみると、呼び出す配列番号が(傾き量番号)+5 x (方向)であることがわかります。
1163 |!gIMGの配列番号|gIMG[0]|gIMG[1]|gIMG[2]|gIMG[3]|gIMG[4]|gIMG[5]|gIMG[6]|gIMG[7]|gIMG[8]|gIMG[9]|
1164 |!(傾き量, 方向) 番号|(0,0)|(1,0)|(2,0)|(3,0)|(4,0)|(0,1)|(1,1)|(2,1)|(3,1)|(4,1)|
1166 次に、刺激提示のフレーム数ですが、これはCanvasクラスのリフレッシュレートを取得する関数getRefreshRate()を使えばCanvasの宣言に依存せずに提示時間を制御できます。^^*1^^
1168 これらをまとめると変数の取得部は、以下の様なコードになります。
1170 direction=Psychlops::random(2); //0:right 1:left
1171 ori_now=invar.get(0,trial)+5*direction;
1172 dura_now=sampleA.getRefreshRate()*(invar.get(1,trial)/1000);
1174 ^^*1 この実験では必ず計算結果が整数フレームとなるようにあらかじめ変数を設定してあることに注意してください。^^
1177 次にユーザー(被験者)の準備が整って、キーを押すまでプログラムを"待ち"の状態にする部分をコーディングします。
1178 ここでは、試行開始のキーにはスペースキーを用います。さらに、被験者がわかりやすいように、プログラム側の準備が整って「待ち」の状態になっている間は試行が何番目に当たるかを表示するようにしてみます。
1179 キー入力待ちのコードは今まで何度も使ってきたとおりに、以下のように書きます。
1181 while(!Input::get(Keyboard::spc));
1183 試行番号を表示するにはCanvas::message()命令を使えばよいのですが、試行番号は試行毎に変化するので少し工夫が必要です。
1184 ここでは、Cの標準関数であるsprintf()関数を使って、試行番号を文字列に埋め込みます。この部分はあまりプログラムの流れには関係ないので、良くわからない方はとりあえず無視して先に進んでください。以上をまとめると以下のようなコードになります。
1188 for(int i=0; i<64; i++) trial_header[i] = 0; // 文字列の初期化
1189 sprintf(trial_header, "%s%d%s%d","Trial: ", trial, " /", trialNum ); //試行番号の埋め込み
1190 sampleA.message(trial_header, sampleA.getHcenter(), sampleA.getVcenter()-100, Color::green);//キー待ちメッセージの描画
1193 while(!Input::get(Keyboard::spc));
1197 いよいよ実際の刺激の描画ですが、これは特に難しくありません。Image::draw()命令を使って裏画面にori_now番のイメージを転送し、Canvas::flip(int)命令を使って、dura_nowフレーム分の描画時間を予約して描画を反映させます。この後に、Canvas::clear()命令を使えば、一定時間たつと自動的に刺激が消えるプログラムになります。
1200 gIMG[ori_now].draw();
1201 sampleA.flip(dura_now);
1206 ! 反応待ち(被験者のキー入力待ち)とデータの格納
1208 刺激描画が終わったら被験者のキー入力(f: 左/j: 右)を待ちます。プログラムは"f"か"j"が押されるまでは待ち、押されたら押されたキーに従って、0(j), 1(f)のいずれかの値を変数ansに格納します。[[6.1|6.1 Gaborパッチの事前描画]]節においてgIMGを描画する際、傾きの方向番号にふられた番号が0が右、1が左であったことに注意してください。また、ここでescを押すと、プログラムを途中終了するコードも入れておきます。
1212 if(Input::get(Keyboard::j)){ans=0;break;} //"j"なら右(0)
1213 if(Input::get(Keyboard::f)){ans=1;break;}//"f"なら左(1)
1214 if(Input::get(Keyboard::esc)){exit(0);}
1218 最後はデータの格納です。上の方でランダムに決定したdirectionとキー入力によって得られた値ansが同じならば正答(1),異なれば誤答(0)を配列answer のtrial番目に格納します。ついでに、後でデータを見やすくするためにここで使った2つ独立変数の値も配列orientationConditionと配列durationConditionに格納しておきます。
1220 if(ans==direction) answer[trial]=1;
1221 else answer[trial]=0;
1222 orientationCondition[trial]=invar.get(0, trial);
1223 durationCondition[trial]=invar.get(1, trial);
1227 以上をまとめて、ループの中に入れると、各試行部分のプログラミングは完成です。
1228 これまで保留していた変数の宣言がループの前にきちんと記述されていることやそれぞれの型がどのようになっているかにも注目してください。特に、answer, orientationCondition, durationConditionの配列を確保するためにはtrialNumをconst int型として宣言する必要があることには注意が必要です。
1231 const int trialNum=2*5*10;
1232 int answer[trialNum], orientationCondition[trialNum], durationCondition[trialNum], ans;
1233 int ori_now, dura_now;
1235 char trial_header[64];
1237 for(int trial=0; trial<trialNum; trial++){
1239 direction=Psychlops::random(2); //0:right 1:left
1240 ori_now=invar.get(0,trial)+5*direction;
1241 dura_now=sampleA.getRefreshRate()*(invar.get(1,trial)/1000);
1245 for(int i=0; i<64; i++) trial_header[i] = 0; // 文字列の初期化
1246 sprintf(trial_header, "%s%d%s%d","Trial: ", trial, " /", trialNum ); //試行番号の埋め込み
1247 sampleA.message(trial_header, sampleA.getHcenter(), sampleA.getVcenter()-100, Color::green);//キー待ちメッセージの描画
1249 while(!Input::get(Keyboard::spc));
1253 gIMG[ori_now].draw();
1254 sampleA.flip(dura_now);
1260 if(Input::get(Keyboard::j)){ans=0;break;}
1261 if(Input::get(Keyboard::f)){ans=1;break;}
1262 if(Input::get(Keyboard::esc)){exit(0);}
1264 if(ans==direction)answer[trial]=1;
1265 else answer[trial]=0;
1266 orientationCondition[trial]=invar.get(0, trial);
1267 durationCondition[trial]=invar.get(1, trial);
1271 <div title="1.4 更新とアンインストール" modifier="YourName" modified="200802191314" created="200709131956" changecount="3">
1272 <pre>2007/8/13以降のバージョンを使用していた場合は、Psychlopsの更新は通常のインストールとまったく同じです。ダウングレードする場合も同様です(つまり、単純に上書きしてしまってかまいません)。それ以前のバージョンが既にインストールされたシステムについては、一度アンインストールをしてからもう一度インストールを行ってください(下記参照)。
1274 Psychlopsは特にレジストリ等の設定を行いませんので、アンインストール時はインストールされたフォルダごと削除してください。Psychlopsは単なるC++ライブラリで、常駐等はしませんので、削除しない場合でもシステムに影響を与えることはありません。
1276 また、C++開発環境を自分で既に運用していて、デフォルトの場所以外の所にライブラリを置くことも可能です。この場合は、インストールを行った後に、自分でFrameworks以下にあるファイル・フォルダを目的の場所に移動して、ご自分でパスの設定をなさってください。
1279 ** /Library/Frameworks/Psychlops.framework
1282 ** (システムドライブ)\Library\Frameworks\Psychlops.framework
1283 *** Windowsには通常Libraryフォルダがありませんので、このフォルダ内にPsychlopsしか入っていなければLibraryフォルダごと削除してかまいません。
1284 ** Relo、Code::Blocksのテンプレートについては、テンプレート一覧ファイルを上書きしています。元に戻す必要がある場合は、再インストールする必要があります。</pre>
1286 <div title="1.4 終了処理とまとめ" modifier="Psychlops_DevelopperG" modified="200908190216" created="200709252145" changecount="1">
1287 <pre>最後にデータをファイルにセーブして、実験の終了メッセージを記録すれば、プログラミングは完了です。
1288 被験者の反応を記録した配列answerと実験条件を記録した配列orientationCondition, durationConditionをData::savearray()命令を使用して"~ExptData.dat"という名前のテキストファイル(tab区切り)にセーブします。
1292 sampleA.message("Press space key to exit.",
1293 sampleA.getHcenter(), sampleA.getVcenter()-100, Color::green);
1294 Data::savearray("ExptData.dat",
1295 "Duration /t Orientation /t Answer", trialNum,
1296 durationCondition, orientationCondition, answer );
1299 これで、すべてのパーツが完成しました。一つにまとめると以下の様になります。
1300 [[Gabor方位判断の実験プログラム例]]
1301 ぱっと見たときには長そうなプログラムですが、よく見ると実際にはクラスの宣言部分がほとんどを占めていて、実験に必要な部分のプログラム(つまりあなたが理解して、書かなければいけない部分!) は比較的短いコードであることがわかります。
1302 コンパイルして1つ1つの部分が正しく実行されているかどうか確かめてみてください。</pre>
1304 <div title="1.5 Psychlopsの基本構造" modifier="Psychlops_DevelopperG" modified="200908190224" created="200711081840" changecount="3">
1305 <pre>次節で詳しく述べるように、PsychlopsはC++のライブラリになっています。このライブラリは、いくつかの命令のグループから成り立っています。それぞれの命令のことをメソッド、グループのことをクラスと呼びます。Psychlopsを用いた描画は、ある変数がどのクラスに属する変数なのかを宣言し、この変数に対してメソッドを指定することで行います。より詳細な具体例は次節や、このマニュアルの中で随時見ていくと理解できるでしょう。今は、このようなグループがいくつかPsychlopsの中にあると言うことだけを理解しておけば十分です。以下に代表的なPsychlopsのクラスをまとめておきます。
1307 |!Psychlops::Canvas|描画ウィンドウの確保と基本的な描画の命令セット|
1308 |!Psychlops::Point|ドット描画と座標指定のための命令セット|
1309 |!Psychlops::Rectangle|矩形描画と矩形範囲指定のための命令セット|
1310 |!Psychlops::Color|描画色指定のための命令セット|
1311 |!Psychlops::Image|オフスクリーン描画のための命令セット|
1312 |!Psychlops::Matrix|行列演算のための命令セット|
1313 |!Psychlops::Clock|時間計測のための命令セット|
1314 |!Psychlops::Input|入出力取得のための命令セット|
1316 それぞれのクラスの先頭についているPsychlops::はこれらのクラスがPsychlopsというライブラリの中のクラスであることを示しています。たとえば、Colorと言うクラスは、別のライブラリの中にも存在するかも知れません。その場合、プログラムを解釈する側は、このクラスがどのライブラリのクラスであるかを判断できなくなってしまいます。これを避けるために、スコープ演算子"::"を使って、あるクラスがどのライブラリに所属するかを示すのです。名字のようなものだと思うとわかりやすいかも知れません。
1318 このほかにもPsychlopsにはData, File, Range等の特殊なクラスが存在しますが、これらについては具体的な使い方とともに解説します。
1322 <div title="1.6 Psychlopsを使ったプログラミング" modifier="Kazushi Maruya" modified="200711081830" created="200709170801" changecount="11">
1323 <pre>Psychlopsを使ったプログラミングはC++のプログラミングと同様ですが、必ずしもその全てを理解しておく必要はありません。(もちろん、理解しているほうがより柔軟なプログラミングができますが。)
1324 ここでは、psychlopsを用いたプログラミングの際に、必要不可欠だと思われる基本要素についてごく簡単な説明を行います。より詳細なC++プログラミングを学びたい方は入門書・専門書を参考にされることをおすすめしますが、ここで説明するいくつかのことに対する大まかなイメージをもっておけばPsychlopsのプログラミングにとっては十分なはずです。より細かなことは実際のプログラミングを通して理解することをおすすめします。
1326 [[1.6.1 プログラミングとは?]]
1327 [[1.6.2 Psychlopsを使ったプログラムの形]]
1331 [[1.6.6 クラスと関数]]</pre>
1333 <div title="1.6.1 プログラミングとは?" modifier="YourName" modified="200802191315" created="200709170805" changecount="4">
1334 <pre>まず最初に、プログラムとは何かについて説明します。
1336 プログラムとは、画像や数値などのデータを、一定の手続きに従って操作する過程を、文字で表記したものです。たとえば、Excelなどの表計算でセルに書き込む式も、一種のプログラムということができます。
1337 ここでは、Psychlopsのプログラムを例に考えてみます。
1339 プログラムでまず最初に必要なことは、取り扱うデータや装置を準備することです。
1340 コンピュータを使わない実験でも、画像や画像を配置する画面などをあらかじめ準備しなければなりませんが、
1341 これと同じことをコンピューター上でも行います。
1347 Image natural_image;
1349 この1行は、画像(Iamge)にnatural_imageという名前をつけて取り扱う準備をしたことになります。
1351 次に、データを操作する手続きを記述します。手続きは
1352 ''操作が行われる対象.操作方法(関係するデータ)''
1353 の方法で記述します。英語のSVO文法に近い記述法ですね。
1356 natural_image.load("Natural Image.png");
1358 この一行は、natural_imageという画像として、「Natural Image.png」という名前の画像ファイルを読み込む手続きを表します。
1361 natural_image.centering().draw();
1363 この一行は、先ほど読み込んだ画像を、画面の中央に配置(センタリング)し、画面に描画する手続きを表します。
1365 プログラムとは、「データを準備する」「データを操作する」ことについて、行う順番どおりに書いていくことなのです。
1366 準備や操作に区切りをつけるには「;」(セミコロン)を記述します。</pre>
1368 <div title="1.6.2 Psychlopsを使ったプログラムの形" modifier="YourName" modified="200802191316" created="200709170816" changecount="5">
1369 <pre>では、Psychlopsで動く簡単なプログラムを例にとって最も簡単なPsychlopsのプログラムの形を見てみます。
1372 #include <psychlops.h>
1373 using namespace Psychlops;
1375 void psychlops_main() {
1376 Canvas display( Canvas::fullscreen );
1377 Image natural_image;
1378 natural_image.load("Natural Image.png");
1379 natural_image.centering().draw();
1384 先頭の2行はPsychlopsを使うための命令です。
1385 意味はわからなくてもかまいませんが、Psychlopsの命令をコンピュータに理解させるためには必ずこの2行をプログラムの先頭に書いておく必要があります。
1388 #include <psychlops.h> // このプログラムにPsychlopsを含める
1389 using namespace Psychlops; // このプログラムはPsychlopsを使う
1392 次の行は、Psychlopsを用いたプログラム本体を書く場所(メインルーチン)を示す行です。
1394 void psychlops_main() { // psychlopsが主に扱うブロック
1396 Psychlopsは、ここから最後の行の閉じ括弧(})までのブロックをまず最初に実行します。
1399 Canvas display( Canvas::fullscreen );
1400 Image natural_image;
1401 natural_image.load("Natural Image.png");
1402 natural_image.centering().draw();
1405 この5行が実際にコンピュータに対して与える命令になります。
1406 今はこの5行の具体的な内容については述べませんが、ユーザーは基本的にはこの部分を書き換えて描画を実行させることになります。</pre>
1408 <div title="1.6.3 配列" modifier="PsychlopsAdmin" modified="200709170821" created="200709170817" changecount="1">
1409 <pre>これまでの例では画像はひとつだけでしたが、何枚かの画像を選んで出したい場合があります。
1410 いくつかのデータに似たような操作をしたい場合、「配列」が役に立ちます。配列とは、データがまとめて並んでいるもののことを言います。内部にいくつも区切りがある箱のようなものを想像するとよいかもしれません。
1412 たとえば、画像を3枚まとめて扱いたい場合、以下のようにデータを準備します。
1414 Image natural_images[3];
1417 これで3枚の画像配列が用意されました。次に、データの並びの中のそれぞれの画像を操作してみましょう。
1420 natural_images[0].load("Natural Image0.png");
1421 natural_images[1].load("Natural Image1.png");
1422 natural_images[2].load("Natural Image2.png");
1426 と書くことで、配列のうち数字で指定した順番の要素(この場合は画像)を操作することができます。
1427 順番は0から始まることに注意してください。画像3枚を用意した場合、0,1,2になります。</pre>
1429 <div title="1.6.4 for命令" modifier="Kazushi Maruya" modified="200711082123" created="200709170819" changecount="7">
1430 <pre>この画像配列を順番に表示してみましょう。ここでは、繰り返し操作を記述するfor文を使うことにします。
1432 for(int i=0; i<3; i++) {
1433 natural_images[i].centering().draw();
1437 for文は、{ } で囲まれたブロックを複数回実行することを示します。
1439 [img[image/formethod.png]]
1440 この命令の後に続く{}で囲まれた部分のブロックは繰り返し、カウンタ(i)の値を0,1,2の順番で変更しながら実行されます。
1443 natural_images[i].centering().draw();
1450 for(int i=0; i<3; i++) {
1451 natural_images[i].centering().draw();
1457 natural_images[1].centering().draw();
1459 natural_images[2].centering().draw();
1461 natural_images[3].centering().draw();
1466 image.centering().draw()は画像をセンタリングして描画する操作なので、以下のプログラムをNatural Image0.png, Natural Image1.png, Natural Image2.pngという3つのファイルがあるディレクトリで実行すると、3つの画像が順番に表示されます。
1469 #include <psychlops.h>
1470 using namespace Psychlops;
1472 void psychlops_main() {
1473 Canvas display( Canvas::fullscreen );
1474 Image natural_image;
1476 natural_images[0].load("Natural Image0.png");
1477 natural_images[1].load("Natural Image1.png");
1478 natural_images[2].load("Natural Image2.png");
1480 for(int i=0; i<3; i++) {
1481 natural_images[i].centering().draw();
1489 <div title="1.6.5 変数型" modifier="PsychlopsAdmin" modified="200709170825" created="200709170821" changecount="3">
1490 <pre>ここまではPsychlopsで扱えるデータを題材に解説してきましたが、最後に、C++言語で取り扱うことのできる基本的なデータについて説明します。
1491 これらのデータの中でもっとも基本的なものは数値です。数値は、主に整数型と小数点型に分かれます。
1493 ** 整数(''int''eger)の略で、整数を取り扱います。小数点以下は切り捨てられますので、注意してください。
1495 ** 浮動小数点を取り扱います(倍精度浮動小数点 ''double'' precision floating point number)
1503 のように使います。データに特定の数値を代入する(格納する)場合は、
1509 C++にはこのほかにも文字列型(char *やstd::string)、真偽値型(bool)などがありますが、ここでは詳しくは説明しません。これらの型の使い方については、これ以降のプログラミングの実例のなかで必要なところだけを説明していきます。詳細を知りたい型は、CやC++の入門書をご覧ください。</pre>
1511 <div title="1.6.6 クラスと関数" modifier="YourName" modified="200802191319" created="200709171609" changecount="14">
1513 クラスとは、関係する変数(データ)や関数(メソッド)をまとめたものです。
1514 [[1.5節|1.5 Psychlopsの基本構造]]で概観したように、Psychlopsでは、画面(Canvas)や画像(Image)、図形(Ractangle)など、描画に関するさまざまなクラスを用意しています。各クラスには、その機能に関連した固有の関数があります。
1517 * Image.pix(x, y, color)
1518 * Image.rect(rect, color)
1519 * Image.save(filename)
1520 *Image. load(filename)
1524 関数はには''返り値''と''引数''があります。たとえば累乗関数(power)を例に見てみましょう。
1528 この式を実行すると、変数aには10の3乗である1000が代入されます。累乗関数が必要とする値である10と3を引数といい、返される計算結果1000を返り値と呼びます。
1530 pow関数の引数のうち、1番目は累乗される数、2番目は累乗する数です。順番や型は固定されていますのでご注意ください。順番を間違えると、計算結果も変わってきてしまいます。たとえば、
1534 を実行すると、3の10乗である59049がaに代入されます。
1538 Psychclopsでは、一部の関数を.でつなぎながら連続で書くことができます。
1540 rect.centering().shift(x, y).draw(Color::red);
1542 この書き方は他のC言語のプログラムではあまり見かけませんが、Psychlopsでは多用されています。プログラムの長さを短くし読みやすくすることができます。
1544 この書き方ができるものは、関数の返り値として変数自身が返されるものに限ります。
1549 このマニュアルでは、各クラスの関数について表で説明が出てきます。この表の読み方を、Imageを表示する関数を題材に説明します。
1551 |!Image::draw()|draw()|指定したオフスクリーンを描画します|
1552 |~|draw(double left, double top)|指定したオフスクリーン領域を(left,top)を中心とした座標に設定します|
1553 |~|~|double left: 描画するオフスクリーン領域の座標軸xを指定|
1554 |~|~|double top: 描画するオフスクリーン領域の座標軸yを指定|
1556 一番左の列は関数の名前を示します。ここでまたスコープ演算子が関数名の名前についていますが、これは1.5節と同様にその関数がどのクラスに所属しているかを示すためのものです。
1561 と書く様な気がしてしまいますが、いくつかのクラスをのぞいて^^*1^^このような書き方をすることはありません。なぜなら、実際のプログラムの中は、あるクラスに属する変数(インスタンス、と呼ばれます)に対しての命令しか行わないからです。Image型として宣言されたインスタンスに対してメソッドを実行するときには"."演算子を用います。たとえば、Image::draw()命令を実際に使うときには以下のような形になるでしょう。
1563 Psychlops::Image img;
1567 *1 Input, Display等のハードウェアと直結したクラスはインスタンスを宣言せずに使います。これは、これらのクラスがPsychlopsのイニシャライズ時にあらかじめ宣言されたデフォルトのインスタンスを持っていて、これに対するメソッドの実行を行うように設計されているためです。
1569 上記の表は、Image::draw()というメソッドに
1571 * Image::draw(double left, double top)
1572 という二つの使い方があることを示しています。
1575 二番目の列は、引数の違ういくつかのバージョンが列挙されています。
1576 2つめの使い方Image::draw(double left, double top)では、括弧の中にコンマで区切られた2つの文字列があります。
1577 これは、この使い方では関数の引数として2つの変数を代入することができることを示しています。
1578 [img[image/FunctionNotation.png]]
1579 一つめの引数はdouble leftと書かれています。これは、一つめの引数がdouble型の変数でなくてはいけないことを示しています。(ただし、後で書くようにleftと言う名前にする必要はありません。)このマニュアルの中ではそれぞれのメソッド型名(スペース)名前という形でそれぞれの使い方に必要な引数の形式を示しています。
1581 実際に使用するときは、型名を書く必要はありません。left,topという2つの引数をこの関数に渡すときには以下のように書きます。
1583 Psychlops::Image img;
1584 double left=10,top=10;
1588 一番右の列は、各バージョンでの引数の説明になっています。Image::draw(double left, double top)の右側の欄を見ると、各引数について、一番目が表示したいx座標、二番目がy座標であることがわかります。
1590 各引数には名前がついていますが、関数を呼び出す際に引き渡す変数はこの名前である必要はありません。マニュアルでは
1591 * image.draw(left, top)
1593 * image.draw(100, 200)
1594 * image.draw( x, y )
1595 など任意の値を渡すことが可能です。</pre>
1597 <div title="2. デモを作成する" modifier="Psychlops_DevelopperG" modified="200908190221" created="200712061915" changecount="3">
1598 <pre>Psychlopsには、プログラム内の変数をプログラムのユーザがインタラクティブに操作できるデモアプリケーションを簡単に作成するためのクラスIndependentが用意されています。
1599 この節では、Independentクラスについて簡単に説明して、これを用いたプログラムの作成例について解説します。
1601 [[2.1 独立変数Independent]]
1605 <div title="2. 基本的な描画を行う" modifier="Psychlops_DevelopperG" modified="200909300724" created="200708211944" changecount="16">
1606 <pre>[[2.1 描画領域の宣言]]
1608 [[2.1.2 Canvasの基本構造と操作]]
1609 [[2.1.3 Canvasのメンバ取得と変数の表示]]
1611 [[2.2.1 画面上に点を描画する-Pointクラス1-]]
1612 [[2.2.2 画面上に線を描画する]]
1613 [[2.2.3 画面上に四角形を描画する-Rectangleクラス1-]]
1614 [[2.2.4 画面上に円を描画する]]
1615 [[2.2.5 描画する図形の色を指定する-Colorクラス-]]
1616 [[2.2.6 画面上に縞を描画する-Rectangleクラス2-]]
1617 [[2.2.7 画面上に縞を描画する-Pointクラス2-]]
1618 [[2.2.8 運動する図形を描画する-Rectangleクラス3-]]
1619 [[2.3 文字列の描画]]</pre>
1621 <div title="2.1 描画領域の宣言" modifier="Psychlops_DevelopperG" modified="200908190225" created="200708212010" changecount="6">
1622 <pre>この節ではPsychlopsのもっとも基本的なクラスであるCanvasクラスの宣言方法と初歩的な操作を説明します。
1624 [[2.1.2 Canvasの基本構造と操作]]
1625 [[2.1.3 Canvasのメンバ取得と変数の表示]]</pre>
1627 <div title="2.1 独立変数Independent" modifier="Psychlops_DevelopperG" modified="200908190217" created="200710220054" changecount="1">
1630 Independentクラスは、プログラム中で独立変数に当たる変数を制御するためのオプション機能です。現在はデモ用のコンソールで使用することが出来ます。
1632 通常、プログラムは画面を描画するなどの作業を行っています。プログラムの中にはたくさんの変数や関数が入っています。
1633 [img[Independentなし|image/Independent1.png]]
1635 プログラム中の変数をIndependentに登録すると、もともとのプログラムの動作を変えずにデモ用のコンソール機能などを追加することが出来ます。
1636 [img[Independentあり|image/Independent2.png]]
1641 (Tiddly Wikiの機能上の都合、記号が全角で表示されていますが、実際は半角です)
1642 |!Independent|<< 変数|Independentクラスに登録する変数を指定します。|
1643 |~|| 文字列|変数につけるラベルを指定します|
1644 |~|| Range|Independenクラスtに指定する変数の変動範囲を指定します。変動範囲チェックはデモコンソールなどからの書き換え時にしか行われないため、元のプログラムで変動範囲外に変化する場合は検出できません。|
1645 |~|| 数値|デモコンソールでのキー押しあたり変化量を設定します。2つ続けて書いた場合、一方がShiftキーとの同時押し時の変化量になります。|
1646 現在のところ(2007年11月現在)、Independentクラスに登録できる変数は数値型(int, double等)のみです。
1648 !!Independentクラスへの登録
1649 以下では、例として3つの変数をIndependentクラスに登録する書き方例を示します。
1651 double rect_size = 100;
1652 double rect_lum = 0.5;
1653 double bg_lum = 0.2;
1656 Independent << rect_size | "Rect Size" | 1< rng< 500 | 10.0 | 2.0 ;
1657 Independent << rect_lum | "Rect Luminance" | 0.0<=rng<=1.0 | 0.1 | 0.05;
1658 Independent << bg_lum | "BG Luminance" | 0.0<=rng<=1.0 | 0.1 | 0.05;
1661 ここで注意することは、変数に適正な初期値を与えておくことです。もしも、Indpendentで設定される範囲の外に初期値が設定されてしまうと、実行時に変更することができなくなります。変数の宣言の時に忘れずに初期値の設定をしておくことをおすすめします。
1664 <div title="2.1.1 Canvasの宣言" modifier="Psychlops_DevelopperG" modified="200910080812" created="200708240239" changecount="25">
1667 絵を書くときには描く筆や絵の具のほかに紙などのキャンバスが必要です。
1668 この処理はコンピュータ上に絵を描くための"Canvas(描画のためのウィンドウ)"を用意します。
1670 Canvasが確保されていないと描画処理を記述しても処理されません。
1671 描画処理を行う前に必ずCanvasの宣言が必要となります。
1674 キャンバスを用意する命令には様々な書式があるのですが、その基本は以下の2つです。
1676 一つは解像度、リフレッシュレートを変更せずに、画面いっぱいに領域を確保する
1677 Canvas(Canvas::fullscreen)
1679 もう一つは解像度、リフレッシュレートを変更して、画面いっぱいに領域を確保する
1680 Canvas(int,int,int,double)
1683 後者で指定した画面モードが選べなかった場合は、例外が送出されプログラムは強制終了します。
1685 そのほかに Canvas::windowスイッチを使ってウインドウを開いて画面を取得する方法があります。
1686 この方法では描画の垂直同期信号への同期が保証されないので(くわしくは[[2.1.2 Canvasの基本構造と操作]]で説明します)、デモやプログラムのデバッグ時に使うとよいでしょう。
1687 詳しくは[[関数一覧|Canvasの宣言]]をご覧ください。
1688 * 参考[[Tips: Canvasとリフレッシュレートの詳細]]
1691 |~|Canvas(Canvas::fullscreen, const Display disp)|>|!現在の画面モードで画面を確保する場合に使います|
1692 |~|~|Canvasmode mode|[[表示モード|CanvasMode]](フルスクリーンorウィンドウ)を選択|
1693 |~|~|const [[Display|Displayの指定値]] disp|[[表示するディスプレイ|Displayの指定値]]を選択(省略可能)|
1694 |~|~|>|マルチディスプレイ環境では、最後の引数でどのディスプレイに表示するか指定できます。省略時はプライマリディスプレイになります。|
1695 |~|Canvas(int width, int height, int colordepth, double refreshrate, const Display disp)|>|!画面モードを指定してフルスクリーン画面を確保します|
1696 |~|~|int width|水平方向の解像度(画面の横幅)を指定|
1697 |~|~|int height|垂直方向の解像度(画面の縦幅)を指定|
1698 |~|~|int colordepth|カラーモード(ビット)を指定|
1699 |~|~|double refreshrate|リフレッシュレート( Hz )を指定|
1700 |~|~|const [[Display|Displayの指定値]] disp|[[表示するディスプレイ|Displayの指定値]]を選択(省略可能)|
1701 |~|~|>|指定した画面モードが選べなかった場合は、例外が送出されプログラムは強制終了します。|
1705 <例1: Canvas::fullscreenオプションを使った宣言例>
1706 #include <psychlops.h>
1707 using namespace Psychlops;
1709 void psychlops_main() {
1710 Canvas sampleA(Canvas::fullscreen);
1714 <例2: 各属性値を指定した宣言例>
1715 #include <psychlops.h>
1716 using namespace Psychlops;
1718 void psychlops_main() {
1719 Canvas sampleB(1024,768,32,60.0);
1724 <例2: 各属性値を指定した宣言例>
1725 #include <psychlops.h>
1726 using namespace Psychlops;
1728 void psychlops_main() {
1729 Canvas sampleC(Canvas1024,768,32,60.0);
1734 上記のソースでは「psychlops_main()」クラスの中で
1735 sampleA,sampleBという名前のCanvasを宣言しています。
1736 これらのプログラム内でCanvas型(クラス)として宣言された変数はインスタンスと呼ばれます。
1737 マニュアル上では、ある命令hoge()の形式を説明するときにCanvas::hoge()と書きますが、実際にプログラム内で宣言されたCanvasに対して命令を実行したいときは、このインスタンス名を使って『インスタンス名.命令文()』という形式で記述します。たとえば、Sample.hoge(hoge)といった形です。より具体的な例はこれ以降に何回も現れます。
1738 ここで注意しなくてはならない点として、ふつうCanvasを宣言する場所はpsychlops_main() 内であることです。つまり、この宣言名はpsychlops_main() 内でしか有効ではありません。
1739 psychlops_main()で呼び出す他の関数内でCanvasを使用したいときは、その関数にCanvasのポインタを引き渡すかDisplayクラスを使って描画する必要があります。
1740 * 参考[[Tips::CanvasクラスとDisplayクラス]]</pre>
1742 <div title="2.1.2 Canvasの基本構造と操作" modifier="Psychlops_DevelopperG" modified="200910080810" created="200708240240" changecount="7">
1743 <pre>Psychlopsでは2つの画面を交互にディスプレイに提示します。
1744 この2つの画面が対となって、1つのCanvasを構成しています。たとえば、紙に裏表があるようなものだと思ってください。
1745 この2枚の画面に描画をすることとflip()メソッドを使って表画面と裏画面を切り替えることがCanvasクラスに対する主な操作です。
1747 [img[image/canvasstructure.png]]
1749 !Canvas::clear() -画面を塗りつぶす-
1750 はじめにCanvasを何かの色で塗りつぶしてみましょう。
1751 そのための処理がCanvas::clear()命令です。
1752 書式はCanvas::clear([[Psychlops::Color]] col,TargetSurface)です。いずれの引数も省略可能です。
1753 |!Canvas::clear()|clear([[Psychlops::Color]] col,TargetSurface)|指定したCanvasを指定色で塗りつぶします|
1754 |~|~|[[Psychlops::Color]] col :塗りつぶす色を指定|
1755 |~|~|TargetSurface:塗りつぶす画面を指定|
1756 |~|clear(TargetSurface)|指定したCanvasを塗りつぶします。色の指定には[[Canvas::setClearColor()]]命令を使用します|
1757 |~|~|TargetSurface:塗りつぶす画面を指定|
1759 2つめの引数は少しわかりにくいかもしれません。
1760 clear()命令は2つめの引数を指定することで、今ディスプレイに表示されている画面(紙の表)と、表示されていない画面(紙の裏)のいずれを(あるいは両方を)塗りつぶすのかを指定することができます。
1761 省略した場合は表示されていない画面を塗りつぶします。
1764 !!Canvas::clear()の書き方
1767 #include <psychlops.h>
1768 using namespace Psychlops;
1770 void psychlops_main() {
1772 Canvas sampleA(Canvas::fullscreen);
1773 sampleA.clear(Color::white,Canvas::FRONT);
1776 今ディスプレイに表示されている画面(紙の表)を白く塗りつぶせ、という命令文ですが
1777 これでは描画が一瞬で結果がわかりにくいですね。
1778 「while(!Input::get(Keyboard::spc));」を追加してスペースキーが押されるまで今ディスプレイに表示されている画面(紙の表)が白く表示されるかを確認できるようにしましょう。
1779 この命令の詳細については、[[4.1節|4.1 外部装置(キーボード/マウス)の操作内容を取得する]] で詳しく説明します。今はこの文がスペースキーの入力待ちをする文であると思っていてください。
1782 #include <psychlops.h>
1783 using namespace Psychlops;
1785 void psychlops_main() {
1787 Canvas sampleA(Canvas::fullscreen);
1788 sampleA.clear(Color::white,Canvas::FRONT);
1789 while(!Input::get(Keyboard::spc));
1793 !Canvas::flip() -画面を入れ替える-
1794 上の例では、オプションを使って現在表示されている画面に対して描画を実行しました。
1795 しかし、これは例外的な描画で、Psychlopsでは描画処理は表示されていない画面(裏画面)に対して行われます。
1796 従って、描画内容を画面に反映させるためには、表示画面と裏画面を入れ替える必要があります。
1797 この表と裏の2つの画面を入れ替え処理を行う命令がCanvas::flip()命令です。
1798 つまり、描画内容はCanvas::flip() 処理で画面を入れ替えることで初めて画面上に表示されます。
1799 この命令は画面の垂直同期信号(=ビデオカードが1枚の画面を書ききったことを示す信号; Vsync)に同期して、画面の切り替えを行います。
1800 * 参考 [[Tips: Canvas::flip()とコマ落ち]]
1802 |!Canvas::flip()|flip(int)|int:入力値のリフレッシュ分描画内容を表示します|
1803 |~|~|引数を入力しない場合(flip())、flip(1)が指定されます|
1804 |~|flipAfter(int wait)|int wait回のリフレッシュ後まで待ってからflip()を実行して描画内容を画面に反映させます|
1805 |~|flipAndWait(int wait)|もっとも近い垂直同期のタイミングでflip()を実行した後に、int wait回ののリフレッシュ分の表示を予約します。次にflip()命令がコールされても、int wait回ののリフレッシュ分が経過するまでは何も起こりません(flip()と全く同じ効果ですが、引数は省略不可)|
1807 flip()系の命令は画面描画の時間制御をする上でもっとも重要な命令です。上の表だけではわかりにくいでしょうから、図と実例を用いて各命令の働きをもう少し詳しく見ていきましょう。
1809 flipAfter(), flipAndWait()命令の動きを図解すると以下のようになります。以下の図ではflipAfter(3)とflip(3)(あるいはflipAndWait(3))の場合を例としてあげました。
1810 [img[image/psychlops_flip.png]]
1812 !!Canvas::flip()の書き方1
1815 #include <psychlops.h>
1816 using namespace Psychlops;
1818 void psychlops_main() {
1820 Canvas sampleA(Canvas::fullscreen);
1821 sampleA.clear(Color::blue,Canvas::BACK);
1822 while(!Input::get(Keyboard::spc));
1826 例2では今ディスプレイに表示されている画面(紙の表)の色を塗りつぶしましたが
1827 今回は表示されていない画面(紙の裏)を塗りつぶし、それをflip()命令で表画面に入れ替えました。
1829 !!Canvas::flip()の書き方2
1830 次は、flip()が実行されるタイミングを制御してみましょう。
1831 flip()命令の引数を省略すると、flip()命令は、描画の計算が終了した時点からもっとも早い同期タイミングで画面の切り替えを行います。
1835 #include <psychlops.h>
1836 using namespace Psychlops;
1838 void psychlops_main() {
1839 Canvas sampleA(Canvas::fullscreen);
1840 sampleA.clear(Color::blue,Canvas::BACK);
1842 sampleA.clear(Color::cyan,Canvas::BACK);
1844 while(!Input::get(Keyboard::spc));
1847 青→シアンに画面の色が塗りつぶされるのを確認できたでしょうか。
1848 次はflipAfter()で同様のソースを実行します。
1851 #include <psychlops.h>
1852 using namespace Psychlops;
1854 void psychlops_main() {
1855 Canvas sampleA(Canvas::fullscreen);
1856 sampleA.clear(Color::blue,Canvas::BACK);
1857 sampleA.flipAfter(60);
1858 sampleA.clear(Color::cyan,Canvas::BACK);
1859 sampleA.flipAfter(60);
1860 while(!Input::get(Keyboard::spc));
1863 <例4>と<例5>の結果の差が見えたでしょうか。
1864 <例4>は60フレームのリフレッシュ分描画内容を表示したのに対し
1865 <例5>は60フレームのリフレッシュ分後、描画内容を表示しました。
1866 <例6>はflipAndWait()による画面入替えの確認をします。
1867 こちらはflip();と同様の結果を返します。
1868 <例4>と同じ結果が返るか確認してください。
1871 #include <psychlops.h>
1872 using namespace Psychlops;
1874 void psychlops_main() {
1875 Canvas sampleA(Canvas::fullscreen);
1876 sampleA.clear(Color::blue,Canvas::BACK);
1877 sampleA.flipAndWait(60);
1878 sampleA.clear(Color::cyan,Canvas::BACK);
1879 sampleA.flipAndWait(60);
1880 while(!Input::get(Keyboard::spc));
1884 <div title="2.1.3 Canvasのメンバ取得と変数の表示" modifier="Psychlops_DevelopperG" modified="200910080827" created="200708240242" changecount="29">
1885 <pre>!Canvas::getXXXX()
1886 Psychlopsではより複雑な描画に必要なCanvasの様々な画面情報を、Canvas::getXXXX()命令を使って取得します(XXXXにはそれぞれの情報に対応した名前が入ります)。
1887 |!Canvas::getXXXX()|getCenter()|画面の中心座標(x,y)を取得します|
1888 |~|getHcenter()|横方向の中心座標(x)を取得します|
1889 |~|getVcenter()|縦方向の中心座標(y)を取得します|
1890 |~|getHeight()|画面の高さ(ピクセル)を取得します|
1891 |~|getWidth()|画面の幅(ピクセル)を取得します|
1892 |~|getColorDepth()|カラーモード(ビット)を取得します|
1893 |~|getRefreshRate()|リフレッシュレート(Hz)を取得します|
1895 [img[image/canvasget.png]]
1897 !!Canvas::getXXXX()の書き方
1901 #include <psychlops.h>
1902 using namespace Psychlops;
1904 void psychlops_main() {
1906 Canvas sampleA(Canvas::fullscreen);
1907 sampleA.getCenter();
1908 sampleA.getHcenter();
1909 sampleA.getVcenter();
1910 sampleA.getHeight();
1912 sampleA.getColorDepth();
1913 sampleA.getRefreshRate();
1917 while(!Input::get(Keyboard::spc));
1920 このコードを実行するだけでは、取得結果を確認できません。
1921 次に取得結果を確認するため、画面に取得した変数の値を表示してみましょう。
1923 !Canvas::var()とCanvas::msg()
1924 取得した変数の値を画面に表示するにはCanvas::var()命令を使います。
1925 |!Canvas::var()|var(string str, double x, double y,[[Psychlops::Color]] col, bool fillForward)|
1926 |~|~|>|>|string str|画面表示をする変数を指定|
1927 |~|~|>|>|double x|画面表示をする座標(x)を指定|
1928 |~|~|>|>|double y|画面表示をする座標(y)を指定|
1929 |~|~|>|>|[[Psychlops::Color]] col|描画する点の色を指定(設定しない場合、白が設定される)|
1930 |~|~|~|bool fillForward|表示位置の指定|true |指定座標(x,y)から左部分に表示する|
1931 |~|~|~|~|~|false |指定座標(x,y)から右部分に表示する|
1932 |~|~|~|~|>|>|(指定しない場合、falseが設定される)|
1935 さらに、わかりやすくするためにラベルをつけましょう。
1936 定型の短い文章を画面上に表示する方法としてCanvas::msg()命令があります。
1937 |!Canvas::msg()|msg(char* string,double x,double y,[[Psychlops::Color]] col)|char* stringを座標(x,y)に指定色で表示します|
1938 |~|~|schar* string:画面表示をする文字列を指定|
1939 |~|~|double x:画面表示をする座標(x)を指定|
1940 |~|~|double y:画面表示をする座標(y)を指定|
1941 |~|~|[[Psychlops::Color]] col:描画する点の色を指定(設定しない場合、白が設定される)|
1942 |~|msg(string str,double x,double y,[[Psychlops::Color]] col)|string strを座標(x,y)に指定色で表示します|
1943 |~|~|string str:画面表示をする文字列を指定|
1944 |~|~|double x:画面表示をする座標(x)を指定|
1945 |~|~|double y:画面表示をする座標(y)を指定|
1946 |~|~|[[Psychlops::Color]] col:描画する点の色を指定(設定しない場合、白が設定される)|
1948 |[img[image/canvasvar.png]]|[img[image/canvasmsg.png]]
1953 #include <psychlops.h>
1954 using namespace Psychlops;
1957 double d1,d2,d3,x,y;
1958 Psychlops::Point point1;
1960 void psychlops_main() {
1962 Canvas sampleA(Canvas::fullscreen);
1964 point1=sampleA.getCenter();
1965 d1=sampleA.getHcenter();
1966 d2=sampleA.getVcenter();
1967 a1=sampleA.getHeight();
1968 a2=sampleA.getWidth();
1969 a3=sampleA.getColorDepth();
1970 d3=sampleA.getRefreshRate();
1975 sampleA.msg("getcenter_x:",50,200,Color::yellow);
1976 sampleA.var(x,200,200,Color::yellow,true);//getCenter:X
1977 sampleA.msg("getcenter_y:",50,250,Color::yellow);
1978 sampleA.var(y,200,250,Color::yellow,true);//getCenter:Y
1979 sampleA.msg("getHcenter:",50,300,Color::green);
1980 sampleA.var(d1,200,300,Color::green,true);//getHcenter
1981 sampleA.msg("getVcenter:",50,350,Color::green);
1982 sampleA.var(d2,200,350,Color::green,true);//getVcenter
1983 sampleA.msg("gettHeight:",50,400,Color::red);
1984 sampleA.var(a1,200,400,Color::red,false);//getHeight
1985 sampleA.msg("getWidth:",50,450,Color::red);
1986 sampleA.var(a2,200,450,Color::red,true);//getWidth
1988 sampleA.msg("getColorDepth:",250,200);
1989 sampleA.var(a3,500,200);//getColorDepth
1990 sampleA.msg("getRefreshRate:",250,250);
1991 sampleA.var(d3,500,250);//getRefreshRate
1994 while(!Input::get(Keyboard::spc));
1997 このコードを実行すると、宣言されたCanvasの各属性値が画面上に表示されます。</pre>
1999 <div title="2.2 デモ環境の表示" modifier="Psychlops_DevelopperG" modified="200908190221" created="200710220134" changecount="3">
2000 <pre>Independentクラスを使って、デモ環境を自動的に構成することが出来ます。
2001 以下では具体例を挙げて簡単なデモ環境の構築を説明します。
2003 1章で述べたやり方に従って、新規プロジェクトを作成すると、cppファイルの中に自動的にPsychlopsのデモプログラムが
2004 書き込まれています。このデモプログラムはIndependentクラスを用いた簡単なデモンストレーションのソースファイルになっています。ここでは、このデモプログラムを題材にIndependentを用いたデモ環境の構築について説明します。
2005 [[Psychlopsプログラムのデフォルトテンプレート]]
2007 [[2.2.1 プログラムの基本構造]]
2008 [[2.2.2 デモ環境に使う描画命令の設定]]
2009 [[2.2.3 デモ環境の実行]]</pre>
2011 <div title="2.2 図形描画のコマンド" modifier="Psychlops_DevelopperG" modified="200908190227" created="200708212305" changecount="14">
2012 <pre>[[2.1|2.1 描画領域の宣言]]節はCanvasそのものについて説明しました。
2013 本節ではCanvas上に点や線、図形を描画します。
2015 [[2.2.1 画面上に点を描画する-Pointクラス1-]]
2016 [[2.2.2 画面上に線を描画する]]
2017 [[2.2.3 画面上に四角形を描画する-Rectangleクラス1-]]
2018 [[2.2.4 画面上に円を描画する]]
2019 [[2.2.5 描画する図形の色を指定する-Colorクラス-]]
2020 [[2.2.6 画面上に縞を描画する-Rectangleクラス2-]]
2021 [[2.2.7 画面上に縞を描画する-Pointクラス2-]]
2022 [[2.2.8 運動する図形を描画する-Rectangleクラス3-]] </pre>
2024 <div title="2.2.1 プログラムの基本構造" modifier="Psychlops_DevelopperG" modified="200908190218" created="200711072356" changecount="1">
2025 <pre>このデモプログラムは大きく分けて2つの部分からなっています。
2027 void RectLuminance() {
2028 double rect_size = 100;
2029 double rect_lum = 0.5;
2030 double bg_lum = 0.2;
2032 Psychlops::Rectangle rect(rect_size,rect_size);
2036 Independent << rect_size | "Rect Size" | 1< rng< 500 | 10.0 | 2.0 ;
2037 Independent << rect_lum | "Rect Luminance" | 0.0<=rng<=1.0 | 0.1 | 0.05;
2038 Independent << bg_lum | "BG Luminance" | 0.0<=rng<=1.0 | 0.1 | 0.05;
2040 while(!Input::get(Keyboard::esc)) {
2041 Display::clear(Color(bg_lum));
2042 rect.resize(rect_size,rect_size);
2043 rect.display(rect_lum);
2050 void psychlops_main() {
2051 Canvas display(Canvas::fullscreen);
2054 p.setDesign(Procedure::DEMO);
2055 p.setProcedure(RectLuminance);
2061 ぱっと見てわかることは、Psychlops_mainの中に描画命令が存在しないことでしょう。実際の描画命令はPsychlops_main()の上にある~RectLuminance()という関数の中にかかれています。
2063 代わりに個々まで一度も触れられていないようなProcedureと言うクラスのインスタンスが宣言されて、これに対してsetDesign(), setProcedure(), run()といったメソッドがかかれています。
2064 実はデモを構築する時には、このProcedureクラスの詳細については理解する必要はありません。
2065 setProcedure()の引数としてかかれている文字列が、実際の描画を行う関数としてプログラム中にかかれていれば、後の部分は変更しなくてもデモ環境を構築することができます。
2066 つまり、もし実際の描画命令を書く関数の名前が~DemoDraw()であったとすると、この描画内容を使ったデモンストレーションを構築するためには、
2071 void psychlops_main() {
2072 Canvas display(Canvas::fullscreen);
2075 p.setDesign(Procedure::DEMO);
2076 p.setProcedure(DemoDraw);
2080 と書けば良いと言うことです。デフォルトのテンプレートと比較して、p.setProcedure()の行だけが異なっていることに注意してください。</pre>
2082 <div title="2.2.1 画面上に点を描画する-Pointクラス1-" modifier="Psychlops_DevelopperG" modified="200910080836" created="200708212310" changecount="29">
2084 Canvas上に点を描画するための命令です。
2085 Canvasに点や線等を描画するときに必要なのは「どこに」「何色」のものを描画するか、です。
2086 ここでの「どこが」が点を描画する「座標」、「何色」が「色の指定」となります。
2087 座標はCanvasの左上端が座標の基点(x=0,y=0)となっています。
2088 色の指定についての詳細は[[2.2.5 描画する図形の色を指定する-Colorクラス-]]を参照してください。ここでは、必要最小限の命令のみを使用・説明します。
2090 |!Canvas::pix()|pix(double x,double y,[[Psychlops::Color]] col) |>|!座標(x,y)に指定色の点を描画します<例1>|
2091 |~|~|double x|描画する点の座標軸xを指定|
2092 |~|~|double y|描画する点の座標軸yを指定|
2093 |~|~|[[Psychlops::Color]] col|描画する点の色を指定|
2094 |~|pix([[Psychlops::Point]] point,[[Psychlops::Color]] col) |>|!Point座標(x,y)に指定色の点を描画します^^*1^^<例4>|
2095 |~|~|[[Psychlops::Point]] point |座標(x,y)を指定|
2096 |~|~|[[Psychlops::Color]] col|描画する点の色を指定|
2097 |~|pix(int dotsCnt,double* xArray,double* yArray),[[Psychlops::Color]]* colArray)|>|!配列の座標(x[],y[])に指定色(配列を用いて複数色指定)の点を描画します<例2>|
2098 |~|~|int dotsCnt|描画する点の数を指定(後ろの引数の配列の個数を超えてはいけない)|
2099 |~|~|double* xArray|描画する点の座標軸xが格納された配列名(ポインタ)を指定|
2100 |~|~|double* yArray|描画する点の座標軸yが格納された配列名(ポインタ)を指定|
2101 |~|~|[[Psychlops::Color]]* colArray|描画する点の色が格納された配列名(ポインタ)を指定|
2102 |~|pix(int dotsCnt,double* xArray,double* yArray,[[Psychlops::Color]] col)|>|!配列の座標(x[],y[])に指定色(一色のみ)の点を描画します<例3>|
2103 |~|~|int dotsCnt|描画する点の数(配列数)を指定|
2104 |~|~|double* xArray|描画する点の座標軸xが格納された配列名(ポインタ)を指定|
2105 |~|~|double* yArray|描画する点の座標軸yが格納された配列名(ポインタ)を指定|
2106 |~|~|[[Psychlops::Color]] col|描画する点の色を指定|
2107 |~|pix(int dotsCnt,[[Psychlops::Point]]* pointArray,[[Psychlops::Color]]* colArray)|>|!配列Point座標(x[],y[])に指定色の点を描画します^^*1^^<例5>|
2108 |~|~|int dotsCnt|描画する点の数(配列数)を指定|
2109 |~|~|[[Psychlops::Point]]* pointArray |配列の座標(x[],y[])が格納された配列名(ポインタ)を指定|
2110 |~|~|[[Psychlops::Color]]* colArray|描画する点の色が格納された配列名(ポインタ)を指定|
2111 ^^*1 次のPoint::set()で説明します。^^
2113 [img[image/canvaspix.png]]
2116 座標(50,50)に青の点を描画します。
2119 #include <psychlops.h>
2120 using namespace Psychlops;
2122 void psychlops_main() {
2124 Canvas sampleA(Canvas::fullscreen);
2125 while(!Input::get(Keyboard::spc)){
2126 sampleA.pix(50,50,Color::blue);
2131 次は一命令で複数の異なる輝度の点を描画します。一つの命令を繰り返し実行する[[for文|1.6.4 for命令]]を使用しなくても複数個の点が描画される点に注目してください。
2134 #include <psychlops.h>
2135 using namespace Psychlops;
2137 //色の配列を宣言. 輝度値は0.0-1.0の範囲で指定
2138 Psychlops::Color colArray[3]={1.0,0.5,0.25};
2140 double xArray[3] = {50,100,150};
2142 double yArray[3] = {50,50,50};
2144 void psychlops_main() {
2146 Canvas sampleA(Canvas::fullscreen);
2147 sampleA.pix(3,xArray,yArray,colArray);
2149 while(!Input::get(Keyboard::spc));
2153 次は<例2>と同様に一命令で複数の点を描画しますが、全点の色は同一です。
2156 #include <psychlops.h>
2157 using namespace Psychlops;
2160 double xArray[3] = {50,100,150};
2162 double yArray[3] = {50,50,50};
2164 void psychlops_main() {
2166 Canvas sampleA(Canvas::fullscreen);
2167 sampleA.pix(3,xArray,yArray,Psychlops::Color(1.0,0.0,0.0));//3つめの引数は描画色に赤を指定
2169 while(!Input::get(Keyboard::spc));
2174 <例1>~<例3>で使用している[[Psychlops::Color]]型について簡単に説明します。
2175 PsychlopsではRGB,GRAY(輝度)の色を描画することが可能です。
2176 <例1>で青の点を描画するため「Color::blue」という命令を使用しています。
2177 Psychlopsではいくつかのよく使用する色をあらかじめ予約語として設定してあります。
2178 これらのプリセットされた色は「Color::色名」と記述することで、使用することができます。
2179 あらかじめプリセットされた色については[[Color::XXXX]]を参照してください。
2180 <例2>では輝度値を使用しています。
2181 輝度値は0.0(黒)~1.0(白)の範囲で指定されています。
2182 Psychlops::Color colArray[3]={1.0,0.5,0.25};
2183 の命令でそれぞれの点に輝度値が設定されました。
2184 <例3>では赤の点を描画しています。
2185 Psychlops::Color型の変数に3つの引数を指定したときは、それぞれの値が順に赤、緑、青の各画素の輝度の指定として解釈されます。
2186 red,green,blueはそれぞれ0.0~1.0の範囲で指定されます。
2187 この例では[[Psychlops::Color]](red=1.0,green=0.0,blue=0.0)と指定することで赤が設定されました。
2190 今まで座標はそれぞれx,y座標の変数を作成して値を設定していました。
2191 今回はPointクラスを使用して座標(x,y),ないし座標X,Yを指定し点を描画します。
2192 Point::set()を使用し座標を取得することでソースの簡略化がなされ
2195 |!Point::set()|set(double x, double y)|座標(x,y)の値を設定します|
2196 |~|~|double x:座標xを指定|
2197 |~|~|double y:座標yを指定|
2198 |~|setX(double val)|X座標の値を設定します|
2199 |~|~|double val :座標xを指定|
2200 |~|setY(double val)|Y座標の値を設定します|
2201 |~|~|double val :座標yを指定|
2204 座標(50,50)に青の点を描画します。
2205 <例1>との差分は、直接座標を記述するかPoint::set()で設定するか、だけです。
2208 #include <psychlops.h>
2209 using namespace Psychlops;
2212 Psychlops::Point point1;
2214 void psychlops_main() {
2216 Canvas sampleA(Canvas::fullscreen);
2219 sampleA.pix(point1,Psychlops::Color(0.0,0.0,1.0));
2221 while(!Input::get(Keyboard::spc));
2224 次はX,Y座標それぞれ座標を取得し、一命令で複数の点を描画します。
2225 <例2>との差分は、直接座標を記述するかPoint::set()で設定するか、だけです。
2228 #include <psychlops.h>
2229 using namespace Psychlops;
2232 Psychlops::Color colArray[3]={1.0,0.5,0.25};
2234 Psychlops::Point pointArray[3];
2236 void psychlops_main() {
2238 Canvas sampleA(Canvas::fullscreen);
2239 //座標(X[],Y[])にそれぞれ値((50,50),(100,50),(150,50)を設定
2240 for(int i=0; i<3; i++){
2241 pointArray[i].setX((i+1)*50);
2242 pointArray[i].setY(50);
2245 sampleA.pix(3,pointArray,colArray);
2247 while(!Input::get(Keyboard::spc));
2252 今度は点を画面中心に描画してみましょう。
2254 今までの命令では画面中心に点を描画することは難しいです。
2255 そこでPoint::centering()を使用します。
2256 この命令を使用することで、点を自動的に画面中心に移動します。
2258 |!Point::centering()|centering()|座標(x,y)の点を画面の中心に移動します|
2259 |~|centering(double h, double v)|任意の座標(h,v)に点を移動します|
2260 |~|~|double h:移動するx軸の値を指定|
2261 |~|~|double v:移動するy軸の値を指定|
2263 !Point::centering()の書き方
2266 #include <psychlops.h>
2267 using namespace Psychlops;
2270 Psychlops::Point point1;
2272 void psychlops_main() {
2274 Canvas sampleA(Canvas::fullscreen);
2279 sampleA.pix(point1,Color::red);
2281 while(!Input::get(Keyboard::spc));
2286 この命令もPoint::centering()と同様に点に対する移動命令です。
2287 元の座標(x,y)を基点に入力値(h,v)だけ移動します。
2289 |!Point::shift()|shift(double h, double v)|座標(X,Y)を(h,v)だけ移動します|
2290 |~|~|double h:右水平方向に移動する値を指定|
2291 |~|~|double v:下垂直方向に移動する値を指定|
2293 [img[image/pointshift.png]]
2295 !!Point::shift()の書き方
2296 <例6>を元に画面中央から(100,100)移動した青の点を描画します。
2299 #include <psychlops.h>
2300 using namespace Psychlops;
2303 Psychlops::Point point1,point2;
2305 void psychlops_main() {
2307 Canvas sampleA(Canvas::fullscreen);
2310 //座標(X,Y)を画面中心に移動し、更に(100,100)移動する
2311 point2.centering().shift(100,100);
2314 sampleA.pix(point1,Color::red);
2315 sampleA.pix(point2,Color::blue);
2317 while(!Input::get(Keyboard::spc));
2320 画面中央の赤色の点(point1)と画面中央の右下に描画された青色の点(point2)が確認できたでしょうか。
2323 <div title="2.2.2 デモ環境に使う描画命令の設定" modifier="Psychlops_DevelopperG" modified="200908190219" created="200711080010" changecount="2">
2324 <pre>さて、次に実際に描画を行っている関数~RectLuminance()について見てみましょう。[[2.2.1節|2.2.1 プログラムの基本構造]]で触れたとおり、デフォルトのデモプログラムでは、デモをする本体がpsychlops_mainとは別の関数になっています。
2327 void RectLuminance() {
2328 double rect_size = 100;
2329 double rect_lum = 0.5;
2330 double bg_lum = 0.2;
2332 Psychlops::Rectangle rect(rect_size,rect_size);
2336 Independent << rect_size | "Rect Size" | 1< rng< 500 | 10.0 | 2.0 ;
2337 Independent << rect_lum | "Rect Luminance" | 0.0<=rng<=1.0 | 0.1 | 0.05;
2338 Independent << bg_lum | "BG Luminance" | 0.0<=rng<=1.0 | 0.1 | 0.05;
2341 while(!Input::get(Keyboard::esc)) {
2342 Display::clear(Color(bg_lum));
2343 rect.resize(rect_size,rect_size);
2344 rect.display(rect_lum);
2351 現段階ではこのデモ環境に使う描画関数は引数をとることができません。同様に値を返すこともできないので、かならずvoid XXXXXXX()という形で宣言が行われることになります。[[2.2.1節|2.2.1 プログラムの基本構造]]でも触れたとおり、名前は何でもかまいません。
2353 次に中身を見てみると、はじめに変数の宣言とIndependentクラスへの変数の登録が行われています。
2354 この部分は、[[前節2.1|2.1 独立変数Independent]]で上げた例と全く同じです。良くわからなくなったら[[2.1節|2.1 独立変数Independent]]も参照してみてください。
2356 double rect_size = 100;
2357 double rect_lum = 0.5;
2358 double bg_lum = 0.2;
2361 Independent << rect_size | "Rect Size" | 1< rng< 500 | 10.0 | 2.0 ;
2362 Independent << rect_lum | "Rect Luminance" | 0.0<=rng<=1.0 | 0.1 | 0.05;
2363 Independent << bg_lum | "BG Luminance" | 0.0<=rng<=1.0 | 0.1 | 0.05;
2367 次の部分では、これまでにも見てきたようにwhile文を使ってループが設定され、その中に描画命令がかかれています。デモ実行は永久ループさせておきます。ここではESCキーを押すと終了するように設定されています。
2369 while(!Input::get(Keyboard::esc)) {
2370 Display::clear(Color(bg_lum));
2371 rect.resize(rect_size,rect_size);
2372 rect.display(rect_lum);
2377 ここでは、描画時に"Display::"命令が使われていることに注意してください。
2378 Canvasクラスの宣言はPsychlops_main()の中で行われるので、ここで宣言されたCanvas名displayを使うことはできません。たとえば、1行目はdisplay.clear(Color(bg_lum));と書いてしまうとコンパイル時にエラーとなってしまいます。そこでデフォルトのCanvasへの描画をするためのクラス"Display::"を用いて宣言されたCanvasへの描画を行っています。</pre>
2380 <div title="2.2.2 画面上に線を描画する" modifier="Psychlops_DevelopperG" modified="200910080837" created="200708230440" changecount="11">
2381 <pre>!Canvas::line()
2382 Canvas上に線を描画するためにはCanvas::line()命令を使います。
2384 |!Canvas::line()|line(double x1, double y1, double x2, double y2, [[Psychlops::Color]] col)|>|!開始座標(X1,Y1)から終端座標(X2,Y2)まで指定色の線を描画します|
2385 |~|~|double x1|描画する線の開始座標x1を指定|
2386 |~|~|double y1|描画する線の開始座標y1を指定|
2387 |~|~|double x2|描画する線の終端座標x2を指定|
2388 |~|~|double y2|描画する線の終端座標y2を指定|
2389 |~|~|[[Psychlops::Color]] col|描画する点の色を指定|
2390 |~|line([[Psychlops::Point]] point1, [[Psychlops::Point]] point2,[[Psychlops::Color]] col)|>|!開始Point座標(x1,x1)から終端Point座標(X2,Y2)まで指定色の線を描画します|
2391 |~|~|[[Psychlops::Point]] point1|開始座標(x1,y1)を指定|
2392 |~|~|[[Psychlops::Point]] point2|終端座標(x2,y2)を指定|
2393 |~|~|[[Psychlops::Color]] col|描画する点の色を指定|
2394 画面上に線を描画するためには、「どこから」「どこに」「何色」の線を引くのかの指定が必要です。
2395 「どこから」は開始座標(x1,y1)、「どこに」は終端座標(x2,y2)、「何色」は[[Psychlops::Color]]型で指定します。
2396 座標はCanvasの左上端が座標の基点(x=0,y=0)となっています。
2397 これらの座標はPoint型の変数を使用して指定することもできます。
2399 [img[image/canvasline.png]]
2401 !!Canvas::line()の書き方
2402 座標(100,100)から(300,300)まで赤い線を描画します。
2405 #include <psychlops.h>
2406 using namespace Psychlops;
2408 void psychlops_main() {
2410 Canvas sampleA(Canvas::fullscreen);
2411 sampleA.line(100, 100,300,300,Color::red);
2413 while(!Input::get(Keyboard::spc));
2416 次に[[Psychlops::Point]]型を使用して黄色の線を描画します。
2419 #include <psychlops.h>
2420 using namespace Psychlops;
2422 Psychlops::Point point1,point2;
2424 void psychlops_main() {
2426 Canvas sampleA(Canvas::fullscreen);
2428 point1.set(100,100);
2429 point2.set(300,300);
2431 sampleA.line(point1,point2,Color::yellow);;
2433 while(!Input::get(Keyboard::spc));
2437 <div title="2.2.3 デモ環境の実行" modifier="Psychlops_DevelopperG" modified="200908190221" created="200712061912" changecount="1">
2438 <pre>[[2.2.1節|2.2.1 プログラムの基本構造]]、[[2.2.2節|2.2.2 デモ環境に使う描画命令の設定]]で作成したプログラムを実行すると、デモ環境を使用したデモを使うことができます。
2440 画面左上にデモ環境コンソールが表示されています。コンソールには、Independentで指定した変数が列挙されています。
2442 [img[部分行列|image/console_1.png]]
2444 コンソールで明るく表示されている変数が変更対象となる(アクティブな)変数です。アクティブな変数は上下(↓↑)キーで切り替えることができます。
2446 [img[部分行列|image/console_2.png]]
2449 コンソールでは、左右キーで変数の値を変更することができます(減←、→増)。shiftキーを押しながら←→キーを押すと、変量が変わります。
2450 [img[部分行列|image/console_3.png]]
2452 [img[部分行列|image/console_4.png]]</pre>
2454 <div title="2.2.3 画面上に四角形を描画する-Rectangleクラス1-" modifier="Psychlops_DevelopperG" modified="200912010832" created="200708231915" changecount="24">
2455 <pre>PsychlopsでCanvasと並んで重要なクラスにRectangleクラスがあります。
2456 RectangleはCanvas上に四角形の領域を設定する命令です。
2457 この領域の設定はコピーやペーストのために使用することもできますし、領域自体を塗りつぶすことで四角形を表示することもできます。
2458 PsychlopsにおけるCanvas上の描画はほとんどこのRectangleクラスを使用して行うことになります。
2461 Rectangle::set()は四角形の領域を設定します。
2462 領域の設定は、四角形の左上座標(x1,y1)と四角形の右下座標(x2,y2)を指定することで行います。
2463 また色指定は[[Psychlops::Color]]型で指定します。
2465 |!Rectangle::set()|set(double width, double height)|>|!width×heightの四角形を設定します|
2466 |~|~|double width|描画する四角形の横幅|
2467 |~|~|double height|描画する四角形の縦幅|
2468 |~|set(double l, double t, double r, double b)|>|!左上座標(l,t)から右下座標(r,b)までの四角形を設定します|
2469 |~|~|double l|描画する四角形の左上座標x1を指定|
2470 |~|~|double t|描画する四角形の左上座標y1を指定|
2471 |~|~|double r|描画する四角形の右下座標x2を指定|
2472 |~|~|double b|描画する四角形の右下座標y2を指定|
2473 |~|set([[Psychlops::Point]] point1, [[Psychlops::Point]] point2)|>|!左上Point座標(x1,x1)から右下Point座標(X2,Y2)までの四角形を設定します|
2474 |~|~|[[Psychlops::Point]] point1|左上座標(x1,y1)を指定|
2475 |~|~|[[Psychlops::Point]] point2|右下座標(x2,y2)を指定|
2477 [img[image/rectset.png]]
2480 画面上に四角形を描画するにはCanvas::rect()命令を使用します。
2481 Rectangle::set命令で四角形の領域設定が終わったらCanvas::rect命令で描画処理を行います。
2482 この二つの命令は四角形の描画において対になっているので併せて覚えてください。
2484 |!Canvas::rect()|rect([[Psychlops::Rectangle]] rect, [[Psychlops::Color]] col)|>|!四角形を指定色で描画します|
2485 |~|~|([[Psychlops::Rectangle]] rect|描画する四角形を指定|
2486 |~|~|[[Psychlops::Color]] col|描画する四角形の色を指定|
2488 [img[image/canvasrect.png]]
2490 !!Rectangle::set()とCanvas::rect()の書き方
2491 Rectangleを宣言して、画面上に四角形を描画して見ましょう。
2492 Rectangleを縦横のサイズのみを用いて宣言したときには、左上の座標は(0,0)に設定されています。
2493 下の例では、200×100ピクセルの白色の四角形を画面左上隅に描画します。
2496 #include <psychlops.h>
2497 using namespace Psychlops;
2499 Psychlops::Rectangle rect1;
2501 void psychlops_main() {
2503 Canvas sampleA(Canvas::fullscreen);
2505 sampleA.rect(rect1,Color::white);
2507 while(!Input::get(Keyboard::spc));
2510 次に(50,100)と(100,150)を頂点とする大きさ50×50ピクセルの四角形を描画します。
2511 描画命令にはCanvas::rect()を使用し、色に黄色を指定しています。
2514 #include <psychlops.h>
2515 using namespace Psychlops;
2517 Psychlops::Rectangle rect1;
2519 void psychlops_main() {
2521 Canvas sampleA(Canvas::fullscreen);
2522 rect1.set(50,100,100,150);
2523 sampleA.rect(rect1,Color::yellow);
2525 while(!Input::get(Keyboard::spc));
2528 次に[[Psychlops::Point]]型を使用して頂点の座標を設定し、四角形を描画します。
2529 描画命令にはCanvas::rect()を使用し、色に緑を指定しています。
2532 #include <psychlops.h>
2533 using namespace Psychlops;
2535 Psychlops::Rectangle rect1;
2536 Psychlops::Point point1,point2;
2538 void psychlops_main() {
2540 Canvas sampleA(Canvas::fullscreen);
2542 point1.set(100,100);
2543 point2.set(300,300);
2544 rect1.set(point1,point2);
2545 sampleA.rect(rect1,Color::green);
2547 while(!Input::get(Keyboard::spc));
2551 ここまでは、基本的な四角形の設定について実行してきました。次に複数の同一四角形を描画します。
2552 複数の同一四角形を描画するのにそれぞれに対応したRectangleを宣言することもできますが、よりよい方法があります。
2553 一つだけRectangleを宣言して、これをシフトしながら描画する方法です。一つのはんこを紙の上に何度も押すようなものです。
2554 Psychlopsでは、Canvas::flip()が起こるまでは、画面上に描画内容が表示されませんから、このような方法を用いても複数の四角形が同時に画面に表示されます。
2555 <例1>の四角形を座標(0,0),画面中心,画面中心から(200,200)移動したところに四角形を描画してみましょう。
2558 #include <psychlops.h>
2559 using namespace Psychlops;
2561 Psychlops::Rectangle rect1,rect2,rect3;
2563 void psychlops_main() {
2565 Canvas sampleA(Canvas::fullscreen);
2570 rect3.centering().shift(200,200);
2571 sampleA.rect(rect1,Color::white);
2572 sampleA.rect(rect2,Color::white);
2573 sampleA.rect(rect3,Color::white);
2575 while(!Input::get(Keyboard::spc));
2580 <div title="2.2.4 画面上に円を描画する" modifier="Psychlops_DevelopperG" modified="200908190229" created="200708232150" changecount="15">
2581 <pre>Canvas上に円を描画します。
2582 座標はCanvasの左上端が座標の基点(x=0,y=0)となっています。
2585 Rectangleを使用してこれに内接するような円を描画することもできます。
2586 [[Rectangle.set()|2.2.3 画面上に四角形を描画する-Rectangleクラス1-]]命令で横軸と縦軸の直径を設定し、Canvas::oval()命令で円を描画します。
2587 色指定は[[Psychlops::Color]]型で指定します。
2589 |!Canvas::oval()|oval([[Psychlops::Rectangle]] rect, [[Psychlops::Color]] col)|[[Psychlops::Rectangle]] rectで指定された直径の円を指定色で塗りつぶし描画します|
2590 |~|~|[[Psychlops::Rectangle]] rect:描画する円の直径|
2591 |~|~|[[Psychlops::Color]] col:描画する点の円を指定|
2593 [img[image/canvasoval.png]]
2595 !!Canvas::oval()の書き方
2596 Rectangleを宣言し、直径100ピクセルの白い円を描画します。
2599 #include <psychlops.h>
2600 using namespace Psychlops;
2602 Psychlops::Rectangle rect1;
2604 void psychlops_main() {
2606 Canvas sampleA(Canvas::fullscreen);
2608 sampleA.oval(rect1,Color::white);
2610 while(!Input::get(Keyboard::spc));
2614 <div title="2.2.5 描画する図形の色を指定する-Colorクラス-" modifier="YourName" modified="200803100611" created="200708232340" changecount="24">
2615 <pre>今までCanvas、描画図形に色を塗りつぶしてきましたが、色の設定については詳しくは触れませんでした。
2616 ここでは色設定について詳しく説明します。
2620 描画対象はCanvas,点・線・四角形・多角形・円等多岐にわたります。
2621 色の設定はおおまかに2パターンあります。
2622 RGBと~GRAYLevel(輝度)です。
2623 RGBは色の三原色に基づいた赤・緑・青で構成されています。
2624 引数が3つ(4つ)あるColor::set()命令はRGB(A)指定になります。
2625 それぞれ値を0.0~1.0の範囲で持ち、3色のバランスで色を構成します。
2626 赤・緑・青の全ての値が0.0の場合は黒、全ての値が1.0の場合は白になります。
2627 4つめの引数(A)は色の透明度を決定します。0.0の場合は完全透過、1.0の場合は完全に不透明になります。省略したときはA=1.0(完全不透過)と解釈されます
2628 ~GRAYLevel(輝度)はグレースケールの輝度値を決定します。
2629 引数は1つで0.0~1.0の範囲を持ち、0.0の場合は黒、1.0の場合は白になります。
2632 白~黒を描画したい場合は~GRAYLevel
2634 |!Color::set()|set(double r, double g, double b, double a)|r,g,bと透明度aで構成されるカラー情報を設定します。|
2635 |~|~|double r:赤の輝度を指定。範囲は0.0~1.0|
2636 |~|~|double g:緑の輝度を指定。範囲は0.0~1.0|
2637 |~|~|double b:青の輝度を指定。範囲は0.0~1.0|
2638 |~|~|double a:透明度を指定。範囲は0.0~1.0 (0.0に近いほど透明度が高い)|
2639 |~|set(double r, double g, double b)|r,g,bで構成されるカラー情報を設定します。|
2640 |~|~|double r:赤の輝度を指定。範囲は0.0~1.0|
2641 |~|~|double g:緑の輝度を指定。範囲は0.0~1.0|
2642 |~|~|double b:青の輝度を指定。範囲は0.0~1.0|
2643 |~|set(double gray)|グレースケールの輝度を設定します。|
2644 |~|~|double gray: 輝度を指定。範囲は0.0~1.0|
2651 #include <psychlops.h>
2652 using namespace Psychlops;
2654 Psychlops::Rectangle rect1,rect2,rect3;
2655 Psychlops::Color col1,col2,col3;
2657 void psychlops_main() {
2659 Canvas sampleA(Canvas::fullscreen);
2660 sampleA.clear(Color(0.25,0.25,0.25));
2664 rect1.centering().shift(-100,0);
2666 rect3.centering().shift(100,0);
2668 col1.set(0.7,0.2,0.4);
2669 col2.set(0.5,0.8,1.0);
2670 col3.set(0.2,0.2,0.5);
2671 sampleA.rect(rect1,col1);
2672 sampleA.rect(rect2,col2);
2673 sampleA.rect(rect3,col3);
2675 while(!Input::get(Keyboard::spc));
2678 では次に透明度の値を変えたときにどう描画されるか確認しましょう。
2679 色は赤(固定)で透明度だけ0.0,0.5,1.0と変化させてみます。
2682 #include <psychlops.h>
2683 using namespace Psychlops;
2685 Psychlops::Rectangle rect1,rect2,rect3;
2686 Psychlops::Color col1,col2,col3;
2688 void psychlops_main() {
2690 Canvas sampleA(Canvas::fullscreen);
2691 sampleA.clear(Color(0.25,0.25,0.25));
2695 rect1.centering().shift(-100,0);
2697 rect3.centering().shift(100,0);
2699 col1.set(1.0,0.0,0.0,0.25);
2700 col2.set(1.0,0.0,0.0,0.5);
2701 col3.set(1.0,0.0,0.0,1.0);
2702 sampleA.rect(rect1,col1);
2703 sampleA.rect(rect2,col2);
2704 sampleA.rect(rect3,col3);
2706 while(!Input::get(Keyboard::spc));
2709 今度はグレースケールで描画してみましょう。
2712 #include <psychlops.h>
2713 using namespace Psychlops;
2715 Psychlops::Rectangle rect1,rect2,rect3;
2716 Psychlops::Color col1,col2,col3;
2718 void psychlops_main() {
2720 Canvas sampleA(Canvas::fullscreen);
2724 rect1.centering().shift(-100,0);
2726 rect3.centering().shift(100,0);
2731 sampleA.rect(rect1,col1);
2732 sampleA.rect(rect2,col2);
2733 sampleA.rect(rect3,col3);
2735 while(!Input::get(Keyboard::spc));
2739 !Canvas::setGammaValue()
2740 他の多くの描画ライブラリ同様、CRTの各画素における輝度レベルとPsychlopsで指定する輝度レベルは線形の関係を維持しません。
2741 これを簡易的に補正するために、R,G,Bの各色チャンネルについてGamma関数を用いた補正を行う方法が広く用いられています。
2742 Psychlopsでは、このGamma関数の係数を指定することで、Gamma補正された色を表示することが可能です。
2743 このための命令として、setGammaValue()命令があります。
2745 |!Canvas::setGammaValue()|setGammaValue(double Cr, double Cg, double Cb)|この命令以降実行されるPsychlops::Colorに対してGamma補正を自動的に行います|
2746 |~|~|Cr: R(赤)チャンネルのガンマ係数|
2747 |~|~|Cg: G(緑)チャンネルのガンマ係数|
2748 |~|~|Cb: B(青)チャンネルのガンマ係数|
2750 コード例は[[ガンマ補正|Tips: ガンマ補正]]を参照ください。
2753 <div title="2.2.6 画面上に縞を描画する-Rectangleクラス2-" modifier="Psychlops_Admin" modified="200709262314" created="200708242023" changecount="82">
2754 <pre>2.2.1節から2.2.5節まで基本的な描画記法を説明してきました。
2755 この節ではここまでのまとめとして、[[Psychlops::Rectangle]]型を使って「縞」を描画します。
2757 ^^ここは、ステップが多いので、かなりかみ砕いた解説をしています。縞の描画になれた方は使用する関数の説明とプログラム例のみをざっとご覧になられることをおすすめいたします。^^
2759 !縞を描いてみようStep1~グラデーションの四角形の描画~
2760 これから[[Psychlops::Rectangle]]型を用いて黒(0.0)と白(1.0)の縞を作ります。
2762 どういう風に縞を作成するか想像してみてください。
2763 白と黒の四角形を並べて描画するのでしょうか。
2765 しかし、これでは、単純な白と黒の繰り返しの縞しか描くことができません。
2767 もう少し複雑な縞を描くためにできそうなことを順に整理してみましょう。
2772 前の2つはこれまでに説明した[[Canvas::rect()]]命令を使えばできそうです。実際にはこの節では[[Canvas::rect()]]命令の代わりに''Rectangle::draw()''命令を用います。この命令は[[Canvas::rect()]]命令と全く同じ効果を持っているので、あなたの好みでどちらを使用してもかまいません。
2773 四角形の位置を移動させるには、''Rectangle::centering()'',''Rectangle::shift()''命令を使います。
2774 以下でまずこれらの命令の書式と使い方を簡単な例とともに説明します。
2776 !!Rectangle::centering()
2777 [[Psychlops::Rectangle]]型の図形を移動します。
2778 [img[image/rect_centering().png]]
2779 座標を指定する場合、その座標に図形の中心が設定されます。
2780 |!Rectangle::centering()|centering()|[[Psychlops::Rectangle]] rectを画面中心に移動します|
2781 |~|centering(double x, double y)|[[Psychlops::Rectangle]] rectを指定した座標(x,y)に移動します|
2782 |~|~|double x:移動する図形のx座標を指定|
2783 |~|~|double y:移動する図形のy座標を指定|
2784 |~|centering([[Psychlops::Point]] po)|[[Psychlops::Rectangle]] rectを指定したpoint座標(x,y)に移動します|
2785 |~|~|[[Psychlops::Point]] po:移動する図形の(x,y)座標を指定|
2786 |~|centering(Rectangle rect)|[[Psychlops::Rectangle]] rectを指定した[[Psychlops::Rectangle]] rectの中心座標(x,y)に移動します|
2787 |~|~|[[Psychlops::Rectangle]] rect :移動する図形の中心座標となる[[Psychlops::Rectangle]] rectを指定|
2789 !!Rectangle::shift()
2790 [[Psychlops::Rectangle]]型の図形を移動します。
2791 元の座標(x,y)を基点に入力値(h,v)だけ移動します。
2792 |!Rectangle::shift()|shift(double h, double v)|座標(X,Y)を(h,v)だけ移動します|
2793 |~|~|double h:入力値分、右水平方向に移動|
2794 |~|~|double v:入力値分、下垂直方向に移動|
2795 [img[image/rect_shift().png]]
2797 !!!Rectangle::centering()とRectangle::shift()の書き方
2798 200×100の四角形を中心座標から(-100,-200)だけ移動します。
2801 #include <psychlops.h>
2802 using namespace Psychlops;
2804 Psychlops::Rectangle rect1;
2806 void psychlops_main() {
2808 Canvas sampleA(Canvas::fullscreen);
2810 rect1.centering().shift(-100,-200);
2811 sampleA.rect(rect1,Color::white);
2813 while(!Input::get(Keyboard::spc));
2818 今まで図を描画する時、[[Psychlops::Canvas]]型の命令を使用しました。
2819 今回は描画命令にRectangle::draw()命令を使用します。
2820 |!Rectangle::draw()|draw()|対象の[[Psychlops::Rectangle]]型を描画します|
2821 |~|draw([[Psychlops::Color]] col)|対象の[[Psychlops::Rectangle]]型を[[Psychlops::Color]] colで塗りつぶし描画します|
2822 |~|~|[[Psychlops::Color]] col:描画する四角形の色を指定|
2824 !!Rectangle::draw()の書き方
2825 <例1>の四角形の色をRectangle::draw()命令を使用して「cyan」に塗りつぶします。
2828 #include <psychlops.h>
2829 using namespace Psychlops;
2831 Psychlops::Rectangle rect1;
2833 void psychlops_main() {
2835 Canvas sampleA(Canvas::fullscreen);
2837 rect1.centering().shift(-100,-200);
2838 rect1.draw(Color::cyan);
2840 while(!Input::get(Keyboard::spc));
2845 とりあえず四角形(w × h)を(200 × 100)の大きさに決めます。
2846 まず黒(0.0)から白(1.0)へ左から右にグラデーションする縞を描画してみましょう。
2850 #include <psychlops.h>
2851 using namespace Psychlops;
2853 Psychlops::Rectangle rect1;
2854 Psychlops::Color col1;
2856 void psychlops_main() {
2858 Canvas sampleA(Canvas::fullscreen);
2860 for(int i=0;i<200;i++){
2861 col1.set((double)i/200);
2862 rect1.centering().shift((double)-1/2*200+i,0);
2866 while(!Input::get(Keyboard::spc));
2870 このプログラムでは、200個の1 x 100ピクセルのRectangleを描画しています。
2871 左から数えてi個目の四角形の色をi / 200とすると、一番左i = 0では色は0.0(黒)となって、右に行くに従ってRectangleの輝度が大きくなるように設定できます。
2872 描画する場所は最終的な縞の中心が画面中央(Xc,Yc)に来るように計算します。
2874 描画する縞は200 × 100ピクセルなので、一番左のRectangleの中心位置は(Xc - 100,Yc)、一番右の位置は(Xc + 100,Yc)です。
2875 一般に幅wの縞を描くときには、一番左のRectangleの位置は(Xc - 1 / 2 * w ,Yc)、一番右の位置は(Xc + 1/2 * w ,Yc)になります。
2877 次にこの一番左のRectangleの位置を使って、i番目のRectangleの座標位置を考えてみます。
2878 i番目のRectangleは一番左のRectangleから見て+ i だけx座標を移動した位置にあるはずです。
2879 従って、i番目の座標は(Xc -1 / 2 * w + i ,Yc)となります。
2881 !!縞を描いてみようStep2~正弦波で縞を作成する~
2882 Step1で黒から白のグラデーション(色変化)の縞を作成しました。
2883 次にもう少し複雑な正弦縞を作成してみましょう。
2884 Step1から使えそうな要素を抜き出しましょう。
2885 *描画する四角形(w × h):(1 × hピクセルの四角形をw回描画する)
2886 *座標:Xc - 1 / 2 * w + i ,Yc
2887 [img[image/正弦波1.png]]
2891 [img[image/正弦波の一般式.png]]
2894 但しy>0であるので(扱うのが色のため)
2895 [img[image/正弦波2.png]]
2896 [img[image/色の式.png]]
2900 A:振動をC:コントラストに置き換えます。
2901 Michelsonコントラストの定義式を用います。
2902 [img[image/contrastEq.png]]
2903 C=2A / 2Lmean = A / Lmean
2905 今回は位相0の縞を描画するのでθ = 0
2908 平均輝度(Lmean): 0.5の縞を描画します。
2910 y=Asin{ ( 2π * x / λ ) + θ } + Lmean
2911 y=( C * Lmean ) sin ( 2π * i / λ ) + Lmean
2912 y=Lmean { C sin ( 2π * i / λ ) + 1 }
2913 i番目の色=Lmean { C sin ( 2π * i / λ ) + 1 }
2915 i番目の色=1.0 { 0.5 sin ( ( 2π * i / 60 ) + 1 }
2922 #include <psychlops.h>
2923 using namespace Psychlops;
2925 Psychlops::Rectangle rect1;
2926 Psychlops::Color col1;
2930 double lmean=0.5, contrast=1.0, theta=0;
2932 void psychlops_main() {
2934 Canvas sampleA(Canvas::fullscreen);
2935 rect1.set(1,height);
2936 for(int i=0;i<width;i++){
2937 col1.set(lmean*((contrast*sin((2*PI*i/lambda)+theta))+1));
2938 rect1.centering().shift((double)-1/2*width+i,0);
2942 while(!Input::get(Keyboard::spc));
2946 !!縞を描いてみようStep3~位相が異なる縞~
2947 Step2では位相0の縞を描画しました。
2948 位相に値を入れて位相90の縞を描画しましょう。
2949 わかりやすいように位相0の下に並べて描画します。
2952 #include <psychlops.h>
2953 using namespace Psychlops;
2955 Psychlops::Rectangle rect1,rect2;
2956 Psychlops::Color col1,col2;
2960 double lmean=0.5, contrast=1.0, theta1=0, theta2=90;
2962 void psychlops_main() {
2964 Canvas sampleA(Canvas::fullscreen);
2965 rect1.set(1,height);
2967 for(int i=0;i<width;i++){
2969 col1.set(lmean*((contrast*sin((2*PI*i/lambda)+theta1))+1));
2970 rect1.centering().shift((double)-1/2*width+i,0);
2973 col2.set(lmean*((contrast*sin((2*PI*i/lambda)+theta2))+1));
2974 rect2.centering().shift((double)-1/2*width+i,150);
2978 while(!Input::get(Keyboard::spc));
2981 !縞を描いてみようStep4~矩形波の縞を作成する~
2982 正弦波の縞の場合、白と黒の境目が曖昧です。
2983 境目をはっきり描画したい場合、矩形波の縞を描画します。
2988 [img[image/正弦波の一般式.png]]
2990 なのでこれを利用し、y > 0の場合はLmax , y < 0の場合はLminを描画するという判定分を追加することで矩形波の縞が描画できます。
2992 コントラスト、Lmeanを使ってLmax,Lmeanの値を求めておくと、
2993 Lmax = Lmean + Lmean * コントラストが求まります。
2994 Lmin = Lmean - Lmean * コントラストが求まります。
2995 これらを使用してコードを書いてみましょう。
2998 #include <psychlops.h>
2999 using namespace Psychlops;
3001 Psychlops::Rectangle rect1;
3006 double lmean=0.5, contrast=1.0, theta=0;
3008 void psychlops_main() {
3010 Canvas sampleA(Canvas::fullscreen);
3011 rect1.set(1,height);
3012 for(int i=0;i<width;i++){
3013 col1=(lmean*contrast*sin((2*PI*i/lambda)+theta));
3014 if(col1>0) col1=lmean+contrast*lmean;
3015 else col1=lmean-contrast*lmean;
3016 rect1.centering().shift((double)-1/2*width+i,0);
3020 while(!Input::get(Keyboard::spc));
3023 !!縞を描いてみようStep5~縞の角度を変えてみる~
3024 今まで縦(90°)の縞を描画してきました。
3025 次は横(0°)の縞を描画してみましょう。
3026 w × hの縦の縞を描画するために1 × hの四角形をw個並べました。
3028 w × hの横の縞を描画するためにw × 1の四角形をh個並べたら作れないでしょうか。
3031 *描画する四角形( w × h ) : ( w × 1の四角形をh回描画する)
3032 *座標X:Xc - 1 / 2 * w + i
3033 *色:y = A sin { ( 2π * x / λ ) + θ } + Lmean
3035 今回はX座標ではなくY座標を指定する必要があります。
3037 *座標Y:Yc - 1 / 2 * h + j (考え方はX座標と同じなので[[Step1|2.2.6 画面上に縞を描画する-Rectangleクラス2-]]参照のこと)
3039 これらから実際にコードを書いて試してみましょう。
3042 #include <psychlops.h>
3043 using namespace Psychlops;
3045 Psychlops::Rectangle rect1;
3046 Psychlops::Color col1;
3050 double lmean=0.5, contrast=1.0, theta=0;
3052 void psychlops_main() {
3054 Canvas sampleA(Canvas::fullscreen);
3056 for(int j=0;j<height;j++){
3057 col1.set(lmean*((contrast*sin((2*PI*j/lambda)+theta))+1));
3058 rect1.centering().shift(0,(double)-1/2*height+j);
3062 while(!Input::get(Keyboard::spc));
3066 矩形波の横の縞はStep4を参考に自分で試してみてください。</pre>
3068 <div title="2.2.7 画面上に縞を描画する-Pointクラス2-" modifier="YourName" modified="200802191327" created="200709252135" changecount="11">
3069 <pre>[[2.2.6節|2.2.6 画面上に縞を描画する-Rectangleクラス2-]]では[[Psychlops::Rectangle]]型を用いて縞を描画しました。
3070 この節では[[Psychlops::Point]]型を用いて縞を描画します。
3072 四角形(w × h)を描画する際、[[Psychlops::Rectangle]]型では1 × hの四角形をw個
3073 もしくはw × 1の四角形を h個並べて描画しました。
3074 [[Psychlops::Point]]型ではw × hの点を並べて描画します。
3076 !縞を描いてみようStep1~正弦縞を描画する~
3077 はじめに一番簡単な垂直方位の縞を書いてみましょう。
3080 *描画する縞のサイズ:w × h (1ピクセルの点をw×h回描画する)
3081 *縞の中心座標X:Xc - 1 / 2 * w + i
3082 *縞の中心座標Y:Yc - 1 / 2 * h + j
3083 *縞の式:L(x,y) = Lmean*(1+contrast*sin { ( 2π * x / λ ) + θ } )
3091 では以上をふまえてコードを書いてみましょう。
3094 #include <psychlops.h>
3095 using namespace Psychlops;
3097 Psychlops::Point p1;
3098 Psychlops::Color col1;
3102 double lmean=0.25, contrast=0.5, theta=0;
3104 void psychlops_main() {
3106 Canvas sampleA(Canvas::fullscreen);
3107 for(int i=0;i<width;i++){
3108 for(int j=0;j<height;j++){
3109 col1.set(lmean*((contrast*sin((2*PI*i/lambda)+theta))+1));
3110 p1.centering().shift((double)-1/2*width+i,(double)-1/2*height+j);
3111 sampleA.pix(p1,col1);
3115 while(!Input::get(Keyboard::spc));
3119 [img[image/grating01.png]]
3121 !縞を描いてみようStep2~斜めの縞を描画する~
3122 [[Psychlops::Rectangle]]型を使った書き方では方位90°の縞(縦)と0°の縞(横)のみを描画しました。
3123 Pointクラスを使った縞の描画では、これ以外の方位の縞も描画することができます。
3124 ここでは回転を使って方位45°の縞(斜め)を描画します。
3127 点(i, j)をθラジアン回転させて(xx,yy)に変換する式は以下の通りです。:
3129 xx = cosθ * i - sinθ * j
3130 yy = sinθ * i + cosθ * j
3132 この式を用いて、斜めの方位を持つ縞を描画してみましょう。
3133 θはラジアン単位です。度数法d(°)で値を指定したいときは下の式で変換した値を用います。
3135 θ ( rad ) = d° * π / 180°
3138 以上をから<例1>を修正して方位が45°の縞を描画してみましょう。
3141 #include <psychlops.h>
3142 using namespace Psychlops;
3144 Psychlops::Point p1;
3145 Psychlops::Color col1;
3149 double lmean=0.5, contrast=0.5, theta=0;
3153 void psychlops_main() {
3155 Canvas sampleA(Canvas::fullscreen);
3156 orientation=45*PI/180;
3157 for(int i=0;i<width;i++){
3158 for(int j=0;j<height;j++){
3160 xx=cos(orientation)*i-sin(orientation)*j;
3161 yy=sin(orientation)*i+cos(orientation)*j;
3163 col1.set(lmean*((contrast*sin((2*PI*yy/lambda)+theta))+1));
3164 p1.centering().shift((double)-1/2*width+i,(double)-1/2*height+j);
3165 sampleA.pix(p1,col1);
3169 while(!Input::get(Keyboard::spc));
3173 [img[image/grating45.png]]
3175 !縞を描いてみようStep3~交差した縞(Plaid)を描画する~
3176 では次にStep2で作成した45°の縞に135°の縞を交差させた縞を描画してみましょう。
3193 #include <psychlops.h>
3194 using namespace Psychlops;
3196 Psychlops::Point p1;
3197 Psychlops::Color col1;
3201 double lmean=0.25, contrast=0.5, theta=0;
3203 double orientation=45*PI/180;
3207 double lmean2=0.25, contrast2=0.25, theta2=0;
3209 double orientation2=135*PI/180;
3211 void psychlops_main() {
3213 Canvas sampleA(Canvas::fullscreen);
3215 for(int i=0;i<width;i++){
3216 for(int j=0;j<height;j++){
3218 xx=cos(orientation)*i-sin(orientation)*j;
3219 yy=sin(orientation)*i+cos(orientation)*j;
3220 xx2=cos(orientation2)*i-sin(orientation2)*j;
3221 yy2=sin(orientation2)*i+cos(orientation2)*j;
3223 col1.set( lmean*(1+contrast*sin(theta+(2*PI*yy/lambda)))+
3224 lmean2*(1+contrast2*sin(theta2+(2*PI*yy2/lambda2)))
3226 p1.centering().shift((double)-1/2*width+i,(double)-1/2*height+j);
3227 sampleA.pix(p1,col1);
3231 while(!Input::get(Keyboard::spc));
3235 [img[image/plaid45_135.png]]
3237 難しいと思われた方は、orientationやcontrast等のパラメタを変えて実行してみるとわかりやすいかもしれません。</pre>
3239 <div title="2.2.8 運動する図形を描画する-Rectangleクラス3-" modifier="Psychlops_DevelopperG" modified="200908190231" created="200709252136" changecount="4">
3240 <pre>この節では[[2.2.6節|2.2.6 画面上に縞を描画する-Rectangleクラス2-]]で作成した縞を動かします。
3242 [[2.2.6節|2.2.6 画面上に縞を描画する-Rectangleクラス2-]]では、位相を変えてみることで縞の位置を移動させました。
3243 「縞を動かす」ことは一見難しそうですが位相を変化させて描画することで
3247 下のコードを実行すると位相 = 0と位相 = 90の四角形がそれぞれ描画されます。
3249 #include <psychlops.h>
3250 using namespace Psychlops;
3252 Psychlops::Rectangle rect1,rect2;
3253 Psychlops::Color col1,col2;
3257 double lmean=0.5, contrast=1.0, theta1=0, theta2=90;
3259 void psychlops_main() {
3261 Canvas sampleA(Canvas::fullscreen);
3262 rect1.set(1,height);
3264 for(int i=0;i<width;i++){
3266 col1.set(lmean*((contrast*sin((2*PI*i/lambda)+theta1))+1));
3267 rect1.centering().shift((double)-1/2*width+i,0);
3270 col2.set(lmean*((contrast*sin((2*PI*i/lambda)+theta2))+1));
3271 rect2.centering().shift((double)-1/2*width+i,150);
3275 while(!Input::get(Keyboard::spc));
3279 ここで、描画する毎にθの値を変更して入力すれば四角形が常に移動しているように見えます。
3283 #include <psychlops.h>
3284 using namespace Psychlops;
3286 Psychlops::Rectangle rect1;
3287 Psychlops::Color col1;
3291 double lmean=0.5, contrast=1.0, theta=0;
3294 void psychlops_main() {
3296 Canvas sampleA(Canvas::fullscreen);
3297 while(!Input::get(Keyboard::spc)){
3298 rect1.set(1,height);
3299 for(int i=0;i<width;i++){
3300 col1.set(lmean*((contrast*sin((2*PI*i/lambda)+theta))+1));
3301 rect1.centering().shift((double)-1/2*width+i,0);
3309 <例2>は<例1>の1箇所だけを変えたコードです。
3310 どこが変わっているのか見つけてみてください。
3313 #include <psychlops.h>
3314 using namespace Psychlops;
3316 Psychlops::Rectangle rect1;
3317 Psychlops::Color col1;
3321 double lmean=0.5, contrast=1.0, theta=0;
3324 void psychlops_main() {
3326 Canvas sampleA(Canvas::fullscreen);
3327 while(!Input::get(Keyboard::spc)){
3328 rect1.set(1,height);
3329 for(int i=0;i<width;i++){
3330 col1.set(lmean*((contrast*sin((2*PI*i/lambda)+theta))+1));
3331 rect1.centering().shift((double)-1/2*width+i,0);
3339 答えはflip()です。<例1>ではflip(),<例2>ではflip(6)になっています。
3340 実行すると運動の速度が随分変化していることに気づくでしょう。
3341 flip(6)では6回リフレッシュが行われるまで描画したままの状態で待ち、次の垂直同期で画面が入れ替わります。
3342 60Hzの画面でflip(6)を実行する場合、100ミリsec描画したままの状態を維持できます。
3343 このようにして、flip(int waitframe)命令を活用すると運動刺激のリフレッシュレート(各フレームの長さ)や細かい描画の待ち時間を設定できます。
3345 flip(int waitframe)について詳しい説明は[[2.1.2節|2.1.2 Canvasの基本構造と操作]]を参照してください。</pre>
3347 <div title="2.3 文字列の描画" modifier="Psychlops_DevelopperG" modified="200912010809" created="200909300449" changecount="25">
3348 <pre>[[2.1.3節|2.1.3 Canvasのメンバ取得と変数の表示]]では簡単な文字列の表示について説明しました。
3349 この節ではより本格的な2バイト文字を用いた文字列の描画について取り扱います。
3351 Psychlopsで2バイト文字を描画するには2つの方法があります。
3352 いずれの方法もCanvas::msg()命令を使用します。
3354 |!Canvas::msg()|msg(char* string,double x,double y,[[Psychlops::Color]] col)|>|char* stringを座標(x,y)に指定色で表示します|
3355 |~|~|schar* string|画面表示をする文字列を指定|
3356 |~|~|double x|画面表示をする座標(x)を指定|
3357 |~|~|double y|画面表示をする座標(y)を指定|
3358 |~|~|[[Psychlops::Color]] col|描画する点の色を指定(設定しない場合、白が設定される)|
3359 |~|msg(string str,double x,double y,[[Psychlops::Color]] col)|>|string strを座標(x,y)に指定色で表示します|
3360 |~|~|string str|画面表示をする文字列を指定|
3361 |~|~|double x|画面表示をする座標(x)を指定|
3362 |~|~|double y|画面表示をする座標(y)を指定|
3363 |~|~|[[Psychlops::Color]] col|描画する点の色を指定(設定しない場合、白が設定される)|
3365 2つの方法とはこの命令を単体で使う方法とLettersと呼ばれるクラスを用いた方法です。
3366 前者の方法は簡便ですが、フォント指定などはできず、それほど美しく表示することはできません。
3367 後者の方法はフォント指定など文字列表示の方法を細かく指定できますが、文字列を表示する前にメモリ領域に文字列を描画しておく必要があります。
3370 [[2.3.1 Canvas::msg()命令単体での文字列描画]]
3371 [[2.3.2 Lettersクラスを用いた文字列描画]]
3372 また、これらのクラスの詳細については[[3.5節|3.5 文字列のレンダリングの仕様と関連情報]]をご覧ください。
3376 <div title="2.3.1 Canvas::msg()命令単体での文字列描画" modifier="Psychlops_DevelopperG" modified="200910260740" created="200910020306" changecount="13">
3377 <pre>!!Canvas::msg()命令単体での文字列描画
3378 まず、[[2.1.3節|2.1.3 Canvasのメンバ取得と変数の表示]]で説明したCanvas::msg()命令をそのまま用いるやり方について説明します。
3379 基本的には[[2.1.3節|2.1.3 Canvasのメンバ取得と変数の表示]]とほとんど同じですが、これまではここにアルファベットや記号、いわゆる1バイト文字でできた文字列を代入してきました。
3382 #include <psychlops.h>
3383 using namespace Psychlops;
3385 void psychlops_main() {
3386 Canvas sampleA(Canvas::fullscreen);
3388 sampleA.msg("A test of Letter Drawing", SampleA.getCenter());
3391 while(!Input::get(Keyboard::spc));
3395 ここで、表示したい文字列がひらがな・カタカナなどの2バイト文字とか全角文字と呼ばれる文字でできている場合は「”」(ダブルクォーテーション)の前に大文字のLを入れます。以下の例を見てください。
3399 #include <psychlops.h>
3400 using namespace Psychlops;
3402 void psychlops_main() {
3403 Canvas sampleA(Canvas::fullscreen);
3405 sampleA.msg(L"日本語の表示", SampleA.getCenter());
3408 while(!Input::get(Keyboard::spc));
3413 sampleA.msg(L"日本語の表示", SampleA.getCenter());
3415 の命令を見てわかるとおり、"日本語の表示" という文字列のまえに大文字のLが入っています。
3418 さらにこの部分に文字変数を入れたい場合はどうすればよいでしょうか。
3419 普通のプログラムで文字列によく使うstring, char型を使ってもコンパイルできません。
3420 このような場合はstring型を拡張したwstring型の変数を宣言してCanvas::msg()命令に代入します。
3423 |!Canvas::msg()|msg(std::wstring string,double x,double y,[[Psychlops::Color]] col)|char* stringを座標(x,y)に指定色で表示します|
3424 |~|~|string str:画面表示をする文字列を指定|
3425 |~|~|double x:画面表示をする座標(x)を指定|
3426 |~|~|double y:画面表示をする座標(y)を指定|
3427 |~|~|[[Psychlops::Color]] col:描画する点の色を指定(設定しない場合、白が設定される)|
3431 #include <psychlops.h>
3432 using namespace Psychlops;
3434 void psychlops_main() {
3436 Canvas sampleA(Canvas::fullscreen);
3438 wstr=L"日本語の表示";
3439 sampleA.msg(wstr, SampleA.getCenter());
3442 while(!Input::get(Keyboard::spc));
3446 ワイド文字列についてはいくつか知っておいた方が便利なことがあります。これらの詳細な情報については[[3.5節|3.5 文字列のレンダリングの仕様と関連情報]]をご覧ください。
3450 <div title="2.3.2 Lettersクラスを用いた文字列描画" modifier="Psychlops_DevelopperG" modified="200910260748" created="200910020318" changecount="34">
3451 <pre>Canvas命令単体での文字列描画は簡単ですが、フォントがぎざぎざしていたりしてあまり美しくは表示されません。
3452 また、もう少し大きな文字を表示したい場合やイタリック体で文字を表示したい場合にはこの方法では対応できません。
3453 この様な複雑な指定をして文字列を描画したい場合には事前にメモリに文字列を描画して、これを画面にコピーするという方法を行います。本や大量の部数の文書を作るときに、活字を組んで版をつくり、インクで紙に刷るのと同じ事です。
3454 この事前のメモリ領域への文字列描画のためのクラスとして、[[Letters]], [[Font]]クラスが用意されています。
3456 この方法による文字列描画の流れを簡単に説明すると以下のようになります
3457 # [[Font]]クラスを用いてフォントを指定する
3458 # [[Letters]]クラスを用いてメモリに文字列を描画
3459 # [[Canvas::msg()命令を用いてメモリ内の文字列を画面に描画]]
3460 以下でそれぞれの段階の具体的な方法について説明します。
3462 ![[Font]]クラスを用いたフォントの指定
3463 Psychlopsにおけるフォントの指定は、[[Font]]クラスの変数を宣言し、これを引数として代入することによって行います。
3464 Fontの宣言は以下の書式に従って行います。
3465 |!Fontの宣言|font(double size, int weight, Style style, std::wstring family);|!フォントを指定します。|>|
3466 |~|~|double size|フォントサイズ(pixel)を指定|
3467 |~|~|int weight|線幅を指定 <br> (normal_style (400) / bold (700) : 省略可能)|
3468 |~|~|Style style|斜体の有無を指定 <br> (normal_style / italic : 省略可能)|
3469 |~|~|std::wstring family|フォント名を指定 <br> (省略可能)|
3473 Font font1(24, Font::bold, Font::italic, L"ArialMT");
3476 Font font2(12, Font::bold, Font::normal_style, L"メイリオ");
3479 Font font3(18, Font::normal_weight, Font::normal_style, L"ヒラギノ角ゴ Pro");
3482 第2引数は文字の太さ、第3引数で斜体の有無を選択します。
3483 第4引数はフォントの名前を2重引用符(")でくくって指定します。
3486 Canvas.msg関数などで使われるデフォルトフォントを変更するには、Font::default_font変数に新しいフォント構造体を指定します。
3488 Font::default_font = Font(L"Arial", 20);
3491 フォントはインストールされていなければその環境標準のものになります。一般的にWindowsとMacOSXでは同じ名前のフォントはありませんので、注意してください。
3493 ![[Letters]]クラスによる文字列のプリレンダリングと描画
3494 次のステップでは、上で用意したフォントを使って、文字列の元版(レンダリング済みの文字列、といいます)をメモリ内に作成します。
3495 これにはLettersクラスを使用します。
3497 |!Lettersの宣言|Letters(std::wstring str, Font font );|!文字列を指定されたフォントでレンダリングします。|>|
3498 |~|~|std::wstring str|描画する文字列を指定|
3499 |~|~|Font font|フォントを指定 <br> (省略可能)|
3501 ここでも実際の例をいくつかあげてみましょう。
3503 Letters let1(L"Space Barを押すと文字が変わります", Font(20, Font::bold));
3506 Letters let2(L"矢印キーを押してください。配置が変わります。");
3509 Letters let3(L"Font \"Arial\" have only Latain characters. 日本語フォントが必要です.", font1);
3512 文字列の内容をプログラム内で変更したいときなど、宣言とは違うところで文字列をレンダリングしたいときには、Letters::string()命令を使います。
3514 |!Letters::string()|string(std::wstring str);|!文字列を指定されたフォントでレンダリングします。|>|
3515 |~|~|std::wstring str|描画する文字列を指定|
3517 このとき、font指定はLetters::font()命令を使用して別に行う事になりますが、何も指定せずデフォルトのフォントでレンダリングする事もできます。
3519 |!Letters::font()|font(Font font);|!文字列を指定されたフォントでレンダリングします。|>|
3520 |~|~|std::wstring str|描画する文字列を指定|
3521 |~|~|Font font|フォントを指定 |
3525 Letters let1(L"Space Barを押すと文字が変わります", Font(20, Font::bold));
3527 をこれらの命令を使って書き直してみるとこの様になるでしょう。
3530 let1.string(L"Space Barを押すと文字が変わります");
3531 let1.font(Font(20, Font::bold));
3536 ! Canvas::msg()命令を用いてメモリ内の文字列を画面に描画
3537 ここまでの段階で、指定されたフォントを使って表示したい文字列の元版ができあがりました。実際に画面にこれを表示するにはCanvas::msg()命令を使います。
3539 |!Canvas::msg()|msg(Letters let, double x, double y, Color col, align, algn );|!文字列を指定されたフォントでレンダリングします。|>|
3540 |~|~|Letters let|描画するレンダリング済み文字列を指定|
3541 |~|~|double x|文字列描画の水平位置を指定|
3542 |~|~|double y|文字列描画の垂直位置を指定|
3543 |~|~|Color col|文字列の描画色を指定 <br> (省略可能)|
3544 |~|~|align, algn|文字詰めの方法を指定 <br> (省略可能)|
3547 Letters let1(L"Space Barを押すと文字が変わります", Font(20, Font::bold));
3548 Display::msg(let1, 100, 100);
3552 ! Letters::draw()命令を用いてメモリ内の文字列を画面に描画
3553 もう一つ同じ目的の命令としてLetters::draw()命令があります。
3554 LettersクラスにはRectangle型に対して使用できるcentering(), shift()などの命令が使えます。
3555 これらの命令を使って同じ文字列を異なる位置に簡単に描画することができます。
3556 |!Letters::centering()|centering(double x, double y)|!レンダリング済みの文字列の描画位置をその中央の座標で指定する。|>|
3557 |~|~|double x|センタリングの水平位置を指定<br> (省略可能)|
3558 |~|~|double y|センタリングの垂直位置を指定<br> (省略可能)|
3559 |~|~|>|>|これらの値が省略されるときは、画面中央に描画位置が設定される|
3561 |!Letters::shift()|shift(double x, double y)|!レンダリング済みの文字列の描画位置を現在の指定位置から移動する。|>|
3562 |~|~|double x|水平方向の移動量を指定|
3563 |~|~|double y|垂直方向の移動量を指定|
3565 |!Letters::draw()|draw(Color col)|!レンダリング済みの文字列を描画する|>|
3566 |~|~|double x|水平方向の移動量を指定|
3567 |~|~|double y|垂直方向の移動量を指定|
3568 |~|~|Color col|文字列の描画色を指定 <br> (省略可能)|
3572 Letters let1(L"Space Barを押すと文字が変わります", Font(20, Font::bold));
3573 let1.centering(Mouse::x, Mouse::y+30).draw(Color::green);
3579 ここまでをまとめて、文字列描画の簡単なプログラム例を作ってみましょう。
3581 #include <psychlops.h>
3582 using namespace Psychlops;
3584 void psychlops_main() {
3586 Canvas sampleA(Canvas::fullscreen); //描画用windowの取得
3588 Letters let1(L"Space Barを押すと文字が変わります", Font(20, Font::bold));
3590 let2.string(L"文字が変わりました");
3591 let2.font(Font(20, Font::bold));
3593 sampleA.clear(Color(0.0));
3594 sampleA.msg(let1, 100, 100);
3596 while(!Input::get(Keyboard::spc));
3598 sampleA.clear(Color(0.5));
3599 let2.centering().draw(Color::red);
3600 while(!Input::get(Keyboard::spc));
3606 <div title="3. 複雑な描画を行う1(オフスクリーンへの描画)" modifier="Psychlops_DevelopperG" modified="200910260736" created="200708211945" changecount="2">
3607 <pre>[[3.1 オフスクリーン描画の基本]]
3608 [[3.1.1 オフスクリーン領域の宣言]]
3609 [[3.1.2 オフスクリーン上に点・四角形を描画する]]
3610 [[3.1.3 オフスクリーン上の任意の位置に描画する]]
3612 [[3.2 オフスクリーンを用いた描画例-ランダムドットパターン-]]
3614 [[3.3.1 画像ファイルを保存する]]
3615 [[3.3.2 画像ファイルを読み込み表示する]]
3616 [[3.3.3 複数の画像ファイルからアニメーションを作成する]]
3617 [[3.4 Canvasのオフスクリーンへの取り込み]]
3618 [[3.5 文字列のレンダリングの仕様と関連情報]]</pre>
3620 <div title="3.1 オフスクリーン描画の基本" modifier="Psychlops_DevelopperG" modified="200908190234" created="200708292225" changecount="7">
3621 <pre>[[3.1.1 オフスクリーン領域の宣言]]
3622 [[3.1.2 オフスクリーン上に点・四角形を描画する]]
3623 [[3.1.3 オフスクリーン上の任意の位置に描画する]]
3624 [[3.1.4 発展的な内容]] </pre>
3626 <div title="3.1.1 オフスクリーン領域の宣言" modifier="Psychlops_Admin" modified="200709142108" created="200708292249" changecount="31">
3629 第2章では直にCanvasに描画してきました。第3章ではオフスクリーンへ領域の描画ないし取扱いをします。
3630 Canvasは裏表2枚しかないので、複雑な動画を描画するには不便です。
3631 裏画面に描画が終わるまでflip()を実行できないので(もし強引にflip()を実行してしまったら、描画途中の中途半端なイメージが表示されてしまいます)、複雑な動画ではアニメーションの質が下がってしまいます。
3632 このような動画の描画や、とても時間のかかる内容の描画を行うときにはオフスクリーン領域(描画バッファともいいます)と呼ばれる特殊な描画領域を用います。
3633 このオフスクリーン領域はメモリ上に確保された画像ファイルのようなものだと考えるとよいかもしれません。
3634 この画像ファイルに複雑な描画内容を保存しておいて、後で紙芝居のように画面に表示することができれば、複雑な内容の描画も素早く行えるわけです。^^*1^^
3635 Psychlopsではこのオフスクリーン領域をImageというクラスを用いて操作します。
3637 ^^ *1もう少し正しく表現するなら、オフスクリーン領域とはコンピュータのメインメモリ(RAM 1GBなどという”あれ”のことです)内に確保された描画内容の保存場所(描画用バッファ)のことです。^^
3640 ここではImageとCanvasの違いについて詳しく説明することはしませんが、これら2つのクラスは物理的に保管場所が違うということは頭に置いておいてください。
3641 Canvasの内容は画面上に素早く提示することのできる位置に保管されています。flip()命令を実行すると正確なタイミングで描画が起こるわけです。
3642 これに対して、ImageはCanvasよりも遙かに大量のデータを保管することができますが、画面上に描画内容を提示するには、ある程度の時間が必要です。
3643 これは、Canvas(の保管場所)にImageの内容を転送する必要があるからです。
3644 より詳しい内容については[[Tips: Image使用上の注意]]を参考にしてください。
3645 [img[image/オフスクリーン領域.png]]
3649 宣言時には取り扱いたい画像の大きさやカラーモード、透明度の指定が必要となってきます。
3650 Image()のように宣言のみの場合は''Image::set()''命令で指定します。
3652 |!Imageの宣言|Image()|オフスクリーン領域を宣言します|
3653 |~|Image([[Psychlops::Rectangle]] rec,PixelComponentsCnt pixcompcntval)|指定された[[Psychlops::Rectangle]] 型と同じ大きさのオフスクリーン領域を宣言します|
3654 |~|~|[[Psychlops::Rectangle]] rect :[[Psychlops::Rectangle]] 型を指定|
3655 |~|~|PixelComponentsCnt pixcompcntval :[[カラーモード]]を指定。省略した場合はRGBが指定される|
3656 |~|Image(long x, long y, PixelComponentsCnt pixcompcntval)|指定されたx×yと同じ大きさのオフスクリーン領域を宣言します|
3657 |~|~|long x :オフスクリーン領域の横幅を指定|
3658 |~|~|long y :オフスクリーン領域の縦幅を指定|
3659 |~|~|PixelComponentsCnt pixcompcntval :[[カラーモード]]を指定。省略した場合はRGBが指定される|
3662 オフスクリーン領域の宣言時に何の指定もしなかった場合、Image::set()命令を使用します。
3663 |!Image::set()|set([[Psychlops::Rectangle]] rect, PixelComponentsCnt pixcompcntval)|指定された[[Psychlops::Rectangle]] 型と同じ大きさのオフスクリーン領域を設定します|
3664 |~|~|[[Psychlops::Rectangle]] rect :[[Psychlops::Rectangle]] 型を指定|
3665 |~|~|PixelComponentsCnt pixcompcntval :[[カラーモード]]を指定。省略した場合はRGBが指定される|
3666 |~|set(long x, long y, PixelComponentsCnt pixcompcntval)|指定されたx×yと同じ大きさのオフスクリーン領域を設定します|
3667 |~|~|long x :オフスクリーン領域の横幅を指定|
3668 |~|~|long y :オフスクリーン領域の縦幅を指定|
3669 |~|~|PixelComponentsCnt pixcompcntval :[[カラーモード]]を指定。省略した場合はRGBが指定される|
3674 #include <psychlops.h>
3675 using namespace Psychlops;
3677 Psychlops::Rectangle rect1(200,100);
3678 Psychlops::Image Noise1(rect1,Image::GRAY);
3680 void psychlops_main() {
3682 Canvas sampleA(Canvas::fullscreen);
3684 while(!Input::get(Keyboard::spc));
3688 次はImageの宣言と[[Image::set()]]命令を組み合わせたコードです。
3691 #include <psychlops.h>
3692 using namespace Psychlops;
3694 Psychlops::Image Noise1;
3695 long Width=200, Height=100;
3697 void psychlops_main() {
3699 Canvas sampleA(Canvas::fullscreen);
3700 Noise1.set(Width,Height,Image::RGBA);
3702 while(!Input::get(Keyboard::spc));
3705 これらのコードは実行しても黒い画面が表示されるだけでした。
3706 次の3.1.2節で実際にImage上に描画を行います。</pre>
3708 <div title="3.1.2 オフスクリーン上に点・四角形を描画する" modifier="Psychlops_DevelopperG" modified="200908190233" created="200709032203" changecount="8">
3709 <pre>[[3.1.1|3.1.1 オフスクリーン領域の宣言]]節ではImageの宣言(領域確保)のみを行っていたので、コードを実行しても黒い画面が表示されるだけでした。
3710 本節では実際にImage上に点・四角形を描画して、これを画面に表示してみます。
3711 Imageクラスの命令(メソッド)はCanvasに対する命令とほとんど変わりません。
3714 オフスクリーン領域に点を描画するための命令です。
3715 [[Canvas::pix()|2.2.1 画面上に点を描画する-Pointクラス1-]]命令同様に点を描画するために「座標」と「色の指定」を行います。
3716 色の指定についての詳細は[[2.2.5 描画する図形の色を指定する-Colorクラス-]]を参照してください。
3718 |!Image::pix()|pix(int x, int y, [[Psychlops::Color]] col)|座標(x,y)に[[Psychlops::Color]] col色の点を描画します|
3719 |~|~|int x: 描画する点の座標軸xを指定|
3720 |~|~|int y: 描画する点の座標軸yを指定|
3721 |~|~|[[Psychlops::Color]] col:描画する点の色を指定|
3722 |~|pix(double x, double y, [[Psychlops::Color]] col)|座標(x,y)に[[Psychlops::Color]] col色の点を描画します|
3723 |~|~|double x: 描画する点の座標軸xを指定|
3724 |~|~|double y: 描画する点の座標軸yを指定|
3725 |~|~|[[Psychlops::Color]] col:描画する点の色を指定|
3726 |~|pix([[Psychlops::Point]] po, [[Psychlops::Color]] col)|Point座標(x,y)に[[Psychlops::Color]] col色の点を描画します|
3727 |~|~|[[Psychlops::Point]] po :描画する点の座標(x,y)を指定|
3728 |~|~|[[Psychlops::Color]] col:描画する点の色を指定|
3731 実際にオフスクリーン領域に描画する際の命令です。
3732 この命令を記述しないといくら''Image::pix()''命令やこの後出てくる''Image::rect()''命令を記述しても描画されませんので注意してください。
3734 |!Image::draw()|draw()|指定したオフスクリーンを描画します|
3735 |~|draw(double left, double top)|指定したオフスクリーン領域を(left,top)を中心とした座標に設定します|
3736 |~|~|double left: 描画するオフスクリーン領域の座標軸xを指定|
3737 |~|~|double top: 描画するオフスクリーン領域の座標軸yを指定|
3739 !!Image::pix(),Image::draw()の書き方
3740 100×100のImageに点を描画します。
3743 #include <psychlops.h>
3744 using namespace Psychlops;
3746 double width=100,height=100;
3747 Psychlops::Rectangle rect1(width,height);
3748 Psychlops::Color col;
3749 Psychlops::Image Noise1(rect1,Image::GRAY);
3751 void psychlops_main() {
3753 Canvas sampleA(Canvas::fullscreen);
3754 //水平方向に2,垂直方向に5間隔の点を描画する。
3755 for(int x=0; x<width; x+=2){
3756 for(int y=0; y<height; y+=5){
3758 Noise1.pix(x,y,col);
3763 while(!Input::get(Keyboard::spc));
3768 オフスクリーン領域に四角形を描画するための命令です。
3769 実際の描画処理は''Image::draw()''命令で行われます。
3770 色の指定についての詳細は[[2.2.5 描画する図形の色を指定する-Colorクラス-]]を参照してください。
3772 |!Image::rect()|rect([[Psychlops::Rectangle]] rect, [[Psychlops::Color]] col)|[[Psychlops::Rectangle]]型の四角形を[[Psychlops::Color]] col色で描画します|
3773 |~|~|[[Psychlops::Rectangle]] rect : 描画する四角形を指定|
3774 |~|~|[[Psychlops::Color]] col:描画する四角形の色を指定|
3777 100×100のImageに青の四角形を描画します。
3780 #include <psychlops.h>
3781 using namespace Psychlops;
3783 Psychlops::Rectangle rect1(100,100);
3784 Psychlops::Image Noise1(rect1);
3786 void psychlops_main() {
3788 Canvas sampleA(Canvas::fullscreen);
3789 Noise1.rect(rect1,Color::blue);
3792 while(!Input::get(Keyboard::spc));
3796 <div title="3.1.3 オフスクリーン上の任意の位置に描画する" modifier="Psychlops_DevelopperG" modified="200908190258" created="200709042020" changecount="16">
3797 <pre>前節でオフスクリーン上に図形の描画を行いました。
3798 描画位置の指定がない場合、オフスクリーン領域は常に画面の左上に設定されます。
3799 この節ではオフスクリーン領域を任意の場所に移動します。
3800 2.2節ではそれぞれ[[Psychlops::Point型の移動|2.2.1 画面上に点を描画する-Pointクラス1-]]と[[Psychlops::Rectangle型の移動|2.2.6 画面上に縞を描画する-Rectangleクラス2-]]の移動について説明しました。
3801 オフスクリーン領域の移動も上記の型と同様の命令文を使用します。
3804 [[Psychlops::Image]]型の領域を移動します。
3805 座標を指定する場合、その座標に領域の中心が設定されます。
3806 |!Image::centering()|centering()|[[Psychlops::Image]]型の領域を画面中心に移動します|
3807 |~|centering(double x, double y)|[[Psychlops::Image]]型の領域の中心を指定した座標(x,y)に移動します|
3808 |~|~|double x:移動する領域のx座標を指定|
3809 |~|~|double y:移動する領域のy座標を指定|
3811 !!Image::centering()の書き方
3812 前節の<例1>で作成したImageを画面中心に移動します。
3815 #include <psychlops.h>
3816 using namespace Psychlops;
3818 double width=100,height=100;
3819 Psychlops::Rectangle rect1(width,height);
3820 Psychlops::Color col;
3821 Psychlops::Image Noise1(rect1,Image::GRAY);
3823 void psychlops_main() {
3825 Canvas sampleA(Canvas::fullscreen);
3828 //水平方向に2,垂直方向に5間隔の点を描画する。
3829 for(int x=0; x<width; x+=2){
3830 for(int y=0; y<height; y+=5){
3832 Noise1.pix(x,y,col);
3837 while(!Input::get(Keyboard::spc));
3842 [[Psychlops::Image]]型の領域を移動します。
3843 元の座標(x1,y1)を基点に入力値(x,y)だけ移動します。
3844 |!Image::shift()|shift(double h, double v)|座標(X,Y)を(h,v)だけ移動します|
3845 |~|~|double x:入力値分、右水平方向に移動|
3846 |~|~|double y:入力値分、下垂直方向に移動|
3848 !!Image::shift()の書き方
3849 前節の<例2>で作成したImageを座標(100,50)分移動します。
3852 #include <psychlops.h>
3853 using namespace Psychlops;
3855 Psychlops::Rectangle rect1(100,100);
3856 Psychlops::Image Noise1(rect1);
3858 void psychlops_main() {
3860 Canvas sampleA(Canvas::fullscreen);
3861 Noise1.shift(100,50);
3862 Noise1.rect(rect1,Color::blue);
3865 while(!Input::get(Keyboard::spc));
3869 <div title="3.1.4 発展的な内容" modifier="Psychlops_Admin" created="200709252141" changecount="1">
3870 <pre>この節ではは少し発展的で込み入った内容を取り扱います。ここまでの命令を使ってとりあえず自分の目的が達成できている場合は無視しても構いません。
3871 複雑で大きなサイズのイメージを高速でどうしても表示したい時にだけ、読んでみてください。
3874 3.1.1節でサイズが大きなImageをCanvasに転送するには時間がかかるという説明をしました。
3876 転送するのにかかる時間の総和を短縮することはできませんが、PsychlopsはImageをCanvasへより早く転送することのできるメモリ領域へ転送することができます。
3878 Canvasに描画する場合はCanvasの裏に描画して[[Canvas::flip()]]命令で画面に表示しています。
3879 Imageはコードの記述はCanvasと大差ありませんが、Canvasの裏ではなくメインメモリ(主記憶装置)に描画しています。
3880 [[Image::draw()]]命令を実行することで初めてメインメモリ(主記憶装置)にある図形がVRAMに転送され、flip()とともに画面に表示されます。
3881 <[[Image::draw()]]命令実行時のイメージ図>
3882 [img[image/draw()命令.png]]
3884 つまり普通であればImageからCanvasへ直接転送が起こります。
3885 ここで、この2つの中間地点にあらかじめ転送しておくことで、Canvasへの転送時間を見かけ上小さくすることができるのです。
3886 この中間地点はVRAMと呼ばれるビデオボード上のメモリチップの中に作られます。
3887 実は、VRAM内でのメモリの転送速度に比べてメインメモリからVRAMへの転送はとても時間がかかるのです。
3888 この時間がかかる部分を事前に行っておくことで、見かけ上の転送時間を大きく縮めることができます。
3889 この転送を事前に一部行っておく命令がImage::quicken()命令です。
3891 [img[image/quicken()命令.png]]
3894 通常、[[Image::draw()]]命令を使用すると上図のようにメインメモリ(主記憶装置)から画面まで一気に転送し描画します。
3895 quicken()を実行すると、Imageの図形をVRAMに転送し、quickenモードに切り替えます。
3896 そして[[Image::draw()]]命令でVRAMにある図形を画面に転送し表示します。
3898 |!Image::quicken()|quicken()|メインメモリ(主記憶装置)にあるImageの内容をあらかじめVRAMに送ります|
3899 |~|~|解除する場合はquicken(false)と記述|
3901 !!Image::quicken()の書き方
3902 [[Image::draw()]]命令の直前に記述します。
3905 #include <psychlops.h>
3906 using namespace Psychlops;
3908 double width=100,height=100;
3909 Psychlops::Rectangle rect1(width,height);
3910 Psychlops::Color col;
3911 Psychlops::Image Noise1(rect1);
3913 void psychlops_main() {
3915 Canvas sampleA(Canvas::fullscreen);
3917 for(int x=0; x<width; x+=3){
3918 for(int y=0; y<height; y+=3){
3919 col.set(0.5,0.2,0.8);
3920 Noise1.pix(x,y,col);
3926 while(!Input::get(Keyboard::spc));
3930 !!Image::quicken()の使用上の注意
3932 # ImageがRGB/RGBAモードで宣言されていること
3933 # Imageへの描画がすでに完了していること
3937 Imageに対する描画命令は全てメインメモリ上にあるImageに対して行われます。
3938 したがって、すでにquicken()してしまったimageに対して描画を行っても、変更が反映されません。
3941 #include <psychlops.h>
3942 using namespace Psychlops;
3944 double width=100,height=100;
3945 Psychlops::Rectangle rect1(width,height);
3946 Psychlops::Color col;
3947 Psychlops::Image Noise1(rect1);
3949 void psychlops_main() {
3951 Canvas sampleA(Canvas::fullscreen);
3954 while(!Input::get(Keyboard::spc)){
3955 for(int x=0; x<width; x+=3){
3956 for(int y=0; y<height; y+=3){
3957 col.set(0.5,0.2,0.8);
3958 Noise1.pix(x,y,col);
3966 この新しい描画内容を画面に反映させるには、quicken(false)命令を使ってquicken()モードを解除するか、再度Image::quicken()命令を実行してもう一度転送を行う必要があります。<プログラム例>
3967 (転送を行ってもメインメモリ上にコピーは残っているので、単にこれをもう一度転送し直すことで描画を反映させることができます)
3970 #include <psychlops.h>
3971 using namespace Psychlops;
3973 double width=100,height=100;
3974 Psychlops::Rectangle rect1(width,height);
3975 Psychlops::Color col;
3976 Psychlops::Image Noise1(rect1);
3978 void psychlops_main() {
3980 Canvas sampleA(Canvas::fullscreen);
3983 while(!Input::get(Keyboard::spc)){
3984 for(int x=0; x<width; x+=3){
3985 for(int y=0; y<height; y+=3){
3986 col.set(0.5,0.2,0.8);
3987 Noise1.pix(x,y,col);
3992 Noise1.quicken(false);
3996 ※2007年9月現在、quicken()命令はRGB/RGBAでのみ稼働</pre>
3998 <div title="3.2 オフスクリーンを用いた描画例-ランダムドットパターン-" modifier="YourName" modified="200802191418" created="200709052137" changecount="18">
3999 <pre>この節ではImageクラスを用いたオフスクリーンへの描画の利用例として、下図に示した砂嵐のような図、ランダムドットパターンを描画してみます。
4000 [img[image/randomdot.png]]
4001 はじめにドットサイズが1ピクセル四方の基本的なパターンを描画する例を説明した後、ドットサイズがもっと大きいものについて説明します。
4003 [[3.2.1 基本的なランダムドットを描画する]]
4004 [[3.2.2 ドットサイズの異なるランダムドットパターンの描画]]
4005 [[3.2.3 ランダムドットキネマトグラムの描画]]
4007 この節では、複数の細いImageをshift()命令を使って並べることによって、ランダムドットを描画しています。この方法は簡単ですが、ドット密度やドットの運動方向を精密に操作したい場合は、Rectangleの配列を用いた方が描画がスムーズな場合があります。これについては、[[Tips: Rectangleの配列を用いたドットパターンの描画例]]を参照してください。</pre>
4009 <div title="3.2.1 基本的なランダムドットを描画する" modifier="YourName" modified="200802232254" created="200709130524" changecount="7">
4010 <pre>PsychlopsではImageを用いてランダムドットパターンが比較的簡単に描画できます。
4011 ランダムドットを描画するためには''Psychlops::random()''命令を使用します。
4012 色の返値を''Psychlops::random()''命令を使用しランダムに値を返すことでランダムドットパターンが描画できます。
4014 !!Psychlops::random()
4015 0~1,0~任意の数等好きに設定した値の中でランダムに数を返します。
4017 |!Psychlops::random()|random()|0~1の値をランダムに返します。|
4018 |~|random(double N)|0~Nの値をランダムに返します。|
4019 |~|~|double N: ランダムで返す値の最大値を指定|
4020 |~|random(int N)|0~Nの整数値をランダムに返します。|
4021 |~|~|int N: ランダムで返す整数値の最大値を指定|
4022 |~|random(double min, double max)|min~maxの値をランダムに返します。|
4023 |~|~|double min: ランダムで返す値の最小値を指定|
4024 |~|~|double max: ランダムで返す値の最大値を指定|
4026 引数が実数(double)か整数(int)かで動作が異なりますので、実数で返したい場合は最大値が整数でも必ず1.0などのように小数点をつけて指定してください。整数変数を指定する場合は(double)でキャストしてください。
4028 random(5) ← [0,1,2,3,4] のいずれかの数がint型で返る
4029 random(5.0) ← [0, 5)の間の浮動小数点値が返る
4032 random(i) ← [0,1,2,3,4] のいずれかの数がint型で返る
4033 random((double)i) ← [0, 5)の間の浮動小数点値が返る
4036 !!!Psychlops::random()の書き方
4037 3.1.3節の<例1>では画面中央のImage上に点を描画しました。
4038 このコードを応用してランダムドットを描画します。
4039 3.1.3節の<例1>では水平方向に2 ピクセル,垂直方向に5 ピクセル間隔に色:0.5の点を描画しました。
4040 ここではグレースケールのランダムドットを描画するために、色(輝度)が0 ~ 1の間でランダムに設定された、100 x 100 ピクセル四方の隙間のないドット列を描画します。
4043 #include <psychlops.h>
4044 using namespace Psychlops;
4046 double width=100,height=100;
4048 Psychlops::Rectangle rect1(width,height);
4049 Psychlops::Color col;
4050 Psychlops::Image Noise1(rect1,Image::GRAY);
4052 void psychlops_main() {
4054 Canvas sampleA(Canvas::fullscreen);
4056 for(int x=0; x<width; x++){
4057 for(int y=0; y<height; y++){
4059 temp1=Psychlops::random();
4061 Noise1.pix(x,y,col);
4066 while(!Input::get(Keyboard::spc));
4069 <例1>では[[カラーモード]]をGRAYで描画しましたがRGB,RBGAでも描画可能です。
4070 <例2>ではRGBAでランダムドットを描画します。
4073 #include <psychlops.h>
4074 using namespace Psychlops;
4076 double width=100,height=100;
4077 double temp1,temp2,temp3,temp4;
4078 Psychlops::Rectangle rect1(width,height);
4079 Psychlops::Color col;
4080 Psychlops::Image Noise1(rect1,Image::RGBA);
4082 void psychlops_main() {
4084 Canvas sampleA(Canvas::fullscreen);
4086 for(int x=0; x<width; x++){
4087 for(int y=0; y<height; y++){
4088 //R,G,B,Aそれぞれに0~1の値をランダムに返します
4089 temp1=Psychlops::random();
4090 temp2=Psychlops::random();
4091 temp3=Psychlops::random();
4092 temp4=Psychlops::random();
4093 col.set(temp1,temp2,temp3,temp4);
4094 Noise1.pix(x,y,col);
4099 while(!Input::get(Keyboard::spc));
4103 [img[image/colornoise.png]]
4107 <div title="3.2.2 ドットサイズの異なるランダムドットパターンの描画" modifier="Psychlops_Admin" modified="200709131828" created="200709130500" changecount="4">
4108 <pre>今まで描画したランダムドットパターンのドットサイズは1 ピクセル四方でした。
4109 次はピクセルサイズがn( n > 1 )のランダムドットパターンを描画します。
4110 1 ピクセルのドットを描画する時[[Image::pix()]]命令を使用していましたが
4111 n ピクセルのドットを描画するためには[[Image::rect()]]命令を使用します。
4112 横幅×高さの領域にn ピクセルのランダムドットパターンを描画する場合、以下の要領で描画します。
4114 1.ドットサイズ × ( 高さ × ドットサイズ )のImageをN個(横幅/ドットサイズ)用意する。
4115 2.ドットサイズ × ドットサイズのrectをN個( 横幅 / ドットサイズ )用意する。
4116 3.1で作成したImage(x)に2で作成したrect(x)をセットする。
4117 4.2で作成したrect(x)にランダムドットパターンを描画する。
4118 5.rectをドットサイズ縦方向に移動し、Image(x)全てに描画されるまで3~4を行う。
4121 [img[image/dotsize.png]]
4123 下の例では、横幅がwドット分、縦幅がhドット分のランダムドットパターンを描画しています。ここで指定した単位がドット単位でピクセル単位でないことに注意して、例を見てみてください。
4126 #include <psychlops.h>
4127 using namespace Psychlops;
4129 const int dotsize=4;
4130 const int width=100;
4133 Psychlops::Rectangle rect1[width];
4134 Psychlops::Color col;
4135 Psychlops::Image Noise1[width];
4137 void psychlops_main() {
4139 Canvas sampleA(Canvas::fullscreen);
4141 for(int x=0; x<width; x++){
4142 Noise1[x].set(dotsize,height*dotsize);
4143 rect1[x].set(dotsize, dotsize);
4144 for(int y=0; y<height; y++){
4145 temp1=Psychlops::random();
4147 Noise1[x].rect(rect1[x],col);
4149 rect1[x].shift(0, dotsize);
4151 Noise1[x].centering().shift((double)-1/2*(width*dotsize)+x*dotsize,0);
4155 while(!Input::get(Keyboard::spc));
4158 ドットサイズを他の値に変えて実行したい場合は『const int dotsize=4;』の値を変更してください。
4159 この例では、画面中央にランダムドットパターンを描画しました。画面中央に描画するための式は[[2.2.6節|2.2.6 画面上に縞を描画する-Rectangleクラス2-]]を参照してください。
4161 ここでは( Xc - 1 / 2 * w + i , Yc )のwとiにドットサイズ dotsize を乗算しています。
4162 ドットサイズを乗算することでImageが重ならないように移動して描画しています。
4163 試しにドットサイズの乗算を外すと、1ピクセル分のみ移動されて最後の列のみ正常に、他の列は細長いドットが表示されます。これはImageが重なって描画されてしまっているためです。
4164 [img[image/dotsize2.png]]
4167 <div title="3.2.3 ランダムドットキネマトグラムの描画" modifier="Psychlops_Admin" created="200709252142" changecount="1">
4168 <pre>!ランダムドットキネマトグラムを描画しよう
4169 次は上記のランダムドットを水平方向に動かしてみましょう。
4170 このような運動するランダムドットパターンをランダムドットキネマトグラムといいます。
4172 今回は水平方向に移動するのでX座標( Xc - 1 / 2 * w + i )について考慮します。
4173 ( Xc - 1 / 2 * w + i ) + α と記述すると α 分移動します。
4174 しかし既存の横幅内での運動を行いたいので
4175 下図のように右に飛び出たα を前に移動します。
4176 移動要素である i + α に対しwで除算し余りを算出することで求まります。
4178 ( Xc - 1 / 2 * w ) + ( i + α ) % w
4180 [img[image/rdk.png]]
4181 <例3>ではドットサイズを変更しているので、それぞれの値をドットサイズ倍する必要があります。
4183 ( Xc - 1 / 2 * w*dotsize ) + ( i *dotsize+ α*dotsize ) % w*dotsize
4186 次に α の内容について考えてみましょう。
4187 1フレームあたりの移動量をnドット分とすると、mフレーム目での0フレーム目の位置からのピクセル単位での移動量αは、 n* dotsize * m となります。
4188 つまりi番目のimageの水平方向の移動量は、以下のようになります。
4190 ( Xc - 1 / 2 * w * dotsize ) + ( i *dotsize+ frame*speed*dotsize ) % w*dotsize
4192 第1項(+の前)はImageをXcを中心として並べるための移動量、第2項が運動するパターンを実現するための移動量です。
4197 #include <psychlops.h>
4198 using namespace Psychlops;
4200 const int dotsize=4;
4201 const int width=100;
4207 Psychlops::Rectangle rect1[width];
4208 Psychlops::Color col;
4209 Psychlops::Image Noise1[width];
4211 void psychlops_main() {
4213 Canvas sampleA(Canvas::fullscreen);
4215 for(int x=0; x<width; x++){
4216 Noise1[x].set(dotsize,height*dotsize);
4217 rect1[x].set(dotsize, dotsize);
4218 for(int y=0; y<height; y++){
4219 temp1=Psychlops::random();
4221 Noise1[x].rect(rect1[x],col);
4222 rect1[x].shift(0, dotsize);
4225 while(!Input::get(Keyboard::spc)){
4226 for(int x=0; x<width; x++){
4227 phase=(double)-1/2*(width*dotsize)+(int)(x*dotsize+frame*speed*dotsize)%(int)(width*dotsize);
4228 Noise1[x].centering().shift(phase,0);
4237 水平方向へ移動するドットパターンが描画されたでしょうか?
4238 これと同様にしてランダムドットステレオグラムを書くこともできます。[[Tips: ランダムドットステレオグラムの描画コード]]を参照してみてください。</pre>
4240 <div title="3.3 画像ファイルの取り扱い" modifier="Psychlops_Admin" modified="200709121657" created="200708292225" changecount="4">
4241 <pre>この節では、画像ファイルの取扱いについて説明します。
4242 現バージョンのPsychlopsではpng形式のファイルの読込と保存が可能です。
4243 この読込と保存はImage型を通して行われます。
4244 読み込まれた画像ファイルはPsychlops内部ではImage型の変数(インスタンス)として扱われます。
4245 同様に保存はImage型の描画内容をPng形式でファイルに書き出すことによって行われます。
4248 [[3.3.1 画像ファイルを保存する]]
4249 [[3.3.2 画像ファイルを読み込み表示する]]
4252 <div title="3.3.1 画像ファイルを保存する" modifier="Psychlops_DevelopperG" modified="200910080846" created="200709111925" changecount="34">
4253 <pre>ディスプレイに提示した画像をプログラムが終了した後でも使えるように保存したい時がよくあります。
4254 この節では、画面上に描画した図形を外部ファイルとして保存する方法について説明します。
4255 PsychlopsではImageの描画内容を、PNG形式の画像ファイルとして保存することができます。
4256 これにはImage::save()命令を使用します。はじめにこのImage::save()命令の使い方と例を見てみましょう。
4261 |!Image::save()|save(filename)|!指定したImageを保存します|>|>|
4262 |~|~|filename| 保存するImageの画像ファイル名称(拡張子まで)を指定|>|>|
4263 |~|~|保存先を指定しない場合のパス|Mac:Macintosh HD/ユーザ/ユーザ名(任意)/書類/Psychlops/ ^^*1^^|>|
4264 |~|~|~|Win:実行ファイルと同じパス|>|
4265 |~|~|保存をする場合のパス指定|%APP%|Mac:実行バンドル(.app)と同じパス|
4266 |~|~|~|~|Win:実行ファイルと同じパス|
4267 |~|~|~|%RESOURCES%|Mac:実行バンドルの内部のResourcesフォルダ|
4268 |~|~|~|~|Win:実行ファイルのパスの直下のrscという名前のフォルダ|
4269 |~|~|~|%~USER_HOME%|Mac:"~/"と同じ(/Users/(user)/)|
4270 |~|~|~|~|Win:ユーザープロファイルフォルダ(C:\Documents and Settings\(user)\)|
4271 |~|~|~|%~USER_SETTING%|Mac:"~/Library/Psychlops/"|
4272 |~|~|~|~|Win:"%~AppData%Psychlops"|
4273 |~|~|~|%~USER_DOCUMENTS%|Mac:"~/Documents/Psychlops"|
4274 |~|~|~|~|Win:ユーザープロファイルフォルダ\My Documents\Psychlops|
4275 |~|~|~|%~USER_DOCUMENTS_ROOT%|Mac:"~/Documents"|
4276 |~|~|~|~|Win:ユーザープロファイルフォルダMy Documents\|
4277 *1 Finderでの表示は上記のとおりですが、Psychlopsで指定する場合は下記のUnix形式でパスを指定してください
4278 ** "~/Documents/Psychlops/"
4279 *2 MacOSX版では、日本語パス名は使用できません。強制終了してしまいます。
4280 *3 VISTA環境の場合、パス指定が正常に動作しません。VISTA環境の方はお手数ですが保存先を指定しないでお使いください。
4283 この命令は[[Psychlops::Rectangle]]型の情報を[[Psychlops::Image]]型に移動する命令です。
4284 この命令と''Image::save()''命令を組み合わせることによってImage上以外で描画した画像が保存できます。
4285 |!Canvas::to()|to([[Psychlops::Image]] img, [[Psychlops::Rectangle]] rect) |>|![[Psychlops::Rectangle]]型の情報を[[Psychlops::Image]]型に移動する|
4286 |~|~|[[Psychlops::Image]] img |移動先のImageを指定|
4287 |~|~|[[Psychlops::Rectangle]] rect |移動元のRectangleを指定|
4290 Canvas::to()命令は[[Psychlops::Rectangle]]型の引数で指定された大きさを取得して移動先のImageのサイズに指定します。
4291 このときに移動先のImageのサイズがあらかじめセットされていると、プログラムが予期しない動作をすることがあります。
4292 変数の宣言だけが行われていて、まだセットをされていないImage型を使用するか、
4293 Image::release()命令を使って一度Imageの中身をクリアしてからCanvas::to()命令を使うようにして下さい。
4295 |!Image::release()|release(void) |メモリ内のイメージの内容を解放する|
4298 [[Canvas::pix()|2.2.1 画面上に点を描画する-Pointクラス1-]]命令でCanvas上に描画したランダムドットをImageに移動し保存します。
4299 ''Canvas::to()''命令は[[Psychlops::Rectangle]] 型が移動元になるので
4300 描画した点と同じ場所に空の[[Psychlops::Rectangle]] 型を設定する必要があります。
4302 <例1>では座標(400,500)に100 × 100のランダムドットを描画し保存します。
4303 centerX,centerYの値を変更することで描画する座標位置が変わります。
4304 0,0を代入もしくは削除すると左上に描画されます。
4307 #include <psychlops.h>
4308 using namespace Psychlops;
4310 double width=100,height=100;
4312 double centerX=700,centerY=500;
4313 Psychlops::Rectangle rect1(width,height);
4314 Psychlops::Color col;
4315 Psychlops::Image Noise1;
4317 void psychlops_main() {
4319 Canvas sampleA(Canvas::fullscreen);
4320 rect1.shift(centerX,centerY);
4321 for(int x=0; x<width; x++){
4322 for(int y=0; y<height; y++){
4323 temp1=Psychlops::random();
4325 sampleA.pix(x+centerX,y+centerY,col);
4328 sampleA.to(Noise1,rect1);
4330 while(!Input::get(Keyboard::spc));
4331 Noise1.save("test.png");
4337 #include <psychlops.h>
4338 using namespace Psychlops;
4340 double width=100,height=100;
4342 double centerX=700,centerY=500;
4343 Psychlops::Rectangle rect1(width,height);
4344 Psychlops::Color col;
4345 Psychlops::Image Noise1;
4347 void psychlops_main() {
4349 Canvas sampleA(Canvas::fullscreen);
4350 rect1.shift(centerX,centerY);
4351 for(int x=0; x<width; x++){
4352 for(int y=0; y<height; y++){
4353 temp1=Psychlops::random();
4355 sampleA.pix(x+centerX,y+centerY,col);
4358 sampleA.to(Noise1,rect1);
4360 while(!Input::get(Keyboard::spc));
4361 Noise1.save("%APP%test.png");
4364 指定のパスに保存されたか確認してください。</pre>
4366 <div title="3.3.2 画像ファイルを読み込み表示する" modifier="YourName" modified="200802131204" created="200709111918" changecount="13">
4367 <pre>今までの節では自分でオフスクリーン上に図形を描画しました。
4368 この節では既存の画像ファイルを読み込みます。
4371 既存の任意の画像ファイルを読み込みます。
4373 |!Image::load()|load(filename)|指定した画像ファイルを読み込みます|>|>|
4374 |~|~|filename: 読み込む画像ファイルの名前を指定|>|>|
4375 |~|~|保存先を指定しない場合のパス|Mac:Macintosh HD/ユーザ/ユーザ名(任意)/書類/Psychlops/ ^^*1^^|>|
4376 |~|~|~|Win:実行ファイルと同じパス|>|
4377 |~|~|読込みをする場合のパス指定|%APP%|Mac:実行バンドル(.app)と同じパス|
4378 |~|~|~|~|Win:実行ファイルと同じパス|
4379 |~|~|~|%RESOURCES%|Mac:実行バンドルの内部のResourcesフォルダ|
4380 |~|~|~|~|Win:実行ファイルのパスの直下のrscという名前のフォルダ|
4381 |~|~|~|%~USER_HOME%|Mac:"~/"と同じ(/Users/(user)/)|
4382 |~|~|~|~|Win:ユーザープロファイルフォルダ(C:\Documents and Settings\(user)\)|
4383 |~|~|~|%~USER_SETTING%|Mac:"~/Library/Psychlops/"|
4384 |~|~|~|~|Win:"%~AppData%Psychlops"|
4385 |~|~|~|%~USER_DOCUMENTS%|Mac:"~/Documents/Psychlops"|
4386 |~|~|~|~|Win:ユーザープロファイルフォルダ\My Documents\Psychlops|
4387 |~|~|~|%~USER_DOCUMENTS_ROOT%|Mac:"~/Documents"|
4388 |~|~|~|~|Win:ユーザープロファイルフォルダMy Documents\|
4389 *1 Finderでの表示は上記のとおりですが、Psychlopsで指定する場合は下記のUnix形式でパスを指定してください
4390 ** "~/Documents/Psychlops/"
4391 *2 MacOSX版では、日本語パス名は使用できません。強制終了してしまいます。
4392 *3 VISTA環境の場合、パス指定が正常に動作しません。VISTA環境の方はお手数ですが保存先を指定しないでお使いください。
4396 [[3.3.1節|3.3.1 画像ファイルを保存する]]の<例1>で保存したランダムドットを読み込みます。
4397 表示は[[Image::draw()]]で行います。
4400 #include <psychlops.h>
4401 using namespace Psychlops;
4403 Psychlops::Image Noise1;
4405 void psychlops_main() {
4407 Canvas sampleA(Canvas::fullscreen);
4408 Noise1.load("test.png");
4412 while(!Input::get(Keyboard::spc));
4415 次にパス指定を行い、画像ファイルを読み込みます。
4418 #include <psychlops.h>
4419 using namespace Psychlops;
4421 Psychlops::Image Noise1;
4423 void psychlops_main() {
4425 Canvas sampleA(Canvas::fullscreen);
4426 Noise1.load("%APP%test.png");
4430 while(!Input::get(Keyboard::spc));
4434 <div title="3.4 Canvasのオフスクリーンへの取り込み" modifier="YourName" modified="200802191336" created="200710240512" changecount="3">
4435 <pre>スクリーンキャプチャのように、画面上に描画したイメージをオフスクリーンに取り込むことができます。
4436 |!Canvas::to()|to(Image target, Rectangle source)|VRAM上にある画面イメージをメインメモリ(主記憶装置)にあるImageに転送します|
4437 |~|~|Image target: 転送先のImageを指定します|
4438 |~|~|Rectangle source: 転送する画面上の範囲を指定します|
4439 この際、転送先のImageがsetされていなければ自動的にsetします。setされていた場合、破棄されて上書きされます。
4442 #include <psychlops.h>
4443 using namespace Psychlops;
4445 void psychlops_main() {
4447 Canvas d(Canvas::fullscreen);
4449 Psychlops::Rectangle rect(100,100);
4452 // check rect external bordar and Rectangle.resize validity
4453 rect.resize(102,102).draw(Color::red);
4455 // check rect internal bordar and Rectangle.resize validity
4456 rect.resize(100,100).draw(Color::green);
4459 rect.resize(98,98).draw(Color::gray);
4462 rect.resize(100,100);
4464 // check Canvas.to(Image, Rectangle) validity
4467 img.centering().shift(0,-200).draw();
4468 d.msg("Canvas.to(Image)", d.getHcenter(), d.getVcenter()-200);
4470 // check Canvas.copy(Rectangle, Rectangle) validity
4471 d.copy(rect, rect.dup().shift(200,0));
4472 d.msg("Canvas.copy", d.getHcenter()+200, d.getVcenter());
4476 while(!Input::get(Keyboard::esc)) {
4483 <div title="3.5 文字列のレンダリングの仕様と関連情報" modifier="Psychlops_DevelopperG" modified="200910260739" created="200910260730" changecount="1">
4484 <pre>[[2.3節|2.3 文字列の描画]]ではLettersクラスを使った2バイト文字列の描画について最低限の事だけを説明しました。実はLettersクラスはImageクラスを応用して作成されています。
4485 ここでは、やや応用的ですが、本節で説明したImageクラスの仕様を踏まえてより詳細なLettersクラスの仕様と関連情報について説明します。
4487 * [[ワイド文字列の入出力]]</pre>
4489 <div title="4. 複雑な描画を行う2(複数オブジェクトのグループ化と回転・拡大縮小)" modifier="Psychlops_DevelopperG" modified="200912010726" created="200909300419" changecount="4">
4490 <pre>この章ではここまで用いてきたいくつかの描画のためのクラスをまとめて一つのグループとして取り扱う方法を紹介します。
4491 たとえば、いくつかのRectangleやImageをまとめて動かしたい時に、それぞれに対してshiftメソッドを使うというやり方は少し面倒です。
4492 こんな時に、これらを一つのグループにまとめてしまって、そのグループ全体に対して移動の操作をすることができれば、プログラムはかなり見通しがよくなるでしょう。
4493 また、描画したものをある点を中心に回転させたいというような時に、回転のための数学操作を考えるのはかなり面倒です。
4494 PsychlopsはOpenGLを用いて描画を行っているのですが、OpenGLは描画内容の回転や拡大・縮小をグラフィックチップに計算させることが簡単にできます。Psychlopsではグループ化したオブジェクトに対してこれらの変換を行うことが可能です。
4496 [[4.1 複数の描画オブジェクトをグループ化するーGroupクラスー]]
4497 [[4.2 描画内容の回転および拡大縮小]]
4502 <div title="4.1 複数の描画オブジェクトをグループ化するーGroupクラスー" modifier="Psychlops_DevelopperG" modified="200912010828" created="200912010750" changecount="27">
4506 2. GroupクラスにShapeを追加する
4507 3. Groupクラスに対して操作を行う
4508 4.(必要に応じて)ShapeをGroupから外す
4511 グループクラスの変数宣言は特に引数などは必要ではありません。
4517 ! 2. GroupクラスにShapeを追加する
4518 ここまで取り扱ってきたRectangleやImageといったクラスはすべてShapeという大きなクラスを元に構成されています(クラスの継承)。Shapeクラスに属するクラスは以下の通りです。
4522 |Ellipse|円を含む楕円形の領域|
4523 |Letters|文字列を格納するための領域(テキストボックス)|
4525 Shapeクラスの詳細については[[]]
4526 これらのクラスはここまででも扱ってきたようなcentering(), shift(), draw()などの共通する命令セットを持っています。これらの命令が実際にどのように振る舞うかはクラスの内容に合わせて異なっていますが、引数などの命令の形はどれも共通になっています。これらの共通の命令セットの一つとしてadd()命令があります。
4528 |!Shape::add()|add(Group gp)|>|Shapeをグループクラスに追加します。|
4529 |~|~|Group gp|追加先のグループを指定|
4536 またGroupに対してappend()命令を使用しても同じことができます。
4538 |!Group::append()|append(Shape shp)|>|Shapeをグループクラスに追加します。|
4539 |~|~|Shape shp|追加するShapeを指定|
4546 <div title="5. 入出力を行う" modifier="Psychlops_DevelopperG" modified="200908190158" created="200708211945" changecount="3">
4547 <pre>[[5.1 外部装置(キーボード/マウス)の操作内容を取得する]]
4548 [[5.2 ファイルの入出力]]</pre>
4550 <div title="5.1 外部装置(キーボード/マウス)の操作内容を取得する" modifier="Psychlops_DevelopperG" modified="200908190201" created="200708232250" changecount="2">
4552 コンピュータにあるキーボードやマウスから反応を取得するためのクラスとしてInputクラスがあります。
4553 現バージョンでPsychlopsはキーボードとマウスの反応を取得することが可能です。Psychlopsでは、キーボードのそれぞれのキーやマウスのボタン、カーソル位置をそれぞれKeyboard, Mouseクラスのメンバとして取り扱っています。具体的な使い方を以下で説明します。
4555 !Input::get() -反応を取得する-
4556 これまでのソースの中でwhile(!Input::get(Keyboard::spc))という命令をスペースキー待ちの命令として使ってきました。これは、"スペースキーが押されるまでループを繰り返しなさい"という命令です。この文でも使っているようにInput::get()命令は引数として指定された入力機器の反応があれば1(TRUE)を返します。入力が無ければは0(FALSE)を返します。Input::get()命令の第2引数は省略可能です。この場合には、Input::get()命令は~KeyStateにはpushedが指定されたとみなします。
4557 |!Input::get()|Input::get([[Keyboard::KeyCode|キーコード表]] code, [[Keyboard::KeyState]] state)|キーボードの反応を取得します|
4558 |~|~|code 反応を取得するキーの名前 [[キーコード表]] 参照|
4559 |~|~|state 反応状態のオプション (省略時はpushed) [[Keyboard::KeyState]]参照|
4560 |~|Input::get([[Mouse::ButtonCode|キーコード表]] code, [[Mouse::ButtonState]] state)|マウスの反応を取得します|
4561 |~|~|code 反応を取得するマウスボタンの名前 [[キーコード表]] 参照|
4562 |~|~|state 反応状態のオプション (省略時はpushed) [[Mouse::ButtonState]]参照|
4564 !! ~KeyState(~MouseState)の使い方
4565 第2引数のの"~KeyState"・"~MouseState"の使い方は少し難しいかもしれません。以下でこれらの"~KeyState"の使い方を見てみましょう。
4566 ~KeyStateには"pushed", "pressed", "released"の3つのモードが存在します。これら3つのモードの意味を実例とともに見ていきましょう。
4568 はじめに"pushed"を使った例を見てみましょう。ここでは、Input::get()命令の第2引数として"pushed"を明示的に指示しましたが、省略しても、挙動は変わりません。
4569 [[例1: pushedを使ったソース]]
4570 このソースを実行すると中央に表示された四角形がカーソルの左右キーで左右に動きます。キーを押し続けても一度しかシフトが起こらないことに注目してください。"pushed"を指定したときにInput::get()命令がTRUEを返すのは、キーが押し込まれたフレームより後に実行されたうちのはじめの一回だけです。キーを離すと元の状態に戻って、またキーが押し込まれたフレーム以降に一度だけTRUEが返されます。
4572 次に、"pressed"を指定した例です。例1とソースはほとんど変わりませんが、Input::get()命令の第2引数だけが"pressed"に変わっています。
4573 [[例2: pressedを使ったソース]]
4574 このソースを実行すると、やはりカーソルキーにしたがって四角形が左右に動きますが、例1と違って押している間、常に動き続けています。"pressed"を指定すると、キーが押し込まれている間ずっとInput::get()命令はTRUEを返します。
4576 次に"released"を使った例について見てみましょう
4577 [[例3: releasedを使ったソース]]
4578 この例では、例2に"released"を指定したInput::get()命令の文が付け加わっています。実行してみると、四角形はやはりキーを押している間、常に動き続けますが、キーを離すと中央に戻ってしまいます。キーが離されたことが"released"を指定したInput::get()命令によって検出されると、四角形がcentering()命令を使って中央に戻るようにコードが組まれています。このように"released"は指定したキーが離されたことを検出するためのオプションです。
4579 ここで、例の中にInput::refresh()という命令が使われています。これは、キーボードやマウスのボタン検出を初期化する命令です。複雑な入出力動作をするプログラムを書くときにはこの命令でキーボード・マウスの初期化をしておくことをおすすめします。
4580 |!Input::refresh()|refresh(void)|キーボード・マウスの入出力ルーチンを初期化します|
4581 参考[[Tips: Psychlopsにおけるキーボードマウスの入出力検出ルーチン]]
4584 最後に、これらをまとめて比較するためのデモプログラム例を載せておきます。a/sでpushedのデモ, z/xでpressed, q/wでpressed & released, cで四角形の位置初期化です。
4585 [[例4: pushed,pressed,releasedを使ったソース]]
4587 以上では、キーボードの入力取得オプションである"~KeyState"を例として扱いましたが、"~MouseState"の使い方もまったく同じです。以下に"~MouseState"を使った簡単なプログラム例を挙げます。上の例2とほとんど変わりません。右クリックで右方向、左クリックで左方向へ移動します。
4588 [[例5: Mouse::pressedを使ったソース]]
4590 ! カーソル座標の取得 とマウスカーソルの表示切替
4591 マウスの座標はMouseクラスのメンバMouse::x(水平座標), Mouse::y(垂直座標)に記録されています。このメンバに対して代入を行うとマウスの位置も移動します。
4592 |!Mouseのメンバ|Mouse::x|マウスの水平座標(代入可能)|
4593 |~|Mouse::y|マウスの水平座標(垂直座標)|
4594 マウスカーソルの表示切替はMouse::show(), Mouse::hide()命令を使用してください。
4595 |!Mouse::show()|show()|マウスカーソルを表示する|
4596 |!Mouse::hide()|hide()|マウスカーソルを非表示にする|
4597 次の例では、マウスカーソルを最初に画面中央に移動した後に、ユーザーのマウスの動きに合わせてマウスカーソルを動かします。左クリックすると、マウスカーソルの下にあるオブジェクトを選択して、ドラッグできます。また、右クリックでマウスカーソルを描画しないようにします。
4601 <div title="5.2 ファイルの入出力" modifier="Psychlops_DevelopperG" modified="200908190200" created="200709170703" changecount="2">
4602 <pre>この節では簡易的なファイルの入出力命令について説明します。
4603 PsychlopsはC, C++の標準命令と混在が可能なので、そちらの方法になれている方は、標準の入出力関数を用いてデータの保存、読み込みを行っていただいても問題ありません。
4604 しかし、単にプログラム中で用いた配列に格納されたデータを保存するだけの時に標準の入出力関数を使うのが面倒な時があります。
4605 Psychlopsではファイルの入出力を簡易的に行う命令Data::savearray(), Data::loadarray()が準備されています。ただし、これらの命令を使用してデータを保存するには以下の条件が満たされている必要があります。
4610 Data::loadarray()も同様にサイズが同じ20個以下の配列を読み込むことができます。
4613 [[5.2.2 ファイルからの入力]]</pre>
4615 <div title="5.2.1 ファイルへの出力" modifier="Psychlops_DevelopperG" modified="200908190159" created="200709170712" changecount="1">
4617 はじめに配列を保存するための命令Data::savearray()から説明を行います。
4619 |!Data::savearray()|savearray(filename, header, length, ...)|指定した配列を保存します|>|>|
4620 |~|~|filename: 保存する配列のファイル名称(拡張子まで)を指定|>|>|
4621 |~|~|保存先を指定しない場合のパス|Mac:Macintosh HD/ユーザ/ユーザ名(任意)/書類/Psychlops/ ^^*1^^|>|
4622 |~|~|~|Win:実行ファイルと同じパス|>|
4623 |~|~|保存をする場合のパス指定|%APP%|Mac:実行バンドル(.app)と同じパス|
4624 |~|~|~|~|Win:実行ファイルと同じパス|
4625 |~|~|~|%RESOURCES%|Mac:実行バンドルの内部のResourcesフォルダ|
4626 |~|~|~|~|Win:実行ファイルのパスの直下のrscという名前のフォルダ|
4627 |~|~|~|%~USER_HOME%|Mac:"~/"と同じ(/Users/(user)/)|
4628 |~|~|~|~|Win:ユーザープロファイルフォルダ(C:\Documents and Settings\(user)\)|
4629 |~|~|~|%~USER_SETTING%|Mac:"~/Library/Psychlops/"|
4630 |~|~|~|~|Win:"%~AppData%Psychlops"|
4631 |~|~|~|%~USER_DOCUMENTS%|Mac:"~/Documents/Psychlops"|
4632 |~|~|~|~|Win:ユーザープロファイルフォルダ\My Documents\Psychlops|
4633 |~|~|~|%~USER_DOCUMENTS_ROOT%|Mac:"~/Documents"|
4634 |~|~|~|~|Win:ユーザープロファイルフォルダMy Documents\|
4635 |~|~|ファイル名に%TIME_という文字列があると、日時分秒に自動変換されます|>|
4636 |~|~|header: 配列の冒頭に加える説明などを記述します|>|>|
4637 |~|~|length: 保存する配列の大きさを指定します|>|>|
4638 |~|~|...: 保存する配列を指定します。最大20個まで指定できます|>|>|
4639 *1 Finderでの表示は上記のとおりですが、Psychlopsで指定する場合は下記のUnix形式でパスを指定してください
4640 ** "~/Documents/Psychlops/"
4641 *2 MacOSX版では、日本語パス名は使用できません。強制終了してしまいます。
4642 *3 VISTA環境の場合、パス指定が正常に動作しません。VISTA環境の方はお手数ですが保存先を指定しないでお使いください。
4645 以下でこの命令の使い方の例を見てみましょう。
4648 #include <psychlops.h>
4649 using namespace Psychlops;
4651 const int MaxTrials = 100;
4652 int condition[MaxTrials], result[MaxTrials]
4654 void psychlops_main() {
4656 for(int CurrentTrial=0; CurrentTrial<MaxTrials; CurrentTrial++) {
4658 condition[CurrentTrial] = Psychlops::random(2);
4662 if(Input::get(Keyboard::left))
4663 result[CurrentTrial] = 0;
4664 if(Input::get(Keyboard::right))
4665 result[CurrentTrial] = 1;
4672 上のプログラム例では、変数配列resultには、被験者のキー押しによって0か1かの数値が入ります。この配列を配列conditionに保存されている実験条件とともにsavearray()命令を用いて、”sample_保存時間.txt"という名前のファイルに保存するには以下のように命令を書きます。
4674 Data::savearray("sample_%TIME_.txt", "conditions\tresults", MaxTrials, condition, result);
4677 第1引数はファイル名の指定、第2引数はファイルのヘッダ(1行目の内容)です。conditionおよび、resultの配列の個数はMaxTrials(100)個なので、第3引数はMaxTrials, それ以下に実際の配列の名前であるcondition, resultが続きます。
4683 #include <psychlops.h>
4684 using namespace Psychlops;
4686 const int MaxTrials = 100;
4687 int condition[MaxTrials], result[MaxTrials]
4689 void psychlops_main() {
4691 for(int CurrentTrial=0; CurrentTrial<MaxTrials; CurrentTrial++) {
4693 condition[CurrentTrial] = Psychlops::random(2);
4697 if(Input::get(Keyboard::left))
4698 result[CurrentTrial] = 0;
4699 if(Input::get(Keyboard::right))
4700 result[CurrentTrial] = 1;
4704 Data::savearray("sample_%TIME_.txt", "conditions\tresults", MaxTrials, condition, result);
4709 標準の出力パスに「sample_(日時).txt」というファイルが出力されたでしょうか?
4711 ファイルは、配列を縦に並べたタブ区切り形式となります。上記の例を適用すると
4713 | Condition[0] | Result[0] |
4714 | Condition[1] | Result[1] |
4715 | Condition[2] | Result[2] |
4717 の順番で記録されています。このファイルを実際に開くと、以下の例のようになっています。
4729 この結果ファイルはそのままExcelなどに読み込むことが出来ます。
4732 <div title="5.2.2 ファイルからの入力" modifier="Psychlops_DevelopperG" modified="200908190200" created="200709170714" changecount="1">
4733 <pre>Data::savearray()とは逆に、ファイルからデータを読み込むことも出来ます。
4734 データは配列に読み込まれるため、savearrayで書き込んだものをそのまま読み込むことが出来ます。
4738 テキストファイルから配列に読み込みます。
4740 |!Data::savearray()|savearray(filename, skipped_lines, length, ...)|指定した配列を読み込みます|>|>|
4741 |~|~|filename: 読み込むファイル名称(拡張子まで)を指定|>|>|
4742 |~|~|保存先を指定しない場合のパス|Mac:Macintosh HD/ユーザ/ユーザ名(任意)/書類/Psychlops/ ^^*1^^|>|
4743 |~|~|~|Win:実行ファイルと同じパス|>|
4744 |~|~|読み込む場合のパス指定|%APP%|Mac:実行バンドル(.app)と同じパス|
4745 |~|~|~|~|Win:実行ファイルと同じパス|
4746 |~|~|~|%RESOURCES%|Mac:実行バンドルの内部のResourcesフォルダ|
4747 |~|~|~|~|Win:実行ファイルのパスの直下のrscという名前のフォルダ|
4748 |~|~|~|%~USER_HOME%|Mac:"~/"と同じ(/Users/(user)/)|
4749 |~|~|~|~|Win:ユーザープロファイルフォルダ(C:\Documents and Settings\(user)\)|
4750 |~|~|~|%~USER_SETTING%|Mac:"~/Library/Psychlops/"|
4751 |~|~|~|~|Win:"%~AppData%Psychlops"|
4752 |~|~|~|%~USER_DOCUMENTS%|Mac:"~/Documents/Psychlops"|
4753 |~|~|~|~|Win:ユーザープロファイルフォルダ\My Documents\Psychlops|
4754 |~|~|~|%~USER_DOCUMENTS_ROOT%|Mac:"~/Documents"|
4755 |~|~|~|~|Win:ユーザープロファイルフォルダMy Documents\|
4756 |~|~|skipped_lines: 冒頭から読み込みを飛ばす行数を指定します|>|>|
4757 |~|~|length: 読み込む配列の大きさを指定します|>|>|
4758 |~|~|...: 読み込む配列を指定します。最大20個まで指定できます|>|>|
4759 *1 Finderでの表示は上記のとおりですが、Psychlopsで指定する場合は下記のUnix形式でパスを指定してください
4760 ** "~/Documents/Psychlops/"
4761 *2 MacOSX版では、日本語パス名は使用できません。強制終了してしまいます。
4762 *3 VISTA環境の場合、パス指定が正常に動作しません。VISTA環境の方はお手数ですが保存先を指定しないでお使いください。
4766 savearrayで保存したファイルを読み込んでみましょう。savearrayでは通常冒頭2行にファイルの説明を加えるため、skipped_linesには2を指定します。ファイル名中で%TIME_を指定すると現在時刻に変換されてしまうため、読み込むファイルはあらかじめ別の名前にしておきます。ここではsample.txtとしました。
4769 #include <psychlops.h>
4770 using namespace Psychlops;
4772 const int MaxTrials = 100;
4773 int condition[MaxTrials], result[MaxTrials]
4775 void psychlops_main() {
4777 Data::loadarray("sample.txt", 2, MaxTrials, condition, result)
4782 これでsample.txtの中身が配列に読み込まれました</pre>
4784 <div title="6. 時間制御と各種ツールを使用する" modifier="Psychlops_DevelopperG" modified="200908190210" created="200709132309" changecount="6">
4785 <pre>[[6.1 時間を計測する]]
4787 [[6.2.1 描画の時間精度を確認する-FPSチェッカー]]
4788 [[6.2.2 画面に反映されない描画の進行度を表示する-プログレスバー]]
4794 [[6.3.5 行列の中身をオフスクリーンに描画する]]
4795 [[6.3.6 メッシュグリッドの作成]]</pre>
4797 <div title="6.1 時間を計測する" modifier="Psychlops_DevelopperG" modified="200908190204" created="200708242225" changecount="1">
4799 Psychlopsで時間を計測する方法は2種類あります。
4800 画面のリフレッシュを基準として、リフレッシュされたフレーム数をカウントすることです。この方法は比較的簡単ですが、1フレームの時間以上の精度で時間を制御することができません。
4801 そこで、より高い精度で時間を計測するためのクラスとしてPsychlopsにはClockクラスが用意されています。このClockクラスはCPUタイマーを利用することによって高い精度の時間間隔の計測を実現しています。
4804 Clockの宣言は特に難しくありません。[[Psychlops::Clock]]型のインスタンスを以下のような形式で単に宣言するだけです。
4806 Psychlops::clock Timer;
4808 このTimerに現在のCPUタイマーの値を入力するにはClock::update()命令を使用します。
4809 |!Clock::update()| Clock::update(void)| 現在のCPUタイマーの値を入力する|
4810 このままでは、保持されている時刻はCPUタイマーのカウントなので、実際にプログラム内でこのクラスを使うときにはClock::at_msec()命令を使用してタイマーのカウントをmsec単位に変換して使用します。
4811 |!Clock::at_msec()| double Clock::at_msec(void)| カウントをmsec単位に変換(double)して返す|
4813 下にClockクラスを用いた簡単な時間計測例を挙げて見ました。スペースキーを押している時間が画面に表示されます。
4816 #include <psychlops.h>
4817 using namespace Psychlops;
4819 void psychlops_main() {
4821 Canvas sampleA(Canvas::fullscreen);
4822 double elapsed=0.0, dcx, dcy;
4825 dcx=sampleA.getHcenter();
4826 dcy=sampleA.getVcenter();
4827 sampleA.clear(Color(0.5));
4830 while(!Input::get(Keyboard::esc)) {
4832 if(Input::get(Keyboard::spc, Keyboard::released)){
4834 elapsed=(End-Start).at_msec();
4835 sampleA.clear(Color(0.5));
4837 sampleA.clear(Color(0.5));
4838 sampleA.msg("Elapsed Time(msec)", dcx-100,dcy-30, Color::white);
4839 sampleA.var((int)elapsed,dcx ,dcy, Color::white);
4843 if(Input::get(Keyboard::spc, Keyboard::pushed)){
4850 ^^ごく稀にWindows環境下でこのプログラムを実行すると、キー押し時間がマイナスになることがあるようです。このような状態が起こったら、並行して動いているプログラムを終了させて、再起動をしてからこのプログラムを実行してみてください。参考[[Tips: 時間精度が必要なプログラムを実行するとき]]^^</pre>
4852 <div title="6.2 各種ツールを使ってみる" modifier="Psychlops_DevelopperG" modified="200908190206" created="200709120042" changecount="3">
4853 <pre>Psychlopsにはここまで扱ったような簡単な描画命令だけではなく、描画内容をチェックするためのツールが実装されています。ここでは、これらのツールについて説明します。
4854 [[6.2.1 描画の時間精度を確認する-FPSチェッカー]]
4855 [[6.2.2 画面に反映されない描画の進行度を表示する-プログレスバー]]</pre>
4857 <div title="6.2.1 描画の時間精度を確認する-FPSチェッカー" modifier="Psychlops_DevelopperG" modified="200908190205" created="200709120043" changecount="1">
4859 Psychlopsには、現在描画している画面がプログラムで指定した時間通りに実行されているかどうかをチェックするツールがあります。
4860 たとえば、以下のようなコードを考えて見ます。
4862 void psychlops_main() {
4863 Canvas sampleA(Canvas::fullscreen);
4869 このプログラムを書いたときの意図は、描画を毎フレームごとに更新するというものでしょう。(でなければ、なんらかの「待ち」処理がsampleA.flip()の前後に入っているはずです。)
4870 しかし、非常に複雑な描画内容をCanvasの裏画面に書いたり、大きなImageを転送するときは、描画に1フレーム分以上の時間かかることがあります。この場合、psychlopsは描画が完了するまで待ってから[[Canvas::flip()]]命令を実行するので実際に描画が更新されるのは2フレーム以上たってからということになります。
4871 問題は、このような"こま落ち"はただ見ているだけではわからないことが多いことです(複雑な刺激呈示の途中に16.7 msと33.3msの区別がつくでしょうか?)。
4872 psychlopsにはこのような"こま落ち"を検出するためのFPSチェッカと呼ばれるツールが準備されています。
4873 このFPSチェッカを使うには、まず時間計測を行いたい部分の前にFPSチェッカを起動する命令を書きます。
4874 これには、Canvas::watchFPS()命令を使用します。
4875 |!Canvas::watchFPS()|void watchFPS(void)|FPSチェッカをonにします|
4876 このままでは、FPSチェッカが起動しているだけで、その内容を見ることはできません。
4877 FPSチェッカの内容を表示するためには、[[Canvas::flip()]]命令の直前にCanvas::showFPS()命令を書いてFPSチェッカのウィンドウを表示させます。
4878 |!Canvas::showFPS()|void showFPS(void)|FPSチェッカのレポートを画面左上に表示します|
4879 Canvas::showFPS()命令を実行すると、半透明のレポートウィンドウが画面右上に表示されます。このレポートウィンドウは、すべての描画内容の上に重ねて表示されます(以下を参照してください)
4883 以下のコードは5フレームに一度描画内容を更新して、コマ落ちが発生しているかどうかを表示します。
4885 #include <psychlops.h>
4886 using namespace Psychlops;
4888 void psychlops_main() {
4889 Canvas sampleA(Canvas::fullscreen);
4891 while(!Input::get(Keyboard::spc)){
4899 このコードを60Hzのディスプレイで実行すると、以下のような画面が表示されます。
4901 [img[image/FPSchecker.png]]
4903 showFPS()命令を実行した後に、Canvas::clear()命令を実行しているにもかかわらずレポートウィンドウが表示されていることに注意してください。このように、実際にはレポートウィンドウは全ての描画が終了した後に描画されます。
4904 このレポートウィンドウを拡大したものが下図です。
4906 [img[image/FPSwindow.png]]
4908 レポートウィンドウは3行から成り立っています。一番上の赤い数字は、これまでにコマ落ちしたフレーム数、真ん中の白い数字は表示された総フレーム数、一番下の青い数字は前回のflip()から今回のflip()までにかかった時間(msec単位)です。
4909 この例では、5フレームに一度描画が更新されているので、60Hzだと画面更新間の時間は1/12 sec = 83.3 msecで、一番下の青い数字と一致していることに注目してください。</pre>
4911 <div title="6.2.2 画面に反映されない描画の進行度を表示する-プログレスバー" modifier="Psychlops_DevelopperG" modified="200908190206" created="200709252142" changecount="1">
4913 大量の計算をコンピュータに行わせているとき、画面には何の変化も起きずにプログラムが暴走しているのか、単に計算に時間がかかっているだけなのかの区別がつかないときが良くあります(本当は、暴走をさせないようなプログラム構造にするべきですが・・・)。進行度を文字で画面に表示する方法もありますが、文字表示のために[[Canvas::flip()]]命令を挟んでしまうと計算速度が落ちてしまいます。
4914 このような状況で、画面に計算の総量に対して今どのくらいの割合が終了したかを表示するためのツールにプログレスバーがあります。このプログレスバーは強制的にフロントバッファ(現在表示中の画面)に表示されるため、[[Canvas::flip()]]命令は必要ありません。
4916 このプログレスバーを表示するにはCanvas::progressbar()命令を使用します。
4917 |!Canvas::progressbar()|void progressbar(X now, X max)|現在のnowの値のmaxに対する比率をプログレスバーとして表示します。now, maxは数値型であればどの型でもかまいません|>|
4918 |~|~|X now|現在の進行度を示す変数値(型は数値型)|
4919 |~|~|X max|nowの最終的な到達値(型は数値型)|
4920 |~|void progressbar(double ratio)|ratioで示される比率をプログレスバーとして表示します。|>|
4921 |~|~|double ratio|比率の値(0.0~1.0)|
4922 この命令を使用すると、画面左上に灰色の四角のなかに時間とともに伸びる青い四角形が表示されます。この青い四角形が灰色の四角いっぱいまで伸びると、nowの値がmaxまで到達した(ratioが1.0になる)ことを示しています。
4927 下の例では100 msecと50 msecの"待ち"を100回繰り返します。
4928 時間経過とともに左上に提示されるプログレスバーが伸びていく様子に注目してください。
4931 #include <psychlops.h>
4932 using namespace Psychlops;
4934 void psychlops_main() {
4936 Canvas sampleA(Canvas::fullscreen);
4937 Clock timer1, timer2;
4943 sampleA.message("calculating...", sampleA.getHcenter(), sampleA.getVcenter());
4947 for(int i=0; i<100; i++){
4949 sampleA.progressbar(i,100);
4950 while((timer2-timer1).at_msec()<100){timer2.update();}
4951 if(Input::get(Keyboard::esc))break;
4955 sampleA.message("10 sec elapsed", sampleA.getHcenter(), sampleA.getVcenter());
4957 while(!Input::get(Keyboard::spc));
4959 sampleA.message("calculating...", sampleA.getHcenter(), sampleA.getVcenter());
4964 for(int i=0; i<100; i++){
4966 sampleA.progressbar((double)i/100.0);
4967 while((timer2-timer1).at_msec()<50){timer2.update();}
4968 if(Input::get(Keyboard::esc))break;
4972 sampleA.message("5 sec elapsed", sampleA.getHcenter(), sampleA.getVcenter());
4974 while(!Input::get(Keyboard::spc));
4978 <div title="6.3 行列演算を行う" modifier="Psychlops_DevelopperG" modified="200908190211" created="200709252143" changecount="3">
4981 Matrixクラスは行列を取り扱います。線形代数としての取り扱いもできますが、Matlabの行列のように数値を2次元的に配置した表や画像(Image)のようなものとしても利用できます。Imageは精度が低いため、Imageに保存した色データをもとに再計算するのはお勧めできませんが、Matrixではそのような計算をしても十分な精度があります。
4984 MatrixとImageは基本的に2次元のよく似たデータ構造を持っており、相互に変換が可能です。(実際にはImageは下で述べるようなより複雑なデータ構造を持っています。)ただし、両者にはいくつかの相違点があります。
4985 [Img[Image/Image_Matrix.png]]
4987 * Matrixの要素アクセスは行(縦)列(横)の順で指定し、起点は1です。ImageはX(横)Y(縦)の順で指定し、起点は0です。
4988 [Img[Image/ImagetoMatrix.png]]
4991 * Imageは一つのx,y値に対してRGB 3値、RGBA 4値を取り扱うことが出来ます(数学的にはテンソルの構造を持っています)。Matrixは行列の要素として一つの値しか取り扱えないため、3値または4値のImageに変換する場合、それぞれの座標におけるR,G,B,(A)の値を示す同じ次元を持った3つまたは4つの行列を用意する必要があります。
4992 [Img[Image/ColImagetoMatrix.png]]
4995 * Matrixは精度を重視して64bit浮動小数点(double型)を取り扱います。Imageは速度を重視して8bit整数(unsigned char型)を基本として取り扱います(32bit浮動小数点 float型にすることもできます)
5001 [[6.3.5 行列の中身をオフスクリーンに描画する]]
5002 [[6.3.6 メッシュグリッドの作成]]</pre>
5004 <div title="6.3.1 行列の宣言" modifier="Psychlops_DevelopperG" modified="200908190207" created="200710171011" changecount="1">
5007 Matrixクラスは行列を取り扱います。線形代数としての取り扱いもできますが、Matlabの行列のように数値を2次元的に配置した表や画像(Image)のようなものとしても利用できます。Imageは精度が低いため、Imageに保存した色データをもとに再計算するのはお勧めできませんが、Matrixではそのような計算をしても十分な精度があります。
5010 MatrixとImageはよく似ており、相互に変換が可能です。
5012 * Matrixは精度を重視して64bit浮動小数点(double型)を取り扱います。Imageは速度を重視して8bit整数(unsigned char型)を基本として取り扱います(32bit浮動小数点 float型にすることもできます)
5013 * ImageはRGB 3値、RGBA 4値を取り扱うことが出来ます。Matrixは1値しか取り扱えないため、3値または4値のImageに変換する場合、3つまたは4つの行列を用意する必要があります。
5014 * Matrixの要素アクセスは行(縦)列(横)の順で指定し、起点は1です。ImageはX(横)Y(縦)の順で指定し、起点は0です。
5017 行列を確保するには、行数と列数を指定します。
5018 Matrix()のように宣言のみの場合は''Matrix::set()''命令で指定します。
5020 |!Matrixの宣言|Matrix()|行列の使用を宣言します|
5021 |~|Matrix(rows, cols)|指定されたrows行cols列の行列を宣言します|
5022 |~|~|long rows :行数を指定|
5023 |~|~|long cols :列数を指定|
5027 行列の宣言時に何の指定もしなかった場合、Matrix::set()命令を使用します。
5028 |!Matrix::set()|Matrix(rows, cols)|指定されたrows行cols列の行列を設定します|
5029 |~|~|long rows :行数を指定|
5030 |~|~|long cols :列数を指定|
5036 #include <fstream>
5037 #include <psychlops.h>
5038 using namespace Psychlops;
5040 Matrix LuminanceMap1(200, 100);
5042 void psychlops_main() {
5043 std::ofstream result("dump.txt");
5045 result << LuminanceMap1;
5048 次はMatrixの宣言と[[Matrix::set()]]命令を組み合わせたコードです。
5051 #include <fstream>
5052 #include <psychlops.h>
5053 using namespace Psychlops;
5055 Matrix LuminanceMap1;
5056 long Width=10, Height=10;
5058 void psychlops_main() {
5059 std::ofstream result("dump.txt");
5061 LuminanceMap1.set(Height, Width);
5062 result << LuminanceMap1;
5066 これらのコードを実行すると、プログラムと同じ場所に作成されるファイルdump.txtにMatrixの中身が保存されます。</pre>
5068 <div title="6.3.2 行列の要素の操作" modifier="Psychlops_DevelopperG" modified="200908190204" created="200710171131" changecount="2">
5069 <pre>6.3.1節ではMatrixの宣言(領域確保)のみを行っていたので、Matrixの要素に0が代入されるだけでした。
5070 この節では実際にMatrix中に数値を代入していきます。
5073 Matrix全体をひとつの値で埋め尽くすには、Matrixに対して数値の代入を行います。
5076 #include <fstream>
5077 #include <psychlops.h>
5078 using namespace Psychlops;
5080 void psychlops_main() {
5081 std::ofstream result("dump.txt");
5083 Matrix LuminanceMap1(5, 5);
5084 LuminanceMap1 = 7.0;
5085 result << LuminanceMap1;
5091 !Matrix(row, col) 単独要素
5092 Matrix中の要素にアクセスするための命令です。値を取り出すことも代入することも出来ます。二次元の配列に似た扱い方になりますが、要素の指定が行(縦)列(横)の順番であること、起点が0ではなく1であることに注意してください。
5094 |!Matrix()|Matrix(row, col) |要素を取り出します。ちょうど配列のように、値を取り出すことも代入することもできます。|
5095 |~|~|int row: 要素の行を指定します。|
5096 |~|~|int col: 要素の列を指定します。|
5097 行列の範囲外の値が指定された場合はエラーになります。エラーメッセージを表示して強制終了します。
5099 !!Matrixの要素へのアクセスの仕方
5100 2行2列目の要素に数値を代入し、さらにその値を取り出します。
5103 #include <fstream>
5104 #include <psychlops.h>
5105 using namespace Psychlops;
5107 const int Width = 10, Height = 10;
5108 Matrix LuminanceMap1(Height, Width);
5110 void psychlops_main() {
5111 std::ofstream result("dump.txt");
5113 LuminanceMap1(2,2) = 1.0;
5114 std::cout << LuminanceMap1;
5116 double a = LuminanceMap1(2,2);
5121 このプログラムを実行すると、行列の中身が10行10列で出力されます。次の行に、行列の(2,2)の値である1.0が出力されます。
5123 !!Matrixの全要素へのアクセスの仕方
5124 全要素にアクセスするには、forループを使って順次アクセスしていきます。
5127 #include <fstream>
5128 #include <psychlops.h>
5129 using namespace Psychlops;
5131 const int Width = 10, Height = 10;
5132 Matrix LuminanceMap1(Height, Width);
5134 void psychlops_main() {
5135 std::ofstream result("dump.txt");
5137 for(int row; row<Width; row++) {
5138 for(int col; col<Width; col++) {
5139 LuminanceMap1(row, col) = row+col;
5142 result << LuminanceMap1;
5147 !Matrix(row, col) 部分行列
5148 要素アクセスで整数を指定すると単一の要素にアクセスできました。行列のある範囲をまとめて取り扱いたい場合は、Range型を使って範囲を指定し、部分行列にアクセスします。
5151 [img[部分行列|image/MatrixPartial.png]]
5152 部分行列は、行列の一部分を切り取ってサイズの異なる行列であるかのように振る舞わせます。部分行列は元の行列の窓のように振る舞うため、部分行列に代入すると元の行列の一部分に代入するのと同じように作用します。
5154 |!Matrix()|Matrix(Range row, Range col)|行列の一部分から部分行列を作ります。|
5155 |~|~|Range row: 要素の行を指定します。|
5156 |~|~|Range col: 要素の列を指定します。|
5159 10×10のMatrixの一部に数値を代入します。部分行列のさらに一部の要素にもアクセスできます。
5162 #include <fstream>
5163 #include <psychlops.h>
5164 using namespace Psychlops;
5166 const int Width = 10, Height = 10;
5167 Matrix LuminanceMap1(Width, Height);
5169 void psychlops_main() {
5170 std::ofstream result("dump.txt");
5173 LuminanceMap1(2<=row<=5, 3<=col<=6) = 1.0;
5174 LuminanceMap1(2<=row<=5, 3<=col<=6)(2,2) = 2.0;
5175 result << LuminanceMap1;
5182 部分行列は元の行列の窓として機能するので、部分行列への代入をすると元の行列にも代入されます。元の行列を壊したくない場合は、明示的にコピーをとっておく必要があります。
5185 #include <fstream>
5186 #include <psychlops.h>
5187 using namespace Psychlops;
5189 const int Width = 10, Height = 10;
5190 Matrix LuminanceMap1(Width, Height), Copy;
5192 void psychlops_main() {
5193 std::ofstream result("dump.txt");
5196 LuminanceMap1(2<=row<=5, 3<=col<=6) = 1.0;
5197 Copy = LuminanceMap1(2<=row<=5, 3<=col<=6);
5199 result << LuminanceMap1 << Copy;
5204 <div title="6.3.3 行列全体の操作" modifier="Psychlops_DevelopperG" modified="200908190207" created="200710171157" changecount="1">
5206 ここでは、行列全体に対して回転、列のスライドなどの操作を行う命令について解説します。
5207 Psychlopsが行列全体に対して行える操作は大まかに以下の表に示した5つです。ここではこれらの命令について解説します。
5215 上記命令はすべて自己破壊的に作用します。
5216 つまり、命令を実行した段階で、元の行列が命令が適用された行列に置き換わってしまいます。
5217 これには、行列の次元の変換も含まれるので、上記命令を式の中に書いた場合には注意が必要です。
5218 いかに注意した方がよい命令例を挙げてみます。
5220 Matrix a(5, 3), b(3, 5);
5221 b = a.transpose() + a;
5225 一行目では、 左側のa.transpose()でaが自己破壊的に転置されてしまうため、右側のaも転置されたものとして扱われてしまいます。つまり、このような命令を実行すると、
5230 2行目では、左辺のbは3行5列、右辺のbは5行3列で不正な代入にはならず、右辺bが自己破壊的に転置されて代入されます。
5234 |!Matrix::slide|slide(row, col)|要素を行方向、列方向にずらします。あふれた分は、反対側に移動します。|
5235 |~|~|int row 行方向にずらす量を指定します。|
5236 |~|~|int col 列方向にずらす量を指定します。|
5261 #include <fstream>
5262 #include <psychlops.h>
5263 using namespace Psychlops;
5265 Psychlops::Matrix LuminanceMap1;
5266 long Width=10, Height=10;
5268 void psychlops_main() {
5269 std::ofstream result("dump.txt");
5272 LuminanceMap1.set(Height, Width);
5273 for(int row; row<Width; row++) {
5274 for(int col; col<Width; col++) {
5275 LuminanceMap1(row, col) = row*Height + col;
5278 result << LuminanceMap1.slide(1,0);
5285 |!Matrix::transpose|transpose()|行列を転置(行と列の読み替え)します。自分自身を書き換えるので注意してください。|
5299 #include <fstream>
5300 #include <psychlops.h>
5301 using namespace Psychlops;
5303 Psychlops::Matrix LuminanceMap1;
5304 long Width=10, Height=10;
5306 void psychlops_main() {
5307 std::ofstream result("dump.txt");
5310 LuminanceMap1.set(Height, Width);
5311 for(int row; row<Width; row++) {
5312 for(int col; col<Width; col++) {
5313 LuminanceMap1(row, col) = row*Height + col;
5316 result << LuminanceMap1.transpose();
5322 |!Matrix::rot90|rot90(count)|行列を90度単位で反時計回りに回転します。|
5323 |~|~|int count 回転角度を90度単位で指定します。-3,1,5のとき90度、-2,2,6のとき180度、-1,3,7のとき270度回転します。|
5337 #include <fstream>
5338 #include <psychlops.h>
5339 using namespace Psychlops;
5341 Psychlops::Matrix LuminanceMap1;
5342 long Width=16, Height=1;
5344 void psychlops_main() {
5345 std::ofstream result("dump.txt");
5348 LuminanceMap1.set(Height, Width);
5349 for(int col; col<Width; col++) {
5350 LuminanceMap1(1, col) = col;
5352 result << LuminanceMap1.rot90(1);
5359 |!Matrix::catRow|catRow(other)|この行列の次の行から別の行列を継ぎ足します。|
5360 |~|~|Matrix other: 付け足す行列を指定します。付け足してもこれ自身は消されません。|
5363 #include <fstream>
5364 #include <psychlops.h>
5365 using namespace Psychlops;
5367 Psychlops::Matrix LuminanceMap1, LuminanceMap2;
5368 long Width=5, Height=1;
5370 void psychlops_main() {
5371 std::ofstream result("dump.txt");
5374 LuminanceMap1.set(Height, Width);
5376 LuminanceMap2.set(Height, Width);
5379 LuminanceMap1.catRows(LuminanceMap2);
5380 result << LuminanceMap1;
5387 |!Matrix::reshape|reshape(row, col)|要素を変えずに、行と列の数のみを変更します。要素数(行*列)は変更前と変更後で同じでなければなりません。|
5388 |~|~|int row 新しい行数を指定します。|
5389 |~|~|int col 新しい列数を指定します。|
5403 #include <fstream>
5404 #include <psychlops.h>
5405 using namespace Psychlops;
5407 Psychlops::Matrix LuminanceMap1;
5408 long Width=16, Height=1;
5410 void psychlops_main() {
5411 std::ofstream result("dump.txt");
5414 LuminanceMap1.set(Height, Width);
5415 for(int col; col<Width; col++) {
5416 LuminanceMap1(1, col) = col;
5418 result << LuminanceMap1.reshape(4, 4);
5423 |!Matrix::min|min()|全要素中の最小値を得ます。|
5424 |!Matrix::max|max()|全要素中の最大値を得ます。|
5426 // 行列の要素に乱数を代入し、その中の最大値と最小値を抽出します。
5427 #include <fstream>
5428 #include <psychlops.h>
5429 using namespace Psychlops;
5431 Psychlops::Matrix LuminanceMap1;
5432 long Width=10, Height=10;
5434 void psychlops_main() {
5435 std::ofstream result("dump.txt");
5438 LuminanceMap1.set(Height, Width);
5439 for(int row; row<Width; row++) {
5440 for(int col; col<Width; col++) {
5441 LuminanceMap1(row, col) = Psychlops::random();
5444 result << LuminanceMap1;
5445 result << LuminanceMap1.max() << " " << LuminanceMap1.min();
5450 |!Matrix::getRows|getRows()|行数を得ます。|
5451 |!Matrix::getCols|getCols()|列数を得ます。|
5453 #include <fstream>
5454 #include <psychlops.h>
5455 using namespace Psychlops;
5457 Psychlops::Matrix LuminanceMap1;
5458 long Width=5, Height=3;
5460 void psychlops_main() {
5461 std::ofstream result("dump.txt");
5464 LuminanceMap1.set(Height, Width);
5466 result << LuminanceMap1.getRows() << " " << LuminanceMap1.getCols();
5470 <div title="6.3.4 行列の加減乗除" modifier="Psychlops_DevelopperG" modified="200908190207" created="200710171242" changecount="1">
5473 実数と行列の四則演算は、行列の各要素と実数の演算として定義されています。例えば、matrix + 1; というコードは、行列のすべての要素に1を足すことを意味しています。演算子の優先順位はC++の仕様に従います。
5476 ( matrix * 10 ) + 1; // 上の式と同じ
5479 matrix + ( 1 * 10 ); // 上の式と同じ
5482 *現在の仕様では、実数は必ず演算子の後ろに書いてください。演算子の前に実数があるとコンパイルエラーになります。
5488 |!Matrix 演算子 実数|>|
5489 |+|行列の要素すべてに実数を加算します|
5490 |-|行列の要素すべてに実数を減算します|
5491 |*|行列の要素すべてに実数を乗算します|
5492 |/|行列の要素すべてに実数を除算します|
5496 #include <fstream>
5497 #include <psychlops.h>
5498 using namespace Psychlops;
5500 void psychlops_main() {
5501 std::ofstream result("dump.txt");
5503 Psychlops::Matrix a(5, 5), b(5, 5);
5517 行列同士の演算は一般的な線形代数の定義に従います。行列の要素同士の乗算(Matlabでのドット演算子)は *~ 演算子でサポートしています。行列同士の除算は定義されていません。
5519 演算の実行前には、行列どうしの行数・列数が適切かどうかチェックし、不適切であればC++の例外機構によってプログラムが強制終了します。コンパイル時にはチェックされません。
5521 実数との演算と行列どうしの演算は同じ式中に混在させることができます。演算子の優先順位はC++の仕様に従います。
5523 |!Matrix 演算子 線形代数演算|>|
5524 |+|行列の加算をします(可換)。両辺の行数・列数は等しくなければなりません。|
5525 |-|行列の減算をします(不可換)。両辺の行数・列数は等しくなければなりません。|
5526 |*|行列の乗算をします(不可換)。左辺項の行数と右辺項の列数は等しくなければなりません。|
5527 |*~|行列の要素どうしを乗算します。Matlabでの.演算子に相当します(可換)。両辺の行数・列数は等しくなければなりません。|
5529 不可換な演算子は、右と左のMatrixを入れ替えると演算結果が変わります。
5540 #include <fstream>
5541 #include <psychlops.h>
5542 using namespace Psychlops;
5544 void psychlops_main() {
5545 std::ofstream result("dump.txt");
5547 Psychlops::Matrix a(5, 5), b(5, 5), c(5, 5);
5561 Psychlopsの行列は、四則演算において最低限の最適化のために、= 演算子で代入を行ったときにすべての計算を行うよう定義されています。このため、=演算子による代入が行われないと機能しないことがあります。
5574 また、再帰的代入(代入先の左辺の変数が右辺にも出てくること)を行う場合は、当該行列が右辺に2回以上出現すると問題が生じる可能性があります。なるべく再帰的代入は行わないでください。
5577 a = a + a * a; // 右辺にaが2回以上出てくるのは危険
5578 b = a + a * a; // 問題なし
5582 <div title="6.3.5 行列の中身をオフスクリーンに描画する" modifier="Psychlops_DevelopperG" modified="200908190208" created="200710171234" changecount="1">
5583 <pre>MatrixとImageは両方とも2次元に並べられた値(輝度や色)で、相互に変換が可能です。ImageをMatrixの計算機能を使って生成・加工するときに役立ちます。また、Matrixに変換すると各色チャネルが独立して制御しやすいため、各色チャネルを別々に操作する場合などにも役立ちます。
5584 ただし、[[この節の最初で述べたように|6.3 行列演算を行う]]、Matrixクラスの書式とImageクラスの書式がずれていることは注意して下さい。
5586 Matrix / Imageの変換命令はImage型の一部になっています。
5591 |!Image::from|from( luminance )|Matrixを輝度としてImageに読み込み、グレースケール画像を作成します。|
5592 |~|~|Matrix luminance: |
5593 |~|from( r, g, b )|Matrixを赤、緑、青チャネルとして読み込み、RGB画像を作成します。|
5597 |~|from( r, g, b, a) |Matrixを赤、緑、青、透明度チャネルとして読み込み、RGBA画像を作成します。|
5602 Imageの中身がすでに確保されていた場合は、破棄されます。
5607 |!Image::to|to( luminance )|グレースケール画像の輝度値をMatrixに書き込みます。|
5608 |~|~|Matrix luminance: |
5609 |~|to( r, g, b ) |RGB画像のMatrixを赤、緑、青チャネル値をMatrixに書き込みます。|
5613 |~|to( r, g, b, a ) |RGBA画像の赤、緑、青、透明度チャネルをMatrixに書き込みます。|
5618 書き込み先のMatrixは空である必要があります。大きさ等がImageと等しくなるように自動的に調整されます。
5622 以下のプログラムは、"sample.png"という名前のPNG画像を読み込み、赤チャネルと緑チャネルの差分を計算して"diff_sample.png"という名前で保存します。
5624 <注>:場合によってはプログラムが正常終了せず、CPUを使い切ってしまうことがあります。そのときは、タスクマネージャ等で強制終了させてください。
5627 #include <psychlops.h>
5628 using namespace Psychlops;
5630 void psychlops_main() {
5636 img.load("sample.png");
5647 img.from(dR, dG, B);
5649 img.save("diff_sample.png");
5654 <div title="6.3.6 メッシュグリッドの作成" modifier="Psychlops_DevelopperG" modified="200908190210" created="200712061907" changecount="1">
5656 メッシュグリッドとは、行列の各要素への代入を、ループを使わず普通の数式に見える形式で書くテクニックです。Matlabでよく使用されています。
5658 5 x 5 の大きさの行列に対する操作を行う場合、まず以下のような行列(メッシュグリッド)を作成します。
5675 この行列を座標値とみなし、各要素について演算する関数を適用します。
5677 Patch = cos(X) * sin(Y);
5679 これは、以下のプログラムと同等の計算を行います。
5682 for(int Y = 0; Y<5; Y++) {
5683 for(int X = 0; X<5; X++) {
5684 Patch(X, Y) = cos(X) * sin(Y);
5691 |!Matrix::mesh|mesh(Range row, int col)|行を変動値とするmeshgridを生成します|
5692 |~|~|row: 行(Imageではy座標に相当)の範囲を指定します。行数は範囲から自動決定されます。|
5693 |~|~|col: 列(Imageではx座標に相当)の列数を指定します。|
5694 |~|mesh(int row, Range col)|列を変動値とするmeshgridを生成します|
5695 |~|~|row: 行(Imageではy座標に相当)の行数を指定します。|
5696 |~|~|col: 列(Imageではx座標に相当)の範囲を指定します。行数は範囲から自動決定されます。|
5701 y = Matrix::mesh(-2<=row<=2, 5);
5702 x = Matrix::mesh(5, -2<=col<=2);
5719 行の範囲、列の範囲を同時に指定する方法もあります。引数で指定した行列にメッシュグリッドの内容が入ります。
5720 |!Matrix::mesh|mesh(Range row, Matrix r, Range col, Matrix c)|行、列を変動値とするmeshgridを生成します|
5721 |~|~|row: 行(Imageではy座標に相当)の範囲を指定します。行数は範囲から自動決定されます。|
5722 |~|~|r: 行用のメッシュグリッドを指定します。|
5723 |~|~|col: 列(Imageではx座標に相当)の範囲を指定します。列数は範囲から自動決定されます。|
5724 |~|~|c: 列用のメッシュグリッドを指定します。|
5729 Matrix::mesh(-2<=row<=2, y,-2<=col<=2,x);
5747 ここに記述されている関数はすべて自己破壊せず、新たな行列を生成して返します。
5748 |!Matrix::sin|sin(Matrix m)|mの各要素に対してsin関数の返り値を代入した行列を返します。|
5749 |!Matrix::cos|cos(Matrix m)|mの各要素にcos関数の返り値を代入した行列を返します。|
5750 |!Matrix::tan|tan(Matrix m)|mの各要素にtan関数の返り値を代入した行列を返します。|
5751 |!Matrix::exp|exp(Matrix m)|mの各要素にexp関数の返り値を代入した行列を返します。|
5752 |!Matrix::sqrt|sqrt(Matrix m)|mの各要素にsqrt関数の返り値を代入した行列を返します。|
5753 |!Matrix::pow|pow(Matrix m, double ex)|mの各要素を引数としてex乗した値を代入した行列を返します。|
5757 #include <psychlops.h>
5758 using namespace Psychlops;
5760 void psychlops_main()
5764 Canvas display(Canvas::fullscreen);
5768 y = Matrix::mesh(-100<=row<=100, 201);
5769 x = Matrix::mesh(201, -100<=col<=100);
5771 m = pow(sin(x/wave_length * 2*PI) + cos(y/wave_length * 2*PI), 4.0);
5776 while(!Input::get(Keyboard::esc)) {
5786 |!Matrix::min|min(Matrix a, Matrix b)|aとbの各要素を比較し、小さいほうを要素とする行列を返します。両Matrixの大きさは同じでなければなりません。|
5787 |!Matrix::max|max(Matrix a, Matrix b)|aとbの各要素を比較し、大きいほうを要素とする行列を返します。両Matrixの大きさは同じでなければなりません。|
5807 <div title="CSSEdit_Code" modifier="Psychlops_Admin" modified="200708280258" created="200708280257" tags="systemConfig" changecount="1">
5808 <pre>config.cssEdit ={};
5809 config.cssEdit.settings = {
5810 tags:"", //記事のタグ
5811 user:"CSSEditBackup", //作成者名
5812 backupname:"CSSEditBackup" //バックアップタイトル名
5815 config.macros.cssEdit = {};
5816 config.macros.cssEdit.handler = function(place,macroName,params){
5817 if(readOnly){return;}
5819 var s = '<form mime="text/plain" name="CSSEditForm">'
5820 + '<p><textarea name="i" rows="20" style="width:90%;"></textarea></p>'
5822 + '<input type="button" name="go" value="GO" onclick="config.macros.cssEdit.go();">&nbsp;'
5823 + '<input type="button" name="ReadStyleSheet" value="Read StyleSheet" onclick="config.macros.cssEdit.readStyleSheet();">&nbsp;'
5824 + '<input type="button" name="Backup" value="Backup" onclick="config.macros.cssEdit.Backup();">&nbsp;'
5825 + '<input type="button" name="Restore" value="Restore" onclick="config.macros.cssEdit.restore();">&nbsp;'
5826 + '<input type="reset" value="reset">&nbsp;'
5829 + '<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>';
5832 var e = createTiddlyElement(place,"div",null,null,"");
5837 config.macros.cssEdit.go=function(){
5838 //setStylesheet(document.CSSEditForm.i.value,"CSSEdit");
5839 var s =document.CSSEditForm.i.value;
5840 var title = "CSSEdit_temp";
5841 config.macros.cssEdit.save(title,s,"temp","");
5842 var html = store.getRecursiveTiddlerText(title,null,10);
5843 store.deleteTiddler(title);
5845 setStylesheet(html,"CSSEdit");
5848 config.macros.cssEdit.readStyleSheet=function(){
5849 if (! window.confirm("Read StyleSheet OK ?"))
5851 document.CSSEditForm.i.value = store.getTiddlerText("StyleSheet");
5854 config.macros.cssEdit.Backup=function(){
5855 if (! window.confirm("Backup OK?"))
5858 var p = config.cssEdit.settings;
5859 var s = document.CSSEditForm.i.value;
5860 var title = p.backupname;
5861 config.macros.cssEdit.save(title,s,p.user,p.tags)
5865 config.macros.cssEdit.restore=function(){
5866 if (! window.confirm("Restore OK ?"))
5869 var p = config.cssEdit.settings;
5870 document.CSSEditForm.i.value = store.getTiddlerText(p.backupname);
5874 config.macros.cssEdit.save = function(title,s,user,tags){
5875 var p = config.HTMLEdit.settings;
5876 var now = new Date();
5877 var tiddler = store.fetchTiddler(title);
5880 created = tiddler.created;
5881 store.deleteTiddler(title);
5883 tiddler = new Tiddler();
5886 tiddler.set(title,s,user,now,tags,created);
5887 store.addTiddler(tiddler);
5890 <div title="Canvas::clear()" modifier="Psychlops_DevelopperG" modified="200910080819" created="200712131650" changecount="4">
5891 <pre>|!Canvas::clear()|clear([[Psychlops::Color]] col,TargetSurface)|>|指定したCanvasを指定色で塗りつぶします|
5892 |~|~|[[Psychlops::Color]] col |塗りつぶす色を指定|
5893 |~|~|TargetSurface|塗りつぶす画面を指定|
5894 |~|clear(TargetSurface)|指定したCanvasを塗りつぶします。色の指定には[[Canvas::setClearColor()]]命令を使用します|
5895 |~|~|TargetSurface|塗りつぶす画面を指定|
5897 [[2.1.2 Canvasの基本構造と操作]]</pre>
5899 <div title="Canvas::copy()" modifier="Psychlops_DevelopperG" modified="200910080847" created="200709170159" changecount="24">
5900 <pre>[[Psychlops::Rectangle]]型(source)で指定される範囲内の情報を[[Psychlops::Rectangle]]型(target)の範囲に複写または移動します。移動元と移動先の矩形の大きさは同じにする必要があります。
5901 |!Canvas::copy()|copy([[Psychlops::Rectangle]] source, [[Psychlops::Rectangle]] target, bool copymode, [[Psychlops::Color]] col)|>|! [[Psychlops::Rectangle]]型の情報を[[Psychlops::Image]]型に複写または移動する|>|>|
5902 |~|~|[[Psychlops::Rectangle]] source|移動(複写)元のRectangleを指定|>|
5903 |~|~|[[Psychlops::Rectangle]] target|移動(複写)先のRectangleを指定|>|
5904 |~|~|bool copymode| trueを指定する場合は移動処理(カット), falseを指定する場合は複写処理(コピー)|指定しない場合、false(複写)が指定される|
5905 |~|~|[[Psychlops::Color]] col|移動処理時の移動元の塗りつぶし色を指定|指定しない場合、黒色が指定される|
5906 |~|~|~|~|複写の場合、指定不要|
5908 !!Canvas::copy()の書き方
5909 rect1の内部にある描画内容ををrect2の内部に複写します。
5912 #include <psychlops.h>
5913 using namespace Psychlops;
5915 Psychlops::Rectangle rect1(100,100);
5916 Psychlops::Rectangle rect2(100,100);
5918 void psychlops_main() {
5920 Canvas sampleA(Canvas::fullscreen);
5922 while(!Input::get(Keyboard::spc)){
5923 sampleA.rect(rect1,Color::yellow);
5924 sampleA.copy(rect1,rect2);
5930 <div title="Canvas::flip()" modifier="Psychlops_DevelopperG" modified="200910080820" created="200709130046" changecount="6">
5931 <pre>表と裏の2つの画面の入れ替え処理です。
5932 描画内容はCanvas::flip() 処理で画面を入れ替えることで初めて画面上に表示されます。
5933 この命令は画面の垂直同期信号(=ビデオカードが1枚の画面を書ききったことを示す信号; Vsync)に同期して、画面の切り替えを行います。
5934 * 参考 [[Tips: Canvas::flip()とコマ落ち]]
5936 |!Canvas::flip()|flip(int)|int入力値のリフレッシュ分描画内容を表示します|
5937 |~|~|引数を入力しない場合(flip())、flip(1)が指定されます|
5938 |~|flipAfter(int wait)|int wait回のリフレッシュ後まで待ってからflip()を実行して描画内容を画面に反映させます|
5939 |~|flipAndWait(int wait)|もっとも近い垂直同期のタイミングでflip()を実行した後に、int wait回ののリフレッシュ分の表示を予約します。次にflip()命令がコールされても、int wait回ののリフレッシュ分が経過するまでは何も起こりません(flip()と全く同じ効果ですが、引数は省略不可)|
5941 flip()系の命令は画面描画の時間制御をする上でもっとも重要な命令です。上の表だけではわかりにくいでしょうから、図と実例を用いて各命令の働きをもう少し詳しく見ていきましょう。
5943 flipAfter(), flipAndWait()命令の動きを図解すると以下のようになります。以下の図ではflipAfter(3)とflip(3)(あるいはflipAndWait(3))の場合を例としてあげました。
5944 [img[image/psychlops_flip.png]]
5946 コード例は[[2.1.3節|2.1.3 Canvasのメンバ取得と変数の表示]]参照</pre>
5948 <div title="Canvas::getXXXX()" modifier="Kazushi Maruya" modified="200712131657" created="200712131652" changecount="2">
5949 <pre>|!Canvas::getXXXX()|getCenter()|画面の中心座標(x,y)を取得します|
5950 |~|getHcenter()|横方向の中心座標(x)を取得します|
5951 |~|getVcenter()|縦方向の中心座標(y)を取得します|
5952 |~|getHeight()|画面の高さ(ピクセル)を取得します|
5953 |~|getWidth()|画面の幅(ピクセル)を取得します|
5954 |~|getColorDepth()|カラーモード(ビット)を取得します|
5955 |~|getRefreshRate()|リフレッシュレート(Hz)を取得します|
5957 [[2.1.3 Canvasのメンバ取得と変数の表示]]</pre>
5959 <div title="Canvas::lastFailedFrames()" modifier="Psychlops_DevelopperG" modified="200910080852" created="200709170302" changecount="6">
5960 <pre>[[Canvas::flip()]]命令で画面の切り替えをする時にコマ落ちする場合があります。
5962 * 参考 [[Tips: Canvas::flip()とコマ落ち]]
5963 |!Canvas::lastFailedFrames()|lastFailedFrames()|!1秒間で落ちたフレーム数を取得します|
5965 !!Canvas::lastFailedFrames()の書き方
5966 フレームが落ちたときにグレーで画面をクリアしています。
5967 (フレームが落ちるのが一瞬のため[[Canvas::var()]]命令で落ちた値を確認することは困難なため)
5968 何枚落ちているかは右上の赤い数字が落ちたフレーム数の和算となっているので確認してください。
5969 Display::~の書き方については [[Tips: CanvasクラスとDisplayクラス]]を参照してください。
5973 #include <psychlops.h>
5974 using namespace Psychlops;
5976 void ullmancylinder() {
5977 const int DOTCNT = 15000;
5980 double bg_lum = 0.0;
5981 double velocity = 1.0;
5985 double CylinderRadius=200;
5989 Independent << dotcnt | "Number of Dots" | 1< rng< DOTCNT | 10 | 1000 ;
5990 Independent << velocity | "Dot Velocity" | 0.0<=rng<=10.0 | 0.1 | 0.05;
5991 Independent << SOA | "SOA Frames" | 1.0<=rng<=60.0 | 1 | 1;
5992 Independent << bg_lum | "Luminance Inv." | 0.0<=rng<=1.0 | 1 | 1;
5993 Independent << SIZE | "Dot Size" | 0< rng<=10 | 1 | 1;
5994 Independent << CylinderRadius| "Cylinder Size" | 0< rng<= 400 | 1 | 50;
5997 Canvas canvas(Canvas::fullscreen);
5998 Psychlops::Rectangle rect(SIZE,SIZE);
5999 Psychlops::Color bgcolor(0), dotcolor(1.0-bg_lum);
6002 double x[DOTCNT], dx[DOTCNT], y[DOTCNT];
6004 for(int i=0; i<DOTCNT; i++) {
6005 x[i] = Psychlops::random(CylinderRadius*2*PI);
6006 y[i] = Psychlops::random(CylinderRadius*2)+200;
6008 Display::watchFPS();
6009 while(!Input::get(Keyboard::spc)) {
6010 bgcolor.set(bg_lum);
6011 dotcolor.set(1.0-bg_lum);
6012 canvas.clear(bgcolor);
6013 rect.resize(SIZE,SIZE);
6018 for(int i=0; i<DOTCNT; i++) dx[i] = (int)(CylinderRadius*sin(x[i]+SOA_pp*phase/100.0))+canvas.getHcenter();//各ドット位置の計算
6019 for(int i=0; i<dotcnt; i++) rect.centering(dx[i], y[i]).draw(dotcolor);
6021 a1=Display::lastFailedFrames();
6022 if(a1)Display::clear(Color(0.25,0.25,0.25));
6029 void psychlops_main()
6033 p.setDesign(Procedure::DEMO);
6034 p.setProcedure(ullmancylinder);
6042 <div title="Canvas::line()" modifier="Psychlops_DevelopperG" modified="200910080837" created="200712131651" changecount="4">
6043 <pre>|!Canvas::line()|line(double x1, double y1, double x2, double y2, [[Psychlops::Color]] col)|>|!開始座標(X1,Y1)から終端座標(X2,Y2)まで指定色の線を描画します|
6044 |~|~|double x1|描画する線の開始座標x1を指定|
6045 |~|~|double y1|描画する線の開始座標y1を指定|
6046 |~|~|double x2|描画する線の終端座標x2を指定|
6047 |~|~|double y2|描画する線の終端座標y2を指定|
6048 |~|~|[[Psychlops::Color]] col|描画する点の色を指定|
6049 |~|line([[Psychlops::Point]] point1, [[Psychlops::Point]] point2,[[Psychlops::Color]] col)|>|!開始Point座標(x1,x1)から終端Point座標(X2,Y2)まで指定色の線を描画します|
6050 |~|~|[[Psychlops::Point]] point1|開始座標(x1,y1)を指定|
6051 |~|~|[[Psychlops::Point]] point2|終端座標(x2,y2)を指定|
6052 |~|~|[[Psychlops::Color]] col|描画する点の色を指定|
6053 画面上に線を描画するためには、「どこから」「どこに」「何色」の線を引くのかの指定が必要です。
6054 「どこから」は開始座標(x1,y1)、「どこに」は終端座標(x2,y2)、「何色」は[[Psychlops::Color]]型で指定します。
6055 座標はCanvasの左上端が座標の基点(x=0,y=0)となっています。
6056 これらの座標はPoint型の変数を使用して指定することもできます。
6058 [img[image/canvasline.png]]
6060 [[2.2.2 画面上に線を描画する]]</pre>
6062 <div title="Canvas::msg()" modifier="Psychlops_DevelopperG" modified="200910080829" created="200709130031" changecount="8">
6063 <pre>画面上に任意の文字列を表示します。
6064 |!Canvas::msg()|msg(char* string,double x,double y,[[Psychlops::Color]] col)|>|!char* stringを座標(x,y)に指定色で表示します|
6065 |~|~|schar* string|画面表示をする文字列を指定|
6066 |~|~|double x|画面表示をする座標(x)を指定|
6067 |~|~|double y|画面表示をする座標(y)を指定|
6068 |~|~|[[Psychlops::Color]] col|描画する点の色を指定<br>(設定しない場合、白が設定される)|
6069 |~|msg(string str,double x,double y,[[Psychlops::Color]] col)|>|!string strを座標(x,y)に指定色で表示します|
6070 |~|~|string str|画面表示をする文字列を指定|
6071 |~|~|double x|画面表示をする座標(x)を指定|
6072 |~|~|double y|画面表示をする座標(y)を指定|
6073 |~|~|[[Psychlops::Color]] col|描画する点の色を指定<br>(設定しない場合、白が設定される)|
6075 [[2.1.3 Canvasのメンバ取得と変数の表示]]</pre>
6077 <div title="Canvas::oval()" modifier="Psychlops_DevelopperG" modified="200910080839" created="200712131653" changecount="2">
6078 <pre>|!Canvas::oval()|oval([[Psychlops::Rectangle]] rect, [[Psychlops::Color]] col)|>|![[Psychlops::Rectangle]] rectで指定された直径の円を指定色で塗りつぶし描画します|
6079 |~|~|[[Psychlops::Rectangle]] rect|描画する円の直径|
6080 |~|~|[[Psychlops::Color]] col|描画する点の円を指定|
6082 [img[image/canvasoval.png]]</pre>
6084 <div title="Canvas::pix()" modifier="Psychlops_DevelopperG" modified="200910080833" created="200712131650" changecount="6">
6085 <pre>|!Canvas::pix()|pix(double x,double y,[[Psychlops::Color]] col) |>|!座標(x,y)に指定色の点を描画します|
6086 |~|~|double x|描画する点の座標軸xを指定|
6087 |~|~|double y|描画する点の座標軸yを指定|
6088 |~|~|[[Psychlops::Color]] col|描画する点の色を指定|
6089 |~|pix([[Psychlops::Point]] point,[[Psychlops::Color]] col) |>|!Point座標(x,y)に指定色の点を描画します|
6090 |~|~|[[Psychlops::Point]] point |座標(x,y)を指定|
6091 |~|~|[[Psychlops::Color]] col|描画する点の色を指定|
6092 |~|pix(int dotsCnt,double* xArray,double* yArray),[[Psychlops::Color]]* colArray )|>|!配列の座標(x[],y[])に指定色(配列を用いて複数色指定)の点を描画します|
6093 |~|~|int dotsCnt|描画する点の数を指定(後ろの引数の配列の個数を超えてはいけない)|
6094 |~|~|double* xArray| 描画する点の座標軸xが格納された配列名(ポインタ)を指定|
6095 |~|~|double* yArray| 描画する点の座標軸yが格納された配列名(ポインタ)を指定|
6096 |~|~|[[Psychlops::Color]]* colArray|描画する点の色が格納された配列名(ポインタ)を指定|
6097 |~|pix(int dotsCnt,double* xArray,double* yArray,[[Psychlops::Color]] col)|>|!配列の座標(x[],y[])に指定色(一色のみ)の点を描画します|
6098 |~|~|int dotsCnt|描画する点の数(配列数)を指定|
6099 |~|~|double* xArray|描画する点の座標軸xが格納された配列名(ポインタ)を指定|
6100 |~|~|double* yArray|描画する点の座標軸yが格納された配列名(ポインタ)を指定|
6101 |~|~|[[Psychlops::Color]] col|描画する点の色を指定|
6102 |~|pix(int dotsCnt,[[Psychlops::Point]]* pointArray,[[Psychlops::Color]]* colArray)|>|!配列Point座標(x[],y[])に指定色の点を描画します|
6103 |~|~|int dotsCnt|描画する点の数(配列数)を指定|
6104 |~|~|[[Psychlops::Point]]* pointArray|配列の座標(x[],y[])が格納された配列名(ポインタ)を指定|
6105 |~|~|[[Psychlops::Color]]* colArray|描画する点の色が格納された配列名(ポインタ)を指定|
6107 [img[image/canvaspix.png]]
6109 [[2.2.1 画面上に点を描画する-Pointクラス1-]]</pre>
6111 <div title="Canvas::pix(double, double, double)という命令はコンパイルできないのですか?" modifier="Psychlops_DevelopperG" created="200908100457" changecount="1">
6112 <pre>Canvas::pix(double, double, double)という命令はコンパイルできないのですか?
6114 できません。最後の色の引数をColor(double)としてColor型にキャスト(変換)してください。
6116 (正) display.pix(100,100,Color(0.5));
6117 (誤) display.pix(100,100,0.5);
6120 <div title="Canvas::progressbar()" modifier="Psychlops_DevelopperG" modified="200910080852" created="200712131654" changecount="4">
6121 <pre>|!Canvas::progressbar()|void progressbar(X now, X max)|!現在のnowの値のmaxに対する比率をプログレスバーとして表示します。now, maxは数値型であればどの型でもかまいません|>|
6122 |~|~|X now|現在の進行度を示す変数値(型は数値型)|
6123 |~|~|X max|nowの最終的な到達値(型は数値型)|
6124 |~|void progressbar(double ratio)|!ratioで示される比率をプログレスバーとして表示します。|>|
6125 |~|~|double ratio|比率の値(0.0~1.0)|
6127 [[5.2.2 画面に反映されない描画の進行度を表示する-プログレスバー]]</pre>
6129 <div title="Canvas::rect()" modifier="Psychlops_DevelopperG" modified="200910080838" created="200709122308" changecount="13">
6132 |!Canvas::rect()|rect([[Psychlops::Rectangle]] rect, [[Psychlops::Color]] col)|>|!四角形を指定色で描画します|
6133 |~|~|([[Psychlops::Rectangle]] rect|描画する四角形を指定|
6134 |~|~|[[Psychlops::Color]] col|描画する四角形の色を指定|
6135 [[2.2.3 画面上に四角形を描画する-Rectangleクラス1-]]</pre>
6137 <div title="Canvas::setClearColor()" modifier="Psychlops_DevelopperG" modified="200910080852" created="200709170246" changecount="6">
6138 <pre>[[Canvas::clear()|2.1.2 Canvasの基本構造と操作]]命令使用時に塗りつぶす色の設定を行います。
6139 setClearColor(Color)命令使用時には必ず[[Canvas::clear()|2.1.2 Canvasの基本構造と操作]]命令を使用してください。
6140 |!Canvas::setClearColor()|setClearColor([[Psychlops::Color]] col)|>|!指定したCanvasを塗りつぶすための色を設定します|
6141 |~|~|[[Psychlops::Color]] col |塗りつぶす色を指定|
6143 !!Canvas::setClearColor()の書き方
6144 今ディスプレイに表示されている画面(紙の表)を白く塗りつぶします。
6147 #include <psychlops.h>
6148 using namespace Psychlops;
6150 void psychlops_main() {
6152 Canvas sampleA(Canvas::fullscreen);
6153 sampleA.setClearColor(Color::white);
6154 sampleA.clear(Canvas::FRONT);
6155 while(!Input::get(Keyboard::spc));
6159 <div title="Canvas::setGammaTable()" modifier="Psychlops_DevelopperG" modified="200910080850" created="200802130702" changecount="5">
6160 <pre>他の多くの描画ライブラリ同様、CRTの各画素における輝度レベルとPsychlopsで指定する輝度レベルは線形の関係を維持しません。これを簡易的に補正するために、R,G,Bの各色チャンネルについて変換表(LUTテーブル)を用いた補正を行う方法が広く用いられています。
6161 Psychlopsでは、この変換表を指定することで、[[ガンマ補正|Tips: ガンマ補正]]された色を表示することが可能です。このための命令として、setGammaTable()命令があります。
6163 通常は[[Color::setGammaTable()]]を用いますが、将来的にCanvasごとに別の補正値が必要なときのためにこの関数が予約されています。
6165 |!Canvas::setGammaTable()|setGammaTable(std::vector<double> Cr, std::vector<double> Cg, std::vector<double> Cb)|>|!Canvasが確保している表示装置のDACテーブルを直接書き換え、ガンマ補正を行います。|
6166 |~|~|std::vector<double> Cr|R(赤)チャンネルの変換表|
6167 |~|~|std::vector<double> Cg|G(緑)チャンネルの変換表|
6168 |~|~|std::vector<double> Cb|B(青)チャンネルの変換表|
6169 *2 256個以下でも自動的に線形補完されますが、正確な補完は256個すべて指定することで実現されます。
6170 *3 値は配列またはC++STLの動的配列(std::vector<double>)で指定し、8ビット環境なら256段階の理論値を配列の要素番号とし、それぞれの段階の補償値を配列要素に書き込みます。
6172 コード例は[[ガンマ補正|Tips: ガンマ補正]]参照</pre>
6174 <div title="Canvas::setGammaValue()" modifier="Psychlops_DevelopperG" modified="200910080849" created="200709170234" changecount="11">
6175 <pre>他の多くの描画ライブラリ同様、CRTの各画素における輝度レベルとPsychlopsで指定する輝度レベルは線形の関係を維持しません。これを簡易的に補正するために、R,G,Bの各色チャンネルについてGamma関数を用いた補正を行う方法が広く用いられています。
6176 Psychlopsでは、このGamma関数の係数を指定することで、[[ガンマ補正|Tips: ガンマ補正]]された色を表示することが可能です。このための命令として、setGammaValue()命令があります。
6178 通常は[[Color::setGammaValue()]]を用いますが、将来的にCanvasごとに別の補正値が必要なときのためにこの関数が予約されています。
6181 |!Canvas::setGammaValue()|setGammaValue(double Cr, double Cg, double Cb)|>|!Canvasが確保している表示装置のDACテーブルを直接書き換え、ガンマ補正を行います。|
6182 |~|~|double Cr|表示装置のR(赤)チャンネルのガンマ係数|
6183 |~|~|double Cg|表示装置のG(緑)チャンネルのガンマ係数|
6184 |~|~|double Cb|表示装置のB(青)チャンネルのガンマ係数|
6185 * 指定する値は表示装置のガンマ値そのものです。Psychlops側で補償指数を自動決定します。
6187 コード例は[[ガンマ補正|Tips: ガンマ補正]]参照</pre>
6189 <div title="Canvas::showFPS()" modifier="Psychlops_DevelopperG" modified="200910080852" created="200712131656" changecount="2">
6190 <pre>|!Canvas::showFPS()|void showFPS(void)|!FPSチェッカのレポートを画面左上に表示します|
6192 [[5.2.1 描画の時間精度を確認する-FPSチェッカー]]
6195 <div title="Canvas::to()" modifier="Psychlops_DevelopperG" modified="200910080843" created="200709130159" changecount="6">
6196 <pre>[[Psychlops::Rectangle]]型で指定される範囲内の情報を[[Psychlops::Image]]型に複写します。
6197 この命令と''Image::save()''命令を組み合わせることによってImage上以外で描画した画像が保存できます。
6198 |!Canvas::to()|to([[Psychlops::Image]] img, [[Psychlops::Rectangle]] rect)|>|![[Psychlops::Rectangle]]型の情報を[[Psychlops::Image]]型に複写する|
6199 |~|~|[[Psychlops::Image]] img |複写先のImage|
6200 |~|~|[[Psychlops::Rectangle]] rect |複写元のRectangle|
6202 コード例は[[3.3.1節|3.3.1 画像ファイルを保存する]]参照</pre>
6204 <div title="Canvas::var()" modifier="Psychlops_DevelopperG" modified="200910080830" created="200709130030" changecount="26">
6205 <pre>取得した変数の値を画面に表示します。
6206 |!Canvas::var()|var(string str, double x, double y,[[Psychlops::Color]] col, bool fillForward)|>|>|>|!string strで指定された変数の値を取得して座標(x,y)に指定色で表示します|
6207 |~|~|>|>|string str|画面表示をする変数を指定|
6208 |~|~|>|>|double x|画面表示をする座標(x)を指定|
6209 |~|~|>|>|double y|画面表示をする座標(y)を指定|
6210 |~|~|>|>|[[Psychlops::Color]] col|描画する点の色を指定(設定しない場合、白が設定される)|
6211 |~|~|~|bool fillForward|表示位置の指定|true |指定座標(x,y)から左部分に表示する|
6212 |~|~|~|~|~|false |指定座標(x,y)から右部分に表示する|
6213 |~|~|~|~|>|>|(指定しない場合、falseが設定される)|
6215 [[2.1.3 Canvasのメンバ取得と変数の表示]]</pre>
6217 <div title="Canvas::watchFPS()" modifier="Psychlops_DevelopperG" modified="200910080852" created="200712131655" changecount="2">
6218 <pre>|!Canvas::watchFPS()|void watchFPS(void)|!FPSチェッカをonにします|
6220 [[5.2.1 描画の時間精度を確認する-FPSチェッカー]]</pre>
6222 <div title="CanvasMode" modifier="Psychlops_DevelopperG" modified="200910080814" created="200708212026" changecount="7">
6224 |Canvas::fullscreen()|画面いっぱいに描画領域を確保する|
6225 |Canvas::window()|指定サイズの描画領域を確保する|</pre>
6227 <div title="Canvasの宣言" modifier="Psychlops_DevelopperG" modified="200910080808" created="200712131704" changecount="1">
6229 |~|Canvas(int width,int height,int colordepth,double refreshrate)|>|!全画面表示をします|
6230 |~|~|int width|水平方向の解像度(画面の横幅)を指定|
6231 |~|~|int height|垂直方向の解像度(画面の縦幅)を指定|
6232 |~|~|int colordepth|カラーモード(ビット)を指定|
6233 |~|~|double refreshrate|リフレッシュレート( Hz )を指定|
6234 |~|Canvas(Canvasmode mode, const Display disp)|>|!現在の画面モードで画面を確保する場合に使います|
6235 |~|~|Canvasmode mode|[[表示モード|CanvasMode]](フルスクリーンorウィンドウ)を選択|
6236 |~|~|const [[Display|Displayの指定値]] disp|[[表示するディスプレイ|Displayの指定値]]を選択(省略可能)|
6237 |~|~|>|マルチディスプレイ環境では、最後の引数でどのディスプレイに表示するか指定できます。省略時はプライマリディスプレイになります。詳細は [[マルチディスプレイの選択]]を参照ください。|
6238 |~|Canvas(int width, int height, int colordepth, double refreshrate, const Display disp)|>|!画面モードを指定してフルスクリーン画面を確保します|
6239 |~|~|int width|水平方向の解像度(画面の横幅)を指定|
6240 |~|~|int height|垂直方向の解像度(画面の縦幅)を指定|
6241 |~|~|int colordepth|カラーモード(ビット)を指定|
6242 |~|~|double refreshrate|リフレッシュレート( Hz )を指定|
6243 |~|~|const [[Display|Displayの指定値]] disp|[[表示するディスプレイ|Displayの指定値]]を選択(省略可能)|
6244 |~|~|>|指定した画面モードが選べなかった場合は、例外が送出されプログラムは強制終了します。|
6245 |~|Canvas(int width, int height, CanvasMode mode, const Display disp)|>|!指定の幅と高さでウィンドウを確保します。|
6246 |~|~|int width|水平方向の解像度(画面の横幅)を指定|
6247 |~|~|int height|垂直方向の解像度(画面の縦幅)を指定|
6248 |~|~|Canvasmode mode|[[表示モード|CanvasMode]](フルスクリーンorウィンドウ)を選択|
6249 |~|~|>|指定した画面モードが選べなかった場合は、例外が送出されプログラムは強制終了します。|
6250 |~|Canvas(Rectangle rect, CanvasMode mode , const Display disp)|>|!バーチャルスクリーン上の指定の矩形でウィンドウを確保します。|
6251 |~|~|Rectangle rect|ディスプレイの大きさ、ディスプレイの位置を指定|
6252 |~|~|Canvasmode mode|[[表示モード|CanvasMode]](フルスクリーンorウィンドウ)を選択|
6253 |~|~|const [[Display|Displayの指定値]] disp|[[表示するディスプレイ|Displayの指定値]]を選択(省略可能)|
6254 |~|~|>|指定した画面モードが選べなかった場合は、例外が送出されプログラムは強制終了します。|
6256 * ここではコンストラクタ(宣言)の書式のみ記述していますが、同じ引数セットのset関数がすべて用意されています。
6257 * CRT は機器によってリフレッシュレートが小数点以下の桁でわずかに異なります(60Hzと表示されていても、厳密には60.2Hzなど小数点以下の桁があります)。初期化時には小数点以下の桁は無視して確保しますが、正確なタイミングでflipするには、ディスプレイ装置の設定等で小数点以下の桁をあらかじめ調べて、正確な値を指定することをお勧めします。
6258 * 1.3以降でではウィンドウモードが実装されました。
6260 [[2.1.1 Canvasの宣言]]</pre>
6262 <div title="Canvas型に関して" modifier="Psychlops_DevelopperG" modified="200908100506" created="200712131716" changecount="9">
6263 <pre>* [[描画内容を画面に反映させたい|Canvas::flip()]]
6264 * [[描画内容を画面に反映させて、一定時間表示したい|Canvas::flip()]]
6265 * [[Canvas型の変数のメンバを取得したい|Canvas::getXXXX()]]
6267 * [[プログラム内の変数の値を表示したい|Canvas::var()]]
6268 * [[簡単なメッセージを表示したい|Canvas::msg()]]
6270 * [[画面領域全てを塗りつぶしたい|Canvas::clear()]]
6271 * [[点を打ちたい|Canvas::pix()]]
6272 * [[四角形を描画したい|Canvas::rect()]]
6273 * [[円・楕円を描画したい|Canvas::oval()]]
6275 * [[描画内容を画面上で複写したい|Canvas::copy()]]
6276 * [[描画内容をオフスクリーンへ複写したい|Canvas::to()]]
6278 * [[FPSチェッカを使用したい|5.2.1 描画の時間精度を確認する-FPSチェッカー]]
6280 * [[Canvas::pix(double, double, double)という命令はコンパイルできないのですか?]]
6281 * [[止まった絵を描画するときCanvas.flip()を単独でwhileループの中に書くと、画面がちらつきます。]]</pre>
6283 <div title="Clock::at_msec()" modifier="Psychlops_Admin" created="200709142037" changecount="1">
6284 <pre>タイマーのカウントをmsec単位に変換します。
6285 |!Clock::at_msec()| double Clock::at_msec(void)| カウントをmsec単位に変換(double)して返す|
6287 詳細とコード例は[[5.1節|5.1 時間を計測する]]参照</pre>
6289 <div title="Clock::update()" modifier="Psychlops_Admin" created="200709142036" changecount="1">
6290 <pre>Timerに現在のCPUタイマーの値を入力します。
6291 |!Clock::update()| Clock::update(void)| 現在のCPUタイマーの値を入力する|
6293 詳細とコード例は[[5.1節|5.1 時間を計測する]]参照</pre>
6295 <div title="Color()の宣言" modifier="Psychlops_Admin" modified="200709132234" created="200709131904" changecount="4">
6296 <pre>描画対象に色をつけたい場合、Color()の宣言を行い色の設定をします。
6297 宣言のみの場合、[[Color::set()|2.2.5 描画する図形の色を指定する-Colorクラス-]]で色の詳細情報を設定しますが
6298 宣言文のみでも色の情報の設定が可能です。
6300 |!Color()の宣言|Color()|[[Psychlops::Color]]型の宣言を行います|
6301 |~|Color(int r, int g, int b, int a)|[[Psychlops::Color]]型の宣言を行いr,g,bと透明度aで構成されるカラー情報を設定します|
6302 |~|~|int r:赤の輝度を指定。範囲は0~1|
6303 |~|~|int g:緑の輝度を指定。範囲は0~1|
6304 |~|~|int b:青の輝度を指定。範囲は0~1|
6305 |~|~|int a:透明度を指定。範囲は0~1。0に近いほど透明度が高い|
6306 |~|Color(double r, double g, double b, double a)|[[Psychlops::Color]]型の宣言を行いr,g,bと透明度aで構成されるカラー情報を設定します|
6307 |~|~|double r:赤の輝度を指定。範囲は0.0~1.0|
6308 |~|~|double g:緑の輝度を指定。範囲は0.0~1.0|
6309 |~|~|double b:青の輝度を指定。範囲は0.0~1.0|
6310 |~|~|double a:透明度を指定。範囲は0.0~1.0。0.0に近いほど透明度が高い|
6311 |~|Color(int r, int g, int b)|[[Psychlops::Color]]型の宣言を行いr,g,bで構成されるカラー情報を設定します|
6312 |~|~|int r:赤の輝度を指定。範囲は0.0~1.0|
6313 |~|~|int g:緑の輝度を指定。範囲は0.0~1.0|
6314 |~|~|int b:青の輝度を指定。範囲は0.0~1.0|
6315 |~|~|透明度は1.0が固定で指定されている|
6316 |~|Color(long r, long g, long b)|[[Psychlops::Color]]型の宣言を行いr,g,bで構成されるカラー情報を設定します|
6317 |~|~|long r:赤の輝度を指定。範囲は0.0~1.0|
6318 |~|~|long g:緑の輝度を指定。範囲は0.0~1.0|
6319 |~|~|long b:青の輝度を指定。範囲は0.0~1.0|
6320 |~|~|透明度は1.0が固定で指定されている|
6321 |~|Color(float r, float g, float b)|[[Psychlops::Color]]型の宣言を行いr,g,bで構成されるカラー情報を設定します|
6322 |~|~|float r:赤の輝度を指定。範囲は0.0~1.0|
6323 |~|~|float g:緑の輝度を指定。範囲は0.0~1.0|
6324 |~|~|float b:青の輝度を指定。範囲は0.0~1.0|
6325 |~|~|透明度は1.0が固定で指定されている|
6326 |~|Color(double r, double g, double b)|[[Psychlops::Color]]型の宣言を行いr,g,bで構成されるカラー情報を設定します|
6327 |~|~|double r:赤の輝度を指定。範囲は0.0~1.0|
6328 |~|~|double g:緑の輝度を指定。範囲は0.0~1.0|
6329 |~|~|double b:青の輝度を指定。範囲は0.0~1.0|
6330 |~|~|透明度は1.0が固定で指定されている|
6331 |~|Color(int gray)|[[Psychlops::Color]]型の宣言を行いグレースケールの輝度を設定します|
6332 |~|~|int gray: 輝度を指定。範囲は0.0~1.0|
6333 |~|~|透明度は1.0が固定で指定されている|
6334 |~|Color(long gray)|[[Psychlops::Color]]型の宣言を行いグレースケールの輝度を設定します|
6335 |~|~|long gray: 輝度を指定。範囲は0.0~1.0|
6336 |~|~|透明度は1.0が固定で指定されている|
6337 |~|Color(float gray)|[[Psychlops::Color]]型の宣言を行いグレースケールの輝度を設定します|
6338 |~|~|透明度は1.0が固定で指定されている|
6339 |~|~|float gray: 輝度を指定。範囲は0.0~1.0|
6340 |~|~|透明度は1.0が固定で指定されている|
6341 |~|Color(double gray)|[[Psychlops::Color]]型の宣言を行いグレースケールの輝度を設定します|
6342 |~|~|double gray: 輝度を指定。範囲は0.0~1.0|
6343 |~|~|透明度は1.0が固定で指定されている|
6346 <div title="Color::XXXX" modifier="YourName" modified="200803100613" created="200708231845" changecount="2">
6347 <pre>あらかじめ用意された基本的な色指定を呼び出すことが可能です。
6349 |!Color::white|白(1.0, 1.0, 1.0)|
6350 |!Color::black|黒(0.0, 0.0, 0.0)|
6351 |!Color::gray|灰色(0.5, 0.5, 0.5)|
6352 |!Color::red|赤(1.0, 0.0, 0.0)|
6353 |!Color::green|緑( 0.0, 1.0, 0.0)|
6354 |!Color::blue|青(0.0, 0.0, 1.0)|
6355 |!Color::cyan|シアン(0.0, 1.0, 1.0)|
6356 |!Color::magenta|マゼンタ(1.0, 1.0, 0.0)|
6357 |!Color::yellow|黄(1.0, 1.0, 0.0)|</pre>
6359 <div title="Color::get()" modifier="YourName" modified="200803140530" created="200802130630" changecount="5">
6360 <pre>Color型に格納されている各値を得ます。
6362 |!Color::get()|get(double r, double g, double b, double a)|r,g,bと透明度aで構成されるカラー情報を取得します。|
6363 |~|~|double r:赤の輝度(0.0~1.0)|
6364 |~|~|double g:緑の輝度(0.0~1.0)|
6365 |~|~|double b:青の輝度(0.0~1.0)|
6366 |~|~|double a:透明度(0.0~1.0)。0に近いほど透明度が高い|
6368 |!Color::getX()|getR()|赤チャネルの値を返します|
6369 |~|getG()|緑チャネルの値を返します|
6370 |~|getB()|青チャネルの値を返します|
6371 |~|getA()|透明チャネルの値を返します|
6378 col.get(r, g, b, a);
6379 // r => 0.5, g => 0.5, b => 0.5, a => 1
6381 col.set(1.0, 0.5, 0.0, 0.5);
6389 <div title="Color::set()" modifier="YourName" modified="200803100609" created="200712131736" changecount="4">
6390 <pre>|!Color::set()|set(double r, double g, double b, double a)|r,g,bと透明度aで構成されるカラー情報を設定します。|
6391 |~|~|double r:赤の輝度を指定。範囲は0.0~1.0|
6392 |~|~|double g:緑の輝度を指定。範囲は0.0~1.0|
6393 |~|~|double b:青の輝度を指定。範囲は0.0~1.0|
6394 |~|~|double a:透明度を指定。範囲は0.0~1.0 (0.0に近いほど透明度が高い)|
6395 |~|set(double r, double g, double b)|r,g,bで構成されるカラー情報を設定します。|
6396 |~|~|double r:赤の輝度を指定。範囲は0.0~1.0|
6397 |~|~|double g:緑の輝度を指定。範囲は0.0~1.0|
6398 |~|~|double b:青の輝度を指定。範囲は0.0~1.0|
6399 |~|set(double gray)|グレースケールの輝度を設定します。|
6400 |~|~|double gray: 輝度を指定。範囲は0.0~1.0|
6402 [[2.2.5 描画する図形の色を指定する-Colorクラス-]]</pre>
6404 <div title="Color::setGammaTable()" modifier="YourName" modified="200802131125" created="200802130701" changecount="3">
6405 <pre>他の多くの描画ライブラリ同様、CRTの各画素における輝度レベルとPsychlopsで指定する輝度レベルは線形の関係を維持しません。
6406 これを簡易的に補正するために、R,G,Bの各色チャンネルについて変換表(LUTテーブル)を用いた補正を行う方法が広く用いられています。
6407 Psychlopsでは、この変換表を指定することで、[[ガンマ補正|Tips: ガンマ補正]]された色を表示することが可能です。
6408 このための命令として、setGammaTable()命令があります。
6410 |!Canvas::setGammaTable()|setGammaTable(double Cr[], double Cg[], double Cb[], int table_size)|この命令以降実行されるPsychlops::Colorに対して変換表によるを自動的に行います^^*1^^|
6411 |~|~|Cr: R(赤)チャンネルの変換表|
6412 |~|~|Cg: G(緑)チャンネルの変換表|
6413 |~|~|Cb: B(青)チャンネルの変換表|
6414 |~|~|table_size: 表の項目数。8ビット環境下ではできれば256個の項目を用意してください。^^*2^^|
6415 |~|setGammaTable(std::vector<double> Cr, std::vector<double> Cg, std::vector<double> Cb)|この命令以降実行されるPsychlops::Colorに対してGamma補正を自動的に行います^^*1^^|
6416 |~|~|Cr: R(赤)チャンネルの変換表|
6417 |~|~|Cg: G(緑)チャンネルの変換表|
6418 |~|~|Cb: B(青)チャンネルの変換表|
6419 *1 Psychlopsはまずハードウェアによるガンマ補正([[Canvas::setGammaTable()]])を試みます。失敗すると、ソフトウェアによるエミュレーションを試みます。
6420 *2 256個以下でも自動的に線形補完されますが、正確な補完は256個すべて指定することで実現されます。
6421 *3 値は配列またはC++STLの動的配列(std::vector<double>)で指定し、8ビット環境なら256段階の理論値を配列の要素番号とし、それぞれの段階の補償値を配列要素に書き込みます。
6423 コード例は[[ガンマ補正|Tips: ガンマ補正]]参照
6426 <div title="Color::setGammaValue" modifier="YourName" modified="200802130652" created="200802130648" changecount="4">
6429 <div title="Color::setGammaValue()" modifier="YourName" modified="200802131124" created="200802130652" changecount="3">
6430 <pre>他の多くの描画ライブラリ同様、CRTの各画素における輝度レベルとPsychlopsで指定する輝度レベルは線形の関係を維持しません。これを簡易的に補正するために、R,G,Bの各色チャンネルについてGamma関数を用いた補正を行う方法が広く用いられています。
6431 Psychlopsでは、このGamma関数の係数を指定することで、[[ガンマ補正|Tips: ガンマ補正]]された色を表示することが可能です。
6432 このための命令として、setGammaValue()命令があります。
6434 |!Canvas::setGammaValue()|setGammaValue(double Cr, double Cg, double Cb)|この命令以降実行されるPsychlops::Colorに対してGamma補正を自動的に行います^^*1^^|
6435 |~|~|Cr: R(赤)チャンネルのガンマ係数|
6436 |~|~|Cg: G(緑)チャンネルのガンマ係数|
6437 |~|~|Cb: B(青)チャンネルのガンマ係数|
6438 *1 Psychlopsはまずハードウェアによるガンマ補正([[Canvas::setGammaValue()]])を試みます。失敗すると、ソフトウェアによるエミュレーションを試みます。
6439 *2 指定する値は表示装置のガンマ値そのものです。Psychlops側で補償指数を自動決定します。
6441 コード例は[[ガンマ補正|Tips: ガンマ補正]]参照
6444 <div title="Color::strict_match" modifier="YourName" modified="200802201822" created="200802130704" changecount="3">
6445 <pre>実験時に色指定を厳密に行わせるよう強制させることができます。具体的には、ガンマ補正や色指定で値が0以上1以下にならないケースで警告する、ソフトウェアエミュレーション(後述)を禁止して警告を出す、という規制が行われます。以下のコードをpsychlops_main()の冒頭に記述してください。
6447 Color::strict_match = true;
6450 なお、警告はC++の例外機能を使って送出されます。警告を受け入れる構文を書かない限り強制終了します。警告を受け入れるtry..catch構文については、C++の参考書をお読みください。
6453 <div title="Color型に関して" modifier="Kazushi Maruya" created="200712131735" changecount="1">
6454 <pre>* [[描画色を設定したい|Color::set()]]
6455 * [[白、黒などのデフォルトの描画色を使用したい|Color::XXXX]]</pre>
6457 <div title="DefaultTiddlers" modifier="PsychlopsAdmin" created="200709161806" changecount="1">
6460 <div title="Displayの指定値" modifier="Psychlops_DevelopperG" modified="200910080812" created="200910080753" changecount="5">
6461 <pre>クラス定数として以下のものが用意されています。
6462 |Display::secondary|その時点でのプライマリディスプレイを取得します|
6463 |Display::primary|その時点でのセカンダリディスプレイを取得します|
6465 より複雑な環境では以下の命令を使ってリストを取得してください。
6466 |!Display::list()|ディスプレイのリストを動的配列(std::vector<Display>)で取得します。|
6471 <div title="GaborImage" modifier="Kazushi Maruya" modified="200712061920" created="200710070016" changecount="3">
6476 virtual void draw(Image &area, double ori, double freq, double phase, double Lmean, double *contrast) {
6477 int center_x = area.getCenter().getX(), center_y = area.getCenter().getY();
6478 int width = area.getWidth()/2, height = area.getHeight()/2;
6480 double sigma = (width)/3;
6481 double SINORI, COSORI, _x, gr, ga, ph, R, G, B;
6484 COSORI=cos(-ori*PI/180);
6485 SINORI=sin(-ori*PI/180);
6489 for(int y=-height; y<height; y++) {
6490 for(int x=-width; x<width; x++) {
6491 _x= COSORI * x - SINORI * y;
6492 ga=exp(-0.5*(pow(x/(sigma),2))-0.5*(pow(y/(sigma), 2)));
6493 gr=(1+cos(2*PI*freq*_x+ph))*0.5;
6494 R=Lmean+Lmean*contrast[0]*(gr*ga);
6495 G=Lmean+Lmean*contrast[1]*(gr*ga);
6496 B=Lmean+Lmean*contrast[2]*(gr*ga);
6497 tmpcol.set(R, G, B);
6498 area.pix(x+width,y+height,tmpcol);
6503 virtual void draw(Image &area, double ori, double freq, double phase, double Lmean, double contrast) {
6504 int center_x = area.getCenter().getX(), center_y = area.getCenter().getY();
6505 int width = area.getWidth()/2, height = area.getHeight()/2;
6507 double sigma = (width)/3;
6509 double SINORI, COSORI, _y, gr, ga, ph;
6511 COSORI=cos(ori*PI/180);
6512 SINORI=sin(ori*PI/180);
6516 for(int y=-height; y<height; y++) {
6517 for(int x=-width; x<width; x++) {
6518 _y= SINORI * x + COSORI * y;
6519 ga=exp(-0.5*(pow(x/(sigma),2))-0.5*(pow(y/(sigma), 2)));
6520 gr=(1+cos(2*PI*freq*_y+ph))*0.5;
6521 tmpcol.set(Lmean+Lmean*contrast*(gr*ga));
6522 area.pix(x+width,y+height,tmpcol);
6529 <div title="Gabor方位判断の実験プログラム例" modifier="Kazushi Maruya" modified="200712061920" created="200709162000" changecount="6">
6531 #include <psychlops.h>
6532 #include <stdio.h>
6533 using namespace Psychlops;
6535 class IndependentVariables
6537 int MaxVarNum, MaxStepNum;
6538 double *VariableData;
6539 int *VariableStepNumber;
6540 int conditionNumber;
6543 Psychlops::Matrix CondMtx;
6548 IndependentVariables(int n, int m, int repeat): MaxVarNum(n), MaxStepNum(m){
6549 VariableData = (double *)malloc(sizeof(double)*(n+1)*m);
6550 VariableStepNumber = (int *)malloc(sizeof(double)*m);
6551 repeatNumber=repeat;
6555 virtual ~IndependentVariables(void){
6557 free(VariableStepNumber);
6560 virtual int set(int vnum, int arraynum, double *array){
6561 if(vnum<MaxVarNum+1 && arraynum<MaxStepNum+1){
6562 VariableStepNumber[vnum]=arraynum;
6563 for(int i=0; i<arraynum; i++){*(VariableData+(vnum+1)*MaxStepNum+i)=*(array+i);}
6570 virtual int randomize(char* dataname=NULL){
6571 trialNumber=repeatNumber;
6572 for(int i=0; i<MaxVarNum; i++)trialNumber*=VariableStepNumber[i];
6573 if(trialNumber<1){return -1;}
6574 CondMtx.set(trialNumber, MaxVarNum);
6577 double temp=1, temp2;
6579 //Make Output Matrix
6581 for(i=0; i<MaxVarNum; i++){
6582 for(j=0; j<i; j++){temp*=(double)VariableStepNumber[j];}
6583 for(j=0; j<trialNumber; j++){CondMtx(j+1,i+1)=(int)ceil(j/temp)% VariableStepNumber[i];}
6587 //Randomize Output Matrix
6588 for(i=1; i<trialNumber;i++){
6589 a = Psychlops::random(i-1)+1;
6590 for(j=1;j<MaxVarNum+1;j++){
6592 CondMtx(a,j)=CondMtx(i,j);
6597 //swtich of the condition matrix save
6599 std::ofstream output;
6600 output.open(dataname);
6601 output << CondMtx << std::endl;
6607 virtual double get(int vnum, int trial_now){
6608 if(vnum<MaxVarNum && (int)trial_now<trialNumber)
6609 return *(VariableData+(vnum+1)*MaxStepNum+getCondMtx(vnum, trial_now));
6613 virtual int getCondMtx(int vnum, unsigned int trial_now){
6614 if(vnum<MaxVarNum && (int)trial_now<trialNumber)
6615 return CondMtx( trial_now+1, vnum+1);
6619 virtual double getIndependent(int vnum, int stepnum){
6620 if(vnum<MaxVarNum && stepnum<VariableStepNumber[vnum])
6621 return *(VariableData+(vnum)*MaxStepNum+stepnum);
6626 virtual int getMStep(int vnum){
6627 if(vnum<MaxVarNum)return VariableStepNumber[vnum];
6635 virtual void draw(Image &area, double ori, double freq, double phase, double Lmean, double *contrast) {
6636 int center_x = area.getCenter().getX(), center_y = area.getCenter().getY();
6637 int width = area.getWidth()/2, height = area.getHeight()/2;
6639 double sigma = (width)/3;
6640 double SINORI, COSORI, _x, gr, ga, ph, R, G, B;
6643 COSORI=cos(-ori*PI/180);
6644 SINORI=sin(-ori*PI/180);
6648 for(int y=-height; y<height; y++) {
6649 for(int x=-width; x<width; x++) {
6650 _x= COSORI * x - SINORI * y;
6651 ga=exp(-0.5*(pow(x/(sigma),2))-0.5*(pow(y/(sigma), 2)));
6652 gr=(1+cos(2*PI*freq*_x+ph))*0.5;
6653 R=Lmean+Lmean*contrast[0]*(gr*ga);
6654 G=Lmean+Lmean*contrast[1]*(gr*ga);
6655 B=Lmean+Lmean*contrast[2]*(gr*ga);
6656 tmpcol.set(R, G, B);
6657 area.pix(x+width,y+height,tmpcol);
6662 virtual void draw(Image &area, double ori, double freq, double phase, double Lmean, double contrast) {
6663 int center_x = area.getCenter().getX(), center_y = area.getCenter().getY();
6664 int width = area.getWidth()/2, height = area.getHeight()/2;
6666 double sigma = (width)/3;
6668 double SINORI, COSORI, _y, gr, ga, ph;
6670 COSORI=cos(ori*PI/180);
6671 SINORI=sin(ori*PI/180);
6675 for(int y=-height; y<height; y++) {
6676 for(int x=-width; x<width; x++) {
6677 _y= SINORI * x + COSORI * y;
6678 ga=exp(-0.5*(pow(x/(sigma),2))-0.5*(pow(y/(sigma), 2)));
6679 gr=(1+cos(2*PI*freq*_y+ph))*0.5;
6680 tmpcol.set(Lmean+Lmean*contrast*(gr*ga));
6681 area.pix(x+width,y+height,tmpcol);
6687 void psychlops_main() {
6689 Canvas sampleA(Canvas::fullscreen);
6690 IndependentVariables invar(2,5,10);
6692 double gaborOrientation[5]={0,1,2,3,4}; //Gaborの方位
6693 double duration[3]={100,250,500}; //刺激の提示時間
6695 invar.set(0, 5, gaborOrientation);
6696 invar.set(1, 3, duration);
6697 invar.randomize("ConditionMatrix.dat");
6699 GaborImage gaborIMG;
6700 Psychlops::Image gIMG[10];
6701 double gIMGsize=100, ori;
6702 for(int i=0; i<10; i++){
6703 if(i<5){ ori=90.0-pow(2.0, i);}
6704 else{ ori=90.0+pow(2.0,i-4);}
6705 gIMG[i].set(gIMGsize, gIMGsize);
6706 gaborIMG.draw(gIMG[i], ori, 1.0/30.0, 0.0, 0.5, 0.2);
6707 gIMG[i].centering();
6711 const int trialNum=2*5*10;
6712 int answer[trialNum], orientationCondition[trialNum], durationCondition[trialNum], ans;
6713 int ori_now, dura_now;
6715 char trial_header[64];
6717 for(int trial=0; trial<trialNum; trial++){
6719 direction=Psychlops::random(2); //0:right 1:left
6720 ori_now=invar.get(0,trial)+5*direction;
6721 dura_now=sampleA.getRefreshRate()*(invar.get(1,trial)/1000);
6725 for(int i=0; i<64; i++) trial_header[i] = 0;// 文字列の初期化
6726 sprintf(trial_header, "%s%d%s%d","Trial: ", trial, " /", trialNum );//試行番号の埋め込み
6727 sampleA.message(trial_header, sampleA.getHcenter(), sampleA.getVcenter()-100, Color::green);//キー待ちメッセージの描画
6729 while(!Input::get(Keyboard::spc));
6733 gIMG[ori_now].draw();
6734 sampleA.flip(dura_now);
6740 if(Input::get(Keyboard::j)){ans=0;break;} //"j"なら右(0)
6741 if(Input::get(Keyboard::f)){ans=1;break;}//"f"なら左(1)
6742 if(Input::get(Keyboard::esc)){exit(0);}
6744 if(ans==direction)answer[trial]=1;
6745 else answer[trial]=0;
6746 orientationCondition[trial]=invar.get(0, trial);
6747 durationCondition[trial]=invar.get(1, trial);
6751 sampleA.message("Press space key to exit.", sampleA.getHcenter(), sampleA.getVcenter()-100, Color::green);
6752 Data::savearray("ExptData.dat", "Duration /t Orientation /t Answer", trialNum, durationCondition, orientationCondition, answer );
6757 <div title="GettingStarted" modifier="PsychlopsAdmin" created="200707220303" changecount="1">
6758 <pre>To get started with this blank TiddlyWiki, you'll need to modify the following tiddlers:
6759 * SiteTitle & SiteSubtitle: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)
6760 * MainMenu: The menu (usually on the left)
6761 * DefaultTiddlers: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened
6762 You'll also need to enter your username for signing your edits: <<option txtUserName>></pre>
6764 <div title="Image()の宣言" modifier="Psychlops_Admin" modified="200709142109" created="200709142045" changecount="2">
6765 <pre>[[Psychlops::Image]]型は主に画像を取り扱います。
6766 [[Psychlops::Image]]型の命令を使用する前に必ず宣言が必要です。
6767 宣言のみの場合、[[Image::set()]]でImageの詳細情報を設定しますが
6770 |!Imageの宣言|Image()|オフスクリーン領域を宣言します|
6771 |~|Image([[Psychlops::Rectangle]] rec,PixelComponentsCnt pixcompcntval)|指定された[[Psychlops::Rectangle]] 型と同じ大きさのオフスクリーン領域を宣言します|
6772 |~|~|[[Psychlops::Rectangle]] rect :[[Psychlops::Rectangle]] 型を指定|
6773 |~|~|PixelComponentsCnt pixcompcntval :[[カラーモード]]を指定。省略した場合はRGBが指定される|
6774 |~|Image(long x, long y, PixelComponentsCnt pixcompcntval)|指定されたx×yと同じ大きさのオフスクリーン領域を宣言します|
6775 |~|~|long x :オフスクリーン領域の横幅を指定|
6776 |~|~|long y :オフスクリーン領域の縦幅を指定|
6777 |~|~|PixelComponentsCnt pixcompcntval :[[カラーモード]]を指定。省略した場合はRGBが指定される|
6779 コード例は[[3.1.1節|3.1.1 オフスクリーン領域の宣言]]参照</pre>
6781 <div title="Image::centering()" modifier="Kazushi Maruya" modified="200712131731" created="200712131730" changecount="2">
6782 <pre>!Image::centering()
6783 [[Psychlops::Image]]型の領域を移動します。
6784 座標を指定する場合、その座標に領域の中心が設定されます。
6785 |!Image::centering()|centering()|[[Psychlops::Image]]型の領域を画面中心に移動します|
6786 |~|centering(double x, double y)|[[Psychlops::Image]]型の領域の中心を指定した座標(x,y)に移動します|
6787 |~|~|double x:移動する領域のx座標を指定|
6788 |~|~|double y:移動する領域のy座標を指定|
6790 [[3.1.3 オフスクリーン上の任意の位置に描画する]]</pre>
6792 <div title="Image::clear()" modifier="Psychlops_Admin" modified="200709142113" created="200709142111" changecount="2">
6793 <pre>オフスクリーン領域をクリアします。
6794 |!Image::clear()|clear([[Psychlops::Color]] col)|指定したオフスクリーン領域を指定色でクリアします|
6795 |~|~|[[Psychlops::Color]] col:クリアする色を指定|
6797 !!Image::clear()の書き方
6801 #include <psychlops.h>
6802 using namespace Psychlops;
6804 double width=100,height=100;
6805 Psychlops::Rectangle rect1(width,height);
6806 Psychlops::Image Noise1(rect1);
6808 void psychlops_main() {
6810 Canvas sampleA(Canvas::fullscreen);
6811 Noise1.clear(Color::magenta);
6814 while(!Input::get(Keyboard::spc));
6818 <div title="Image::draw()" modifier="YourName" modified="200803100605" created="200709142049" changecount="5">
6820 実際にオフスクリーン領域に描画する際の命令です。
6821 この命令を記述しないといくら[[Image::pix()]]命令や[[Image::rect()]]命令を記述しても描画されませんので注意してください。
6823 |!Image::draw()|draw()|指定したオフスクリーンを描画します|
6824 |~|draw(double left, double top)|指定したオフスクリーン領域を(left,top)を中心とした座標に設定します|
6825 |~|~|double left: 描画するオフスクリーン領域の水平座標値|
6826 |~|~|double top: 描画するオフスクリーン領域の垂直座標値|
6828 コード例は[[3.1.2節|3.1.2 オフスクリーン上に点・四角形を描画する]]参照</pre>
6830 <div title="Image::from()" modifier="Kazushi Maruya" created="200712131721" changecount="1">
6831 <pre>|!Image::from|from( luminance )|Matrixを輝度としてImageに読み込み、グレースケール画像を作成します。|
6832 |~|~|Matrix luminance: |
6833 |~|from( r, g, b )|Matrixを赤、緑、青チャネルとして読み込み、RGB画像を作成します。|
6837 |~|from( r, g, b, a) |Matrixを赤、緑、青、透明度チャネルとして読み込み、RGBA画像を作成します。|
6842 Imageの中身がすでに確保されていた場合は、破棄されます。
6844 [[5.3.5 行列の中身をオフスクリーンに描画する]]</pre>
6846 <div title="Image::getXXXX()" modifier="Psychlops_Admin" modified="200709142210" created="200709142100" changecount="7">
6847 <pre>Imageの様々な情報を、Image::getXXXX()命令を使って取得します(XXXXにはそれぞれの情報に対応した名前が入ります)。
6848 |!Image::getXXXX()|getCenter()|オフスクリーン領域の中心座標(x,y)を取得します|
6849 |~|getHcenter()|オフスクリーン領域の横方向の中心座標(x)を取得します|
6850 |~|getVcenter()|オフスクリーン領域の縦方向の中心座標(y)を取得します|
6851 |~|getHeight()|オフスクリーン領域の高さ(ピクセル)を取得します|
6852 |~|getWidth()|オフスクリーン領域の幅(ピクセル)を取得します|
6853 |~|getPix(int x, int y)|オフスクリーン領域上の座標(x,y)の色を取得します|
6854 |~|~|int x :オフスクリーン領域上の座標xを指定|
6855 |~|~|int y :オフスクリーン領域上の座標yを指定|
6856 |~|~|注:オフスクリーン領域の左上を座標(0,0)とする。オフスクリーン領域外の座標は指定しないこと|
6859 [[Psychlops::Image]]型の情報を取得し画面表示を行います。
6860 画面表示は[[Canvas::var()]]命令と[[Canvas::msg()]]命令を使用します。
6863 #include <psychlops.h>
6864 using namespace Psychlops;
6866 Psychlops::Image Noise1(300,300);
6867 Psychlops::Point point1;
6868 Psychlops::Color col1,col2;
6869 Psychlops::Rectangle rect1(50,50);
6870 double d1,d2,d3,d4,x,y;
6872 void psychlops_main() {
6874 Canvas sampleA(Canvas::fullscreen);
6876 col1.set(0.8,0.4,0.1);
6879 point1=Noise1.getCenter();
6880 d1=Noise1.getHcenter();
6881 d2=Noise1.getVcenter();
6882 d3=Noise1.getHeight();
6883 d4=Noise1.getWidth();
6884 col2=Noise1.getPix(100,100);
6889 sampleA.msg("getCenter_x:",50,200);
6890 sampleA.var(x,200,200);//getCenter:X
6891 sampleA.msg("getCenter_y:",50,250);
6892 sampleA.var(y,200,250);//getCenter:Y
6893 sampleA.msg("getHcenter:",50,300);
6894 sampleA.var(d1,200,300);//getHcenter
6895 sampleA.msg("getVcenter:",50,350);
6896 sampleA.var(d2,200,350);//getVcenter
6897 sampleA.msg("gettHeight:",50,400);
6898 sampleA.var(d3,200,400);//getHeight
6899 sampleA.msg("getWidth:",50,450);
6900 sampleA.var(d4,200,450);//getWidth
6902 sampleA.oval(rect1,col2);
6906 while(!Input::get(Keyboard::spc));
6910 <div title="Image::line()" modifier="YourName" modified="200803100604" created="200709142118" changecount="4">
6911 <pre>オフスクリーン領域に線を描画します。
6912 指定する座標は画面上左上を座標(0,0)にするのではなく
6913 オフスクリーン領域の左上を座標(0,0)とします。
6914 |!Image::line()|line(double x1, double y1, double x2, double y2, [[Psychlops::Color]] col)|座標(x1,y1)から座標(x2,y2)に指定色で線を描画します|
6915 |~|~|double x1 :描画する線の開始水平座標値|
6916 |~|~|double y1 :描画する線の開始垂直座標値|
6917 |~|~|double x2 :描画する線の終端水平座標値|
6918 |~|~|double y2 :描画する線の終端垂直座標値|
6919 |~|~|[[Psychlops::Color]] col:描画する線の描画色|
6922 座標(100,100)から座標(200,200)に赤線を描画します。
6923 指定Image外の座標に設定すると描画されないのでオフスクリーンの位置に注意すること。
6926 #include <psychlops.h>
6927 using namespace Psychlops;
6929 Psychlops::Image Noise1(500,500);
6931 void psychlops_main() {
6933 Canvas sampleA(Canvas::fullscreen);
6934 Noise1.line(100,100,200,200,Color::red);
6937 while(!Input::get(Keyboard::spc));
6941 <div title="Image::load()" modifier="Kazushi Maruya" modified="200712131733" created="200712131731" changecount="2">
6943 既存の任意の画像ファイルを読み込みます。
6945 |!Image::load()|load(filename)|指定した画像ファイルを読み込みます|>|>|
6946 |~|~|filename: 読み込む画像ファイルの名前を指定|>|>|
6947 |~|~|読込み先を指定しない場合のパス|Mac:Macintosh HD¥ユーザ¥ユーザ名(任意)¥書類¥Psychlops¥|>|
6948 |~|~|~|Win:実行ファイルと同じパス|>|
6949 |~|~|読込みをする場合のパス指定|%APP%|Mac:実行バンドル(.app)と同じパス|
6950 |~|~|~|~|Win:実行ファイルと同じパス|
6951 |~|~|~|%RESOURCES%|Mac:実行バンドルの内部のResourcesフォルダ|
6952 |~|~|~|~|Win:実行ファイルのパスの直下のrscという名前のフォルダ|
6953 |~|~|~|%~USER_HOME%|Mac:"~/"と同じ(/Users/(user)/)|
6954 |~|~|~|~|Win:ユーザープロファイルフォルダ(C:\Documents and Settings\(user)\)|
6955 |~|~|~|%~USER_SETTING%|Mac:"~/Library/Psychlops/"|
6956 |~|~|~|~|Win:"%~AppData%Psychlops"|
6957 |~|~|~|%~USER_DOCUMENTS%|Mac:"~/Documents/Psychlops"|
6958 |~|~|~|~|Win:ユーザープロファイルフォルダ\My Documents\Psychlops|
6959 |~|~|~|%~USER_DOCUMENTS_ROOT%|Mac:"~/Documents"|
6960 |~|~|~|~|Win:ユーザープロファイルフォルダMy Documents\|
6961 **VISTA環境の場合、パス指定が正常に動作しません。VISTA環境の方はお手数ですが保存先を指定しないでお使いください。
6963 [[3.3.2 画像ファイルを読み込み表示する]]</pre>
6965 <div title="Image::oval()" modifier="Psychlops_Admin" modified="200709142129" created="200709142125" changecount="2">
6966 <pre>オフスクリーン領域に円を描画します。
6967 円の描画位置はオフスクリーン領域の座標(0,0)に描画されます。
6968 |!Image::oval()|([[Psychlops::Rectangle]] rect, [[Psychlops::Color]] col)|[[Psychlops::Rectangle]] rectで指定された直径の円を指定色で塗りつぶし描画します|
6969 |~|~|[[Psychlops::Rectangle]] rect:描画する円の直径|
6970 |~|~|[[Psychlops::Color]] col:描画する円の色を指定|
6973 座標(300,300)からはじまるオフスクリーン領域に直径(100,100)の赤円を描画します。
6976 #include <psychlops.h>
6977 using namespace Psychlops;
6979 Psychlops::Image Noise1(300,300);
6980 Psychlops::Rectangle rect1(100,100);
6982 void psychlops_main() {
6984 Canvas sampleA(Canvas::fullscreen);
6986 Noise1.oval(rect1,Color::red);
6989 while(!Input::get(Keyboard::spc));
6993 <div title="Image::pix()" modifier="YourName" modified="200803100603" created="200709142049" changecount="4">
6995 オフスクリーン領域に点を描画するための命令です。
6996 [[Canvas::pix()|2.2.1 画面上に点を描画する-Pointクラス1-]]命令同様に点を描画するために「座標」と「色の指定」を行います。
6997 色の指定についての詳細は[[2.2.5 描画する図形の色を指定する-Colorクラス-]]を参照してください。
6999 |!Image::pix()|pix(double x, double y, [[Psychlops::Color]] col)|座標(x,y)に[[Psychlops::Color]] col色の点を描画します。|
7000 |~|~|double x: 描画する点の水平座標値|
7001 |~|~|double y: 描画する点の垂直座標値|
7002 |~|~|[[Psychlops::Color]] col:描画する点の描画色|
7003 |~|pix([[Psychlops::Point]] po, [[Psychlops::Color]] col)|Point座標(x,y)に[[Psychlops::Color]] col色の点を描画します|
7004 |~|~|[[Psychlops::Point]] po :描画する点の座標|
7005 |~|~|[[Psychlops::Color]] col:描画する点の色|
7007 コード例は[[3.1.2節|3.1.2 オフスクリーン上に点・四角形を描画する]]参照</pre>
7009 <div title="Image::rect()" modifier="YourName" modified="200803100619" created="200709142050" changecount="5">
7010 <pre>オフスクリーン領域に四角形を描画するための命令です。
7011 実際の描画処理は[[Image::draw()]]命令で行われます。
7012 色の指定についての詳細は[[2.2.5 描画する図形の色を指定する-Colorクラス-]]を参照してください。
7014 |!Image::rect()|rect([[Psychlops::Rectangle]] rect, [[Psychlops::Color]] col)|[[Psychlops::Rectangle]]型の四角形を[[Psychlops::Color]] col色で描画します|
7015 |~|~|[[Psychlops::Rectangle]] rect : 矩形の描画範囲|
7016 |~|~|[[Psychlops::Color]] col:描画する四角形の描画色|
7018 コード例は[[3.1.2節|3.1.2 オフスクリーン上に点・四角形を描画する]]参照</pre>
7020 <div title="Image::save()" modifier="Kazushi Maruya" created="200712131732" changecount="1">
7021 <pre>|!Image::save()|save(filename)|指定したImageを保存します|>|>|
7022 |~|~|filename: 保存するImageの画像ファイル名称(拡張子まで)を指定|>|>|
7023 |~|~|保存先を指定しない場合のパス|Mac:Macintosh HD¥ユーザ¥ユーザ名(任意)¥書類¥Psychlops¥|>|
7024 |~|~|~|Win:実行ファイルと同じパス|>|
7025 |~|~|保存をする場合のパス指定|%APP%|Mac:実行バンドル(.app)と同じパス|
7026 |~|~|~|~|Win:実行ファイルと同じパス|
7027 |~|~|~|%RESOURCES%|Mac:実行バンドルの内部のResourcesフォルダ|
7028 |~|~|~|~|Win:実行ファイルのパスの直下のrscという名前のフォルダ|
7029 |~|~|~|%~USER_HOME%|Mac:"~/"と同じ(/Users/(user)/)|
7030 |~|~|~|~|Win:ユーザープロファイルフォルダ(C:\Documents and Settings\(user)\)|
7031 |~|~|~|%~USER_SETTING%|Mac:"~/Library/Psychlops/"|
7032 |~|~|~|~|Win:"%~AppData%Psychlops"|
7033 |~|~|~|%~USER_DOCUMENTS%|Mac:"~/Documents/Psychlops"|
7034 |~|~|~|~|Win:ユーザープロファイルフォルダ\My Documents\Psychlops|
7035 |~|~|~|%~USER_DOCUMENTS_ROOT%|Mac:"~/Documents"|
7036 |~|~|~|~|Win:ユーザープロファイルフォルダMy Documents\|
7037 **VISTA環境の場合、パス指定が正常に動作しません。VISTA環境の方はお手数ですが保存先を指定しないでお使いください。
7039 [[3.3.1 画像ファイルを保存する]]</pre>
7041 <div title="Image::set()" modifier="YourName" modified="200803100559" created="200709142046" changecount="4">
7042 <pre>宣言時に何の指定もしなかった場合、Image::set()命令を使用します。
7043 |!Image::set()|set([[Psychlops::Rectangle]] rect, PixelComponentsCnt pixcompcntval)|指定された[[Psychlops::Rectangle]] 型と同じ大きさのオフスクリーン領域を設定します|
7044 |~|~|[[Psychlops::Rectangle]] rect :オフスクリーンの大きさを与える[[Psychlops::Rectangle]] 型を指定(座標は引き継がれない)|
7045 |~|~|PixelComponentsCnt pixcompcntval :[[カラーモード]]を指定。省略した場合はRGBが指定される|
7046 |~|set(long x, long y, PixelComponentsCnt pixcompcntval)|指定されたx×yと同じ大きさのオフスクリーン領域を設定します|
7047 |~|~|long x :オフスクリーン領域の横幅|
7048 |~|~|long y :オフスクリーン領域の縦幅|
7049 |~|~|PixelComponentsCnt pixcompcntval :[[カラーモード]]を指定。省略した場合はRGBが指定される|
7051 コード例は[[3.1.1節|3.1.1 オフスクリーン領域の宣言]]参照</pre>
7053 <div title="Image::shift()" modifier="YourName" modified="200803100601" created="200709142053" changecount="2">
7054 <pre>[[Psychlops::Image]]型の領域を移動します。
7055 元の座標(x1,y1)を基点に入力値(x,y)だけ移動します。
7056 |!Image::shift()|shift(double h, double v)|座標(X,Y)を(h,v)だけ移動します|
7057 |~|~|double x:右水平方向の移動量|
7058 |~|~|double y:下垂直方向の移動量|
7060 コード例は[[3.1.3節|3.1.3 オフスクリーン上の任意の位置に描画する]]参照</pre>
7062 <div title="Image::to()" modifier="Psychlops_DevelopperG" modified="200910070830" created="200712131722" changecount="31">
7063 <pre>|!Image::to|>|>|!グレースケール画像の輝度値をMatrixに書き込みます|
7064 |~|to( Matrix luminance )|
7065 |~|~|Matrix luminance|輝度値の行列|
7066 |~|to( Point po, double width, double height, Matrix luminance)|
7067 |~|~|Point po |コピー元領域の左上座標<br>(省略時は画像左上を起点とします)|
7068 |~|~|double width |コピー元領域の幅(pixel)|
7069 |~|~|double height |コピー元領域の高さ(pixel)|
7070 |~|~|Matrix luminance |輝度値の行列|
7071 |~|to( Point po, Rectangle rect, Matrix lumiannce) |
7072 |~|~|Point po |コピー元領域の左上座標<br>(省略時は画像左上を起点とします)|
7073 |~|~|Rectangle rect |コピー元領域のサイズを持つ矩形|
7074 |~|~|Matrix luminance|輝度値の行列|
7075 |~|to( Interval h_intvl, Interval v_intvl, Matrix luminance) |
7076 |~|~|Interval h_intvl |コピー元領域の横座標の範囲(pixel)|
7077 |~|~|Interval v_intvl |コピー元領域の縦座標の範囲(pixel)|
7078 |~|~|Matrix luminance |輝度値の行列|
7080 |!Image::to|>|>|!RGB画像の赤、緑、青チャネル値をMatrixに書き込みます|
7082 |~|~|Matrix r |赤チャンネルの輝度値の行列|
7083 |~|~|Matrix g |緑チャンネルの輝度値の行列|
7084 |~|~|Matrix b |青チャンネルの輝度値の行列|
7085 |~|to( Point po, double width, double height, Matrix r, Matrix g, Matrix b) |
7086 |~|~|Point po |コピー元領域の左上座標<br>(省略時は画像左上を起点とします)|
7087 |~|~|double width |コピー元領域の幅(pixel)|
7088 |~|~|double height |コピー元領域の高さ(pixel)|
7089 |~|~|Matrix r |赤チャネルの輝度値の行列|
7090 |~|~|Matrix g |緑チャネルの輝度値の行列|
7091 |~|~|Matrix b |青チャネルの輝度値の行列|
7092 |~|to( Point po, Rectangle rect, Matrix r, Matrix g, Matrix b) |
7093 |~|~|Point po |コピー元領域の左上座標<br>(省略時は画像左上を起点とします)|
7094 |~|~|Rectangle rect |コピー元領域のサイズを持つ矩形|
7095 |~|~|Matrix r |赤チャンネルの輝度値の行列|
7096 |~|~|Matrix g |緑チャンネルの輝度値の行列|
7097 |~|~|Matrix b |青チャンネルの輝度値の行列|
7098 |~|to( Interval h_intvl, Interval v_intvl, Matrix r, Matrix g, Matrix b) |
7099 |~|~|Interval h_intvl |コピー元領域の横座標の範囲(pixel)|
7100 |~|~|Interval v_intvl |コピー元領域の縦座標の範囲(pixel)|
7101 |~|~|Matrix r |赤チャネルの輝度値の行列|
7102 |~|~|Matrix g |緑チャネルの輝度値の行列|
7103 |~|~|Matrix b |青チャネルの輝度値の行列|
7106 |!Image::to|>|>|!RGBA画像の各チャネルの輝度値・透明度チャネルの値をMatrixに書き込みます|
7107 |~|to( Matrix r, Matrix g, Matrix b, Matrix a) |
7108 |~|~|Matrix r |赤チャネルの輝度値の行列|
7109 |~|~|Matrix g |緑チャネルの輝度値の行列|
7110 |~|~|Matrix b |青チャネルの輝度値の行列|
7111 |~|~|Matrix a |透明度チャネルの行列|
7112 |~|to( Point po, double width, double height, Matrix r, Matrix g, Matrix b, Matrix a ) |
7113 |~|~|Point po |コピー元領域の左上座標<br>(省略時は画像左上を起点とします)|
7114 |~|~|double width |コピー元領域の幅(pixel)|
7115 |~|~|double height |コピー元領域の高さ(pixel)|
7116 |~|~|Matrix r |赤チャネルの輝度値の行列|
7117 |~|~|Matrix g |緑チャネルの輝度値の行列|
7118 |~|~|Matrix b |青チャネルの輝度値の行列|
7119 |~|~|Matrix a |透明度チャネルの行列|
7120 |~|to( Point po, Rectangle rect, Matrix r, Matrix g, Matrix b, Matrix a) |
7121 |~|~|Point po |コピー元領域の左上座標<br>(省略時は画像左上を起点とします)|
7122 |~|~|Rectangle rect |コピー元領域のサイズを持つ矩形|
7123 |~|~|Matrix r |赤チャンネルの輝度値の行列|
7124 |~|~|Matrix g |緑チャンネルの輝度値の行列|
7125 |~|~|Matrix b |青チャンネルの輝度値の行列|
7126 |~|~|Matrix a |透明度チャネルの行列|
7127 |~|to( Interval h_intvl, Interval v_intvl, Matrix r, Matrix g, Matrix b, Matrix a ) |
7128 |~|~|Interval h_intvl |コピー元領域の横座標の範囲(pixel)|
7129 |~|~|Interval v_intvl |コピー元領域の縦座標の範囲(pixel)|
7130 |~|~|Matrix r |赤チャネルの輝度値の行列|
7131 |~|~|Matrix g |緑チャネルの輝度値の行列|
7132 |~|~|Matrix b |青チャネルの輝度値の行列|
7133 |~|~|Matrix a |透明度チャネルの行列|
7137 Img.to(1<=rng<=100, 1<=rng<=50, luminance);
7140 書き込み先のMatrixはいったん破棄されて作り直されます。
7141 大きさ等はImageのサイズと等しくなるように自動的に調整されます。
7143 [[5.3.5 行列の中身をオフスクリーンに描画する]]</pre>
7145 <div title="Image型に関して" modifier="Kazushi Maruya" modified="200712131731" created="200712131730" changecount="2">
7146 <pre>*[[オフスクリーンのサイズを指定したい|Image::set()]]
7147 *[[オフスクリーンを特定の描画色で塗りつぶしたい|Image::clear()]]
7148 *[[オフスクリーン上に点を打ちたい|Image::pix()]]
7149 *[[オフスクリーン上に線を引きたい|Image::line()]]
7150 *[[オフスクリーン上に四角形を描画したい|Image::rect()]]
7151 *[[オフスクリーン上に円を描画したい|Image::oval()]]
7152 *[[オフスクリーンの内容を画面に描画したい|Image::draw()]]
7153 *[[オフスクリーンの描画位置を画面中央や特定の座標にセンタリングしたい|Image::centering()]]
7154 *[[オフスクリーンの描画位置を移動させたい|Image::shift()]]
7155 *[[オフスクリーンのメンバ(情報)を取得したい|Image::getXXXX()]]
7156 *[[オフスクリーンに画像を読み込みたい|Image::load()]]
7157 *[[オフスクリーンの描画内容をファイルとして保存したい|Image::save()]]
7158 *[[Matrix型を用いて計算した描画内容をオフスクリーンへ読み込みたい|Image::to()]]
7159 *[[オフスクリーンの描画内容をMatrix型へ変換したい|Image::to()]]</pre>
7161 <div title="IndependentVariables" modifier="Psychlops_Admin" modified="200710070017" created="200709162126" changecount="3">
7163 class IndependentVariables
7165 int MaxVarNum, MaxStepNum;
7166 double *VariableData;
7167 int *VariableStepNumber;
7168 int conditionNumber;
7171 Psychlops::Matrix CondMtx;
7176 IndependentVariables(int n, int m, int repeat): MaxVarNum(n), MaxStepNum(m){
7177 VariableData = (double *)malloc(sizeof(double)*(n+1)*m);
7178 VariableStepNumber = (int *)malloc(sizeof(double)*m);
7179 repeatNumber=repeat;
7183 virtual ~IndependentVariables(void){
7185 free(VariableStepNumber);
7188 virtual int set(int vnum, int arraynum, double *array){
7189 if(vnum<MaxVarNum+1 && arraynum<MaxStepNum+1){
7190 VariableStepNumber[vnum]=arraynum;
7191 for(int i=0; i<arraynum; i++){*(VariableData+(vnum+1)*MaxStepNum+i)=*(array+i);}
7198 virtual int randomize(char* dataname=NULL){
7199 trialNumber=repeatNumber;
7200 for(int i=0; i<MaxVarNum; i++)trialNumber*=VariableStepNumber[i];
7201 if(trialNumber<1){return -1;}
7202 CondMtx.set(trialNumber, MaxVarNum);
7205 double temp=1, temp2;
7207 //Make Output Matrix
7209 for(i=0; i<MaxVarNum; i++){
7210 for(j=0; j<i; j++){temp*=(double)VariableStepNumber[j];}
7211 for(j=0; j<trialNumber; j++){CondMtx(j+1,i+1)=(int)ceil(j/temp)% VariableStepNumber[i];}
7215 //Randomize Output Matrix
7216 for(i=1; i<trialNumber;i++){
7217 a = Psychlops::random(i-1)+1;
7218 for(j=1;j<MaxVarNum+1;j++){
7220 CondMtx(a,j)=CondMtx(i,j);
7225 //swtich of the condition matrix save
7227 std::ofstream output;
7228 output.open(dataname);
7229 output << CondMtx << std::endl;
7235 virtual double get(int vnum, int trial_now){
7236 if(vnum<MaxVarNum && (int)trial_now<trialNumber)
7237 return *(VariableData+(vnum+1)*MaxStepNum+getCondMtx(vnum, trial_now));
7241 virtual int getCondMtx(int vnum, unsigned int trial_now){
7242 if(vnum<MaxVarNum && (int)trial_now<trialNumber)
7243 return CondMtx( trial_now+1, vnum+1);
7247 virtual double getIndependent(int vnum, int stepnum){
7248 if(vnum<MaxVarNum && stepnum<VariableStepNumber[vnum])
7249 return *(VariableData+(vnum)*MaxStepNum+stepnum);
7254 virtual int getMStep(int vnum){
7255 if(vnum<MaxVarNum)return VariableStepNumber[vnum];
7262 <div title="Input::get()" modifier="Kazushi Maruya" created="200712131738" changecount="1">
7263 <pre>|!Input::get()|Input::get([[Keyboard::KeyCode|キーコード表]] code, [[Keyboard::KeyState]] state)|キーボードの反応を取得します|
7264 |~|~|code 反応を取得するキーの名前 [[キーコード表]] 参照|
7265 |~|~|state 反応状態のオプション (省略時はpushed) [[Keyboard::KeyState]]参照|
7266 |~|Input::get([[Mouse::ButtonCode|キーコード表]] code, [[Mouse::ButtonState]] state)|マウスの反応を取得します|
7267 |~|~|code 反応を取得するマウスボタンの名前 [[キーコード表]] 参照|
7268 |~|~|state 反応状態のオプション (省略時はpushed) [[Mouse::ButtonState]]参照|
7270 [[4.1 外部装置(キーボード/マウス)の操作内容を取得する]]</pre>
7272 <div title="Input::refresh()" modifier="PsychlopsAdmin" modified="200709171526" created="200709171520" changecount="2">
7273 <pre>キーボードやマウスといった外部入力装置の入出力ルーチンの初期化処理
7274 |!Input::refresh()|refresh(void)|キーボード・マウスの入出力ルーチンを初期化します|
7276 コード例は[[Tips: Psychlopsにおけるキーボードマウスの入出力検出ルーチン]]参照</pre>
7278 <div title="Keyboard::KeyState" modifier="Psychlops_Admin" modified="200709142243" created="200708240330" changecount="3">
7279 <pre>| !~ButtonState | pressed |ボタンが押し続けられている |
7280 |~| pushed | 判定が行われるまでにボタンが押された |
7281 |~| released | 判定が行われるまでにボタンが離された |</pre>
7283 <div title="MainMenu" modifier="Psychlops_DevelopperG" modified="200909300421" created="200707220305" changecount="64">
7284 <pre>[[このマニュアルの見方について(ここをクリックしてください)]]
7288 [[第I部 Psychlopsの基本]]
7291 [[3. 複雑な描画を行う1(オフスクリーンへの描画)]]
7292 [[4. 複雑な描画を行う2(複数オブジェクトのグループ化と回転・拡大縮小)]]
7294 [[6. 時間制御と各種ツールを使用する]]
7296 [[第II部 Psychlopsの応用]]
7297 [[1. 既成クラスを用いた簡単な実験プログラムを作成する]]
7299 [[3. 透明度を使って刺激描画を高速化する]]
7303 [[関数一覧_FunctionList]]
7309 <div title="Math::mod" modifier="Kazushi Maruya" modified="200712180137" created="200712100223" changecount="5">
7312 負の数を割ったときの剰余は多くの言語で未定義とされていますが、C++(%演算子やfmod関数)では負の数になります。また、C++の剰余演算子は整数型のみ定義されています。
7313 Math::modは剰余の計算を浮動小数点型に拡張するとともに、負の数を割ったときの挙動を周期関数的にしてあります。視覚実験では周期関数の位相を値とすることが多くありますが、この関数を使うと位相が負でも正でも一定の範囲の剰余が得られます。ifやswitchで値ごとに条件分岐している場合には特に役立ちます。
7315 たとえば、-450を360で割った剰余をとる場合、
7318 270 == Math::mod(-450, 360)
7320 %演算子では剰余が負の数になってしまいますが、Math::modを使うと正の数で得ることができます。両関数をグラフで描画すると下図のようになります。
7322 [img[image/Math.mod.png]]
7325 |!Math::mod()|mod(double lhs, double rhs)|剰余を計算します。|
7326 |~|~|double lhs: 左辺項(割られる数)を指定します。|
7327 |~|~|double rhs: 右辺項(割る数)を指定します。|
7330 以下の例では%演算子とMath::mod()関数の返り値をプロットしています。
7334 #include <psychlops.h>
7335 using namespace Psychlops;
7337 void psychlops_main() {
7338 Canvas display(Canvas::fullscreen);
7340 double upper_v = display.getVcenter()-100, lower_v = display.getVcenter()+100;
7342 while(!Input::get(Keyboard::esc)) {
7344 display.line(display.getHcenter()-300, upper_v, display.getHcenter()+300, upper_v, Color::white);
7345 display.line(display.getHcenter()-300, lower_v, display.getHcenter()+300, lower_v, Color::white);
7346 display.line(display.getHcenter(), upper_v+60, display.getHcenter(), upper_v-60, Color::white);
7347 display.line(display.getHcenter(), lower_v+60, display.getHcenter(), lower_v-60, Color::white);
7348 display.msg("C++ %", display.getHcenter()-100, upper_v -20, Color::white);
7349 display.msg("Math::mod", display.getHcenter()-100, lower_v -20, Color::white);
7352 for(int i=-300; i<300; i++) {
7353 display.pix(i+display.getHcenter(), upper_v - i%50, Color::red);
7354 display.pix(i+display.getHcenter(), lower_v - Math::mod(i, 50), Color::green);
7361 <div title="Math::shuffle" modifier="YourName" modified="200712100209" created="200712100206" changecount="2">
7362 <pre>配列の中身をシャッフルします。
7364 |!Psychlops::shuffle()|shuffle(X array, int num)|配列の中身をシャッフルします。|
7365 |~|~|X array: 配列変数を指定します。型=代入が可能であればなんでもかまいません。|
7366 |~|~|int num: 配列の要素数を指定します。|
7367 |~|shuffle(X array, int num, X init)|配列を初期化してから中身をシャッフルします。|
7368 |~|~|X array: 配列変数を指定します。型はint, double等で有効です。|
7369 |~|~|int num: 配列の要素数を指定します。|
7370 |~|~|X init: 配列の初期値です。0を指定すると、配列が0~num-1の数で埋められてから、シャッフルを行います。|
7375 #include <psychlops.h>
7376 using namespace Psychlops;
7378 void psychlops_main() {
7379 Canvas display(Canvas::fullscreen);
7383 Math::shuffle(array, NUM, 0);
7385 while(!Input::get(Keyboard::esc)) {
7386 for(int i=0; i<NUM; i++) {
7387 display.var(array[i], 100, 20*i + 100);
7394 <div title="Matrix::catRow()" modifier="YourName" created="200712100125" changecount="1">
7395 <pre>この行列の下に他の行列を継ぎ足します。
7397 |!Matrix::catRow|catRow(other)|この行列の次の行から別の行列を継ぎ足します。|
7398 |~|~|Matrix other: 付け足す行列を指定します。付け足してもこれ自身は消されません。|
7401 #include <fstream>
7402 #include <psychlops.h>
7403 using namespace Psychlops;
7405 Psychlops::Matrix LuminanceMap1, LuminanceMap2;
7406 long Width=5, Height=1;
7408 void psychlops_main() {
7409 std::ofstream result("dump.txt");
7412 LuminanceMap1.set(Height, Width);
7414 LuminanceMap2.set(Height, Width);
7417 LuminanceMap1.catRows(LuminanceMap2);
7418 result << LuminanceMap1;
7425 <div title="Matrix::getXXXX()" modifier="YourName" created="200712100131" changecount="2">
7426 <pre>[[Psychlops::Matrix]]型の情報を、Matrix::getXXXX()命令を使って取得します(XXXXにはそれぞれの情報に対応した
7429 |!Matrix::getXXXX()|getRows()|行列の行数を取得します|
7430 |~|getCols()|行列の列数を取得します|
7432 !!Matrix::getXXXX()の書き方
7433 [[Psychlops::Matrix]]型の情報を取得しファイルに出力します。
7436 #include <fstream>
7437 #include <psychlops.h>
7438 using namespace Psychlops;
7440 Psychlops::Matrix LuminanceMap1;
7441 long Width=5, Height=3;
7443 void psychlops_main() {
7444 std::ofstream result("dump.txt");
7447 LuminanceMap1.set(Height, Width);
7449 result << LuminanceMap1.getRows() << " " << LuminanceMap1.getCols();
7454 <div title="Matrix::min() / max()" modifier="YourName" created="200712100135" changecount="1">
7455 <pre>行列の全要素中の最大値・最小値を得ます
7457 |!Matrix::min|min()|全要素中の最小値を得ます。|
7458 |!Matrix::max|max()|全要素中の最大値を得ます。|
7460 // 行列の要素に乱数を代入し、その中の最大値と最小値を抽出します。
7461 #include <fstream>
7462 #include <psychlops.h>
7463 using namespace Psychlops;
7465 Psychlops::Matrix LuminanceMap1;
7466 long Width=10, Height=10;
7468 void psychlops_main() {
7469 std::ofstream result("dump.txt");
7472 LuminanceMap1.set(Height, Width);
7473 for(int row; row<Width; row++) {
7474 for(int col; col<Width; col++) {
7475 LuminanceMap1(row, col) = Psychlops::random();
7478 result << LuminanceMap1;
7479 result << LuminanceMap1.max() << " " << LuminanceMap1.min();
7486 要素どうしで大きいものをまとめた行列、小さいものをまとめた行列を返すmax/minもあります。
7488 |!Matrix::min|min(Matrix a, Matrix b)|aとbの各要素を比較し、小さいほうを要素とする行列を返します。両Matrixの大きさは同じでなければなりません。|
7489 |!Matrix::max|max(Matrix a, Matrix b)|aとbの各要素を比較し、大きいほうを要素とする行列を返します。両Matrixの大きさは同じでなければなりません。|
7509 <div title="Matrix::reshape()" modifier="YourName" created="200712100127" changecount="1">
7510 <pre>行列をいったん1行の配列に並べ、新たに行を仕切りなおします。
7512 |!Matrix::reshape|reshape(row, col)|要素を変えずに、行と列の数のみを変更します。要素数(行*列)は変更前と変更後で同じでなければなりません。|
7513 |~|~|int row 新しい行数を指定します。|
7514 |~|~|int col 新しい列数を指定します。|
7528 #include <fstream>
7529 #include <psychlops.h>
7530 using namespace Psychlops;
7532 Psychlops::Matrix LuminanceMap1;
7533 long Width=16, Height=1;
7535 void psychlops_main() {
7536 std::ofstream result("dump.txt");
7539 LuminanceMap1.set(Height, Width);
7540 for(int col; col<Width; col++) {
7541 LuminanceMap1(1, col) = col;
7543 result << LuminanceMap1.reshape(4, 4);
7549 <div title="Matrix::rot90()" modifier="YourName" created="200712100103" changecount="1">
7550 <pre>行列を90度単位で回転させます。
7552 |!Matrix::rot90|rot90(count)|行列を90度単位で反時計回りに回転します。|
7553 |~|~|int count 回転角度を90度単位で指定します。-3,1,5のとき90度、-2,2,6のとき180度、-1,3,7のとき270度回転します。|
7567 #include <fstream>
7568 #include <psychlops.h>
7569 using namespace Psychlops;
7571 Psychlops::Matrix LuminanceMap1;
7572 long Width=16, Height=1;
7574 void psychlops_main() {
7575 std::ofstream result("dump.txt");
7578 LuminanceMap1.set(Height, Width);
7579 for(int col; col<Width; col++) {
7580 LuminanceMap1(1, col) = col;
7582 result << LuminanceMap1.rot90(1);
7587 <div title="Matrix::set()" modifier="YourName" modified="200803100614" created="200712131744" changecount="3">
7589 行列の宣言時に何の指定もしなかった場合、Matrix::set()命令を使用します。
7590 |!Matrix::set()|Matrix(rows, cols)|指定されたrows行cols列の行列を設定します|
7591 |~|~|long rows :行列の行数|
7592 |~|~|long cols :行列の列数|
7594 [[5.3.1 行列の宣言]]</pre>
7596 <div title="Matrix::slide()" modifier="YourName" created="200712100100" changecount="1">
7597 <pre>各要素を行・列各方向にずらす命令です。
7599 |!Matrix::slide|slide(row, col)|要素を行方向、列方向にずらします。あふれた分は、反対側に移動します。|
7600 |~|~|int row 行方向にずらす量を指定します。|
7601 |~|~|int col 列方向にずらす量を指定します。|
7626 #include <fstream>
7627 #include <psychlops.h>
7628 using namespace Psychlops;
7630 Psychlops::Matrix LuminanceMap1;
7631 long Width=10, Height=10;
7633 void psychlops_main() {
7634 std::ofstream result("dump.txt");
7637 LuminanceMap1.set(Height, Width);
7638 for(int row; row<Width; row++) {
7639 for(int col; col<Width; col++) {
7640 LuminanceMap1(row, col) = row*Height + col;
7643 result << LuminanceMap1.slide(1,0);
7648 <div title="Matrix::transpose()" modifier="YourName" modified="200803140532" created="200712100101" changecount="2">
7651 |!Matrix::transpose|Matrix transpose(void)|行列を転置(行と列の読み替え)します。自分自身を書き換えるので注意してください。|
7665 #include <fstream>
7666 #include <psychlops.h>
7667 using namespace Psychlops;
7669 Psychlops::Matrix LuminanceMap1;
7670 long Width=10, Height=10;
7672 void psychlops_main() {
7673 std::ofstream result("dump.txt");
7676 LuminanceMap1.set(Height, Width);
7677 for(int row; row<Width; row++) {
7678 for(int col; col<Width; col++) {
7679 LuminanceMap1(row, col) = row*Height + col;
7682 result << LuminanceMap1.transpose();
7687 <div title="Matrix::要素アクセス・部分行列" modifier="Psychlops_DevelopperG" modified="200908190257" created="200712131744" changecount="2">
7688 <pre>[[6.3.2 行列の要素の操作]]</pre>
7690 <div title="Mouse::ButtonState" modifier="Psychlops_Admin" created="200708240330" changecount="1">
7691 <pre>| ~KeyState | pressed | キーが押し続けられている |
7692 |~| pushed | 判定が行われるまでにキーが押された |
7693 |~| released | 判定が行われるまでにキーが離された |</pre>
7695 <div title="Mouse::hide()" modifier="PsychlopsAdmin" created="200709171527" changecount="1">
7696 <pre>マウスカーソルの表示切り替え。
7697 [[Mouse::show()]]命令と対になっている命令です。
7698 |!Mouse::hide()|hide()|マウスカーソルを非表示にする|
7700 コード例は[[例6: マウスのデモ]]参照</pre>
7702 <div title="Mouse::show()" modifier="PsychlopsAdmin" created="200709171527" changecount="2">
7703 <pre>マウスカーソルの表示切り替え。
7704 [[Mouse::hide()]]命令と対になっている命令です。
7705 |!Mouse::show()|show()|マウスカーソルを表示する|
7707 コード例は[[例6: マウスのデモ]]参照</pre>
7709 <div title="PixelComponentsCnt" modifier="Psychlops_Admin" modified="200709041934" created="200709032052" changecount="5">
7710 <pre>PixelComponentsCntの指定方法
7712 |RGB|色の三原色に基づいた赤・緑・青で構成された色|
7713 |RGBA|RGBに透明度が追加された色|
7715 詳しくは[[2.2.5 描画する図形の色を指定する-Colorクラス-]]を参照</pre>
7717 <div title="PluginManager" modifier="PsychlopsAdmin" created="200707220245" changecount="1">
7718 <pre><<plugins>></pre>
7720 <div title="Point()の宣言" modifier="Psychlops_Admin" modified="200709140117" created="200709132353" changecount="3">
7722 [[Psychlops::Point]]型の命令を使用する前に必ず宣言が必要です。
7723 宣言のみの場合、[[Point::set()]]で点の詳細情報を設定しますが
7726 |!Pointの宣言|Point()|[[Psychlops::Point]]型の宣言を行います|
7727 |~|Point(double x, double y)|[[Psychlops::Point]]型の宣言を行い座標(x,y)に値を指定します|
7728 |~|~|double x:座標xを指定|
7729 |~|~|double y:座標yを指定|
7732 座標(100 × 100)に赤の点を描画します。
7735 #include <psychlops.h>
7736 using namespace Psychlops;
7738 Psychlops::Point point1;
7739 void psychlops_main() {
7741 Canvas sampleA(Canvas::fullscreen);
7742 point1.set(100,100);
7743 sampleA.pix(point1,Color::red);
7745 while(!Input::get(Keyboard::spc));
7749 <div title="Point::centering()" modifier="YourName" modified="200803100601" created="200709132303" changecount="4">
7752 |!Point::centering()|centering()|座標(x,y)の点を画面の中心に移動します|
7753 |~|centering(Psychlops::Rectangle rect)|rectの中心に点を移動します|
7756 コード例は[[2.2.1節|2.2.1 画面上に点を描画する-Pointクラス1-]]参照</pre>
7758 <div title="Point::get()" modifier="Psychlops_Admin" modified="200709142232" created="200709140110" changecount="4">
7761 |!Point::get()|getX()|対象の点のX座標の値を取得します|
7762 |~|getY()|対象の点のY座標の値を取得します|
7767 #include <psychlops.h>
7768 using namespace Psychlops;
7770 Psychlops::Point point1(100,100);
7773 void psychlops_main() {
7775 Canvas sampleA(Canvas::fullscreen);
7777 sampleA.pix(point1,Color::red);
7783 sampleA.msg("getX:",50,200);
7784 sampleA.var(d1,200,200);//getX
7785 sampleA.msg("getY:",50,250);
7786 sampleA.var(d2,200,250);//getY
7789 while(!Input::get(Keyboard::spc));
7793 <div title="Point::set()" modifier="YourName" modified="200803100556" created="200709132300" changecount="6">
7794 <pre>座標(x,y),ないし座標X,Yを指定し点を描画します。
7795 Point::set()を使用し座標を取得することでソースの簡略化がなされ
7798 |!Point::set()|set(double x, double y)|座標(x,y)の値を設定します|
7801 |~|setX(double val)|水平座標の値を設定します|
7802 |~|~|double val :水平座標値|
7803 |~|setY(double val)|垂直座標の値を設定します|
7804 |~|~|double val :垂直座標値|
7806 コード例は[[2.2.1節|2.2.1 画面上に点を描画する-Pointクラス1-]]参照</pre>
7808 <div title="Point::shift()" modifier="YourName" modified="200803100557" created="200709132303" changecount="3">
7810 元の座標(x,y)を基点に入力値(h,v)だけ移動します。
7812 |!Point::shift()|shift(double h, double v)|座標(X,Y)を(h,v)だけ移動します|
7813 |~|~|double h:右水平方向の移動量|
7814 |~|~|double v:下垂直方向の移動量|
7816 コード例は[[2.2.1節|2.2.1 画面上に点を描画する-Pointクラス1-]]参照</pre>
7818 <div title="Point型に関して" modifier="Kazushi Maruya" created="200712131720" changecount="1">
7819 <pre>* [[Point型の変数の座標を設定したい|Point::set()]]
7820 * [[Point型の変数を画面中央やRectangle型の変数の中央に移動したい|Point::centering()]]
7821 * [[Point型の変数の座標を移動させたい|Point::shift()]]
7822 * [[Point型の現在の座標を取得したい|Point::get()]]</pre>
7824 <div title="Psychlops::Canvas" modifier="Psychlops_DevelopperG" modified="200910080816" created="200707220311" changecount="40">
7827 **[[Canvas::clear()]]
7828 **[[Canvas::flip()]]
7829 **[[Canvas::flipAfter()|Canvas::flip()]]
7830 **[[Canvas::flipAndWait()|Canvas::flip()]]
7831 **[[Canvas::getXXXX()]]
7835 **[[Canvas::line()]]
7836 **[[Canvas::rect()]]
7837 **[[Canvas::oval()]]
7839 **[[Canvas::copy()]]
7840 **[[Canvas::setGammaValue()]]
7841 **[[Canvas::setGammaTable()]]
7842 **[[Canvas::setClearColor()]]
7843 **[[Canvas::progressbar()]]
7844 **[[Canvas::watchFPS()]]
7845 **[[Canvas::showFPS()]]
7846 **[[Canvas::lastFailedFrames()]]
7848 **TargetSurface</pre>
7850 <div title="Psychlops::Clock" modifier="Psychlops_Admin" modified="200709142035" created="200709142034" changecount="2">
7852 **[[Clock::update()]]
7853 **[[Clock::at_msec()]]</pre>
7855 <div title="Psychlops::Color" modifier="YourName" modified="200802131229" created="200708212259" changecount="17">
7860 ** [[Color::setGammaValue()]]
7861 ** [[Color::strict_match]]
7862 ** [[Color::XXXX]]</pre>
7864 <div title="Psychlops::Image" modifier="YourName" modified="200803050504" created="200709032052" changecount="15">
7868 **[[Image::clear()]]
7874 **[[Image::quicken()|3.1.4 発展的な内容]]
7875 **[[Image::centering()]]
7876 **[[Image::shift()]]
7877 **[[Image::getXXXX()]]
7881 **[[Image::to()]]</pre>
7883 <div title="Psychlops::Input" modifier="Kazushi Maruya" modified="200712131738" created="200709142237" changecount="6">
7886 **[[Input::refresh()]]</pre>
7888 <div title="Psychlops::Keyboad" modifier="PsychlopsAdmin" modified="200709170639" created="200709142246" changecount="2">
7890 **[[Keyboad::get()]]
7893 <div title="Psychlops::Math" modifier="Kazushi Maruya" modified="200712131628" created="200712100137" changecount="4">
7895 ** [[Psychlops::random]]
7896 ** [[Math::shuffle]]
7897 ** [[Math::mod]]</pre>
7899 <div title="Psychlops::Matrix" modifier="Psychlops_DevelopperG" modified="200910070546" created="200712100024" changecount="9">
7901 **[[Matrix()の宣言|6.3.1 行列の宣言]]
7903 **[[Matrix::要素アクセス・部分行列|6.3.2 行列の要素の操作]]
7904 **[[Matrix::四則演算|6.3.4 行列の加減乗除]]
7905 **[[Matrix::slide()]]
7906 **[[Matrix::transpose()]]
7907 **[[Matrix::rot90()]]
7908 **[[Matrix::catRow()]]
7909 **[[Matrix::reshape()]]
7910 **[[Matrix::getXXXX()]]
7911 **[[Matrix::min() / max()]]
7912 **[[Matrix::mesh()|6.3.6 メッシュグリッドの作成]]</pre>
7914 <div title="Psychlops::Mouse" modifier="PsychlopsAdmin" modified="200709170736" created="200709142247" changecount="3">
7917 **[[Mouse::show()]]</pre>
7919 <div title="Psychlops::Point" modifier="Psychlops_Admin" modified="200709140121" created="200709122119" changecount="8">
7923 **[[Point::centering()]]
7924 **[[Point::shift()]]
7925 **[[Point::get()]]</pre>
7927 <div title="Psychlops::Rectangle" modifier="Psychlops_DevelopperG" modified="200910080843" created="200708230127" changecount="15">
7930 **[[Rectangle::set()]]
7931 **[[Rectangle::centering()]]
7932 **[[Rectangle::shift()]]
7933 **[[Rectangle::draw()]]
7934 **[[Rectangle::resize()]]
7935 **[[Rectangle::setColor()]]
7936 **[[Rectangle::getXXXX()]]
7937 **[[Rectangle::include()]]
7938 **[[Rectangle::dup()]]</pre>
7940 <div title="Psychlops::random" modifier="YourName" modified="200802232256" created="200712100149" changecount="11">
7941 <pre>乱数を返します。標準の擬似乱数生成アルゴリズムは[[dSFMT|http://www.math.sci.hiroshima-u.ac.jp/%7Em-mat/MT/SFMT/index-jp.html]]を利用しています。
7942 |!Psychlops::random()|random()|[0.0, 1.0)の範囲の一様乱数を生成します。|
7943 |~|random(double max)|[0.0, max)の範囲の一様乱数を生成します。|
7944 |~|random(int levels)|[0, levels)の範囲の離散的一様乱数を生成します。|
7945 |~|random(double min, double max)|[min, max)の値をランダムに返します。|
7946 |~|~|double min: ランダムで返す値の最小値を指定|
7947 |~|~|double max: ランダムで返す値の最大値を指定|
7949 引数が実数(double)か整数(int)かで動作が異なりますので、実数で返したい場合は最大値が整数でも必ず1.0などのように小数点をつけて指定してください。整数変数を指定する場合は(double)でキャストしてください。
7951 random(5) ← [0,1,2,3,4] のいずれかの数がint型で返る
7952 random(5.0) ← [0, 5)の間の浮動小数点値が返る
7955 random(i) ← [0,1,2,3,4] のいずれかの数がint型で返る
7956 random((double)i) ← [0, 5)の間の浮動小数点値が返る
7960 以下の例では100 ms毎にrandom命令を使用して作成したランダム配列を元に10000個のドットを画面に表示します。
7963 #include <psychlops.h>
7964 using namespace Psychlops;
7966 void psychlops_main() {
7967 Canvas display(Canvas::fullscreen);
7968 double wid = 200, hei = 200;
7969 int refresh=display.getRefreshRate()/10;
7970 const int DOTNUM = 1000;
7971 double x[DOTNUM], y[DOTNUM];
7974 while(!Input::get(Keyboard::esc)) {
7976 for(int i=0; i<DOTNUM; i++) {
7977 x[i] = random(wid)+display.getHcenter()-wid*0.5;
7978 y[i] = random(hei)+display.getVcenter()-hei*0.5;
7980 display.pix(DOTNUM, x, y, Color::white);
7981 display.flip(refresh);
7986 <div title="Psychlopsについて開発者に質問するには?" modifier="Kazushi Maruya" modified="200712180142" created="200712131755" changecount="2">
7987 <pre>直接の質問には(開発者と面識がある場合を除いて)お答えしておりません。
7988 Psychlops Authors Mailingリストに入っていただくのが便利です。
7989 このメーリングリストはGoogleグループサービスを利用しております。
7990 このメーリングリストに参加なされたい場合は、[[ここ|http://groups.google.com/group/psychlops/post?sendowner=1]]から申請してください。</pre>
7992 <div title="Psychlopsを既存・自作のC/C++ライブラリと併用することができますか?" modifier="Kazushi Maruya" created="200712180146" changecount="1">
7994 OpenGL命令に関しては、Psychlopsライブラリのインクルード時に既にOpenGLライブラリのインクルードが行われていますので、そのまま書いていただいてコンパイル・実行可能となっています。
7995 自作ライブラリについては、関数名の衝突が無ければそのまま書いていただいて結構です。関数名が衝突する場合は、スコープ演算子などを用いて衝突を回避すれば利用が可能です。</pre>
7997 <div title="Psychlopsプログラムのデフォルトテンプレート" modifier="YourName" created="200802191428" changecount="1">
7999 void RectLuminance() {
8000 double rect_size = 100;
8001 double rect_lum = 0.5;
8002 double bg_lum = 0.2;
8004 Psychlops::Rectangle rect(rect_size,rect_size);
8008 Independent << rect_size | "Rect Size" | 1< rng< 500 | 10.0 | 2.0 ;
8009 Independent << rect_lum | "Rect Luminance" | 0.0<=rng<=1.0 | 0.1 | 0.05;
8010 Independent << bg_lum | "BG Luminance" | 0.0<=rng<=1.0 | 0.1 | 0.05;
8012 while(!Input::get(Keyboard::esc)) {
8013 Display::clear(Color(bg_lum));
8014 rect.resize(rect_size,rect_size);
8015 rect.display(rect_lum);
8020 void psychlops_main() {
8021 Canvas display(Canvas::fullscreen);
8024 p.setDesign(Procedure::DEMO);
8025 p.setProcedure(RectLuminance);
8030 <div title="Rectangle::centering()" modifier="Psychlops_DevelopperG" modified="200910080843" created="200709131913" changecount="8">
8031 <pre>[[Psychlops::Rectangle]]型の図形を移動します。
8032 [img[image/rect_centering().png]]
8033 座標を指定する場合、その座標に図形の中心が設定されます。
8034 |!Rectangle::centering()|centering()|[[Psychlops::Rectangle]] を画面中心に移動します|
8035 |~|centering(double x, double y)|[[Psychlops::Rectangle]] を指定した座標(x,y)に移動します|
8036 |~|~|double x:移動する図形のx座標|
8037 |~|~|double y:移動する図形のy座標|
8038 |~|centering([[Psychlops::Point]] po)|[[Psychlops::Rectangle]]を指定したpoint座標(x,y)に移動します|
8039 |~|~|[[Psychlops::Point]] po:移動する図形の(x,y)座標|
8040 |~|centering(Rectangle rect)|[[Psychlops::Rectangle]] を指定した[[Psychlops::Rectangle]] rectの中心座標(x,y)に移動します(2つのRectangleを中央ぞろえします)|
8041 |~|~|[[Psychlops::Rectangle]] rect :センタリングの中心座標を与える[[Psychlops::Rectangle]]|
8043 コード例は[[2.2.6節|2.2.6 画面上に縞を描画する-Rectangleクラス2-]]参照</pre>
8045 <div title="Rectangle::draw()" modifier="YourName" modified="200803100553" created="200709131911" changecount="7">
8046 <pre>[[Psychlops::Rectangle]]型の図形の描画命令です。
8047 |!Rectangle::draw()|draw()|対象の[[Psychlops::Rectangle]]型を描画します|
8048 |~|draw([[Psychlops::Color]] col)|対象の[[Psychlops::Rectangle]]型を[[Psychlops::Color]] colで塗りつぶし描画します|
8049 |~|~|[[Psychlops::Color]] col:描画する四角形の描画色|
8051 コード例は[[2.2.6節|2.2.6 画面上に縞を描画する-Rectangleクラス2-]]参照</pre>
8053 <div title="Rectangle::dup()" modifier="YourName" modified="200803100555" created="200802281432" changecount="2">
8054 <pre>[[Psychlops::Rectangle]]型を複製した一時オブジェクトを作成します。
8056 |!Rectangle::dup()|矩形領域の複製を行います|
8059 Psychlops::Rectangle rect1(10,10), rect2(10,10);
8060 rect1.centering(); // rect1の中心位置は変わっている
8061 rect2.dup().centering() // rect2の中心位置は変わっていないが、複製がセンタリングされている
8064 <div title="Rectangle::getXXXX()" modifier="Psychlops_Admin" modified="200709132236" created="200709131915" changecount="12">
8065 <pre>[[Psychlops::Rectangle]]型の情報を、Rectangle::getXXXX()命令を使って取得します(XXXXにはそれぞれの情報に対応した名前が入ります)。
8066 |!Rectangle::getXXXX()|getCenter()|四角形の中心座標(x,y)を取得します|
8067 |~|getHcenter()|横方向の中心座標(x)を取得します|
8068 |~|getVcenter()|縦方向の中心座標(y)を取得します|
8069 |~|getHeight()|四角形の高さ(ピクセル)を取得します|
8070 |~|getWidth()|四角形の幅(ピクセル)を取得します|
8071 |~|getTop()|四角形上辺のX座標(x)を取得します|
8072 |~|getLeft()|四角形左辺のX座標(x)を取得します|
8073 |~|getBottom()|四角形下辺のY座標(y)を取得します|
8074 |~|getRight()|四角形右辺のY座標(y)を取得します|
8075 [img[image/RectangleGet.png]]
8077 !!Rectangle::getXXXX()の書き方
8078 [[Psychlops::Rectangle]]型の情報を取得し画面表示を行います。
8079 画面表示は[[Canvas::var()]]命令と[[Canvas::msg()]]命令を使用します。
8082 #include <psychlops.h>
8083 using namespace Psychlops;
8085 double d1,d2,d3,d4,d5,d6,d7,d8,x,y;
8086 Psychlops::Point point1;
8087 Psychlops::Rectangle rect1;
8089 void psychlops_main() {
8091 Canvas sampleA(Canvas::fullscreen);
8094 sampleA.rect(rect1,Color::cyan);
8096 point1=rect1.getCenter();
8097 d1=rect1.getHcenter();
8098 d2=rect1.getVcenter();
8099 d3=rect1.getHeight();
8100 d4=rect1.getWidth();
8103 d7=rect1.getBottom();
8104 d8=rect1.getRight();
8109 sampleA.msg("getCenter_x:",50,200);
8110 sampleA.var(x,200,200);//getCenter:X
8111 sampleA.msg("getCenter_y:",50,250);
8112 sampleA.var(y,200,250);//getCenter:Y
8113 sampleA.msg("getHcenter:",50,300);
8114 sampleA.var(d1,200,300);//getHcenter
8115 sampleA.msg("getVcenter:",50,350);
8116 sampleA.var(d2,200,350);//getVcenter
8117 sampleA.msg("gettHeight:",50,400);
8118 sampleA.var(d3,200,400);//getHeight
8119 sampleA.msg("getWidth:",50,450);
8120 sampleA.var(d4,200,450);//getWidth
8122 sampleA.msg("getTop:",250,200);
8123 sampleA.var(d5,400,200);//getTop
8124 sampleA.msg("getLeft:",250,250);
8125 sampleA.var(d6,400,250);//getLeft
8126 sampleA.msg("getBottom:",250,300);
8127 sampleA.var(d7,400,300);//getBottom
8128 sampleA.msg("getRight:",250,350);
8129 sampleA.var(d8,400,350);//getRight
8132 while(!Input::get(Keyboard::spc));
8136 <div title="Rectangle::include()" modifier="YourName" modified="200803100554" created="200712100008" changecount="2">
8137 <pre>[[Psychlops::Rectangle]]型の座標の中にに他のRectangleや点が含まれるかどうかを判定します。
8139 |!Rectangle::include()|include(double x, double y)|対象の座標を指定|
8140 |~|include(Point point)|[[Psychlops::Point]] point:判定対象の点|
8141 |~|include(Rectangle rect)|[[Psychlops::Rectangle]] rect:判定対象の四角形|
8144 !!Rectangle::include()の書き方
8146 [[例6: マウスのデモ]]をRectangle::include()を使って判定してみます。
8147 実行すると画面左上に黒い四角形が、中央に白色の四角形が表示されます。
8149 以下の2行でマウスのポインタ位置が白色の四角形の上にあるかどうかを判定しています。
8150 もし、マウスポインタが四角形の上にあって、マウスの左ボタンが押され続けていれば、変数draggingに1が代入されます。
8152 if(Input::get(Mouse::left, Mouse::pressed) && rect.include(Mouse::x, Mouse::y)) dragging = 1;
8153 if(Input::get(Mouse::left, Mouse::released)) dragging = 0;
8155 マウスで白色の四角形をドラッグアンドドロップして、黒い四角形に重ねてみましょう。
8158 #include <psychlops.h>
8159 using namespace Psychlops;
8162 void psychlops_main() {
8164 Canvas display(Canvas::fullscreen);
8168 Psychlops::Rectangle rect(rectsize,rectsize);
8170 Psychlops::Rectangle drop_here(rectsize*2,rectsize*2);
8171 drop_here.shift(10,10);
8172 Color rectcol, dropareacol;
8176 while(!Input::get(Keyboard::spc)){
8177 display.clear(Color::gray);
8180 if(Input::get(Mouse::left, Mouse::pressed) && rect.include(Mouse::x,
8181 Mouse::y)) dragging = 1;
8182 if(Input::get(Mouse::left, Mouse::released)) dragging = 0;
8184 rect.centering(Mouse::x, Mouse::y);
8190 if(drop_here.include(rect)) {
8191 dropareacol.set(1.0,0.0,0.0);
8193 dropareacol.set(0.0,0.0,0.0);
8196 drop_here.draw(dropareacol);
8197 display.msg("Drop a rect here!", drop_here.getLeft()+10,
8198 drop_here.getTop()+10);
8207 <div title="Rectangle::resize()" modifier="YourName" modified="200803100553" created="200709132255" changecount="2">
8208 <pre>[[Psychlops::Rectangle]]型のサイズの再設定を行います。
8210 |!Rectangle::resize()|resize(double w, double h)|対象の[[Psychlops::Rectangle]]型のサイズをw×hに変更します|
8211 |~|~|double w:変更後の横幅|
8212 |~|~|double h:変更後の高さ|
8214 !!Rectangle::resize()の書き方
8215 200 × 200の四角形を50 × 100に変更します。
8218 #include <psychlops.h>
8219 using namespace Psychlops;
8221 Psychlops::Rectangle rect1(200,200);
8223 void psychlops_main() {
8225 Canvas sampleA(Canvas::fullscreen);
8226 rect1.setColor(Color::green);
8227 rect1.resize(50,100);
8232 while(!Input::get(Keyboard::spc));
8236 <div title="Rectangle::set()" modifier="Psychlops_DevelopperG" created="200910080842" changecount="1">
8237 <pre>|!Rectangle::set()|set(double width, double height)|>|!width×heightの四角形を設定します|
8238 |~|~|double width|描画する四角形の横幅|
8239 |~|~|double height|描画する四角形の縦幅|
8240 |~|set(double l, double t, double r, double b)|>|!左上座標(l,t)から右下座標(r,b)までの四角形を設定します|
8241 |~|~|double l|描画する四角形の左上座標x1を指定|
8242 |~|~|double t|描画する四角形の左上座標y1を指定|
8243 |~|~|double r|描画する四角形の右下座標x2を指定|
8244 |~|~|double b|描画する四角形の右下座標y2を指定|
8245 |~|set([[Psychlops::Point]] point1, [[Psychlops::Point]] point2)|>|!左上Point座標(x1,x1)から右下Point座標(X2,Y2)までの四角形を設定します|
8246 |~|~|[[Psychlops::Point]] point1|左上座標(x1,y1)を指定|
8247 |~|~|[[Psychlops::Point]] point2|右下座標(x2,y2)を指定|</pre>
8249 <div title="Rectangle::setColor()" modifier="YourName" modified="200803100554" created="200709132244" changecount="5">
8250 <pre>[[Psychlops::Rectangle]]型の色設定を行います。
8251 この命令を使用する場合は、[[Rectangle::draw()]]命令を使用してください。
8253 |!Rectangle::setColor()|setColor([[Psychlops::Color]] col)|対象の[[Psychlops::Rectangle]]型を[[Psychlops::Color]] colで塗りつぶします|
8254 |~|~|[[Psychlops::Color]] col:四角形の描画色|
8256 !!Rectangle::setColor()の書き方
8259 #include <psychlops.h>
8260 using namespace Psychlops;
8262 Psychlops::Rectangle rect1;
8264 void psychlops_main() {
8266 Canvas sampleA(Canvas::fullscreen);
8268 rect1.setColor(Color::red);
8273 while(!Input::get(Keyboard::spc));
8277 <div title="Rectangle::shift()" modifier="YourName" modified="200803100552" created="200709131914" changecount="3">
8278 <pre>[[Psychlops::Rectangle]]型の図形を移動します。
8279 元の座標(x,y)を基点に入力値(h,v)だけ移動します。
8280 |!Rectangle::shift()|shift(double h, double v)|座標(X,Y)を(h,v)だけ移動します|
8281 |~|~|double h:右水平方向の移動量|
8282 |~|~|double v:下垂直方向の移動量|
8283 [img[image/rect_shift().png]]
8285 コード例は[[2.2.6節|2.2.6 画面上に縞を描画する-Rectangleクラス2-]]参照</pre>
8287 <div title="Rectangleの宣言" modifier="Psychlops_DevelopperG" modified="200910080842" created="200709132233" changecount="1">
8288 <pre>RectangleはCanvas上に四角形の領域を設定する命令です。
8289 [[Psychlops::Rectangle]]型の命令を使用する前に必ず宣言が必要です。
8290 宣言のみの場合、[[Rectangle::set()|2.2.3 画面上に四角形を描画する-Rectangleクラス1-]]で四角形の詳細情報を設定しますが
8293 |!Rectangleの宣言|Rectangle()|[[Psychlops::Rectangle]]型の宣言を行います|
8294 |~|Rectangle(double width, double height)|[[Psychlops::Rectangle]]型の宣言を行いwidth×heightの四角形を設定します|
8295 |~|~|double width:描画する四角形の横幅|
8296 |~|~|double height:描画する四角形の縦幅|
8297 |~|Rectangle(double l, double t, double r, double b)|[[Psychlops::Rectangle]]型の宣言を行い左上座標(l,t)から右下座標(r,b)までの四角形を設定します|
8298 |~|~|double l:描画する四角形の左上座標x1を指定|
8299 |~|~|double t:描画する四角形の左上座標y1を指定|
8300 |~|~|double r:描画する四角形の右下座標x2を指定|
8301 |~|~|double b:描画する四角形の右下座標y2を指定|
8303 !!Rectangle()の宣言の書き方
8304 100 × 100の四角形を描画します。
8307 #include <psychlops.h>
8308 using namespace Psychlops;
8310 Psychlops::Rectangle rect1(100,100);
8312 void psychlops_main() {
8314 Canvas sampleA(Canvas::fullscreen);
8315 sampleA.rect(rect1,Color::cyan);
8318 while(!Input::get(Keyboard::spc));
8322 <div title="Rectangle型に関して" modifier="Kazushi Maruya" modified="200712131648" created="200712131647" changecount="2">
8323 <pre>* [[Rectangle型の変数を画面中央に持って行きたい|Rectangle::centering()]]
8324 * [[Rectangle型の変数を任意の位置に持って行きたい|Rectangle::shift()]]
8325 * [[Rectangle型の領域を塗りつぶしたい|Rectangle::draw()]]
8326 * [[Rectangle型の変数の画面上のサイズを再設定したい|Rectangle::resize()]]
8327 * [[Rectangle型の変数のメンバを取得したい|Rectangle::getXXXX()]]
8328 * [[Rectanble型の変数に対して包含関係を判定したい|Reectangle::include()]]</pre>
8330 <div title="SideBarTabs" modifier="PsychlopsAdmin" created="200709170655" changecount="1">
8331 <pre><<tabs TabTimeline 'タグ' 'タグ一覧' TabTags '詳細' '詳細' TabMore>></pre>
8333 <div title="SiteSubtitle" modifier="YourName" modified="200908100431" created="200707220301" changecount="9">
8334 <pre>Version 1.3.5 (2009/September)
8335 Generated by TiddlyWiki
8336 Authored by Psychlops Admin Group</pre>
8338 <div title="SiteTitle" modifier="YourName" modified="200908100430" created="200707220259" changecount="19">
8339 <pre>Psychlops Manual
8342 <div title="StyleSheet" modifier="Psychlops_DevelopperG" modified="200910020133" created="200708280415" changecount="169">
8347 background-color: #f0f8f0;
8350 font-family: arial,helvetica, centurygothic;
8355 background-color: #e0f8e0;
8365 font-family: arial,helvetica, centurygothic;
8367 background: #ffffcc;
8368 border: 1px dotted #54c354;
8391 padding: 2.0em 0em 0.7em 1em;
8395 padding: 2.0em 0em 0.7em 1em;
8417 background: #8bd78b;
8425 background: #8bd78b;
8434 background: #8bd78b;
8442 border: 1px solid #6a6;
8445 font-family: Helvetica, Arial;
8468 <div title="TargetSurface" modifier="YourName" modified="200803140518" created="200708212223" changecount="4">
8471 |!BACK|バックグラウンドのCanvas|
8473 ※省略時にはBACKが指定される</pre>
8475 <div title="TiddlyWiki" modifier="YourName" created="200707220242" changecount="1">
8476 <pre>Type the text for 'Psychlops Manual'</pre>
8478 <div title="Tips" modifier="Psychlops_DevelopperG" modified="200912010831" created="200708240236" changecount="41">
8479 <pre>!! Psychlopsの基本
8480 * [[Tips: OpenGL 1.4に対応した一般向けビデオチップ]]
8481 * [[Tips: OS環境による関数名の衝突について]]
8483 * [[Tips: Canvasとリフレッシュレートの詳細]]
8484 * [[Tips: Canvas::flip()とコマ落ち]]
8485 * [[Tips: CanvasクラスとDisplayクラス]]
8486 * [[Tips: Canvasを宣言しないと動作しない命令群]]
8487 * [[Tips: Image使用上の注意]]
8488 * [[Tips: 時間精度が必要なプログラムを実行するとき]]
8489 * [[Tips: プログラムが暴走したとき]]
8490 * [[Tips: Xcodeで実行時エラーが起きるとフリーズする]]
8491 * [[Tips: intどうしの比率の計算がおかしい]]
8493 * [[Tips: Psychlopsにおけるキーボードマウスの入出力検出ルーチン]]
8494 * [[Tips: トリガーとアナログ入力]]
8497 * [[Tips: Shapeクラスの概要]]
8498 * [[Tips: Strokeクラスの概要]]
8499 * [[Tips: ランダムドットステレオグラムの描画コード]]
8500 * [[Tips: Rectangleの配列を用いたドットパターンの描画例]]
8501 * [[Tips: アルファ混色の活用]]
8502 * [[Tips: プログラムを複数のファイルに分割する]]
8503 * [[Tips: QUESTの実装]]
8504 * [[Tips: 自分の機器の描画性能を確かめる]]
8505 * [[Tips: 独自のクラスを作る]]
8507 !! 他ライブラリの命令を使用した機能の補完
8509 * [[Tips: OpenGL命令の混ぜ方]]
8510 * [[Tips: 画像のフィルタリング処理]]
8511 * [[Tips: トリガーとアナログ入力2-外部機器を利用した例-]]
8512 * [[Tips: Bits++の利用]]</pre>
8514 <div title="Tips: Bits++の利用" modifier="YourName" modified="200803050448" created="200802201839" changecount="5">
8515 <pre>Cambridge Research Systems社Bits++は14bit DACを備えるアナログ画像信号出力用機器で、DVIとUSBにつなぐだけで機能し、特殊な関数などを使う必要はありません。従って、Psychlopsの命令系と問題なく結合することが可能です^^*1^^。 Bits++の動作モード別に説明します。
8517 公式サイト: [[http://www.crsltd.com/catalog/bits++/modes.html]]
8519 なお、Bits++を利用する場合、Psychlops側の一切の[[ガンマ補正]]機能は以下の命令で無効化し、LUTテーブルを管理するなどの方法で自力で補正をかけてください。
8521 Color::setGammaValue(1,1,1);
8524 *1 現時点(2008/3/1現在)でBits++で扱える3つのモード(basic/mono/color)のうちで、実際に動作することを確認しているのはmonoモード(下記参照)だけですが、他の2つのモードについても、仕様上は問題なく動作をすると考えられます。この2モードでプログラムコードを実装された方がいらっしゃいましたら、ぜひPsychlops開発フォーラム(http://psychlops.l.u-tokyo.ac.jp/?forum)までご一報ください。
8528 DVI出力のうち、赤8bit+緑6bitを合成して14bitの輝度値として解釈します。 → 全色14bitで、連動します。青8bitについては別に指定したパレットオーバーレイとして動作すると記述されていますが、詳細についてはまだわかりません。使用法としては、Imageであれば、RGB, BYTEで確保した上で値を16bit整数に変換してビットシフトでRとGに書き込む方法があります(確認済)。
8530 Color luminance_to_Color(double luminance) {
8531 unsigned short bits_mono = (unsigned short)(65535 * luminance);
8532 unsigned char upper = (unsigned char)(bits_mono >> 8), (unsigned char)(lower = bits_mono && 255) ;
8533 return Color(upper/255.0, lower/255.0, 0);
8536 Display::pix(1,1,luminance_to_Color(0.567));
8540 単純にDVI出力をトラップし、RGB各色をLUTに従って変換して14bitアナログ出力します。 → 各色14 bit中8 bitの色を使うことができます。 Bits++では、(0,0)から横に数ピクセルが特定の値であるとき、 y軸が0のラインのデジタル値をLUT配列として解釈する、というシステムになっています。従って、パレットとなるImageを(0,0)起点で描画すればLUTを書き換えることができます。パレットは配列アクセス演算子 [] を備えるクラスでくるめば扱いやすいかもしれません。このモードを使用するとLUTアニメーションが使えます。
8543 DVI出力の2ピクセル分の値を合成して各色16bitの値を得ます(実効14bit)。従って、解像度は半分になります。
8546 <div title="Tips: Canvas::flip()とコマ落ち" modifier="YourName" modified="200802201734" created="200709171713" changecount="1">
8547 <pre>実験やデモには、各フレーム(コマ)がCanvas.flip(duration)で指定した正しい時間にに出ている必要があります。しかし、実際には正しい時間にコマが表示されないことがあります。この状態をコマ落ちと呼びます。
8549 フレームが正しく表示されない原因は、フレームを提示する時間内に計算が終わらないためです。
8550 Psychlopsでは指定された時間よりも描画の計算時間が大きくなったときは描画終了時以降でもっとも近い垂直同期にあわせて描画内容を更新します。描画途中のちぎれた画像が表示される可能性はありませんが、その代わり指定された時間に表示されない可能性があります。
8551 現在のコンピュータは同時に多数のソフトが起動しており、その計算に処理時間をとられてフレーム落ちする場合があります。これを回避するには、[[Tips: 時間精度が必要なプログラムを実行するとき]]をご覧ください。</pre>
8553 <div title="Tips: Canvasとリフレッシュレートの詳細" modifier="Psychlops_Admin" created="200708240232" changecount="1">
8554 <pre>CRTは機器によってリフレッシュレートが小数点以下の桁でわずかに異なります(60Hzと表示されていても、厳密には60.2Hzなど小数点以下の桁があります)。
8555 Psychlopsでは、初期化時には小数点以下の桁は無視して確保しますが、正確なタイミングでflipするには、ディスプレイ装置の設定等で小数点以下の桁をあらかじめ調べて、正確な値を指定することをお勧めします。</pre>
8557 <div title="Tips: Canvasを宣言しないと動作しない命令群" modifier="YourName" modified="200908100434" created="200908100433" changecount="4">
8558 <pre>下記命令群はCanvasの実体が確保されていることを前提に動作します。
8560 !!! Display::に属するすべての命令
8561 事前に確保されたCanvas実体への抜け道命令群です。
8562 事前にCanvas実体を確保することが必要です。
8564 !!! Figure.draw() / Shape.draw(Color)
8565 Image.draw()やRectangle.draw(Color)はDisplay::に属する命令と同様に、事前に確保されたCanvas実体を探索して実行する抜け道命令です。
8566 事前にCanvas実体を確保することが必要です。
8568 !!! Color::setGammaValue()
8569 この命令はハードウェアガンマを設定するためにCanvas実体を探索します。
8570 Canvas確保前にこの命令を実行しようとすると失敗します。
8573 デモ用の変数操作コンソールをCanvas上に作ろうとするためにCanvasを探索します。
8574 Canvas確保前にこの命令を実行しようとすると失敗します。
8576 !!! ExperimentalMethods::Demo
8577 Independentと同様ですが、initialize時にCanvasが確保されている必要があります
8579 !!! Widgets::に属するクラスの大半
8580 自身をCanvas上に自動登録しようとするためにCanvasを探索します。
8581 Canvas確保前にこの命令を実行しようとすると失敗します。</pre>
8583 <div title="Tips: CanvasクラスとDisplayクラス" modifier="PsychlopsAdmin" created="200709171711" changecount="1">
8584 <pre>Canvasクラスと同様に使えるクラスとして、Displayクラスがあります。
8586 Cの変数は、その変数が定義されている関数やクラス以外からは操作することができません。
8588 void psychlops_main() {
8589 Canvas display( Canvas::fullscreen );
8592 display.flip(); // エラー、displayはこの関数からは操作できない
8596 Displayクラスは、最後に宣言したCanvasクラスの別名として機能します。このクラスはどこからでもアクセスすることができますが、Canvasの宣言の代わりはできません。
8598 void psychlops_main() {
8599 Canvas display( Canvas::fullscreen );
8602 Display::flip(); // OK
8607 <div title="Tips: Image使用上の注意" modifier="YourName" modified="200803050504" created="200709040623" changecount="4">
8608 <pre>また、この転送はグラフィックボードやマザーボードなどの規格やOSの種類によって速度が違いますが、一般にある程度の時間がかかります。この転送はCanvasの描画内容を画面上へ反映させるよりはずっと大きいもので、かつ信号転送量が大きくなればなるほど、時間が必要になります。
8609 2008年現在の平均的なマシン(iMacやメーカー製のWindowsデスクトップマシン)で、100 Hzの描画に間に合う(つまり10ms以内に転送と描画反映が完了する)Imageのサイズはおおよそ300 x 300 pixel程度でしょう。以上のようなことを考えて、Imageを用いたオフスクリーン描画とCanvasに対する直接の描画を上手く使い分ける必要があります。
8610 また、高速のオフスクリーン転送を実行したい場合には[[Image::quicken()|3.1.4 発展的な内容]]命令が有効な場合があります。詳しくは、関数一覧の[[Image::quicken()|3.1.4 発展的な内容]]の項を参照してください。</pre>
8612 <div title="Tips: OS環境による関数名の衝突について" modifier="Psychlops_DevelopperG" modified="200908100455" created="200908100437" changecount="1">
8613 <pre>ほかのライブラリ、特にOS・コンパイラ標準のライブラリにあるクラスとこの特定のクラスが同じ名前を持っているために、名前が衝突してしまってコンパイルができなくなっていることが考えられます。
8614 このような衝突を起こすことがある命令は以下の通りです。
8616 !! windows.hで問題が生じるシンボル
8618 | !Rectangle | Psychlops::Rectangle |
8619 | !Ellipse | Psychlops::Ellipse |
8620 | !round | Math::round |
8621 | !random | Math::random |
8623 !! MacOSXで問題が生じるシンボル
8625 | !Point | Psychlops::Point |
8626 | !round | Math::round |
8627 | !random | Math::random |
8630 <div title="Tips: OpenGL 1.4に対応した一般向けビデオチップ" modifier="YourName" modified="200803141457" created="200709131950" changecount="15">
8631 <pre>OpenGL 1.4仕様は2002年7月24日に発行されており、基本的にはそれ以降に設計されたビデオチップはこれに対応しています。
8632 http://www.opengl.org/documentation/specs/version1.4/glspec14.pdf
8635 |!開発元|!汎用カード|!OpenGL特殊化カード|
8636 |nVidia|GeForce 6000(NV30)系、FX5000(NV40)系 以降|Quadro FX系|
8637 |AMD(旧ATI)|RADEON 8000(R200)以降、X系|FireGL 8000以降、X, T, Z, V系|
8638 |Intel|945チップセット(GMA950)以降(915でも一応の動作確認はあり)| |
8639 |VIA(S3 Graphics)|DeltaChrome(S8)以降| |
8642 ただし2008年3月現在で以下のマシンにおける動作不良を確認しております。
8643 これらのマシンについてはドライバレベルでのOpenGL動作の改変が行われているようです。
8644 現在のところ対応は未定です。また、今後の改良のため、動作不良等がございましたら、ぜひ[[Psychlops WIKI Trouble Report|http://psychlops.l.u-tokyo.ac.jp/?Trouble+Report]]までご報告をお願いいたします。
8646 |!製造会社名|!機種名|!搭載ビデオチップ|!動作不良の内容|!暫定的な回避策|
8647 |SONY|Vaio Type-T VGN-TZ92S|Intel 945GM|垂直同期を待たずに画面の描画が行われてしまう|未発見|
8648 |Apple|Mac Pro MA463J/A |Radeon X1900XT(他ビデオボードのものについては未検証) |一定以下の負荷しかない描画内容が画面に反映されないことがある|各フレームの描画命令の前に大きなサイズ(全画面)のRectangleを1~数枚表示する|
8650 より新しい情報は、 [[Psychlops WIKI 動作環境 |http://psychlops.l.u-tokyo.ac.jp/?Environment]]に随時反映されます。
8651 また、これらの動作改変はOpenGLを用いたソフトウェア全般で起こっている可能性があります。これらの他のアプリケーションの情報についてもよろしければ[[Psychlops WIKI Trouble Report|http://psychlops.l.u-tokyo.ac.jp/?Trouble+Report]]に情報をあげていただきますよう、お願い申し上げます。
8654 <div title="Tips: OpenGL命令の混ぜ方" modifier="YourName" created="200802201824" changecount="1">
8655 <pre>現在Psychlopsの描画命令形はOpenGLのみで行われており、OpenGLの描画命令を混ぜることができます。この際、いくつかの注意が必要です。
8657 * 回転(glRotate)と拡大縮小(glScale)はほとんどのビデオカードで近似値を使うよう実装されており、ピクセル単位での正しい計算ができなくなりジャギやモアレの原因になります。これを了解した上でお使いください。
8658 * glRotateとglScaleはPsychlops命令形の原点座標を中心に行われます。これらの命令を使った上で表示位置を指定する場合は、回転中心からの相対座標に描画した後、glTranslateをご使用ください。
8659 * Image型はquicken()を適用しないと使うことができません。
8661 Imageを回転させるサンプル(モアレが発生していることをお確かめください)
8663 #include <psychlops.h>
8664 using namespace Psychlops; // Initially developed with Psychlops Win32 20070313a
8667 void psychlops_main() {
8669 Canvas display(Canvas::fullscreen);
8672 Image img(100, 100);
8674 for(int i=0; i<100; i++) {
8675 for(int j=0; j<100; j++) {
8676 col.set(0.25+sin(i/10.0)/4 + 0.25+sin(j/10.0)/4);
8681 // External Image File
8684 img.load("sample.png");
8689 double orientation = 0.0;
8692 while(!Input::get(Keyboard::esc)) {
8695 orientation += PI/10;
8698 glTranslated(display.getHcenter(),display.getVcenter(),0);
8699 glRotated(orientation,0,0,1);
8711 <div title="Tips: Psychlopsにおけるキーボードマウスの入出力検出ルーチン" modifier="YourName" modified="200802201727" created="200709150147" changecount="2">
8712 <pre>Psychlopsのキーボード・マウスの入力監視は、主のプログラムとは独立に自動的に一定の間隔(マシンに依存しますが、通常0.1ミリ秒程度)で行われています。
8714 監視ルーチンは各キーについてPushed, Pressed, Releasedのイベントがあったかどうかの表を内部に保持しています。
8716 監視ルーチンではまず入力デバイスから送られてくるPushed, Releasedのイベントを監視し、イベントがあれば当該キーの当該イベントの表に記録します。この記録はInput::refresh()をするまで残ります。次に、PushedされてからReleasedされるまでの間、Pressedであると表に書き込みます。これはrefreshしなくても常時更新されています。
8718 本来はPressedのデータを基本に取得すべきなのですが、近年のコンピュータではUSB化やOSのセキュリティ強化のためにイベント依存のデータしか取得できないようになっており、このような形式になっています。
8720 マウスの座標系に関してはMac OS X、Windowsともに常時取得・設定が可能です。
8722 現在、多くのUSBキーボード・マウスでは、コンピュータに転送する時間精度は1 msより悪いものとなっています。このため、PsychlopsのKeyboardやMouse命令は、精密な反応時間の取得(数 ms以下のオーダーの精度が必要な反応時間取得)には向きません(ただし、この転送の時間ノイズは数多くの試行を平均加算することで除去できる可能性はあります)。ただし、この値は表示装置の更新間隔(フレームレート)よりは十分に短いため、キーボードやマウスの反応を表示に反映する場合には最悪でも1フレーム遅れる程度にとどまります</pre>
8724 <div title="Tips: QUESTの実装" modifier="YourName" modified="200803140630" created="200803100446" changecount="11">
8725 <pre>QUESTは上下法の一種です。何らかの閾値を測定する場合に恒常法よりも効率的に測定することができます。最尤法とベイズ推定を用いて閾値を推定するのが特徴です。今回は、Psychlopsに追加で組み込めるQUESTのサンプルを実行してみます。
8729 まず最初に、[[QUEST Sample|http://satolab.l.u-tokyo.ac.jp/~psychlops_site/download/BasicSamples/QUEST_Sample.zip]]をダウンロードしてください。
8731 圧縮ファイルを展開すると、3つのファイルが生成されます。
8735 QUESTの本体が書かれています。XcodeやReloで新規プロジェクトを作成した後、以上の2ファイルをプロジェクトと同じフォルダにおいてください。次に、[[プロジェクトにこのプログラムを追加|Tips: プログラムを複数のファイルに分割する]]してください
8738 上記QUESTを呼び出してコントラスト閾値測定を行うデモプログラムです。新しいプロジェクトのプログラムにコピーして貼り付けてください。
8742 このQUESTの実装では、QUEST用の確率表を内部で作成し、それをユーザプログラムからメソッドを呼び出して更新していくモデルをとります。
8744 [img[image/tips/QUEST.png]]
8749 QUESTでは様々な事前情報が必要です。
8751 * その閾値の推測の確からしさはどの程度か
8752 * QUESTで求める閾値は何%に設定するのか
8753 * 被験者がボタンを押し間違える確率はどの程度ぐらいか
8755 といった情報が必要です。これを実験を始める前に設定します。
8758 QUEST(double priorEstimatedThreshold, double logPriorSD, double thresholdProbability,
8759 double slope, double errorRate, double chanceLevel,
8760 Range thresholdRange, double logStep = 0.005, Measure placementMeasure=MEAN);
8763 |!QUEST()|QUEST(double priorEstimatedThreshold, double logPriorSD, double thresholdProbability, double slope, double errorRate, double chanceLevel, Range thresholdRange, double logStep, Measure placementMeasure);|QUEST進行用のクラスを作ります|>|
8764 |~|~|double priorEstimatedThreshold|閾値が大体どのくらいにあるかという推測値です。|
8765 |~|~|double priorSD|推測値の確かさ。閾値が推測値とあまり離れていない場合は小さな値を、離れているかもしれない場合は大きめの値を代入してください。|
8766 |~|~|double thresholdProbability|閾値とみなす正答率。たとえば2肢強制選択(チャンスレベル0.5)の場合は、正答率0.75が閾値とみなされることが多いです。|
8767 |~|~|double slope|心理物理関数の傾き。傾きが既知の場合はその値を代入してください。傾きがわからない場合は効率は落ちますが大きめ値を代入しておいたほうがよいでしょう。|
8768 |~|~|double errorRate|被験者がボタン押しを間違える確率|>|
8769 |~|~|double chanceLevel|チャンスレベル。2肢強制選択なら0.5を代入してください。|
8770 |~|~|Range thresholdRange|閾値の存在範囲を0以上の範囲で指定|
8771 |~|~|double logStep|ステップの値|
8772 |~|~|Measure placementMeasure|推定値を確率分布の最頻値か平均値にしたがって推定します(MODEかMEAN)|
8777 初期化が終われば試行を40-100回ほど繰り返します。
8780 # ループ開始前に刺激の初期値を得ます。
8781 ** 初期値はdouble QUEST::nextTrial()を実行することで最適な値を求めることができます。
8782 ** 通常このようにして求められた値をそのまま使います(手を加える例もあります)。
8783 # 刺激を提示し、被験者からの反応を得ます。
8784 # 実験結果をQUESTに与え、次の試行で使う値を決定します。
8785 ** 2で提示するとき実際に用いた刺激強度と、被験者が成功したか失敗したかをvoid QUEST::next(double intensity, int result)でQUESTに通知します。成功した場合はresultに1を、失敗した場合は0を渡してください。
8786 # 1へと戻り所定の試行数が終わるまでこれを繰り返します
8787 # 所定の試行数が終了したら最後にfinalEstimate()を呼び出すことで実験の結果、推定された閾値を得ることができます。
8789 |!next()|試行の結果をQUESTに通知し、次の試行で用いる値を返します(下記2関数の合成)|>|
8790 |~|double intensity|前回の試行で使われた刺激強度|
8791 |~|int result|前回の試行の被験者の回答。成功した場合はresultに1を、失敗した場合は0を渡してください。|
8793 |!QUEST::nextTrial()|double nextTrial(void)|次の試行で用いる値を返します|>|
8795 |!QUEST::update()|void update(double intencity, int result)|試行の結果をQUESTに通知し、状態をアップデートします。|>|
8796 |~|~|double intensity|前回の試行で使われた刺激強度|
8797 |~|~|int result|前回の試行の被験者の回答。成功した場合はresultに1を、失敗した場合は0を渡してください。|
8799 |!QUEST::finalEstimate()|double finalEstimate(void)|最終結果を返します|>|
8802 #include <psychlops.h>
8803 using namespace Psychlops;
8805 #include "QUEST.h"
8807 void psychlops_main() {
8809 //initialize QUEST method
8810 const double estimatedThreshold = 0.05; //estimated threshold, mean of prior pdf
8811 const double pdfSD = 2.0; //standard deviation of prior pdf
8812 const double thresholdProbability = 0.75; //the probability defined as threshold
8813 const double slope = 3.5; //slope of psychometric function
8814 const double errorRate = 0.01; //error rate
8815 const double chanceLevel = 0.5; //chance level
8816 Range contrastRange;
8817 0< contrastRange <=1.0;
8819 QUEST quest(estimatedThreshold, pdfSD, thresholdProbability, slope, errorRate, chanceLevel, contrastRange);
8822 double contrast = quest.nextTrial(); //contrast of stimuli at each trial
8823 int result; //result of trial
8824 int trial = 50; //number of trial
8828 Canvas canvas(Canvas::fullscreen);
8830 gabor.set(50, 50, 1.0, 0, 0).centering();
8831 int display_frames = 1*canvas.getRefreshRate(); // display gabor for 1 second.
8834 for (int i = 0; i<trial; i++) {
8836 //replace this section to your experiment
8837 // displaying stimuli
8838 for(int i=0; i<display_frames; i++) {
8848 if(Input::get(Keyboard::left)) { result = 1; break; } // positive reaction
8849 if(Input::get(Keyboard::right)) { result = 0; break; } // negative reaction
8853 gabor.contrast = quest.next(contrast, result);
8857 double threshold = quest.finalEstimate();
8858 double sd = quest.getSD();
8865 QUESTでは心理物理関数にWeibull分布を仮定しています。そのため、心理物理関数がWeibull分布で近似できない場合は使えませんのでご注意ください。
8869 <div title="Tips: Rectangleの配列を用いたドットパターンの描画例" modifier="YourName" modified="200802201227" created="200802191422" changecount="5">
8870 <pre>ランダムドットパターンなどの複数のドットを使ったパターンの描画で、
8871 ドット密度や各ドットの運動方向を精密に操作したいときは、
8872 [[3.2節|3.2 オフスクリーンを用いた描画例-ランダムドットパターン-]]で紹介した方法よりは、
8873 Rectangleの配列を用いた方法のほうがプログラミングが容易です。
8874 いかにRectangleの配列を用いたドットパターンの描画の典型的なプログラム例をしめします。
8875 ここでは、Rectangleの配列のうちの一部をランダムな方向に運動させ(Noise)、残りを右方向へ運動させています(Signal)。
8878 #include <psychlops.h>
8879 using namespace Psychlops;
8881 void psychlops_main() {
8883 Canvas display(Canvas::fullscreen);
8885 // Generic Random Dot Parameters
8886 const int DOT_SIZE = 3;
8887 const int MAX_NUM_DOTS = 1000;
8888 const double speed=3.0;
8889 const double noise_dots_ratio = 0.5;
8891 // Parameters of each dots
8892 Psychlops::Rectangle dot[MAX_NUM_DOTS];
8893 Psychlops::Rectangle canvas_rect(display.getWidth(), display.getHeight());
8896 // Randomize Dots' Position
8897 for(int i=0; i<MAX_NUM_DOTS; i++) {
8898 dot[i].set(DOT_SIZE, DOT_SIZE);
8900 Psychlops::random(display.getWidth()),
8901 Psychlops::random(display.getHeight())
8905 while(!Input::get(Keyboard::esc)) {
8908 // Update noise dots' position
8909 for(int i=0; i<MAX_NUM_DOTS*noise_dots_ratio; i++) {
8910 direction=Psychlops::random(2*PI);
8911 dot[i].shift(speed*cos(direction), speed*sin(direction));
8912 if(!canvas_rect.include(dot[i]))
8914 Psychlops::random(display.getWidth()),
8915 Psychlops::random(display.getHeight())
8918 dot[i].draw(Color::white);
8921 // Update signal dots' position
8922 for(int i=MAX_NUM_DOTS*noise_dots_ratio; i<MAX_NUM_DOTS; i++) {
8923 dot[i].shift(speed, 0.0);
8924 if(!canvas_rect.include(dot[i])) dot[i].shift(-display.getWidth(), 0.0);
8925 dot[i].draw(Color::white);
8934 <div title="Tips: Shapeクラスの概要" modifier="Psychlops_DevelopperG" modified="200912010828" created="200912010822" changecount="1">
8935 <pre>Shapeクラスに属するクラスは、デフォルトの描画色や枠線を指定することができます。
8936 このデフォルトの色と線Groupでまとめたりする際にとくに有用です。
8937 これらの指定はShapeクラスのメンバ fill, strokeに対して代入を行うことで実行します。
8939 |!Shape::fill|デフォルトの塗り色です。Colorが代入できます。初期状態では無色です。|
8940 |!Shape::stroke|デフォルトの枠線をStroke型で指定します。初期状態では太さ0の線です。|
8942 枠線を指定ためにはstrokeクラスの変数を宣言して、書式指定を行っておく必要があります。
8943 書式指定には宣言時に指定をする方法とset()命令を使用する方法があります。
8944 これらの詳細については[[Tips: Strokeクラスの概要]]をご覧ください。
8947 #include <psychlops.h>
8948 using namespace Psychlops;
8950 void psychlops_main() {
8951 Canvas canvas(Canvas::window);
8953 Psychlops::Rectangle rect(100,100);
8954 rect.centering().shift( 100, 0);
8955 rect.stroke.set(Color::red, 5, Stroke::DOTTED);
8957 Shape::Colored<Psychlops::Ellipse> ellipse;
8959 ellipse.centering().shift(-100, 0);
8960 ellipse.fill = Color::white;
8962 while(!Keyboard::esc.pushed()) {
8972 この命令セットのうちでここまで扱わなかったものとしてgetDatum()があります。
8974 |!Point Shape::getDatum()|getDatum(void)|Figureの「基準座標」をPoint型で返します. <br> 基準座標の定義は各クラスによりますが、原則として中心点や起点(図形の左上の点)です|</pre>
8976 <div title="Tips: Strokeクラスの概要" modifier="Psychlops_DevelopperG" modified="200912010835" created="200912010830" changecount="5">
8977 <pre>Strokeクラスは、線の書式を表現するためのクラスです。
8978 描画系の関数にColorの代わりに Stroke構造体を与えることで線を描画します。
8981 * ImageにStrokeを描画することはできません。
8982 * 線が角ごとに途切れるうえ、円が多角形近似のため、ある程度以上太くしたり破線を指定したりするとアラが目に見えてわかります。
8985 コンストラクタ, set関数 (Color color, double thick, Stroke::Pattern pattern)
8990 線の太さです。現状ピクセル単位になります。
8992 バターンの種類はSOLID, DASHED, DOTTEDです
8996 Stroke solid(Color::red, 1, Stroke::SOLID);
8997 // 宣言と設定を同時に行う方法は関数内でのみ使用できます。
8998 Stroke dashed, dotted;
8999 dashed.set(Color::green, 3, Stroke::DASHED);
9000 dotted.color = Color::blue;
9002 dotted.pattern = Stroke::DOTTED;
9004 Psychlops::Rectangle rect(100,100);
9006 while(!Keyboard::esc.pushed()) {
9009 rect.shift(-150,0).draw(solid);
9010 rect.shift( 150,0).draw(dashed);
9011 rect.shift( 150,0).draw(dotted);
9017 [img[image/Stroke_Sample.png]]
9021 <div title="Tips: Vistaにおけるインストール" modifier="YourName" modified="200802210940" created="200802191504" changecount="3">
9022 <pre>Windows VistaでPsychlopsを利用する場合、いくつかの注意が必要です。
9024 !! ビデオカードのドライバがOpenGLをサポートしているか確かめる
9025 Windows Vistaでは、DirectXがOpenGLより優先されたため、OpenGLへのサポートが十分でないビデオカードがあります。2008/2/14現在でWindows VistaでOpenGLが十分にサポートされているビデオカードは以下のとおりです。
9027 * nVidia GeForce 7000番台以降
9028 * AMD(ATI) Radeon Xシリーズ
9031 !! インストーラの実行時に管理者権限で実行する
9033 BCC、SetBCC、Psychlopsのインストーラをダブルクリックしても、何も起きない(ように見える)、あるいは明らかに実行に失敗しているように見える場合があります。その場合、実行ファイルを右クリックして「管理者として実行」をクリックし、管理者のパスワードを入力して実行してください。
9034 [img[Vista|image/Win/VistaBCC_1.png]]
9037 !! Borland C++ Compilerの環境変数を設定する
9039 [[BCCのインストール|1.3 インストール作業(Windows)]]を実行した後、追加で以下の作業を行います。
9041 0. スタートボタン → コントロールパネルを開きます
9043 2. 「環境変数を編集」をクリックします
9044 3. 開いたウインドウで、「変数」の中に「PATH」があるかどうかを調べます
9045 4.1. 「PATH」があった場合、それを選んで「編集」ボタンを押します。開いたウィンドウで変数値の末尾に以下のテキストを''追加します''。セミコロンは必要なので落とさないようにしてください。
9047 ; C:\borland\bcc55\Bin;
9049 4.2. 「PATH」がなかった場合、「新規」ボタンを押します。開いたウィンドウで、変数名に「PATH」、変数値に以下のテキストを書き込みます
9051 C:\borland\bcc55\Bin;
9053 [img[Vista|image/Win/VistaBCC_2.png]]
9057 <div title="Tips: Xcodeで実行時エラーが起きるとフリーズする" modifier="YourName" modified="200802130846" created="200709171710" changecount="5">
9058 <pre>Xcodeで実行時エラーが起きると操作を受け付けなくなる現象があります。これは実際は実行時エラー発生時にデバッガが起動しており、フルスクリーンのために見えないだけです。フリーズはしていません。この現象は以下に例示する方法で回避できます。
9061 複数ディスプレイを使用した場合、Psychlopsは主ディスプレイ(メニューバーのあるディスプレイ)に表示され、副ディスプレイを拘束しません。副ディスプレイにデバッガウィンドウを表示しておくことで、デバッガからのプログラム終了やエラー追跡ができます。
9064 デバッガの起動をとめるには、プロジェクト中の「実行可能ファイル」タブから、
9068 と並んでいるうちの一番上のstaticを右クリックまたはctrl+クリックして、「情報を見る」を選びます。
9070 [img[実行ファイルの情報|image/OSX/Psychlops_OSX_dbg1.png]]
9072 開いたダイアログで「デバッガ」タブを選び、すべてのチェックボックスをオフにします。
9073 [img[実行ファイルの情報|image/OSX/Psychlops_OSX_dbg2.png]]
9076 演算性能によりますが、数分間待つと回復することがあります
9080 <div title="Tips: intどうしの比率の計算がおかしい" modifier="YourName" modified="200803050457" created="200802202143" changecount="2">
9081 <pre>C++では、intどうしの割り算を行うと小数点切捨ての整数で帰ってきます。このため、特にfor文のカウント変数と最大値から比率を求めようとするときなどで精度差が出ることがあります。これを回避するには型キャストしなければなりません。また、定数項はなるべく「.0」をつけて強制的に浮動小数点値と解釈させると、その項の演算結果を利用する演算子の結果はすべてdouble型にキャストされます。これを覚えておけば「式はあっているはずなのに何も表示されない」といったバグはずいぶんと減るはずです。
9084 for(int i=0; i<max; i++) {
9085 Display.pix(i, 0, Color( (double)(i)/max ));
9086 Display.pix(i, 2, Color( i*2.0 / max));
9092 <div title="Tips: アルファ混色の活用" modifier="YourName" modified="200803140631" created="200802201822" changecount="7">
9093 <pre>Psychlopsが標準で扱うアルファ値(透明度)は、以下のように定義されています。
9096 結果 = α×上書き色 + (1-α)×元の色
9099 したがって、アルファ値を0.5とおくと元の色と上書き色の平均値になります。アルファ合成は合成結果が重み付け平均として表せるときに有効です。これ以外の計算方法でアルファ合成を行いたい場合には[[glBlendFunc命令を使用して直接指定をおこなってください|Tips: OpenGL命令の混ぜ方]]。
9101 アルファ合成はビデオカード上で行われるために高速ですが、[[ガンマ補正|Tips: ガンマ補正]]がハードウェア合成の場合にのみ正常に機能し、ソフトウェアエミュレーションである場合には補正済みの値どうしの平均を行ってしまうために誤った値が表示されます。これを防ぐためには、[[Color::strict_match = true;|Color::strict_match]]を指定してソフトウェアエミュレーションを機能させないようにしてください。
9104 以下のコードを実行すると、横縞のガボール刺激に縦縞のダイナミックノイズがかかった刺激が描画されます。ここでは、縦縞のダイナミックノイズをアルファ合成を用いてガボール刺激に重ね合わせています。[[Tips: 独自のクラスを作る]]で例示されている~QuickGaborもアルファ合成を用いています。
9106 [img[image/tips/Sample_alpha.png]]
9109 #include <psychlops.h>
9110 using namespace Psychlops;
9112 void psychlops_main() {
9114 Canvas display(Canvas::fullscreen);
9116 Psychlops::Rectangle noise;
9120 gabor.set(30,30).centering();
9121 gabor.orientation = 90.0; // don't
9123 while(!Input::get(Keyboard::esc)) {
9128 gabor.contrast = sin(PI*gabor.phase/360.0);
9132 noise.set(1, display.getHeight()).centering(-1,display.getVcenter());
9133 for(int i=0; i<display.getWidth(); i++) {
9134 noise_lum = Psychlops::random();
9136 // **************draw with alpha-blending*****************************
9137 noise.shift(1,0).draw(Color(noise_lum, noise_lum, noise_lum, 0.5));
9147 <div title="Tips: ガンマ補正" modifier="Psychlops_DevelopperG" modified="200910080850" created="200802130705" changecount="15">
9148 <pre>ディスプレイやプリンタなど、コンピュータから利用できる画像表示機器では、明るさが指定した値通りに表示されず、歪みが生じます。多くの場合はこの歪みは指数関数に従います。その指数を'''ガンマ値'''と呼び、表示装置の特性を記述するパラメタになります。CRTの場合、ガンマ値は赤、緑、青の蛍光体の間で独立です。
9150 [img[輝度歪み|image/DisplayGamma.png]]
9152 PsychlopsのColor型はガンマ補正機能を備えています。ガンマ補正にはガンマ値による指定のほかに、テーブル(補正表)による指定方法があります。CRT以外の表示装置の特性は必ずしもガンマ値型指数関数に従うとは限りませんので、液晶やプラズマ、DLPプロジェクタなどではテーブルによる指定を試みる必要があります(番、山本、江島、2006)。
9156 double gamma_r=1.7, gamma_g=1.6, gamma_b = 0.6;
9157 Color::setGammaValue(gamma_r, gamma_g, gamma_b);
9162 Psychlopsのガンマ補正は、標準でハードウェアの調整機能を起動し、それに失敗した場合ソフトウェアによるエミュレーションを試みます。
9164 [img[ソフト補正とハード補正|image/ColorCalibrationModel.png]]
9167 最近のビデオ表示機器は、理論値と電気出力値の変換テーブルを内部に保持しており、このテーブルに補正値を書き込むことで補正機能を有効にすることができます。書き込みはDVI接続時でも成功しますが、有効に機能するかはハードウェアに依存します。確認するためには実測が必要です。
9169 ハードウェア補正では、以下のOSのAPI関数を呼び出します。
9174 CGSetDisplayTransferByFormula
9175 CGSetDisplayTransferByTable
9179 ソフトウェア補正では、Color型とImage型の入出力時に変換関数を適用することで、ハードウェア補正と同様の機能を実現します。Color型とImage型の入出力関数で補正を行い、補正後の値を保存することで実現しています。
9181 ソフトウェア補正はハードウェア補正に比べいくつかの点で劣ります。
9182 * 理論値の変化に対して鋭敏に輝度が変化する部分とそうでない部分が生じます。鋭敏でない部分では輝度変化がつぶれてしまいます(下図)
9183 * ガンマ値をプログラム中で変更した場合、それ以前までにsetされたColor情報は古いガンマ値のエミュレーション結果に従った値が保存されており、新しいガンマ値に従わない誤った色が表示されます。いっさいの警告が出ませんのでご注意ください。
9184 * 変換をCPUで計算するため、速度が低下します。
9185 * ソフトウェア補正で補正テーブルを使用した場合、さらに問題が生じます
9186 ** 色指定は通常0~1の実数ですが、補正テーブル適用時は入力値がテーブルの行数にあわせて離散化されます。このため、Color.set()で入力した値とget()で取得した値が異なることがあります。
9187 ** 複数の理論値に対して同一の補正値が割り当てられている場合、get()で復元する場合に入力理論値と異なることがあります。
9189 [img[ソフトウェア補正の鋭敏さ|image/SoftwareColorCalibration.png]]
9192 !! 表示装置固有の輝度特性測定時の注意
9193 ハードウェア補正が適用されたPsychlopsを用いて表示装置の特性を測定する場合には、OSが標準で行っている補正(ほとんどの場合非線形ですを外すために、以下の関数を実行してください。
9195 Color::setGammaValue(1,1,1);
9198 *この記述はver1.0 (Psychlops20080214)以降での注意です。これ以前のバージョンに使用されているsetGammaValue命令は常にソフトウェア補正を用いているために、OSによる標準の補正を気にする必要はありません。
9201 特に厳密な色補正を保証したい時に、ガンマ補正や色指定で値が0以上1以下にならないケースやソフトウェア補正が適用される環境下ではプログラムが停止されるように強制させることができます。以下のコードをpsychlops_main()の冒頭に記述してください。
9203 Color::strict_match = true;
9209 番浩志、山本洋紀、江島義道 (2006) "Mcalibrator: MATLAB言語を用いたディスプレイ較正のための統合型GUIソフトウェアの開発" 基礎心理学研究、第24巻、149-161頁
9212 <div title="Tips: トリガーとアナログ入力" modifier="YourName" modified="200802201728" created="200802201438" changecount="4">
9213 <pre>脳波やfMRI研究では電気的トリガを出す必要がありますが、[[Tips: 機器間の同期]]で説明したとおり、機器間の同期を取ることが非常に困難であり、視覚刺激提示装置と電気パルスを同期制御することは難しくなっています。
9215 この問題を確実に解決する方法としては、画面の輝度をフォトトランジスタで電気信号に変換する方法があります。フォトトランジスタの照度に対する反応はきわめて高い時間精度を持つため、輝度の状態を得るには適しているといえます。
9217 一般的な電気信号入出力装置は電流ではなく電圧を信号として扱いますので、フォトトランジスタの照度への反応を電圧の変化として取り出す回路が必要です。一般的な回路図を以下に示します。
9219 [img[image/Tips/phototrigger.png]]
9221 フォトトランジスタを吸盤などに埋め込んで画面に設置できるようにして、フォトトランジスタに直列につないだ抵抗器の両端の電圧を電圧計に入力します。
9223 * たとえばCRTの場合、陰極線が画面を走査するには一定の時間が必要です。したがって、画面上部と画面下部では輝度の変化する時間に数ミリ秒の差があります。
9224 * 電圧の範囲は、測定照度、フォトトランジスタの特性、抵抗器の抵抗値、によって決まります。一般的には、抵抗を変えることで必要な電圧範囲になるようにします。
9225 * 刺激提示画面上に電圧計を設置するのが難しい場合、マルチディスプレイを用いて2画面に同じ画像を提示し、実験に使わないほうの画面でトリガーをとる方法があります。この方法では、画面間の同期はとれていませんが、垂直同期間隔は一定です。あらかじめ両画面の同期の時間差を測定しておくことで、正確な同期タイミングを算出することが可能です。
9228 <div title="Tips: トリガーとアナログ入力2-外部機器を利用した例-" modifier="YourName" modified="200803062207" created="200803062201" changecount="6">
9229 <pre>脳波実験等では参照トリガ電圧が必要になることがありますが、近年のコンピュータでは、電気的外部入出力端子(~RS-232Cなど)を持つものが少なく利用しにくくなっています。そこで今回は、比較的容易に入手可能なNational Instruments社製[[USB-6009|http://sine.ni.com/nips/cds/view/p/lang/en/nid/14605]]を用いてアナログ入出力をしてみます。
9231 ~USB-6009は電圧データ入出力用機器です。
9232 * アナログ入力 4 ch、アナログ出力 1 ch
9233 * 時間分解能:入力 48 kHz 出力 150 kHz
9234 * 電圧分解能:入力 14 bit 出力 12 bit
9235 と、心理物理学には十分な性能を持っています。価格は35000円程度で、電圧入出力機器としては比較的安価に入手できます。
9237 実際に実験に使う上では、他の入出力機器との時間同期を気にする必要があります。
9240 ** USBにはキーボード・マウス等、最低限のものしかつながないでください。USBメモリ・ハードディスク・USBオーディオ・Webカメラ等、大量のデータを転送するものとの併用は危険です。
9242 ** データ取得までの時間遅れは、少なくとも10ms以下程度に抑えられています。したがって、入力を元に画像を制御する用途には用いることができます。
9243 ** 入力は4ch同時に取ることができるので、画面のトリガと反応ボックス等を組み合わせて入力することで、入力した機器間で厳密な同期をとることができます。
9245 ** データを~NI-6009に送信した後、~USB-6009のデジタル入力端子に外部から別のトリガ信号を入力することで、そのトリガに同期してデータを出力することができます。
9246 *** たとえば、VGAの垂直同期信号をデジタル入力端子に誘導しておくと、任意の時点で送信したアナログ出力データを次のflipに同期して出す、といったことが可能になります。
9249 今回は、もっとも簡単なサンプルとして、~USB-6009をひとつ接続し、10ms程度の精度で「電圧値を得る」「電圧を出す」を単発で行う拡張サンプルを実行してみます。高度な時間制御は行っておりません。なお、このサンプルを応用したプログラムを配布する場合は、配布先の環境でも同様の装置が必要です。
9255 !! Mac OS XでXcodeを使用する場合
9256 * [[NI-DAQmx Base|http://joule.ni.com/nidu/cds/fn/p/sn/n19:MacOS.MACOS10/sb/navsRel/lang/en?q=NI-DAQmx+Base&x=10&y=7]]ドライバの最新版をダウンロードします。
9257 * ダウンロードしたファイルを実行し、ドライバをインストールします(要再起動)。
9259 * [[NIDAQmxBase_Sample_OSX.zip|http://psychlops.l.u-tokyo.ac.jp/download/BasicSamples/NIDAQmxBase_Sample_OSX.zip]]をダウンロードし、解凍します
9261 ** 「~NIDAQmxBase_Ext_Installer」をダブルクリックして実行します(下記動作を行うシェルスクリプトです)
9262 *** 失敗した場合、手動で「nidaqmxbase_ex_sample.cpp」を/Library/Frameworks/Psychlops.framework/Headers/Extentionsにコピーします。
9264 新規プロジェクトを作成し、プロジェクトのプロパティでNI-DAQmx Baseドライバを読み込む設定を行います。
9265 * 「External Frameworks and Libraries」をCtrl+クリックし、追加→既存のフレームワークから「nidaqmxbase.framework」を選択
9266 [img[image/OSX/nidacmxbase_OSX_1.png]]
9268 !! WindowsでBCC5.5 + Reloを使用する場合
9270 まず最初に、必要なファイルのセットアップを行います。
9272 * [[NI-DAQmx Base|http://joule.ni.com/nidu/cds/fn/p/sn/n19:Windows,n23:3478/sb/navsRel/lang/en?q=NI-DAQmx+Base&x=0&y=0]]ドライバの最新版をダウンロードします。
9273 * ダウンロードしたファイルを実行し、ドライバをインストールします(要再起動)。
9275 * [[NIDAQmxBase_Sample_win32.zip|http://psychlops.l.u-tokyo.ac.jp/download/BasicSamples/NIDAQmxBase_Sample_win32.zip]]をダウンロードし、解凍します
9277 ** 「~NIDAQmxBase対応Psychlops拡張のインストーラ.bat」をダブルクリックして実行します(nidaqmxbase_ex_sample.cppをExtentionフォルダにコピーします。
9278 ** 「~NIDAQmxBaseをBCCに対応させる.bat」をダブルクリックして実行します(bccのツールを呼び出します・要管理者権限)
9281 新規プロジェクトを作成し、プロジェクトのOptionsダイアログで、NI-DAQmx Baseドライバを読み込む設定を行います。
9282 *「Linker」タブの下部にある「Library」に「nidaqmxbase_bcc.lib」を追加(末尾にスペースをひとつ空けて追加します)
9283 * DirectoriesタブにNI-DAQmx Baseを追加する
9284 ** 「Extra Include Paths」に「C:\Program Files\National Instruments\NI-DAQmx Base\Include\」を追加
9285 ** 「Extra Library Paths」に「C:\Program Files\National Instruments\NI-DAQmx Base\Lib\」を追加
9286 [img[image/win/nidaqmxbase_Win32_1.png]]
9293 * NI ~USB-6009 の2番(+)、3番(-)ポートに入力線を接続する
9294 * #include <Extentions/Samples/nidaqmxbase_ex_sample.cpp>を追加
9295 * ~NIDAQmxBase_AnalogInput_Dev1 クラスの変数を作成し、ポートを初期化する
9296 * 変数のget()メソッドを呼び、ポート間の電位差を得る
9297 これで、0Vから5Vまでの電位差を得ることが可能になります。
9299 以下のサンプルでは、中心に長方形が表示され、その縦幅が電圧値(V) * 100 ピクセルとしてリアルタイムに表示されます。
9302 #include <psychlops.h>
9303 using namespace Psychlops;
9304 #include <Extentions/Samples/nidaqmxbase_ex_sample.cpp>
9306 void psychlops_main() {
9307 Canvas canvas(Canvas::fullscreen);
9309 double rect_size = 100;
9310 Psychlops::Rectangle rect;
9313 NIDAQmxBase_AnalogInput_Dev1 analog_in;
9315 while(!Input::get(Keyboard::esc)) {
9317 rect.resize(rect_size, rect_size * analog_in.get()).draw(Color::white);
9323 このサンプルをフォトトランジスタと接続して照度に連動する実行例が[[Cliplife|http://cliplife.jp/clip/?content_id=usqdz3cv]]にアップロードされています。
9328 * NI USB-6009 の14番、15番ポートに出力線を接続する
9329 * ~NIDAQmxBase_AnalogOutput_Dev1 クラスの変数を作成し、ポートを初期化する
9330 * 変数のput(double voltage)メソッドを呼び、ポート間の電位差を渡す
9331 これで、0Vから5Vまでの電位差の出力が可能になります。
9333 以下のサンプルでは、マウスが画面最上部にあるとき1V、最下部にあるとき0Vになります。
9336 #include <psychlops.h>
9337 using namespace Psychlops;
9338 #include <Extentions/nidaqmxbase_ex_sample.cpp>
9340 void psychlops_main() {
9341 Canvas canvas(Canvas::fullscreen);
9343 NIDAQmxBase_AnalogOutput_Dev1 analog_out;
9346 while(!Input::get(Keyboard::esc)) {
9348 analog_out.put(1.0-1.0*Mouse::y/Display::getHeight());
9354 このサンプルを電圧計で計測した実行例が[[cliplife|http://cliplife.jp/clip/?content_id=opt044zr]]にアップロードされています。
9355 出力を直接ヘッドホンにつなぐと音が鳴りますが、解像度が150 Hzしかないので精度には期待しないほうがいいでしょう。
9358 <div title="Tips: ビープを出す" modifier="YourName" modified="200908100433" created="200802201253" changecount="4">
9359 <pre>精度をまったく気にせず単に音がなればいい場合、システムのビープ音を利用する方法があります。
9362 Windowsの場合、以下の命令でPCのビープ音を発生させます。ただし、PCによっては機械ビープが省略されているものがあります。
9364 Beep(int 持続時間, int 周波数);
9368 Mac OS Xの場合、以下の命令でOSの警告音を発生させることができます
9373 * Apple社より、この命令は将来的に廃止されることが予告されています。
9374 * OSの警告音が1度なるだけで、持続時間の指定にはまったく効果はありません。
9377 将来的には、Psychlopsで機種非依存の命令を追加する予定です。
9380 <div title="Tips: プログラムが暴走したとき" modifier="PsychlopsAdmin" created="200709171720" changecount="1">
9381 <pre>プログラムの実行中に強制的に終了したい場合があります。このような場合はOSの強制終了コマンドを使ってください。
9384 ** ESC + Option + Command(花記号)キーを同時に押す
9387 ** 上記方法で失敗した場合、Ctrl + Alt + Delを同時に押して、タスクマネージャから強制終了する</pre>
9389 <div title="Tips: プログラムを複数のファイルに分割する" modifier="YourName" modified="200803140500" created="200803110823" changecount="4">
9390 <pre>実験間で共通の処理がある場合、管理を容易にするためにその部分のプログラムを別ファイルに分けて使いまわすことができます。
9393 既存のファイルをプロジェクトに追加する場合は、以下の操作を行います。
9397 # 「ループとファイル」の「Sources」を右クリックし、「追加」「既存のファイル」と選ぶ
9398 # 開いたウィンドウでファイルを選択し、追加する
9400 [img[image/osx/Xcode_addFiles.png]]
9404 # プロジェクトのファイル一覧を右クリックし、「Add Files」を押す
9405 # 開いたウィンドウでファイルを選択し、追加する
9407 [img[image/win/Relo_addFiles.png]]
9410 新規にファイルを作ってプロジェクトに追加する場合は、以下の操作を行います。
9414 # 「ループとファイル」の「Sources」を右クリックし、「追加」「新規ファイル」と選ぶ
9415 # ファイル名等を決めて新規ファイルを作成
9419 # プロジェクトのファイル一覧を右クリックし、「New Souce File」を押す
9420 # 新しいタブ「untitled」が開かれているので、保存ボタンを押し、名前と保存場所を決める
9424 <div title="Tips: ランダムドットステレオグラムの描画コード" modifier="Psychlops_Admin" created="200709130536" changecount="1">
9425 <pre>下の例ではダイナミックランダムドットステレオグラムを描画します。
9426 スペースキーを押すと、ポーズがかかります。
9427 リターンキーを押すと、対応領域の可視化モードが切り替わります。
9428 エスケープキーを押すと、プログラムが終了します。
9431 #include <psychlops.h>
9432 using namespace Psychlops;
9434 void psychlops_main()
9437 Canvas display(Canvas::fullscreen);
9438 const int BUFFER = 10;
9439 const int whole_size = 200;
9440 const int stereo_size = 100;
9441 double disprity = 10.0;
9446 Image base[2][BUFFER], stereo[2][BUFFER];
9447 for(int i=0; i<BUFFER; i++) {
9448 for(int j=0; j<2; j++) {
9449 base[j][i].set(whole_size, whole_size);
9450 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())); }}
9452 stereo[0][i].set(stereo_size, stereo_size);
9453 stereo[1][i].set(stereo_size, stereo_size);
9454 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)); }}
9455 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())); }}
9459 while(!Input::get(Keyboard::esc)) {
9460 if(Input::get(Keyboard::spc)) pause = !pause;
9461 if(Input::get(Keyboard::rtn)) dotsurface = 1-dotsurface;
9467 base[0][loop].centering().shift(-whole_size, 0).display();
9468 stereo[dotsurface][loop].centering().shift(-whole_size, 0).display();
9469 base[1][loop].centering().shift(whole_size, 0).display();
9470 stereo[dotsurface][loop].centering().shift(whole_size+disprity, 0).display();
9471 Display::msg("Press space key to pause.", 400, 100);
9472 Display::msg("Press return key to switch visualize mode.", 400, 120);
9473 Display::msg("Press esc key to exit.", 400, 140);
9481 <div title="Tips: 時間精度が必要なプログラムを実行するとき" modifier="PsychlopsAdmin" created="200709171719" changecount="1">
9482 <pre>現在のコンピュータは同時に多数のソフトが起動しており、その計算に処理時間をとられてフレーム落ちする場合があります。これを回避するにはいくつかの方法があります。
9484 * いったん再起動する(余計なソフトウェアを強制終了させることができます)
9485 * AppState::setThreadPriority( AppState::HIGH );関数を実行する(今後改名するかもしれません)
9489 ** forやwhile内で不必要な繰り返し計算を除去する
9490 ** pixの配列を書き込む計算の場合、for文をやめてCanvas.pix(ドット数, 配列)の呼び出しを行う
9491 * Imageが原因ならquicken()の使用を検討する
9492 * より処理速度の速いコンピュータを使用する
9495 <div title="Tips: 機器間の同期" modifier="YourName" modified="200802201725" created="200802201301" changecount="4">
9496 <pre>現代のコンピュータはOSやハードウェア規格のレベルで機器間の同期を保証できないシステムに置き換えられています。このTipsでは、各機器の同期精度について説明します。
9499 現代のコンピュータは、互換性の保持、失敗からの回復、暴走の回避を目的として、さまざまなインターフェースが仮想化され、パケット通信化されています。このため、コンピュータのクロックのみに依存した同期確保方法が利用できなくなっています。
9501 ただし時間管理が必要な機器、たとえばCPUのクロック、ビデオカードの垂直同期信号、サウンドカードの連続的音声出力などはそれぞれ独自のクロック信号により精確な時間管理を行うことができます。これらの機器内部で精確なインターバルを得ることはできます。
9503 しかし、機器間で同期を取ることは困難です。視聴覚時間同期といった信号は生成するのが非常に困難です。機器間で同期を取る方法としては、同期を制御する装置をひとつに絞り、その装置の外部にアナログ的な方法で同期装置を設ける方法が推奨されます。この方法は非常に精確です。
9507 最近のOS(Windows 2000以降やMac OS X)はプリエンプティブマルチタスク方式と呼ばれる、ひとつのアプリケーションが中央演算装置(CPU)を独占できない方式を採用しています。このため、あるアプリケーションが処理される速度は不定で、周辺機器の割り込みを実時間で拾えるとは限りません。
9511 近年のビデオカードはダブルバッファリングとバッファ切り替えの垂直同期信号との同期を行うことが標準化されており、ドライバの設定によって(Windowsでは画面の設定で指定、Mac OS Xでは標準)でその機能を利用できるようになり、描画途中で描画内容の転送が起こることのないように設計されています。Psychlopsではこれらのビデオボードに指示を行い、各描画フレームの垂直同期信号との同期を実現しています。
9513 しかし、バッファ切り替えの行われた正確な時間を知ることはできません。
9517 キーボード、マウスなどHID(Human Interface Devices)は、一般的にはインタラプト転送と呼ばれる、一定間隔で行われる転送を用いて入力されます。これは比較的よい時間精度を持っていますが、一般的なGUIを持つOSでは、ウィンドウの配置を加味してカーネル等から各アプリケーションにプロセス間通信(時間がかかり、同期が維持されない)を用いてHIDイベント(キーを押した、離したなど)の情報が送られます。このため、アプリケーション側がHIDイベントを受け取るタイミングは同期が保証されません。
9519 多くのOSでは、HIDイベントにそれが発生した時間情報が付加されます。アプリケーションが知ることができるHIDの時間情報はそれに制限されます。
9521 HIDイベントに付加された時間情報が十分に正確であるならば、たとえば押し続けた時間は比較的正確に得ることができます。しかし視覚刺激提示からの反応時間をとることは、視覚刺激提示装置とHIDの同期が必要であり、そのままではできません。
9525 一般的なサウンドカードは、同期信号出力に用いるには向いていません。
9526 * 音声信号が途切れないように大き目のバッファを設けているため、数10ms以上のレイテンシがあります。
9527 ** ASIOとよばれる規格に対応したドライバ、サウンドカードをセットで使用すると、低レイテンシでの通信ができることがあります。一般に、5ms以下のレイテンシで動きます。
9528 * 信号の正確性があまり求められていないため、エラー訂正が不十分である場合や、通信失敗時に再送出をあきらめる場合があります。
9533 <div title="Tips: 独自のクラスを作る" modifier="YourName" modified="200803140504" created="200803062209" changecount="6">
9534 <pre>自分で作成した機能(コード)を多数のプロジェクトで使いまわす場合、ユーザ独自の拡張としてクラス化し、Psychlopsのライブラリフォルダ追加すると簡単に利用することができます。
9536 ここでは、例として扇形(パックマン図形)を多角形近似したものをクラス化してみます。このクラスは、以下の動作を行います。
9537 * 扇の半径、開口角、開口方向を指定して、多角形として作成する。
9538 * Rectangleと同じようにshift, centering, drawできるようにする。
9540 クラスの中身はひとまず置いておき、ひとまずこのクラスを拡張として登録してみましょう。テキストエディタ(Windows: メモ帳、MacOSX: Xcode→メニュバー→ファイル→新規ファイル)に下記コードをコピー&ペーストし、「pacman.cpp」という名前をつけて保存します。
9543 #include <psychlops_lib.h>
9544 using namespace Psychlops;
9547 // Polygonally approximated Pacman(sector) shape
9551 Psychlops::Point center;
9552 Psychlops::Point *vertices;
9557 Pacman(double radius, double open_angle_radian, double orientation_radian) {
9558 int end = floor(radius * (1 - Math::mod(open_angle_radian, 2*PI)/(2*PI)));
9559 num_vertices = end+2;
9560 vertices = new Psychlops::Point[num_vertices];
9562 double offset = Math::mod(orientation_radian+open_angle_radian/2.0, 2*PI);
9563 vertices[0].set(0,0);
9564 for(int i=0; i<end; i++) {
9566 radius*cos(offset+2.0*((double)i/radius)*PI),
9567 radius*sin(offset+2.0*((double)i/radius)*PI)
9570 vertices[num_vertices-1].set(
9571 radius*cos(offset+2*PI-open_angle_radian),
9572 radius*sin(offset+2*PI-open_angle_radian)
9580 Pacman& shift(double x, double y) {
9582 for(int i=0; i<num_vertices; i++) vertices[i].shift(x,y);
9585 Pacman& centering() {
9586 shift(Display::getHcenter()-center.getX(), Display::getVcenter()-center.getY());
9589 Pacman& draw(Color col) {
9590 Display::polygon(vertices, num_vertices, col);
9597 次に、pacman.cppをPsychlops拡張フォルダ(以下のフォルダの中の適当な場所)にコピーします。この際、MacOSXやWindows Vistaでは管理者パスワードを求められることがあります。
9599 * C:\Library\Frameworks\Psychlops.framework\Headers\Extentions
9600 * /Library/Frameworks/Psychlops.framework/Headers/Extentions
9602 ここでは「Samples」というフォルダをつくり、ここに置いてみます。
9604 次に、呼び出し側コードで今回作った独自拡張を#incluide指令を使って呼び出します。#include指令のオプションには、/Library/Frameworks/Psychlops.framework/Headers/より後の部分のファイルパスを指定します。
9606 #include <Extentions/Samples/pacman.cpp>
9611 #include <psychlops.h>
9612 using namespace Psychlops;
9613 #include <Extentions/Samples/pacman.cpp>
9615 void psychlops_main() {
9617 Canvas display(Canvas::fullscreen);
9618 Pacman pacman(100, PI/2, 0); // radius: 100 px, opening: 90 deg, orientation: 0 deg (right)
9621 while(!Input::get(Keyboard::esc)) {
9623 pacman.draw(Color::red);
9631 #includeでPacman拡張を呼び出すことで、いつでもPacman図形を使用することができるようになりました。
9636 Extentionとして別ファイルに分離したものを配布する際は、必要なすべてのファイルを配布する必要があります。もしExtentionフォルダに入れることが前提のファイルを配布するのであれば、その旨を添付テキスト等に記述し、登録するよう指示しましょう。拡張ではない通常の複数ファイル使用の場合は、プロジェクトへの追加が必要な旨を書きましょう。
9639 !! 上級者向け:複数ファイルから拡張を呼び出す場合
9640 この解説は、ユーザ側のプログラムが単一のファイルになるという初心者向けの状況を想定して書かれています。単一バイナリを生成するのに複数のファイルを使い、それらが同時にひとつの拡張を#includeするような場合は、この方法は使うことができません。このような場合、ヘッダファイルと実装の分離をする必要がありますが、これについては[[Wikipediaの項目|http://ja.wikipedia.org/wiki/%E3%83%98%E3%83%83%E3%83%80%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB]]を参照ください。
9650 簡単な解説については、下記サイトが参考になります。
9651 [[C++入門:クラス|http://wisdom.sakura.ne.jp/programming/cpp/cpp5.html]]
9653 また、psychlops.hの中には真のmain / WinMain関数が入っているため、これを2回以上includeするとコンパイラは本体にも拡張側にもmain関数があるかのようにみなし、衝突してしまいます。これを防ぐために、拡張側ではpsychlops_lib.hをインクルードしてください。
9656 #include <psychlops_lib.h>
9661 <div title="Tips: 画像のフィルタリング処理" modifier="YourName" modified="200803140632" created="200803100447" changecount="27">
9662 <pre>視覚刺激の中には特定の空間周波数でローパス・ハイパスのフィルタをかけた画像を使用することがあります。現在Psychlops本体ではFFT([[高速フーリエ変換|http://ja.wikipedia.org/wiki/%E9%AB%98%E9%80%9F%E3%83%95%E3%83%BC%E3%83%AA%E3%82%A8%E5%A4%89%E6%8F%9B]])機能を持っていませんが、既存のライブラリと併用することで実現できます。ここでは、MATLABでも採用されている[[FFTW|http://www.fftw.org/]]をPsychlops拡張化して利用してみます。
9665 * FFTWはGPLと有料ライセンスの選択制です。GPLを選択される場合、GPLの通称「感染条項」によりこのライブラリを使用したブログラムはすべてGPLとしなければなりません。このプログラムの配布に当たっては、下記の添付ファイルを必ず添付し、パッケージ作成に必要なソースコードをすべて公開しなければなりません。
9668 ! FFTWのPsychlops拡張のインストール
9672 # [[FFTW_demo_osx.zip|http://psychlops.l.u-tokyo.ac.jp/download/BasicSamples/FFTW_demo_osx.zip]]をダウンロードし、解答します。
9673 # 「~FFTW-Installer」をダブルクリックして拡張ををインストールします(要管理者権限)。
9674 #* インストール時にはTerminalが開き、ユーザのパスワードを聞かれますので、入力してください。
9676 次に、拡張を使ったプロジェクトを作成します。
9678 # 「External Frameworks and Libraries」を右クリックし、「追加」→「既存のファイルの追加」をクリックします
9679 ## ファイル選択ウィンドウが開いたら、右上の検索ボックスで以下のパスを''手打ちしてください''(コピー&ペースト不可)
9681 ## 開いたフォルダで以下のファイルを選択してください。リンクするライブラリ一覧に追加されます。
9683 # デモを実行する場合、~FilterDemo.cppの内容をコピー&ペーストします
9685 [img[Image/OSX/Xcode_addLib1.png]]
9686 [img[Image/OSX/Xcode_addLib2.png]]
9690 # [[FFTW_demo_win.zip|http://psychlops.l.u-tokyo.ac.jp/download/BasicSamples/FFTW_demo_win.zip]]をダウンロードし、解凍します。
9691 # 「FFTWのPsychlops拡張インストーラ.bat」を使って拡張ををインストールします(要管理者権限)。
9693 次に、拡張を使ったプロジェクトを作成します。
9695 # いったんテンプレートをビルドします。
9696 # 作成された実行ファイルと同じフォルダに「libfftw3-3.dll」をコピーします
9697 # プロジェクトのオプションを開き、リンクするライブラリに「libfftw3-3_bcc.lib」を加えます(Codeblocks+gccであれば「libfftw3-3.def」を加えます)
9698 # デモを実行する場合、~FilterDemo.cppの内容をコピー&ペーストします
9700 [img[Image/Win/Relo_addLib_1.png]]
9701 [img[Image/Win/Relo_addLib_2.png]]
9706 このサンプルでは、FFTWを用いてローパスフィルタを作成するための関数群が用意されています。配列の大きさが奇数の配列に対しては適用しないでください。
9712 |!Matrix makeLowPassFilter()|makeLowPassFilter(int height, int width, double frequency)|高さheight, 幅width, カットオフ周波数がfrequencyのローパスフィルターとなるMatrixを作成します。|
9713 |~|~|int height: ローパスフィルタの高さ|
9714 |~|~|int width: ローパスフィルタの幅|
9715 |~|~|double frequency: カットオフ周波数|
9717 内部では、カットオフより内側が1、カットオフより外側が0の行列を作成します。
9720 |!Matrix filterImage()|filterImage(Matrix image, Matrix filter)|imageに対してfilterで指定した行列を元にフィルタリングを適用します。|
9721 |~|~|Matrix image: フィルタをかける元画像|
9722 |~|~|Matrix filter: 適用するフィルタ|
9725 # imageにFFTをかけ、周波数表現を作る
9726 # 周波数表現でフィルタを要素ごとに乗算する
9727 # フィルタリング後の周波数表現を逆FFTする
9730 [img[image/tips/sample_FFTW.png]]
9735 2次元のFFTを実行します。ただし、fft2やifft2で使う配列は行および列が偶数である必要があります。奇数の場合は正常に動作しない可能性があります。
9737 fft2を用いてFFTを行った後の行列は中心に直流成分が存在し、周辺部に高周波数成分が存在します。行列の大きさがMatrix(rows, cols)の場合、直流成分はMatrix(rows / 2 + 1, cols / 2 + 1)に存在します。
9739 周波数分解能Δf(Matrixの要素が1つずれた場合にどれだけ周波数が変化するか)は、通常の視覚実験においてΔfは1 / 画像の大きさ(視角)となります。
9741 FFTにおけるΔfはサンプリング周波数 / データ数です。視覚実験においてはサンプリング周波数は視角1度が何ピクセルかに相当します。データ数は画像の高さもしくは幅のピクセル数となります。画像の高さや幅(ピクセル数)はサンプリング周波数に画像の高さや幅(視角)をかけたものに等しくなります。このことから通常の視覚実験においてΔfは1 / 画像の大きさ(視角)となります。
9743 |!void fft2()|fft2(Matrix src, Matrix dstReal, Matrix dstImaginary)|2次元のフーリエ変換を実行します。|
9744 |~|~|Matrix src:フーリエ変換を適用する配列。|
9745 |~|~|Matrix dstReal:フーリエ変換後の配列の実部が代入されます。|
9746 |~|~|Matrix dstImaginary:フーリエ変換後の配列の虚部が代入されます。|
9747 |!void fft2()|fft2(Matrix srcReal, Matrix srcImaginary, Matrix dstReal, Matrix dstImaginary)|2次元のフーリエ変換を実行します。|
9748 |~|~|Matrix srcReal:フーリエ変換を適用する配列の実部。|
9749 |~|~|Matrix srcImaginary:フーリエ変換を適用する配列の虚部。|
9750 |~|~|Matrix dstReal:フーリエ変換後の配列の実部が代入されます。|
9751 |~|~|Matrix dstImaginary:フーリエ変換後の配列の虚部が代入されます。|
9753 実行後の配列は中心部に低周波数領域があります。
9755 |!void ifft2()|ifft2(Matrix srcReal, const Matrix srcImaginary, Matrix dst)|2次元の逆フーリエ変換を実行します。|
9756 |~|~|Matrix srcReal:逆フーリエ変換を適用する配列の実部。|
9757 |~|~|Matrix srcImaginary:逆フーリエ変換後の配列の虚部。|
9758 |~|~|Matrix dst:逆フーリエ変換後の配列の絶対値が代入されます。|
9759 |!void ifft2()|ifft2(Matrix srcReal, Matrix srcImaginary, Matrix dstReal, Matrix dstImaginary)|2次元の逆フーリエ変換を実行します。|
9760 |~|~|Matrix srcReal:逆フーリエ変換を適用する配列の実部。|
9761 |~|~|Matrix srcImaginary:逆フーリエ変換後の配列の虚部。|
9762 |~|~|Matrix dstReal:逆フーリエ変換後の配列の実部が代入されます。|
9763 |~|~|Matrix dstImaginary:逆フーリエ変換後の配列の虚が代入されます。|
9768 |!Matrix normalize()|normalize(Matrix matrix)|行列の各要素の平均値が0、標準偏差が1となるよう正規化します。|
9769 |~|~|Matrix matrix: 正規化対象の行列|
9771 フィルター処理をしたあとの画像は[0, 1]の範囲に収まらず、表示可能な領域からはみ出してしまいます。そこで表示可能な範囲に収める処理が必要となります。フィルター処理をした後の画像の輝度値の分布は正規分布に従うことが知られています。そのため、一度正規化をします。normalizeはその正規化を行うための関数です。
9773 正規化を行った後はコントラストをかけ、平均輝度を足します。このときのコントラストはマイケルソンコントラストではなく、RMSコントラストです。RMSコントラストが1/3を超えると表示可能な領域からはみ出してしまう点が多くなるのでなるべくこの値を最大値としてください。
9775 最後に、それでも表示可能な領域が残る可能性は存在しますので、それを表示可能な領域に納めます。
9778 |!Matrix makeNoise()|makeNoise(int height, int width)|高さheight、幅widthのホワイトノイズ行列を作成します。|
9779 |~|~|int height: 縦の大きさ|
9780 |~|~|int width: 横の大きさ|
9786 以下の関数はfft2やifft2内部で呼び出される関数です。通常ユーザーがこの関数を実行する必要はありません。
9788 |!Matrix fftShift()|fftShift(const Matrix &matrix)|FFTをかけた後の周波数ごとの分布を移動させます。|
9789 |~|~|Matrix matrix: シフトを行う対象を指定します|
9790 FFTWではFFTを行った後の行列は低周波成分が4隅にあり、中心部には高周波成分が存在します。このままではフィルターを作成する際に計算しづらいので、中心部に低周波数成分を、周辺部に高周波数成分が並ぶようにします。そのための関数です。
9792 逆に中心にある低周波数成分を周辺部に、周辺にある高周波数成分を中心にするように並べ替えることもできます。
9794 |!void fftExecute()|fftExecute(const Matrix &srcReal, const Matrix &srcImaginary, Matrix &dstReal, Matrix &dstImaginary, fftw_complex *input, fftw_complex *output, const fftw_plan &plan)|フーリエ変換を実行します。|
9795 |~|~|Matrix srcReal:フーリエ変換を適用する配列の実部。|
9796 |~|~|Matrix srcImaginary:フーリエ変換を適用する配列の虚部。|
9797 |~|~|Matrix dstReal:フーリエ変換後の配列の実部が代入されます。|
9798 |~|~|Matrix dstImaginary:フーリエ変換後の配列の虚部が代入されます。|
9799 |~|~|fftw_complex *input: FFTW作業用の変数|
9800 |~|~|fftw_complex *output: FFTW作業用の変数|
9801 |~|~|fftw_plan plan: FFTW作業用の変数|
9804 <div title="Tips: 自分の機器の描画性能を確かめる" modifier="YourName" modified="200803062209" created="200803062208" changecount="2">
9805 <pre>Psychlopsの実行速度は環境によって異なります。現在の実行環境での実行速度を知りたい場合は、簡易に計測するプログラムを用意しましたので、これを実行して調べることができます。下記ファイルをダウンロードし、コンパイルして実行してみてください。
9807 [[Benchmark.cpp|http://psychlops.l.u-tokyo.ac.jp/download/BasicSamples/Benchmark.cpp]]
9809 2~5分程度のテストを行い、各種命令の「フレームが落ちずに実行できる最大処理能力」を測ります。結果は終了後に画面に表示されるほか、実行ファイルと同じ場所に結果ファイルを出力します(Mac OS Xの場合、プロジェクトフォルダ/build/Release/)。
9811 現在のバージョンでは4つのテストを行います。各テストの項目のうち、
9812 * 上「今回のテストで使われたリフレッシュレートで、1フレームで表示できる最大数」
9813 * 下「100 Hzで正規化した場合に、1フレームで表示できる最大サイズ」(この値が1000の場合、100 Hz時に1000x1000ピクセルの画像が1枚表示可能ということを示します。)
9816 Canvas.pix命令の処理能力を計測します。
9817 GPUの頂点処理能力と、CPUからGPUに命令を出す帯域幅が主な律速要因になります。
9820 Canvas.rect命令の処理能力を計測します。
9821 GPUのピクセル処理能力がが主な律速要因になります。
9824 オフスクリーン(Image.draw)の処理能力を調べます。
9825 メインメモリからグラフィックカードへImageを転送する帯域幅が主な律速要因になります。
9828 Image.quicken()されたオフスクリーン画像の処理能力を計測します。
9829 GPUのピクセル処理能力がが主な律速要因になります。
9833 また、計測結果には10%前後の誤差があります。</pre>
9835 <div title="Tips::CanvasクラスとDisplayクラス" modifier="Psychlops_Admin" modified="200708230528" created="200708230500" changecount="1">
9836 <pre>PsychlopsではデフォルトのCanvasに対して(インスタンス名を無視して)描画を強制的に行うDisplayクラスが用意されています。
9837 現バージョンでのPsychlopsはCanvasを同時に1つしか確保できない仕様となっているので(つまり宣言したCanvasインスタンスは常にデフォルトになる)、Displayクラスを使って描画を行っても、インスタンス名を使って描画を行った場合と同じ結果が得られます。たとえば、以下の2つのプログラムは同じ結果になります。
9839 <インスタンス名を使用したソース>
9840 #include <psychlops.h>
9841 using namespace Psychlops;
9842 void psychlops_main() {
9843 Canvas sampleB(Canvas::fullscreen);
9844 sampleB.clear(Color::blue);
9846 while(!Input::get(Keyboard::spc));
9851 <Displayクラスを使用したソース>
9852 #include <psychlops.h>
9853 using namespace Psychlops;
9854 void psychlops_main() {
9855 Canvas sampleB(Canvas::fullscreen);
9856 Display::clear(Color::blue);
9858 while(!Input::get(Keyboard::spc));
9862 また、psychlops_main外の関数で描画を行うときには、Canvasのポインタを渡す必要のないDisplayクラスを使った方法のほうが便利かもしれません。下の例を比べて見てください。
9865 <インスタンス名を使用したソース>
9866 #include <psychlops.h>
9867 using namespace Psychlops;
9869 void stimulusDraw(Canvas &sample){
9870 sample.clear(Color::blue);
9872 while(!Input::get(Keyboard::spc));
9875 void psychlops_main() {
9876 Canvas sampleB(Canvas::fullscreen);
9877 stimulusDraw(sampleB);
9883 <Displayクラスを使用したソース>
9884 #include <psychlops.h>
9885 using namespace Psychlops;
9886 void stimulusDraw(){
9887 Display::clear(Color::blue);
9889 while(!Input::get(Keyboard::spc));
9891 void psychlops_main() {
9892 Canvas sampleB(Canvas::fullscreen);
9900 <div title="Update from 1.0 to 1.3" modifier="Psychlops_DevelopperG" modified="200912010831" created="200908100506" changecount="18">
9901 <pre>* 2.1.1節のCanvasの宣言をマルチスクリーンに対応したバージョンに書き換え
9904 * [[2.3.1 Canvas::msg()命令単体での文字列描画]]
9905 * [[2.3.2 Lettersクラスを用いた文字列描画]]
9906 * [[3.5 文字列のレンダリングの仕様と関連情報]]
9908 * 3.3.1節にCanvas::toの注意事項とImage::release()についての説明を追加。
9910 [[3.3.3 複数の画像ファイルからアニメーションを作成する]]
9912 [[4. 複雑な描画を行う2(複数オブジェクトのグループ化と回転・拡大縮小)]]
9913 [[8. 透明度を使って刺激描画を高速化する]]
9917 * [[Tips: Shapeクラスの概要]]
9918 * [[Tips: Strokeクラスの概要]]
9919 * [[Tips: Canvasを宣言しないと動作しない命令群]]
9920 * [[Tips: OS環境による関数名の衝突について]]
9922 setGammaTable(std::vector<double> Cr, std::vector<double> Cg, std::vector<double> Cb)のコード例
9931 * [[Canvas::pix(double, double, double)という命令はコンパイルできないのですか?]]
9932 * [[止まった絵を描画するときCanvas.flip()を単独でwhileループの中に書くと、画面がちらつきます。]]
9933 * [[特定の命令に対してコンパイル時にエラーが出る]]
9938 <div title="memo" modifier="Psychlops_Admin" modified="200708280427" created="200708280335" changecount="2">
9943 background: #f0efc0;
9947 background: #f0efc0;
9948 border: 2px dotted #6a6;
9957 padding: 2.0em 0em 0.7em 1em;
9961 padding: 2.0em 0em 0.7em 1em;
10008 border: 1px solid #6a6;
10038 font: 80% arial,sans-serif;
10042 position: absolute;
10045 padding: 0em 1em 0em 1em;
10047 background-color: #eee;
10053 position: absolute;
10060 .headerForeground {
10064 .viewer blockquote {
10067 border-left: 3px solid #666666;
10068 padding-left: 1.0em;
10069 margin-left: 2.5em;
10082 padding-left:0.1em;
10084 margin-bottom:10px;
10087 border-bottom:2px solid #ccc;
10091 <div title="wiki文法" modifier="Psychlops_DevelopperG" modified="200912010832" created="200707220320" changecount="6">
10092 <pre>http://hsj.jp/junknews/archives/tiddlywiki_susume.html
10096 [img[image/canvasrect.png]]
10099 <div title="このマニュアルの見方について(ここをクリックしてください)" modifier="PsychlopsAdmin" created="200709171623" changecount="1">
10100 <pre>このマニュアルはTiddelyWikiによって作成されています。
10101 今、このウィンドウが開いたように、見出しをダブルクリックすると、該当する記事のウィンドウが自動的に開きます。必要なくなった(見終わった)ウィンドウは、見出しの左上のコマンドから"close"をクリックすれば閉じることができます。
10102 Editを押すと内容を改変して、ご自分でコメントをメモすることができますが、改変した部分は元には戻せないのでご注意ください。
10103 また、画面左端にメニューバーから各記事にアクセスしたり記事の内容を検索することもできます。</pre>
10105 <div title="その他" modifier="Kazushi Maruya" modified="200712180151" created="200712131752" changecount="4">
10106 <pre>* [[Psychlopsについて開発者に質問するには?]]
10107 * [[素晴らしいデモプログラムを作成したので、公開したい]]
10108 * [[Psychlopsを既存・自作のC/C++ライブラリと併用することができますか?]]</pre>
10110 <div title="カラーモード" modifier="Psychlops_Admin" created="200709041935" changecount="1">
10111 <pre>|GRAY|グレースケールの輝度値|
10112 |RGB|色の三原色に基づいた赤・緑・青で構成された色|
10113 |RGBA|RGBに透明度が追加された色|
10114 詳しくは[[2.2.5 描画する図形の色を指定する-Colorクラス-]]を参照</pre>
10116 <div title="ガンマ補正" modifier="YourName" modified="200802130705" created="200802130649" changecount="4">
10119 <div title="キーコード表" modifier="Psychlops_DevelopperG" modified="200908100437" created="200708240330" changecount="3">
10122 |アルファベットキー (a~z)|a~zのまま(小文字)|
10124 |キーボード最上段(1~10) |1|2|3|4|5|6|7|8|9|0|
10125 |~|one|two|three|four|five|six|seven|eight|nine|zero|
10127 |特殊キー|Return|Space|Escape|カーソル上|カーソル下|カーソル左|カーソル右|Shift|Control|Alt|
10128 |~| rtn | spc | esc | up | down | left | right | shift | ctrl | alt |
10130 |テンキー|0|1|2|3|4|5|6|7|8|9|
10131 |~|pad0|pad1|pad2|pad3|pad4|pad5|pad6|pad7|pad8|pad9|
10132 |~|ー|>|+|>|*|>|/|>|Enter|>|.|
10133 |~|padminus|>|padpuls|>|padasterisk|>|padslash|>|padenter|>|padperiod|>|
10136 |左ボタン|右ボタン|中ボタン|すべてのボタンのいずれか|
10137 | left | right | middle | any |
10140 <div title="例1: pushedを使ったソース" modifier="Psychlops_Admin" modified="200708272051" created="200708272048" changecount="2">
10143 #include <psychlops.h>
10144 using namespace Psychlops;
10147 Psychlops::Rectangle rect(60,60), rect2(60,60);
10149 void psychlops_main() {
10151 Canvas display(Canvas::fullscreen);
10152 display.clear(Color::gray);
10157 while(!Input::get(Keyboard::spc)){
10160 display.clear(Color::gray);
10162 if(Input::get(Keyboard::c, Keyboard::pushed))code=0; //centering
10163 if(Input::get(Keyboard::left, Keyboard::pushed))code=1;
10164 else if(Input::get(Keyboard::right, Keyboard::pushed))code=2;
10168 case 0: rect.centering(); break;
10169 case 1: rect.shift(-1,0); break;
10170 case 2: rect.shift(1,0); break;
10173 rect2.centering().draw(Psychlops::Color(0.75,0.75,0.75));
10174 rect.draw(Psychlops::Color(1.0,1.0,1.0,0.25));
10181 <div title="例2: pressedを使ったソース" modifier="Psychlops_Admin" modified="200708272052" created="200708272051" changecount="2">
10183 *例2: pressedを使ったソース
10184 #include <psychlops.h>
10185 using namespace Psychlops;
10188 Psychlops::Rectangle rect(60,60), rect2(60,60);
10190 void psychlops_main() {
10192 Canvas display(Canvas::fullscreen);
10193 display.clear(Color::gray);
10198 while(!Input::get(Keyboard::spc)){
10201 display.clear(Color::gray);
10203 if(Input::get(Keyboard::c, Keyboard::pushed))code=0; //centering
10204 if(Input::get(Keyboard::left, Keyboard::pressed))code=1;
10205 else if(Input::get(Keyboard::right, Keyboard::pressed))code=2;
10209 case 0: rect.centering(); break;
10210 case 1: rect.shift(-1,0); break;
10211 case 2: rect.shift(1,0); break;
10214 rect2.centering().draw(Psychlops::Color(0.75,0.75,0.75));
10215 rect.draw(Psychlops::Color(1.0,1.0,1.0,0.25));
10221 <div title="例3: releasedを使ったソース" modifier="Psychlops_Admin" created="200708272053" changecount="1">
10223 *例3: releasedを使ったソース
10224 #include <psychlops.h>
10225 using namespace Psychlops;
10228 Psychlops::Rectangle rect(60,60), rect2(60,60);
10230 void psychlops_main() {
10232 Canvas display(Canvas::fullscreen);
10233 display.clear(Color::gray);
10238 while(!Input::get(Keyboard::spc)){
10241 display.clear(Color::gray);
10243 if(Input::get(Keyboard::c, Keyboard::pushed))code=0; //centering
10245 //state "pressed" and "released" Demo
10246 if(Input::get(Keyboard::left, Keyboard::pressed))code=1;
10247 else if(Input::get(Keyboard::right, Keyboard::pressed))code=2;
10249 if(Input::get(Keyboard::left, Keyboard::released))code=3;
10250 else if(Input::get(Keyboard::right, Keyboard::released))code=3;
10254 case 0: rect.centering(); break;
10255 case 1: rect.shift(-1,0); break;
10256 case 2: rect.shift(1,0); break;
10257 case 3: rect.centering(); break;
10260 rect2.centering().draw(Psychlops::Color(0.75,0.75,0.75));
10261 rect.draw(Psychlops::Color(1.0,1.0,1.0,0.25));
10267 <div title="例4: pushed,pressed,releasedを使ったソース" modifier="Psychlops_Admin" modified="200708272058" created="200708272055" changecount="1">
10270 #include <psychlops.h>
10271 using namespace Psychlops;
10273 double x=0,y=0, dX,dY;
10275 Psychlops::Rectangle rect(60,60), rect2(60,60);
10277 void psychlops_main() {
10279 Canvas display(Canvas::fullscreen);
10280 display.clear(Color::gray);
10282 dX= display.getHcenter();
10283 dY= display.getVcenter();
10289 while(!Input::get(Keyboard::spc)){
10293 //state "pushed" Demo
10294 if(Input::get(Keyboard::c, Keyboard::pushed))code=0; //centering
10296 if(Input::get(Keyboard::a, Keyboard::pushed))code=1;
10297 else if(Input::get(Keyboard::s, Keyboard::pushed))code=2;
10299 //state "pressed" Demo
10300 if(Input::get(Keyboard::z, Keyboard::pressed))code=3;
10301 else if(Input::get(Keyboard::x, Keyboard::pressed))code=4;
10303 //state "pressed" and "released" Demo
10304 if(Input::get(Keyboard::q, Keyboard::pressed)){
10306 if(oldcode==5||oldcode==7)code=7;
10308 else if(Input::get(Keyboard::w, Keyboard::pressed)){
10310 if(oldcode==6||oldcode==8)code=8;
10312 if(Input::get(Keyboard::q, Keyboard::released))code=9;
10313 else if(Input::get(Keyboard::w, Keyboard::released))code=9;
10317 case 0: rect.centering(); break;
10318 case 1: rect.shift(-1,0); break;
10319 case 2: rect.shift(1,0); break;
10320 case 3: rect.shift(-1,0); break;
10321 case 4: rect.shift(1,0); break;
10322 case 5: x=ceil(rect.getHcenter()); y=ceil(rect.getVcenter()); rect.shift(-1,0);break;
10323 case 6: x=ceil(rect.getHcenter()); y=ceil(rect.getVcenter()); rect.shift(1,0);break;
10324 case 7: rect.shift(-1,0); break;
10325 case 8: rect.shift(1,0); break;
10326 case 9: rect.centering(x,y); break;
10328 display.clear(Color::gray);
10331 display.message("KeyState:", dX-100, dY-120, Color::green);
10332 display.var(code, dX,dY-120, Color::green);
10333 display.message("X:", dX-100, dY-100, Color::white);
10334 display.var(ceil(rect.getHcenter()),dX,dY-100, Color::white);
10335 display.message("Y:", dX-100, dY-80, Color::white);
10336 display.var(ceil(rect.getVcenter()),dX,dY-80, Color::white);
10339 rect2.centering(x,y).draw(Psychlops::Color(0.75,0.75,0.75));
10340 rect.draw(Psychlops::Color(1.0,1.0,1.0,0.25));
10342 else rect.draw(1.0);
10347 このプログラム内で使用されているCanvas::message, Canvas::var命令については[[6.1 時間を計測する]]を参照してください。
10350 <div title="例5: Mouse::pressedを使ったソース" modifier="Psychlops_Admin" created="200708272055" changecount="1">
10353 #include <psychlops.h>
10354 using namespace Psychlops;
10358 Psychlops::Rectangle rect(60,60), rect2(60,60);
10360 void psychlops_main() {
10362 Canvas display(Canvas::fullscreen);
10363 display.clear(Color::gray);
10368 while(!Input::get(Keyboard::spc)){
10371 display.clear(Color::gray);
10373 if(Input::get(Keyboard::c, Keyboard::pushed))code=0; //centering
10375 //state "pressed" and "released" Demo
10376 if(Input::get(Mouse::left, Mouse::pressed))code=1;
10377 else if(Input::get(Mouse::right, Mouse::pressed))code=2;
10381 case 0: rect.centering(); break;
10382 case 1: rect.shift(-1,0); break;
10383 case 2: rect.shift(1,0); break;
10386 rect2.centering().draw(Psychlops::Color(0.75,0.75,0.75));
10387 rect.draw(Psychlops::Color(1.0,1.0,1.0,0.25));
10394 <div title="例6: マウスのデモ" modifier="Psychlops_Admin" created="200708272056" changecount="1">
10397 #include <psychlops.h>
10398 using namespace Psychlops;
10400 double dcX,dcY, rectcol;
10401 double rectsize=80;
10402 int display_cursor=1, in_rect=0;
10403 Psychlops::Rectangle rect(rectsize*2,rectsize*2);
10404 Psychlops::Range rngx,rngy;
10406 void psychlops_main() {
10408 Canvas display(Canvas::fullscreen);
10409 display.clear(Color::gray);
10410 AppState::setThreadPriority(AppState::HIGH);
10412 dcX= display.getHcenter();
10413 dcY= display.getVcenter();
10416 //Move mouse cursor to the center
10420 while(!Input::get(Keyboard::spc)){
10421 display.clear(Color::gray);
10423 //set current rect area
10424 rect.getHcenter()-rectsize<rngx<rect.getHcenter()+rectsize;
10425 rect.getVcenter()-rectsize<rngy<rect.getVcenter()+rectsize;
10427 if(display_cursor)Mouse::show();
10428 else Mouse::hide();
10430 if(Input::get(Mouse::right, Mouse::pushed))display_cursor=1-display_cursor;
10432 rect.centering(Mouse::x, Mouse::y);
10436 rect.draw(rectcol);
10438 if(Input::get(Mouse::left, Mouse::pushed)){
10439 if (rngx.includes(Mouse::x) && rngy.includes(Mouse::y))in_rect=1;
10442 if(Input::get(Mouse::left, Mouse::released)) {
10443 if (rngx.includes(Mouse::x) && rngy.includes(Mouse::y))in_rect=0;
10449 <div title="入力系のクラスに関して" modifier="Kazushi Maruya" modified="200712131742" created="200712131740" changecount="2">
10450 <pre>* [[キーボード/マウスの反応を取得したい|Input::get()]]
10451 * [[キーボード/マウスの反応をリセットしたい|Input::refresh()]]
10452 * [[マウスカーソルを表示したい|Mouse::show()]]
10453 * [[マウスカーソルを非表示にしたい|Mouse::hide()]]</pre>
10455 <div title="基本的な命令の使用" modifier="Psychlops_DevelopperG" modified="200908100506" created="200712131619" changecount="5">
10456 <pre>* [[描画画面を取得したい|2.1 描画領域の宣言]]
10457 * [[取得した画面の情報を取得したい|2.1.3 Canvasのメンバ取得と変数の表示]]
10458 * [[画面を特定の色で塗りつぶしたい|2.1.2 Canvasの基本構造と操作]]
10459 * [[画面に点を打ちたい|2.2.1 画面上に点を描画する-Pointクラス1-]]
10460 * [[画面に四角形を書きたい|2.2.3 画面上に四角形を描画する-Rectangleクラス1-]]
10462 * [[画面に円を描きたい|2.2.4 画面上に円を描画する]]
10463 * [[描画色を指定したい|2.2.5 描画する図形の色を指定する-Colorクラス-]]
10464 * [[リアルタイムでは描画できない複雑な図形描画を事前に計算したい|3. 複雑な描画を行う(オフスクリーンへの描画)]]
10465 * [[自然画像を読み込み・表示したい|3.3 画像ファイルの取り扱い]]
10467 * [[図形をXX msecだけ提示したい|2.1.2 Canvasの基本構造と操作]]
10468 * [[時間を正確に計測したい|5.1 時間を計測する]]
10470 * [[キーボードの入力を取得したい|4.1 外部装置(キーボード/マウス)の操作内容を取得する]]
10471 * [[マウス入力を取得したい|4.1 外部装置(キーボード/マウス)の操作内容を取得する]]
10472 * [[配列の中身をファイルに記録したい|4.2 ファイルの入出力]]
10474 * [[特定の命令に対してコンパイル時にエラーが出る]]
10477 <div title="数値計算" modifier="Kazushi Maruya" modified="200712131748" created="200712131747" changecount="2">
10478 <pre>*[[簡単な行列演算がしたい|5.3 行列演算を行う]]
10479 *[[配列の要素をランダムに並べ替えたい|Math::shuffle]]
10480 *[[負の値、浮動小数点型に対して剰余を求めたい|Math::mod]]</pre>
10482 <div title="日本語化" modifier="Psychlops_Admin" modified="200709121644" created="200707220238" tags="systemConfig systemConfigForce" changecount="19">
10483 <pre>// Shadow tiddlers for emergencies
10484 config.shadowTiddlers.SideBarOptions = "<<search>><<closeAll>><<permaview>><<newTiddler>><<saveChanges>><<slider chkSliderOptionsPanel OptionsPanel 設定 'TiddlyWikiの設定を変更する。'>>";
10485 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]]";
10486 config.shadowTiddlers.AdvancedOptions = "<<option chkOpenInNewWindow>> 新しいウィンドウでリンクを開く\n<<option chkSaveEmptyTemplate>> 空のテンプレートファイル(empty.html)を保存する\n<<option chkToggleLinks>> 既に開いているTiddlerをクリックした時に閉じる\n^^(override with Control or other modifier key)^^";
10487 config.shadowTiddlers.SideBarTabs = "<<tabs txtMainTab '更新順' '更新順に表示する' TabTimeline 'タグ' 'タグ一覧' TabTags '詳細' '詳細' TabMore>>";
10488 config.shadowTiddlers.TabTimeline = "<<timeline>>";
10489 config.shadowTiddlers.TabTags = "<<allTags>>";
10490 config.shadowTiddlers.TabMore = "<<tabs txtMoreTab '全部' 'すべてのTiddler' TabMoreAll '定義なし' '定義されていないTiddler一覧' TabMoreMissing 'リンク切れ' 'リンク切れしている単独のTiddler' TabMoreOrphans>>";
10491 config.shadowTiddlers.TabMoreAll = "<<list all>>";
10492 config.shadowTiddlers.TabMoreMissing = "<<list missing>>";
10493 config.shadowTiddlers.TabMoreOrphans = "<<list orphans>>";
10495 config.messages.customConfigError = "カスタムコンフィグにてエラー発生。 - %0";
10496 config.messages.savedSnapshotError = "保存に失敗しました。";
10497 config.messages.subtitleUnknown = "(不明)";
10498 config.messages.undefinedTiddlerToolTip = "'%0'というTiddlerはまだ存在しません。";
10499 config.messages.externalLinkTooltip = "(外部リンク) %0";
10500 config.messages.noTags = "タグの設定されていないTiddler";
10501 config.messages.notFileUrlError = "変更を保存したい場合、このTiddlyWikiをファイルに保存(ダウンロード)する必要があります。";
10502 config.messages.cantSaveError = "このブラウザでは保存することができません。できればFirefoxを使ってください。";
10503 config.messages.invalidFileError = "元のファイル '%0' は妥当なTiddlyWikiのファイルではありません。";
10504 config.messages.backupSaved = "バックアップファイルを保存しました。";
10505 config.messages.backupFailed = "バックアップファイルの保存に失敗しました。";
10506 config.messages.rssSaved = "RSSファイルを保存しました。";
10507 config.messages.rssFailed = "RSSファイルの保存に失敗しました。";
10508 config.messages.emptySaved = "空のテンプレートファイルを保存しました。";
10509 config.messages.emptyFailed = "空のテンプレートファイルの保存に失敗しました。";
10510 config.messages.mainSaved = "TiddlyWikiファイルを保存しました。";
10511 config.messages.mainFailed = "TiddlyWikiファイルの保存に失敗しました。修正内容は保存されていません。";
10512 config.messages.macroError = "マクロ実行時エラー: '%0'";
10513 config.messages.overwriteWarning = "'%0'というTiddlerはすでに存在します。OKを選択すると上書きします。";
10514 config.messages.unsavedChangesWarning = "警告! 保存されていない変更が存在します。\n\nOKを選択:保存\nCancelを選択:編集内容を破棄";
10515 config.messages.months = ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月","12月"];
10516 config.messages.dates.days = ["日", "月", "火", "水", "木", "金", "土"];
10517 // Tiddler表示時ツールバーなど
10518 config.views.wikified.tag.labelNoTags = "no tags";
10519 config.views.wikified.tag.labelTags = "タグ = ";
10520 config.views.wikified.tag.tooltip = "タグ'%0'が設定されたTiddlerを閲覧する。";
10521 config.views.wikified.tag.openAllText = "タグ'%0'のTiddlerをすべて開く。";
10522 config.views.wikified.tag.openAllTooltip = "このTiddlerをすべて開く。";
10523 config.views.wikified.tag.popupNone = "タグ'%0'はこれ以外のTiddlerに設定されていません。";
10524 config.views.wikified.toolbarEdit = "編集";
10525 config.views.wikified.toolbarPermalink = "permalink";
10526 config.views.wikified.toolbarReferences = "参照一覧";
10527 config.views.wikified.toolbarReferences.popupNone = "参照されていません。";
10528 config.views.wikified.defaultText = "'%0'はまだ存在していません。ダブルクリックで作成できます。";
10529 // Tiddler編集時ツールバーなど
10530 config.views.editor.tagPrompt = "[[tags]]のスタイルでtagsをスペース区切りに入力します。";
10531 config.views.editor.tagChooser.text = "タグ";
10532 config.views.editor.tagChooser.tooltip = "既存のタグから追加するものを選択してください。";
10533 config.views.editor.tagChooser.popupNone = "タグが設定されていません。";
10534 config.views.editor.toolbarDone = "確定";
10535 config.views.editor.toolbarCancel = "編集中止";
10536 config.views.editor.toolbarDelete = "削除";
10538 config.views.editor.defaultText = "'%0'の内容を入力してください。";
10539 // Each has a 'handler' member that is inserted later
10540 config.macros.search.label = "Wiki内検索";
10541 config.macros.search.prompt = "このTiddlyWiki内を検索します。"
10542 config.macros.search.successMsg = "%0 件見つかりました。- %1";
10543 config.macros.search.failureMsg = "%0 に該当するデータはありません。";
10544 config.macros.timeline.dateFormat = "YYYY年MM月DD日";
10545 config.macros.allTags.tooltip = "タグ'%0'のデータをすべて表示します。";
10546 config.macros.allTags.noTags = "タグが設定されていません。";
10547 config.macros.list.all.prompt = "アルファベット順のTiddler一覧";
10548 config.macros.list.missing.prompt = "リンクはされているが定義されていないTiddler一覧";
10549 config.macros.list.orphans.prompt = "他のどこからもリンクされていないTiddler一覧";
10550 config.macros.closeAll.label = "すべて閉じる";
10551 config.macros.closeAll.prompt = "編集されているもの以外の表示されているすべてTiddlerを閉じます。";
10552 config.macros.permaview.label = "permaview";
10553 config.macros.permaview.prompt = "現在表示されているTiddlerの状態を表示するURLです。"
10554 config.macros.saveChanges.label = "保存";
10555 config.macros.saveChanges.prompt = "すべてのTiddlerを保存します。";
10556 config.macros.newTiddler.label = "新規作成";
10557 config.macros.newTiddler.Title = "新規Tiddler";
10558 config.macros.newTiddler.prompt = "新しいTiddlerを作成します。";
10559 config.macros.newJournal.label = "新規日報";
10560 config.macros.newJournal.prompt = "新しいTiddlerを現在の日時をタイトルとして作成します";</pre>
10562 <div title="時間計測系のクラスに関して" modifier="Kazushi Maruya" created="200712131743" changecount="1">
10563 <pre>* [[現在のCPU時間を取得したい|Clock::update()]]
10564 * [[clock型の値をmsec単位で変数に代入したい|Clock::at_msec()]]</pre>
10566 <div title="止まった絵を描画するときCanvas.flip()を単独でwhileループの中に書くと、画面がちらつきます。" modifier="Psychlops_DevelopperG" created="200908100500" changecount="1">
10567 <pre>Psychlopsの仕様として、描画はすべてダブルバッファリングの裏画面に行っています。
10568 このため、描画命令を書いた後に、単にCanvas.flip()命令だけを繰り返すと、2枚に1枚は何も描画されていない画面が表示されます。
10569 止まった絵を表示させたいときにこれを回避するには、2枚の画面の両方に同じ刺激を描画してください。</pre>
10571 <div title="特定の命令に対してコンパイル時にエラーが出る" modifier="Psychlops_DevelopperG" modified="200908100456" created="200908100444" changecount="6">
10573 Psychlopsで使っている命令をコンパイルするときに、ある特定のクラスについて大量のエラーが出ることがあります。
10574 とくに、変数の宣言自体についてエラーが出る場合のの原因の一つとして、ほかのライブラリ、特にOS・コンパイラ標準のライブラリにあるクラスとこの特定のクラスが同じ名前を持っているために、名前が衝突してしまってコンパイルができなくなっていることが考えられます。
10575 このような場合、以下のように スコープ演算子 "Psychlops::" をクラス名の前に付け加えることで問題を解決できます。
10578 !!! (1-1) Macintoshでコンパイル時に、Point型を使った命令すべてに対してエラーが出ます。
10580 Macintoshでは標準ライブラリにPoint型がすでにあるために、名前が衝突してしまい、コンパイルができません。スコープ演算子Psychlops::を使って、衝突を回避してください。
10582 (コンパイル不可)Point A[n]
10583 (コンパイル可) Psychlops::Point A[n]
10586 !!! (1-2) Windowsでコンパイル時に、Rectangle型を使った命令すべてに対してエラーが出ます。
10588 (1-1)と同様にWindowsでは標準ライブラリにRectangle型がすでにあるために、名前が衝突してしまい、コンパイルができません。スコープ演算子Psychlops::を使って、衝突を回避してください。
10590 (コンパイル不可)Rectangle A[n]
10591 (コンパイル可) Psychlops::Rectangle A[n]
10594 !!! (1-3) random()命令に対してエラーが出ます。
10596 スコープ演算子Psychlops::を使って、衝突を回避してください。
10599 (コンパイル可) Psychlops::random()
10602 名前空間の問題についての詳細は、[[OS環境による関数名の衝突について]]をご覧ください。
10605 <div title="特定の種類の刺激描画" modifier="YourName" modified="200802191333" created="200712131629" changecount="3">
10606 <pre>[[ランダムドットを書きたい|3.2 オフスクリーンを用いた描画例-ランダムドットパターン-]]
10607 [[ダイナミックランダムドットを書きたい|Psychlops::random]]
10608 [[一定方向に運動するランダムドットを書きたい|3.2.3 ランダムドットキネマトグラムの描画]]
10609 [[ランダムドットステレオグラムを書きたい|Tips: ランダムドットステレオグラムの描画コード]]
10611 [[垂直・水平方位の正弦縞を書きたい|2.2.6 画面上に縞を描画する-Rectangleクラス2-]]
10612 [[任意の方位の正弦縞を書きたい|2.2.7 画面上に縞を描画する-Pointクラス2-]]
10613 [[運動する縞を書きたい|2.2.8 運動する図形を描画する-Rectangleクラス3-]]
10614 [[Plaid刺激を書きたい|2.2.7 画面上に縞を描画する-Pointクラス2-]]
10615 [[Gabor Patchを書きたい|6.2 Gaborパッチの事前描画]]
10617 [[インタラクティブなデモプログラムを作成したい|7. デモを作成する]]</pre>
10619 <div title="第II部 Psychlopsの応用" modifier="Psychlops_DevelopperG" modified="200909150151" created="200908100512" changecount="2">
10620 <pre>[[1. 既成クラスを用いた簡単な実験プログラムを作成する]]
10621 [[1.1 実験計画のプログラミング]]
10622 [[1.2 Gaborパッチの事前描画]]
10623 [[1.3 各試行部分のプログラミング]]
10627 [[2.1 独立変数Independent]]
10628 [[2.2 デモ環境の表示]] </pre>
10630 <div title="第I部 Psychlopsの基本" modifier="Psychlops_DevelopperG" modified="200908190223" created="200908100511" changecount="3">
10631 <pre>[[1. インストールを行う]]
10635 [[1.2 インストール作業(Mac) ]]
10636 [[1.3 インストール作業(Windows) ]]
10637 [[1.4 更新とアンインストール]]
10638 [[1.5 Psychlopsの基本構造]]
10639 [[1.6 Psychlopsを使ったプログラミング]]
10640 [[1.6.1 プログラミングとは?]]
10641 [[1.6.2 Psychlopsを使ったプログラムの形]]
10648 [[2.1.1 Canvasの宣言]]
10649 [[2.1.2 Canvasの基本構造と操作]]
10650 [[2.1.3 Canvasのメンバ取得と変数の表示]]
10652 [[2.2.1 画面上に点を描画する-Pointクラス1-]]
10653 [[2.2.2 画面上に線を描画する]]
10654 [[2.2.3 画面上に四角形を描画する-Rectangleクラス1-]]
10655 [[2.2.4 画面上に円を描画する]]
10656 [[2.2.5 描画する図形の色を指定する-Colorクラス-]]
10657 [[2.2.6 画面上に縞を描画する-Rectangleクラス2-]]
10658 [[2.2.7 画面上に縞を描画する-Pointクラス2-]]
10659 [[2.2.8 運動する図形を描画する-Rectangleクラス3-]]
10661 [[3. 複雑な描画を行う1(オフスクリーンへの描画)]]
10662 [[3.1 オフスクリーン描画の基本]]
10663 [[3.1.1 オフスクリーン領域の宣言]]
10664 [[3.1.2 オフスクリーン上に点・四角形を描画する]]
10665 [[3.1.3 オフスクリーン上の任意の位置に描画する]]
10667 [[3.2 オフスクリーンを用いた描画例-ランダムドットパターン-]]
10668 [[3.2.1 基本的なランダムドットを描画する]]
10669 [[3.2.2 ドットサイズの異なるランダムドットパターンの描画]]
10670 [[3.2.3 ランダムドットキネマトグラムの描画]]
10671 [[3.3 画像ファイルの取り扱い]]
10672 [[3.3.1 画像ファイルを保存する]]
10673 [[3.3.2 画像ファイルを読み込み表示する]]
10674 [[3.4 Canvasのオフスクリーンへの取り込み]]
10677 [[5.1 外部装置(キーボード/マウス)の操作内容を取得する]]
10680 [[6. 時間制御と各種ツールを使用する]]
10682 [[6.2 各種ツールを使ってみる]]
10683 [[6.2.1 描画の時間精度を確認する-FPSチェッカー]]
10684 [[6.2.2 画面に反映されない描画の進行度を表示する-プログレスバー]]
10690 [[6.3.5 行列の中身をオフスクリーンに描画する]]
10691 [[6.3.6 メッシュグリッドの作成]]</pre>
10693 <div title="素晴らしいデモプログラムを作成したので、公開したい" modifier="Kazushi Maruya" modified="200712180151" created="200712131803" changecount="3">
10694 <pre>[[Visiome Platform|http://platform.visiome.neuroinf.jp]]には様々なデモンストレーションが公開されています。
10695 Psychlopsによるデモプログラムも多数公開されておりますので、是非ご参加ください。
10696 (現在の所このサイトにアップロードするためのアカウント取得には制限があります。詳しくはVisiomeのホームページをご覧下さい。)</pre>
10698 <div title="関数一覧_FunctionList" modifier="Psychlops_DevelopperG" modified="200910080818" created="200707220316" changecount="22">
10700 **[[Psychlops::Canvas]]
10701 **[[Psychlops::Rectangle]]
10702 **[[Psychlops::Point]]
10703 **[[Psychlops::Image]]
10704 **[[Psychlops::Color]]
10706 **[[Psychlops::Input]]
10707 **[[Psychlops::Mouse]]
10708 **[[Psychlops::Clock]]
10710 **[[Psychlops::Matrix]]
10711 **[[Psychlops::Math]]</pre>
10713 <div title="関数逆引きとQ&A" modifier="Kazushi Maruya" modified="200712131749" created="200712131608" changecount="1">
10714 <pre>*[[Canvas型に関して]]
10715 *[[Rectangle型に関して]]
10728 <!--POST-STOREAREA-->
10729 <!--POST-BODY-START-->
10730 <!--POST-BODY-END-->
10731 <script type="text/javascript">
10736 // * This code is designed to be readable but for compactness it only includes brief comments. You can see fuller comments
10737 // in the project Subversion repository at http://svn.tiddlywiki.org/Trunk/core/
10739 // * You should never need to modify this source code directly. TiddlyWiki is carefully designed to allow deep customisation
10740 // without changing the core code. Please consult the development group at http://groups.google.com/group/TiddlyWikiDev
10744 //-- Configuration repository
10747 // Miscellaneous options
10749 numRssItems: 20, // Number of items in the RSS feed
10750 animDuration: 400, // Duration of UI animations in milliseconds
10751 cascadeFast: 20, // Speed for cascade animations (higher == slower)
10752 cascadeSlow: 60, // Speed for EasterEgg cascade animations
10753 cascadeDepth: 5 // Depth of cascade animation
10757 config.adaptors = {};
10763 config.annotations = {};
10765 // Custom fields to be automatically added to new tiddlers
10766 config.defaultCustomFields = {};
10769 config.messages = {
10775 // Options that can be set in the options panel and/or cookies
10777 chkRegExpSearch: false,
10778 chkCaseSensitiveSearch: false,
10780 chkSaveBackups: true,
10781 chkAutoSave: false,
10782 chkGenerateAnRssFeed: false,
10783 chkSaveEmptyTemplate: false,
10784 chkOpenInNewWindow: true,
10785 chkToggleLinks: false,
10786 chkHttpReadOnly: true,
10787 chkForceMinorUpdate: false,
10788 chkConfirmDelete: true,
10789 chkInsertTabs: false,
10790 chkUsePreForStorage: true, // Whether to use <pre> format for storage
10791 chkDisplayStartupTime: false,
10792 txtBackupFolder: "",
10793 txtMainTab: "tabTimeline",
10794 txtMoreTab: "moreTabAll",
10795 txtMaxEditRows: "30",
10796 txtFileSystemCharSet: "UTF-8"
10798 config.optionsDesc = {};
10800 // List of notification functions to be called when certain tiddlers are changed or deleted
10801 config.notifyTiddlers = [
10802 {name: "StyleSheetLayout", notify: refreshStyles},
10803 {name: "StyleSheetColors", notify: refreshStyles},
10804 {name: "StyleSheet", notify: refreshStyles},
10805 {name: "StyleSheetPrint", notify: refreshStyles},
10806 {name: "PageTemplate", notify: refreshPageTemplate},
10807 {name: "SiteTitle", notify: refreshPageTitle},
10808 {name: "SiteSubtitle", notify: refreshPageTitle},
10809 {name: "ColorPalette", notify: refreshColorPalette},
10810 {name: null, notify: refreshDisplay}
10813 // Default tiddler templates
10814 var DEFAULT_VIEW_TEMPLATE = 1;
10815 var DEFAULT_EDIT_TEMPLATE = 2;
10816 config.tiddlerTemplates = {
10821 // More messages (rather a legacy layout that shouldn't really be like this)
10832 config.backstageTasks = ["save","sync","importTask","tweak","plugins"];
10834 // Macros; each has a 'handler' member that is inserted later
10838 search: {sizeTextbox: 15},
10870 refreshDisplay: {},
10871 importTiddlers: {},
10876 // Commands supported by the toolbar macro
10877 config.commands = {
10881 saveTiddler: {hideReadOnly: true},
10883 deleteTiddler: {hideReadOnly: true},
10885 references: {type: "popup"},
10886 jump: {type: "popup"},
10887 syncing: {type: "popup"},
10888 fields: {type: "popup"}
10891 // Browser detection... In a very few places, there's nothing else for it but to know what browser we're using.
10892 config.userAgent = navigator.userAgent.toLowerCase();
10894 isIE: config.userAgent.indexOf("msie") != -1 && config.userAgent.indexOf("opera") == -1,
10895 isGecko: config.userAgent.indexOf("gecko") != -1,
10896 ieVersion: /MSIE (\d.\d)/i.exec(config.userAgent), // config.browser.ieVersion[1], if it exists, will be the IE version string, eg "6.0"
10897 isSafari: config.userAgent.indexOf("applewebkit") != -1,
10898 isBadSafari: !((new RegExp("[\u0150\u0170]","g")).test("\u0150")),
10899 firefoxDate: /gecko\/(\d{8})/i.exec(config.userAgent), // config.browser.firefoxDate[1], if it exists, will be Firefox release date as "YYYYMMDD"
10900 isOpera: config.userAgent.indexOf("opera") != -1,
10901 isLinux: config.userAgent.indexOf("linux") != -1,
10902 isUnix: config.userAgent.indexOf("x11") != -1,
10903 isMac: config.userAgent.indexOf("mac") != -1,
10904 isWindows: config.userAgent.indexOf("win") != -1
10907 // Basic regular expressions
10908 config.textPrimitives = {
10909 upperLetter: "[A-Z\u00c0-\u00de\u0150\u0170]",
10910 lowerLetter: "[a-z0-9_\\-\u00df-\u00ff\u0151\u0171]",
10911 anyLetter: "[A-Za-z0-9_\\-\u00c0-\u00de\u00df-\u00ff\u0150\u0170\u0151\u0171]",
10912 anyLetterStrict: "[A-Za-z0-9\u00c0-\u00de\u00df-\u00ff\u0150\u0170\u0151\u0171]"
10914 if(config.browser.isBadSafari) {
10915 config.textPrimitives = {
10916 upperLetter: "[A-Z\u00c0-\u00de]",
10917 lowerLetter: "[a-z0-9_\\-\u00df-\u00ff]",
10918 anyLetter: "[A-Za-z0-9_\\-\u00c0-\u00de\u00df-\u00ff]",
10919 anyLetterStrict: "[A-Za-z0-9\u00c0-\u00de\u00df-\u00ff]"
10922 config.textPrimitives.sliceSeparator = "::";
10923 config.textPrimitives.urlPattern = "[a-z]{3,8}:[^\\s:'\"][^\\s'\"]*(?:/|\\b)";
10924 config.textPrimitives.unWikiLink = "~";
10925 config.textPrimitives.wikiLink = "(?:(?:" + config.textPrimitives.upperLetter + "+" +
10926 config.textPrimitives.lowerLetter + "+" +
10927 config.textPrimitives.upperLetter +
10928 config.textPrimitives.anyLetter + "*)|(?:" +
10929 config.textPrimitives.upperLetter + "{2,}" +
10930 config.textPrimitives.lowerLetter + "+))";
10932 config.textPrimitives.cssLookahead = "(?:(" + config.textPrimitives.anyLetter + "+)\\(([^\\)\\|\\n]+)(?:\\):))|(?:(" + config.textPrimitives.anyLetter + "+):([^;\\|\\n]+);)";
10933 config.textPrimitives.cssLookaheadRegExp = new RegExp(config.textPrimitives.cssLookahead,"mg");
10935 config.textPrimitives.brackettedLink = "\\[\\[([^\\]]+)\\]\\]";
10936 config.textPrimitives.titledBrackettedLink = "\\[\\[([^\\[\\]\\|]+)\\|([^\\[\\]\\|]+)\\]\\]";
10937 config.textPrimitives.tiddlerForcedLinkRegExp = new RegExp("(?:" + config.textPrimitives.titledBrackettedLink + ")|(?:" +
10938 config.textPrimitives.brackettedLink + ")|(?:" +
10939 config.textPrimitives.urlPattern + ")","mg");
10940 config.textPrimitives.tiddlerAnyLinkRegExp = new RegExp("("+ config.textPrimitives.wikiLink + ")|(?:" +
10941 config.textPrimitives.titledBrackettedLink + ")|(?:" +
10942 config.textPrimitives.brackettedLink + ")|(?:" +
10943 config.textPrimitives.urlPattern + ")","mg");
10947 function() {return config.browser.isIE;},
10948 function() {return true}
10952 downTriangle: ["\u25BC","\u25BE"],
10953 downArrow: ["\u2193","\u2193"],
10954 bentArrowLeft: ["\u2190","\u21A9"],
10955 bentArrowRight: ["\u2192","\u21AA"]
10960 //-- Shadow tiddlers
10963 config.shadowTiddlers = {
10965 MarkupPreHead: "<!--{{{-->\n<link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml'/>\n<!--}}}-->",
10966 MarkupPostHead: "",
10968 MarkupPostBody: "",
10969 TabTimeline: '<<timeline>>',
10970 TabAll: '<<list all>>',
10971 TabTags: '<<allTags excludeLists>>',
10972 TabMoreMissing: '<<list missing>>',
10973 TabMoreOrphans: '<<list orphans>>',
10974 TabMoreShadowed: '<<list shadowed>>',
10975 AdvancedOptions: '<<options>>',
10976 PluginManager: '<<plugins>>',
10977 ImportTiddlers: '<<importTiddlers>>'
10981 //-- Translateable strings
10984 // Strings in "double quotes" should be translated; strings in 'single quotes' should be left alone
10986 merge(config.options,{
10987 txtUserName: "YourName"});
10989 merge(config.tasks,{
10990 save: {text: "save", tooltip: "Save your changes to this TiddlyWiki", action: saveChanges},
10991 sync: {text: "sync", tooltip: "Synchronise changes with other TiddlyWiki files and servers", content: '<<sync>>'},
10992 importTask: {text: "import", tooltip: "Import tiddlers and plugins from other TiddlyWiki files and servers", content: '<<importTiddlers>>'},
10993 tweak: {text: "tweak", tooltip: "Tweak the appearance and behaviour of TiddlyWiki", content: '<<options>>'},
10994 plugins: {text: "plugins", tooltip: "Manage installed plugins", content: '<<plugins>>'}
10997 // Options that can be set in the options panel and/or cookies
10998 merge(config.optionsDesc,{
10999 txtUserName: "Username for signing your edits",
11000 chkRegExpSearch: "Enable regular expressions for searches",
11001 chkCaseSensitiveSearch: "Case-sensitive searching",
11002 chkAnimate: "Enable animations",
11003 chkSaveBackups: "Keep backup file when saving changes",
11004 chkAutoSave: "Automatically save changes",
11005 chkGenerateAnRssFeed: "Generate an RSS feed when saving changes",
11006 chkSaveEmptyTemplate: "Generate an empty template when saving changes",
11007 chkOpenInNewWindow: "Open external links in a new window",
11008 chkToggleLinks: "Clicking on links to open tiddlers causes them to close",
11009 chkHttpReadOnly: "Hide editing features when viewed over HTTP",
11010 chkForceMinorUpdate: "Don't update modifier username and date when editing tiddlers",
11011 chkConfirmDelete: "Require confirmation before deleting tiddlers",
11012 chkInsertTabs: "Use the tab key to insert tab characters instead of moving between fields",
11013 txtBackupFolder: "Name of folder to use for backups",
11014 txtMaxEditRows: "Maximum number of rows in edit boxes",
11015 txtFileSystemCharSet: "Default character set for saving changes (Firefox/Mozilla only)"});
11017 merge(config.messages,{
11018 customConfigError: "Problems were encountered loading plugins. See PluginManager for details",
11019 pluginError: "Error: %0",
11020 pluginDisabled: "Not executed because disabled via 'systemConfigDisable' tag",
11021 pluginForced: "Executed because forced via 'systemConfigForce' tag",
11022 pluginVersionError: "Not executed because this plugin needs a newer version of TiddlyWiki",
11023 nothingSelected: "Nothing is selected. You must select one or more items first",
11024 savedSnapshotError: "It appears that this TiddlyWiki has been incorrectly saved. Please see http://www.tiddlywiki.com/#DownloadSoftware for details",
11025 subtitleUnknown: "(unknown)",
11026 undefinedTiddlerToolTip: "The tiddler '%0' doesn't yet exist",
11027 shadowedTiddlerToolTip: "The tiddler '%0' doesn't yet exist, but has a pre-defined shadow value",
11028 tiddlerLinkTooltip: "%0 - %1, %2",
11029 externalLinkTooltip: "External link to %0",
11030 noTags: "There are no tagged tiddlers",
11031 notFileUrlError: "You need to save this TiddlyWiki to a file before you can save changes",
11032 cantSaveError: "It's not possible to save changes. Possible reasons include:\n- your browser doesn't support saving (Firefox, Internet Explorer, Safari and Opera all work if properly configured)\n- the pathname to your TiddlyWiki file contains illegal characters\n- the TiddlyWiki HTML file has been moved or renamed",
11033 invalidFileError: "The original file '%0' does not appear to be a valid TiddlyWiki",
11034 backupSaved: "Backup saved",
11035 backupFailed: "Failed to save backup file",
11036 rssSaved: "RSS feed saved",
11037 rssFailed: "Failed to save RSS feed file",
11038 emptySaved: "Empty template saved",
11039 emptyFailed: "Failed to save empty template file",
11040 mainSaved: "Main TiddlyWiki file saved",
11041 mainFailed: "Failed to save main TiddlyWiki file. Your changes have not been saved",
11042 macroError: "Error in macro <<\%0>>",
11043 macroErrorDetails: "Error while executing macro <<\%0>>:\n%1",
11044 missingMacro: "No such macro",
11045 overwriteWarning: "A tiddler named '%0' already exists. Choose OK to overwrite it",
11046 unsavedChangesWarning: "WARNING! There are unsaved changes in TiddlyWiki\n\nChoose OK to save\nChoose CANCEL to discard",
11047 confirmExit: "--------------------------------\n\nThere are unsaved changes in TiddlyWiki. If you continue you will lose those changes\n\n--------------------------------",
11048 saveInstructions: "SaveChanges",
11049 unsupportedTWFormat: "Unsupported TiddlyWiki format '%0'",
11050 tiddlerSaveError: "Error when saving tiddler '%0'",
11051 tiddlerLoadError: "Error when loading tiddler '%0'",
11052 wrongSaveFormat: "Cannot save with storage format '%0'. Using standard format for save.",
11053 invalidFieldName: "Invalid field name %0",
11054 fieldCannotBeChanged: "Field '%0' cannot be changed",
11055 loadingMissingTiddler: "Attempting to retrieve the tiddler '%0' from the '%1' server at:\n\n'%2' in the workspace '%3'"});
11057 merge(config.messages.messageClose,{
11059 tooltip: "close this message area"});
11061 config.messages.backstage = {
11062 open: {text: "backstage", tooltip: "Open the backstage area to perform authoring and editing tasks"},
11063 close: {text: "close", tooltip: "Close the backstage area"},
11064 prompt: "backstage: ",
11066 edit: {text: "edit", tooltip: "Edit the tiddler '%0'"}
11070 config.messages.listView = {
11071 tiddlerTooltip: "Click for the full text of this tiddler",
11072 previewUnavailable: "(preview not available)"
11075 config.messages.dates.months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November","December"];
11076 config.messages.dates.days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
11077 config.messages.dates.shortMonths = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
11078 config.messages.dates.shortDays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
11079 // suffixes for dates, eg "1st","2nd","3rd"..."30th","31st"
11080 config.messages.dates.daySuffixes = ["st","nd","rd","th","th","th","th","th","th","th",
11081 "th","th","th","th","th","th","th","th","th","th",
11082 "st","nd","rd","th","th","th","th","th","th","th",
11084 config.messages.dates.am = "am";
11085 config.messages.dates.pm = "pm";
11087 merge(config.messages.tiddlerPopup,{
11090 merge(config.views.wikified.tag,{
11091 labelNoTags: "no tags",
11092 labelTags: "tags: ",
11093 openTag: "Open tag '%0'",
11094 tooltip: "Show tiddlers tagged with '%0'",
11095 openAllText: "Open all",
11096 openAllTooltip: "Open all of these tiddlers",
11097 popupNone: "No other tiddlers tagged with '%0'"});
11099 merge(config.views.wikified,{
11100 defaultText: "The tiddler '%0' doesn't yet exist. Double-click to create it",
11101 defaultModifier: "(missing)",
11102 shadowModifier: "(built-in shadow tiddler)",
11103 dateFormat: "DD MMM YYYY",
11104 createdPrompt: "created"});
11106 merge(config.views.editor,{
11107 tagPrompt: "Type tags separated with spaces, [[use double square brackets]] if necessary, or add existing",
11108 defaultText: "Type the text for '%0'"});
11110 merge(config.views.editor.tagChooser,{
11112 tooltip: "Choose existing tags to add to this tiddler",
11113 popupNone: "There are no tags defined",
11114 tagTooltip: "Add the tag '%0'"});
11116 merge(config.messages,{
11119 {unit: 1024*1024*1024, template: "%0\u00a0GB"},
11120 {unit: 1024*1024, template: "%0\u00a0MB"},
11121 {unit: 1024, template: "%0\u00a0KB"},
11122 {unit: 1, template: "%0\u00a0B"}
11125 merge(config.macros.search,{
11127 prompt: "Search this TiddlyWiki",
11129 successMsg: "%0 tiddlers found matching %1",
11130 failureMsg: "No tiddlers found matching %0"});
11132 merge(config.macros.tagging,{
11133 label: "tagging: ",
11134 labelNotTag: "not tagging",
11135 tooltip: "List of tiddlers tagged with '%0'"});
11137 merge(config.macros.timeline,{
11138 dateFormat: "DD MMM YYYY"});
11140 merge(config.macros.allTags,{
11141 tooltip: "Show tiddlers tagged with '%0'",
11142 noTags: "There are no tagged tiddlers"});
11144 config.macros.list.all.prompt = "All tiddlers in alphabetical order";
11145 config.macros.list.missing.prompt = "Tiddlers that have links to them but are not defined";
11146 config.macros.list.orphans.prompt = "Tiddlers that are not linked to from any other tiddlers";
11147 config.macros.list.shadowed.prompt = "Tiddlers shadowed with default contents";
11148 config.macros.list.touched.prompt = "Tiddlers that have been modified locally";
11150 merge(config.macros.closeAll,{
11151 label: "close all",
11152 prompt: "Close all displayed tiddlers (except any that are being edited)"});
11154 merge(config.macros.permaview,{
11155 label: "permaview",
11156 prompt: "Link to an URL that retrieves all the currently displayed tiddlers"});
11158 merge(config.macros.saveChanges,{
11159 label: "save changes",
11160 prompt: "Save all tiddlers to create a new TiddlyWiki",
11163 merge(config.macros.newTiddler,{
11164 label: "new tiddler",
11165 prompt: "Create a new tiddler",
11166 title: "New Tiddler",
11169 merge(config.macros.newJournal,{
11170 label: "new journal",
11171 prompt: "Create a new tiddler from the current date and time",
11174 merge(config.macros.options,{
11175 wizardTitle: "Tweak advanced options",
11176 step1Title: "These options are saved in cookies in your browser",
11177 step1Html: "<input type='hidden' name='markList'></input><br><input type='checkbox' checked='false' name='chkUnknown'>Show unknown options</input>",
11178 unknownDescription: "//(unknown)//",
11179 listViewTemplate: {
11181 {name: 'Option', field: 'option', title: "Option", type: 'String'},
11182 {name: 'Description', field: 'description', title: "Description", type: 'WikiText'},
11183 {name: 'Name', field: 'name', title: "Name", type: 'String'}
11186 {className: 'lowlight', field: 'lowlight'}
11190 merge(config.macros.plugins,{
11191 wizardTitle: "Manage plugins",
11192 step1Title: "Currently loaded plugins",
11193 step1Html: "<input type='hidden' name='markList'></input>", // DO NOT TRANSLATE
11194 skippedText: "(This plugin has not been executed because it was added since startup)",
11195 noPluginText: "There are no plugins installed",
11196 confirmDeleteText: "Are you sure you want to delete these plugins:\n\n%0",
11197 removeLabel: "remove systemConfig tag",
11198 removePrompt: "Remove systemConfig tag",
11199 deleteLabel: "delete",
11200 deletePrompt: "Delete these tiddlers forever",
11201 listViewTemplate: {
11203 {name: 'Selected', field: 'Selected', rowName: 'title', type: 'Selector'},
11204 {name: 'Tiddler', field: 'tiddler', title: "Tiddler", type: 'Tiddler'},
11205 {name: 'Size', field: 'size', tiddlerLink: 'size', title: "Size", type: 'Size'},
11206 {name: 'Forced', field: 'forced', title: "Forced", tag: 'systemConfigForce', type: 'TagCheckbox'},
11207 {name: 'Disabled', field: 'disabled', title: "Disabled", tag: 'systemConfigDisable', type: 'TagCheckbox'},
11208 {name: 'Executed', field: 'executed', title: "Loaded", type: 'Boolean', trueText: "Yes", falseText: "No"},
11209 {name: 'Startup Time', field: 'startupTime', title: "Startup Time", type: 'String'},
11210 {name: 'Error', field: 'error', title: "Status", type: 'Boolean', trueText: "Error", falseText: "OK"},
11211 {name: 'Log', field: 'log', title: "Log", type: 'StringList'}
11214 {className: 'error', field: 'error'},
11215 {className: 'warning', field: 'warning'}
11219 merge(config.macros.toolbar,{
11221 morePrompt: "Reveal further commands"
11224 merge(config.macros.refreshDisplay,{
11226 prompt: "Redraw the entire TiddlyWiki display"
11229 merge(config.macros.importTiddlers,{
11230 readOnlyWarning: "You cannot import into a read-only TiddlyWiki file. Try opening it from a file:// URL",
11231 wizardTitle: "Import tiddlers from another file or server",
11232 step1Title: "Step 1: Locate the server or TiddlyWiki file",
11233 step1Html: "Specify the type of the server: <select name='selTypes'><option value=''>Choose...</option></select><br>Enter the URL or pathname here: <input type='text' size=50 name='txtPath'><br>...or browse for a file: <input type='file' size=50 name='txtBrowse'><br><hr>...or select a pre-defined feed: <select name='selFeeds'><option value=''>Choose...</option></select>",
11235 openPrompt: "Open the connection to this file or server",
11236 openError: "There were problems fetching the tiddlywiki file",
11237 statusOpenHost: "Opening the host",
11238 statusGetWorkspaceList: "Getting the list of available workspaces",
11239 step2Title: "Step 2: Choose the workspace",
11240 step2Html: "Enter a workspace name: <input type='text' size=50 name='txtWorkspace'><br>...or select a workspace: <select name='selWorkspace'><option value=''>Choose...</option></select>",
11241 cancelLabel: "cancel",
11242 cancelPrompt: "Cancel this import",
11243 statusOpenWorkspace: "Opening the workspace",
11244 statusGetTiddlerList: "Getting the list of available tiddlers",
11245 step3Title: "Step 3: Choose the tiddlers to import",
11246 step3Html: "<input type='hidden' name='markList'></input><br><input type='checkbox' checked='true' name='chkSync'>Keep these tiddlers linked to this server so that you can synchronise subsequent changes</input><br><input type='checkbox' name='chkSave'>Save the details of this server in a 'systemServer' tiddler called:</input> <input type='text' size=25 name='txtSaveTiddler'>",
11247 importLabel: "import",
11248 importPrompt: "Import these tiddlers",
11249 confirmOverwriteText: "Are you sure you want to overwrite these tiddlers:\n\n%0",
11250 step4Title: "Step 4: Importing %0 tiddler(s)",
11251 step4Html: "<input type='hidden' name='markReport'></input>", // DO NOT TRANSLATE
11253 donePrompt: "Close this wizard",
11254 statusDoingImport: "Importing tiddlers",
11255 statusDoneImport: "All tiddlers imported",
11256 systemServerNamePattern: "%2 on %1",
11257 systemServerNamePatternNoWorkspace: "%1",
11258 confirmOverwriteSaveTiddler: "The tiddler '%0' already exists. Click 'OK' to overwrite it with the details of this server, or 'Cancel' to leave it unchanged",
11259 serverSaveTemplate: "|''Type:''|%0|\n|''URL:''|%1|\n|''Workspace:''|%2|\n\nThis tiddler was automatically created to record the details of this server",
11260 serverSaveModifier: "(System)",
11261 listViewTemplate: {
11263 {name: 'Selected', field: 'Selected', rowName: 'title', type: 'Selector'},
11264 {name: 'Tiddler', field: 'tiddler', title: "Tiddler", type: 'Tiddler'},
11265 {name: 'Size', field: 'size', tiddlerLink: 'size', title: "Size", type: 'Size'},
11266 {name: 'Tags', field: 'tags', title: "Tags", type: 'Tags'}
11272 merge(config.macros.sync,{
11273 listViewTemplate: {
11275 {name: 'Selected', field: 'selected', rowName: 'title', type: 'Selector'},
11276 {name: 'Tiddler', field: 'tiddler', title: "Tiddler", type: 'Tiddler'},
11277 {name: 'Server Type', field: 'serverType', title: "Server type", type: 'String'},
11278 {name: 'Server Host', field: 'serverHost', title: "Server host", type: 'String'},
11279 {name: 'Server Workspace', field: 'serverWorkspace', title: "Server workspace", type: 'String'},
11280 {name: 'Status', field: 'status', title: "Synchronisation status", type: 'String'},
11281 {name: 'Server URL', field: 'serverUrl', title: "Server URL", text: "View", type: 'Link'}
11286 {caption: "Sync these tiddlers", name: 'sync'}
11288 wizardTitle: "Synchronize with external servers and files",
11289 step1Title: "Choose the tiddlers you want to synchronize",
11290 step1Html: "<input type='hidden' name='markList'></input>", // DO NOT TRANSLATE
11292 syncPrompt: "Sync these tiddlers",
11293 hasChanged: "Changed while unplugged",
11294 hasNotChanged: "Unchanged while unplugged",
11296 none: {text: "...", color: "none"},
11297 changedServer: {text: "Changed on server", color: '#80ff80'},
11298 changedLocally: {text: "Changed while unplugged", color: '#80ff80'},
11299 changedBoth: {text: "Changed while unplugged and on server", color: '#ff8080'},
11300 notFound: {text: "Not found on server", color: '#ffff80'},
11301 putToServer: {text: "Saved update on server", color: '#ff80ff'},
11302 gotFromServer: {text: "Retrieved update from server", color: '#80ffff'}
11306 merge(config.macros.annotations,{
11309 merge(config.commands.closeTiddler,{
11311 tooltip: "Close this tiddler"});
11313 merge(config.commands.closeOthers,{
11314 text: "close others",
11315 tooltip: "Close all other tiddlers"});
11317 merge(config.commands.editTiddler,{
11319 tooltip: "Edit this tiddler",
11320 readOnlyText: "view",
11321 readOnlyTooltip: "View the source of this tiddler"});
11323 merge(config.commands.saveTiddler,{
11325 tooltip: "Save changes to this tiddler"});
11327 merge(config.commands.cancelTiddler,{
11329 tooltip: "Undo changes to this tiddler",
11330 warning: "Are you sure you want to abandon your changes to '%0'?",
11331 readOnlyText: "done",
11332 readOnlyTooltip: "View this tiddler normally"});
11334 merge(config.commands.deleteTiddler,{
11336 tooltip: "Delete this tiddler",
11337 warning: "Are you sure you want to delete '%0'?"});
11339 merge(config.commands.permalink,{
11341 tooltip: "Permalink for this tiddler"});
11343 merge(config.commands.references,{
11344 text: "references",
11345 tooltip: "Show tiddlers that link to this one",
11346 popupNone: "No references"});
11348 merge(config.commands.jump,{
11350 tooltip: "Jump to another open tiddler"});
11352 merge(config.commands.syncing,{
11354 tooltip: "Control synchronisation of this tiddler with a server or external file",
11355 currentlySyncing: "<div>Currently syncing via <span class='popupHighlight'>'%0'</span> to:</"+"div><div>host: <span class='popupHighlight'>%1</span></"+"div><div>workspace: <span class='popupHighlight'>%2</span></"+"div>", // Note escaping of closing <div> tag
11356 notCurrentlySyncing: "Not currently syncing",
11357 captionUnSync: "Stop synchronising this tiddler",
11358 chooseServer: "Synchronise this tiddler with another server:",
11359 currServerMarker: "\u25cf ",
11360 notCurrServerMarker: " "});
11362 merge(config.commands.fields,{
11364 tooltip: "Show the extended fields of this tiddler",
11365 emptyText: "There are no extended fields for this tiddler",
11366 listViewTemplate: {
11368 {name: 'Field', field: 'field', title: "Field", type: 'String'},
11369 {name: 'Value', field: 'value', title: "Value", type: 'String'}
11376 merge(config.shadowTiddlers,{
11377 DefaultTiddlers: "GettingStarted",
11378 MainMenu: "GettingStarted",
11379 SiteTitle: "My TiddlyWiki",
11380 SiteSubtitle: "a reusable non-linear personal web notebook",
11381 SiteUrl: "http://www.tiddlywiki.com/",
11382 SideBarOptions: '<<search>><<closeAll>><<permaview>><<newTiddler>><<newJournal "DD MMM YYYY">><<saveChanges>><<slider chkSliderOptionsPanel OptionsPanel "options »" "Change TiddlyWiki advanced options">>',
11383 SideBarTabs: '<<tabs txtMainTab "Timeline" "Timeline" TabTimeline "All" "All tiddlers" TabAll "Tags" "All tags" TabTags "More" "More lists" TabMore>>',
11384 TabMore: '<<tabs txtMoreTab "Missing" "Missing tiddlers" TabMoreMissing "Orphans" "Orphaned tiddlers" TabMoreOrphans "Shadowed" "Shadowed tiddlers" TabMoreShadowed>>'});
11386 merge(config.annotations,{
11387 AdvancedOptions: "This shadow tiddler provides access to several advanced options",
11388 ColorPalette: "These values in this shadow tiddler determine the colour scheme of the ~TiddlyWiki user interface",
11389 DefaultTiddlers: "The tiddlers listed in this shadow tiddler will be automatically displayed when ~TiddlyWiki starts up",
11390 EditTemplate: "The HTML template in this shadow tiddler determines how tiddlers look while they are being edited",
11391 GettingStarted: "This shadow tiddler provides basic usage instructions",
11392 ImportTiddlers: "This shadow tiddler provides access to importing tiddlers",
11393 MainMenu: "This shadow tiddler is used as the contents of the main menu in the left-hand column of the screen",
11394 MarkupPreHead: "This tiddler is inserted at the top of the <head> section of the TiddlyWiki HTML file",
11395 MarkupPostHead: "This tiddler is inserted at the bottom of the <head> section of the TiddlyWiki HTML file",
11396 MarkupPreBody: "This tiddler is inserted at the top of the <body> section of the TiddlyWiki HTML file",
11397 MarkupPostBody: "This tiddler is inserted at the end of the <body> section of the TiddlyWiki HTML file immediately before the script block",
11398 OptionsPanel: "This shadow tiddler is used as the contents of the options panel slider in the right-hand sidebar",
11399 PageTemplate: "The HTML template in this shadow tiddler determines the overall ~TiddlyWiki layout",
11400 PluginManager: "This shadow tiddler provides access to the plugin manager",
11401 SideBarOptions: "This shadow tiddler is used as the contents of the option panel in the right-hand sidebar",
11402 SideBarTabs: "This shadow tiddler is used as the contents of the tabs panel in the right-hand sidebar",
11403 SiteSubtitle: "This shadow tiddler is used as the second part of the page title",
11404 SiteTitle: "This shadow tiddler is used as the first part of the page title",
11405 SiteUrl: "This shadow tiddler should be set to the full target URL for publication",
11406 StyleSheetColours: "This shadow tiddler contains CSS definitions related to the color of page elements",
11407 StyleSheet: "This tiddler can contain custom CSS definitions",
11408 StyleSheetLayout: "This shadow tiddler contains CSS definitions related to the layout of page elements",
11409 StyleSheetLocale: "This shadow tiddler contains CSS definitions related to the translation locale",
11410 StyleSheetPrint: "This shadow tiddler contains CSS definitions for printing",
11411 TabAll: "This shadow tiddler contains the contents of the 'All' tab in the right-hand sidebar",
11412 TabMore: "This shadow tiddler contains the contents of the 'More' tab in the right-hand sidebar",
11413 TabMoreMissing: "This shadow tiddler contains the contents of the 'Missing' tab in the right-hand sidebar",
11414 TabMoreOrphans: "This shadow tiddler contains the contents of the 'Orphans' tab in the right-hand sidebar",
11415 TabMoreShadowed: "This shadow tiddler contains the contents of the 'Shadowed' tab in the right-hand sidebar",
11416 TabTags: "This shadow tiddler contains the contents of the 'Tags' tab in the right-hand sidebar",
11417 TabTimeline: "This shadow tiddler contains the contents of the 'Timeline' tab in the right-hand sidebar",
11418 ViewTemplate: "The HTML template in this shadow tiddler determines how tiddlers look"
11425 var params = null; // Command line parameters
11426 var store = null; // TiddlyWiki storage
11427 var story = null; // Main story
11428 var formatter = null; // Default formatters for the wikifier
11429 config.parsers = {}; // Hashmap of alternative parsers for the wikifier
11430 var anim = new Animator(); // Animation engine
11431 var readOnly = false; // Whether we're in readonly mode
11432 var highlightHack = null; // Embarrassing hack department...
11433 var hadConfirmExit = false; // Don't warn more than once
11434 var safeMode = false; // Disable all plugins and cookies
11435 var installedPlugins = []; // Information filled in when plugins are executed
11436 var startingUp = false; // Whether we're in the process of starting up
11437 var pluginInfo,tiddler; // Used to pass information to plugins in loadPlugins()
11439 // Whether to use the JavaSaver applet
11440 var useJavaSaver = config.browser.isSafari || config.browser.isOpera;
11445 var t9,t8,t7,t6,t5,t4,t3,t2,t1,t0 = new Date();
11447 window.onbeforeunload = function(e) {if(window.confirmExit) return confirmExit();};
11448 params = getParameters();
11450 params = params.parseParams("open",null,false);
11451 store = new TiddlyWiki();
11452 invokeParamifier(params,"oninit");
11453 story = new Story("tiddlerDisplay","tiddler");
11454 addEvent(document,"click",Popup.onDocumentClick);
11456 loadOptionsCookie();
11457 for(var s=0; s<config.notifyTiddlers.length; s++)
11458 store.addNotification(config.notifyTiddlers[s].name,config.notifyTiddlers[s].notify);
11460 store.loadFromDiv("storeArea","store",true);
11462 loadShadowTiddlers();
11464 invokeParamifier(params,"onload");
11466 readOnly = (window.location.protocol == "file:") ? false : config.options.chkHttpReadOnly;
11467 var pluginProblem = loadPlugins();
11469 formatter = new Formatter(config.formatters);
11470 invokeParamifier(params,"onconfig");
11476 if(pluginProblem) {
11477 story.displayTiddler(null,"PluginManager");
11478 displayMessage(config.messages.customConfigError);
11480 for(var m in config.macros) {
11481 if(config.macros[m].init)
11482 config.macros[m].init();
11487 if(config.options.chkDisplayStartupTime) {
11488 displayMessage("Load in " + (t2-t1) + " ms");
11489 displayMessage("Loadshadows in " + (t3-t2) + " ms");
11490 displayMessage("Loadplugins in " + (t5-t4) + " ms");
11491 displayMessage("Notify in " + (t7-t6) + " ms");
11492 displayMessage("Restart in " + (t8-t7) + " ms");
11493 displayMessage("Total startup in " + (t9-t0) + " ms");
11495 startingUp = false;
11501 invokeParamifier(params,"onstart");
11502 if(story.isEmpty()) {
11503 var defaultParams = store.getTiddlerText("DefaultTiddlers").parseParams("open",null,false);
11504 invokeParamifier(defaultParams,"onstart");
11506 window.scrollTo(0,0);
11509 function saveTest()
11511 var s = document.getElementById("saveTest");
11512 if(s.hasChildNodes())
11513 alert(config.messages.savedSnapshotError);
11514 s.appendChild(document.createTextNode("savetest"));
11517 function loadShadowTiddlers()
11519 var shadows = new TiddlyWiki();
11520 shadows.loadFromDiv("shadowArea","shadows",true);
11521 shadows.forEachTiddler(function(title,tiddler){config.shadowTiddlers[title] = tiddler.text;});
11525 function loadPlugins()
11529 var tiddlers = store.getTaggedTiddlers("systemConfig");
11533 var nPlugins = tiddlers.length;
11534 installedPlugins = [];
11535 for(var i=0; i<nPlugins; i++) {
11536 var p = getPluginInfo(tiddlers[i]);
11537 installedPlugins[i] = p;
11544 var visit = function(p) {
11548 var reqs = p.Requires;
11550 reqs = reqs.readBracketedList();
11551 for(var i=0; i<reqs.length; i++)
11552 visit(map[reqs[i]]);
11556 for(i=0; i<nPlugins; i++)
11557 visit(installedPlugins[i]);
11558 for(i=0; i<toLoad.length; i++) {
11561 tiddler = p.tiddler;
11562 if(isPluginExecutable(p)) {
11563 if(isPluginEnabled(p)) {
11565 var startTime = new Date();
11568 window.eval(tiddler.text);
11571 p.log.push(config.messages.pluginError.format([exceptionText(ex)]));
11574 pluginInfo.startupTime = String((new Date()) - startTime) + "ms";
11582 return nLoaded != nPlugins;
11585 function getPluginInfo(tiddler)
11587 var p = store.getTiddlerSlices(tiddler.title,["Name","Description","Version","Requires","CoreVersion","Date","Source","Author","License","Browsers"]);
11588 p.tiddler = tiddler;
11589 p.title = tiddler.title;
11594 // Check that a particular plugin is valid for execution
11595 function isPluginExecutable(plugin)
11597 if(plugin.tiddler.isTagged("systemConfigForce"))
11598 return verifyTail(plugin,true,config.messages.pluginForced);
11599 if(plugin["CoreVersion"]) {
11600 var coreVersion = plugin["CoreVersion"].split(".");
11601 var w = parseInt(coreVersion[0]) - version.major;
11602 if(w == 0 && coreVersion[1])
11603 w = parseInt(coreVersion[1]) - version.minor;
11604 if(w == 0 && coreVersion[2])
11605 w = parseInt(coreVersion[2]) - version.revision;
11607 return verifyTail(plugin,false,config.messages.pluginVersionError);
11612 function isPluginEnabled(plugin)
11614 if(plugin.tiddler.isTagged("systemConfigDisable"))
11615 return verifyTail(plugin,false,config.messages.pluginDisabled);
11619 function verifyTail(plugin,result,message)
11621 plugin.log.push(message);
11625 function invokeMacro(place,macro,params,wikifier,tiddler)
11628 var m = config.macros[macro];
11630 m.handler(place,macro,params.readMacroParams(),wikifier,params,tiddler);
11632 createTiddlyError(place,config.messages.macroError.format([macro]),config.messages.macroErrorDetails.format([macro,config.messages.missingMacro]));
11634 createTiddlyError(place,config.messages.macroError.format([macro]),config.messages.macroErrorDetails.format([macro,ex.toString()]));
11642 function getParameters()
11645 if(window.location.hash) {
11646 p = decodeURI(window.location.hash.substr(1));
11647 if(config.browser.firefoxDate != null && config.browser.firefoxDate[1] < "20051111")
11648 p = convertUTF8ToUnicode(p);
11653 function invokeParamifier(params,handler)
11655 if(!params || params.length == undefined || params.length <= 1)
11657 for(var t=1; t<params.length; t++) {
11658 var p = config.paramifiers[params[t].name];
11659 if(p && p[handler] instanceof Function)
11660 p[handler](params[t].value);
11664 config.paramifiers = {};
11666 config.paramifiers.start = {
11667 oninit: function(v) {
11668 safeMode = v.toLowerCase() == "safe";
11672 config.paramifiers.open = {
11673 onstart: function(v) {
11674 story.displayTiddler("bottom",v,null,false,null);
11678 config.paramifiers.story = {
11679 onstart: function(v) {
11680 var list = store.getTiddlerText(v,"").parseParams("open",null,false);
11681 invokeParamifier(list,"onstart");
11685 config.paramifiers.search = {
11686 onstart: function(v) {
11687 story.search(v,false,false);
11691 config.paramifiers.searchRegExp = {
11692 onstart: function(v) {
11693 story.prototype.search(v,false,true);
11697 config.paramifiers.tag = {
11698 onstart: function(v) {
11699 var tagged = store.getTaggedTiddlers(v,"title");
11700 for(var t=0; t<tagged.length; t++)
11701 story.displayTiddler("bottom",tagged[t].title,null,false,null);
11705 config.paramifiers.newTiddler = {
11706 onstart: function(v) {
11708 story.displayTiddler(null,v,DEFAULT_EDIT_TEMPLATE);
11709 story.focusTiddler(v,"text");
11714 config.paramifiers.newJournal = {
11715 onstart: function(v) {
11717 var now = new Date();
11718 var title = now.formatString(v.trim());
11719 story.displayTiddler(null,title,DEFAULT_EDIT_TEMPLATE);
11720 story.focusTiddler(title,"text");
11725 config.paramifiers.readOnly = {
11726 onconfig: function(v) {
11727 var p = v.toLowerCase();
11728 readOnly = p == "yes" ? true : (p == "no" ? false : readOnly);
11733 //-- Formatter helpers
11736 function Formatter(formatters)
11738 this.formatters = [];
11740 for(var n=0; n<formatters.length; n++) {
11741 pattern.push("(" + formatters[n].match + ")");
11742 this.formatters.push(formatters[n]);
11744 this.formatterRegExp = new RegExp(pattern.join("|"),"mg");
11747 config.formatterHelpers = {
11749 createElementAndWikify: function(w)
11751 w.subWikifyTerm(createTiddlyElement(w.output,this.element),this.termRegExp);
11754 inlineCssHelper: function(w)
11757 config.textPrimitives.cssLookaheadRegExp.lastIndex = w.nextMatch;
11758 var lookaheadMatch = config.textPrimitives.cssLookaheadRegExp.exec(w.source);
11759 while(lookaheadMatch && lookaheadMatch.index == w.nextMatch) {
11761 if(lookaheadMatch[1]) {
11762 s = lookaheadMatch[1].unDash();
11763 v = lookaheadMatch[2];
11765 s = lookaheadMatch[3].unDash();
11766 v = lookaheadMatch[4];
11769 s = "backgroundColor";
11770 styles.push({style: s, value: v});
11771 w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
11772 config.textPrimitives.cssLookaheadRegExp.lastIndex = w.nextMatch;
11773 lookaheadMatch = config.textPrimitives.cssLookaheadRegExp.exec(w.source);
11778 applyCssHelper: function(e,styles)
11780 for(var t=0; t< styles.length; t++) {
11782 e.style[styles[t].style] = styles[t].value;
11788 enclosedTextHelper: function(w)
11790 this.lookaheadRegExp.lastIndex = w.matchStart;
11791 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
11792 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
11793 var text = lookaheadMatch[1];
11794 if(config.browser.isIE)
11795 text = text.replace(/\n/g,"\r");
11796 createTiddlyElement(w.output,this.element,null,null,text);
11797 w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
11801 isExternalLink: function(link)
11803 if(store.tiddlerExists(link) || store.isShadowTiddler(link)) {
11806 var urlRegExp = new RegExp(config.textPrimitives.urlPattern,"mg");
11807 if(urlRegExp.exec(link)) {
11810 if (link.indexOf(".")!=-1 || link.indexOf("\\")!=-1 || link.indexOf("/")!=-1){
11819 //-- Standard formatters
11822 config.formatters = [
11825 match: "^\\|(?:[^\\n]*)\\|(?:[fhck]?)$",
11826 lookaheadRegExp: /^\|([^\n]*)\|([fhck]?)$/mg,
11827 rowTermRegExp: /(\|(?:[fhck]?)$\n?)/mg,
11828 cellRegExp: /(?:\|([^\n\|]*)\|)|(\|[fhck]?$\n?)/mg,
11829 cellTermRegExp: /((?:\x20*)\|)/mg,
11830 rowTypes: {"c":"caption", "h":"thead", "":"tbody", "f":"tfoot"},
11832 handler: function(w)
11834 var table = createTiddlyElement(w.output,"table",null,"twtable");
11835 var prevColumns = [];
11836 var currRowType = null;
11839 w.nextMatch = w.matchStart;
11840 this.lookaheadRegExp.lastIndex = w.nextMatch;
11841 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
11842 while(lookaheadMatch && lookaheadMatch.index == w.nextMatch) {
11843 var nextRowType = lookaheadMatch[2];
11844 if(nextRowType == "k") {
11845 table.className = lookaheadMatch[1];
11846 w.nextMatch += lookaheadMatch[0].length+1;
11848 if(nextRowType != currRowType) {
11849 rowContainer = createTiddlyElement(table,this.rowTypes[nextRowType]);
11850 currRowType = nextRowType;
11852 if(currRowType == "c") {
11855 if(rowContainer != table.firstChild)
11856 table.insertBefore(rowContainer,table.firstChild);
11857 rowContainer.setAttribute("align",rowCount == 0?"top":"bottom");
11858 w.subWikifyTerm(rowContainer,this.rowTermRegExp);
11860 this.rowHandler(w,createTiddlyElement(rowContainer,"tr",null,(rowCount&1)?"oddRow":"evenRow"),prevColumns);
11864 this.lookaheadRegExp.lastIndex = w.nextMatch;
11865 lookaheadMatch = this.lookaheadRegExp.exec(w.source);
11868 rowHandler: function(w,e,prevColumns)
11871 var colSpanCount = 1;
11872 var prevCell = null;
11873 this.cellRegExp.lastIndex = w.nextMatch;
11874 var cellMatch = this.cellRegExp.exec(w.source);
11875 while(cellMatch && cellMatch.index == w.nextMatch) {
11876 if(cellMatch[1] == "~") {
11878 var last = prevColumns[col];
11880 last.rowSpanCount++;
11881 last.element.setAttribute("rowspan",last.rowSpanCount);
11882 last.element.setAttribute("rowSpan",last.rowSpanCount); // Needed for IE
11883 last.element.valign = "center";
11885 w.nextMatch = this.cellRegExp.lastIndex-1;
11886 } else if(cellMatch[1] == ">") {
11889 w.nextMatch = this.cellRegExp.lastIndex-1;
11890 } else if(cellMatch[2]) {
11892 if(prevCell && colSpanCount > 1) {
11893 prevCell.setAttribute("colspan",colSpanCount);
11894 prevCell.setAttribute("colSpan",colSpanCount); // Needed for IE
11896 w.nextMatch = this.cellRegExp.lastIndex;
11901 var styles = config.formatterHelpers.inlineCssHelper(w);
11902 var spaceLeft = false;
11903 var chr = w.source.substr(w.nextMatch,1);
11904 while(chr == " ") {
11907 chr = w.source.substr(w.nextMatch,1);
11911 cell = createTiddlyElement(e,"th");
11914 cell = createTiddlyElement(e,"td");
11917 prevColumns[col] = {rowSpanCount:1,element:cell};
11918 if(colSpanCount > 1) {
11919 cell.setAttribute("colspan",colSpanCount);
11920 cell.setAttribute("colSpan",colSpanCount); // Needed for IE
11923 config.formatterHelpers.applyCssHelper(cell,styles);
11924 w.subWikifyTerm(cell,this.cellTermRegExp);
11925 if(w.matchText.substr(w.matchText.length-2,1) == " ") // spaceRight
11926 cell.align = spaceLeft ? "center" : "left";
11928 cell.align = "right";
11932 this.cellRegExp.lastIndex = w.nextMatch;
11933 cellMatch = this.cellRegExp.exec(w.source);
11941 termRegExp: /(\n)/mg,
11942 handler: function(w)
11944 w.subWikifyTerm(createTiddlyElement(w.output,"h" + w.matchLength),this.termRegExp);
11950 match: "^(?:[\\*#;:]+)",
11951 lookaheadRegExp: /^(?:(?:(\*)|(#)|(;)|(:))+)/mg,
11952 termRegExp: /(\n)/mg,
11953 handler: function(w)
11955 var stack = [w.output];
11956 var currLevel = 0, currType = null;
11957 var listLevel, listType, itemType;
11958 w.nextMatch = w.matchStart;
11959 this.lookaheadRegExp.lastIndex = w.nextMatch;
11960 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
11961 while(lookaheadMatch && lookaheadMatch.index == w.nextMatch) {
11962 if(lookaheadMatch[1]) {
11965 } else if(lookaheadMatch[2]) {
11968 } else if(lookaheadMatch[3]) {
11971 } else if(lookaheadMatch[4]) {
11975 listLevel = lookaheadMatch[0].length;
11976 w.nextMatch += lookaheadMatch[0].length;
11978 if(listLevel > currLevel) {
11979 for(t=currLevel; t<listLevel; t++) {
11980 var target = (currLevel == 0) ? stack[stack.length-1] : stack[stack.length-1].lastChild;
11981 stack.push(createTiddlyElement(target,listType));
11983 } else if(listLevel < currLevel) {
11984 for(t=currLevel; t>listLevel; t--)
11986 } else if(listLevel == currLevel && listType != currType) {
11988 stack.push(createTiddlyElement(stack[stack.length-1].lastChild,listType));
11990 currLevel = listLevel;
11991 currType = listType;
11992 var e = createTiddlyElement(stack[stack.length-1],itemType);
11993 w.subWikifyTerm(e,this.termRegExp);
11994 this.lookaheadRegExp.lastIndex = w.nextMatch;
11995 lookaheadMatch = this.lookaheadRegExp.exec(w.source);
12001 name: "quoteByBlock",
12003 termRegExp: /(^<<<(\n|$))/mg,
12004 element: "blockquote",
12005 handler: config.formatterHelpers.createElementAndWikify
12009 name: "quoteByLine",
12011 lookaheadRegExp: /^>+/mg,
12012 termRegExp: /(\n)/mg,
12013 element: "blockquote",
12014 handler: function(w)
12016 var stack = [w.output];
12018 var newLevel = w.matchLength;
12021 if(newLevel > currLevel) {
12022 for(t=currLevel; t<newLevel; t++)
12023 stack.push(createTiddlyElement(stack[stack.length-1],this.element));
12024 } else if(newLevel < currLevel) {
12025 for(t=currLevel; t>newLevel; t--)
12028 currLevel = newLevel;
12029 w.subWikifyTerm(stack[stack.length-1],this.termRegExp);
12030 createTiddlyElement(stack[stack.length-1],"br");
12031 this.lookaheadRegExp.lastIndex = w.nextMatch;
12032 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
12033 var matched = lookaheadMatch && lookaheadMatch.index == w.nextMatch;
12035 newLevel = lookaheadMatch[0].length;
12036 w.nextMatch += lookaheadMatch[0].length;
12044 match: "^----+$\\n?",
12045 handler: function(w)
12047 createTiddlyElement(w.output,"hr");
12052 name: "monospacedByLine",
12053 match: "^\\{\\{\\{\\n",
12054 lookaheadRegExp: /^\{\{\{\n((?:^[^\n]*\n)+?)(^\}\}\}$\n?)/mg,
12056 handler: config.formatterHelpers.enclosedTextHelper
12060 name: "monospacedByLineForCSS",
12061 match: "^/\\*\\{\\{\\{\\*/\\n",
12062 lookaheadRegExp: /\/\*\{\{\{\*\/\n*((?:^[^\n]*\n)+?)(\n*^\/\*\}\}\}\*\/$\n?)/mg,
12064 handler: config.formatterHelpers.enclosedTextHelper
12068 name: "monospacedByLineForPlugin",
12069 match: "^//\\{\\{\\{\\n",
12070 lookaheadRegExp: /^\/\/\{\{\{\n\n*((?:^[^\n]*\n)+?)(\n*^\/\/\}\}\}$\n?)/mg,
12072 handler: config.formatterHelpers.enclosedTextHelper
12076 name: "monospacedByLineForTemplate",
12077 match: "^<!--\\{\\{\\{-->\\n",
12078 lookaheadRegExp: /<!--\{\{\{-->\n*((?:^[^\n]*\n)+?)(\n*^<!--\}\}\}-->$\n?)/mg,
12080 handler: config.formatterHelpers.enclosedTextHelper
12084 name: "wikifyCommentForPlugin",
12085 match: "^/\\*\\*\\*\\n",
12086 termRegExp: /(^\*\*\*\/\n)/mg,
12087 handler: function(w)
12089 w.subWikifyTerm(w.output,this.termRegExp);
12094 name: "wikifyCommentForTemplate",
12095 match: "^<!---\\n",
12096 termRegExp: /(^--->\n)/mg,
12097 handler: function(w)
12099 w.subWikifyTerm(w.output,this.termRegExp);
12106 lookaheadRegExp: /<<([^>\s]+)(?:\s*)((?:[^>]|(?:>(?!>)))*)>>/mg,
12107 handler: function(w)
12109 this.lookaheadRegExp.lastIndex = w.matchStart;
12110 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
12111 if(lookaheadMatch && lookaheadMatch.index == w.matchStart && lookaheadMatch[1]) {
12112 w.nextMatch = this.lookaheadRegExp.lastIndex;
12113 invokeMacro(w.output,lookaheadMatch[1],lookaheadMatch[2],w,w.tiddler);
12119 name: "prettyLink",
12121 lookaheadRegExp: /\[\[(.*?)(?:\|(~)?(.*?))?\]\]/mg,
12122 handler: function(w)
12124 this.lookaheadRegExp.lastIndex = w.matchStart;
12125 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
12126 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
12128 var text = lookaheadMatch[1];
12129 if(lookaheadMatch[3]) {
12130 // Pretty bracketted link
12131 var link = lookaheadMatch[3];
12132 e = (!lookaheadMatch[2] && config.formatterHelpers.isExternalLink(link)) ?
12133 createExternalLink(w.output,link) : createTiddlyLink(w.output,link,false,null,w.isStatic,w.tiddler);
12135 // Simple bracketted link
12136 e = createTiddlyLink(w.output,text,false,null,w.isStatic,w.tiddler);
12138 createTiddlyText(e,text);
12139 w.nextMatch = this.lookaheadRegExp.lastIndex;
12145 name: "unWikiLink",
12146 match: config.textPrimitives.unWikiLink+config.textPrimitives.wikiLink,
12147 handler: function(w)
12149 w.outputText(w.output,w.matchStart+1,w.nextMatch);
12155 match: config.textPrimitives.wikiLink,
12156 handler: function(w)
12158 if(w.matchStart > 0) {
12159 var preRegExp = new RegExp(config.textPrimitives.anyLetterStrict,"mg");
12160 preRegExp.lastIndex = w.matchStart-1;
12161 var preMatch = preRegExp.exec(w.source);
12162 if(preMatch.index == w.matchStart-1) {
12163 w.outputText(w.output,w.matchStart,w.nextMatch);
12167 if(w.autoLinkWikiWords == true || store.isShadowTiddler(w.matchText)) {
12168 var link = createTiddlyLink(w.output,w.matchText,false,null,w.isStatic,w.tiddler);
12169 w.outputText(link,w.matchStart,w.nextMatch);
12171 w.outputText(w.output,w.matchStart,w.nextMatch);
12178 match: config.textPrimitives.urlPattern,
12179 handler: function(w)
12181 w.outputText(createExternalLink(w.output,w.matchText),w.matchStart,w.nextMatch);
12187 match: "\\[[<>]?[Ii][Mm][Gg]\\[",
12188 lookaheadRegExp: /\[([<]?)(>?)[Ii][Mm][Gg]\[(?:([^\|\]]+)\|)?([^\[\]\|]+)\](?:\[([^\]]*)\])?\]/mg,
12189 handler: function(w)
12191 this.lookaheadRegExp.lastIndex = w.matchStart;
12192 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
12193 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
12195 if(lookaheadMatch[5]) {
12196 var link = lookaheadMatch[5];
12197 e = config.formatterHelpers.isExternalLink(link) ? createExternalLink(w.output,link) : createTiddlyLink(w.output,link,false,null,w.isStatic,w.tiddler);
12198 addClass(e,"imageLink");
12200 var img = createTiddlyElement(e,"img");
12201 if(lookaheadMatch[1])
12202 img.align = "left";
12203 else if(lookaheadMatch[2])
12204 img.align = "right";
12205 if(lookaheadMatch[3])
12206 img.title = lookaheadMatch[3];
12207 img.src = lookaheadMatch[4];
12208 w.nextMatch = this.lookaheadRegExp.lastIndex;
12215 match: "<[Hh][Tt][Mm][Ll]>",
12216 lookaheadRegExp: /<[Hh][Tt][Mm][Ll]>((?:.|\n)*?)<\/[Hh][Tt][Mm][Ll]>/mg,
12217 handler: function(w)
12219 this.lookaheadRegExp.lastIndex = w.matchStart;
12220 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
12221 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
12222 createTiddlyElement(w.output,"span").innerHTML = lookaheadMatch[1];
12223 w.nextMatch = this.lookaheadRegExp.lastIndex;
12229 name: "commentByBlock",
12231 lookaheadRegExp: /\/%((?:.|\n)*?)%\//mg,
12232 handler: function(w)
12234 this.lookaheadRegExp.lastIndex = w.matchStart;
12235 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
12236 if(lookaheadMatch && lookaheadMatch.index == w.matchStart)
12237 w.nextMatch = this.lookaheadRegExp.lastIndex;
12242 name: "boldByChar",
12244 termRegExp: /('')/mg,
12246 handler: config.formatterHelpers.createElementAndWikify
12250 name: "italicByChar",
12252 termRegExp: /(\/\/)/mg,
12254 handler: config.formatterHelpers.createElementAndWikify
12258 name: "underlineByChar",
12260 termRegExp: /(__)/mg,
12262 handler: config.formatterHelpers.createElementAndWikify
12266 name: "strikeByChar",
12267 match: "--(?!\\s|$)",
12268 termRegExp: /((?!\s)--|(?=\n\n))/mg,
12270 handler: config.formatterHelpers.createElementAndWikify
12274 name: "superscriptByChar",
12276 termRegExp: /(\^\^)/mg,
12278 handler: config.formatterHelpers.createElementAndWikify
12282 name: "subscriptByChar",
12284 termRegExp: /(~~)/mg,
12286 handler: config.formatterHelpers.createElementAndWikify
12290 name: "monospacedByChar",
12291 match: "\\{\\{\\{",
12292 lookaheadRegExp: /\{\{\{((?:.|\n)*?)\}\}\}/mg,
12293 handler: function(w)
12295 this.lookaheadRegExp.lastIndex = w.matchStart;
12296 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
12297 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
12298 createTiddlyElement(w.output,"code",null,null,lookaheadMatch[1]);
12299 w.nextMatch = this.lookaheadRegExp.lastIndex;
12305 name: "styleByChar",
12307 termRegExp: /(@@)/mg,
12308 handler: function(w)
12310 var e = createTiddlyElement(w.output,"span");
12311 var styles = config.formatterHelpers.inlineCssHelper(w);
12312 if(styles.length == 0)
12313 e.className = "marked";
12315 config.formatterHelpers.applyCssHelper(e,styles);
12316 w.subWikifyTerm(e,this.termRegExp);
12322 match: "\\n|<br ?/?>",
12323 handler: function(w)
12325 createTiddlyElement(w.output,"br");
12331 match: "\\\"{3}|<nowiki>",
12332 lookaheadRegExp: /(?:\"{3}|<nowiki>)((?:.|\n)*?)(?:\"{3}|<\/nowiki>)/mg,
12333 handler: function(w)
12335 this.lookaheadRegExp.lastIndex = w.matchStart;
12336 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
12337 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
12338 createTiddlyElement(w.output,"span",null,null,lookaheadMatch[1]);
12339 w.nextMatch = this.lookaheadRegExp.lastIndex;
12347 handler: function(w)
12349 createTiddlyElement(w.output,"span").innerHTML = "—";
12354 name: "htmlEntitiesEncoding",
12355 match: "(?:(?:&#?[a-zA-Z0-9]{2,8};|.)(?:&#?(?:x0*(?:3[0-6][0-9a-fA-F]|1D[c-fC-F][0-9a-fA-F]|20[d-fD-F][0-9a-fA-F]|FE2[0-9a-fA-F])|0*(?:76[89]|7[7-9][0-9]|8[0-7][0-9]|761[6-9]|76[2-7][0-9]|84[0-3][0-9]|844[0-7]|6505[6-9]|6506[0-9]|6507[0-1]));)+|&#?[a-zA-Z0-9]{2,8};)",
12356 handler: function(w)
12358 createTiddlyElement(w.output,"span").innerHTML = w.matchText;
12363 name: "customClasses",
12365 termRegExp: /(\}\}\})/mg,
12366 lookaheadRegExp: /\{\{[\s]*([\w]+[\s\w]*)[\s]*\{(\n?)/mg,
12367 handler: function(w)
12369 this.lookaheadRegExp.lastIndex = w.matchStart;
12370 var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
12371 if(lookaheadMatch) {
12372 var e = createTiddlyElement(w.output,lookaheadMatch[2] == "\n" ? "div" : "span",null,lookaheadMatch[1]);
12373 w.nextMatch = this.lookaheadRegExp.lastIndex;
12374 w.subWikifyTerm(e,this.termRegExp);
12385 function getParser(tiddler,format)
12389 format = tiddler.fields["wikiformat"];
12391 for(var i in config.parsers) {
12392 if(format == config.parsers[i].format)
12393 return config.parsers[i];
12396 for(var i in config.parsers) {
12397 if(tiddler.isTagged(config.parsers[i].formatTag))
12398 return config.parsers[i];
12405 function wikify(source,output,highlightRegExp,tiddler)
12407 if(source && source != "") {
12408 var wikifier = new Wikifier(source,getParser(tiddler),highlightRegExp,tiddler);
12409 wikifier.subWikifyUnterm(output);
12413 function wikifyStatic(source,highlightRegExp,tiddler,format)
12415 var e = createTiddlyElement(document.body,"div");
12416 e.style.display = "none";
12418 if(source && source != "") {
12419 var wikifier = new Wikifier(source,getParser(tiddler,format),highlightRegExp,tiddler);
12420 wikifier.isStatic = true;
12421 wikifier.subWikifyUnterm(e);
12422 html = e.innerHTML;
12428 function wikifyPlain(title,theStore,limit)
12432 if(theStore.tiddlerExists(title) || theStore.isShadowTiddler(title)) {
12433 return wikifyPlainText(theStore.getTiddlerText(title),limit,tiddler);
12439 function wikifyPlainText(text,limit,tiddler)
12442 text = text.substr(0,limit);
12443 var wikifier = new Wikifier(text,formatter,null,tiddler);
12444 return wikifier.wikifyPlain();
12447 function highlightify(source,output,highlightRegExp,tiddler)
12449 if(source && source != "") {
12450 var wikifier = new Wikifier(source,formatter,highlightRegExp,tiddler);
12451 wikifier.outputText(output,0,source.length);
12455 function Wikifier(source,formatter,highlightRegExp,tiddler)
12457 this.source = source;
12458 this.output = null;
12459 this.formatter = formatter;
12460 this.nextMatch = 0;
12461 this.autoLinkWikiWords = tiddler && tiddler.autoLinkWikiWords() == false ? false : true;
12462 this.highlightRegExp = highlightRegExp;
12463 this.highlightMatch = null;
12464 this.isStatic = false;
12465 if(highlightRegExp) {
12466 highlightRegExp.lastIndex = 0;
12467 this.highlightMatch = highlightRegExp.exec(source);
12469 this.tiddler = tiddler;
12472 Wikifier.prototype.wikifyPlain = function()
12474 var e = createTiddlyElement(document.body,"div");
12476 var text = getPlainText(e);
12481 Wikifier.prototype.subWikify = function(output,terminator)
12484 this.subWikifyTerm(output,new RegExp("(" + terminator + ")","mg"));
12486 this.subWikifyUnterm(output);
12489 Wikifier.prototype.subWikifyUnterm = function(output)
12491 // subWikify() can be indirectly recursive, so we need to save the old output pointer
12492 var oldOutput = this.output;
12493 this.output = output;
12494 this.formatter.formatterRegExp.lastIndex = this.nextMatch;
12495 var formatterMatch = this.formatter.formatterRegExp.exec(this.source);
12496 while(formatterMatch) {
12497 // Output any text before the match
12498 if(formatterMatch.index > this.nextMatch)
12499 this.outputText(this.output,this.nextMatch,formatterMatch.index);
12500 // Set the match parameters for the handler
12501 this.matchStart = formatterMatch.index;
12502 this.matchLength = formatterMatch[0].length;
12503 this.matchText = formatterMatch[0];
12504 this.nextMatch = this.formatter.formatterRegExp.lastIndex;
12505 for(var t=1; t<formatterMatch.length; t++) {
12506 if(formatterMatch[t]) {
12507 this.formatter.formatters[t-1].handler(this);
12508 this.formatter.formatterRegExp.lastIndex = this.nextMatch;
12512 formatterMatch = this.formatter.formatterRegExp.exec(this.source);
12514 if(this.nextMatch < this.source.length) {
12515 this.outputText(this.output,this.nextMatch,this.source.length);
12516 this.nextMatch = this.source.length;
12518 this.output = oldOutput;
12521 Wikifier.prototype.subWikifyTerm = function(output,terminatorRegExp)
12523 // subWikify() can be indirectly recursive, so we need to save the old output pointer
12524 var oldOutput = this.output;
12525 this.output = output;
12526 // Get the first matches for the formatter and terminator RegExps
12527 terminatorRegExp.lastIndex = this.nextMatch;
12528 var terminatorMatch = terminatorRegExp.exec(this.source);
12529 this.formatter.formatterRegExp.lastIndex = this.nextMatch;
12530 var formatterMatch = this.formatter.formatterRegExp.exec(terminatorMatch ? this.source.substr(0,terminatorMatch.index) : this.source);
12531 while(terminatorMatch || formatterMatch) {
12532 if(terminatorMatch && (!formatterMatch || terminatorMatch.index <= formatterMatch.index)) {
12533 if(terminatorMatch.index > this.nextMatch)
12534 this.outputText(this.output,this.nextMatch,terminatorMatch.index);
12535 this.matchText = terminatorMatch[1];
12536 this.matchLength = terminatorMatch[1].length;
12537 this.matchStart = terminatorMatch.index;
12538 this.nextMatch = this.matchStart + this.matchLength;
12539 this.output = oldOutput;
12542 if(formatterMatch.index > this.nextMatch)
12543 this.outputText(this.output,this.nextMatch,formatterMatch.index);
12544 this.matchStart = formatterMatch.index;
12545 this.matchLength = formatterMatch[0].length;
12546 this.matchText = formatterMatch[0];
12547 this.nextMatch = this.formatter.formatterRegExp.lastIndex;
12548 for(var t=1; t<formatterMatch.length; t++) {
12549 if(formatterMatch[t]) {
12550 this.formatter.formatters[t-1].handler(this);
12551 this.formatter.formatterRegExp.lastIndex = this.nextMatch;
12555 terminatorRegExp.lastIndex = this.nextMatch;
12556 terminatorMatch = terminatorRegExp.exec(this.source);
12557 formatterMatch = this.formatter.formatterRegExp.exec(terminatorMatch ? this.source.substr(0,terminatorMatch.index) : this.source);
12559 if(this.nextMatch < this.source.length) {
12560 this.outputText(this.output,this.nextMatch,this.source.length);
12561 this.nextMatch = this.source.length;
12563 this.output = oldOutput;
12566 Wikifier.prototype.outputText = function(place,startPos,endPos)
12568 while(this.highlightMatch && (this.highlightRegExp.lastIndex > startPos) && (this.highlightMatch.index < endPos) && (startPos < endPos)) {
12569 if(this.highlightMatch.index > startPos) {
12570 createTiddlyText(place,this.source.substring(startPos,this.highlightMatch.index));
12571 startPos = this.highlightMatch.index;
12573 var highlightEnd = Math.min(this.highlightRegExp.lastIndex,endPos);
12574 var theHighlight = createTiddlyElement(place,"span",null,"highlight",this.source.substring(startPos,highlightEnd));
12575 startPos = highlightEnd;
12576 if(startPos >= this.highlightRegExp.lastIndex)
12577 this.highlightMatch = this.highlightRegExp.exec(this.source);
12579 if(startPos < endPos) {
12580 createTiddlyText(place,this.source.substring(startPos,endPos));
12585 //-- Macro definitions
12588 config.macros.today.handler = function(place,macroName,params)
12590 var now = new Date();
12593 text = now.formatString(params[0].trim());
12595 text = now.toLocaleString();
12596 createTiddlyElement(place,"span",null,null,text);
12599 config.macros.version.handler = function(place)
12601 createTiddlyElement(place,"span",null,null,version.major + "." + version.minor + "." + version.revision + (version.beta ? " (beta " + version.beta + ")" : ""));
12604 config.macros.list.handler = function(place,macroName,params)
12606 var type = params[0] ? params[0] : "all";
12607 var list = document.createElement("ul");
12608 place.appendChild(list);
12609 if(this[type].prompt)
12610 createTiddlyElement(list,"li",null,"listTitle",this[type].prompt);
12612 if(this[type].handler)
12613 results = this[type].handler(params);
12614 for(var t = 0; t < results.length; t++) {
12615 var li = document.createElement("li");
12616 list.appendChild(li);
12617 createTiddlyLink(li,typeof results[t] == "string" ? results[t] : results[t].title,true);
12621 config.macros.list.all.handler = function(params)
12623 return store.reverseLookup("tags","excludeLists",false,"title");
12626 config.macros.list.missing.handler = function(params)
12628 return store.getMissingLinks();
12631 config.macros.list.orphans.handler = function(params)
12633 return store.getOrphans();
12636 config.macros.list.shadowed.handler = function(params)
12638 return store.getShadowed();
12641 config.macros.list.touched.handler = function(params)
12643 return store.getTouched();
12646 config.macros.allTags.handler = function(place,macroName,params)
12648 var tags = store.getTags(params[0]);
12649 var ul = createTiddlyElement(place,"ul");
12650 if(tags.length == 0)
12651 createTiddlyElement(ul,"li",null,"listTitle",this.noTags);
12652 for(var t=0; t<tags.length; t++) {
12653 var title = tags[t][0];
12654 var info = getTiddlyLinkInfo(title);
12655 var li =createTiddlyElement(ul,"li");
12656 var btn = createTiddlyButton(li,title + " (" + tags[t][1] + ")",this.tooltip.format([title]),onClickTag,info.classes);
12657 btn.setAttribute("tag",title);
12658 btn.setAttribute("refresh","link");
12659 btn.setAttribute("tiddlyLink",title);
12663 config.macros.timeline.handler = function(place,macroName,params)
12665 var field = params[0] ? params[0] : "modified";
12666 var tiddlers = store.reverseLookup("tags","excludeLists",false,field);
12668 var last = params[1] ? tiddlers.length-Math.min(tiddlers.length,parseInt(params[1])) : 0;
12669 for(var t=tiddlers.length-1; t>=last; t--) {
12670 var tiddler = tiddlers[t];
12671 var theDay = tiddler[field].convertToLocalYYYYMMDDHHMM().substr(0,8);
12672 if(theDay != lastDay) {
12673 var theDateList = document.createElement("ul");
12674 place.appendChild(theDateList);
12675 createTiddlyElement(theDateList,"li",null,"listTitle",tiddler[field].formatString(this.dateFormat));
12678 var theDateListItem = createTiddlyElement(theDateList,"li",null,"listLink");
12679 theDateListItem.appendChild(createTiddlyLink(place,tiddler.title,true));
12683 config.macros.search.handler = function(place,macroName,params)
12685 var searchTimeout = null;
12686 var btn = createTiddlyButton(place,this.label,this.prompt,this.onClick);
12687 var txt = createTiddlyElement(place,"input",null,"txtOptionInput");
12689 txt.value = params[0];
12690 txt.onkeyup = this.onKeyPress;
12691 txt.onfocus = this.onFocus;
12692 txt.setAttribute("size",this.sizeTextbox);
12693 txt.setAttribute("accessKey",this.accessKey);
12694 txt.setAttribute("autocomplete","off");
12695 txt.setAttribute("lastSearchText","");
12696 if(config.browser.isSafari) {
12697 txt.setAttribute("type","search");
12698 txt.setAttribute("results","5");
12700 txt.setAttribute("type","text");
12704 // Global because there's only ever one outstanding incremental search timer
12705 config.macros.search.timeout = null;
12707 config.macros.search.doSearch = function(txt)
12709 if(txt.value.length > 0) {
12710 story.search(txt.value,config.options.chkCaseSensitiveSearch,config.options.chkRegExpSearch);
12711 txt.setAttribute("lastSearchText",txt.value);
12715 config.macros.search.onClick = function(e)
12717 config.macros.search.doSearch(this.nextSibling);
12721 config.macros.search.onKeyPress = function(e)
12723 if(!e) var e = window.event;
12724 switch(e.keyCode) {
12725 case 13: // Ctrl-Enter
12726 case 10: // Ctrl-Enter on IE PC
12727 config.macros.search.doSearch(this);
12734 if(this.value.length > 2) {
12735 if(this.value != this.getAttribute("lastSearchText")) {
12736 if(config.macros.search.timeout)
12737 clearTimeout(config.macros.search.timeout);
12739 config.macros.search.timeout = setTimeout(function() {config.macros.search.doSearch(txt);},500);
12742 if(config.macros.search.timeout)
12743 clearTimeout(config.macros.search.timeout);
12747 config.macros.search.onFocus = function(e)
12752 config.macros.tiddler.handler = function(place,macroName,params,wikifier,paramString,tiddler)
12754 params = paramString.parseParams("name",null,true,false,true);
12755 var names = params[0]["name"];
12756 var tiddlerName = names[0];
12757 var className = names[1] ? names[1] : null;
12758 var args = params[0]["with"];
12759 var wrapper = createTiddlyElement(place,"span",null,className);
12761 wrapper.setAttribute("refresh","content");
12762 wrapper.setAttribute("tiddler",tiddlerName);
12764 var text = store.getTiddlerText(tiddlerName);
12766 var stack = config.macros.tiddler.tiddlerStack;
12767 if(stack.indexOf(tiddlerName) !== -1)
12769 stack.push(tiddlerName);
12771 var n = args ? Math.min(args.length,9) : 0;
12772 for(var i=0; i<n; i++) {
12773 var placeholderRE = new RegExp("\\$" + (i + 1),"mg");
12774 text = text.replace(placeholderRE,args[i]);
12776 config.macros.tiddler.renderText(wrapper,text,tiddlerName,params);
12783 config.macros.tiddler.renderText = function(place,text,tiddlerName,params)
12785 wikify(text,place,null,store.getTiddler(tiddlerName));
12788 config.macros.tiddler.tiddlerStack = [];
12790 config.macros.tag.handler = function(place,macroName,params)
12792 createTagButton(place,params[0]);
12795 config.macros.tags.handler = function(place,macroName,params,wikifier,paramString,tiddler)
12797 params = paramString.parseParams("anon",null,true,false,false);
12798 var theList = createTiddlyElement(place,"ul");
12799 var title = getParam(params,"anon","");
12800 if(title && store.tiddlerExists(title))
12801 tiddler = store.getTiddler(title);
12802 var sep = getParam(params,"sep"," ");
12803 var lingo = config.views.wikified.tag;
12804 var prompt = tiddler.tags.length == 0 ? lingo.labelNoTags : lingo.labelTags;
12805 createTiddlyElement(theList,"li",null,"listTitle",prompt.format([tiddler.title]));
12806 for(var t=0; t<tiddler.tags.length; t++) {
12807 createTagButton(createTiddlyElement(theList,"li"),tiddler.tags[t],tiddler.title);
12808 if(t<tiddler.tags.length-1)
12809 createTiddlyText(theList,sep);
12813 config.macros.tagging.handler = function(place,macroName,params,wikifier,paramString,tiddler)
12815 params = paramString.parseParams("anon",null,true,false,false);
12816 var theList = createTiddlyElement(place,"ul");
12817 var title = getParam(params,"anon","");
12818 if(title == "" && tiddler instanceof Tiddler)
12819 title = tiddler.title;
12820 var sep = getParam(params,"sep"," ");
12821 theList.setAttribute("title",this.tooltip.format([title]));
12822 var tagged = store.getTaggedTiddlers(title);
12823 var prompt = tagged.length == 0 ? this.labelNotTag : this.label;
12824 createTiddlyElement(theList,"li",null,"listTitle",prompt.format([title,tagged.length]));
12825 for(var t=0; t<tagged.length; t++) {
12826 createTiddlyLink(createTiddlyElement(theList,"li"),tagged[t].title,true);
12827 if(t<tagged.length-1)
12828 createTiddlyText(theList,sep);
12832 config.macros.closeAll.handler = function(place)
12834 createTiddlyButton(place,this.label,this.prompt,this.onClick);
12837 config.macros.closeAll.onClick = function(e)
12839 story.closeAllTiddlers();
12843 config.macros.permaview.handler = function(place)
12845 createTiddlyButton(place,this.label,this.prompt,this.onClick);
12848 config.macros.permaview.onClick = function(e)
12854 config.macros.saveChanges.handler = function(place)
12857 createTiddlyButton(place,this.label,this.prompt,this.onClick,null,null,this.accessKey);
12860 config.macros.saveChanges.onClick = function(e)
12866 config.macros.slider.onClickSlider = function(e)
12868 if(!e) var e = window.event;
12869 var n = this.nextSibling;
12870 var cookie = n.getAttribute("cookie");
12871 var isOpen = n.style.display != "none";
12872 if(config.options.chkAnimate && anim && typeof Slider == "function")
12873 anim.startAnimating(new Slider(n,!isOpen,null,"none"));
12875 n.style.display = isOpen ? "none" : "block";
12876 config.options[cookie] = !isOpen;
12877 saveOptionCookie(cookie);
12881 config.macros.slider.createSlider = function(place,cookie,title,tooltip)
12883 var cookie = cookie ? cookie : "";
12884 var btn = createTiddlyButton(place,title,tooltip,this.onClickSlider);
12885 var panel = createTiddlyElement(null,"div",null,"sliderPanel");
12886 panel.setAttribute("cookie",cookie);
12887 panel.style.display = config.options[cookie] ? "block" : "none";
12888 place.appendChild(panel);
12892 config.macros.slider.handler = function(place,macroName,params)
12894 var panel = this.createSlider(place,params[0],params[2],params[3]);
12895 var text = store.getTiddlerText(params[1]);
12896 panel.setAttribute("refresh","content");
12897 panel.setAttribute("tiddler",params[1]);
12899 wikify(text,panel,null,store.getTiddler(params[1]));
12902 config.macros.option.genericCreate = function(place,type,opt,className,desc)
12904 var typeInfo = config.macros.option.types[type];
12905 var c = document.createElement(typeInfo.elementType);
12906 if(typeInfo.typeValue)
12907 c.setAttribute("type",typeInfo.typeValue);
12908 c[typeInfo.eventName] = typeInfo.onChange;
12909 c.setAttribute("option",opt);
12911 c.className = className;
12913 c.className = typeInfo.className;
12914 if(config.optionsDesc[opt])
12915 c.setAttribute("title",config.optionsDesc[opt]);
12916 place.appendChild(c);
12918 createTiddlyText(place,config.optionsDesc[opt] ? config.optionsDesc[opt] : opt);
12919 c[typeInfo.valueField] = config.options[opt];
12923 config.macros.option.genericOnChange = function(e)
12925 var opt = this.getAttribute("option");
12927 var optType = opt.substr(0,3);
12928 var handler = config.macros.option.types[optType];
12929 if (handler.elementType && handler.valueField)
12930 config.macros.option.propagateOption(opt,handler.valueField,this[handler.valueField],handler.elementType);
12935 config.macros.option.types = {
12937 elementType: "input",
12938 valueField: "value",
12939 eventName: "onkeyup",
12940 className: "txtOptionInput",
12941 create: config.macros.option.genericCreate,
12942 onChange: config.macros.option.genericOnChange
12945 elementType: "input",
12946 valueField: "checked",
12947 eventName: "onclick",
12948 className: "chkOptionInput",
12949 typeValue: "checkbox",
12950 create: config.macros.option.genericCreate,
12951 onChange: config.macros.option.genericOnChange
12955 config.macros.option.propagateOption = function(opt,valueField,value,elementType)
12957 config.options[opt] = value;
12958 saveOptionCookie(opt);
12959 var nodes = document.getElementsByTagName(elementType);
12960 for(var t=0; t<nodes.length; t++) {
12961 var optNode = nodes[t].getAttribute("option");
12963 nodes[t][valueField] = value;
12967 config.macros.option.handler = function(place,macroName,params,wikifier,paramString,tiddler)
12969 params = paramString.parseParams("anon",null,true,false,false);
12970 var opt = (params[1] && params[1].name == "anon") ? params[1].value : getParam(params,"name",null);
12971 var className = (params[2] && params[2].name == "anon") ? params[2].value : getParam(params,"class",null);
12972 var desc = getParam(params,"desc","no");
12973 var type = opt.substr(0,3);
12974 var h = config.macros.option.types[type];
12976 h.create(place,type,opt,className,desc);
12979 config.macros.options.handler = function(place,macroName,params,wikifier,paramString,tiddler)
12981 params = paramString.parseParams("anon",null,true,false,false);
12982 var showUnknown = getParam(params,"showUnknown","no");
12983 var wizard = new Wizard();
12984 wizard.createWizard(place,this.wizardTitle);
12985 wizard.addStep(this.step1Title,this.step1Html);
12986 var markList = wizard.getElement("markList");
12987 var chkUnknown = wizard.getElement("chkUnknown");
12988 chkUnknown.checked = showUnknown == "yes";
12989 chkUnknown.onchange = this.onChangeUnknown;
12990 var listWrapper = document.createElement("div");
12991 markList.parentNode.insertBefore(listWrapper,markList);
12992 wizard.setValue("listWrapper",listWrapper);
12993 this.refreshOptions(listWrapper,showUnknown == "yes");
12996 config.macros.options.refreshOptions = function(listWrapper,showUnknown)
12999 for(var n in config.options) {
13003 opt.lowlight = !config.optionsDesc[n];
13004 opt.description = opt.lowlight ? this.unknownDescription : config.optionsDesc[n];
13005 if(!opt.lowlight || showUnknown)
13008 opts.sort(function(a,b) {return a.name.substr(3) < b.name.substr(3) ? -1 : (a.name.substr(3) == b.name.substr(3) ? 0 : +1);});
13009 var listview = ListView.create(listWrapper,opts,this.listViewTemplate);
13010 for(n=0; n<opts.length; n++) {
13011 var type = opts[n].name.substr(0,3);
13012 var h = config.macros.option.types[type];
13013 if (h && h.create) {
13014 h.create(opts[n].colElements['option'],type,opts[n].name,null,"no");
13019 config.macros.options.onChangeUnknown = function(e)
13021 var wizard = new Wizard(this);
13022 var listWrapper = wizard.getValue("listWrapper");
13023 removeChildren(listWrapper);
13024 config.macros.options.refreshOptions(listWrapper,this.checked);
13028 config.macros.newTiddler.createNewTiddlerButton = function(place,title,params,label,prompt,accessKey,newFocus,isJournal)
13031 for(var t=1; t<params.length; t++) {
13032 if((params[t].name == "anon" && t != 1) || (params[t].name == "tag"))
13033 tags.push(params[t].value);
13035 label = getParam(params,"label",label);
13036 prompt = getParam(params,"prompt",prompt);
13037 accessKey = getParam(params,"accessKey",accessKey);
13038 newFocus = getParam(params,"focus",newFocus);
13039 var customFields = getParam(params,"fields","");
13040 if(!customFields && !store.isShadowTiddler(title))
13041 customFields = String.encodeHashMap(config.defaultCustomFields);
13042 var btn = createTiddlyButton(place,label,prompt,this.onClickNewTiddler,null,null,accessKey);
13043 btn.setAttribute("newTitle",title);
13044 btn.setAttribute("isJournal",isJournal ? "true" : "false");
13045 if(tags.length > 0)
13046 btn.setAttribute("params",tags.join("|"));
13047 btn.setAttribute("newFocus",newFocus);
13048 btn.setAttribute("newTemplate",getParam(params,"template",DEFAULT_EDIT_TEMPLATE));
13049 if(customFields !== "")
13050 btn.setAttribute("customFields",customFields);
13051 var text = getParam(params,"text");
13052 if(text !== undefined)
13053 btn.setAttribute("newText",text);
13057 config.macros.newTiddler.onClickNewTiddler = function()
13059 var title = this.getAttribute("newTitle");
13060 if(this.getAttribute("isJournal") == "true") {
13061 var now = new Date();
13062 title = now.formatString(title.trim());
13064 var params = this.getAttribute("params");
13065 var tags = params ? params.split("|") : [];
13066 var focus = this.getAttribute("newFocus");
13067 var template = this.getAttribute("newTemplate");
13068 var customFields = this.getAttribute("customFields");
13069 story.displayTiddler(null,title,template,false,null,null);
13070 var tiddlerElem = document.getElementById(story.idPrefix + title);
13072 story.addCustomFields(tiddlerElem,customFields);
13073 var text = this.getAttribute("newText");
13074 if(typeof text == "string")
13075 story.getTiddlerField(title,"text").value = text.format([title]);
13076 for(var t=0;t<tags.length;t++)
13077 story.setTiddlerTag(title,tags[t],+1);
13078 story.focusTiddler(title,focus);
13082 config.macros.newTiddler.handler = function(place,macroName,params,wikifier,paramString,tiddler)
13085 params = paramString.parseParams("anon",null,true,false,false);
13086 var title = params[1] && params[1].name == "anon" ? params[1].value : this.title;
13087 title = getParam(params,"title",title);
13088 this.createNewTiddlerButton(place,title,params,this.label,this.prompt,this.accessKey,"title",false);
13092 config.macros.newJournal.handler = function(place,macroName,params,wikifier,paramString,tiddler)
13095 params = paramString.parseParams("anon",null,true,false,false);
13096 var title = params[1] && params[1].name == "anon" ? params[1].value : "";
13097 title = getParam(params,"title",title);
13098 config.macros.newTiddler.createNewTiddlerButton(place,title,params,this.label,this.prompt,this.accessKey,"text",true);
13102 config.macros.sparkline.handler = function(place,macroName,params)
13107 for(var t=0; t<params.length; t++) {
13108 var v = parseInt(params[t]);
13115 if(data.length < 1)
13117 var box = createTiddlyElement(place,"span",null,"sparkline",String.fromCharCode(160));
13118 box.title = data.join(",");
13119 var w = box.offsetWidth;
13120 var h = box.offsetHeight;
13121 box.style.paddingRight = (data.length * 2 - w) + "px";
13122 box.style.position = "relative";
13123 for(var d=0; d<data.length; d++) {
13124 var tick = document.createElement("img");
13126 tick.className = "sparktick";
13127 tick.style.position = "absolute";
13128 tick.src = "data:image/gif,GIF89a%01%00%01%00%91%FF%00%FF%FF%FF%00%00%00%C0%C0%C0%00%00%00!%F9%04%01%00%00%02%00%2C%00%00%00%00%01%00%01%00%40%02%02T%01%00%3B";
13129 tick.style.left = d*2 + "px";
13130 tick.style.width = "2px";
13131 var v = Math.floor(((data[d] - min)/(max-min)) * h);
13132 tick.style.top = (h-v) + "px";
13133 tick.style.height = v + "px";
13134 box.appendChild(tick);
13138 config.macros.tabs.handler = function(place,macroName,params)
13140 var cookie = params[0];
13141 var numTabs = (params.length-1)/3;
13142 var wrapper = createTiddlyElement(null,"div",null,cookie);
13143 var tabset = createTiddlyElement(wrapper,"div",null,"tabset");
13144 tabset.setAttribute("cookie",cookie);
13145 var validTab = false;
13146 for(var t=0; t<numTabs; t++) {
13147 var label = params[t*3+1];
13148 var prompt = params[t*3+2];
13149 var content = params[t*3+3];
13150 var tab = createTiddlyButton(tabset,label,prompt,this.onClickTab,"tab tabUnselected");
13151 tab.setAttribute("tab",label);
13152 tab.setAttribute("content",content);
13153 tab.title = prompt;
13154 if(config.options[cookie] == label)
13158 config.options[cookie] = params[1];
13159 place.appendChild(wrapper);
13160 this.switchTab(tabset,config.options[cookie]);
13163 config.macros.tabs.onClickTab = function(e)
13165 config.macros.tabs.switchTab(this.parentNode,this.getAttribute("tab"));
13169 config.macros.tabs.switchTab = function(tabset,tab)
13171 var cookie = tabset.getAttribute("cookie");
13173 var nodes = tabset.childNodes;
13174 for(var t=0; t<nodes.length; t++) {
13175 if(nodes[t].getAttribute && nodes[t].getAttribute("tab") == tab) {
13177 theTab.className = "tab tabSelected";
13179 nodes[t].className = "tab tabUnselected";
13183 if(tabset.nextSibling && tabset.nextSibling.className == "tabContents")
13184 removeNode(tabset.nextSibling);
13185 var tabContent = createTiddlyElement(null,"div",null,"tabContents");
13186 tabset.parentNode.insertBefore(tabContent,tabset.nextSibling);
13187 var contentTitle = theTab.getAttribute("content");
13188 wikify(store.getTiddlerText(contentTitle),tabContent,null,store.getTiddler(contentTitle));
13190 config.options[cookie] = tab;
13191 saveOptionCookie(cookie);
13196 // <<gradient [[tiddler name]] vert|horiz rgb rgb rgb rgb... >>
13197 config.macros.gradient.handler = function(place,macroName,params,wikifier)
13199 var terminator = ">>";
13202 panel = createTiddlyElement(place,"div",null,"gradient");
13205 panel.style.position = "relative";
13206 panel.style.overflow = "hidden";
13207 panel.style.zIndex = "0";
13210 var styles = config.formatterHelpers.inlineCssHelper(wikifier);
13211 config.formatterHelpers.applyCssHelper(panel,styles);
13214 for(t=1; t<params.length; t++) {
13215 var c = new RGB(params[t]);
13219 drawGradient(panel,params[0] != "vert",colours);
13221 wikifier.subWikify(panel,terminator);
13223 panel.style.height = "100%";
13224 panel.style.width = "100%";
13228 config.macros.message.handler = function(place,macroName,params)
13232 var p = params[0].split(".");
13233 for(var t=0; t<p.length; t++) {
13239 createTiddlyText(place,m.toString().format(params.splice(1)));
13243 config.macros.view.handler = function(place,macroName,params,wikifier,paramString,tiddler)
13245 if((tiddler instanceof Tiddler) && params[0]) {
13246 var value = store.getValue(tiddler,params[0]);
13247 if(value != undefined) {
13248 switch(params[1]) {
13250 highlightify(value,place,highlightHack,tiddler);
13253 createTiddlyLink(place,value,true);
13256 wikify(value,place,highlightHack,tiddler);
13259 value = Date.convertFromYYYYMMDDHHMM(value);
13260 createTiddlyText(place,value.formatString(params[2] ? params[2] : config.views.wikified.dateFormat));
13267 config.macros.edit.handler = function(place,macroName,params,wikifier,paramString,tiddler)
13269 var field = params[0];
13270 var rows = params[1];
13271 if((tiddler instanceof Tiddler) && field) {
13272 story.setDirty(tiddler.title,true);
13273 if(field != "text" && !rows) {
13274 var e = createTiddlyElement(null,"input");
13275 if(tiddler.isReadOnly())
13276 e.setAttribute("readOnly","readOnly");
13277 e.setAttribute("edit",field);
13278 e.setAttribute("type","text");
13279 var v = store.getValue(tiddler,field);
13283 e.setAttribute("size","40");
13284 e.setAttribute("autocomplete","off");
13285 place.appendChild(e);
13287 var wrapper1 = createTiddlyElement(null,"fieldset",null,"fieldsetFix");
13288 var wrapper2 = createTiddlyElement(wrapper1,"div");
13289 var e = createTiddlyElement(wrapper2,"textarea");
13290 if(tiddler.isReadOnly())
13291 e.setAttribute("readOnly","readOnly");
13292 var v = store.getValue(tiddler,field);
13296 var rows = rows ? rows : 10;
13297 var lines = v.match(/\n/mg);
13298 var maxLines = Math.max(parseInt(config.options.txtMaxEditRows),5);
13299 if(lines != null && lines.length > rows)
13300 rows = lines.length + 5;
13301 rows = Math.min(rows,maxLines);
13302 e.setAttribute("rows",rows);
13303 e.setAttribute("edit",field);
13304 place.appendChild(wrapper1);
13309 config.macros.tagChooser.onClick = function(e)
13311 if(!e) var e = window.event;
13312 var lingo = config.views.editor.tagChooser;
13313 var popup = Popup.create(this);
13314 var tags = store.getTags();
13315 if(tags.length == 0)
13316 createTiddlyText(createTiddlyElement(popup,"li"),lingo.popupNone);
13317 for(var t=0; t<tags.length; t++) {
13318 var theTag = createTiddlyButton(createTiddlyElement(popup,"li"),tags[t][0],lingo.tagTooltip.format([tags[t][0]]),config.macros.tagChooser.onTagClick);
13319 theTag.setAttribute("tag",tags[t][0]);
13320 theTag.setAttribute("tiddler",this.getAttribute("tiddler"));
13323 e.cancelBubble = true;
13324 if(e.stopPropagation) e.stopPropagation();
13328 config.macros.tagChooser.onTagClick = function(e)
13330 if(!e) var e = window.event;
13331 var tag = this.getAttribute("tag");
13332 var title = this.getAttribute("tiddler");
13334 story.setTiddlerTag(title,tag,0);
13338 config.macros.tagChooser.handler = function(place,macroName,params,wikifier,paramString,tiddler)
13340 if(tiddler instanceof Tiddler) {
13341 var title = tiddler.title;
13342 var lingo = config.views.editor.tagChooser;
13343 var btn = createTiddlyButton(place,lingo.text,lingo.tooltip,this.onClick);
13344 btn.setAttribute("tiddler",title);
13348 // Create a toolbar command button
13349 config.macros.toolbar.createCommand = function(place,commandName,tiddler,theClass)
13351 if(typeof commandName != "string") {
13353 for(var t in config.commands) {
13354 if(config.commands[t] == commandName)
13359 if((tiddler instanceof Tiddler) && (typeof commandName == "string")) {
13360 var command = config.commands[commandName];
13361 if(command.isEnabled ? command.isEnabled(tiddler) : this.isCommandEnabled(command,tiddler)) {
13362 var text = command.getText ? command.getText(tiddler) : this.getCommandText(command,tiddler);
13363 var tooltip = command.getTooltip ? command.getTooltip(tiddler) : this.getCommandTooltip(command,tiddler);
13365 switch(command.type) {
13367 cmd = this.onClickPopup;
13371 cmd = this.onClickCommand;
13374 var btn = createTiddlyButton(null,text,tooltip,cmd);
13375 btn.setAttribute("commandName",commandName);
13376 btn.setAttribute("tiddler",tiddler.title);
13378 addClass(btn,theClass);
13379 place.appendChild(btn);
13384 config.macros.toolbar.isCommandEnabled = function(command,tiddler)
13386 var title = tiddler.title;
13387 var ro = tiddler.isReadOnly();
13388 var shadow = store.isShadowTiddler(title) && !store.tiddlerExists(title);
13389 return (!ro || (ro && !command.hideReadOnly)) && !(shadow && command.hideShadow);
13392 config.macros.toolbar.getCommandText = function(command,tiddler)
13394 return tiddler.isReadOnly() && command.readOnlyText ? command.readOnlyText : command.text;
13397 config.macros.toolbar.getCommandTooltip = function(command,tiddler)
13399 return tiddler.isReadOnly() && command.readOnlyTooltip ? command.readOnlyTooltip : command.tooltip;
13402 config.macros.toolbar.onClickCommand = function(e)
13404 if(!e) var e = window.event;
13405 e.cancelBubble = true;
13406 if (e.stopPropagation) e.stopPropagation();
13407 var command = config.commands[this.getAttribute("commandName")];
13408 return command.handler(e,this,this.getAttribute("tiddler"));
13411 config.macros.toolbar.onClickPopup = function(e)
13413 if(!e) var e = window.event;
13414 e.cancelBubble = true;
13415 if (e.stopPropagation) e.stopPropagation();
13416 var popup = Popup.create(this);
13417 var command = config.commands[this.getAttribute("commandName")];
13418 var title = this.getAttribute("tiddler");
13419 var tiddler = store.fetchTiddler(title);
13420 popup.setAttribute("tiddler",title);
13421 command.handlePopup(popup,title);
13426 // Invoke the first command encountered from a given place that is tagged with a specified class
13427 config.macros.toolbar.invokeCommand = function(place,theClass,event)
13429 var children = place.getElementsByTagName("a");
13430 for(var t=0; t<children.length; t++) {
13431 var c = children[t];
13432 if(hasClass(c,theClass) && c.getAttribute && c.getAttribute("commandName")) {
13433 if(c.onclick instanceof Function)
13434 c.onclick.call(c,event);
13440 config.macros.toolbar.onClickMore = function(e)
13442 var e = this.nextSibling;
13443 e.style.display = "inline";
13448 config.macros.toolbar.handler = function(place,macroName,params,wikifier,paramString,tiddler)
13450 for(var t=0; t<params.length; t++) {
13454 var btn = createTiddlyButton(place,this.moreLabel,this.morePrompt,config.macros.toolbar.onClickMore);
13455 addClass(btn,"moreCommand");
13456 var e = createTiddlyElement(place,"span",null,"moreCommand");
13457 e.style.display = "none";
13462 switch(c.substr(0,1)) {
13464 theClass = "defaultCommand";
13468 theClass = "cancelCommand";
13472 if(c in config.commands)
13473 this.createCommand(place,c,tiddler,theClass);
13479 config.macros.refreshDisplay.handler = function(place)
13481 createTiddlyButton(place,this.label,this.prompt,this.onClick);
13484 config.macros.refreshDisplay.onClick = function(e)
13490 config.macros.annotations.handler = function(place,macroName,params,wikifier,paramString,tiddler)
13492 var title = tiddler ? tiddler.title : null;
13493 var a = title ? config.annotations[title] : null;
13494 if(!tiddler || !title || !a)
13496 var text = a.format([title]);
13497 wikify(text,createTiddlyElement(place,"div",null,"annotation"),null,tiddler);
13501 //-- Menu and toolbar commands
13504 config.commands.closeTiddler.handler = function(event,src,title)
13506 story.closeTiddler(title,true);
13510 config.commands.closeOthers.handler = function(event,src,title)
13512 story.closeAllTiddlers(title);
13516 config.commands.editTiddler.handler = function(event,src,title)
13519 var tiddlerElem = document.getElementById(story.idPrefix + title);
13520 var fields = tiddlerElem.getAttribute("tiddlyFields");
13521 story.displayTiddler(null,title,DEFAULT_EDIT_TEMPLATE,false,null,fields);
13522 story.focusTiddler(title,"text");
13526 config.commands.saveTiddler.handler = function(event,src,title)
13528 var newTitle = story.saveTiddler(title,event.shiftKey);
13530 story.displayTiddler(null,newTitle);
13534 config.commands.cancelTiddler.handler = function(event,src,title)
13536 if(story.hasChanges(title) && !readOnly) {
13537 if(!confirm(this.warning.format([title])))
13540 story.setDirty(title,false);
13541 story.displayTiddler(null,title);
13545 config.commands.deleteTiddler.handler = function(event,src,title)
13547 var deleteIt = true;
13548 if (config.options.chkConfirmDelete)
13549 deleteIt = confirm(this.warning.format([title]));
13551 store.removeTiddler(title);
13552 story.closeTiddler(title,true);
13558 config.commands.permalink.handler = function(event,src,title)
13560 var t = encodeURIComponent(String.encodeTiddlyLink(title));
13561 if(window.location.hash != t)
13562 window.location.hash = t;
13566 config.commands.references.handlePopup = function(popup,title)
13568 var references = store.getReferringTiddlers(title);
13570 for(var r=0; r<references.length; r++) {
13571 if(references[r].title != title && !references[r].isTagged("excludeLists")) {
13572 createTiddlyLink(createTiddlyElement(popup,"li"),references[r].title,true);
13577 createTiddlyText(createTiddlyElement(popup,"li",null,"disabled"),this.popupNone);
13580 config.commands.jump.handlePopup = function(popup,title)
13582 story.forEachTiddler(function(title,element) {
13583 createTiddlyLink(createTiddlyElement(popup,"li"),title,true,null,false,null,true);
13587 config.commands.syncing.handlePopup = function(popup,title)
13589 var tiddler = store.fetchTiddler(title);
13592 var serverType = tiddler.getServerType();
13593 var serverHost = tiddler.fields['server.host'];
13594 var serverWorkspace = tiddler.fields['server.workspace'];
13595 if(!serverWorkspace)
13596 serverWorkspace = "";
13598 var e = createTiddlyElement(popup,"li",null,"popupMessage");
13599 e.innerHTML = config.commands.syncing.currentlySyncing.format([serverType,serverHost,serverWorkspace]);
13601 createTiddlyElement(popup,"li",null,"popupMessage",config.commands.syncing.notCurrentlySyncing);
13604 createTiddlyElement(createTiddlyElement(popup,"li",null,"listBreak"),"div");
13605 var btn = createTiddlyButton(createTiddlyElement(popup,"li"),this.captionUnSync,null,config.commands.syncing.onChooseServer);
13606 btn.setAttribute("tiddler",title);
13607 btn.setAttribute("server.type","");
13609 createTiddlyElement(createTiddlyElement(popup,"li",null,"listBreak"),"div");
13610 createTiddlyElement(popup,"li",null,"popupMessage",config.commands.syncing.chooseServer);
13611 var feeds = store.getTaggedTiddlers("systemServer","title");
13612 for(var t=0; t<feeds.length; t++) {
13614 var feedServerType = store.getTiddlerSlice(f.title,"Type");
13615 if(!feedServerType)
13616 feedServerType = "file";
13617 var feedServerHost = store.getTiddlerSlice(f.title,"URL");
13618 if(!feedServerHost)
13619 feedServerHost = "";
13620 var feedServerWorkspace = store.getTiddlerSlice(f.title,"Workspace");
13621 if(!feedServerWorkspace)
13622 feedServerWorkspace = "";
13623 var caption = f.title;
13624 if(serverType == feedServerType && serverHost == feedServerHost && serverWorkspace == feedServerWorkspace) {
13625 caption = config.commands.syncing.currServerMarker + caption;
13627 caption = config.commands.syncing.notCurrServerMarker + caption;
13629 btn = createTiddlyButton(createTiddlyElement(popup,"li"),caption,null,config.commands.syncing.onChooseServer);
13630 btn.setAttribute("tiddler",title);
13631 btn.setAttribute("server.type",feedServerType);
13632 btn.setAttribute("server.host",feedServerHost);
13633 btn.setAttribute("server.workspace",feedServerWorkspace);
13637 config.commands.syncing.onChooseServer = function(e)
13639 var tiddler = this.getAttribute("tiddler");
13640 var serverType = this.getAttribute("server.type");
13642 store.addTiddlerFields(tiddler,{
13643 'server.type': serverType,
13644 'server.host': this.getAttribute("server.host"),
13645 'server.workspace': this.getAttribute("server.workspace")
13648 store.setValue(tiddler,'server',null);
13653 config.commands.fields.handlePopup = function(popup,title)
13655 var tiddler = store.fetchTiddler(title);
13659 store.forEachField(tiddler,function(tiddler,fieldName,value) {fields[fieldName] = value;},true);
13661 for(var t in fields) {
13662 items.push({field: t,value: fields[t]});
13664 items.sort(function(a,b) {return a.field < b.field ? -1 : (a.field == b.field ? 0 : +1);});
13665 if(items.length > 0)
13666 ListView.create(popup,items,this.listViewTemplate);
13668 createTiddlyElement(popup,"div",null,null,this.emptyText);
13672 //-- Tiddler() object
13675 function Tiddler(title)
13677 this.title = title;
13679 this.modifier = null;
13680 this.modified = new Date();
13681 this.created = new Date();
13683 this.linksUpdated = false;
13689 Tiddler.prototype.getLinks = function()
13691 if(this.linksUpdated==false)
13696 // Returns the fields that are inherited in string field:"value" field2:"value2" format
13697 Tiddler.prototype.getInheritedFields = function()
13700 for(i in this.fields) {
13701 if(i=="server.host" || i=="server.workspace" || i=="wikiformat"|| i=="server.type") {
13702 f[i] = this.fields[i];
13705 return String.encodeHashMap(f);
13708 // Increment the changeCount of a tiddler
13709 Tiddler.prototype.incChangeCount = function()
13711 var c = this.fields['changecount'];
13712 c = c ? parseInt(c) : 0;
13713 this.fields['changecount'] = String(c+1);
13716 // Clear the changeCount of a tiddler
13717 Tiddler.prototype.clearChangeCount = function()
13719 if(this.fields['changecount']) {
13720 delete this.fields['changecount'];
13724 // Returns true if the tiddler has been updated since the tiddler was created or downloaded
13725 Tiddler.prototype.isTouched = function()
13727 var changeCount = this.fields['changecount'];
13728 if(changeCount === undefined)
13730 return changeCount > 0;
13733 // Format the text for storage in an RSS item
13734 Tiddler.prototype.saveToRss = function(url)
13738 s.push("<title" + ">" + this.title.htmlEncode() + "</title" + ">");
13739 s.push("<description>" + wikifyStatic(this.text,null,this).htmlEncode() + "</description>");
13740 for(var t=0; t<this.tags.length; t++)
13741 s.push("<category>" + this.tags[t] + "</category>");
13742 s.push("<link>" + url + "#" + encodeURIComponent(String.encodeTiddlyLink(this.title)) + "</link>");
13743 s.push("<pubDate>" + this.modified.toGMTString() + "</pubDate>");
13745 return s.join("\n");
13748 // Change the text and other attributes of a tiddler
13749 Tiddler.prototype.set = function(title,text,modifier,modified,tags,created,fields)
13751 this.assign(title,text,modifier,modified,tags,created,fields);
13756 // Change the text and other attributes of a tiddler without triggered a tiddler.changed() call
13757 Tiddler.prototype.assign = function(title,text,modifier,modified,tags,created,fields)
13759 if(title != undefined)
13760 this.title = title;
13761 if(text != undefined)
13763 if(modifier != undefined)
13764 this.modifier = modifier;
13765 if(modified != undefined)
13766 this.modified = modified;
13767 if(created != undefined)
13768 this.created = created;
13769 if(fields != undefined)
13770 this.fields = fields;
13771 if(tags != undefined)
13772 this.tags = (typeof tags == "string") ? tags.readBracketedList() : tags;
13773 else if(this.tags == undefined)
13778 // Get the tags for a tiddler as a string (space delimited, using [[brackets]] for tags containing spaces)
13779 Tiddler.prototype.getTags = function()
13781 return String.encodeTiddlyLinkList(this.tags);
13784 // Test if a tiddler carries a tag
13785 Tiddler.prototype.isTagged = function(tag)
13787 return this.tags.indexOf(tag) != -1;
13790 // Static method to convert "\n" to newlines, "\s" to "\"
13791 Tiddler.unescapeLineBreaks = function(text)
13793 return text ? text.unescapeLineBreaks() : "";
13796 // Convert newlines to "\n", "\" to "\s"
13797 Tiddler.prototype.escapeLineBreaks = function()
13799 return this.text.escapeLineBreaks();
13802 // Updates the secondary information (like links[] array) after a change to a tiddler
13803 Tiddler.prototype.changed = function()
13806 var t = this.autoLinkWikiWords() ? 0 : 1;
13807 var tiddlerLinkRegExp = t==0 ? config.textPrimitives.tiddlerAnyLinkRegExp : config.textPrimitives.tiddlerForcedLinkRegExp;
13808 tiddlerLinkRegExp.lastIndex = 0;
13809 var formatMatch = tiddlerLinkRegExp.exec(this.text);
13810 while(formatMatch) {
13811 var lastIndex = tiddlerLinkRegExp.lastIndex;
13812 if(t==0 && formatMatch[1] && formatMatch[1] != this.title) {
13814 if(formatMatch.index > 0) {
13815 var preRegExp = new RegExp(config.textPrimitives.unWikiLink+"|"+config.textPrimitives.anyLetter,"mg");
13816 preRegExp.lastIndex = formatMatch.index-1;
13817 var preMatch = preRegExp.exec(this.text);
13818 if(preMatch.index != formatMatch.index-1)
13819 this.links.pushUnique(formatMatch[1]);
13821 this.links.pushUnique(formatMatch[1]);
13824 else if(formatMatch[2-t] && !config.formatterHelpers.isExternalLink(formatMatch[3-t])) // titledBrackettedLink
13825 this.links.pushUnique(formatMatch[3-t]);
13826 else if(formatMatch[4-t] && formatMatch[4-t] != this.title) // brackettedLink
13827 this.links.pushUnique(formatMatch[4-t]);
13828 tiddlerLinkRegExp.lastIndex = lastIndex;
13829 formatMatch = tiddlerLinkRegExp.exec(this.text);
13831 this.linksUpdated = true;
13834 Tiddler.prototype.getSubtitle = function()
13836 var theModifier = this.modifier;
13838 theModifier = config.messages.subtitleUnknown;
13839 var theModified = this.modified;
13841 theModified = theModified.toLocaleString();
13843 theModified = config.messages.subtitleUnknown;
13844 return config.messages.tiddlerLinkTooltip.format([this.title,theModifier,theModified]);
13847 Tiddler.prototype.isReadOnly = function()
13852 Tiddler.prototype.autoLinkWikiWords = function()
13854 return !(this.isTagged("systemConfig") || this.isTagged("excludeMissing"));
13857 Tiddler.prototype.generateFingerprint = function()
13859 return "0x" + Crypto.hexSha1Str(this.text);
13862 Tiddler.prototype.getServerType = function()
13864 var serverType = null;
13865 if(this.fields && this.fields['server.type'])
13866 serverType = this.fields['server.type'];
13868 serverType = this.fields['wikiformat'];
13869 if(serverType && !config.adaptors[serverType])
13874 Tiddler.prototype.getAdaptor = function()
13876 var serverType = this.getServerType();
13878 return new config.adaptors[serverType];
13884 //-- TiddlyWiki() object contains Tiddler()s
13887 function TiddlyWiki()
13889 var tiddlers = {}; // Hashmap by name of tiddlers
13890 this.tiddlersUpdated = false;
13891 this.namedNotifications = []; // Array of {name:,notify:} of notification functions
13892 this.notificationLevel = 0;
13893 this.slices = {}; // map tiddlerName->(map sliceName->sliceValue). Lazy.
13894 this.clear = function() {
13896 this.setDirty(false);
13898 this.fetchTiddler = function(title) {
13899 return tiddlers[title];
13901 this.deleteTiddler = function(title) {
13902 delete this.slices[title];
13903 delete tiddlers[title];
13905 this.addTiddler = function(tiddler) {
13906 delete this.slices[tiddler.title];
13907 tiddlers[tiddler.title] = tiddler;
13909 this.forEachTiddler = function(callback) {
13910 for(var t in tiddlers) {
13911 var tiddler = tiddlers[t];
13912 if(tiddler instanceof Tiddler)
13913 callback.call(this,t,tiddler);
13918 TiddlyWiki.prototype.setDirty = function(dirty)
13920 this.dirty = dirty;
13923 TiddlyWiki.prototype.isDirty = function()
13928 TiddlyWiki.prototype.suspendNotifications = function()
13930 this.notificationLevel--;
13933 TiddlyWiki.prototype.resumeNotifications = function()
13935 this.notificationLevel++;
13938 // Invoke the notification handlers for a particular tiddler
13939 TiddlyWiki.prototype.notify = function(title,doBlanket)
13941 if(!this.notificationLevel) {
13942 for(var t=0; t<this.namedNotifications.length; t++) {
13943 var n = this.namedNotifications[t];
13944 if((n.name == null && doBlanket) || (n.name == title))
13950 // Invoke the notification handlers for all tiddlers
13951 TiddlyWiki.prototype.notifyAll = function()
13953 if(!this.notificationLevel) {
13954 for(var t=0; t<this.namedNotifications.length; t++) {
13955 var n = this.namedNotifications[t];
13962 // Add a notification handler to a tiddler
13963 TiddlyWiki.prototype.addNotification = function(title,fn)
13965 for(var i=0; i<this.namedNotifications.length; i++) {
13966 if((this.namedNotifications[i].name == title) && (this.namedNotifications[i].notify == fn))
13969 this.namedNotifications.push({name: title, notify: fn});
13973 TiddlyWiki.prototype.removeTiddler = function(title)
13975 var tiddler = this.fetchTiddler(title);
13977 this.deleteTiddler(title);
13978 this.notify(title,true);
13979 this.setDirty(true);
13983 TiddlyWiki.prototype.tiddlerExists = function(title)
13985 var t = this.fetchTiddler(title);
13986 return t != undefined;
13989 TiddlyWiki.prototype.isShadowTiddler = function(title)
13991 return typeof config.shadowTiddlers[title] == "string";
13994 TiddlyWiki.prototype.getTiddler = function(title)
13996 var t = this.fetchTiddler(title);
14003 TiddlyWiki.prototype.getTiddlerText = function(title,defaultText)
14005 var tiddler = this.fetchTiddler(title);
14007 return tiddler.text;
14009 return defaultText;
14010 var pos = title.indexOf(config.textPrimitives.sliceSeparator);
14012 var slice = this.getTiddlerSlice(title.substr(0,pos),title.substr(pos + config.textPrimitives.sliceSeparator.length));
14016 if(this.isShadowTiddler(title))
14017 return config.shadowTiddlers[title];
14018 if(defaultText != undefined)
14019 return defaultText;
14023 TiddlyWiki.prototype.slicesRE = /(?:[\'\/]*~?([\.\w]+)[\'\/]*\:[\'\/]*\s*(.*?)\s*$)|(?:\|[\'\/]*~?([\.\w]+)\:?[\'\/]*\|\s*(.*?)\s*\|)/gm;
14026 TiddlyWiki.prototype.calcAllSlices = function(title)
14029 var text = this.getTiddlerText(title,"");
14030 this.slicesRE.lastIndex = 0;
14032 var m = this.slicesRE.exec(text);
14035 slices[m[1]] = m[2];
14037 slices[m[3]] = m[4];
14043 // Returns the slice of text of the given name
14044 TiddlyWiki.prototype.getTiddlerSlice = function(title,sliceName)
14046 var slices = this.slices[title];
14048 slices = this.calcAllSlices(title);
14049 this.slices[title] = slices;
14051 return slices[sliceName];
14054 // Build an hashmap of the specified named slices of a tiddler
14055 TiddlyWiki.prototype.getTiddlerSlices = function(title,sliceNames)
14058 for(var t=0; t<sliceNames.length; t++) {
14059 var slice = this.getTiddlerSlice(title,sliceNames[t]);
14061 r[sliceNames[t]] = slice;
14066 TiddlyWiki.prototype.getRecursiveTiddlerText = function(title,defaultText,depth)
14068 var bracketRegExp = new RegExp("(?:\\[\\[([^\\]]+)\\]\\])","mg");
14069 var text = this.getTiddlerText(title,null);
14071 return defaultText;
14075 var match = bracketRegExp.exec(text);
14077 textOut.push(text.substr(lastPos,match.index-lastPos));
14080 textOut.push(match[1]);
14082 textOut.push(this.getRecursiveTiddlerText(match[1],"[[" + match[1] + "]]",depth-1));
14084 lastPos = match.index + match[0].length;
14086 textOut.push(text.substr(lastPos));
14089 return textOut.join("");
14092 TiddlyWiki.prototype.setTiddlerTag = function(title,status,tag)
14094 var tiddler = this.fetchTiddler(title);
14096 var t = tiddler.tags.indexOf(tag);
14098 tiddler.tags.splice(t,1);
14100 tiddler.tags.push(tag);
14102 this.incChangeCount(title);
14103 this.notify(title,true);
14104 this.setDirty(true);
14108 TiddlyWiki.prototype.addTiddlerFields = function(title,fields)
14110 var tiddler = this.fetchTiddler(title);
14113 merge(tiddler.fields,fields);
14115 this.incChangeCount(title);
14116 this.notify(title,true);
14117 this.setDirty(true);
14120 TiddlyWiki.prototype.saveTiddler = function(title,newTitle,newBody,modifier,modified,tags,fields,clearChangeCount,created)
14122 var tiddler = this.fetchTiddler(title);
14124 created = created ? created : tiddler.created; // Preserve created date
14125 this.deleteTiddler(title);
14127 created = created ? created : modified;
14128 tiddler = new Tiddler();
14130 tiddler.set(newTitle,newBody,modifier,modified,tags,created,fields);
14131 this.addTiddler(tiddler);
14132 if(clearChangeCount)
14133 tiddler.clearChangeCount();
14135 tiddler.incChangeCount();
14136 if(title != newTitle)
14137 this.notify(title,true);
14138 this.notify(newTitle,true);
14139 this.setDirty(true);
14143 // Reset the sync status of a freshly synced tiddler
14144 TiddlyWiki.prototype.resetTiddler = function(title)
14146 var tiddler = this.fetchTiddler(title);
14148 tiddler.clearChangeCount();
14149 this.notify(title,true);
14150 this.setDirty(true);
14154 TiddlyWiki.prototype.incChangeCount = function(title)
14156 var tiddler = this.fetchTiddler(title);
14158 tiddler.incChangeCount();
14161 TiddlyWiki.prototype.createTiddler = function(title)
14163 var tiddler = this.fetchTiddler(title);
14165 tiddler = new Tiddler();
14166 tiddler.title = title;
14167 this.addTiddler(tiddler);
14168 this.setDirty(true);
14173 // Load contents of a TiddlyWiki from an HTML DIV
14174 TiddlyWiki.prototype.loadFromDiv = function(src,idPrefix,noUpdate)
14176 this.idPrefix = idPrefix;
14177 var storeElem = (typeof src == "string") ? document.getElementById(src) : src;
14180 var tiddlers = this.getLoader().loadTiddlers(this,storeElem.childNodes);
14181 this.setDirty(false);
14183 for(var i = 0;i<tiddlers.length; i++)
14184 tiddlers[i].changed();
14188 // Load contents of a TiddlyWiki from a string
14189 // Returns null if there's an error
14190 TiddlyWiki.prototype.importTiddlyWiki = function(text)
14192 var posDiv = locateStoreArea(text);
14195 var content = "<" + "html><" + "body>" + text.substring(posDiv[0],posDiv[1] + endSaveArea.length) + "<" + "/body><" + "/html>";
14196 // Create the iframe
14197 var iframe = document.createElement("iframe");
14198 iframe.style.display = "none";
14199 document.body.appendChild(iframe);
14200 var doc = iframe.document;
14201 if(iframe.contentDocument)
14202 doc = iframe.contentDocument; // For NS6
14203 else if(iframe.contentWindow)
14204 doc = iframe.contentWindow.document; // For IE5.5 and IE6
14205 // Put the content in the iframe
14207 doc.writeln(content);
14209 // Load the content into a TiddlyWiki() object
14210 var storeArea = doc.getElementById("storeArea");
14211 this.loadFromDiv(storeArea,"store");
14212 // Get rid of the iframe
14213 iframe.parentNode.removeChild(iframe);
14217 TiddlyWiki.prototype.updateTiddlers = function()
14219 this.tiddlersUpdated = true;
14220 this.forEachTiddler(function(title,tiddler) {
14225 // Return all tiddlers formatted as an HTML string
14226 TiddlyWiki.prototype.allTiddlersAsHtml = function()
14228 return store.getSaver().externalize(store);
14231 // Return an array of tiddlers matching a search regular expression
14232 TiddlyWiki.prototype.search = function(searchRegExp,sortField,excludeTag)
14234 var candidates = this.reverseLookup("tags",excludeTag,false);
14236 for(var t=0; t<candidates.length; t++) {
14237 if((candidates[t].title.search(searchRegExp) != -1) || (candidates[t].text.search(searchRegExp) != -1))
14238 results.push(candidates[t]);
14241 sortField = "title";
14242 results.sort(function(a,b) {return a[sortField] < b[sortField] ? -1 : (a[sortField] == b[sortField] ? 0 : +1);});
14246 // Return an array of all the tags in use. Each member of the array is another array where [0] is the name of the tag and [1] is the number of occurances
14247 TiddlyWiki.prototype.getTags = function(excludeTag)
14250 this.forEachTiddler(function(title,tiddler) {
14251 for(var g=0; g<tiddler.tags.length; g++) {
14252 var tag = tiddler.tags[g];
14254 var t = store.fetchTiddler(tag);
14255 if(t && t.isTagged(excludeTag))
14259 for(var c=0; c<results.length; c++) {
14260 if(results[c][0] == tag) {
14266 results.push([tag,1]);
14269 results.sort(function(a,b) {return a[0].toLowerCase() < b[0].toLowerCase() ? -1 : (a[0].toLowerCase() == b[0].toLowerCase() ? 0 : +1);});
14273 // Return an array of the tiddlers that are tagged with a given tag
14274 TiddlyWiki.prototype.getTaggedTiddlers = function(tag,sortField)
14276 return this.reverseLookup("tags",tag,true,sortField);
14279 // Return an array of the tiddlers that link to a given tiddler
14280 TiddlyWiki.prototype.getReferringTiddlers = function(title,unusedParameter,sortField)
14282 if(!this.tiddlersUpdated)
14283 this.updateTiddlers();
14284 return this.reverseLookup("links",title,true,sortField);
14287 // Return an array of the tiddlers that do or do not have a specified entry in the specified storage array (ie, "links" or "tags")
14288 // lookupMatch == true to match tiddlers, false to exclude tiddlers
14289 TiddlyWiki.prototype.reverseLookup = function(lookupField,lookupValue,lookupMatch,sortField)
14292 this.forEachTiddler(function(title,tiddler) {
14293 var f = !lookupMatch;
14294 for(var lookup=0; lookup<tiddler[lookupField].length; lookup++) {
14295 if(tiddler[lookupField][lookup] == lookupValue)
14299 results.push(tiddler);
14302 sortField = "title";
14303 results.sort(function(a,b) {return a[sortField] < b[sortField] ? -1 : (a[sortField] == b[sortField] ? 0 : +1);});
14307 // Return the tiddlers as a sorted array
14308 TiddlyWiki.prototype.getTiddlers = function(field,excludeTag)
14311 this.forEachTiddler(function(title,tiddler) {
14312 if(excludeTag == undefined || !tiddler.isTagged(excludeTag))
14313 results.push(tiddler);
14316 results.sort(function(a,b) {return a[field] < b[field] ? -1 : (a[field] == b[field] ? 0 : +1);});
14320 // Return array of names of tiddlers that are referred to but not defined
14321 TiddlyWiki.prototype.getMissingLinks = function(sortField)
14323 if(!this.tiddlersUpdated)
14324 this.updateTiddlers();
14326 this.forEachTiddler(function (title,tiddler) {
14327 if(tiddler.isTagged("excludeMissing") || tiddler.isTagged("systemConfig"))
14329 for(var n=0; n<tiddler.links.length;n++) {
14330 var link = tiddler.links[n];
14331 if(this.fetchTiddler(link) == null && !this.isShadowTiddler(link))
14332 results.pushUnique(link);
14339 // Return an array of names of tiddlers that are defined but not referred to
14340 TiddlyWiki.prototype.getOrphans = function()
14343 this.forEachTiddler(function (title,tiddler) {
14344 if(this.getReferringTiddlers(title).length == 0 && !tiddler.isTagged("excludeLists"))
14345 results.push(title);
14351 // Return an array of names of all the shadow tiddlers
14352 TiddlyWiki.prototype.getShadowed = function()
14355 for(var t in config.shadowTiddlers) {
14356 if(typeof config.shadowTiddlers[t] == "string")
14363 // Return an array of tiddlers that have been touched since they were downloaded or created
14364 TiddlyWiki.prototype.getTouched = function()
14367 this.forEachTiddler(function(title,tiddler) {
14368 if(tiddler.isTouched())
14369 results.push(tiddler);
14375 // Resolves a Tiddler reference or tiddler title into a Tiddler object, or null if it doesn't exist
14376 TiddlyWiki.prototype.resolveTiddler = function(tiddler)
14378 var t = (typeof tiddler == 'string') ? this.getTiddler(tiddler) : tiddler;
14379 return t instanceof Tiddler ? t : null;
14382 TiddlyWiki.prototype.getLoader = function()
14385 this.loader = new TW21Loader();
14386 return this.loader;
14389 TiddlyWiki.prototype.getSaver = function()
14392 this.saver = new TW21Saver();
14396 // Returns true if path is a valid field name (path),
14397 // i.e. a sequence of identifiers, separated by '.'
14398 TiddlyWiki.isValidFieldName = function(name)
14400 var match = /[a-zA-Z_]\w*(\.[a-zA-Z_]\w*)*/.exec(name);
14401 return match && (match[0] == name);
14404 // Throws an exception when name is not a valid field name.
14405 TiddlyWiki.checkFieldName = function(name)
14407 if(!TiddlyWiki.isValidFieldName(name))
14408 throw config.messages.invalidFieldName.format([name]);
14411 function StringFieldAccess(n,readOnly)
14413 this.set = readOnly ?
14414 function(t,v) {if(v != t[n]) throw config.messages.fieldCannotBeChanged.format([n]);} :
14415 function(t,v) {if(v != t[n]) {t[n] = v; return true;}};
14416 this.get = function(t) {return t[n];};
14419 function DateFieldAccess(n)
14421 this.set = function(t,v) {
14422 var d = v instanceof Date ? v : Date.convertFromYYYYMMDDHHMM(v);
14424 t[n] = d; return true;
14427 this.get = function(t) {return t[n].convertToYYYYMMDDHHMM();};
14430 function LinksFieldAccess(n)
14432 this.set = function(t,v) {
14433 var s = (typeof v == "string") ? v.readBracketedList() : v;
14434 if(s.toString() != t[n].toString()) {
14435 t[n] = s; return true;
14438 this.get = function(t) {return String.encodeTiddlyLinkList(t[n]);};
14441 TiddlyWiki.standardFieldAccess = {
14442 // The set functions return true when setting the data has changed the value.
14443 "title": new StringFieldAccess("title",true),
14444 // Handle the "tiddler" field name as the title
14445 "tiddler": new StringFieldAccess("title",true),
14446 "text": new StringFieldAccess("text"),
14447 "modifier": new StringFieldAccess("modifier"),
14448 "modified": new DateFieldAccess("modified"),
14449 "created": new DateFieldAccess("created"),
14450 "tags": new LinksFieldAccess("tags")
14453 TiddlyWiki.isStandardField = function(name)
14455 return TiddlyWiki.standardFieldAccess[name] != undefined;
14458 // Sets the value of the given field of the tiddler to the value.
14459 // Setting an ExtendedField's value to null or undefined removes the field.
14460 // Setting a namespace to undefined removes all fields of that namespace.
14461 // The fieldName is case-insensitive.
14462 // All values will be converted to a string value.
14463 TiddlyWiki.prototype.setValue = function(tiddler,fieldName,value)
14465 TiddlyWiki.checkFieldName(fieldName);
14466 var t = this.resolveTiddler(tiddler);
14469 fieldName = fieldName.toLowerCase();
14470 var isRemove = (value === undefined) || (value === null);
14471 var accessor = TiddlyWiki.standardFieldAccess[fieldName];
14474 // don't remove StandardFields
14476 var h = TiddlyWiki.standardFieldAccess[fieldName];
14477 if(!h.set(t,value))
14480 var oldValue = t.fields[fieldName];
14482 if(oldValue !== undefined) {
14483 // deletes a single field
14484 delete t.fields[fieldName];
14486 // no concrete value is defined for the fieldName
14487 // so we guess this is a namespace path.
14488 // delete all fields in a namespace
14489 var re = new RegExp('^'+fieldName+'\\.');
14491 for(var n in t.fields) {
14493 delete t.fields[n];
14501 // the "normal" set case. value is defined (not null/undefined)
14502 // For convenience provide a nicer conversion Date->String
14503 value = value instanceof Date ? value.convertToYYYYMMDDHHMMSSMMM() : String(value);
14504 if(oldValue == value)
14506 t.fields[fieldName] = value;
14509 // When we are here the tiddler/store really was changed.
14510 this.notify(t.title,true);
14511 if(!fieldName.match(/^temp\./))
14512 this.setDirty(true);
14515 // Returns the value of the given field of the tiddler.
14516 // The fieldName is case-insensitive.
14517 // Will only return String values (or undefined).
14518 TiddlyWiki.prototype.getValue = function(tiddler,fieldName)
14520 var t = this.resolveTiddler(tiddler);
14523 fieldName = fieldName.toLowerCase();
14524 var accessor = TiddlyWiki.standardFieldAccess[fieldName];
14526 return accessor.get(t);
14528 return t.fields[fieldName];
14531 // Calls the callback function for every field in the tiddler.
14532 // When callback function returns a non-false value the iteration stops
14533 // and that value is returned.
14534 // The order of the fields is not defined.
14535 // @param callback a function(tiddler,fieldName,value).
14536 TiddlyWiki.prototype.forEachField = function(tiddler,callback,onlyExtendedFields)
14538 var t = this.resolveTiddler(tiddler);
14541 for(var n in t.fields) {
14542 var result = callback(t,n,t.fields[n]);
14546 if(onlyExtendedFields)
14548 for(var n in TiddlyWiki.standardFieldAccess) {
14550 // even though the "title" field can also be referenced through the name "tiddler"
14551 // we only visit this field once.
14553 var result = callback(t,n,TiddlyWiki.standardFieldAccess[n].get(t));
14561 //-- Story functions
14564 function Story(container,idPrefix)
14566 this.container = container;
14567 this.idPrefix = idPrefix;
14568 this.highlightRegExp = null;
14571 Story.prototype.forEachTiddler = function(fn)
14573 var place = document.getElementById(this.container);
14576 var e = place.firstChild;
14578 var n = e.nextSibling;
14579 var title = e.getAttribute("tiddler");
14580 fn.call(this,title,e);
14585 Story.prototype.displayTiddlers = function(srcElement,titles,template,animate,unused,customFields,toggle)
14587 for(var t = titles.length-1;t>=0;t--)
14588 this.displayTiddler(srcElement,titles[t],template,animate,unused,customFields);
14591 Story.prototype.displayTiddler = function(srcElement,title,template,animate,unused,customFields,toggle)
14593 var place = document.getElementById(this.container);
14594 var tiddlerElem = document.getElementById(this.idPrefix + title);
14597 this.closeTiddler(title,true);
14599 this.refreshTiddler(title,template,false,customFields);
14601 var before = this.positionTiddler(srcElement);
14602 tiddlerElem = this.createTiddler(place,before,title,template,customFields);
14604 if(srcElement && typeof srcElement !== "string") {
14605 if(config.options.chkAnimate && (animate == undefined || animate == true) && anim && typeof Zoomer == "function" && typeof Scroller == "function")
14606 anim.startAnimating(new Zoomer(title,srcElement,tiddlerElem),new Scroller(tiddlerElem));
14608 window.scrollTo(0,ensureVisible(tiddlerElem));
14612 Story.prototype.positionTiddler = function(srcElement)
14614 var place = document.getElementById(this.container);
14616 if(typeof srcElement == "string") {
14617 switch(srcElement) {
14619 before = place.firstChild;
14626 var after = this.findContainingTiddler(srcElement);
14627 if(after == null) {
14628 before = place.firstChild;
14629 } else if(after.nextSibling) {
14630 before = after.nextSibling;
14631 if(before.nodeType != 1)
14638 Story.prototype.createTiddler = function(place,before,title,template,customFields)
14640 var tiddlerElem = createTiddlyElement(null,"div",this.idPrefix + title,"tiddler");
14641 tiddlerElem.setAttribute("refresh","tiddler");
14643 tiddlerElem.setAttribute("tiddlyFields",customFields);
14644 place.insertBefore(tiddlerElem,before);
14645 var defaultText = null;
14646 if(!store.tiddlerExists(title) && !store.isShadowTiddler(title))
14647 defaultText = this.loadMissingTiddler(title,customFields,tiddlerElem);
14648 this.refreshTiddler(title,template,false,customFields,defaultText);
14649 return tiddlerElem;
14652 Story.prototype.loadMissingTiddler = function(title,fields,tiddlerElem)
14654 var tiddler = new Tiddler(title);
14655 tiddler.fields = typeof fields == "string" ? fields.decodeHashMap() : (fields ? fields : {});
14656 var serverType = tiddler.getServerType();
14657 var host = tiddler.fields['server.host'];
14658 var workspace = tiddler.fields['server.workspace'];
14659 if(!serverType | !host)
14661 var sm = new SyncMachine(serverType,{
14662 start: function() {
14663 return this.openHost(host,"openWorkspace");
14665 openWorkspace: function() {
14666 return this.openWorkspace(workspace,"getTiddler");
14668 getTiddler: function() {
14669 return this.getTiddler(title,"gotTiddler");
14671 gotTiddler: function(tiddler) {
14672 if(tiddler && tiddler.text) {
14673 var downloaded = new Date();
14674 if(!tiddler.created)
14675 tiddler.created = downloaded;
14676 if(!tiddler.modified)
14677 tiddler.modified = tiddler.created;
14678 store.saveTiddler(tiddler.title,tiddler.title,tiddler.text,tiddler.modifier,tiddler.modified,tiddler.tags,tiddler.fields,true,tiddler.created);
14684 error: function(message) {
14685 displayMessage("Error loading missing tiddler from %0: %1".format([host,message]));
14689 return config.messages.loadingMissingTiddler.format([title,serverType,host,workspace]);
14692 Story.prototype.chooseTemplateForTiddler = function(title,template)
14695 template = DEFAULT_VIEW_TEMPLATE;
14696 if(template == DEFAULT_VIEW_TEMPLATE || template == DEFAULT_EDIT_TEMPLATE)
14697 template = config.tiddlerTemplates[template];
14701 Story.prototype.getTemplateForTiddler = function(title,template,tiddler)
14703 return store.getRecursiveTiddlerText(template,null,10);
14706 Story.prototype.refreshTiddler = function(title,template,force,customFields,defaultText)
14708 var tiddlerElem = document.getElementById(this.idPrefix + title);
14710 if(tiddlerElem.getAttribute("dirty") == "true" && !force)
14711 return tiddlerElem;
14712 template = this.chooseTemplateForTiddler(title,template);
14713 var currTemplate = tiddlerElem.getAttribute("template");
14714 if((template != currTemplate) || force) {
14715 var tiddler = store.getTiddler(title);
14717 tiddler = new Tiddler();
14718 if(store.isShadowTiddler(title)) {
14719 tiddler.set(title,store.getTiddlerText(title),config.views.wikified.shadowModifier,version.date,[],version.date);
14721 var text = template=="EditTemplate" ?
14722 config.views.editor.defaultText.format([title]) :
14723 config.views.wikified.defaultText.format([title]);
14724 text = defaultText ? defaultText : text;
14725 var fields = customFields ? customFields.decodeHashMap() : null;
14726 tiddler.set(title,text,config.views.wikified.defaultModifier,version.date,[],version.date,fields);
14729 tiddlerElem.setAttribute("tags",tiddler.tags.join(" "));
14730 tiddlerElem.setAttribute("tiddler",title);
14731 tiddlerElem.setAttribute("template",template);
14733 tiddlerElem.onmouseover = this.onTiddlerMouseOver;
14734 tiddlerElem.onmouseout = this.onTiddlerMouseOut;
14735 tiddlerElem.ondblclick = this.onTiddlerDblClick;
14736 tiddlerElem[window.event?"onkeydown":"onkeypress"] = this.onTiddlerKeyPress;
14737 var html = this.getTemplateForTiddler(title,template,tiddler);
14738 tiddlerElem.innerHTML = html;
14739 applyHtmlMacros(tiddlerElem,tiddler);
14740 if(store.getTaggedTiddlers(title).length > 0)
14741 addClass(tiddlerElem,"isTag");
14743 removeClass(tiddlerElem,"isTag");
14744 if(!store.tiddlerExists(title)) {
14745 if(store.isShadowTiddler(title))
14746 addClass(tiddlerElem,"shadow");
14748 addClass(tiddlerElem,"missing");
14750 removeClass(tiddlerElem,"shadow");
14751 removeClass(tiddlerElem,"missing");
14754 this.addCustomFields(tiddlerElem,customFields);
14758 return tiddlerElem;
14761 Story.prototype.addCustomFields = function(place,customFields)
14763 var fields = customFields.decodeHashMap();
14764 var w = document.createElement("div");
14765 w.style.display = "none";
14766 place.appendChild(w);
14767 for(var t in fields) {
14768 var e = document.createElement("input");
14769 e.setAttribute("type","text");
14770 e.setAttribute("value",fields[t]);
14772 e.setAttribute("edit",t);
14776 Story.prototype.refreshAllTiddlers = function()
14778 var place = document.getElementById(this.container);
14779 var e = place.firstChild;
14782 this.refreshTiddler(e.getAttribute("tiddler"),e.getAttribute("template"),true);
14783 while((e = e.nextSibling) != null)
14784 this.refreshTiddler(e.getAttribute("tiddler"),e.getAttribute("template"),true);
14787 Story.prototype.onTiddlerMouseOver = function(e)
14789 if(window.addClass instanceof Function)
14790 addClass(this,"selected");
14793 Story.prototype.onTiddlerMouseOut = function(e)
14795 if(window.removeClass instanceof Function)
14796 removeClass(this,"selected");
14799 Story.prototype.onTiddlerDblClick = function(e)
14801 if(!e) var e = window.event;
14802 var theTarget = resolveTarget(e);
14803 if(theTarget && theTarget.nodeName.toLowerCase() != "input" && theTarget.nodeName.toLowerCase() != "textarea") {
14804 if(document.selection && document.selection.empty)
14805 document.selection.empty();
14806 config.macros.toolbar.invokeCommand(this,"defaultCommand",e);
14807 e.cancelBubble = true;
14808 if(e.stopPropagation) e.stopPropagation();
14815 Story.prototype.onTiddlerKeyPress = function(e)
14817 if(!e) var e = window.event;
14819 var consume = false;
14820 var title = this.getAttribute("tiddler");
14821 var target = resolveTarget(e);
14822 switch(e.keyCode) {
14824 if(config.options.chkInsertTabs && target.tagName.toLowerCase() == "textarea") {
14825 replaceSelection(target,String.fromCharCode(9));
14828 if(config.isOpera) {
14829 target.onblur = function() {
14831 this.onblur = null;
14835 case 13: // Ctrl-Enter
14836 case 10: // Ctrl-Enter on IE PC
14837 case 77: // Ctrl-Enter is "M" on some platforms
14840 config.macros.toolbar.invokeCommand(this,"defaultCommand",e);
14846 config.macros.toolbar.invokeCommand(this,"cancelCommand",e);
14850 e.cancelBubble = consume;
14852 if(e.stopPropagation) e.stopPropagation(); // Stop Propagation
14853 e.returnValue = true; // Cancel The Event in IE
14854 if(e.preventDefault ) e.preventDefault(); // Cancel The Event in Moz
14859 Story.prototype.getTiddlerField = function(title,field)
14861 var tiddlerElem = document.getElementById(this.idPrefix + title);
14863 if(tiddlerElem != null) {
14864 var children = tiddlerElem.getElementsByTagName("*");
14865 for(var t=0; t<children.length; t++) {
14866 var c = children[t];
14867 if(c.tagName.toLowerCase() == "input" || c.tagName.toLowerCase() == "textarea") {
14870 if(c.getAttribute("edit") == field)
14878 Story.prototype.focusTiddler = function(title,field)
14880 var e = this.getTiddlerField(title,field);
14887 Story.prototype.blurTiddler = function(title)
14889 var tiddlerElem = document.getElementById(this.idPrefix + title);
14890 if(tiddlerElem != null && tiddlerElem.focus && tiddlerElem.blur) {
14891 tiddlerElem.focus();
14892 tiddlerElem.blur();
14896 Story.prototype.setTiddlerField = function(title,tag,mode,field)
14898 var c = story.getTiddlerField(title,field);
14900 var tags = c.value.readBracketedList();
14901 tags.setItem(tag,mode);
14902 c.value = String.encodeTiddlyLinkList(tags);
14905 Story.prototype.setTiddlerTag = function(title,tag,mode)
14907 Story.prototype.setTiddlerField(title,tag,mode,"tags");
14910 Story.prototype.closeTiddler = function(title,animate,unused)
14912 var tiddlerElem = document.getElementById(this.idPrefix + title);
14913 if(tiddlerElem != null) {
14915 this.scrubTiddler(tiddlerElem);
14916 if(config.options.chkAnimate && animate && anim && typeof Slider == "function")
14917 anim.startAnimating(new Slider(tiddlerElem,false,null,"all"));
14919 removeNode(tiddlerElem);
14925 Story.prototype.scrubTiddler = function(tiddlerElem)
14927 tiddlerElem.id = null;
14930 Story.prototype.setDirty = function(title,dirty)
14932 var tiddlerElem = document.getElementById(this.idPrefix + title);
14933 if(tiddlerElem != null)
14934 tiddlerElem.setAttribute("dirty",dirty ? "true" : "false");
14937 Story.prototype.isDirty = function(title)
14939 var tiddlerElem = document.getElementById(this.idPrefix + title);
14940 if(tiddlerElem != null)
14941 return tiddlerElem.getAttribute("dirty") == "true";
14945 Story.prototype.areAnyDirty = function()
14948 this.forEachTiddler(function(title,element) {
14949 if(this.isDirty(title))
14955 Story.prototype.closeAllTiddlers = function(exclude)
14958 this.forEachTiddler(function(title,element) {
14959 if((title != exclude) && element.getAttribute("dirty") != "true")
14960 this.closeTiddler(title);
14962 window.scrollTo(0,ensureVisible(this.container));
14965 Story.prototype.isEmpty = function()
14967 var place = document.getElementById(this.container);
14968 return place && place.firstChild == null;
14971 Story.prototype.search = function(text,useCaseSensitive,useRegExp)
14973 this.closeAllTiddlers();
14974 highlightHack = new RegExp(useRegExp ? text : text.escapeRegExp(),useCaseSensitive ? "mg" : "img");
14975 var matches = store.search(highlightHack,"title","excludeSearch");
14977 for(var t=0;t<matches.length;t++)
14978 titles.push(matches[t].title);
14979 this.displayTiddlers(null,titles);
14980 highlightHack = null;
14981 var q = useRegExp ? "/" : "'";
14982 if(matches.length > 0)
14983 displayMessage(config.macros.search.successMsg.format([titles.length.toString(),q + text + q]));
14985 displayMessage(config.macros.search.failureMsg.format([q + text + q]));
14988 Story.prototype.findContainingTiddler = function(e)
14990 while(e && !hasClass(e,"tiddler"))
14995 Story.prototype.gatherSaveFields = function(e,fields)
14997 if(e && e.getAttribute) {
14998 var f = e.getAttribute("edit");
15000 fields[f] = e.value.replace(/\r/mg,"");
15001 if(e.hasChildNodes()) {
15002 var c = e.childNodes;
15003 for(var t=0; t<c.length; t++)
15004 this.gatherSaveFields(c[t],fields);
15009 Story.prototype.hasChanges = function(title)
15011 var e = document.getElementById(this.idPrefix + title);
15014 this.gatherSaveFields(e,fields);
15015 var tiddler = store.fetchTiddler(title);
15018 for(var n in fields) {
15019 if(store.getValue(title,n) != fields[n])
15026 Story.prototype.saveTiddler = function(title,minorUpdate)
15028 var tiddlerElem = document.getElementById(this.idPrefix + title);
15029 if(tiddlerElem != null) {
15031 this.gatherSaveFields(tiddlerElem,fields);
15032 var newTitle = fields.title ? fields.title : title;
15033 if(store.tiddlerExists(newTitle) && newTitle != title) {
15034 if(!confirm(config.messages.overwriteWarning.format([newTitle.toString()])))
15037 if(newTitle != title)
15038 this.closeTiddler(newTitle,false);
15039 tiddlerElem.id = this.idPrefix + newTitle;
15040 tiddlerElem.setAttribute("tiddler",newTitle);
15041 tiddlerElem.setAttribute("template",DEFAULT_VIEW_TEMPLATE);
15042 tiddlerElem.setAttribute("dirty","false");
15043 if(config.options.chkForceMinorUpdate)
15044 minorUpdate = !minorUpdate;
15045 if(!store.tiddlerExists(newTitle))
15046 minorUpdate = false;
15047 var newDate = new Date();
15048 var extendedFields = store.tiddlerExists(newTitle) ? store.fetchTiddler(newTitle).fields : {};
15049 for(var n in fields) {
15050 if(!TiddlyWiki.isStandardField(n))
15051 extendedFields[n] = fields[n];
15053 var tiddler = store.saveTiddler(title,newTitle,fields.text,minorUpdate ? undefined : config.options.txtUserName,minorUpdate ? undefined : newDate,fields.tags,extendedFields);
15054 autoSaveChanges(null,[tiddler]);
15060 Story.prototype.permaView = function()
15063 this.forEachTiddler(function(title,element) {
15064 links.push(String.encodeTiddlyLink(title));
15066 var t = encodeURIComponent(links.join(" "));
15069 if(window.location.hash != t)
15070 window.location.hash = t;
15092 var cmb = config.messages.backstage;
15093 this.area = document.getElementById("backstageArea");
15094 this.toolbar = document.getElementById("backstageToolbar");
15095 this.button = document.getElementById("backstageButton");
15096 this.button.style.display = "block";
15097 var t = cmb.open.text + " " + glyph("bentArrowLeft");
15098 this.showButton = createTiddlyButton(this.button,t,cmb.open.tooltip,
15099 function (e) {backstage.show(); return false;},null,"backstageShow");
15100 t = glyph("bentArrowRight") + " " + cmb.close.text;
15101 this.hideButton = createTiddlyButton(this.button,t,cmb.close.tooltip,
15102 function (e) {backstage.hide(); return false;},null,"backstageHide");
15103 this.cloak = document.getElementById("backstageCloak");
15104 this.panel = document.getElementById("backstagePanel");
15105 this.panelFooter = createTiddlyElement(this.panel,"div",null,"backstagePanelFooter");
15106 this.panelBody = createTiddlyElement(this.panel,"div",null,"backstagePanelBody");
15107 this.cloak.onmousedown = function(e) {
15108 backstage.switchTab(null);
15110 createTiddlyText(this.toolbar,cmb.prompt);
15111 for(t=0; t<config.backstageTasks.length; t++) {
15112 var taskName = config.backstageTasks[t];
15113 var task = config.tasks[taskName];
15114 var handler = task.action ? this.onClickCommand : this.onClickTab;
15115 var text = task.text + (task.action ? "" : glyph("downTriangle"));
15116 var btn = createTiddlyButton(this.toolbar,text,task.tooltip,handler,"backstageTab");
15117 btn.setAttribute("task",taskName);
15118 addClass(btn,task.action ? "backstageAction" : "backstageTask");
15120 this.content = document.getElementById("contentWrapper");
15121 if(config.options.chkBackstage)
15127 isVisible: function () {
15128 return this.area ? this.area.style.display == "block" : false;
15132 this.area.style.display = "block";
15133 if(anim && config.options.chkAnimate) {
15134 backstage.toolbar.style.left = findWindowWidth() + "px";
15136 {style: "left", start: findWindowWidth(), end: 0, template: "%0px"}
15138 anim.startAnimating(new Morpher(backstage.toolbar,config.animDuration,p));
15140 backstage.area.style.left = "0px";
15142 this.showButton.style.display = "none";
15143 this.hideButton.style.display = "block";
15144 config.options.chkBackstage = true;
15145 saveOptionCookie("chkBackstage");
15146 addClass(this.content,"backstageVisible");
15150 if(this.currTabElem) {
15151 this.switchTab(null);
15153 backstage.toolbar.style.left = "0px";
15154 if(anim && config.options.chkAnimate) {
15156 {style: "left", start: 0, end: findWindowWidth(), template: "%0px"}
15158 var c = function(element,properties) {backstage.area.style.display = "none";};
15159 anim.startAnimating(new Morpher(backstage.toolbar,config.animDuration,p,c));
15161 this.area.style.display = "none";
15163 this.showButton.style.display = "block";
15164 this.hideButton.style.display = "none";
15165 config.options.chkBackstage = false;
15166 saveOptionCookie("chkBackstage");
15167 removeClass(this.content,"backstageVisible");
15171 onClickCommand: function(e) {
15172 var task = config.tasks[this.getAttribute("task")];
15173 displayMessage(task);
15175 backstage.switchTab(null);
15181 onClickTab: function(e) {
15182 backstage.switchTab(this.getAttribute("task"));
15186 // Switch to a given tab, or none if null is passed
15187 switchTab: function(tabName) {
15188 var tabElem = null;
15189 var e = this.toolbar.firstChild;
15192 if(e.getAttribute && e.getAttribute("task") == tabName)
15196 if(tabName == backstage.currTabName)
15198 if(backstage.currTabElem) {
15199 removeClass(this.currTabElem,"backstageSelTab");
15201 if(tabElem && tabName) {
15202 backstage.preparePanel();
15203 addClass(tabElem,"backstageSelTab");
15204 var task = config.tasks[tabName];
15205 wikify(task.content,backstage.panelBody,null,null);
15206 backstage.showPanel();
15207 } else if(backstage.currTabElem) {
15208 backstage.hidePanel();
15210 backstage.currTabName = tabName;
15211 backstage.currTabElem = tabElem;
15214 isPanelVisible: function() {
15215 return backstage.panel ? backstage.panel.style.display == "block" : false;
15218 preparePanel: function() {
15219 backstage.cloak.style.height = findWindowHeight() + "px";
15220 backstage.cloak.style.display = "block";
15221 removeChildren(backstage.panelBody);
15222 return backstage.panelBody;
15225 showPanel: function() {
15226 backstage.panel.style.display = "block";
15227 if(anim && config.options.chkAnimate) {
15228 backstage.panel.style.top = (-backstage.panel.offsetHeight) + "px";
15230 {style: "top", start: -backstage.panel.offsetHeight, end: 0, template: "%0px"}
15232 anim.startAnimating(new Morpher(backstage.panel,config.animDuration,p),new Scroller(backstage.panel,false));
15234 backstage.panel.style.top = "0px";
15236 return backstage.panelBody;
15239 hidePanel: function() {
15240 backstage.currTabName = null;
15241 backstage.currTabElem = null;
15242 if(anim && config.options.chkAnimate) {
15244 {style: "top", start: 0, end: -(backstage.panel.offsetHeight), template: "%0px"},
15245 {style: "display", atEnd: "none"}
15247 var c = function(element,properties) {backstage.cloak.style.display = "none";};
15248 anim.startAnimating(new Morpher(backstage.panel,config.animDuration,p,c));
15250 backstage.panel.style.display = "none";
15251 backstage.cloak.style.display = "none";
15256 config.macros.backstage = {};
15258 config.macros.backstage.handler = function(place,macroName,params,wikifier,paramString,tiddler)
15260 var backstageTask = config.tasks[params[0]];
15262 createTiddlyButton(place,backstageTask.text,backstageTask.tooltip,function(e) {backstage.switchTab(params[0]); return false;});
15266 //-- ImportTiddlers macro
15269 config.macros.importTiddlers.handler = function(place,macroName,params,wikifier,paramString,tiddler)
15272 createTiddlyElement(place,"div",null,"marked",this.readOnlyWarning);
15275 var w = new Wizard();
15276 w.createWizard(place,this.wizardTitle);
15280 config.macros.importTiddlers.onCancel = function(e)
15282 var wizard = new Wizard(this);
15283 var place = wizard.clear();
15284 config.macros.importTiddlers.restart(wizard);
15288 config.macros.importTiddlers.restart = function(wizard)
15290 wizard.addStep(this.step1Title,this.step1Html);
15291 var s = wizard.getElement("selTypes");
15292 for(var t in config.adaptors) {
15293 var e = createTiddlyElement(s,"option",null,null,t);
15296 s = wizard.getElement("selFeeds");
15297 var feeds = this.getFeeds();
15299 e = createTiddlyElement(s,"option",null,null,t);
15302 wizard.setValue("feeds",feeds);
15303 s.onchange = config.macros.importTiddlers.onFeedChange;
15304 var fileInput = wizard.getElement("txtBrowse");
15305 fileInput.onchange = config.macros.importTiddlers.onBrowseChange;
15306 fileInput.onkeyup = config.macros.importTiddlers.onBrowseChange;
15307 wizard.setButtons([{caption: this.openLabel, tooltip: this.openPrompt, onClick: config.macros.importTiddlers.onOpen}]);
15310 config.macros.importTiddlers.getFeeds = function()
15313 var tagged = store.getTaggedTiddlers("systemServer","title");
15314 for(var t=0; t<tagged.length; t++) {
15315 var title = tagged[t].title;
15316 var serverType = store.getTiddlerSlice(title,"Type");
15318 serverType = "file";
15319 feeds[title] = {title: title,
15320 url: store.getTiddlerSlice(title,"URL"),
15321 workspace: store.getTiddlerSlice(title,"Workspace"),
15322 workspaceList: store.getTiddlerSlice(title,"WorkspaceList"),
15323 tiddlerFilter: store.getTiddlerSlice(title,"TiddlerFilter"),
15324 serverType: serverType,
15325 description: store.getTiddlerSlice(title,"Description")};
15330 config.macros.importTiddlers.onFeedChange = function(e)
15332 var wizard = new Wizard(this);
15333 var selTypes = wizard.getElement("selTypes");
15334 var fileInput = wizard.getElement("txtPath");
15335 var feeds = wizard.getValue("feeds");
15336 var f = feeds[this.value];
15338 selTypes.value = f.serverType;
15339 fileInput.value = f.url;
15340 this.selectedIndex = 0;
15341 wizard.setValue("feedName",f.serverType);
15342 wizard.setValue("feedHost",f.url);
15343 wizard.setValue("feedWorkspace",f.workspace);
15344 wizard.setValue("feedWorkspaceList",f.workspaceList);
15345 wizard.setValue("feedTiddlerFilter",f.tiddlerFilter);
15350 config.macros.importTiddlers.onBrowseChange = function(e)
15352 var wizard = new Wizard(this);
15353 var fileInput = wizard.getElement("txtPath");
15354 fileInput.value = "file://" + this.value;
15355 var serverType = wizard.getElement("selTypes");
15356 serverType.value = "file";
15360 config.macros.importTiddlers.onOpen = function(e)
15362 var wizard = new Wizard(this);
15363 var fileInput = wizard.getElement("txtPath");
15364 var url = fileInput.value;
15365 var serverType = wizard.getElement("selTypes").value;
15366 var adaptor = new config.adaptors[serverType];
15367 wizard.setValue("adaptor",adaptor);
15368 wizard.setValue("serverType",serverType);
15369 wizard.setValue("host",url);
15371 var ret = adaptor.openHost(url,context,wizard,config.macros.importTiddlers.onOpenHost);
15373 displayMessage(ret);
15374 wizard.setButtons([{caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}],config.macros.importTiddlers.statusOpenHost);
15378 config.macros.importTiddlers.onOpenHost = function(context,wizard)
15380 var adaptor = wizard.getValue("adaptor");
15381 if(context.status !== true)
15382 displayMessage("Error in importTiddlers.onOpenHost: " + context.statusText);
15383 var ret = adaptor.getWorkspaceList(context,wizard,config.macros.importTiddlers.onGetWorkspaceList);
15385 displayMessage(ret);
15386 wizard.setButtons([{caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}],config.macros.importTiddlers.statusGetWorkspaceList);
15389 config.macros.importTiddlers.onGetWorkspaceList = function(context,wizard)
15391 if(context.status !== true)
15392 displayMessage("Error in importTiddlers.onGetWorkspaceList: " + context.statusText);
15393 wizard.addStep(config.macros.importTiddlers.step2Title,config.macros.importTiddlers.step2Html);
15394 var s = wizard.getElement("selWorkspace");
15395 s.onchange = config.macros.importTiddlers.onWorkspaceChange;
15396 for(var t=0; t<context.workspaces.length; t++) {
15397 var e = createTiddlyElement(s,"option",null,null,context.workspaces[t].title);
15398 e.value = context.workspaces[t].title;
15400 var workspaceList = wizard.getValue("feedWorkspaceList");
15401 if(workspaceList) {
15402 var list = workspaceList.parseParams("workspace",null,false,true);
15403 for(var n=1; n<list.length; n++) {
15404 if(context.workspaces.findByField("title",list[n].value) == null) {
15405 e = createTiddlyElement(s,"option",null,null,list[n].value);
15406 e.value = list[n].value;
15410 var workspace = wizard.getValue("feedWorkspace");
15412 t = wizard.getElement("txtWorkspace");
15413 t.value = workspace;
15415 wizard.setButtons([{caption: config.macros.importTiddlers.openLabel, tooltip: config.macros.importTiddlers.openPrompt, onClick: config.macros.importTiddlers.onChooseWorkspace}]);
15418 config.macros.importTiddlers.onWorkspaceChange = function(e)
15420 var wizard = new Wizard(this);
15421 var t = wizard.getElement("txtWorkspace");
15422 t.value = this.value;
15423 this.selectedIndex = 0;
15427 config.macros.importTiddlers.onChooseWorkspace = function(e)
15429 var wizard = new Wizard(this);
15430 var adaptor = wizard.getValue("adaptor");
15431 var workspace = wizard.getElement("txtWorkspace").value;
15432 wizard.setValue("workspace",workspace);
15434 var ret = adaptor.openWorkspace(workspace,context,wizard,config.macros.importTiddlers.onOpenWorkspace);
15436 displayMessage(ret);
15437 wizard.setButtons([{caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}],config.macros.importTiddlers.statusOpenWorkspace);
15441 config.macros.importTiddlers.onOpenWorkspace = function(context,wizard)
15443 if(context.status !== true)
15444 displayMessage("Error in importTiddlers.onOpenWorkspace: " + context.statusText);
15445 var adaptor = wizard.getValue("adaptor");
15446 var ret = adaptor.getTiddlerList(context,wizard,config.macros.importTiddlers.onGetTiddlerList,wizard.getValue("feedTiddlerFilter"));
15448 displayMessage(ret);
15449 wizard.setButtons([{caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}],config.macros.importTiddlers.statusGetTiddlerList);
15452 config.macros.importTiddlers.onGetTiddlerList = function(context,wizard)
15454 if(context.status !== true)
15455 displayMessage("Error in importTiddlers.onGetTiddlerList: " + context.statusText);
15456 // Extract data for the listview
15457 var listedTiddlers = [];
15458 if(context.tiddlers) {
15459 for(var n=0; n<context.tiddlers.length; n++) {
15460 var tiddler = context.tiddlers[n];
15461 listedTiddlers.push({
15462 title: tiddler.title,
15463 modified: tiddler.modified,
15464 modifier: tiddler.modifier,
15465 text: tiddler.text ? wikifyPlainText(tiddler.text,100) : "",
15466 tags: tiddler.tags,
15467 size: tiddler.text ? tiddler.text.length : 0,
15472 listedTiddlers.sort(function(a,b) {return a.title < b.title ? -1 : (a.title == b.title ? 0 : +1);});
15473 // Display the listview
15474 wizard.addStep(config.macros.importTiddlers.step3Title,config.macros.importTiddlers.step3Html);
15475 var markList = wizard.getElement("markList");
15476 var listWrapper = document.createElement("div");
15477 markList.parentNode.insertBefore(listWrapper,markList);
15478 var listView = ListView.create(listWrapper,listedTiddlers,config.macros.importTiddlers.listViewTemplate);
15479 wizard.setValue("listView",listView);
15480 var txtSaveTiddler = wizard.getElement("txtSaveTiddler");
15481 txtSaveTiddler.value = config.macros.importTiddlers.generateSystemServerName(wizard);
15482 wizard.setButtons([
15483 {caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel},
15484 {caption: config.macros.importTiddlers.importLabel, tooltip: config.macros.importTiddlers.importPrompt, onClick: config.macros.importTiddlers.doImport}
15488 config.macros.importTiddlers.generateSystemServerName = function(wizard)
15490 var serverType = wizard.getValue("serverType");
15491 var host = wizard.getValue("host");
15492 var workspace = wizard.getValue("workspace");
15493 var pattern = config.macros.importTiddlers[workspace ? "systemServerNamePattern" : "systemServerNamePatternNoWorkspace"];
15494 return pattern.format([serverType,host,workspace]);
15497 config.macros.importTiddlers.saveServerTiddler = function(wizard)
15499 var txtSaveTiddler = wizard.getElement("txtSaveTiddler").value;
15500 if(store.tiddlerExists(txtSaveTiddler)) {
15501 if(!confirm(config.macros.importTiddlers.confirmOverwriteSaveTiddler.format([txtSaveTiddler])))
15503 store.suspendNotifications();
15504 store.removeTiddler(txtSaveTiddler);
15505 store.resumeNotifications();
15507 var serverType = wizard.getValue("serverType");
15508 var host = wizard.getValue("host");
15509 var workspace = wizard.getValue("workspace");
15510 var text = config.macros.importTiddlers.serverSaveTemplate.format([serverType,host,workspace]);
15511 store.saveTiddler(txtSaveTiddler,txtSaveTiddler,text,config.macros.importTiddlers.serverSaveModifier,new Date(),["systemServer"]);
15514 config.macros.importTiddlers.doImport = function(e)
15516 var wizard = new Wizard(this);
15517 if(wizard.getElement("chkSave").checked)
15518 config.macros.importTiddlers.saveServerTiddler(wizard);
15519 var chkSync = wizard.getElement("chkSync").checked;
15520 wizard.setValue("sync",chkSync);
15521 var listView = wizard.getValue("listView");
15522 var rowNames = ListView.getSelectedRows(listView);
15523 var adaptor = wizard.getValue("adaptor");
15524 var overwrite = new Array();
15526 for(t=0; t<rowNames.length; t++) {
15527 if(store.tiddlerExists(rowNames[t]))
15528 overwrite.push(rowNames[t]);
15530 if(overwrite.length > 0) {
15531 if(!confirm(config.macros.importTiddlers.confirmOverwriteText.format([overwrite.join(", ")])))
15534 wizard.addStep(config.macros.importTiddlers.step4Title.format([rowNames.length]),config.macros.importTiddlers.step4Html);
15535 for(t=0; t<rowNames.length; t++) {
15536 var link = document.createElement("div");
15537 createTiddlyLink(link,rowNames[t],true);
15538 var place = wizard.getElement("markReport");
15539 place.parentNode.insertBefore(link,place);
15541 wizard.setValue("remainingImports",rowNames.length);
15542 wizard.setButtons([
15543 {caption: config.macros.importTiddlers.cancelLabel, tooltip: config.macros.importTiddlers.cancelPrompt, onClick: config.macros.importTiddlers.onCancel}
15544 ],config.macros.importTiddlers.statusDoingImport);
15545 for(t=0; t<rowNames.length; t++) {
15547 context.allowSynchronous = true;
15548 var inbound = adaptor.getTiddler(rowNames[t],context,wizard,config.macros.importTiddlers.onGetTiddler);
15553 config.macros.importTiddlers.onGetTiddler = function(context,wizard)
15555 if(!context.status)
15556 displayMessage("Error in importTiddlers.onGetTiddler: " + context.statusText);
15557 var tiddler = context.tiddler;
15558 store.suspendNotifications();
15559 store.saveTiddler(tiddler.title, tiddler.title, tiddler.text, tiddler.modifier, tiddler.modified, tiddler.tags, tiddler.fields, true, tiddler.created);
15560 if(!wizard.getValue("sync")) {
15561 store.setValue(tiddler.title,'server',null);
15563 store.resumeNotifications();
15564 if(!context.isSynchronous)
15565 store.notify(tiddler.title,true);
15566 var remainingImports = wizard.getValue("remainingImports")-1;
15567 wizard.setValue("remainingImports",remainingImports);
15568 if(remainingImports == 0) {
15569 if(context.isSynchronous) {
15573 wizard.setButtons([
15574 {caption: config.macros.importTiddlers.doneLabel, tooltip: config.macros.importTiddlers.donePrompt, onClick: config.macros.importTiddlers.onCancel}
15575 ],config.macros.importTiddlers.statusDoneImport);
15584 // Synchronisation handlers
15585 config.syncers = {};
15588 var currSync = null;
15591 config.macros.sync.handler = function(place,macroName,params,wikifier,paramString,tiddler)
15593 if(!wikifier.isStatic)
15594 this.startSync(place);
15597 config.macros.sync.startSync = function(place)
15600 config.macros.sync.cancelSync();
15602 currSync.syncList = this.getSyncableTiddlers();
15603 this.createSyncTasks();
15604 this.preProcessSyncableTiddlers();
15605 var wizard = new Wizard();
15606 currSync.wizard = wizard;
15607 wizard.createWizard(place,this.wizardTitle);
15608 wizard.addStep(this.step1Title,this.step1Html);
15609 var markList = wizard.getElement("markList");
15610 var listWrapper = document.createElement("div");
15611 markList.parentNode.insertBefore(listWrapper,markList);
15612 currSync.listView = ListView.create(listWrapper,currSync.syncList,this.listViewTemplate);
15613 this.processSyncableTiddlers();
15614 wizard.setButtons([
15615 {caption: this.syncLabel, tooltip: this.syncPrompt, onClick: this.doSync}
15619 config.macros.sync.getSyncableTiddlers = function ()
15622 store.forEachTiddler(function(title,tiddler) {
15624 syncItem.serverType = tiddler.getServerType();
15625 syncItem.serverHost = tiddler.fields['server.host'];
15626 syncItem.serverWorkspace = tiddler.fields['server.workspace'];
15627 syncItem.tiddler = tiddler;
15628 syncItem.title = tiddler.title;
15629 syncItem.isTouched = tiddler.isTouched();
15630 syncItem.selected = syncItem.isTouched;
15631 syncItem.syncStatus = config.macros.sync.syncStatusList[syncItem.isTouched ? "changedLocally" : "none"];
15632 syncItem.status = syncItem.syncStatus.text;
15633 if(syncItem.serverType && syncItem.serverHost)
15634 list.push(syncItem);
15636 list.sort(function(a,b) {return a.title < b.title ? -1 : (a.title == b.title ? 0 : +1);});
15640 config.macros.sync.preProcessSyncableTiddlers = function()
15642 for(var t=0; t<currSync.syncList.length; t++) {
15643 si = currSync.syncList[t];
15644 var ti = si.syncTask.syncMachine.generateTiddlerInfo(si.tiddler);
15645 si.serverUrl = ti.uri;
15649 config.macros.sync.processSyncableTiddlers = function()
15651 for(var t=0; t<currSync.syncList.length; t++) {
15652 si = currSync.syncList[t];
15653 si.rowElement.style.backgroundColor = si.syncStatus.color;
15657 config.macros.sync.createSyncTasks = function()
15659 currSync.syncTasks = [];
15660 for(var t=0; t<currSync.syncList.length; t++) {
15661 var si = currSync.syncList[t];
15663 for(var st=0; st<currSync.syncTasks.length; st++) {
15664 var cst = currSync.syncTasks[st];
15665 if(si.serverType == cst.serverType && si.serverHost == cst.serverHost && si.serverWorkspace == cst.serverWorkspace)
15669 si.syncTask = this.createSyncTask(si);
15670 currSync.syncTasks.push(si.syncTask);
15673 r.syncItems.push(si);
15678 config.macros.sync.createSyncTask = function(syncItem)
15681 st.serverType = syncItem.serverType;
15682 st.serverHost = syncItem.serverHost;
15683 st.serverWorkspace = syncItem.serverWorkspace;
15684 st.syncItems = [syncItem];
15685 st.syncMachine = new SyncMachine(st.serverType,{
15686 start: function() {
15687 return this.openHost(st.serverHost,"openWorkspace");
15689 openWorkspace: function() {
15690 return this.openWorkspace(st.serverWorkspace,"getTiddlerList");
15692 getTiddlerList: function() {
15693 return this.getTiddlerList("gotTiddlerList");
15695 gotTiddlerList: function(tiddlers) {
15696 for(var t=0; t<st.syncItems.length; t++) {
15697 var si = st.syncItems[t];
15698 var f = tiddlers.findByField("title",si.title);
15700 if(tiddlers[f].fields['server.page.revision'] > si.tiddler.fields['server.page.revision']) {
15701 si.syncStatus = config.macros.sync.syncStatusList[si.isTouched ? 'changedBoth' : 'changedServer'];
15704 si.syncStatus = config.macros.sync.syncStatusList.notFound;
15706 config.macros.sync.updateSyncStatus(si);
15709 getTiddler: function(title) {
15710 return this.getTiddler(title,"onGetTiddler");
15712 onGetTiddler: function(tiddler) {
15713 var syncItem = st.syncItems.findByField("title",tiddler.title);
15714 if(syncItem !== null) {
15715 syncItem = st.syncItems[syncItem];
15716 store.saveTiddler(tiddler.title, tiddler.title, tiddler.text, tiddler.modifier, tiddler.modified, tiddler.tags, tiddler.fields, true, tiddler.created);
15717 syncItem.syncStatus = config.macros.sync.syncStatusList.gotFromServer;
15718 config.macros.sync.updateSyncStatus(syncItem);
15721 putTiddler: function(tiddler) {
15722 return this.putTiddler(tiddler,"onPutTiddler");
15724 onPutTiddler: function(tiddler) {
15725 var syncItem = st.syncItems.findByField("title",tiddler.title);
15726 if(syncItem !== null) {
15727 syncItem = st.syncItems[syncItem];
15728 store.resetTiddler(tiddler.title);
15729 syncItem.syncStatus = config.macros.sync.syncStatusList.putToServer;
15730 config.macros.sync.updateSyncStatus(syncItem);
15734 st.syncMachine.go();
15738 config.macros.sync.updateSyncStatus = function(syncItem)
15740 var e = syncItem.colElements["status"];
15742 createTiddlyText(e,syncItem.syncStatus.text);
15743 syncItem.rowElement.style.backgroundColor = syncItem.syncStatus.color;
15746 config.macros.sync.doSync = function(e)
15748 var rowNames = ListView.getSelectedRows(currSync.listView);
15749 for(var t=0; t<currSync.syncList.length; t++) {
15750 var si = currSync.syncList[t];
15751 if(rowNames.indexOf(si.title) != -1) {
15752 config.macros.sync.doSyncItem(si);
15758 config.macros.sync.doSyncItem = function(syncItem)
15761 var sl = config.macros.sync.syncStatusList;
15762 switch(syncItem.syncStatus) {
15763 case sl.changedServer:
15764 r = syncItem.syncTask.syncMachine.go("getTiddler",syncItem.title);
15767 case sl.changedLocally:
15768 case sl.changedBoth:
15769 r = syncItem.syncTask.syncMachine.go("putTiddler",syncItem.tiddler);
15775 displayMessage("Error in doSyncItem: " + r);
15778 config.macros.sync.cancelSync = function()
15783 function SyncMachine(serverType,steps)
15785 this.serverType = serverType;
15786 this.adaptor = new config.adaptors[serverType];
15787 this.steps = steps;
15790 SyncMachine.prototype.go = function(step,varargs)
15794 var h = this.steps[step];
15798 for(var t=1; t<arguments.length; t++)
15799 a.push(arguments[t]);
15800 var r = h.apply(this,a);
15801 if(typeof r == "string")
15802 this.invokeError(r);
15806 SyncMachine.prototype.invokeError = function(message)
15808 if(this.steps.error)
15809 this.steps.error(message);
15812 SyncMachine.prototype.openHost = function(host,nextStep)
15815 return me.adaptor.openHost(host,null,null,function(context) {
15816 if(typeof context.status == "string")
15817 me.invokeError(context.status);
15823 SyncMachine.prototype.getWorkspaceList = function(nextStep)
15826 return me.adaptor.getWorkspaceList(null,null,function(context) {
15827 if(typeof context.status == "string")
15828 me.invokeError(context.status);
15830 me.go(nextStep,context.workspaces);
15834 SyncMachine.prototype.openWorkspace = function(workspace,nextStep)
15837 return me.adaptor.openWorkspace(workspace,null,null,function(context) {
15838 if(typeof context.status == "string")
15839 me.invokeError(context.status);
15845 SyncMachine.prototype.getTiddlerList = function(nextStep)
15848 return me.adaptor.getTiddlerList(null,null,function(context) {
15849 if(typeof context.status == "string")
15850 me.invokeError(context.status);
15852 me.go(nextStep,context.tiddlers);
15856 SyncMachine.prototype.generateTiddlerInfo = function(tiddler)
15858 return this.adaptor.generateTiddlerInfo(tiddler);
15861 SyncMachine.prototype.getTiddler = function(title,nextStep)
15864 return me.adaptor.getTiddler(title,null,null,function(context) {
15865 if(typeof context.status == "string")
15866 me.invokeError(context.status);
15868 me.go(nextStep,context.tiddler);
15872 SyncMachine.prototype.putTiddler = function(tiddler,nextStep)
15875 return me.adaptor.putTiddler(tiddler,null,null,function(context) {
15876 if(typeof context.status == "string")
15877 me.invokeError(context.status);
15879 me.go(nextStep,tiddler);
15884 //-- Manager UI for groups of tiddlers
15887 config.macros.plugins.handler = function(place,macroName,params,wikifier,paramString,tiddler)
15889 var wizard = new Wizard();
15890 wizard.createWizard(place,this.wizardTitle);
15891 wizard.addStep(this.step1Title,this.step1Html);
15892 var markList = wizard.getElement("markList");
15893 var listWrapper = document.createElement("div");
15894 markList.parentNode.insertBefore(listWrapper,markList);
15895 listWrapper.setAttribute("refresh","macro");
15896 listWrapper.setAttribute("macroName","plugins");
15897 listWrapper.setAttribute("params",paramString);
15898 this.refresh(listWrapper,paramString);
15901 config.macros.plugins.refresh = function(listWrapper,params)
15903 var wizard = new Wizard(listWrapper);
15904 var selectedRows = [];
15905 ListView.forEachSelector(listWrapper,function(e,rowName) {
15907 selectedRows.push(e.getAttribute("rowName"));
15909 removeChildren(listWrapper);
15910 params = params.parseParams("anon");
15911 var plugins = installedPlugins.slice(0);
15913 var configTiddlers = store.getTaggedTiddlers("systemConfig");
15914 for(t=0; t<configTiddlers.length; t++) {
15915 tiddler = configTiddlers[t];
15916 if(plugins.findByField("title",tiddler.title) == null) {
15917 p = getPluginInfo(tiddler);
15918 p.executed = false;
15919 p.log.splice(0,0,this.skippedText);
15923 for(t=0; t<plugins.length; t++) {
15925 p.size = p.tiddler.text ? p.tiddler.text.length : 0;
15926 p.forced = p.tiddler.isTagged("systemConfigForce");
15927 p.disabled = p.tiddler.isTagged("systemConfigDisable");
15928 p.Selected = selectedRows.indexOf(plugins[t].title) != -1;
15930 if(plugins.length == 0) {
15931 createTiddlyElement(listWrapper,"em",null,null,this.noPluginText);
15932 wizard.setButtons([]);
15934 var listView = ListView.create(listWrapper,plugins,this.listViewTemplate,this.onSelectCommand);
15935 wizard.setValue("listView",listView);
15936 wizard.setButtons([
15937 {caption: config.macros.plugins.removeLabel, tooltip: config.macros.plugins.removePrompt, onClick: config.macros.plugins.doRemoveTag},
15938 {caption: config.macros.plugins.deleteLabel, tooltip: config.macros.plugins.deletePrompt, onClick: config.macros.plugins.doDelete}
15943 config.macros.plugins.doRemoveTag = function(e)
15945 var wizard = new Wizard(this);
15946 var listView = wizard.getValue("listView");
15947 var rowNames = ListView.getSelectedRows(listView);
15948 if(rowNames.length == 0) {
15949 alert(config.messages.nothingSelected);
15951 for(var t=0; t<rowNames.length; t++)
15952 store.setTiddlerTag(rowNames[t],false,"systemConfig");
15956 config.macros.plugins.doDelete = function(e)
15958 var wizard = new Wizard(this);
15959 var listView = wizard.getValue("listView");
15960 var rowNames = ListView.getSelectedRows(listView);
15961 if(rowNames.length == 0) {
15962 alert(config.messages.nothingSelected);
15964 if(confirm(config.macros.plugins.confirmDeleteText.format([rowNames.join(", ")]))) {
15965 for(t=0; t<rowNames.length; t++) {
15966 store.removeTiddler(rowNames[t]);
15967 story.closeTiddler(rowNames[t],true);
15977 function getMessageDiv()
15979 var msgArea = document.getElementById("messageArea");
15982 if(!msgArea.hasChildNodes())
15983 createTiddlyButton(createTiddlyElement(msgArea,"div",null,"messageToolbar"),
15984 config.messages.messageClose.text,
15985 config.messages.messageClose.tooltip,
15987 msgArea.style.display = "block";
15988 return createTiddlyElement(msgArea,"div");
15991 function displayMessage(text,linkText)
15993 var e = getMessageDiv();
15999 var link = createTiddlyElement(e,"a",null,null,text);
16000 link.href = linkText;
16001 link.target = "_blank";
16003 e.appendChild(document.createTextNode(text));
16007 function clearMessage()
16009 var msgArea = document.getElementById("messageArea");
16011 removeChildren(msgArea);
16012 msgArea.style.display = "none";
16018 //-- Refresh mechanism
16021 config.refreshers = {
16022 link: function(e,changeList)
16024 var title = e.getAttribute("tiddlyLink");
16025 refreshTiddlyLink(e,title);
16029 tiddler: function(e,changeList)
16031 var title = e.getAttribute("tiddler");
16032 var template = e.getAttribute("template");
16033 if(changeList && changeList.indexOf(title) != -1 && !story.isDirty(title))
16034 story.refreshTiddler(title,template,true);
16036 refreshElements(e,changeList);
16040 content: function(e,changeList)
16042 var title = e.getAttribute("tiddler");
16043 var force = e.getAttribute("force");
16044 if(force != null || changeList == null || changeList.indexOf(title) != -1) {
16046 wikify(store.getTiddlerText(title,title),e,null);
16052 macro: function(e,changeList)
16054 var macro = e.getAttribute("macroName");
16055 var params = e.getAttribute("params");
16057 macro = config.macros[macro];
16058 if(macro && macro.refresh)
16059 macro.refresh(e,params);
16064 function refreshElements(root,changeList)
16066 var nodes = root.childNodes;
16067 for(var c=0; c<nodes.length; c++) {
16068 var e = nodes[c], type = null;
16069 if(e.getAttribute && (e.tagName ? e.tagName != "IFRAME" : true))
16070 type = e.getAttribute("refresh");
16071 var refresher = config.refreshers[type];
16072 var refreshed = false;
16073 if(refresher != undefined)
16074 refreshed = refresher(e,changeList);
16075 if(e.hasChildNodes() && !refreshed)
16076 refreshElements(e,changeList);
16080 function applyHtmlMacros(root,tiddler)
16082 var e = root.firstChild;
16084 var nextChild = e.nextSibling;
16085 if(e.getAttribute) {
16086 var macro = e.getAttribute("macro");
16089 var p = macro.indexOf(" ");
16091 params = macro.substr(p+1);
16092 macro = macro.substr(0,p);
16094 invokeMacro(e,macro,params,null,tiddler);
16097 if(e.hasChildNodes())
16098 applyHtmlMacros(e,tiddler);
16103 function refreshPageTemplate(title)
16105 var stash = createTiddlyElement(document.body,"div");
16106 stash.style.display = "none";
16107 var display = document.getElementById("tiddlerDisplay");
16110 nodes = display.childNodes;
16111 for(t=nodes.length-1; t>=0; t--)
16112 stash.appendChild(nodes[t]);
16114 var wrapper = document.getElementById("contentWrapper");
16116 title = "PageTemplate";
16117 var html = store.getRecursiveTiddlerText(title,null,10);
16118 wrapper.innerHTML = html;
16119 applyHtmlMacros(wrapper);
16120 refreshElements(wrapper);
16121 display = document.getElementById("tiddlerDisplay");
16122 removeChildren(display);
16124 display = createTiddlyElement(wrapper,"div","tiddlerDisplay");
16125 nodes = stash.childNodes;
16126 for(t=nodes.length-1; t>=0; t--)
16127 display.appendChild(nodes[t]);
16131 function refreshDisplay(hint)
16133 if(typeof hint == "string")
16135 var e = document.getElementById("contentWrapper");
16136 refreshElements(e,hint);
16137 if(backstage.isPanelVisible()) {
16138 e = document.getElementById("backstage");
16139 refreshElements(e,hint);
16143 function refreshPageTitle()
16145 document.title = getPageTitle();
16148 function getPageTitle()
16150 var st = wikifyPlain("SiteTitle");
16151 var ss = wikifyPlain("SiteSubtitle");
16152 return st + ((st == "" || ss == "") ? "" : " - ") + ss;
16155 function refreshStyles(title,doc)
16159 setStylesheet(title == null ? "" : store.getRecursiveTiddlerText(title,"",10),title,doc);
16162 function refreshColorPalette(title)
16168 function refreshAll()
16170 refreshPageTemplate();
16172 refreshStyles("StyleSheetLayout");
16173 refreshStyles("StyleSheetColors");
16174 refreshStyles("StyleSheet");
16175 refreshStyles("StyleSheetPrint");
16179 //-- Options cookie stuff
16182 config.optionHandlers = {
16184 get: function(name) {return encodeCookie(config.options[name].toString());},
16185 set: function(name,value) {config.options[name] = decodeCookie(value);}
16188 get: function(name) {return config.options[name] ? "true" : "false";},
16189 set: function(name,value) {config.options[name] = value == "true";}
16193 function loadOptionsCookie()
16197 var cookies = document.cookie.split(";");
16198 for(var c=0; c<cookies.length; c++) {
16199 var p = cookies[c].indexOf("=");
16201 var name = cookies[c].substr(0,p).trim();
16202 var value = cookies[c].substr(p+1).trim();
16203 var optType = name.substr(0,3);
16204 if(config.optionHandlers[optType] && config.optionHandlers[optType].set)
16205 config.optionHandlers[optType].set(name,value);
16210 function saveOptionCookie(name)
16214 var c = name + "=";
16215 var optType = name.substr(0,3);
16216 if(config.optionHandlers[optType] && config.optionHandlers[optType].get)
16217 c += config.optionHandlers[optType].get(name);
16218 c += "; expires=Fri, 1 Jan 2038 12:00:00 UTC; path=/";
16219 document.cookie = c;
16222 function encodeCookie(s)
16224 return escape(manualConvertUnicodeToUTF8(s));
16227 function decodeCookie(s)
16230 var re = /&#[0-9]{1,5};/g;
16231 return s.replace(re,function($0) {return String.fromCharCode(eval($0.replace(/[&#;]/g,"")));});
16238 var saveUsingSafari = false;
16240 var startSaveArea = '<div id="' + 'storeArea">'; // Split up into two so that indexOf() of this source doesn't find it
16241 var endSaveArea = '</d' + 'iv>';
16243 // If there are unsaved changes, force the user to confirm before exitting
16244 function confirmExit()
16246 hadConfirmExit = true;
16247 if((store && store.isDirty && store.isDirty()) || (story && story.areAnyDirty && story.areAnyDirty()))
16248 return config.messages.confirmExit;
16251 // Give the user a chance to save changes before exitting
16252 function checkUnsavedChanges()
16254 if(store && store.isDirty && store.isDirty() && window.hadConfirmExit === false) {
16255 if(confirm(config.messages.unsavedChangesWarning))
16260 function updateLanguageAttribute(s)
16262 if(config.locale) {
16263 var mRE = /(<html(?:.*?)?)(?: xml:lang\="([a-z]+)")?(?: lang\="([a-z]+)")?>/;
16264 var m = mRE.exec(s);
16268 t += ' xml:lang="' + config.locale + '"';
16270 t += ' lang="' + config.locale + '"';
16272 s = s.substr(0,m.index) + t + s.substr(m.index+m[0].length);
16278 function updateMarkupBlock(s,blockName,tiddlerName)
16280 return s.replaceChunk(
16281 "<!--%0-START-->".format([blockName]),
16282 "<!--%0-END-->".format([blockName]),
16283 "\n" + store.getRecursiveTiddlerText(tiddlerName,"") + "\n");
16286 function updateOriginal(original,posDiv)
16289 posDiv = locateStoreArea(original);
16291 alert(config.messages.invalidFileError.format([localPath]));
16294 var revised = original.substr(0,posDiv[0] + startSaveArea.length) + "\n" +
16295 convertUnicodeToUTF8(store.allTiddlersAsHtml()) + "\n" +
16296 original.substr(posDiv[1]);
16297 var newSiteTitle = convertUnicodeToUTF8(getPageTitle()).htmlEncode();
16298 revised = revised.replaceChunk("<title"+">","</title"+">"," " + newSiteTitle + " ");
16299 revised = updateLanguageAttribute(revised);
16300 revised = updateMarkupBlock(revised,"PRE-HEAD","MarkupPreHead");
16301 revised = updateMarkupBlock(revised,"POST-HEAD","MarkupPostHead");
16302 revised = updateMarkupBlock(revised,"PRE-BODY","MarkupPreBody");
16303 revised = updateMarkupBlock(revised,"POST-SCRIPT","MarkupPostBody");
16307 function locateStoreArea(original)
16309 // Locate the storeArea div's
16310 var posOpeningDiv = original.indexOf(startSaveArea);
16311 var limitClosingDiv = original.indexOf("<"+"!--POST-STOREAREA--"+">");
16312 if(limitClosingDiv == -1)
16313 limitClosingDiv = original.indexOf("<"+"!--POST-BODY-START--"+">");
16314 var posClosingDiv = original.lastIndexOf(endSaveArea,limitClosingDiv == -1 ? original.length : limitClosingDiv);
16315 return (posOpeningDiv != -1 && posClosingDiv != -1) ? [posOpeningDiv,posClosingDiv] : null;
16318 function autoSaveChanges(onlyIfDirty,tiddlers)
16320 if(config.options.chkAutoSave)
16321 saveChanges(onlyIfDirty,tiddlers);
16324 // Save this tiddlywiki with the pending changes
16325 function saveChanges(onlyIfDirty,tiddlers)
16327 if(onlyIfDirty && !store.isDirty())
16330 // Get the URL of the document
16331 var originalPath = document.location.toString();
16332 // Check we were loaded from a file URL
16333 if(originalPath.substr(0,5) != "file:") {
16334 alert(config.messages.notFileUrlError);
16335 if(store.tiddlerExists(config.messages.saveInstructions))
16336 story.displayTiddler(null,config.messages.saveInstructions);
16339 var localPath = getLocalPath(originalPath);
16340 // Load the original file
16341 var original = loadFile(localPath);
16342 if(original == null) {
16343 alert(config.messages.cantSaveError);
16344 if(store.tiddlerExists(config.messages.saveInstructions))
16345 story.displayTiddler(null,config.messages.saveInstructions);
16348 // Locate the storeArea div's
16349 var posDiv = locateStoreArea(original);
16351 alert(config.messages.invalidFileError.format([localPath]));
16354 saveBackup(localPath,original);
16355 saveRss(localPath);
16356 saveEmpty(localPath,original,posDiv);
16357 saveMain(localPath,original,posDiv);
16360 function saveBackup(localPath,original)
16363 if(config.options.chkSaveBackups) {
16364 var backupPath = getBackupPath(localPath);
16365 var backup = config.browser.isIE ? ieCopyFile(backupPath,localPath) : saveFile(backupPath,original);
16367 displayMessage(config.messages.backupSaved,"file://" + backupPath);
16369 alert(config.messages.backupFailed);
16373 function saveRss(localPath)
16375 if(config.options.chkGenerateAnRssFeed) {
16376 var rssPath = localPath.substr(0,localPath.lastIndexOf(".")) + ".xml";
16377 var rssSave = saveFile(rssPath,convertUnicodeToUTF8(generateRss()));
16379 displayMessage(config.messages.rssSaved,"file://" + rssPath);
16381 alert(config.messages.rssFailed);
16385 function saveEmpty(localPath,original,posDiv)
16387 if(config.options.chkSaveEmptyTemplate) {
16389 if((p = localPath.lastIndexOf("/")) != -1)
16390 emptyPath = localPath.substr(0,p) + "/empty.html";
16391 else if((p = localPath.lastIndexOf("\\")) != -1)
16392 emptyPath = localPath.substr(0,p) + "\\empty.html";
16394 emptyPath = localPath + ".empty.html";
16395 var empty = original.substr(0,posDiv[0] + startSaveArea.length) + original.substr(posDiv[1]);
16396 var emptySave = saveFile(emptyPath,empty);
16398 displayMessage(config.messages.emptySaved,"file://" + emptyPath);
16400 alert(config.messages.emptyFailed);
16404 function saveMain(localPath,original,posDiv)
16408 var revised = updateOriginal(original,posDiv);
16409 save = saveFile(localPath,revised);
16414 displayMessage(config.messages.mainSaved,"file://" + localPath);
16415 store.setDirty(false);
16417 alert(config.messages.mainFailed);
16421 function getLocalPath(origPath)
16423 var originalPath = convertUriToUTF8(origPath,config.options.txtFileSystemCharSet);
16424 // Remove any location or query part of the URL
16425 var argPos = originalPath.indexOf("?");
16427 originalPath = originalPath.substr(0,argPos);
16428 var hashPos = originalPath.indexOf("#");
16430 originalPath = originalPath.substr(0,hashPos);
16431 // Convert file://localhost/ to file:///
16432 if(originalPath.indexOf("file://localhost/") == 0)
16433 originalPath = "file://" + originalPath.substr(16);
16434 // Convert to a native file format
16436 if(originalPath.charAt(9) == ":") // pc local file
16437 localPath = unescape(originalPath.substr(8)).replace(new RegExp("/","g"),"\\");
16438 else if(originalPath.indexOf("file://///") == 0) // FireFox pc network file
16439 localPath = "\\\\" + unescape(originalPath.substr(10)).replace(new RegExp("/","g"),"\\");
16440 else if(originalPath.indexOf("file:///") == 0) // mac/unix local file
16441 localPath = unescape(originalPath.substr(7));
16442 else if(originalPath.indexOf("file:/") == 0) // mac/unix local file
16443 localPath = unescape(originalPath.substr(5));
16444 else // pc network file
16445 localPath = "\\\\" + unescape(originalPath.substr(7)).replace(new RegExp("/","g"),"\\");
16449 function getBackupPath(localPath)
16451 var backSlash = true;
16452 var dirPathPos = localPath.lastIndexOf("\\");
16453 if(dirPathPos == -1) {
16454 dirPathPos = localPath.lastIndexOf("/");
16457 var backupFolder = config.options.txtBackupFolder;
16458 if(!backupFolder || backupFolder == "")
16459 backupFolder = ".";
16460 var backupPath = localPath.substr(0,dirPathPos) + (backSlash ? "\\" : "/") + backupFolder + localPath.substr(dirPathPos);
16461 backupPath = backupPath.substr(0,backupPath.lastIndexOf(".")) + "." + (new Date()).convertToYYYYMMDDHHMMSSMMM() + ".html";
16465 function generateRss()
16468 var d = new Date();
16469 var u = store.getTiddlerText("SiteUrl");
16470 // Assemble the header
16471 s.push("<" + "?xml version=\"1.0\"?" + ">");
16472 s.push("<rss version=\"2.0\">");
16473 s.push("<channel>");
16474 s.push("<title" + ">" + wikifyPlain("SiteTitle").htmlEncode() + "</title" + ">");
16476 s.push("<link>" + u.htmlEncode() + "</link>");
16477 s.push("<description>" + wikifyPlain("SiteSubtitle").htmlEncode() + "</description>");
16478 s.push("<language>en-us</language>");
16479 s.push("<copyright>Copyright " + d.getFullYear() + " " + config.options.txtUserName.htmlEncode() + "</copyright>");
16480 s.push("<pubDate>" + d.toGMTString() + "</pubDate>");
16481 s.push("<lastBuildDate>" + d.toGMTString() + "</lastBuildDate>");
16482 s.push("<docs>http://blogs.law.harvard.edu/tech/rss</docs>");
16483 s.push("<generator>TiddlyWiki " + version.major + "." + version.minor + "." + version.revision + "</generator>");
16485 var tiddlers = store.getTiddlers("modified","excludeLists");
16486 var n = config.numRssItems > tiddlers.length ? 0 : tiddlers.length-config.numRssItems;
16487 for (var t=tiddlers.length-1; t>=n; t--)
16488 s.push(tiddlers[t].saveToRss(u));
16490 s.push("</channel>");
16493 return s.join("\n");
16497 //-- Filesystem code
16500 function convertUTF8ToUnicode(u)
16502 if(window.netscape == undefined)
16503 return manualConvertUTF8ToUnicode(u);
16505 return mozConvertUTF8ToUnicode(u);
16508 function manualConvertUTF8ToUnicode(utf)
16515 while(src < utf.length) {
16516 b1 = utf.charCodeAt(src++);
16519 } else if(b1 < 0xE0) {
16520 b2 = utf.charCodeAt(src++);
16521 c = String.fromCharCode(((b1 & 0x1F) << 6) | (b2 & 0x3F));
16522 uni = uni.substring(0,dst++).concat(c,utf.substr(src));
16524 b2 = utf.charCodeAt(src++);
16525 b3 = utf.charCodeAt(src++);
16526 c = String.fromCharCode(((b1 & 0xF) << 12) | ((b2 & 0x3F) << 6) | (b3 & 0x3F));
16527 uni = uni.substring(0,dst++).concat(c,utf.substr(src));
16533 function mozConvertUTF8ToUnicode(u)
16536 netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
16537 var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
16538 converter.charset = "UTF-8";
16540 return manualConvertUTF8ToUnicode(u);
16542 var s = converter.ConvertToUnicode(u);
16543 var fin = converter.Finish();
16544 return (fin.length > 0) ? s+fin : s;
16547 function convertUnicodeToUTF8(s)
16549 if(window.netscape == undefined)
16550 return manualConvertUnicodeToUTF8(s);
16552 return mozConvertUnicodeToUTF8(s);
16555 function manualConvertUnicodeToUTF8(s)
16557 var re = /[^\u0000-\u007F]/g ;
16558 return s.replace(re,function($0) {return "&#" + $0.charCodeAt(0).toString() + ";";});
16561 function mozConvertUnicodeToUTF8(s)
16564 netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
16565 var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
16566 converter.charset = "UTF-8";
16568 return manualConvertUnicodeToUTF8(s);
16570 var u = converter.ConvertFromUnicode(s);
16571 var fin = converter.Finish();
16578 function convertUriToUTF8(uri,charSet)
16580 if(window.netscape == undefined || charSet == undefined || charSet == "")
16583 netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
16584 var converter = Components.classes["@mozilla.org/intl/utf8converterservice;1"].getService(Components.interfaces.nsIUTF8ConverterService);
16588 return converter.convertURISpecToUTF8(uri,charSet);
16591 function saveFile(fileUrl,content)
16595 r = mozillaSaveFile(fileUrl,content);
16597 r = ieSaveFile(fileUrl,content);
16599 r = javaSaveFile(fileUrl,content);
16603 function loadFile(fileUrl)
16606 if((r == null) || (r == false))
16607 r = mozillaLoadFile(fileUrl);
16608 if((r == null) || (r == false))
16609 r = ieLoadFile(fileUrl);
16610 if((r == null) || (r == false))
16611 r = javaLoadFile(fileUrl);
16615 // Returns null if it can't do it, false if there's an error, true if it saved OK
16616 function ieSaveFile(filePath,content)
16619 var fso = new ActiveXObject("Scripting.FileSystemObject");
16623 var file = fso.OpenTextFile(filePath,2,-1,0);
16624 file.Write(content);
16629 // Returns null if it can't do it, false if there's an error, or a string of the content if successful
16630 function ieLoadFile(filePath)
16633 var fso = new ActiveXObject("Scripting.FileSystemObject");
16634 var file = fso.OpenTextFile(filePath,1);
16635 var content = file.ReadAll();
16643 function ieCopyFile(dest,source)
16646 var fso = new ActiveXObject("Scripting.FileSystemObject");
16647 fso.GetFile(source).Copy(dest);
16654 // Returns null if it can't do it, false if there's an error, true if it saved OK
16655 function mozillaSaveFile(filePath,content)
16657 if(window.Components) {
16659 netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
16660 var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
16661 file.initWithPath(filePath);
16663 file.create(0,0664);
16664 var out = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream);
16665 out.init(file,0x20|0x02,00004,null);
16666 out.write(content,content.length);
16677 // Returns null if it can't do it, false if there's an error, or a string of the content if successful
16678 function mozillaLoadFile(filePath)
16680 if(window.Components) {
16682 netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
16683 var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
16684 file.initWithPath(filePath);
16687 var inputStream = Components.classes["@mozilla.org/network/file-input-stream;1"].createInstance(Components.interfaces.nsIFileInputStream);
16688 inputStream.init(file,0x01,00004,null);
16689 var sInputStream = Components.classes["@mozilla.org/scriptableinputstream;1"].createInstance(Components.interfaces.nsIScriptableInputStream);
16690 sInputStream.init(inputStream);
16691 return sInputStream.read(sInputStream.available());
16699 function javaUrlToFilename(url)
16701 var f = "//localhost";
16702 if(url.indexOf(f) == 0)
16703 return url.substring(f.length);
16704 var i = url.indexOf(":");
16706 return url.substring(i-1);
16710 function javaSaveFile(filePath,content)
16713 if(document.applets["TiddlySaver"])
16714 return document.applets["TiddlySaver"].saveFile(javaUrlToFilename(filePath),"UTF-8",content);
16718 var s = new java.io.PrintStream(new java.io.FileOutputStream(javaUrlToFilename(filePath)));
16727 function javaLoadFile(filePath)
16730 if(document.applets["TiddlySaver"])
16731 return String(document.applets["TiddlySaver"].loadFile(javaUrlToFilename(filePath),"UTF-8"));
16736 var r = new java.io.BufferedReader(new java.io.FileReader(javaUrlToFilename(filePath)));
16738 while((line = r.readLine()) != null)
16739 content.push(new String(line));
16744 return content.join("\n");
16748 //-- Server adaptor for talking to static files
16751 function FileAdaptor()
16758 FileAdaptor.NotLoadedError = "TiddlyWiki file has not been loaded";
16759 FileAdaptor.serverType = 'file';
16761 // Open the specified host/server
16762 FileAdaptor.prototype.openHost = function(host,context,userParams,callback)
16767 context.adaptor = this;
16768 context.callback = callback;
16769 context.userParams = userParams;
16770 var ret = loadRemoteFile(host,FileAdaptor.openHostCallback,context);
16771 return typeof(ret) == "string" ? ret : true;
16774 FileAdaptor.openHostCallback = function(status,context,responseText,url,xhr)
16776 var adaptor = context.adaptor;
16777 context.status = status;
16779 context.statusText = "Error reading file: " + xhr.statusText;
16781 // Load the content into a TiddlyWiki() object
16782 adaptor.store = new TiddlyWiki();
16783 if(!adaptor.store.importTiddlyWiki(responseText))
16784 context.statusText = config.messages.invalidFileError.format([url]);
16786 context.callback(context,context.userParams);
16789 // Gets the list of workspaces on a given server
16790 FileAdaptor.prototype.getWorkspaceList = function(context,userParams,callback)
16794 context.workspaces = [{title:"(default)"}];
16795 context.status = true;
16796 window.setTimeout(function() {callback(context,userParams);},10);
16800 // Open the specified workspace
16801 FileAdaptor.prototype.openWorkspace = function(workspace,context,userParams,callback)
16805 context.status = true;
16806 window.setTimeout(function() {callback(context,userParams);},10);
16810 // Gets the list of tiddlers within a given workspace
16811 FileAdaptor.prototype.getTiddlerList = function(context,userParams,callback)
16814 return FileAdaptor.NotLoadedError;
16817 context.tiddlers = [];
16818 this.store.forEachTiddler(function(title,tiddler)
16820 var t = new Tiddler(title);
16821 t.text = tiddler.text;
16822 t.modified = tiddler.modified;
16823 t.modifier = tiddler.modifier;
16824 t.fields['server.page.revision'] = tiddler.modified.convertToYYYYMMDDHHMM();
16825 t.tags = tiddler.tags;
16826 context.tiddlers.push(t);
16828 context.status = true;
16829 window.setTimeout(function() {callback(context,userParams);},10);
16833 FileAdaptor.prototype.generateTiddlerInfo = function(tiddler)
16836 info.uri = tiddler.fields['server.host'] + "#" + tiddler.title;
16840 // Retrieves a tiddler from a given workspace on a given server
16841 FileAdaptor.prototype.getTiddler = function(title,context,userParams,callback)
16844 return FileAdaptor.NotLoadedError;
16847 context.tiddler = this.store.fetchTiddler(title);
16848 if(context.tiddler) {
16849 context.tiddler.fields['server.type'] = FileAdaptor.serverType;
16850 context.tiddler.fields['server.host'] = this.host;
16851 context.tiddler.fields['server.page.revision'] = context.tiddler.modified.convertToYYYYMMDDHHMM();
16853 context.status = true;
16854 if(context.allowSynchronous) {
16855 context.isSynchronous = true;
16856 callback(context,userParams);
16858 window.setTimeout(function() {callback(context,userParams);},10);
16863 FileAdaptor.prototype.close = function()
16869 config.adaptors[FileAdaptor.serverType] = FileAdaptor;
16872 //-- Remote HTTP requests
16875 function loadRemoteFile(url,callback,params)
16877 return doHttp("GET",url,null,null,null,null,callback,params,null);
16880 // HTTP status codes
16883 ContentCreated: 201,
16888 MethodNotAllowed: 405
16891 function doHttp(type,url,data,contentType,username,password,callback,params,headers)
16893 // Get an xhr object
16894 var x = getXMLHttpRequest();
16896 return "Can't create XMLHttpRequest object";
16897 // Install callback
16898 x.onreadystatechange = function() {
16899 if (x.readyState == 4 && callback && (x.status !== undefined)) {
16900 if([0, httpStatus.OK, httpStatus.ContentCreated, httpStatus.NoContent].contains(x.status))
16901 callback(true,params,x.responseText,url,x);
16903 callback(false,params,null,url,x);
16904 x.onreadystatechange = function(){};
16909 if(window.Components && window.netscape && window.netscape.security && document.location.protocol.indexOf("http") == -1)
16910 window.netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
16912 url = url + (url.indexOf("?") < 0 ? "?" : "&") + "nocache=" + Math.random();
16913 x.open(type,url,true,username,password);
16915 x.setRequestHeader("Content-Type", contentType ? contentType : "application/x-www-form-urlencoded");
16916 if (x.overrideMimeType)
16917 x.setRequestHeader("Connection", "close");
16920 x.setRequestHeader(n,headers[n]);
16922 x.setRequestHeader("X-Requested-With", "TiddlyWiki " + version.major + "." + version.minor + "." + version.revision + (version.beta ? " (beta " + version.beta + ")" : ""));
16925 return exceptionText(ex);
16930 function getXMLHttpRequest()
16933 var x = new XMLHttpRequest(); // Modern
16936 x = new ActiveXObject("Msxml2.XMLHTTP"); // IE 6
16945 //-- TiddlyWiki-specific utility functions
16948 function createTiddlyButton(theParent,theText,theTooltip,theAction,theClass,theId,theAccessKey)
16950 var theButton = document.createElement("a");
16952 theButton.onclick = theAction;
16953 theButton.setAttribute("href","javascript:;");
16956 theButton.setAttribute("title",theTooltip);
16958 theButton.appendChild(document.createTextNode(theText));
16960 theButton.className = theClass;
16962 theButton.className = "button";
16964 theButton.id = theId;
16966 theParent.appendChild(theButton);
16968 theButton.setAttribute("accessKey",theAccessKey);
16972 function createTiddlyLink(place,title,includeText,theClass,isStatic,linkedFromTiddler,noToggle)
16974 var text = includeText ? title : null;
16975 var i = getTiddlyLinkInfo(title,theClass);
16976 var btn = isStatic ? createExternalLink(place,store.getTiddlerText("SiteUrl",null) + "#" + title) : createTiddlyButton(place,text,i.subTitle,onClickTiddlerLink,i.classes);
16977 btn.setAttribute("refresh","link");
16978 btn.setAttribute("tiddlyLink",title);
16980 btn.setAttribute("noToggle","true");
16981 if(linkedFromTiddler) {
16982 var fields = linkedFromTiddler.getInheritedFields();
16984 btn.setAttribute("tiddlyFields",fields);
16989 function refreshTiddlyLink(e,title)
16991 var i = getTiddlyLinkInfo(title,e.className);
16992 e.className = i.classes;
16993 e.title = i.subTitle;
16996 function getTiddlyLinkInfo(title,currClasses)
16998 var classes = currClasses ? currClasses.split(" ") : [];
16999 classes.pushUnique("tiddlyLink");
17000 var tiddler = store.fetchTiddler(title);
17003 subTitle = tiddler.getSubtitle();
17004 classes.pushUnique("tiddlyLinkExisting");
17005 classes.remove("tiddlyLinkNonExisting");
17006 classes.remove("shadow");
17008 classes.remove("tiddlyLinkExisting");
17009 classes.pushUnique("tiddlyLinkNonExisting");
17010 if(store.isShadowTiddler(title)) {
17011 subTitle = config.messages.shadowedTiddlerToolTip.format([title]);
17012 classes.pushUnique("shadow");
17014 subTitle = config.messages.undefinedTiddlerToolTip.format([title]);
17015 classes.remove("shadow");
17018 if(config.annotations[title])
17019 subTitle = config.annotations[title];
17020 return {classes: classes.join(" "),subTitle: subTitle};
17023 function createExternalLink(place,url)
17025 var theLink = document.createElement("a");
17026 theLink.className = "externalLink";
17027 theLink.href = url;
17028 theLink.title = config.messages.externalLinkTooltip.format([url]);
17029 if(config.options.chkOpenInNewWindow)
17030 theLink.target = "_blank";
17031 place.appendChild(theLink);
17035 // Event handler for clicking on a tiddly link
17036 function onClickTiddlerLink(e)
17038 if(!e) e = window.event;
17039 var theTarget = resolveTarget(e);
17040 var theLink = theTarget;
17043 var noToggle = null;
17045 title = theLink.getAttribute("tiddlyLink");
17046 fields = theLink.getAttribute("tiddlyFields");
17047 noToggle = theLink.getAttribute("noToggle");
17048 theLink = theLink.parentNode;
17049 } while(title == null && theLink != null);
17050 if(!fields && !store.isShadowTiddler(title))
17051 fields = String.encodeHashMap(config.defaultCustomFields);
17053 var toggling = e.metaKey || e.ctrlKey;
17054 if(config.options.chkToggleLinks)
17055 toggling = !toggling;
17058 story.displayTiddler(theTarget,title,null,true,null,fields,toggling);
17064 // Create a button for a tag with a popup listing all the tiddlers that it tags
17065 function createTagButton(place,tag,excludeTiddler)
17067 var theTag = createTiddlyButton(place,tag,config.views.wikified.tag.tooltip.format([tag]),onClickTag);
17068 theTag.setAttribute("tag",tag);
17070 theTag.setAttribute("tiddler",excludeTiddler);
17074 // Event handler for clicking on a tiddler tag
17075 function onClickTag(e)
17077 if(!e) var e = window.event;
17078 var theTarget = resolveTarget(e);
17079 var popup = Popup.create(this);
17080 var tag = this.getAttribute("tag");
17081 var title = this.getAttribute("tiddler");
17083 var tagged = store.getTaggedTiddlers(tag);
17086 for(r=0;r<tagged.length;r++) {
17087 if(tagged[r].title != title)
17088 titles.push(tagged[r].title);
17090 var lingo = config.views.wikified.tag;
17091 if(titles.length > 0) {
17092 var openAll = createTiddlyButton(createTiddlyElement(popup,"li"),lingo.openAllText.format([tag]),lingo.openAllTooltip,onClickTagOpenAll);
17093 openAll.setAttribute("tag",tag);
17094 createTiddlyElement(createTiddlyElement(popup,"li",null,"listBreak"),"div");
17095 for(r=0; r<titles.length; r++) {
17096 createTiddlyLink(createTiddlyElement(popup,"li"),titles[r],true);
17099 createTiddlyText(createTiddlyElement(popup,"li",null,"disabled"),lingo.popupNone.format([tag]));
17101 createTiddlyElement(createTiddlyElement(popup,"li",null,"listBreak"),"div");
17102 var h = createTiddlyLink(createTiddlyElement(popup,"li"),tag,false);
17103 createTiddlyText(h,lingo.openTag.format([tag]));
17106 e.cancelBubble = true;
17107 if(e.stopPropagation) e.stopPropagation();
17111 // Event handler for 'open all' on a tiddler popup
17112 function onClickTagOpenAll(e)
17114 if(!e) var e = window.event;
17115 var tag = this.getAttribute("tag");
17116 var tagged = store.getTaggedTiddlers(tag);
17118 for(var t=0; t<tagged.length; t++)
17119 titles.push(tagged[t].title);
17120 story.displayTiddlers(this,titles);
17124 function onClickError(e)
17126 if(!e) var e = window.event;
17127 var popup = Popup.create(this);
17128 var lines = this.getAttribute("errorText").split("\n");
17129 for(var t=0; t<lines.length; t++)
17130 createTiddlyElement(popup,"li",null,null,lines[t]);
17132 e.cancelBubble = true;
17133 if(e.stopPropagation) e.stopPropagation();
17137 function createTiddlyDropDown(place,onchange,options,defaultValue)
17139 var sel = createTiddlyElement(place,"select");
17140 sel.onchange = onchange;
17141 for(var t=0; t<options.length; t++) {
17142 var e = createTiddlyElement(sel,"option",null,null,options[t].caption);
17143 e.value = options[t].name;
17144 if(options[t].name == defaultValue)
17150 function createTiddlyPopup(place,caption,tooltip,tiddler)
17153 createTiddlyLink(place,caption,true);
17154 var btn = createTiddlyButton(place,glyph("downArrow"),tooltip,onClickTiddlyPopup,"tiddlerPopupButton");
17155 btn.tiddler = tiddler;
17157 createTiddlyText(place,caption);
17161 function onClickTiddlyPopup(e)
17163 if(!e) var e = window.event;
17164 var tiddler = this.tiddler;
17166 var popup = Popup.create(this,"div","popupTiddler");
17167 wikify(tiddler.text,popup,null,tiddler);
17170 if(e) e.cancelBubble = true;
17171 if(e && e.stopPropagation) e.stopPropagation();
17175 function createTiddlyError(place,title,text)
17177 var btn = createTiddlyButton(place,title,null,onClickError,"errorButton");
17178 if(text) btn.setAttribute("errorText",text);
17181 function merge(dst,src,preserveExisting)
17184 if(!preserveExisting || dst[p] === undefined)
17190 // Returns a string containing the description of an exception, optionally prepended by a message
17191 function exceptionText(e,message)
17193 var s = e.description ? e.description : e.toString();
17194 return message ? "%0:\n%1".format([message,s]) : s;
17197 // Displays an alert of an exception description with optional message
17198 function showException(e,message)
17200 alert(exceptionText(e,message));
17203 function alertAndThrow(m)
17209 function glyph(name)
17211 var g = config.glyphs;
17212 var b = g.currBrowser;
17215 while(!g.browsers[b]() && b < g.browsers.length-1)
17221 return g.codes[name][b];
17224 //- Animation engine
17227 function Animator()
17229 this.running = 0; // Incremented at start of each animation, decremented afterwards. If zero, the interval timer is disabled
17230 this.timerID = 0; // ID of the timer used for animating
17231 this.animations = []; // List of animations in progress
17235 // Start animation engine
17236 Animator.prototype.startAnimating = function() // Variable number of arguments
17238 for(var t=0; t<arguments.length; t++)
17239 this.animations.push(arguments[t]);
17240 if(this.running == 0) {
17242 this.timerID = window.setInterval(function() {me.doAnimate(me);},10);
17244 this.running += arguments.length;
17247 // Perform an animation engine tick, calling each of the known animation modules
17248 Animator.prototype.doAnimate = function(me)
17251 while(a < me.animations.length) {
17252 var animation = me.animations[a];
17253 if(animation.tick()) {
17256 me.animations.splice(a,1);
17257 if(--me.running == 0)
17258 window.clearInterval(me.timerID);
17263 // Map a 0..1 value to 0..1, but slow down at the start and end
17264 Animator.slowInSlowOut = function(progress)
17266 return(1-((Math.cos(progress * Math.PI)+1)/2));
17270 //-- Morpher animation
17273 // Animate a set of properties of an element
17274 function Morpher(element,duration,properties,callback)
17276 this.element = element;
17277 this.duration = duration;
17278 this.properties = properties;
17279 this.startTime = new Date();
17280 this.endTime = Number(this.startTime) + duration;
17281 this.callback = callback;
17286 Morpher.prototype.assignStyle = function(element,style,value)
17289 case "-tw-vertScroll":
17290 window.scrollTo(findScrollX(),value);
17292 case "-tw-horizScroll":
17293 window.scrollTo(value,findScrollY());
17296 element.style[style] = value;
17301 Morpher.prototype.stop = function()
17303 for(var t=0; t<this.properties.length; t++) {
17304 var p = this.properties[t];
17305 if(p.atEnd !== undefined) {
17306 this.assignStyle(this.element,p.style,p.atEnd);
17310 this.callback(this.element,this.properties);
17313 Morpher.prototype.tick = function()
17315 var currTime = Number(new Date());
17316 progress = Animator.slowInSlowOut(Math.min(1,(currTime-this.startTime)/this.duration));
17317 for(var t=0; t<this.properties.length; t++) {
17318 var p = this.properties[t];
17319 if(p.start !== undefined && p.end !== undefined) {
17320 var template = p.template ? p.template : "%0";
17324 var v = p.start + (p.end-p.start) * progress;
17325 this.assignStyle(this.element,p.style,template.format([v]));
17332 if(currTime >= this.endTime) {
17340 //-- Zoomer animation
17343 function Zoomer(text,startElement,targetElement,unused)
17345 var e = createTiddlyElement(document.body,"div",null,"zoomer");
17346 createTiddlyElement(e,"div",null,null,text);
17347 var winWidth = findWindowWidth();
17348 var winHeight = findWindowHeight();
17350 {style: 'left', start: findPosX(startElement), end: findPosX(targetElement), template: '%0px'},
17351 {style: 'top', start: findPosY(startElement), end: findPosY(targetElement), template: '%0px'},
17352 {style: 'width', start: Math.min(startElement.scrollWidth,winWidth), end: Math.min(targetElement.scrollWidth,winWidth), template: '%0px', atEnd: 'auto'},
17353 {style: 'height', start: Math.min(startElement.scrollHeight,winHeight), end: Math.min(targetElement.scrollHeight,winHeight), template: '%0px', atEnd: 'auto'},
17354 {style: 'fontSize', start: 8, end: 24, template: '%0pt'}
17356 var c = function(element,properties) {removeNode(element);};
17357 return new Morpher(e,config.animDuration,p,c);
17361 //-- Scroller animation
17364 function Scroller(targetElement,unused)
17367 {style: '-tw-vertScroll', start: findScrollY(), end: ensureVisible(targetElement)}
17369 return new Morpher(targetElement,config.animDuration,p);
17373 //-- Slider animation
17376 // deleteMode - "none", "all" [delete target element and it's children], [only] "children" [but not the target element]
17377 function Slider(element,opening,unused,deleteMode)
17379 element.style.overflow = 'hidden';
17381 element.style.height = '0px'; // Resolves a Firefox flashing bug
17382 element.style.display = 'block';
17383 var left = findPosX(element);
17384 var width = element.scrollWidth;
17385 var height = element.scrollHeight;
17386 var winWidth = findWindowWidth();
17390 p.push({style: 'height', start: 0, end: height, template: '%0px', atEnd: 'auto'});
17391 p.push({style: 'opacity', start: 0, end: 1, template: '%0'});
17392 p.push({style: 'filter', start: 0, end: 100, template: 'alpha(opacity:%0)'});
17394 p.push({style: 'height', start: height, end: 0, template: '%0px'});
17395 p.push({style: 'display', atEnd: 'none'});
17396 p.push({style: 'opacity', start: 1, end: 0, template: '%0'});
17397 p.push({style: 'filter', start: 100, end: 0, template: 'alpha(opacity:%0)'});
17398 switch(deleteMode) {
17400 c = function(element,properties) {removeNode(element);};
17403 c = function(element,properties) {removeChildren(element);};
17407 return new Morpher(element,config.animDuration,p,c);
17415 stack: [] // Array of objects with members root: and popup:
17418 Popup.create = function(root,elem,theClass)
17421 var popup = createTiddlyElement(document.body,elem ? elem : "ol","popup",theClass ? theClass : "popup");
17422 Popup.stack.push({root: root, popup: popup});
17426 Popup.onDocumentClick = function(e)
17428 if (!e) var e = window.event;
17429 var target = resolveTarget(e);
17430 if(e.eventPhase == undefined)
17432 else if(e.eventPhase == Event.BUBBLING_PHASE || e.eventPhase == Event.AT_TARGET)
17437 Popup.show = function(unused1,unused2)
17439 var curr = Popup.stack[Popup.stack.length-1];
17440 this.place(curr.root,curr.popup);
17441 addClass(curr.root,"highlight");
17442 if(config.options.chkAnimate && anim && typeof Scroller == "function")
17443 anim.startAnimating(new Scroller(curr.popup));
17445 window.scrollTo(0,ensureVisible(curr.popup));
17448 Popup.place = function(root,popup,offset)
17450 if(!offset) var offset = {x:0, y:0};
17451 var rootLeft = findPosX(root);
17452 var rootTop = findPosY(root);
17453 var rootHeight = root.offsetHeight;
17454 var popupLeft = rootLeft + offset.x;
17455 var popupTop = rootTop + rootHeight + offset.y;
17456 var winWidth = findWindowWidth();
17457 if(popup.offsetWidth > winWidth*0.75)
17458 popup.style.width = winWidth*0.75 + "px";
17459 var popupWidth = popup.offsetWidth;
17460 if(popupLeft + popupWidth > winWidth)
17461 popupLeft = winWidth - popupWidth;
17462 popup.style.left = popupLeft + "px";
17463 popup.style.top = popupTop + "px";
17464 popup.style.display = "block";
17467 Popup.remove = function()
17469 if(Popup.stack.length > 0) {
17470 Popup.removeFrom(0);
17474 Popup.removeFrom = function(from)
17476 for(var t=Popup.stack.length-1; t>=from; t--) {
17477 var p = Popup.stack[t];
17478 removeClass(p.root,"highlight");
17479 removeNode(p.popup);
17481 Popup.stack = Popup.stack.slice(0,from);
17485 //-- Wizard support
17488 function Wizard(elem)
17491 this.formElem = findRelated(elem,"wizard","className");
17492 this.bodyElem = findRelated(this.formElem.firstChild,"wizardBody","className","nextSibling");
17493 this.footElem = findRelated(this.formElem.firstChild,"wizardFooter","className","nextSibling");
17495 this.formElem = null;
17496 this.bodyElem = null;
17497 this.footElem = null;
17501 Wizard.prototype.setValue = function(name,value)
17504 this.formElem[name] = value;
17507 Wizard.prototype.getValue = function(name)
17509 return this.formElem ? this.formElem[name] : null;
17512 Wizard.prototype.createWizard = function(place,title)
17514 this.formElem = createTiddlyElement(place,"form",null,"wizard");
17515 createTiddlyElement(this.formElem,"h1",null,null,title);
17516 this.bodyElem = createTiddlyElement(this.formElem,"div",null,"wizardBody");
17517 this.footElem = createTiddlyElement(this.formElem,"div",null,"wizardFooter");
17520 Wizard.prototype.clear = function()
17522 removeChildren(this.bodyElem);
17525 Wizard.prototype.setButtons = function(buttonInfo,status)
17527 removeChildren(this.footElem);
17528 for(var t=0; t<buttonInfo.length; t++) {
17529 createTiddlyButton(this.footElem,buttonInfo[t].caption,buttonInfo[t].tooltip,buttonInfo[t].onClick);
17530 insertSpacer(this.footElem);
17532 if(typeof status == "string") {
17533 createTiddlyElement(this.footElem,"span",null,"status",status);
17537 Wizard.prototype.addStep = function(stepTitle,html)
17539 removeChildren(this.bodyElem);
17540 var w = createTiddlyElement(this.bodyElem,"div");
17541 createTiddlyElement(w,"h2",null,null,stepTitle);
17542 var step = createTiddlyElement(w,"div",null,"wizardStep");
17543 step.innerHTML = html;
17544 applyHtmlMacros(step,tiddler);
17547 Wizard.prototype.getElement = function(name)
17549 return this.formElem.elements[name];
17553 //-- ListView gadget
17558 // Create a listview
17559 ListView.create = function(place,listObject,listTemplate,callback,className)
17561 var table = createTiddlyElement(place,"table",null,className ? className : "listView twtable");
17562 var thead = createTiddlyElement(table,"thead");
17563 var r = createTiddlyElement(thead,"tr");
17564 for(var t=0; t<listTemplate.columns.length; t++) {
17565 var columnTemplate = listTemplate.columns[t];
17566 var c = createTiddlyElement(r,"th");
17567 var colType = ListView.columnTypes[columnTemplate.type];
17568 if(colType && colType.createHeader)
17569 colType.createHeader(c,columnTemplate,t);
17571 var tbody = createTiddlyElement(table,"tbody");
17572 for(var rc=0; rc<listObject.length; rc++) {
17573 rowObject = listObject[rc];
17574 r = createTiddlyElement(tbody,"tr");
17575 for(c=0; c<listTemplate.rowClasses.length; c++) {
17576 if(rowObject[listTemplate.rowClasses[c].field])
17577 addClass(r,listTemplate.rowClasses[c].className);
17579 rowObject.rowElement = r;
17580 rowObject.colElements = {};
17581 for(var cc=0; cc<listTemplate.columns.length; cc++) {
17582 c = createTiddlyElement(r,"td");
17583 columnTemplate = listTemplate.columns[cc];
17584 var field = columnTemplate.field;
17585 colType = ListView.columnTypes[columnTemplate.type];
17586 if(colType && colType.createItem)
17587 colType.createItem(c,rowObject,field,columnTemplate,cc,rc);
17588 rowObject.colElements[field] = c;
17591 if(callback && listTemplate.actions)
17592 createTiddlyDropDown(place,ListView.getCommandHandler(callback),listTemplate.actions);
17593 if(callback && listTemplate.buttons) {
17594 for(t=0; t<listTemplate.buttons.length; t++) {
17595 var a = listTemplate.buttons[t];
17596 if(a && a.name != "")
17597 createTiddlyButton(place,a.caption,null,ListView.getCommandHandler(callback,a.name,a.allowEmptySelection));
17603 ListView.getCommandHandler = function(callback,name,allowEmptySelection)
17605 return function(e) {
17606 var view = findRelated(this,"TABLE",null,"previousSibling");
17608 ListView.forEachSelector(view,function(e,rowName) {
17610 tiddlers.push(rowName);
17612 if(tiddlers.length == 0 && !allowEmptySelection) {
17613 alert(config.messages.nothingSelected);
17615 if(this.nodeName.toLowerCase() == "select") {
17616 callback(view,this.value,tiddlers);
17617 this.selectedIndex = 0;
17619 callback(view,name,tiddlers);
17625 // Invoke a callback for each selector checkbox in the listview
17626 ListView.forEachSelector = function(view,callback)
17628 var checkboxes = view.getElementsByTagName("input");
17629 var hadOne = false;
17630 for(var t=0; t<checkboxes.length; t++) {
17631 var cb = checkboxes[t];
17632 if(cb.getAttribute("type") == "checkbox") {
17633 var rn = cb.getAttribute("rowName");
17643 ListView.getSelectedRows = function(view)
17646 ListView.forEachSelector(view,function(e,rowName) {
17648 rowNames.push(rowName);
17653 ListView.columnTypes = {};
17655 ListView.columnTypes.String = {
17656 createHeader: function(place,columnTemplate,col)
17658 createTiddlyText(place,columnTemplate.title);
17660 createItem: function(place,listObject,field,columnTemplate,col,row)
17662 var v = listObject[field];
17664 createTiddlyText(place,v);
17668 ListView.columnTypes.WikiText = {
17669 createHeader: ListView.columnTypes.String.createHeader,
17670 createItem: function(place,listObject,field,columnTemplate,col,row)
17672 var v = listObject[field];
17674 wikify(v,place,null,null);
17678 ListView.columnTypes.Tiddler = {
17679 createHeader: ListView.columnTypes.String.createHeader,
17680 createItem: function(place,listObject,field,columnTemplate,col,row)
17682 var v = listObject[field];
17683 if(v != undefined && v.title)
17684 createTiddlyPopup(place,v.title,config.messages.listView.tiddlerTooltip,v);
17688 ListView.columnTypes.Size = {
17689 createHeader: ListView.columnTypes.String.createHeader,
17690 createItem: function(place,listObject,field,columnTemplate,col,row)
17692 var v = listObject[field];
17693 if(v != undefined) {
17695 while(t<config.messages.sizeTemplates.length-1 && v<config.messages.sizeTemplates[t].unit)
17697 createTiddlyText(place,config.messages.sizeTemplates[t].template.format([Math.round(v/config.messages.sizeTemplates[t].unit)]));
17702 ListView.columnTypes.Link = {
17703 createHeader: ListView.columnTypes.String.createHeader,
17704 createItem: function(place,listObject,field,columnTemplate,col,row)
17706 var v = listObject[field];
17707 var c = columnTemplate.text;
17709 createTiddlyText(createExternalLink(place,v),c ? c : v);
17713 ListView.columnTypes.Date = {
17714 createHeader: ListView.columnTypes.String.createHeader,
17715 createItem: function(place,listObject,field,columnTemplate,col,row)
17717 var v = listObject[field];
17719 createTiddlyText(place,v.formatString(columnTemplate.dateFormat));
17723 ListView.columnTypes.StringList = {
17724 createHeader: ListView.columnTypes.String.createHeader,
17725 createItem: function(place,listObject,field,columnTemplate,col,row)
17727 var v = listObject[field];
17728 if(v != undefined) {
17729 for(var t=0; t<v.length; t++) {
17730 createTiddlyText(place,v[t]);
17731 createTiddlyElement(place,"br");
17737 ListView.columnTypes.Selector = {
17738 createHeader: function(place,columnTemplate,col)
17740 createTiddlyCheckbox(place,null,false,this.onHeaderChange);
17742 createItem: function(place,listObject,field,columnTemplate,col,row)
17744 var e = createTiddlyCheckbox(place,null,listObject[field],null);
17745 e.setAttribute("rowName",listObject[columnTemplate.rowName]);
17747 onHeaderChange: function(e)
17749 var state = this.checked;
17750 var view = findRelated(this,"TABLE");
17753 ListView.forEachSelector(view,function(e,rowName) {
17759 ListView.columnTypes.Tags = {
17760 createHeader: ListView.columnTypes.String.createHeader,
17761 createItem: function(place,listObject,field,columnTemplate,col,row)
17763 var tags = listObject[field];
17764 createTiddlyText(place,String.encodeTiddlyLinkList(tags));
17768 ListView.columnTypes.Boolean = {
17769 createHeader: ListView.columnTypes.String.createHeader,
17770 createItem: function(place,listObject,field,columnTemplate,col,row)
17772 if(listObject[field] == true)
17773 createTiddlyText(place,columnTemplate.trueText);
17774 if(listObject[field] == false)
17775 createTiddlyText(place,columnTemplate.falseText);
17779 ListView.columnTypes.TagCheckbox = {
17780 createHeader: ListView.columnTypes.String.createHeader,
17781 createItem: function(place,listObject,field,columnTemplate,col,row)
17783 var e = createTiddlyCheckbox(place,null,listObject[field],this.onChange);
17784 e.setAttribute("tiddler",listObject.title);
17785 e.setAttribute("tag",columnTemplate.tag);
17787 onChange : function(e)
17789 var tag = this.getAttribute("tag");
17790 var tiddler = this.getAttribute("tiddler");
17791 store.setTiddlerTag(tiddler,this.checked,tag);
17795 ListView.columnTypes.TiddlerLink = {
17796 createHeader: ListView.columnTypes.String.createHeader,
17797 createItem: function(place,listObject,field,columnTemplate,col,row)
17799 var v = listObject[field];
17800 if(v != undefined) {
17801 var link = createTiddlyLink(place,listObject[columnTemplate.tiddlerLink],false,null);
17802 createTiddlyText(link,listObject[field]);
17808 //-- Augmented methods for the JavaScript Number(), Array(), String() and Date() objects
17811 // Clamp a number to a range
17812 Number.prototype.clamp = function(min,max)
17822 // Add indexOf function if browser does not support it
17823 if(!Array.indexOf) {
17824 Array.prototype.indexOf = function(item,from)
17828 for(var i=from; i<this.length; i++) {
17829 if(this[i] === item)
17835 // Find an entry in a given field of the members of an array
17836 Array.prototype.findByField = function(field,value)
17838 for(var t=0; t<this.length; t++) {
17839 if(this[t][field] == value)
17845 // Return whether an entry exists in an array
17846 Array.prototype.contains = function(item)
17848 return this.indexOf(item) != -1;
17851 // Adds, removes or toggles a particular value within an array
17852 // value - value to add
17853 // mode - +1 to add value, -1 to remove value, 0 to toggle it
17854 Array.prototype.setItem = function(value,mode)
17856 var p = this.indexOf(value);
17858 mode = (p == -1) ? +1 : -1;
17862 } else if(mode == -1) {
17868 // Return whether one of a list of values exists in an array
17869 Array.prototype.containsAny = function(items)
17871 for(var i=0; i<items.length; i++) {
17872 if (this.indexOf(items[i]) != -1)
17878 // Return whether all of a list of values exists in an array
17879 Array.prototype.containsAll = function(items)
17881 for (var i = 0; i<items.length; i++) {
17882 if (this.indexOf(items[i]) == -1)
17888 // Push a new value into an array only if it is not already present in the array. If the optional unique parameter is false, it reverts to a normal push
17889 Array.prototype.pushUnique = function(item,unique)
17891 if(unique === false) {
17894 if(this.indexOf(item) == -1)
17899 Array.prototype.remove = function(item)
17901 var p = this.indexOf(item);
17906 // Get characters from the right end of a string
17907 String.prototype.right = function(n)
17909 return n < this.length ? this.slice(this.length-n) : this;
17912 // Trim whitespace from both ends of a string
17913 String.prototype.trim = function()
17915 return this.replace(/^\s*|\s*$/g,"");
17918 // Convert a string from a CSS style property name to a JavaScript style name ("background-color" -> "backgroundColor")
17919 String.prototype.unDash = function()
17921 var s = this.split("-");
17923 for(var t=1; t<s.length; t++)
17924 s[t] = s[t].substr(0,1).toUpperCase() + s[t].substr(1);
17929 // Substitute substrings from an array into a format string that includes '%1'-type specifiers
17930 String.prototype.format = function(substrings)
17932 var subRegExp = /(?:%(\d+))/mg;
17936 var match = subRegExp.exec(this);
17937 if(match && match[1]) {
17938 if(match.index > currPos)
17939 r.push(this.substring(currPos,match.index));
17940 r.push(substrings[parseInt(match[1])]);
17941 currPos = subRegExp.lastIndex;
17944 if(currPos < this.length)
17945 r.push(this.substring(currPos,this.length));
17949 // Escape any special RegExp characters with that character preceded by a backslash
17950 String.prototype.escapeRegExp = function()
17952 var s = "\\^$*+?()=!|,{}[].";
17954 for(var t=0; t<s.length; t++)
17955 c = c.replace(new RegExp("\\" + s.substr(t,1),"g"),"\\" + s.substr(t,1));
17959 // Convert "\" to "\s", newlines to "\n" (and remove carriage returns)
17960 String.prototype.escapeLineBreaks = function()
17962 return this.replace(/\\/mg,"\\s").replace(/\n/mg,"\\n").replace(/\r/mg,"");
17965 // Convert "\n" to newlines, "\b" to " ", "\s" to "\" (and remove carriage returns)
17966 String.prototype.unescapeLineBreaks = function()
17968 return this.replace(/\\n/mg,"\n").replace(/\\b/mg," ").replace(/\\s/mg,"\\").replace(/\r/mg,"");
17971 // Convert & to "&", < to "<", > to ">" and " to """
17972 String.prototype.htmlEncode = function()
17974 return this.replace(/&/mg,"&").replace(/</mg,"<").replace(/>/mg,">").replace(/\"/mg,""");
17977 // Convert "&" to &, "<" to <, ">" to > and """ to "
17978 String.prototype.htmlDecode = function()
17980 return this.replace(/</mg,"<").replace(/>/mg,">").replace(/"/mg,"\"").replace(/&/mg,"&");
17983 // Convert a string to it's JSON representation by encoding control characters, double quotes and backslash. See json.org
17984 String.prototype.toJSONString = function()
17995 var replaceFn = function(a,b) {
17999 c = b.charCodeAt();
18000 return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
18002 if(/["\\\x00-\x1f]/.test(this))
18003 return '"' + this.replace(/([\x00-\x1f\\"])/g,replaceFn) + '"';
18004 return '"' + this + '"';
18007 // Parse a space-separated string of name:value parameters
18008 // The result is an array of objects:
18009 // result[0] = object with a member for each parameter name, value of that member being an array of values
18010 // result[1..n] = one object for each parameter, with 'name' and 'value' members
18011 String.prototype.parseParams = function(defaultName,defaultValue,allowEval,noNames,cascadeDefaults)
18013 var parseToken = function(match,p) {
18015 if(match[p]) // Double quoted
18017 else if(match[p+1]) // Single quoted
18019 else if(match[p+2]) // Double-square-bracket quoted
18021 else if(match[p+3]) // Double-brace quoted
18025 n = window.eval(n);
18027 throw "Unable to evaluate {{" + match[p+3] + "}}: " + exceptionText(ex);
18029 else if(match[p+4]) // Unquoted
18031 else if(match[p+5]) // empty quote
18036 var dblQuote = "(?:\"((?:(?:\\\\\")|[^\"])+)\")";
18037 var sngQuote = "(?:'((?:(?:\\\\\')|[^'])+)')";
18038 var dblSquare = "(?:\\[\\[((?:\\s|\\S)*?)\\]\\])";
18039 var dblBrace = "(?:\\{\\{((?:\\s|\\S)*?)\\}\\})";
18040 var unQuoted = noNames ? "([^\"'\\s]\\S*)" : "([^\"':\\s][^\\s:]*)";
18041 var emptyQuote = "((?:\"\")|(?:''))";
18042 var skipSpace = "(?:\\s*)";
18043 var token = "(?:" + dblQuote + "|" + sngQuote + "|" + dblSquare + "|" + dblBrace + "|" + unQuoted + "|" + emptyQuote + ")";
18044 var re = noNames ? new RegExp(token,"mg") : new RegExp(skipSpace + token + skipSpace + "(?:(\\:)" + skipSpace + token + ")?","mg");
18047 var match = re.exec(this);
18049 var n = parseToken(match,1);
18051 r.push({name:"",value:n});
18053 var v = parseToken(match,8);
18054 if(v == null && defaultName) {
18057 } else if(v == null && defaultValue) {
18060 r.push({name:n,value:v});
18061 if(cascadeDefaults) {
18068 // Summarise parameters into first element
18069 for(var t=1; t<r.length; t++) {
18070 if(r[0][r[t].name])
18071 r[0][r[t].name].push(r[t].value);
18073 r[0][r[t].name] = [r[t].value];
18078 // Process a string list of macro parameters into an array. Parameters can be quoted with "", '',
18079 // [[]], {{ }} or left unquoted (and therefore space-separated). Double-braces {{}} results in
18080 // an *evaluated* parameter: e.g. {{config.options.txtUserName}} results in the current user's name.
18081 String.prototype.readMacroParams = function()
18083 var p = this.parseParams("list",null,true,true);
18085 for(var t=1; t<p.length; t++)
18086 n.push(p[t].value);
18090 // Process a string list of unique tiddler names into an array. Tiddler names that have spaces in them must be [[bracketed]]
18091 String.prototype.readBracketedList = function(unique)
18093 var p = this.parseParams("list",null,false,true);
18095 for(var t=1; t<p.length; t++)
18096 n.pushUnique(p[t].value,unique);
18100 // Returns array with start and end index of chunk between given start and end marker, or undefined.
18101 String.prototype.getChunkRange = function(start,end)
18103 var s = this.indexOf(start);
18106 var e = this.indexOf(end,s);
18112 // Replace a chunk of a string given start and end markers
18113 String.prototype.replaceChunk = function(start,end,sub)
18115 var r = this.getChunkRange(start,end);
18116 return r ? this.substring(0,r[0]) + sub + this.substring(r[1]) : this;
18119 // Returns a chunk of a string between start and end markers, or undefined
18120 String.prototype.getChunk = function(start,end)
18122 var r = this.getChunkRange(start,end);
18124 return this.substring(r[0],r[1]);
18128 // Static method to bracket a string with double square brackets if it contains a space
18129 String.encodeTiddlyLink = function(title)
18131 return title.indexOf(" ") == -1 ? title : "[[" + title + "]]";
18134 // Static method to encodeTiddlyLink for every item in an array and join them with spaces
18135 String.encodeTiddlyLinkList = function(list)
18139 for(var t=0; t<list.length; t++)
18140 results.push(String.encodeTiddlyLink(list[t]));
18141 return results.join(" ");
18147 // Convert a string as a sequence of name:"value" pairs into a hashmap
18148 String.prototype.decodeHashMap = function()
18150 var fields = this.parseParams("anon","",false);
18152 for(var t=1; t<fields.length; t++)
18153 r[fields[t].name] = fields[t].value;
18157 // Static method to encode a hashmap into a name:"value"... string
18158 String.encodeHashMap = function(hashmap)
18161 for(var t in hashmap)
18162 r.push(t + ':"' + hashmap[t] + '"');
18163 return r.join(" ");
18166 // Static method to left-pad a string with 0s to a certain width
18167 String.zeroPad = function(n,d)
18169 var s = n.toString();
18171 s = "000000000000000000000000000".substr(0,d-s.length) + s;
18175 String.prototype.startsWith = function(prefix)
18177 return !prefix || this.substring(0,prefix.length) == prefix;
18180 // Returns the first value of the given named parameter.
18181 function getParam(params,name,defaultValue)
18184 return defaultValue;
18185 var p = params[0][name];
18186 return p ? p[0] : defaultValue;
18189 // Returns the first value of the given boolean named parameter.
18190 function getFlag(params,name,defaultValue)
18192 return !!getParam(params,name,defaultValue);
18195 // Substitute date components into a string
18196 Date.prototype.formatString = function(template)
18198 var t = template.replace(/0hh12/g,String.zeroPad(this.getHours12(),2));
18199 t = t.replace(/hh12/g,this.getHours12());
18200 t = t.replace(/0hh/g,String.zeroPad(this.getHours(),2));
18201 t = t.replace(/hh/g,this.getHours());
18202 t = t.replace(/mmm/g,config.messages.dates.shortMonths[this.getMonth()]);
18203 t = t.replace(/0mm/g,String.zeroPad(this.getMinutes(),2));
18204 t = t.replace(/mm/g,this.getMinutes());
18205 t = t.replace(/0ss/g,String.zeroPad(this.getSeconds(),2));
18206 t = t.replace(/ss/g,this.getSeconds());
18207 t = t.replace(/[ap]m/g,this.getAmPm().toLowerCase());
18208 t = t.replace(/[AP]M/g,this.getAmPm().toUpperCase());
18209 t = t.replace(/wYYYY/g,this.getYearForWeekNo());
18210 t = t.replace(/wYY/g,String.zeroPad(this.getYearForWeekNo()-2000,2));
18211 t = t.replace(/YYYY/g,this.getFullYear());
18212 t = t.replace(/YY/g,String.zeroPad(this.getFullYear()-2000,2));
18213 t = t.replace(/MMM/g,config.messages.dates.months[this.getMonth()]);
18214 t = t.replace(/0MM/g,String.zeroPad(this.getMonth()+1,2));
18215 t = t.replace(/MM/g,this.getMonth()+1);
18216 t = t.replace(/0WW/g,String.zeroPad(this.getWeek(),2));
18217 t = t.replace(/WW/g,this.getWeek());
18218 t = t.replace(/DDD/g,config.messages.dates.days[this.getDay()]);
18219 t = t.replace(/ddd/g,config.messages.dates.shortDays[this.getDay()]);
18220 t = t.replace(/0DD/g,String.zeroPad(this.getDate(),2));
18221 t = t.replace(/DDth/g,this.getDate()+this.daySuffix());
18222 t = t.replace(/DD/g,this.getDate());
18226 Date.prototype.getWeek = function()
18228 var dt = new Date(this.getTime());
18229 var d = dt.getDay();
18230 if (d==0) d=7;// JavaScript Sun=0, ISO Sun=7
18231 dt.setTime(dt.getTime()+(4-d)*86400000);// shift day to Thurs of same week to calculate weekNo
18232 var n = Math.floor((dt.getTime()-new Date(dt.getFullYear(),0,1)+3600000)/86400000);
18233 return Math.floor(n/7)+1;
18236 Date.prototype.getYearForWeekNo = function()
18238 var dt = new Date(this.getTime());
18239 var d = dt.getDay();
18240 if (d==0) d=7;// JavaScript Sun=0, ISO Sun=7
18241 dt.setTime(dt.getTime()+(4-d)*86400000);// shift day to Thurs of same week
18242 return dt.getFullYear();
18245 Date.prototype.getHours12 = function()
18247 var h = this.getHours();
18248 return h > 12 ? h-12 : ( h > 0 ? h : 12 );
18251 Date.prototype.getAmPm = function()
18253 return this.getHours() >= 12 ? config.messages.dates.pm : config.messages.dates.am;
18256 Date.prototype.daySuffix = function()
18258 return config.messages.dates.daySuffixes[this.getDate()-1];
18261 // Convert a date to local YYYYMMDDHHMM string format
18262 Date.prototype.convertToLocalYYYYMMDDHHMM = function()
18264 return String.zeroPad(this.getFullYear(),4) + String.zeroPad(this.getMonth()+1,2) + String.zeroPad(this.getDate(),2) + String.zeroPad(this.getHours(),2) + String.zeroPad(this.getMinutes(),2);
18267 // Convert a date to UTC YYYYMMDDHHMM string format
18268 Date.prototype.convertToYYYYMMDDHHMM = function()
18270 return String.zeroPad(this.getUTCFullYear(),4) + String.zeroPad(this.getUTCMonth()+1,2) + String.zeroPad(this.getUTCDate(),2) + String.zeroPad(this.getUTCHours(),2) + String.zeroPad(this.getUTCMinutes(),2);
18273 // Convert a date to UTC YYYYMMDD.HHMMSSMMM string format
18274 Date.prototype.convertToYYYYMMDDHHMMSSMMM = function()
18276 return String.zeroPad(this.getUTCFullYear(),4) + String.zeroPad(this.getUTCMonth()+1,2) + String.zeroPad(this.getUTCDate(),2) + "." + String.zeroPad(this.getUTCHours(),2) + String.zeroPad(this.getUTCMinutes(),2) + String.zeroPad(this.getUTCSeconds(),2) + String.zeroPad(this.getUTCMilliseconds(),4);
18279 // Static method to create a date from a UTC YYYYMMDDHHMM format string
18280 Date.convertFromYYYYMMDDHHMM = function(d)
18282 return new Date(Date.UTC(parseInt(d.substr(0,4),10),
18283 parseInt(d.substr(4,2),10)-1,
18284 parseInt(d.substr(6,2),10),
18285 parseInt(d.substr(8,2),10),
18286 parseInt(d.substr(10,2),10),0,0));
18290 //-- Crypto functions and associated conversion routines
18293 // Crypto "namespace"
18294 function Crypto() {}
18296 // Convert a string to an array of big-endian 32-bit words
18297 Crypto.strToBe32s = function(str)
18300 var len = Math.floor(str.length/4);
18302 for(i=0, j=0; i<len; i++, j+=4) {
18303 be[i] = ((str.charCodeAt(j)&0xff) << 24)|((str.charCodeAt(j+1)&0xff) << 16)|((str.charCodeAt(j+2)&0xff) << 8)|(str.charCodeAt(j+3)&0xff);
18305 while (j<str.length) {
18306 be[j>>2] |= (str.charCodeAt(j)&0xff)<<(24-(j*8)%32);
18312 // Convert an array of big-endian 32-bit words to a string
18313 Crypto.be32sToStr = function(be)
18316 for(var i=0;i<be.length*32;i+=8)
18317 str += String.fromCharCode((be[i>>5]>>>(24-i%32)) & 0xff);
18321 // Convert an array of big-endian 32-bit words to a hex string
18322 Crypto.be32sToHex = function(be)
18324 var hex = "0123456789ABCDEF";
18326 for(var i=0;i<be.length*4;i++)
18327 str += hex.charAt((be[i>>2]>>((3-i%4)*8+4))&0xF) + hex.charAt((be[i>>2]>>((3-i%4)*8))&0xF);
18331 // Return, in hex, the SHA-1 hash of a string
18332 Crypto.hexSha1Str = function(str)
18334 return Crypto.be32sToHex(Crypto.sha1Str(str));
18337 // Return the SHA-1 hash of a string
18338 Crypto.sha1Str = function(str)
18340 return Crypto.sha1(Crypto.strToBe32s(str),str.length);
18343 // Calculate the SHA-1 hash of an array of blen bytes of big-endian 32-bit words
18344 Crypto.sha1 = function(x,blen)
18346 // Add 32-bit integers, wrapping at 32 bits
18347 add32 = function(a,b)
18349 var lsw = (a&0xFFFF)+(b&0xFFFF);
18350 var msw = (a>>16)+(b>>16)+(lsw>>16);
18351 return (msw<<16)|(lsw&0xFFFF);
18353 // Add five 32-bit integers, wrapping at 32 bits
18354 add32x5 = function(a,b,c,d,e)
18356 var lsw = (a&0xFFFF)+(b&0xFFFF)+(c&0xFFFF)+(d&0xFFFF)+(e&0xFFFF);
18357 var msw = (a>>16)+(b>>16)+(c>>16)+(d>>16)+(e>>16)+(lsw>>16);
18358 return (msw<<16)|(lsw&0xFFFF);
18360 // Bitwise rotate left a 32-bit integer by 1 bit
18361 rol32 = function(n)
18363 return (n>>>31)|(n<<1);
18367 // Append padding so length in bits is 448 mod 512
18368 x[len>>5] |= 0x80 << (24-len%32);
18370 x[((len+64>>9)<<4)+15] = len;
18373 var k1 = 0x5A827999;
18374 var k2 = 0x6ED9EBA1;
18375 var k3 = 0x8F1BBCDC;
18376 var k4 = 0xCA62C1D6;
18378 var h0 = 0x67452301;
18379 var h1 = 0xEFCDAB89;
18380 var h2 = 0x98BADCFE;
18381 var h3 = 0x10325476;
18382 var h4 = 0xC3D2E1F0;
18384 for(var i=0;i<x.length;i+=16) {
18391 for(j = 0;j<16;j++) {
18393 t = add32x5(e,(a>>>27)|(a<<5),d^(b&(c^d)),w[j],k1);
18394 e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
18396 for(j=16;j<20;j++) {
18397 w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
18398 t = add32x5(e,(a>>>27)|(a<<5),d^(b&(c^d)),w[j],k1);
18399 e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
18401 for(j=20;j<40;j++) {
18402 w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
18403 t = add32x5(e,(a>>>27)|(a<<5),b^c^d,w[j],k2);
18404 e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
18406 for(j=40;j<60;j++) {
18407 w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
18408 t = add32x5(e,(a>>>27)|(a<<5),(b&c)|(d&(b|c)),w[j],k3);
18409 e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
18411 for(j=60;j<80;j++) {
18412 w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
18413 t = add32x5(e,(a>>>27)|(a<<5),b^c^d,w[j],k4);
18414 e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
18423 return Array(h0,h1,h2,h3,h4);
18427 //-- RGB colour object
18430 // Construct an RGB colour object from a '#rrggbb', '#rgb' or 'rgb(n,n,n)' string or from separate r,g,b values
18431 function RGB(r,g,b)
18436 if(typeof r == "string") {
18437 if(r.substr(0,1) == "#") {
18438 if(r.length == 7) {
18439 this.r = parseInt(r.substr(1,2),16)/255;
18440 this.g = parseInt(r.substr(3,2),16)/255;
18441 this.b = parseInt(r.substr(5,2),16)/255;
18443 this.r = parseInt(r.substr(1,1),16)/15;
18444 this.g = parseInt(r.substr(2,1),16)/15;
18445 this.b = parseInt(r.substr(3,1),16)/15;
18448 var rgbPattern = /rgb\s*\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)/;
18449 var c = r.match(rgbPattern);
18451 this.r = parseInt(c[1],10)/255;
18452 this.g = parseInt(c[2],10)/255;
18453 this.b = parseInt(c[3],10)/255;
18464 // Mixes this colour with another in a specified proportion
18465 // c = other colour to mix
18466 // f = 0..1 where 0 is this colour and 1 is the new colour
18467 // Returns an RGB object
18468 RGB.prototype.mix = function(c,f)
18470 return new RGB(this.r + (c.r-this.r) * f,this.g + (c.g-this.g) * f,this.b + (c.b-this.b) * f);
18473 // Return an rgb colour as a #rrggbb format hex string
18474 RGB.prototype.toString = function()
18476 return "#" + ("0" + Math.floor(this.r.clamp(0,1) * 255).toString(16)).right(2) +
18477 ("0" + Math.floor(this.g.clamp(0,1) * 255).toString(16)).right(2) +
18478 ("0" + Math.floor(this.b.clamp(0,1) * 255).toString(16)).right(2);
18482 //-- DOM utilities - many derived from www.quirksmode.org
18485 function drawGradient(place,horiz,colours)
18487 for(var t=0; t<= 100; t+=2) {
18488 var bar = document.createElement("div");
18489 place.appendChild(bar);
18490 bar.style.position = "absolute";
18491 bar.style.left = horiz ? t + "%" : 0;
18492 bar.style.top = horiz ? 0 : t + "%";
18493 bar.style.width = horiz ? (101-t) + "%" : "100%";
18494 bar.style.height = horiz ? "100%" : (101-t) + "%";
18495 bar.style.zIndex = -1;
18497 var p = f*(colours.length-1);
18498 bar.style.backgroundColor = colours[Math.floor(p)].mix(colours[Math.ceil(p)],p-Math.floor(p)).toString();
18502 function createTiddlyText(theParent,theText)
18504 return theParent.appendChild(document.createTextNode(theText));
18507 function createTiddlyCheckbox(theParent,caption,checked,onChange)
18509 var cb = document.createElement("input");
18510 cb.setAttribute("type","checkbox");
18511 cb.onclick = onChange;
18512 theParent.appendChild(cb);
18513 cb.checked = checked;
18514 cb.className = "chkOptionInput";
18516 wikify(caption,theParent);
18520 function createTiddlyElement(theParent,theElement,theID,theClass,theText)
18522 var e = document.createElement(theElement);
18523 if(theClass != null)
18524 e.className = theClass;
18526 e.setAttribute("id",theID);
18527 if(theText != null)
18528 e.appendChild(document.createTextNode(theText));
18529 if(theParent != null)
18530 theParent.appendChild(e);
18534 function addEvent(obj,type,fn)
18536 if(obj.attachEvent) {
18537 obj['e'+type+fn] = fn;
18538 obj[type+fn] = function(){obj['e'+type+fn](window.event);};
18539 obj.attachEvent('on'+type,obj[type+fn]);
18541 obj.addEventListener(type,fn,false);
18545 function removeEvent(obj,type,fn)
18547 if(obj.detachEvent) {
18548 obj.detachEvent('on'+type,obj[type+fn]);
18549 obj[type+fn] = null;
18551 obj.removeEventListener(type,fn,false);
18555 function addClass(e,theClass)
18557 var currClass = e.className.split(" ");
18558 if(currClass.indexOf(theClass) == -1)
18559 e.className += " " + theClass;
18562 function removeClass(e,theClass)
18564 var currClass = e.className.split(" ");
18565 var i = currClass.indexOf(theClass);
18567 currClass.splice(i,1);
18568 i = currClass.indexOf(theClass);
18570 e.className = currClass.join(" ");
18573 function hasClass(e,theClass)
18576 if(e.className.split(" ").indexOf(theClass) != -1)
18582 // Find the closest relative with a given property value (property defaults to tagName, relative defaults to parentNode)
18583 function findRelated(e,value,name,relative)
18585 name = name ? name : "tagName";
18586 relative = relative ? relative : "parentNode";
18587 if(name == "className") {
18588 while(e && !hasClass(e,value)) {
18592 while(e && e[name] != value) {
18599 // Resolve the target object of an event
18600 function resolveTarget(e)
18605 else if(e.srcElement)
18606 obj = e.srcElement;
18607 if(obj.nodeType == 3) // defeat Safari bug
18608 obj = obj.parentNode;
18612 // Return the content of an element as plain text with no formatting
18613 function getPlainText(e)
18617 text = e.innerText;
18618 else if(e.textContent)
18619 text = e.textContent;
18623 // Get the scroll position for window.scrollTo necessary to scroll a given element into view
18624 function ensureVisible(e)
18626 var posTop = findPosY(e);
18627 var posBot = posTop + e.offsetHeight;
18628 var winTop = findScrollY();
18629 var winHeight = findWindowHeight();
18630 var winBot = winTop + winHeight;
18631 if(posTop < winTop) {
18633 } else if(posBot > winBot) {
18634 if(e.offsetHeight < winHeight)
18635 return posTop - (winHeight - e.offsetHeight);
18643 // Get the current width of the display window
18644 function findWindowWidth()
18646 return window.innerWidth ? window.innerWidth : document.documentElement.clientWidth;
18649 // Get the current height of the display window
18650 function findWindowHeight()
18652 return window.innerHeight ? window.innerHeight : document.documentElement.clientHeight;
18655 // Get the current horizontal page scroll position
18656 function findScrollX()
18658 return window.scrollX ? window.scrollX : document.documentElement.scrollLeft;
18661 // Get the current vertical page scroll position
18662 function findScrollY()
18664 return window.scrollY ? window.scrollY : document.documentElement.scrollTop;
18667 function findPosX(obj)
18670 while(obj.offsetParent) {
18671 curleft += obj.offsetLeft;
18672 obj = obj.offsetParent;
18677 function findPosY(obj)
18680 while(obj.offsetParent) {
18681 curtop += obj.offsetTop;
18682 obj = obj.offsetParent;
18687 // Blur a particular element
18688 function blurElement(e)
18690 if(e != null && e.focus && e.blur) {
18696 // Create a non-breaking space
18697 function insertSpacer(place)
18699 var e = document.createTextNode(String.fromCharCode(160));
18701 place.appendChild(e);
18705 // Remove all children of a node
18706 function removeChildren(e)
18708 while(e && e.hasChildNodes())
18709 removeNode(e.firstChild);
18712 // Remove a node and all it's children
18713 function removeNode(e)
18716 e.parentNode.removeChild(e);
18719 // Remove any event handlers or non-primitve custom attributes
18720 function scrubNode(e)
18722 var att = e.attributes;
18724 for(var t=0; t<att.length; t++) {
18725 var n = att[t].name;
18726 if(n !== 'style' && (typeof e[n] === 'function' || (typeof e[n] === 'object' && e[n] != null))) {
18734 var c = e.firstChild;
18741 // Add a stylesheet, replacing any previous custom stylesheet
18742 function setStylesheet(s,id,doc)
18745 id = "customStyleSheet";
18748 var n = doc.getElementById(id);
18749 if(doc.createStyleSheet) {
18750 // Test for IE's non-standard createStyleSheet method
18752 n.parentNode.removeChild(n);
18753 // This failed without the
18754 doc.getElementsByTagName("head")[0].insertAdjacentHTML("beforeEnd"," <style id='" + id + "'>" + s + "</style>");
18757 n.replaceChild(doc.createTextNode(s),n.firstChild);
18759 n = doc.createElement("style");
18760 n.type = "text/css";
18762 n.appendChild(doc.createTextNode(s));
18763 doc.getElementsByTagName("head")[0].appendChild(n);
18768 // Force the browser to do a document reflow when needed to workaround browser bugs
18769 function forceReflow()
18771 if(config.browser.isGecko) {
18772 setStylesheet("body {top:-1em;margin-top:1em;}");
18777 // Replace the current selection of a textarea or text input and scroll it into view
18778 function replaceSelection(e,text)
18780 if(e.setSelectionRange) {
18781 var oldpos = e.selectionStart;
18782 var isRange = e.selectionEnd > e.selectionStart;
18783 e.value = e.value.substr(0,e.selectionStart) + text + e.value.substr(e.selectionEnd);
18784 e.setSelectionRange(isRange ? oldpos : oldpos + text.length,oldpos + text.length);
18785 var linecount = e.value.split('\n').length;
18786 var thisline = e.value.substr(0,e.selectionStart).split('\n').length-1;
18787 e.scrollTop = Math.floor((thisline - e.rows / 2) * e.scrollHeight / linecount);
18788 } else if(document.selection) {
18789 var range = document.selection.createRange();
18790 if(range.parentElement() == e) {
18791 var isCollapsed = range.text == "";
18794 range.moveStart('character', -text.length);
18801 // Returns the text of the given (text) node, possibly merging subsequent text nodes
18802 function getNodeText(e)
18805 while(e && e.nodeName == "#text") {
18813 //-- LoaderBase and SaverBase
18816 function LoaderBase() {}
18818 LoaderBase.prototype.loadTiddler = function(store,node,tiddlers)
18820 var title = this.getTitle(store,node);
18822 var tiddler = store.createTiddler(title);
18823 this.internalizeTiddler(store,tiddler,title,node);
18824 tiddlers.push(tiddler);
18828 LoaderBase.prototype.loadTiddlers = function(store,nodes)
18831 for(var t = 0; t < nodes.length; t++) {
18833 this.loadTiddler(store,nodes[t],tiddlers);
18835 showException(ex,config.messages.tiddlerLoadError.format([this.getTitle(store,nodes[t])]));
18841 function SaverBase() {}
18843 SaverBase.prototype.externalize = function(store)
18846 var tiddlers = store.getTiddlers("title");
18847 for(var t = 0; t < tiddlers.length; t++)
18848 results.push(this.externalizeTiddler(store,tiddlers[t]));
18849 return results.join("\n");
18853 //-- TW21Loader (inherits from LoaderBase)
18856 function TW21Loader() {}
18858 TW21Loader.prototype = new LoaderBase();
18860 TW21Loader.prototype.getTitle = function(store,node)
18863 if(node.getAttribute) {
18864 title = node.getAttribute("title");
18866 title = node.getAttribute("tiddler");
18868 if(!title && node.id) {
18869 var lenPrefix = store.idPrefix.length;
18870 if (node.id.substr(0,lenPrefix) == store.idPrefix)
18871 title = node.id.substr(lenPrefix);
18876 TW21Loader.prototype.internalizeTiddler = function(store,tiddler,title,node)
18878 var e = node.firstChild;
18880 if(node.getAttribute("tiddler")) {
18881 text = getNodeText(e).unescapeLineBreaks();
18883 while(e.nodeName!="PRE" && e.nodeName!="pre") {
18886 text = e.innerHTML.replace(/\r/mg,"").htmlDecode();
18888 var modifier = node.getAttribute("modifier");
18889 var c = node.getAttribute("created");
18890 var m = node.getAttribute("modified");
18891 var created = c ? Date.convertFromYYYYMMDDHHMM(c) : version.date;
18892 var modified = m ? Date.convertFromYYYYMMDDHHMM(m) : created;
18893 var tags = node.getAttribute("tags");
18895 var attrs = node.attributes;
18896 for(var i = attrs.length-1; i >= 0; i--) {
18897 var name = attrs[i].name;
18898 if (attrs[i].specified && !TiddlyWiki.isStandardField(name)) {
18899 fields[name] = attrs[i].value.unescapeLineBreaks();
18902 tiddler.assign(title,text,modifier,modified,tags,created,fields);
18907 //-- TW21Saver (inherits from SaverBase)
18910 function TW21Saver() {}
18912 TW21Saver.prototype = new SaverBase();
18914 TW21Saver.prototype.externalizeTiddler = function(store,tiddler)
18917 var extendedAttributes = "";
18918 var usePre = config.options.chkUsePreForStorage;
18919 store.forEachField(tiddler,
18920 function(tiddler,fieldName,value) {
18921 // don't store stuff from the temp namespace
18922 if(typeof value != "string")
18924 if (!fieldName.match(/^temp\./))
18925 extendedAttributes += ' %0="%1"'.format([fieldName,value.escapeLineBreaks().htmlEncode()]);
18927 var created = tiddler.created.convertToYYYYMMDDHHMM();
18928 var modified = tiddler.modified.convertToYYYYMMDDHHMM();
18929 var vdate = version.date.convertToYYYYMMDDHHMM();
18930 var attributes = tiddler.modifier ? ' modifier="' + tiddler.modifier.htmlEncode() + '"' : "";
18931 attributes += (usePre && modified == created) ? "" : ' modified="' + modified +'"';
18932 attributes += (usePre && created == vdate) ? "" :' created="' + created + '"';
18933 var tags = tiddler.getTags();
18934 if(!usePre || tags)
18935 attributes += ' tags="' + tags.htmlEncode() + '"';
18936 return ('<div %0="%1"%2%3>%4</'+'div>').format([
18937 usePre ? "title" : "tiddler",
18938 tiddler.title.htmlEncode(),
18940 extendedAttributes,
18941 usePre ? "\n<pre>" + tiddler.text.htmlEncode() + "</pre>\n" : tiddler.text.escapeLineBreaks().htmlEncode()
18944 throw exceptionText(ex,config.messages.tiddlerSaveError.format([tiddler.title]));
18949 //-- Deprecated code
18952 // @Deprecated: Use createElementAndWikify and this.termRegExp instead
18953 config.formatterHelpers.charFormatHelper = function(w)
18955 w.subWikify(createTiddlyElement(w.output,this.element),this.terminator);
18958 // @Deprecated: Use enclosedTextHelper and this.lookaheadRegExp instead
18959 config.formatterHelpers.monospacedByLineHelper = function(w)
18961 var lookaheadRegExp = new RegExp(this.lookahead,"mg");
18962 lookaheadRegExp.lastIndex = w.matchStart;
18963 var lookaheadMatch = lookaheadRegExp.exec(w.source);
18964 if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
18965 var text = lookaheadMatch[1];
18966 if(config.browser.isIE)
18967 text = text.replace(/\n/g,"\r");
18968 createTiddlyElement(w.output,"pre",null,null,text);
18969 w.nextMatch = lookaheadRegExp.lastIndex;
18973 // @Deprecated: Use <br> or <br /> instead of <<br>>
18974 config.macros.br.handler = function(place)
18976 createTiddlyElement(place,"br");
18979 // Find an entry in an array. Returns the array index or null
18980 // @Deprecated: Use indexOf instead
18981 Array.prototype.find = function(item)
18983 var i = this.indexOf(item);
18984 return i == -1 ? null : i;
18987 // Load a tiddler from an HTML DIV. The caller should make sure to later call Tiddler.changed()
18988 // @Deprecated: Use store.getLoader().internalizeTiddler instead
18989 Tiddler.prototype.loadFromDiv = function(divRef,title)
18991 return store.getLoader().internalizeTiddler(store,this,title,divRef);
18994 // Format the text for storage in an HTML DIV
18995 // @Deprecated Use store.getSaver().externalizeTiddler instead.
18996 Tiddler.prototype.saveToDiv = function()
18998 return store.getSaver().externalizeTiddler(store,this);
19001 // @Deprecated: Use store.allTiddlersAsHtml() instead
19002 function allTiddlersAsHtml()
19004 return store.allTiddlersAsHtml();
19007 // @Deprecated: Use refreshPageTemplate instead
19008 function applyPageTemplate(title)
19010 refreshPageTemplate(title);
19013 // @Deprecated: Use story.displayTiddlers instead
19014 function displayTiddlers(srcElement,titles,template,unused1,unused2,animate,unused3)
19016 story.displayTiddlers(srcElement,titles,template,animate);
19019 // @Deprecated: Use story.displayTiddler instead
19020 function displayTiddler(srcElement,title,template,unused1,unused2,animate,unused3)
19022 story.displayTiddler(srcElement,title,template,animate);
19025 // @Deprecated: Use functions on right hand side directly instead
19026 var createTiddlerPopup = Popup.create;
19027 var scrollToTiddlerPopup = Popup.show;
19028 var hideTiddlerPopup = Popup.remove;
19030 // @Deprecated: Use right hand side directly instead
19031 var regexpBackSlashEn = new RegExp("\\\\n","mg");
19032 var regexpBackSlash = new RegExp("\\\\","mg");
19033 var regexpBackSlashEss = new RegExp("\\\\s","mg");
19034 var regexpNewLine = new RegExp("\n","mg");
19035 var regexpCarriageReturn = new RegExp("\r","mg");
19037 //-- End of scripts
19041 <script type="text/javascript">
19044 document.write("<applet style='position:absolute;left:-1px' name='TiddlySaver' code='TiddlySaver.class' archive='TiddlySaver.jar' width='1' height='1'></applet>");
19047 <!--POST-SCRIPT-START-->
19049 <!--POST-SCRIPT-END-->