3D Z-rotatie functie zoekt tweaker met wiskunde knobbel

Pagina: 1
Acties:

Vraag


Acties:
  • 0 Henk 'm!

  • Antitech
  • Registratie: December 2022
  • Laatst online: 19-11-2023
Geachte Tweakers,

Ik heb een ledmatrix gemaakt met ws2812 ledjes en daar hebben we software voor gemaakt om mooie plaatjes en animaties weer te geven. (https://github.com/psy0rz/ledder)

Dat gaat allemaal best prima, maar nu kreeg ik het idee om 3D objecten weer te geven op de led-matrix. Op zich heb ik redelijk wat programmeerervaring (>40 jaar) maar mijn wiskunde-knobbel heeft zich in de loop der jaren omgevormd tot een vetkwab. En dat is dan ook de reden dat ik hulp vraag. De code klopt wel maar de wiskunde achter de code klopt waarschijnlijk niet.

De onderstaande code is in javascript/typescript. Dit zijn 3 functies, rotateX, rotateY en rotateZ die een object om zijn eigen as moeten laten draaien.


code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
rotateX(degrees:number)
    {
        let degreesRad=degrees * (Math.PI / 180) //deg to rad
        let sinTheta=Math.sin(degreesRad)
        let cosTheta=Math.cos(degreesRad)
        for (let p=0;p<this.points.length;p++)
        {
            let point=this.points[p].projection
            this.points[p].projection.y=  ((point.y * cosTheta) - (point.z * sinTheta))
            this.points[p].projection.z=  ((point.z * cosTheta) + (point.y * sinTheta))
        }

    }

    rotateY(degrees:number)
    {
        let degreesRad=degrees * (Math.PI / 180) //deg to rad
        let sinTheta=Math.sin(degreesRad)
        let cosTheta=Math.cos(degreesRad)
        for (let p=0;p<this.points.length;p++)
        {
            let point=this.points[p].projection
            this.points[p].projection.x=  ((point.x * cosTheta) + (point.z * sinTheta))
            this.points[p].projection.z=  ((point.z * cosTheta) + (point.x * sinTheta))
        }

    }

    rotateZ(degrees:number)
    {
        let degreesRad=degrees * (Math.PI / 180) //deg to rad
        let sinTheta=Math.sin(degreesRad)
        let cosTheta=Math.cos(degreesRad)
        for (let p=0;p<this.points.length;p++)
        {
            let point=this.points[p].projection
            this.points[p].projection.x= ((point.x * cosTheta) - (point.y * sinTheta))
            this.points[p].projection.y= ((point.y * cosTheta) + (point.x * sinTheta))
        }
    }


De X en Y rotatie lijken goed te werken, en geven een 3d object terug. Het ziet er goed uit (maar dat kan toeval zijn).
Bij de Z rotatie lijkt alles in een plat vlak te vallen. Dus die klopt niet.
Heb ik een sinus en een cosinus verwisseld? staan er plusjes en minnetje verkeerd? Ik heb zelf gezocht en geprobeerd maar ik denk dat ik iets over het hoofd zie want als ik dingen verander dan wordt het erger.

Beste antwoord (via Antitech op 05-11-2023 22:15)


  • EfBe
  • Registratie: Januari 2000
  • Niet online
Je roteert 1 as per keer, dan moet je het in de juiste volgorde doen, anders gaan je rotaties niet goed. Je moet verder eerst roteren, en dan projecteren op het 2D scherm, dat zijn 2 verschillende dingen. 2D projecties kan zelfs in minder dan 50 regels assembly dus dat is het probleem niet. :)

Wat je kunt doen is wat inlezen over matrix based rotaties. Eenmaal de rotatie opgesteld in een matrix hoef je de vertices alleen maar met de matrix te vermenigvuldigen en je hebt het punt geroteerd. Als je de projectie matrix met die rotatiematrix vermenigvuldigt is je punt ook in 1 keer geprojecteerd op het 2D vlak. Je gebruikt javascript begrijp ik? Er zijn libraries voor deze zaken voor javascript, zodat je alleen de vertices hoeft aan te leveren en je krijgt geroteerde vertices terug. Een van die libraries is Three.js

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com

Alle reacties


Acties:
  • 0 Henk 'm!

  • Antitech
  • Registratie: December 2022
  • Laatst online: 19-11-2023
Hier nog een paar video's van het display:
demo - YouTube: ledder demo
pole position demo - YouTube: ledder pole position demo
meer video's - youtube playlist


En hieronder een video van het probleem. De bovenste is de rotateX, de middelste is rotateY en de onderste is rotateZ. Je ziet dat de onderste in de plat vlak komt te vallen:
YouTube: 23 oktober 2023

[ Voor 24% gewijzigd door Antitech op 23-10-2023 12:39 . Reden: aanvulling ]


Acties:
  • +1 Henk 'm!

  • alex3305
  • Registratie: Januari 2004
  • Laatst online: 13-06 22:12
Voor mij is het ook alweer een hele tijd geleden, maar dit kun je volgens mij het beste doen met een transformatie matrix. Maar de details zijn mij helaas ook iets te vaag O-).

Acties:
  • 0 Henk 'm!

  • Antitech
  • Registratie: December 2022
  • Laatst online: 19-11-2023
alex3305 schreef op maandag 23 oktober 2023 @ 11:59:
Voor mij is het ook alweer een hele tijd geleden, maar dit kun je volgens mij het beste doen met een transformatie matrix. Maar de details zijn mij helaas ook iets te vaag O-).
Volgens mij doe ik nu precies wat op die pagina staat beschreven onder het kopje "rotation".

Acties:
  • 0 Henk 'm!

  • alex3305
  • Registratie: Januari 2004
  • Laatst online: 13-06 22:12
Antitech schreef op maandag 23 oktober 2023 @ 12:10:
[...]

Volgens mij doe ik nu precies wat op die pagina staat beschreven onder het kopje "rotation".
Excuses. Ik was een beetje in de war omdat je geen gebruik maakt van een matrix. Maar je hebt inderdaad gelijk. Daarnaast wilde ik je in ieder geval niet de juiste term(en) onthouden ;). En zoals ik al zei, het is voor mij ook alweer een tijdje geleden.

Daarnaast heb ik ook nog even gekeken in mijn oude, schoolcode van 14 jaar geleden :X. Daarin gebruikte we destijds XNA om 3D manipulatie te doen. XNA wordt echter niet meer ondersteund, maar is wel open source geworden door MonoGame. Wellicht heb je iets aan de relevante code uit dat project?

Acties:
  • 0 Henk 'm!

  • Garyu
  • Registratie: Mei 2003
  • Laatst online: 22:38

Garyu

WW

Volgens mij klopt je formule voor z gewoon...

x' = x * cos(angle) - y * sin(angle);
y' = x * sin(angle) + y * cos(angle);
z' = z

Dus misschien dat het probleem toch in je code zit en niet in je wiskunde?

It's Difficult to Make Predictions - Especially About the Future


Acties:
  • 0 Henk 'm!

  • Antitech
  • Registratie: December 2022
  • Laatst online: 19-11-2023
Hier de volledige code voor deze demo. Wie spot de bug?

(de code is nog in ontwikkeling, dus hier en daar zit er wat overbodige troep in en commentaar is schaars)

*snip*

Nee, sorry, dat is hier niet de bedoeling. We gaan hier niet 350+ regels code dumpen en "wie debugged mijn code even" spelen ;) Je mag relevante snippets delen (10, 20, 30 in een uitzonderlijk geval 40, 50 ofzo regels (relevante!) code o.i.d.) maar daar blijft 't bij :>

[ Voor 143% gewijzigd door RobIII op 23-10-2023 14:16 ]


Acties:
  • +1 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 17:13
Heb je misschien last van gimbal lock?

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • +1 Henk 'm!

  • wheez50
  • Registratie: Oktober 2003
  • Laatst online: 14-06 13:47
Ik denk dat je in de problemen komt met het volgende: Bij rotate x en y roteer je op de andere as (y ipv x en x ipv y). Bij rotate z roteer je om de as naar je toe. En in het platte vlak is dat iets anders. In feite projecteer je een 3d-rotatie op 2d. En rotaties met de as in dat platte 2d vlak gaan dus goed. En je rotatie in het 3dvlak (waarbij je as *niet* in het platte vlak ligt, maar naar je toe komt) heeft dus een as die in 2d-ruimte op elk moment 0,0 is. En de link naar rotatiematrices geeft ook duidelijk aan:"These formulae assume that the x axis points right and the y axis points up."

Ik vermoed dusa wat er gebeurt is dat je rotatie-as zelf ook roteert. En dus krijg je een erg leuke (maar ongewenste) rotatie van een vierkant met een roterende as :)

Acties:
  • +1 Henk 'm!

  • Laurens-R
  • Registratie: December 2002
  • Laatst online: 29-12-2024
Kan het geval zijn. Wellicht is een Quaternion een goede oplossing. Die hebben daar geen last van.

[ Voor 4% gewijzigd door Laurens-R op 23-10-2023 20:15 ]


Acties:
  • 0 Henk 'm!

  • Antitech
  • Registratie: December 2022
  • Laatst online: 19-11-2023
[b]Antitech in "3D Z-rotatie functie zoekt tweaker met wiskunde knobbel"
[mbr]*snip*

Nee, sorry, dat is hier niet de bedoeling. We gaan hier niet 350+ regels code dumpen en "wie debugged mijn code even" spelen ;) Je mag relevante snippets delen (10, 20, 30 in een uitzonderlijk geval 40, 50 ofzo regels (relevante!) code o.i.d.) maar daar blijft 't bij :>
[/]
De code heb ik gepost omdat daarin duidelijk werd hoe de 3d naar 2d transformatie werkt. Dat kan niet in 30 of 40 regels code. Ik had het ook in 500 regels tekst kunnen uitleggen (want dat mag wel) maar dan was de uitleg nog steeds niet zo adequaat geweest als de code.
Sourcecode is een manier waarmee developers met elkaar communiceren en ook met computers communiceren en zou daarmee op een ICT website als Tweakers ook geaccepteerd moeten zijn.

Acties:
  • +1 Henk 'm!

  • RayNbow
  • Registratie: Maart 2003
  • Laatst online: 21:11

RayNbow

Kirika <3

Antitech schreef op dinsdag 24 oktober 2023 @ 22:37:
[...]


De code heb ik gepost omdat daarin duidelijk werd hoe de 3d naar 2d transformatie werkt. Dat kan niet in 30 of 40 regels code.
Waarom zou dat niet kunnen? Je gaat me niet vertellen dat je voor iets als het volgende meer dan 30 regels code nodig hebt?

Afbeeldingslocatie: https://tweakers.net/i/ApqmVjb2NqO1k3AxvLtTEMZuqHU=/full-fit-in/4000x4000/filters:no_upscale():fill(white):strip_exif()/f/image/GewlESeIDRpISQgFPxxGNzSY.png?f=user_large
Sourcecode is een manier waarmee developers met elkaar communiceren
Een manier inderdaad. Ik communiceer het liefst met collega-ontwikkelaars op een abstractieniveau die relevant is en dat is niet per se met code.

Ipsa Scientia Potestas Est
NNID: ShinNoNoir


Acties:
  • 0 Henk 'm!

  • Kheos
  • Registratie: Juni 2011
  • Laatst online: 23:07

Kheos

FP ProMod
Antitech schreef op dinsdag 24 oktober 2023 @ 22:37:
[...]
Sourcecode is een manier waarmee developers met elkaar communiceren en ook met computers communiceren en zou daarmee op een ICT website als Tweakers ook geaccepteerd moeten zijn.
En de beste manier om 500 lijnen code uit te wisselen is ze hier gewoon te plakken?

Acties:
  • Beste antwoord
  • +2 Henk 'm!

  • EfBe
  • Registratie: Januari 2000
  • Niet online
Je roteert 1 as per keer, dan moet je het in de juiste volgorde doen, anders gaan je rotaties niet goed. Je moet verder eerst roteren, en dan projecteren op het 2D scherm, dat zijn 2 verschillende dingen. 2D projecties kan zelfs in minder dan 50 regels assembly dus dat is het probleem niet. :)

Wat je kunt doen is wat inlezen over matrix based rotaties. Eenmaal de rotatie opgesteld in een matrix hoef je de vertices alleen maar met de matrix te vermenigvuldigen en je hebt het punt geroteerd. Als je de projectie matrix met die rotatiematrix vermenigvuldigt is je punt ook in 1 keer geprojecteerd op het 2D vlak. Je gebruikt javascript begrijp ik? Er zijn libraries voor deze zaken voor javascript, zodat je alleen de vertices hoeft aan te leveren en je krijgt geroteerde vertices terug. Een van die libraries is Three.js

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com


Acties:
  • +2 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 22:56

.oisyn

Moderator Devschuur®

Demotivational Speaker

Je formule bij de y-rotatie klopt in ieder geval niet, er mist daar een - (minus) teken.

Het helpt mij altijd om rotaties te zien in een vlak, niet om een as (in 2d is er immers geen as om te roteren, maar zijn er wel rotaties). Een rotatie om de z-as is dus een rotatie in het xy-vlak, en is daarmee gereduceerd tot 2 dimensies.

Als je je een 90-graden rotatie in 2 dimensies voorstelt, dan, afhankelijk van je coordinatenstelsel en de draairichting, dan is een van deze twee dingen waar:
• de x-as draait naar de positieve y-as, en de y-as draait naar de negatieve x-as
• de x-as draait naar de negatieve y-as, en de y-as draait naar de positieve x-as.

Als de x-as naar de positieve y-as draait, en de y-as draait naar de positieve x-as, dan ben je x en y gewon aan het omwisselen en dat is geen rotatie (het is een rotatie met een spiegeling). Maar precies dit doe je in rotateY


Maar dan nu naar wat volgens mij de oorzaak is van je probleem. Ik ben niet heel bekend met typescript maar ik ga er even vanuit dat het zich precies hetzelfde gedraagt als javascript. Een statement als
TypeScript:
1
let point = this.points[p].projection;

Maakt geen kopie van het point object, het geeft je slechts een referentie naar het object.

Als je daarna de twee regels hebt
TypeScript:
1
2
this.points[p].projection.x= ((point.x * cosTheta) - (point.y * sinTheta))
this.points[p].projection.y= ((point.y * cosTheta) + (point.x * sinTheta))


Dan gebruik je dus in de tweede regel het resultaat van de eerste regel in de berekening: point.x (die gewoon wijst naar hetzelfde object als this.points[p].projection) heeft op dat moment niet meer de originele waarde.

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • +2 Henk 'm!

  • RayNbow
  • Registratie: Maart 2003
  • Laatst online: 21:11

RayNbow

Kirika <3

.oisyn schreef op vrijdag 3 november 2023 @ 13:04:
Ik ben niet heel bekend met typescript maar ik ga er even vanuit dat het zich precies hetzelfde gedraagt als javascript.
offtopic:
Ja, TypeScript gedraagt zich praktisch hetzelfde als JavaScript. Kort door de bocht is het JavaScript, maar dan met extra syntax voor type-annotaties en elementen uit ECMAScript Next.

Ipsa Scientia Potestas Est
NNID: ShinNoNoir


Acties:
  • 0 Henk 'm!

  • Antitech
  • Registratie: December 2022
  • Laatst online: 19-11-2023
Het probleem is opgelost. De code werkt nu.
De oude code werkte prima voor 2d transformaties maar bij 3d ging het fout door de gimbal lock.
Uiteindelijk heb ik maar de npm matrix_transformer library gebruikt. Deze doet precies wat ik nodig heb en niets meer (dus hij is lekker klein en heeft geen dependencies).

de rotate functies zien er nu zo uit:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
Matrix.prototype.rotateZ = function (theta) {
  var trig = this._degreesToTrigVals({ x: 0, y: 0, z: theta });
  var newRotationMatrix = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]];
  newRotationMatrix[0][0] = parseFloat(trig.gamma.cos.toFixed(3));
  newRotationMatrix[0][1] = parseFloat(-trig.gamma.sin.toFixed(3));
  newRotationMatrix[1][0] = parseFloat(trig.gamma.sin.toFixed(3));
  newRotationMatrix[1][1] = parseFloat(trig.gamma.cos.toFixed(3));

  var newValues = this._multiplyMatrices(newRotationMatrix, this.value());
  return new Matrix({ x: newValues[0][0], y: newValues[1][0], z: newValues[2][0] });
};

Matrix.prototype.rotateY = function (theta) {
  var trig = this._degreesToTrigVals({ x: 0, y: theta, z: 0 });
  var newRotationMatrix = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]];
  newRotationMatrix[0][0] = parseFloat(trig.beta.cos.toFixed(3));
  newRotationMatrix[0][2] = parseFloat(trig.beta.sin.toFixed(3));
  newRotationMatrix[2][0] = parseFloat(-trig.beta.sin.toFixed(3));
  newRotationMatrix[2][2] = parseFloat(trig.beta.cos.toFixed(3));

  var newValues = this._multiplyMatrices(newRotationMatrix, this.value());
  return new Matrix({ x: newValues[0][0], y: newValues[1][0], z: newValues[2][0] });
};

Matrix.prototype.rotateX = function (theta) {
  var trig = this._degreesToTrigVals({ x: theta, y: 0, z: 0 });
  var newRotationMatrix = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]];
  newRotationMatrix[1][1] = parseFloat(trig.alpha.cos.toFixed(3));
  newRotationMatrix[1][2] = parseFloat(-trig.alpha.sin.toFixed(3));
  newRotationMatrix[2][1] = parseFloat(trig.alpha.sin.toFixed(3));
  newRotationMatrix[2][2] = parseFloat(trig.alpha.cos.toFixed(3));

  var newValues = this._multiplyMatrices(newRotationMatrix, this.value());
  return new Matrix({ x: newValues[0][0], y: newValues[1][0], z: newValues[2][0] });
};

Acties:
  • 0 Henk 'm!

  • Antitech
  • Registratie: December 2022
  • Laatst online: 19-11-2023
EfBe schreef op woensdag 25 oktober 2023 @ 09:02:
Er zijn libraries voor deze zaken voor javascript, zodat je alleen de vertices hoeft aan te leveren en je krijgt geroteerde vertices terug. Een van die libraries is Three.js
Ja, ik heb gekeken naar Three.js. Het is de eerste library die tegenkomt als je met Google gaat zoeken. Het is een hele mooie library maar behoorlijk oversized voor mijn probleem. Ik heb daarom voor een kleinere gekozen zonder dependencies.
Pagina: 1