【问题标题】:What's the best way to create a line that starts at a point on another line and extends at a given angle for a given length?创建一条从另一条线上的一点开始并以给定角度延伸给定长度的线的最佳方法是什么?
【发布时间】:2016-02-02 19:41:55
【问题描述】:

我有一个程序,我需要在其中递归地从父行生成新行,以便它最终看起来像一棵树。我遇到的问题是我不知道如何使子线以 A 角制作,其中 A 相对于其父线在 10 到 80 度或 100 到 170 度之间。

我目前的算法步骤如下:

  1. 从父节点的 (x1, y1) 点中选择一个小于父节点总长度的随机长度(称为 newBranchDist)(使用距离公式)
  2. 使用 Math.Cos(A) 乘以 newBranchDist 查找新行的 x 坐标
  3. 使用 Math.Sin(A) 乘以 newBranchDist 找到新行的 y 坐标
  4. 现在我们得到了直线上一个点的 (x1, y1) 坐标。

此时,我需要计算 (x2, y2) 相对于父线的角度为 A。有什么建议吗?

编辑:另外,我的程序将随机选择父线的哪一侧来绘制新线。所以,有时是角度 A,有时是 A + 90。

【问题讨论】:

  • 我看不到任何关于随机化角度的内容。您需要计算它还是给定间隔内的“任何”角度?此外,您可能会发布到目前为止所拥有的算法的 short 示例。有时,它可以帮助理解问题。第 2 行是否会从与原行相同的点开始?
  • 这似乎更像是一个几何问题而不是编程问题。目前还不完全清楚您要做什么。
  • @MattBurland - 问题似乎是关于找出子分支的算法,我认为混淆正是因为 OP 对如何生成这样的算法感到困惑。他似乎在程序化思考,完全错过了如何使用 OO 思维来解决问题。但我认为你是对的,它有一个沉重的几何组件。可能是几何组件让他脱离了面向对象,让他只考虑程序。
  • 嗨 Reuben,请注意,在这里给出答案的专业人士渴望获得声誉积分。如果您对有用的答案进行投票,那将是非常好的 - 如果一个答案帮助您解决了您的问题 - 您应该将其标记为已接受的答案,这将向其他人表明该问题已解决。谢谢!
  • @Shnugo 是的,我一直在计划。我想在给出反馈之前尝试让算法正常工作,但不幸的是我还不能让它工作。事实上,我改变了我只需要递归生成 90 度角的线,这样我就不必在我的算法中处理三角函数了。

标签: c# line


【解决方案1】:

对结果做一些假设,因为我不清楚你的目标是什么,我会说计算如下。

int x2 = x1 + newBranchDist * Math.Cos(a);
int y2 = y1 + newBranchDist * Math.Sin(a);

然后,您可以通过勾股定理验证长度。

double lengthSquared = Math.Pow(x2 - x1, 2) + Math.Pow(y2 - y1, 2);
double lengthRooted = Math.Pow(lengthSquared, 0.5);

【讨论】:

    【解决方案2】:

    您需要知道父线的角度以及子线的角度。您的术语令人困惑,因此我将使用一些不同的术语。需要注意的是,所有分支的角度都应存储为相对于水平方向的角度,尽管您需要相对于父分支计算它们以执行 10-80,100-170 的操作。但是水平角度的计算很容易,如下所示:

    1. figure out origin of the new branch<p>
        a. BreakOffDistance = a random number less than the parent length (random distance from the start of the parent branch)
        b. NewBranchOriginX = ParentBranchOriginX + BreakOffDistance * cos(ParentBranchAngle);
        c. NewBranchOriginY = ParentBranchOriginY + BreakOffDistance * sin(ParentBranchAngle);
    2. figure out a random angle to the new, child line;
        a. figure out random angle between 10 and 80 or 100 and 170.
        b. NewBranchAngle = ParentBranchAngle - 90 + RandomAngle.
           (all branch angles relative to horizontal, right?)
    3. figure out random length of new branch - less than parent?
    4. The previous steps determine the new branch - origin point, angle and length.  But to figure out its endpoint so you can draw it:
        a. NewBranchEndX = NewBranchOriginX + NewBranchLength * cos(NewBranchAngle);
        b. NewBranchEndY = NewBranchOriginY + NewBranchLength * sin(NewBranchAngle);
    

    由于屏幕坐标是上下颠倒的,您可能需要将步骤 1b、1c 和 4a 和 4b 中的加号替换为减号。

    另外,如果您要模拟一棵树,我认为将新分支的角度选择为 10-80 或 100-170 是不正确的。树上的树枝更喜欢长出来,而且在计算出每个新树枝的角度时并不难。最后,如果您从三个维度考虑它,您的树会更加真实。有一棵树,它的树枝朝向和远离你以及向两侧生长。这也很简单,但比你要求的要多。

    【讨论】:

    • 它的要点对我来说似乎是正确的。然而,它确实比这更复杂。只实现这个算法仍然对我不起作用(见上面的评论)——我永远无法让一条线在其父分支上具有其起始 (x,y) 坐标。但是,我确信这更多是由于我在处理三角函数垃圾方面的无能,而不是该算法中的缺陷。不过,我认为这比这要复杂一些。
    【解决方案3】:

    使用这样的类,您可以先进行计算,然后在第三步绘制整个事物。

    此代码完全未经测试。会有一些错误,但您可能会知道如何做到这一点...

    public class MyLine {
        private Random random;
        public MyLine(int Level, PointF Start, PointF End, int Angle) {
            this.random = new Random();
            this.Level = Level;
            this.Start = Start;
            this.End = End;
            this.Angle = Angle;
        }
    
        public int Level{get;set;}
    
        public PointF Start { get; set; }
        public PointF End { get; set; }
    
        public float MyLength {
            get {
                return (float)Math.Sqrt(Math.Pow(End.X - Start.X, 2) + Math.Pow(End.Y - Start.Y, 2));
            }
        }
        public int Angle { get; set; }
    
        public MyLine MySideLine { get; set; }
    
        public void CalculateSideLine() {
            float middleX = Start.X + (End.X - Start.X) / 2f;
    
            float k = (End.Y - Start.Y) / (End.X - Start.X);
            float d = (End.X * Start.Y - Start.X * End.Y) / (End.X - Start.X);
    
            float middleY = k * middleX + d;
    
            PointF newStart = new PointF(middleX, middleY);
            int angle = random.Next(10, 80);
            if (random.Next(0, 1) == 0)
                angle = angle + 90;
    
            float LengthPercentage = (float)random.NextDouble();
            if (LengthPercentage < 0.5)
                LengthPercentage = 0.5f;
    
            float newLength = MyLength * LengthPercentage;
    
            //Now we know the starting point of the new line, its angle and the length
            //I do not have enough time to write the complete calculation down but it's result would be a new endPoint
            //You think of a circle with its middle on "newStart" and its radius = "newLength".
            //This circle you'll have to intersect with the line through "newStart" with the given angle.
            //There are two results, you have to choose the one in the right direction
            PointF newEnd = new PointF(0, 0); //This you'll have to find yourself...
    
            this.MySideLine = new MyLine(this.Level++, newStart, newEnd, angle);
    
            //this will calculate a new nested side line and - kind of recursively - go deeper and deeper.
            //you'll have to find a break condition on a certain level.
            this.MySideLine.CalculateSideLine();
    
            //Be aware of randomly angle of 90 or 0. you might want to calculate two side lines on each twig (otherwise it will not look like a tree)
    
        }
    
        //This will draw the line to a Graphics (e.g. look at Form.CreateGraphics() )
        //it will kind of recursively draw down the full tree.
        public void DrawMeAndMySideLine(Graphics g){
            g.DrawLine(Pens.Black,this.Start,this.End);
            this.MySideLine.DrawMeAndMySideLine(g);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多