スケルトン アニメーション (パート 1 – シェーダー)

この記事では、キャラクターやゲーム環境などをアニメーション化するために最新のすべての 3D エンジンで使用されているスケルトン アニメーションについての私の理解を説明します。
最も具体的な部分から説明を始めます –なぜなら、計算パス全体は、それがどれほど複雑であっても、表示用に準備されたデータを頂点シェーダーに転送することで終了するからです。

スケルトン アニメーションは、CPU で処理された後、頂点シェーダーに入ります。
スケルトン アニメーションを使用しない頂点の公式を思い出させてください。
gl_Position = 投影マトリックス * ビューマトリックス * モデルマトリックス * 頂点;
この式がどのように生まれたのか理解できない人は、OpenGL のコンテキストで 3D コンテンツを表示するための行列の操作原理を説明した私の記事を読んでください。
残りについては –スケルトン アニメーションを実装するための公式:
” vec4 アニメーション頂点 = ボーン 0 マトリックス * 頂点 * ボーン 0 ウェイト +”
“ボーン 1 マトリックス * 頂点 * ボーン 1 ウェイト +”
“ボーン 2 マトリックス * 頂点 * ボーン 2 ウェイト +”
“bone3matrix * 頂点 *bone3weight;\n”
” gl_Position = projectMatrix * viewMatrix * modelMatrix * animeVertex;\n”

つまり、最終的なボーン変換行列に頂点と、頂点に対するこの行列の重みを乗算します。各頂点は 4 つのボーンでアニメーション化でき、衝撃の強さはボーンの重量パラメータによって制御され、衝撃の合計は 1 に等しくなる必要があります。
頂点に影響を与えるボーンが 4 つ未満の場合はどうすればよいですか?重みを分担して残りの影響をゼロにする必要があります
。数学的には、重みと行列を乗算することを「行列のスカラー乗算」と呼びます。スカラーを乗算すると、結果の頂点に対する行列の効果を合計できます。

ボーン変換行列自体は配列として送信されます。 さらに、配列には各メッシュの行列ではなく、モデル全体の行列が含まれています。

ただし、頂点ごとに次の情報が個別に送信されます。
–頂点に影響を与える行列のインデックス
です。–頂点に影響を与える行列の重み
です。複数のボーンが送信され、通常は頂点の 4 つのボーンの効果が使用されます。
また、4つのサイコロの重みの合計は必ず1に
ならなければなりません。次にシェーダーでの見え方を
見てみましょう。行列配列:
“均一マット 4 ボーン行列[kMaxBones];”

各頂点の 4 つのボーンの効果に関する情報:
“属性 vec2bone0info;”
“属性 vec2bone1info;”
“属性 vec2bone2info;”
“属性 vec2bone3info;”

vec2 – X 座標にはボーンのインデックスを保存し (シェーダーで int に変換します)、Y 座標には頂点に対するボーンの影響の重みを保存します。なぜこのデータを 2 次元ベクトルで送信する必要があるのでしょうか? GLSL は、有効なフィールドを持つ C 読み取り可能な構造体をシェーダーに渡すことをサポートしていないためです。

以下に、animatedVertex 式にさらに代入するために必要な情報をベクトルから取得する例を示します。

“intbone0Index = int(bone0info.x);”
“floatbone0weight =bone0info.y;”
“mat4bone0matrix =bonesMatrices[bone0Index];”

“intbone1Index = int(bone1info.x);”
“floatbone1weight =bone1info.y;”
“mat4bone1matrix =boneMatrices[bone1Index];”

“intbone2Index = int(bone2info.x);”
“floatbone2weight =bone2info.y;”
“mat4bone2matrix =bonesMatrices[bone2Index];”

“intbone3Index = int(bone3info.x);”
“floatbone3weight =bone3info.y;”
“mat4bone3matrix =bonesMatrices[bone3Index];”

これで、CPU に設定された頂点構造は次のようになります。
x、y、z、u、v、bone0index、bone0weight、bone1index、bone1weight、bone2index、bone2weight、bone3index、bone3weight

頂点バッファ構造はモデルの読み込み中に一度埋められますが、変換マトリックスは各レンダリング フレームで CPU からシェーダーに転送されます。

残りの部分では、CPU 上でアニメーションを計算する原理を説明します。アニメーションを頂点シェーダーに転送する前に、アニメーション、モデル、ノード、メッシュ階層、マトリックスをたどるボーン ノードのツリーについて説明します。補間。

ソース

http://ogldev.atspace.co.英国/www/tutorial38/tutorial38.html

ソースコード

https://gitlab.com/demensdeum/skeletal-animation

テンプレートメソッド

パターン手法とは、動作設計パターンを指します。このパターンは、クラスのロジックの一部をオンデマンドで置き換え、全体の部分は子孫に対して変更しないままにする方法を説明します。

Cuban Cars

クライアント銀行を開発していると仮定して、承認モジュールを開発するタスクを考えてみましょう –ユーザーは抽象ログイン データを使用してアプリケーションにログインできる必要があります。
認可モジュールはクロスプラットフォームである必要があり、さまざまな認可テクノロジをサポートし、さまざまなプラットフォームの暗号化されたデータを保存する必要があります。モジュールを実装するには、クロスプラットフォーム Kotlin 言語を選択し、認可モジュールの抽象クラス (プロトコル) を使用して、MyPhone 電話の実装を作成します。

class MyPhoneSuperDuperSecretMyPhoneAuthorizationStorage {
    fun loginAndPassword() : Pair {
        return Pair("admin", "qwerty65435")
    }
}

class ServerApiClient {
    fun authorize(authorizationData: AuthorizationData) : Unit {
        println(authorizationData.login)
        println(authorizationData.password)
        println("Authorized")
    }
}

class AuthorizationData {
    var login: String? = null
    var password: String? = null
}

interface AuthorizationModule {
    abstract fun fetchAuthorizationData() : AuthorizationData
    abstract fun authorize(authorizationData: AuthorizationData)
}

class MyPhoneAuthorizationModule: AuthorizationModule {
    
    override fun fetchAuthorizationData() : AuthorizationData {
        val loginAndPassword = MyPhoneSuperDuperSecretMyPhoneAuthorizationStorage().loginAndPassword()
        val authorizationData = AuthorizationData()
        authorizationData.login = loginAndPassword.first
        authorizationData.password = loginAndPassword.second
        
        return authorizationData
    }
    
    override fun authorize(authorizationData: AuthorizationData) {
        ServerApiClient().authorize(authorizationData)
    }
    
}

fun main() {
    val authorizationModule = MyPhoneAuthorizationModule()
    val authorizationData = authorizationModule.fetchAuthorizationData()
    authorizationModule.authorize(authorizationData)
}

ここで、電話/プラットフォームごとに、サーバーに認証を送信するためのコードを複製する必要があります。これは DRY 原則に違反します。上記の例は非常に単純ですが、より複雑なクラスではさらに多くの重複が発生します。コードの重複を排除するには、テンプレート メソッド パターンを使用する必要があります。
モジュールの共通部分を不変メソッドに移動し、暗号化されたデータ転送の機能を特定のプラットフォーム クラスに転送しましょう。

class MyPhoneSuperDuperSecretMyPhoneAuthorizationStorage {
    fun loginAndPassword() : Pair {
        return Pair("admin", "qwerty65435")
    }
}

class ServerApiClient {
    fun authorize(authorizationData: AuthorizationData) : Unit {
        println(authorizationData.login)
        println(authorizationData.password)
        println("Authorized")
    }
}

class AuthorizationData {
    var login: String? = null
    var password: String? = null
}

interface AuthorizationModule {
    abstract fun fetchAuthorizationData() : AuthorizationData
    
    fun authorize(authorizationData: AuthorizationData) {
        ServerApiClient().authorize(authorizationData)
    }
}

class MyPhoneAuthorizationModule: AuthorizationModule {
    
    override fun fetchAuthorizationData() : AuthorizationData {
        val loginAndPassword = MyPhoneSuperDuperSecretMyPhoneAuthorizationStorage().loginAndPassword()
        val authorizationData = AuthorizationData()
        authorizationData.login = loginAndPassword.first
        authorizationData.password = loginAndPassword.second
        
        return authorizationData
    }
    
}

fun main() {
    val authorizationModule = MyPhoneAuthorizationModule()
    val authorizationData = authorizationModule.fetchAuthorizationData()
    authorizationModule.authorize(authorizationData)
}

ソース

https://refactoring.guru/ru/design-パターン/テンプレート メソッド

ソースコード

https://gitlab.com/demensdeum/patterns/< /p>

パターンブリッジ

ブリッジ パターンは、構造設計パターンを指します。ロジックを別の抽象クラスに移動することで、クラス ロジックの実装を抽象化できます。簡単そうですよね?

さまざまな種類のメッセンジャーにメッセージを送信できるスパム ボットを実装するとします。
共通のプロトコルを使用して実装します。

protocol User {
    let token: String
    let username: String
}

protocol Messenger {
    var authorize(login: String, password: String)
    var send(message: String, to user: User)
}

class iSeekUUser: User {
    let token: String
    let username: String
}

class iSeekU: Messenger {

    var authorizedUser: User?
    var requestSender: RequestSender?
    var requestFactory: RequestFactory?

    func authorize(login: String, password: String) {
        authorizedUser = requestSender?.perform(requestFactory.loginRequest(login: login, password: password))
    }
    
    func send(message: String, to user: User) {
        requestSender?.perform(requestFactory.messageRequest(message: message, to: user)
    }
}

class SpamBot {
    func start(usersList: [User]) {
        let iSeekUMessenger = iSeekU()
        iSeekUMessenger.authorize(login: "SpamBot", password: "SpamPassword")
        
        for user in usersList {
            iSeekUMessennger.send(message: "Hey checkout demensdeum blog! http://demensdeum.com", to: user)
        }
    }
}

次に、iSekU メッセンジャーのメッセージを送信するための、より高速な新しいプロトコルのリリースを想像してみましょう。新しいプロトコルを追加するには、iSekU ボットの実装を複製し、その一部のみを変更する必要があります。クラスロジックのほんの一部が変更されただけの場合、なぜこれを行うのかは不明です。このアプローチでは、DRY 原則に違反します。製品がさらに開発されると、新機能の実装におけるエラーや遅延によって柔軟性の欠如が顕著になります。
プロトコルのロジックを抽象クラスに移動して、Bridge パターンを実装しましょう。

protocol User {
    let token: String
    let username: String
}

protocol Messenger {
    var authorize(login: String, password: String)
    var send(message: String, to user: User)
}

protocol MessagesSender {
    func send(message: String, to user: User)
}

class iSeekUUser: User {
    let token: String
    let username: String
}

class iSeekUFastMessengerSender: MessagesSender {
    func send(message: String, to user: User) {
        requestSender?.perform(requestFactory.messageRequest(message: message, to: user)
    }
}

class iSeekU: Messenger {

    var authorizedUser: User?
    var requestSender: RequestSender?
    var requestFactory: RequestFactory?
    var messagesSender: MessengerMessagesSender?

    func authorize(login: String, password: String) {
        authorizedUser = requestSender?.perform(requestFactory.loginRequest(login: login, password: password))
    }
    
    func send(message: String, to user: User) {
        messagesSender?.send(message: message, to: user)
    }
}

class SpamBot {

    var messagesSender: MessagesSender?

    func start(usersList: [User]) {
        let iSeekUMessenger = iSeekU()
        iSeekUMessenger.authorize(login: "SpamBot", password: "SpamPassword")
        
        for user in usersList {
            messagesSender.send(message: "Hey checkout demensdeum blog! http://demensdeum.com", to: user)
        }
    }
}

このアプローチの利点の 1 つは、メイン アプリケーションのコードを変更せずに、抽象化されたロジックを実装するプラグイン/ライブラリを作成することで、アプリケーションの機能を拡張できることです。
戦略パターンとの違いは何ですか?どちらのパターンも非常に似ていますが、Strategy では *アルゴリズム* の切り替えについて説明しますが、Bridge では *任意の複雑なロジック* の大部分を切り替えることができます。

ソース

https://refactoring.guru/ru/design-patterns/bridge

ソースコード

https://gitlab.com/demensdeum/patterns/p>

責任連鎖パターン

責任の連鎖とは、行動設計パターンを指します。


ガンナ・ドルビエワ

映画会社 Jah-Pictures は、リベリアの共産主義者ラスタファリアンについてのドキュメンタリー映画「Red Dawn of Marley」を製作しました。この映画は非常に長く(8時間)、興味深いものですが、公開前に、一部の国では映画のショットやフレーズが異端とみなされ、配給ライセンスが与えられないことが判明しました。映画のプロデューサーは、手動および自動で、疑わしいフレーズを含む瞬間を映画から切り取ることにしました。一部の国では、手動による検査と設置中にエラーが発生した場合に販売代理店の担当者が単純に射殺されないよう、二重のチェックが必要です。
国は 4 つのグループに分けられます。検閲のない国、中程度、中程度、非常に厳しい検閲がある国。ニューラル ネットワークを使用して、視聴された映画の断片の異端のレベルを分類することが決定されました。このプロジェクトでは、非常に高価な最先端のニューロンが購入され、開発者の任務であるさまざまなレベルの検閲に合わせてトレーニングされます。フィルムを断片に分割し、フリーから厳密まで一連のニューラル ネットワークを介して送信し、そのうちの 1 つが異端を検出した後、断片は手動レビュー用に転送され、さらなる編集が行われます。すべてのニューロンを通過することは不可能です。彼らの作業には多大なコンピューティング能力が必要です (結局のところ、電気代を支払わなければなりません)。最初に動作するもので停止すれば十分です。
単純な擬似コードの実装:

import StateOfArtCensorshipHLNNClassifiers

protocol MovieCensorshipClassifier {
    func shouldBeCensored(movieChunk: MovieChunk) -> Bool
}

class CensorshipClassifier: MovieCensorshipClassifier {

    let hnnclassifier: StateOfArtCensorshipHLNNClassifier

    init(_ hnnclassifier: StateOfArtCensorshipHLNNClassifier) {
        self.hnnclassifier = hnnclassifier
    }
    
    func shouldBeCensored(_ movieChunk: MovieChunk) -> Bool {
        return hnnclassifier.shouldBeCensored(movieChunk)
    }
}

let lightCensorshipClassifier = CensorshipClassifier(StateOfArtCensorshipHLNNClassifier("light"))
let normalCensorshipClassifier = CensorshipClassifier(StateOfArtCensorshipHLNNClassifier("normal"))
let hardCensorshipClassifier = CensorshipClassifier(StateOfArtCensorshipHLNNClassifier("hard"))

let classifiers = [lightCensorshipClassifier, normalCensorshipClassifier, hardCensorshipClassifier]

let movie = Movie("Red Jah rising")
for chunk in movie.chunks {
    for classifier in classifiers {
        if classifier.shouldBeCensored(chunk) == true {
            print("Should censor movie chunk: \(chunk), reported by \(classifier)")
        }
   }
}

一般に、分類子の配列を使用したソリューションはそれほど悪くはありませんが、配列を作成できず、映画の断片の検閲の種類をすでに決定している分類子エンティティを 1 つだけ作成する機会があると想像してみましょう。アプリケーション(プラグイン)の機能を拡張するライブラリを開発する場合、このような制限が
発生する可能性があります。デコレータ パターン – を使用してみましょう。チェーン内の次の分類子への参照を分類子クラスに追加し、最初に分類が成功した時点で検証プロセスを停止しましょう。
したがって、責任連鎖パターンを実装します。

import StateOfArtCensorshipHLNNClassifiers

protocol MovieCensorshipClassifier {
    func shouldBeCensored(movieChunk: MovieChunk) -> Bool
}

class CensorshipClassifier: MovieCensorshipClassifier {

    let nextClassifier: CensorshipClassifier?
    let hnnclassifier: StateOfArtCensorshipHLNNClassifier

    init(_ hnnclassifier: StateOfArtCensorshipHLNNClassifier, nextClassifier: CensorshipClassifiers?) {
            self.nextClassifier = nextClassifier
            self.hnnclassifier = hnnclassifier
    }
    
    func shouldBeCensored(_ movieChunk: MovieChunk) -> Bool {
        let result = hnnclassifier.shouldBeCensored(movieChunk)
        
        print("Should censor movie chunk: \(movieChunk), reported by \(self)")
        
        if result == true {
                return true
        }
        else {
                return nextClassifier?.shouldBeCensored(movieChunk) ?? false
        }
    }
}

let censorshipClassifier = CensorshipClassifier(StateOfArtCensorshipHLNNClassifier("light"), nextClassifier: CensorshipClassifier(StateOfArtCensorshipHLNNClassifier("normal", nextClassifier: CensorshipClassifier(StateOfArtCensorshipHLNNClassifier("hard")))))

let movie = Movie("Red Jah rising")
for chunk in movie.chunks {
    censorshipClassifier.shouldBeCensored(chunk)
}

参考文献

https://refactoring.guru/ru/デザインパターン/責任連鎖

ソースコード

https://gitlab.com/demensdeum/patterns/< /p>

パターンデコレーター

Decorator パターンは、構造設計パターンを指します。

デコレータは、クラスの機能を拡張するための継承の代替として使用されます。
製品の種類に応じてアプリケーションの機能を拡張するという課題があります。顧客は 3 種類の製品を必要としています。ベーシック、プロフェッショナル、究極。
基本–文字数をカウントする、Professional –機能 Basic + テキストを大文字で印刷します。Ultimate – Basic + Professional + 究極というテキストを印刷します。
継承を使用して実装します。

protocol Feature {
	func textOperation(text: String)
}

class BasicVersionFeature: Feature {
	func textOperation(text: String) {
		print("\(text.count)")
	}
}

class ProfessionalVersionFeature: BasicVersionFeature {
	override func textOperation(text: String) {
		super.textOperation(text: text)
		print("\(text.uppercased())")
	}
}

class UltimateVersionFeature: ProfessionalVersionFeature {
	override func textOperation(text: String) {
		super.textOperation(text: text)
		print("ULTIMATE: \(text)")
	}
}

let textToFormat = "Hello Decorator"

let basicProduct = BasicVersionFeature()
basicProduct.textOperation(text: textToFormat)

let professionalProduct = ProfessionalVersionFeature()
professionalProduct.textOperation(text: textToFormat)

let ultimateProduct = UltimateVersionFeature()
ultimateProduct.textOperation(text: textToFormat)

ここで、製品「Ultimate Light」– を実装する必要があります。 Basic + Ultimate ですが、Professional バージョンの機能はありません。最初の OH! が起こるからです。このような単純なタスク用に別のクラスを作成し、コードを複製する必要があります。
継承を使用して実装を続けましょう:

protocol Feature {
	func textOperation(text: String)
}

class BasicVersionFeature: Feature {
	func textOperation(text: String) {
		print("\(text.count)")
	}
}

class ProfessionalVersionFeature: BasicVersionFeature {
	override func textOperation(text: String) {
		super.textOperation(text: text)
		print("\(text.uppercased())")
	}
}

class UltimateVersionFeature: ProfessionalVersionFeature {
	override func textOperation(text: String) {
		super.textOperation(text: text)
		print("ULTIMATE: \(text)")
	}
}

class UltimateLightVersionFeature: BasicVersionFeature {
	override func textOperation(text: String) {
		super.textOperation(text: text)
		print("ULTIMATE: \(text)")	
	}
}

let textToFormat = "Hello Decorator"

let basicProduct = BasicVersionFeature()
basicProduct.textOperation(text: textToFormat)

let professionalProduct = ProfessionalVersionFeature()
professionalProduct.textOperation(text: textToFormat)

let ultimateProduct = UltimateVersionFeature()
ultimateProduct.textOperation(text: textToFormat)

let ultimateLightProduct = UltimateLightVersionFeature()
ultimateLightProduct.textOperation(text: textToFormat)

わかりやすくするためにこの例をさらに発展させることもできますが、継承ベースに基づいてシステムをサポートする複雑さは今でも明らかです –面倒で柔軟性に欠け
ます。デコレータは機能を説明する一連のプロトコルであり、機能を拡張するデコレータ クラスの子具象インスタンスへの参照を含む抽象クラスです。
パターンを使用して上記の例を書き直してみましょう。

protocol Feature {
	func textOperation(text: String)
}

class FeatureDecorator: Feature {
	private var feature: Feature?
	
	init(feature: Feature? = nil) {
		self.feature = feature
	}
	
	func textOperation(text: String) {
		feature?.textOperation(text: text)
	}
}

class BasicVersionFeature: FeatureDecorator {
	override func textOperation(text: String) {
		super.textOperation(text: text)
		print("\(text.count)")
	}
}

class ProfessionalVersionFeature: FeatureDecorator {
	override func textOperation(text: String) {
		super.textOperation(text: text)
		print("\(text.uppercased())")
	}
}

class UltimateVersionFeature: FeatureDecorator {
	override func textOperation(text: String) {
		super.textOperation(text: text)
		print("ULTIMATE: \(text)")
	}
}

let textToFormat = "Hello Decorator"

let basicProduct = BasicVersionFeature(feature: UltimateVersionFeature())
basicProduct.textOperation(text: textToFormat)

let professionalProduct = ProfessionalVersionFeature(feature: UltimateVersionFeature())
professionalProduct.textOperation(text: textToFormat)

let ultimateProduct = BasicVersionFeature(feature: UltimateVersionFeature(feature: ProfessionalVersionFeature()))
ultimateProduct.textOperation(text: textToFormat)

let ultimateLightProduct = BasicVersionFeature(feature: UltimateVersionFeature())
ultimateLightProduct.textOperation(text: textToFormat)

これで、あらゆる種類の製品のバリエーションを作成できるようになりました –アプリケーションの起動段階で結合タイプを初期化するだけで十分です。以下の例は、Ultimate + Professional バージョンの作成です。

ultimateProfessionalProduct.textOperation(text: textToFormat)

ソース

https://refactoring.guru/ru/design-patterns/decorator

ソースコード

https://gitlab.com/demensdeum/patterns

パターンメディエーター

メディエーター パターンは、動作デザイン パターンを指します。

ある日、ジョーク アプリケーションの開発を依頼されました。ユーザーが画面中央のボタンを押すと、アヒルが鳴く面白い音が聞こえます。
アプリストアにアップロードすると、アプリケーションはヒットします。誰もがあなたのアプリケーションを大騒ぎし、イーロン・マスクは火星の超高速トンネルの次回の打ち上げで自身のインスタグラムで大騒ぎし、ヒラリー・クリントンは討論会でドナルド・トランプを圧倒します。そしてウクライナの選挙に勝利し、成功しましょう!
アプリケーションの単純な実装は次のようになります。

class DuckButton {
    func didPress() {
        print("quack!")
    }
}

let duckButton = DuckButton()
duckButton.didPress()

次に、犬の鳴き声を追加することにします。そのためには、音を選択するための 2 つのボタンを表示する必要があります –アヒルと犬と一緒に。 DuckButton と DogButton という 2 つのボタン クラスを作成しましょう。
コードを変更します:

class DuckButton {
    func didPress() {
        print("quack!")
    }
}

class DogButton {
    func didPress() {
        print("bark!")
    }
}

let duckButton = DuckButton()
duckButton.didPress()

let dogButton = DogButton()
dogButton.didPress()

さらに成功したら、豚の鳴き声を追加します。ボタンには 3 つのクラスがあります。

class DuckButton {
    func didPress() {
        print("quack!")
    }
}

class DogButton {
    func didPress() {
        print("bark!")
    }
}

class PigButton {
    func didPress() {
        print("oink!")
    }
}

let duckButton = DuckButton()
duckButton.didPress()

let dogButton = DogButton()
dogButton.didPress()

let pigButton = PigButton()
pigButton.didPress()

ユーザーからは音が重なっているとの苦情が寄せられています。
これが起こらないようにチェックを追加し、同時にクラスを相互に導入します。

class DuckButton {
    var isMakingSound = false
    var dogButton: DogButton?
    var pigButton: PigButton?
    func didPress() {
        guard dogButton?.isMakingSound ?? false == false &&
                pigButton?.isMakingSound ?? false == false else { return }
        isMakingSound = true
        print("quack!")
        isMakingSound = false
    }
}

class DogButton {
    var isMakingSound = false
    var duckButton: DuckButton?
    var pigButton: PigButton?
    func didPress() {
        guard duckButton?.isMakingSound ?? false == false &&
                pigButton?.isMakingSound ?? false == false else { return }
        isMakingSound = true
        print("bark!")
        isMakingSound = false
    }
}

class PigButton {
    var isMakingSound = false
    var duckButton: DuckButton?
    var dogButton: DogButton?
    func didPress() {
        guard duckButton?.isMakingSound ?? false == false && 
                dogButton?.isMakingSound ?? false == false else { return }
        isMakingSound = true
        print("oink!")
        isMakingSound = false
    }
}

let duckButton = DuckButton()
duckButton.didPress()

let dogButton = DogButton()
dogButton.didPress()

let pigButton = PigButton()
pigButton.didPress()

あなたの申請が成功したことを受けて、政府はモバイル デバイスでの鳴き声、吠え声、うなり声を平日の午前 9 時から 15 時までのみ許可するという法律を制定することを決定しました。その場合、あなたのアプリケーションのユーザーは、個人用電子機器を使用したわいせつな音の生成により、5 年間の懲役刑に処される危険があります。
コードを変更します:

import Foundation

extension Date {
    func mobileDeviceAllowedSoundTime() -> Bool {
        let hour = Calendar.current.component(.hour, from: self)
        let weekend = Calendar.current.isDateInWeekend(self)
        
        let result = hour >= 9 && hour <= 14 && weekend == false
        
        return result
    }
}

class DuckButton {
    var isMakingSound = false
    var dogButton: DogButton?
    var pigButton: PigButton?
    func didPress() {
        guard dogButton?.isMakingSound ?? false == false &&
                pigButton?.isMakingSound ?? false == false &&
                 Date().mobileDeviceAllowedSoundTime() == true else { return }
        isMakingSound = true
        print("quack!")
        isMakingSound = false
    }
}

class DogButton {
    var isMakingSound = false
    var duckButton: DuckButton?
    var pigButton: PigButton?
    func didPress() {
        guard duckButton?.isMakingSound ?? false == false &&
                pigButton?.isMakingSound ?? false == false &&
                 Date().mobileDeviceAllowedSoundTime() == true else { return }
        isMakingSound = true
        print("bark!")
        isMakingSound = false
    }
}

class PigButton {
    var isMakingSound = false
    var duckButton: DuckButton?
    var dogButton: DogButton?
    func didPress() {
        guard duckButton?.isMakingSound ?? false == false && 
                dogButton?.isMakingSound ?? false == false &&
                 Date().mobileDeviceAllowedSoundTime() == true else { return }
        isMakingSound = true
        print("oink!")
        isMakingSound = false
    }
}

let duckButton = DuckButton()
let dogButton = DogButton()
let pigButton = PigButton()

duckButton.dogButton = dogButton
duckButton.pigButton = pigButton

dogButton.duckButton = duckButton
dogButton.pigButton = pigButton

pigButton.duckButton = duckButton
pigButton.dogButton = dogButton

duckButton.didPress()
dogButton.didPress()
pigButton.didPress()

突然、懐中電灯アプリケーションが私たちのアプリケーションを市場から追い出し始めます。それに負けないように、「oink-oink」ボタンと残りのボタンを押して懐中電灯を追加しましょう。

import Foundation

extension Date {
    func mobileDeviceAllowedSoundTime() -> Bool {
        let hour = Calendar.current.component(.hour, from: self)
        let weekend = Calendar.current.isDateInWeekend(self)
        
        let result = hour >= 9 && hour <= 14 && weekend == false
        
        return result
    }
}

class Flashlight {

    var isOn = false

    func turn(on: Bool) {
        isOn = on
    }
}

class DuckButton {
    var isMakingSound = false
    var dogButton: DogButton?
    var pigButton: PigButton?
    var flashlight: Flashlight?
    func didPress() {
        flashlight?.turn(on: true)
        guard dogButton?.isMakingSound ?? false == false &&
                pigButton?.isMakingSound ?? false == false &&
                 Date().mobileDeviceAllowedSoundTime() == true else { return }
        isMakingSound = true
        print("quack!")
        isMakingSound = false
    }
}

class DogButton {
    var isMakingSound = false
    var duckButton: DuckButton?
    var pigButton: PigButton?
    var flashlight: Flashlight?
    func didPress() {
        flashlight?.turn(on: true)
        guard duckButton?.isMakingSound ?? false == false &&
                pigButton?.isMakingSound ?? false == false &&
                 Date().mobileDeviceAllowedSoundTime() == true else { return }
        isMakingSound = true
        print("bark!")
        isMakingSound = false
    }
}

class PigButton {
    var isMakingSound = false
    var duckButton: DuckButton?
    var dogButton: DogButton?
    var flashlight: Flashlight?
    func didPress() {
        flashlight?.turn(on: true)
        guard duckButton?.isMakingSound ?? false == false && 
                dogButton?.isMakingSound ?? false == false &&
                 Date().mobileDeviceAllowedSoundTime() == true else { return }
        isMakingSound = true
        print("oink!")
        isMakingSound = false
    }
}

let flashlight = Flashlight()
let duckButton = DuckButton()
let dogButton = DogButton()
let pigButton = PigButton()

duckButton.dogButton = dogButton
duckButton.pigButton = pigButton
duckButton.flashlight = flashlight

dogButton.duckButton = duckButton
dogButton.pigButton = pigButton
dogButton.flashlight = flashlight

pigButton.duckButton = duckButton
pigButton.dogButton = dogButton
pigButton.flashlight = flashlight

duckButton.didPress()
dogButton.didPress()
pigButton.didPress()

その結果、大量のコピー&ペーストコードを含む巨大なアプリケーションができました。内部のクラスはデッドリンクによって相互に接続されています。弱い結合は存在せず、このような奇跡を維持するのは非常に困難です。エラーが発生する可能性が高いため、将来変更される可能性があります。

メディエーターを使用する

中間メディエーター クラス ApplicationController を追加しましょう。このクラスはオブジェクトの疎結合を提供し、クラス間の責任を確実に分離し、重複したコードを排除します。
書き直してみましょう:

import Foundation

class ApplicationController {

    private var isMakingSound = false
    private let flashlight = Flashlight()
    private var soundButtons: [SoundButton] = []

    func add(soundButton: SoundButton) {
        soundButtons.append(soundButton)
    }
    
    func didPress(soundButton: SoundButton) {
        flashlight.turn(on: true)
        guard Date().mobileDeviceAllowedSoundTime() && 
                isMakingSound == false else { return }
        isMakingSound = true
        soundButton.didPress()
        isMakingSound = false
    }
}

class SoundButton {
    let soundText: String
    
    init(soundText: String) {
        self.soundText = soundText
    }
    
    func didPress() {
        print(soundText)
    }
}

class Flashlight {
    var isOn = false

    func turn(on: Bool) {
        isOn = on
    }
}

extension Date {
    func mobileDeviceAllowedSoundTime() -> Bool {
        let hour = Calendar.current.component(.hour, from: self)
        let weekend = Calendar.current.isDateInWeekend(self)
        
        let result = hour >= 9 && hour <= 14 && weekend == false
        
        return result
    }
}

let applicationController = ApplicationController()
let pigButton = SoundButton(soundText: "oink!")
let dogButton = SoundButton(soundText: "bark!")
let duckButton = SoundButton(soundText: "quack!")

applicationController.add(soundButton: pigButton)
applicationController.add(soundButton: dogButton)
applicationController.add(soundButton: duckButton)

pigButton.didPress()
dogButton.didPress()
duckButton.didPress()

ユーザー インターフェイス アプリケーション アーキテクチャに関する多くの記事で、MVC パターンとその派生について説明しています。モデルはビジネス ロジック データを操作するために使用され、ビューまたは表現はインターフェイスでユーザーに情報を表示し、ユーザーとの対話を提供します。コントローラーはシステム コンポーネントの対話を保証するメディエーターです。

ソース

https://refactoring.guru/ru/design-patterns/仲介者

ソースコード

https://gitlab.com/demensdeum/patterns/< /p>

Strategy pattern

The Strategy pattern allows you to select the type of algorithm that implements a common interface, right while the application is running. This pattern refers to the behavioral design patterns.

Sun Tzu

Suppose we are developing a music player with embedded codecs. The built-in codecs imply reading music formats without using external sources of the operating system (codecs), the player should be able to read tracks of different formats and play them. VLC player has such capabilities, it supports various types of video and audio formats, it runs on popular and not very operating systems.

Imagine what a naive player implementation looks like:

var player: MusicPlayer?

func play(filePath: String) {
    let extension = filePath.pathExtension

    if extension == "mp3" {
        playMp3(filePath)
    }
    else if extension == "ogg" {
        playOgg(filePath)
    }
}

func playMp3(_ filePath: String) {
    player = MpegPlayer()
    player?.playMp3(filePath)
}

func playOgg(_ filePath: String) {
    player = VorbisPlayer()
    player?.playMusic(filePath)
}

Next, we add several formats, which leads to the need to write additional methods. Plus, the player must support plug-in libraries, with new audio formats that will appear later. There is a need to switch the music playback algorithm, the Strategy pattern is used to solve this problem.

Let’s create a common protocol MusicPlayerCodecAlgorithm, write the implementation of the protocol in two classes MpegMusicPlayerCodecAlgorithm and VorbisMusicPlayerCodecAlgorithm, to play mp3 and ogg files with-but. Create a class MusicPlayer, which will contain a reference for the algorithm that needs to be switched, then by the file extension we implement codec type switching:

import Foundation

class MusicPlayer {
    var playerCodecAlgorithm: MusicPlayerCodecAlgorithm?
    
	func play(_ filePath: String) {
            playerCodecAlgorithm?.play(filePath)
	}
}

protocol MusicPlayerCodecAlgorithm {
    func play(_ filePath: String)
}

class MpegMusicPlayerCodecAlgorithm: MusicPlayerCodecAlgorithm {
	func play(_ filePath: String) {
		debugPrint("mpeg codec - play")
	}
}

class VorbisMusicPlayerCodecAlgorithm: MusicPlayerCodecAlgorithm {
	func play(_ filePath: String) {
		debugPrint("vorbis codec - play")	
	}
}

func play(fileAtPath path: String) {
	guard let url = URL(string: path) else { return }
	let fileExtension = url.pathExtension
		
	let musicPlayer = MusicPlayer()
	var playerCodecAlgorithm: MusicPlayerCodecAlgorithm? 
		
	if fileExtension == "mp3" {
                playerCodecAlgorithm = MpegMusicPlayerCodecAlgorithm()
	}
	else if fileExtension == "ogg" {
                playerCodecAlgorithm = VorbisMusicPlayerCodecAlgorithm()
	}
		
	musicPlayer.playerCodecAlgorithm = playerCodecAlgorithm
	musicPlayer.playerCodecAlgorithm?.play(path)
}

play(fileAtPath: "Djentuggah.mp3")
play(fileAtPath: "Procrastinallica.ogg")

The above example also shows the simplest example of a factory (switching the codec type from the file extension) It is important to note that the Strategy strategy does not create objects, it only describes how to create a common interface for switching the family of algorithms.

Documentation

https://refactoring.guru/en/design-patterns/strategy

Source code

https://gitlab.com/demensdeum/patterns/

Iterator pattern

In this article I will describe the Iterator pattern.
This pattern refers to the behavioral design patterns.

Print it

Suppose we need to print a list of tracks from the album “Procrastinate them all” of the group “Procrastinallica”.
The naive implementation (Swift) looks like this:

for i=0; i < tracks.count; i++ {
    print(tracks[i].title)
}

Suddenly during compilation, it is detected that the class of the tracks object does not give the number of tracks in the count call, and moreover, its elements cannot be accessed by index. Oh…

Filter it

Suppose we are writing an article for the magazine “Wacky Hammer”, we need a list of tracks of the group “Djentuggah” in which bpm exceeds 140 beats per minute. An interesting feature of this group is that its records are stored in a huge collection of underground groups, not sorted by albums, or for any other grounds. Let’s imagine that we work with a language without functionality:

var djentuggahFastTracks = [Track]()

for track in undergroundCollectionTracks {
    if track.band.title == "Djentuggah" && track.info.bpm == 140 {
        djentuggahFastTracks.append(track)
    }
}

Suddenly, a couple of tracks of the group are found in the collection of digitized tapes, and the editor of the magazine suggests finding tracks in this collection and writing about them. A Data Scientist friend suggests to use the Djentuggah track classification algorithm, so you don’t need to listen to a collection of 200 thousand tapes manually. Try:

var djentuggahFastTracks = [Track]()

for track in undergroundCollectionTracks {
    if track.band.title == "Djentuggah" && track.info.bpm == 140 {
        djentuggahFastTracks.append(track)
    }
}

let tracksClassifier = TracksClassifier()
let bpmClassifier = BPMClassifier()

for track in cassetsTracks {
    if tracksClassifier.classify(track).band.title == "Djentuggah" && bpmClassifier.classify(track).bpm == 140 {
        djentuggahFastTracks.append(track)
    }
}

Mistakes

Now, just before sending to print, the editor reports that 140 beats per minute are out of fashion, people are more interested in 160, so the article should be rewritten by adding the necessary tracks.
Apply changes:

var djentuggahFastTracks = [Track]()

for track in undergroundCollectionTracks {
    if track.band.title == "Djentuggah" && track.info.bpm == 160 {
        djentuggahFastTracks.append(track)
    }
}

let tracksClassifier = TracksClassifier()
let bpmClassifier = BPMClassifier()

for track in cassetsTracks {
    if tracksClassifier.classify(track).band.title == "Djentuggah" && bpmClassifier.classify(track).bpm == 140 {
        djentuggahFastTracks.append(track)
    }
}

The most attentive ones noticed an error; the bpm parameter was changed only for the first pass through the list. If there were more passes through the collections, then the chance of a mistake would be higher, that is why the DRY principle should be used. The above example can be developed further, for example, by adding the condition that you need to find several groups with different bpm, by the names of vocalists, guitarists, this will increase the chance of error due to duplication of code.

Behold the Iterator!

In the literature, an iterator is described as a combination of two protocols / interfaces, the first is an iterator interface consisting of two methods – next(), hasNext(), next() returns an object from the collection, and hasNext() reports that there is an object and the list is not over. However in practice, I observed iterators with one method – next(), when the list ended, null was returned from this object. The second is a collection that should have an interface that provides an iterator – the iterator() method, there are variations with the collection interface that returns an iterator in the initial position and in end – the begin() and end() methods are used in C ++ std.
Using the iterator in the example above will remove duplicate code, eliminate the chance of mistake due to duplicate filtering conditions. It will also be easier to work with the collection of tracks on a single interface – if you change the internal structure of the collection, the interface will remain old and the external code will not be affected.
Wow!

let bandFilter = Filter(key: "band", value: "Djentuggah")
let bpmFilter = Filter(key: "bpm", value: 140)
let iterator = tracksCollection.filterableIterator(filters: [bandFilter, bpmFilter])

while let track = iterator.next() {
    print("\(track.band) - \(track.title)")
}

Changes

While the iterator is running, the collection may change, thus causing the iterator’s internal counter to be invalid, and generally breaking such a thing as “next object”. Many frameworks contain a check for changing the state of the collection, and in case of changes they return an error / exception. Some implementations allow you to remove objects from the collection while the iterator is running, by providing the remove() method in the iterator.

Documentation

https://refactoring.guru/en/design-patterns/iterator

Source code

https://gitlab.com/demensdeum/patterns/

パターン「スナップショット」

この投稿では、パターン「スナップショット」について説明します。または「メメント」

このパターンは「行動」を指します。デザインパターン。

グラフィック エディタを開発していて、ユーザー コマンドに基づいてアクションをロールバックする機能を追加する必要があるとします。このパターンを実装する場合、システム コンポーネントがロールバックされた「アクション」の内部状態にアクセスできないことも非常に重要です。他のシステム コンポーネントはスナップショット オブジェクトにのみアクセスでき、変更することはできません。内部状態を示し、明確でシンプルな外部インターフェイスを提供します。この問題を解決するには、「スナップショット」パターンが使用されます。または「キーパー」。

作品例「スナップショット」以下に示します:


クリックするとスプライトが表示され、カールした矢印をクリックするとアクションがキャンセルされます。スプライトが消えます。この例は 3 つのクラスで構成されています。

<オル>

  • スプライトとグラフィカル インターフェイスが表示されるキャンバス。
  • 画面コントローラー。クリックを処理し、画面のロジックを制御します。
  • 変更のたびに保持されるキャンバスの状態は、必要に応じて、画面コントローラを使用してロールバックされます。
  • パターン「スナップショット」のコンテキストでは、クラスは次のとおりです:

    <オル>

  • キャンバス–ソースでは、このクラスの状態は、後で要求に応じてロールバックできるように「スナップショット」として保存されます。また、ソースは「スナップショット」を転送するときに状態を復元できる必要があります。
  • コントローラー–管理者であるこのクラスは、状態を保存/ロールバックする方法とタイミングを知っています。
  • 状態–スナップショット。ソースの状態に加えて、ロールバック順序を正確に確立できる日付情報またはインデックスを保存するクラスです。
  • このパターンの重要な特徴は、ソースのみがスナップショット内の保存された状態の内部フィールドにアクセスできる必要があることです。これは、スナップショットを外部からの変更 (カプセル化をバイパスして何かを変更したい便利な開発者による) から保護するために必要です。システムロジックを破壊します)。カプセル化を実装するには、組み込みクラスが使用され、C++ ではフレンド クラスを指定する機能が使用されます。個人的には、Rise ではカプセル化を行わないシンプルなバージョンを実装し、Swift で実装する場合は Generic を使用しました。私のバージョンでは – Memento は、同じクラス状態のエンティティにのみ内部状態を与えます。

    ソース

    https://refactoring.guru/design-patterns/memento

    ソースコード

    https://gitlab.com/demensdeum/patterns/< /p>

    訪問者のパターン

    この投稿では、「訪問者」と呼ばれるデザイン パターンについて説明します。または「訪問者」
    このパターンは行動パターンのグループに属します。

    問題を考えてみましょう

    このパターンは主に、早期バインド言語における単一ディスパッチの制限を回避するために使用されます。

    アリス X by NFGPhoto (CC-2.0)
    抽象クラス/プロトコル Band を作成し、MurpleDeep のサブクラスを作成し、2 つのメソッドを持つ Visitor クラスを作成しましょう。 1 つは Band の子孫をコンソールに出力するためのもので、2 つ目は MurpleDeep を出力するためのものです。主なことは、メソッドの名前 (シグネチャ) が同じで、引数がクラスによってのみ異なることです。 Band 引数を指定した中間出力メソッドを使用して Visitor のインスタンスを作成し、MurpleDeep の visit メソッドを呼び出します。
    以下は Kotlin のコードです。

    出力は “これはバンド クラスです

    どうしてそんなことが可能なのでしょうか?!

    なぜこれが起こるのかは、ロシア語を含む多くの記事で賢明な言葉で説明されていますが、コンパイラーがコードをどのように認識するかを想像してみることをお勧めします。おそらくすべてがすぐに明らかになるでしょう。

    問題の解決

    この問題を解決するには多くの解決策があります。次に、訪問者パターンを使用した解決策を検討します。
    Visitor 引数を持つ accept メソッドを抽象クラス/プロトコルに追加し、メソッド内でvisitor.visit(this) を呼び出し、次に accept メソッドのオーバーライド/実装を MurpleDeep クラスに追加します。これにより、決定的かつ冷静に DRY に違反し、再度次のように記述します。 Visitor.visit(this).< br />最終コード:

    ソース

    https://refactoring.guru/ru/デザインパターン/訪問者-ダブルディスパッチ

    ソースコード

    https://gitlab.com/demensdeum/patterns

    フライウェイトパターン

    この投稿では、構造パターン「軽量」について説明します。または「日和見主義者」 (フライ級)
    このパターンは構造パターンのグループに属します。

    以下でパターンがどのように機能するかの例を見てみましょう。


    なぜ必要なのでしょうか? RAM を節約するため。 Java (CPU とメモリを無駄に消費する) が広く使用されている現在では、これはそれほど重要ではなくなりましたが、使用する価値はあるという意見には私も同意します。
    上記の例では40個のオブジェクトしか出力していませんが、120,000個に増やすとその分メモリ消費量も増加します
    。Chromium ブラウザでフライウェイト パターンを使用しないメモリ消費量を見てみましょう。

    パターンを使用しない場合、メモリ消費量は最大 300 メガバイトになります。

    次に、パターンをアプリケーションに追加して、メモリ消費量を確認してみましょう。

    このパターンを使用すると、メモリ消費量は約 200 メガバイトになるため、テスト アプリケーションでは 100 メガバイトのメモリを節約できます。この差はさらに大きくなる可能性があります。

    それはどのように機能しますか?

    上の例では、40 匹の猫を描画します。わかりやすくするために、120,000 匹の猫を描画します。各猫は PNG イメージとしてメモリにロードされ、ほとんどのレンダリングではレンダリング用のビットマップ (実際には bmp) に変換されます。圧縮された PNG はレンダリングに非常に時間がかかるため、これは速度を上げるために行われます。パターンを使用しない場合、12万枚の猫の写真をRAMにロードして描画しますが、パターンを使用すると「軽量」になります。 1 匹の猫をメモリにロードし、さまざまな位置と透明度で 12 万回描画します。重要なのは、猫の画像とは別に座標と透明度を実装することです。レンダリングでは 1 匹の猫だけを受け取り、正しいレンダリングのために座標と透明度を持つオブジェクトを使用します。

    コードではどのようになりますか?

    以下は、言語 Rise< /p>

    パターンを使用しない場合:


    猫の画像はループ内のオブジェクトごとに個別にロードされます。猫の画像。

    使用パターン:

    猫の 1 枚の写真が 12 万個のオブジェクトで使用されています。

    どこで使用されますか?

    Apple の「再利用」などの GUI フレームワークで使用されます。 (再利用) UITableViewCell テーブル セル。これにより、このパターンを知らない初心者にとっては参入障壁が高くなります。ゲーム開発でもよく使用されます。

    ソースコード

    https://gitlab.com/demensdeum/patterns/< /p>

    ソース

    https://refactoring.guru/ru/design-patterns/フライ級
    http://gameprogrammingpatterns.com/flyweight.html

    С++ Application Plugins

    In this post I will describe an example of adding functionality to a C ++ application using plugins. The practical part of the implementation for Linux is described; the theory can be found at the links at the end of the article.

    Composition over inheritance!

    To begin with, we will write a plugin – a function that we will call:

    #include "iostream"
    
    using namespace std;
    
    extern "C" void extensionEntryPoint() {
    	cout << "Extension entry point called" << endl;
    };
    

    Next, we will build the plugin as a dynamic library “extension.so”, which we will connect in the future:
    clang++ -shared -fPIC extension.cpp -o extension.so

    Next we write the main application that will load the file “extension.so”, look for a pointer to the function “extensionEntryPoint” there, and call it, typing errors if necessary:

    #include "iostream"
    #include "dlfcn.h"
    
    using namespace std;
    
    typedef void (*VoidFunctionPointer)();	
    
    int main (int argc, char *argv[]) {
    
    	cout << "C++ Plugins Example" << endl;
    
    	auto extensionHandle = dlopen("./extension.so", RTLD_LAZY);
    	if (!extensionHandle) {
    		string errorString = dlerror();
    		throw runtime_error(errorString);
    	}
    
    	auto functionPointer = VoidFunctionPointer();
    	functionPointer = (VoidFunctionPointer) dlsym(extensionHandle, "extensionEntryPoint");
    	auto dlsymError = dlerror();
     	if (dlsymError) {
    		string errorString = dlerror();
    		throw runtime_error(errorString);
     	}
    
    	functionPointer();
    
    	exit(0);
    } 
    

    The dlopen function returns a handler for working with a dynamic library; dlsym function returns a pointer to the required function by string; dlerror contains a pointer to the string with the error text, if any.

    Next, build the main application, copy the file of the dynamic library in the folder with it and run. The output should be the “Extension entry point called”

    Difficult moments include the lack of a single standard for working with dynamic libraries, because of this there is a need to export the function to a relatively global scope with extern C; the difference in working with different operating systems associated with this subtlety of work; the lack of a C ++ interface to implement OOP approach to working with dynamic libraries, however, there are open-source wrappers, for example m-renaud/libdlibxx

    Example Source Code

    https://gitlab.com/demensdeum/cpppluginsexample

    Documents

    http://man7.org/linux/man-pages/man3/dlopen.3.htm
    https://gist.github.com/tailriver/30bf0c943325330b7b6a
    https://stackoverflow.com/questions/840501/how-do-function-pointers-in-c-work

    ミシェルのようにときめいて

    [人工知能の力を感じてください]
    この投稿では未来を予測する方法について説明します。

    統計学には、ある種の問題があります –時系列分析。特定の変数の日付と値があれば、将来のこの変数の値を予測できます。
    最初は、TensorFlow ですが、ライブラリ 預言者 by Facebook.
    Prophet では、日付 (ds) 列と変数値 (y) 列を含むデータ (csv) に基づいて予測を行うことができます。この操作方法については、公式 Web サイトのドキュメントのセクション クイック スタート
    データセットとして、https://www.investing.com、実装時に R 言語 および Prophet API 彼にとって。私は R がとても気に入りました。R の構文は大量のデータの操作を簡素化し、よりシンプルに記述でき、ラムダ式を使用する必要がある従来の言語 (Python) を使用する場合よりも間違いが少ないからです。 R にはすべてのラムダ式がすでにあります。
    処理用のデータを準備しないようにするために、パッケージ いつでも、前処理なしで文字列を日付に変換できます。通貨文字列を数値に変換するには、readr パッケージを使用します。 .

    その結果、ビットコインの価格は 2019 年末までに 8,400 ドルになり、ドルの為替レートは 61 ルーブルになるという予測を受け取りました。これらの予測を信じるべきでしょうか?個人的にはそこまでの価値は無いと思うので… 本質を理解せずに数学的手法を使用することはできません。

    出典

    https:// facebook.github.io/prophet
    https://habr.com/company/ods/blog/323730/
    https://www.r-project.org/

    ソース コード

    https://gitlab.com/demensdeum/MachineLearning/tree/master/4prophet

    テスラが語る

    この投稿では、見積もりジェネレーター を作成するプロセスについて説明します。

    TL;DR

    トレーニングとテキスト生成用 –ライブラリを使用します textgenrnnhunspell とその C/python ライブラリ。共同作業では、テキストの生成を開始できます。テキストの約 90% は完全に判読できませんが、残りの 10% には少し意味が含まれており、手動で修正するとかなり見栄えの良いフレーズになります。
    最も簡単な方法は、Colaboratory で既製のニューラル ネットワークを起動することです。
    https://colab.research.google.com/drive/1-wbZMmxvsm3SoclJv11villo9VbUesbc(新しいタブで開きます)”>https://colab.research.google.com/drive/1-wbZMmxvsm3SoclJv11villo9VbUesbc

    ソース コード

    https://gitlab.com/demensdeum/MachineLearning/tree/master/3quotesGenerator

    出典

    https://karpathy.github.io/2015/05/21/rnn-effectiveness/https://medium.com/deep-learning-turkey/google-colab-free-gpu-tutorial-e113627b9f5dhttps://minimaxir.com/2018/05/text-neural-networks/
    https://github.com/wooorm/dictionaries (opens in a new tab)” href=”https://minimaxir.com/2018/05/text-neural-networks/” target=”_blank”>https://minimaxir.com/2018/05/text-neural-networks/
    https://karpathy.github.io/2015/05/21/rnn-effectiveness/
    https://medium.com/deep-learning-turkey/google-colab-free-gpu-tutorial-e113627b9f5d
    https://karpathy.github.io/2015/05/21/rnn-effectiveness/ (opens in a new tab)” href=”https://karpathy.github.io/2015/05/21/rnn-effectiveness/” target=”_blank”>https://karpathy.github.io/2015/05/21/rnn-effectiveness/
    https://medium.com/deep-learning-turkey/google-colab-free-gpu-tutorial-e113627b9f5d
    https://karpathy.github.io/2015/05/21/rnn-effectiveness/https://medium.com/deep-learning-turkey/google-colab-free-gpu-tutorial-e113627b9f5dhttps://github.com/wooorm/dictionaries

    ” rel=”noopener” target=”_blank”>https://github.com/wooorm/dictionaries (opens in a new tab)”>https://github.com/wooorm/dictionaries

    そこには何個のエラーがありますか?

    Hacker News で、著者がピーターセン-リンカーン法は、生物学者が鳥、猿、その他の動物の個体数を数えるために使用し、アプリケーションで * ドラム ロール* 虫を数える ために使用されます。

    自然生息地のバグ– ビッグフットの目撃情報 (デレク・ハットフィールド

    方法は非常に簡単です。鳥類学者は、特定の種の鳥、その任務 –これらの鳥の個体数を調べます。発見された鳥は両方の鳥類学者によってマークされ、その後、一般的な鳥の数が計算され、リンカーン指数の式に代入され、おおよその個体数サイズが得られます。
    さあ、アプリケーションです –方法も非常に簡単です。2 つの QA を受けて、アプリケーションのバグを見つけます。 1 人のテスターが 10 個のバグ (E1) を発見し、2 番目のテスターが 20 個のバグ (E2) を発見したとします。ここでバグの合計数を計算します。 3 (S)、式を使用してリンカーン指数を取得します。

    これはアプリケーション全体のバグ数の予測です。この例では、最大 66 個のバグがあります。

    迅速な例

    メソッドをテストするためのテストベンチを実装しました。ここで確認できます。
    https://paiza.io/projects/AY_9T3oaN9a-xICAx_H4qw?language=swift

    変更できるパラメータ:

    aliceErrorFindProbability = 20 – にします。 QA アリスによって発見されたバグの割合 (20%)
    let bobErrorFindProbability = 60 – QA Bob によって発見されたバグの割合 (60%)
    実際のバグ数 = 200 – にします。アプリケーションには実際にどれくらいのバグがあるのか​​

    最後の実行では、次のデータを受け取りました。
    推定バグ数: 213
    実際のバグ数: 200

    つまり、アプリケーションには 200 個のバグがあり、リンカーン指数では – という予測が得られます。 213:
    “アリスは 36 個のバグを見つけました”
    “ボブは 89 個のバグを見つけました”
    “一般的なバグ数: 15”

    推定バグ数:213件
    実際のバグ数: 200

    弱点

    この方法は、開発のすべての段階でアプリケーションのエラー数を評価するために使用できます。理想的にはバグの数が減少するはずです。 2 人のテスターから見つかったバグの数は異なるはずであり異なるバグが見つかったため、この方法の弱点には人的要因が含まれます。 共通のものを見つける必要があります。そうでない場合、メソッドは機能しません (一般的なバグがゼロ – ゼロ除算)< br />また、一般的なバグなどの概念には、 それらの共通性

    を理解する専門家の存在が必要です。 >

    ソース

    あといくつのエラーを見つける必要がありますか? –ジョン D. クック博士、社長
    The thrill of the chase – Brian Hayes

    ソースコード

    https://paiza.io/projects/AY_9T3oaN9a-xICAx_H4qw ?言語=スウィフト
    https://gitlab.com/demensdeum/statistics/tree/master/1_BugsCountEstimation/src

    Malevich、Black Squares OpenGLを破りました

    Malevichは、OpenGLの開発者に定期的にやって来ます。これは予期せずに大胆に起こります。プロジェクトを開始して、素晴らしいレンダリングの代わりに黒い広場を見るだけです:

    今日、私が黒い広場に訪れた理由について説明します。これは、OpenGLが画面に何も描かず、時には窓を透明にするために発見された問題について説明します。

    ツールを使用

    openglをデバッグすると、2つのツールが私を助けてくれました: renderdoc and and Apitrace 。 RenderDoc&#8211; OpenGLレンダリングプロセスをデバッグするためのツール、すべてを表示できます&#8211;頂点、シェーダー、テクスチャ、ドライバーからの借金メッセージ。 Apitrace&#8211;グラフィックAPIの課題を追跡するためのツールは、ダンプコールを行い、議論を示します。また、WDIFFを介して2つのダンプを比較する絶好の機会もあります(またはそれほど便利ではありません)

    あなたが働いている人とチェック

    古い依存関係SDL2、GLM、ASSIMP、GLEWを備えたオペレーティングシステムUbuntu 16.10があります。 Ubuntu 18.04の最新バージョンでは、ゲームのアセンブリを取得します death-mask 16.10でChrootとアセンブリを使用する場合、グラフィックスを使用してゲームの作業アセンブリを取得します。

    Ubuntu 18.04

    で何かが壊れたようです

    ldd は、Linkkaを同一ライブラリSDL2、GLに示しました。 Renderdocで非公式のビルドを運転して、頂点シェーダーの入り口でゴミが見られましたが、よりしっかりした確認が必要でした。二ナリック間の違いを理解するために、私はそれらを apitrace を通して両方を運転しました。ダンプの比較により、新鮮なUbuntaのアセンブリがOpenGLの見込み客のプログラムを破り、実際にゴミをそこに送信することがわかりました:

    マトリックスはGLMライブラリに集まります。 16.04&#8211からGLMをコピーした後;私は再びゲームのワーキングビルドを手に入れました。問題は、GLM 9.9.0の単一マトリックスの初期化の違いであり、コンストラクターのMAT4(1.0F)の引数を明確に示す必要があります。初期化とライブラリの著者である 「Noopener」> FSGL のテスト。 FSGLで欠陥を見つけた執筆の過程で、それらについてさらに説明します。

    誰が人生にいるのかを決定します

    OpenGLを使用した正しい作業では、 自発的に強制的に 特定のバージョンのコンテキストを要求する必要があります。したがって、SDL2を探します(コンテキストを初期化する前に、バージョンを厳密に配置する必要があります):

    <! - hilite.me->

    を使用して生成されたHTML

    “>

     sdl_gl_seettrtribute(sdl_gl_context_major_version、 3 );
    sdl_gl_settribute(sdl_gl_context_minor_version、 2 );
    sdl_gl_settribute(sdl_gl_context_profile_mask、sdl_gl_context_profile_core);

    たとえば、RenderDocは3.2未満のコンテキストでは動作しません。コンテキストを切り替えた後、同じ黒い画面を見る可能性が高い。なぜ?
    OpenGL 3.2のコンテキストは、VAOバッファーの存在を必要とする必要があるため、グラフィックドライバーの99%が機能しません。簡単に追加:
    <! - hilite.me->

    を使用して生成されたHTML

    ">

     glgenvertexarrays( 1  vao);
    glbindvertexaray(VAO);

    眠らないでください、凍結

    私はクブントゥで興味深い問題にも会いました。黒い正方形の代わりに透明に見えることがあり、時々すべてが正しくレンダリングされました。スタックオーバーフローでこの問題の解決策を見つけました:
    https://stackoverflow.com/questions/38411515/sdl2-opengl-window-appears-semi-transparent-sometimes

    FSGLテストレンダリングコードも存在していました睡眠(2s);そのため、XubuntuとUbuntuで正しいレンダリングを受け取り、アプリケーションを睡眠に送りましたが、Kubuntuでは、イルカの発売の80%と発射とターミナルの30%で透明なスクリーンを受け取りました。この問題を解決するために、ドキュメントで推奨されているように、Slevent調査の後、各フレームにレンダリングを追加しました。

    テストコード:
    https://gitlab.com/demensdeum/FSGLtests/blob/master/renderModelTest/

    ドライバーに話しかけます

    OpenGLは、アプリケーションとドライバーの間の通信チャネルをサポートし、それをアクティブにするには、GL_DEBUG_OUTPUT、GL_DEBUG_OUTPUT_SYNCHRONUSをオンにする必要があります。
    ここで初期化の例を撮ることができます:
    https://github.com/rock-core/gui-vizkit3d/blob/master/src/EnableGLDebugOperation.cpp

    怖がらないで成長を見守ってください

    この投稿では、shared_ptr スマート ポインターに関する私の失敗について話します。ゲーム Death-Mask に次のレベルの世代を実装した後、ある記憶に気づきました。漏れがある新しいレベルごとに、消費される RAM が + 1 メガバイト増加しました。一部のオブジェクトがメモリ内に残り、解放されなかったのは明らかです。この事実を修正するには、レベルが過負荷になったときにリソースを正しく実装する必要がありましたが、明らかに実行されていませんでした。私はスマート ポインターを使用していたので、この問題を解決するにはいくつかのオプションがありました。1 つ目はコードを手動でレビューすること (長くて退屈)、2 つ目は lldb デバッガーの機能と libstdc++ ソース コードを調べて自動追跡の可能性を調べることです。カウンタの変更。

    インターネット上では、すべてのアドバイスは要約すると、手動でコードを確認して修正し、問題のあるコード行を見つけたら鞭で自分を叩くというものでした。また、C++11 標準にスマート ポインターが登場する前に、90 年代と 2000 年代以降に開発されたすべての主要プロジェクトと同様に、メモリを操作するための独自のシステムを実装することも提案されました。すべてのshared_ptrsのコピーのコンストラクターでブレークポイントを使用しようとしましたが、数日経っても何も役に立ちませんでした。 libstdc++ ライブラリにロギングを追加するというアイデアがありましたが、人件費が膨大であることが判明しました。


    カウボーイ ビバップ (1998)

    プライベート変数shared_ptr –の変更を追跡するという解決策が突然思いつきました。 use_count。これは、lldb に組み込まれているウォッチポイントを使用して行うことができます。make_shared でshared_ptr を作成した後、次の行を使用して lldb のカウンターへの変更を追跡できます。

    セットを見る var カメラ._M_refcount._M_pi->_M_use_count

    「カメラ」はどこにあるのかこれは、カウンタの状態を追跡する必要があるshared_ptrオブジェクトです。もちろん、shared_ptr の内部は libstdc++ のバージョンによって異なりますが、一般的な原理は理解できます。ウォッチポイントをインストールした後、アプリケーションを起動して各カウンターの変更のスタックトレースを読み取り、コードを確認して (原文どおり!)、問題を見つけて修正します。私の場合、オブジェクトはキャッシュ テーブルとゲーム ロジック テーブルから解放されませんでした。この方法が、shared_ptr を使用する際のリークへの対処に役立ち、このメモリ ツールをさらに気に入っていただけることを願っています。デバッグを楽しんでください。

    単純な TensorFlow の例

    ここでは、ディープ ラーニングを使用するためのフレームワークを使用する簡単な例を紹介します。テンソルフロー。この例では、ニューラル ネットワークに正、負の数、およびゼロを検出するよう教えます。 TensorFlowCUDA 教えておきますが、このタスクは実際には簡単ではありません)

    分類問題を解決するには、分類子TensorFlow には、最小限の構成で動作する既製の高レベル分類器がいくつかあります。まず、DNNClassifier をトレーニングします。正、負の数値、ゼロを含むデータセット正しい「ラベル」を付けてください。人間のレベルでは、データセットは分類結果 (ラベル) を含む一連の数値です。

    10 –ポジティブ
    -22 –ネガティブ
    0 –ゼロ
    42 –ポジティブ
    な…分類のあるその他の番号

    次に、トレーニングが開始され、その後、データセットにも含まれていない数値を入力できるようになります。ニューラル ネットワークはそれらを正しく識別する必要があります。
    以下は、トレーニングおよび入力データ用のデータセット ジェネレーターを備えた分類子の完全なコードです。

    インポート テンソルフローインポート itertoolsインポートランダムから時刻 インポート時間クラス 分類番号:__number = 0__classifiedAs = 3def __init__(自分、番号):自分.__number =数値if 数値 == 0:self.__classifiedAs = <スパン スタイル="カラー: #0000dd; font-weight: ボールド;">0 <スパン スタイル="カラー: #888888;">#ゼロエリフ 番号 > 0:self.__classifiedAs = <スパン スタイル="カラー: #0000dd; font-weight: ボールド;">1 <スパン スタイル="カラー: #888888;">#ポジティブエリフ 番号 < 0:self.__classifiedAs = <スパン スタイル="カラー: #0000dd; font-weight: ボールド;">2 <スパン スタイル="カラー: #888888;">#否定的def 数値(自分):戻る 自分自身.__番号def classifiedAs(自分):戻る 自分自身.__classifiedAsdef classifiedAsString(classifiedAs):if として分類== 0:return 「ゼロ」elif== として分類されました 1:return 「ポジティブ」elif== として分類されました 2:return "Negative"def trainDatasetFunction():trainNumbers = []trainNumberLabels = []私は 範囲(-10001001):数値 = ClassifiedNumber(i)trainNumbers.append(number.number())trainNumberLabels.append(number.classifiedAs())return ( {"number" : trainNumbers } , trainNumberLabels)def inputDatasetFunction():グローバル ランダムシードrandom.seed(randomSeed) # 同じ結果を得る数字= []私は 範囲(0, 4):数値.append(random.randint(-99999999999999))return {"number" : 数値 }def メイン():print("TensorFlow 正負ゼロ数値分類子テスト (demensdeum 2017 による) (demensdeum@gmail. com)")maximalClassesCount = len(set< /span>(trainDatasetFunction()[1])) + 1numberFeature = tensorflow.feature_columnnumeric_column("数値")分類子= tensorflow.推定子DNNClassifier(feature_columns = [numberFeature], hidden_​​units = [102010]、n_classes = maximalClassesCount)ジェネレーター= 分類子.train(input_fn = trainDatasetFunction、ステップ = 1000).predict(input_fn =  inputDatasetFunction)inputDataset = inputDatasetFunction()結果 = リスト(itertools)。 islice(generator, len(inputDatasetFunction()["数値"])))i = 0結果の結果:print("番号: %d は %s として分類されました" % (inputDataset["number"][i], generatedAsString(result["class_ids"][0 ])))i += 1ランダムシード = time()主要()

    すべては main() メソッドで始まり、分類子が機能する数値列を設定します – tensorflow.feature_column.numeric_column(“number”) 次に、分類子のパラメーターを設定します。 API は毎日変更されるため、現在の初期化引数を説明することは役に立ちません。古いマニュアルに依存せず、インストールされているバージョンの TensorFlow のドキュメントを必ず参照する必要があります。

    次に、正、負、またはゼロに基づいてこれらの数値を正しく分類して、-1000 から 1000 までの数値のデータセットを返す関数 (trainDatasetFunction) を示すトレーニングが開始されます。次に、トレーニング データセットに含まれていない数値を入力として送信します。 -9999999 か​​ら 9999999 までのランダム (inputDatasetFunction) を使用して分類します。

    最後に、入力データ (itertools.islice) の数に基づいて反復を開始し、結果を出力して実行すると、驚くべき結果が得られます。

    番号: 4063470 は陽性として分類されました番号: 6006715 陽性として分類番号: -5367127 は陰性として分類されました番号: -7834276 陰性として分類

    iT’S ALIVE

    正直に言うと、私が教えていない数値さえも分類子が「理解」していることに今でも少し驚いています。将来的には、機械学習のトピックをより詳細に理解できるようになり、より多くのチュートリアルが提供されることを願っています。

    GitLab:
    https://gitlab.com/demensdeum/MachineLearning

    リンク:
    https://developers.googleblog.com/2017/09/introducing-tensorflow-datasets.html
    https://www.tensorflow.org/versions/master/api_docs/python/tf/estimator/DNNClassifier

    ビットコインの破壊

    このメモは行動を促すものではありません。ここではビットコインとブロックチェーン テクノロジーの脆弱で潜在的に危険な側面について説明します。

    脆弱なセンター

    ビットコインとブロックチェーンの動作原理は、共通のデータベースを保存および変更することであり、その完全なコピーは各ネットワーク参加者によって保存されます。このシステムは分散化しているように見えます。その理由は…データベースが保存される単一の組織/サーバーはありません。また、ブロックチェーンの主な利点として分散化が挙げられます。これにより、ユーザーの知らないうちにビットコインに何も起こらないことが保証されます。


    Elkin のブロック ペスト原則

    ブロックチェーンが機能するためには、各ユーザーがブロックチェーン データベースの最新コピーをダウンロードし、特定のルールに従ってそれを操作する必要があります。これらのルールには、あるウォレットから別のウォレットへの資金移動の確認時に各取引の一定割合(取引手数料)を受け取る、ビットコインマイニングの原則の実装が含まれます。ユーザーは自分で 1,000,000 ビットコインを引き出して、それで何かを購入することはできません。他のユーザーの場合、アカウント内の金額は変更されません。また、自分のデータベース内でのみ他人のウォレットから資金を引き出すオプションも除外されます。この変更は他のビットコイン ユーザーには反映されず、無視されます。
    現在の実装の脆弱性は、ビットコイン ウォレットがサーバー github 上に配置されていることです。分散化に関する広告スローガンを完全にブロックします。 単一のセンターからウォレットをダウンロードする必要はありません –開発者のウェブサイトでは、 ビットコインを扱うことは不可能です。つまり、開発者はいつでもネットワークを完全に制御できます。したがって、 ブロックチェーン技術自体は分散化されていますが、 ネットワークと連携するためのクライアントは単一のセンターからダウンロードされます。 >。
    攻撃シナリオ–すべての資金を引き出して第三者の口座に現金化するためのコードがウォレットに追加されたとします。その後、最新バージョンのウォレットのユーザーはすべてのビットコインを自動的に(回復の可能性なしで)失うことになります。多くのウォレット所有者がソースコードをチェックして構築しているとは思えないため、そのような攻撃の結果はほとんどのユーザーに影響を与えるでしょう。

    多数決で決定

    ブロックチェーンは分散型 P2P ネットワークであり、すべてのトランザクションはユーザー自身によって自動的に確認されます。攻撃シナリオ–残りの 49% の確認を無視するには、ネットワークの 51% を取得する必要があり、その後、攻撃者はビットコイン/ブロックチェーンを完全に制御できるようになります。これは、残りの部分と重複するコンピューティング能力を接続することで実現できます。この攻撃シナリオは、51% 攻撃として知られています。 p >

    できるかどうか教えてください

    最初にウォレットを起動すると、コンピュータはペア – を生成します。秘密鍵と公開鍵を使用して、正しい動作を保証します。これらのキーの一意性は非常に高いですが、コード ワード – を使用してキーを生成するオプションもあります。いわゆる “ブレイン ウォレット“。ユーザーは鍵を頭の中に保存するため、wallet.dat ファイルをバックアップする必要はありません。このコードワードを使用して、いつでもキーを再生成できます。攻撃シナリオ –攻撃者はコードワードを選択または学習し、秘密鍵と公開鍵のペアを生成してウォレットを制御します。

    コピーするだけ

    秘密鍵と公開鍵のペアは、wallet.dat ファイルに含まれています。このファイルにアクセスできるソフトウェア –ビットコインウォレットにアクセスできる。このような攻撃に対する保護は、ユーザーがウォレットとのすべてのトランザクションで覚えて入力する必要があるコードワードを追加することです。コードワードを追加した後、攻撃者が完全な制御を得るには、wallet.dat とコードワードが必要になります。
    また、コード ワードを入力すると、そのコード ワードがコンピュータのメモリに取り込まれるため、*他人* のメモリの読み取りを可能にするハードウェアやソフトウェアの脆弱性があると、ウイルス ソフトウェアがこのコード ワードを読み取ることが可能になることも付け加えておきます。

    システムエラー

    ビットコインの暗号化アルゴリズムをハッキングすると、ビットコインは即座に死に至ります。アルゴリズムの実装でエラーが発生し、それを発見した攻撃者がブロックチェーンの完全または部分的な制御を取得したとします。また、ビットコインで使用される暗号化アルゴリズムは、将来の量子コンピューター、その出現と量子アルゴリズムの実装によるハッキングから保護されていません。これにより、 ビットコインの現在の実装が終了します。ただし、これはポスト量子暗号化アルゴリズムに切り替えることで解決できます。

    WebGL + SDL + Emscripten

    最終的に、SDL 1 と Emscripten を使用して、Mika を WebGL に移植しました。

    次に、JavaScript でのビルドを正常に完了するためにコードの何を変更する必要があるかを説明します。

    <オル>

  • SDL 2 の代わりに SDL 1 を使用します。現時点では、emscripten 用の SDL 2 ポートがありますが、emscripten に組み込まれている SDL 1 を使用する方が適切であることがわかりました。コンテキストはウィンドウ内では初期化されませんが、SDL_SetVideoMode と SDL_OPENGL フラグを使用して初期化されます。バッファは SDL_GL_SwapBuffers() コマンドを使用して描画されます
  • JavaScript のループ方法による –レンダリングは別の関数に配置され、その定期的な呼び出しは emscripten_set_main_loop
  • 関数を使用して行われます。

  • アセンブリはキー “-s FULL_ES2=1“ を使用して実行する必要もあります
  • assimp ライブラリを放棄し、ファイル システムからモデルをロードし、ディスクからテクスチャをロードする必要がありました。必要なバッファはすべてデスクトップ バージョンにロードされ、emscripten を使用してアセンブリ用の C ヘッダー ファイルに挿入されました。
  • コード:
    https://github.com/demensdeum/OpenGLES3-Experiments/tree/master/9-sdl-gles-obj-textured-assimp-miku-webgl/mikuWebGL

    記事:
    http://blog.scottlogic.com/2014/03/12/native-code-emscripten-webgl-simmer-gently.html
    https://kripken.github.io/emscripten-site/docs/porting/multimedia_and_graphics/OpenGL-support.html

    モデル:
    https://sketchfab.com/models/7310aaeb8370428e966bdcff414273e7

    ミクしかいない

    OpenGL ES とコードを使用して FSGL ライブラリを操作した結果:

    次に、すべてがどのようにプログラムされ、 さまざまな興味深い問題が解決されたかを説明します。

    最初に、前の投稿で書いたように、OpenGL ES コンテキストを初期化します。さらに、コードのレンダリングと簡単な説明のみを考慮します。

    マトリックスがあなたを見ています

    ビデオ内のこのミクの図は三角形で構成されています。 OpenGL で三角形を描くには、座標 x、y、z で 3 つの点を指定する必要があります。 OpenGL コンテキストの 2D 座標で。
    3D 座標を含む図形を描画する必要があるため、射影行列を使用する必要があります。また、モデルを回転したり、ズームインしたり、その他の操作を行う必要もあります。この目的のためにモデル マトリックスが使用されます。実際、OpenGL にはカメラの概念がありません。オブジェクトは静的なカメラの周りを回転します。このために、ビュー マトリックスが使用されます。

    OpenGL ES の実装を簡素化するため –行列データは含まれません。 GLM など、不足している機能を追加するライブラリを使用できます。

    シェーダー

    開発者があらゆる方法であらゆるものを描画できるようにするには、OpenGL ES は頂点シェーダーとフラグメント シェーダーを実装する必要があります。頂点シェーダーは、レンダリング座標を入力として受け取り、行列を使用して変換を実行し、その座標を gl_Position に渡す必要があります。フラグメントまたはピクセル シェーダ –すでにカラー/テクスチャを描画し、オーバーレイを適用しています。

    GLSL でシェーダーを作成しました。現在の実装では、シェーダはメイン アプリケーション コードに C-string として直接組み込まれています。

    バッファ

    頂点バッファには頂点 (頂点) の座標が含まれており、このバッファにはテクスチャリング用の座標やシェーダに必要なその他のデータも含まれています。頂点バッファーを生成した後、ポインターを頂点シェーダーのデータにバインドする必要があります。これは、glVertexAttribPointer コマンドを使用して行われます。このコマンドでは、要素の数、データの先頭へのポインター、およびバッファーをトラバースするために使用されるステップ サイズを指定する必要があります。私の実装では、ピクセル シェーダの頂点座標とテクスチャ座標のバインドが行われます。ただし、フラグメント シェーダーへのデータ (テクスチャ座標) の転送は頂点シェーダーを通じて実行されることは言うまでもありません。これを実現するために、 座標は変数を使用して宣言されます。

    これにより、OpenGL は三角形の点をどの順序で描画するかを認識できるようになります –インデックスバッファ(インデックス)が必要になります。インデックス バッファには、そのような 3 つのインデックスを使用して配列内の頂点番号が含まれており、三角形が取得されます。

    テクスチャ

    まず、OpenGL 用のテクスチャをロード/生成する必要があります。このために、テクスチャは bmp ファイルからロードされる SDL_LoadBMP を使用しました。ただし、24 ビット BMP のみが適切であり、その色は通常の RGB 順序ではなく BGR で保存されることに注意してください。つまり、ロード後に赤チャンネルを青チャンネルに置き換える必要があります
    。テクスチャ座標は の形式で指定します。 UV、つまり、2 つの座標を転送するだけで済みます。テクスチャの出力はフラグメント シェーダーで行われます。これを行うには、テクスチャをフラグメント シェーダにバインドする必要があります。

    余分なものは何もありません

    私たちの指示によれば、OpenGL は 2D を通じて 3D を描画するため、–次に深度を実装し、非表示の三角形を選択します –カリングと深度バッファ (Z バッファ) を使用する必要があります。私の実装では、glEnable(GL_DEPTH_TEST); という 2 つのコマンドを使用して、深度バッファの手動生成を回避できました。および選択 glEnable(GL_CULL_FACE);
    また、射影行列の近平面がゼロより大きいことも必ず確認してください。ヌル近傍平面を使用した深さのチェックは機能しません。

    レンダリング

    頂点バッファ、インデックス バッファを意識的なもの (ミク モデルなど) で埋めるには、このモデルをロードする必要があります。このために、assimp ライブラリを使用しました。ミクは Wavefront OBJ 形式ファイルに配置され、assimp を使用してロードされ、assimp から頂点およびインデックス バッファーへのデータ変換が実装されました。

    レンダリングはいくつかの段階で行われます:

    <オル>

  • モデル行列の回転を使用してミクを回転します
  • 画面と深度バッファをクリアする
  • glDrawElements コマンドを使用して三角形を描画します。
  • 次のステージ – Emscripten を使用した WebGL でのレンダリングの実装。

    ソースコード:
    https://github.com/demensdeum/OpenGLES3-Experiments/tree/master/8-sdl-gles-obj-textured-assimp-miku
    モデル:
    https://sketchfab.com/models/7310aaeb8370428e966bdcff414273e7

     

    投影してみよう

    赤いティーポットを 3D で描いたので、その作り方を簡単に説明するのが私の義務だと考えています。

    最新の OpenGL は 3D を描画せず、2D 画面座標で三角形や点などを描画するだけです。
    OpenGL を使用して少なくとも何かを出力するには、頂点バッファを提供し、頂点シェーダーを作成し、必要なすべての行列 (投影、モデル、ビュー) を頂点シェーダーに追加し、 すべての入力データを関連付ける必要があります。シェーダでは、OpenGL でメソッド レンダリングを呼び出します。シンプルに見えますか?


    頂点バッファとは何ですか?描画する座標(x,y,z)のリスト
    頂点シェーダーは GPU にどの座標を描画するかを指示します。
    ピクセル シェーダーは何を描画するか (色、テクスチャ、ブレンディングなど) を指示します。
    マトリックスは、3D 座標をレンダリングできる 2D OpenGL 座標に変換します。

    次の記事では、コード例と結果を示します。

    SDL2 – OpenGL ES

    I love Panda3D game engine. But right now this engine is very hard to compile and debug on Microsoft Windows operation system. So as I said some time ago, I begin to develop my own graphics library. Right now it’s based on OpenGL ES and SDL2.
    In this article I am going to tell how to initialize OpenGL ES context and how SDL2 helps in this task. We are going to show nothing.

    King Nothing

    First of all you need to install OpenGL ES3 – GLES 3 libraries. This operation is platform dependant, for Ubuntu Linux you can just type sudo apt-get install libgles2-mesa-dev. To work with OpenGL you need to initialize OpenGL context. There is many ways to do that, by using one of libraries – SDL2, GLFW, GLFM etc. Actually there is no one right way to initialize OpenGL context, but I chose SDL2 because it’s cross-platform solution, code will look same for Windows/*nix/HTML5/iOS/Android/etc.

    To install sdl2 on Ubuntu use this command sudo apt-get install libsdl2-dev

    So here is OpenGL context initialization code with SDL2:

        SDL_Window *window = SDL_CreateWindow(
                "SDL2 - OGLES",
                SDL_WINDOWPOS_UNDEFINED,
                SDL_WINDOWPOS_UNDEFINED,
                640,
                480,
                SDL_WINDOW_OPENGL
                );
    	    
    
        SDL_GLContext glContext = SDL_GL_CreateContext(window);
    

    After that, you can use any OpenGL calls in that context.

    Here is example code for this article:
    https://github.com/demensdeum/OpenGLES3-Experiments/tree/master/3sdl-gles
    https://github.com/demensdeum/OpenGLES3-Experiments/blob/master/3sdl-gles/sdlgles.cpp

    You can build and test it with command cmake . && make && ./SDLGles

    量子ハッキング RSA

    先日、私は RSA 公開キー暗号化アルゴリズムの実装を書きました。このアルゴリズムの簡単なハックも行ったので、このトピックについて短いメモを書きたいと思います。 RSA の耐タンパー性は因数分解問題に基づいています。因数分解…ひどい言葉ですね…

    それほど怖いものではありません

    実際、キー作成の最初の段階で 2 つの乱数を取得しますが、その数値はそれ自体と 1 つで割り切れる必要があります。 素数の数字
    それらをpqと呼びます。次に、数値 n = p *q を取得する必要があります。これはさらなるキー生成に使用され、そのキーはメッセージの暗号化と復号化に使用されます。秘密鍵と公開鍵の最終バージョンでは、数値 n は変更せずに転送されます。
    RSA キーの 1 つと暗号化されたメッセージを手に持っているとします。キーから数字 n を取り出し、ハッキングを開始します。

    n を因数分解

    因数分解 –数値を素因数に分解すること。まず、キーから数値 n を取り出します (実際のキーでは openssl を使用して実行できます)。たとえば、n = 35 とします。次に、それを単純な因数に因数分解します。 n = 35 = 5 * 7、これがpqです。これで、受信した pq を使用してキーを再生成し、元の作成者の可視性を確保しながらメッセージを復号して暗号化できるようになります。

    量子ビットはそれほど単純ではありません

    RSA をそんなに簡単に破ることは本当に可能でしょうか?実際、いいえ、数値 pq は意図的に大きく取られているため、古典的なコンピューターの因数分解タスクには非常に長い時間がかかります (ある程度 10 年)。 br / >ただし、Shor の量子アルゴリズムを使用すると、非常に短時間で数値を因数分解することができます。現時点では、このトピックに関する記事では、指定された数値を乗算する時間が、つまり事実上瞬時にかかると記載されています。ショールのアルゴリズムが機能するには、多数の量子ビットを備えた量子コンピューターを実装する必要があります。 2001 年に、IBM は 7 量子ビットを使用して 15 という数字を因数分解しました。したがって、この瞬間までに長い時間待つ必要がありますが、その時までにポスト量子暗号化アルゴリズムに切り替えられているでしょう。

    タッチショール

    ピーター・ショアが因数分解アルゴリズムについて語る

    量子シミュレータでショールのアルゴリズムを試すには、ProjectQ の例には、ユーザーが入力した数値を因数分解できる shor.py の実装が含まれています。シミュレーターでは、実行時間は憂鬱ですが、量子コンピューターの動作を楽しく遊び心のあるシミュレーションのように見えます。

    記事:
    http://www.pagedon.com/rsa-explained-simply/my_programming/
    http://southernpacificreview.com/2014/01/06/rsa-key-generation-example/
    https://0day.work/how-i-recovered-your-private-key-or-why-small-keys-are-bad/

    RSA の Python 実装:
    https://github.com/demensdeum/RSA-Python

    Russian Quantum Hack and Number Generator

    [Translation may be, some day]

    Эта заметка увеличит длину вашего резюме на 5 см!

    Без лишних слов о крутости квантовых компьютеров и всего такого, сегодня я покажу как сделать генератор чисел на реальном квантовом процессоре IBM.
    Для этого мы будем использовать всего один кубит, фреймворк для разработки квантового ПО для python – ProjectQ, и 16 кубитовый процессор от IBM, онлайн доступ к которому открыт любому желающему по программе IBM Quantum Experience.

    Установка ProjectQ

    Для начала у вас должен быть Linux, Python и pip. Какие либо инструкции по установке этих базовых вещей приводить бесполезно, т.к. в любом случае инструкции устареют через неделю, поэтому просто найдите гайд по установке на официальном сайте. Далее устанавливаем ProjectQ, гайд по установке приведен в документации. На данный момент все свелось к установке пакета ProjectQ через pip, одной командой: python -m pip install –user projectq

    Ставим кубит в суперпозицию

    Создаем файл quantumNumberGenerator.py и берем пример генератора бинарного числа из документации ProjectQ, просто добавляем в него цикл на 32 шага, собираем бинарную строку и переводим в 32-битное число:

    import projectq.setups.ibm
    from projectq.ops import H, Measure
    from projectq import MainEngine
    from projectq.backends import IBMBackend
    
    binaryString = ""
    
    eng = MainEngine()
    
    for i in range(1, 33):
    
     qubit = eng.allocate_qubit()
    
     H | qubit
    
     Measure | qubit
    
     eng.flush()
    
     binaryString = binaryString + str(int(qubit))
    
     print("Step " + str(i))
    
    number = int(binaryString, 2)
    
    print("\n--- Quantum 32-Bit Number Generator by demensdeum@gmail.com (2017) ---\n")
    print("Binary: " + binaryString)
    print("Number: " + str(number))
    print("\n---")
    

    Запускаем и получаем число из квантового симулятора с помощью команды python quantumNumberGenerator.py

    Незнаю как вы, но я получил вывод и число 3974719468:

    --- Quantum 32-Bit Number Generator by demensdeum@gmail.com (2017) ---
    
    Binary: 11101100111010010110011111101100
    Number: 3974719468
    
    ---
    

    Хорошо, теперь мы запустим наш генератор на реальном квантовом процессоре IBM.

    Хакаем IBM

    Проходим регистрацию на сайте IBM Quantum Experience, подтверждаем email, в итоге должен остаться email и пароль для доступа.
    Далее включаем айбиэмовский движок, меняем строку eng = MainEngine() -> eng = MainEngine(IBMBackend())
    В теории после этого вы запускаете код снова и теперь он работает на реальном квантовом процессоре, используя один кубит. Однако после запуска вам придется 32 раза набрать свой email и пароль при каждой аллокации реального кубита. Обойти это можно прописав свой email и пароль прямо в библиотеки ProjectQ.

    Заходим в папку где лежит фреймворк ProjectQ, ищем файл с помощью grep по строке IBM QE user (e-mail).
    В итоге я исправил строки в файле projectq/backends/_ibm/_ibm_http_client.py:

    email = input_fun('IBM QE user (e-mail) > ') -> email = "quantumPsycho@aport.ru"
    
    password = getpass.getpass(prompt='IBM QE password > ') -> password = "ilovequbitsandicannotlie"
    

    Напишите свой email и password со-но.

    После этого IBM будет отправлять результаты работы с кубитом онлайн прямо в ваш скрипт, процесс генерации занимает около 20 секунд.

    Возможно в дальнейшем я доберусь до работы квантового регистра, и возможно будет туториал, но это не обязательно.
    Да прибудет с вами запутанность.

    Статья на похожую тему:
    Introducing the world’s first game for a quantum computer

    ThreeJS に基づく WebGL 上の悪いロボット

    本日、ゲーム Bad Robots のバージョンが、ライブラリ ThreeJS.
    >これは、Flame Steel Engine での最初の OpenGL (WebGL) ゲームです。
    ここで再生できます。
    http://demensdeum.com/games/BadRobotsGL/

    ThreeJS に基づく IOSystem ソース コードは、ここから入手できます。
    https://github.com/demensdeum/FlameSteelEngineGameToolkitWeb

    Porting SDL C++ Game to HTML5 (Emscripten)

    [Translation may be some day]

    За последний год я написал простейший движок Flame Steel Engine и набор классов для игровой разработки Flame Steel Engine Game Toolkit. В данной статье я опишу как производил портирование движка и SDL игры Bad Robots на HTML 5, с использованием компилятора Emscripten.

    Установка Hello World – Emscripten

    Для начала нужно установить Emscripten. Простейшим вариантом оказалось использование скрипта emsdk для Linux. На официальном сайте данный тип установки называется как “Portable Emscripten SDK for Linux and OS X“. Внутри архива есть инструкция по установке с использованием скрипта. Я производил установку в директорию ~/emsdk/emsdk_portable.

    После установки emscripten нужно проверить корректность работы компилятора, для этого создаем простейший hello_world.cpp и собираем его в hello_world.html с помощью команд:

    source ~/emsdk/emsdk_portable/emsdk_env.sh
    emcc hello_world.cpp -o hello_world.html

    После компиляции в папке появится hello_world.html и вспомогательные файлы, откройте его в лучшем браузере Firefox, проверьте что все работает корректно.

    Портирование кода игры

    В javascript нежелательно вызывать бесконечный цикл – это приводит к зависанию браузера. На данный момент корректная стратегия – запрашивать один шаг цикла у браузера с помощью вызова window.requestAnimationFrame(callback)

    В Emscripten данное обстоятельство решено с помощью вызова:

    emscripten_set_main_loop(em_callback_func func, int fps, int simulate_infinite_loop);

    Таким образом, нужно изменить код игры для корректного вызова метода emscripten. Для этого я сделал глобальный метод GLOBAL_fsegt_emscripten_gameLoop, в котором вызываю шаг цикла игрового контроллера. Главный игровой контроллер также вынесен в глобальную видимость:

    #ifdef __EMSCRIPTEN__
    
    void GLOBAL_fsegt_emscripten_gameLoop() {
    
    GLOBAL_fsegt_emscripten_gameController->gameLoop();
    
    }
    #endif
    

    Также для обработки специфических для Emscripten моментов, нужно использовать макрос __EMSCRIPTEN__.

    Ресурсы и оптимизация

    Emscripten поддерживает ресурсы и сборку с оптимизацией.

    Для добавления изображений, музыки и прочего, положите все файлы в одну папку, например data. Далее в скрипт сборки добавьте:

    emcc <файлы для сборки> –use-preload-plugins –preload-file data

    Флаг –use-preload-plugins включает красивый прелоадер в углу экрана, –preload-file добавляет указанный ресурс в файл <имя проекта>.data
    Код постоянно останавливался с ошибками доступа к ресурсам, пока я не включил оба этих флага. Также стоит заметить что для корректного доступа к ресурсам, желательно запускать игру на https (возможно и http) сервере, или отключить защиту локального доступа к файлам в вашем браузере.

    Для включения оптимизации добавьте флаги:

    -s TOTAL_MEMORY=67108864 -O3 -ffast-math

    TOTAL_MEMORY – оперативная память в байтах(?) необходимая для корректной работы игры. Вы можете использовать флаг для динамического выделения памяти, но тогда часть оптимизаций работать не будет.

    Производительность

    Код javascript из C++ работает гораздо медленнее, даже со включенными оптимизациями. Поэтому если ваша цель это разработка для HTML5, то приготовьтесь к ручной оптимизации алгоритмов игры, паралелльному тестированию, также к написанию javascript кода вручную в особо узких местах. Для написания javascript кода используется макрос EM_ASM. Во время реализации рейкастера на emscripten, мне удалось добиться повышения fps с 2-4 до 30 с помощью прямого использования методов canvas.drawImage, в обход обертки SDL->Canvas, что почти приравнялось к написанию всего на javascript.

    Поддержка SDL

    На данный момент почти не работает SDL_TTF, поэтому отрисовка шрифта для Game Score в BadRobots очень проста. SDL_Image, SDL_Mixer работают корректно, в mixer я проверил только проигрывание музыки.

    Исходный код Flame Steel Engine, Flame Steel Engine Game Toolkit, игры Bad Robots:

    https://github.com/demensdeum/BadRobots
    https://github.com/demensdeum/FlameSteelEngine
    https://github.com/demensdeum/FlameSteelEngineGameToolkit

    Статья на эту тему:

    https://hacks.mozilla.org/2012/04/porting-me-my-shadow-to-the-web-c-to-javascriptcanvas-via-emscripten/

    ECSの希釈


    Commission: Mad Scientist by Culpeo-Fox on DeviantArt

    この記事では、ECS パターンと Flame Steel Engine ゲーム ツールキットでの実装について大まかに説明します。 Entity Component System パターンは、ゲームなどで使用されます。 Unityエンジン上で。ゲーム内の各オブジェクトはコンポーネントで満たされたエッセンスです。  OOP があるのになぜこれが必要なのでしょうか?
    次に、ゲームの実行中にオブジェクトのプロパティ、動作、表示を直接変更します。このようなことは現実世界のアプリケーションでは見られません。パラメータ、オブジェクトのプロパティ、表示、サウンドの変化のダイナミクスは、会計ソフトウェアよりもゲームの特徴です。


    バナナを渡さなかった

    ゲームにバナナ クラスがあるとします。そしてゲームデザイナーはバナナを武器として使用したいと考えていました。現在のアーキテクチャでは、バナナは武器とは何の関係もないとしましょう。バナナを武器にする?全てのアイテムを武器に
    しますか?ECS は、この差し迫った問題に対する解決策を提供します。ゲーム内のすべてのオブジェクトはコンポーネントで構成されている必要があります。以前はバナナは Banana クラスでしたが、今後はこれを作成し、他のすべてのオブジェクトを Entity クラスにして、それらにコンポーネントを追加します。バナナが次のコンポーネントで構成されているとします。

    <オル>

  • 位置コンポーネント (ゲーム世界の座標 – x、y、z)
  • 回転コンポーネント (x、y、z 座標)
  • バナナのカロリー成分 (主人公は太りすぎないように注意してください)
  • バナナ画像コンポーネント
  • 現在、すべてのバナナに新しいコンポーネントを追加しています。これは、バナナが武器として使用できることを示すフラグです –武器のコンポーネント。ここで、ゲーム システムは、プレイヤーがバナナに近づいたことを認識すると、バナナに武器コンポーネントの存在を確認し、存在する場合は、プレイヤーにバナナを武装させます。
    私のゲーム「Flame Steel Call Of The Death Mask」では、ECS パターンが全体的に使用されています。オブジェクトはコンポーネントで構成されており、コンポーネント自体にコンポーネントを含めることができます。一般に、オブジェクトの分割 < – >私の実装ではコンポーネントが欠落していますが、これはさらにプラスです。

    screenshot_2016-09-24_14-33-43

    このスクリーンショットのショットガンはプレーヤーのコンポーネントであると同時に、2 番目のショットガンは通常のオブジェクトとしてゲーム マップ上にぶら下がっているだけです。
    このスクリーンショットでは、2 つのシステムが実行されています –シーンレンダラーとインターフェイスレンダラー。シーン レンダラーはマップ上でショットガン画像コンポーネントを操作し、インターフェイス レンダラーはプレイヤーの手の中でショットガン画像コンポーネントを操作します。

    関連リンク:
    https://habrahabr.ru/post/197920/
    https://www.youtube.com/watch?v=NTWSeQtHZ9M

    Flame Steel Engine ゲーム ツールキットのアーキテクチャ

    今日は、ゲーム開発用のツールキットであるFlame Steel Engine ゲーム ツールキットのアーキテクチャについて説明します。
    Flame Steel Engine ゲーム ツールキットを使用すると、Flame Steel Engine に基づいたゲームを作成できます。
    flamesteelgametoolkitschematics

    Flame Steel Engine のすべてのクラスは、接頭辞 FSE (Flame Steel ) で始まります。 >Engine)、FSEGTFlameSteelEngineG)強いアメTookit) ツールキット用。
    ゲーム シーン、オブジェクト、ボタンはすべて FSEObject のサブクラスであり、FSEGTGameData クラス内に置く必要があります。各FSEObject はFSESerialize インターフェイスを実装する必要があります。これにより、ゲーム データの保存/ロードが可能になり、保存メカニズムが提供されます。
    FSEController クラスは、FSEObject クラスのオブジェクトを操作します。ツールキットには、基本ゲーム シーン コントローラー クラスがあります。 FSEGTGameSceneController では、このクラスを継承してゲーム ロジックを実装できます。
    IOSystemFSEGTIOSystem インターフェイスのオブジェクトであり、このインターフェイスには FSEGTRendererFSEGTInputControllerFSEGTUIRendererFSEGTIOSystem はレンダラーを実装し、キーボード、ジョイスティック (入力デバイス) からデータを受信し、アクセス可能な インターフェイス要素のレンダリングを提供する必要があります。 href=”https://ru.wikipedia.org/wiki/%D0%92%D0%B2%D0%BE%D0%B4-%D0%B2%D1%8B%D0%B2%D0%BE%D0このプラットフォームの %B4″>入出力システム
    現時点では、SDL ライブラリに基づいたキーボード コントローラであるレンダラーが実装されており、 >FSEGTIOSDL システムクラス。

    Flame Steel Engine Raycaster Demo
    Flame Steel Engine Raycaster Demo

    将来的には、OpenGL に基づいて IOSystem を作成する予定です。クラスは FSEGTIOGLSystem と呼ばれます。 。任意のプラットフォームに基づいて IOSystem を作成する場合は、FSEGTIOSystem インターフェイスを使用し、そのための FSEGTRenderer レンダラ、FSEGTInputController を実装する必要があります。プラットフォーム 。

    Flame Steel Engine、ツールキット、ゲームのソース コード:
    https://github.com/demensdeum/FlameSteelCallOfTheDeathMask