めもがき
曲線の始点終点どっちが近いのかな?判断
曲線の始点終点どっちが近いのかな?判断 - C#ATIA
関連のなにか。
実環境が無いのでスペルミス上等ということで。
Option Explicit Private Sub Sample(crv As INFITF.Reference, pln As INFITF.Reference) Dim measureCrv As SPATypeLib.Measurable Set measureCrv = GetMeasurable(crv) Select Case measureCrv.GeometryName Case CatMeasurableCurve, CatMeasurableCircle, CatMeasurableLine 'OK Case Else: Err.Raise 13 End Select Dim A As Double, B As Double, C As Double, D As Double ComputePlaneEquationABCD pln, A, B, C, D Dim onCrvPointsCoordinates(0 To 8) As Variant Call asDisp(measureCrv).GetPointsOnCurve(onCrvPointsCoordinates) 'Start point coordinates Dim ptX As Double, ptY As Double, ptZ As Double ptX = onCrvPointsCoordinates(0) ptY = onCrvPointsCoordinates(1) ptZ = onCrvPointsCoordinates(2) '面の方程式と、始点から伸びる直線上の点の座標の方程式を解く 'Origin point coordinates as ptX, ptY, ptZ 'Plane projection point coordinates as prjX, prjY , prjZ 'Distance of origin to projection as L 'A * prjX + B * prjY + C * prjZ = D 'prjX = ptX + A * L 'prjY = ptY + B * L 'prjZ = ptZ + C * L Dim L As Double L = (D + A * ptX + B * ptY + C * ptZ) / _ (A ^ 2 + B ^ 2 + C ^ 2) Dim prjX As Double, prjY As Double, prjZ As Double prjX = ptX + A * L prjY = ptY + B * L prjZ = ptZ + C * L Debug.Print ComputeScalar(ptX - prjX, ptY - prjY, ptZ - prjZ) End Sub 'Plane Equation 'Ax + By + Cz = D Private Sub ComputePlaneEquationABCD( _ iPlane As INFITF.Reference, _ ByRef oA As Double, _ ByRef oB As Double, _ ByRef oC As Double, _ ByRef oD As Double _ ) Dim measurePln As SPATypeLib.Measurable Set measurePln = GetMeasurable(iPlane) Select Case measureCrv.GeometryName Case CatMeasurablePlane 'OK Case Else: Err.Raise 13 End Select Dim planeComponents(0 To 8) As Variant Call asDisp(measurePln).GetPlane(planeComponents) Dim x1st As Double, y1st As Double, z1st As Double x1st = planeComponents(3) y1st = planeComponents(4) z1st = planeComponents(5) Dim x2nd As Double, y2nd As Double, z2nd As Double x2nd = planeComponents(6) y2nd = planeComponents(7) z2nd = planeComponents(8) Dim planeNomalDirection() As Double planeNomalDirection = CrossProduct( _ x1st, y1st, z1st, _ x2nd, y2nd, z2nd _ ) Let oA = planeNomalDirection(0) Let oB = planeNomalDirection(1) Let oC = planeNomalDirection(2) Dim plnOriginX As Double, plnOriginY As Double, plnOriginZ As Double plnOriginX = planeComponents(0) plnOriginY = planeComponents(1) plnOriginZ = planeComponents(2) Let oD = ComputeScalar(plnOriginX, plnOriginY, plnOriginZ) End Sub 'ベクトルの外積 Public Function CrossProduct( _ iX1 As Double, iY1 As Double, iZ1 As Double, _ iX2 As Double, iY2 As Double, iZ2 As Double _ ) As Double() 'Double(0 To 2) Const X = 0, Y = 1, Z = 2 Dim resultVector(0 To 2) As Double resultVector(X) = iY1 * iZ2 - iZ1 * iY2 resultVector(Y) = iZ1 * iX2 - iX1 * iZ2 resultVector(Z) = iX1 * iY2 - iY1 * iX2 Let CrossProduct = resultVector End Function 'ベクトルから大きさを求める Public Function ComputeScalar( _ iX As Double, _ iY As Double, _ Optional iZ As Double = 0# _ ) As Double Let ComputeScalar = VBA.Math.Sqr(iX ^ 2 + iY ^ 2 + iZ ^ 2) End Function 'てきとう Public Function GetMeasurable(iRef As INFITF.Reference) As SPATypeLib.Measurable Dim doc As INFITF.Document Set doc = GetModelElement(iRef).Document Dim spaWb As SPATypeLib.SPAWorkbench Set spaWb = doc.GetWorkbench("SPAWorkbench") Set GetMeasurable = spaWb.GetMeasurable(iRef) End Function '[選択要素からドキュメントを取得する - C#ATIA](http://kantoku.hatenablog.com/entry/2016/04/07/183709 "選択要素からドキュメントを取得する - C#ATIA") Public Function GetModelElement(iAnyObject As INFITF.AnyObject) As INFITF.ModelElement Set GetModelElement = iAnyObject.GetItem("ModelElement") End Function 'disable VBE static syntax check. Private Function asDisp(o As INFITF.CATBaseDispatch) As INFITF.CATBaseDispatch Set asDisp = o End Function
参考
何度でもよみがえるメモ帳(ネタ)
とあるソフトを間違えて閉じてしまうことが頻発したため、終了してもゾンビのごとく蘇るようにしてみた。
もっと良い方法がありそう……。
# メモ帳を起動してイベントを購読する処理 [scriptblock]$startNotepad = { # メモ帳を起動 [Diagnostics.Process]$notepadProc = Start-Process -FilePath notepad -PassThru # 起動を待機 $notepadProc.WaitForInputIdle() > $null # イベントを通知させる $notepadProc.EnableRaisingEvents = $true # Exited(終了時)のイベントを購読開始 Register-ObjectEvent -InputObject $notepadProc -EventName Exited } # 無限ループ while ($true) { # メモ帳を起動 $startNotepad.Invoke() # 何かしらイベントが起きるまで待つ Wait-Event # 発生したイベント情報を取得して破棄(破棄しないと`Wait-Event`で待機しない) Get-Event | Remove-Event }
PowerShellなり、Windows PowerShell ISEなりに貼り付けて実行すると、何回閉じても復活するメモ帳が起動する。
終了したい場合は、PowerShellのウィンドウでCtrl+C
を押すか、PowerShellそのものを終了する。
190111追記
そもそもイベントにする必要が無かった。
# 無限ループ while ($true) { # メモ帳を起動 [Diagnostics.Process]$notepadProc = Start-Process -FilePath notepad -PassThru # 終了を待機 $notepadProc.WaitForExit() }
【VBA実験】何回NotしてもTrueになるTrueを作る
あけましておめでとうございます。 今年もよろしくお願いいたします。
前書き
私が職場で使っているVBAのライブラリの中には、「何回NotしてもTrueになるTrue」を返すAPIを持つものがあります(一般には使われていないライブラリ)。
この「何回NotしてもTrueになるTrue」をVBAだけで作成する方法の記事となります。
そのため、実際のコードに役立つことはほぼ無いでしょう。
VBAのBoolean周りの動作
本来、VBAのTrueは、16bitの符号付き整数で表すと-1
になります(VBAの16進数表現で&HFFFF
)。
これを、Not
演算子でビット単位で否定すると0
(&H0000
)、すなわちFalseになります。
対して、問題のTrueは16bitの符号付き整数で表すと1
(&H0001
)になっており、これに対してNot
演算子を使用すると-2
(&HFFFE
)、0ではないためTrueになります。
コード
以下のコードにあるGetTrueNotTrue()
関数の返り値が「何回NotしてもTrueになるTrue」になります。
Private Type intType Value As Integer End Type Private Type boolType Value As Boolean End Type Function GetTrueNotTrue() As Boolean Dim i As intType i.Value = 1 Dim b As boolType LSet b = i Let GetTrueNotTrue = b.Value End Function
コードの解説
普通に数値をBoolean型の変数に代入しても、自動型変換が行われ、VBA本来のTrue・Falseとして代入されてしまいます。
今回は数値のバイナリ表現を保ったまま、Boolean型の変数に代入したいため、別の方法をとる必要がありました。
上記のコードでは、ユーザー定義型のバイナリコピーができるLSet
ステートメントを使用し、Integer型の1
のバイナリ表現をBoolean型にコピーしています。
Private Type intType Value As Integer End Type Private Type boolType Value As Boolean End Type
で入れ物となるユーザー定義型を定義します (VBAではIntegerもBooleanも2バイトの領域を必要とします)。
Dim i As intType i.Value = 1
でInteger型の1
を設定、
Dim b As boolType LSet b = i
で別のユーザー定義型にバイナリコピーしています。
動作の確認
Private Sub Sample() Debug.Print GetTrueNotTrue() '-> True Debug.Print Not GetTrueNotTrue() '-> True Debug.Print CInt(GetTrueNotTrue()) '-> 1 End Sub
無事、「何回NotしてもTrueになるTrue」を作成できました。
タスクバーに通知を表示するPowerShellスクリプト
小ネタ
以下のようなメッセージを簡単に表示できるPowerShellスクリプト。
表示はそれぞれWIn 8.1 Win10
PowerShellスクリプト
本体。適当な場所に「○○.ps1」として保存する。
param( [string]$Prompt = 'メッセージ', [string]$Title = '通知', $CallBack = '' ) Add-Type -AssemblyName System.Windows.Forms, System.Drawing function Show-NotifyIcon { param( [string]$Prompt = 'メッセージ', [string]$Title = '通知', [scriptblock]$CallBack = {} ) [Windows.Forms.NotifyIcon]$notifyIcon = New-Object -TypeName Windows.Forms.NotifyIcon -Property @{ BalloonTipIcon = [Windows.Forms.ToolTipIcon]::Info BalloonTipText = $Prompt BalloonTipTitle = $Title Icon = [Drawing.SystemIcons]::Information Text = $Title Visible = $true } # イベント定義 $notifyIcon.add_BalloonTipClicked( $CallBack ) [int]$timeout = 3 # sec [DateTimeOffset]$finishTime = [DateTimeOffset]::UtcNow.AddSeconds( $timeout ) $notifyIcon.ShowBalloonTip( $timeout ) # そのままだとイベントが走らない&すぐに消えてしまうので適当wait while ( [DateTimeOffset]::UtcNow -lt $finishTime ) { Start-Sleep -Milliseconds 1 } $notifyIcon.Dispose() } $parameters = $MyInvocation.BoundParameters $parameters.CallBack = [scriptblock]::Create( $CallBack ) Show-NotifyIcon @parameters
使い方
引数1:表示するメッセージ
引数2:タイトル
引数3:クリックされたときのコールバック処理
を文字列で渡す。
バッチ
powershell.exe -Sta -NoProfile -WindowStyle Hidden -ExecutionPolicy RemoteSigned -File 上記のps1ファイル メッセージ タイトル コールバック処理
VBA
VBAならWindows API使えば?という話はさておく。
Sub ShowNotifySample() Const NotifyScriptPath = "保存したスクリプト(ps1ファイル)の保存場所" Const PsCommandLineBase = "powershell.exe -Sta -NoProfile -WindowStyle Hidden -ExecutionPolicy RemoteSigned -File """ Const WQuoteSpaceWQuote = """ """ '表示するメッセージ及びタイトル Dim notifyMessage As String notifyMessage = "めっせーじ" Dim notifyTitle As String notifyTitle = "たいとる" 'クリックされた時の処理(PowerShellスクリプト) 'エクスプローラーでエクセルの場所を開く Dim callBackPsScript As String callBackPsScript = "explorer.exe " & Excel.Application.Path Dim execCmd As String execCmd = PsCommandLineBase & NotifyScriptPath & WQuoteSpaceWQuote & _ notifyMessage & WQuoteSpaceWQuote & _ notifyTitle & WQuoteSpaceWQuote & _ callBackPsScript & """" Call VBA.Shell(execCmd, vbHide) End Sub
im@sparqlを触ってみる その3 - クエリの内容を理解する
の続き。
実際にちょこちょこ触って、クエリの雰囲気を掴めてきたので、自分なりの理解の仕方を書いておく(一部間違えているかも)。
用語
Resource Description Framework - Wikipedia
略称RDF。 データベース(im@sparql)内のデータの構成を示すもの。
主語(subject)・述語(predicate)・目的語(object)の3要素(トリプル)で関係情報を示す。 (リレーショナルデータベースでは無い=NoSQLと考えて良いのだろうか?)
無理矢理オブジェクトに当てはめると、「主語」はオブジェクト本体、「述語」はプロパティ名及び型、「目的語」はプロパティの値、のようなものと解釈した。
主語・述語にはURI、目的語には値やURIなどが指定される(目的語がURIの場合はオブジェクト型のプロパティのような形で階層構造になる)。
SPARQL - Wikipedia
RDFに対するクエリ言語。 SQL風の文法らしい(私自身はSQLを碌に触ったことないので不明)。
今回の記事で扱うクエリはこの言語で書かれたものになる。
どことなくXPathに近い雰囲気も感じる。
クエリ
今回は以下のクエリの内容を理解する。
PREFIX
PREFIX schema: <http://schema.org/> PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> PREFIX imas: <https://sparql.crssnky.xyz/imasrdf/URIs/imas-schema.ttl#>
主語・述語・目的語の一部はURIで示されるが、いちいち書くのは大変なのでエイリアスを設定するのが一般的。
例えば
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
はhttp://www.w3.org/1999/02/22-rdf-syntax-ns#
をrdf:
で省略できるようになり、
<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>
をrdf:type
と書けるようになる。
SELECT
SELECT *
SELECT ?変数名
とすることで、結果として出力する値(変数)を指定できる。
今回は*
が指定されているため、次のWHERE
内で使用された変数全てが結果として出力される。
WHERE-1
WHERE内は文が多いので、前後に分けて考えてみる。
WHERE { ?s rdf:type imas:ScriptText; imas:Source ?source; schema:text ?text.
WHERE内には主語 述語 目的語
と書き、書かれた条件に当てはまるものが探索される。
まず?s rdf:type imas:ScriptText
となっているため、主語(?s)は任意で、述語がrdf:type
、目的語がimas:ScriptText
となるものが探索され、結果が?s
変数に入る。
3行目には2個しか要素が書かれていないが、前の行が;
で終わっているため、主語は前の行と同じもの指定する、となる(4行目も同じ)。
3行目、4行目では2行目で見つかった主語(?s)についてimas:Source
、schema:text
の情報を?source
変数、?text
変数に格納している。
WHERE-2
?source schema:name ?name; filter(regex(str(?name),"千早")) }
1行目は先ほどと同じで、?source
についてschema:name
の情報を?name
変数に格納している。
2行目ではフィルター、特定の要素のみの抽出が行われている。
filter
はかっこ内の評価結果がTrueとなるものだけを結果として出力する。
中の式は、?name
変数をstr()
関数で文字列化し、regex
(正規表現マッチ)で"千早"
と一致するものだけがTrueとなる。
WHEREまとめ
出力される変数(結果)
rdf:type
がimas:ScriptText
となる要素?s
?s
のimas:Source
を示す?source
?s
のschema:text
を示す?text
?source
のschema:name
を示す?name
ただし、?name
が"千早"
に一致するもののみ。
Order By
order by ?text
?text
変数の値でソート。
自分なりの解釈まとめ
はてなブログだとシンタックスハイライトが効かないので読みにくいけれど……。
# 使用する名前空間の宣言 PREFIX schema: <http://schema.org/> PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> PREFIX imas: <https://sparql.crssnky.xyz/imasrdf/URIs/imas-schema.ttl#> # WHERE内の全変数出力 SELECT * WHERE { # rdf:type が imas:ScriptText のものを ?s に入れて ?s rdf:type imas:ScriptText; # $s の imas:Source を ?source に imas:Source ?source; # $s の schema:text を ?text に入れる schema:text ?text. # ?source の schema:name を ?name に入れて ?source schema:name ?name; # ?name が "千早" と一致するものだけを抽出 filter(regex(str(?name),"千早")) }order by ?text # ?text の値でソート
参考サイト
im@sparqlを触ってみる その2 - PowerShellのデータ取得を関数化する
上記の記事でPowerShellから情報を取得することには成功したが、そのままでは使いにくいため、関数に切り出してみた。
動作環境
Windows10 Pro 64bit上のWindows PowerShellのみで確認。
> $PSVersionTable Name Value ---- ----- PSVersion 5.1.17134.165 PSEdition Desktop PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...} BuildVersion 10.0.17134.165 CLRVersion 4.0.30319.42000 WSManStackVersion 3.0 PSRemotingProtocolVersion 2.3 SerializationVersion 1.1.0.1
コード(psm1)
毎回クエリを投げるのは負荷的に避けたかったため、簡単なキャッシュ機構を付けてみた。
ただ、関数だけでは状態を保持しにくいため、モジュール(psm1)として、モジュール内の変数にキャッシュを保持させる構成としている。
# 結果のキャッシュ [Collections.Generic.Dictionary[string,psobject[]]]$jsonCache = New-Object -TypeName 'Collections.Generic.Dictionary[string,psobject[]]' function Request-Imasparql { [CmdletBinding()]Param ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$QueryString ) Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' # 改行及びインデント削除 [string]$trimQuery = [regex]::Replace( $QueryString , ' *[\r\n]{1,2} *' , [string]::Empty ) # キャッシュにあれば情報取得&リターン [psobject[]]$jsons = @() if ( $jsonCache.TryGetValue( $trimQuery , [ref]$jsons ) ) { Write-Information 'use cache' Write-Information ('query = ' + $trimQuery) return $jsons } Write-Information 'access' [Net.WebClient]$wc = New-Object -TypeName Net.WebClient -Property @{ BaseAddress = 'https://sparql.crssnky.xyz/spql/imas/query' Encoding = [Text.UTF8Encoding]$false } # クエリの設定 $wc.QueryString.Set( 'output', 'json' ) # 出力形式 [string]$escapedQuery = [uri]::EscapeDataString( $trimQuery ) $wc.QueryString.Set( 'query', $escapedQuery ) Write-Information ('escaped query = ' + $escapedQuery) [string]$jsonTxt = $wc.DownloadString( [string]::Empty ) [psobject]$data = ConvertFrom-Json -InputObject $jsonTxt $jsons = $data.results.bindings $jsonCache.Add( $trimQuery , $jsons ) return $jsons } Export-ModuleMember -Function Request-Imasparql
サンプル
上記モジュールを使って前回の記事と同じ処理を書くと以下のようになる。
Import-Module -Name '上記コードを○○.psm1として保存して、保存したファイルのフルパス' # https://sparql.crssnky.xyz/imas/ 内の「千早のセリフテキストを取得」のクエリ [string]$query = @' PREFIX schema: <http://schema.org/> PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> PREFIX imas: <https://sparql.crssnky.xyz/imasrdf/URIs/imas-schema.ttl#> SELECT * WHERE { ?s rdf:type imas:ScriptText; imas:Source ?source; schema:text ?text. ?source schema:name ?name; filter(regex(str(?name),"千早")) }order by ?text '@ # 情報取得 [psobject[]]$resultJson = Request-Imasparql -QueryString $query -InformationAction Continue # 必要な情報をコンソール出力 $resultJson | select -Property @( @{N="Name";E={$_.name.value}}, @{N="Text";E={$_.text.value}} ) | Write-Output
im@sparqlを触ってみる その1 - PowerShellでデータを取得する
→ その2
はじめに
自分のプログラミング以外の趣味関係のファンサイトに、「im@sparql」というサイトがある。
こちらはWEB上にデータベースがあり、クエリ文字列を付加してGETすれば情報が取れるというものらしい。 (自分も良く分かっていないので詳細は以下なども参照)
自分自身、各種基本知識・理解が不足しているため、「とりあえず動かしてみる」を目標にPowerShellから情報を取得するコードを作ってみた。
取得する情報
今回はim@sparqlの「千早のセリフテキストを取得」のクエリをそのまま使用して、情報を取得する。
コード
# ベースのURL [string]$baseUrl = 'https://sparql.crssnky.xyz/spql/imas/query?query=' # https://sparql.crssnky.xyz/imas/ 内の「千早のセリフテキストを取得」のクエリ # 一行に納めるため、SplitしてからJoinしている [string]$query = [string]::Join( '' , @' PREFIX schema: <http://schema.org/> PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> PREFIX imas: <https://sparql.crssnky.xyz/imasrdf/URIs/imas-schema.ttl#> SELECT * WHERE { ?s rdf:type imas:ScriptText; imas:Source ?source; schema:text ?text. ?source schema:name ?name; filter(regex(str(?name),"千早")) }order by ?text '@.Split("`r`n") ) # クエリ文字列をエスケープ # また、そのままだとXMLで結果が返されるため '&output=json' を付与する [uri]$openUri = [uri]($baseUrl + [uri]::EscapeDataString( $query ) + '&output=json') # 情報取得 $result = Invoke-WebRequest -Method Get -Uri $openUri if ( $result.StatusCode -ne 200 ) { throw } # $result.Content はByte配列なので、文字列(JSON)に変換し # JSON文字列をPSObjectへ変換 $json = [Text.Encoding]::UTF8.GetString( $result.Content ) | ConvertFrom-Json # 必要な情報を出力 $json.results.bindings | select -Property @( @{N="Name";E={$_.name.value}}, @{N="Text";E={$_.text.value}} )
コンソール出力
Name Text ---- ---- 如月千早 …! 如月千早 …………。 如月千早 …いえ、もうすぐ本番ですし、そこで体力を使い果たしてしまったら問題ですよ。 (中略) 如月千早 静香…あなたならいつか、できる時がくるわ。アイドルを続けていれば、必ず。…必ずよ。
ハマったところ
結果がXML
JavaScriptのXMLHttpRequest
やCOMのMSXML2.XMLHTTP
を使用した場合、responseText
としてJSON文字列が取得できた。
しかし、.NET Framework系のメソッドを使用した場合はXMLで結果が返却された。
詳しい人からGET時のURLにパラメータを追加することで結果を制御できるとの話があり、試すとうまくいった('&output=json'の付与)。