<!--{{{-->
<link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml' />
<!--}}}-->
Background: #fff
Foreground: #000
PrimaryPale: #8cf
PrimaryLight: #18f
PrimaryMid: #04b
PrimaryDark: #014
SecondaryPale: #ffc
SecondaryLight: #fe8
SecondaryMid: #db4
SecondaryDark: #841
TertiaryPale: #eee
TertiaryLight: #ccc
TertiaryMid: #999
TertiaryDark: #666
Error: #f88
/*{{{*/
body {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}

a {color:[[ColorPalette::PrimaryMid]];}
a:hover {background-color:[[ColorPalette::PrimaryMid]]; color:[[ColorPalette::Background]];}
a img {border:0;}

h1,h2,h3,h4,h5,h6 {color:[[ColorPalette::SecondaryDark]]; background:transparent;}
h1 {border-bottom:2px solid [[ColorPalette::TertiaryLight]];}
h2,h3 {border-bottom:1px solid [[ColorPalette::TertiaryLight]];}

.button {color:[[ColorPalette::PrimaryDark]]; border:1px solid [[ColorPalette::Background]];}
.button:hover {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::SecondaryLight]]; border-color:[[ColorPalette::SecondaryMid]];}
.button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::SecondaryDark]];}

.header {background:[[ColorPalette::PrimaryMid]];}
.headerShadow {color:[[ColorPalette::Foreground]];}
.headerShadow a {font-weight:normal; color:[[ColorPalette::Foreground]];}
.headerForeground {color:[[ColorPalette::Background]];}
.headerForeground a {font-weight:normal; color:[[ColorPalette::PrimaryPale]];}

.tabSelected{color:[[ColorPalette::PrimaryDark]];
	background:[[ColorPalette::TertiaryPale]];
	border-left:1px solid [[ColorPalette::TertiaryLight]];
	border-top:1px solid [[ColorPalette::TertiaryLight]];
	border-right:1px solid [[ColorPalette::TertiaryLight]];
}
.tabUnselected {color:[[ColorPalette::Background]]; background:[[ColorPalette::TertiaryMid]];}
.tabContents {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::TertiaryPale]]; border:1px solid [[ColorPalette::TertiaryLight]];}
.tabContents .button {border:0;}

#sidebar {}
#sidebarOptions input {border:1px solid [[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel {background:[[ColorPalette::PrimaryPale]];}
#sidebarOptions .sliderPanel a {border:none;color:[[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel a:hover {color:[[ColorPalette::Background]]; background:[[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel a:active {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::Background]];}

.wizard {background:[[ColorPalette::PrimaryPale]]; border:1px solid [[ColorPalette::PrimaryMid]];}
.wizard h1 {color:[[ColorPalette::PrimaryDark]]; border:none;}
.wizard h2 {color:[[ColorPalette::Foreground]]; border:none;}
.wizardStep {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];
	border:1px solid [[ColorPalette::PrimaryMid]];}
.wizardStep.wizardStepDone {background:[[ColorPalette::TertiaryLight]];}
.wizardFooter {background:[[ColorPalette::PrimaryPale]];}
.wizardFooter .status {background:[[ColorPalette::PrimaryDark]]; color:[[ColorPalette::Background]];}
.wizard .button {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryLight]]; border: 1px solid;
	border-color:[[ColorPalette::SecondaryPale]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryPale]];}
.wizard .button:hover {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Background]];}
.wizard .button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::Foreground]]; border: 1px solid;
	border-color:[[ColorPalette::PrimaryDark]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryDark]];}
	
.wizard .notChanged {background:transparent;}
.wizard .changedLocally {background:#80ff80;}
.wizard .changedServer {background:#8080ff;}
.wizard .changedBoth {background:#ff8080;}
.wizard .notFound {background:#ffff80;}
.wizard .putToServer {background:#ff80ff;}
.wizard .gotFromServer {background:#80ffff;}

#messageArea {border:1px solid [[ColorPalette::SecondaryMid]]; background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]];}
#messageArea .button {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::SecondaryPale]]; border:none;}

.popupTiddler {background:[[ColorPalette::TertiaryPale]]; border:2px solid [[ColorPalette::TertiaryMid]];}

.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]];}
.popup hr {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::PrimaryDark]]; border-bottom:1px;}
.popup li.disabled {color:[[ColorPalette::TertiaryMid]];}
.popup li a, .popup li a:visited {color:[[ColorPalette::Foreground]]; border: none;}
.popup li a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border: none;}
.popup li a:active {background:[[ColorPalette::SecondaryPale]]; color:[[ColorPalette::Foreground]]; border: none;}
.popupHighlight {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
.listBreak div {border-bottom:1px solid [[ColorPalette::TertiaryDark]];}

.tiddler .defaultCommand {font-weight:bold;}

.shadow .title {color:[[ColorPalette::TertiaryDark]];}

.title {color:[[ColorPalette::SecondaryDark]];}
.subtitle {color:[[ColorPalette::TertiaryDark]];}

.toolbar {color:[[ColorPalette::PrimaryMid]];}
.toolbar a {color:[[ColorPalette::TertiaryLight]];}
.selected .toolbar a {color:[[ColorPalette::TertiaryMid]];}
.selected .toolbar a:hover {color:[[ColorPalette::Foreground]];}

.tagging, .tagged {border:1px solid [[ColorPalette::TertiaryPale]]; background-color:[[ColorPalette::TertiaryPale]];}
.selected .tagging, .selected .tagged {background-color:[[ColorPalette::TertiaryLight]]; border:1px solid [[ColorPalette::TertiaryMid]];}
.tagging .listTitle, .tagged .listTitle {color:[[ColorPalette::PrimaryDark]];}
.tagging .button, .tagged .button {border:none;}

.footer {color:[[ColorPalette::TertiaryLight]];}
.selected .footer {color:[[ColorPalette::TertiaryMid]];}

.sparkline {background:[[ColorPalette::PrimaryPale]]; border:0;}
.sparktick {background:[[ColorPalette::PrimaryDark]];}

.error, .errorButton {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Error]];}
.warning {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryPale]];}
.lowlight {background:[[ColorPalette::TertiaryLight]];}

.zoomer {background:none; color:[[ColorPalette::TertiaryMid]]; border:3px solid [[ColorPalette::TertiaryMid]];}

.imageLink, #displayArea .imageLink {background:transparent;}

.annotation {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border:2px solid [[ColorPalette::SecondaryMid]];}

.viewer .listTitle {list-style-type:none; margin-left:-2em;}
.viewer .button {border:1px solid [[ColorPalette::SecondaryMid]];}
.viewer blockquote {border-left:3px solid [[ColorPalette::TertiaryDark]];}

.viewer table, table.twtable {border:2px solid [[ColorPalette::TertiaryDark]];}
.viewer th, .viewer thead td, .twtable th, .twtable thead td {background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::Background]];}
.viewer td, .viewer tr, .twtable td, .twtable tr {border:1px solid [[ColorPalette::TertiaryDark]];}

.viewer pre {border:1px solid [[ColorPalette::SecondaryLight]]; background:[[ColorPalette::SecondaryPale]];}
.viewer code {color:[[ColorPalette::SecondaryDark]];}
.viewer hr {border:0; border-top:dashed 1px [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::TertiaryDark]];}

.highlight, .marked {background:[[ColorPalette::SecondaryLight]];}

.editor input {border:1px solid [[ColorPalette::PrimaryMid]];}
.editor textarea {border:1px solid [[ColorPalette::PrimaryMid]]; width:100%;}
.editorFooter {color:[[ColorPalette::TertiaryMid]];}

#backstageArea {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::TertiaryMid]];}
#backstageArea a {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
#backstageArea a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; }
#backstageArea a.backstageSelTab {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
#backstageButton a {background:none; color:[[ColorPalette::Background]]; border:none;}
#backstageButton a:hover {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
#backstagePanel {background:[[ColorPalette::Background]]; border-color: [[ColorPalette::Background]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]];}
.backstagePanelFooter .button {border:none; color:[[ColorPalette::Background]];}
.backstagePanelFooter .button:hover {color:[[ColorPalette::Foreground]];}
#backstageCloak {background:[[ColorPalette::Foreground]]; opacity:0.6; filter:'alpha(opacity:60)';}
/*}}}*/
/*{{{*/
* html .tiddler {height:1%;}

body {font-size:.75em; font-family:arial,helvetica; margin:0; padding:0;}

h1,h2,h3,h4,h5,h6 {font-weight:bold; text-decoration:none;}
h1,h2,h3 {padding-bottom:1px; margin-top:1.2em;margin-bottom:0.3em;}
h4,h5,h6 {margin-top:1em;}
h1 {font-size:1.35em;}
h2 {font-size:1.25em;}
h3 {font-size:1.1em;}
h4 {font-size:1em;}
h5 {font-size:.9em;}

hr {height:1px;}

a {text-decoration:none;}

dt {font-weight:bold;}

ol {list-style-type:decimal;}
ol ol {list-style-type:lower-alpha;}
ol ol ol {list-style-type:lower-roman;}
ol ol ol ol {list-style-type:decimal;}
ol ol ol ol ol {list-style-type:lower-alpha;}
ol ol ol ol ol ol {list-style-type:lower-roman;}
ol ol ol ol ol ol ol {list-style-type:decimal;}

.txtOptionInput {width:11em;}

#contentWrapper .chkOptionInput {border:0;}

.externalLink {text-decoration:underline;}

.indent {margin-left:3em;}
.outdent {margin-left:3em; text-indent:-3em;}
code.escaped {white-space:nowrap;}

.tiddlyLinkExisting {font-weight:bold;}
.tiddlyLinkNonExisting {font-style:italic;}

/* the 'a' is required for IE, otherwise it renders the whole tiddler in bold */
a.tiddlyLinkNonExisting.shadow {font-weight:bold;}

#mainMenu .tiddlyLinkExisting,
	#mainMenu .tiddlyLinkNonExisting,
	#sidebarTabs .tiddlyLinkNonExisting {font-weight:normal; font-style:normal;}
#sidebarTabs .tiddlyLinkExisting {font-weight:bold; font-style:normal;}

.header {position:relative;}
.header a:hover {background:transparent;}
.headerShadow {position:relative; padding:4.5em 0em 1em 1em; left:-1px; top:-1px;}
.headerForeground {position:absolute; padding:4.5em 0em 1em 1em; left:0px; top:0px;}

.siteTitle {font-size:3em;}
.siteSubtitle {font-size:1.2em;}

#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;}

#sidebar {position:absolute; right:3px; width:16em; font-size:.9em;}
#sidebarOptions {padding-top:0.3em;}
#sidebarOptions a {margin:0em 0.2em; padding:0.2em 0.3em; display:block;}
#sidebarOptions input {margin:0.4em 0.5em;}
#sidebarOptions .sliderPanel {margin-left:1em; padding:0.5em; font-size:.85em;}
#sidebarOptions .sliderPanel a {font-weight:bold; display:inline; padding:0;}
#sidebarOptions .sliderPanel input {margin:0 0 .3em 0;}
#sidebarTabs .tabContents {width:15em; overflow:hidden;}

.wizard {padding:0.1em 1em 0em 2em;}
.wizard h1 {font-size:2em; font-weight:bold; background:none; padding:0em 0em 0em 0em; margin:0.4em 0em 0.2em 0em;}
.wizard h2 {font-size:1.2em; font-weight:bold; background:none; padding:0em 0em 0em 0em; margin:0.4em 0em 0.2em 0em;}
.wizardStep {padding:1em 1em 1em 1em;}
.wizard .button {margin:0.5em 0em 0em 0em; font-size:1.2em;}
.wizardFooter {padding:0.8em 0.4em 0.8em 0em;}
.wizardFooter .status {padding:0em 0.4em 0em 0.4em; margin-left:1em;}
.wizard .button {padding:0.1em 0.2em 0.1em 0.2em;}

#messageArea {position:fixed; top:2em; right:0em; margin:0.5em; padding:0.5em; z-index:2000; _position:absolute;}
.messageToolbar {display:block; text-align:right; padding:0.2em 0.2em 0.2em 0.2em;}
#messageArea a {text-decoration:underline;}

.tiddlerPopupButton {padding:0.2em 0.2em 0.2em 0.2em;}
.popupTiddler {position: absolute; z-index:300; padding:1em 1em 1em 1em; margin:0;}

.popup {position:absolute; z-index:300; font-size:.9em; padding:0; list-style:none; margin:0;}
.popup .popupMessage {padding:0.4em;}
.popup hr {display:block; height:1px; width:auto; padding:0; margin:0.2em 0em;}
.popup li.disabled {padding:0.4em;}
.popup li a {display:block; padding:0.4em; font-weight:normal; cursor:pointer;}
.listBreak {font-size:1px; line-height:1px;}
.listBreak div {margin:2px 0;}

.tabset {padding:1em 0em 0em 0.5em;}
.tab {margin:0em 0em 0em 0.25em; padding:2px;}
.tabContents {padding:0.5em;}
.tabContents ul, .tabContents ol {margin:0; padding:0;}
.txtMainTab .tabContents li {list-style:none;}
.tabContents li.listLink { margin-left:.75em;}

#contentWrapper {display:block;}
#splashScreen {display:none;}

#displayArea {margin:1em 17em 0em 14em;}

.toolbar {text-align:right; font-size:.9em;}

.tiddler {padding:1em 1em 0em 1em;}

.missing .viewer,.missing .title {font-style:italic;}

.title {font-size:1.6em; font-weight:bold;}

.missing .subtitle {display:none;}
.subtitle {font-size:1.1em;}

.tiddler .button {padding:0.2em 0.4em;}

.tagging {margin:0.5em 0.5em 0.5em 0; float:left; display:none;}
.isTag .tagging {display:block;}
.tagged {margin:0.5em; float:right;}
.tagging, .tagged {font-size:0.9em; padding:0.25em;}
.tagging ul, .tagged ul {list-style:none; margin:0.25em; padding:0;}
.tagClear {clear:both;}

.footer {font-size:.9em;}
.footer li {display:inline;}

.annotation {padding:0.5em; margin:0.5em;}

* html .viewer pre {width:99%; padding:0 0 1em 0;}
.viewer {line-height:1.4em; padding-top:0.5em;}
.viewer .button {margin:0em 0.25em; padding:0em 0.25em;}
.viewer blockquote {line-height:1.5em; padding-left:0.8em;margin-left:2.5em;}
.viewer ul, .viewer ol {margin-left:0.5em; padding-left:1.5em;}

.viewer table, table.twtable {border-collapse:collapse; margin:0.8em 1.0em;}
.viewer th, .viewer td, .viewer tr,.viewer caption,.twtable th, .twtable td, .twtable tr,.twtable caption {padding:3px;}
table.listView {font-size:0.85em; margin:0.8em 1.0em;}
table.listView th, table.listView td, table.listView tr {padding:0px 3px 0px 3px;}

.viewer pre {padding:0.5em; margin-left:0.5em; font-size:1.2em; line-height:1.4em; overflow:auto;}
.viewer code {font-size:1.2em; line-height:1.4em;}

.editor {font-size:1.1em;}
.editor input, .editor textarea {display:block; width:100%; font:inherit;}
.editorFooter {padding:0.25em 0em; font-size:.9em;}
.editorFooter .button {padding-top:0px; padding-bottom:0px;}

.fieldsetFix {border:0; padding:0; margin:1px 0px 1px 0px;}

.sparkline {line-height:1em;}
.sparktick {outline:0;}

.zoomer {font-size:1.1em; position:absolute; overflow:hidden;}
.zoomer div {padding:1em;}

* html #backstage {width:99%;}
* html #backstageArea {width:99%;}
#backstageArea {display:none; position:relative; overflow: hidden; z-index:150; padding:0.3em 0.5em 0.3em 0.5em;}
#backstageToolbar {position:relative;}
#backstageArea a {font-weight:bold; margin-left:0.5em; padding:0.3em 0.5em 0.3em 0.5em;}
#backstageButton {display:none; position:absolute; z-index:175; top:0em; right:0em;}
#backstageButton a {padding:0.1em 0.4em 0.1em 0.4em; margin:0.1em 0.1em 0.1em 0.1em;}
#backstage {position:relative; width:100%; z-index:50;}
#backstagePanel {display:none; z-index:100; position:absolute; width:90%; margin:0em 3em 0em 3em; padding:1em 1em 1em 1em;}
.backstagePanelFooter {padding-top:0.2em; float:right;}
.backstagePanelFooter a {padding:0.2em 0.4em 0.2em 0.4em;}
#backstageCloak {display:none; z-index:20; position:absolute; width:100%; height:100px;}

.whenBackstage {display:none;}
.backstageVisible .whenBackstage {display:block;}
/*}}}*/
/***
StyleSheet for use when a translation requires any css style changes.
This StyleSheet can be used directly by languages such as Chinese, Japanese and Korean which need larger font sizes.
***/
/*{{{*/
body {font-size:0.8em;}
#sidebarOptions {font-size:1.05em;}
#sidebarOptions a {font-style:normal;}
#sidebarOptions .sliderPanel {font-size:0.95em;}
.subtitle {font-size:0.8em;}
.viewer table.listView {font-size:0.95em;}
/*}}}*/
/*{{{*/
@media print {
#mainMenu, #sidebar, #messageArea, .toolbar, #backstageButton, #backstageArea {display: none ! important;}
#displayArea {margin: 1em 1em 0em 1em;}
/* Fixes a feature in Firefox 1.5.0.2 where print preview displays the noscript content */
noscript {display:none;}
}
/*}}}*/
<!--{{{-->
<div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'>
<div class='headerShadow'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
<div class='headerForeground'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
</div>
<div id='mainMenu' refresh='content' tiddler='MainMenu'></div>
<div id='sidebar'>
<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
</div>
<div id='displayArea'>
<div id='messageArea'></div>
<div id='tiddlerDisplay'></div>
</div>
<!--}}}-->
<!--{{{-->
<div class='toolbar' macro='toolbar [[ToolbarCommands::ViewToolbar]]'></div>
<div class='title' macro='view title'></div>
<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>
<div class='tagging' macro='tagging'></div>
<div class='tagged' macro='tags'></div>
<div class='viewer' macro='view text wikified'></div>
<div class='tagClear'></div>
<!--}}}-->
<!--{{{-->
<div class='toolbar' macro='toolbar [[ToolbarCommands::EditToolbar]]'></div>
<div class='title' macro='view title'></div>
<div class='editor' macro='edit title'></div>
<div macro='annotations'></div>
<div class='editor' macro='edit text'></div>
<div class='editor' macro='edit tags'></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser'></span></div>
<!--}}}-->
To get started with this blank TiddlyWiki, you'll need to modify the following tiddlers:
* SiteTitle & SiteSubtitle: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)
* MainMenu: The menu (usually on the left)
* DefaultTiddlers: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened
You'll also need to enter your username for signing your edits: <<option txtUserName>>
These InterfaceOptions for customising TiddlyWiki are saved in your browser

Your username for signing your edits. Write it as a WikiWord (eg JoeBloggs)

<<option txtUserName>>
<<option chkSaveBackups>> SaveBackups
<<option chkAutoSave>> AutoSave
<<option chkRegExpSearch>> RegExpSearch
<<option chkCaseSensitiveSearch>> CaseSensitiveSearch
<<option chkAnimate>> EnableAnimations

----
Also see AdvancedOptions
<<importTiddlers>>
|''Type:''|file|
|''URL:''|http://ogoshi.tiddlyspot.com/|
|''Workspace:''|(default)|

This tiddler was automatically created to record the details of this server
\[5^2 = 25\]
\[15^2 = 225\]
\[25^2 = 625\]
\[35^2 = 1225\]
\[\vdots\]
\[105^2 = 11025\]
\[\vdots\]

\[(10 a + 5)^2 = 100 a^2 + 100 a + 25 = 100 a (a + 1) + 25\]

{{{
var result = int.Parse((a * (a + 1)).ToString() + "25");
}}}
!!三次
\[ \mathbf{p}(t) = \frac 1 6 \left[ \matrix { t^3 & t^2 & t & 1 } \right] \left[ \matrix {
-1&3&-3&1\\
3&-6&3&0\\
-3&0&3&0\\
1&4&1&0
} \right] \left[ \matrix {
\mathbf{a}\\
\mathbf{b}\\
\mathbf{c}\\
\mathbf{d}\\
} \right] \]
!!三次
\[ \mathbf{p}(t) = \left[ \matrix { t^3 & t^2 & t & 1 } \right] \left[ \matrix {
-1&3&-3&1\\
3&-6&3&0\\
-3&3&0&0\\
1&0&0&0
} \right] \left[ \matrix {
\mathbf{a}\\
\mathbf{b}\\
\mathbf{c}\\
\mathbf{d}\\
} \right] \]

!!!接線
\[ \mathbf{p}'(t) = \left[ \matrix { t^2 & t & 1 } \right] \left[ \matrix {
-3&9&-9&3\\
6&-12&6&0\\
-3&3&0&0
} \right] \left[ \matrix {
\mathbf{a}\\
\mathbf{b}\\
\mathbf{c}\\
\mathbf{d}\\
} \right] \]
!!三次
\[ \mathbf{p}(t) = \frac 1 2 \left[ \matrix { t^3 & t^2 & t & 1 } \right] \left[ \matrix {
-1&3&-3&1\\
2&-5&4&-1\\
-1&0&1&0\\
0&2&0&0
} \right] \left[ \matrix {
\mathbf{a}\\
\mathbf{b}\\
\mathbf{c}\\
\mathbf{d}\\
} \right] \]
Background: #fff
Foreground: #000
PrimaryPale: #fc8
PrimaryLight: #f18
PrimaryMid: #b04
PrimaryDark: #401
SecondaryPale: #ffc
SecondaryLight: #fe8
SecondaryMid: #db4
SecondaryDark: #841
TertiaryPale: #eee
TertiaryLight: #ccc
TertiaryMid: #999
TertiaryDark: #666
Error: #f88
[[数学クラスライブラリ編]]
[[このページの操作について]]
 [[デュアルクォータニオン]]を表します。XNA Game Studio 3.0には相当する機能はありません。
/***
|Name|ExportTiddlersPlugin|
|Source|http://www.TiddlyTools.com/#ExportTiddlersPlugin|
|Documentation|http://www.TiddlyTools.com/#ExportTiddlersPluginInfo|
|Version|2.8.4|
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type|plugin|
|Requires||
|Overrides||
|Description|select and extract tiddlers from your ~TiddlyWiki documents and save them to a separate file|
ExportTiddlersPlugin lets you select and extract tiddlers from your ~TiddlyWiki documents using interactive control panel lets you specify a destination, and then select which tiddlers to export. Tiddler data can be output as complete, stand-alone TiddlyWiki documents, or just the selected tiddlers ('~PureStore' format -- smaller files!) that can be imported directly into another ~TiddlyWiki, or as an ~RSS-compatible XML file that can be published for RSS syndication.
!!!!!Documentation
>see [[ExportTiddlersPluginInfo]]
!!!!!Inline control panel (live):
><<exportTiddlers inline>>
!!!!!Revisions
<<<
2008.09.29 [2.8.4] in getData(), convert existing TW file from UTF8 to Unicode before merging to correct handling of international characters and symbols.
|please see [[ExportTiddlersPluginInfo]] for additional revision details|
2005.10.09 [0.0.0] development started
<<<
!!!!!Code
***/
//{{{
// version
version.extensions.ExportTiddlersPlugin= {major: 2, minor: 8, revision: 4, date: new Date(2008,9,29)};

// default shadow definition
config.shadowTiddlers.ExportTiddlers='<<exportTiddlers inline>>';

// add 'export' backstage task (following built-in import task)
if (config.tasks) { // TW2.2 or above
	config.tasks.exportTask = {
		text:'export',
		tooltip:'Export selected tiddlers to another file',
		content:'<<exportTiddlers inline>>'
	}
	config.backstageTasks.splice(config.backstageTasks.indexOf('importTask')+1,0,'exportTask');
}

// $(...) function: 'shorthand' convenience syntax for document.getElementById()
if (typeof($)=='undefined') { // avoid redefinition
function $() {
	var elements=new Array();
	for (var i=0; i<arguments.length; i++) {
		var element=arguments[i];
		if (typeof element=='string') element=document.getElementById(element);
		if (arguments.length==1) return element;
		elements.push(element);
	}
	return elements;
}
}

config.macros.exportTiddlers = {
	label: 'export tiddlers',
	prompt: 'Copy selected tiddlers to an export document',
	okmsg: '%0 tiddlers written to %1',
	failmsg: 'An error occurred while creating %1',
	mergeprompt: '%0\nalready contains tiddler definitions.\n'
		+'\nPress OK to add new/revised tiddlers to current file contents.'
		+'\nPress Cancel to completely replace file contents',
	mergestatus: 'Merged %0 new/revised tiddlers with %1 previously saved tiddlers',
	statusmsg: '%0 tiddler%1 - %2 selected for export',
	newdefault: 'export.html',
	datetimefmt: '0MM/0DD/YYYY 0hh:0mm:0ss',  // for 'filter date/time' edit fields
	type_TW: 'tw', type_PS: 'ps', type_TX: 'tx', type_NF: 'nf', // file type tokens
	type_map: { // map filetype param alternatives/abbreviations to token values
		tiddlywiki:'tw', tw:'tw', wiki: 'tw',
		purestore: 'ps', ps:'ps', store:'ps',
		plaintext: 'tx', tx:'tx', text: 'tx',
		newsfeed:  'nf', nf:'nf', xml:  'nf', rss:'nf'
	},
	handler: function(place,macroName,params) {
		if (params[0]!='inline')
			{ createTiddlyButton(place,this.label,this.prompt,this.togglePanel); return; }
		var panel=this.createPanel(place);
		panel.style.position='static';
		panel.style.display='block';
	},
	createPanel: function(place) {
		var panel=$('exportPanel');
		if (panel) { panel.parentNode.removeChild(panel); }
		setStylesheet(this.css,'exportTiddlers');
		panel=createTiddlyElement(place,'span','exportPanel',null,null)
		panel.innerHTML=this.html;
		this.initFilter();
		this.refreshList(0);
		var fn=$('exportFilename');
		if (window.location.protocol=='file:' && !fn.value.length) {
			// get new target path/filename
			var newPath=getLocalPath(window.location.href);
			var slashpos=newPath.lastIndexOf('/'); if (slashpos==-1) slashpos=newPath.lastIndexOf('\\'); 
			if (slashpos!=-1) newPath=newPath.substr(0,slashpos+1); // trim filename
			fn.value=newPath+this.newdefault;
		}
		return panel;
	},
	togglePanel: function(e) {
		if (!e) var e = window.event;
		var parent=resolveTarget(e).parentNode;
		var panel = $('exportPanel');
		if (panel==undefined || panel.parentNode!=parent)
			panel=config.macros.exportTiddlers.createPanel(parent);
		var isOpen = panel.style.display=='block';
		if(config.options.chkAnimate)
			anim.startAnimating(new Slider(panel,!isOpen,e.shiftKey || e.altKey,'none'));
		else
			panel.style.display = isOpen ? 'none' : 'block' ;
		if (panel.style.display!='none') { // update list and set focus when panel is made visible
			config.macros.exportTiddlers.refreshList(0);
			var fn=$('exportFilename'); fn.focus(); fn.select();
		}
		e.cancelBubble = true;
		if (e.stopPropagation) e.stopPropagation();
		return(false);
	},
	css: '\
		#exportPanel {\
			display: none; position:absolute; z-index:12; width:35em; right:105%; top:6em;\
			background-color: #eee; color:#000; font-size: 8pt; line-height:110%;\
			border:1px solid black; border-bottom-width: 3px; border-right-width: 3px;\
			padding: 0.5em; margin:0em; -moz-border-radius:1em;-webkit-border-radius:1em;\
		}\
		#exportPanel a, #exportPanel td a { color:#009; display:inline; margin:0px; padding:1px; }\
		#exportPanel table { \
			width:100%; border:0px; padding:0px; margin:0px;\
			font-size:8pt; line-height:110%; background:transparent;\
		}\
		#exportPanel tr { border:0px;padding:0px;margin:0px; background:transparent; }\
		#exportPanel td { color:#000; border:0px;padding:0px;margin:0px; background:transparent; }\
		#exportPanel select { width:98%;margin:0px;font-size:8pt;line-height:110%;}\
		#exportPanel input  { width:98%;padding:0px;margin:0px;font-size:8pt;line-height:110%; }\
		#exportPanel textarea  { width:98%;padding:0px;margin:0px;overflow:auto;font-size:8pt; }\
		#exportPanel .box { \
			border:1px solid black; padding:3px; margin-bottom:5px; \
			background:#f8f8f8; -moz-border-radius:5px;-webkit-border-radius:5px; }\
		#exportPanel .topline { border-top:2px solid black; padding-top:3px; margin-bottom:5px; }\
		#exportPanel .rad { width:auto;border:0 }\
		#exportPanel .chk { width:auto;border:0 }\
		#exportPanel .btn { width:auto; }\
		#exportPanel .btn1 { width:98%; }\
		#exportPanel .btn2 { width:48%; }\
		#exportPanel .btn3 { width:32%; }\
		#exportPanel .btn4 { width:24%; }\
		#exportPanel .btn5 { width:19%; }\
	',
	html: '\
		<!-- target path/file  -->\
		<div>\
		export to path/filename:<br>\
		<input type="text" id="exportFilename" size=40 style="width:93%"><input \
			type="button" id="exportBrowse" value="..." title="select or enter a local folder/file..." style="width:5%" \
			onclick="var fn=config.macros.exportTiddlers.askForFilename(this); if (fn.length) this.previousSibling.value=fn; ">\
		</div>\
		<!-- output format -->\
		<div>\
		output file format:\
		<select id="exportFormat" size=1>\
			<option value="TW">TiddlyWiki HTML document (includes core code)</option>\
			<option value="PS">TiddlyWiki "PureStore" HTML file (tiddler data only)</option>\
			<option value="TX">TiddlyWiki plain text TXT file (tiddler source listing)</option>\
			<option value="NF">RSS NewsFeed XML file</option>\
		</select>\
		</div>\
		<!-- notes -->\
		<div>\
		notes:<br>\
		<textarea id="exportNotes" rows=3 cols=40 style="height:4em;margin-bottom:5px;" onfocus="this.select()"></textarea> \
		</div>\
		<!-- list of tiddlers -->\
		<table><tr align="left"><td>\
			select:\
			<a href="JavaScript:;" id="exportSelectAll"\
				onclick="config.macros.exportTiddlers.process(this)" title="select all tiddlers">\
				&nbsp;all&nbsp;</a>\
			<a href="JavaScript:;" id="exportSelectChanges"\
				onclick="config.macros.exportTiddlers.process(this)" title="select tiddlers changed since last save">\
				&nbsp;changes&nbsp;</a> \
			<a href="JavaScript:;" id="exportSelectOpened"\
				onclick="config.macros.exportTiddlers.process(this)" title="select tiddlers currently being displayed">\
				&nbsp;opened&nbsp;</a> \
			<a href="JavaScript:;" id="exportSelectRelated"\
				onclick="config.macros.exportTiddlers.process(this)" title="select tiddlers related to the currently selected tiddlers">\
				&nbsp;related&nbsp;</a> \
			<a href="JavaScript:;" id="exportToggleFilter"\
				onclick="config.macros.exportTiddlers.process(this)" title="show/hide selection filter">\
				&nbsp;filter&nbsp;</a>  \
		</td><td align="right">\
			<a href="JavaScript:;" id="exportListSmaller"\
				onclick="config.macros.exportTiddlers.process(this)" title="reduce list size">\
				&nbsp;&#150;&nbsp;</a>\
			<a href="JavaScript:;" id="exportListLarger"\
				onclick="config.macros.exportTiddlers.process(this)" title="increase list size">\
				&nbsp;+&nbsp;</a>\
		</td></tr></table>\
		<select id="exportList" multiple size="10" style="margin-bottom:5px;"\
			onchange="config.macros.exportTiddlers.refreshList(this.selectedIndex)">\
		</select><br>\
		</div><!--box-->\
		<!-- selection filter -->\
		<div id="exportFilterPanel" style="display:none">\
		<table><tr align="left"><td>\
			selection filter\
		</td><td align="right">\
			<a href="JavaScript:;" id="exportHideFilter"\
				onclick="config.macros.exportTiddlers.process(this)" title="hide selection filter">hide</a>\
		</td></tr></table>\
		<div class="box">\
		<input type="checkbox" class="chk" id="exportFilterStart" value="1"\
			onclick="config.macros.exportTiddlers.showFilterFields(this)"> starting date/time<br>\
		<table cellpadding="0" cellspacing="0"><tr valign="center"><td width="50%">\
			<select size=1 id="exportFilterStartBy" \
				onchange="config.macros.exportTiddlers.showFilterFields(this);">\
				<option value="0">today</option>\
				<option value="1">yesterday</option>\
				<option value="7">a week ago</option>\
				<option value="30">a month ago</option>\
				<option value="file">file date</option>\
				<option value="other">other (mm/dd/yyyy hh:mm)</option>\
			</select>\
		</td><td width="50%">\
			<input type="text" id="exportStartDate" onfocus="this.select()"\
				onchange="$(\'exportFilterStartBy\').value=\'other\';">\
		</td></tr></table>\
		<input type="checkbox" class="chk" id="exportFilterEnd" value="1"\
			onclick="config.macros.exportTiddlers.showFilterFields(this)"> ending date/time<br>\
		<table cellpadding="0" cellspacing="0"><tr valign="center"><td width="50%">\
			<select size=1 id="exportFilterEndBy" \
				onchange="config.macros.exportTiddlers.showFilterFields(this);">\
				<option value="0">today</option>\
				<option value="1">yesterday</option>\
				<option value="7">a week ago</option>\
				<option value="30">a month ago</option>\
				<option value="file">file date</option>\
				<option value="other">other (mm/dd/yyyy hh:mm)</option>\
			</select>\
		</td><td width="50%">\
			<input type="text" id="exportEndDate" onfocus="this.select()"\
				onchange="$(\'exportFilterEndBy\').value=\'other\';">\
		</td></tr></table>\
		<input type="checkbox" class="chk" id=exportFilterTags value="1"\
			onclick="config.macros.exportTiddlers.showFilterFields(this)"> match tags<br>\
		<input type="text" id="exportTags" onfocus="this.select()">\
		<input type="checkbox" class="chk" id=exportFilterText value="1"\
			onclick="config.macros.exportTiddlers.showFilterFields(this)"> match titles/tiddler text<br>\
		<input type="text" id="exportText" onfocus="this.select()">\
		</div> <!--box-->\
		</div> <!--panel-->\
		<!-- action buttons -->\
		<div style="text-align:center">\
		<input type=button class="btn4" onclick="config.macros.exportTiddlers.process(this)"\
			id="exportFilter" value="apply filter">\
		<input type=button class="btn4" onclick="config.macros.exportTiddlers.process(this)"\
			id="exportStart" value="export tiddlers">\
		<input type=button class="btn4" onclick="config.macros.exportTiddlers.process(this)"\
			id="exportDelete" value="delete tiddlers">\
		<input type=button class="btn4" onclick="config.macros.exportTiddlers.process(this)"\
			id="exportClose" value="close">\
		</div><!--center-->\
	',
	process: function(which) { // process panel control interactions
		// DEBUG alert(which.id);
		var theList=$('exportList'); if (!theList) return;
		var count = 0;
		var total = store.getTiddlers('title').length;
		switch (which.id) {
			case 'exportFilter':
				count=this.filterExportList();
				var panel=$('exportFilterPanel');
				if (count==-1) { panel.style.display='block'; break; }
				$('exportStart').disabled=(count==0);
				$('exportDelete').disabled=(count==0);
				this.displayStatus(count,total);
				if (count==0) { alert('No tiddlers were selected'); panel.style.display='block'; }
				break;
			case 'exportStart':
				this.go();
				break;
			case 'exportDelete':
				this.deleteTiddlers();
				break;
			case 'exportHideFilter':
			case 'exportToggleFilter':
				var panel=$('exportFilterPanel')
				panel.style.display=(panel.style.display=='block')?'none':'block';
				break;
			case 'exportSelectChanges':
				var lastmod=new Date(document.lastModified);
				for (var t = 0; t < theList.options.length; t++) {
					if (theList.options[t].value=='') continue;
					var tiddler=store.getTiddler(theList.options[t].value); if (!tiddler) continue;
					theList.options[t].selected=(tiddler.modified>lastmod);
					count += (tiddler.modified>lastmod)?1:0;
				}
				$('exportStart').disabled=(count==0);
				$('exportDelete').disabled=(count==0);
				this.displayStatus(count,total);
				if (count==0) alert('There are no unsaved changes');
				break;
			case 'exportSelectAll':
				for (var t = 0; t < theList.options.length; t++) {
					if (theList.options[t].value=='') continue;
					theList.options[t].selected=true;
					count += 1;
				}
				$('exportStart').disabled=(count==0);
				$('exportDelete').disabled=(count==0);
				this.displayStatus(count,count);
				break;
			case 'exportSelectOpened':
				for (var t = 0; t < theList.options.length; t++) theList.options[t].selected=false;
				var tiddlerDisplay = $('tiddlerDisplay'); // for TW2.1-
				if (!tiddlerDisplay) tiddlerDisplay = $('storyDisplay'); // for TW2.2+
				for (var t=0;t<tiddlerDisplay.childNodes.length;t++) {
					var tiddler=tiddlerDisplay.childNodes[t].id.substr(7);
					for (var i = 0; i < theList.options.length; i++) {
						if (theList.options[i].value!=tiddler) continue;
						theList.options[i].selected=true; count++; break;
					}
				}
				$('exportStart').disabled=(count==0);
				$('exportDelete').disabled=(count==0);
				this.displayStatus(count,total);
				if (count==0) alert('There are no tiddlers currently opened');
				break;
			case 'exportSelectRelated':
				// recursively build list of related tiddlers
				function getRelatedTiddlers(tid,tids) {
					var t=store.getTiddler(tid); if (!t || tids.contains(tid)) return tids;
					tids.push(t.title);
					if (!t.linksUpdated) t.changed();
					for (var i=0; i<t.links.length; i++)
						if (t.links[i]!=tid) tids=getRelatedTiddlers(t.links[i],tids);
					return tids;
				}
				// for all currently selected tiddlers, gather up the related tiddlers (including self) and select them as well
				var tids=[];
				for (var i=0; i<theList.options.length; i++)
					if (theList.options[i].selected) tids=getRelatedTiddlers(theList.options[i].value,tids);
				// select related tiddlers (includes original selected tiddlers)
				for (var i=0; i<theList.options.length; i++)
					theList.options[i].selected=tids.contains(theList.options[i].value);
				this.displayStatus(tids.length,total);
				break;
			case 'exportListSmaller':	// decrease current listbox size
				var min=5;
				theList.size-=(theList.size>min)?1:0;
				break;
			case 'exportListLarger':	// increase current listbox size
				var max=(theList.options.length>25)?theList.options.length:25;
				theList.size+=(theList.size<max)?1:0;
				break;
			case 'exportClose':
				$('exportPanel').style.display='none';
				break;
		}
	},
	displayStatus: function(count,total) {
		var txt=this.statusmsg.format([total,total!=1?'s':'',!count?'none':count==total?'all':count]);
		clearMessage();	displayMessage(txt);
		return txt;
	},
	refreshList: function(selectedIndex) {
		var theList = $('exportList'); if (!theList) return;
		// get the sort order
		var sort;
		if (!selectedIndex)   selectedIndex=0;
		if (selectedIndex==0) sort='modified';
		if (selectedIndex==1) sort='title';
		if (selectedIndex==2) sort='modified';
		if (selectedIndex==3) sort='modifier';
		if (selectedIndex==4) sort='tags';

		// unselect headings and count number of tiddlers actually selected
		var count=0;
		for (var t=5; t < theList.options.length; t++) {
			if (!theList.options[t].selected) continue;
			if (theList.options[t].value!='')
				count++;
			else { // if heading is selected, deselect it, and then select and count all in section
				theList.options[t].selected=false;
				for ( t++; t<theList.options.length && theList.options[t].value!=''; t++) {
					theList.options[t].selected=true;
					count++;
				}
			}
		}

		// disable 'export' and 'delete' buttons if no tiddlers selected
		$('exportStart').disabled=(count==0);
		$('exportDelete').disabled=(count==0);

		// show selection count
		var tiddlers = store.getTiddlers('title');
		if (theList.options.length) this.displayStatus(count,tiddlers.length);

		// if a [command] item, reload list... otherwise, no further refresh needed
		if (selectedIndex>4) return;

		// clear current list contents
		while (theList.length > 0) { theList.options[0] = null; }
		// add heading and control items to list
		var i=0;
		var indent=String.fromCharCode(160)+String.fromCharCode(160);
		theList.options[i++]=
			new Option(tiddlers.length+' tiddlers in document', '',false,false);
		theList.options[i++]=
			new Option(((sort=='title'   )?'>':indent)+' [by title]', '',false,false);
		theList.options[i++]=
			new Option(((sort=='modified')?'>':indent)+' [by date]', '',false,false);
		theList.options[i++]=
			new Option(((sort=='modifier')?'>':indent)+' [by author]', '',false,false);
		theList.options[i++]=
			new Option(((sort=='tags'    )?'>':indent)+' [by tags]', '',false,false);

		// output the tiddler list
		switch(sort) {
			case 'title':
				for(var t = 0; t < tiddlers.length; t++)
					theList.options[i++] = new Option(tiddlers[t].title,tiddlers[t].title,false,false);
				break;
			case 'modifier':
			case 'modified':
				var tiddlers = store.getTiddlers(sort);
				// sort descending for newest date first
				tiddlers.sort(function (a,b) {if(a[sort] == b[sort]) return(0); else return (a[sort] > b[sort]) ? -1 : +1; });
				var lastSection = '';
				for(var t = 0; t < tiddlers.length; t++) {
					var tiddler = tiddlers[t];
					var theSection = '';
					if (sort=='modified') theSection=tiddler.modified.toLocaleDateString();
					if (sort=='modifier') theSection=tiddler.modifier;
					if (theSection != lastSection) {
						theList.options[i++] = new Option(theSection,'',false,false);
						lastSection = theSection;
					}
					theList.options[i++] = new Option(indent+indent+tiddler.title,tiddler.title,false,false);
				}
				break;
			case 'tags':
				var theTitles = {}; // all tiddler titles, hash indexed by tag value
				var theTags = new Array();
				for(var t=0; t<tiddlers.length; t++) {
					var title=tiddlers[t].title;
					var tags=tiddlers[t].tags;
					if (!tags || !tags.length) {
						if (theTitles['untagged']==undefined) { theTags.push('untagged'); theTitles['untagged']=new Array(); }
						theTitles['untagged'].push(title);
					}
					else for(var s=0; s<tags.length; s++) {
						if (theTitles[tags[s]]==undefined) { theTags.push(tags[s]); theTitles[tags[s]]=new Array(); }
						theTitles[tags[s]].push(title);
					}
				}
				theTags.sort();
				for(var tagindex=0; tagindex<theTags.length; tagindex++) {
					var theTag=theTags[tagindex];
					theList.options[i++]=new Option(theTag,'',false,false);
					for(var t=0; t<theTitles[theTag].length; t++)
						theList.options[i++]=new Option(indent+indent+theTitles[theTag][t],theTitles[theTag][t],false,false);
				}
				break;
			}
		theList.selectedIndex=selectedIndex; // select current control item
		$('exportStart').disabled=true;
		$('exportDelete').disabled=true;
		this.displayStatus(0,tiddlers.length);
	},
	askForFilename: function(here) {
		var msg=here.title; // use tooltip as dialog box message
		var path=getLocalPath(document.location.href);
		var slashpos=path.lastIndexOf('/'); if (slashpos==-1) slashpos=path.lastIndexOf('\\'); 
		if (slashpos!=-1) path = path.substr(0,slashpos+1); // remove filename from path, leave the trailing slash
		var filetype=$('exportFormat').value.toLowerCase();
		var defext='html';
		if (filetype==this.type_TX) defext='txt';
		if (filetype==this.type_NF) defext='xml';
		var file=this.newdefault.replace(/html$/,defext);
		var result='';
		if(window.Components) { // moz
			try {
				netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
				var nsIFilePicker = window.Components.interfaces.nsIFilePicker;
				var picker = Components.classes['@mozilla.org/filepicker;1'].createInstance(nsIFilePicker);
				picker.init(window, msg, nsIFilePicker.modeSave);
				var thispath = Components.classes['@mozilla.org/file/local;1'].createInstance(Components.interfaces.nsILocalFile);
				thispath.initWithPath(path);
				picker.displayDirectory=thispath;
				picker.defaultExtension=defext;
				picker.defaultString=file;
				picker.appendFilters(nsIFilePicker.filterAll|nsIFilePicker.filterText|nsIFilePicker.filterHTML);
				if (picker.show()!=nsIFilePicker.returnCancel) var result=picker.file.persistentDescriptor;
			}
			catch(e) { alert('error during local file access: '+e.toString()) }
		}
		else { // IE
			try { // XPSP2 IE only
				var s = new ActiveXObject('UserAccounts.CommonDialog');
				s.Filter='All files|*.*|Text files|*.txt|HTML files|*.htm;*.html|XML files|*.xml|';
				s.FilterIndex=defext=='txt'?2:'html'?3:'xml'?4:1;
				s.InitialDir=path;
				s.FileName=file;
				if (s.showOpen()) var result=s.FileName;
			}
			catch(e) {  // fallback
				var result=prompt(msg,path+file);
			}
		}
		return result;
	},
	initFilter: function() {
		// start date
		$('exportFilterStart').checked=false;
		$('exportStartDate').value='';
		// end date
		$('exportFilterEnd').checked=false;
		$('exportEndDate').value='';
		// tags
		$('exportFilterTags').checked=false;
		$('exportTags').value='';
		// text
		$('exportFilterText').checked=false;
		$('exportText').value='';
		// show/hide filter input fields
		this.showFilterFields();
	},
	showFilterFields: function(which) {
		var show=$('exportFilterStart').checked;
		$('exportFilterStartBy').style.display=show?'block':'none';
		$('exportStartDate').style.display=show?'block':'none';
		var val=$('exportFilterStartBy').value;
		$('exportStartDate').value
			=this.getFilterDate(val,'exportStartDate').formatString(this.datetimefmt);
		if (which && (which.id=='exportFilterStartBy') && (val=='other'))
			$('exportStartDate').focus();

		var show=$('exportFilterEnd').checked;
		$('exportFilterEndBy').style.display=show?'block':'none';
		$('exportEndDate').style.display=show?'block':'none';
		var val=$('exportFilterEndBy').value;
		$('exportEndDate').value
			=this.getFilterDate(val,'exportEndDate').formatString(this.datetimefmt);
		 if (which && (which.id=='exportFilterEndBy') && (val=='other'))
			$('exportEndDate').focus();

		var show=$('exportFilterTags').checked;
		$('exportTags').style.display=show?'block':'none';

		var show=$('exportFilterText').checked;
		$('exportText').style.display=show?'block':'none';
	},
	getFilterDate: function(val,id) {
		var result=0;
		switch (val) {
			case 'file':
				result=new Date(document.lastModified);
				break;
			case 'other':
				result=new Date($(id).value);
				break;
			default: // today=0, yesterday=1, one week=7, two weeks=14, a month=31
				var now=new Date(); var tz=now.getTimezoneOffset()*60000; now-=tz;
				var oneday=86400000;
				if (id=='exportStartDate')
					result=new Date((Math.floor(now/oneday)-val)*oneday+tz);
				else
					result=new Date((Math.floor(now/oneday)-val+1)*oneday+tz-1);
				break;
		}
		return result;
	},
	filterExportList: function() {
		var theList  = $('exportList'); if (!theList) return -1;
		var filterStart=$('exportFilterStart').checked;
		var val=$('exportFilterStartBy').value;
		var startDate=config.macros.exportTiddlers.getFilterDate(val,'exportStartDate');
		var filterEnd=$('exportFilterEnd').checked;
		var val=$('exportFilterEndBy').value;
		var endDate=config.macros.exportTiddlers.getFilterDate(val,'exportEndDate');
		var filterTags=$('exportFilterTags').checked;
		var tags=$('exportTags').value;
		var filterText=$('exportFilterText').checked;
		var text=$('exportText').value;
		if (!(filterStart||filterEnd||filterTags||filterText)) {
			alert('Please set the selection filter');
			$('exportFilterPanel').style.display='block';
			return -1;
		}
		if (filterStart&&filterEnd&&(startDate>endDate)) {
			var msg='starting date/time:\n'
			msg+=startDate.toLocaleString()+'\n';
			msg+='is later than ending date/time:\n'
			msg+=endDate.toLocaleString()
			alert(msg);
			return -1;
		}
		// if filter by tags, get list of matching tiddlers
		// use getMatchingTiddlers() (if MatchTagsPlugin is installed) for full boolean expressions
		// otherwise use getTaggedTiddlers() for simple tag matching
		if (filterTags) {
			var fn=store.getMatchingTiddlers||store.getTaggedTiddlers;
			var t=fn.apply(store,[tags]);
			var tagged=[];
			for (var i=0; i<t.length; i++) tagged.push(t[i].title);
		}
		// scan list and select tiddlers that match all applicable criteria
		var total=0;
		var count=0;
		for (var i=0; i<theList.options.length; i++) {
			// get item, skip non-tiddler list items (section headings)
			var opt=theList.options[i]; if (opt.value=='') continue;
			// get tiddler, skip missing tiddlers (this should NOT happen)
			var tiddler=store.getTiddler(opt.value); if (!tiddler) continue; 
			var sel=true;
			if ( (filterStart && tiddler.modified<startDate)
			|| (filterEnd && tiddler.modified>endDate)
			|| (filterTags && !tagged.contains(tiddler.title))
			|| (filterText && (tiddler.text.indexOf(text)==-1) && (tiddler.title.indexOf(text)==-1)))
				sel=false;
			opt.selected=sel;
			count+=sel?1:0;
			total++;
		}
		return count;
	},
	deleteTiddlers: function() {
		var list=$('exportList'); if (!list) return;
		var tids=[];
		for (i=0;i<list.length;i++)
			if (list.options[i].selected && list.options[i].value.length)
				tids.push(list.options[i].value);
		if (!confirm('Are you sure you want to delete these tiddlers:\n\n'+tids.join(', '))) return;
		store.suspendNotifications();
		for (t=0;t<tids.length;t++) {
			var tid=store.getTiddler(tids[t]); if (!tid) continue;
			var msg="'"+tid.title+"' is tagged with 'systemConfig'.\n\n";
			msg+='Removing this tiddler may cause unexpected results.  Are you sure?'
			if (tid.tags.contains('systemConfig') && !confirm(msg)) continue;
			store.removeTiddler(tid.title);
			story.closeTiddler(tid.title);
		}
		store.resumeNotifications();
		alert(tids.length+' tiddlers deleted');
		this.refreshList(0); // reload listbox
		store.notifyAll(); // update page display
	},
	go: function() {
		if (window.location.protocol!='file:') // make sure we are local
			{ displayMessage(config.messages.notFileUrlError); return; }
		// get selected tidders, target filename, target type, and notes
		var list=$('exportList'); if (!list) return;
		var tids=[]; for (var i=0; i<list.options.length; i++) {
			var opt=list.options[i]; if (!opt.selected||!opt.value.length) continue;
			var tid=store.getTiddler(opt.value); if (!tid) continue;
			tids.push(tid);
		}
		if (!tids.length) return; // no tiddlers selected
		var target = $('exportFilename').value.trim();
		if (!target.length) {
			displayMessage('A local target path/filename is required',target);
			return;
		}
		var filetype = $('exportFormat').value.toLowerCase();
		var notes=$('exportNotes').value.replace(/\n/g,'<br>');
		var total={val:0};
		var out=this.assembleFile(target,filetype,tids,notes,total);
		var link='file:///'+target.replace(/\\/g,'/');
		var samefile=link==decodeURIComponent(window.location.href);
		var p=getLocalPath(document.location.href);
		if (samefile) {
			if (config.options.chkSaveBackups) { var t=loadOriginal(p);if(t)saveBackup(p,t); }
			if (config.options.chkGenerateAnRssFeed && saveRss instanceof Function) saveRss(p);
		}
		var ok=saveFile(target,out);
		displayMessage((ok?this.okmsg:this.failmsg).format([total.val,target]),link);
	},
	plainTextHeader:
		 '// Source'+':\n//\t%0\n'
		+'// Title:\n//\t%1\n'
		+'// Subtitle:\n//\t%2\n'
		+'// Created:\n//\t%3 by %4\n'
		+'// Application:\n//\tTiddlyWiki %5 / %6 %7\n',
	plainTextTiddler:
		'\n// ----- %0 (by %1 on %2) -----\n\n%3',
	plainTextFooter:
		'',
	newsFeedHeader:
		 '<'+'?xml version="1.0"?'+'>\n'
		+'<rss version="2.0">\n'
		+'<channel>\n'
		+'<title>%1</title>\n'
		+'<link>%0</link>\n'
		+'<description>%2</description>\n'
		+'<language>en-us</language>\n'
		+'<copyright>Copyright '+(new Date().getFullYear())+' %4</copyright>\n'
		+'<pubDate>%3</pubDate>\n'
		+'<lastBuildDate>%3</lastBuildDate>\n'
		+'<docs>http://blogs.law.harvard.edu/tech/rss</docs>\n'
		+'<generator>TiddlyWiki %5 / %6 %7</generator>\n',
	newsFeedTiddler:
		'\n%0\n',
	newsFeedFooter:
		'</channel></rss>',
	pureStoreHeader:
		 '<html><body>'
		+'<style type="text/css">'
		+'	#storeArea {display:block;margin:1em;}'
		+'	#storeArea div {padding:0.5em;margin:1em;border:2px solid black;height:10em;overflow:auto;}'
		+'	#pureStoreHeading {width:100%;text-align:left;background-color:#eeeeee;padding:1em;}'
		+'</style>'
		+'<div id="pureStoreHeading">'
		+'	TiddlyWiki "PureStore" export file<br>'
		+'	Source'+': <b>%0</b><br>'
		+'	Title: <b>%1</b><br>'
		+'	Subtitle: <b>%2</b><br>'
		+'	Created: <b>%3</b> by <b>%4</b><br>'
		+'	TiddlyWiki %5 / %6 %7<br>'
		+'	Notes:<hr><pre>%8</pre>'
		+'</div>'
		+'<div id="storeArea">',
	pureStoreTiddler:
		'%0\n%1',
	pureStoreFooter:
		'</div><!--POST-BODY-START-->\n<!--POST-BODY-END--></body></html>',
	assembleFile: function(target,filetype,tids,notes,total) {
		var revised='';
		var now = new Date().toLocaleString();
		var src=convertUnicodeToUTF8(document.location.href);
		var title = convertUnicodeToUTF8(wikifyPlain('SiteTitle').htmlEncode());
		var subtitle = convertUnicodeToUTF8(wikifyPlain('SiteSubtitle').htmlEncode());
		var user = convertUnicodeToUTF8(config.options.txtUserName.htmlEncode());
		var twver = version.major+'.'+version.minor+'.'+version.revision;
		var v=version.extensions.ExportTiddlersPlugin; var pver = v.major+'.'+v.minor+'.'+v.revision;
		var headerargs=[src,title,subtitle,now,user,twver,'ExportTiddlersPlugin',pver,notes];
		switch (filetype) {
			case this.type_TX: // plain text
				var header=this.plainTextHeader.format(headerargs);
				var footer=this.plainTextFooter;
				break;
			case this.type_NF: // news feed (XML)
				headerargs[0]=store.getTiddlerText('SiteUrl','');
				var header=this.newsFeedHeader.format(headerargs);
				var footer=this.newsFeedFooter;
				break;
			case this.type_PS: // PureStore (no code)
				var header=this.pureStoreHeader.format(headerargs);
				var footer=this.pureStoreFooter;
				break;
			case this.type_TW: // full TiddlyWiki
			default:
				var currPath=getLocalPath(window.location.href);
				var original=loadFile(currPath);
				if (!original) { displayMessage(config.messages.cantSaveError); return; }
				var posDiv = locateStoreArea(original);
				if (!posDiv) { displayMessage(config.messages.invalidFileError.format([currPath])); return; }
				var header = original.substr(0,posDiv[0]+startSaveArea.length)+'\n';
				var footer = '\n'+original.substr(posDiv[1]);
				break;
		}
		var out=this.getData(target,filetype,tids);
		var revised = header+convertUnicodeToUTF8(out.join('\n'))+footer;
		// if full TW, insert page title and language attr, and reset all MARKUP blocks...
		if (filetype==this.type_TW) {
			var newSiteTitle=convertUnicodeToUTF8(getPageTitle()).htmlEncode();
			revised=revised.replaceChunk('<title'+'>','</title'+'>',' ' + newSiteTitle + ' ');
			revised=updateLanguageAttribute(revised);
			var titles=[]; for (var i=0; i<tids.length; i++) titles.push(tids[i].title);
			revised=updateMarkupBlock(revised,'PRE-HEAD',
				titles.contains('MarkupPreHead')? 'MarkupPreHead' :null);
			revised=updateMarkupBlock(revised,'POST-HEAD',
				titles.contains('MarkupPostHead')?'MarkupPostHead':null);
			revised=updateMarkupBlock(revised,'PRE-BODY',
				titles.contains('MarkupPreBody')? 'MarkupPreBody' :null);
			revised=updateMarkupBlock(revised,'POST-SCRIPT',
				titles.contains('MarkupPostBody')?'MarkupPostBody':null);
		}
		total.val=out.length;
		return revised;
	},
	formatItem: function(s,f,t,u) {
		if (f==this.type_TW)
			var r=s.getSaver().externalizeTiddler(s,t);
		if (f==this.type_PS)
			var r=config.macros.exportTiddlers.pureStoreTiddler.format([t.title,s.getSaver().externalizeTiddler(s,t)]);
		if (f==this.type_NF)
			var r=this.newsFeedTiddler.format([t.saveToRss(u)]);
		if (f==this.type_TX)
			var r=this.plainTextTiddler.format([t.title,t.modifier,t.modified.toLocaleString(),t.text]);
		return r||'';
	},
	getData: function(target,filetype,tids) {
		// output selected tiddlers and gather list of titles (for use with merge)
		var out=[]; var titles=[];
		var url=store.getTiddlerText('SiteUrl','');
		for (var i=0; i<tids.length; i++) {
			out.push(this.formatItem(store,filetype,tids[i],url));
			titles.push(tids[i].title);
		}
		// if TW or PureStore format, ask to merge with existing tiddlers (if any)
		if (filetype==this.type_TW || filetype==this.type_PS) {
			var text=loadFile(target);
			if (text && text.length) {
				var remoteStore=new TiddlyWiki();
				if (remoteStore.importTiddlyWiki(convertUTF8ToUnicode(text))
					&& confirm(this.mergeprompt.format([target]))) {
					var existing=remoteStore.getTiddlers('title');
					for (var i=0; i<existing.length; i++)
						if (!titles.contains(existing[i].title))
							out.push(this.formatItem(remoteStore,filetype,existing[i],url));
					displayMessage(this.mergestatus.format([tids.length,out.length-tids.length]));
				}
			}
		}
		return out;
	}
}
//}}}
!!三次
\[ \mathbf{p}(t) = \left[ \matrix { t^3 & t^2 & t & 1 } \right] \left[ \matrix {
2&-2&1&1\\
-3&3&-2&-1\\
0&0&1&0\\
1&0&0&0
} \right] \left[ \matrix {
\mathbf{a}\\
\mathbf{b}\\
\mathbf{a'}\\
\mathbf{b'}\\
} \right] \]
/***
|Name|ImageSizePlugin|
|Source|http://www.TiddlyTools.com/#ImageSizePlugin|
|Version|1.1.0|
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type|plugin,formatter|
|Requires||
|Overrides|'image' formatter|
|Description|extends image syntax to add optional CSS width/height values|
!!!!!Usage
<<<
Extends standard TiddlyWiki image syntax, ''{{{[img[...]]}}}'', so you can specify CSS width/height values.

The extended syntax is:
>''{{{[img(x,y)[...]]}}}''
>where x and y are the desired width and height of the image, specified using CSS units of measurement (e.g., px, em, cm, in, or %).  Use ''auto'' for either the width or height to scale image proportionally (i.e., maintain aspect ratio).  You may also calculate a CSS value on-the-fly by using //evaluated javascript//, enclosed between """{{""" and """}}""", e.g, {{{({{widthFunction()}},{{heightFunction()}})}}}.

Note: this plugin also includes enhancements to support:
*[[AttachFilePluginFormatters]] (embed image files as text-encoded tiddlers)
* [[ImagePathPlugin]] (fallback locations for missing images)
Please refer to those plugins for details...
<<<
!!!!!Examples
<<<
{{{
[<img(34%,auto)[images/meow.gif]]
[<img(21%,auto)[images/meow.gif]]
[<img(13%,auto)[images/meow.gif]]
[<img(8%,auto)[images/meow.gif]]
[<img(5%,auto)[images/meow.gif]]
[<img(3%,auto)[images/meow.gif]]
[<img(2%,auto)[images/meow.gif]]
[img(1%,auto)[images/meow.gif]]
}}}
[<img(34%,auto)[images/meow.gif]]
[<img(21%,auto)[images/meow.gif]]
[<img(13%,auto)[images/meow.gif]]
[<img(8%,auto)[images/meow.gif]]
[<img(5%,auto)[images/meow.gif]]
[<img(3%,auto)[images/meow.gif]]
[<img(2%,auto)[images/meow.gif]]
[img(1%,auto)[images/meow.gif]]
{{clear block{}}}
<<<
!!!!!Revisions
<<<
2008.01.19 [1.1.0] added support for evaluated width/height values!!
2008.01.18 [1.0.1] code cleanup plus improved regexp for matching "(width,height)" by eliminating hard-coded recognition of [px,em,cm,in,%] CSS units.  Syntax now accepts ANY values for width/height, and leaves it to the browser's CSS processing to handle any invalid values.
2008.01.17 [1.0.0] initial release
<<<
!!!!!Code
***/
//{{{
version.extensions.ImageSizePlugin= {major: 1, minor: 1, revision: 0, date: new Date(2008,1,19)};

// replace standard handler for image formatter
// note: includes modifications for [[AttachFilePluginFormatters]] AND [[ImagePathPlugin]]
var f=config.formatters.findByField("name","image");
config.formatters[f].match="\\[[<>]?[Ii][Mm][Gg](?:\\([^,]*,[^\\)]*\\))?\\[";
config.formatters[f].lookaheadRegExp=/\[([<]?)(>?)[Ii][Mm][Gg](\([^,]*,[^\)]*\))?\[(?:([^\|\]]+)\|)?([^\[\]\|]+)\](?:\[([^\]]*)\])?\]/mg;
config.formatters[f].handler=function(w) {
	this.lookaheadRegExp.lastIndex = w.matchStart;
	var lookaheadMatch = this.lookaheadRegExp.exec(w.source)
	if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
		var floatLeft=lookaheadMatch[1];
		var floatRight=lookaheadMatch[2];
		var XY=lookaheadMatch[3];
		var tooltip=lookaheadMatch[4];
		var src=lookaheadMatch[5];
		var link=lookaheadMatch[6];
		// Simple bracketted link
		var e = w.output;
		if(link) { // LINKED IMAGE
			if (config.formatterHelpers.isExternalLink(link)) {
				if (config.macros.attach && config.macros.attach.isAttachment(link)) {
					// see [[AttachFilePluginFormatters]]
					e = createExternalLink(w.output,link);
					e.href=config.macros.attach.getAttachment(link);
					e.title = config.macros.attach.linkTooltip + link;
				} else
					e = createExternalLink(w.output,link);
			} else 
				e = createTiddlyLink(w.output,link,false,null,w.isStatic);
			addClass(e,"imageLink");
		}
		var img = createTiddlyElement(e,"img");
		if(floatLeft) img.align="left"; else if(floatRight) img.align="right"; // FLOAT LEFT/RIGHT
		if(XY) { // CUSTOM SIZE with optional EVAL'ED width/height ({{...}},{{...}})
			var parts=XY.replace(/[\(\)]/g,'').split(","); var x=parts[0]; var y=parts[1];
			if (x.substr(0,2)=="{{") {
				try{img.style.width=eval(x.substr(2,x.length-4));}
				catch(e){displayMessage(e.description||e.toString())}
			} else img.style.width=x;

			if (y.substr(0,2)=="{{") {
				try{img.style.height=eval(y.substr(2,y.length-4));}
				catch(e){displayMessage(e.description||e.toString())}
			} else img.style.height=y;
		}
		if(tooltip) img.title = tooltip; // TOOLTIP
		// GET IMAGE SOURCE (get attachment or resolve fallback path as needed)
		if (config.macros.attach && config.macros.attach.isAttachment(src))
			src=config.macros.attach.getAttachment(src); // see [[AttachFilePluginFormatters]]
		else if (config.formatterHelpers.resolvePath) { // see [[ImagePathPlugin]]
			// Note: IE and Safari use onError to call resolvePath() only if initial lookup fails
			// (avoids security messages for initial filesystem access)... otherwise, attempt to
			// resolve the original path/file before initial rendering
			if (config.browser.isIE || config.browser.isSafari) {
				img.onerror=(function(){
					this.src=config.formatterHelpers.resolvePath(this.src,false);
					return false;
				});
			} else
				src=config.formatterHelpers.resolvePath(lookaheadMatch[5],true);
		}
		img.src=src; // RENDER IMAGE
		w.nextMatch = this.lookaheadRegExp.lastIndex;
	}
}
//}}}
/***
|Name|ImportTiddlersPlugin|
|Source|http://www.TiddlyTools.com/#ImportTiddlersPlugin|
|Documentation|http://www.TiddlyTools.com/#ImportTiddlersPluginInfo|
|Version|4.4.0|
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type|plugin|
|Requires||
|Overrides|config.macros.importTiddlers.handler|
|Description|interactive controls for import/export with filtering.|
This plugin lets you selectively combine tiddlers from any two TiddlyWiki documents.  An interactive control panel lets you pick a document to import from, and then select which tiddlers to import, with prompting for skip, rename, merge or replace actions when importing tiddlers that match existing titles.  Automatically add tags to imported tiddlers so they are easy to find later on.  Generates a detailed report of import 'history' in ImportedTiddlers.
!!!!!Documentation
<<<
see [[ImportTiddlersPluginInfo]] for details
<<<
!!!!!interactive control panel:
<<<
<<importTiddlers inline>>
{{clear{
^^(see also: [[ImportTiddlers]] shadow tiddler)^^}}}
<<<
!!!!!Installation Notes
<<<
* As of 6/27/2007, 'patch' functions that provide backward-compatibility with TW2.1.x and earlier have been split into a separate [[ImportTiddlersPluginPatch]] tiddler to reduce installation overhead for //this// plugin.  You only need to install the additional plugin tiddler when using ImportTiddlersPlugin in documents using TW2.1.x or earlier.
* As of 3/21/2007, the interactive {{{<<importTiddlers>>}}} and non-interactive {{{<<loadTiddlers>>}}} macro definitions and related code have been split into separate [[ImportTiddlersPlugin]] and [[LoadTiddlersPlugin]] to permit selective installation of either the interactive and/or non-interactive macro functions.
* Quick Installation Tip: If you are using an unmodified version of TiddlyWiki (core release version <<version>>), you can get a new, empty TiddlyWiki with the Import Tiddlers plugin pre-installed (''[[download from here|TW+ImportExport.html]]''), and then simply import all your content from your old document into this new, empty document.
<<<
!!!!!Revisions
<<<
2008.09.30 [4.4.0] added fallback definition of merge() for use with TW2.0.x and TW1.2.x
2008.08.12 [4.3.3] rewrite backstage and shadow tiddler definitions for easier customization
|please see [[ImportTiddlersPluginInfo]] for additional revision details|
2005.07.20 [1.0.0] Initial Release
<<<
!!!!!Code
***/
//{{{
version.extensions.ImportTiddlersPlugin= {major: 4, minor: 4, revision: 0, date: new Date(2008,8,30)};

// IE needs explicit global scoping for functions/vars called from browser events
window.onClickImportButton=onClickImportButton;
window.refreshImportList=refreshImportList;

// default cookie/option values
if (!config.options.chkImportReport) config.options.chkImportReport=true;

// default shadow definition
config.shadowTiddlers.ImportTiddlers='<<importTiddlers inline>>';

// use shadow tiddler content in backstage panel
if (config.tasks) config.tasks.importTask.content='<<tiddler ImportTiddlers>>' // TW2.2 or above

// $(...) function: 'shorthand' convenience syntax for document.getElementById()
if (typeof($)=='undefined') { // avoid redefinition
window.$ = function() {
	var elements=new Array();
	for (var i=0; i<arguments.length; i++) {
		var element=arguments[i];
		if (typeof element=='string') element=document.getElementById(element);
		if (arguments.length==1) return element;
		elements.push(element);
	}
	return elements;
}
}
//}}}
//{{{
// backward-compatiblity for TW2.0.x and TW1.2.x
if (config.macros.importTiddlers==undefined)
	config.macros.importTiddlers={};
if (typeof merge=='undefined') {
	function merge(dst,src,preserveExisting) {
		for(var i in src)
			{ if(!preserveExisting || dst[i] === undefined) dst[i] = src[i]; }
		return dst;
	}
}
if (config.browser.isGecko===undefined)
	config.browser.isGecko=(config.userAgent.indexOf('gecko')!=-1);
//}}}
//{{{
merge(config.macros.importTiddlers,{
	label: 'import tiddlers',
	prompt: 'Copy tiddlers from another document',
	openMsg: 'Opening %0',
	openErrMsg: 'Could not open %0 - error=%1',
	readMsg: 'Read %0 bytes from %1',
	foundMsg: 'Found %0 tiddlers in %1',
	filterMsg: "Filtered %0 tiddlers matching '%1'",
	summaryMsg: '%0 tiddler%1 in the list',
	summaryFilteredMsg: '%0 of %1 tiddler%2 in the list',
	plural: 's are',
	single: ' is',
	countMsg: '%0 tiddlers selected for import',
	processedMsg: 'Processed %0 tiddlers',
	importedMsg: 'Imported %0 of %1 tiddlers from %2',
	loadText: 'please load a document...',
	closeText: 'close',
	doneText: 'done',
	startText: 'import',
	stopText: 'stop',
	local: true,		// default to import from local file
	src: '',		// path/filename or URL of document to import (retrieved from SiteUrl)
	proxy: '',		// URL for remote proxy script (retrieved from SiteProxy)
	useProxy: false,	// use specific proxy script in front of remote URL
	inbound: null,		// hash-indexed array of tiddlers from other document
	newTags: '',		// text of tags added to imported tiddlers
	addTags: true,		// add new tags to imported tiddlers
	listsize: 10,		// # of lines to show in imported tiddler list
	importTags: true,	// include tags from remote source document when importing a tiddler
	keepTags: true,		// retain existing tags when replacing a tiddler
	sync: false,		// add 'server' fields to imported tiddlers (for sync function)
	lastFilter: '',		// most recent filter (URL hash) applied
	lastAction: null,	// most recent collision button performed
	index: 0,		// current processing index in import list
	sort: ''		// sort order for imported tiddler listbox
});
//}}}
//{{{
// replace core macro handler
if (config.macros.importTiddlers.coreHandler==undefined)
	config.macros.importTiddlers.coreHandler=config.macros.importTiddlers.handler; // save built-in handler
config.macros.importTiddlers.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
	if (!params[0] || params[0].toLowerCase()=='core') { // default to built in
		if (config.macros.importTiddlers.coreHandler)
			config.macros.importTiddlers.coreHandler.apply(this,arguments);
		else 
			createTiddlyButton(place,this.label,this.prompt,onClickImportMenu);
	} else if (params[0]=='link') { // show link to floating panel
		createTiddlyButton(place,params[1]||this.label,params[2]||this.prompt,onClickImportMenu);
	} else if (params[0]=='inline') {// show panel as INLINE tiddler content
		createImportPanel(place);
		$('importPanel').style.position='static';
		$('importPanel').style.display='block';
	} else if (config.macros.loadTiddlers)
		config.macros.loadTiddlers.handler(place,macroName,params); // any other params: loadtiddlers
}
//}}}
//{{{
// Handle link click to create/show/hide control panel
function onClickImportMenu(e)
{
	if (!e) var e = window.event;
	var parent=resolveTarget(e).parentNode;
	var panel = $('importPanel');
	if (panel==undefined || panel.parentNode!=parent)
		panel=createImportPanel(parent);
	var isOpen = panel.style.display=='block';
	if(config.options.chkAnimate)
		anim.startAnimating(new Slider(panel,!isOpen,e.shiftKey || e.altKey,'none'));
	else
		panel.style.display = isOpen ? 'none' : 'block' ;
	e.cancelBubble = true;
	if (e.stopPropagation) e.stopPropagation();
	return(false);
}
//}}}
//{{{
// Create control panel: HTML, CSS
function createImportPanel(place) {
	var cmi=config.macros.importTiddlers; // abbreviation
	var panel=$('importPanel');
	if (panel) { panel.parentNode.removeChild(panel); }
	setStylesheet(cmi.css,'importTiddlers');
	panel=createTiddlyElement(place,'span','importPanel',null,null)
	panel.innerHTML=cmi.html;
	refreshImportList();
	var siteURL=store.getTiddlerText('SiteUrl'); if (!siteURL) siteURL='';
	$('importSourceURL').value=siteURL;
	cmi.src=siteURL;
	var siteProxy=store.getTiddlerText('SiteProxy'); if (!siteProxy) siteProxy='SiteProxy';
	$('importSiteProxy').value=siteProxy;
	cmi.proxy=siteProxy;
	if (config.browser.isGecko) { // FF3 FIXUP
		$('fileImportSource').style.display='none';
		$('importLocalPanelFix').style.display='block';
	}
	return panel;
}
//}}}
//{{{
config.macros.importTiddlers.css = '\
#importPanel {\
	display: none; position:absolute; z-index:11; width:35em; right:105%; top:3em;\
	background-color: #eee; color:#000; font-size: 8pt; line-height:110%;\
	border:1px solid black; border-bottom-width: 3px; border-right-width: 3px;\
	padding: 0.5em; margin:0em; -moz-border-radius:1em;-webkit-border-radius:1em;\
}\
#importPanel a, #importPanel td a { color:#009; display:inline; margin:0px; padding:1px; }\
#importPanel table { width:100%; border:0px; padding:0px; margin:0px; font-size:8pt; line-height:110%; background:transparent; }\
#importPanel tr { border:0px;padding:0px;margin:0px; background:transparent; }\
#importPanel td { color:#000; border:0px;padding:0px;margin:0px; background:transparent; }\
#importPanel select { width:100%;margin:0px;font-size:8pt;line-height:110%;}\
#importPanel input  { width:98%;padding:0px;margin:0px;font-size:8pt;line-height:110%}\
#importPanel .box { border:1px solid #000; background-color:#eee; padding:3px 5px; margin-bottom:5px; -moz-border-radius:5px;-webkit-border-radius:5px;}\
#importPanel .topline { border-top:1px solid #999; padding-top:2px; margin-top:2px; }\
#importPanel .rad { width:auto; }\
#importPanel .chk { width:auto; margin:1px;border:0; }\
#importPanel .btn { width:auto; }\
#importPanel .btn1 { width:98%; }\
#importPanel .btn2 { width:48%; }\
#importPanel .btn3 { width:32%; }\
#importPanel .btn4 { width:23%; }\
#importPanel .btn5 { width:19%; }\
#importPanel .importButton { padding: 0em; margin: 0px; font-size:8pt; }\
#importPanel .importListButton { padding:0em 0.25em 0em 0.25em; color: #000000; display:inline }\
#backstagePanel #importPanel { left:10%; right:auto; }\
';
//}}}
//{{{
config.macros.importTiddlers.html = '\
<!-- source and report -->\
<table><tr><td align=left>\
	import from\
	<input type="radio" class="rad" name="importFrom" id="importFromFile" value="file" CHECKED\
		onclick="onClickImportButton(this,event)" title="show file controls"> local file\
	<input type="radio" class="rad" name="importFrom" id="importFromWeb"  value="http"\
		onclick="onClickImportButton(this,event)" title="show web controls"> web server\
</td><td align=right>\
	<input type=checkbox class="chk" id="chkImportReport" checked\
		onClick="config.options[\'chkImportReport\']=this.checked;"> create report\
</td></tr></table>\
\
<div class="box" id="importSourcePanel" style="margin:.5em">\
<div id="importLocalPanel" style="display:block;margin-bottom:2px;"><!-- import from local file  -->\
enter or browse for source path/filename<br>\
<input type="file" id="fileImportSource" size=57 style="width:100%"\
	onKeyUp="config.macros.importTiddlers.src=this.value"\
	onChange="config.macros.importTiddlers.src=this.value;$(\'importLoad\').onclick()">\
<div id="importLocalPanelFix" style="display:none"><!-- FF3 FIXUP -->\
	<input type="text" id="fileImportSourceFix" style="width:90%"\
		title="Enter a path/file to import"\
		onKeyUp="config.macros.importTiddlers.src=this.value"\
		onChange="config.macros.importTiddlers.src=this.value; $(\'importLoad\').onclick()">\
	<input type="button" id="fileImportSourceFixButton" style="width:7%" value="..."\
		title="Select a path/file to import"\
		onClick="var r=config.macros.importTiddlers.askForFilename(this); if (!r||!r.length) return;\
			$(\'fileImportSourceFix\').value=r;\
			config.macros.importTiddlers.src=r;\
			$(\'importLoad\').onclick()">\
</div><!--end FF3 FIXUP-->\
</div><!--end local-->\
<div id="importHTTPPanel" style="display:none;margin-bottom:2px;"><!-- import from http server -->\
<table><tr><td align=left>\
	enter a URL or <a href="javascript:;" id="importSelectFeed"\
		onclick="onClickImportButton(this,event)" title="select a pre-defined \'systemServer\' URL">\
		select a server</a><br>\
</td><td align=right>\
	<input type="checkbox" class="chk" id="importUsePassword"\
		onClick="config.macros.importTiddlers.usePassword=this.checked;\
			config.macros.importTiddlers.showPanel(\'importIDPWPanel\',this.checked,true);">password\
	<input type="checkbox" class="chk" id="importUseProxy"\
		onClick="config.macros.importTiddlers.useProxy=this.checked;\
			config.macros.importTiddlers.showPanel(\'importSiteProxy\',this.checked,true);">proxy\
</td></tr></table>\
<input type="text" id="importSiteProxy" style="display:none;margin-bottom:1px" onfocus="this.select()" value="SiteProxy"\
	onKeyUp="config.macros.importTiddlers.proxy=this.value"\
	onChange="config.macros.importTiddlers.proxy=this.value;">\
<input type="text" id="importSourceURL" onfocus="this.select()" value="SiteUrl"\
	onKeyUp="config.macros.importTiddlers.src=this.value"\
	onChange="config.macros.importTiddlers.src=this.value;">\
<div id="importIDPWPanel" style="text-align:center;margin-top:2px;display:none";>\
username: <input type=text id="txtImportID" style="width:25%" \
	onChange="config.options.txtRemoteUsername=this.value;">\
 password: <input type=password id="txtImportPW" style="width:25%" \
	onChange="config.options.txtRemotePassword=this.value;">\
</div><!--end idpw-->\
</div><!--end http-->\
</div><!--end source-->\
\
<div class="box" id="importSelectPanel" style="display:none;margin:.5em;">\
<table><tr><td align=left>\
select:\
<a href="javascript:;" id="importSelectAll"\
	onclick="onClickImportButton(this);return false;" title="SELECT all tiddlers">\
	all</a>\
&nbsp;<a href="javascript:;" id="importSelectNew"\
	onclick="onClickImportButton(this);return false;" title="SELECT tiddlers not already in destination document">\
	added</a>\
&nbsp;<a href="javascript:;" id="importSelectChanges"\
	onclick="onClickImportButton(this);return false;" title="SELECT tiddlers that have been updated in source document">\
	changes</a>\
&nbsp;<a href="javascript:;" id="importSelectDifferences"\
	onclick="onClickImportButton(this);return false;" title="SELECT tiddlers that have been added or are different from existing tiddlers">\
	differences</a>\
</td><td align=right>\
<a href="javascript:;" id="importListSmaller"\
	onclick="onClickImportButton(this);return false;" title="SHRINK list size">\
	&nbsp;&#150;&nbsp;</a>\
<a href="javascript:;" id="importListLarger"\
	onclick="onClickImportButton(this);return false;" title="GROW list size">\
	&nbsp;+&nbsp;</a>\
<a href="javascript:;" id="importListMaximize"\
	onclick="onClickImportButton(this);return false;" title="MAXIMIZE/RESTORE list size">\
	&nbsp;=&nbsp;</a>\
</td></tr></table>\
<select id="importList" size=8 multiple\
	onchange="setTimeout(\'refreshImportList(\'+this.selectedIndex+\')\',1)">\
	<!-- NOTE: delay refresh so list is updated AFTER onchange event is handled -->\
</select>\
<div style="text-align:center">\
	<a href="javascript:;"\
		title="click for help using filters..."\
		onclick="alert(\'A filter consists of one or more space-separated combinations of:\\n\\ntiddler titles\\ntag:[[tagvalue]]\\ntag:[[tag expression]] (requires MatchTagsPlugin)\\nstory:[[TiddlerName]]\\nsearch:[[searchtext]]\\n\\nUse a blank filter for all tiddlers.\')"\
	>filter</a>\
	<input type="text" id="importLastFilter" style="margin-bottom:1px; width:65%"\
		title="Enter a combination of one or more filters. Use a blank filter for all tiddlers."\
		onfocus="this.select()" value=""\
		onKeyUp="config.macros.importTiddlers.lastFilter=this.value"\
		onChange="config.macros.importTiddlers.lastFilter=this.value;">\
	<input type="button" id="importApplyFilter" style="width:20%" value="apply"\
		title="filter list of tiddlers to include only those that match certain criteria"\
		onclick="onClickImportButton(this)">\
	</div>\
</div><!--end select-->\
\
<div class="box" id="importOptionsPanel" style="text-align:center;margin:.5em;display:none;">\
	apply tags: <input type=checkbox class="chk" id="chkImportTags" checked\
		onClick="config.macros.importTiddlers.importTags=this.checked;">from source&nbsp;\
	<input type=checkbox class="chk" id="chkKeepTags" checked\
		onClick="config.macros.importTiddlers.keepTags=this.checked;">keep existing&nbsp;\
	<input type=checkbox class="chk" id="chkAddTags" \
		onClick="config.macros.importTiddlers.addTags=this.checked;\
			config.macros.importTiddlers.showPanel(\'txtNewTags\',this.checked,true);\
			if (this.checked) $(\'txtNewTags\').focus();">add tags<br>\
	<input type=text id="txtNewTags" style="margin-top:4px;display:none;" size=15\ onfocus="this.select()" \
		title="enter tags to be added to imported tiddlers" \
		onKeyUp="config.macros.importTiddlers.newTags=this.value;\
		$(\'chkAddTags\').checked=this.value.length>0;" autocomplete=off>\
	<nobr><input type=checkbox class="chk" id="chkSync" \
		onClick="config.macros.importTiddlers.sync=this.checked;">\
		link imported tiddlers to source document (for sync later)</nobr>\
</div><!--end options-->\
\
<div id="importButtonPanel" style="text-align:center">\
	<input type=button id="importLoad"	class="importButton btn3" value="open"\
		title="load listbox with tiddlers from source document"\
		onclick="onClickImportButton(this)">\
	<input type=button id="importOptions"	class="importButton btn3" value="options..."\
		title="set options for tags, sync, etc."\
		onclick="onClickImportButton(this)">\
	<input type=button id="importStart"	class="importButton btn3" value="import"\
		title="start/stop import of selected source tiddlers into current document"\
		onclick="onClickImportButton(this)">\
	<input type=button id="importClose"	class="importButton btn3" value="done"\
		title="clear listbox or hide control panel"\
		onclick="onClickImportButton(this)">\
</div>\
\
<div class="none" id="importCollisionPanel" style="display:none;margin:.5em 0 .5em .5em;">\
	<table><tr><td style="width:65%" align="left">\
		<table><tr><td align=left>\
			tiddler already exists:\
		</td><td align=right>\
			<input type=checkbox class="chk" id="importApplyToAll" \
			onclick="$(\'importRename\').disabled=this.checked;"\
			checked>apply to all\
		</td></tr></table>\
		<input type=text id="importNewTitle" size=15 autocomplete=off">\
	</td><td style="width:34%" align="center">\
		<input type=button id="importMerge"\
			class="importButton" style="width:47%" value="merge"\
			title="append the incoming tiddler to the existing tiddler"\
			onclick="onClickImportButton(this)"><!--\
		--><input type=button id="importSkip"\
			class="importButton" style="width:47%" value="skip"\
			title="do not import this tiddler"\
			onclick="onClickImportButton(this)"><!--\
		--><br><input type=button id="importRename"\
			class="importButton" style="width:47%" value="rename"\
			title="rename the incoming tiddler"\
			onclick="onClickImportButton(this)"><!--\
		--><input type=button id="importReplace"\
			class="importButton" style="width:47%" value="replace"\
			title="discard the existing tiddler"\
			onclick="onClickImportButton(this)">\
	</td></tr></table>\
</div><!--end collision-->\
';
//}}}
//{{{
// process control interactions
function onClickImportButton(which,event)
{
	var cmi=config.macros.importTiddlers; // abbreviation

	var list = $('importList');
	if (!list) return;
	var thePanel = $('importPanel');
	var theCollisionPanel = $('importCollisionPanel');
	var theNewTitle = $('importNewTitle');
	var count=0;
	switch (which.id)
		{
		case 'importFromFile':	// show local panel
		case 'importFromWeb':	// show HTTP panel
			cmi.local=(which.id=='importFromFile');
			cmi.showPanel('importLocalPanel',cmi.local);
			cmi.showPanel('importHTTPPanel',!cmi.local);
			break;
		case 'importOptions':	// show/hide options panel
			cmi.showPanel('importOptionsPanel',$('importOptionsPanel').style.display=='none');
			break;
		case 'fileImportSource':
		case 'importLoad':		// load import source into hidden frame
			importReport();		// if an import was in progress, generate a report
			cmi.inbound=null;	// clear the imported tiddler buffer
			refreshImportList();	// reset/resize the listbox
			if (cmi.src=='') break;
			// Load document, read it's DOM and fill the list
			cmi.loadRemoteFile(cmi.src,cmi.filterTiddlerList);
			break;
		case 'importSelectFeed':	// select a pre-defined systemServer feed URL
			var p=Popup.create(which); if (!p) return;
			var tids=store.getTaggedTiddlers('systemServer');
			if (!tids.length)
				createTiddlyText(createTiddlyElement(p,'li'),'no pre-defined server feeds');
			for (var t=0; t<tids.length; t++) {
				var u=store.getTiddlerSlice(tids[t].title,'URL');
				var d=store.getTiddlerSlice(tids[t].title,'Description');
				if (!d||!d.length) d=store.getTiddlerSlice(tids[t].title,'description');
				if (!d||!d.length) d=u;
				createTiddlyButton(createTiddlyElement(p,'li'),tids[t].title,d,
					function(){
						var u=this.getAttribute('url');
						$('importSourceURL').value=u;
						config.macros.importTiddlers.src=u;
						$('importLoad').onclick();
					},
					null,null,null,{url:u});
			}
			Popup.show(p,false);
			event.cancelBubble = true;
			if (event.stopPropagation) event.stopPropagation();
			return(false);
			// create popup with feed list
			// onselect, insert feed URL into input field.
			break;
		case 'importSelectAll':		// select all tiddler list items (i.e., not headings)
			importReport();		// if an import was in progress, generate a report
			for (var t=0,count=0; t < list.options.length; t++) {
				if (list.options[t].value=='') continue;
				list.options[t].selected=true;
				count++;
			}
			clearMessage(); displayMessage(cmi.countMsg.format([count]));
			$('importStart').disabled=!count;
			break;
		case 'importSelectNew':		// select tiddlers not in current document
			importReport();		// if an import was in progress, generate a report
			for (var t=0,count=0; t < list.options.length; t++) {
				list.options[t].selected=false;
				if (list.options[t].value=='') continue;
				list.options[t].selected=!store.tiddlerExists(list.options[t].value);
				count+=list.options[t].selected?1:0;
			}
			clearMessage(); displayMessage(cmi.countMsg.format([count]));
			$('importStart').disabled=!count;
			break;
		case 'importSelectChanges':		// select tiddlers that are updated from existing tiddlers
			importReport();		// if an import was in progress, generate a report
			for (var t=0,count=0; t < list.options.length; t++) {
				list.options[t].selected=false;
				if (list.options[t].value==''||!store.tiddlerExists(list.options[t].value)) continue;
				for (var i=0; i<cmi.inbound.length; i++) // find matching inbound tiddler
					{ var inbound=cmi.inbound[i]; if (inbound.title==list.options[t].value) break; }
				list.options[t].selected=(inbound.modified-store.getTiddler(list.options[t].value).modified>0); // updated tiddler
				count+=list.options[t].selected?1:0;
			}
			clearMessage(); displayMessage(cmi.countMsg.format([count]));
			$('importStart').disabled=!count;
			break;
		case 'importSelectDifferences':		// select tiddlers that are new or different from existing tiddlers
			importReport();		// if an import was in progress, generate a report
			for (var t=0,count=0; t < list.options.length; t++) {
				list.options[t].selected=false;
				if (list.options[t].value=='') continue;
				if (!store.tiddlerExists(list.options[t].value)) { list.options[t].selected=true; count++; continue; }
				for (var i=0; i<cmi.inbound.length; i++) // find matching inbound tiddler
					{ var inbound=cmi.inbound[i]; if (inbound.title==list.options[t].value) break; }
				list.options[t].selected=(inbound.modified-store.getTiddler(list.options[t].value).modified!=0); // changed tiddler
				count+=list.options[t].selected?1:0;
			}
			clearMessage(); displayMessage(cmi.countMsg.format([count]));
			$('importStart').disabled=!count;
			break;
		case 'importApplyFilter':	// filter list to include only matching tiddlers
			importReport();		// if an import was in progress, generate a report
			clearMessage();
			if (!cmi.all) // no tiddlers loaded = '0 selected'
				{ displayMessage(cmi.countMsg.format([0])); return false; }
			var hash=$('importLastFilter').value;
			cmi.inbound=cmi.filterByHash('#'+hash,cmi.all);
			refreshImportList();	// reset/resize the listbox
			break;
		case 'importStart':		// initiate the import processing
			importReport();		// if an import was in progress, generate a report
			$('importApplyToAll').checked=false;
			$('importStart').value=cmi.stopText;
			if (cmi.index>0) cmi.index=-1; // stop processing
			else cmi.index=importTiddlers(0); // or begin processing
			importStopped();
			break;
		case 'importClose':		// unload imported tiddlers or hide the import control panel
			// if imported tiddlers not loaded, close the import control panel
			if (!cmi.inbound) { thePanel.style.display='none'; break; }
			importReport();		// if an import was in progress, generate a report
			cmi.inbound=null;	// clear the imported tiddler buffer
			refreshImportList();	// reset/resize the listbox
			break;
		case 'importSkip':	// don't import the tiddler
			cmi.lastAction=which;
			var theItem	= list.options[cmi.index];
			for (var j=0;j<cmi.inbound.length;j++)
			if (cmi.inbound[j].title==theItem.value) break;
			var theImported = cmi.inbound[j];
			theImported.status='skipped after asking';			// mark item as skipped
			theCollisionPanel.style.display='none';
			cmi.index=importTiddlers(cmi.index+1);	// resume with NEXT item
			importStopped();
			break;
		case 'importRename':		// change name of imported tiddler
			cmi.lastAction=which;
			var theItem		= list.options[cmi.index];
			for (var j=0;j<cmi.inbound.length;j++)
			if (cmi.inbound[j].title==theItem.value) break;
			var theImported		= cmi.inbound[j];
			theImported.status	= 'renamed from '+theImported.title;	// mark item as renamed
			theImported.set(theNewTitle.value,null,null,null,null);		// change the tiddler title
			theItem.value		= theNewTitle.value;			// change the listbox item text
			theItem.text		= theNewTitle.value;			// change the listbox item text
			theCollisionPanel.style.display='none';
			cmi.index=importTiddlers(cmi.index);	// resume with THIS item
			importStopped();
			break;
		case 'importMerge':	// join existing and imported tiddler content
			cmi.lastAction=which;
			var theItem	= list.options[cmi.index];
			for (var j=0;j<cmi.inbound.length;j++)
			if (cmi.inbound[j].title==theItem.value) break;
			var theImported	= cmi.inbound[j];
			var theExisting	= store.getTiddler(theItem.value);
			var theText	= theExisting.text+'\n----\n^^merged from: ';
			theText		+='[['+cmi.src+'#'+theItem.value+'|'+cmi.src+'#'+theItem.value+']]^^\n';
			theText		+='^^'+theImported.modified.toLocaleString()+' by '+theImported.modifier+'^^\n'+theImported.text;
			var theDate	= new Date();
			var theTags	= theExisting.getTags()+' '+theImported.getTags();
			theImported.set(null,theText,null,theDate,theTags);
			theImported.status   = 'merged with '+theExisting.title;	// mark item as merged
			theImported.status  += ' - '+theExisting.modified.formatString('MM/DD/YYYY 0hh:0mm:0ss');
			theImported.status  += ' by '+theExisting.modifier;
			theCollisionPanel.style.display='none';
			cmi.index=importTiddlers(cmi.index);	// resume with this item
			importStopped();
			break;
		case 'importReplace':		// substitute imported tiddler for existing tiddler
			cmi.lastAction=which;
			var theItem		  = list.options[cmi.index];
			for (var j=0;j<cmi.inbound.length;j++)
			if (cmi.inbound[j].title==theItem.value) break;
			var theImported     = cmi.inbound[j];
			var theExisting	  = store.getTiddler(theItem.value);
			theImported.status  = 'replaces '+theExisting.title;		// mark item for replace
			theImported.status += ' - '+theExisting.modified.formatString('MM/DD/YYYY 0hh:0mm:0ss');
			theImported.status += ' by '+theExisting.modifier;
			theCollisionPanel.style.display='none';
			cmi.index=importTiddlers(cmi.index);	// resume with THIS item
			importStopped();
			break;
		case 'importListSmaller':		// decrease current listbox size, minimum=5
			if (list.options.length==1) break;
			list.size-=(list.size>5)?1:0;
			cmi.listsize=list.size;
			break;
		case 'importListLarger':		// increase current listbox size, maximum=number of items in list
			if (list.options.length==1) break;
			list.size+=(list.size<list.options.length)?1:0;
			cmi.listsize=list.size;
			break;
		case 'importListMaximize':	// toggle listbox size between current and maximum
			if (list.options.length==1) break;
			list.size=(list.size==list.options.length)?cmi.listsize:list.options.length;
			break;
		}
}
//}}}
//{{{
config.macros.importTiddlers.showPanel=function(place,show,skipAnim) {
	if (typeof place == 'string') var place=$(place);
	if (!place||!place.style) return;
	if(!skipAnim && anim && config.options.chkAnimate) anim.startAnimating(new Slider(place,show,false,'none'));
	else place.style.display=show?'block':'none';
}
//}}}
//{{{
function refreshImportList(selectedIndex)
{
	var cmi=config.macros.importTiddlers; // abbreviation

	var list  = $('importList');
	if (!list) return;
	// if nothing to show, reset list content and size
	if (!cmi.inbound) 
	{
		while (list.length > 0) { list.options[0] = null; }
		list.options[0]=new Option(cmi.loadText,'',false,false);
		list.size=cmi.listsize;

		// toggle buttons and panels
		$('importLoad').disabled=false;
		$('importLoad').style.display='inline';
		$('importStart').disabled=true;
		$('importOptions').disabled=true;
		$('importOptions').style.display='none';
		$('fileImportSource').disabled=false;
		$('importFromFile').disabled=false;
		$('importFromWeb').disabled=false;
		$('importStart').value=cmi.startText;
		$('importClose').value=cmi.doneText;
		$('importSelectPanel').style.display='none';
		$('importOptionsPanel').style.display='none';
		return;
	}
	// there are inbound tiddlers loaded...
	// toggle buttons and panels
	$('importLoad').disabled=true;
	$('importLoad').style.display='none';
	$('importOptions').style.display='inline';
	$('importOptions').disabled=false;
	$('fileImportSource').disabled=true;
	$('importFromFile').disabled=true;
	$('importFromWeb').disabled=true;
	$('importClose').value=cmi.closeText;
	if ($('importSelectPanel').style.display=='none')
		cmi.showPanel('importSelectPanel',true);

	// get the sort order
	if (!selectedIndex)   selectedIndex=0;
	if (selectedIndex==0) cmi.sort='title';		// heading
	if (selectedIndex==1) cmi.sort='title';
	if (selectedIndex==2) cmi.sort='modified';
	if (selectedIndex==3) cmi.sort='tags';
	if (selectedIndex>3) {
		// display selected tiddler count
		for (var t=0,count=0; t < list.options.length; t++) {
			if (!list.options[t].selected) continue;
			if (list.options[t].value!='')
				count+=1;
			else { // if heading is selected, deselect it, and then select and count all in section
				list.options[t].selected=false;
				for ( t++; t<list.options.length && list.options[t].value!=''; t++) {
					list.options[t].selected=true;
					count++;
				}
			}
		}
		clearMessage(); displayMessage(cmi.countMsg.format([count]));
	}
	$('importStart').disabled=!count;
	if (selectedIndex>3) return; // no refresh needed

	// get the alphasorted list of tiddlers
	var tiddlers=cmi.inbound;
	tiddlers.sort(function (a,b) {if(a['title'] == b['title']) return(0); else return (a['title'] < b['title']) ? -1 : +1; });
	// clear current list contents
	while (list.length > 0) { list.options[0] = null; }
	// add heading and control items to list
	var i=0;
	var indent=String.fromCharCode(160)+String.fromCharCode(160);
	if (cmi.all.length==tiddlers.length)
		var summary=cmi.summaryMsg.format([tiddlers.length,(tiddlers.length!=1)?cmi.plural:cmi.single]);
	else
		var summary=cmi.summaryFilteredMsg.format([tiddlers.length,cmi.all.length,(cmi.all.length!=1)?cmi.plural:cmi.single]);
	list.options[i++]=new Option(summary,'',false,false);
	list.options[i++]=new Option(((cmi.sort=='title'   )?'>':indent)+' [by title]','',false,false);
	list.options[i++]=new Option(((cmi.sort=='modified')?'>':indent)+' [by date]','',false,false);
	list.options[i++]=new Option(((cmi.sort=='tags')?'>':indent)+' [by tags]','',false,false);
	// output the tiddler list
	switch(cmi.sort) {
		case 'title':
			for(var t = 0; t < tiddlers.length; t++)
				list.options[i++] = new Option(tiddlers[t].title,tiddlers[t].title,false,false);
			break;
		case 'modified':
			// sort descending for newest date first
			tiddlers.sort(function (a,b) {if(a['modified'] == b['modified']) return(0); else return (a['modified'] > b['modified']) ? -1 : +1; });
			var lastSection = '';
			for(var t = 0; t < tiddlers.length; t++) {
				var tiddler = tiddlers[t];
				var theSection = tiddler.modified.toLocaleDateString();
				if (theSection != lastSection) {
					list.options[i++] = new Option(theSection,'',false,false);
					lastSection = theSection;
				}
				list.options[i++] = new Option(indent+indent+tiddler.title,tiddler.title,false,false);
			}
			break;
		case 'tags':
			var theTitles = {}; // all tiddler titles, hash indexed by tag value
			var theTags = new Array();
			for(var t=0; t<tiddlers.length; t++) {
				var title=tiddlers[t].title;
				var tags=tiddlers[t].tags;
				if (!tags || !tags.length) {
					if (theTitles['untagged']==undefined) { theTags.push('untagged'); theTitles['untagged']=new Array(); }
					theTitles['untagged'].push(title);
				}
				else for(var s=0; s<tags.length; s++) {
					if (theTitles[tags[s]]==undefined) { theTags.push(tags[s]); theTitles[tags[s]]=new Array(); }
					theTitles[tags[s]].push(title);
				}
			}
			theTags.sort();
			for(var tagindex=0; tagindex<theTags.length; tagindex++) {
				var theTag=theTags[tagindex];
				list.options[i++]=new Option(theTag,'',false,false);
				for(var t=0; t<theTitles[theTag].length; t++)
					list.options[i++]=new Option(indent+indent+theTitles[theTag][t],theTitles[theTag][t],false,false);
			}
			break;
		}
	list.selectedIndex=selectedIndex;		  // select current control item
	if (list.size<cmi.listsize) list.size=cmi.listsize;
	if (list.size>list.options.length) list.size=list.options.length;
}
//}}}
//{{{
// re-entrant processing for handling import with interactive collision prompting
function importTiddlers(startIndex)
{
	var cmi=config.macros.importTiddlers; // abbreviation

	if (!cmi.inbound) return -1;

	var list = $('importList');
	if (!list) return;
	var t;
	// if starting new import, reset import status flags
	if (startIndex==0)
		for (var t=0;t<cmi.inbound.length;t++)
			cmi.inbound[t].status='';
	for (var i=startIndex; i<list.options.length; i++)
		{
		// if list item is not selected or is a heading (i.e., has no value), skip it
		if ((!list.options[i].selected) || ((t=list.options[i].value)==''))
			continue;
		for (var j=0;j<cmi.inbound.length;j++)
			if (cmi.inbound[j].title==t) break;
		var inbound = cmi.inbound[j];
		var theExisting = store.getTiddler(inbound.title);
		// avoid redundant import for tiddlers that are listed multiple times (when 'by tags')
		if (inbound.status=='added')
			continue;
		// don't import the 'ImportedTiddlers' history from the other document...
		if (inbound.title=='ImportedTiddlers')
			continue;
		// if tiddler exists and import not marked for replace or merge, stop importing
		if (theExisting && (inbound.status.substr(0,7)!='replace') && (inbound.status.substr(0,5)!='merge'))
			return i;
		// assemble tags (remote + existing + added)
		var newTags = '';
		if (cmi.importTags)
			newTags+=inbound.getTags()	// import remote tags
		if (cmi.keepTags && theExisting)
			newTags+=' '+theExisting.getTags(); // keep existing tags
		if (cmi.addTags && cmi.newTags.trim().length)
			newTags+=' '+cmi.newTags; // add new tags
		inbound.set(null,null,null,null,newTags.trim());
		// set the status to 'added' (if not already set by the 'ask the user' UI)
		inbound.status=(inbound.status=='')?'added':inbound.status;
		// set sync fields
		if (cmi.sync) {
			if (!inbound.fields) inbound.fields={}; // for TW2.1.x backward-compatibility
			inbound.fields['server.page.revision']=inbound.modified.convertToYYYYMMDDHHMM();
			inbound.fields['server.type']='file';
			inbound.fields['server.host']=(cmi.local?'file://':'')+cmi.src;
		}
		// do the import!
		store.suspendNotifications();
		store.saveTiddler(inbound.title, inbound.title, inbound.text, inbound.modifier, inbound.modified, inbound.tags, inbound.fields, true, inbound.created);
                store.fetchTiddler(inbound.title).created = inbound.created; // force creation date to imported value (needed for TW2.1.x and earlier)
		store.resumeNotifications();
		}
	return(-1);	// signals that we really finished the entire list
}
function importStopped()
{
	var cmi=config.macros.importTiddlers; // abbreviation
	var list = $('importList');
	var theNewTitle = $('importNewTitle');
	if (!list) return;
	if (cmi.index==-1){ 
		$('importStart').value=cmi.startText;
		importReport();		// import finished... generate the report
	} else {
		// import collision...
		// show the collision panel and set the title edit field
		$('importStart').value=cmi.stopText;
		cmi.showPanel('importCollisionPanel',true);
		theNewTitle.value=list.options[cmi.index].value;
		if ($('importApplyToAll').checked
			&& cmi.lastAction
			&& cmi.lastAction.id!='importRename') {
			onClickImportButton(cmi.lastAction);
		}
	}
}
//}}}
//{{{
function importReport()
{
	var cmi=config.macros.importTiddlers; // abbreviation
	if (!cmi.inbound) return;

	// if import was not completed, the collision panel will still be open... close it now.
	var panel=$('importCollisionPanel'); if (panel) panel.style.display='none';

	// get the alphasorted list of tiddlers
	var tiddlers = cmi.inbound;
	// gather the statistics
	var count=0; var total=0;
	for (var t=0; t<tiddlers.length; t++) {
		if (!tiddlers[t].status || !tiddlers[t].status.trim().length) continue;
		if (tiddlers[t].status.substr(0,7)!='skipped') count++;
		total++;
	}
	// generate a report
	if (total) displayMessage(cmi.processedMsg.format([total]));
	if (count && config.options.chkImportReport) {
		// get/create the report tiddler
		var theReport = store.getTiddler('ImportedTiddlers');
		if (!theReport) { theReport=new Tiddler(); theReport.title='ImportedTiddlers'; theReport.text=''; }
		// format the report content
		var now = new Date();
		var newText = 'On '+now.toLocaleString()+', '+config.options.txtUserName
		newText +=' imported '+count+' tiddler'+(count==1?'':'s')+' from\n[['+cmi.src+'|'+cmi.src+']]:\n';
		if (cmi.addTags && cmi.newTags.trim().length)
			newText += 'imported tiddlers were tagged with: "'+cmi.newTags+'"\n';
		newText += '<<<\n';
		for (var t=0; t<tiddlers.length; t++) if (tiddlers[t].status)
			newText += '#[['+tiddlers[t].title+']] - '+tiddlers[t].status+'\n';
		newText += '<<<\n';
		// update the ImportedTiddlers content and show the tiddler
		theReport.text	 = newText+((theReport.text!='')?'\n----\n':'')+theReport.text;
		theReport.modifier = config.options.txtUserName;
		theReport.modified = new Date();
                store.saveTiddler(theReport.title, theReport.title, theReport.text, theReport.modifier, theReport.modified, theReport.tags, theReport.fields);
		story.displayTiddler(null,theReport.title,1,null,null,false);
		story.refreshTiddler(theReport.title,1,true);
	}
	// reset status flags
	for (var t=0; t<cmi.inbound.length; t++) cmi.inbound[t].status='';
	// mark document as dirty and let display update as needed
	if (count) { store.setDirty(true); store.notifyAll(); }
	// always show final message when tiddlers were actually loaded
	if (count) displayMessage(cmi.importedMsg.format([count,tiddlers.length,cmi.src.replace(/%20/g,' ')]));
}
//}}}
//{{{
// // File and XMLHttpRequest I/O
config.macros.importTiddlers.askForFilename=function(here) {
	var msg=here.title; // use tooltip as dialog box message
	var path=getLocalPath(document.location.href);
	var slashpos=path.lastIndexOf('/'); if (slashpos==-1) slashpos=path.lastIndexOf('\\'); 
	if (slashpos!=-1) path = path.substr(0,slashpos+1); // remove filename from path, leave the trailing slash
	var file='';
	var result='';
	if(window.Components) { // moz
		try {
			netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');

			var nsIFilePicker = window.Components.interfaces.nsIFilePicker;
			var picker = Components.classes['@mozilla.org/filepicker;1'].createInstance(nsIFilePicker);
			picker.init(window, msg, nsIFilePicker.modeOpen);
			var thispath = Components.classes['@mozilla.org/file/local;1'].createInstance(Components.interfaces.nsILocalFile);
			thispath.initWithPath(path);
			picker.displayDirectory=thispath;
			picker.defaultExtension='html';
			picker.defaultString=file;
			picker.appendFilters(nsIFilePicker.filterAll|nsIFilePicker.filterText|nsIFilePicker.filterHTML);
			if (picker.show()!=nsIFilePicker.returnCancel) var result=picker.file.persistentDescriptor;
		}
		catch(e) { alert('error during local file access: '+e.toString()) }
	}
	else { // IE
		try { // XPSP2 IE only
			var s = new ActiveXObject('UserAccounts.CommonDialog');
			s.Filter='All files|*.*|Text files|*.txt|HTML files|*.htm;*.html|';
			s.FilterIndex=3; // default to HTML files;
			s.InitialDir=path;
			s.FileName=file;
			if (s.showOpen()) var result=s.FileName;
		}
		catch(e) {  // fallback
			var result=prompt(msg,path+file);
		}
	}
	return result;
}

config.macros.importTiddlers.loadRemoteFile = function(src,callback) {
	if (src==undefined || !src.length) return null; // filename is required
	var original=src; // URL as specified
	var hashpos=src.indexOf('#'); if (hashpos!=-1) src=src.substr(0,hashpos); // URL with #... suffix removed (needed for IE)
	clearMessage();
	displayMessage(this.openMsg.format([src.replace(/%20/g,' ')]));
	if (src.substr(0,5)!='http:' && src.substr(0,5)!='file:') { // if not a URL, read from local filesystem
		var txt=loadFile(src);
		if (!txt) { // file didn't load, might be relative path.. try fixup
			var pathPrefix=document.location.href;  // get current document path and trim off filename
			var slashpos=pathPrefix.lastIndexOf('/'); if (slashpos==-1) slashpos=pathPrefix.lastIndexOf('\\'); 
			if (slashpos!=-1 && slashpos!=pathPrefix.length-1) pathPrefix=pathPrefix.substr(0,slashpos+1);
			src=pathPrefix+src;
			if (pathPrefix.substr(0,5)!='http:') src=getLocalPath(src);
			var txt=loadFile(src);
		}
		if (!txt) { // file still didn't load, report error
			displayMessage(config.macros.importTiddlers.openErrMsg.format([src.replace(/%20/g,' '),'(filesystem error)']));
		} else {
			displayMessage(config.macros.importTiddlers.readMsg.format([txt.length,src.replace(/%20/g,' ')]));
			if (callback) callback(true,original,convertUTF8ToUnicode(txt),src,null);
		}
	} else {
		var name=config.options.txtRemoteUsername; var pass=config.options.txtRemotePassword;
		var xhr=doHttp('GET',src,null,null,name,pass,callback,original,null)
		if (!xhr) displayMessage(config.macros.importTiddlers.openErrMsg.format([src,'(XMLHTTPRequest error)']));
	}
}

config.macros.importTiddlers.readTiddlersFromHTML=function(html)
{
	var remoteStore=new TiddlyWiki();
	remoteStore.importTiddlyWiki(html);
	return remoteStore.getTiddlers('title');	
}

config.macros.importTiddlers.filterTiddlerList=function(success,params,txt,src,xhr) {
	var cmi=config.macros.importTiddlers; // abbreviation
	var src=src.replace(/%20/g,' ');
	if (!success) { displayMessage(cmi.openErrMsg.format([src,xhr.status])); return; }
	cmi.all = cmi.readTiddlersFromHTML(txt);
	var count=cmi.all?cmi.all.length:0;
	var querypos=src.lastIndexOf('?'); if (querypos!=-1) src=src.substr(0,querypos);
	displayMessage(cmi.foundMsg.format([count,src]));
	cmi.inbound=cmi.filterByHash(params,cmi.all); // use full URL including hash (if any)
	$('importLastFilter').value=cmi.lastFilter;
	window.refreshImportList(0);
}

config.macros.importTiddlers.filterByHash=function(src,tiddlers)
{
	var hashpos=src.lastIndexOf('#'); if (hashpos==-1) return tiddlers;
	var hash=src.substr(hashpos+1); if (!hash.length) return tiddlers;
	var tids=[];
	var params=hash.parseParams('anon',null,true,false,false);
	for (var p=1; p<params.length; p++) {
		switch (params[p].name) {
			case 'anon':
			case 'open':
				tids.pushUnique(params[p].value);
				break;
			case 'tag':
				if (store.getMatchingTiddlers) { // for boolean expressions - see MatchTagsPlugin
					var r=store.getMatchingTiddlers(params[p].value,null,tiddlers);
					for (var t=0; t<r.length; t++) tids.pushUnique(r[t].title);
				} else for (var t=0; t<tiddlers.length; t++)
					if (tiddlers[t].isTagged(params[p].value))
						tids.pushUnique(tiddlers[t].title);
				break;
			case 'story':
				for (var t=0; t<tiddlers.length; t++)
					if (tiddlers[t].title==params[p].value) {
						tiddlers[t].changed();
						for (var s=0; s<tiddlers[t].links.length; s++)
							tids.pushUnique(tiddlers[t].links[s]);
						break;
					}
				break;
			case 'search':
				for (var t=0; t<tiddlers.length; t++)
					if (tiddlers[t].text.indexOf(params[p].value)!=-1)
						tids.pushUnique(tiddlers[t].title);
				break;
		}
	}
	var matches=[];
	for (var t=0; t<tiddlers.length; t++)
		if (tids.contains(tiddlers[t].title))
			matches.push(tiddlers[t]);
	displayMessage(config.macros.importTiddlers.filterMsg.format([matches.length,hash]));
	config.macros.importTiddlers.lastFilter=hash;
	return matches;
}
//}}}
/***
|''Name:''|JapaneseTranslationPlugin |
|''Description:''|Translation of TiddlyWiki into Japanese |
|''Author:''|OGOSHI Masayuki &lt;ogoshima@gmail.com&gt; |
|''Source:''|http://ogoshi.tiddlyspot.com/#JapaneseTranslationPlugin |
|''Version:''|0.3.7.1-ja|
|''Date:''|Sep 04, 2008|
|''License:''|[[Creative Commons Attribution-ShareAlike 2.1 Japan |http://creativecommons.org/licenses/by-sa/2.1/jp/]] |
|''~CoreVersion:''|2.4|

TiddlyWiki を日本語化するプラグイン。TiddlyWiki Version 2.4 上で動作を確認しました。
ライセンスは英語版のCCライセンスに準じる日本語版の CC-by-SA 2.1 ライセンスとします。

英語版のクレジットは以下のとおり。
|''Name:''|EnglishTranslationPlugin|
|''Description:''|Translation of TiddlyWiki into English|
|''Author:''|MartinBudden (mjbudden (at) gmail (dot) com)|
|''Source:''|www.example.com |
|''CodeRepository:''|http://svn.tiddlywiki.org/Trunk/association/locales/core/en/locale.en.js |
|''Version:''|0.3.7|
|''Date:''|Jul 6, 2007|
|''Comments:''|Please make comments at http://groups.google.co.uk/group/TiddlyWikiDev |
|''License:''|[[Creative Commons Attribution-ShareAlike 3.0 License|http://creativecommons.org/licenses/by-sa/3.0/]] |
|''~CoreVersion:''|2.4|
***/

//{{{
//--
//-- Translateable strings
//--

// Strings in "double quotes" should be translated; strings in 'single quotes' should be left alone

config.locale = "ja"; // W3C language tag

if (config.options.txtUserName == 'YourName') // do not translate this line, but do translate the next line
	merge(config.options,{txtUserName: "氏名"});

merge(config.tasks,{
	save: {text: "保存", tooltip: "このTiddlyWikiを保存します", action: saveChanges},
	sync: {text: "同期", tooltip: "他のTiddlyWikiファイルやサーバと同期をとります", content: '<<sync>>'},
	importTask: {text: "取り込み", tooltip: "他のTiddlyWikiファイルやサーバからtiddlerやプラグインを取り込みます", content: '<<importTiddlers>>'},
	tweak: {text: "詳細設定", tooltip: "TiddlyWikiの細かな振る舞いを設定します", content: '<<options>>'},
	upgrade: {text: "アップグレード", tooltip: "TiddlyWiki本体をバージョンアップします", content: '<<upgrade>>'},
	plugins: {text: "プラグイン", tooltip: "インストール済みのプラグインを管理します", content: '<<plugins>>'}
});

// Options that can be set in the options panel and/or cookies
merge(config.optionsDesc,{
	txtUserName: "編集したtiddlerに記録されるユーザ名",
	chkRegExpSearch: "検索に正規表現を使います",
	chkCaseSensitiveSearch: "検索で大文字小文字を区別します",
	chkIncrementalSearch: "インクリメンタルサーチを行います",
	chkAnimate: "アニメーションを許可します",
	chkSaveBackups: "保存時にバックアップファイルを残します",
	chkAutoSave: "自動保存します",
	chkGenerateAnRssFeed: "保存時にRSSフィードを生成します",
	chkSaveEmptyTemplate: "空のテンプレートファイルを保存時に生成します",
	chkOpenInNewWindow: "外部へのリンクを新しいウィンドウで開きます",
	chkToggleLinks: "tiddlerへのリンククリックでtiddlerを閉じます",
	chkHttpReadOnly: "HTTP経由で開いているときに編集機能を隠します",
	chkForceMinorUpdate: "更新時にユーザ名と日付を変更しません",
	chkConfirmDelete: "tiddlerを消去する時に確認をします",
	chkInsertTabs: "タブキーを押したとき、フィールド間の移動ではなくタブ文字を挿入します",
	txtBackupFolder: "バックアップ用フォルダの名前",
	txtMaxEditRows: "編集領域の最大行数",
	txtFileSystemCharSet: "保存時のデフォルト文字コード(Firefox/Mozillaのみ)"});

merge(config.messages,{
	customConfigError: "プラグインの読み込み時に問題が発生しました。詳細は PluginManager をご覧ください",
	pluginError: "エラー: %0",
	pluginDisabled: "'systemConfigDisable'タグによって実行が禁止されています",
	pluginForced: "'systemConfigForce'タグによって強制実行されました",
	pluginVersionError: "このプラグインの実行には、新しいバージョンの TiddlyWiki が必要です。",
	nothingSelected: "何も選択されていません。一つ以上選択する必要があります。",
	savedSnapshotError: "この~TiddlyWikiは正常に保存されていません。詳細は http://www.tiddlywiki.com/#DownloadSoftware をご覧ください。",
	subtitleUnknown: "(unknown)",
	undefinedTiddlerToolTip: "この tiddler '%0' はまだ作成されていません",
	shadowedTiddlerToolTip: "この tiddler '%0' はまだ作成されていませんが、隠された規定値があります",
	tiddlerLinkTooltip: "%0 - %1, %2",
	externalLinkTooltip: "(外部へのリンク) %0",
	noTags: "タグの付いた tiddler はありません",
	notFileUrlError: "変更を保存するにはこの~TiddlyWikiをファイルとして保存(ダウンロード)する必要があります",
	cantSaveError: "変更を保存できませんでした。以下の理由が考えられます:\n- 使用しているブラウザが保存に対応していない(Firefox/Internet Explorer/Safari/Operaは、正しく設定していれば保存できます)\n- TiddlyWikiファイルの保存path名に不正な文字が含まれている\n- TiddlyWiki HTMLファイルが移動または名前を変更された",
	invalidFileError: "元のファイル '%0' は正しい~TiddlyWikiファイルではありません",
	backupSaved: "バックアップを保存しました",
	backupFailed: "バックアップの保存に失敗しました",
	rssSaved: "RSSフィードを保存しました",
	rssFailed: "RSSフィードの保存に失敗しました",
	emptySaved: "空のテンプレートファイルを保存しました",
	emptyFailed: "空のテンプレートファイルの保存に失敗しました",
	mainSaved: "TiddlyWikiファイルを保存しました",
	mainFailed: "TiddlyWikiファイルの保存に失敗しました。変更した内容は保存されていません",
	macroError: "次のマクロでエラー発生 <<\%0>>",
	macroErrorDetails: "次のマクロを実行中にエラー発生 <<\%0>>:\n%1",
	missingMacro: "マクロがありません",
	overwriteWarning: "'%0'という名前のtiddlerはすでに存在します。OKで上書きします",
	unsavedChangesWarning: "注意! TiddlyWiki の変更が保存されていません。\n\n'OK'で保存\n'キャンセル'で変更を破棄",
	confirmExit: "--------------------------------\n\nTiddlyWikiの変更が保存されていません。このまま続けると変更が失われます\n\n--------------------------------",
	saveInstructions: "変更を保存",
	unsupportedTWFormat: "次の TiddlyWiki フォーマットには対応していません '%0'",
	tiddlerSaveError: "tiddler '%0' を保存時にエラー発生",
	tiddlerLoadError: "tiddler '%0' の読込時にエラー発生",
	wrongSaveFormat: "保存フォーマット '%0' で保存できません。標準フォーマットで保存します",
	invalidFieldName: "%0 は不正なファイル名です",
	fieldCannotBeChanged: "領域 '%0' は変更できません",
	loadingMissingTiddler: "tiddler '%0' の '%1' サーバーからの回復を試しています:\n\nワークスペース '%3' の中の '%2'",
	upgradeDone: "バージョン %0 へのアップグレードが完了しました。\n'OK' をクリックすると新しくなったTiddlyWikiをリロードします。"});

merge(config.messages.messageClose,{
	text: "閉じる",
	tooltip: "このメッセージを閉じます"});

config.messages.backstage = {
	open: {text: "クイックメニュー", tooltip: "クイックメニューを開きます"},
	close: {text: "閉じる", tooltip: "クイックメニューを閉じます"},
	prompt: "クイックメニュー: ",
	decal: {
		edit: {text: "編集", tooltip: "tiddler '%0' を編集します"}
	}
};

config.messages.listView = {
	tiddlerTooltip: "このtiddlerのテキスト全体を表示します",
	previewUnavailable: "(プレビューがありません)"
};

config.messages.dates.months = ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月","12月"];
config.messages.dates.days = ["日曜日", "月曜日", "火曜日", "水曜日", "木曜日", "金曜日", "土曜日"];
config.messages.dates.shortMonths = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"];
config.messages.dates.shortDays = ["日", "月", "火", "水", "木", "金", "土"];
// suffixes for dates, eg "1st","2nd","3rd"..."30th","31st"
config.messages.dates.daySuffixes = ["日","日","日","日","日","日","日","日","日","日",
		"日","日","日","日","日","日","日","日","日","日",
		"日","日","日","日","日","日","日","日","日","日",
		"日"];
config.messages.dates.am = "am";
config.messages.dates.pm = "pm";

merge(config.messages.tiddlerPopup,{
	});

merge(config.views.wikified.tag,{
	labelNoTags: "タグ無し",
	labelTags: "タグ: ",
	openTag: "'%0' タグを開く",
	tooltip: "'%0' タグの付いたtiddlerを表示",
	openAllText: "全て開く",
	openAllTooltip: "以下のtiddlerを全て開く",
	popupNone: "'%0' タグの付いたtiddlerは他にありません"});

merge(config.views.wikified,{
	defaultText: "tiddler '%0' はまだ作成されていません。ダブルクリックで作成できます",
	defaultModifier: "(missing)",
	shadowModifier: "(built-in shadow tiddler)",
	dateFormat: "YYYY.MM.DD", // use this to change the date format for your locale, eg "YYYY MMM DD", do not translate the Y, M or D
	createdPrompt: "作成"});

merge(config.views.editor,{
	tagPrompt: "スペース区切りでタグを入力。スペースを含める場合は[[二重の角括弧]]で囲みます。既存のタグを選択≫",
	defaultText: "'%0' の内容を入力してください"});

merge(config.views.editor.tagChooser,{
	text: "タグ",
	tooltip: "既存のタグを選択して追加します",
	popupNone: "タグが定義されていません",
	tagTooltip: "'%0' タグを追加します"});

merge(config.messages,{
	sizeTemplates:
		[
		{unit: 1024*1024*1024, template: "%0\u00a0GB"},
		{unit: 1024*1024, template: "%0\u00a0MB"},
		{unit: 1024, template: "%0\u00a0KB"},
		{unit: 1, template: "%0\u00a0B"}
		]});

merge(config.macros.search,{
	label: "検索",
	prompt: "この TiddlyWiki 内を検索します",
	accessKey: "F",
	successMsg: "%0 件のtiddlerで %1 が見つかりました",
	failureMsg: "%0 は見つかりませんでした"});

merge(config.macros.tagging,{
	label: "タグあり: ",
	labelNotTag: "タグなし",
	tooltip: "'%0' タグを付けたtiddlerリスト"});

merge(config.macros.timeline,{
	dateFormat: "YYYY年MM月DD日"});// use this to change the date format for your locale, eg "YYYY MMM DD", do not translate the Y, M or D

merge(config.macros.allTags,{
	tooltip: "'%0' タグの付いたtiddlerを表示",
	noTags: "タグの付いたtiddlerがありません"});

config.macros.list.all.prompt = "アルファベット順 全tiddler";
config.macros.list.missing.prompt = "リンクがあるのに存在しないtiddler";
config.macros.list.orphans.prompt = "どこからもリンクされていないtiddler";
config.macros.list.shadowed.prompt = "規定で隠されているtiddler";
config.macros.list.touched.prompt = "ローカルに変更されているtiddler";

merge(config.macros.closeAll,{
	label: "全て閉じる",
	prompt: "表示されている全てのtiddlerを閉じます (編集中を除く)"});

merge(config.macros.permaview,{
	label: "現況リンク",
	prompt: "現在のtiddler表示状態を再現するURLをアドレス欄に生成します"});

merge(config.macros.saveChanges,{
	label: "保存",
	prompt: "全てのtiddlerを保存します",
	accessKey: "S"});

merge(config.macros.newTiddler,{
	label: "新規作成",
	prompt: "新しいtiddlerを作成します",
	title: "新規作成",
	accessKey: "N"});

merge(config.macros.newJournal,{
	label: "新規ジャーナル",
	prompt: "現在日時がタイトルの新しいtiddlerを作成します",
	accessKey: "J"});

merge(config.macros.options,{
	wizardTitle: "詳細設定",
	step1Title: "これらのオプション設定はブラウザのcookieに保存されます",
	step1Html: "<input type='hidden' name='markList'></input><br><input type='checkbox' checked='false' name='chkUnknown'>未知のオプションを表示</input>",
	unknownDescription: "//(未知)//",
	listViewTemplate: {
		columns: [
			{name: 'Option', field: 'option', title: "オプション設定", type: 'String'},
			{name: 'Description', field: 'description', title: "説明", type: 'WikiText'},
			{name: 'Name', field: 'name', title: "オプション名", type: 'String'}
			],
		rowClasses: [
			{className: 'lowlight', field: 'lowlight'}
			]}
	});

merge(config.macros.plugins,{
	wizardTitle: "プラグイン管理",
	step1Title: "ロードされているプラグイン",
	step1Html: "<input type='hidden' name='markList'></input>", // DO NOT TRANSLATE
	skippedText: "(このプラグインは起動後に追加されたので実行されていません)",
	noPluginText: "プラグインはインストールされていません",
	confirmDeleteText: "本当にこのプラグインを削除して良いですか?:\n\n%0",
	removeLabel: "systemConfig タグを除去",
	removePrompt: "systemConfig タグを除去します",
	deleteLabel: "削除",
	deletePrompt: "これらのtiddlerを削除します",
	listViewTemplate: {
		columns: [
			{name: 'Selected', field: 'Selected', rowName: 'title', type: 'Selector'},
			{name: 'Tiddler', field: 'tiddler', title: "Tiddler", type: 'Tiddler'},
			{name: 'Size', field: 'size', tiddlerLink: 'size', title: "サイズ", type: 'Size'},
			{name: 'Forced', field: 'forced', title: "強制実行", tag: 'systemConfigForce', type: 'TagCheckbox'},
			{name: 'Disabled', field: 'disabled', title: "無効化", tag: 'systemConfigDisable', type: 'TagCheckbox'},
			{name: 'Executed', field: 'executed', title: "ロード済み", type: 'Boolean', trueText: "Yes", falseText: "No"},
			{name: 'Startup Time', field: 'startupTime', title: "起動時実行", type: 'String'},
			{name: 'Error', field: 'error', title: "ステータス", type: 'Boolean', trueText: "Error", falseText: "OK"},
			{name: 'Log', field: 'log', title: "ログ", type: 'StringList'}
			],
		rowClasses: [
			{className: 'error', field: 'error'},
			{className: 'warning', field: 'warning'}
			]}
	});

merge(config.macros.toolbar,{
	moreLabel: "その他",
	morePrompt: "その他のコマンドも表示します"
	});

merge(config.macros.refreshDisplay,{
	label: "再表示",
	prompt: "TiddlyWiki全体を再描画します"
	});

merge(config.macros.importTiddlers,{
	readOnlyWarning: "読込専用のTiddlyWikiには取り込めません。TiddlyWikiファイルを file:// 形式のURLで開いてみてください",
	wizardTitle: "他のファイルあるいはサーバーからtiddlerを取り込む",
	step1Title: "手順 1: TiddlyWikiファイルあるいはサーバーの位置を指定します",
	step1Html: "種別指定: <select name='selTypes'><option value=''>選択...</option></select><br>URLまたはパス名を入力: <input type='text' size=50 name='txtPath'><br>またはファイルを選択: <input type='file' size=50 name='txtBrowse'><br><hr>または既定のフィードを選択: <select name='selFeeds'><option value=''>選択...</option></select>",
	openLabel: "開く",
	openPrompt: "このファイルあるいはサーバーへ接続する",
	openError: "TiddlyWikiファイルを取り込む際に問題が発生しました",
	statusOpenHost: "ホストをオープン中",
	statusGetWorkspaceList: "有効なワークスペースのリストを取得中",
	step2Title: "手順 2: ワークスペースの選択",
	step2Html: "ワークスペース名を入力: <input type='text' size=50 name='txtWorkspace'><br>またはワークスペースを選択: <select name='selWorkspace'><option value=''>選択...</option></select>",
	cancelLabel: "キャンセル",
	cancelPrompt: "この取り込みをキャンセルする",
	statusOpenWorkspace: "ワークスペースをオープン中",
	statusGetTiddlerList: "有効なtiddlerのリストを取得中",
	errorGettingTiddlerList: "tiddlerのリストを取得中にエラーが発生しました。'キャンセル'でやり直します。",
	step3Title: "手順 3: 取り込むtiddlerの選択",
	step3Html: "<input type='hidden' name='markList'></input><br><input type='checkbox' checked='true' name='chkSync'>変更を同期できるよう、各tiddlerにこのサーバー(ファイル)へのリンクを保持する</input><br><input type='checkbox' name='chkSave'>'systemServer' タグを付けたtiddlerにこのサーバーの詳細を保存する:</input> <input type='text' size=25 name='txtSaveTiddler'>",
	importLabel: "取込",
	importPrompt: "これらのtiddlerを取り込む",
	confirmOverwriteText: "本当にこれらのtiddlerを上書きして良いですか? :\n\n%0",
	step4Title: "手順 4: tiddler %0 を取り込み",
	step4Html: "<input type='hidden' name='markReport'></input>", // DO NOT TRANSLATE
	doneLabel: "完了",
	donePrompt: "ウィザードを閉じる",
	statusDoingImport: "tidderlを取り込み中",
	statusDoneImport: "全てのtiddlerを取り込みました",
	systemServerNamePattern: "%1 / %2",
	systemServerNamePatternNoWorkspace: "%1",
	confirmOverwriteSaveTiddler: "'%0' というtiddlerは既に存在します。'OK'で上書きします。'キャンセル'で変更しません。",
	serverSaveTemplate: "|''種別:''|%0|\n|''URL:''|%1|\n|''ワークスペース:''|%2|\n\nこのtiddlerはこのサーバーの詳細情報を記録するために自動的に作成されました",
	serverSaveModifier: "(System)",
	listViewTemplate: {
		columns: [
			{name: 'Selected', field: 'Selected', rowName: 'title', type: 'Selector'},
			{name: 'Tiddler', field: 'tiddler', title: "Tiddler", type: 'Tiddler'},
			{name: 'Size', field: 'size', tiddlerLink: 'size', title: "サイズ", type: 'Size'},
			{name: 'Tags', field: 'tags', title: "タグ", type: 'Tags'}
			],
		rowClasses: [
			]}
	});

merge(config.macros.upgrade,{
	wizardTitle: "TiddlyWiki本体のアップグレード",
	step1Title: "このTiddlyWikiを最新版へ更新(あるいは修復)",
	step1Html: "TiddlyWiki本体のバージョンを <a href='%0' class='externalLink' target='_blank'>%1</a> から最新版に更新しようとしています。この更新をしてもあなたの作成したデータが削除されることはありません。<br><br>なお、本体をアップデートすることで旧プラグインの動作に支障が出る可能性があります。もし更新後の動作に問題が生じたときは、次のサイトを参照してください。<a href='http://www.tiddlywiki.org/wiki/CoreUpgrades' class='externalLink' target='_blank'>http://www.tiddlywiki.org/wiki/CoreUpgrades</a>",
	errorCantUpgrade: "このTiddlyWikiを更新できませんでした。ローカルに保存したファイルにしか、TiddlyWikiの更新はできません。",
	errorNotSaved: "更新を行う前にまずファイルを保存してください。",
	step2Title: "更新作業の詳細を確認",
	step2Html_downgrade: "TiddlyWikiのバージョンを %1 から %0 へダウングレードしようとしています。<br><br>TiddlyWiki本体を旧バージョンにダウングレードすることは推奨されません。",
	step2Html_restore: "このTiddlyWikiはすでに最新版(%0)です。<br><br>もちろんTiddlyWiki本体が破損していたときなどのために、このまま更新を継続することもできます。",
	step2Html_upgrade: "TiddlyWikiのバージョンを %1 から %0 に更新しようとしています。",
	upgradeLabel: "更新",
	upgradePrompt: "更新処理の準備",
	statusPreparingBackup: "バックアップの準備中",
	statusSavingBackup: "バックアップファイル保存中",
	errorSavingBackup: "バックアップファイルの保存中にエラーが発生しました",
	statusLoadingCore: "本体プログラムを読み込み中",
	errorLoadingCore: "本体プログラムの読み込み中にエラーが発生しました",
	errorCoreFormat: "新しいプログラムにエラーが発生しました",
	statusSavingCore: "本体プログラムの保存中",
	statusReloadingCore: "本体プログラムのリロード中",
	startLabel: "開始",
	startPrompt: "更新処理を開始する",
	cancelLabel: "キャンセル",
	cancelPrompt: "更新処理を中断する",
	step3Title: "更新処理を中断",
	step3Html: "更新処理を中断しました"
	});

merge(config.macros.sync,{
	listViewTemplate: {
		columns: [
			{name: 'Selected', field: 'selected', rowName: 'title', type: 'Selector'},
			{name: 'Tiddler', field: 'tiddler', title: "Tiddler", type: 'Tiddler'},
			{name: 'Server Type', field: 'serverType', title: "種別", type: 'String'},
			{name: 'Server Host', field: 'serverHost', title: "サーバーホスト", type: 'String'},
			{name: 'Server Workspace', field: 'serverWorkspace', title: "ワークスペース", type: 'String'},
			{name: 'Status', field: 'status', title: "同期ステータス", type: 'String'},
			{name: 'Server URL', field: 'serverUrl', title: "サーバーURL", text: "開く", type: 'Link'}
			],
		rowClasses: [
			],
		buttons: [
			{caption: "これらのtiddlerを同期", name: 'sync'}
			]},
	wizardTitle: "外部サーバーやファイルとの同期",
	step1Title: "同期したいtiddlerを選択してください",
	step1Html: "<input type='hidden' name='markList'></input>", // DO NOT TRANSLATE
	syncLabel: "同期",
	syncPrompt: "各tiddlerを同期します",
	hasChanged: "ローカル側変更あり",
	hasNotChanged: "ローカル側変更なし",
	syncStatusList: {
		none: {text: "...", color: "transparent", display:null},
		changedServer: {text: "サーバー側で変更あり", color: '#8080ff', display:null},
		changedLocally: {text: "ローカル側で変更あり", color: '#80ff80', display:null},
		changedBoth: {text: "双方で変更あり", color: '#ff8080', display:null},
		notFound: {text: "サーバーに見つかりません", color: '#ffff80', display:null},
		putToServer: {text: "更新をサーバーに保存しました", color: '#ff80ff', display:null},
		gotFromServer: {text: "サーバーから更新を取得しました", color: '#80ffff', display:null}
		}
	});

merge(config.commands.closeTiddler,{
	text: "閉じる",
	tooltip: "このtiddlerを閉じます"});

merge(config.commands.closeOthers,{
	text: "他を閉じる",
	tooltip: "他の全てのtiddlerを閉じます"});

merge(config.commands.editTiddler,{
	text: "編集",
	tooltip: "このtiddlerを編集します",
	readOnlyText: "閲覧",
	readOnlyTooltip: "このtiddlerのソースを表示します"});

merge(config.commands.saveTiddler,{
	text: "確定",
	tooltip: "このtiddlerへの変更を保存します"});

merge(config.commands.cancelTiddler,{
	text: "キャンセル",
	tooltip: "このtiddlerへの変更を破棄します",
	warning: "本当に '%0' の変更を破棄して良いですか?",
	readOnlyText: "終了",
	readOnlyTooltip: "このtiddlerを通常表示にします"});

merge(config.commands.deleteTiddler,{
	text: "削除",
	tooltip: "このtiddlerを削除します",
	warning: "本当に '%0' を削除して良いですか?"});

merge(config.commands.permalink,{
	text: "リンクURL",
	tooltip: "このtiddlerへのURLをアドレス欄に生成します"});

merge(config.commands.references,{
	text: "参照一覧",
	tooltip: "このtiddlerへの参照を一覧表示します",
	popupNone: "参照がありません"});

merge(config.commands.jump,{
	text: "ジャンプ",
	tooltip: "他に開いているtiddlerへジャンプ"});

merge(config.commands.syncing,{
	text: "同期",
	tooltip: "このtiddlerと外部のサーバー(ファイル)との同期を制御します",
	currentlySyncing: "<div>現在の同期状態<br>種別: <span class='popupHighlight'>'%0'</span><br></"+"div><div>ホスト: <span class='popupHighlight'>%1</span></"+"div><br><div>ワークスペース: <span class='popupHighlight'>%2</span></"+"div>", // Note escaping of closing <div> tag
	notCurrentlySyncing: "同期されていません",
	captionUnSync: "このtiddlerの同期を停止",
	chooseServer: "このtiddlerを次のサーバーと同期する:",
	currServerMarker: "\u25cf ",
	notCurrServerMarker: "  "});

merge(config.commands.fields,{
	text: "拡張情報",
	tooltip: "このtiddlerの拡張情報を表示します",
	emptyText: "このtiddlerには拡張情報がありません",
	listViewTemplate: {
		columns: [
			{name: 'Field', field: 'field', title: "項目", type: 'String'},
			{name: 'Value', field: 'value', title: "値", type: 'String'}
			],
		rowClasses: [
			],
		buttons: [
			]}});

merge(config.shadowTiddlers,{
	DefaultTiddlers: "[[TranslatedGettingStarted]]",
	MainMenu: "[[TranslatedGettingStarted]]\n\n\n^^~TiddlyWiki version <<version>>\n(c) 2007 [[UnaMesa|http://www.unamesa.org/]]^^",
	TranslatedGettingStarted: "この空の~TiddlyWikiを使い始めるにあたって、まずは以下のtiddlerを編集してください。:\n;SiteTitle & SiteSubtitle: \n:このサイトのタイトルおよびサブタイトル。この上に表示されています。<br>保存後はブラウザのタイトルバーにも表示されます。\n;MainMenu: \n:メニュー。たいていは左側に表示されています。\n;DefaultTiddlers: \n:ここにtiddlerの名前が書かれていると、この TiddlyWiki を開いたときに、<br>そのtiddlerが初期表示されます。\nあなたの名前(編集したtiddlerに表示されます): <<option txtUserName>>",
	SiteTitle: "My TiddlyWiki",
	SiteSubtitle: "a reusable non-linear personal web notebook",
	SiteUrl: "http://www.tiddlywiki.com/",
	OptionsPanel: "これらの~TiddlyWikiを制御する各オプションの設定は、使用中のブラウザに保存されます。\n\n署名として使用するあなたの名前を~WikiWord形式(例 JoeBloggs)で入力してください。\n<<option txtUserName>>\n\n<<option chkSaveBackups>> バックアップを保存\n<<option chkAutoSave>> 自動保存\n<<option chkRegExpSearch>> 正規表現で検索\n<<option chkCaseSensitiveSearch>> 検索で大文字小文字を区別\n<<option chkAnimate>> アニメーション\n\n----\n詳細設定 [[TranslatedAdvancedOptions|AdvancedOptions]]",
	SideBarOptions: '<<search>><<closeAll>><<permaview>><<newTiddler>><<newJournal "YYYY年MM月DD日" "ジャーナル">><<saveChanges>><<slider chkSliderOptionsPanel OptionsPanel "オプション \u00bb" "TiddlyWiki の詳細設定">>',
	SideBarTabs: '<<tabs txtMainTab "時系列" "更新時刻の降順" TabTimeline "全て" "全てのtiddler" TabAll "タグ別" "全てのタグ" TabTags "その他" "その他の一覧" TabMore>>',
	TabMore: '<<tabs txtMoreTab "未作成" "リンクがあるのに存在しないtiddler" TabMoreMissing "孤立" "どこからもリンクされていないtiddler" TabMoreOrphans "隠し" "隠されているtiddler" TabMoreShadowed>>'});

merge(config.annotations,{
	AdvancedOptions: "このtiddlerでは詳細オプションを設定できます",
	ColorPalette: "この隠しtiddlerで設定された各値によって、この~TiddlyWikiでの色の枠組みが規定されます。",
	DefaultTiddlers: "この隠しtiddlerに列挙された各tiddlerは、この~TiddlyWIkiを開くと同時に自動的に表示されます。",
	EditTemplate: "この隠しtiddlerにあるHTMLテンプレートは、tiddler編集中の表示方法を決定します。",
	GettingStarted: "この隠しtiddlerは基本的な使用方法を説明します。",
	ImportTiddlers: "この隠しtiddlerは他のtiddlerの取り込み機能を提供します。",
	MainMenu: "この隠しtiddlerの内容は「メインメニュー」に表示されます。画面左手に表示されます。",
	MarkupPreHead: "この隠しtiddlerの内容は、このTiddlyWikiHTMLファイルの<head>セクション開始直後に挿入されます。",
	MarkupPostHead: "この隠しtiddlerの内容は、このTiddlyWikiHTMLファイルの<head>セクション終了直前に挿入されます。",
	MarkupPreBody: "この隠しtiddlerの内容は、このTiddlyWikiHTMLファイルの<body>セクション開始直後に挿入されます。",
	MarkupPostBody: "この隠しtiddlerの内容は、このTiddlyWikiHTMLファイルのスクリプトブロック直後にある、<body>セクション終了直前に挿入されます。",
	OptionsPanel: "この隠しtiddlerの内容は、右手のサイドバー内でスライド式のオプションパネルとして表示されます。",
	PageTemplate: "この隠しtiddlerにあるHTMLテンプレートは、~TiddlyWiki全体のレイアウトを決定します。",
	PluginManager: "この隠しtiddlerはプラグインマネージャ機能を提供します。",
	SideBarOptions: "この隠しtiddlerの内容は右手のサイドバー内のオプションパネルとして表示されます。",
	SideBarTabs: "この隠しtiddlerの内容は右手のサイドバー内にタブパネルとして表示されます。",
	SiteSubtitle: "この隠しtiddlerはページのサブタイトルとして利用されます。",
	SiteTitle: "この隠しtiddlerはページのメインタイトルとして利用されます。",
	SiteUrl: "この隠しtiddlerには、このTiddlyWikiを公開する際のURLを指定する必要があります。",
	StyleSheetColors: "この隠しtiddlerはページ内各要素の色に関するCSSを規定します。このtiddlerを編集しないでください。色を修正するには代わりに StyleSheet 隠しtiddler を編集してください。",
	StyleSheet: "この隠しtiddlerはカスタムCSSを規定します。",
	StyleSheetLayout: "この隠しtiddlerはページ内各要素のレイアウトに関するCSSを規定します。このtiddlerを編集しないでください。レイアウトを修正するには代わりに StyleSheet 隠しtiddler を編集してください。",
	StyleSheetLocale: "この隠しtiddlerはページ内各要素の翻訳ロケールに関するCSSを規定します。",
	StyleSheetPrint: "この隠しtiddlerは印刷に関するCSSを規定します。",
	TabAll: "この隠しtiddlerの内容は右手のサイドバー内「全て」タブに表示されます。",
	TabMore: "この隠しtiddlerの内容は右手のサイドバー内「その他」タブに表示されます。",
	TabMoreMissing: "この隠しtiddlerの内容は右手のサイドバー内「未作成」タブに表示されます。",
	TabMoreOrphans: "この隠しtiddlerの内容は右手のサイドバー内「孤立」タブに表示されます。",
	TabMoreShadowed: "この隠しtiddlerの内容は右手のサイドバー内「隠し」タブに表示されます。",
	TabTags: "この隠しtiddlerの内容は右手のサイドバー内「タグ別」タブに表示されます。",
	TabTimeline: "この隠しtiddlerの内容は右手のサイドバー内「時系列」タブに表示されます。",
	ToolbarCommands: "この隠しtiddlerはtiddlerツールバーにどのようなコマンドを表示するかを決定します。",
	ViewTemplate: "この隠しtiddlerにあるHTMLテンプレートは、各tiddlerの表示方法を決定します。"
	});
//}}}
----
[[数学クラスライブラリ編]]
----
![[ベクトル]]
[[和と差|ベクトルの和と差]] / [[スカラー倍|ベクトルのスカラー倍]] / [[大きさ|ベクトルの大きさ]] / [[内積|ベクトルの内積]] / [[外積|ベクトルの外積]] / [[正規化|ベクトルの正規化]] / [[法線]] / [[スカラー三重積]] / [[四面体の体積]] / [[正反射]]
![[行列]]
[[転置行列]] / [[積|行列の積]] / [[変換行列]] / [[同次座標系]] / [[単位行列]] / [[平行移動|平行移動行列]] / [[スケーリング|スケーリング行列]] / [[回転|回転行列]] / [[任意軸周りの回転行列]] / [[逆行列]]
![[クォータニオン]]
[[和と差|クォータニオンの和と差]] / [[内積|クォータニオンの内積]] / [[大きさ|クォータニオンの大きさ]]  / [[共役クォータニオン]] / [[逆クォータニオン]] / [[単位元|乗法の単位元クォータニオン]] / [[正規化|クォータニオンの正規化]] / [[軸と角度からクォータニオン]] / [[積|クォータニオンの積]] / [[球面線形補間]] / [[回転クォータニオンを行列にする]] / [[ベクトルをクォータニオンで回転]] / [[指数|クォータニオンの指数]] / [[対数|クォータニオンの対数]] / [[べき乗|クォータニオンのべき乗]] / [[指数写像Catmull-Romスプライン|クォータニオンの指数写像Catmull-Romスプライン]] / [[ユークリッド外積|クォータニオンのユークリッド外積]] / [[Even Product|クォータニオンのEven Product]] / [[Odd Product|クォータニオンのOdd Product]]
----
![[デュアルクォータニオン]]
[[積|デュアルクォータニオンの積]] / [[共役デュアルクォータニオン|共役デュアルクォータニオン]] / [[デュアルクォータニオンのノルム]] / [[逆デュアルクォータニオン]] / [[単位元|乗法の単位元デュアルクォータニオン]] / [[回転と平行移動からデュアルクォータニオン]] / [[デュアルクォータニオンから行列]]
----
![[慣性テンソル]]
[[直方体|直方体の慣性テンソル]] / [[球|球の慣性テンソル]] / [[楕円体|楕円体の慣性テンソル]] / [[四面体|四面体の慣性テンソル]] / [[多面体|多面体の慣性テンソル]] / [[平行移動|慣性テンソルの平行移動]]
----
!型
[[Vector2構造体]] / [[Vector3構造体]] / [[Vector4構造体]] / [[Matrix構造体]] / [[Quaternion構造体]] / [[DualQuaternion構造体]]
----
![[補間]]
[[線形補間]] / [[Bézier曲線]] / [[B-Spline曲線]] / [[Catmull-Rom曲線]] / [[Ferguson曲線]]
----
![[プリュッカー座標]]
[[点と点からプリュッカー直線]] / [[点と方向からプリュッカー直線]] / [[面と面からプリュッカー直線]] / [[プリュッカー内積]] / [[プリュッカー直線と面の交点]]
----
!その他
[[頂点の変換]] / [[絶対値]] / [[符号関数]]

----
[[ホーム|../index.html]]
----
©2009 XELF
----
[[RSS|mathematics.xml]] [[管理]]
 数学用の定数や計算を提供する静的な補助クラスです。

※XNA用ビルドのライブラリでは、Microsoft.XNA.Framework.MathHelperクラスです。それ以外用のプラットフォームでは、FarseerGames.FarseerPhysics.Mathematics.MathHelperクラスが肩代わりします。この場合には、完全な互換実装ではないので注意しましょう。
 4行4列の行列を表す構造体です。

![[Farseer Physics Engine 2.0]]
※XNA用ビルドのライブラリでは、Microsoft.XNA.Framework.Matrix構造体です。それ以外用のプラットフォームでは、FarseerGames.FarseerPhysics.Mathematics.Matrix構造体が肩代わりします。この場合には、完全な互換実装ではないので注意しましょう。

/***
|Name|Plugin: jsMath|
|Created by|BobMcElrath|
|Email|my first name at my last name dot org|
|Location|http://bob.mcelrath.org/tiddlyjsmath.html|
|Version|1.5.1|
|Requires|[[TiddlyWiki|http://www.tiddlywiki.com]] &ge; 2.0.3, [[jsMath|http://www.math.union.edu/~dpvc/jsMath/]] &ge; 3.0|
!Description
LaTeX is the world standard for specifying, typesetting, and communicating mathematics among scientists, engineers, and mathematicians.  For more information about LaTeX itself, visit the [[LaTeX Project|http://www.latex-project.org/]].  This plugin typesets math using [[jsMath|http://www.math.union.edu/~dpvc/jsMath/]], which is an implementation of the TeX math rules and typesetting in javascript, for your browser.  Notice the small button in the lower right corner which opens its control panel.
!Installation
In addition to this plugin, you must also [[install jsMath|http://www.math.union.edu/~dpvc/jsMath/download/jsMath.html]] on the same server as your TiddlyWiki html file.  If you're using TiddlyWiki without a web server, then the jsMath directory must be placed in the same location as the TiddlyWiki html file.

I also recommend modifying your StyleSheet use serif fonts that are slightly larger than normal, so that the math matches surrounding text, and \\small fonts are not unreadable (as in exponents and subscripts).
{{{
.viewer {
  line-height: 125%;
  font-family: serif;
  font-size: 12pt;
}
}}}

If you had used a previous version of [[Plugin: jsMath]], it is no longer necessary to edit the main tiddlywiki.html file to add the jsMath <script> tag.  [[Plugin: jsMath]] now uses ajax to load jsMath.
!History
* 11-Nov-05, version 1.0, Initial release
* 22-Jan-06, version 1.1, updated for ~TW2.0, tested with jsMath 3.1, editing tiddlywiki.html by hand is no longer necessary.
* 24-Jan-06, version 1.2, fixes for Safari, Konqueror
* 27-Jan-06, version 1.3, improved error handling, detect if ajax was already defined (used by ZiddlyWiki)
* 12-Jul-06, version 1.4, fixed problem with not finding image fonts
* 26-Feb-07, version 1.5, fixed problem with Mozilla "unterminated character class".
* 27-Feb-07, version 1.5.1, Runs compatibly with TW 2.1.0+, by Bram Chen
!Examples
|!Source|!Output|h
|{{{The variable $x$ is real.}}}|The variable $x$ is real.|
|{{{The variable \(y\) is complex.}}}|The variable \(y\) is complex.|
|{{{This \[\int_a^b x = \frac{1}{2}(b^2-a^2)\] is an easy integral.}}}|This \[\int_a^b x = \frac{1}{2}(b^2-a^2)\] is an easy integral.|
|{{{This $$\int_a^b \sin x = -(\cos b - \cos a)$$ is another easy integral.}}}|This $$\int_a^b \sin x = -(\cos b - \cos a)$$ is another easy integral.|
|{{{Block formatted equations may also use the 'equation' environment \begin{equation}  \int \tan x = -\ln \cos x \end{equation} }}}|Block formatted equations may also use the 'equation' environment \begin{equation}  \int \tan x = -\ln \cos x \end{equation}|
|{{{Equation arrays are also supported \begin{eqnarray} a &=& b \\ c &=& d \end{eqnarray} }}}|Equation arrays are also supported \begin{eqnarray} a &=& b \\ c &=& d \end{eqnarray} |
|{{{I spent \$7.38 on lunch.}}}|I spent \$7.38 on lunch.|
|{{{I had to insert a backslash (\\) into my document}}}|I had to insert a backslash (\\) into my document|
!Code
***/
//{{{

// AJAX code adapted from http://timmorgan.org/mini
// This is already loaded by ziddlywiki...
if(typeof(window["ajax"]) == "undefined") {
  ajax = {
      x: function(){try{return new ActiveXObject('Msxml2.XMLHTTP')}catch(e){try{return new ActiveXObject('Microsoft.XMLHTTP')}catch(e){return new XMLHttpRequest()}}},
      gets: function(url){var x=ajax.x();x.open('GET',url,false);x.send(null);return x.responseText}
  }
}

// Load jsMath
jsMath = {
  Setup: {inited: 1},          // don't run jsMath.Setup.Body() yet
  Autoload: {root: new String(document.location).replace(/[^\/]*$/,'jsMath/')}  // URL to jsMath directory, change if necessary
};
var jsMathstr;
try {
  jsMathstr = ajax.gets(jsMath.Autoload.root+"jsMath.js");
} catch(e) {
  alert("jsMath was not found: you must place the 'jsMath' directory in the same place as this file.  "
       +"The error was:\n"+e.name+": "+e.message);
  throw(e);  // abort eval
}
try {
  window.eval(jsMathstr);
} catch(e) {
  alert("jsMath failed to load.  The error was:\n"+e.name + ": " + e.message + " on line " + e.lineNumber);
}
jsMath.Setup.inited=0;  //  allow jsMath.Setup.Body() to run again

// Define wikifers for latex
config.formatterHelpers.mathFormatHelper = function(w) {
    var e = document.createElement(this.element);
    e.className = this.className;
    var endRegExp = new RegExp(this.terminator, "mg");
    endRegExp.lastIndex = w.matchStart+w.matchLength;
    var matched = endRegExp.exec(w.source);
    if(matched) {
        var txt = w.source.substr(w.matchStart+w.matchLength, 
            matched.index-w.matchStart-w.matchLength);
        if(this.keepdelim) {
          txt = w.source.substr(w.matchStart, matched.index+matched[0].length-w.matchStart);
        }
        e.appendChild(document.createTextNode(txt));
        w.output.appendChild(e);
        w.nextMatch = endRegExp.lastIndex;
    }
}

config.formatters.push({
  name: "displayMath1",
  match: "\\\$\\\$",
  terminator: "\\\$\\\$\\n?", // 2.0 compatability
  termRegExp: "\\\$\\\$\\n?",
  element: "div",
  className: "math",
  handler: config.formatterHelpers.mathFormatHelper
});

config.formatters.push({
  name: "inlineMath1",
  match: "\\\$", 
  terminator: "\\\$", // 2.0 compatability
  termRegExp: "\\\$",
  element: "span",
  className: "math",
  handler: config.formatterHelpers.mathFormatHelper
});

var backslashformatters = new Array(0);

backslashformatters.push({
  name: "inlineMath2",
  match: "\\\\\\\(",
  terminator: "\\\\\\\)", // 2.0 compatability
  termRegExp: "\\\\\\\)",
  element: "span",
  className: "math",
  handler: config.formatterHelpers.mathFormatHelper
});

backslashformatters.push({
  name: "displayMath2",
  match: "\\\\\\\[",
  terminator: "\\\\\\\]\\n?", // 2.0 compatability
  termRegExp: "\\\\\\\]\\n?",
  element: "div",
  className: "math",
  handler: config.formatterHelpers.mathFormatHelper
});

backslashformatters.push({
  name: "displayMath3",
  match: "\\\\begin\\{equation\\}",
  terminator: "\\\\end\\{equation\\}\\n?", // 2.0 compatability
  termRegExp: "\\\\end\\{equation\\}\\n?",
  element: "div",
  className: "math",
  handler: config.formatterHelpers.mathFormatHelper
});

// These can be nested.  e.g. \begin{equation} \begin{array}{ccc} \begin{array}{ccc} ...
backslashformatters.push({
  name: "displayMath4",
  match: "\\\\begin\\{eqnarray\\}",
  terminator: "\\\\end\\{eqnarray\\}\\n?", // 2.0 compatability
  termRegExp: "\\\\end\\{eqnarray\\}\\n?",
  element: "div",
  className: "math",
  keepdelim: true,
  handler: config.formatterHelpers.mathFormatHelper
});

// The escape must come between backslash formatters and regular ones.
// So any latex-like \commands must be added to the beginning of
// backslashformatters here.
backslashformatters.push({
    name: "escape",
    match: "\\\\.",
    handler: function(w) {
        w.output.appendChild(document.createTextNode(w.source.substr(w.matchStart+1,1)));
        w.nextMatch = w.matchStart+2;
    }
});

config.formatters=backslashformatters.concat(config.formatters);

window.wikify = function(source,output,highlightRegExp,tiddler)
{
    if(source && source != "") {
        if(version.major == 2 && version.minor > 0) {
            var wikifier = new Wikifier(source,getParser(tiddler),highlightRegExp,tiddler);
            wikifier.subWikifyUnterm(output);
        } else {
            var wikifier = new Wikifier(source,formatter,highlightRegExp,tiddler);
            wikifier.subWikify(output,null);
        }
        jsMath.ProcessBeforeShowing();
    }
}
//}}}
 [[クォータニオン]]です。
 XNAでは、Microsoft.XNA.Framework.Quaternion構造体です。

!Farseer Physics Engine 2.0

 Farseer Physics Engine 2.0で使われないため、対応する型はありません。
数学クラスライブラリ
Sphynx Project
http://xelf.info/physics/mathematics.html
/%
|Name|TidIDECommand|
|Source|http://www.TiddlyTools.com/#TidIDECommand|
|Version|2.0.0|
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type|script|
|Requires|TidIDEPlugin, NestedSlidersPlugin, MoveablePanelPlugin, TextAreaPlugin, TiddlerTweakerPlugin, InlineJavascriptPlugin, CompareTiddlers|
|Overrides||
|Description|command link invokes TidIDE editor for current tiddler|

Usage (in ViewTemplate):
	<span class='toolbar' macro='tiddler TidIDECommand'></span>
OR embedded directly in tiddler content:
	<<tiddler TidIDECommand>>

%/+++^[TidIDE|Edit this tiddler using the TiddlyWiki Integrated Development Environment].../%
	%/{{fine smallform nowrap{<<moveablePanel>>/%
	%/<<tidIDE SystemInfo TiddlerTweaker CompareTiddlers +edit:here>>/%
	%/<<resizeEditor>>}}}/%
%/===
TidIDE - TiddlyWiki Integrated Development Environment

Provides tools for authors and developers to help construct and debug the contents of their TiddlyWiki documents.
/***
|Name|TidIDEPlugin|
|Source|http://www.TiddlyTools.com/#TidIDEPlugin|
|Documentation|http://www.TiddlyTools.com/#TidIDEPluginInfo|
|Version|1.8.3|
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type|plugin|
|Requires||
|Overrides||
|Options|##Configuration|
|Description|TiddlyWiki Integrated Development Environment - tools for authors and plugin writers|
~TidIDE (//prounounced "Tie Dyed"//) - ''Tid''dlyWiki ''I''ntegrated ''D''evelopment ''E''nvironment - lets you define a set of checkboxes to toggle a stack of 'tool panels' containing tools for TiddlyWiki authors to use when creating and debugging their TiddlyWiki documents.  Each tool is defined by a separate tiddler, allowing you to define any convenient set of tools simply by adding/removing tiddler references from the {{{<<tidIDE...>>}}} macro call.

In addition to presenting checkboxes/tool panels that are defined in separate tiddlers, the {{{<<tidIDE>>}}} macro can invoke an optional built-in "editor panel" that presents an alternative tiddler editor to create, modify, and manage the tiddlers in your document... and, if you have also installed [[PreviewPlugin]], the editor can automatically display a ''//formatted preview//'' of the current tiddler content that is updated ''live, key-by-key'' while you edit the tiddler source.
!!!!!Documentation
>see [[TidIDEPluginInfo]]
!!!!!Configuration
<<<
Number of rows to display in text input area <<option txtTidIDEMaxEditRows>> 
{{{usage: <<option txtTidIDEMaxEditRows>>}}}
^^Note: if value is not specified here, default is to use {{{<<option txtMaxEditRows>>}}} core setting (see [[AdvancedOptions]])^^
<<<
!!!!!Revisions
<<<
2008.04.24 [1.8.3] fixed 'run' button onclick handler
2007.12.21 [1.8.2] added txtTidIDEMaxEditRows option as custom override for standard core txtMaxEditRows setting
2007.09.27 [1.8.0] split preview functionality into separate stand-alone plugin (see [[PreviewPlugin]]).  Moved {{{<<DOMViewer>>}}} macro definition to separate plugin (see [[DOMViewerPlugin]]).  Replicated ''run'' button functionality in stand-along plugin (see [[RunTiddlerPlugin]]). Major re-write of documentation
|please see [[TidIDEPluginInfo]] for additional revision details|
2006.04.15 [0.5.0] Initial ALPHA release. Converted from inline script.
<<<
!!!!!Code
***/
// // version
//{{{
version.extensions.TidIDEPlugin= {major: 1, minor: 8, revision: 3, date: new Date(2008,4,24)};
//}}}
// // settings
//{{{
if (config.options.txtTidIDEMaxEditRows==undefined)
	config.options.txtTidIDEMaxEditRows=config.options.txtMaxEditRows
//}}}
// //  macro definition
//{{{
config.macros.tidIDE = {
	versionMsg: "TidIDE v%0.%1.%2: ",
	datetimefmt: "0MM/0DD/YYYY 0hh:0mm",
	titleMsg: "Please enter a new tiddler title",
	isShadowMsg: "'%0' is a shadow tiddler and cannot be removed.",
	evalMsg: "Warning!! Processing '%0' as a systemConfig (plugin) tiddler may produce unexpected results! Are you sure you want to proceed?",
	evalCompletedMsg: "Processing completed",
	toolsDef: "<html><a href='javascript:config.macros.tidIDE.set(\"%0\",\"%1\");'>edit %1...</a></html>",
	editorLabel: "TiddlerEditor"
};
config.macros.tidIDE.handler= function(place,macroName,params) {
	var here=story.findContainingTiddler(place);
	var selectors="";
	var panels="";
	var showsys=false;
	var title="";
	var id=""; if (here) id=here.getAttribute("tiddler").replace(/ /g,"_");
	var p=params.shift();
	if (!p) p="edit:here"; // default to editor if no params
	var openpanels=[];
	var panelcount=0;
	while (p) {
		var defOpen=(p.substr(0,1)=="+"); if (defOpen) p=p.substr(1);
		if (p.substr(0,3)=="id:")
			{ id=p.substr(3); }
		else if (p.substr(0,4)=="edit") {
			panelcount++;
			defOpen=defOpen || (!params[0] && panelcount==1); // if only one panel to show, default to open
			var toolname=this.editorLabel;
			if (p.indexOf('|')!=-1) toolname=p.substr(0,p.indexOf('|'));
			selectors+=this.html.editorchk.replace(/%toolname%/mg,toolname);
			selectors=selectors.replace(/%showpanel%/mg,defOpen?"CHECKED":"");
			panels+=this.html.editorpanel;
			// editor panel setup...
			panels=panels.replace(/%showpanel%/mg,defOpen?"block":"none");
			panels=panels.replace(/%maxrows%/mg,config.options.txtTidIDEMaxEditRows);
			panels=panels.replace(/%disabled%/mg,readOnly?"DISABLED":"");
			panels=panels.replace(/%readonlychk%/mg,readOnly?"CHECKED":"");
			panels=panels.replace(/%minoredits%/mg,config.options.chkForceMinorUpdate&&!readOnly?"":"DISABLED");
			panels=panels.replace(/%minorchk%/mg,config.options.chkForceMinorUpdate?"CHECKED":"");
			var tiddlers=store.getTiddlers("title"); var tiddlerlist=""; 
			for (var t=0; t<tiddlers.length; t++)
				tiddlerlist+='<option value="'+tiddlers[t].title+'">'+tiddlers[t].title+'</option>';
			for (var t in config.shadowTiddlers)
				if (!store.tiddlerExists(t)) tiddlerlist+="<option value='"+t+"'>"+t+" (shadow)</option>";
			panels=panels.replace(/%tiddlerlist%/mg,tiddlerlist);
			var tags = store.getTags(); var taglist="";
			for (var t=0; t<tags.length; t++)
				taglist+="<option value='"+tags[t][0]+"'>"+tags[t][0]+"</option>";
			panels=panels.replace(/%taglist%/mg,taglist);
			if (p.substr(0,5)=="edit:") { 
				title=p.substr(5); 
				if (here && title=="here") title=here.id.substr(7);
			}
		}
		else {
			panelcount++;
			defOpen=defOpen || (!params[0] && panelcount==1); // if only one panel to show, default to open
			var toolid=toolname=p;
			if (p.indexOf('|')!=-1)
				{ toolname=p.substr(0,p.indexOf('|')); toolid=p.substr(p.indexOf('|')+1); }
			selectors+=this.html.toolschk.replace(/%toolid%/mg,toolid).replace(/%toolname%/mg,toolname);
			selectors=selectors.replace(/%showpanel%/mg,defOpen?"CHECKED":"");
			panels+=this.html.toolspanel.replace(/%toolid%/mg,toolid);
			panels=panels.replace(/%showpanel%/mg,defOpen?"block":"none");
			if (defOpen) openpanels.push(toolid);
		}
		p=params.shift(); // next param
	}
	var html=this.html.framework;
	if (panelcount<2)
		html=html.replace(/%version%/mg,'').replace(/%selector%/mg,''); // omit header/selectors if just one panel to display
	else {
		var v=version.extensions.TidIDEPlugin;
		html=html.replace(/%version%/mg, this.versionMsg.format([v.major,v.minor,v.revision]));
		html=html.replace(/%selector%/mg,selectors+"<hr style='margin:0;padding:0'>");
	}
	html=html.replace(/%panels%/mg,panels);
	html=html.replace(/%id%/mg,id);
	var newIDE=createTiddlyElement(place,"span");
	newIDE.innerHTML=html;
	if (title.length) this.set(id,title);  // pre-load tiddler editor values (if needed)
	if (openpanels.length) for (i=0;i<openpanels.length;i++) { config.macros.tidIDE.loadPanel(id,openpanels[i]); }
	// see [[TextAreaPlugin]] for extended ctrl-F/G (search/search again)and TAB handler definitions
	if (window.addKeyDownHandlers!=undefined) {
		var elems=newIDE.getElementsByTagName("textarea");
		for (var i=0;i<elems.length;i++) window.addKeyDownHandlers(elems[i]);
	}
	var prev=document.getElementById(id+'_previewpanel');
	if (config.macros.preview && prev)  // add previewer to editor (if installed)
		config.macros.preview.handler(prev,"preview",["text","15"]);
}
//}}}

// // CUSTOM PANEL FUNCTIONS 
//{{{
config.macros.tidIDE.loadPanel=function(id,toolid) {
	var place=document.getElementById(id+"_"+toolid+"_panel"); if (!place) return;
	var t=store.getTiddlerText(toolid,"");
	place.innerHTML=""; 
	if (t) wikify(t,place); else place.innerHTML=this.toolsDef.format([id,toolid]);
}
//}}}

// // EDITOR PANEL FUNCTIONS
//{{{
config.macros.tidIDE.set=function(id,title) {
	var place=document.getElementById(id+"_editorpanel"); if (!place) return;
	var f=document.getElementById(id+"_editorform");
	if (f.dirty && !confirm(config.commands.cancelTiddler.warning.format([f.current]))) return;
	// reset to form defaults
	f.dirty=false;
	f.current="";
	f.created.value=f.created.defaultValue;
	f.modified.value=f.modified.defaultValue;
	f.author.value=f.author.defaultValue;
	f.content.value=f.content.defaultValue;
	f.tags.value=f.tags.defaultValue;
	f.size.value=f.size.defaultValue;
	if (!title.length) return;
	f.current=title;
	// values for new/shadow tiddlers
	var cdate=new Date();
	var mdate=new Date();
	var modifier=config.options.txtUserName;
	var text=config.views.editor.defaultText.format([title]);
	var tags="";
	// adjust values for shadow tiddlers
	if (store.isShadowTiddler(title))
		{ modifier=config.views.wikified.shadowModifier; text=store.getTiddlerText(title) }
	// get values for specified tiddler (if it exists)
	var t=store.getTiddler(title);
	if (t)	{ var cdate=t.created; var mdate=t.modified; var modifier=t.modifier; var text=t.text; var tags=t.getTags(); }
	if (!t && !store.isShadowTiddler(title)) f.tiddlers.options[f.tiddlers.options.length]=new Option(title,title,false,true); // add item to list
	f.tiddlers.value=title; // select current title (just in case it wasn't already selected)
	f.created.value=cdate.formatString(this.datetimefmt);
	f.modified.value=mdate.formatString(this.datetimefmt);
	f.author.value=modifier;
	f.content.value=text;
	f.tags.value=tags;
	f.minoredits.checked=config.options.chkForceMinorUpdate&&!readOnly;
	f.size.value=f.content.value.length+" bytes";
}

config.macros.tidIDE.add=function(id) {
	var place=document.getElementById(id+"_editorpanel"); if (!place) return;
	var f=document.getElementById(id+"_editorform");
	if (f.dirty && !confirm(config.commands.cancelTiddler.warning.format([f.current]))) return;
	var title=prompt(this.titleMsg,config.macros.newTiddler.title);
	while (title && store.tiddlerExists(title) && !confirm(config.messages.overwriteWarning.format([title])))
		title=prompt(this.titleMsg,config.macros.newTiddler.title);
	if (!title || !title.trim().length) return; // cancelled by user
	f.dirty=false; // suppress unneeded confirmation message
	this.set(id,title);
}

config.macros.tidIDE.remove=function(id) {
	var place=document.getElementById(id+"_editorpanel"); if (!place) return;
	var f=document.getElementById(id+"_editorform");
	if (!f.current.length) return;
	if (!store.tiddlerExists(f.current) && store.isShadowTiddler(f.current)) { alert(this.isShadowMsg.format([f.current])); return; }
	if (config.options.chkConfirmDelete && !confirm(config.commands.deleteTiddler.warning.format([f.current]))) return;
	if (store.tiddlerExists(f.current)) {
		story.closeTiddler(f.current);
		store.removeTiddler(f.current);
		store.setDirty(true);
		if(config.options.chkAutoSave) saveChanges();
	}
	f.tiddlers.options[f.tiddlers.selectedIndex]=null; // remove item from list
	f.dirty=false; // suppress unneeded confirmation message
	this.set(id,""); // clear form controls
}

config.macros.tidIDE.save=function(id,saveAs) {
	var place=document.getElementById(id+"_editorpanel"); if (!place) return;
	var f=document.getElementById(id+"_editorform");
	var title=f.current;
	if (!title || !title.trim().length || saveAs) { // get a new title
		title=prompt(this.titleMsg,config.macros.newTiddler.title);
		while (title && store.tiddlerExists(title) && !confirm(config.messages.overwriteWarning.format([title])))
			title=prompt(this.titleMsg,config.macros.newTiddler.title);
		if (!title || !title.trim().length) return; // cancelled by user
		f.tiddlers.options[f.tiddlers.options.length]=new Option(title,title,false,true); // add item to list
		f.current=title;
	}
	var author=config.options.txtUserName;
	var mdate=new Date();
	var content=f.content.value;
	var tags=f.tags.value;
	var tiddler=store.saveTiddler(title,title,content,author,mdate,tags);
	if (f.minoredits.checked) {
		var author=f.author.value;
		var mdate=new Date(f.modified.value);
		var cdate=new Date(f.created.value);
		tiddler.assign(null,null,author,mdate,null,cdate);
	}
	store.setDirty(true);
	if(config.options.chkAutoSave) saveChanges();
	story.refreshTiddler(title,null,true);
	f.dirty=false;
}
//}}}

// // HTML DEFINITIONS
//{{{
config.macros.tidIDE.html = { };
config.macros.tidIDE.html.framework = " \
	<html> %version% <form style='display:inline;margin:0;padding:0;'>%selector%</form> %panels% </html> \
";
//}}}
//{{{
config.macros.tidIDE.html.editorchk = " \
	<input type=checkbox name=editor \
		style='display:inline;width:auto;margin:1px;' \
		title='add/delete/modify tiddlers' %showpanel% \
		onclick='document.getElementById(\"%id%_editorpanel\").style.display=this.checked?\"block\":\"none\";'>%toolname% \
";
config.macros.tidIDE.html.toolschk = " \
	<input type=checkbox name=tools \
		style='display:inline;width:auto;margin:1px;' \
		title='' %showpanel% \
		onclick='document.getElementById(\"%id%_%toolid%_panel\").style.display=this.checked?\"block\":\"none\"; \
			if (this.checked) config.macros.tidIDE.loadPanel(\"%id%\",\"%toolid%\");'>%toolname% \
";
//}}}
//{{{
config.macros.tidIDE.html.toolspanel = " \
	<div id='%id%_%toolid%_panel' style='display:%showpanel%;margin:0;margin-top:0.5em'> \
	</div> \
";
//}}}
//{{{
config.macros.tidIDE.html.editorpanel = " \
	<div id='%id%_editorpanel' style='display:%showpanel%;margin:0;margin-top:0.5em'> \
	<form id='%id%_editorform' style='display:inline;margin:0;padding:0;'> \
	<!-- tiddler editor list and buttons --> \
	<select size=1 name=tiddlers style='display:inline;width:44%;'  \
		onchange='config.macros.tidIDE.set(\"%id%\",this.value); this.value=this.form.current;'> \
	<option value=''>select a tiddler...</option> \
	%tiddlerlist% \
	</select><!-- \
	--><input name=add type=button style='display:inline;width:8%' \
		value='new' title='create a new tiddler' \
		onclick='config.macros.tidIDE.add(\"%id%\")' %disabled%><!-- \
	--><input name=remove type=button style='display:inline;width:8%' \
		value='remove' title='delete this tiddler' \
		onclick='config.macros.tidIDE.remove(\"%id%\")' %disabled%><!-- \
	--><input name=save type=button style='display:inline;width:8%' \
		value='save' title='save changes to this tiddler' \
		onclick='config.macros.tidIDE.save(\"%id%\")' %disabled%><!-- \
	--><input name=saveas type=button style='display:inline;width:8%' \
		value='save as' title='save changes to a new tiddler' \
		onclick='config.macros.tidIDE.save(\"%id%\",true)' %disabled%><!-- \
	--><input name=view type=button style='display:inline;width:8%' \
		value='open' title='open this tiddler for regular viewing' \
		onclick='if (!this.form.current.length) return;	story.displayTiddler(null,this.form.current)'><!-- \
	--><input name=run type=button style='display:inline;width:8%' \
		value='run' title='evaluate this tiddler as a javascript \"systemConfig\" plugin' \
		onclick='if (!confirm(config.macros.tidIDE.evalMsg.format([this.form.current]))) return false; \
			try { window.eval(this.form.content.value); displayMessage(config.macros.tidIDE.evalCompletedMsg); } \
			catch(e) { displayMessage(config.messages.pluginError.format([err])); }'><!-- \
	--><input name=previewbutton type=button style='display:inline;width:8%;' \
		value='preview' title='show \"live\" preview display' \
		onclick='if (!config.macros.preview) { alert(\"Please install PreviewPlugin\"); return false; } \
			this.form.preview.checked=!this.form.preview.checked; \
			document.getElementById(\"%id%_previewpanel\").style.display=this.form.preview.checked?\"block\":\"none\"; \
			if (this.form.freeze) this.form.freeze.checked=!this.form.preview.checked; \
			if (this.form.preview.checked) config.macros.preview.render(this.form.content.id,this.form.content.getAttribute(\"previewid\"));'><!-- \
	hidden field for preview show/hide state: \
	--><input name=preview type=checkbox style='display:none;'>\
	<!-- tiddler content edit --> \
	<div><textarea id='%id%_content' name='content' edit='text' cols=60 rows=%maxrows% \
		style='width:100%;' \
		onkeyup='var f=this.form; f.dirty=true; f.size.value=this.value.length+\" bytes\";'></textarea></div> \
	<!-- tag edit and droplist --> \
	<table width='100%' style='border:0;padding:0;margin:0'><tr style='border:0;padding:0;margin:0'> \
	<td style='border:0;padding:0;margin:0'> \
		<input type=text name=tags size=60 style='width:100%;' value='' \
			onchange='this.form.dirty=true' %disabled%> \
	</td><td width='1' style='border:0;padding:0;margin:0;'> \
		<select size=1 name=taglist \
			onchange='this.form.dirty=true; this.form.tags.value+=\" \"+this.value' %disabled%> \
		<option value=''>select tags...</option> \
		%taglist% \
		</select> \
	</td></tr></table> \
	<!--  created/modified dates, author, current tiddler size --> \
	<div style='float:right;'> \
		created <input type=text name=created size=15 \
			style='display:inline;;text-align:center;padding:0;' value='' \
			onchange='this.form.dirty=true' %minoredits%> \
		modified <input type=text name=modified size=15 \
			style='display:inline;text-align:center;padding:0;' value='' \
			onchange='this.form.dirty=true;' %minoredits%> \
		by <input type=text name=author size=15 \
			style='display:inline;padding:0;' value='' \
			onfocus='this.select()' onchange='this.form.dirty=true' %minoredits%> \
		<input type=text name=size size=10 \
			style='display:inline;text-align:center;padding:0;' value='' \
			onfocus='this.blur()' onkeydown='return false' DISABLED>  \
	</div> \
	<!-- toggles: read-only, minor edit --> \
	<span style='white-space:nowrap'> \
	<input type=checkbox name=readonly \
		style='display:inline;width:auto;margin:1px;' %readonlychk% \
		title='do not allow tiddler changes to be saved' \
		onclick='readOnly=config.options.chkHttpReadOnly=this.checked;saveOptionCookie(\"chkHttpReadOnly\"); \
			var f=this.form; f.minoredits.disabled=f.tags.disabled=f.taglist.disabled=this.checked; \
			f.add.disabled=f.remove.disabled=f.save.disabled=f.saveas.disabled=this.checked; \
			f.created.disabled=f.modified.disabled=f.author.disabled=this.checked||!f.minoredits.checked;'>readonly \
	<input type=checkbox name=minoredits \
		style='display:inline;width:auto;margin:1px;' %disabled% %minorchk% \
		title='check: save datestamps/author as entered, uncheck: auto-update modified/author' \
		onclick='this.form.created.disabled=this.form.modified.disabled=this.form.author.disabled=!this.checked; \
			config.options.chkForceMinorUpdate=this.checked;saveOptionCookie(\"chkForceMinorUpdate\");'>minor edits \
	</span> \
	<!-- tiddler preview display --> \
	<div id='%id%_previewpanel' style='display:none;white-space:nowrap'></div> \
";
//}}}
/***
|Name|TiddlerTweakerPlugin|
|Source|http://www.TiddlyTools.com/#TiddlerTweakerPlugin|
|Version|2.2.3|
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type|plugin|
|Requires||
|Overrides||
|Description|select multiple tiddlers and modify author, created, modified and/or tag values|
~TiddlerTweaker is a tool for TiddlyWiki authors.  It allows you to select multiple tiddlers from a listbox, either by direct interaction or automatically matching specific criteria.  You can then modify the creator, author, created, modified and/or tag values of those tiddlers using a compact set of form fields.  The values you enter into the fields simultantously overwrite the existing values in all tiddlers you have selected.
!!!!!Usage
<<<
{{{<<tiddlerTweaker>>}}}
{{smallform{<<tiddlerTweaker>>}}}
By default, any tags you enter into the TiddlerTweaker will //replace// the existing tags in all the tiddlers you have selected.  However, you can also use TiddlerTweaker to quickly filter specified tags from the selected tiddlers, while leaving any other tags assigned to those tiddlers unchanged:
>Any tag preceded by a "+" (plus) or "-" (minus), will be added or removed from the existing tags //instead of replacing the entire tag definition// of each tiddler (e.g., enter "-excludeLists" to remove that tag from all selected tiddlers.  When using this syntax, care should be taken to ensure that //every// tag is preceded by "+" or "-", to avoid inadvertently overwriting any other existing tags on the selected tiddlers.  (note: the "+" or "-" prefix on each tag value is NOT part of the tag value, and is only used by TiddlerTweaker to control how that tag value is processed)
Important Notes:
* Inasmuch as TiddlerTweaker is a 'power user' tool that can perform 'batch' functions (operating on many tiddlers at once), you should always have a recent backup of your document (or "save changes" just *before* tweaking the tiddlers), just in case you "shoot yourself in the foot".
* By design, TiddlerTweaker does NOT update the 'modified' date of tiddlers simply by making changes to the tiddler's values.  A tiddler's dates are ONLY updated when the corresponding 'created' and/or 'modified' checkboxes are selected and you enter new values for those dates.  As a general rule, after using TiddlerTweaker, always ''//remember to save your document//'' when you are done, even though the tiddler timeline tab may not show any recently modified tiddlers.
* Because you may be changing the values on many tiddlers simultaneously, selecting and updating all tiddlers in a document operation may take a while and your browser might warn about an "unresponsive script"... you should give it a whole bunch of time to 'continue'... it should complete the processing... eventually.
<<<
!!!!!Revisions
<<<
2008.10.27 [2.2.3] in setTiddlers(), fixed Safari bug by replacing static Array.concat(...) with new Array().concat(...)
2008.09.07 [2.2.2] added removeCookie() function for compatibility with [[CookieManagerPlugin]]
2008.05.12 [2.2.1] replace built-in backstage "tweak" task with tiddler tweaker control panel (moved from BackstageTweaks)
2008.01.13 [2.2.0] added "auto-selection" links: all, changed, tags, title, text
2007.12.26 [2.1.0] added support for managing 'creator' custom field (see [[CoreTweaks]])
2007.11.01 [2.0.3] added config.options.txtTweakerSortBy for cookie-based persistence of list display order preference setting.
2007.09.28 [2.0.2] in settiddlers() and deltiddlers(), added suspend/resume notification handling (improves performance when operating on multiple tiddlers)
2007.08.03 [2.0.1] added shadow definition for [[TiddlerTweaker]] tiddler for use as parameter references with {{{<<tiddler>>, <<slider>> or <<tabs>>}}} macros.
2007.08.03 [2.0.0] converted from inline script
2006.01.01 [1.0.0] initial release
<<<
!!!!!Code
***/
//{{{
version.extensions.TiddlerTweakerPlugin= {major: 2, minor: 2, revision: 3, date: new Date(2008,10,27)};

// shadow tiddler
config.shadowTiddlers.TiddlerTweaker="<<tiddlerTweaker>>";

/// backstage task
if (config.tasks) { // for TW2.2b3 or above
	config.tasks.tweak.tooltip="review/modify tiddler internals: dates, authors, tags, etc.";
	config.tasks.tweak.content="{{smallform small groupbox{<<tiddlerTweaker>>}}}";
}

if (config.options.txtTweakerSortBy==undefined) config.options.txtTweakerSortBy="modified";

// if removeCookie() function is not defined by TW core, define it here.
if (window.removeCookie===undefined) {
	window.removeCookie=function(name) {
		document.cookie = name+'=; expires=Thu, 01-Jan-1970 00:00:01 UTC; path=/;'; 
	}
}

config.macros.tiddlerTweaker = {
	html: '<form style="display:inline"><!--\
		--><table style="padding:0;margin:0;border:0;width:100%"><tr valign="top" style="padding:0;margin:0;border:0"><!--\
		--><td  style="text-align:center;white-space:nowrap;width:99%;padding:0;margin:0;border:0"><!--\
			--><font size=-2><div style="text-align:left;"><span style="float:right"><!--\
			-->&nbsp; <a href="javascript:;" \
				title="select all tiddlers"\
				onclick="\
				var f=this; while (f&&f.nodeName.toLowerCase()!=\'form\')f=f.parentNode;\
				for (var t=0; t<f.list.options.length; t++)\
					if (f.list.options[t].value.length) f.list.options[t].selected=true;\
				config.macros.tiddlerTweaker.selecttiddlers(f.list);\
				return false">all</a><!--\
			-->&nbsp; <a href="javascript:;" \
				title="select tiddlers that are new/changed since the last file save"\
				onclick="\
				var lastmod=new Date(document.lastModified);\
				var f=this; while (f&&f.nodeName.toLowerCase()!=\'form\')f=f.parentNode;\
				for (var t=0; t<f.list.options.length; t++) {\
					var tid=store.getTiddler(f.list.options[t].value);\
					f.list.options[t].selected=tid&&tid.modified>lastmod;\
				}\
				config.macros.tiddlerTweaker.selecttiddlers(f.list);\
				return false">changed</a><!--\
			-->&nbsp; <a href="javascript:;" \
				title="select tiddlers with at least one matching tag"\
				onclick="\
				var t=prompt(\'Enter space-separated tags (match ONE)\');\
				if (!t||!t.length) return false;\
				var tags=t.readBracketedList();\
				var f=this; while (f&&f.nodeName.toLowerCase()!=\'form\')f=f.parentNode;\
				for (var t=0; t<f.list.options.length; t++) {\
					f.list.options[t].selected=false;\
					var tid=store.getTiddler(f.list.options[t].value);\
					if (tid&&tid.tags.containsAny(tags)) f.list.options[t].selected=true;\
				}\
				config.macros.tiddlerTweaker.selecttiddlers(f.list);\
				return false">tags</a><!--\
			-->&nbsp; <a href="javascript:;" \
				title="select tiddlers whose titles include matching text"\
				onclick="\
				var txt=prompt(\'Enter a title (or portion of a title) to match\');\
				if (!txt||!txt.length) return false;\
				var f=this; while (f&&f.nodeName.toLowerCase()!=\'form\')f=f.parentNode;\
				for (var t=0; t<f.list.options.length; t++) {\
					f.list.options[t].selected=f.list.options[t].value.indexOf(txt)!=-1;\
				}\
				config.macros.tiddlerTweaker.selecttiddlers(f.list);\
				return false">titles</a><!--\
			-->&nbsp; <a href="javascript:;" \
				title="select tiddlers containing matching text"\
				onclick="\
				var txt=prompt(\'Enter tiddler text (content) to match\');\
				if (!txt||!txt.length) return false;\
				var f=this; while (f&&f.nodeName.toLowerCase()!=\'form\')f=f.parentNode;\
				for (var t=0; t<f.list.options.length; t++) {\
					var tt=store.getTiddlerText(f.list.options[t].value,\'\');\
					f.list.options[t].selected=(tt.indexOf(txt)!=-1);\
				}\
				config.macros.tiddlerTweaker.selecttiddlers(f.list);\
				return false">text</a> &nbsp;<!--\
			--></span><span>select tiddlers</span><!--\
			--></div><!--\
			--></font><select multiple name=list size="10" style="width:99.99%" \
				title="use click, shift-click and/or ctrl-click to select multiple tiddler titles" \
				onclick="config.macros.tiddlerTweaker.selecttiddlers(this)" \
				onchange="config.macros.tiddlerTweaker.setfields(this)"><!--\
			--></select><br><!--\
			-->show<input type=text size=1 value="10" \
				onchange="this.form.list.size=this.value; this.form.list.multiple=(this.value>1);"><!--\
			-->by<!--\
			--><select name=sortby size=1 \
				onchange="config.macros.tiddlerTweaker.init(this.form,this.value)"><!--\
			--><option value="title">title</option><!--\
			--><option value="size">size</option><!--\
			--><option value="modified">modified</option><!--\
			--><option value="created">created</option><!--\
			--></select><!--\
			--><input type="button" value="refresh" \
				onclick="config.macros.tiddlerTweaker.init(this.form,this.form.sortby.value)"<!--\
			--> <input type="button" name="stats" disabled value="totals..." \
				onclick="config.macros.tiddlerTweaker.stats(this)"><!--\
		--></td><td style="white-space:nowrap;padding:0;margin:0;border:0;width:1%"><!--\
			--><div style="text-align:left"><font size=-2>&nbsp;modify values</font></div><!--\
			--><table border=0 style="width:100%;padding:0;margin:0;border:0;"><tr style="padding:0;border:0;"><!--\
			--><td style="padding:1px;border:0;white-space:nowrap"><!--\
				--><input type=checkbox name=settitle unchecked \
					title="allow changes to tiddler title (rename tiddler)" \
					onclick="this.form.title.disabled=!this.checked">title<!--\
			--></td><td style="padding:1px;border:0;white-space:nowrap"><!--\
				--><input type=text name=title size=35 style="width:98%" disabled><!--\
			--></td></tr><tr style="padding:0;border:0;"><td style="padding:1px;border:0;white-space:nowrap"><!--\
				--><input type=checkbox name=setcreator unchecked \
					title="allow changes to tiddler creator" \
					onclick="this.form.creator.disabled=!this.checked">created by<!--\
			--></td><td style="padding:1px;border:0;white-space:nowrap"><!--\
				--><input type=text name=creator size=35 style="width:98%" disabled><!--\
			--></td></tr><tr style="padding:0;border:0;"><td style="padding:1px;border:0;white-space:nowrap"><!--\
				--><input type=checkbox name=setwho unchecked \
					title="allow changes to tiddler author" \
					onclick="this.form.who.disabled=!this.checked">modified by<!--\
			--></td><td style="padding:1px;border:0;white-space:nowrap"><!--\
				--><input type=text name=who size=35 style="width:98%" disabled><!--\
			--></td></tr><tr style="padding:0;border:0;"><td style="padding:1px;border:0;white-space:nowrap"><!--\
				--><input type=checkbox name=setcdate unchecked \
					title="allow changes to created date" \
					onclick="var f=this.form; f.cm.disabled=f.cd.disabled=f.cy.disabled=f.ch.disabled=f.cn.disabled=!this.checked"><!--\
				-->created on<!--\
			--></td><td style="padding:1px;border:0;white-space:nowrap"><!--\
				--><input type=text name=cm size=2 style="width:2em;padding:0;text-align:center" disabled><!--\
				--> / <input type=text name=cd size=2 style="width:2em;padding:0;text-align:center" disabled><!--\
				--> / <input type=text name=cy size=4 style="width:3em;padding:0;text-align:center" disabled><!--\
				--> at <input type=text name=ch size=2 style="width:2em;padding:0;text-align:center" disabled><!--\
				--> : <input type=text name=cn size=2 style="width:2em;padding:0;text-align:center" disabled><!--\
			--></td></tr><tr style="padding:0;border:0;"><td style="padding:1px;border:0;white-space:nowrap"><!--\
				--><input type=checkbox name=setmdate unchecked \
					title="allow changes to modified date" \
					onclick="var f=this.form; f.mm.disabled=f.md.disabled=f.my.disabled=f.mh.disabled=f.mn.disabled=!this.checked"><!--\
				-->modified on<!--\
			--></td><td style="padding:1px;border:0;white-space:nowrap"><!--\
				--><input type=text name=mm size=2 style="width:2em;padding:0;text-align:center" disabled><!--\
				--> / <input type=text name=md size=2 style="width:2em;padding:0;text-align:center" disabled><!--\
				--> / <input type=text name=my size=4 style="width:3em;padding:0;text-align:center" disabled><!--\
				--> at <input type=text name=mh size=2 style="width:2em;padding:0;text-align:center" disabled><!--\
				--> : <input type=text name=mn size=2 style="width:2em;padding:0;text-align:center" disabled><!--\
			--></td></tr><tr style="padding:0;border:0;"><td style="padding:1px;border:0;white-space:nowrap"><!--\
				--><input type=checkbox name=settags checked \
					title="allow changes to tiddler tags" \
					onclick="this.form.tags.disabled=!this.checked">tags<!--\
			--></td><td style="padding:1px;border:0;white-space:nowrap"><!--\
				--><input type=text name=tags size=35 value="" style="width:98%" \
					title="enter new tags or use \'+tag\' and \'-tag\' to add/remove tags from existing tags"><!--\
			--></td></tr></table><!--\
			--><div style="margin-top:.8em;text-align:center"><!--\
			--><nobr><input type=button name=display disabled style="width:32%" value="display tiddlers" \
				onclick="config.macros.tiddlerTweaker.displaytiddlers(this)"><!--\
			--> <input type=button name=del disabled style="width:32%" value="delete tiddlers" \
				onclick="config.macros.tiddlerTweaker.deltiddlers(this)"><!--\
			--> <input type=button name=set disabled style="width:32%" value="update tiddlers" \
				onclick="config.macros.tiddlerTweaker.settiddlers(this)"></nobr><!--\
			--></div><!--\
		--></td></tr></table><!--\
		--></form><span style="display:none"><!--content replaced by tiddler "stats"--></span>\
	',
	handler: function(place,macroName,params,wikifier,paramString,tiddler) {
		var span=createTiddlyElement(place,"span");
		span.innerHTML=this.html;
		this.init(span.firstChild,config.options.txtTweakerSortBy);
	},
	init: function(f,sortby) { // initialize form controls
		if (!f) return; // form might not be rendered yet...
		while (f.list.options[0]) f.list.options[0]=null; // empty current list content
		var tids=store.getTiddlers(sortby);
		if (sortby=="size") // descending order (largest tiddlers listed first)
			tids.sort(function(a,b) {return a.text.length > b.text.length ? -1 : (a.text.length == b.text.length ? 0 : +1);});
		for (i=0; i<tids.length; i++) {
			var label=tids[i].title; var value=tids[i].title;
			if (sortby=="modified" || sortby=="created") {
				label=tids[tids.length-i-1][sortby].formatString("YY.0MM.0DD 0hh:0mm ")+tids[tids.length-i-1].title;
				value=tids[tids.length-i-1].title;
			}
			if (sortby=="size") label="["+tids[i].text.length+"] "+label;
			f.list.options[f.list.length]=new Option(label,value,false,false);
		}
		f.title.value=f.who.value=f.creator.value=f.tags.value="";
		f.cm.value=f.cd.value=f.cy.value=f.ch.value=f.cn.value="";
		f.mm.value=f.md.value=f.my.value=f.mh.value=f.mn.value="";
		f.stats.disabled=f.set.disabled=f.del.disabled=f.display.disabled=true;
		f.settitle.disabled=false;
		config.options.txtTweakerSortBy=sortby; // remember current setting
		f.sortby.value=sortby; // sync droplist selection with current setting
		if (sortby!="modified") // non-default preference... save cookie
			saveOptionCookie("txtTweakerSortBy");
		else removeCookie("txtTweakerSortBy"); // default preference... clear cookie
	},
	selecttiddlers: function(here) { // enable/disable tweaker fields based on number of items selected
		// count how many tiddlers are selected
		var f=here.form; var list=f.list;
		var c=0; for (i=0;i<list.length;i++) if (list.options[i].selected) c++;
		if (c>1) f.title.disabled=true;
		if (c>1) f.settitle.checked=false;
		f.set.disabled=(c==0);
		f.del.disabled=(c==0);
		f.display.disabled=(c==0);
		f.settitle.disabled=(c>1);
		f.stats.disabled=(c==0);
		var msg=(c==0)?'select tiddlers':(c+' tiddler'+(c!=1?'s':'')+' selected');
		here.previousSibling.firstChild.firstChild.nextSibling.innerHTML=msg;
		if (c) clearMessage(); else displayMessage("no tiddlers selected");
	},
	setfields: function(here) { // set tweaker edit fields from first selected tiddler
		var f=here.form;
		if (!here.value.length) {
			f.title.value=f.who.value=f.creator.value=f.tags.value="";
			f.cm.value=f.cd.value=f.cy.value=f.ch.value=f.cn.value="";
			f.mm.value=f.md.value=f.my.value=f.mh.value=f.mn.value="";
			return;
		}
		var tid=store.getTiddler(here.value); if (!tid) return;
		f.title.value=tid.title;
		f.who.value=tid.modifier;
		f.creator.value=tid.fields['creator']||''; // custom field - might not exist
		f.tags.value=tid.tags.join(' ');
		var c=tid.created; var m=tid.modified;
		f.cm.value=c.getMonth()+1;
		f.cd.value=c.getDate();
		f.cy.value=c.getFullYear();
		f.ch.value=c.getHours();
		f.cn.value=c.getMinutes();
		f.mm.value=m.getMonth()+1;
		f.md.value=m.getDate();
		f.my.value=m.getFullYear();
		f.mh.value=m.getHours();
		f.mn.value=m.getMinutes();
	},
	settiddlers: function(here) {
		var f=here.form; var list=f.list;
		var tids=[];
		for (i=0;i<list.length;i++) if (list.options[i].selected) tids.push(list.options[i].value);
		if (!tids.length) { alert("please select at least one tiddler"); return; }
		var cdate=new Date(f.cy.value,f.cm.value-1,f.cd.value,f.ch.value,f.cn.value);
		var mdate=new Date(f.my.value,f.mm.value-1,f.md.value,f.mh.value,f.mn.value);
		if (tids.length>1 && !confirm("Are you sure you want to update these tiddlers:\n\n"+tids.join(', '))) return;
		store.suspendNotifications();
		for (t=0;t<tids.length;t++) {
			var tid=store.getTiddler(tids[t]); if (!tid) continue;
			var title=!f.settitle.checked?tid.title:f.title.value;
			var who=!f.setwho.checked?tid.modifier:f.who.value;
			var tags=tid.tags
			if (f.settags.checked) { 
				var intags=f.tags.value.readBracketedList();
				var addtags=[]; var deltags=[]; var reptags=[];
				for (i=0;i<intags.length;i++) {
					if (intags[i].substr(0,1)=='+')
						addtags.push(intags[i].substr(1));
					else if (intags[i].substr(0,1)=='-')
						deltags.push(intags[i].substr(1));
					else
						reptags.push(intags[i]);
				}
				if (reptags.length)
					tags=reptags;
				if (addtags.length)
					tags=new Array().concat(tags,addtags);
				if (deltags.length)
					for (i=0;i<deltags.length;i++)
						{ var pos=tags.indexOf(deltags[i]); if (pos!=-1) tags.splice(pos,1); }
			}
			if (!f.setcdate.checked) cdate=tid.created;
			if (!f.setmdate.checked) mdate=tid.modified;
			store.saveTiddler(tid.title,title,tid.text,who,mdate,tags,tid.fields);
			if (f.setcreator.checked) store.setValue(tid.title,'creator',f.creator.value); // set creator
			if (f.setcdate.checked) tid.assign(null,null,null,null,null,cdate); // set create date
		}
		store.resumeNotifications();
		this.init(f,f.sortby.value);
	},
	displaytiddlers: function(here) {
		var f=here.form; var list=f.list;
		var tids=[];
		for (i=0; i<list.length;i++) if (list.options[i].selected) tids.push(list.options[i].value);
		if (!tids.length) { alert("please select at least one tiddler"); return; }
		story.displayTiddlers(story.findContainingTiddler(f),tids)
	},
	deltiddlers: function(here) {
		var f=here.form; var list=f.list;
		var tids=[];
		for (i=0;i<list.length;i++) if (list.options[i].selected) tids.push(list.options[i].value);
		if (!tids.length) { alert("please select at least one tiddler"); return; }
		if (!confirm("Are you sure you want to delete these tiddlers:\n\n"+tids.join(', '))) return;
		store.suspendNotifications();
		for (t=0;t<tids.length;t++) {
			var tid=store.getTiddler(tids[t]); if (!tid) continue;
			if (tid.tags.contains("systemConfig"))
				if (!confirm("'"+tid.title+"' is tagged with 'systemConfig'.\n\nRemoving this tiddler may cause unexpected results.  Are you sure?"))
					continue;
			store.removeTiddler(tid.title);
			story.closeTiddler(tid.title);
		}
		store.resumeNotifications();
		this.init(f,f.sortby.value);
	},
	stats: function(here) {
		var f=here.form; var list=f.list; var tids=[]; var out=''; var tot=0;
		var target=f.nextSibling;
		for (i=0;i<list.length;i++) if (list.options[i].selected) tids.push(list.options[i].value);
		if (!tids.length) { alert("please select at least one tiddler"); return; }
		for (t=0;t<tids.length;t++) {
			var tid=store.getTiddler(tids[t]); if (!tid) continue;
			out+='[['+tid.title+']] '+tid.text.length+'\n'; tot+=tid.text.length;
		}
		var avg=tot/tids.length;
		out=tot+' bytes in '+tids.length+' selected tiddlers ('+avg+' bytes/tiddler)\n<<<\n'+out+'<<<\n';
		removeChildren(target);
		target.innerHTML="<hr><font size=-2><a href='javascript:;' style='float:right' "
			+"onclick='this.parentNode.parentNode.style.display=\"none\"'>close</a></font>";
		wikify(out,target);
		target.style.display="block";
	}
};
//}}}
 2次元ベクトルを表す構造体です。

![[Farseer Physics Engine 2.0]]
※XNA用ビルドのライブラリでは、Microsoft.XNA.Framework.Vector2構造体です。それ以外用のプラットフォームでは、FarseerGames.FarseerPhysics.Mathematics.Vector2構造体が肩代わりします。この場合には、完全な互換実装ではないので注意しましょう。

 3次元ベクトルを表す構造体です。

![[Farseer Physics Engine 2.0]]
※XNA用ビルドのライブラリでは、Microsoft.XNA.Framework.Vector3構造体です。それ以外用のプラットフォームでは、FarseerGames.FarseerPhysics.Mathematics.Vector3構造体が肩代わりします。この場合には、フィールドが定義されているのみで、メソッドは定義されていません。互換実装ではないので注意しましょう。[[Vector3構造体]]は[[Matrix構造体]]のプロパティで用いられています。
 4次元ベクトルを表す構造体です。通常のゲームでは3Dか2Dの世界を取り扱うので、4次元空間を表すためではなく、3次元同次座標系のベクトルや点として用いることがあります。また、X, Y, Z, WをR, G, B, Aに対応させて、色を表現する場合もあります。
{{{
public partial struct Vector4 {
	float X;
	float Y;
	float Z;
	float W;
}
}}}
 XNA Game Studio 3.0とは、マイクロソフトが提供するゲーム用フレームワークです。対応プラットフォームは、WindowsとXbox 360、Zuneです。

!XNA Framework
 XNA FrameworkのAPIには、Direct3D 9.0のシェーダモデル3.0までの3Dや、シェーダ言語のHLSLに対応する機能が含まれています。パフォーマンスに配慮しつつ、C#としてスマートに記述できるAPIになっています。

!プログラミング言語
 通常、開発に使用するプログラミング言語は、C# 3.0です。

!統合開発環境
 Visual C# 2008 Expressエディションまたは、Visual Studio 2008の各エディションです。Visual C# 2008 Expressは無償で利用でき、商用利用も可能です。

!基盤技術
 .NET
|''種別:''|file|
|''URL:''|http://www.tiddlytools.com/|
|''ワークスペース:''|(default)|

このtiddlerはこのサーバーの詳細情報を記録するために自動的に作成されました
 このページでは、外部リンク以外の項目を選択するとページ内に表示されます。不要な項目は「閉じる」こともできます。
 オイラー法(単純差分法・前進オイラー法)は、常微分方程式を離散的に数値解析する簡単な方法です。

 次の式が1階常微分方程式です。

\[ \frac{\mathbf{x}(t)}{dt} = f(\mathbf{x}(t)) \]

 $\mathbf{x}(t)$の接線の傾きが関数$f$で表されることを意味しています。

 この式と初期値を与えられたときに、オイラー法では、次のように近似します。

\[ \mathbf{x}(t+\Delta t) = \mathbf{x}(t) + \Delta t f(\mathbf{x}(t)) \]

 $\Delta t$は、小さな値(微小時間)を表しています。ここでは、$\Delta t$は経過時間です。前回の状態から変化量を加えることで、$\Delta t$時間後の状態を計算します。

 なお、この方法は誤差が大きいため、うまく運動量が保存されない場合や、収束しない場合があり、不安定です。学習用と考えた方がよいでしょう。
 クォータニオン(Quaternion)とは、4つの数で表される数です。四元数(しげんすう)とも呼ばれます。特に3Dの回転を表すために用いられます。特に回転を表すクォータニオンは、回転クォータニオンと呼ばれます。

 ここでは、次のように表記します。
\[ \mathbf{q} = [\mathbf{q_v}, q_w] = [(q_x, q_y, q_z), q_w] \]

!理論
 クォータニオンを回転の計算の道具として使うぶんには、気にする必要はありませんが、クォータニオンは複素数を拡張した数で、超複素数と呼ばれる数の一種です。
 $q_w$は実数のスカラー、$q_x$、$q_y$、$q_z$は3つの虚数からなるベクトル$\mathbf{q_v}$です。
\[ \mathbf{q} = [\mathbf{q_v}, q_w] = [(q_x, q_y, q_z), q_w]
 = q_x\mathbf{i} + q_y\mathbf{j} + q_z\mathbf{k} + q_w \]
 ここで、$\mathbf{i}$、$\mathbf{j}$、$\mathbf{k}$は、クォータニオン単位で、次のように計算するものと定義します。掛ける順序を変えると符号が変わることに注意してください。
\[ \mathbf{i}\mathbf{j}\mathbf{k} = \mathbf{i}^2 + \mathbf{j}^2 + \mathbf{k}^2 = -1 \]
\[ \mathbf{i}\mathbf{j} = - \mathbf{j}\mathbf{i} = \mathbf{k} \]
\[ \mathbf{j}\mathbf{k} = - \mathbf{k}\mathbf{j} = \mathbf{i} \]
\[ \mathbf{k}\mathbf{i} = - \mathbf{i}\mathbf{k} = \mathbf{j} \]

!実装

 クォータニオンの実装は、[[Quaternion構造体]]です。

!クォータニオン Even Product(Even Product)

 この計算はあまり使われません。

\[ \rm{Even}(\mathbf{a}, \mathbf{b}) = \rm{Even}(\mathbf{b}, \mathbf{a}) = \frac{\mathbf{a}\mathbf{b}+\mathbf{b}\mathbf{a}}{2}
 = a_w b_w - \mathbf{a_v} \cdot \mathbf{b_v} + a_w\mathbf{b_v} + b_w\mathbf{a_v} \]
\[= \left[ \left( a_w b_x + a_x b_w, a_w b_y + a_y b_w, a_w b_z + a_z b_w \right), a_w b_w - a_x b_x - a_y b_y - a_z b_z \right] \]
!クォータニオン Odd Product(Cross Product, Odd Product)

 ベクトル部の外積と等価です。

\[ \rm{Odd}(\mathbf{a}, \mathbf{b}) = \mathbf{a} \times \mathbf{b}
 = \frac{\mathbf{a}\mathbf{b}-\mathbf{b}\mathbf{a}}{2} = \mathbf{a_v} \times \mathbf{b_v}\]
\[= \left[ \left(a_y b_z - a_z b_y, a_z b_x - a_x b_z, a_x b_y - a_y b_x \right), 0 \right] \]
 クォータニオンのべき乗は次のように計算できます。

\[ \mathbf{q}^\mathbf{p} = \exp(\log(\mathbf{q})\mathbf{p}) \]
また、pがスカラーの場合には次のように計算できます。
\[ \mathbf{q}^p = \exp(\log(\mathbf{q})p) \]

 回転クォータニオンであれば、$p = -1$のときは[[クォータニオンの逆数]]と同じです。

\[ \mathbf{q}^{-1} = \exp(-\log(\mathbf{q})) \]

{{{
		/// <summary>
		/// べき乗
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <param name="result"></param>
		public static void Pow(ref Quaternion a, float b, out Quaternion result) {
			Log(ref a, out result);
			Quaternion.Multiply(ref result, b, out result);
			Exp(ref result, out result);
		}

		/// <summary>
		/// べき乗
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <returns></returns>
		public static Quaternion Pow(Quaternion a, float b) {
			Quaternion result;
			Pow(ref a, b, out result);
			return result;
		}
}}}
!ユークリッド外積(Euclidean Outer Product)
 この計算はあまり使われません。

\[ \rm{Outer}(\mathbf{a}, \mathbf{b})
 = \frac{\overline{\mathbf{a}}\mathbf{b}-\overline{\mathbf{b}}\mathbf{a}}{2}
 = b_w \mathbf{a}_v - a_w \mathbf{b}_v - \mathbf{a} \times \mathbf{b}
\]
\[ = \left[\left(
b_w a_x - a_w b_x - a_y b_z + a_z b_y,
b_w a_y - a_w b_y - a_z b_x + a_x b_z,
b_w a_z - a_w b_z - a_x b_y + a_y b_x
\right), 0 \right]
\]
 クォータニオンの内積は、ベクトルの内積と同様です。

\[ \mathbf{a} \cdot \mathbf{b} = a_x b_x + a_y b_y + a_z b_z + a_w b_w \]

 あるクォータニオン自身の内積はクォータニオンの大きさの2乗になります。2つの単位クォータニオン同士のなす角は内積の計算で求められます。

 また、次のように書きなおすこともできます。

!ユークリッド内積(Euclidean Inner Product)

\[ \mathbf{a} \cdot \mathbf{b} = \mathbf{b} \cdot \mathbf{a}
 = \frac{\overline{\mathbf{a}}\mathbf{b}+\overline{\mathbf{b}}\mathbf{a}}{2} \]
\[= \left[ (0, 0, 0), a_x b_x + a_y b_y + a_z b_z + a_w b_w \right] = a_x b_x + a_y b_y + a_z b_z + a_w b_w \]

{{{
	public partial struct Quaternion {
		/// <summary>
		/// 内積
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <returns></returns>
		public static float Dot(Quaternion a, Quaternion b) {
			float result;
			Dot(ref a, ref b, out result);
			return result;
		}
		/// <summary>
		/// 内積
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <param name="result"></param>
		public static void Dot(ref Quaternion a, ref Quaternion b, out float result) {
			result = a.X * b.X + a.Y * b.Y + a.Z * b.Z + a.W * b.W;
		}
	}
}}}
 クォータニオンの和と差はベクトルの場合と同様に、成分同士の和と差を計算するだけです。

\[ \mathbf{a} \pm \mathbf{b} = [(a_x \pm b_x, a_y \pm b_y, a_z \pm b_z), a_w \pm b_w] \]

!実装
{{{
	public partial struct Quaternion {

		/// <summary>
		/// 加算
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <param name="result"></param>
		public static void Add(ref Quaternion a, ref Quaternion b, out Quaternion result) {
			result.X = a.X + b.X;
			result.Y = a.Y + b.Y;
			result.Z = a.Z + b.Z;
			result.W = a.W + b.W;
		}
		/// <summary>
		/// 減算
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <param name="result"></param>
		public static void Subtract(ref Quaternion a, ref Quaternion b, out Quaternion result) {
			result.X = a.X - b.X;
			result.Y = a.Y - b.Y;
			result.Z = a.Z - b.Z;
			result.W = a.W - b.W;
		}

		/// <summary>
		/// 加算
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <returns></returns>
		public static Quaternion operator +(Quaternion a, Quaternion b) {
			Quaternion result;
			Add(ref a, ref b, out result);
			return result;
		}

		/// <summary>
		/// 減算
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <returns></returns>
		public static Quaternion operator -(Quaternion a, Quaternion b) {
			Quaternion result;
			Subtract(ref a, ref b, out result);
			return result;
		}
	}
}}}
\[ \left| \mathbf{q} \right| = \sqrt{\mathbf{q} \cdot \mathbf{q}} = \sqrt{q_x^2+q_y^2+q_z^2+q_w^2} \]

{{{
	public partial struct Quaternion {
		/// <summary>
		/// 長さ
		/// </summary>
		public float Length() {
			return (float)System.Math.Sqrt(LengthSquared());
		}
		/// <summary>
		/// 長さの2乗
		/// </summary>
		public float LengthSquared() {
			return X * X + Y * Y + Z * Z + W * W;
		}
		/// <summary>
		/// 長さの逆数
		/// </summary>
		/// <returns></returns>
		public float ReciprocalLength() {
			return MathHelper.Rsqrt(LengthSquared());
		}
	}
}}}
 クォータニオンの対数は次のように計算します。

\[ \log \mathbf{q}
 = \log \left[ \mathbf{q_v}, q_w \right]
 = \log \left[ \mathbf{n} \sin \alpha, \cos \alpha \right]
 \equiv \left[ \alpha \mathbf{n} , 0 \right]
 = \left[ \frac { \cos^{-1} q_w } { \sin (\cos^{-1} q_w) } \mathbf{q_v} ,0 \right]
 = \left[ \frac { \cos^{-1} q_w } { \sqrt { 1 - q_w^2 } } \mathbf{q_v} ,0 \right] \]

 ただし、$q_w^2=1$の場合には、次の式に基づいて計算します。
\[ [ (0, 0, 0), 1 ] = e^{(0, 0, 0)} \]

{{{
		/// <summary>
		/// 対数
		/// </summary>
		/// <param name="a"></param>
		/// <param name="result"></param>
		public static void Log(ref Quaternion a, out Quaternion result) {
			if (Math.Abs(a.W) < 1 - float.Epsilon) {
				var s = (float)Math.Acos(a.W) * MathHelper.Rsqrt(1 - a.W * a.W);
				result.X = a.X * s;
				result.Y = a.Y * s;
				result.Z = a.Z * s;
				result.W = 0;
			} else {
				result.X = a.X;
				result.Y = a.Y;
				result.Z = a.Z;
				result.W = 0;
			}
		}
}}}

 クォータニオンの指数は次のように計算できます。

\[ \theta = \left| \mathbf{v} \right| \]
\[
\exp\mathbf{q} = e^\mathbf{q}
= e^\mathbf{v} = \left[
\sin\left(\frac 1 2 \theta\right)
\mathbf{n},
\cos\left(\frac 1 2 \theta\right) \right]
= \left[
\frac {\sin\left(\frac 1 2 \theta\right)} {\theta} \mathbf{v},
\cos\left(\frac 1 2 \theta\right) \right]
\]

 ここで、$\mathbf{n}$は、正規化された$\mathbf{v}$です。
\[ \mathbf{n} = \frac {\mathbf{v}} {\left| \mathbf{v} \right|} \]

ただし、$\mathbf{v} = \mathbf{0} = (0, 0, 0)$のときは、次のように計算します。
\[ e^{(0, 0, 0)} = [ (0, 0, 0), 1 ] \]
 これは、次の通常の対数の計算と同じです。
\[e^0 = 1\]

 また、変数$ \alpha = \frac \theta 2 $を導入すると次のようになります。

\[
\exp\mathbf{q} = e^\mathbf{q}
= e^\mathbf{v} = \left[ \mathbf{n} \sin \alpha, \cos \alpha \right]
= \left[ \mathbf{v} \frac {\sin \alpha}{\alpha}, \cos \alpha \right]
= \left[ \frac{\sin \left|\mathbf{q_v}\right|}{\left|\mathbf{q}_v\right|} \mathbf{q}_v, \cos \left|\mathbf{q_v}\right| \right]
\]

 この計算は$\alpha$が0に近いときに不安定なので、代わりに次のように計算することができます。

\[ \frac {\sin \alpha} {\alpha}
= \frac 1 \alpha \left( \frac {\alpha}{1!}
- \frac {\alpha^3}{3!}
+ \frac {\alpha^5}{5!}
- \frac {\alpha^7}{7!} + ... \right)
= 1
- \frac {\alpha^2}{6}
+ \frac {\alpha^4}{120}
- \frac {\alpha^6}{5040} + ...
\]

!クォータニオンの指数の級数展開

 クォータニオンの指数は、実数の指数の級数展開への拡張で定義できます。

!!実数の指数の級数展開
\[ e^x = \sum^\infty_{n=0}{\frac{x^n}{n!}}
 = 1 + \frac{x}{1!} + \frac{x^2}{2!} + \frac{x^3}{3!} + \frac{x^4}{4!} + ... \]

!!クォータニオンの指数の級数展開
\[ e^\mathbf{q} =  \sum^\infty_{n=0}{\frac{\mathbf{q}^n}{n!}} 
 = 1 + \frac{\mathbf{q}}{1!} + \frac{\mathbf{q}^2}{2!} + \frac{\mathbf{q}^3}{3!} + \frac{\mathbf{q}^4}{4!} + ... \]

{{{
		/// <summary>
		/// 指数
		/// </summary>
		/// <param name="a"></param>
		/// <param name="result"></param>
		public static void Exp(ref Quaternion a, out Quaternion result) {
			var alpha = a.X * a.X + a.Y * a.Y + a.Z * a.Z;
			if (alpha > float.Epsilon) {
				alpha *= MathHelper.Rsqrt(alpha);
				var s = (float)Math.Sin(alpha) / alpha;
				result.X = a.X * s;
				result.Y = a.Y * s;
				result.Z = a.Z * s;
				result.W = (float)Math.Cos(alpha);
			} else {
				result.X = a.X;
				result.Y = a.Y;
				result.Z = a.Z;
				result.W = 1;
			}
		}
}}}
!指数写像Catmull-Romスプライン

 図は、横軸がパラメータ$t$です。縦軸はクォータニオンの$x$、$y$,、$z$、$w$成分を、赤、緑、青、黒で表しています。適当な4つの回転クォータニオン$\mathbf{a}$、$\mathbf{b}$、$\mathbf{c}$、$\mathbf{d}$がパラメータ$t$が$0 \le t \le 1$の範囲で変化して、補間結果のクォータニオン$\mathbf{q}$の値を図示したものです。点が元の$\mathbf{a}$、$\mathbf{b}$、$\mathbf{c}$、$\mathbf{d}$のクォータニオンの値を表し、曲線が補完されたクォータニオン$\mathbf{q}$の値です。

[img[QuaternionCatmullRom.png]]
 クォータニオンの正規化とは、クォータニオンの大きさを1にする操作です。
\[ \frac { \mathbf{q} } { \left| \mathbf{q} \right| } = \frac { [(q_x, q_y, q_z), q_w] } { \sqrt { q_x^2 + q_y^2 + q_z^2 + q_w^2 } } \]
\[ = \left[\left(\frac {q_x} {\sqrt { q_x^2 + q_y^2 + q_z^2 + q_w^2 }}, \frac {q_y} {\sqrt { q_x^2 + q_y^2 + q_z^2 + q_w^2 }}, \frac {q_z} {\sqrt { q_x^2 + q_y^2 + q_z^2 + q_w^2 }}\right), \frac {q_w} {\sqrt { q_x^2 + q_y^2 + q_z^2 + q_w^2 }} \right] \]
 クォータニオンの積は次のように計算します。特に回転クォータニオン同士の積は、2つの回転を結合してできる1つの回転を表します。

\[ \mathbf{q} = \mathbf{a} \mathbf{b}
 = [\mathbf{a_v}\times \mathbf{b_v} + a_w \mathbf{b_v} + b_w \mathbf{a_v}, a_w b_w - \mathbf{a_v} \cdot \mathbf{b_v}] \]

\[ q_x = +a_wb_x +a_xb_w +a_yb_z -a_zb_y \]
\[ q_y = +a_wb_y +a_yb_w -a_xb_z +a_zb_x \]
\[ q_z = +a_wb_z +a_zb_w +a_xb_y -a_yb_x \]
\[ q_w = +a_wb_w -a_xb_x -a_yb_y -a_zb_z \]

 一般的には、物体を回転Aで回したあと、回転Bで回した回転状態と、回転Bで回したあと、回転Aで回した回転状態は一致しません。これと同様に、一般的に2つのクォータニオンの積を入れ替えると結果は一致しません。
 交換則が成り立たないことを非可換(noncommutative)といいます。回転に用いられるクォータニオンの積は非可換です。

\[ \mathbf{a}\mathbf{b} \neq \mathbf{b}\mathbf{a} \]

{{{
	public partial struct Quaternion {
		/// <summary>
		/// 乗算
		/// </summary>
		/// <param name="a">A</param>
		/// <param name="b">B</param>
		/// <param name="result">結果</param>
		public static void Multiply(ref Quaternion a, ref Quaternion b, out Quaternion result) {
			result = new Quaternion();
			result.X = a.W * b.X + a.X * b.W + a.Y * b.Z - a.Z * b.Y;
			result.Y = a.W * b.Y + a.Y * b.W - a.X * b.Z + a.Z * b.X;
			result.Z = a.W * b.Z + a.Z * b.W + a.X * b.Y - a.Y * b.X;
			result.W = a.W * b.W - a.X * b.X - a.Y * b.Y - a.Z * b.Z;
		}

		/// <summary>
		/// 乗算
		/// </summary>
		public static Quaternion operator *(Quaternion a, Quaternion b) {
			Quaternion result;
			Multiply(ref a, ref b, out result);
			return result;
		}
	}
}}}
 回転クォータニオン$\mathbf{q}$を角速度$\mathbf{\omega}$で回転させる式は次のようになります。

\[ \mathbf{\dot{q}}(t) =  \mathbf{\omega}(t) \mathbf{q}(t) \]

!角速度を回転クォータニオンにする

 角速度$\mathbf{\omega}(t)$と時間差分$\Delta t$に対応する回転$\mathbf{q}_\omega(t)$は、[[軸と角度からクォータニオン]]の計算で得ることができます。回転軸が$ \frac{\mathbf{\omega}(t)}{\left| \mathbf{\omega}(t) \right|}$、回転すべき角度が$\mathbf{\omega}(t) \Delta t$ですから、$\mathbf{q}_\omega(t)$は次のようになります。

\[ \mathbf{q}_\omega(t)
= \left[ \frac{\mathbf{\omega}(t)}{\left| \mathbf{\omega}(t) \right|}
\sin \frac{\left| \mathbf{\omega}(t) \right| \Delta t}{2},
\cos \frac{\left| \mathbf{\omega}(t) \right| \Delta t}{2}  \right] \]

 角速度$\mathbf{q}_\omega(t)$が一定であるとすれば、[[オイラー法]]の式に適用すると次のようになります。

\[ \mathbf{q}(t + \Delta t)
= \left[ \frac{\mathbf{\omega}(t)}{\left| \mathbf{\omega}(t) \right|}
\sin \frac{\left| \mathbf{\omega}(t) \right| \Delta t}{2},
\cos \frac{\left| \mathbf{\omega}(t) \right| \Delta t}{2}  \right] \mathbf{q}(t) \]

 微分は次のようになります。
\[ \frac {d\mathbf{\dot{q}}(t)} {dt} = \frac{1}{2} \mathbf{q}(t) [ \mathbf{\omega}(t), 0 ] dt \]

 [[オイラー法]]を適用すると次のようになります。
\[ \mathbf{q}(t + \Delta t) = \mathbf{q}(t) + \frac{\Delta t}{2} \mathbf{q}(t) [ \mathbf{\omega}(t), 0 ] \]

 なお、回転クォータニオンは大きさを1に保つ必要があります。しかし、この計算はクォータニオンの大きさを変えてしまうため、正規化の併用が必要です。また、回転クォータニオンの差分法を繰り返す場合には、誤差蓄積によって回転クォータニオンの大きさが崩れることがあります。いずれにしても、正規化は含めた方がよいでしょう。
!!Phongの反射モデル

|!環境反射係数 (Ambient)|$k_a$|!光源$j$の環境反射についての彩度|$i_{ja}$|
|!拡散反射係数 (Diffuse)|$k_d$|!光源$j$の拡散反射についての彩度|$i_{jd}$|
|!鏡面反射係数 (Specular)|$k_s$|!光源$j$の鏡面反射についての彩度|$i_{js}$|
|!鏡面反射の鋭さ (Specular Power)|$\alpha$|! | |
|!反射ベクトル([[正反射]])|$\mathbf{R}$|!視線ベクトル|$\mathbf{E}$|
|!表面の単位法線ベクトル|$\mathbf{N}$|!光源への単位方向ベクトル|$\mathbf{L}$|


\[ I_p = k_a i_a + \sum_{j=0}^n \left( k_d (\mathbf{L} \cdot \mathbf{N}) i_{jd} + k_s (\mathbf{R} \cdot \mathbf{E})^\alpha i_{js} \right) \]

 一般的に、これら係数や彩度の変数は、R,G,Bの色の成分をもたせ、それぞれに式を適用します。

!!Blinn-Phongの反射モデル

 このモデルでは、反射ベクトルの代用としてハーフベクトルを用いて、ハーフベクトルと法線との内積を計算します。

|!ハーフベクトル | \[ \mathbf{H} = \frac{\mathbf{L}+\mathbf{E}}{|\mathbf{L}+\mathbf{E}|} \] |

\[ I_p = k_a i_a + \sum_{j=0}^n \left( k_d (\mathbf{L} \cdot \mathbf{N}) i_{jd} + k_s (\mathbf{H} \cdot \mathbf{N})^\alpha i_{js} \right) \]

!!

!![[XNA Game Studio 3.1]]のBasicEffectクラスの反射モデル


\[ I_p = k_a + \sum_{j=0}^2 \left( k_d (\mathbf{L} \cdot \mathbf{N}) i_{jd} + k_s (\mathbf{H} \cdot \mathbf{N})^\alpha i_{js} \right) \]
 スカラー(Scalar)とは、ひとつの数値で表される方向を持たない量です。大きさと方向を持つ[[ベクトル]]と対比されます。

 時間、質量、長さなどを表すために使われます。
 $(\mathbf{a} \times \mathbf{b}) \cdot \mathbf{c}$をスカラー三重積(Scalar Triple Product)といい、$\mathbf{a}$と$\mathbf{b}$と$\mathbf{c}$を辺とする平行六面体の符号付き体積を表します。

\[ (\mathbf{a} \times \mathbf{b}) \cdot \mathbf{c}
 = (a_y b_z - a_z b_y) c_x + (a_z b_x - a_x b_z) c_y + (a_x b_y - a_y b_x) c_z \]
\[ = a_y b_z c_x + a_z b_x c_y + a_x b_y c_z
 - a_z b_y c_x - a_x b_z c_y - a_y b_x c_z \]

 スカラー三重積は行列式を使って表すこともできます。
\[ (\mathbf{a} \times \mathbf{b}) \cdot \mathbf{c} = \begin{vmatrix}
 \mathbf{a}_x & \mathbf{a}_y & \mathbf{a}_z \\
 \mathbf{b}_x & \mathbf{b}_y & \mathbf{b}_z \\
 \mathbf{c}_x & \mathbf{c}_y & \mathbf{c}_z \\
 \end{vmatrix} \]

{{{
	public partial struct Vector3 {
		/// <summary>
		/// スカラー三重積(平行六面体の符号付き体積)
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <param name="c"></param>
		/// <returns></returns>
		public static float ScalarTripleProduct(ref Vector3 a, ref Vector3 b, ref Vector3 c) {
			return
				(a.Y * b.Z - a.Z * b.Y) * c.X +
				(a.Z * b.X - a.X * b.Z) * c.Y +
				(a.X * b.Y - a.Y * b.X) * c.Z;
		}
	}
}}}
*スケール:$\mathbf{v} = (v_x, v_y, v_z)$
*スケーリング(拡大縮小)を表す行列:$\mathbf{M}$
\[
\mathbf{M} =
\left[ \array{
v_x & 0 & 0 & 0\\
0 & v_y & 0 & 0\\
0 & 0 & v_z & 0\\
0 & 0 & 0 & 1\\
} \right]
\]

{{{
	public partial struct Matrix {
		/// <summary>
		/// スケーリング行列を作る。
		/// </summary>
		/// <param name="x">X</param>
		/// <param name="y">Y</param>
		/// <param name="z">Z</param>
		/// <param name="result"></param>
		public static void CreateScale(float x, float y, float z, out Matrix result) {
			result = new Matrix();
			result.M11 = x; result.M12 = 0; result.M13 = 0; result.M14 = 0;
			result.M21 = 0; result.M22 = y; result.M23 = 0; result.M24 = 0;
			result.M31 = 0; result.M32 = 0; result.M33 = z; result.M34 = 0;
			result.M41 = 0; result.M42 = 0; result.M43 = 0; result.M44 = 1;
		}
		/// <summary>
		/// スケーリング行列を作る。
		/// </summary>
		/// <param name="scale">スケール</param>
		/// <param name="result"></param>
		public static void CreateScale(float scale, out Matrix result) {
			CreateScale(scale, out result);
		}
	}
}}}
 デュアルクォータニオン(Dual Quaternion)は、2つの[[クォータニオン]]によって回転と平行移動を表すことができる数です。つまり、8つの成分を持ちます。デュアルクォータニオンは、二元数(Dual Number)のように、クォータニオンにデュアル単位$\varepsilon$を加えた数です。デュアル単位$\varepsilon$は次のように計算するものとします。

\[ \varepsilon^2 = 0\]

 ここでは、デュアルクォータニオン$\mathbf{Q}$を次のように表記します。

回転を表すクォータニオン: $\mathbf{r}$
平行移動を表すクォータニオン: $\mathbf{d}$
\[ \mathbf{Q} = \mathbf{r} + \varepsilon \mathbf{d}
 = \left[\left(r_x, r_y, r_z\right), r_w\right] + \varepsilon \left[\left(d_x, d_y, d_z\right), d_w \right] \]

 $\mathbf{r}$を実部(Real Part)、$\mathbf{d}$を非実部(Dual Part / Non Real Part)と呼びます。

 デュアル単位$\varepsilon$とクォータニオン単位$i, j, k$との計算方法には、可換と非可換の2つの定義があります。物体の回転と平行移動(剛体の運動)の表現には非可換の定義が使われます。

!実装

 デュアルクォータニオンの実装は、[[DualQuaternion構造体]]です。XNA Game Studio 3.0には相当する機能はありません。

{{{
	/// <summary>
	/// デュアルクォータニオン
	/// </summary>
	public partial struct DualQuaternion {
		public Quaternion Real;
		public Quaternion Dual;
	}
}}}
 デュアルクォータニオンから行列$\mathbf{M}$を作るには次のようにします。

デュアルクォータニオン : $ \mathbf{Q} = \mathbf{r} + \varepsilon \mathbf{d}
 = \left[\left(r_x, r_y, r_z\right), r_w\right] + \varepsilon \left[\left(d_x, d_y, d_z\right), d_w \right] $

 回転を表す実部$\mathbf{r}$から、[[回転クォータニオンを行列にする]]計算と同様に計算して、行列の回転成分にします。
 平行移動を表す非実部$\mathbf{d}$から、$2\mathbf{d}\overline{\mathbf{r}}$を計算して、行列の平行移動成分にします。

\[
\mathbf{M} =
\left[ \begin{array}{cccc}
 1 - 2r_y^2 - 2r_z^2 & 2r_xr_y + 2r_wr_z & 2r_xr_z - 2r_wr_y & 0 \\
 2r_xr_y - 2r_wr_z & 1 - 2r_x^2 - 2r_z^2 & 2r_yr_z + 2r_wr_x & 0 \\
 2r_xr_z + 2r_wr_y & 2r_yr_z - 2r_wr_x & 1 - 2r_x^2 - 2r_y^2 & 0 \\
 2(-d_w r_x + r_w d_x - d_y r_z + r_y d_z) &
 2(-d_w r_y + d_x r_z - r_x d_z + r_w d_y) &
 2(-d_w r_z + r_x d_y + r_w d_z - d_x r_y) &
 1 \\
\end{array} \right]
\]

!実装
 実装では成分の大きさを調整する処理が入っています。

{{{
	public partial struct Matrix {
		/// <summary>
		/// デュアルクォータニオンから行列
		/// </summary>
		/// <param name="value"></param>
		/// <param name="result"></param>
		/// <returns></returns>
		public static Matrix CreateFromDualQuaternion(ref DualQuaternion value, out Matrix result) {
			var ll = value.Real.LengthSquared();
			var r = 1 / ll;
			float w = value.Real.W, x = value.Real.X, y = value.Real.Y, z = value.Real.Z;
			float w2 = w + w, x2 = x + x, y2 = y + y, z2 = z + z;
			float ww = w * w2, xx = x * x2, yy = y * y2, zz = z * z2;
			float wx = w * x2, wy = w * y2, wz = w * z2, xy = x * y2, xz = x * z2, yz = y * z2;
			float a = value.Dual.W, b = value.Dual.X, c = value.Dual.Y, d = value.Dual.Z;

			result = new Matrix();
			result.M11 = ll - yy - zz;
			result.M12 = xy + wz;
			result.M13 = xz - wy;
			result.M21 = xy - wz;
			result.M22 = ll - xx - zz;
			result.M23 = yz + wx;
			result.M31 = xz + wy;
			result.M32 = yz - wx;
			result.M33 = ll - xx - yy;
			result.M41 = -a * x2 + w2 * b - c * z2 + y2 * d;
			result.M42 = -a * y2 + b * z2 - x2 * d + w2 * c;
			result.M43 = -a * z2 + x2 * c + w2 * d - b * y2;
			result.M14 = 0;
			result.M24 = 0;
			result.M34 = 0;
			result.M44 = ll;

			Matrix.Multiply(ref result, r, out result);
			return result;
		}
	}
}}}
\[ \left|\left| \mathbf{Q} \right|\right|
 = {\mathbf{Q} \bar{\mathbf{Q}}}
 = {\bar{\mathbf{Q}} \mathbf{Q}} \]
\[= (r_x + \varepsilon d_x)^2
 + (r_y + \varepsilon d_y)^2
 + (r_z + \varepsilon d_z)^2
 + (r_w + \varepsilon d_w)^2 \]
\[ = (r_x^2 + r_y^2 + r_z^2 + r_w^2)
 + 2 \varepsilon (r_x d_x + r_y d_y + r_z d_z + r_w d_w) \]

 また、$\left|\left| \mathbf{Q} \right|\right| = 1$のとき、$\mathbf{Q}$は単位デュアルクォータニオンといいます。
 デュアルクォータニオンの積は次のように計算します。

\[ \mathbf{A} = \mathbf{r} + \varepsilon \mathbf{d}
 = \left[ \left(r_x, r_y, r_z\right), r_w \right]
 + \varepsilon \left[ \left(d_x, d_y, d_z\right), d_w \right] \]
\[ \mathbf{B} = \mathbf{R} + \varepsilon \mathbf{D}
 = \left[ \left(R_x, R_y, R_z\right), R_w \right]
 + \varepsilon \left[ \left(D_x, D_y, D_z\right), D_w \right] \]
\[ \mathbf{A}\mathbf{B}
 = \left[ \left(r_x', r_y', r_z'\right), r_w' \right]
 + \varepsilon \left[ \left(d_x', d_y', d_z'\right), d_w' \right]
 = \left( \mathbf{r} + \varepsilon \mathbf{d} \right) \left( \mathbf{R} + \varepsilon \mathbf{D} \right)
 = \mathbf{r} \mathbf{R}
 + \varepsilon ( \mathbf{r} \mathbf{D} + \mathbf{d} \mathbf{R} ) \]

\[ r_x' = r_w R_w + r_x R_w + r_y R_z - r_z R_y \]
\[ r_y' = r_w R_y - r_x R_z + r_y R_w + r_z R_x \]
\[ r_z' = r_w R_z + r_x R_y - r_y R_x + r_z R_w \]
\[ r_w' = r_w R_w - r_x R_x - r_y R_y - r_z R_z \]
\[ d_x' = r_w D_w + r_x D_w + r_y D_z - r_z D_y
           + d_w R_w + d_x R_w + d_y R_z - d_z R_y \]
\[ d_y' = r_w D_y - r_x D_z + r_y D_w + r_z D_x
           + d_w R_y - d_x R_z + d_y R_w + d_z R_x \]
\[ d_z' = r_w D_z + r_x D_y - r_y D_x + r_z D_w
           + d_w R_z + d_x R_y - d_y R_x + d_z R_w \]
\[ d_w' = r_w D_w - r_x D_x - r_y D_y - r_z D_z
           + d_w R_w - d_x R_x - d_y R_y - d_z R_z \]

 デュアルクォータニオンで剛体の位置と平行移動を表すとき、2つのデュアルクォータニオンの積によって結合すると、位置と平行移動を表す1つのデュアルクォータニオンを得ることができます。これは次のような使い方ができることを意味しています。

!例
 次のような剛体による多関節の階層構造があるとします。

*ワールド座標系 : $\mathbf{W}$
**親座標系 : $\mathbf{P}$
***子座標系 : $\mathbf{C}$

 このとき、子座標系からワールド座標系への変換するデュアルクォータニオン$\mathbf{Q}$は次のように計算できます。

\[ \mathbf{Q} = \mathbf{W} \mathbf{P} \mathbf{C} \]

 4行4列の行列で操作する場合と同様に、単純に積による結合です。ただし、DirectXスタイルの行列とは変数の並びが逆であることに注意しましょう。
 これは、回転のみの計算であれば、クォータニオンの積による結合と同じ結果となり、変数の並びも同じです。

!実装
{{{
	public partial struct DualQuaternion {
		/// <summary>
		/// 乗算
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <param name="result"></param>
		public static void Multiply(ref DualQuaternion a, ref DualQuaternion b, out DualQuaternion result) {
			result = new DualQuaternion(
				a.Real.W * b.Real.X + a.Real.X * b.Real.W + a.Real.Y * b.Real.Z - a.Real.Z * b.Real.Y,
				a.Real.W * b.Real.Y - a.Real.X * b.Real.Z + a.Real.Y * b.Real.W + a.Real.Z * b.Real.X,
				a.Real.W * b.Real.Z + a.Real.X * b.Real.Y - a.Real.Y * b.Real.X + a.Real.Z * b.Real.W,
				a.Real.W * b.Real.W - a.Real.X * b.Real.X - a.Real.Y * b.Real.Y - a.Real.Z * b.Real.Z,
				a.Real.W * b.Dual.X + a.Real.X * b.Dual.W + a.Real.Y * b.Dual.Z - a.Real.Z * b.Dual.Y
				+ a.Dual.W * b.Real.X + a.Dual.X * b.Real.W + a.Dual.Y * b.Real.Z - a.Dual.Z * b.Real.Y,
				a.Real.W * b.Dual.Y - a.Real.X * b.Dual.Z + a.Real.Y * b.Dual.W + a.Real.Z * b.Dual.X
				+ a.Dual.W * b.Real.Y - a.Dual.X * b.Real.Z + a.Dual.Y * b.Real.W + a.Dual.Z * b.Real.X,
				a.Real.W * b.Dual.Z + a.Real.X * b.Dual.Y - a.Real.Y * b.Dual.X + a.Real.Z * b.Dual.W
				+ a.Dual.W * b.Real.Z + a.Dual.X * b.Real.Y - a.Dual.Y * b.Real.X + a.Dual.Z * b.Real.W,
				a.Real.W * b.Dual.W - a.Real.X * b.Dual.X - a.Real.Y * b.Dual.Y - a.Real.Z * b.Dual.Z
				+ a.Dual.W * b.Real.W - a.Dual.X * b.Real.X - a.Dual.Y * b.Real.Y - a.Dual.Z * b.Real.Z);
		}
		/// <summary>
		/// 乗算
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <returns></returns>
		public static DualQuaternion Multiply(DualQuaternion a, DualQuaternion b) {
			DualQuaternion result;
			Multiply(ref a, ref b, out result);
			return result;
		}
		/// <summary>
		/// 乗算
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <returns></returns>
		public static DualQuaternion operator *(DualQuaternion a, DualQuaternion b) {
			DualQuaternion result;
			Multiply(ref a, ref b, out result);
			return result;
		}
	}
}}}
視野(Field of View): $f [\mathrm{rad}]$
アスペクト比(Aspect Ratio) ( = ビューポート幅 / ビューポート高さ): $a$
近い面の距離: $z_n$
遠い面の距離: $z_f$
パースペクティブ射影行列(Perspective Projection Matrix): $\mathbf{M}$

\[ \mathbf{M} = \left[ \matrix {
\cot \frac{af}{2} & 0 & 0 & 0 \\
0 & \cot \frac{f}{2} & 0 & 0 \\
0 & 0 & -\frac{z_f}{z_f-z_n} & -1 \\
0 & 0 & -\frac{z_f}{z_f-z_n} z_n & 0 \\ } \right]
\]

※$\cot x = \frac{1}{\tan x}$
※$\mathbf{M}$の座標系はXNA Game StudioのMatrix.CreatePerspectiveFieldOfViewメソッド相当です。

{{{
	public partial struct Matrix {
		/// <summary>
		/// パースペクティブ射影行列をつくる。
		/// </summary>
		/// <param name="fieldOfView"></param>
		/// <param name="aspectRatio"></param>
		/// <param name="nearPlaneDistance"></param>
		/// <param name="farPlaneDistance"></param>
		/// <returns></returns>
		public static Matrix CreatePerspectiveFieldOfView(
			float fieldOfView, float aspectRatio, float nearPlaneDistance, float farPlaneDistance) {
			var q = farPlaneDistance / (farPlaneDistance - nearPlaneDistance);
			fieldOfView *= 0.5f;
			var result = new Matrix
			{
				M11 = (float)(1 / Math.Tan(fieldOfView * aspectRatio)),
				M22 = (float)(1 / Math.Tan(fieldOfView)),
				M33 = -q,
				M34 = -1,
				M43 = -q * nearPlaneDistance,
			};
			return result;
		}
	}
}}}
カメラ位置: $\mathbf{p}$
カメラ注視点: $\mathbf{t}$
カメラ上方ベクトル: $\mathbf{u}$
ビュー行列: $\mathbf{M}$

\[ \mathbf{z}' = (z'_x, z'_y, z'_z) = \frac{\mathbf{p} - \mathbf{t}}{|\mathbf{p} - \mathbf{t}|} \]
\[ \mathbf{x}' = (x'_x, x'_y, x'_z) = \frac{\mathbf{u} \times \mathbf{z}}{|\mathbf{u} \times \mathbf{z}|} \]
\[ \mathbf{y}' = (y'_x, y'_y, y'_z) = \mathbf{z}' \times \mathbf{x}' \]

\[ \mathbf{M}
= \left[ \matrix {
 x'_x & y'_x & z'_x & 0 \\
 x'_y & y'_y & z'_y & 0 \\
 x'_z & y'_z & z'_z & 0 \\
 0 & 0 & 0 & 1 \\
} \right]
 \left[ \matrix {
 1 & 0 & 0 & 0 \\
 0 & 1 & 0 & 0 \\
 0 & 0 & 1 & 0 \\
 -p_x & -p_y & -p_z & 1 \\
} \right]

= \left[ \matrix {
 x'_x & y'_x & z'_x & 0 \\
 x'_y & y'_y & z'_y & 0 \\
 x'_z & y'_z & z'_z & 0 \\
 -p \cdot x' & -p \cdot y' & -p \cdot z' & 1 \\
} \right]
 \]
!Fresnel Equations

|! 入射角 | $\alpha$ |
|! 屈折角 | $\beta$ |
|! 入射元絶対屈折率 | $n_1$ |
|! 入射先絶対屈折率 | $n_2$ |
|! p波反射率 | \[ r_p = \left| \frac{ n_1 \cos \beta - n_2 \cos \alpha }{ n_1 \cos \beta + n_2 \cos \alpha } \right| \] |
|! s波反射率 | \[ r_s = \left| \frac{ n_1 \cos \alpha - n_2 \cos \beta }{ n_1 \cos \alpha + n_2 \cos \beta } \right| \] |
|! p波透過率 | \[ t_p = \sqrt { \frac{ n_2 \cos \beta }{ n_1 \cos \alpha } } \frac{ 2 n_1 \cos \alpha }{ n_1 \cos \beta + n_2 \cos \alpha } \] |
|! s波透過率 | \[ t_s = \sqrt { \frac{ n_2 \cos \beta }{ n_1 \cos \alpha } } \frac{ 2 n_1 \cos \alpha }{ n_1 \cos \alpha + n_2 \cos \beta } \] |

|p波|面に垂直|
|s波|面に平行|

!!Schlickのフレネルの式の近似式
\[ F_r = (1 - F_0)(1 - \cos \theta)^5 + F_0 \]
\[ F_0 = \left(\frac{1-n}{1+n}\right)^2 \]
 プリュッカー内積(Plücker Dot Product)は、置換された内積(Permutated Inner Product)として定義されます。符号によって有向直線同士の位置関係を調べることができます。

プリュッカー直線1: $ \mathbf{a} = \{\mathbf{a}_u : \mathbf{a}_v\}
 = \{ a_1 : a_2 : a_3 : a_4 : a_5 : a_6 \} $
プリュッカー直線2: $ \mathbf{b} = \{\mathbf{b}_u : \mathbf{b}_v\}
 = \{ b_1 : b_2 : b_3 : b_4 : b_5 : b_6 \} $

\[ \mathrm{Side}(\mathbf{a}, \mathbf{b})
 = \mathbf{a}_u \cdot \mathbf{b}_v + \mathbf{a}_v \cdot \mathbf{b}_u
 = a_1 b_4 + a_2 b_5 + a_3 b_6 + a_4 b_1 + a_5 b_2 + a_6 b_3 \]

*$\mathrm{Side}(\mathbf{a}, \mathbf{b}) < 0$のとき、時計回り
*$\mathrm{Side}(\mathbf{a}, \mathbf{b}) > 0$のとき、反時計回り
*$\mathrm{Side}(\mathbf{a}, \mathbf{b}) = 0$のとき、交差
!!プリュッカー座標(Plücker Coordinate)

 ここでは、「プリュッカー座標の3D有向直線」「平面」「ベクトル(点、方向)」を次のように表記します。

プリュッカー直線: $ \mathbf{L} = \{\mathbf{L}_u : \mathbf{L}_v\}
 = \{ L_1 : L_2 : L_3 : L_4 : L_5 : L_6 \} $
平面: $\mathbf{n} = [\mathbf{n}_v : n_w] = [n_x : n_y : n_z : n_w] $
3D同次座標: $ \mathbf{p} = (\mathbf{p}_v : p_w) = (p_x : p_y : p_z : p_w) $
3D座標: $ \mathbf{q} = (q_x, q_y, q_z) = (\frac{p_x}{p_w}, \frac{p_y}{p_w}, \frac{p_z}{p_w}) $
!!面と線の交点
プリュッカー直線: $ \mathbf{a} = \{\mathbf{a}_u : \mathbf{a}_v\} $
平面: $ \mathbf{n} = [\mathbf{n}_n : n_d] $
\[ \mathbf{p} = (\mathbf{a}_v \times \mathbf{n}_n - \mathbf{a}_u n_d : \mathbf{a}_u \cdot \mathbf{n}_n) \]

 ベクトル(Vector)とは大きさと方向をもつ量です。また、複数の数値を組にして表した量です。「ベクタ」「ベクター」とも呼ばれます。
 ひとつの数値で表す[[スカラー]]と対比されます。

 ベクトルは、速度、加速度、力などを表すために使われます。
*2Dの場合
\[\mathbf{v} = (v_x, v_y)\]
*3Dの場合
\[\mathbf{v} = (v_x, v_y, v_z)\]
*4Dの場合
\[\mathbf{v} = (v_x, v_y, v_z, v_w)\]

 また、ベクトルは幾何的な平面上や空間内の始点と終点を持つ「有向線分」を表しています。有向線分は図中では矢印で表されます。

[img[Vector.png]]

!位置ベクトル
 始点を原点に置くと、任意のベクトルは終点の座標によって表すことができます。このような点を表すベクトルを位置ベクトルといいます。

!実装

 ベクトルの実装は、2Dベクトルが[[Vector2構造体]]、3Dベクトルが[[Vector3構造体]]、4Dベクトルが[[Vector4構造体]]です。

 概念上は、ベクトルと点を区別することがあり、それを明確にするために2つの型を実装することもできます。その場合には、およそ倍の実装が必要になり、様々な処理で相互の型変換が登場することになり、パフォーマンス上では不利になりがちです。
 点は位置ベクトルとして表現することで、ベクトルも点も同じように扱えることから、ベクトルも点もひとつの型をもって処理することにします。

{{{
	/// <summary>
	/// 2Dベクトル
	/// </summary>
	public partial struct Vector2 {
		/// <summary>
		/// X
		/// </summary>
		public float X;
		/// <summary>
		/// Y
		/// </summary>
		public float Y;
	}
}}}
{{{
	/// <summary>
	/// 3Dベクトル
	/// </summary>
	public partial struct Vector3 {
		/// <summary>
		/// X
		/// </summary>
		public float X;
		/// <summary>
		/// Y
		/// </summary>
		public float Y;
		/// <summary>
		/// Z
		/// </summary>
		public float Z;
	}
}}}
{{{
	/// <summary>
	/// 4Dベクトル
	/// </summary>
	public partial struct Vector4 {
		/// <summary>
		/// X
		/// </summary>
		public float X;
		/// <summary>
		/// Y
		/// </summary>
		public float Y;
		/// <summary>
		/// Z
		/// </summary>
		public float Z;
		/// <summary>
		/// W
		/// </summary>
		public float W;
	}
}}}
ベクトル$\mathbf{a}$と方向が同じで$k$倍の長さを持つベクトル$\mathbf{v}$は次のように計算します。
*2Dの場合
\[ \mathbf{a} = (a_x, a_y) \]
\[ \mathbf{v} = k\mathbf{a} = k(a_x, a_y) = (ka_x, ka_y) \]

[img[VectorScale.png]]

{{{
	public partial struct Vector2 {
		/// <summary>
		/// ベクトルのスカラー倍。
		/// </summary>
		/// <param name="a">元のベクトル</param>
		/// <param name="scale">スカラー</param>
		/// <returns>結果</returns>
		public static Vector2 operator *(Vector2 a, float scale) {
			return new Vector2(a.X * scale, a.Y * scale);
		}
		/// <summary>
		/// ベクトルのスカラー倍。
		/// </summary>
		/// <param name="a">元のベクトル</param>
		/// <param name="scale">スカラー</param>
		/// <returns>結果</returns>
		public static Vector2 operator *(float scale, Vector2 a) {
			return new Vector2(a.X * scale, a.Y * scale);
		}
	}
}}}
{{{
	public partial struct Vector3 {
		/// <summary>
		/// ベクトルのスカラー倍。
		/// </summary>
		/// <param name="a">元のベクトル</param>
		/// <param name="scale">スカラー</param>
		/// <returns>結果</returns>
		public static Vector3 operator *(Vector3 a, float scale) {
			return new Vector3(a.X * scale, a.Y * scale, a.Z * scale);
		}
		/// <summary>
		/// ベクトルのスカラー倍。
		/// </summary>
		/// <param name="a">元のベクトル</param>
		/// <param name="scale">スカラー</param>
		/// <returns>結果</returns>
		public static Vector3 operator *(float scale, Vector3 a) {
			return new Vector3(a.X * scale, a.Y * scale, a.Z * scale);
		}
	}
}}}
{{{
	public partial struct Vector4 {
		/// <summary>
		/// ベクトルのスカラー倍。
		/// </summary>
		/// <param name="a">元のベクトル</param>
		/// <param name="scale">スカラー</param>
		/// <returns>結果</returns>
		public static Vector4 operator *(Vector4 a, float scale) {
			return new Vector4(a.X * scale, a.Y * scale, a.Z * scale, a.W * scale);
		}
		/// <summary>
		/// ベクトルのスカラー倍。
		/// </summary>
		/// <param name="a">元のベクトル</param>
		/// <param name="scale">スカラー</param>
		/// <returns>結果</returns>
		public static Vector4 operator *(float scale, Vector4 a) {
			return new Vector4(a.X * scale, a.Y * scale, a.Z * scale, a.W * scale);
		}
	}
}}}
!内積(Inner Product, Dot Product, Scalar Product)
 2つのベクトル$\mathbf{a}$とベクトル$\mathbf{b}$の内積は次のように計算します。

*2Dの場合
\[ \mathbf{a} \cdot \mathbf{b} = a_x b_x + a_y b_y \]
*3Dの場合
\[ \mathbf{a} \cdot \mathbf{b} = a_x b_x + a_y b_y + a_z b_z \]
*4Dの場合
\[ \mathbf{a} \cdot \mathbf{b} = a_x b_x + a_y b_y + a_z b_z + a_w b_w \]

 また、2つのベクトル$\mathbf{a}$とベクトル$\mathbf{b}$のなす角が$\theta$であるとすると、次のようにも定義されます。

\[ \mathbf{a} \cdot \mathbf{b} = |\mathbf{a}| |\mathbf{b}| \cos \theta \]

!同じベクトルの内積の平方根
 同じベクトルの内積の平方根を計算すると[[ベクトルの大きさ]]になります。

!ベクトルのなす角
 2つのベクトル$\mathbf{a}$とベクトル$\mathbf{b}$のなす角$\theta$は、内積を使って計算できます。これは先ほどの定義の式の変形です。

\[ \cos \theta = \frac{ \mathbf{a} \cdot \mathbf{b} } { |\mathbf{a}| |\mathbf{b}| } \]

\[ \theta = \cos^{-1} \frac{ \mathbf{a} \cdot \mathbf{b} } { |\mathbf{a}| |\mathbf{b}| } \]

 また、2つのベクトルが正規化されていれば、分母は$1$になり、単純に内積が$\cos \theta$を表します。このような場合には、2つのベクトルの内積が$1$であれば同じ向き、$0$であれば直角、$-1$であれば逆向きであると簡単に判定ができます。

!実装

{{{
	public partial struct Vector2 {
		/// <summary>
		/// 内積
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <returns></returns>
		public static float Dot(Vector2 a, Vector2 b) {
			float result;
			Dot(ref a, ref b, out result);
			return result;
		}
		/// <summary>
		/// 内積
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <param name="result"></param>
		public static void Dot(ref Vector2 a, ref Vector2 b, out float result) {
			result = a.X * b.X + a.Y * b.Y;
		}
	}
}}}

{{{
	public partial struct Vector3 {
		/// <summary>
		/// 内積
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <returns></returns>
		public static float Dot(Vector3 a, Vector3 b) {
			float result;
			Dot(ref a, ref b, out result);
			return result;
		}
		/// <summary>
		/// 内積
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <param name="result"></param>
		public static void Dot(ref Vector3 a, ref Vector3 b, out float result) {
			result = a.X * b.X + a.Y * b.Y + a.Z * b.Z;
		}
	}
}}}

{{{
	public partial struct Vector4 {
		/// <summary>
		/// 内積
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <returns></returns>
		public static float Dot(Vector4 a, Vector4 b) {
			float result;
			Dot(ref a, ref b, out result);
			return result;
		}
		/// <summary>
		/// 内積
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <param name="result"></param>
		public static void Dot(ref Vector4 a, ref Vector4 b, out float result) {
			result = a.X * b.X + a.Y * b.Y + a.Z * b.Z + a.W * b.W;
		}
	}
}}}
 ベクトルの和や差は、成分単位に和や差を計算するだけです。
!2Dの場合
\[ \mathbf{a} = (a_x, a_y) \]
\[ \mathbf{b} = (b_x, b_y) \]
\[ \mathbf{a} \pm \mathbf{b} = (a_x \pm b_x, a_y \pm b_y) \]

[img[VectorAdd.png]]

 また、$\mathbf{a} + \mathbf{b} = \mathbf{b} + \mathbf{a}$が成り立ちます。

!Vector/Add.cs

{{{
	public partial struct Vector2 {
		/// <summary>
		/// 2つのベクトルの和。
		/// </summary>
		/// <param name="a">元のベクトル</param>
		/// <param name="b">元のベクトル</param>
		/// <returns>和</returns>
		public static Vector2 operator +(Vector2 a, Vector2 b) {
			return new Vector2(a.X + b.X, a.Y + b.Y);
		}
		/// <summary>
		/// 2つのベクトルの差。
		/// </summary>
		/// <param name="a">元のベクトル</param>
		/// <param name="b">元のベクトル</param>
		/// <returns>差</returns>
		public static Vector2 operator -(Vector2 a, Vector2 b) {
			return new Vector2(a.X - b.X, a.Y - b.Y);
		}
	}
}}}

{{{
	public partial struct Vector3 {
		/// <summary>
		/// 加算
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <param name="result"></param>
		public static void Add(ref Vector3 a, ref Vector3 b, out Vector3 result) {
			result = new Vector3(a.X + b.X, a.Y + b.Y, a.Z + b.Z);
		}
		/// <summary>
		/// 減算
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <param name="result"></param>
		public static void Subtract(ref Vector3 a, ref Vector3 b, out Vector3 result) {
			result = new Vector3(a.X - b.X, a.Y - b.Y, a.Z - b.Z);
		}

		/// <summary>
		/// 2つのベクトルの和。
		/// </summary>
		/// <param name="a">元のベクトル</param>
		/// <param name="b">元のベクトル</param>
		/// <returns>和</returns>
		public static Vector3 operator +(Vector3 a, Vector3 b) {
			return new Vector3(a.X + b.X, a.Y + b.Y, a.Z + b.Z);
		}
		/// <summary>
		/// 2つのベクトルの差。
		/// </summary>
		/// <param name="a">元のベクトル</param>
		/// <param name="b">元のベクトル</param>
		/// <returns>差</returns>
		public static Vector3 operator -(Vector3 a, Vector3 b) {
			return new Vector3(a.X - b.X, a.Y - b.Y, a.Y - b.Y);
		}
	}
}}}

{{{
	public partial struct Vector4 {
		/// <summary>
		/// 2つのベクトルの和。
		/// </summary>
		/// <param name="a">元のベクトル</param>
		/// <param name="b">元のベクトル</param>
		/// <returns>和</returns>
		public static Vector4 operator +(Vector4 a, Vector4 b) {
			return new Vector4(a.X + b.X, a.Y + b.Y, a.Z + b.Z, a.W + b.W);
		}
		/// <summary>
		/// 2つのベクトルの差。
		/// </summary>
		/// <param name="a">元のベクトル</param>
		/// <param name="b">元のベクトル</param>
		/// <returns>差</returns>
		public static Vector4 operator -(Vector4 a, Vector4 b) {
			return new Vector4(a.X - b.X, a.Y - b.Y, a.Y - b.Y, a.W - b.W);
		}
	}
}}}
!外積(Outer Product, Cross Product, Vector Product)
*3Dの場合
\[ \mathbf{a} \times \mathbf{b} = (a_y b_z - a_z b_y, a_z b_x - a_x b_z, a_x b_y - a_y b_x) \]
また、 $ \mathbf{a} \times \mathbf{b} = - \mathbf{b} \times \mathbf{a} $ が成り立ちます。

!外積の性質
*$\mathbf{a} \times \mathbf{b}$は、$\mathbf{a}$にも$\mathbf{b}$にも垂直なベクトルです。このことから、[[法線]]の計算として多用されます。
*$\left| \mathbf{a} \times \mathbf{b} \right|$は、$\mathbf{a}$と$\mathbf{b}$を辺とする平行四辺形の符号付き面積です。
*$(\mathbf{a} \times \mathbf{b}) \cdot \mathbf{c}$は[[スカラー三重積]]といい、$\mathbf{a}$と$\mathbf{b}$と$\mathbf{c}$を辺とする平行六面体の符号付き体積です。

 外積は行列式を使って表すこともできます。
\[ \mathbf{a} \times \mathbf{b} = \begin{vmatrix}
 \mathbf{i}_x & \mathbf{i}_y & \mathbf{i}_z \\
 \mathbf{a}_x & \mathbf{a}_y & \mathbf{a}_z \\
 \mathbf{b}_x & \mathbf{b}_y & \mathbf{b}_z \\
 \end{vmatrix} \]

 ここで、$\mathbf{i}_x$、$\mathbf{i}_y$、$\mathbf{i}_z$は、それぞれ$x$軸、$y$軸、$z$軸方向の単位ベクトルです。
 ベクトルの大きさ$\left| \mathbf{v} \right|$は次のように計算します。ベクトルの大きさは、ベクトルの絶対値、ベクトルの長さと同じ計算になります。
*2Dの場合
\[ \left| \mathbf{v} \right| = \sqrt{v_x^2+v_y^2} \]
*3Dの場合
\[ \left| \mathbf{v} \right| = \sqrt{v_x^2+v_y^2+v_z^2} \]
*4Dの場合
\[ \left| \mathbf{v} \right| = \sqrt{v_x^2+v_y^2+v_z^2+v_w^2} \]
!正規化(Normalization)
 [[ベクトルの大きさ]]を1にする操作をベクトルの正規化といいます。あるベクトル$\mathbf{a}$を正規化するには次のように計算します。
\[ \frac { \mathbf{a} } { \left| \mathbf{a} \right| } \]
*2Dの場合
\[ \frac { \mathbf{a} } { \left| \mathbf{a} \right| } = \frac { (a_x, a_y) } { \sqrt { a_x^2 + a_y^2 } } = \left(\frac {a_x} {\sqrt { a_x^2 + a_y^2 }}, \frac {a_y} {\sqrt { a_x^2 + a_y^2 }} \right) \]
*3Dの場合
\[ \frac { \mathbf{a} } { \left| \mathbf{a} \right| } = \frac { (a_x, a_y, a_z) } { \sqrt { a_x^2 + a_y^2 + a_z^2 } } \]
\[ = \left(\frac {a_x} {\sqrt { a_x^2 + a_y^2 + a_z^2 }}, \frac {a_y} {\sqrt { a_x^2 + a_y^2 + a_z^2 }}, \frac {a_z} {\sqrt { a_x^2 + a_y^2 + a_z^2 }} \right) \]
*4Dの場合
\[ \frac { \mathbf{a} } { \left| \mathbf{a} \right| } = \frac { (a_x, a_y, a_z, a_w) } { \sqrt { a_x^2 + a_y^2 + a_z^2 + a_w^2 } } \]
\[ = \left(\frac {a_x} {\sqrt { a_x^2 + a_y^2 + a_z^2 + a_w^2 }}, \frac {a_y} {\sqrt { a_x^2 + a_y^2 + a_z^2 + a_w^2 }}, \frac {a_z} {\sqrt { a_x^2 + a_y^2 + a_z^2 + a_w^2 }}, \frac {a_w} {\sqrt { a_x^2 + a_y^2 + a_z^2 + a_w^2 }} \right) \]

{{{
	public partial struct Vector2 {
		/// <summary>
		/// 正規化する。
		/// </summary>
		public void Normalize() {
			var r = ReciprocalLength();
			X *= r;
			Y *= r;
		}
	}
}}}

{{{
	public partial struct Vector3 {
		/// <summary>
		/// 正規化する。
		/// </summary>
		public void Normalize() {
			var r = ReciprocalLength();
			X *= r;
			Y *= r;
			Z *= r;
		}
	}
}}}

{{{
	public partial struct Vector4 {
		/// <summary>
		/// 正規化する。
		/// </summary>
		public void Normalize() {
			var r = ReciprocalLength();
			X *= r;
			Y *= r;
			Z *= r;
			W *= r;
		}
	}
}}}
 ベクトル$\mathbf{v}$を直接クォータニオン$\mathbf{q}$で回転させることもできます。たくさんのベクトルを変換するときは、$\mathbf{q}$を行列にして変換した方がよいでしょう。

\[ \left[ \mathbf{\dot{v}}, w \right] = \mathbf{q} \left[ \mathbf{v}, 0 \right] \mathbf{q}^{-1} \]

 ベクトル$\mathbf{v}$をクォータニオンのベクトル部として、$\left[ \mathbf{v}, 0 \right]$にします。このように実数部が$0$のクォータニオンを純粋クォータニオンと呼びます。
 上式のようにクォータニオンの乗算をすると、結果のクォータニオンのベクトル部$\mathbf{\dot{v}}$が回転後の位置ベクトルになります。

!実装

{{{
	public partial struct Vector3 {
		/// <summary>
		/// 変換したベクトルを得る。クォータニオンが回転クォータニオンであれば、回転したベクトルを得る。
		/// 多くの点を変換するときには、クォータニオンを行列に変換して対応するメソッドを使うと良い。
		/// </summary>
		/// <param name="position"></param>
		/// <param name="rotation"></param>
		/// <param name="result"></param>
		public static void Transform(ref Vector3 position, ref Quaternion rotation, out Vector3 result) {
			Quaternion q = new Quaternion(position.X, position.Y, position.Z, 0);
			Quaternion inverse;
			Quaternion.Inverse(ref rotation, out inverse);
			Quaternion.Multiply(ref rotation, ref q, out q);
			Quaternion.Multiply(ref q, ref inverse, out q);
			result.X = q.X;
			result.Y = q.Y;
			result.Z = q.Z;
		}
	}
}}}
衝突点と衝突面の距離: $d$
バネ係数: $k$
ダンパ係数: $b$
衝突面の法線: $\mathbf{n}$
衝突面の法線方向に対する剛体の相対速度: $v_\mathrm{relative} = \mathbf{n} \cdot ( \mathbf{v_a} - \mathbf{v_b} ) $

\[ \mathbf{f} = \left( k d - b v_\mathrm{relative} \right) \mathbf{n} \]
(**検討事項**)

レイを表すプリュッカー直線: $ \mathbf{r} = \{ r_{ux} : r_{uy} : r_{uz} :r_{vx} : r_{vy} : r_{vz} \} $
AABB: $ \mathbf{B} = \{ \mathbf{b}_0, \mathbf{b}_1 \} = \{ (b_{0x}, b_{0y}, b_{0z}), (b_{1x}, b_{1y}, b_{1z}) \} $
AABBの頂点の1つ: $ \mathbf{b} = (b_x, b_y, b_z) $

!![1] AABBの頂点$\mathbf{b}$を通る辺の方向を表すプリュッカー直線(6つの方向)
\[ \mathbf{e}_{+00} = \{(1, 0, 0) : (1, 0, 0) \times (b_x, b_y, b_z)\} = \{1 : 0 : 0 : 0 : -b_z : b_y\} \]
\[ \mathbf{e}_{0+0} = \{(0, 1, 0) : (0, 1, 0) \times (b_x, b_y, b_z)\} = \{0 : 1 : 0 : b_z : 0 : -b_x\} \]
\[ \mathbf{e}_{00+} = \{(0, 0, 1) : (0, 0, 1) \times (b_x, b_y, b_z)\} = \{0 : 0 : 1 : -b_y : b_x : 0\} \]
\[ \mathbf{e}_{-00} = \{(-1, 0, 0) : (-1, 0, 0) \times (b_x, b_y, b_z)\} = \{-1 : 0 : 0 : 0 : b_z : -b_y\} \]
\[ \mathbf{e}_{0-0} = \{(0, -1, 0) : (0, -1, 0) \times (b_x, b_y, b_z)\} = \{0 : -1 : 0 : -b_z : 0 : b_x\} \]
\[ \mathbf{e}_{00-} = \{(0, 0, -1) : (0, 0, -1) \times (b_x, b_y, b_z)\} = \{0 : 0 : -1 : b_y : -b_x : 0\} \]

!![2] レイ$\mathbf{r}$とAABBの各方向の辺$\mathbf{e}$とのプリュッカー内積
\[ \mathrm{Side}_{+00}(\mathbf{r}, \mathbf{e}_{+00}) = r_{vx} - r_{uy} b_z + r_{uz} b_y \]
\[ \mathrm{Side}_{0+0}(\mathbf{r}, \mathbf{e}_{0+0}) = r_{vy} + r_{ux} b_z - r_{uz} b_x \]
\[ \mathrm{Side}_{00+}(\mathbf{r}, \mathbf{e}_{00+}) = r_{vz} - r_{ux} b_y + r_{uy} b_x \]
\[ \mathrm{Side}_{-00}(\mathbf{r}, \mathbf{e}_{-00}) = -r_{vx} + r_{uy} b_z - r_{uz} b_y \]
\[ \mathrm{Side}_{0-0}(\mathbf{r}, \mathbf{e}_{0-0}) = -r_{vy} - r_{ux} b_z + r_{uz} b_x \]
\[ \mathrm{Side}_{00-}(\mathbf{r}, \mathbf{e}_{00-}) = -r_{vz} + r_{ux} b_y - r_{uy} b_x \]

!!AABBの頂点と辺
| $\mathbf{v}_0$ | $\mathbf{e}_{01}$ | $\mathbf{v}_1$ |
| $\mathbf{e}_{02}$ | | $\mathbf{e}_{13}$ |
| $\mathbf{v}_2$ | $\mathbf{e}_{23}$ | $\mathbf{v}_3$ |

| $\mathbf{e}_{04}$ | | $\mathbf{e}_{15}$ |
| | | |
| $\mathbf{e}_{26}$ | | $\mathbf{e}_{37}$ |

| $\mathbf{v}_4$ | $\mathbf{e}_{45}$ | $\mathbf{v}_5$ |
| $\mathbf{e}_{46}$ | | $\mathbf{e}_{57}$ |
| $\mathbf{v}_6$ | $\mathbf{e}_{67}$ | $\mathbf{v}_7$ |

!![3] [2]の単純化された計算を使って、辺の判定
\[ s_{01} = \mathrm{Side}_{+00}(\mathbf{r}, \mathbf{e}_{01}) = r_{vx} - r_{uy} b_0z + r_{uz} b_0y,
 s_{13} = \mathrm{Side}_{0+0}(\mathbf{r}, \mathbf{e}_{13}) = r_{vy} + r_{ux} b_0z - r_{uz} b_1x \]
\[ s_{23} = \mathrm{Side}_{+00}(\mathbf{r}, \mathbf{e}_{23}) = r_{vx} - r_{uy} b_0z + r_{uz} b_1y,
 s_{02} = \mathrm{Side}_{0+0}(\mathbf{r}, \mathbf{e}_{02}) = r_{vy} + r_{ux} b_0z - r_{uz} b_0x \]
\[ s_{45} = \mathrm{Side}_{+00}(\mathbf{r}, \mathbf{e}_{45}) = r_{vx} - r_{uy} b_1z + r_{uz} b_0y,
 s_{57} = \mathrm{Side}_{0+0}(\mathbf{r}, \mathbf{e}_{57}) = r_{vy} + r_{ux} b_1z - r_{uz} b_1x \]
\[ s_{67} = \mathrm{Side}_{+00}(\mathbf{r}, \mathbf{e}_{67}) = r_{vx} - r_{uy} b_1z + r_{uz} b_1y,
 s_{46} = \mathrm{Side}_{0+0}(\mathbf{r}, \mathbf{e}_{46}) = r_{vy} + r_{ux} b_1z - r_{uz} b_0x \]
\[ s_{04} = \mathrm{Side}_{00+}(\mathbf{r}, \mathbf{e}_{04}) = r_{vz} - r_{ux} b_0y + r_{uy} b_0x,
 s_{15} = \mathrm{Side}_{00+}(\mathbf{r}, \mathbf{e}_{15}) = r_{vz} - r_{ux} b_0y + r_{uy} b_1x \]
\[ s_{37} = \mathrm{Side}_{00+}(\mathbf{r}, \mathbf{e}_{37}) = r_{vz} - r_{ux} b_1y + r_{uy} b_1x,
 s_{26} = \mathrm{Side}_{00+}(\mathbf{r}, \mathbf{e}_{26}) = r_{vz} - r_{ux} b_1y + r_{uy} b_0x \]

{{{
float4 a/*01-13-23-02*/ = r.v.xyxy
 - r.u.yzyz * float4(b.Min.z, b.Max.x, b.Min.z, b.Min.x)
 + r.u.zxzx * float4(b.Min.y, b.Min.z, b.Max.y, b.Min.z);
float4 b/*45-57-67-46*/ = r.v.xyxy
 - r.u.yzyz * float4(b.Max.z, b.Max.x, b.Max.z, b.Min.x)
 + r.u.zxzx * float4(b.Min.y, b.Max.z, b.Max.y, b.Max.z);
float4 c/*04-15-37-26*/ = r.v.zzzz
 - r.u.xyxy * float4(b.Min.y, b.Min.x, b.Max.y, b.Min.x)
 + r.u.yxyx * float4(b.Min.x, b.Min.y, b.Max.x, b.Max.y);
}}}

!![4] 面の判定
\[ f_{0x} = \mathrm{QuadIntersects}(s_{01}, s_{13}, -s_{23}, -s_{02}) \]
\[ f_{0y} = \mathrm{QuadIntersects}(s_{45}, s_{57}, -s_{67}, -s_{46}) \]
\[ f_{1x} = \mathrm{QuadIntersects}(s_{04}, s_{45}, -s_{15}, -s_{01}) \]
\[ f_{1y} = \mathrm{QuadIntersects}(s_{23}, s_{37}, -s_{67}, -s_{26}) \]
\[ f_{0z} = \mathrm{QuadIntersects}(s_{02}, s_{26}, -s_{46}, -s_{04}) \]
\[ f_{1z} = \mathrm{QuadIntersects}(s_{15}, s_{57}, -s_{37}, -s_{13}) \]

{{{
bool QuadIntersects(float4 value) {
 return all(value.xy >= 0) && all(value.zw < 0);
}
}}}
{{{
bool3 f0, f1;
f0.x = QuadIntersects(float4(a.x, a.y, a.z, a.w));
f0.y = QuadIntersects(float4(b.x, b.y, b.z, b.w));
f1.x = QuadIntersects(float4(c.x, b.x, c.y, a.x));
f1.y = QuadIntersects(float4(a.z, c.z, b.z, c.w));
f0.z = QuadIntersects(float4(a.w, c.w, b.w, c.x));
f1.z = QuadIntersects(float4(c.y, b.y, c.z, a.y));
}}}

※レイ方向の各成分の符号に合わせた3面の判定で絞り込みできる(?)
※0の判定は要検討。

!![5] いずれかの面と交差するときAABBとレイの直線が交差
\[ \mathrm{AABBIntersects}(f_{0x}, f_{0y}, f_{0z}, f_{1x}, f_{1y}, f_{1z})
 = f_{0x} \vee f_{0y} \vee f_{0z} \vee f_{1x} \vee f_{1y} \vee f_{1z} \]
{{{
bool hit = any(f0) || any(f1);
}}}
 以下は、三角関数(Trigonometric Function)に関する式です。

\[ \sin^2 \alpha + \cos^2 \alpha = 1 \]

!!加法定理
\[ \sin(\alpha \pm \beta) = \sin \alpha \cos \beta \pm \cos \alpha \sin \beta \]
\[ \cos(\alpha \pm \beta) = \cos \alpha \cos \beta \mp \sin \alpha \sin \beta \]
\[ \tan(\alpha \pm \beta) = \frac { \tan \alpha \pm \tan \beta }{ 1 \mp \tan \alpha \tan \beta } \]

!!倍角の公式
\[ \sin 2 \alpha = 2 \sin \alpha \cos \alpha \]
\[ \cos 2 \alpha = 1 - 2 \sin^2 \alpha \]
\[ \tan 2 \alpha = \frac{2 \tan \alpha}{1 - \tan^2 \alpha} \]

!!半角の公式
\[ \cos^2 \frac{\alpha}{2} = \frac{1 + \cos \alpha}{2} \]
\[ \sin^2 \frac{\alpha}{2} = \frac{1 - \cos \alpha}{2} \]
\[ \tan^2 \frac{\alpha}{2} = \frac{1 - \cos \alpha}{1 + \cos \alpha} \]
 乗法の単位元クォータニオン(Multiplicative Identity Quaternion)は、次のクォータニオンです。

\[ \mathbf{1} = \left[(0, 0, 0), 1\right] \]

 これは次の式のように、あるクォータニオン$\mathbf{q}$との積の結果があるクォータニオン$\mathbf{q}$のまま変化しないクォータニオンです。このように何もしない変換を恒等変換(Identity Transformation)といいます。

\[ \mathbf{q}\mathbf{1} = \mathbf{1}\mathbf{q}= \mathbf{q} \]

 たとえば、回転のためにクォータニオン表現を使うならば、単位元クォータニオンは回転していない初期状態を表します。

!実装
 実装ではこれを単位元(Identity)と呼んで定義しています。

{{{
	public partial struct Quaternion {
		static readonly Quaternion identity = new Quaternion(0, 0, 0, 1);
		/// <summary>
		/// 単位元 Quaternion(0, 0, 0, 1)
		/// </summary>
		public static Quaternion Identity { get { return identity; } }
	}
}}}
 次のデュアルクォータニオンを乗法の単位元デュアルクォータニオンと言います。これは実数$1$であり、[[乗法の単位元クォータニオン]]に一致します。

\[ \mathbf{I} = \left[\left(0, 0, 0\right), 1\right] + \varepsilon\left[\left(0, 0, 0\right), 0\right] = 1\]

 乗法の単位元デュアルクォータニオン(Identity Dual Quaternion)は、デュアルクォータニオンの積の単位元です。積の単位元とは、任意のデュアルクォータニオン$\mathbf{A}$に対して、次の式が成り立つことを言います。

\[ \mathbf{I}\mathbf{A} = \mathbf{A}\mathbf{I} = \mathbf{A} \]

 このように何もしない変換を恒等変換(Identity Transformation)といいます。物体の姿勢にデュアルクォータニオン表現を用いたならば、乗法の単位元デュアルクォータニオンは回転や平行移動をしていない初期状態を表します。

{{{
	public partial struct DualQuaternion {
		static readonly DualQuaternion identity = new DualQuaternion(0, 0, 0, 1, 0, 0, 0, 0);

		/// <summary>
		///	単位デュアルクォータニオン(乗法の単位元)
		/// </summary>
		public static DualQuaternion Identity { get { return identity; } }
	}
}}}
*回転軸の単位ベクトル: $\mathbf{v} = (v_x, v_y, v_z)$
*回転角度[rad]: $\alpha$

\[ c = \cos \alpha \]
\[ s = \sin \alpha \]

\[
\mathbf{M} =
\left[ \array{
v_x^2+(1-v_x^2)c & v_xv_y(1-c)+v_zs & v_xv_z(1-c)-v_ys & 0 \\
v_xv_y(1-c)-v_zs & v_y^2+(1-v_y^2)c & v_yv_z(1-c)+v_xs & 0 \\
v_xv_z(1-c)+v_ys & v_yv_z(1-c)-v_xs & v_z^2+(1-v_z^2)c & 0 \\
0 & 0 & 0 & 1 \\
}
\right]
\]

 なお、任意軸周りの回転は、[[クォータニオン]]でも表現できます。

{{{
	public partial struct Matrix {
		/// <summary>
		/// 軸周りの回転から回転行列を作る。
		/// </summary>
		/// <param name="axis">軸</param>
		/// <param name="angle">角度[Radian]</param>
		/// <returns>回転行列</returns>
		public static Matrix CreateFromAxisAngle(Vector3 axis, float angle) {
			var c = (float)Math.Cos(angle);
			var s = (float)Math.Sin(angle);
			var i = 1 - c;
			var xs = axis.X * s;
			var ys = axis.Y * s;
			var zs = axis.Z * s;
			var xi = axis.X * i;
			var yi = axis.Y * i;
			var zi = axis.Z * i;
			var xx = xi * axis.X;
			var yy = yi * axis.Y;
			var zz = zi * axis.Z;
			var xy = xi * axis.Y;
			var yz = yi * axis.Z;
			var zx = zi * axis.X;

			return new Matrix(
				xx + c, xy + zs, zx - ys, 0,
				xy - zs, yy + c, yz + xs, 0,
				zx + ys, yz - xs, zz + c, 0,
				0, 0, 0, 1);
		}
	}
}}}
 共役クォータニオン(Quaternion Conjugate)は、虚数部の符号を反転させたクォータニオンとして定義されます。

 $\mathbf{q} = \left[ \left(q_x, q_y, q_z\right), q_w \right]$に対する共役クォータニオンは次のようになります。

\[ \overline{\mathbf{q}} \equiv \left[-\mathbf{q_v}, q_w\right]
 = \left[ \left(-q_x, -q_y, -q_z\right), q_w \right] \]

 単位クォータニオンであれば、軸が反転していることからわかるように、逆回転を表します。

{{{
	public partial struct Quaternion {
		/// <summary>
		/// 共役クォータニオン
		/// </summary>
		public void Conjugate() {
			X = -X;
			Y = -Y;
			Z = -Z;
		}
		/// <summary>
		/// 共役クォータニオン
		/// </summary>
		public static void Conjugate(ref Quaternion value, out Quaternion result) {
			result = new Quaternion(-value.X, -value.Y, -value.Z, value.W);
		}
		/// <summary>
		/// 共役クォータニオン
		/// </summary>
		public static Quaternion Conjugate(ref Quaternion value) {
			return new Quaternion(-value.X, -value.Y, -value.Z, value.W);
		}
	}
}}}
 共役デュアルクォータニオンは、次の3種類が定義されます。

\[ \bar{\mathbf{Q}} = \bar{\mathbf{r}} + \varepsilon \bar{\mathbf{d}} \]
\[ \mathbf{Q}_\varepsilon = \mathbf{r} - \varepsilon \mathbf{d} \]
\[ \bar{\mathbf{Q}}_\varepsilon = \bar{\mathbf{r}} - \varepsilon \bar{\mathbf{d}} \]

\[ \overline{\mathbf{Q_1}\mathbf{Q_2}} = \bar{\mathbf{Q_2}} \bar{\mathbf{Q_1}} \]

{{{
	public partial struct DualQuaternion {
		/// <summary>
		/// 共役(乗算入れ替え用)
		/// </summary>
		/// <param name="a"></param>
		/// <param name="result"></param>
		public static void SwapConjugate(ref DualQuaternion a, out DualQuaternion result) {
			Quaternion.Conjugate(ref a.Real, out result.Real);
			Quaternion.Conjugate(ref a.Dual, out result.Dual);
		}

		/// <summary>
		/// 共役(座標変換用)
		/// </summary>
		/// <param name="a"></param>
		/// <param name="result"></param>
		public static void TransformConjugate(ref DualQuaternion a, out DualQuaternion result) {
			Quaternion.Conjugate(ref a.Real, out result.Real);
			result.Dual.W = -a.Dual.W;
			result.Dual.X = +a.Dual.X;
			result.Dual.Y = +a.Dual.Y;
			result.Dual.Z = +a.Dual.Z;
		}
	}
}}}
加える力: $\mathbf{f}_r$
力を加える点の位置: $\mathbf{r}$

\[ \mathbf{f}' = \mathbf{f} + \mathbf{f}_r \]
\[ \mathbf{\tau}' = \mathbf{\tau} + ( \mathbf{r} - \mathbf{x} ) \times \mathbf{f}_r \]
 次の式は、ニュートンの運動方程式です。
\[
m \mathbf{a} = m \frac{d^2 \mathbf{x}}{dt^2} = \mathbf{F}
\]

 これをもとに並進運動を表します。
\[ \frac{d\mathbf{P}(t)}{dt} = m \frac{d\mathbf{v}(t)}{dt} = \mathbf{F} \]
\[ \frac{d\mathbf{x}(t)}{dt} = d\mathbf{v}(t) \]

 ここで、剛体の運動の状態をまとめて、状態ベクトル$\mathbf{Y}(t)$として表すことにします。剛体の並進運動は、位置と速度で表現できます。

\[ \mathbf{Y}(t) = \left( \array{ \mathbf{x}(t) \\ \mathbf{v}(t) } \right) \]

\[ \frac{d}{dt} \mathbf{Y}(t) = \frac{d}{dt} \left( \array{ \mathbf{x}(t) \\ \mathbf{v}(t) } \right)
 = \left( \array{ \mathbf{v}(t) \\ \frac{\mathbf{F}(t)}{m} } \right) \]

 これは微分方程式なので、数値計算をするには、差分法を適用します。

 [[オイラー法]]を状態ベクトルに当てはめると次のようになります。

\[ \mathbf{Y}(t+\Delta t) = \mathbf{Y}(t)
+ \Delta t \left( \array{ \mathbf{v}(t) \\ \frac{\mathbf{F}(t)}{m} } \right) \]

\[ \left( \array{ \mathbf{x}(t+\Delta t) \\ \mathbf{v}(t+\Delta t) } \right)
= \left( \array{ \mathbf{x}(t) \\ \mathbf{v}(t) } \right)
+ \Delta t \left( \array{ \mathbf{v}(t) \\ \frac{\mathbf{F}(t)}{m} } \right) \]

!運動量

 ニュートン力学では運動量$\mathbf{P}$を次のように定義します。
\[ \mathbf{P} = m \mathbf{v} \]

 運動量$\mathbf{P}$を速度$\mathbf{v}$の代わりに使うと、並進運動は次のように表すことができます。

\[ \mathbf{Y}(t) = \left( \array{ \mathbf{x}(t) \\ \mathbf{P}(t) } \right) \]

\[ \mathbf{Y}(t+\Delta t) = \mathbf{Y}(t)
+ \Delta t \left( \array{ \mathbf{P}(t) \\ \mathbf{F}(t) } \right) \]

\[ \left( \array{ \mathbf{x}(t+\Delta t) \\ \mathbf{P}(t+\Delta t) } \right)
= \left( \array{ \mathbf{x}(t) \\ \mathbf{P}(t) } \right)
+ \Delta t \left( \array{ \frac{\mathbf{P}(t)}{m} \\ \mathbf{F}(t) } \right) \]
 剛体の回転運動はオイラーの運動方程式で表すことができます。並進運動に比べて回転運動は少し複雑ですので、簡単に触れることにします。また、3Dにおける回転の表現には回転行列やクォータニオンがありますが、ここでは、クォータニオンを積極的に使う場合を示します。

 ここで登場する、角速度$\omega(t)$、トルク$\mathbf{\tau}(t)$、角運動量$\mathbf{L}(t)$は、それぞれベクトルで表されます。方向が回転軸、大きさが量を表します。

 剛体の回転運動を表す状態ベクトル$\mathbf{Y}(t)$に必要なパラメータは、姿勢$\mathbf{R}^\mathrm{Q}(t)$と角運動量$\mathbf{L}(t)$です。
 ここで、$\mathbf{R}^\mathrm{Q}(t)$は、行列表現の姿勢$\mathbf{R}(t)$のクォータニオン表現とします。

\[ \mathbf{Y}(t) = \left( \array{ \mathbf{R}^\mathrm{Q}(t) \\ \mathbf{L}(t) } \right) \]

 状態ベクトルに[[オイラー法]]を当てはめると次のようになります。

\[ \mathbf{Y}(t+\Delta t)
= \left( \array{ \mathbf{R}^\mathrm{Q}(t+\Delta t) \\ \mathbf{L}(t+\Delta t) } \right)
= \left( \array{
 \mathbf{R}^\mathrm{Q}(t) + \frac{1}{2} \Delta t \mathbf{\omega}^\mathrm{Q}(t) \mathbf{R}^\mathrm{Q}(t) \\
 \mathbf{L}(t) + \Delta t \mathbf{\tau}(t)
 } \right) \]

 ここで、$\frac{1}{2} \mathbf{\omega}^\mathrm{Q}(t) \mathbf{R}^\mathrm{Q}(t)$は、[[クォータニオンを角速度で回転]]する計算を適用したものです。$\mathbf{\omega}^\mathrm{Q}(t)$は、角速度$\mathbf{\omega}(t)$をクォータニオンのベクトル部とする純粋クォータニオンを表します。クォータニオンによる姿勢計算の実装では、正規化も必要です。
\[ \mathbf{\omega}^\mathrm{Q}(t) = \left[\mathbf{\omega}(t), 0\right] = \left[\left(\omega_x, \omega_y, \omega_z \right), 0 \right] \]

 また、角速度$\mathbf{\omega}(t)$は、[[慣性テンソル]]$\mathbf{I}(t)$と角運動量$\mathbf{L}(t)$から、次の計算で求められます。

\[ \mathbf{\omega}(t) = \mathbf{I}^{-1}(t) \mathbf{L}(t)
 = \mathbf{R}^{-1}(t) \mathbf{I_0}^{-1}(t) \mathbf{R}(t) \mathbf{L}(t) \]

 初期姿勢の[[慣性テンソル]]$\mathbf{I}_0(t)$は、剛体の質量特性として事前に求めておくことができます。
点の位置: $\mathbf{r}$
点の速度: $\mathbf{v_r}$

\[ \mathbf{v_r} = \mathbf{v} + \mathbf{\omega} \times ( \mathbf{r} - \mathbf{x} ) \]
 剛体の物理運動シミュレーションは、ニュートン力学に基づいて計算することができます。ニュートン力学は古典力学であり、物体の速度が光速より十分に遅い場合に通用する相対性理論の近似です。日常的な物体の運動を計算するぶんにはニュートン力学で十分です。

 剛体の運動は、並進運動と回転運動で表されます。並進運動はニュートンの運動方程式を、回転運動にはオイラーの運動方程式で表現することができます。

※関連項目は、次の記号で表します。

|! C#変数 | ! 変数: 記号 | |! C#変数 | ! 変数: 記号 |
|! | 時間: $t$ |                                         |! elapsed | 経過時間: $\Delta t$ |
|! mass | 質量: $m$ |                            |! inertiaTensor | 慣性テンソル: $\mathbf{I}$ |
|! | 加速度: $\mathbf{a}$ |                     |! | 角加速度: $\mathbf{\alpha}$ |
|! position | 位置: $\mathbf{x}$ |            |! rotation | 回転: $\mathbf{R}$ |
|! force | 力: $\mathbf{F}$ |                   |! torque | トルク: $\mathbf{\tau}$ |
|! velocity | 速度: $\mathbf{v}$ |            |! angularVelocity | 角速度: $\mathbf{\omega}$ |
|! momentum | 運動量: $\mathbf{P}$ | |! angularMomentum | 角運動量: $\mathbf{L}$ |

!剛体$a$の点と剛体$b$の面の衝突

撃力: $\mathbf{j}$
[[反発係数]]: $\epsilon$
衝突面の法線: $\mathbf{n}$
衝突面の法線方向に対する剛体の相対速度: $v_\mathrm{relative} = \mathbf{n} \cdot ( \mathbf{v_a} - \mathbf{v_b} ) $
衝突点の剛体$a$の重心からの相対座標: $\mathbf{r_a}$
衝突点の剛体$b$の重心からの相対座標: $\mathbf{r_b}$

\[ \mathbf{j} = \frac{ -(1 + \epsilon) v_\mathrm{relative} }
 { \frac{1}{m_a} + \frac{1}{m_b}
 + \mathbf{n} \cdot
 \left( \mathbf{I}_a^{-1} \left( \mathbf{r_a} \times \mathbf{n} \right) \right) \times \mathbf{r_a}
 + \mathbf{n} \cdot
 \left( \mathbf{I}_b^{-1} \left( \mathbf{r_b} \times \mathbf{n} \right) \right) \times \mathbf{r_b}
 } \mathbf{n}
 \]
 剛体の運動は、[[剛体の並進運動]]と[[剛体の回転運動]]を合わせることで表されます。状態ベクトル$\mathbf{Y}(t)$をまとめて定義し直すと次のようになります。

!状態ベクトル
\[ \mathbf{Y}(t) = \left( \array{ \mathbf{x}(t) \\ \mathbf{P}(t) \\ \mathbf{R}^\mathrm{Q}(t) \\ \mathbf{L}(t) } \right) \]

!状態ベクトルの更新(オイラー法)

\[ \mathbf{Y}(t+\Delta t)
= \left( \array{ \mathbf{x}(t+\Delta t) \\ \mathbf{P}(t+\Delta t) \\
 \mathbf{R}^\mathrm{Q}(t+\Delta t) \\ \mathbf{L}(t+\Delta t) } \right)
= \left( \array{ \mathbf{x}(t) + \Delta t \frac{\mathbf{P}(t)}{m} \\
 \mathbf{P}(t) + \Delta t \mathbf{F}(t) \\
 \mathbf{R}^\mathrm{Q}(t) + \frac{1}{2} \Delta t \mathbf{\omega}^\mathrm{Q}(t) \mathbf{R}^\mathrm{Q}(t) \\
 \mathbf{L}(t) + \Delta t \mathbf{\tau}(t)
 } \right) \]

 この式は、剛体の時間が進むときの状態ベクトルの計算方法です。
 この式からも、剛体に外力として力やトルクが加わる瞬間にだけ、運動量や角運動量が変化することがわかります。それに伴って、速度や角速度も変化します。
 逆に、無重力空間で外力が加わらなければ、初期状態で与えられた運動量や角運動量が保たれ、位置は等速に変化し続けます。姿勢については、一般的な形状の物体において、一定でない変化を続けます。これは、慣性テンソルが姿勢によって変化し、角速度は一定ではないことによります。

 また、重力のある空間では、重力を力として加え続けることになります。剛体同士の衝突は、計算方法にもよりますが、衝突した点への力として、力とトルクを加えることで計算できます。
 対角成分が$1$、それ以外の成分が$0$の行列を単位行列(Identity Matrix)といいます。4行4列の行列の場合は次のようになります。

\[ \mathbf{I} = \left[ \array {
1 & 0 & 0 & 0 \\
0 & 1 & 0 & 0 \\
0 & 0 & 1 & 0 \\
0 & 0 & 0 & 1 \\
} \right] \]

 単位行列は、行列の積の単位元です。積の単位元とは、任意のデュアルクォータニオン$\mathbf{A}$に対して、次の式が成り立つことを言います。

\[ \mathbf{I}\mathbf{A} = \mathbf{A}\mathbf{I} = \mathbf{A} \]

 このように何もしない変換を恒等変換(Identity Transformation)といいます。物体の姿勢に行列表現を用いたならば、単位行列は回転や平行移動などをしていない初期状態を表します。

!実装
{{{
	public partial struct Matrix {
		static readonly Matrix identity = new Matrix(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);

		/// <summary>
		///	単位行列(乗法の単位元)
		/// </summary>
		public static Matrix Identity { get { return identity; } }
	}
}}}
!反発係数(Restitution Coefficient)

 反発係数$\epsilon$とは、2つの物体の衝突前の相対速度$v_\mathrm{relative}$と衝突後の相対速度$v_\mathrm{relative}'$の比のことです。

\[ \epsilon = -\frac { v_\mathrm{relative}' } { v_\mathrm{relative} } = -\frac{ v_b' - v_a' }{ v_b - v_a  } \]

 理想的な剛体の反発係数$ \epsilon$は$1$ですが、柔らかい物体ではそれより小さくなります。剛体シミュレーションにおいても、擬似的に柔らかい物体を表すために反発係数を使うことがあります。
$ \mathbf{a} = (\mathbf{a}_v : a_w) $
$ \mathbf{b} = (\mathbf{b}_v : b_w) $

\[ \mathbf{a} \pm \mathbf{b} = (b_w \mathbf{a}_v \pm a_w \mathbf{b}_v : a_w b_w) \]

\[ \mathbf{a} \cdot \mathbf{b} = \frac { \mathbf{a}_v \cdot \mathbf{b}_v }{ a_w b_w } \]
!同次座標系(Homogeneous Coordinate System)

 同次座標系は、もとの座標の次元から1つ次元を増やして、次のように座標を表現するための座標系です。利点については後述します。

!同次座標
 同次座標では3Dベクトル$(x, y, z)$を、4Dベクトル$(x, y, z, w)$として扱います。たとえば、3Dの点$(1, 2, 3)$は、$w=1$を用いて$(1, 2, 3, 1)$と表すことができます。また、$(2, 4, 6, 2)$や$(3, 6, 9, 3)$も3D上の同じ点を表すものとします。

 つまり、次のような4Dベクトルは、同じ3Dベクトル$(x, y, z)$を表すものとして扱います。
\[ (w x, w y, w z, w) \]

 このような4Dベクトルを、$w$成分が1になるように計算(射影)すると、$x, y, z$成分が3Dベクトルの値となって、$(x, y, z)$を取り出せるというわけです。

\[ \left(\frac{w x}{w}, \frac{w y}{w}, \frac{w z}{w}, \frac{w}{w}\right) = (x, y, z, 1) \]

!同次座標系の利点

 平行移動や投影変換を1つの行列で表すことができます。それぞれ1つの行列で表されることで、複数の変換を積で結合して、1つの変換行列を作ることができます。これによって、計算式が整理しやすくなり、効率的になります。

!!直交座標系と同次座標系による座標変換の比較

 直交座標系と同次座標系による計算方法を次のような場面で比較してみることにしましょう。「多関節の物体」や「物体とカメラ」のような複数の階層構造がある処理では、複数回の座標変換が必要になります。
 ここでは、回転と平行移動で1つの座標系を表すものとします。関節が親と子の2階層があるとした場合に、「子のローカル座標の点は、ワールド座標での値はどうなるか」という座標の計算は欠かせません。

 直交座標系での計算は次のようになります。

$(v_x, v_y, v_z)$ : 子のローカル座標上の点
$(v_x', v_y', v_z')$ : 親のローカル座標上の点
$(v_x'', v_y'', v_z'')$ : ワールド座標上の点
$\left[ \array {
a & b & c \\
d & e & f \\
g & h & i \\
} \right]$ : 子の回転
$(t_x, t_y, t_z)$ : 子の平行移動
$\left[ \array {
j & k & l \\
m & n & o \\
p & q & r \\
} \right]$ : 親の回転
$(t_x', t_y', t_z')$ : 親の平行移動

\[ (v_x', v_y', v_z') = (v_x, v_y, v_z) \left[ \array {
a & b & c \\
d & e & f \\
g & h & i \\
} \right] + (t_x, t_y, t_z) \]
\[ (v_x'', v_y'', v_z'') = (v_x', v_y', v_z') \left[ \array {
j & k & l \\
m & n & o \\
p & q & r \\
} \right] + (t_x', t_y', t_z') \]

 このように、3D座標に対して3行3列の行列変換を用いる場合、平行移動は行列とは別に表現する必要があります。この方法で任意の関節数に対応できる座標変換プログラムにした場合、各階層の座標系を順に計算することになるでしょう。煩雑な計算になることは明らかです。

 同次座標系の場合をみてみましょう。同次座標系では同様の座標変換を次のように計算できます。

\[ (v_x'', v_y'', v_z'', v_w'') = (v_x, v_y, v_z, v_w) \left[ \array {
a & b & c & 0 \\
d & e & f & 0 \\
g & h & i & 0 \\
t_x & t_y & t_z & 1
} \right]
 \left[ \array {
j & k & l & 0 \\
m & n & o & 0 \\
p & q & r & 0 \\
t_x' & t_y' & t_z' & 1
} \right] \]

 この式の2つの行列の積は先に計算することができます。次のように1つの変換行列$\mathbf{M}$を計算しておき、変換行列$\mathbf{M}$を使って変換を計算することができます。

\[\mathbf{M} =
\left[ \array {
a & b & c & 0 \\
d & e & f & 0 \\
g & h & i & 0 \\
t_x & t_y & t_z & 1
} \right]
 \left[ \array {
j & k & l & 0 \\
m & n & o & 0 \\
p & q & r & 0 \\
t_x' & t_y' & t_z' & 1
} \right] \]
\[ (v_x'', v_y'', v_z'', v_w'') = (v_x, v_y, v_z, v_w) \mathbf{M} \]

 このように計算式は単純になります。物体上のたくさんの点に対して同様の座標変換をする場合には、同じ変換行列$\mathbf{M}$を使って計算するだけです。このような利点から、同次座標系が用いられます。
 点$\mathbf{p}$、点$\mathbf{q}$、点$\mathbf{r}$、点$\mathbf{s}$からなる四面体の符号付き体積$v$は次のように計算できます。

\[ v = \frac {1} {6} \begin{vmatrix}
 p_x - s_x & p_y - s_y & p_z - s_z \\
 q_x - s_x & q_y - s_y & q_z - s_z \\
 r_x - s_x & r_y - s_y & r_z - s_z \\
 \end{vmatrix} \]

 辺$\mathbf{a}$、辺$\mathbf{b}$、辺$\mathbf{c}$で表すこともできます。

\[ \mathbf{a} = \mathbf{p} - \mathbf{s} \]
\[ \mathbf{b} = \mathbf{q} - \mathbf{s} \]
\[ \mathbf{c} = \mathbf{r} - \mathbf{s} \]

\[ v = \frac {1} {6} \begin{vmatrix}
 a_x & a_y & a_z \\
 b_x & b_y & b_z \\
 c_x & c_y & c_z \\
 \end{vmatrix} \]

!四面体と平行六面体の体積

 次のような図形の体積を考えます。

*$v_0$ : 辺$\mathbf{a}$、辺$\mathbf{b}$、辺$\mathbf{c}$からなる四面体の体積
*$v_1$ : 辺$\mathbf{a}$、辺$\mathbf{b}$、辺$\mathbf{c}$からなる斜三角柱の体積
*$v_2$ : 辺$\mathbf{a}$、辺$\mathbf{b}$、辺$\mathbf{c}$からなる平行六面体の体積([[スカラー三重積]]で求まる)

 すると、説明は割愛しますが、$v_2$は、$v_0$の6倍になります。$v_0$の3倍が$v_1$に等しく、$v_1$の2倍が$v_2$に等しいためです。

\[ 6v_0 = 2v_1 = v_2 \]

!用例
 四面体の体積は次のような場面で有用です。

 三角形のポリゴンメッシュによって、立体の表面を過不足なく包み込んで表された物体があるとします。
 その三角形のポリゴンメッシュが表す立体の体積を計算するのに、四面体の符号付き体積が利用できます。各三角形について、三角形の点と原点からなる四面体の符号付き体積を合計することで、ポリゴンメッシュが表す立体全体の体積が計算できます。

 この物体が一様の密度を持っているとすると、密度$\rho$と体積$v$から、物理計算に必要な質量$m$が計算できるというわけです。

\[ m = \rho v \]

これによりポリゴンメッシュで近似できる任意形状の質量を得ることができます。
!原点と点$\mathbf{a}$と$\mathbf{b}$と$\mathbf{c}$からなる四面体

\[ \mathbf{p} = \frac{\mathbf{a} + \mathbf{b} + \mathbf{c} }{4} \]

\[m = v\rho = \frac{1}{6} \left| \array{a & b & c} \right| \rho = \frac{\rho}{6} (a \times b) \cdot c \]

 $v = \frac{1}{6} \left| \array{a & b & c} \right|$は、[[四面体の体積]]です。

\[k_x = a_x + b_x + c_x\]
\[k_y = a_y + b_y + c_y\]
\[k_z = a_z + b_z + c_z\]
\[k_{xx} = a_x^2 + b_x^2 + c_x^2\]
\[k_{yy} = a_y^2 + b_y^2 + c_y^2\]
\[k_{zz} = a_z^2 + b_z^2 + c_z^2\]
\[k_{xy} = a_{x}a_{y} + b_{x}b_{y} + c_{x}c_{y}\]
\[k_{yz} = a_{y}a_{z} + b_{y}b_{z} + c_{y}c_{z}\]
\[k_{zx} = a_{z}a_{x} + b_{z}b_{x} + c_{z}c_{x}\]
\[
\mathbf{I_0} = \frac{m}{20} \left[ \array {
2 (k_y^2 + k_z^2  - k_{yy} - k_{zz}) & -k_x k_y - k_{xy} & -k_z k_x - k_{zx} & 0 \\
-k_x k_y - k_{xy} & 2 (k_z^2 + k_x^2  - k_{zz} - k_{xx}) & -k_y k_z - k_{yz} & 0 \\
-k_z k_x - k_{zx} & -k_y k_z -k_{yz} & 2 (k_x^2 + k_y^2  - k_{xx} - k_{yy}) & 0 \\
0 & 0 & 0 & 1} \right]
\]
!回転
 純粋な回転を表すデュアルクォータニオン$\mathbf{R}$は、実部が回転クォータニオンそのものになり、非実部が0です。つまり、回転クォータニオンと同じ形式の式で表すことができます。

回転クォータニオン: $ \left[\left(r_x, r_y, r_z\right), r_w\right]$

\[ \mathbf{R} = \left[\left(r_x, r_y, r_z\right), r_w\right] + \varepsilon \left[\left(0, 0, 0\right), 0\right]
 = \left[\left(r_x, r_y, r_z\right), r_w\right] \]

!平行移動
 純粋な平行移動を表すデュアルクォータニオン$\mathbf{T}$を次のように定義します。

平行移動のベクトル : $(t_x, t_y, t_z)$

\[ \mathbf{T} = \left[\left(0, 0, 0\right),1 \right] + \varepsilon \left[\left(\frac{t_x}{2}, \frac{t_y}{2}, \frac{t_z}{2}\right), 0 \right]
 = 1 + \frac{\varepsilon}{2} \left[\left(t_x, t_y, t_z\right), 0 \right] \]

!回転と平行移動
 剛体のような回転と平行移動をまとめたデュアルクォータニオン$\mathbf{Q}$は、平行移動と回転を乗算した結果になります。

\[ \mathbf{Q} = \mathbf{T} \mathbf{R}
 = \left(1 + \frac{\varepsilon}{2} \left[\left(t_x, t_y, t_z\right), 0 \right]\right) \left[\left(r_x, r_y, r_z\right), r_w\right] \]
\[= \left[\left(r_x, r_y, r_z\right), r_w\right] + \frac{\varepsilon}{2} \left[\left(t_x, t_y, t_z\right), 0 \right] \left[\left(r_x, r_y, r_z\right), r_w\right] \]
\[= \left[\left(r_x, r_y, r_z\right), r_w\right] + \varepsilon \left[\left(
\frac{t_x r_w + t_y r_z - t_z r_y}{2},
\frac{-t_x r_z + t_y r_w + t_z r_x}{2},
\frac{t_x r_y - t_y r_x + t_z r_w}{2}\right),
\frac{-t_x r_x -t_y r_y - t_z r_z}{2}\right] \]

{{{
	public partial struct DualQuaternion {
		/// <summary>
		/// 回転と位置からデュアルクォータニオン
		/// </summary>
		/// <param name="rotation"></param>
		/// <param name="position"></param>
		/// <returns></returns>
		public static DualQuaternion CreateFromRotationPosition(Quaternion rotation, Vector3 position) {
			DualQuaternion result;
			CreateFromRotationPosition(ref rotation, ref position, out result);
			return result;
		}
		/// <summary>
		/// 回転と位置からデュアルクォータニオン
		/// </summary>
		/// <param name="rotation"></param>
		/// <param name="position"></param>
		/// <param name="result"></param>
		public static void CreateFromRotationPosition(ref Quaternion rotation, ref Vector3 position,
			out DualQuaternion result) {
			result = new DualQuaternion(rotation, new Quaternion(
				0.5f * (position.X * rotation.W + position.Y * rotation.Z - position.Z * rotation.Y),
				0.5f * (-position.X * rotation.Z + position.Y * rotation.W + position.Z * rotation.X),
				0.5f * (position.X * rotation.Y - position.Y * rotation.X + position.Z * rotation.W),
				-0.5f * (position.X * rotation.X + position.Y * rotation.Y + position.Z * rotation.Z)));
		}
	}
}}}
\[ \mathbf{q} = [ (q_x, q_y, q_z), q_w ] \]
\[
\mathbf{M} =
\left[ \begin{array}{cccc}
 1 - 2q_y^2 - 2q_z^2 & 2q_xq_y + 2q_wq_z & 2q_xq_z - 2q_wq_y & 0 \\
 2q_xq_y - 2q_wq_z & 1 - 2q_x^2 - 2q_z^2 & 2q_yq_z + 2q_wq_x & 0 \\
 2q_xq_z + 2q_wq_y & 2q_yq_z - 2q_wq_x & 1 - 2q_x^2 - 2q_y^2 & 0 \\
 0 & 0 & 0 & 1 \\
\end{array} \right]
\]

{{{
		/// <summary>
		/// 回転クォータニオンから4行4列行列を作る。
		/// </summary>
		/// <param name="q"></param>
		/// <param name="m"></param>
		public static void CreateFromQuaternion(ref Quaternion q, out Matrix m) {
			m = new Matrix();
			Quaternion q0;
			Quaternion.Add(ref q, ref q, out q0);
			Quaternion q1 = new Quaternion(q0.X * q.X, q0.Y * q.Y, q0.Z * q.Z, q0.W * q.W);
			float xy = q0.X * q.Y;
			float wz = q0.W * q.Z;
			float xz = q0.X * q.Z;
			float wy = q0.W * q.Y;
			float yz = q0.Y * q.Z;
			float wx = q0.W * q.X;
			m.M11 = 1 - q1.Y - q1.Z;
			m.M12 = xy + wz;
			m.M13 = xz - wy;
			m.M21 = xy - wz;
			m.M22 = 1 - q1.X - q1.Z;
			m.M23 = yz + wx;
			m.M31 = xz + wy;
			m.M32 = yz - wx;
			m.M33 = 1 - q1.X - q1.Y;
			m.M14 = 0; m.M24 = 0; m.M34 = 0;
			m.M41 = 0; m.M42 = 0; m.M43 = 0; m.M44 = 1;
		}
	}
}}}
!座標軸周りの回転

!!X軸周りの回転

*回転角度[rad]:$\mathbf{\phi}$
*X軸周りの回転を表す行列:$\mathbf{M}$
\[\mathbf{M} = \left[ \array{
1 & 0 & 0 & 0\\
0 & \cos \phi & \sin \phi & 0\\
0 & -\sin \phi & \cos \phi & 0\\
0 & 0 & 0 & 1\\
} \right] \]

!!Y軸周りの回転

*回転角度[rad]:$\mathbf{\theta}$
*X軸周りの回転を表す行列:$\mathbf{M}$
\[\mathbf{M} = \left[ \array{
\cos \theta & 0 & -\sin \theta & 0\\
0 & 1 & 0 & 0\\
\sin \theta & 0 & \cos \theta & 0\\
0 & 0 & 0 & 1\\
} \right] \]

!!Z軸周りの回転

*回転角度[rad]:$\mathbf{\psi}$
*X軸周りの回転を表す行列:$\mathbf{M}$
\[\mathbf{M} = \left[ \array{
\cos \psi & \sin \psi & 0 & 0\\
-\sin \psi & \cos \psi & 0 & 0\\
0 & 0 & 1 & 0\\
0 & 0 & 0 & 1\\
} \right] \]

!オイラー角(Euler Angle)

 「X軸周りの回転」「Y軸周りの回転」「Z軸周りの回転」のうちから、3つの回転$(\alpha, \beta, \gamma)$をある順序で組み合わせて回転を表現する方法です。掛け合わせる順序は、回転の結果に影響します。1回目と3回目に同じ軸を選ぶ場合も含んで、組み合わせは12通りあります。これらが広義のオイラー角です。狭義のオイラー角はZ-X-Zの順を指します。

 オイラー角を使って、回転行列を作ることはできます。ただし、この方法にはジンバルロックと呼ばれる問題があります。回転をさせた結果として回転軸が重なると、次の軸でいくら回転させても思い通りの傾きを作ることができなくなります。

 これらの問題を避けるためには、[[クォータニオン]]や、[[任意軸周りの回転行列]]など、別の回転の表現が有用です。
 また、回転行列では、回転行列同士の角度を求めるときや、回転行列の結合を繰り返すことによる数値演算の誤差を補正する(正規化)ときに、計算コストがかかる弱点があります。[[クォータニオン]]でも正規化を利用しますが、計算は簡単です。

!逆回転させる

 逆の変換をするには、逆行列を使います。ただし、純粋な回転行列であれば、計算コストの高い逆行列を求めることなく、転置行列と逆行列が一致するので、転置行列で逆回転を表すことができます。

!実装

{{{
	public partial struct Matrix {
		/// <summary>
		/// X軸周りの回転
		/// </summary>
		/// <param name="radians"></param>
		/// <returns></returns>
		public static Matrix CreateRotaionX(float radians) {
			var c = (float)Math.Cos(radians);
			var s = (float)Math.Sin(radians);
			return new Matrix(
				+1, 0, 0, 0,
				 0, +c, +s, 0,
				 0, -s, +c, 0,
				 0, 0, 0, +1);
		}
	}

	public partial struct Matrix {
		/// <summary>
		/// Y軸周りの回転
		/// </summary>
		/// <param name="radians"></param>
		/// <returns></returns>
		public static Matrix CreateRotaionY(float radians) {
			var c = (float)Math.Cos(radians);
			var s = (float)Math.Sin(radians);
			return new Matrix(
				+c, 0, -s, 0,
				 0, +1, 0, 0,
				+s, 0, +c, 0,
				 0, 0, 0, +1);
		}
	}

	public partial struct Matrix {
		/// <summary>
		/// Z軸周りの回転
		/// </summary>
		/// <param name="radians"></param>
		/// <returns></returns>
		public static Matrix CreateRotaionZ(float radians) {
			var c = (float)Math.Cos(radians);
			var s = (float)Math.Sin(radians);
			return new Matrix(
				+c, +s, 0, 0,
				-s, +c, 0, 0,
				 0, 0, 1, 0,
				 0, 0, 0, +1);
		}
	}
}}}
 変換行列は、DirectXスタイルで説明します。定義の違いにより、OpenGLスタイルとは、変換行列は互いに転置行列の関係になります。また行列の結合(乗算)のときに並べる順序が逆になります。

 点$\mathbf{v}$を変換行列$\mathbf{M}$で点$\mathbf{v}'$に変換するときには、次のように計算します。

\[ \mathbf{v}' = \mathbf{v}\mathbf{M} \]

 ここで、$m_{41} =0, m_{42} =0, m_{43} =0, m_{44} =1$であるものとします。

\[ (v_x', v_y', v_z', 1)
= (v_x, v_y, v_z, 1)
\left[ \array{
m_{11} & m_{12} & m_{13} & 0\\
m_{21} & m_{22} & m_{23} & 0\\
m_{31} & m_{32} & m_{33} & 0\\
m_{41} & m_{42} & m_{43} & 1\\
} \right]
\]

\[ v_x' = v_x m_{11} + v_y m_{21} + v_z m_{31} + m_{41} \]
\[ v_y' = v_x m_{12} + v_y m_{22} + v_z m_{32} + m_{42} \]
\[ v_z' = v_x m_{13} + v_y m_{23} + v_z m_{33} + m_{43} \]

!同次座標系での座標変換
 [[同次座標系]]では、次のようになります。

\[ (v_x', v_y', v_z', v_w')
= (v_x, v_y, v_z, v_w)
\left[ \array{
m_{11} & m_{12} & m_{13} & m_{14}\\
m_{21} & m_{22} & m_{23} & m_{24}\\
m_{31} & m_{32} & m_{33} & m_{34}\\
m_{41} & m_{42} & m_{43} & m_{44}\\
} \right]
\]

\[ v_x' = v_x m_{11} + v_y m_{21} + v_z m_{31} + v_w m_{41} \]
\[ v_y' = v_x m_{12} + v_y m_{22} + v_z m_{32} + v_w m_{42} \]
\[ v_z' = v_x m_{13} + v_y m_{23} + v_z m_{33} + v_w m_{43} \]
\[ v_w' = v_x m_{13} + v_y m_{24} + v_z m_{34} + v_w m_{44} \]

 同次座標の$\mathbf{v}'$を、3Dの点にするには、$v_w'$で割ります。

\[ \mathbf{p} = \left(\frac{v_x'}{v_w'}, \frac{v_y'}{v_w'}, \frac{v_z'}{v_w'}\right) \]

----
 次のように変換行列の要素は、「スケーリングと回転」および、「平行移動」の成分に分けられます。

*${\large{R}}$: スケーリングと回転を表す。
*$\large{T}$: 平行移動を表す。

\[
\mathbf{M} =
\left[ \array{
 & & & 0\\
 & {\huge{R}} & & 0\\
 & & & 0\\
  & \large{T} & & 1\\
} \right]
\]

!実装
 以下に示す実装コードは、各ベクトル構造体に定義しているTransformCoordinateメソッドです。これは同次座標系の位置ベクトルのための座標変換です。

{{{
	public partial struct Vector2 {
		/// <summary>
		/// 変換
		/// </summary>
		/// <param name="position"></param>
		/// <param name="matrix"></param>
		/// <param name="result"></param>
		public static void Transform(ref Vector2 position, ref Matrix matrix, out Vector2 result) {
			result.X = position.X * matrix.M11 + position.Y * matrix.M31 + matrix.M41;
			result.Y = position.X * matrix.M12 + position.Y * matrix.M32 + matrix.M42;
			float w = position.X * matrix.M14 + position.Y * matrix.M34 + matrix.M44;
			Vector2.Multiply(ref result, MathHelper.Rcp(w), out result);
		}
		/// <summary>
		/// 変換
		/// </summary>
		/// <param name="position"></param>
		/// <param name="matrix"></param>
		/// <returns></returns>
		public static Vector2 Transform(Vector2 position, Matrix matrix) {
			Vector2 result;
			Transform(ref position, ref matrix, out result);
			return result;
		}
	}
}}}

{{{
	public partial struct Vector3 {
		/// <summary>
		/// 変換してW=1に投影する。
		/// </summary>
		/// <param name="position"></param>
		/// <param name="matrix"></param>
		/// <param name="result"></param>
		public static void TransformCoordinate(ref Vector3 position, ref Matrix matrix, out Vector3 result) {
			result = new Vector3();
			result.X = position.X * matrix.M11 + position.Y * matrix.M21 + position.Z * matrix.M31 + matrix.M41;
			result.Y = position.X * matrix.M12 + position.Y * matrix.M22 + position.Z * matrix.M32 + matrix.M42;
			result.Z = position.X * matrix.M13 + position.Y * matrix.M23 + position.Z * matrix.M33 + matrix.M43;
			float w = position.X * matrix.M14 + position.Y * matrix.M24 + position.Z * matrix.M34 + matrix.M44;
			Vector3.Multiply(ref result, MathHelper.Rcp(w), out result);
		}
		/// <summary>
		/// 変換してW=1に投影する。
		/// </summary>
		/// <param name="position"></param>
		/// <param name="matrix"></param>
		/// <returns></returns>
		public static Vector3 TransformCoordinate(Vector3 position, Matrix matrix) {
			Vector3 result;
			TransformCoordinate(ref position, ref matrix, out result);
			return result;
		}
	}
}}}

{{{
	public partial struct Vector4 {
		/// <summary>
		/// 変換
		/// </summary>
		/// <param name="position"></param>
		/// <param name="matrix"></param>
		/// <param name="result"></param>
		public static void Transform(ref Vector4 position, ref Matrix matrix, out Vector4 result) {
			result.X = position.X * matrix.M11 + position.Y * matrix.M21 + position.Z * matrix.M31 + matrix.M41;
			result.Y = position.X * matrix.M12 + position.Y * matrix.M22 + position.Z * matrix.M32 + matrix.M42;
			result.Z = position.X * matrix.M13 + position.Y * matrix.M23 + position.Z * matrix.M33 + matrix.M43;
			result.W = position.X * matrix.M14 + position.Y * matrix.M24 + position.Z * matrix.M34 + matrix.M44;
		}
		/// <summary>
		/// 変換
		/// </summary>
		/// <param name="position"></param>
		/// <param name="matrix"></param>
		/// <returns></returns>
		public static Vector4 Transform(Vector4 position, Matrix matrix) {
			Vector4 result;
			Transform(ref position, ref matrix, out result);
			return result;
		}
	}
}}}
 表面を三角形メッシュで過不足なく表される多面体の場合には、四面体の集合として計算できます。

 各三角形の点と原点を四面体として計算します。
 なお、原点の位置は重要ではなく、凹形多面体でも問題ありません。4点の位置関係によって、体積の符号が変わります。計算上で重複する部分が符号によって相殺されて、正しく全体の体積を求めることができます。体積に依存する重心、質量、慣性テンソルも同様です。

 n個の部分からなる剛体の質量特性は、質量と慣性テンソルを総和、重心を加重平均で求めることができます。

\[ m = \sum_{i=0}^{n-1} { m_i } \]
\[ \mathbf{p} = \frac{\sum_{i=0}^{n-1} { m_i \mathbf{p}_i }}{\sum_{i=0}^{n-1} { m_i }}
= \frac{\sum_{i=0}^{n-1} { m_i \mathbf{p}_i }}{m} \]
\[ \mathbf{I}_0 = \sum_{i=0}^{n-1} { \mathbf{I}_{0i} } \]

 なお、質量特性を平行移動させるには、[[慣性テンソルの平行移動]]を使います。
*平行移動量:$\mathbf{v} = (v_x, v_y, v_z)$
*平行移動を表す行列:$\mathbf{M}$
\[
\mathbf{M} =
\left[ \array{
1 & 0 & 0 & 0\\
0 & 1 & 0 & 0\\
0 & 0 & 1 & 0\\
v_x & v_y & v_z & 1\\
} \right]
\]

※この行列はDirectXスタイルの行列です。

{{{
	public partial struct Matrix {
		/// <summary>
		/// 並行移動の行列を作る。
		/// </summary>
		/// <param name="x">X</param>
		/// <param name="y">Y</param>
		/// <param name="z">Z</param>
		/// <param name="result"></param>
		public static void CreateTranslation(float x, float y, float z, out Matrix result) {
			result.M11 = 1; result.M12 = 0; result.M13 = 0; result.M14 = 0;
			result.M21 = 0; result.M22 = 1; result.M23 = 0; result.M24 = 0;
			result.M31 = 0; result.M32 = 0; result.M33 = 1; result.M34 = 0;
			result.M41 = x; result.M42 = y; result.M43 = z; result.M44 = 1;
		}
		/// <summary>
		/// 並行移動の行列を作る。
		/// </summary>
		/// <param name="translation">並行移動</param>
		/// <param name="result"></param>
		public static void CreateTranslation(ref Vector3 translation, out Matrix result) {
			CreateTranslation(translation.X, translation.Y, translation.Z, out result);
		}
	}
}}}
!!3Dの場合

平面の方程式: $ ax+by+cz+d=0 $

 ここでは、この平面を$ \mathbf{n} = \left[ \mathbf{n}_v:n_d \right] = \left[ a : b : c : d \right] $と表記します。
 $\mathbf{n}_v$は平面の法線です。

!!3点$\mathbf{p}, \mathbf{q}, \mathbf{r}$を通る平面$\mathbf{n}$

 3点$\mathbf{p}, \mathbf{q}, \mathbf{r}$を通る平面$\mathbf{n}$は次のように計算できます。

\[ \mathbf{p} = (p_x, p_y, p_z) \]
\[ \mathbf{q} = (q_x, q_y, q_z) \]
\[ \mathbf{r} = (r_x, r_y, r_z) \]

\[ \mathbf{n} = \left[ a : b : c : d \right]
 = \left[ \left| \array{
 1 & p_y & p_z \\
 1 & q_y & q_z \\
 1 & r_y & r_z \\
 } \right| : \left| \array{
 p_x & 1 & p_z \\
 q_x & 1 & q_z \\
 r_x & 1 & r_z \\
 } \right| : \left| \array{
 p_x & p_y & 1 \\
 q_x & q_y & 1 \\
 r_x & r_y & 1 \\
 } \right| : - \left| \array{
 p_x & p_y & p_z \\
 q_x & q_y & q_z \\
 r_x & r_y & r_z \\
 } \right| \right]
 \]

\[ a = p_y(q_z - r_z) + q_y(r_z - p_z) + r_y(p_z - q_z) \]
\[ b = p_z(q_x - r_x) + q_z(r_x - p_x) + r_z(p_x - q_x) \]
\[ c = p_x(q_y - r_y) + q_x(r_y - p_y) + r_x(p_y - q_y) \]
\[ d = - p_x (q_y r_z - r_y q_z) - q_x (r_y p_z - p_y r_z) - r_x (p_y q_z - q_y p_z) \]
 慣性テンソル$\mathbf{I}$は、物体の回転運動の変化のしにくさを表します。質量の分布が均一でない物体は姿勢によってその値が変化します。次の式のように、初期姿勢の慣性テンソル$\mathbf{I_0}$をあらかじめ計算しておき、現在の姿勢$\mathbf{R}$で使って、現在の姿勢における慣性テンソル$\mathbf{I}$を求めることができます。

$\mathbf{I}$: 現在の姿勢の慣性テンソル
$\mathbf{R}$: 現在の姿勢(回転行列)
$\mathbf{I_0}$: 初期姿勢の慣性テンソル
\[ \mathbf{I}
 = \mathbf{R} \mathbf{I_0} \mathbf{R}^{-1}
 = \mathbf{R} \mathbf{I_0} \mathbf{R^{\mathrm{T}}} \]

 なお、純粋な回転行列を用いる限り、その逆行列と転置は等価です。
\[ \mathbf{R}^{-1} = \mathbf{R^{\mathrm{T}}} \]

 また、物理計算で使う$\mathbf{I}^{-1}$の計算は次のようになります。

\[ \mathbf{I}^{-1}
 = (\mathbf{R} \mathbf{I_0} \mathbf{R}^{-1})^{-1}
 = \mathbf{R} \mathbf{I_0}^{-1} \mathbf{R}^{-1}
 = \mathbf{R} \mathbf{I_0}^{-1} \mathbf{R^{\mathrm{T}}} \]

!初期姿勢の慣性テンソル
 初期姿勢の慣性テンソル$I_0$は計算で求めることができます。球や直方体などの基本的な形状をした均質な物体は次のように計算ができます。また、三角形メッシュで表される多面体は、四面体の集合として計算することができます。
 なお、3Dの回転行列や慣性テンソルは3行3列の行列で表現できますが、ここでは[[Matrix構造体]]で扱うことを考慮して、4行4列で表記します。

 関連項目では、次の記号を使います。

$\mathbf{p}$ : 重心
$m = v\rho$ : 質量
$v$ : 体積
$\rho$ : 密度
$\mathbf{I}_0$ : 慣性テンソル

*[[球の慣性テンソル]]
*[[楕円体の慣性テンソル]]
*[[直方体の慣性テンソル]]
*[[四面体の慣性テンソル]]
*[[多面体の慣性テンソル]]
 質量$m$の剛体の慣性テンソル$\mathbf{I}$の軸を$\mathbf{t}$だけ平行移動させた慣性テンソル$\dot{\mathbf{I}}_0$は次のように計算します。
\[
\dot{\mathbf{I}}_0 = \mathbf{I}_0
+ m \left[
\array {
-t_y^2 - t_z^2 & t_x t_y & t_x t_z & 0 \\
t_y t_x & -t_z^2 - t_x^2 & t_y t_z & 0 \\
t_z t_x & t_z t_y & -t_x^2 - t_y^2 & 0 \\
0 & 0 & 0 & 0
} \right]
\]

{{{
		/// <summary>
		/// 慣性テンソルの平行移動
		/// </summary>
		/// <param name="inertiaTensor"></param>
		/// <param name="translation"></param>
		/// <param name="mass"></param>
		/// <param name="result"></param>
		public static void TranslateInertiaTensor(
			ref Matrix inertiaTensor, ref Vector3 translation, float mass, out Matrix result) {
			Vector3 k;
			Vector3.Multiply(ref translation, ref translation, out k);
			Vector3.Multiply(ref k, mass, out k);
			var xy = translation.X * translation.Y * mass;
			var yz = translation.Y * translation.Z * mass;
			var zx = translation.Z * translation.X * mass;
			result = new Matrix(
				inertiaTensor.M11 - (k.Y + k.Z), inertiaTensor.M12 + xy, inertiaTensor.M13 + zx, inertiaTensor.M14,
				inertiaTensor.M21 + xy, inertiaTensor.M22 - (k.Z + k.X), inertiaTensor.M23 + yz, inertiaTensor.M42,
				inertiaTensor.M31 + zx, inertiaTensor.M32 + yz, inertiaTensor.M33 - (k.X + k.Y), inertiaTensor.M43,
				inertiaTensor.M41, inertiaTensor.M42, inertiaTensor.M43, inertiaTensor.M44);
		}
}}}
\[ f(d, z) = \lim_{\alpha \rightarrow \infty} e^{-\alpha (d - z)} \]
\[ f(d, z) = e^{-c(d - z)} = e^{-cd} e^{cz} \]
 ここでは、2Dと3Dのゲームグラフィックスとゲーム物理で必要となる基礎的な数学を、その実装のクラスライブラリと照らし合わせて説明します。「数学を道具として使う」ために、実践で必要な部分を中心にしています。
 クラスライブラリはC#で実装し、C#を用いて説明しています。C#のコードではありますが、Java、C++のプログラマーでも、どのような計算がなされるのか十分に理解することができると思います。なお、すべての部分が実装を説明しているわけではなく、関連する話題にも触れています。

 このクラスライブラリでは、数学の[[ベクトル]]、[[行列]]、[[クォータニオン]]、[[デュアルクォータニオン]]に対応する実践上の実装があります。対応する機能があるものについては、XNA Frameworkの数学関連のAPIに近づけてあります。そのため、[[XNA Game Studio 3.0]]の数学関連クラスを使うときの参考にもなります。ただし、互換性を保障するものではありません。
 また、XNAおよびHLSLなどとの親和性を考慮して、ベクトルやクォータニオンの要素を表すフィールドの名前と順序には、X, Y, Z, Wを用います。

※このページは修正または更新することがあります。
※この内容は書籍「リアルな動きのための 物理ゲームプログラミング」にも含まれます。ただし、内容は差異があり、ウェブにのみあるものと、書籍のみの部分(用例)があります。
※現在のところライブラリは、書籍のサンプルコードとしての公開版があります。

!クラスライブラリの構成
|!アセンブリ|!概要|
|XELF.Framework.dll|スタンドアロン版 (.NET Framework用)|
|XELF.Xna.dll|XNA Game Studio 3.1併用版 (XNA Game Studio 3.1用)|
 [[Farseer Physics Engine 2.0]]では、XNAの定義に基づいた数学用の型が用いられます。XNA用ビルドのライブラリでは、XNAで定義されているMicrosoft.XNA.Framework名前空間の型がそのまま用いられます。
 それ以外のプラットフォームでは、FarseerGames.FarseerPhysics.Mathematics名前空間で定義されて、これらが代用されます。これらの実装は必要最低限のサブセットです。

 以上の条件に当てはまる型は、次のものです。

|!型|
|[[Vector2構造体]]|
|[[Vector3構造体]]|
|[[Matrix構造体]]|
|[[MathHelperクラス]]|

 [[Farseer Physics Engine 2.0]]ライブラリのプラットフォームを切り替えてビルドするには、次のようにします。

|!対象プラットフォーム|!条件付きコンパイルシンボル|
|XNA Game Studio 2.0以降|条件付きコンパイルシンボルに「XNA」を定義する|
|その他のプラットフォーム。Siliverlightなど|上記を定義しない|
制御点: $\mathbf{c}_i$
曲線や曲面上の点: $\mathbf{p}$
パラメータ: $u, v$

!!n次Bézier曲線
\[ \mathbf{p}(u) = \sum_{i=0}^{n} B_i^n(u) {\mathbf{c}_i} \]

!!Bernstein基底関数
\[ B_i^n(u) = \left(\array{n\\i}\right) u^i (1 - u)^{n - i} \]

!!二項係数(Binomial Coefficient)
\[ \left(\array{n\\i}\right) = {}_n \mathrm{C} _i = \frac{n!}{i!(n-i)!} \]

!!Bézier曲面(パッチ)

\[ \mathbf{p}(u, v) = \sum_{i=0}^{n} \sum_{j=0}^{m} B_i^n(u) B_j^m(v) {\mathbf{c}_{ij}} \]

!!3次の場合

\[ B_0^3(u) = 1u^0(1 - u)^3 = -u^3+3u^2-3u+1 \]
\[B_1^3(u) = 3u(1-u)^2 = 3u^3-6u^2+3u \]
\[B_2^3(u) = 3u^2(1-u) = -3u^3+3u^2 \]
\[B_3^3(u) = 1u^3(1-u)^0 = u^3 \]

!!!双3次Bézier曲面(行列形式)

\[ \mathbf{p}(u,v) = \mathbf{U} \mathbf{B} \mathbf{C} \mathbf{B}^\mathrm{T} \mathbf{V}^\mathrm{T} \]
\[ \mathbf{C} = \left[ \matrix {
\mathbf{c}_{11} & \mathbf{c}_{21} & \mathbf{c}_{31} & \mathbf{c}_{41}\\
\mathbf{c}_{12} & \mathbf{c}_{22} & \mathbf{c}_{31} & \mathbf{c}_{42}\\
\mathbf{c}_{13} & \mathbf{c}_{23} & \mathbf{c}_{33} & \mathbf{c}_{43}\\
\mathbf{c}_{14} & \mathbf{c}_{24} & \mathbf{c}_{34} & \mathbf{c}_{44}
} \right] \]
\[ \mathbf{U} = (u^3, u^2, u, 1) \]
\[ \mathbf{V} = (v^3, v^2, v, 1) \]
\[ \mathbf{B} = \mathbf{B}^\mathrm{T} = \left[ \matrix {
-1&3&-3&1\\
3&-6&3&0\\
-3&3&0&0\\
1&0&0&0
} \right] \]

!!!接線
\[ \frac{\partial \mathbf{p}}{\partial u}(u,v) = \mathbf{U}' \mathbf{B} \mathbf{C} \mathbf{B}^\mathrm{T} \mathbf{V}^\mathrm{T} \]
\[ \frac{\partial \mathbf{p}}{\partial v}(u,v) = \mathbf{U} \mathbf{B} \mathbf{C} \mathbf{B}^\mathrm{T} \mathbf{V}'^\mathrm{T} \]
\[ \mathbf{U}' = (3u^2, 2u, 1, 0) \]
\[ \mathbf{V}' = (3v^2, 2v, 1, 0) \]

!!!法線
\[ \mathbf{n}(u,v) = \frac{ \frac{\partial \mathbf{p}}{\partial u}(u,v) \times \frac{\partial \mathbf{p}}{\partial v}(u,v) }{
\left| \frac{\partial \mathbf{p}}{\partial u}(u,v) \times \frac{\partial \mathbf{p}}{\partial v}(u,v) \right| } \]
!各軸方向の半径が$x, y, z$の楕円体

\[\mathbf{p} = \mathbf{0}\]

\[m = \frac{4\pi}{3} xyz\rho\]

\[
\mathbf{I_0} = \frac{m}{5} \left[ \array {
y^2+z^2 & 0 & 0 & 0 \\
0 & z^2+x^2 & 0 & 0 \\
0 & 0 & x^2+y^2 & 0 \\
0 & 0 & 0 & 1} \right]
\]
$\mathbf{n}$: 正規化された面法線ベクトル
$\mathbf{e}$: 正規化された入射方向ベクトル
$\mathbf{r}$: 正反射方向ベクトル

\[ \mathbf{r} = 2 (\mathbf{e} \cdot \mathbf{n}) \mathbf{n} - \mathbf{e} \]
!法線(Normal)

 法線ベクトル(Normal Vector)とは、2Dではある線に垂直なベクトルです。3Dではある面に垂直なベクトルです。表裏を区別するならば、特に面の表の向きを表すために使われます。たとえば、3Dグラフィックスでは、「明かりで照らされた物体の表面の各点がどのような色になるか」を計算するときに法線が用いられます。

*3Dの場合
 点$\mathbf{a}$、点$\mathbf{b}$、点$\mathbf{c}$からなる三角形の法線$\mathbf{n}$は、次のように[[外積|ベクトルの外積]]を使って計算できます。法線は大きさを1にしておくと都合がよい場合が多いため、正規化を含めて、単位法線ベクトルにしています。
\[ \mathbf{e}_{ac} = \mathbf{a} - \mathbf{c} \]
\[ \mathbf{e}_{bc} = \mathbf{b} - \mathbf{c} \]

\[ \mathbf{n} = \frac { \mathbf{e}_{ac} \times \mathbf{e}_{bc} } { \left| \mathbf{e}_{ac} \times \mathbf{e}_{bc} \right| } \]

[img[Normal.png]]
直線の方向: $ \mathbf{d} = (d_x, d_y, d_z) $
直線上の点: $ \mathbf{p} = (p_x, p_y, p_z) $

プリュッカー直線: $ \mathbf{L} = \{ \mathbf{d} : \mathbf{d} \times \mathbf{p} \} $
!!3D同次座標の点と点の場合
点1: $ \mathbf{a} = (\mathbf{a}_v : a_w) $
点2: $ \mathbf{b} = (\mathbf{b}_v : b_w) $

プリュッカー直線:
\[ \mathbf{L} = \{ b_w \mathbf{a}_v - a_w \mathbf{b}_v : \mathbf{a}_v \times \mathbf{b}_v \} \]

 直線は、$\mathbf{b}$から$\mathbf{a}$の方向を持ちます。

!!3Dの点と点の場合

 特に同次座標ではない3Dの点の場合には、W成分が$1$として計算できるので、次のようになります。

点1: $ \mathbf{a} $
点2: $ \mathbf{b} $

プリュッカー直線:
\[ \mathbf{L} = \{ \mathbf{a} - \mathbf{b} : \mathbf{a} \times \mathbf{b} \} \]

 直線は、$\mathbf{b}$から$\mathbf{a}$の方向を持ちます。
!半径が$r$の球

\[\mathbf{p} = \mathbf{0}\]

\[m = \frac{4\pi}{3} r^3\rho\]

\[
\mathbf{I_0} = \frac{2m}{5} \left[ \array {
r^2 & 0 & 0 & 0 \\
0 & r^2 & 0 & 0 \\
0 & 0 & r^2 & 0 \\
0 & 0 & 0 & 1} \right]
\]
!球面線形補間(Slerp: Spherical Linear intERPolation)

 球面線形補間(Spherical Linear intERPolation)は、略して「Slerp」と呼ばれています。
 球面線形補間は、2つの回転クォータニオン$\mathbf{a}$と$\mathbf{b}$の間を角度について、パラメータ$t$で線形補間した回転クォータニオンを計算します。球面線形補間を使えることがクォータニオンの利点の1つです。

 $\mathbf{a}$と$\mathbf{b}$のなす角度を$\theta$とします。

\[ \mathrm{Slerp}(\mathbf{a}, \mathbf{b}; t) = \frac {\sin (1 - t) \theta} {\sin \theta} \mathbf{a} + \frac {\sin t\theta} {\sin \theta} \mathbf{b} \]
 ただし、$\theta \simeq 0$のときには、次の式を使います。
\[ \mathrm{Slerp}(\mathbf{a}, \mathbf{b}; t) = \sin (1 - t) \theta \mathbf{a} + \sin t \theta \mathbf{b} \]

 2つめの式は、$\theta = 0$のときに、$\sin \theta = 0$となり、式の分数が計算できなくなるのを避けるために使います。また、線形補間を代用することもあります。

!性質
*パラメータ$t$を連続的に変化させたときに、その補完結果の回転が最小の変化をします。球面上の最短経路を通ります。
*パラメータ$t$を一定速度で変化させたときに、回転速度が一定です。

!実装

 実装では、若干異なるコードになっています。
[[ベクトル]]
[[行列]]
[[クォータニオン]]
[[デュアルクォータニオン]]
[[補間]]
!軸に平行な各辺が$x, y, z$の直方体

\[\mathbf{p} = \mathbf{0}\]

\[m = xyz\rho\]

\[
\mathbf{I_0} = \frac{m}{12} \left[ \array {
y^2+z^2 & 0 & 0 & 0 \\
0 & z^2+x^2 & 0 & 0 \\
0 & 0 & x^2+y^2 & 0 \\
0 & 0 & 0 & 1} \right]
\]
$x$: 実数

\[ \mathrm{sgn} x = \begin{cases}
1 & x>0 \\
0 & x=0 \\
-1 & x<0
\end{cases} \]

\[ \mathrm{sgn} x = \left[ x > 0 \right] - \left[ x < 0 \right] \]

※$\left[ \right]$は、アイバーソンの角括弧です。
 ご意見・ご感想などは、ブログのコメントもしくはメールをご利用ください。

----

※以下の項目は管理・編集者用のメニューです。

;SiteTitle & SiteSubtitle: 
:このサイトのタイトルおよびサブタイトル。この上に表示されています。<br>保存後はブラウザのタイトルバーにも表示されます。
;MainMenu: 
:メニュー。たいていは左側に表示されています。
;DefaultTiddlers: 
:ここにtiddlerの名前が書かれていると、この TiddlyWiki を開いたときに、<br>そのtiddlerが初期表示されます。
あなたの名前(編集したtiddlerに表示されます): <<option txtUserName>>
$x$: 実数

\[ | x | = \begin{cases}
x & x \ge 0 \\
-x & x<0 \\
\end{cases} \]
!線形補間(Lerp: Linear IntERPolation)
\[ \mathrm{Lerp}(\mathbf{a}, \mathbf{b}; t) = \mathbf{p}(t) = \left[ \matrix { t & 1 } \right] \left[ \matrix {
-1&1\\
1&0\\
} \right] \left[ \matrix {
\mathbf{a}\\
\mathbf{b}\\
} \right]
= (1-t) \mathbf{a} + t \mathbf{b}
= \mathbf{a} + t (\mathbf{b} - \mathbf{a}) \]
!理論

 行列(《単》Matrix、《複》Matrices)とは、縦横に並べた数値の組です。縦を行、横を列といいます。
 ここでは、すべての成分が実数である実行列だけを扱います。また、後述する理由により、基本的に4行4列を用います。

\[
\mathbf{M} =
\left[ \array{
m_{11} & m_{12} & m_{13} & m_{14}\\
m_{21} & m_{22} & m_{23} & m_{24}\\
m_{31} & m_{32} & m_{33} & m_{34}\\
m_{41} & m_{42} & m_{43} & m_{44}\\
} \right]
\]

 4行4列の行列は、3Dグラフィックスにおいて、位置ベクトルや法線ベクトルの座標変換のための[[変換行列]]として用います。
 スケーリングを表す[[スケーリング行列]]、回転を表す[[回転行列]]、平行移動を表す[[平行移動行列]]などの変換行列を作って変換することができます。また、行列同士の乗算によって、複数の行列の結合をすることで、これら複数の操作をひとつの行列で表すことができます。

!実装

 実装は4行4列の行列を表す[[Matrix構造体]]です。

 数学的にはn行m列の行列というように、任意の行や列の数を持つ行列が考えられます。しかし、3Dグラフィックスの多くの場面や、物理計算の一部の場面では、4行4列の行列があれば間に合います。計算の内容によって、3行4列、4行3列、3行3列などで十分な場合もありますが、このような場合には不要な成分には適切に0や1を入れて同様の計算ができます。4行4列の行列を単に行列と呼んで[[Matrix構造体]]を実装します。

!!可変サイズの行列でない理由
 もし、行や列の大きさに合わせて別の型を作ると、たくさんの型が必要になり扱いづらくなります。また、可変サイズの行列にするとパフォーマンス上で明らかに不利になります。

*行や列に対して繰り返しのforループが必要です。制御文のないコードは並列実行できる命令になる可能性がありますが、制御文のあるコードはそのような命令にならない可能性があり、制御文での分岐もまた処理がストールする原因になりがちです。
*2つの行列同士が計算可能な行と列を持っているか、計算の前に判定が必要になります。条件分岐は処理がストールする原因になりがちです。

{{{
	/// <summary>
	/// 4行4列の行列
	/// </summary>
	public partial struct Matrix {
		/// <summary>M11</summary>
		public float M11;
		/// <summary>M12</summary>
		public float M12;
		/// <summary>M13</summary>
		public float M13;
		/// <summary>M14</summary>
		public float M14;
		/// <summary>M21</summary>
		public float M21;
		/// <summary>M22</summary>
		public float M22;
		/// <summary>M23</summary>
		public float M23;
		/// <summary>M24</summary>
		public float M24;
		/// <summary>M31</summary>
		public float M31;
		/// <summary>M32</summary>
		public float M32;
		/// <summary>M33</summary>
		public float M33;
		/// <summary>M34</summary>
		public float M34;
		/// <summary>M41</summary>
		public float M41;
		/// <summary>M42</summary>
		public float M42;
		/// <summary>M43</summary>
		public float M43;
		/// <summary>M44</summary>
		public float M44;
	}
}}}
 $l$行$m$列の行列$\mathbf{A}$の各成分を$a_{ij}$、$m$行$n$列の行列$\mathbf{B}$の各成分を$b_{ij}$、$l$行$n$列の行列$\mathbf{C}$の各成分を$c_{ij}$すると、行列の積は次のように表されます。

\[ \mathbf{C} = \mathbf{A}\mathbf{B} \]

 このとき、$\mathbf{C}$の各成分$(i, j)$は次のように計算されます。

\[ c_{ij} = \sum_{k=1}^{m}{a_{ik} b_{kj}} \]

 一般に$\mathbf{A}\mathbf{B} \neq \mathbf{B}\mathbf{A}$です。
 行列の積は、2つの行列を1つに結合します。複数の変換行列を1つにまとめるために使うことができます。

!実装

 実装は4行4列の行列の積です。

{{{
	public partial struct Matrix {
		/// <summary>
		/// 乗算
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <param name="result"></param>
		public static void Multiply(ref Matrix a, ref Matrix b, out Matrix result) {
			Matrix m = new Matrix();
			m.M11 = a.M11 * b.M11 + a.M12 * b.M21 + a.M13 * b.M31 + a.M14 * b.M41;
			m.M12 = a.M11 * b.M12 + a.M12 * b.M22 + a.M13 * b.M32 + a.M14 * b.M42;
			m.M13 = a.M11 * b.M13 + a.M12 * b.M23 + a.M13 * b.M33 + a.M14 * b.M43;
			m.M14 = a.M11 * b.M14 + a.M12 * b.M24 + a.M13 * b.M34 + a.M14 * b.M44;
			m.M21 = a.M21 * b.M11 + a.M22 * b.M21 + a.M23 * b.M31 + a.M24 * b.M41;
			m.M22 = a.M21 * b.M12 + a.M22 * b.M22 + a.M23 * b.M32 + a.M24 * b.M42;
			m.M23 = a.M21 * b.M13 + a.M22 * b.M23 + a.M23 * b.M33 + a.M24 * b.M43;
			m.M24 = a.M21 * b.M14 + a.M22 * b.M24 + a.M23 * b.M34 + a.M24 * b.M44;
			m.M31 = a.M31 * b.M11 + a.M32 * b.M21 + a.M33 * b.M31 + a.M34 * b.M41;
			m.M32 = a.M31 * b.M12 + a.M32 * b.M22 + a.M33 * b.M32 + a.M34 * b.M42;
			m.M33 = a.M31 * b.M13 + a.M32 * b.M23 + a.M33 * b.M33 + a.M34 * b.M43;
			m.M34 = a.M31 * b.M14 + a.M32 * b.M24 + a.M33 * b.M34 + a.M34 * b.M44;
			m.M41 = a.M41 * b.M11 + a.M42 * b.M21 + a.M43 * b.M31 + a.M44 * b.M41;
			m.M42 = a.M41 * b.M12 + a.M42 * b.M22 + a.M43 * b.M32 + a.M44 * b.M42;
			m.M43 = a.M41 * b.M13 + a.M42 * b.M23 + a.M43 * b.M33 + a.M44 * b.M43;
			m.M44 = a.M41 * b.M14 + a.M42 * b.M24 + a.M43 * b.M34 + a.M44 * b.M44;
			result = m;
		}
		/// <summary>
		/// 乗算
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		public static Matrix Multiply(Matrix a, Matrix b) {
			Matrix result;
			Multiply(ref a, ref b, out result);
			return result;
		}
		/// <summary>
		/// 乗算
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <returns></returns>
		public static Matrix operator *(Matrix a, Matrix b) {
			Matrix result;
			Multiply(ref a, ref b, out result);
			return result;
		}
	}
}}}
!補間(Interpolation)
[[線形補間]]
!!曲線(Curve)
[[Bézier曲線]]
[[B-Spline曲線]]
[[Catmull-Rom曲線]]
[[Ferguson曲線]]
 転置行列(Transposed Matrix)は、要素$m_{ij}$を$m_{ji}$に入れ替えてできる行列です。ここで記号$\mathrm{T}$は、転置を表しています。

!例
\[
\mathbf{M} =
\left[ \array{
a & b & c & d \\
e & f & g & h \\
i & j & k & l \\
m & n & o & p \\
} \right]
\]

\[
\mathbf{M}^\mathrm{T} =
\left[ \array{
a & e & i & m \\
b & f & j & n \\
c & g & k & o \\
d & h & l & p \\
} \right]
\]

{{{
	public partial struct Matrix {
		/// <summary>
		/// 転置
		/// </summary>
		public static void Transpose(ref Matrix a, out Matrix result) {
			result = new Matrix();
			result.M11 = a.M11; result.M12 = a.M21; result.M13 = a.M31; result.M14 = a.M41;
			result.M21 = a.M12; result.M22 = a.M22; result.M23 = a.M32; result.M24 = a.M42;
			result.M31 = a.M13; result.M32 = a.M23; result.M33 = a.M33; result.M34 = a.M43;
			result.M41 = a.M14; result.M42 = a.M24; result.M43 = a.M34; result.M44 = a.M44;
		}
		/// <summary>
		/// 転置
		/// </summary>
		public static Matrix Transpose(Matrix a) {
			var result = new Matrix();
			result.M11 = a.M11; result.M12 = a.M21; result.M13 = a.M31; result.M14 = a.M41;
			result.M21 = a.M12; result.M22 = a.M22; result.M23 = a.M32; result.M24 = a.M42;
			result.M31 = a.M13; result.M32 = a.M23; result.M33 = a.M33; result.M34 = a.M43;
			result.M41 = a.M14; result.M42 = a.M24; result.M43 = a.M34; result.M44 = a.M44;
			return result;
		}
	}
}}}
*回転軸(単位ベクトル): $\mathbf{v} = (v_x, v_y, v_z) $
*回転軸周りの角度: $\theta$

\[ \mathbf{q} = \left[ \mathbf{v} \sin \frac \theta 2, \cos \frac \theta 2 \right] \]
\[ = \left[\left(
 v_x \sin {\frac \theta 2},
 v_y \sin {\frac \theta 2},
 v_z \sin {\frac \theta 2}\right),
 \cos \frac \theta 2 \right] \]

[img[QuaternionFromAxisAngle.png]]

!回転軸$\mathbf{v}$が単位ベクトルでない場合

 正規化を含めると次のようになります。

\[ \mathbf{q} = \left[ \frac { \mathbf{v}} { \left| \mathbf{v} \right|} \sin {\frac \theta 2}, \cos \frac \theta 2 \right] \]
\[ = \left[\left(
 \frac {v_x} {\sqrt{ v_x^2+v_y^2+v_z^2 }} \sin {\frac \theta 2},
 \frac {v_y} {\sqrt{ v_x^2+v_y^2+v_z^2 }} \sin {\frac \theta 2},
 \frac {v_z} {\sqrt{ v_x^2+v_y^2+v_z^2 }} \sin {\frac \theta 2}\right),
 \cos \frac \theta 2 \right] \]

!実装

{{{
		/// <summary>
		/// 軸周りの回転からクォータニオンを作る。
		/// </summary>
		/// <param name="axis">軸</param>
		/// <param name="angle">角度[Radian]</param>
		/// <returns></returns>
		public static Quaternion CreateFromAxisAngle(Vector3 axis, float angle) {
			axis.Normalize();
			angle *= 0.5f;
			var s = (float)System.Math.Sin(angle);
			var c = (float)System.Math.Cos(angle);
			Vector3.Multiply(ref axis, s, out axis);
			return new Quaternion(axis, c);
		}
}}}
 逆クォータニオン$\mathbf{q}^{-1}$とは、元のクォータニオンとの積が[[乗法の単位元クォータニオン]]となるクォータニオンをいいます。
\[ \mathbf{q}{\mathbf{q}^{-1}} = \mathbf{1} \]

 逆クォータニオンは、次のように計算できます。

\[ \mathbf{q}^{-1}
= \frac{\overline{\mathbf{q}}}{{\left| \mathbf{q} \right|}^2}
= \frac{\overline{\mathbf{q}}}{{\left|\left| \mathbf{q} \right|\right|}}
= \frac{\left[ \left(-q_x, -q_y, -q_z\right), q_w \right]}{q_x^2+q_y^2+q_z^2+q_w^2} \]

 大きさが$0$のクォータニオンでは、分母が$0$になることから、逆クォータニオンは存在しません。
 また、単位クォータニオンであれば、分母は$1$になるため、[[共役クォータニオン]]と同じ値になります。

{{{
	public partial struct Quaternion {
		/// <summary>
		/// 逆クォータニオン
		/// </summary>
		public static void Inverse(ref Quaternion value, out Quaternion result) {
			var r = MathHelper.Rcp(value.LengthSquared());
			result = new Quaternion(-value.X * r, -value.Y * r, -value.Z * r, value.W * r);
		}
		/// <summary>
		/// 逆クォータニオン
		/// </summary>
		public static Quaternion Inverse(Quaternion value) {
			Quaternion result;
			Inverse(ref value, out result);
			return result;
		}
	}
}}}
\[ \mathbf{Q}^{-1}
 = \frac{\bar{\mathbf{Q}}}{\left|\left| \mathbf{Q} \right|\right|}
 = \frac{\bar{\mathbf{Q}}}{\mathbf{Q} \bar{\mathbf{Q}}}
 = \frac{\bar{\mathbf{Q}}}{\bar{\mathbf{Q}} \mathbf{Q}} \]
\[ = \frac{ \bar{\mathbf{r}} }{ \left|\left| \mathbf{r} \right|\right| }
 + \varepsilon \left(
 \frac{ \bar{\mathbf{d}} }{ \left|\left| \mathbf{r} \right|\right| }
 - 2 \frac{ \mathbf{r} \cdot \mathbf{d} }{ \left|\left| \mathbf{r} \right|\right| } \frac{ \bar{\mathbf{r}} }{ \left|\left| \mathbf{r} \right|\right| }
 \right) \]
\[ = \frac{ \left[(-r_x, -r_y, -r_z), r_w \right] }{r_x^2+r_y^2+r_z^2+r_w^2}
 + \varepsilon \left(
 \frac{ \left[(-d_x, -d_y, -d_z), d_w \right] }{r_x^2+r_y^2+r_z^2+r_w^2}
 - 2 \frac{ r_x d_x + r_y d_y + r_z d_z + r_w d_w }{ { \left( r_x^2+r_y^2+r_z^2+r_w^2 \right) }^2} \left[(-r_x, -r_y, -r_z), r_w \right]
 \right) \]

 逆デュアルクォータニオンは、$\mathbf{r} = \left[(0, 0, 0), 0 \right]$のときには定義されません。それ以外のとき、次の式を満たします。

\[ \mathbf{Q}\mathbf{Q}^{-1} = \mathbf{Q}^{-1}\mathbf{Q} = 1 = \left[(0, 0, 0), 1 \right] + \varepsilon \left[(0, 0, 0), 0 \right] \]


{{{
	public partial struct DualQuaternion {
		/// <summary>
		/// 逆デュアルクォータニオン
		/// </summary>
		/// <param name="a"></param>
		/// <param name="result"></param>
		public static void Invert(ref DualQuaternion a, out DualQuaternion result) {
			var rr = 1 / a.Real.LengthSquared();
			var rd = -2 * (a.Real.X * a.Dual.X + a.Real.Y * a.Dual.Y + a.Real.Z * a.Dual.Z + a.Real.W * a.Dual.W) * rr * rr;
			result = new DualQuaternion(
				-a.Real.X * rr,
				-a.Real.Y * rr,
				-a.Real.Z * rr,
				+a.Real.W * rr,
				-a.Dual.X * rr - a.Real.X * rd,
				-a.Dual.Y * rr - a.Real.Y * rd,
				-a.Dual.Z * rr - a.Real.Z * rd,
				+a.Dual.W * rr + a.Real.W * rd);
		}
		/// <summary>
		/// 逆デュアルクォータニオン
		/// </summary>
		/// <param name="a"></param>
		/// <returns></returns>
		public static DualQuaternion Invert(DualQuaternion a) {
			DualQuaternion result;
			Invert(ref a, out result);
			return result;
		}
	}
}}}
 逆行列$\mathbf{M}^{-1}$にはいくつかの計算方法があります。ここでは、計算量が多い代わりに、分岐の不要な方法を示します。

!!クラメールの公式(Cramer's Fomula)

行列: $ \mathbf{M} $
行列式(Determinant): $ |\mathbf{M}| $
余因子行列(Cofactor Matrix): $ \tilde{\mathbf{M}} $
\[ \mathbf{M}^{-1} = \frac { \mathbf{\tilde{M}} } { |\mathbf{M}| } \]

※具体的な計算式は次のようになります。行と列が増えると計算量が増大するため、4行4列の式以外に、2行2列と3行3列の式も示します。

!!2行2列
\[ \mathbf{M} = \left[\matrix{ m_{11}&m_{12}\\m_{21}&m_{22} }\right] \]
\[ \mathbf{|\mathbf{M}|} = \left| \matrix{ m_{11}&m_{12}\\m_{21}&m_{22} } \right|
 = m_{11} m_{22} - m_{12} m_{21} \]
\[ \tilde{\mathbf{M}} = \left[\matrix{m_{22}&-m_{12}\\-m_{21}&m_{11}}\right] \]

\[ \mathbf{M}^{-1}
 = \frac { \mathbf{\tilde{M}} } { |\mathbf{M}| }
 = \frac{ \left[\matrix{m_{22}&-m_{12}\\-m_{21}&m_{11}}\right] }
 { \left| \matrix{ m_{11}&m_{12}\\m_{21}&m_{22} } \right| }
 = \frac{ \left[\matrix{m_{22}&-m_{12}\\-m_{21}&m_{11}}\right] }
 { m_{11} m_{22} - m_{12} m_{21} } \]

!!3行3列

\[
\mathbf{M} =
\left[ \array{
m_{11} & m_{12} & m_{13} \\
m_{21} & m_{22} & m_{23} \\
m_{31} & m_{32} & m_{33} \\
} \right]
\]

\[ |\mathbf{M}| =
\left| \array{
m_{11} & m_{12} & m_{13} \\
m_{21} & m_{22} & m_{23} \\
m_{31} & m_{32} & m_{33} \\
} \right| =
 + m_{11} m_{22} m_{33}
 + m_{12} m_{23} m_{31}
 + m_{13} m_{21} m_{32}
 -  m_{11} m_{23} m_{32}
 -  m_{12} m_{21} m_{33}
 -  m_{13} m_{22} m_{31}
\]

\[
\mathbf{M}^{-1} = \frac {1}{|\mathbf{M}|}
\left[ \array{
 m_{22} m_{33} - m_{23} m_{32} &
 m_{13} m_{32} - m_{12} m_{33} &
 m_{12} m_{23} - m_{13} m_{22} \\
 m_{23} m_{31} - m_{21} m_{33} &
 m_{11} m_{33} - m_{13} m_{31} &
 m_{13} m_{21} - m_{11} m_{23} \\
 m_{21} m_{32} - m_{22} m_{31} &
 m_{12} m_{31} - m_{11} m_{32} &
 m_{11} m_{22} - m_{12} m_{21} \\
} \right]
\]

!!4行4列

\[
\mathbf{M} =
\left[ \array{
m_{11} & m_{12} & m_{13} & m_{14}\\
m_{21} & m_{22} & m_{23} & m_{24}\\
m_{31} & m_{32} & m_{33} & m_{34}\\
m_{41} & m_{42} & m_{43} & m_{44}\\
} \right]
\]

\[ |\mathbf{M}|
 = \left| \array{
m_{11} & m_{12} & m_{13} & m_{14}\\
m_{21} & m_{22} & m_{23} & m_{24}\\
m_{31} & m_{32} & m_{33} & m_{34}\\
m_{41} & m_{42} & m_{43} & m_{44}\\
} \right|
 = \array{
+m_{11}m_{22}m_{33}m_{44} +m_{11}m_{22}m_{34}m_{42} +m_{11}m_{24}m_{32}m_{43} \\
+m_{12}m_{21}m_{34}m_{43} +m_{12}m_{23}m_{31}m_{44} +m_{12}m_{24}m_{33}m_{41} \\
+m_{13}m_{21}m_{32}m_{44} +m_{13}m_{22}m_{34}m_{41} +m_{13}m_{24}m_{31}m_{42} \\
+m_{14}m_{21}m_{34}m_{42} +m_{14}m_{22}m_{31}m_{43} +m_{14}m_{23}m_{32}m_{41} \\
-m_{11}m_{22}m_{34}m_{43} -m_{11}m_{23}m_{34}m_{41} -m_{11}m_{24}m_{33}m_{42} \\
-m_{12}m_{21}m_{33}m_{44} -m_{12}m_{23}m_{34}m_{41} -m_{12}m_{24}m_{31}m_{43} \\
-m_{13}m_{21}m_{34}m_{42} -m_{13}m_{22}m_{31}m_{44} -m_{13}m_{24}m_{32}m_{41} \\
-m_{14}m_{21}m_{32}m_{43} -m_{14}m_{22}m_{33}m_{41} -m_{14}m_{23}m_{31}m_{42} \\
\array}\]

(**placeholder**)
!!2つの異なる平面同士の交わる直線

平面1: $ a = [ \mathbf{a}_v : a_d ] $
平面2: $ b = [ \mathbf{b}_v : b_d ] $

\[ \mathbf{L} = \{ \mathbf{a}_v \times \mathbf{b}_v : b_d \mathbf{a}_v - a_d \mathbf{b}_v \} \]

 3Dグラフィックスでは、ローカル座標で表現されたポリゴンメッシュを構成する頂点の位置や法線を、最終的に画面座標へ変換するためにいくつかの[[変換行列]]を使います。
 このとき、ベクトルと変換行列には、同次座標による同次変換を用います。
 
 DirectXスタイルでは、ワールド(World)、ビュー(View)、投影(Projection)の3つの変換行列を組み合わせることが一般的です。この3つにより、投影空間に変換されます。主に、ワールドは物体のスケーリング・回転・位置による変換、ビューはカメラのスケーリング・回転・位置から計算される変換、投影はカメラの遠近法や視野を決める変換を表すために使われます。
 投影変換行列で見える範囲は視錐台(View Frustum)と呼ばれます。視錐台を外れるポリゴンはカリング(Culling)「切り捨て」によって、無駄な処理を省くことができます。

 変換行列を結合させるには、[[行列の積]]を使います。

 頂点を変換する式は次のようになります。これはDirectXスタイルです。ローカル空間から画面空間に変換する変数が左から右に並ぶ形式になります。

*投影空間の頂点位置: $v'$
*ローカル空間の頂点位置: $v$
*ワールド変換行列: $M_W$
*ビュー変換行列: $M_V$
*投影変換行列: $M_P$

\[ v' = v M_W M_V M_P \]

 たとえば3Dの位置座標$(x, y, z)$は、$v = (v_x, v_y, v_z, v_w) = (x, y, z, 1)$というように表します。投影空間の座標が$v' = (v_x', v_y', v_z', v_w')$と得られるので、最後に$v' = (\frac{v_x'}{v_w'}, \frac{v_y'}{v_w'}, \frac{v_z'}{v_w'}, )$とすると、投影された位置が得られます。

 行列やベクトルの変数の順序は変えることができません。一方で、ベクトルや行列の間の積は、結合側を満たすため、次の式のように乗算をする順序を変えても理論上の問題はありません。ただし、浮動小数点値の演算の誤差が起きることはあります。

\[ v' = ((v M_W) M_V) M_P \]
\[ v' = v ((M_W M_V) M_P) \]
\[ v' = v (M_W (M_V M_P)) \]

 このように、位置ベクトルを1つずつ順に変換行列で変換していくことも、結合した変換行列1つで変換することもできます。
 また、法線ベクトルについては、平行移動成分を$0$にした変換行列、または4行3列にした変換行列を使って変換することで計算できます。

 ビューポート(Viewport)のパラメータで位置ベクトル$v'$変換することで、レンダーターゲット(フレームバッファ)の画素を指す画面座標や深度が得られます。
 このローカル座標から画面座標への変換をProject(投影する)といい、逆変換をUnprojectといいます。
 逆変換は、たとえば画面座標で表されたマウスでクリックした位置から、レイ(Ray)を奥へ伸ばして、命中する物体のローカル座標を求めるといった場合に使います。