Rotasi koordinat 3D kurang Presisi dalam perspektif 2D

Jadi saya menulis sebuah Program untuk menggambar dan menampilkan kubus 3D, menggunakan rumus konversi sederhana seperti yang digunakan dalam grafik isometrik:

x2 = x*cos(30) - y*cos(30)
y2 = x*sin(30) + y*sin(30) + z

Konversi koordinat baik-baik saja dan semuanya terlihat dalam perspektif.

Masalahnya berputar, Rotasi derajat besar mengacaukan semua koordinat dan memberi saya bentuk keseluruhan. Dan memutar pada derajat kecil berkali-kali, (yaitu 1000 putaran 1 derajat atau lebih) mengurangi ukuran kubus.

public void rotateX(double dg) //cube is shrinking along y and z  
{
    y = (y*Math.cos(dg)-z*Math.sin(dg));  
    z = (y*Math.sin(dg)+z*Math.cos(dg));  
}
public void rotateY(double dg) //cube is shrinking along x and z  
{  
    x = x*Math.cos(dg)-z*Math.sin(dg);  
    z = x*Math.sin(dg)+z*Math.cos(dg);  
}  
public void rotateZ(double dg) //cube is shrinking along x and y  
{  
    x = x*Math.cos(dg)-y*Math.sin(dg);  
    y = x*Math.sin(dg)+y*Math.cos(dg);  
}

Bagaimana saya bisa mengatasi kurangnya ketepatan cos dan sin setelah beberapa kali penggunaan??

Berikut keseluruhan kode yang ditulis dalam 3 kelas terpisah:
Kelas utama:

import java.awt.*;
import javax.swing.*;
import java.util.Random;
public class Frame extends JFrame
{
    private Random rnd = new Random();
    private cubeGUI cube;
    public Frame()
    {
        super();
    }

    public void paint(Graphics g)
    {
        cube = new cubeGUI(75,300.0,300.0);
        cube.convertall();
        double dg = 0.5; // The Smaller the degree, the less the error after long rotations.
        int sl = 5;
        int turns, axe;  
        while (1 == 1)
        {
            turns = rnd.nextInt(200)-100;
            axe = rnd.nextInt(3);
            for(int i = 0; i<turns; i++)
            {
                switch (axe)
                {
                    case 0: cube.rotatx(dg); break;
                    case 1: cube.rotaty(dg); break;
                    case 2: cube.rotatz(dg); break;
                }
                g.clearRect(0,0,600,600);
                g.drawLine(cube.a.x2,cube.a.y2,cube.b.x2,cube.b.y2);
                g.drawLine(cube.a.x2,cube.a.y2,cube.c.x2,cube.c.y2);
                g.drawLine(cube.c.x2,cube.c.y2,cube.d.x2,cube.d.y2);
                g.drawLine(cube.b.x2,cube.b.y2,cube.d.x2,cube.d.y2);
                g.drawLine(cube.e.x2,cube.e.y2,cube.f.x2,cube.f.y2);
                g.drawLine(cube.e.x2,cube.e.y2,cube.g.x2,cube.g.y2);
                g.drawLine(cube.g.x2,cube.g.y2,cube.h.x2,cube.h.y2);
                g.drawLine(cube.f.x2,cube.f.y2,cube.h.x2,cube.h.y2);
                g.drawLine(cube.a.x2,cube.a.y2,cube.e.x2,cube.e.y2);
                g.drawLine(cube.b.x2,cube.b.y2,cube.f.x2,cube.f.y2);
                g.drawLine(cube.c.x2,cube.c.y2,cube.g.x2,cube.g.y2);
                g.drawLine(cube.d.x2,cube.d.y2,cube.h.x2,cube.h.y2);
                try
                {
                    Thread.sleep(sl); //Rotation Speed, In relation with Angle of rotation.
                } catch(InterruptedException ex)
                {
                    Thread.currentThread().interrupt();
                }
            }
        }
}
public static void main(String[] args)
{
    Frame cube = new Frame();
        cube.setSize(600,600);
        cube.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        cube.setVisible(true);
    }
}

kelas kubus:

public class cubeGUI
{
    public Point center,a,b,c,d,e,f,g,h;
    private double x, y;
    public cubeGUI(int m, double x, double y)
    {
        this.x = x;
        this.y = y;
        a = new Point(-m,-m,-m);
        b = new Point(m,-m,-m);
        c = new Point(-m,m,-m);
        d = new Point(m,m,-m);
        e = new Point(-m,-m,m);
        f = new Point(m,-m,m);
        g = new Point(-m,m,m);
        h = new Point(m,m,m);
    }
    public void rotatx(double dg)
    {
        a.rotateX(Math.toRadians(dg));
        b.rotateX(Math.toRadians(dg));
        c.rotateX(Math.toRadians(dg));
        d.rotateX(Math.toRadians(dg));
        e.rotateX(Math.toRadians(dg));
        f.rotateX(Math.toRadians(dg));
        g.rotateX(Math.toRadians(dg));
        h.rotateX(Math.toRadians(dg));
        convertall();
    }
    public void rotaty(double dg)
    {
        a.rotateY(Math.toRadians(dg));
        b.rotateY(Math.toRadians(dg));
        c.rotateY(Math.toRadians(dg));
        d.rotateY(Math.toRadians(dg));
        e.rotateY(Math.toRadians(dg));
        f.rotateY(Math.toRadians(dg));
        g.rotateY(Math.toRadians(dg));
        h.rotateY(Math.toRadians(dg));
        convertall();
    }
    public void rotatz(double dg)
    {
        a.rotateZ(Math.toRadians(dg));
        b.rotateZ(Math.toRadians(dg));
        c.rotateZ(Math.toRadians(dg));
        d.rotateZ(Math.toRadians(dg));
        e.rotateZ(Math.toRadians(dg));
        f.rotateZ(Math.toRadians(dg));
        g.rotateZ(Math.toRadians(dg));
        h.rotateZ(Math.toRadians(dg));
        convertall();
    }
    public void convertall()
    {
        a.convert(x,y);
        b.convert(x,y);
        c.convert(x,y);
        d.convert(x,y);
        e.convert(x,y);
        f.convert(x,y);
        g.convert(x,y);
        h.convert(x,y);
    }
}

Kelas titik (ini menghitung semua koordinat):

public class Point
{
    private double x, y, z, F;
    public int x2, y2;
    public Point(double a, double b, double c)
    {
        x = a;
        y = b;
        z = c;
    }
    public int getX()
    {
        return (int)x;
    }
    public int getY()
    {
        return (int)y;
    }
    public int getZ()
    {
        return (int)z;
    }
    public void rotateX(double dg) //cube is shrinking along y and z
    {
        y = (y*Math.cos(dg)-z*Math.sin(dg));
        z = (y*Math.sin(dg)+z*Math.cos(dg));
    }
    public void rotateY(double dg) //cube is shrinking along x and z
    {
        x = x*Math.cos(dg)-z*Math.sin(dg);
        z = x*Math.sin(dg)+z*Math.cos(dg);
    }
    public void rotateZ(double dg) //cube is shrinking along x and y
    {
        x = x*Math.cos(dg)-y*Math.sin(dg);
        y = x*Math.sin(dg)+y*Math.cos(dg);
    }
    public void convert(double xx, double yy)
    {
        x2 = (int)(-(Math.cos(Math.toRadians(30))*x - Math.cos(Math.toRadians(30))*y) + xx);
        y2 = (int)(-(Math.sin(Math.toRadians(30))*x + Math.sin(Math.toRadians(30))*y + z) + yy);
    }
    public String toString()
    {
        return ("Y = " + y + ", Z = " + z);
    }
}

person ThaBomb    schedule 08.05.2013    source sumber
comment
Sudahkah Anda mencoba menggunakan BigDecimal?   -  person durron597    schedule 08.05.2013
comment
Saya tidak ingin menggunakan BigDecimal karena hanya akan membuang-buang memori, dan hanya mengurangi masalah, bukan menghapusnya. Yang harus saya lakukan adalah terus memutar hingga bilangan BigDecimal hampir tidak cukup untuk menghilangkan kesalahan yang ditemui oleh cos/sin karena bilangan tersebut memberikan banyak bilangan irasional dengan angka tak terhingga.   -  person ThaBomb    schedule 08.05.2013


Jawaban (2)


Pendekatan yang biasa dilakukan adalah merepresentasikan kubus sebagai konfigurasi titik dan transformasi saat ini. Saat memutar, perbarui transformasinya tetapi jangan perbarui poinnya sendiri. Hanya ketika koordinat titik diperlukan (untuk rendering, menampilkan nilai koordinat, dll.) transformasi dapat diterapkan pada titik tersebut. Poin-poinnya sendiri tidak boleh diubah.

Ini akan menghilangkan kesalahan yang terakumulasi ketika banyak rotasi diterapkan secara berurutan. Namun, matriks transformasi harus dipertahankan sebagai rotasi (determinan 1). Jika tidak, transformasi akan tetap menimbulkan artefak acak (penskalaan, kemiringan, atau distorsi lainnya). Jadi, setelah setiap rotasi diterapkan, matriks transformasi harus dinormalisasi ulang sehingga tetap merupakan transformasi murni. Normalisasinya bisa sesederhana membagi setiap entri dengan determinannya.

person Ted Hopp    schedule 08.05.2013
comment
Terima kasih atas tipnya :) Meskipun saya mungkin memiliki 1 masalah, ketika memutar dengan derajat yang lebih besar, contoh paling dasar yang dapat saya berikan, adalah memutar sebesar 90. jika saya menggunakan 1 derajat dan memutar 90 kali, kubus baik-baik saja. Namun jika saya menggunakan 90 derajat satu kali, kubus berubah menjadi persegi panjang 2D dan mengacaukan koordinat semua titik. Bagaimana cara mengatasi ini? - person ThaBomb; 08.05.2013
comment
Terima kasih, ini menyelesaikan masalah presisi, tetapi sekarang menimbulkan masalah lain :P. Memutar 1 sumbu saja tidak masalah, jika saya memutar sumbu lain setelahnya, bentuknya mulai kacau dan saya merasa seperti sedang memutar hypercube 4D. - person ThaBomb; 09.05.2013
comment
@ThaBomb - Kedengarannya seperti kesalahan matematika. Saya akan memeriksa kode yang memperbarui transformasi. - person Ted Hopp; 09.05.2013
comment
Ini kodenya: mediafire.com/?lpc63dfqba778b8 Menurut saya masalahnya adalah saat memutar pada kapak kedua , ini mengambil koordinat asli, bukan koordinat rendering yang diputar oleh sumbu sebelumnya. Jika itu masalahnya, saya akan mencoba mencari cara untuk menyelesaikannya. Terima kasih lagi - person ThaBomb; 09.05.2013

Anda menggunakan x, yang sudah diubah: x = x*Math.cos(dg)-y*Math.sin(dg);
y = x*Math.sin(dg)+y*Math.cos(dg );

itu varian yang tepat. ganda xx =x; x = x*Matematika.cos(dg)-y*Matematika.sin(dg); y = xx*Matematika.sin(dg)+y*Matematika.cos(dg);

person Ksenia    schedule 16.02.2014
comment
Masalah saya dengan metode ini adalah kurangnya presisi untuk sin dan cos, yang berarti setelah beberapa rotasi (sekitar 1000) koordinat akan sangat berbeda dari koordinat awal, dan objek akan diskalakan dan terkadang diterjemahkan. Semuanya baik-baik saja sekarang, Program ini bekerja dengan baik. - person ThaBomb; 17.02.2014