การหมุนพิกัด 3 มิติขาดความแม่นยำในมุมมอง 2 มิติ

ดังนั้นฉันจึงเขียนโปรแกรมเพื่อวาดและแสดงลูกบาศก์ 3 มิติ โดยใช้สูตรการแปลงอย่างง่ายเหล่านี้ที่ใช้ในกราฟสามมิติ:

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

การแปลงพิกัดเป็นเรื่องปกติและทุกอย่างจะออกมาในมุมมอง

ประเด็นคือการหมุน การหมุนองศามากทำให้พิกัดทั้งหมดยุ่งเหยิง และทำให้ฉันได้รูปร่างทั้งหมด และหมุนด้วยองศาเล็กๆ หลายๆ ครั้ง (เช่น การหมุน 1,000 องศา 1 องศาขึ้นไป) จะช่วยลดขนาดของลูกบาศก์

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

ฉันจะแก้ไขการขาดความแม่นยำของ cos และ sin หลังจากใช้งานหลายครั้งได้อย่างไร

นี่คือโค้ดทั้งหมดที่เขียนใน 3 คลาสแยก:
คลาสหลัก:

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

คลาสคิวบ์:

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

คลาสพอยต์ (คำนวณพิกัดทั้งหมด):

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 แหล่งที่มา
comment
คุณได้ลองใช้ BigDecimal แล้วหรือยัง?   -  person durron597    schedule 08.05.2013
comment
ฉันไม่ต้องการใช้ BigDecimal เพราะมันจะทำให้เสียหน่วยความจำ และลดปัญหาเท่านั้น ไม่ใช่ลบออก สิ่งที่ฉันต้องทำคือหมุนต่อไปจนกว่าตัวเลข BigDecimal จะไม่เพียงพอที่จะกำจัดข้อผิดพลาดที่ cos/sin พบเจอ เนื่องจากพวกมันให้จำนวนอตรรกยะจำนวนมากพร้อมกับตัวเลขอนันต์   -  person ThaBomb    schedule 08.05.2013


คำตอบ (2)


แนวทางปกติคือการแสดงคิวบ์เป็นการกำหนดค่าจุดและการแปลงปัจจุบัน เมื่อหมุน ให้อัปเดตการแปลงแต่อย่าอัปเดตจุดด้วยตนเอง เฉพาะเมื่อจำเป็นต้องใช้พิกัดจุด (สำหรับการเรนเดอร์ การแสดงค่าพิกัด ฯลฯ) ควรนำการแปลงไปใช้กับจุดต่างๆ ประเด็นต่างๆ ไม่ควรได้รับการแก้ไข

วิธีนี้จะขจัดข้อผิดพลาดที่สะสมเมื่อมีการหมุนหลายครั้งตามลำดับ อย่างไรก็ตาม สิ่งสำคัญคือต้องรักษาเมทริกซ์การแปลงให้เป็นการหมุน (ดีเทอร์มิแนนต์ 1) มิฉะนั้น การเปลี่ยนแปลงจะยังคงทำให้เกิดสิ่งประดิษฐ์แบบสุ่ม (การปรับขนาด การเอียง หรือการบิดเบือนอื่นๆ) ดังนั้น หลังจากใช้การหมุนแต่ละครั้ง เมทริกซ์การแปลงควรจะถูกทำให้เป็นมาตรฐานใหม่ เพื่อให้ยังคงเป็นการแปลงที่บริสุทธิ์ การทำให้เป็นมาตรฐานอาจทำได้ง่ายเพียงแค่หารแต่ละรายการด้วยดีเทอร์มิแนนต์

person Ted Hopp    schedule 08.05.2013
comment
ขอบคุณสำหรับคำแนะนำ :) แม้ว่าฉันอาจมี 1 ปัญหา แต่เมื่อหมุนด้วยองศาที่ใหญ่กว่า ตัวอย่างพื้นฐานที่สุดที่ฉันสามารถให้คุณได้คือหมุน 90 ถ้าฉันใช้ 1 องศาแล้วหมุน 90 ครั้ง ลูกบาศก์ก็ใช้ได้ แต่ถ้าฉันใช้ 90 องศาหนึ่งครั้ง ลูกบาศก์จะกลายเป็นสี่เหลี่ยม 2 มิติ และทำให้พิกัดของจุดทั้งหมดเละเทะ ฉันจะแก้ปัญหานี้ได้อย่างไร? - person ThaBomb; 08.05.2013
comment
ขอบคุณ นี่เป็นการแก้ไขปัญหาความแม่นยำ แต่ตอนนี้สร้างปัญหาอื่นขึ้นมาแล้ว :P การหมุน 1 แกนเพียงอย่างเดียวก็ใช้ได้ ถ้าฉันหมุนแกนอื่นหลังจากนั้น รูปร่างจะเริ่มเลอะเทอะ และฉันรู้สึกเหมือนกำลังบิดไฮเปอร์คิวบ์ 4 มิติ - person ThaBomb; 09.05.2013
comment
@ThaBomb - ฟังดูเหมือนเป็นข้อผิดพลาดทางคณิตศาสตร์ ฉันจะตรวจสอบโค้ดที่อัปเดตการเปลี่ยนแปลง - person Ted Hopp; 09.05.2013
comment
นี่คือโค้ด: mediafire.com/?lpc63dfqba778b8 ฉันคิดว่าปัญหาคือเมื่อหมุนขวานที่สอง มันใช้พิกัดดั้งเดิม แทนที่จะเป็นพิกัดที่เรนเดอร์หมุนด้วยแกนก่อนหน้า ถ้านั่นคือปัญหา ฉันจะพยายามหาวิธีแก้ปัญหา ขอบคุณอีกครั้ง - person ThaBomb; 09.05.2013

คุณใช้ x ซึ่งมีการเปลี่ยนแปลงไปแล้ว: x = x*Math.cos(dg)-y*Math.sin(dg);
y = x*Math.sin(dg)+y*Math.cos(dg) );

มันเป็นตัวแปรที่ถูกต้อง สองเท่า xx =x; x = x*Math.cos(dg)-y*Math.sin(dg); y = xx*Math.sin(dg)+y*Math.cos(dg);

person Ksenia    schedule 16.02.2014
comment
ปัญหาของฉันเกี่ยวกับวิธีการนี้คือการขาดความแม่นยำสำหรับ sin และ cos ซึ่งหมายความว่าหลังจากการหมุนหลายครั้ง (ประมาณ 1,000) พิกัดจะแตกต่างอย่างสิ้นเชิงจากพิกัดเริ่มต้นโดยสิ้นเชิง และวัตถุจะมีการปรับขนาดและบางครั้งถูกแปล ตอนนี้ทุกอย่างเรียบร้อยดี โปรแกรมทำงานได้ดี - person ThaBomb; 17.02.2014