Menu

Skip to content
AppleScriptの穴
  • Home
  • Products
  • Books
  • Docs
  • Events
  • Forum
  • About This Blog
  • License
  • 仕事依頼

AppleScriptの穴

Useful & Practical AppleScript archive. Click '★Click Here to Open This Script' Link to download each AppleScript

新刊電子書籍「AppleScriptでたのしむ レトロ・グラフィック プログラム集」を刊行

Posted on 8月 31, 2024 by Takaaki Naganoya

電子書籍新刊「AppleScriptでたのしむ レトロ・グラフィック プログラム集」を刊行しました。全154ページ、サンプルAppleScriptアーカイブつき。
→ 販売ページ

1980年代や90年代の8/16ビットPCのBASICで描かせていた、三角関数による各種グラフィックスをAppleScriptで再現。ダイアログで表示するだけでなく、各種GUIアプリ(Keynote、Numbers、Pages、Excel、PowerPoint、Word、Pixelmator Pro)を操作して描画したり、画像書き出ししてAirDropでiOSデバイスに転送するようなサンプルを収録しています。

目次

1章 レトロ・グラフィックスの世界

懐かしのレトロCGの世界を再現
時代を経て感じる郷愁とも異なるテイスト
その昔、十数分かけて描いた三角関数グラフ
1秒以下で終了 vs 6分で終了
最新環境で動くAppleScriptにBASICのプログラムを移植
アップルスクリプトは、構文色分け必須の、色で要素を見分ける環境
最低限の知識でAppleScriptによるグラフィックを
AppleScript書類内に、実行に必要なライブラリを同梱
筆者の関数計算ライブラリ「calcLibAS」内蔵関数
コラム ポケコンエミュレータ“pockemul”

2章 早足で紹介するAppleScriptの世界

1994年から採用され続けている言語
GUIアプリを操作するために存在。搭載実行環境がとても多い
書き方は、アプリ内に存在する用語辞書を参照
本来の機能を利用するためにはシステム設定で許可する必要が
10.10以降でCocoaを直接呼べるようになったインタプリタ言語
GUI部品を直接操作してアプリを操作する強制操作機能が人気?
Web上のAPIを呼んでクラウド系の機能も利用
AS自体で予約語と機能を記述するライブラリ機能
コラム AppleScriptの世界の全体像 OS機能の最深部からGUIそのものの操作まで

3章 AppleScriptでグラフィックスを扱う

Cocoaの機能を呼び出してメモリ上で画像を作成
NSAlertの上にNSImageViewを作成しグラフィック表示
Cocoaのグラフィックス座標系”
主要なアプリケーションの座標系”
画像ファイルに書き出せば”
他のアプリにコピー&ペースト”
当時は存在していなかった透過画像”
パラメータを変えると動作が変わる”
コラム GUIアプリごとの応答速度の違い

4章 レトロ・グラフィックスプログラム集

スクリプトエディタでオープンして実行するだけ
必要なライブラリはバンドル内にすべて格納
掲載リストはグラフィックス描画にかかわる箇所のみ
How to use/ダイアログ表示AppleScript
How to use/ファイル出力AppleScript
How to use/ファイル出力+AirDrop AppleScript
How to use/クリップボード転送AppleScript
How to use/各種GUIアプリ操作AppleScript
OS標準搭載の13の実行環境およびサードパーティの数十の実行環境

線画テスト
円画テスト①
円画テスト②
サイクロイド曲線
バラ曲線
パスカルの蝸牛形
リサージュ曲線
ダイヤモンドパターン
アルキメデスの螺旋
メキシカンハット①
メキシカンハット②
メキシカンハット③
メキシカンハット④
コラム マシンごとの実行速度の違い

Posted in Books news | Tagged 13.0savvy 14.0savvy 15.0savvy Excel Keynote Numbers Pages Pixelmator Pro PowerPoint Word | Leave a comment

AppleScript入門③AppleScriptを使った「自動化」とは?

Posted on 8月 24, 2024 by Takaaki Naganoya

Windowsの開発者と話をしていて、「複数のGUIアプリを操作して自動化するという話が理解できない」と言われます。

 ファイル情報(選択中のファイル、選択中のフォルダ)をFinderから取得して
 spotlight機能を用いて選択中のフォルダ以下のPages書類を抽出し
 個別にPagesでオープンしてPDFを書き出し
 書き出したPDFをファイル名順にならべかえて連結し
 DropboxにREST API経由でアップロードして共有し
 得られた共有URLを指定の送付先にメール(Mail.app)で送信する

と、複数のGUIアプリを操作した自動化のワークフローは、普通にありえるものです。ありふれすぎています。

自動化のサイズ

ここで、ひとことに「自動化」といったときに、ユーザーごとに想定する「サイズ」がぜんぜん違うことに気づきます。

・(A)メニュー項目をいくつか連続して操作する「操作自動化」
・(B)1つの連続した操作を繰り返して実行する「作業自動化」
・(C)MacがタイマーでいくつかのScriptを実行してロボット動作する「業務自動化」

(A)<(B)<(c)と粒度の大きさが変わるところで、世間で言われる「自動化」というのは、ほぼ(A)の操作自動化のレベルと思われます。

自分が行なっているのは、(B)や(c)のレベルの自動化です。

(A)のレベルの「操作自動化」が悪いとは言いませんし、そういうレベルでも自動化に取り組むことには意義はあることでしょう。

ただ、(B)(c)と比べると成果として得られる「メリットが少ない」とは思います。操作を行うための心理的な障壁を下げる、という意味ではメリットがあるのかもしれませんが……。

(B)や(C)では、まとまった動作を放置状態のMacで行うものであり、人間が作業を眺めている必要はないものです。

放置状態で勝手に作業

(B「作業自動化」)や(C)「業務自動化」では、人間がいちいち指示する必要がないか、Scriptの動作を開始する場合にのみ対象フォルダなどのパラメータを指定するものです。

独立した作業なり業務を実行できるようにしておけば、実行するMacの台数を増やせば、それだけ同時にこなせる仕事が増やせます。

それだけ自動化してあれば、作業のためにアルバイトを雇う必要もありませんし、夜通し作業を行わせておいてもよいでしょう。

作業自動化や業務自動化に必要な「仕様書」

これらのまとまった作業を行うAppleScriptを作って運用する場合には、開発前に「仕様書」が必要になります。たいしたものでなくてもいいですが、どういった作業をどういう手順で行うのか、プログラムに書く前に人間の言葉でまとめておく必要はあるのです。

それは、箇条書きの文章で書いておいてもいいですし、画面キャプチャをまとめておいてもよいでしょう。

仕様書を書かないと、どこに向かって、どのように進んでいくのかを誰も理解していないという話になってしまいますし、それではScriptはおろか関与する人々が誰も「正しい作業」を理解できていないという話になってしまいます。

Posted in Beginner | Leave a comment

macOS 14で変更になったOSバージョン取得APIの返り値

Posted on 8月 24, 2024 by Takaaki Naganoya

macOSのバージョンを求める機能については、かならず複数の方法で取得できるように調査しています。

それは、Appleがバグを作る可能性が一番高い機能だからです(なんで懲りないんだろ?)。

OSバージョンを求めるのに、

・AppleScriptの標準装備コマンド「system info」で求める
–> {AppleScript version:”2.8″, AppleScript Studio version:”1.5.3″, system version:”13.6.9″, short user name:”XXXX”, long user name:”XXXXX XXXXX”, user ID:504, user locale:”ja_JP”, home directory:alias “Macintosh HD:Users:XXXX:”, boot volume:”Macintosh HD:”, computer name:”M1 mini”, host name:”m1mini.local”, IPv4 address:”192.168.0.99″, primary Ethernet address:”99:99:99:99:ZZ:99″, CPU type:”ARM64E”, CPU speed:missing value, physical memory:16384}

・AppleScriptの環境プロパティ「system attribute」を求める
set v1 to system attribute “sys1” –> 10
set v2 to system attribute “sys2” –> 4
set v3 to system attribute “sys3” –> 11

・shell commandで求める
–> sw_vers
–> sw_vers -productVersion

などに加えて、NSProcessInfos processInfo()’s operatingSystemVersion() を呼び出すという方法も加わりました。

ただ、これが最近おかしな挙動をするようになったのに気づいていろいろ調査してみたら、macOS 14になって返り値のフォーマットを変えたことが判明しました。ラベルつきの値(NSDictionary)で返してきたのが、ラベルなしの配列(NSArray)で返すように変わっています。macOS 10.13で座標系のデータをラベルなしで返すように変更してきたのと同様の変更が加えられています。

macOS 14.xの初期バージョンでは従来タイプの値を返してきたように記憶していますが、いつ変更したのやら。

AppleScript名:OSバージョンを求める.scpt
use AppleScript
use framework "Foundation"
use scripting additions

set pInfo to (current application’s NSProcessInfo’s processInfo()’s operatingSystemVersion())
–> {majorVersion:10, minorVersion:14, patchVersion:6}–10.14
–> {majorVersion:13, minorVersion:6, patchVersion:9}–13.6.9
–> {14, 6, 1}–14.6.1
–> {15, 1, 0}–15.0.1

★Click Here to Open This Script 

こういう基本的な情報を提供するAPIの仕様を変更することに、どういうインセンティブがあるのか不明です。変える価値があると判断したから変えたんでしょう。自分にはどういう理由なのか、さっぱり分かりません。

安全のためには、AppleScriptのビルトイン機能を使うか、OS内のplistを読み取って自分で処理するとかいった「対策」が必要なのでしょう。

Posted in System | Tagged 14.0savvy 15.0savvy | Leave a comment

AppleScript入門②AppleScriptを使う「考え方」

Posted on 8月 15, 2024 by Takaaki Naganoya

AppleScriptを使ったことのない人が、AppleScriptを使うときに、どのような考え方で問題を解決したらよいでしょうか?

(1)特定のアプリ上でのデータの作成/取得

AppleScriptの一番やりやすい作業は、アプリケーションの書類を大量に作成/チェックするというものです。

書類をともなうアプリで、AppleScriptに対応しているものは、AppleScriptによる書類作成やチェックの自動化が行える可能性が高いと考えてよいでしょう。

Word、Excel、PowerPoint、Pages、Numbers、Keynoteなどのオフィスアプリ。

InDesign、Illustrator、Photoshopといったクリエイティブ系のアプリ。

そうしたアプリを中心に作業を自動化したい、という話はとても「やりようがある」と考えます。難易度が低いわけではありませんが、何か困っていて解決したいことがある場合には、解決できる可能性が高い、ということです。

(2)Webブラウザやメーラーの操作

日常的に使う機会の多いWebブラウザやメーラーで行う作業をまとめて自動化することができます。

やや、難易度が高くなりますが……Webブラウザを操作して特定のWebサイトにログインして、データを取得し、ログアウトするという操作は実際にAppleScriptで行えるものの代表的なものです(Webサイト側の「作り方」によってうまく操作できないこともあります)。

メーラー関係の作業についても、大量のメールを、内容を変更しつつ送信するという作業はAppleScriptに適した作業です。ただし、日常的に利用しているメーラーが、Appleのメール.appである必要があります。他のメーラーではAppleScriptへの対応度が低いとかまったく対応していないケースがあるため、これ以外の選択肢がないといって差し支えないでしょう。

(3)AppleScriptだけでデータ処理

AppleScriptに特殊なデータを扱うような(他のGUIアプリのデータを、アプリなしで読み書きして操作するような)機能は用意されていません。

テキスト、画像、PDF、QuickTimeムービーといった一般的なデータであれば、AppleScriptだけで処理できる可能性は高いところです。とくに、PDFについてはmacOS 10.10以降でmacOSのAPI CocoaをAppleScriptから直接呼べるようになったために、ウォーターマークを追加する「以外」の操作であれば実際に処理できます。

Posted in Beginner | Leave a comment

AppleScript入門① AppleScriptってなんだろう?

Posted on 8月 15, 2024 by Takaaki Naganoya

当Blogでは、日々AppleScriptを書いている中で、「この情報は有用なので共有しておくべき」と考えたものを掲載し、すぐに使っていただけるようにして公開しています。

書き方、使い方、詳しい説明については、電子書籍にまとめて約90冊ほど出しています。

つまり、当BlogはAppleScriptが何であるかを理解していて、実際に書ける人に対して情報を発信する立場です。

ただ、ちょうど夏休みということもあり、ちょっとそのあたりについて書いてみることにしました。

AppleScriptってなに?

「macOS上で動くマクロ言語」とか言うと、何か理解できたような気になりますが……これだけでは何も説明わかりません。

macOS上の機能、各種GUIアプリの機能、shell scriptの機能など、さまざまな機能を呼び出して、データをやりとりする、インタプリタ型(逐次解釈実行型)のプログラミング言語です。

macOS上の大きな機能や小さな機能を呼び出せるようになっていて、とくにGUIアプリの操作や、GUIアプリの設定情報や、GUIアプリがオープンしている「書類」にアクセスして情報をとってきたり、設定したり……という操作が行えます。

AppleScriptがGUIアプリを操作する方法は、いくつか手段が用意されています。

(1)アプリ内の用語辞書を通じてやりとりする

AppleScriptがGUIアプリを操作するやりかたは、マウスカーソルを操作してメニューを選ぶといった方法ではありません(別途ありますが)。それでは、遅すぎるし、画面に表示されていなかったら操作できないですし、「できないこと」が多すぎます。

なので、あらかじめ「こういうコマンドを受け付けるよ!」というコマンドの一覧がAppleScriptに対して読める形でアプリ内に格納されています。ユーザーもこのコマンド一覧(用語辞書)を読んで、AppleScriptを書くことになります。

(2)GUI部品を操作してGUIアプリを操作する

「メニューのx番目の項目を選んでクリックする」といったGUI部品を操作するやりかたです。これはこれで便利ではあるものの、スピードが(1)にくらべて100倍以上遅いですし、毎回確実に動くAppleScriptをこの方法で書くのは、けっこう難しいです。

いまだと、システム設定.appを操作していろんなシステムの状態を変更するようなAppleScriptを動かしたいと考えているユーザーさんがけっこういて、そのための方法をいろいろ探してはいるんですけれど、システム設定.appで細部まですべて操作するのはかなり無理があるので(iOSっぽい作り方になったことが災いして、GUI部品の階層を追いきれない)、この(2)の方法があるからできるっしょ! と、安易に考えないほうがいいです。

先日、X(旧Twitter)上でMacにつながっている複数のディスプレイのうち、メインディスプレイを切り替えたり、画面の解像度を切り替えたいという人がいて、ちょっとやりとりしました。その彼は、この(2)の方法でなんとかなるんじゃないかと考えていたようですが、実際に自分でやってみたところ、それはできませんでした。

なので、かつて調べてあった「SwitchResX」を用いる記事を紹介したところ、「有料アプリを買うほどではない」とのことでした。事実上、SwitchResXを使う方法でしか実現できないのですが、どうするんでしょう?

(3)その他の手段をさがす

shell commandなどでディスプレイ解像度を取得・変更するものがあれば、それを呼び出すことになります。

Posted in Beginner | Leave a comment

デフォルトインストールされたフォント名を取得するAppleScript

Posted on 8月 3, 2024 by Takaaki Naganoya

macOSにインストールされているフォントの情報をsystem_profiler経由で取得し、各フォントのパス情報をもとに、/System/Library/Fonts以下に入っているものだけを抽出して、フォント名をリストアップするAppleScriptです。

Pages書類に含まれているフォントのうち、デフォルトインストールされたものではないものを抽出するために、OSにデフォルトインストールされたものだけを取得すべく、基礎情報を得るために書いてみたものです(現在執筆中の電子書籍「Pages+AppleScriptで本をつくろう!」の付録Scriptとして用意しました)。

事前に何か設定するといった必要はありません。スクリプトエディタやScript Debuggerで実行するだけです。

AppleScriptからフォントの存在するパス情報はなかなか取得しにくい、FontBook.appがAppleScript非対応になったので、ほぼ無理なのですが、system_profilerから取得すれば、それほど難しくありません。

実行所要時間は、

  M1 Mac mini (macOS 13.6):6秒
  M1 Mac mini (macOS 15.6):13秒
  M2 Mac mini(macOS 14.6):10秒
  M2 MacBook Air(macOS 14.6):15秒
  M4 MacBook Air(macOS 15.6):15秒

ぐらいで、M1 Mac mini@macOS 13の速さが光ります。OSの違いから、macOS 13.6環境ではallFontsが1168、macOS 14.6環境では1603とフォント数がどうやら異なっているのですが(M2 miniも同じ)、フォント数を考慮してもM1 Mac miniの処理の速さが光ります。

もしくは、macOSのバージョンの違いにより、system_profilerの実行速度が大きく異なるとか? macOS 13.6環境と14.6環境にそれほど差があるものなんでしょうか。

無駄な処理も入っていますが、いろいろチェックしながら作ったもので、ベンチマーク用に作ったものではありません。

# M2 Airってなんでこんなに時間がかかるんだろう? 放熱台に乗せて処理しているのに。処理のほとんどの時間がsystem_profilerの実行で消費しています

AppleScript名:OSデフォルトインストールされたフォント名を取得.scpt
—
–  Created by: Takaaki Naganoya
–  Created on: 2024/08/03
—
–  Copyright © 2024 Piyomaru Software, All Rights Reserved
—

use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

property NSArray : a reference to current application’s NSArray
property NSString : a reference to current application’s NSString
property NSPredicate : a reference to current application’s NSPredicate
property NSMutableArray : a reference to current application’s NSMutableArray
property NSMutableDictionary : a reference to current application’s NSMutableDictionary
property NSPropertyListFormat : a reference to current application’s NSPropertyListFormat
property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding
property NSPropertyListImmutable : a reference to current application’s NSPropertyListImmutable
property NSPropertyListSerialization : a reference to current application’s NSPropertyListSerialization

script spd
  property sRes : missing value
  
property aSource : missing value
  
property bRes : missing value
  
property fontNames : {}
  
property fontPath : {}
  
property outList : {}
end script

–システムにインストールされているフォントの情報を全抽出
set sRes of spd to do shell script "system_profiler -xml SPFontsDataType"
set aSource of spd to first item of (readPlistFromStr(sRes of spd) of me)

–全フォント情報から、名称とパス情報を抽出
set bRes of spd to (((aSource of spd)’s valueForKeyPath:"_items"))
set fontNames of spd to ((bRes of spd)’s valueForKeyPath:"_name") as list
set fontPath of spd to ((bRes of spd)’s valueForKeyPath:"path") as list

–ループでOSデフォルトインストールされているフォントを抽出
set outList of spd to {}
set aCount to 1

repeat with i in (fontPath of spd)
  set j to contents of i
  
  
if j begins with "/System/Library/Fonts/" then
    –指定のフォントに複数のTypefaceが含まれていることを考慮し、情報を取り出す
    
set outItem to (((item aCount of (bRes of spd)))’s valueForKeyPath:"typefaces._name") as list
    
set the end of (outList of spd) to outItem
  end if
  
  
set aCount to aCount + 1
end repeat

–すべてのTypefaceを入れた入れ子の2D Listから1D Listに変換
set allFonts to FlattenList((outList of spd)) of me

–ドット(.)ではじまるフォントを除外
set outList to {}
repeat with i in allFonts
  set j to contents of i
  
if j does not start with "." then
    set the end of outList to j
  end if
end repeat

return outList

–stringのplistを読み込んでRecordに
on readPlistFromStr(theString)
  set aSource to NSString’s stringWithString:theString
  
set pListData to aSource’s dataUsingEncoding:(NSUTF8StringEncoding)
  
set aPlist to NSPropertyListSerialization’s propertyListFromData:pListData mutabilityOption:(NSPropertyListImmutable) format:(NSPropertyListFormat) errorDescription:(missing value)
  
return aPlist
end readPlistFromStr

–リストに入れたレコードを、指定の属性ラベルの値で抽出
on filterRecListByLabel(aRecList as list, aPredicate as string)
  set aArray to NSArray’s arrayWithArray:aRecList
  
  
set aPredicate to NSPredicate’s predicateWithFormat:aPredicate
  
set filteredArray to aArray’s filteredArrayUsingPredicate:aPredicate
  
  
set bList to filteredArray as list
  
return bList
end filterRecListByLabel

–リストに入れたレコードを、指定の属性ラベルの値で抽出
on filterRecListByLabel1(aRecList, aPredicate as string)
  set aArray to current application’s NSArray’s arrayWithArray:aRecList
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat:aPredicate
  
set filteredArray to aArray’s filteredArrayUsingPredicate:aPredicate
  
return filteredArray
end filterRecListByLabel1

–By Paul Berkowitz
–2009年1月27日 2:24:08:JST
–Re: Flattening Nested Lists
on FlattenList(aList)
  set oldDelims to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to {"????"}
  
set aString to aList as text
  
set aList to text items of aString
  
set AppleScript’s text item delimiters to oldDelims
  
return aList
end FlattenList

★Click Here to Open This Script 

Posted in Font | Tagged 12.0savvy 13.0savvy 14.0savvy 15.0savvy | Leave a comment

AppleScriptでMyriad Tables Libが使えないケース

Posted on 7月 28, 2024 by Takaaki Naganoya

Myriad Tables Lib(最新版はv1.13)はShane StanleyによるAppleScriptライブラリで、かつてのClassic MacOSに存在していたGUI記述系のOSAX(名前は忘れた)を思わせるほど柔軟性が高いAppleScriptライブラリの逸品です。

自分としては、これを用いる場合でも過度な作り込みを行わず、編集可能なデータビューワー的な機能を手軽に追加するのがよいと感じています。

自分でもさらに機能を簡略化した「display table by list」ライブラリを用意しています。

macOS自体のセキュリティ制限により使えないケースも

ところが、本ライブラリが機能しないケースが確認されました。

特定の呼び方を行うと、ダイアログ+表の表示そのものが封じられるという、macOS側の新たな制限によるもののようです。

そのため、自分がmacOS 10.12用にAppleScriptで開発したシステムが、現在のmacOS 14.xで部分的に動かなくなってしまいました。目下、システムを改修中です。Myriad Tables Libを使って手抜きをしていた箇所を、すべて自力で表UI(NSTableView)を使って表示・編集する必要があるようです(けっこう大変)。

ステータスメニューから呼び出した場合にブロック

同システムはステータスメニューからタイマー機能を呼び出すようなシステムだったのですが、このメニューからMyriad Tables Libを呼び出すと、Xcode上で作成したAppleScriptアプリであっても、Script Debugger上で実行したAppleScriptであっても、表示がブロックされてしまいます。

ランタイム環境がどれであっても、ステータスメニューから呼び出してMyriad Tables Libによるテーブル表示は行えませんでした。

macOS 10.12:可能
macOS 10.13:不可
macOS 10.14:(未検証)
macOS 10.15:(未検証)
macOS 11.x:(未検証)
macOS 12.x:(未検証)
macOS 13.6:不可
macOS 14.6:不可
macOS 15β:(未検証)

これは、Script Debugger上で実行したAppleScriptから、ステータスメニュー上に動的に項目を作成し、そこにメニューを追加した場合でも、Xcode上で作成した場合でも同様の挙動が見られます。

Shaneに聞いてみた

その後、Shaneに実際に聞いてみたところ、ダイアログ表示をメインスレッド実行しないとできないらしい(意訳)ことが明らかになってきました。

ダメ元で表UIつきダイアログ表示を明示的にメインスレッド指定して実行してみたところ、ハネられました。

自作の「display table by list」ライブラリを同様に呼び出してみたところ、これも表示できませんでした。このライブラリは自分が作ったのでソースコードをすべて自分が管理しており、アラートダイアログの表示ハンドラをメインスレッド指定で呼び出しています。どの時点でメインスレッド指定実行しないとダメなのかは明確ではありませんが、割と八方ふさがりな状況です。

Posted in GUI Script Libraries Security | Tagged 13.0savvy 14.0savvy | Leave a comment

メキシカンハット v3a

Posted on 7月 28, 2024 by Takaaki Naganoya

メキシカンハットの描画AppleScriptのedama2さんバージョンで、さらに多色対応したものです。

–> Download mexicanHatv3a

AppleScript名:メキシカンハット v3a.scptd
— Created 2024-06-28 by Takaaki Naganoya
— Modified 2024-07-09 by Edama2
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "AppKit"
use mLib : script "calcLibAS"
use imgDispLib : script "imageDisplayLib"

on run
  my main()
end run

on main()
  (*
  set userReply to display dialog "拡大率を入力してください(1-8)" default answer "3"
  set aScale to userReply’s text returned as number
  if aScale < 1 or 8 < aScale then return beep
  *)

  
set aScale to 3
  
  
–描画
  
set listBezierPaths to {}
  
repeat 3 times
    set listBezierPaths’s end to current application’s NSBezierPath’s bezierPath()
  end repeat
  
  
–https://x.com/yone/status/1805552100400939161
  
set D to {}
  
set dR to pi / 180
  
  
repeat 255 times
    set end of D to 192
  end repeat
  
  
repeat with y from -120 to 120 by 4
    
    
if y < 0 then
      set rY to -60
    else
      set rY to 60
    end if
    
    
repeat with x from -180 to 180 by 4
      
      
if (x < -60) then
        set rX to -120
      else if (x < 60) then
        set rX to 0
      else
        set rX to 120
      end if
      
      
set rR to (x – rX) ^ 2 + (y – rY) ^ 2
      
set z to 180 * (exp (rR / -800))
      
set sX to floor (128 + x / 2 – y / 4)
      
set sY to floor (128 – y / 4 – z / 2)
      
      
if not (sX < 0 or 256 ≤ sX) then
        if not ((D’s item (sX + 1)) ≤ sY) then
          
          
set zz to (floor (z * 0.0389)) + 1
          
log result
          
set idRect to current application’s NSMakeRect(sX * aScale, sY * aScale, aScale, aScale)
          
          
if ((zz = 1) or (zz = 3) or (zz = 5) or (zz = 7)) then
            (listBezierPaths’s item 1’s appendBezierPathWithRect:idRect)
          end if
          
if ((zz = 2) or (zz = 3) or (zz ≥ 6)) then
            (listBezierPaths’s item 2’s appendBezierPathWithRect:idRect)
          end if
          
if (zz ≥ 4) then
            (listBezierPaths’s item 3’s appendBezierPathWithRect:idRect)
          end if
          
          
set (D’s item (sX + 1)) to sY
        end if
      end if
    end repeat
  end repeat
  
  
#
  
set srcWidth to 0
  
set srcHeight to 0
  
repeat with idBezierPath in listBezierPaths
    set tmpWidth to current application’s NSMaxX(idBezierPath’s |bounds|())
    
set tmpHeight to current application’s NSMaxY(idBezierPath’s |bounds|())
    
if srcWidth < tmpWidth then set srcWidth to tmpWidth
    
if srcHeight < tmpHeight then set srcHeight to tmpHeight
  end repeat
  
  
#
  
set srcSize to current application’s NSMakeSize(srcWidth, srcHeight)
  
log result
  
set srcImage to current application’s NSImage’s alloc()’s initWithSize:srcSize
  
  
set operation to current application’s NSCompositingOperationScreen
  
–>set operation to current application’s NSCompositingOperationSourceOver
  
  
repeat with num from 1 to count listBezierPaths
    
    
if num is 1 then
      set idColor to current application’s NSColor’s blueColor()
    else if num is 2 then
      set idColor to current application’s NSColor’s redColor()
    else if num is 3 then
      set idColor to current application’s NSColor’s greenColor()
    end if
    
    
tell srcImage
      lockFocus()
      
      
tell current application’s NSImage’s alloc()
        tell initWithSize_(srcSize)
          lockFocus()
          
idColor’s setFill()
          
listBezierPaths’s item num’s fill()
          
unlockFocus()
          (
its drawAtPoint:(current application’s NSZeroPoint) fromRect:(current application’s NSZeroRect) operation:operation fraction:1.0)
        end tell
      end tell
      
      
unlockFocus()
    end tell
  end repeat
  
  
  
# 反転して合成
  
set dstImage to current application’s NSImage’s alloc()’s initWithSize:srcSize
  
tell dstImage
    lockFocus()
    
    
current application’s NSColor’s blackColor()’s setFill()
    
current application’s NSRectFill(current application’s NSMakeRect(0, 0, its |size|()’s width, its |size|()’s height))
    
    
set xform to current application’s NSAffineTransform’s transform()
    
xform’s translateXBy:0.0 yBy:(its |size|()’s height)
    
xform’s scaleXBy:1.0 yBy:-1.0
    
xform’s concat()
    
    
srcImage’s drawInRect:(current application’s NSMakeRect(0, 0, its |size|()’s width, its |size|()’s height))
    
unlockFocus()
  end tell
  
  
–結果表示
  
dispImage(dstImage, "Mexican Hat") of imgDispLib
end main

★Click Here to Open This Script 

Posted in GUI | Tagged 13.0savvy 14.0savvy 15.0savvy | Leave a comment

メキシカンハット v3

Posted on 7月 28, 2024 by Takaaki Naganoya

メキシカンハットの描画AppleScriptの、edama2さんバージョンです。

–> Download mexicanHatv3

AppleScript名:メキシカンハット v3.scptd
— Created 2024-06-28 by Takaaki Naganoya
— Modified 2024-07-09 by Edama2
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "AppKit"
use mLib : script "calcLibAS"
use imgDispLib : script "imageDisplayLib"

on run
  my main()
end run

on main()
  (*
  set userReply to display dialog "拡大率を入力してください(1-8)" default answer "3"
  set aScale to userReply’s text returned as number
  if aScale < 1 or 8 < aScale then return beep
  *)

  
set aScale to 3
  
  
  
–描画
  
set idBezierPath to current application’s NSBezierPath’s bezierPath()
  
  
–https://x.com/Stosstruppe/status/1279774386396880897
  
set dT to {}
  
set dB to {}
  
  
repeat 256 times
    set dT’s end to 192
  end repeat
  
repeat 256 times
    set dB’s end to -1
  end repeat
  
  
repeat with y from -120 to 120 by 4
    
    
if y < 0 then
      set rY to -60
    else
      set rY to 60
    end if
    
    
repeat with x from -180 to 180 by 4
      
      
if (x < -60) then
        set rX to -120
      else if (x < 60) then
        set rX to 0
      else
        set rX to 120
      end if
      
      
set rR to (x – rX) ^ 2 + (y – rY) ^ 2
      
set z to 180 * (exp (rR / -800))
      
set cX to floor (128 + x / 2 – y / 4)
      
set cY to floor (128 – y / 4 – z / 2)
      
      
if not (cX < 0 or 256 ≤ cX) then
        set pSet to false
        
        
if ((dT’s item (cX + 1)) > cY) then
          set (dT’s item (cX + 1)) to cY
          
set pSet to true
        end if
        
if ((dB’s item (cX + 1)) < cY) then
          set dB’s item (cX + 1) to cY
          
set pSet to true
        end if
        
        
if pSet then
          set idRect to current application’s NSMakeRect(cX * aScale, cY * aScale, aScale, aScale)
          (
idBezierPath’s appendBezierPathWithRect:idRect)
        end if
        
      end if
    end repeat
  end repeat
  
  
log idBezierPath’s |bounds|()
  
set srcWidth to current application’s NSMaxX(idBezierPath’s |bounds|())
  
set srcHeight to current application’s NSMaxY(idBezierPath’s |bounds|())
  
set srcSize to current application’s NSMakeSize(srcWidth, srcHeight)
  
log result
  
  
–set idColor to current application’s NSColor’s blueColor()
  
–set idColor to current application’s NSColor’s redColor()
  
set idColor to current application’s NSColor’s greenColor()
  
  
tell current application’s NSImage’s alloc()
    tell initWithSize_(srcSize)
      lockFocus()
      
idColor’s setFill()
      
idBezierPath’s fill()
      
unlockFocus()
      
set srcImage to it
    end tell
  end tell
  
  
# 反転して合成
  
set dstImage to current application’s NSImage’s alloc()’s initWithSize:srcSize
  
tell dstImage
    lockFocus()
    
    
current application’s NSColor’s blackColor()’s setFill()
    
current application’s NSRectFill(current application’s NSMakeRect(0, 0, its |size|()’s width, its |size|()’s height))
    
    
set xform to current application’s NSAffineTransform’s transform()
    
xform’s translateXBy:0.0 yBy:(its |size|()’s height)
    
xform’s scaleXBy:1.0 yBy:-1.0
    
xform’s concat()
    
    
srcImage’s drawInRect:(current application’s NSMakeRect(0, 0, its |size|()’s width, its |size|()’s height))
    
unlockFocus()
  end tell
  
  
–結果表示
  
dispImage(dstImage, "Mexican Hat") of imgDispLib
end main

★Click Here to Open This Script 

Posted in GUI | Tagged 13.0savvy 14.0savvy 15.0savvy | Leave a comment

メキシカンハット v2a

Posted on 7月 28, 2024 by Takaaki Naganoya

以前掲載したメシキカンハット描画AppleScriptが、Retinaディスプレイ環境で表示が崩れていたため、修正してみました。

実行は、以下のScript Bundleをダウンロードして行なってください。

–> Download maxicanv2a.scptd


▲非Retina環境のMacで実行したところ(M1 Mac mini)


▲Retina環境のMacで実行したところ(M2 MacBook Air)

AppleScript名:メキシカンハット v2a.scptd
— Created 2024-06-28 by Takaaki Naganoya
— Modified 2024-07-28 by Takaaki Naganoya
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "AppKit"
use mLib : script "calcLibAS"
use imgDispLib : script "imageDisplayLib"

property NSColor : a reference to current application’s NSColor
property NSString : a reference to current application’s NSString
property NSImage : a reference to current application’s NSImage
property NSScreen : a reference to current application’s NSScreen
property NSBezierPath : a reference to current application’s NSBezierPath
property NSBitmapImageRep : a reference to current application’s NSBitmapImageRep

property retinaF : 1.0

set retinaF to (NSScreen’s mainScreen()’s backingScaleFactor()) as real
–>  2.0 (Retina) / 1.0 (Non Retina)

–パラメータから下地になる画像を作成する
set aSize to current application’s NSMakeSize(300, 200)
set anImage to NSImage’s alloc()’s initWithSize:aSize
set backlColor to (NSColor’s colorWithCalibratedRed:0.0 green:0.0 blue:0.0 alpha:1.0)
drawImageWithColorFill(anImage, {0, 0, 300, 200}, backlColor) of me

–描画色
set drawColor to (NSColor’s colorWithCalibratedRed:0.0 green:1.0 blue:0.0 alpha:1.0)

–Mexican Hat
set D to {}
set rDnum to pi / 180

repeat 256 times
  set end of D to 100
end repeat

repeat with y from -180 to 180 by 6
  repeat with x from -180 to 180 by 4
    set u to 120 + (x / 3) – (y / 6)
    
if u < 0 or u ≥ 240 then
      —
    else
      set r to rDnum * (sqrt ((x ^ 2) + (y ^ 2)))
      
set z to 100 * (cos (r – 30)) * (cos (3 * r))
      
set v to 40 – y / 6 – z / 2
      
      
if (item u of D) > v then
        set anImage to psetOnImage(anImage, {(u + 20) * retinaF, (120 – v) * retinaF}, drawColor) of me
        
copy v to (item u of D)
      end if
    end if
  end repeat
end repeat

–結果表示
dispImage(anImage, "Mexican Hat") of imgDispLib

on psetOnImage(anImage, posDat, fillColor)
  set retinaF to (NSScreen’s mainScreen()’s backingScaleFactor()) as real
  
–>  2.0 (Retina) / 1.0 (Non Retina)
  
  
anImage’s lockFocus() –描画開始
  
  
set origX to (item 1 of posDat) / retinaF
  
set origY to (item 2 of posDat) / retinaF
  
set sizeX to (1) * retinaF
  
set sizeY to (1) * retinaF
  
  
set theRect to {{x:origX, y:origY}, {width:sizeX, height:sizeY}}
  
  
set theNSBezierPath to NSBezierPath’s bezierPath
  (
theNSBezierPath’s appendBezierPathWithRect:theRect)
  
  
fillColor’s |set|() –色設定
  
theNSBezierPath’s fill() –ぬりつぶし
  
  
anImage’s unlockFocus() –描画ここまで
  
  
return anImage –returns NSImage
end psetOnImage

on drawImageWithColorFill(anImage, drawList, fillColor)
  set retinaF to (NSScreen’s mainScreen()’s backingScaleFactor()) as real
  
–>  2.0 (Retina) / 1.0 (Non Retina)
  
  
anImage’s lockFocus() –描画開始
  
  
set origX to (item 1 of drawList) –* retinaF
  
set origY to (item 2 of drawList) –* retinaF
  
set sizeX to (item 3 of drawList) –* retinaF
  
set sizeY to (item 4 of drawList) –* retinaF
  
  
set theRect to {{x:origX, y:origY}, {width:sizeX, height:sizeY}}
  
  
set theNSBezierPath to NSBezierPath’s bezierPath
  (
theNSBezierPath’s appendBezierPathWithRect:theRect)
  
  
fillColor’s |set|() –色設定
  
theNSBezierPath’s fill() –ぬりつぶし
  
  
anImage’s unlockFocus() –描画ここまで
  
  
–return anImage –returns NSImage
end drawImageWithColorFill

★Click Here to Open This Script 

Posted in Number | Leave a comment

YouTube Picture In Pictureウィンドウの制御

Posted on 7月 23, 2024 by Takaaki Naganoya

AppleScriptでYouTubeのPicture In Pictureウィンドウを操作したいという話を見かけて、そんなことは一度も考えたことがなかったので調べてみました。macOS 10.13あたりで搭載されたPicture In Pictureのムービー再生機能をAppleScriptから操作します。

結論からいえば、Picture In PictureはmacOS側が用意している専用のプログラム「PIPAgent」(/System/Library/CoreServices/PIPAgent.app)がPicture In Pictureの処理を行っています。Webブラウザの内蔵機能ではなく、各ブラウザで共通してこれを呼び出すようです。

Picture In Picture機能は、たとえばSafariでYouTubeムービーを再生した状態で、マウスの右クリックをムービー上で2回行うことで、その機能が呼び出せます。


▲YouTubeムービーの上でマウスの右クリックを行なった状態(1回目)


▲YouTubeムービーの上でマウスの右クリックを行なった状態(2回目)。ここで「ピクチャインピクチャにする」を実行するとPIP表示になる


▲ピクチャインピクチャ表示状態

プロセス一覧で「それっぽい名前」のプロセスに当たりをつけてGUI Scriptingからウィンドウを操作すれば、ウィンドウの位置と大きさをAppleScriptから変更できました。

ただし、このPIPAgentのウィンドウは画面のすみにしか置けませんし、サイズについてもいくつかのサイズを指定できても細かい大きさは指定できません。ここで紹介するAppleScriptも、自分の環境(1920×1200)の画面上で試したものであり、複数の画面をつないでいたり、2x Retinaや3x Retinaなどの解像度の画面をつないである場合には、座標値を書き換えたほうがよいでしょう(あくまで、実験レベルのコードです)。

PIPAgentのウィンドウの縦横比はオリジナルのムービーのものが維持されるため、AppleScriptでサイズを指定しても縦横比が崩れることはありません。

なお、「ピクチャインピクチャ」表示状態にするまで、PIPAgent.appは起動しません。同プロセスの起動を確認したうえでウィンドウの制御を行う必要があります。プロセス確認については、山のように方法がありますのでお好きなように。

AppleScript名:YouTube Picture In Pictureウィンドウの制御.scpt
—
–  Created by: Takaaki Naganoya
–  Created on: 2024/07/22
—
–  Copyright © 2024 Piyomaru Software, All Rights Reserved
—

tell application "Safari" to activate

tell application "System Events"
  try
    tell process "PIPAgent"
      set wCount to unix id
    end tell
  on error
    return –YouTubeのPicture In Picture表示プロセスが起動していない
  end try
  
  
tell process "PIPAgent"
    set wCount to count every window
    
if wCount = 0 then return
    
tell window 1
      set size to {500, 282}
      
repeat 10 times
        set position to {1493, 800}
        
delay 3
        
set position to {0, 800}
        
delay 3
        
set position to {0, 0}
        
delay 3
        
set position to {1493, 0}
        
delay 3
      end repeat
    end tell
  end tell
end tell

★Click Here to Open This Script 

Posted in GUI Scripting | Tagged 13.0savvy 14.0savvy 15.0savvy Safari | Leave a comment

国民の祝日を求める v7

Posted on 7月 17, 2024 by Takaaki Naganoya

日本のカレンダーにおける国民の祝日(休日)を、dateオブジェクトのリストで求めるAppleScriptです。

2020〜2021年のイレギュラーな休日変更に対応してみました。

# 何か不具合や不足分があったらおしらせください
# 某・呪われた手帳メーカー系の仕事には不十分かもしれないので、不幸にもその案件に関わった方はメーカー支給の祝祭日データを利用してください

こうした高度なカレンダー計算AppleScriptは、「Newt On Project」の直系の遺産です。自然言語で曜日や日数の指定を行う上で、休日の計算は欠かせないものでした。

休日に関しては政府がCSVファイルで配布しているものもありますが、来年や再来年、5年後のカレンダーを扱いたいといった場合に、自前で計算できないと話になりません。そうした処理に備える意味でも、自前で計算できる「意義」はあります。

AppleScript名:国民の祝日を求める v7.scpt
use AppleScript
use scripting additions
use framework "Foundation"

(*

本バージョンのテーマ:
東京オリンピック(2020/2021年)関連の超イレギュラーな休日調整に対応

*)

set aList to retHolidayRec(2020) of me
–> {{date "2020年1月1日 水曜日 0:00:00", "元旦"}, {date "2020年1月13日 月曜日 0:00:00", "成人の日"}, {date "2020年2月11日 火曜日 0:00:00", "建国記念日"}, {date "2020年2月23日 日曜日 0:00:00", "天皇誕生日"}, {date "2020年2月24日 月曜日 0:00:00", "振替休日"}, {date "2020年3月20日 金曜日 0:00:00", "春分の日"}, {date "2020年5月3日 日曜日 0:00:00", "憲法記念日"}, {date "2020年5月4日 月曜日 0:00:00", "みどりの日"}, {date "2020年5月5日 火曜日 0:00:00", "こどもの日"}, {date "2020年5月6日 水曜日 0:00:00", "振替休日"}, {date "2020年7月22日 水曜日 0:00:00", "海の日"}, {date "2020年7月24日 金曜日 0:00:00", "スポーツの日"}, {date "2020年8月10日 月曜日 0:00:00", "山の日"}, {date "2020年9月21日 月曜日 0:00:00", "敬老の日"}, {date "2020年9月22日 火曜日 0:00:00", "秋分の日"}, {date "2020年11月3日 火曜日 0:00:00", "文化の日"}, {date "2020年11月23日 月曜日 0:00:00", "勤労感謝の日"}}

–国民の祝日を求める(属性つき)
on retHolidayRec(aYear as integer)
  –固定の祝日
  
if aYear < 2020 then
    set holidayList to {{"1/1", "元旦"}, {"2/11", "建国記念日"}, {"4/29", "昭和の日"}, {"5/3", "憲法記念日"}, {"5/4", "みどりの日"}, {"5/5", "こどもの日"}, {"11/3", "文化の日"}, {"11/23", "勤労感謝の日"}}
    
  else if aYear = 2020 then
    set holidayList to {{"1/1", "元旦"}, {"2/11", "建国記念日"}, {"5/3", "憲法記念日"}, {"5/4", "みどりの日"}, {"5/5", "こどもの日"}, {"11/3", "文化の日"}, {"11/23", "勤労感謝の日"}}
    
  else if aYear = 2021 then
    set holidayList to {{"1/1", "元旦"}, {"2/11", "建国記念日"}, {"5/3", "憲法記念日"}, {"5/4", "みどりの日"}, {"5/5", "こどもの日"}, {"11/3", "文化の日"}, {"11/23", "勤労感謝の日"}}
    
  else
    –2022年から
    
set holidayList to {{"1/1", "元旦"}, {"2/11", "建国記念日"}, {"5/3", "憲法記念日"}, {"5/4", "みどりの日"}, {"5/5", "こどもの日"}, {"11/3", "文化の日"}, {"11/23", "勤労感謝の日"}}
  end if
  
  
  
–天皇誕生日の計算
  
set curEmpBD to getCurEmpBitrthday(aYear) of me
  
if curEmpBD is not equal to false then
    set the end of holidayList to {curEmpBD, "天皇誕生日"}
  end if
  
  
set the end of holidayList to {get_specifiedDay(aYear, 1, 2, 2), "成人の日"} –成人の日–1月の第2月曜日
  
  
–令和2年(2020年)及び3年(2021年)における「国民の祝日」の移動について
  
if aYear = 2020 then
    set the end of holidayList to {"7/ 22", "海の日"}
  else if aYear = 2021 then
    set the end of holidayList to {"7/ 23", "海の日"}
  else
    set the end of holidayList to {get_specifiedDay(aYear, 7, 2, 3), "海の日"} –海の日–7月の第3月曜日
  end if
  
  
  
set the end of holidayList to {get_specifiedDay(aYear, 9, 2, 3), "敬老の日"} –敬老の日–9月の第3月曜日
  
  
–令和2年(2020年)及び3年(2021年)における「国民の祝日」の移動について
  
if aYear = 2020 then
    set the end of holidayList to {"7/24", "スポーツの日"}
  else if aYear = 2021 then
    set the end of holidayList to {"7/23", "スポーツの日"}
    
  else if (aYear < 2000) then
    set the end of holidayList to {"10/10", "体育の日"} –体育の日–10月10日
  else if (aYear < 2020) then
    set the end of holidayList to {get_specifiedDay(aYear, 10, 2, 2), "体育の日"} –体育の日–10月の第2月曜日
  else
    set the end of holidayList to {get_specifiedDay(aYear, 10, 2, 2), "スポーツの日"} –スポーツの日–10月の第2月曜日
  end if
  
  
–令和2年(2020年)及び3年(2021年)における「国民の祝日」の移動について
  
if aYear = 2020 then
    set the end of holidayList to {"8/10", "山の日"}
  else if aYear = 2021 then
    set the end of holidayList to {"8/8", "山の日"}
  else if aYear ≥ 2016 then
    set the end of holidayList to {"8/11", "山の日"} –山の日– 8月11日
  end if
  
  
  
set the end of holidayList to {"3/" & get_ShunbunNoHi(aYear), "春分の日"} –春分の日
  
set the end of holidayList to {"9/" & get_ShuubunNoHi(aYear), "秋分の日"} –秋分の日
  
  
  
  
set holiDate to {}
  
repeat with i in holidayList
    set holiD to date (aYear & "/" & (item 1 of i) as text)
    
set holiNum to weekday of holiD as number
    
    
–元日以外を対象とする(元旦に振替休日なし)–> いや、ある(汗)
    
–if ((item 1 of i) as text) is not "1/1" then
    
–振替休日付加処理
    
if holiNum = 1 then –祝祭日が日曜日だったら
      –日付を動かすのではなく、振替休日を追加する
      
set holiD_furikae to holiD + (1 * days)
      
set the end of holiDate to {holiD_furikae, "振替休日"}
    end if
    
–end if
    
set the end of holiDate to {holiD, item 2 of i}
    
  end repeat
  
  
  
–重複した休日が発生した場合の再振替処理  
  
–基本ルール:  振替休日を後に送る
  
–        「振替休日」が重複リストに入っていないかどうかをチェックし、振替休日の再配置を行う
  
set itemNum to 1
  
set holiDateDup to detectDuplicatesFromNestedList(holiDate, itemNum) of me
  
  
set huriList to {}
  
repeat with i in holiDateDup
    set iCount to length of i
    
repeat with ii in i
      set {aDate, aDateName} to ii
      
if aDateName = "振替休日" then
        set the end of huriList to contents of ii
      end if
    end repeat
  end repeat
  
  
set holiDate to shellSortListAscending(holiDate, 1) of me
  
set holiDateList to spritOrderedItemFromNestedList(holiDate, 1) of me
  
  
repeat with i in huriList
    set {aDate, aName} to i
    
set j to contents of i
    
set offsetDate to 1
    
repeat
      set bDate to aDate + (offsetDate * days)
      
if bDate is not in holiDateList then
        exit repeat
      end if
      
set offsetDate to offsetDate + 1
    end repeat
    
    
set iCount to 1
    
repeat with ii in holiDate
      set jj to contents of ii
      
if jj = j then
        –「複数要素一括削除サブルーチン」などという高機能すぎるサブルーチンを使用。ちょっともったいない
        
set holiDate to itemsDelete(holiDate, {iCount}) of me
      end if
      
set iCount to iCount + 1
    end repeat
    
    
set the end of holiDate to {bDate, "振替休日"}
    
  end repeat
  
  
  
–秋分の日と敬老の日の「間の日」の休日判定処理
  
–参考文献:
  
–http://ja.wikipedia.org/wiki/秋分の日
  
–国民の祝日に関する法律第3条第3項に規定する休日(例)
  
set septDL to {}
  
set the end of septDL to "9/" & get_ShuubunNoHi(aYear) of me –秋分の日
  
set the end of septDL to get_specifiedDay(aYear, 9, 2, 3) –敬老の日 –9月の第3月曜日
  
set septDL to shellSort(septDL) of me
  
if septDL = {"9/21", "9/23"} then
    set kokuminShukujitu to (aYear as string) & "/9/22"
    
set kokuminShukujitu to date kokuminShukujitu
    
set the end of holiDate to {kokuminShukujitu, "国民の祝日"}
  end if
  
  
  
–重複を解消
  
set holiDate to removeDuplicates(holiDate) of me
  
  
–最後に、並べ替えを行って仕上げ
  
set holiDate to shellSortListAscending(holiDate, 1) of me
  
  
return holiDate
end retHolidayRec

–春分の日を求める
–2000年から2099年の間まで計算可能
on get_ShunbunNoHi(aYear)
  set a to 20.69115
  
set b to (aYear – 2000) * 0.2421904
  
set c to round ((aYear – 2000) / 4) rounding toward zero
  
set d to round (a + b – c) rounding toward zero
  
return d
end get_ShunbunNoHi

–秋分の日を求める
–2000年から2099年の間まで計算可能
on get_ShuubunNoHi(aYear)
  set a to 23.09
  
set b to (aYear – 2000) * 0.2421904
  
set c to round ((aYear – 2000) / 4) rounding toward zero
  
set d to round (a + b – c) rounding toward zero
  
return d
end get_ShuubunNoHi

–指定月の第x指定曜日に該当する日付を求める(mm/dd形式)
– 曜日の指定を数値(weekday of (current date) as number)で行えるようにした。
– 曜日を「日曜日」などの日本語ローカライズド文字列で指定するのをやめた
–パラメータ: 年, 月, 曜日番号, 順番
on get_specifiedDay(aYear as integer, aMonth as integer, Youbi as integer, orderNum as integer)
  set sDat to date ((aYear & "/" & aMonth & "/1") as text)
  
set eDat to getMlenInternational(aYear, aMonth) of me
  
  
set countNum to 0
  
  
repeat with i from 1 to eDat
    set aCal to date ((aYear & "/" & aMonth & "/" & (i as text)) as text)
    
set aWeekDayNum to weekday of aCal as integer
    
if Youbi = aWeekDayNum then
      set countNum to countNum + 1
      
if countNum is orderNum then
        set aCalText to (aMonth & "/" & i as text)
        
return aCalText
      end if
    end if
  end repeat
end get_specifiedDay

–指定日の月のみ返す
on getMonth(aDat as date)
  set bDate to month of aDat
  
return bDate as integer
end getMonth

–指定日の日付のみ返す
on getDate(aDat as date)
  set bDate to day of aDat
  
return bDate as integer
end getDate

–指定日の年のみ返す
on getYear(aDat as date)
  set bDate to year of aDat
  
return bDate as integer
end getYear

–現在のカレンダーで指定年月の日数を返す(getMlenから置き換えた)
on getMlenInternational(aYear, aMonth)
  set theNSCalendar to current application’s NSCalendar’s currentCalendar() — do *not* use initWithCalendarIdentifier:
  
set theDate to theNSCalendar’s dateWithEra:1 |year|:aYear |month|:aMonth |day|:1 hour:0 minute:0 |second|:0 nanosecond:0
  
set theResult to theNSCalendar’s rangeOfUnit:(current application’s NSDayCalendarUnit) inUnit:(current application’s NSMonthCalendarUnit) forDate:theDate
  
return |length| of theResult
end getMlenInternational

–リスト中から重複項目をリストアップする
on detectDuplicates(aList)
  set aCount to length of aList
  
  
set duplicationList to {}
  
repeat aCount times
    set anItem to contents of (first item of aList)
    
set aList to rest of aList
    
if anItem is in aList then
      set the end of duplicationList to anItem
    end if
  end repeat
  
  
return duplicationList
end detectDuplicates

–リストから重複部分を除外
on removeDuplicates(aList)
  set newList to {}
  
repeat with i from 1 to (length of aList)
    set anItem to item 1 of aList
    
set aList to rest of aList
    
if {anItem} is not in aList then set end of newList to anItem
  end repeat
  
return newList
end removeDuplicates

–シェルソート
on shellSort(aSortList)
  script oBj
    property list : aSortList
  end script
  
set len to count oBj’s list’s items
  
set gap to 1
  
repeat while (gap ≤ len)
    set gap to ((gap * 3) + 1)
  end repeat
  
repeat while (gap > 0)
    set gap to (gap div 3)
    
if (gap < len) then
      repeat with i from gap to (len – 1)
        set temp to oBj’s list’s item (i + 1)
        
set j to i
        
repeat while ((j ≥ gap) and (oBj’s list’s item (j – gap + 1) > temp))
          set oBj’s list’s item (j + 1) to oBj’s list’s item (j – gap + 1)
          
set j to j – gap
        end repeat
        
set oBj’s list’s item (j + 1) to temp
      end repeat
    end if
  end repeat
  
return oBj’s list
end shellSort

–シェルソートで入れ子のリストを昇順ソート
on shellSortListAscending(a, keyItem)
  set n to length of a
  
set cols to {1391376, 463792, 198768, 86961, 33936, 13776, 4592, 1968, 861, 336, 112, 48, 21, 7, 3, 1}
  
repeat with h in cols
    if (h ≤ (n – 1)) then
      repeat with i from h to (n – 1)
        set v to item (i + 1) of a
        
set j to i
        
repeat while (j ≥ h) and ((contents of item keyItem of item (j – h + 1) of a) > (item keyItem of v))
          set (item (j + 1) of a) to (item (j – h + 1) of a)
          
set j to j – h
        end repeat
        
set item (j + 1) of a to v
      end repeat
    end if
  end repeat
  
return a
end shellSortListAscending

–シェルソートで入れ子のリストを降順ソート
on shellSortListDecending(a, keyItem)
  set n to length of a
  
set cols to {1391376, 463792, 198768, 86961, 33936, 13776, 4592, 1968, 861, 336, 112, 48, 21, 7, 3, 1}
  
repeat with h in cols
    if (h ≤ (n – 1)) then
      repeat with i from h to (n – 1)
        set v to item (i + 1) of a
        
set j to i
        
repeat while (j ≥ h) and ((contents of item keyItem of item (j – h + 1) of a) < (item keyItem of v))
          set (item (j + 1) of a) to (item (j – h + 1) of a)
          
set j to j – h
        end repeat
        
set item (j + 1) of a to v
      end repeat
    end if
  end repeat
  
return a
end shellSortListDecending

–入れ子のリスト中から重複項目をアイテム番号つきでリストアップする
on detectDuplicatesFromNestedList(aList, itemNum)
  set aCount to length of aList
  
copy aList to orig_aList
  
  
set duplicationList to {}
  
repeat aCount times
    set anItem to contents of (first item of aList)
    
set aList to rest of aList
    
    
–指定アイテムだけのリストを毎回再生成して存在確認を行う
    
set aaList to spritOrderedItemFromNestedList(aList, itemNum) of me
    
if (contents of (item itemNum of anItem)) is in aaList then
      set the end of duplicationList to anItem
    end if
    
  end repeat
  
  
–検出した重複データを元に、該当するデータをリストアップ
  
set detectList to {}
  
repeat with i in duplicationList
    set j to contents of (item itemNum of i)
    
set detectItem to {}
    
repeat with ii in orig_aList
      set jj to contents of (item itemNum of ii)
      
      
if jj = j then
        set the end of detectItem to (contents of ii)
      end if
    end repeat
    
set the end of detectList to detectItem
  end repeat
  
  
return detectList
end detectDuplicatesFromNestedList

–入れ子のリストの全要素から指定アイテム目の要素だけを取り出してリストで返す
on spritOrderedItemFromNestedList(aList, itemNum)
  set aaList to {}
  
repeat with i in aList
    set the end of aaList to contents of (item itemNum of i)
  end repeat
  
return aaList
end spritOrderedItemFromNestedList

–リスト中の指定要素を削除して返す
on itemsDelete(aList, delNumList)
  set delLen to length of delNumList
  
  
repeat with i from 1 to delLen
    
    
set newList to {}
    
set aLen to length of aList
    
    
set ii to item i of delNumList
    
    
if ii = 1 then
      set maeList to items 2 thru aLen of aList
      
set newList to maeList
      
    else if ii = aLen then
      set maeList to items 1 thru (aLen – 1) of aList
      
set newList to maeList
      
    else
      set maeList to items 1 thru (ii – 1) of aList
      
set atoList to items (ii + 1) thru -1 of aList
      
set newList to maeList & atoList
    end if
    
    
–アイテム指定の補正
    
set delNumList to adjustItemNo(ii, delNumList) of me
    
    
set aList to newList
  end repeat
  
  
return newList
end itemsDelete

–itemsDeleteのサブルーチン
–リストに対して複数アイテムの削除を行う場合に、1つ削除した後にはアイテム指定が
–狂ってしまうため、毎回削除するたびにアイテム指定の補正を行う
–paramNumとelemListの間でのパラメータの衝突は関知しない

–項目要素補正をリストに対して行う
on adjustItemNo(paramNum, elemList)
  –項目ゼロを指定してきた場合には、そのままelemListを戻す
  
if paramNum = 0 then return elemList
  
–プラス方向のレンジ外判定は行っていない。elemListのlengthよりも大きな値は関知しない
  
set retList to {}
  
repeat with i in elemList
    set j to contents of i
    
if j > paramNum then
      set ansNum to j – 1
    else
      set ansNum to j
    end if
    
set the end of retList to ansNum
  end repeat
  
  
return retList
end adjustItemNo

–現在のカレンダーで指定年月のdate objectを返す(年、月、日、時、分、秒)
on getDateInternationalYMDhms(aYear, aMonth, aDay, anHour, aMinute, aSecond)
  set theNSCalendar to current application’s NSCalendar’s currentCalendar()
  
set theDate to theNSCalendar’s dateWithEra:1 |year|:aYear |month|:aMonth |day|:aDay hour:anHour minute:aMinute |second|:aSecond nanosecond:0
  
return theDate as date
end getDateInternationalYMDhms

–現在のカレンダーで指定年月のdate objectを返す(年、月、日)
on getDateInternational(aYear, aMonth, aDay)
  set theNSCalendar to current application’s NSCalendar’s currentCalendar()
  
set theDate to theNSCalendar’s dateWithEra:1 |year|:aYear |month|:aMonth |day|:aDay hour:0 minute:0 |second|:0 nanosecond:0
  
return theDate as date
end getDateInternational

–天皇誕生日の計算
on getCurEmpBitrthday(targYear)
  –昭和、平成、令和 の誕生日定義
  
set curEmperrorsBirthday to {{"4/29", {1926, 1988}}, {"12/23", {1989, 2018}}, {"2/23", {2020, 9999}}}
  
–浩宮氏が崩御の際には、崩御年を記入
  
  
set hitF to false
  
repeat with i in curEmperrorsBirthday
    copy i to {targDate, {beginYear, endYear}}
    
if targYear ≥ beginYear and targYear ≤ endYear then
      set hitF to true
      
exit repeat
    end if
  end repeat
  
  
if hitF = false then
    return false
  end if
  
  
return targDate
end getCurEmpBitrthday

★Click Here to Open This Script 

Posted in Calendar | Tagged 10.15savvy 11.0savvy 12.0savvy 13.0savvy 14.0savvy 15.0savvy | Leave a comment

Pixelmator Pro v3.6.4でAppleScriptからの操作時の挙動に違和感が

Posted on 7月 12, 2024 by Takaaki Naganoya

Pixelmator Proでたまに実行する「1024×1024の画像からアイコン用の各種解像度の画像を作成する」AppleScriptですが、Pixelmator Pro v3.6.4で久しぶりに動かしたら、エラーが出てうまく動かなくなっていました。

このAppleScriptは、1024×1024の画像をオープンした状態で実行するものです。

すぐに、各サイズにリサイズしつつ、ファイル名に解像度を反映させた状態で書き出しを行います。

AppleScript名:アイコン書き出しv2.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/10/19
—
–  Copyright © 2020 Piyomaru Software, All Rights Reserved
—

use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

set resolList to {1024, 512, 256, 128, 64, 32, 16}
set aTargFileBase to (choose file name with prompt "Select Export base name") as string

tell application "Pixelmator Pro"
  if (exists of document 1) = false then
    display dialog "There is no document" buttons {"OK"} default button 1 with icon 1
    
return
  end if
  
  
tell front document
    set aWidth to width
    
set aHeight to height
    
    
if {aWidth, aHeight} is not equal to {1024.0, 1024.0} then
      display dialog "Wrong Image Size (1024×1024 required)" buttons {"OK"} default button 1 with icon 2 with title "Size Error"
      
return
    end if
    
    
repeat with i in resolList
      resize image width i height i resolution 72 algorithm bilinear
      
export to file (aTargFileBase & "_" & (i as string) & "x" & (i as string) & ".png") as PNG
      
undo
    end repeat
  end tell
end tell

★Click Here to Open This Script 

これが、macOS 13.6.8+Pixelmator Pro v3.6.4の組み合わせで実行したらエラーが出て実行できなくなっていました。

問題点は割と明らかです。documentへのtellブロック内で、documentをパラメータとして要求するexportといったコマンドを実行したときに、tellブロックを認識せず、コマンドの直後にdocumentオブジェクトを表記しないとエラーになります。

解決策は、tellブロックでdocumentを指定するのをやめて、コマンドのパラメータとしてfront documentといったオブジェクトを表記することです。または、「属性値 of it」のようにdocumentへの参照を記述しておくことです(他の言語のユーザーが腰を抜かすので、なるべくitは使わないで記述していますが……)。

これで解決できるものの、macOS側で問題を起こしているのか、Pixelmator Pro側で問題を起こしているのかがわかりにくいところ。

おそらく、Pixelmator Pro側の問題かと思われますが、言い切ることもできないでしょう。

AppleScript名:アイコン書き出しv3.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/10/19
–  Modified on: 2024/07/11
—
–  Copyright © 2020-2024 Piyomaru Software, All Rights Reserved
—

use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

set resolList to {1024, 512, 256, 128, 64, 32, 16}
set aTargFileBase to (choose file name with prompt "Select Export base name") as string

tell application "Pixelmator Pro"
  if (exists of document 1) = false then
    display dialog "There is no document" buttons {"OK"} default button 1 with icon 1
    
return
  end if
  
  
tell the front document
    set aWidth to width
    
set aHeight to height
  end tell
  
  
if {aWidth, aHeight} is not equal to {1024.0, 1024.0} then
    display dialog "Wrong Image Size (1024×1024 required)" buttons {"OK"} default button 1 with icon 2 with title "Size Error"
    
return
  end if
  
  
repeat with i in resolList
    resize image front document width i height i resolution 72 algorithm bilinear
    
export front document to file (aTargFileBase & "_" & (i as string) & "x" & (i as string) & ".png") as PNG
    
undo
  end repeat
  
end tell

★Click Here to Open This Script 

Posted in Bug Object control | Tagged 13.0savvy 14.0savvy 15.0savvy Pixelmator Pro | Leave a comment

Safariで「プロファイル」機能を使うとAppleScriptの処理に影響

Posted on 7月 6, 2024 by Takaaki Naganoya

Safariの最近のバージョンで導入されたものの、使わないでいた「プロファイル」機能。

Appleが提案してきた鳴り物入りの新機能のうち、ほとんどのものはその日のうちにオフにしていたり、「なかったもの」にしているものが(個人的に)多いです。最新のものでは、macOS 15.0の「ウィンドウの吸着機能」は、その日のうちにオフにしました。

「プロファイル機能」も、そんな「Appleが作った新たなゴミ機能」だと思っていたのですが、よくよく調べると、

拡張機能やクッキーなど環境情報をプロファイルごとに切り替えられるようになっています。

Safariを用いたプログラムの開発を行う必要があり、この「プロファイル」機能に注目。個人環境と開発環境を区分けできることにメリットを感じて使いはじめました。

ところが、AppleScriptでSafariのウィンドウのタイトルを取得する処理を実行すると、

AppleScript名:最前面のウィンドウのタイトルを取得する.scpt
set aTitle to getPageTitleOfFrontmostWindow()
–> "個人用 ― AppleScriptの穴 – Useful & Practical AppleScript archive. Click ’★Click Here to Open This Script’ Link to download each AppleScript"

on getPageTitleOfFrontmostWindow()
  tell application "Safari"
    if (count every window) = 0 then return false
    
tell window 1
      set aProp to properties
    end tell
    
set aDoc to document of aProp
    
set aText to name of aDoc
  end tell
  
return aText
end getPageTitleOfFrontmostWindow

★Click Here to Open This Script 

タイトルの前にプロファイル名がついてきてしまいます。これは、嬉しくない状況です。

現在のプロファイル名を取得できるとか、使用プロファイルを指定できるとかいった機能なら前向きなのですが……。

JavaScript経由でページのタイトルを取得したところ、プロファイル名はついてきませんでした。

この2つの方法を試すことで、どちらか最適な方を選ぶとか、両方を組み合わせてプロファイル名を抽出するといった処理を行うべきでしょう。

AppleScript名:最前面のウィンドウのタイトルを取得する(JS).scpt
set aTitle to getPageTitleOfFrontmostWindowByJS()
–> "AppleScriptの穴 – Useful & Practical AppleScript archive. Click ’★Click Here to Open This Script’ Link to download each AppleScript"

on getPageTitleOfFrontmostWindowByJS()
  tell application "Safari"
    if (count every window) = 0 then return false
    
set aRes to do JavaScript "document.title;" in front document
    
return aRes
  end tell
end getPageTitleOfFrontmostWindowByJS

★Click Here to Open This Script 

Posted in Object control | Tagged 13.0savvy 14.0savvy 15.0savvy Safari | Leave a comment

Developer CD November 1993

Posted on 7月 6, 2024 by Takaaki Naganoya

たまたま、当時の職場にあったCDを入手して(いらなかったのをもらってきた)、長年机のコヤシになっていたものを再発掘。

このCD自体はHFSフォーマットだったので、macOS 10.7搭載のオリジナルMacBook Airでマウントして中身を見ました。

New System Software Extensionフォルダに、AppleScriptが入っています。この数年前のCDから見ていましたが、この時点で初登場というわけではなく、2・3年前からずっと収録されていました。

目下、当時のSystem 7.xの環境は手元にないので(エミュレータぐらい?)これをインストールすることはできませんが、いろいろ入っていることがわかります。

このScripting Additionsフォルダにコマンドが入っています。当時はコマンド1つに1つのScripting Additionsが存在していたようなので、いまでは標準搭載のコマンドが別になっていました。

これだけでどうやってScriptを書いていいのかわからないぐらい素朴な命令群。Hypercardで書いたプログラムをScripting Additionsに変換できたようなので、割とHypercard由来のScripting Additionsが存在していました。

Posted in History | Leave a comment

国民の祝日を求める v6

Posted on 7月 5, 2024 by Takaaki Naganoya

日本のカレンダーにおける国民の祝日(休日)を、dateオブジェクトのリストで求めるAppleScriptです。

2018年に改元(平成→令和)、2020年に予定されていた東京オリンピックがCOVID-19の影響を受けて2021年に順延し、そのあたりで休日・祝日運用がイレギュラーになりまくって心が折れてアップデートしていませんでしたが、ようやく本ルーチンを最新の状況に合わせてアップデートしました。

2020〜2021年の計算については本ルーチンでは合わなくなる可能性がありますが、その点を理解したうえでご利用ください。一応、今年(2024年)の休日については、内閣府のホームページに掲載されているカレンダーと付け合わせを行なって、処理内容を確認してあります。

# 何か不具合や不足分があったらおしらせください
# 某・呪われた手帳メーカー系の仕事には不十分かもしれないので、不幸にもその案件に関わった方はメーカー支給の祝祭日データを利用してください

AppleScript名:国民の祝日を求める v6.scpt
use AppleScript
use scripting additions
use framework "Foundation"

(*

本バージョンのテーマ:
令和への改元にともない、天皇が交代。天皇誕生日を変更。祝日関連を最新アップデート
東京オリンピック(2021年)関連の超イレギュラーな休日調整は考慮していない

*)

set aList to retHolidayRec(2024) of me
–> {{date "2024年1月1日 月曜日 0:00:00", "元旦"}, {date "2024年1月8日 月曜日 0:00:00", "成人の日"}, {date "2024年2月11日 日曜日 0:00:00", "建国記念日"}, {date "2024年2月12日 月曜日 0:00:00", "振替休日"}, {date "2024年2月23日 金曜日 0:00:00", "天皇誕生日"}, {date "2024年3月20日 水曜日 0:00:00", "春分の日"}, {date "2024年5月3日 金曜日 0:00:00", "憲法記念日"}, {date "2024年5月4日 土曜日 0:00:00", "みどりの日"}, {date "2024年5月5日 日曜日 0:00:00", "こどもの日"}, {date "2024年5月6日 月曜日 0:00:00", "振替休日"}, {date "2024年7月15日 月曜日 0:00:00", "海の日"}, {date "2024年8月11日 日曜日 0:00:00", "山の日"}, {date "2024年8月12日 月曜日 0:00:00", "振替休日"}, {date "2024年9月16日 月曜日 0:00:00", "敬老の日"}, {date "2024年9月22日 日曜日 0:00:00", "秋分の日"}, {date "2024年9月23日 月曜日 0:00:00", "振替休日"}, {date "2024年10月14日 月曜日 0:00:00", "スポーツの日"}, {date "2024年11月3日 日曜日 0:00:00", "文化の日"}, {date "2024年11月4日 月曜日 0:00:00", "振替休日"}, {date "2024年11月23日 土曜日 0:00:00", "勤労感謝の日"}}

–国民の祝日を求める(属性つき)
on retHolidayRec(aYear as text)
  –固定の祝日
  
if aYear < 2020 then
    set holidayList to {{"1/1", "元旦"}, {"2/11", "建国記念日"}, {"4/29", "昭和の日"}, {"5/3", "憲法記念日"}, {"5/4", "みどりの日"}, {"5/5", "こどもの日"}, {"11/3", "文化の日"}, {"11/23", "勤労感謝の日"}}
  else
    –2020年から
    
set holidayList to {{"1/1", "元旦"}, {"2/11", "建国記念日"}, {"5/3", "憲法記念日"}, {"5/4", "みどりの日"}, {"5/5", "こどもの日"}, {"8/11", "山の日"}, {"11/3", "文化の日"}, {"11/23", "勤労感謝の日"}}
  end if
  
  
  
–天皇誕生日の計算
  
set curEmpBD to getCurEmpBitrthday(aYear) of me
  
if curEmpBD is not equal to false then
    set the end of holidayList to {curEmpBD, "天皇誕生日"}
  end if
  
  
set the end of holidayList to {get_specifiedDay(aYear, 1, 2, 2), "成人の日"} –成人の日–1月の第2月曜日
  
set the end of holidayList to {get_specifiedDay(aYear, 7, 2, 3), "海の日"} –海の日–7月の第3月曜日
  
set the end of holidayList to {get_specifiedDay(aYear, 9, 2, 3), "敬老の日"} –敬老の日–9月の第3月曜日
  
set the end of holidayList to {get_specifiedDay(aYear, 10, 2, 2), "スポーツの日"} –体育の日–10月の第2月曜日
  
set the end of holidayList to {"3/" & get_ShunbunNoHi(aYear), "春分の日"} –春分の日
  
set the end of holidayList to {"9/" & get_ShuubunNoHi(aYear), "秋分の日"} –秋分の日
  
  
  
set holiDate to {}
  
repeat with i in holidayList
    set holiD to date (aYear & "/" & (item 1 of i) as text)
    
set holiNum to weekday of holiD as number
    
    
–元日以外を対象とする(元旦に振替休日なし)–> いや、ある(汗)
    
–if ((item 1 of i) as text) is not "1/1" then
    
–振替休日付加処理
    
if holiNum = 1 then –祝祭日が日曜日だったら
      –日付を動かすのではなく、振替休日を追加する
      
set holiD_furikae to holiD + (1 * days)
      
set the end of holiDate to {holiD_furikae, "振替休日"}
    end if
    
–end if
    
set the end of holiDate to {holiD, item 2 of i}
    
  end repeat
  
  
  
–重複した休日が発生した場合の再振替処理  
  
–基本ルール:  振替休日を後に送る
  
–        「振替休日」が重複リストに入っていないかどうかをチェックし、振替休日の再配置を行う
  
set itemNum to 1
  
set holiDateDup to detectDuplicatesFromNestedList(holiDate, itemNum) of me
  
  
set huriList to {}
  
repeat with i in holiDateDup
    set iCount to length of i
    
repeat with ii in i
      set {aDate, aDateName} to ii
      
if aDateName = "振替休日" then
        set the end of huriList to contents of ii
      end if
    end repeat
  end repeat
  
  
set holiDate to shellSortListAscending(holiDate, 1) of me
  
set holiDateList to spritOrderedItemFromNestedList(holiDate, 1) of me
  
  
repeat with i in huriList
    set {aDate, aName} to i
    
set j to contents of i
    
set offsetDate to 1
    
repeat
      set bDate to aDate + (offsetDate * days)
      
if bDate is not in holiDateList then
        exit repeat
      end if
      
set offsetDate to offsetDate + 1
    end repeat
    
    
set iCount to 1
    
repeat with ii in holiDate
      set jj to contents of ii
      
if jj = j then
        –「複数要素一括削除サブルーチン」などという高機能すぎるサブルーチンを使用。ちょっともったいない
        
set holiDate to itemsDelete(holiDate, {iCount}) of me
      end if
      
set iCount to iCount + 1
    end repeat
    
    
set the end of holiDate to {bDate, "振替休日"}
    
  end repeat
  
  
  
–秋分の日と敬老の日の「間の日」の休日判定処理
  
–参考文献:
  
–http://ja.wikipedia.org/wiki/秋分の日
  
set septDL to {}
  
set the end of septDL to "9/" & get_ShuubunNoHi(aYear) of me –秋分の日
  
set the end of septDL to get_specifiedDay(aYear, 9, 2, 3) –敬老の日 –9月の第3月曜日
  
set septDL to shellSort(septDL) of me
  
if septDL = {"9/21", "9/23"} then
    set kokuminShukujitu to (aYear as string) & "/9/22"
    
set kokuminShukujitu to date kokuminShukujitu
    
set the end of holiDate to {kokuminShukujitu, "国民の祝日"}
  end if
  
  
–最後に、並べ替えを行って仕上げ
  
set holiDate to shellSortListAscending(holiDate, 1) of me
  
  
return holiDate
end retHolidayRec

–春分の日を求める
–2000年から2099年の間まで計算可能
on get_ShunbunNoHi(aYear)
  set a to 20.69115
  
set b to (aYear – 2000) * 0.2421904
  
set c to round ((aYear – 2000) / 4) rounding toward zero
  
set d to round (a + b – c) rounding toward zero
  
return d
end get_ShunbunNoHi

–秋分の日を求める
–2000年から2099年の間まで計算可能
on get_ShuubunNoHi(aYear)
  set a to 23.09
  
set b to (aYear – 2000) * 0.2421904
  
set c to round ((aYear – 2000) / 4) rounding toward zero
  
set d to round (a + b – c) rounding toward zero
  
return d
end get_ShuubunNoHi

–指定月の第x指定曜日に該当する日付を求める(mm/dd形式)
– 曜日の指定を数値(weekday of (current date) as number)で行えるようにした。
– 曜日を「日曜日」などの日本語ローカライズド文字列で指定するのをやめた
–パラメータ: 年, 月, 曜日番号, 順番
on get_specifiedDay(aYear as integer, aMonth as integer, Youbi as integer, orderNum as integer)
  set sDat to date ((aYear & "/" & aMonth & "/1") as text)
  
set eDat to getMlenInternational(aYear, aMonth) of me
  
  
set countNum to 0
  
  
repeat with i from 1 to eDat
    set aCal to date ((aYear & "/" & aMonth & "/" & (i as text)) as text)
    
set aWeekDayNum to weekday of aCal as integer
    
if Youbi = aWeekDayNum then
      set countNum to countNum + 1
      
if countNum is orderNum then
        set aCalText to (aMonth & "/" & i as text)
        
return aCalText
      end if
    end if
  end repeat
end get_specifiedDay

–指定日の月のみ返す
on getMonth(aDat as date)
  set bDate to month of aDat
  
return bDate as integer
end getMonth

–指定日の日付のみ返す
on getDate(aDat as date)
  set bDate to day of aDat
  
return bDate as integer
end getDate

–指定日の年のみ返す
on getYear(aDat as date)
  set bDate to year of aDat
  
return bDate as integer
end getYear

–現在のカレンダーで指定年月の日数を返す(getMlenから置き換えた)
on getMlenInternational(aYear, aMonth)
  set theNSCalendar to current application’s NSCalendar’s currentCalendar() — do *not* use initWithCalendarIdentifier:
  
set theDate to theNSCalendar’s dateWithEra:1 |year|:aYear |month|:aMonth |day|:1 hour:0 minute:0 |second|:0 nanosecond:0
  
set theResult to theNSCalendar’s rangeOfUnit:(current application’s NSDayCalendarUnit) inUnit:(current application’s NSMonthCalendarUnit) forDate:theDate
  
return |length| of theResult
end getMlenInternational

–リスト中から重複項目をリストアップする
on detectDuplicates(aList)
  set aCount to length of aList
  
  
set duplicationList to {}
  
repeat aCount times
    set anItem to contents of (first item of aList)
    
set aList to rest of aList
    
if anItem is in aList then
      set the end of duplicationList to anItem
    end if
  end repeat
  
  
return duplicationList
end detectDuplicates

–リストから重複部分を除外
on removeDuplicates(aList)
  set newList to {}
  
repeat with i from 1 to (length of aList)
    set anItem to item 1 of aList
    
set aList to rest of aList
    
if {anItem} is not in aList then set end of newList to anItem
  end repeat
  
return newList
end removeDuplicates

–シェルソート
on shellSort(aSortList)
  script oBj
    property list : aSortList
  end script
  
set len to count oBj’s list’s items
  
set gap to 1
  
repeat while (gap ≤ len)
    set gap to ((gap * 3) + 1)
  end repeat
  
repeat while (gap > 0)
    set gap to (gap div 3)
    
if (gap < len) then
      repeat with i from gap to (len – 1)
        set temp to oBj’s list’s item (i + 1)
        
set j to i
        
repeat while ((j ≥ gap) and (oBj’s list’s item (j – gap + 1) > temp))
          set oBj’s list’s item (j + 1) to oBj’s list’s item (j – gap + 1)
          
set j to j – gap
        end repeat
        
set oBj’s list’s item (j + 1) to temp
      end repeat
    end if
  end repeat
  
return oBj’s list
end shellSort

–シェルソートで入れ子のリストを昇順ソート
on shellSortListAscending(a, keyItem)
  set n to length of a
  
set cols to {1391376, 463792, 198768, 86961, 33936, 13776, 4592, 1968, 861, 336, 112, 48, 21, 7, 3, 1}
  
repeat with h in cols
    if (h ≤ (n – 1)) then
      repeat with i from h to (n – 1)
        set v to item (i + 1) of a
        
set j to i
        
repeat while (j ≥ h) and ((contents of item keyItem of item (j – h + 1) of a) > (item keyItem of v))
          set (item (j + 1) of a) to (item (j – h + 1) of a)
          
set j to j – h
        end repeat
        
set item (j + 1) of a to v
      end repeat
    end if
  end repeat
  
return a
end shellSortListAscending

–シェルソートで入れ子のリストを降順ソート
on shellSortListDecending(a, keyItem)
  set n to length of a
  
set cols to {1391376, 463792, 198768, 86961, 33936, 13776, 4592, 1968, 861, 336, 112, 48, 21, 7, 3, 1}
  
repeat with h in cols
    if (h ≤ (n – 1)) then
      repeat with i from h to (n – 1)
        set v to item (i + 1) of a
        
set j to i
        
repeat while (j ≥ h) and ((contents of item keyItem of item (j – h + 1) of a) < (item keyItem of v))
          set (item (j + 1) of a) to (item (j – h + 1) of a)
          
set j to j – h
        end repeat
        
set item (j + 1) of a to v
      end repeat
    end if
  end repeat
  
return a
end shellSortListDecending

–入れ子のリスト中から重複項目をアイテム番号つきでリストアップする
on detectDuplicatesFromNestedList(aList, itemNum)
  set aCount to length of aList
  
copy aList to orig_aList
  
  
set duplicationList to {}
  
repeat aCount times
    set anItem to contents of (first item of aList)
    
set aList to rest of aList
    
    
–指定アイテムだけのリストを毎回再生成して存在確認を行う
    
set aaList to spritOrderedItemFromNestedList(aList, itemNum) of me
    
if (contents of (item itemNum of anItem)) is in aaList then
      set the end of duplicationList to anItem
    end if
    
  end repeat
  
  
–検出した重複データを元に、該当するデータをリストアップ
  
set detectList to {}
  
repeat with i in duplicationList
    set j to contents of (item itemNum of i)
    
set detectItem to {}
    
repeat with ii in orig_aList
      set jj to contents of (item itemNum of ii)
      
      
if jj = j then
        set the end of detectItem to (contents of ii)
      end if
    end repeat
    
set the end of detectList to detectItem
  end repeat
  
  
return detectList
end detectDuplicatesFromNestedList

–入れ子のリストの全要素から指定アイテム目の要素だけを取り出してリストで返す
on spritOrderedItemFromNestedList(aList, itemNum)
  set aaList to {}
  
repeat with i in aList
    set the end of aaList to contents of (item itemNum of i)
  end repeat
  
return aaList
end spritOrderedItemFromNestedList

–リスト中の指定要素を削除して返す
on itemsDelete(aList, delNumList)
  set delLen to length of delNumList
  
  
repeat with i from 1 to delLen
    
    
set newList to {}
    
set aLen to length of aList
    
    
set ii to item i of delNumList
    
    
if ii = 1 then
      set maeList to items 2 thru aLen of aList
      
set newList to maeList
      
    else if ii = aLen then
      set maeList to items 1 thru (aLen – 1) of aList
      
set newList to maeList
      
    else
      set maeList to items 1 thru (ii – 1) of aList
      
set atoList to items (ii + 1) thru -1 of aList
      
set newList to maeList & atoList
    end if
    
    
–アイテム指定の補正
    
set delNumList to adjustItemNo(ii, delNumList) of me
    
    
set aList to newList
  end repeat
  
  
return newList
end itemsDelete

–itemsDeleteのサブルーチン
–リストに対して複数アイテムの削除を行う場合に、1つ削除した後にはアイテム指定が
–狂ってしまうため、毎回削除するたびにアイテム指定の補正を行う
–paramNumとelemListの間でのパラメータの衝突は関知しない

–項目要素補正をリストに対して行う
on adjustItemNo(paramNum, elemList)
  –項目ゼロを指定してきた場合には、そのままelemListを戻す
  
if paramNum = 0 then return elemList
  
–プラス方向のレンジ外判定は行っていない。elemListのlengthよりも大きな値は関知しない
  
set retList to {}
  
repeat with i in elemList
    set j to contents of i
    
if j > paramNum then
      set ansNum to j – 1
    else
      set ansNum to j
    end if
    
set the end of retList to ansNum
  end repeat
  
  
return retList
end adjustItemNo

–現在のカレンダーで指定年月のdate objectを返す(年、月、日、時、分、秒)
on getDateInternationalYMDhms(aYear, aMonth, aDay, anHour, aMinute, aSecond)
  set theNSCalendar to current application’s NSCalendar’s currentCalendar()
  
set theDate to theNSCalendar’s dateWithEra:1 |year|:aYear |month|:aMonth |day|:aDay hour:anHour minute:aMinute |second|:aSecond nanosecond:0
  
return theDate as date
end getDateInternationalYMDhms

–現在のカレンダーで指定年月のdate objectを返す(年、月、日)
on getDateInternational(aYear, aMonth, aDay)
  set theNSCalendar to current application’s NSCalendar’s currentCalendar()
  
set theDate to theNSCalendar’s dateWithEra:1 |year|:aYear |month|:aMonth |day|:aDay hour:0 minute:0 |second|:0 nanosecond:0
  
return theDate as date
end getDateInternational

–天皇誕生日の計算
on getCurEmpBitrthday(targYear)
  –昭和、平成、令和 の誕生日定義
  
set curEmperrorsBirthday to {{"4/29", {1926, 1988}}, {"12/23", {1989, 2018}}, {"2/23", {2020, 9999}}}
  
–浩宮氏が崩御の際には、崩御年を記入
  
  
set hitF to false
  
repeat with i in curEmperrorsBirthday
    copy i to {targDate, {beginYear, endYear}}
    
if targYear ≥ beginYear and targYear ≤ endYear then
      set hitF to true
      
exit repeat
    end if
  end repeat
  
  
if hitF = false then
    return false
  end if
  
  
return targDate
end getCurEmpBitrthday

★Click Here to Open This Script 

Posted in Calendar | Tagged 12.0savvy 13.0savvy 14.0savvy 15.0savvy | Leave a comment

相対年月を計算(月の相対指定)v4

Posted on 7月 5, 2024 by Takaaki Naganoya

現在日時からの相対月計算を行うAppleScriptです。

# 実戦投入してみたらマイナス月で不具合が出たので修正しました

カレンダー計算を行うのは、たいてい「日」単位です。

相対日では、今日(day ±0)、昨日(day -1)、一昨日(day -2)、翌一昨日(day -3)、明日(day +1)、明後日(day +2)、明々後日(day +3)など英語に翻訳できない、日本語特有の相対日指定の予約語を処理すべく、「日」ベースのカレンダー計算についてはやたらとAppleScriptで強力なルーチンを作りためてきました。

たまたま、「月」単位の相対計算を行う必要があった(ストック分に存在していなかった)ので、作ることになりました。たいがいの基礎ルーチンは作り置きがあるのですが、本当にこうして基礎ルーチンを作ったのは久しぶりです。

本当は固定で「現在月-2」の計算だけができればよかったのですが、±100か月ぐらいの範囲で動作確認したルーチンを作り置きしておきたいところです。気乗りしなかったので、ChatGPTに問い合わせてみたら、monthを秒で表現するタイプのルーチンを提案してきて、しかも内容が間違っていたので(構文確認を通過しないような内容を提案してくださります)、しぶしぶ作ってみました。ChatGPTが提案してきた作り方だと、おおよそ概算で500か月ぐらいの計算を行うと月単位の誤差が出てしまいます。

ChatGPTが「AppleScriptでrepeatループに0は指定できないよ?」とかの嘘を山のように出力してくるので、本当に初心者が使うと目が回るはずです(配列の要素が1から始まるのを「repeatで指定できない」という謎の過学習を発生させたもよう。ユーザーが間違ったフィードバックをやらかしている???)。

幾度かのアップデートを経て、完成しました。完成したといってよいのか? なんか無駄な処理が異様に多いような気もするのですが、結果が正しいのでよしとしましょう。

Cocoaの自然言語系の処理を用いて楽をして相対日付を処理しようかと試してみたものの、「本日」をtodayとしてピックアップしてしまうので、「本日から2か月前」といった認識ができませんでした。「現在のカレンダー」云々というのはCocoaの機能を使って処理しようとしていた頃のなごりです。

AppleScript名:相対年月を計算(月の相対指定)v4.scpt
use AppleScript
use scripting additions
use framework "Foundation"

repeat with i from 1 to 12
  set curDate to getDateInternational(2024, i, 1) of me
  
log curDate
  
set {yNum1, mNum1} to retYMbyRelativeMonth(curDate, -2) of me
  
log {yNum1, mNum1}
end repeat

on retYMbyRelativeMonth(curDate, relativeMonth)
  set yNum to (year of curDate) as number
  
set mNum to (month of curDate) as integer
  
set dNum to day of curDate
  
  
if relativeMonth = 0 then return {yNum, mNum}
  
  
set mDiff to (mNum + relativeMonth)
  
  
if mDiff ≤ 0 then
    –log {"case 1"}
    
set yDiv to (mDiff) div 12
    
set mNum to 12 + ((mDiff) mod 12)
    
set yNum to yNum + (yDiv) – 1
    
  else if mDiff > 12 then
    –log {"case 2"}
    
set yDiv to (mDiff – 1) div 12
    
set mNum to ((mDiff – 1) mod 12) + 1
    
set yNum to yNum + yDiv
    
  else
    –log {"case 3"}
    
set mNum to mDiff
  end if
  
  
return {yNum, mNum}
end retYMbyRelativeMonth

–現在のカレンダーで指定年月のdate objectを返す
on getDateInternational(aYear, aMonth, aDay)
  set theNSCalendar to current application’s NSCalendar’s currentCalendar()
  
set theDate to theNSCalendar’s dateWithEra:1 |year|:aYear |month|:aMonth |day|:aDay hour:0 minute:0 |second|:0 nanosecond:0
  
return theDate as date
end getDateInternational

★Click Here to Open This Script 

Posted in Calendar | Tagged 12.0savvy 13.0savvy 14.0savvy 15.0savvy | Leave a comment

Skimで現在表示中のPDFで、現在表示中のページ以降を削除

Posted on 7月 2, 2024 by Takaaki Naganoya

Skimでオープン中のPDFに対して、現在表示中のページ以降をすべて削除するAppleScriptです。

もともとは、macOS 10.13以前のバージョンのmacOSで、BridgePlusを呼び出すようにしてあったものですが、スクリプトメニューからBridgePlusを呼び出せなくなって、BridgePlus呼び出し部分を別のサブルーチンに置き換えてみたものです。

# 一応、Script Debuggerから拡張アプレットで書き出せば、スクリプトメニューに入れたScript(アプレット?)からBridgePlusの呼び出し自体は行えます。

もともと、SkimにはPDFのページ削除の機能が存在していないので、情報取得後にPDFをいったんクローズ、AppleScriptだけでPDFKitの機能を呼び出して削除処理を行い、再度Skimでオープンするという処理を行なっています。

現在表示中のページから前を削除するScriptも書いて使っていますが、余計に処理に時間がかかります。

SkimのScript本を提案したこともあったのですが、開発チームのとくに誰が許可を出すというわけでもなく、そのまま時間が流れてしまいました。Skimの機能範囲だけだと実現できることに制限がありますが、AppleScriptからPDFKitの機能を呼び出すと、Skim自体で持っていない機能も実装できて便利です。

そういうものを目指していたのですが、いまひとつテンションが上がらずそのまま放置状態に。

AppleScript名:現在表示中のページ以降を削除.scptd
— Created 2018-06-30 by Takaaki Naganoya
— Modified 2024-07-01 by Takaaki Naganoya
— 2018–2024 Piyomaru Software
use AppleScript version "2.8"
use scripting additions
use framework "Foundation"
use framework "AppKit"
use framework "PDFKit"

property NSSet : a reference to current application’s NSSet
property |NSURL| : a reference to current application’s |NSURL|
property NSArray : a reference to current application’s NSArray
property NSString : a reference to current application’s NSString
property NSImage : a reference to current application’s NSImage
property PDFDocument : a reference to current application’s PDFDocument
property NSSortDescriptor : a reference to current application’s NSSortDescriptor

tell application "Skim"
  set docCount to count every document
  
if docCount = 0 then return
  
  
tell front document
    set curInd to index of current page
    
set docFile to file of it
    
close without saving
  end tell
end tell

set aPOSIX to POSIX path of docFile
set aURL to (|NSURL|’s fileURLWithPath:aPOSIX)

set aPDFdoc to PDFDocument’s alloc()’s initWithURL:aURL
set pCount to aPDFdoc’s pageCount()

set pRes to removePDFPageAfter(docFile, curInd) of me

tell application "Skim"
  open docFile
  
tell front document
    go to last page
  end tell
end tell

–指定パスのPDFの指定ページ以降(指定ページも含む)をすべて削除
on removePDFPageAfter(inFile, sPage)
  set pCount to pdfPageCount(inFile) of me
  
set eCount to pCount – sPage + 1
  
  
set targPageList to makeSuccessfulNumArray(sPage, pCount) of me
  
set rList to reverse of targPageList
  
return removeSpecificPagesFromPDF(inFile, rList) of me
end removePDFPageAfter

–指定PDF書類の複数ページの一括削除
on removeSpecificPagesFromPDF(inFileAlias, targPageNumList as list)
  set inNSURL to |NSURL|’s fileURLWithPath:(POSIX path of inFileAlias)
  
set theDoc to PDFDocument’s alloc()’s initWithURL:inNSURL
  
  
–削除対象ページリストをユニーク化して降順ソート(後方から削除)
  
set pRes to theDoc’s pageCount()
  
set t3List to relativeToAbsNumList(targPageNumList, pRes) of me
  
  
repeat with i in t3List
    copy i to targPageNum
    (
theDoc’s removePageAtIndex:(targPageNum – 1))
  end repeat
  
  
–Overwrite Exsiting PDF
  
set aRes to (theDoc’s writeToURL:inNSURL) as boolean
  
  
return aRes
end removeSpecificPagesFromPDF

–絶対ページと相対ページが混在した削除対象ページリストを絶対ページに変換して重複削除して降順ソート
on relativeToAbsNumList(aList, aMax)
  set newList to {}
  
  
repeat with i in aList
    set j to contents of i
    
if i < 0 then
      set j to aMax + j
    end if
    
    
if (j ≤ aMax) and (j is not equal to 0) then
      set the end of newList to j
    end if
  end repeat
  
  
set t1List to uniquify1DList(newList, true) of me
  
set t2List to sort1DNumListWithOrder(t1List, false) of me
  
  
return t2List
end relativeToAbsNumList

–1D/2D Listをユニーク化
on uniquify1DList(theList as list, aBool as boolean)
  set aArray to NSArray’s arrayWithArray:theList
  
set bArray to aArray’s valueForKeyPath:"@distinctUnionOfObjects.self"
  
return bArray as list
end uniquify1DList

–Sort 1-Dimension List(String Number List)
on sort1DNumListWithOrder(theList as list, aBool as boolean)
  tell NSSet to set theSet to setWithArray_(theList)
  
tell NSSortDescriptor to set theDescriptor to sortDescriptorWithKey_ascending_("floatValue", aBool)
  
set sortedList to theSet’s sortedArrayUsingDescriptors:{theDescriptor}
  
return (sortedList) as list
end sort1DNumListWithOrder

–指定PDFのページ数をかぞえる
on pdfPageCount(aFile)
  set aFile to POSIX path of aFile
  
set theURL to |NSURL|’s fileURLWithPath:aFile
  
set aPDFdoc to PDFDocument’s alloc()’s initWithURL:theURL
  
set aRes to aPDFdoc’s pageCount()
  
return aRes as integer
end pdfPageCount

–連番1D Listを作成
on makeSuccessfulNumArray(sNum, eNum)
  script spd
    property aList : {}
  end script
  
  
if sNum ≥ eNum then return {}
  
  
set (aList of spd) to {}
  
  
repeat with i from sNum to eNum
    set the end of (aList of spd) to i
  end repeat
  
  
return (aList of spd)
end makeSuccessfulNumArray

★Click Here to Open This Script 

Posted in PDF | Tagged 12.0savvy 13.0savvy 14.0savvy Skim | Leave a comment

開発機としてM2 Mac miniが来たのでガチレビュー

Posted on 7月 2, 2024 by Takaaki Naganoya

目下、AppleScriptで組んだ割と大きな規模(中規模ぐらい?)のシステムのアップデート開発依頼を受けて、開発機としてM2 Mac mini(RAM 8GB)が手元に来ています(開発機なので、終わったら返却しますが……)。

このため、M1 Mac mini(macOS 13.6.7)、M2 Mac mini(macOS 14.5)、そしてオリジナルのシステムが動いているIntel Mac mini(2014)(macOS 10.12.6)の3台が積み上がっている状態です。さして場所をとっていませんが……。

# 新規導入したMacBook Air M2 15インチ(RAM 16GB)の結果も追記しています

M2 Mac miniがどの程度M1 Mac miniからスピードアップしているのか、についてはこれまで比較する機会がありませんでした。店頭でちょっとさわって試すぐらいだと、単純ループ10億回とかの「実用性皆無」な内容でチェックするほかありませんでした。

いまのコンピュータは速くなっている箇所とそうでもない箇所の「差」が大きくなっていて、特定分野の処理だけやたら速くなっている(あるいはその逆も)という現象をよく見かけます。

そこで、いろんな分野のAppleScriptで実行速度を比較してみました。

■Pages書類の全書き出しとソート+連結

実際の、本気で業務をまるごとこなすようなAppleScriptのシステムを動かすと、どうなるんでしょうか?

たとえば指定フォルダ以下のPages書類をSpotlight検索ですべてピックアップして個別にPDF書き出しを行い、ファイル名順に連結するようなAppleScriptがあります。

ちょうど、いま作っている書籍「Pages+AppleScriptで本を作ろう!」が現段階で320ページなので、テスト用には十分な規模でしょう。

これを処理すると、

  M1 Mac mini(RAM 16GB):2分35秒
  M2 Mac mini(RAM 8GB):2分30秒

と、M1よりは若干速いものの、騒ぐほどではないぐらいの差でした。誤差範囲内といってよいでしょう。数回書き出しを行なって落ち着いた結果を採用しています(初回はどうもキャッシュせずに遅くなるようで)。

■8,000箇所の駅×700箇所の位置情報の距離計算とソート

日本全国の鉄道駅8,000箇所と全国700箇所(当時)のゲームセンター施設の距離計算を行い、「最寄駅が一番遠いゲームセンター」を計算するAppleScriptを試してみました。このぐらいの規模の処理なら、割と現実的なシステムといえるでしょう。

  M1 Mac mini(RAM 16GB):1分 31秒
  M2 Mac mini(RAM 8GB):1分 23秒
  M2 MacBook Air(RAM 16GB):1分 28秒

数回実行してみましたが、ほぼ同じ結果になりました。9%ぐらいM2が高速です。さきほどの結果よりも性能向上幅が大きくなっています。

■指定桁の順列組み合わせ計算(Permutation)

このあたりから、様子がおかしくなってきます。結果は結果として受け取らざるを得ないのですが、振れ幅がおかしい。9桁のデータの順列組み合わせ計算で、

  M1 Mac mini(RAM 16GB):62.7379秒
  M2 Mac mini(RAM 8GB):30.564秒
  M2 MacBook Air(RAM 16GB):44.905秒

M1→M2 Mac miniで約2倍。

■Numbersの選択中のセル(1000×5)への値の設定

GUIアプリであるNumbersの表の選択範囲(1,000行×5列)に対して、連続する値を書き込むテストです。Numbersは表からの値の取得が高速である反面、値の書き込みについては信じられないほど遅くなるという特性を持っています。この、Numbersの表データ設定はさまざまな「回避技」を生むことにもなっています。

ひとえに、このNumbersへの書き込み処理に時間がかかるためです。これを比較すると、

  M1 Mac mini(RAM 16GB):96.3056秒
  M2 Mac mini(RAM 8GB):35.1062秒
  M2 MacBook Air(RAM 16GB):43.649秒

おおよそ、M2が2.7倍速いという結果になりました。意味がわかりません。

一応、AppleScriptのバースト実行モード=アプリ側の応答を待たずに値設定を行う「ignoring application responses」指定で実行すると、

  M1 Mac mini(RAM 16GB):2.76秒
  M2 Mac mini(RAM 8GB):2.42秒
  M2 MacBook Air(RAM 16GB):0.77秒??????

と、処理時間の短縮が頭がおかしいレベル(通常は指定前の33%ぐらいの時間になる)であるものの、CPU間の差は10%程度と、納得できるレベルに落ち着きます。

ちなみに、Numbers側はAppleScriptが処理を終了したあとも処理を継続し、当初と同じぐらいの時間で処理を終えました。

■画像の空白検出

上位機種でトンでもなく(遅い)結果が出たりと、さまざまな場所で物議をかもしている、画像の空白検出処理。

処理速度を測る場合には、他のプログラムをなるべく起動しないとか、いろいろ条件を揃えてあげる必要があるわけですが、今回も波乱の結果を出すものでしょうか。慎重を期して、10回実行して平均値を取っています。単位は秒で、数値が小さく、バーが短いほうが高速です。

1×1の画像処理については、データが小さすぎて傾向をつかむのには適していないようですが、4Kや8Kの画像を扱うとM1より遅い処理を見かけます。

グラフにすると、

こんな感じで……M2のほうがわずかに遅い処理が散見されます。正直、このベンチマークは4Kとか8KとかフルHDの画像の1つの点だけ点灯しているものを検出しろというもので、実用性はあるものの、ベンチマークとしては特殊な内容といえるでしょう。

■総評

ときおり、目が覚めるような処理速度を叩き出すことがあると思えば、日常的に動かしている処理だと5〜10%程度の処理速度向上におさまっています。全体的には、ほぼこのような評価になることでしょう。

同じく16GBのRAMを搭載しているマシンであるとか、上位機種のM2 Proを搭載したマシンではどうなのかといった検証を行いたかったところです(手元にないのでテストできないのですが)。

ただ、決してM2 miniはM1より遅いということはありませんし、M1からの買い替えという話は同意できないものの、悪くないマシンだと思います。

Posted in Benchmark | Tagged 13.0savvy 14.0savvy | Leave a comment

メキシカンハットの描画

Posted on 6月 28, 2024 by Takaaki Naganoya

X(旧称Twitter)の一部で流行っている、むかーーしむかしのBASIC処理サンプルの1つ、メキシカンハット型グラフの描画AppleScriptです。

–> Download full script bundle with libraries

自分もChipmunk BASIC用に移植して投稿していたのですが、AppleScriptにフル書き換えするとどんなもんだと思い、書いてみました。

関数演算ライブラリは、自作のcalcLibASがあるので、これを使い、画像のダイアログ表示用ライブラリを使って画面表示。

AppleScriptでも1秒ぐらいで処理が終了してしまいますが、黒地に緑で描画すると、謎の「味」があります。

AppleScript名:メキシカンハット v2.scptd
— Created 2024-06-28 by Takaaki Naganoya
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "AppKit"
use mLib : script "calcLibAS"
use imgDispLib : script "imageDisplayLib"

property NSColor : a reference to current application’s NSColor
property NSString : a reference to current application’s NSString
property NSImage : a reference to current application’s NSImage
property NSScreen : a reference to current application’s NSScreen
property NSBezierPath : a reference to current application’s NSBezierPath
property NSBitmapImageRep : a reference to current application’s NSBitmapImageRep

–パラメータから下地になる画像を作成する
set aSize to current application’s NSMakeSize(300, 300)
set anImage to NSImage’s alloc()’s initWithSize:aSize
set backlColor to (NSColor’s colorWithCalibratedRed:0.0 green:0.0 blue:0.0 alpha:1.0)
set anImage to drawImageWithColorFill(anImage, {0, 0, 300, 300}, backlColor) of me

–描画色
set drawColor to (NSColor’s colorWithCalibratedRed:0.0 green:1.0 blue:0.0 alpha:1.0)

–Mexican Hat
set D to {}
set rDnum to pi / 180

repeat 256 times
  set end of D to 100
end repeat

repeat with y from -180 to 180 by 6
  repeat with x from -180 to 180 by 4
    set u to 120 + (x / 3) – (y / 6)
    
if u < 0 or u ≥ 240 then
      —
    else
      set r to rDnum * (sqrt ((x ^ 2) + (y ^ 2)))
      
set z to 100 * (cos (r – 30)) * (cos (3 * r))
      
set v to 40 – y / 6 – z / 2
      
      
if (item u of D) > v then
        set anImage to psetOnImage(anImage, {u, 256 – v}, drawColor) of me
        
copy v to (item u of D)
      end if
    end if
  end repeat
end repeat

–結果表示
dispImage(anImage, "Mexican Hat") of imgDispLib

on psetOnImage(anImage, posDat, fillColor)
  set retinaF to (NSScreen’s mainScreen()’s backingScaleFactor()) as real
  
–>  2.0 (Retina) / 1.0 (Non Retina)
  
  
anImage’s lockFocus() –描画開始
  
  
set origX to (item 1 of posDat) / retinaF
  
set origY to (item 2 of posDat) / retinaF
  
set sizeX to (1) / retinaF
  
set sizeY to (1) / retinaF
  
  
set theRect to {{x:origX, y:origY}, {width:sizeX, height:sizeY}}
  
  
set theNSBezierPath to NSBezierPath’s bezierPath
  (
theNSBezierPath’s appendBezierPathWithRect:theRect)
  
  
fillColor’s |set|() –色設定
  
theNSBezierPath’s fill() –ぬりつぶし
  
  
anImage’s unlockFocus() –描画ここまで
  
  
return anImage –returns NSImage
end psetOnImage

on drawImageWithColorFill(anImage, drawList, fillColor)
  set retinaF to (NSScreen’s mainScreen()’s backingScaleFactor()) as real
  
–>  2.0 (Retina) / 1.0 (Non Retina)
  
  
anImage’s lockFocus() –描画開始
  
  
set origX to (item 1 of drawList) / retinaF
  
set origY to (item 2 of drawList) / retinaF
  
set sizeX to (item 3 of drawList) / retinaF
  
set sizeY to (item 4 of drawList) / retinaF
  
  
set theRect to {{x:origX, y:origY}, {width:sizeX, height:sizeY}}
  
  
set theNSBezierPath to NSBezierPath’s bezierPath
  (
theNSBezierPath’s appendBezierPathWithRect:theRect)
  
  
fillColor’s |set|() –色設定
  
theNSBezierPath’s fill() –ぬりつぶし
  
  
anImage’s unlockFocus() –描画ここまで
  
  
return anImage –returns NSImage
end drawImageWithColorFill

★Click Here to Open This Script 

Posted in 3D dialog Image | Tagged 13.0savvy 14.0savvy 15.0savvy | Leave a comment

Post navigation

  • Older posts
  • Newer posts

電子書籍(PDF)をオンラインストアで販売中!

Google Search

Popular posts

  • Numbersで選択範囲のセルの前後の空白を削除
  • macOS 26, Tahoe
  • macOS 15でも変化したText to Speech環境
  • KagiのWebブラウザ、Orion
  • Script Debuggerの開発と販売が2025年に終了
  • 【続報】macOS 15.5で特定ファイル名パターンのfileをaliasにcastすると100%クラッシュするバグ
  • macOS 15 リモートApple Eventsにバグ?
  • NSObjectのクラス名を取得 v2.1
  • 2024年に書いた価値あるAppleScript
  • 有害ではなくなっていたSpaces
  • macOS 15:スクリプトエディタのAppleScript用語辞書を確認できない
  • Xcode上のAppleScriptObjCのプログラムから、Xcodeのログ欄へのメッセージ出力を実行
  • (確認中)AppleScript Dropletのバグっぽい動作が解消?
  • AVSpeechSynthesizerで読み上げテスト
  • AppleScript Dropletのバグっぽい動作が「復活」(macOS 15.5β)
  • 指定フォルダ以下の画像のMD5チェックサムを求めて、重複しているものをピックアップ
  • macOS 26, 15.5でShortcuts.app「AppleScriptを実行」アクションのバグが修正される
  • Numbersで選択中の2列のセルを比較して並べ直して書き戻す v2
  • Apple、macOS標準搭載アプリ「写真」のバージョン表記を間違える
  • Script DebuggerがmacOS 15.x上で起動せず→起動

Tags

10.11savvy (1101) 10.12savvy (1242) 10.13savvy (1391) 10.14savvy (587) 10.15savvy (438) 11.0savvy (283) 12.0savvy (212) 13.0savvy (204) 14.0savvy (159) 15.0savvy (155) CotEditor (66) Finder (52) Keynote (119) NSAlert (61) NSArray (51) NSBitmapImageRep (20) NSBundle (20) NSButton (34) NSColor (53) NSDictionary (28) NSFileManager (23) NSFont (21) NSImage (41) NSJSONSerialization (21) NSMutableArray (63) NSMutableDictionary (22) NSPredicate (36) NSRunningApplication (56) NSScreen (30) NSScrollView (22) NSString (119) NSURL (98) NSURLRequest (23) NSUTF8StringEncoding (30) NSView (33) NSWorkspace (20) Numbers (76) Pages (56) Pixelmator Pro (20) Safari (44) Script Editor (27) WKUserContentController (21) WKUserScript (20) WKWebView (23) WKWebViewConfiguration (22)

カテゴリー

  • 2D Bin Packing
  • 3D
  • AirDrop
  • AirPlay
  • Animation
  • AppleScript Application on Xcode
  • Beginner
  • Benchmark
  • beta
  • Bluetooth
  • Books
  • boolean
  • bounds
  • Bug
  • Calendar
  • call by reference
  • check sum
  • Clipboard
  • Cocoa-AppleScript Applet
  • Code Sign
  • Color
  • Custom Class
  • date
  • dialog
  • diff
  • drive
  • Droplet
  • exif
  • file
  • File path
  • filter
  • folder
  • Font
  • Font
  • GAME
  • geolocation
  • GUI
  • GUI Scripting
  • Hex
  • History
  • How To
  • iCloud
  • Icon
  • Image
  • Input Method
  • Internet
  • iOS App
  • JavaScript
  • JSON
  • JXA
  • Keychain
  • Keychain
  • Language
  • Library
  • list
  • Locale
  • Localize
  • Machine Learning
  • Map
  • Markdown
  • Menu
  • Metadata
  • MIDI
  • MIME
  • Natural Language Processing
  • Network
  • news
  • Newt On Project
  • Noification
  • Notarization
  • Number
  • Object control
  • OCR
  • OSA
  • parallel processing
  • PDF
  • Peripheral
  • process
  • PRODUCTS
  • QR Code
  • Raw AppleEvent Code
  • Record
  • rectangle
  • recursive call
  • regexp
  • Release
  • Remote Control
  • Require Control-Command-R to run
  • REST API
  • Review
  • RTF
  • Sandbox
  • Screen Saver
  • Script Libraries
  • Scripting Additions
  • sdef
  • search
  • Security
  • selection
  • shell script
  • Shortcuts Workflow
  • Sort
  • Sound
  • Spellchecker
  • Spotlight
  • SVG
  • System
  • Tag
  • Telephony
  • Text
  • Text to Speech
  • timezone
  • Tools
  • Update
  • URL
  • UTI
  • Web Contents Control
  • WiFi
  • XML
  • XML-RPC
  • イベント(Event)
  • 未分類

アーカイブ

  • 2025年10月
  • 2025年9月
  • 2025年8月
  • 2025年7月
  • 2025年6月
  • 2025年5月
  • 2025年4月
  • 2025年3月
  • 2025年2月
  • 2025年1月
  • 2024年12月
  • 2024年11月
  • 2024年10月
  • 2024年9月
  • 2024年8月
  • 2024年7月
  • 2024年6月
  • 2024年5月
  • 2024年4月
  • 2024年3月
  • 2024年2月
  • 2024年1月
  • 2023年12月
  • 2023年11月
  • 2023年10月
  • 2023年9月
  • 2023年8月
  • 2023年7月
  • 2023年6月
  • 2023年5月
  • 2023年4月
  • 2023年3月
  • 2023年2月
  • 2023年1月
  • 2022年12月
  • 2022年11月
  • 2022年10月
  • 2022年9月
  • 2022年8月
  • 2022年7月
  • 2022年6月
  • 2022年5月
  • 2022年4月
  • 2022年3月
  • 2022年2月
  • 2022年1月
  • 2021年12月
  • 2021年11月
  • 2021年10月
  • 2021年9月
  • 2021年8月
  • 2021年7月
  • 2021年6月
  • 2021年5月
  • 2021年4月
  • 2021年3月
  • 2021年2月
  • 2021年1月
  • 2020年12月
  • 2020年11月
  • 2020年10月
  • 2020年9月
  • 2020年8月
  • 2020年7月
  • 2020年6月
  • 2020年5月
  • 2020年4月
  • 2020年3月
  • 2020年2月
  • 2020年1月
  • 2019年12月
  • 2019年11月
  • 2019年10月
  • 2019年9月
  • 2019年8月
  • 2019年7月
  • 2019年6月
  • 2019年5月
  • 2019年4月
  • 2019年3月
  • 2019年2月
  • 2019年1月
  • 2018年12月
  • 2018年11月
  • 2018年10月
  • 2018年9月
  • 2018年8月
  • 2018年7月
  • 2018年6月
  • 2018年5月
  • 2018年4月
  • 2018年3月
  • 2018年2月

https://piyomarusoft.booth.pm/items/301502

メタ情報

  • ログイン
  • 投稿フィード
  • コメントフィード
  • WordPress.org

Forum Posts

  • 人気のトピック
  • 返信がないトピック

メタ情報

  • ログイン
  • 投稿フィード
  • コメントフィード
  • WordPress.org
Proudly powered by WordPress
Theme: Flint by Star Verte LLC