Вращение 3D-координат отсутствует Точность в перспективе 2D

Поэтому я написал программу для рисования и отображения трехмерного куба, используя эти простые формулы преобразования, используемые в изометрических графиках:

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

Преобразование координат в порядке, и все выглядит в перспективе.

Проблема заключается в вращении. Вращение на большой градус искажает все координаты и дает мне всю форму. И многократное вращение на малые градусы (т.е. 1000 вращений на 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);
    }
}

Класс Point (это вычисляет все координаты):

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 градусов, куб превращается в двумерный прямоугольник и искажает координаты всех точек. Как мне это решить? - person ThaBomb; 08.05.2013
comment
Спасибо, это решило проблему точности, но теперь создало другую проблему: P. Вращение одной оси в порядке, если я поверну любую другую после нее, формы начнут путаться, и я чувствую, что скручиваю 4D-гиперкуб. - 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 );

это правильный вариант. двойной хх = х; х = х*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, что означало, что после нескольких поворотов (около 1000) координаты будут полностью отличаться от первоначальных, а объект будет масштабироваться и иногда перемещаться. Сейчас все хорошо, программа работает нормально. - person ThaBomb; 17.02.2014