Animação esquelética (Parte 1 – shader)

Neste artigo descreverei minha compreensão da animação esquelética, que é usada em todos os motores 3D modernos para animar personagens, ambientes de jogos, etc.
Começarei a descrição com a parte mais tangível – sombreador de vértice, pois todo o caminho de cálculo, por mais complexo que seja, termina com a transferência dos dados preparados para exibição para o sombreador de vértice.

A animação do esqueleto, após ser processada na CPU, vai para o vertex shader.
Deixe-me lembrá-lo da fórmula para vértice sem animação esquelética:
gl_Position = projeçãoMatrix * viewMatrix * modelMatrix * vértice;
Para quem não entende como surgiu essa fórmula, pode ler meu artigo que descreve o princípio de trabalhar com matrizes para exibição de conteúdo 3D no contexto do OpenGL.
Quanto ao resto – fórmula para implementar animação esquelética:
” vec4 animadoVertex = bone0matrix * vértice * bone0weight +”
“bone1matrix * vértice * bone1weight +”
“bone2matrix * vértice * bone2weight +”
“bone3matrix * vértice * bone3weight;\n”
” gl_Position = projeçãoMatrix * viewMatrix * modelMatrix * animadoVertex;\n”

Ou seja, multiplicamos a matriz de transformação óssea final pelo vértice e pelo peso desta matriz em relação ao vértice. Cada vértice pode ser animado por 4 ossos, a força do impacto é regulada pelo parâmetro peso do osso, a soma dos impactos deve ser igual a um.
O que fazer se menos de 4 ossos afetarem o vértice? Precisamos dividir o peso entre eles e fazer com que o impacto do restante seja igual a zero.
Matematicamente, multiplicar um peso por uma matriz é chamado de “multiplicação escalar de matriz”. Multiplicar por um escalar permite resumir o efeito das matrizes no vértice resultante.

As próprias matrizes de transformação óssea são transmitidas como uma matriz. Além disso, a matriz contém matrizes para todo o modelo como um todo, e não para cada malha separadamente.

Mas para cada vértice as seguintes informações são transmitidas separadamente:
– Índice da matriz que afeta o vértice
– Peso da matriz que afeta o vértice
Mais de um osso é transmitido, geralmente é utilizado o efeito de 4 ossos no vértice.
Além disso, a soma dos pesos dos 4 dados deve ser sempre igual a um.
A seguir, vamos ver como fica no shader.
Matriz matricial:
“bonesMatrices mat4 uniformes[kMaxBones];”

Informações sobre o efeito de 4 ossos em cada vértice:
“atributo vec2 bone0info;”
“atributo vec2 bone1info;”
“atributo vec2 bone2info;”
“atributo vec2 bone3info;”

vec2 – na coordenada X armazenamos o índice do osso (e convertemos para int no shader), na coordenada Y armazenamos o peso do impacto do osso no vértice. Por que você tem que transmitir esses dados em um vetor bidimensional? Porque o GLSL não suporta a passagem de estruturas legíveis em C com campos válidos para o shader.

A seguir darei um exemplo de obtenção das informações necessárias de um vetor para posterior substituição na fórmula animadoVertex:

“int bone0Index = int(bone0info.x);”
“float bone0weight = bone0info.y;”
“mat4 bone0matrix = boneMatrices[bone0Index];”

“int bone1Index = int(bone1info.x);”
“float bone1weight = bone1info.y;”
“mat4 bone1matrix = boneMatrices[bone1Index];”

“int bone2Index = int(bone2info.x);”
“float bone2weight = bone2info.y;”
“mat4 bone2matrix = boneMatrices[bone2Index];”

“int bone3Index = int(bone3info.x);”
“float bone3weight = bone3info.y;”
“mat4 bone3matrix = boneMatrices[bone3Index];”

Agora a estrutura de vértices preenchida na CPU deve ficar assim:
x, y, z, u, v, bone0index, bone0weight, bone1index, bone1weight, bone2index, bone2weight, bone3index, bone3weight

A estrutura do buffer de vértice é preenchida uma vez durante o carregamento do modelo, mas as matrizes de transformação são transferidas da CPU para o shader em cada quadro de renderização.

Nas partes restantes, descreverei o princípio de cálculo da animação na CPU, antes de transferi-la para o vertex shader, descreverei a árvore de nós ósseos, percorrendo a hierarquia de animação-modelo-nós-malha, matriz interpolação.

Fontes

http://ogldev.atspace.co. pt/www/tutorial38/tutorial38.html

Código fonte

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

Método de modelo

O método padrão refere-se a padrões de design comportamentais. O padrão descreve uma maneira de substituir parte da lógica de uma classe sob demanda, deixando a parte geral inalterada para os descendentes.

Cuban Cars

Suponha que estejamos desenvolvendo um banco cliente, considere a tarefa de desenvolver um módulo de autorização – o usuário deve ser capaz de fazer login no aplicativo usando dados de login abstratos.
O módulo de autorização deve ser multiplataforma, suportando diferentes tecnologias de autorização e armazenando dados criptografados de diferentes plataformas. Para implementar o módulo, escolhemos a linguagem Kotlin multiplataforma, usando a classe abstrata (protocolo) do módulo de autorização, escreveremos uma implementação para o telefone 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)
}

Agora para cada telefone/plataforma teremos que duplicar o código de envio de autorização ao servidor, isso é uma violação do princípio DRY. O exemplo acima é muito simples, em classes mais complexas haverá ainda mais duplicação. Para eliminar a duplicação de código, você deve usar o padrão Template Method.
Vamos mover as partes comuns do módulo para métodos imutáveis ​​e transferir a funcionalidade de transferência de dados criptografados para classes de plataforma específicas:

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)
}

Fontes

https://refactoring.guru/ru/design- padrões/método de modelo

Código fonte

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

Ponte Padrão

O padrão Bridge refere-se a padrões de projeto estrutural. Ele permite abstrair a implementação da lógica de classe movendo a lógica para uma classe abstrata separada. Parece simples, certo?

Suponha que implementemos um bot de spam que seja capaz de enviar mensagens para diferentes tipos de mensageiros.
Nós o implementamos usando um protocolo comum:

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)
        }
    }
}

Agora vamos imaginar o lançamento de um protocolo novo e mais rápido para envio de mensagens para o mensageiro iSekU. Para adicionar um novo protocolo, você precisará duplicar a implementação do bot iSekU, alterando apenas uma pequena parte dele. Não está claro por que fazer isso se apenas uma pequena parte da lógica da classe mudou. Com esta abordagem, o princípio DRY é violado; com o desenvolvimento do produto, a falta de flexibilidade se fará sentir através de erros e atrasos na implementação de novos recursos.
Vamos mover a lógica do protocolo para uma classe abstrata, implementando assim o padrão 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)
        }
    }
}

Uma das vantagens desta abordagem é, sem dúvida, a capacidade de expandir a funcionalidade da aplicação escrevendo plugins/bibliotecas que implementam lógica abstraída sem alterar o código da aplicação principal.
Qual é a diferença com o padrão Estratégia? Ambos os padrões são muito semelhantes, no entanto, Estratégia descreve *algoritmos* de comutação, enquanto Bridge permite alternar grandes partes de *qualquer lógica complexa*.

Fontes

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

Código fonte

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

Padrão de Cadeia de Responsabilidade

Cadeia de responsabilidade refere-se a padrões de design comportamental.


Ganna Dolbieva

A produtora Jah-Pictures fez um documentário sobre rastafáris comunistas da Libéria chamado “Red Dawn of Marley”. O filme é muito longo (8 horas), interessante, mas antes de ser lançado descobriu-se que em alguns países os planos e frases do filme podem ser considerados heresia e não receberão licença de distribuição. Os produtores do filme decidem recortar do filme momentos que contenham frases duvidosas, de forma manual e automática. É necessária uma dupla verificação para que os representantes do distribuidor não sejam simplesmente fuzilados em alguns países em caso de erro durante a inspeção e instalação manual.
Os países estão divididos em quatro grupos – países sem censura, com censura moderada, média e muito rigorosa. É tomada a decisão de usar redes neurais para classificar o nível de heresia no fragmento assistido do filme. Para o projeto, são adquiridos neurônios de última geração, caríssimos, treinados para diferentes níveis de censura, tarefa do desenvolvedor – tarefa do desenvolvedor. quebrar o filme em fragmentos e transmiti-los através de uma cadeia de redes neurais, de livre a estrita, até que uma delas detecte heresia, então o fragmento é transferido para revisão manual para posterior edição. É impossível passar por todos os neurônios, porque seu trabalho exige muito poder de computação (afinal, ainda temos que pagar pela eletricidade), basta parar no primeiro que funcionar.
Implementação ingênua de pseudocódigo:

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)")
        }
   }
}

Em geral, a solução com um array de classificadores não é tão ruim, porém! Vamos imaginar que não podemos criar um array, temos a oportunidade de criar apenas uma entidade classificadora, que já determina o tipo de censura para um fragmento de filme. Tais restrições são possíveis ao desenvolver uma biblioteca que expanda a funcionalidade da aplicação (plugin).
Vamos usar o padrão decorador – Vamos adicionar uma referência ao próximo classificador da cadeia à classe do classificador e interromper o processo de verificação na primeira classificação bem-sucedida.
Assim, implementamos o padrão Cadeia de Responsabilidade:

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)
}

Referências

https://refactoring.guru/ru/ padrões de design/cadeia de responsabilidade

Código Fonte

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

Decorador de padrões

O padrão Decorator refere-se a padrões de projeto estrutural.

O decorador é usado como alternativa à herança para estender a funcionalidade das classes.
Existe a tarefa de expandir a funcionalidade do aplicativo dependendo do tipo de produto. O cliente exige três tipos de produto – Básico, Profissional, Final.
Básico– conta o número de caracteres, Profissional – recursos Basic + imprime texto em letras maiúsculas, Ultimate – Básico + Profissional + imprime texto dizendo ULTIMATE.
Nós o implementamos usando herança:

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)

Agora existe a necessidade de implementação do produto “Ultimate Light” – Basic + Ultimate, mas sem os recursos da versão Professional. O primeiro OH! acontece, porque… você terá que criar uma classe separada para uma tarefa tão simples e duplicar o código.
Vamos continuar a implementação usando herança:

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)

O exemplo pode ser desenvolvido para maior clareza, mas mesmo agora a complexidade de suportar um sistema baseado em uma base de herança é visível – complicado e falta de flexibilidade.
Um decorador é um conjunto de protocolos que descreve a funcionalidade, uma classe abstrata contendo uma referência a uma instância concreta filha da classe decoradora que estende a funcionalidade.
Vamos reescrever o exemplo acima usando o padrão:

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)

Agora podemos criar variações de qualquer tipo de produto – basta inicializar os tipos combinados na fase de inicialização do aplicativo; o exemplo abaixo é a criação da versão Ultimate + Professional:

ultimateProfessionalProduct.textOperation(text: textToFormat)

Fontes

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

Código fonte

https://gitlab.com/demensdeum/patterns

Mediador de padrões

O padrão Mediador refere-se a padrões de design comportamentais.

Um dia você recebe um pedido para desenvolver um aplicativo de piadas – um aplicativo de piadas. o usuário pressiona um botão no meio da tela e ouve-se um som engraçado de um pato grasnando.
Depois de fazer o upload para a app store, o aplicativo se torna um sucesso: todo mundo grasna seu aplicativo, Elon Musk grasna em seu Instagram no próximo lançamento de um túnel de altíssima velocidade em Marte, Hillary Clinton supera Donald Trump no debate e vence as eleições na Ucrânia, sucesso!
A implementação ingênua do aplicativo é assim:

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

let duckButton = DuckButton()
duckButton.didPress()

Em seguida você decide adicionar o som de um cachorro latindo, para isso você precisa mostrar dois botões para selecionar o som – com um pato e um cachorro. Vamos criar duas classes de botões: DuckButton e DogButton.
Altere o código:

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

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

let duckButton = DuckButton()
duckButton.didPress()

let dogButton = DogButton()
dogButton.didPress()

Depois de mais um sucesso, adicionamos o som do guincho de um porco, agora existem três classes de botões:

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()

Os usuários reclamam que os sons se sobrepõem.
Adicionamos uma verificação para evitar que isso aconteça e, ao mesmo tempo, apresentamos as classes umas às outras:

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()

Na esteira do sucesso de seu aplicativo, o governo decide fazer uma lei segundo a qual charlatões, latidos e grunhidos em dispositivos móveis só podem ser feitos das 9h às 15h nos demais dias de semana; Nesse momento, o usuário do seu aplicativo corre o risco de ser preso por 5 anos por produção de som obsceno usando dispositivos eletrônicos pessoais.
Altere o código:

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()

De repente, o aplicativo lanterna começa a tirar o nosso do mercado, não vamos deixar que ele nos derrote e adicione uma lanterna pressionando o botão “oink-oink” e o restante dos botões:

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()

Como resultado, temos um aplicativo enorme que contém muito código de copiar e colar, as classes internas são conectadas entre si por um link morto - não há acoplamento fraco, tal milagre é muito difícil de manter e mudança no futuro devido às grandes chances de cometer um erro.

Use o Mediador

Vamos adicionar uma classe mediadora intermediária - ApplicationController. Esta classe fornecerá baixo acoplamento de objetos, garantirá a separação de responsabilidades entre classes e eliminará código duplicado.
Vamos reescrever:

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()

Muitos artigos sobre arquiteturas de aplicativos de interface de usuário descrevem o padrão MVC e seus derivados. O modelo é utilizado para trabalhar com dados lógicos de negócio, a visualização ou representação mostra informações ao usuário na interface/fornece interação com o usuário, o controlador é um mediador que garante a interação dos componentes do sistema.

Fontes

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

Código fonte

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/

Padrão “Instantâneo”

Neste post irei descrever o padrão “Snapshot” ou “Memento”

Este padrão refere-se a padrões “Comportamentais” padrões de design.

Suponha que estamos desenvolvendo um editor gráfico e precisamos adicionar a capacidade de reverter ações mediante um comando do usuário. Também é muito importante que os componentes do sistema não tenham acesso ao estado interno das “ações” seu estado interno, fornecendo uma interface externa clara e simples. Para resolver este problema, o padrão “Snapshot” ou “Guardião”.

Exemplo de trabalho “Instantâneo” apresentado abaixo:


Ao clicar, um sprite aparece, ao clicar na seta enrolada, a ação é cancelada – O sprite desaparece. O exemplo consiste em três classes:

  1. Tela na qual os sprites e a interface gráfica são exibidos.
  2. Controlador de tela, processa cliques e controla a lógica da tela.
  3. Os estados da tela que persistem a cada alteração são revertidos, se necessário, usando o controlador de tela.

No contexto do padrão “Snapshot” as aulas são:

  1. Tela – fonte, os estados desta classe são salvos como “instantâneos”, para reversão posterior mediante solicitação. Além disso, a fonte deve ser capaz de restaurar o estado ao transferir um “instantâneo”
  2. Controlador – custodiante, esta classe sabe como e quando salvar/reverter estados.
  3. Estado – snapshot, uma classe que armazena o estado da origem, além de informações de data ou um índice a partir do qual a ordem de reversão pode ser estabelecida com precisão.

Uma característica importante do padrão é que apenas a fonte deve ter acesso aos campos internos do estado salvo no instantâneo. Isso é necessário para proteger os instantâneos de alterações externas (de desenvolvedores úteis que desejam alterar algo ignorando o encapsulamento; , quebrando a lógica do sistema). Para implementar o encapsulamento, são usadas classes integradas e, em C++, elas usam a capacidade de especificar classes amigas. Pessoalmente, implementei uma versão simples sem encapsulamento para Rise e usando Generic ao implementar para Swift. Na minha versão – Memento fornece seu estado interno apenas para entidades do mesmo estado de classe:

Fontes

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

Código fonte

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

Padrão de visitante

Neste post irei descrever um padrão de design chamado “Visitante” ou “Visitante”
Este padrão pertence ao grupo de Padrões de comportamento.

Vamos encontrar um problema

Esse padrão é usado principalmente para contornar a limitação do envio único em linguagens de ligação antecipada.

Alice X por NFGPhoto (CC-2.0)
Vamos criar uma classe/protocolo abstrato Band, fazer uma subclasse de MurpleDeep, criar uma classe Visitor com dois métodos – um para enviar qualquer descendente de Band para o console, o segundo para gerar qualquer MurpleDeep, o principal é que os nomes (assinaturas) dos métodos sejam os mesmos e os argumentos diferem apenas por classe. Usando o método de impressão intermediário com o argumento Band, criamos uma instância de Visitor e chamamos o método visit para MurpleDeep.
Abaixo está o código em Kotlin:

A saída será “Esta é a classe Band

Como isso é possível?!

Por que isso acontece é descrito em palavras inteligentes em muitos artigos, inclusive em russo, mas sugiro que você imagine como o compilador vê o código, talvez tudo fique claro imediatamente:

Resolvendo o problema

Existem muitas soluções para resolver este problema, a seguir consideraremos uma solução usando o padrão Visitor.
Adicionamos o método Accept com o argumento Visitor à classe/protocolo abstrato, chamamos Visitor.visit(this) dentro do método e, em seguida, adicionamos uma substituição/implementação do método Accept à classe MurpleDeep, violando DRY de forma decisiva e calma, novamente escrevendo visitante.visita(este).< br />Código final:

Fontes

https://refactoring.guru/ru/ padrões de design/visitor-double-dispatch

Código fonte

https://gitlab.com/demensdeum/patterns

Padrão peso mosca

Neste post irei descrever o padrão estrutural “Leve” ou “Oportunista” (Peso mosca)
Este padrão pertence ao grupo de Padrões Estruturais.

Vejamos abaixo um exemplo de como o padrão funciona:


Por que é necessário? Para economizar RAM. Concordo que em tempos de uso generalizado do Java (que consome CPU e memória à toa), isso não é mais tão importante, mas vale a pena usar.
No exemplo acima, apenas 40 objetos são gerados, mas se você aumentar o número para 120.000, o consumo de memória aumentará proporcionalmente.
Vejamos o consumo de memória sem usar o padrão flyweight no navegador Chromium:

Sem usar um padrão, o consumo de memória é de aproximadamente 300 megabytes.

Agora vamos adicionar um padrão ao aplicativo e ver o consumo de memória:

Usando o padrão, o consumo de memória é de aproximadamente 200 megabytes, então economizamos 100 megabytes de memória no aplicativo de teste; em projetos sérios, a diferença pode ser muito maior.

Como funciona?

No exemplo acima, desenhamos 40 gatos, ou para maior clareza, 120 mil. Cada gato é carregado na memória como uma imagem png e, na maioria das renderizações, ele é convertido em um bitmap para renderização (na verdade, bmp), isso é feito para aumentar a velocidade, já que um png compactado leva muito tempo para ser renderizado. Sem usar o padrão, carregamos 120 mil fotos de gatos na RAM e desenhamos, mas ao usar o padrão “leve” carregamos um gato na memória e desenhamos 120 mil vezes com diferentes posições e transparência. A mágica é que implementamos coordenadas e transparência separadamente da imagem do gato durante a renderização, a renderização pega apenas um gato e usa um objeto com coordenadas e transparência para a renderização correta.

Como é isso no código?

A seguir estão exemplos da linguagem Rise

Sem usar um padrão:


A imagem do gato é carregada separadamente para cada objeto no loop – catImage.

Usando padrão:

Uma imagem de um gato é usada por 120 mil objetos.

Onde é usado?

Usado em estruturas GUI, por exemplo, o “reuse” (reutilizar) células da tabela UITableViewCell, o que aumenta a barreira de entrada para iniciantes que não conhecem esse padrão. Também comumente usado no desenvolvimento de jogos.

Código fonte

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

Fontes

https://refactoring.guru/ru/design-patterns/ peso mosca
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

Flutter como Michelle

[Sinta o poder da Inteligência Artificial]
Neste post vou lhe dizer como prever o futuro.

Na estatística existe uma classe de problemas – análise de série temporal. Tendo uma data e o valor de uma determinada variável, você pode prever o valor desta variável no futuro.
Primeiro eu queria implementar uma solução para este problema usando TensorFlow, mas encontrou a biblioteca Profeta por Facebook.
Prophet permite que você faça uma previsão com base em dados (csv) contendo colunas de data (ds) e valor variável (y). Você pode descobrir como trabalhar com ele na documentação do site oficial na seção Início rápido
Como conjunto de dados, usei um upload csv do site https://www.investing.com, ao implementar usei Linguagem R e API do Profeta para ele. Gostei muito do R, pois sua sintaxe simplifica o trabalho com grandes quantidades de dados, permite escrever de forma mais simples e cometer menos erros do que ao trabalhar com linguagens convencionais (Python), já que teria que trabalhar com expressões lambda, e no R você já tem todas as expressões lambda.
Para não preparar os dados para processamento, utilizei o pacote a qualquer momento, que pode converter strings em uma data, sem pré-processamento. A conversão de strings de moeda em números é feita usando o pacote readr

Como resultado, recebi uma previsão segundo a qual o Bitcoin custará US$ 8.400 até o final de 2019 e a taxa de câmbio do dólar será de 61 rublos. Devemos acreditar nessas previsões? Pessoalmente, acho que não vale a pena, porque… Você não pode usar métodos matemáticos sem compreender sua essência.

Fontes

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

Código fonte

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

Tesla fala

Nesta postagem, descreverei o processo de criação de um gerador de cotações.

TL;DR

Para treinamento e geração de texto – use a biblioteca textgenrnn, para filtrar frases você precisa usar a verificação ortográfica usando o hunspell e suas bibliotecas para C/python. Após o treinamento em Colaboratório, você pode começar a gerar texto. Cerca de 90% do texto será completamente ilegível, no entanto, os 10% restantes conterão um pouco de significado e, com modificação manual, as frases ficarão muito boas.
A maneira mais fácil é lançar uma rede neural pronta no Colaboratory:
https://colab.research.google.com/drive/1-wbZMmxvsm3SoclJv11villo9VbUesbc(abre em uma nova guia)”>https://colab.research.google.com/drive/1-wbZMmxvsm3SoclJv11villo9VbUesbc

Código-fonte

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

Fontes

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

Quantos erros você tem aí?

No Hacker News encontrei um artigo muito interessante no qual o autor sugere o uso do Método Petersen-Lincoln , que é usado por biólogos para contar a população de pássaros, macacos e outros animais, para *rufar de tambores* contar bugs no aplicativo.

Erro em habitat natural – Avistamento do Pé Grande por Derek Hatfield

O método é muito simples, pegamos dois ornitólogos, eles descobrem aves de uma determinada espécie, sua tarefa é – determinar o tamanho da população dessas aves. As aves encontradas são marcadas por ambos os ornitólogos, então o número de aves comuns é calculado, substituído na fórmula do índice de Lincoln e obtemos o tamanho aproximado da população.
Agora, para aplicativos – o método também é muito simples, pegamos dois QAs e eles encontram bugs na aplicação. Digamos que um testador encontrou 10 bugs (E1) e o segundo testador encontrou 20 bugs (E2), agora pegamos o número total de bugs – 3 (S), então usando a fórmula obtemos o índice Lincoln:

Esta é uma previsão do número de bugs em todo o aplicativo. No exemplo dado, há aproximadamente 66 bugs.

Exemplo rápido

Implementei um banco de testes para testar o método, você pode vê-lo aqui:
https://paiza.io/projects/AY_9T3oaN9a-xICAx_H4qw?language=swift

Parâmetros que podem ser alterados:

deixe aliceErrorFindProbability = 20 – porcentagem de bugs encontrados pelo QA Alice (20%)
deixe bobErrorFindProbability = 60 – porcentagem de bugs encontrados pelo QA Bob (60%)
deixe actualBugsCount = 200 – quantos bugs existem realmente no aplicativo

Na última execução recebi os seguintes dados:
Contagem de bugs estimados: 213
Contagem real de bugs: 200

Ou seja, existem 200 bugs no aplicativo, o Índice Lincoln dá uma previsão – 213:
“Alice encontrou 36 bugs”
“Bob encontrou 89 bugs”
“Contagem de bugs comuns: 15”

Contagem de bugs estimados: 213
Contagem real de bugs: 200

Fraquezas

Este método pode ser usado para avaliar o número de erros na aplicação em todos os estágios de desenvolvimento. idealmente, o número de bugs deve diminuir. Os pontos fracos do método incluem o fator humano, uma vez que o número de bugs encontrados por dois testadores deveria ser diferente e bugs diferentes foram encontrados, no entanto os mais comuns devem ser encontrados, caso contrário o método não funcionará (zero bugs comuns – divisão por zero)< br/>Além disso, um conceito como bugs comuns requer a presença de um especialista para entender seus pontos em comum.

Fontes

Quantos erros faltam encontrar? – John D. Cook, PhD, Presidente
The thrill of the chase – Brian Hayes

Código fonte

https://paiza.io/projects/AY_9T3oaN9a-xICAx_H4qw ?idioma=rápido
https://gitlab.com/demensdeum/statistics/tree/master/1_BugsCountEstimation/src

Vencemos Malevich, Black Squares OpenGl

Malevich chega periodicamente a qualquer desenvolvedor no OpenGL. Isso acontece inesperadamente e com ousadia, você apenas inicia o projeto e vê um quadrado preto em vez de uma renderização maravilhosa:

Hoje descreverei por que motivo fui visitado por um quadrado preto, os problemas encontrados por causa dos quais o OpenGL não desenha nada na tela e às vezes até torna a janela transparente.

Use ferramentas

Para depurar o OpenGL, duas ferramentas me ajudaram: renderdoc e Death-Mask O que não mostra nada na tela (apenas um quadrado). Ao usar chroot e montagem em 16.10 i recebo uma assembléia de trabalho do jogo com gráficos .

Parece que algo quebrou no Ubuntu 18.04

ldd mostrou a linkka às bibliotecas idênticas sdl2, gl. Dirigindo uma construção não trabalhadora no RenderDoc, vi lixo na entrada do shader do vértice, mas precisava de uma confirmação mais sólida. Para entender a diferença entre os binarticos, eu os dirigi através do Apitrace . A comparação de lixeiras me mostrou que a assembléia em uma nova ubunta quebra o programa das perspectivas no OpenGL, na verdade enviando lixo lá:

Matrizes se reúnem na biblioteca GLM. Depois de copiar o GLM de 16.04 – Recebi a construção do jogo novamente. O problema foi a diferença na inicialização de uma única matriz no GLM 9.9.0, é necessário indicar claramente o argumento MAT4 (1.0F) nele no construtor. Tendo mudado a inicialização e escrevendo O autor da biblioteca, eu comecei a fazer testes para fsgl . No processo de escrita que encontrei falhas no FSGL, as descrevo ainda mais.

Determine quem está na vida

Para o trabalho correto com o OpenGL, você precisa voluntariamente à força solicitar o contexto de uma determinada versão. Então, ele procura SDL2 (você precisa colocar a versão estritamente antes de inicializar o contexto):


 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);

Por exemplo, o renderDoc não funciona com contextos abaixo de 3.2. Gostaria de observar que, depois de alternar o contexto , há uma alta probabilidade de ver a mesma tela preta . Por que?

Como o contexto do OpenGL 3.2 deve exigir a presença de buffer VAO , sem o qual 99% dos drivers gráficos não funcionam. Adicione fácil:


 glgenvertexarrays ( 1 ,  &  vao);
Glbindvertexaray (VAO);

Não durma, congele

Também encontrei um problema interessante no Kubuntu, em vez de um quadrado preto, fui exibido transparente e, às vezes, tudo era renderizado corretamente. Encontrei a solução para esse problema no Stack Overflow:
https://stackoverflow.com/questions/38411515/sdl2-opengl-window-appears-semi-transparent-sometimes

O código de renderização do teste FSGL também estava presente Sleep (2s) ; Então, no Xubuntu e Ubuntu, recebi a renderização correta e enviei o pedido para dormir, mas no Kubuntu recebi uma tela transparente em 80% do lançamento do golfinho e 30% dos lançamentos e terminais. Para resolver esse problema, adicionei a renderização em cada quadro, após uma pesquisa do SDLEVENT, conforme recomendado na documentação.

Código de teste:
https://gitlab.com/demensdeum/FSGLtests/blob/master/renderModelTest/

Fale com o motorista

OpenGL suporta o canal de comunicação entre o aplicativo e o driver, para ativá-lo, você precisa ativar os sinalizadores GL_DEBUG_OUTPUT, GL_DEBUG_OUTPUT_SYNCHRONUS, Afixar o aviso GLDebugMessagEntrol e tie the callback através de .
Um exemplo de inicialização pode ser tomado aqui:
https://github.com/rock-core/gui-vizkit3d/blob/master/src/EnableGLDebugOperation.cpp

Não tenha medo, veja crescer

Neste post falarei sobre minhas desventuras com ponteiros inteligentes shared_ptr. Depois de implementar a geração do próximo nível em meu jogo Death-Mask, notei uma memória vazar . Cada novo nível proporcionou um aumento de + 1 megabyte na RAM consumida. É Obviamente que alguns objetos permaneceram na memória e não a liberaram. Para corrigir este facto, foi necessário implementar a correcta implementação de recursos quando o nível se encontra sobrecarregado, o que aparentemente não foi feito. Como usei ponteiros inteligentes, havia várias opções para resolver esse problema, a primeira envolvia a revisão manual do código (longa e enfadonha), enquanto a segunda envolvia pesquisar os recursos do depurador lldb e do código-fonte libstdc++ para a possibilidade de rastreamento automático contra alterações.

Na Internet, todos os conselhos se resumiam a revisar manualmente o código, corrigi-lo e bater-se com chicotes depois de encontrar a linha de código problemática. Também foi proposta a implementação de um sistema próprio para trabalhar com memória, como fazem todos os grandes projetos desenvolvidos desde as décadas de 90 e 2000, antes da chegada dos ponteiros inteligentes no padrão C++11. Tentei usar pontos de interrupção no construtor de uma cópia de todos os shared_ptrs, mas depois de vários dias nada de útil aconteceu. Houve uma ideia de adicionar log à biblioteca libstdc++, mas os custos de mão de obra acabaram sendo monstruosos.


Cowboy Bebop (1998)

A solução me ocorreu de repente na forma de rastrear alterações na variável privada shared_ptr – use_count. Isso pode ser feito usando watchpoints embutidos no lldb. Depois de criar um shared_ptr via make_shared, as alterações no contador no lldb podem ser rastreadas usando a linha:
.

assistir definir var camera._M_refcount._M_pi->_M_use_count

Onde a “câmera” este é um objeto shared_ptr cujo estado do contador precisa ser rastreado. Claro, os aspectos internos do shared_ptr irão variar dependendo da versão do libstdc++, mas o princípio geral pode ser entendido. Após instalar o watchpoint, iniciamos os aplicativos e lemos o stacktrace de cada alteração do contador, depois olhamos o código (sic!), encontramos o problema e corrigimos. No meu caso, os objetos não foram liberados das tabelas de cache e das tabelas lógicas do jogo. Espero que este método ajude você a lidar com vazamentos ao trabalhar com shared_ptr e ame ainda mais essa ferramenta de memória. Boa depuração.

Exemplo simples do TensorFlow

Apresento a sua atenção um exemplo simples de como trabalhar com um framework para trabalhar com Deep Learning – TensorFlow. Neste exemplo, ensinaremos uma rede neural a detectar números positivos, negativos e zero. Instalação do TensorFlow e CUDA Eu te instruo, esta tarefa realmente não é fácil)

Para resolver problemas de classificação, classificadores. TensorFlow tem vários classificadores de alto nível prontos que exigem configuração mínima para funcionar. Primeiro, treinaremos DNNClassifier usando conjunto de dados com números positivos, negativos e zero – com os “rótulos” corretos. No nível humano, um conjunto de dados é um conjunto de números com resultados de classificação (rótulos):

10 – positivo
-22– negativo
0 – zero
42 – positivo
… outros números com classificação

Em seguida, inicia-se o treinamento, após o qual você pode inserir números que nem foram incluídos no conjunto de dados – a rede neural deve identificá-los corretamente.
Abaixo está o código completo do classificador com gerador de conjunto de dados para treinamento e dados de entrada:

importar tensorflowimportar itertoolsimportar aleatóriode tempo tempo de importaçãoclass ClassifiedNumber:__número = 0__classifiedAs = 3def __init__(self, número):self.__number = númeroif número == 0:self.__classifiedAs = 0 # zeroelif número > 0:self.__classifiedAs = 1 # positivoelif número < 0:self.__classifiedAs = 2 # negativodef número(eu):return self.__númerodef classificadoAs(eu):return self.__classificadoAsdef classifiedAsString(classifiedAs):if classificadoAs == 0:return "Zero"elif classificadoAs == 1:return "Positivo"elif classificadoAs == 2:return "Negativo"def trainDatasetFunction():trainNumbers = []trainNumberLabels = []para i em intervalo(-1000, 1001):número = NúmeroClassificado(i)trainNumbers.append(number.number())trainNumberLabels.append(number.classifiedAs())return ( {"number" : trainNumbers } , trainNumberLabels)def inputDatasetFunction():global randomSeedrandom.seed(randomSeed) # para obter o mesmo resultadonúmeros = []para i em intervalo(0, 4):números.append(random.randint(-9999999, 9999999))return {"número" : números }def principal():print("Teste do classificador de números Positive-Negative-Zero do TensorFlow por demensdeum 2017 (demensdeum@gmail. com)")maximalClassesCount = len(definir< /span>(trainDatasetFunction()[1])) + 1numberFeature = tensorflow.feature_column. numeric_column("número")classificador = tensorflow.estimador. DNNClassifier(feature_columns = [numberFeature], unidades_ocultas = [10, 20, 10], n_classes = maximalClassesCount)gerador = classificador.train(input_fn = trainDatasetFunction, etapas = 1000).predict(input_fn =  inputDatasetFunction)inputDataset = inputDatasetFunction()resultados = lista(itertools. islice(generator, len(inputDatasetFunction()["número"])))i = 0para resultado em resultados:imprimir("número: %d classificado como %s" % (inputDataset["número"][i], classificadoAsString(result["class_ids"][0 ])))i += 1randomSeed = tempo()principal()

Tudo começa no método main(), definimos a coluna numérica com a qual o classificador irá funcionar – tensorflow.feature_column.numeric_column(“number”) Em seguida, os parâmetros do classificador são definidos. É inútil descrever os argumentos de inicialização atuais, pois a API muda todos os dias, e você definitivamente deve consultar a documentação da versão instalada do TensorFlow e não confiar em manuais desatualizados.

Em seguida, é lançado o treinamento indicando uma função que retorna um conjunto de dados de números de -1000 a 1000 (trainDatasetFunction), com a classificação correta desses números com base em positivo, negativo ou zero. A seguir, enviamos como entrada números que não estavam no conjunto de dados de treinamento – aleatório de -9999999 a 9999999 (inputDatasetFunction) para classificá-los.

Finalmente, lançamos iterações com base no número de dados de entrada (itertools.islice), imprimimos o resultado, executamos e ficamos surpresos:

número: 4063470 classificado como Positivonúmero: 6006715 classificado como Positivonúmero: -5367127 classificado como Negativonúmero: -7834276 classificado como Negativo

ESTÁ VIVO

Para ser honesto, ainda estou um pouco surpreso que o classificador *entenda* até mesmo aqueles números que eu não ensinei. Espero que no futuro eu entenda o tópico de aprendizado de máquina com mais detalhes e que haja mais tutoriais.

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

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

Quebrando Bitcoin

Esta nota não é um apelo à ação; aqui descreverei os aspectos fracos e potencialmente perigosos do Bitcoin e da tecnologia blockchain.

Centro vulnerável

O princípio de operação do Bitcoin e do blockchain é armazenar e alterar um banco de dados comum, cuja cópia completa é armazenada por cada participante da rede. O sistema parece descentralizado porque… não existe uma única organização/servidor no qual o banco de dados esteja armazenado. Além disso, a descentralização é apresentada como a principal vantagem do blockchain, pois garante que nada acontecerá com seus bitcoins sem o seu conhecimento.


O princípio da praga de bloqueio de Elkin

Para que o blockchain funcione, é necessário garantir que cada usuário baixe a cópia mais recente do banco de dados blockchain e trabalhe com ele de acordo com certas regras. Essas regras incluem a implementação do princípio da mineração de Bitcoin, recebendo um percentual de cada transação mediante confirmação (taxa de transação) da transferência de fundos de uma carteira para outra. O usuário não pode sacar 1.000.000 de bitcoins para si e comprar algo com eles, porque… Para outros usuários, a quantidade de dinheiro em sua conta permanecerá inalterada. Também está excluída a opção de retirar fundos da carteira de outra pessoa apenas dentro do seu próprio banco de dados porque esta mudança não será refletida em outros usuários de Bitcoin e será ignorada.
A vulnerabilidade da implementação atual é que a carteira Bitcoin está localizada no servidor github que bloqueia completamente os slogans publicitários sobre descentralização. Não há download de carteira de um único centro – site do desenvolvedor, é impossível trabalhar com bitcoin, ou seja, a qualquer momento os desenvolvedores têm controle total sobre a rede. Assim, a tecnologia blockchain em si é descentralizada, mas o cliente para trabalhar com a rede é baixado de um centro único.
Cenário de ataque – Digamos que um código foi adicionado à carteira para sacar todos os fundos e sacar para contas de terceiros, após o qual qualquer usuário da versão mais recente da carteira perderá todos os bitcoins automaticamente (sem possibilidade de recuperação). Duvido que muitos proprietários de carteiras verifiquem e construam a partir do código-fonte, portanto as consequências de tal ataque afetarão a maioria dos usuários.

A maioria decide

Blockchain é uma rede p2p descentralizada; todas as transações são confirmadas pelos próprios usuários automaticamente. Cenário de ataque – é necessário obter 51% da rede para ignorar as confirmações dos 49% restantes, após o que o invasor ganha controle total do Bitcoin/blockchain. Isto pode ser conseguido conectando o poder de computação que se sobrepõe ao resto. Este cenário de ataque é conhecido como ataque de 51%.

Adivinhe se puder

Quando você inicia a carteira pela primeira vez, o computador gera um par – chave privada e pública para garantir seu correto funcionamento. A exclusividade dessas chaves é extremamente alta, mas existe a opção de gerar chaves usando a palavra-código – a chamada “carteira cerebral“. Uma pessoa armazena chaves em sua cabeça; ela não precisa fazer backup do arquivo wallet.dat, porque A qualquer momento, as chaves podem ser regeneradas usando esta palavra de código. Cenário de Ataque – o invasor seleciona ou aprende a palavra-código, gera um par de chaves pública-privada e obtém o controle da carteira.

Basta copiar

O par de chaves pública-privada está contido no arquivo wallet.dat. Qualquer software que tenha acesso a este arquivo – tem acesso a uma carteira Bitcoin. A proteção contra tal ataque é a adição de uma palavra-código que o usuário deverá lembrar e inserir para todas as transações com a carteira. Depois de adicionar a palavra-código, o invasor precisará ter wallet.dat e a palavra-código para obter controle total.
Também vale a pena acrescentar que quando você insere uma palavra de código, ela vai para a memória do computador, portanto, qualquer vulnerabilidade de hardware e/ou software que permita a leitura da memória de *outra pessoa* permitirá que o software antivírus leia essa palavra de código.

Erro de sistema

Hackear os algoritmos de criptografia do Bitcoin levará instantaneamente à sua morte. Digamos que seja cometido um erro na implementação dos algoritmos, o invasor que o encontrar ganha controle total ou parcial sobre o blockchain. Além disso, os algoritmos de criptografia usados ​​no Bitcoin não estão protegidos contra hackers por futuros computadores quânticos, seu aparecimento e implementação de algoritmos quânticos – o que significa que eles não estão protegidos contra ataques de hackers. porá fim à implementaçãoatualdo Bitcoin. No entanto, isso pode ser resolvido mudando para algoritmos de criptografia pós-quântica.

WebGL + SDL + Emscript

Acabei migrando Mika para WebGL usando SDL 1 e Emscripten.

A seguir descreverei o que precisou ser alterado no código para que a compilação em JavaScript fosse concluída com sucesso.

  1. Use SDL 1 em vez de SDL 2. No momento existe uma porta SDL 2 para emscripten, mas achei mais apropriado usar o SDL 1 embutido no emscripten. O contexto não é inicializado na janela, mas usando SDL_SetVideoMode e o sinalizador SDL_OPENGL. O buffer é desenhado usando o comando SDL_GL_SwapBuffers()
  2. Devido à forma como o JavaScript faz loops – a renderização é colocada em uma função separada e sua chamada periódica é feita usando a função emscripten_set_main_loop
  3. A montagem também deve ser realizada com a chave “-s FULL_ES2=1
  4. Tive que abandonar a biblioteca assimp, carregando o modelo do sistema de arquivos e carregando a textura do disco. Todos os buffers necessários foram carregados na versão desktop e inseridos no arquivo c-header para montagem usando emscripten.

Código:
https://github.com/demensdeum/OpenGLES3-Experiments/tree/master/9-sdl-gles-obj-textured-assimp-miku-webgl/mikuWebGL

Artigos:
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

Modelo:
https://sketchfab.com/models/7310aaeb8370428e966bdcff414273e7

Só existe Miku

O resultado de trabalhar na biblioteca FSGL com OpenGL ES e código:

A seguir descreverei como tudo foi programado, vários problemas interessantes foram resolvidos.

Primeiro, inicializaremos o contexto OpenGL ES, conforme escrevi no post anterior. Além disso, consideraremos apenas a renderização e uma breve descrição do código.

A Matrix está observando você

Esta figura de Miku no vídeo consiste em triângulos. Para desenhar um triângulo no OpenGL, você precisa especificar três pontos com coordenadas x, y, z. em coordenadas 2D do contexto OpenGL.
Como precisamos desenhar uma figura contendo coordenadas 3D, precisamos usar uma matriz de projeção. Também precisamos girar, ampliar ou o que quisermos fazer com o modelo. Para tanto, é utilizada a matriz modelo. Não existe o conceito de câmera no OpenGL; na verdade, os objetos giram em torno de uma câmera estática; Para isso, é utilizada uma matriz de visualização.

Para simplificar a implementação do OpenGL ES – não contém dados de matriz. Você pode usar bibliotecas que adicionam funcionalidades ausentes, por exemplo, GLM.

Sombreadores

Para permitir que o desenvolvedor desenhe qualquer coisa, e de qualquer forma, o OpenGL ES deve implementar shaders de vértices e fragmentos. O vertex shader deve receber coordenadas de renderização como entrada, realizar transformações usando matrizes e passar as coordenadas para gl_Position. Fragmento ou pixel shader – já desenha cor/textura, aplica sobreposição, etc.

Eu escrevi shaders em GLSL. Na minha implementação atual, os shaders são integrados diretamente no código principal do aplicativo como strings C.

Buffers

O buffer de vértices contém as coordenadas dos vértices (vértices); este buffer também contém coordenadas para texturização e outros dados necessários para shaders. Depois de gerar o buffer de vértice, você precisa vincular o ponteiro aos dados do sombreador de vértice. Isso é feito com o comando glVertexAttribPointer, onde você precisa especificar o número de elementos, um ponteiro para o início dos dados e o tamanho do passo que será usado para percorrer o buffer. Na minha implementação, é feita a ligação de coordenadas de vértice e coordenadas de textura para o pixel shader. Porém, vale ressaltar que a transferência dos dados (coordenadas de textura) para o fragment shader é realizada através do vertex shader. Para conseguir isso, as coordenadas são declaradas usando variando.

Para que o OpenGL saiba em que ordem desenhar pontos para triângulos – você precisará de um buffer de índice (índice). O buffer de índice contém o número do vértice na matriz. Usando três desses índices, um triângulo é obtido.

Texturas

Primeiro você precisa carregar/gerar uma textura para OpenGL. Para isso utilizei SDL_LoadBMP, a textura é carregada a partir de um arquivo bmp. No entanto, é importante notar que apenas BMPs de 24 bits são adequados e as cores neles são armazenadas não na ordem RGB usual, mas em BGR. Ou seja, após o carregamento, é necessário substituir o canal vermelho por um azul.
As coordenadas de textura são especificadas no formato UV< /a>, ou seja, você só precisa transferir duas coordenadas. A saída da textura é feita no fragment shader. Para fazer isso, você precisa vincular a textura em um fragment shader.

Nada extra

Como, de acordo com nossas instruções, o OpenGL desenha de 3D a 2D – em seguida, implemente a profundidade e selecione triângulos invisíveis – você precisa usar seleção e um buffer de profundidade (Z-Buffer). Na minha implementação, consegui evitar a geração manual do buffer de profundidade usando dois comandos: glEnable(GL_DEPTH_TEST); e seleções glEnable(GL_CULL_FACE);
Certifique-se também de verificar se o plano próximo da matriz de projeção é maior que zero, porque verificar a profundidade com um plano próximo nulo não funcionará.

Renderização

Para preencher o buffer de vértice, buffer de índice com algo consciente, por exemplo o modelo Miku, você precisa carregar este modelo. Para isso usei a biblioteca assimp. Miku foi colocado em um arquivo no formato Wavefront OBJ, carregado usando assimp, e a conversão de dados de assimp para vértice e buffers de índice foi implementada.

A renderização ocorre em vários estágios:

  1. Gire Miku usando a rotação da matriz do modelo
  2. Limpando a tela e o buffer de profundidade
  3. Desenhar triângulos usando o comando glDrawElements.

Próxima etapa – Implementação de renderização em WebGL usando Emscripten.

Código fonte:
https://github.com/demensdeum/OpenGLES3-Experiments/tree/master/8-sdl-gles-obj-textured-assimp-miku
Modelo:
https://sketchfab.com/models/7310aaeb8370428e966bdcff414273e7

 

Projete

Tendo desenhado um bule vermelho em 3D, considero meu dever descrever brevemente como isso é feito.

O OpenGL moderno não desenha em 3D, apenas desenha triângulos, pontos, etc. em coordenadas de tela 2D.
Para produzir pelo menos algo usando OpenGL, você precisa fornecer um buffer de vértice, escrever um sombreador de vértice, adicionar todas as matrizes necessárias (projeção, modelo, visualização) ao sombreador de vértice,associar todos os dados de entrada com o shader, chame o método renderização em OpenGL. Parece simples?


Ok, o que é um buffer de vértice? Lista de coordenadas a serem desenhadas (x, y, z)
O vertex shader informa à GPU quais coordenadas desenhar.
O pixel shader informa o que desenhar (cor, textura, mesclagem, etc.)
As matrizes traduzem coordenadas 3D em coordenadas OpenGL 2D que podem ser renderizadas

Nos artigos a seguir fornecerei exemplos de código e resultados.

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

Hacking quântico RSA

Outro dia escrevi minha implementação do algoritmo de criptografia de chave pública RSA. Também fiz um hack simples desse algoritmo, então queria escrever uma breve nota sobre esse assunto. A resistência à violação do RSA é baseada em um problema de fatoração. Fatoração… Que palavra terrível…

Não é tão assustador assim

Na verdade, na primeira etapa da criação das chaves, pegamos dois números aleatórios, mas os números só devem ser divisíveis por si mesmos e um – Números primos.
Vamos chamá-los de p e q. A seguir devemos obter o número n = p *q. Ele será usado para geração adicional de chaves, as chaves, por sua vez, serão usadas para criptografar e descriptografar mensagens. Na versão final da chave privada e pública, o número n será transferido sem alterações.
Digamos que temos uma das chaves RSA e uma mensagem criptografada em mãos. Retiramos o número n da chave e iniciamos o hack.

Fatorar n

Fatoração – decomposição de um número em fatores primos. Primeiro, retiramos o número n da chave (em chaves reais você pode fazer isso usando openssl), digamos n = 35. Depois, fatoramos isso em fatores simples n = 35 = 5 * 7, este é e existem nossos p e q. Agora você pode regenerar as chaves usando o p, q recebido, descriptografar a mensagem e criptografá-la garantindo a visibilidade do autor original.

Qubits não são tão simples

É realmente possível quebrar qualquer RSA tão facilmente? Na verdade, não, os números p, q são considerados deliberadamente grandes, de modo que a tarefa de fatoração em computadores clássicos leva muito tempo (10 anos até certo ponto)< br/>Porém, usando o algoritmo quântico de Shor, é possível fatorar um número em um tempo muito curto. No momento, artigos sobre esse tema informam o momento de multiplicar um determinado número, ou seja, praticamente instantaneamente. Para que o algoritmo de Shor funcione, é necessário implementar computadores quânticos com grande número de qubits. Em 2001, a IBM fatorou o número 15 usando 7 qubits. Portanto, teremos que esperar muito tempo por esse momento, quando já teremos mudado para algoritmos de criptografia pós-quântica.

Toque curto

Peter Shore fala sobre seu algoritmo de fatoração

Para testar o algoritmo de Shor em um simulador quântico, você pode instalar ProjectQ, seus exemplos incluem uma implementação de shor.py que permite fatorar um número inserido pelo usuário. No simulador, o tempo de execução é deprimente, mas parece ser uma simulação divertida e lúdica do funcionamento de um computador quântico.

Artigos:
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/

Implementação Python do RSA:
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

Bad Robots em WebGL baseado em ThreeJS

Hoje uma versão do jogo Bad Robots foi lançada em um renderizador WebGL experimental baseado na biblioteca ThreeJS.
Este é o primeiro jogo OpenGL (WebGL) no Flame Steel Engine.
Você pode jogar aqui:
http://demensdeum.com/games/BadRobotsGL/

O código-fonte do IOSystem baseado em ThreeJS está disponível aqui:
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/

Diluindo SEC


Commission: Mad Scientist by Culpeo-Fox on DeviantArt

Neste artigo descreverei aproximadamente o padrão ECS e minha implementação no Flame Steel Engine Game Toolkit. O padrão Entity Component System é usado em jogos, incl. no motor Unity. Cada objeto no jogo é uma Essência preenchida com Componentes.  Por que isso é necessário se existe POO?
Em seguida, altere as propriedades, comportamento e exibição de objetos diretamente durante a execução do jogo. Essas coisas não são encontradas em aplicativos do mundo real; a dinâmica de alteração de parâmetros, propriedades de objetos, exibição e som é mais característica de jogos do que de software de contabilidade.


Não passamos bananas

Digamos que temos uma classe de banana em nosso jogo. E o designer do jogo queria que as bananas fossem usadas como armas. Digamos que na arquitetura atual as bananas não tenham nada a ver com armas. Fazendo de uma banana uma arma? Fazer todos os itens armas?
A ECS oferece uma solução para este problema premente – Todos os objetos do jogo devem consistir em componentes. Anteriormente banana era a classe Banana, agora faremos ela, e todos os outros objetos, a classe Entity, adicionando componentes a eles. Digamos que uma banana agora consiste nos seguintes componentes:

  1. Componente de posição (coordenadas no mundo do jogo – x, y, z)
  2. Componente de rotação (coordenadas x, y, z)
  3. Componente calórico de uma banana (o personagem principal não deve engordar muito)
  4. Componente de imagem de banana

Agora estamos adicionando um novo componente para todas as bananas, que é um sinalizador de que elas podem ser usadas como arma – Componente de arma. Agora, quando o sistema do jogo vê que um jogador se aproximou de uma banana, ele verifica a presença de um componente de arma na banana e, se isso acontecer, arma o jogador com uma banana.
No meu jogo Flame Steel Call Of The Death Mask, o padrão ECS é usado por toda parte. Os objetos são compostos de componentes e os próprios componentes podem conter componentes. Em geral, dividir o objeto < – > o componente está faltando na minha implementação, mas isso é até uma vantagem.

screenshot_2016-09-24_14-33-43

A espingarda nesta captura de tela é um componente do jogador e, ao mesmo tempo, a segunda espingarda simplesmente fica pendurada no mapa do jogo como um objeto comum.
Nesta captura de tela, há dois sistemas em execução – renderizador de cena e renderizador de interface. O renderizador de cena funciona com o componente de imagem shotgun no mapa, o renderizador de interface funciona com o componente de imagem shotgun nas mãos do jogador.

Links relacionados:
https://habrahabr.ru/post/197920/
https://www.youtube.com/watch?v=NTWSeQtHZ9M

Arquitetura do kit de ferramentas do jogo Flame Steel Engine

Hoje falarei sobre a arquitetura do kit de ferramentas para desenvolvimento de jogos Flame Steel Engine Game Toolkit.
O Flame Steel Engine Game Toolkit permite que você crie jogos baseados no Flame Steel Engine:
flamesteelgametoolkitschematics

Todas as classes do Flame Steel Engine começam com o prefixo FSE (Flame Steel E motor) eFSEGT (FlameSteelEMotorG forte>ame Toolkit) para kit de ferramentas.
Cena do jogo, objetos e botões são subclasses de FSEObject e devem estar dentro da classe FSEGTGameData. Cada FSEObject deveimplementar a interface FSESerialize, isso permitirá que você salve/carregue dados do jogo e forneça um mecanismo de salvamento.
A classe FSEController funciona com objetos da classe FSEObject. O kit de ferramentas possui uma classe base de controlador de cena de jogo – FSEGTGameSceneController, você pode herdar esta classe para implementar a lógica do seu jogo.
IOSystem é um objeto da interface FSEGTIOSystem, esta interface contém FSEGTRenderer, FSEGTInputController, FSEGTUIRenderer forte>.FSEGTIOSystem deve implementar um renderizador, receber dados do teclado, joysticks (dispositivos de entrada) e fornecer renderização de elementos de interface para sistemas de entrada/saída desta plataforma.
No momento, foi implementado um renderizador, um controlador de teclado baseado na biblioteca SDL, está disponível na classe FSEGTIOSDLSystem

.

Flame Steel Engine Raycaster Demo
Flame Steel Engine Raycaster Demo

Planos futuros para criar um IOSystem baseado em OpenGL, a classe será chamada FSEGTIOGLSystem . Se você quiser criar um IOSystem baseado em qualquer plataforma, então você precisa usar a interface FSEGTIOSystem e implementar o renderizador FSEGTRenderer, FSEGTInputController para isso. plataforma.

Código fonte do Flame Steel Engine, kit de ferramentas, jogo:
https://github.com/demensdeum/FlameSteelCallOfTheDeathMask