在Java中旋转离散点而不会发生冲突

时间:2016-08-26 20:02:56

标签: java math rotation point

我使用GenerateSolidThetaZero函数生成只包含整数组件的点列表。我的目标是使用弧度角度θ旋转这些离散点,旋转后的新点仍然应该包含整数分量。问题是我不希望任何两个点映射到相同的值。我想在旋转之前和之后获得相同数量的独特点。我使用round函数来解决这个问题,但我仍然会得到一些非唯一的映射。基本上我只是想找到一种方法来旋转这些点并保留尽可能多的结构(尽可能少地丢失点数)。我愿意使用任何图书馆。任何帮助或指导都会很棒。

注意:在我的代码中,半径为2,生成了13个点。在Pi / 6旋转后,由于这些点映射到另一个已经映射到的点的相同值,我最终失去了4个点。

    public class pointcheck{
        // this HashSet will be used to check if a point is already in the rotated list
        public static HashSet<Point> pointSet = new HashSet<Point>();

        public static void main(String args[]) {
            //generates sort of circular solid with param being the radius
            ArrayList<Point> solid_pointList =  GenerateSolidThetaZero(2);

            //used to store original point as first part of pair and rotated point as second part of pair
            ArrayList<Pair> point_pair = new ArrayList<Pair>();

            //goes through all points in Solid_pointList adds each point to Point List with its corresponding rotated angle
            for(Point t : solid_pointList){
                point_pair.add(new Pair(t,rotation_about_origin(t,Math.PI / 6)));
            }

            for(Pair t : point_pair){
                System.out.println(t.getFirst() + "   " + t.getSecond());
            }

            System.out.println(pointSet.size());

        }

        //takes the point we want to rotate and then the angle to rotate it by
        public static Point rotation_about_origin(Point P, double theta){
            Point new_P = null;
            double old_X = P.x;
            double old_Y = P.y;
            double cos_theta = Math.cos(theta);
            double sin_theta = Math.sin(theta);
            double new_X = old_X * cos_theta - old_Y * sin_theta;
            double new_Y = old_X * sin_theta + old_Y * cos_theta;

            new_P = new Point((int)Math.round(new_X),(int)Math.round(new_Y));

            //if new_p is already in rotated solid
            if(pointSet.contains(new_P))
                System.out.println("Conflict       " + P + "          " + new_P);
            else
                //add new_P to pointSet so we know a point already rotated to that spot
                pointSet.add(new_P);
            return new_P;
        }


        private static ArrayList<Point> GenerateSolidThetaZero(int r){
            int rsq = r * r;
            ArrayList<Point> solidList=new ArrayList<Point>();
            for (int x=-r;x<=r;x++)
                for (int y=-r;y<=r;y++)
                    if (x*x + y*y <= rsq)
                        solidList.add(new Point(x,y));

            return solidList;
        }

    public static class Pair<F,S>{
        private F first; //first member of pair
        private S second; //second member of pair

        public Pair(F first, S second) {
            this.first = first;
            this.second = second;
        }

        public void setFirst(F first) {
            this.first = first;
        }

        public void setSecond(S second) {
            this.second = second;
        }

        public F getFirst() {
            return first;
        }

        public S getSecond() {
            return second;
        }
      }
    }//end of pointcheck class

如何使用不使用90的整数倍的角度旋转点?如果已经采用了映射,我应该在旋转后转换点?

1 个答案:

答案 0 :(得分:0)

旋转的磁盘将覆盖与原始磁盘完全相同的像素。因此,您实际上想要解决从原始像素到旋转像素的分配问题。

将原始像素(ox, oy)指定给对应像素(cx, cy)的成本可以用电位表示。例如,距离:

E_o,c = length(R(ox, oy, theta) - (cx, cy))

,其中R是旋转运算符。或者,您也可以尝试其他规范,例如二次距离。

然后,问题是找到最小化整体能量的对应关系:

min_C Sum_{o \in O} E_o,c

完全解决这个问题的算法是Hungarian Algorithm。但是,如果你有大量的像素,这是非常昂贵的。

相反,这是一个近似的概念:

在目标像素中,还存储旋转位置,而不是仅具有颜色。然后,像以前一样顺序旋转原始像素。围绕旋转位置并检查相应像素是否仍然被占用。

如果没有,请将旋转(未接地)位置与新颜色一起存储。

如果它已被占用,请检查如果您交换了对应关系,能量是否会减少。如果是这样,交换它们,这将留下前一个像素。在任何情况下,您都有一个未映射的原始像素。将此像素存储在列表中。

完成此过程后,您将获得部分对应关系图和未映射像素列表。选择任何未映射的像素。分析目标像素的邻居。可能总会有一个未占用的像素(虽然我没有证明)。如果是这样,请选择此项。如果没有,请检查所有相邻像素以获得最佳能量减少和交换。继续,直到列表为空。

近似算法只是一个想法,我没有证据证明它实际上是可行的。但听起来好像值得一试。它肯定会比匈牙利算法更快。但是,这种近似只适用于具有p> = 1的Lp范数,用于潜在的定义。