Skip to content

Instantly share code, notes, and snippets.

View banjun's full-sized avatar

banjun banjun

View GitHub Profile
@banjun
banjun / TimerOnMainActor.swift
Last active September 13, 2024 09:09
Timer.scheduledTimer on MainActor
import Foundation
extension Timer {
@MainActor @discardableResult static func scheduledTimerOnMainRunLoop(withTimeInterval: TimeInterval, repeats: Bool, block: @escaping @MainActor (Timer) -> Void) -> Timer {
// The doc says: Creates and returns a new NSTimer object initialized with the specified block object and schedules it on the current run loop
// But the block is not annotated with @MainActor and we need MainActor.assumeIsolated
// Since Timer is non-Sendable, capture by nonisolated(unsafe) to pass from nonisolated context to MainActor.
Timer.scheduledTimer(withTimeInterval: withTimeInterval, repeats: repeats) { timer in
nonisolated(unsafe) let timer: Timer = timer
MainActor.assumeIsolated {
@banjun
banjun / generatePlaneMesh.swift
Last active August 2, 2024 09:55
Create Plane USDA
import simd
func generatePlaneMesh(lengthInMeter: Float = 1, divisions: Int = 100, name: String = "GeneratedPlane") {
let n = divisions
let xys: [(Int, Int)] = (0..<n).flatMap { y in (0..<n).map { x in (x, y) }}
let points: [SIMD3<Float>] = xys.map { x, y in
SIMD3(Float(x) / Float(n - 1) - 0.5, Float(y) / Float(n - 1) - 0.5, 0) * lengthInMeter
}
let faceVertexIndices: [Int] = xys.flatMap { x, y in
guard x < n - 1, y < n - 1 else { return [Int]() }
@banjun
banjun / MultiColumnText.swift
Last active May 2, 2024 07:53
SwiftUI multi column text backed by TextKit 1 multi column layout
struct MultiColumnText: View {
private let storage: NSTextStorage
private let layoutManager: NSLayoutManager = .init()
private let containers: [NSTextContainer]
init(text: NSAttributedString, columns: Int) {
storage = .init(attributedString: text)
storage.addLayoutManager(layoutManager)
containers = (0..<columns).map {_ in .init()}
containers.forEach {
@banjun
banjun / Inwardbox.swift
Created April 16, 2024 05:19
for visionOS gestures, generate a box with its surfaces facing inward
import RealityKit
extension ShapeResource {
/// generate a box with its surfaces facing inward.
/// NOTE: size 30 works with interactions, 50 does not work with interactions
static func generateInwardBox(size: Float) async throws -> ShapeResource {
// An array of vertex positions containing discrete points on the mesh.
let meshPositions: [SIMD3<Float>] = [
.init(-1, -1, -1), // 0 LBF
.init(+1, -1, -1), // 1 RBF
@banjun
banjun / UserActivityWindowGroup.swift
Last active March 19, 2024 16:49
Open new window on drop UserActivity
import SwiftUI
// prerequisites in Info.plist: NSUserActivityTypes contains type, UIApplicationSceneManifest/UIApplicationSupportsMultipleScenes = YES
// accepts NSUserActivity.targetContentIdentifier = type
// see also: https://developer.apple.com/documentation/swiftui/scene/handlesexternalevents(matching:)
struct UserActivityWindowGroup<Content: View, Payload: Codable>: Scene {
var type: String
@ViewBuilder var content: (Payload) -> Content
init(type: String, payloadType: Payload.Type, @ViewBuilder content: @escaping (Payload) -> Content) {
@banjun
banjun / convert-lib-fw-to-sim-device-universal-xcfw.sh
Last active October 30, 2023 07:28
create an xcframework from a universal static lib .a or universal static .framework without rebuild
#!/bin/zsh
if [[ -z $BOGO_ARM64_TO_SIM ]]; then
echo "❌ requires BOGO_ARM64_TO_SIM: set an executable binary path built from https://github.com/bogo/arm64-to-sim"
exit 1;
fi
set -eu
# set -x # debug log
@banjun
banjun / ffmpeg.rb
Created November 21, 2020 11:38
brew formula diff for arm64 M1 -- Library/Taps/homebrew/homebrew-core/Formula/ffmpeg.rb
@@ -41,18 +41,18 @@
depends_on "gnutls"
depends_on "lame"
depends_on "libass"
- depends_on "libbluray"
- depends_on "libsoxr"
+ #depends_on "libbluray"
+ #depends_on "libsoxr"
depends_on "libvidstab"
depends_on "libvorbis"
@banjun
banjun / BlackHoleAVCaptureAudioPreviewOutput.swift
Created April 18, 2020 17:50
Set AVCaptureSession output to BlackHole or another arbitrary device by an output device name
private let audioOutput: AVCaptureAudioPreviewOutput = {
let audioOutput = AVCaptureAudioPreviewOutput()
audioOutput.volume = UserDefaults.standard.volume
struct Device {
var name: String?
var uid: String?
}
var devicesProperty = AudioObjectPropertyAddress(mSelector: kAudioHardwarePropertyDevices, mScope: kAudioDevicePropertyScopeOutput, mElement: kAudioObjectPropertyElementWildcard)
var devicesSize: UInt32 = 0
@banjun
banjun / sample.rq
Last active January 2, 2020 07:03 — forked from takanakahiko/sample.rq
そらみスマイルのライブ一覧
PREFIX prism: <https://prismdb.takanakahiko.me/prism-schema.ttl#>
SELECT ?n ?st ?title
WHERE {
?live a prism:Live;
prism:performer/prism:name_kana "そらみすまいる";
prism:songPerformed/prism:name ?title;
prism:liveOfEpisode ?ep.
?ep a prism:Episode;
prism:話数 ?n;
@banjun
banjun / generate-prismdb-characters.jisyo.md
Created December 28, 2019 10:33
generate-prismdb-characters.jisyo.md

oneliner

curl 'https://prismdb.takanakahiko.me/sparql?default-graph-uri=&query=PREFIX+prism%3A+%3Chttps%3A%2F%2Fprismdb.takanakahiko.me%2Fprism-schema.ttl%23%3E%0D%0A%0D%0ASELECT+concat%28%22%5Cn%3B%3B+okuri-ari+entries.%5Cn%3B%3B+okuri-nasi+entries.%5Cn%5Cn%22%2C%0D%0Agroup_concat%28concat%28replace%28%3Fckana%2C+%22+%22%2C+%22%22%29%2C+%22+%2F%22%2C+replace%28%3Fcname%2C+%22+%22%2C+%22%22%29%2C+%22%3B%22%2C+%3Fcclass%2C+if%28bound%28%3Fccv%29%2C+concat%28%22+CV%3A%22%2C+%3Fccv%29%2C+%22%22%29%2C+%22%2F%22%29%3B+separator%3D%22%5Cn%22%29%29%0D%0AWHERE+%7B%0D%0A++++%3Fc+a+prism%3ACharacter%3B%0D%0A++++++++prism%3Aname+%3Fcname%3B%0D%0A++++++++prism%3Aname_kana+%3Fckana%3B%0D%0A++++++++a%2Frdfs%3Alabel+%3Fcclass.%0D%0A++++optional+%7B%3Fc+prism%3Acv+%3Fccv.%7D%0D%0A++++bind%28%22%22+as+%3Fg%29%0D%0A%7D%0D%0Agroup+by+%3Fg&format=text%2Fcsv' | tail +3 | LC_ALL=C sort > prismdb-characters.jisyo

SPARQL