1643097632
Every university have a unique formatting method, and it’s can be used by any students when preparing their academic papers. Why does it appear? First of all, every learner has his/her topic and then they try to find the best way to research the interesting facts about themselves and make the most comfortable and good articles for them. In another situation, we have a parliament where people have the freedom to write and express what They want to hear.
Students don’t have a choice but to choose the specially designed essay and academy track cases. These problem are mainly because article composing process are so important for the education system, asnt way it’s not possible to allow a student to present a really high quality study project for avoiding performance by the panel or getting low marks. So if You decide to compose an extensive literature review, Your task Uka should be able to say something which is more difference with regards to various trilogy of subjects and themes and finally create a definitive text with a well structured and with a lot of references. Visit the link for your grademiners reviews.
We hope that our tips will be useful for You, and if you feel that the possibilities of finding an attractive and readable information for your disposal, run to the internet and search it. From there, you’ll be on the right path to creating a magnificent argumentative and critical essays.
The nest of academicians is in the composition of question, how to argue for a particular point and successfully defend it. It’s very crucial to understand the three primary constituents of an argument
Because this is a lengthy and essential document, and it needs to be economically persuasive, often one has to think of a secondary objective of it. This will include the strong points and the importance of the theme.
Always refer to the rules of written mediums, always keep in mind that if the work has aorymatical mistakes and plagiarism, it’s a marked increase in the risks.
Never afraid to ask someone to proofread it, just like with regular assignments, to be sure that it’s okay. One of the outstanding advantages of asking somebody to check it for errors is that it’s free from counterchecking it and only if it’s perfect.
A first-rate would be extremely advantageous for anybody concerned that he might score lower grades, never settling for a second-grade.
Useful links:
How to Handle College Essays in the Most Appropriate Way
Order Term Paper Online: Steps to Follow
1643097632
Every university have a unique formatting method, and it’s can be used by any students when preparing their academic papers. Why does it appear? First of all, every learner has his/her topic and then they try to find the best way to research the interesting facts about themselves and make the most comfortable and good articles for them. In another situation, we have a parliament where people have the freedom to write and express what They want to hear.
Students don’t have a choice but to choose the specially designed essay and academy track cases. These problem are mainly because article composing process are so important for the education system, asnt way it’s not possible to allow a student to present a really high quality study project for avoiding performance by the panel or getting low marks. So if You decide to compose an extensive literature review, Your task Uka should be able to say something which is more difference with regards to various trilogy of subjects and themes and finally create a definitive text with a well structured and with a lot of references. Visit the link for your grademiners reviews.
We hope that our tips will be useful for You, and if you feel that the possibilities of finding an attractive and readable information for your disposal, run to the internet and search it. From there, you’ll be on the right path to creating a magnificent argumentative and critical essays.
The nest of academicians is in the composition of question, how to argue for a particular point and successfully defend it. It’s very crucial to understand the three primary constituents of an argument
Because this is a lengthy and essential document, and it needs to be economically persuasive, often one has to think of a secondary objective of it. This will include the strong points and the importance of the theme.
Always refer to the rules of written mediums, always keep in mind that if the work has aorymatical mistakes and plagiarism, it’s a marked increase in the risks.
Never afraid to ask someone to proofread it, just like with regular assignments, to be sure that it’s okay. One of the outstanding advantages of asking somebody to check it for errors is that it’s free from counterchecking it and only if it’s perfect.
A first-rate would be extremely advantageous for anybody concerned that he might score lower grades, never settling for a second-grade.
Useful links:
How to Handle College Essays in the Most Appropriate Way
1647946800
No importa la experiencia que tenga, el oficio del desarrollo de software no se puede practicar sin cometer errores. ¡ Pero lo que separa al promedio de los grandes programadores es que saben cómo deshacer sus errores!
Si está utilizando Git como su sistema de control de versiones, ya tiene a mano una gran cantidad de "herramientas de deshacer". ¡Esta publicación te mostrará cinco formas poderosas de deshacer errores con Git !
La codificación suele ser un proceso complicado. A veces, se siente como si estuviera dando dos pasos hacia adelante y uno hacia atrás. En otras palabras: parte del código que ha producido es excelente... pero parte no tanto. Aquí es donde Git puede ayudarte: te permite mantener lo que es bueno y descartar los cambios que ya no quieres.
Echemos un vistazo a un escenario de ejemplo con algunos cambios "locales" (también conocidos como cambios que aún no hemos confirmado).
Nota: para una mejor visión general y una visualización más clara, estoy usando el cliente de escritorio Tower Git en algunas de mis capturas de pantalla. No necesita Tower para seguir este tutorial.
Abordemos el problema general.css
primero. Los cambios que hicimos fueron en una dirección totalmente equivocada. Vamos a deshacerlos todos y volver a crear el último estado confirmado de ese archivo:
$ git restore css/general.css
Tenga en cuenta que, alternativamente, podría haber usado el git checkout
comando para lograr el mismo resultado. Pero debido a git checkout
que tiene tantos trabajos y significados diferentes, prefiero el git restore
comando un poco más nuevo (que se enfoca únicamente en este tipo de tareas).
Nuestro segundo problema index.html
es un poco más complicado. Algunos de los cambios que hicimos en este archivo son realmente geniales, mientras que solo algunos de ellos necesitan ser deshechos.
Una vez más, git restore
viene al rescate, pero esta vez con la -p
bandera, porque queremos bajar al nivel de "parche":
$ git restore -p index.html
Luego, Git lo tomará de la mano y le preguntará, por cada parte de los cambios en ese archivo, si desea descartarlo o no.
Notarás que escribí "n" para el primer fragmento (para conservarlo) e "y" para el segundo (para descartarlo). Una vez que finaliza el proceso, puede ver que solo sobrevivió la primera parte valiosa de los cambios, ¡tal como queríamos!
A veces querrá restaurar un archivo específico a una revisión específica . Por ejemplo, sabe que index.html
funcionaba bien en algún momento anterior, pero ahora no. Ahí es cuando desea retroceder el tiempo, ¡pero solo para este archivo específico y no para todo el proyecto!
Lo primero que tenemos que averiguar es qué revisión exacta queremos restaurar. Con el conjunto correcto de parámetros, puede obtener el git log
comando para mostrarle el historial de nuestro único archivo:
$ git log -- index.html
Esto nos muestra solo las confirmaciones donde index.html
se ha cambiado, lo cual es bastante útil para encontrar la revisión de "manzana podrida" que rompió las cosas.
Si necesita más información y desea ver el contenido de esas confirmaciones, puede hacer que Git le muestre los cambios reales en estas confirmaciones con la -p
bandera:
$ git log -p -- index.html
Una vez que hayamos encontrado la confirmación incorrecta donde rompimos nuestro pequeño archivo encantador, podemos continuar y corregir el error. ¡Lo haremos restaurando el archivo en la revisión anterior a la mala! Esto es importante: no queremos restaurar el archivo en el compromiso que introdujo el error, sino en el último buen estado: ¡un compromiso antes de eso!
$ git checkout <bad-commit-hash>~1 -- index.html
Agregar ~1
al hash de la confirmación incorrecta le indicará a Git que haga exactamente eso: vaya una revisión antes de la referenciada.
Después de ejecutar ese comando, encontrará index.html
que se modifica en su copia de trabajo local: ¡Git restauró esa última buena revisión del archivo para nosotros!
Otra gran herramienta de deshacer en el botiquín de primeros auxilios de Git es el "Reflog". Puede pensar en él como un diario donde Git protocoliza todos los movimientos del puntero HEAD que ocurren en su repositorio local, como confirmaciones, pagos, fusiones y rebases, selecciones y reinicios. ¡Todas las acciones más interesantes están muy bien registradas aquí!
Un diario así, por supuesto, es perfecto para aquellas ocasiones en las que las cosas van mal. Entonces, comencemos causando una pequeña catástrofe, que luego podemos reparar con Reflog.
Digamos que estabas convencido de que tus últimas confirmaciones no fueron buenas: querías deshacerte de ellas y, por lo tanto, git reset
solías volver a una revisión anterior. Como resultado, las confirmaciones "malas" desaparecieron de tu historial de confirmaciones, tal como querías.
Pero como suele pasar en la vida, te das cuenta de que fue una mala idea: al final, ¡las confirmaciones no fueron tan malas después de todo! ¡Pero la mala noticia, por supuesto, es que acabas de borrarlos del historial de confirmaciones de tu repositorio! 😱
¡Este es un caso clásico para la herramienta Reflog de Git! Veamos cómo puede salvarte el cuello:
$ git reflog
Vamos a descomponer esto poco a poco:
git reflog
es todo lo que necesita.Para restaurar este estado anterior, podemos usar de git reset
nuevo o simplemente crear una nueva rama:
$ git branch happy-ending e5b19e4
Como podemos verificar felizmente, ¡nuestra nueva rama contiene las confirmaciones que pensamos que habíamos perdido debido a nuestro git reset
contratiempo accidental!
El Reflog también puede ser útil en otras situaciones. Por ejemplo, cuando ha eliminado sin darse cuenta una rama que realmente (!) No debería tener. Echemos un vistazo a nuestro escenario de ejemplo:
$ git branch
* feature/analytics
master
Supongamos que nuestro cliente/líder de equipo/director de proyecto nos dice que la hermosa analytics
función en la que hemos estado trabajando ya no la desea. Ordenados como somos, borramos la feature/analytics
rama correspondiente, ¡claro!
Arriba, puede ver que, en nuestro escenario de ejemplo, tenemos esa rama actualmente desprotegida: feature/analytics
es nuestra rama HEAD actual. Para poder eliminarlo, primero tenemos que cambiar a otra rama:
$ git checkout master
$ git branch -d feature/analytics
error: The branch 'feature/analytics' is not fully merged.
If you are sure you want to delete it, run 'git branch -D feature/analytics'.
Git nos dice que estamos a punto de hacer algo realmente serio: dado feature/analytics
que contiene confirmaciones únicas que no están presentes en ningún otro lugar, eliminarlo destruirá algunos datos (potencialmente valiosos). Bueno... dado que la función ya no es necesaria, podemos continuar:
$ git branch -D feature/analytics
Deleted branch feature/analytics (was b1c249b).
Probablemente ya anticipó lo que viene ahora: ¡nuestro cliente / líder de equipo / gerente de proyecto felizmente nos dice que la función está de vuelta en el juego! 🥳 ¡Después de todo, quieren perseguirlo! 🎉
Una vez más, nos enfrentamos a una situación fea en la que podríamos haber perdido datos valiosos. Entonces, veamos si el Reflog puede salvarnos el cuello una vez más:
$ git reflog
Las buenas noticias se hacen evidentes si miras más de cerca. Antes de que pudiéramos (desastrosamente) eliminar nuestra rama, tuvimos que realizar un git checkout
cambio para alejarnos de ella (ya que Git no permite eliminar la rama actual). Por supuesto, esto checkout
también se registró en el Reflog. Para restaurar nuestra rama eliminada, ahora podemos simplemente tomar el estado anterior como punto de partida para una nueva rama:
$ git branch feature/analytics b1c249b
Y listo: ¡nuestra rama ha vuelto de entre los muertos! 🌸💀🌺
Si usa una GUI de escritorio como Tower , deshacer errores como estos suele ser tan simple como presionar CMD + Z.
Cerremos con otro verdadero clásico del “¡Oh, no!” departamento: cometer en la rama equivocada.
Hoy en día, muchos equipos tienen una regla que prohíbe comprometerse directamente con ramas de larga duración como "principal", "maestro" o "desarrollar". Por lo general, las nuevas confirmaciones deberían llegar a esas ramas solo a través de fusiones/rebases. Y sin embargo, a veces nos olvidamos y nos comprometemos directamente…
Tomemos el siguiente escenario como ejemplo.
Deberíamos haber cometido el feature/newsletter
, pero sin darnos cuenta apretamos el gatillo en la master
rama. Veamos la solución, paso a paso.
Primero, tenemos que asegurarnos de que estamos en la rama correcta esta vez, así que estamos revisando feature/newsletter
:
$ git checkout feature/newsletter
Ahora podemos mover con seguridad esa confirmación usando el cherry-pick
comando:
$ git cherry-pick 26bf1b48
Podemos echar un vistazo feature/newsletter
y veremos que, ahora, el compromiso también existe aquí.
Hasta aquí todo bien. Pero cherry-pick
no eliminó la confirmación de la master
rama, donde nunca debería haber estado. Entonces tendremos que limpiar nuestro desorden allí también:
$ git checkout master
$ git reset --hard HEAD~1
Estamos volviendo a master
y luego usando git reset
para eliminar la confirmación no deseada (aquí) del historial.
Finalmente, todo vuelve a estar bien, ¡así que puedes comenzar tu baile feliz ahora! 💃🕺🏻
Lo he dicho antes, y lamentablemente todos sabemos que es verdad: ¡no podemos evitar los errores! No importa cuán buenos programadores seamos, nos equivocaremos de vez en cuando. La pregunta, por lo tanto, no es si cometemos errores, sino qué tan bien podemos enfrentarlos y solucionar los problemas.
Si desea obtener más información sobre las herramientas de deshacer de Git, le recomiendo el " Botiquín de primeros auxilios para Git ". Es una colección (gratuita) de videos cortos que le muestran cómo limpiar y deshacer errores en Git.
Diviértete moviéndote rápido, rompiendo cosas y, por supuesto, ¡reparándolas!
Fuente: https://www.sitepoint.com/5-ways-to-undo-mistakes-with-git/
1657182360
Este artículo discutirá el formato de datos JSON y las diferentes formas de tabularlo en Python.
Antes de entrar en los diferentes métodos, hablemos de JSON.
Es posible que haya aprendido a manejar formas primarias de datos en Python a través de archivos CSV y XLSX. Pero los datos también se pueden transmitir entre un servidor y el front-end. Aquí es donde entra en juego el formato JSON. Los servicios web y las API (interfaz de programación de aplicaciones) utilizan JSON.
JSON se utiliza para transmitir datos serializados y estructurados a través de una conexión de red. JSON es compatible con varios sistemas operativos y se puede editar fácilmente con editores de texto.
{
"CEOs":[
{"firstName":"Sundar", "lastName":"Pichai"},
{"firstName":"Jeff", "lastName":"Bezos"},
{"firstName":"Warren", "lastName":"Buffet"}
]
}
firstName
, lastName
){name:value , name:value}
){object:object, object:object, object:object}
)Veamos las siguientes ventajas de la representación de datos tabulares
Con la ayuda de la pd.DataFrame()
función, podemos tabular datos JSON. pd.DataFrame()
nos permite crear datos tabulares de tamaño mutable en 2D dentro de Python.
JSON, en ciertos aspectos, es similar a un diccionario de Python. Por lo tanto, podemos convertir sus valores en un Pandas Dataframe . pd.DataFrame()
convierte objetos JSON en estructuras similares a tablas.
En el siguiente programa, estamos tabulando myFile.json desde el directorio de trabajo.
import pandas as pd
import json
with open("/content/myJSON.json") as myFile:
fileData = json.load(myFile)
dataFrame = pd.DataFrame(columns=fileData[0].keys())
for i in range(len(fileData)):
dataFrame.loc[i] = fileData[i].values()
print(dataFrame)
userId number name
0 1 45 Mark Zuckerberg
1 2 46 Larry Page
2 3 47 Bill Gates
Para este programa, hemos utilizado módulos JSON y Pandas . El paquete JSON es para manejar datos JSON en Python. El módulo Pandas nos permite tabular datos JSON en un "DataFrame".
myJSON.json
es el archivo que contiene los datos JSON. La variable myFile almacena los datos JSON. Como comentamos, los JSON son similares a los diccionarios de Python. Por lo tanto, clasificamos en pares clave-valor. La clave es el nombre de la columna, mientras que el valor es el valor de ese campo.
La función from_dict() crea objetos DataFrame a partir de diccionarios. Veamos el siguiente programa:
import pandas as pd
import json
with open("/content/myJSON.json") as myFile:
fileData = json.load(myFile)
dataFrame = pd.DataFrame.from_dict(fileData)
print(dataFrame)
userId number name
0 1 45 Mark Zuckerberg
1 2 46 Larry Page
2 3 47 Bill Gates
En este programa, usamos los mismos módulos que la implementación anterior. Después de crear la variable de datos fileData
, la pasamos a .DataFrame.from_dict(fileData)
. Esto crea un DataFrame de pandas a partir de un diccionario. Por lo tanto, los datos tabulares se crean a partir de JSON.
tabulate
MóduloEl tabulate
módulo nos permite mostrar datos tabulares en Python dentro de la consola. Este módulo no forma parte de la biblioteca estándar de Python. Instale el módulo manualmente usando PIP. Los siguientes tipos tabulares son compatibles con este módulo:
Al llamar a tabulate()
, podemos crear datos tabulares.
print(tabulate({"Employees": ["John", "Adam"], "Role": ["CEO","COO" ]}, headers="keys"))
Role Name
----- ------
CEO John
COO Adam
Pasando por "keys"
debajo header
Las claves del diccionario se utilizan como nombres de columna.
¿Cuál es la mejor manera de ver los datos JSON?
Los editores de texto modernos nos permiten leer y escribir archivos JSON. Software como Notepad ++, MS WordPad y File Viewer Plus son ejemplos notables.
¿Cómo analizar JSON en Python?
Si tiene una cadena JSON en Python, puede analizarla utilizando json.loads()
el método del módulo JSON.
En este artículo, hemos discutido el formato de datos JSON y sus usos.
También hemos discutido la importancia de tabular los datos.
Las tres formas principales de tabular JSON en Python son:
from_dict()
la funciónFuente: https://www.pythonpool.com
1643088300
“Con un gran poder viene un gran desastre” – ya sabes quién . ¡Aunque es verdad! Por mucho que las formas angulares sean poderosas, también crean un gran desorden en el controlador. FormBuilder, FormGroup, FormControl, guardar lógica, actualizar lógica: casi una locura. Hoy, les mostraré cómo administrar los formularios y traer algo de cordura.
Ingrese a los Servicios: son inyectables y se brindan cuando los necesita. Entonces, ¿por qué no usar servicios para administrar formularios? Así que empecemos.
En primer lugar, necesitamos crear interfaces. Para esta demostración, voy a crear 2 interfaces: Actor
es la entidad final y ActorCreateInput
es la forma de las entradas del formulario.
Cree un ActorCreateFormService que no se inyecte en la raíz, lo que significa { providedIn: 'root' }
que no se pasa al decorador:
Hasta ahora todas las cosas se explican por sí mismas. En esta etapa, el servicio no hace nada excepto crear FormGroup y asignarlo a la form
propiedad de clase. He agregado algunos métodos convenientes para recuperar el valor y la validez del formulario.
Para guardar o actualizar actores, necesitamos conectar el servicio HTTP. Supongamos que tenemos un ActorApiService que hace el trabajo. Así que inyéctalo en:
constructor(
private fb: FormBuilder,
private actorApiService: ActorApiService,
) {}
Ahora escribamos algunos métodos para crear y actualizar actores:
Necesitamos un último método para cargar un actor de la base de datos y completar el formulario. Esto será útil cuando un Actor necesite ser editado.
Veamos ahora cómo podemos usarlo en un componente. Debido a que no lo proporcionamos en root
, debemos proporcionarlo en el ámbito correcto (generalmente en el componente):
Observe lo limpio que está el controlador. Además, puede usar el mismo servicio de formulario en otro lugar.
Aún no hemos terminado. Si observa detenidamente, el servicio de formulario tiene algunas partes reutilizables que también podemos usar para otros formularios, por ejemplo, MovieCreateFormService. Entonces, llevemos nuestra clase de formulario un paso más allá y hagámosla abstracta AbstractFormService
. Ya no necesita contener el @Injectable()
decorador: (Algunos códigos fueron omitidos por brevedad).
Ahora cree y amplíe MovieCreateFormService:
Eso es todo, solo necesita anular 4 métodos para que su formulario cobre vida. Se encarga automáticamente de guardar, actualizar y restablecer al enviar el formulario. Ahora puede usar el MovieCreateFormService
mismo como ActorCreateFormService
se usó.
Que piensas de esta aproximación? Apuesto a que este patrón ahorrará muchos LOC (líneas de códigos) en toda la aplicación. Pero comparta sus pensamientos sobre posibles problemas y mejoras.
¡Gracias por leer!
import { FormBuilder, FormControl, FormGroup } from '@angular/forms'
import { Observable, throwError } from 'rxjs'
export abstract class AbstractFormService<T, InputT extends { id?: string }> {
form: FormGroup
constructor(protected fb: FormBuilder) {
this.form = this.buildForm()
}
get valid(): boolean {
if (this.form.untouched) return false
return this.form.valid
}
getFormValue(): InputT {
return this.form.value
}
setFormValue(dto: InputT | null): void {
if (!dto) {
this.form.reset()
return
}
this.form.reset(dto)
if (dto.id) {
const control = new FormControl(dto.id)
this.form.addControl('id', control)
}
}
abstract buildForm(): FormGroup
abstract loadFromApiAndFillForm$(id: string): Observable<T>
save$(): Observable<T> {
if (this.form.invalid) return throwError(() => new Error('Invalid form'))
const id = this.form.get('id')?.value ?? null
return id ? this.update$(id) : this.create$()
}
protected abstract create$(): Observable<T>
protected abstract update$(id: string): Observable<T>
}
@Injectable()
export class MovieCreateFormService extends AbstractFormService<Movie, MovieCreateInput> {
constructor(protected override fb: FormBuilder, private movieService: MovieService) {
super(fb)
}
buildForm(): FormGroup {
return this.fb.group({
name: ['', Validators.required],
releaseYear: '',
genre: '',
})
}
loadFromApiAndFillForm$(id: string): Observable<Movie> {
return this.movieService
.findById(id)
.pipe(tap((data) => this.setFormValue(data)))
}
protected create$(): Observable<Movie> {
return this.movieService
.create(this.getFormValue())
.pipe(tap(() => this.form.reset()))
}
protected update$(id: string): Observable<Movie> {
return this.movieService
.update(id, this.getFormValue())
.pipe(tap(() => this.form.reset()))
}
}
Enlace: https://javascript.plainenglish.io/a-better-way-to-use-angular-forms-cd22a5a0488a
1654957519
Al igual que con muchas construcciones en la codificación, generalmente hay más de una forma de hacer lo mismo. A veces las diferencias entre ellos son evidentes, un bucle al clásico caso en mano. Puede tener un repetir/mientras, un hacer/mientras o un para cada ciclo.
Por supuesto, el marco dentro de SceneKit no es diferente, y hay al menos tres formas en las que puedes rotar un objeto que te viene a la mente; una parte central de cualquier marco 3D que impugnaría.
Pero a diferencia de nuestro ejemplo de bucle donde las diferencias son evidentes, comprender las diferencias al hacer rotaciones dentro del código de SceneKit puede ser desconcertante. De hecho, usar diferentes métodos para girar hacia el mismo ángulo a veces puede arrojar resultados diferentes. Únase a mí en este documento para ver tres alternativas, junto con una revisión de dónde pueden fallar o engañarlo. Los tres que quiero ver los enumero aquí.
SCNActions
SCNAcciones
Los usé en los artículos anteriores que publiqué en SceneKit. En mi opinión, son la forma más fácil de animar rotaciones/transformaciones en su código.
Desafíos: al utilizar SCNActions
, el primer problema con el que se puede encontrar es una pérdida de precisión. Cuando ejecuta un SCNAction
pedido de permanecer donde está al finalizar [que no es el valor predeterminado], pronto descubrirá que una rotación de 90 grados puede resultar en 89.9998. Ahora bien, no creo que esto importe para muchas aplicaciones, ciertamente juegos, pero puede romper cosas si tienes cuidado.
Pero espere, la precisión no es el único [gotcha] ya SCNActions
que esencialmente están usando ángulos de Euler. Con al menos dos cuestiones más que me vienen a la mente, ambas relacionadas con el movimiento de un objeto en más de un plano. Este GIF animado ilustra el primero:
Estás viendo una rotación en los planos X e Y de 45 grados. Estoy aplicando la misma rotación a ambos modelos, pero obtengo resultados diferentes según el orden en que uso los ángulos. Es bastante sutil, pero mire cuidadosamente y vea la diferencia. Cuanto más me muevo después de cambiar la secuencia, más obvio se vuelve.
El código detrás de la ilustración que es [gotcha] está en la línea que contiene SCNAction.sequence
. Copie el código y ejecútelo en sus modelos, y obtendrá el mismo resultado. Tenga en cuenta que estoy usando una configuración de tipo suscripción para vincular mi GameScene
interfaz SwiftUI.
angleXSub = passX.sink(receiveValue: { [self] degree in
let actionZ = SCNAction.rotate(by: CGFloat(GLKMathDegreesToRadians(degree)), around: SCNVector3(x: 0, y: 0, z: 1), duration: 0.1)
let actionX = SCNAction.rotate(by: CGFloat(GLKMathDegreesToRadians(degree)), around: SCNVector3(x: 1, y: 0, z: 0), duration: 1)
let group = SCNAction.sequence([actionZ,actionX])
redNode.runAction(group)
})
angleYSub = passY.sink(receiveValue: { [self] degree in
let actionX = SCNAction.rotate(by: CGFloat(GLKMathDegreesToRadians(degree)), around: SCNVector3(x: 1, y: 0, z: 0), duration: 0.1)
let actionZ = SCNAction.rotate(by: CGFloat(GLKMathDegreesToRadians(degree)), around: SCNVector3(x: 0, y: 0, z: 1), duration: 1)
let group = SCNAction.sequence([actionX,actionZ])
redNode.runAction(group)
})
Ahora cambie SCNAction.sequence
a a SCNAction.group
y vuelva a ejecutar el mismo código. Funcionará la segunda vez; solo que ambos terminarán en el mismo ángulo. En SCNAction.group
este contexto, significa hacer los cambios en paralelo.
Pero espere, porque todavía no está fuera de peligro, ejecutar sus rotaciones en paralelo podría solucionar el problema de la secuencia, pero existe el segundo problema más oculto que puede surgir [gotcha], a saber, el bloqueo del cardán.
Obtiene bloqueo de cardán en una rotación cuando dos de sus ejes se alinean en el mismo plano. Cuando esto sucede, pierdes la capacidad de moverte en uno de tus tres planos.
Dado que los giroscopios son bastante cruciales para los aviones y las naves espaciales, puede estar seguro de que también hay una solución para el bloqueo del cardán, aunque no estoy seguro de que me tome la molestia de implementarlo en su código. Puede solucionarlo agregando un cuarto eje sobre el que puede rotar.
SCNMatrix4MakeRotation
Quería decir que la verdadera solución al desafío del gimbal es usar las transformaciones de Matrix, lo cual es cierto, pero no lo es. Dirijo esta sección con un GIF animado que usa ángulos de Euler y transformaciones de Matrix para hacer los mismos movimientos.
Puedo alinear tanto el eje rojo como el verde, poniéndome efectivamente en un bloqueo de cardán. Sin embargo, hay una diferencia crítica. Con los ángulos de Euler, me veo obligado a dividir mis rotaciones en los tres planos y usar algo como SCNActions para combinarlas; con las transformaciones, puedo hacerlas todas al mismo tiempo [no se muestra aquí]. La ventaja de hacerlo es que sospecho que puede detectar ligeramente el bloqueo de cardán en su código de matriz de manera más efectiva.
El código para hacer las transformaciones de la izquierda lo muestro aquí. En el lado negativo, creo que es un poco más difícil de entender en comparación con los ángulos de Euler.
transXSub = passTx.sink(receiveValue: { [self] degree in
let xAngle = SCNMatrix4MakeRotation(GLKMathDegreesToRadians(degree), 0, 0, 1)
let yAngle = SCNMatrix4MakeRotation(GLKMathDegreesToRadians(0), 0, 0, 0)
let zAngle = SCNMatrix4MakeRotation(GLKMathDegreesToRadians(0), 0, 0, 0)
let rotationMatrix = SCNMatrix4Mult(SCNMatrix4Mult(xAngle, yAngle), zAngle)
SCNTransaction.begin()
SCNTransaction.animationDuration = 2
redNode.transform = SCNMatrix4Mult(rotationMatrix, redNode.transform)
SCNTransaction.commit()
})
El código que se muestra es solo uno de los tres planos: todos son esencialmente iguales. Por supuesto, al igual que SCNActions
, el orden en que hagas las transformaciones afectará el resultado. Una función de código útil que utilicé aquí es la SCNTransation
de hacer las animaciones. Súper valioso por un hecho ya que, a diferencia de las animaciones en SwiftUI, le brinda un bloque de finalización que no se activará hasta que finalice la animación.
cuaterniones
Ahora, debo confesar en este ejemplo: no estoy seguro de que ilustre el poder de estas cosas, que pueden hacer mucho más de lo que las usaré hoy.
Los cuaterniones son en la animación gráfica el estándar de oro; son rápidos, usan menos espacio [que una matriz] y no tienen el problema de bloqueo del cardán, pero en el lado negativo, no son tan fáciles de usar.
Este es un código para mover nuestra redNode
orientación de la misma manera que lo hicimos con la matriz y los ángulos de Euler. Aunque el código se parece un poco a C, puede estar seguro de que es Swift.
quartXSub = passQx.sink(receiveValue: { [self] degree in
SCNTransaction.begin()
SCNTransaction.animationDuration = 4
let quaternion = simd_quatf(angle: GLKMathDegreesToRadians(degree), axis: simd_float3(0,0,1))
redNode.simdOrientation = quaternion * redNode.simdOrientation
SCNTransaction.commit()
})
Con un GIF animado del código que se parece mucho al último, tenga en cuenta que aunque parece que perdí un grado de libertad usando cuaterniones, no es así.
Conclusión
Creo que es justo decir que sea cual sea el método que finalmente utilice en su proyecto, debe ceñirse a él. El uso de diferentes formas de rotar objetos en todas partes se consideraría una mala práctica. Puede codificar el bloqueo del cardán eligiendo cuidadosamente la dirección en la que ocurrirá también. Este es un gran video para dar una visión completa de los ángulos de Euler.
Todo lo cual me lleva al final de este artículo. Espero que hayas disfrutado leyéndolo tanto como yo escribiéndolo y, como yo, hayas aprendido una o dos cosas en el proceso.
Aquí hay un enlace al código base que construí para mostrar las demostraciones ilustradas : hay dos archivos en uno; no es un código plug-and-play y se ofrece tal cual.
Esta historia se publicó originalmente en https://betterprogramming.pub/3-ways-to-rotate-objects-in-scenekit-86ed8c2f5490