创建从另一条线上的一点开始并以给定角度延伸给定长度的线的最佳方法是什么?

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?

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

我目前的算法步骤如下:

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

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

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

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

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

您需要知道父线的角度以及子线的角度。您的术语令人困惑,所以我将使用一些不同的术语。需要注意的是,所有分支的角度都应存储为相对于水平的角度,尽管您需要相对于父分支计算它们才能完成 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 是不对的。树上的树枝更喜欢向上生长,在计算每个新树枝的角度时并不难做到这一点。最后,如果您从三个维度考虑它,您的树会更加逼真。有一棵树,它的树枝会朝向和远离您以及向两侧生长。这也相当简单,但比您要求的要多。

有了这样的class,你可以先做计算,然后在第三步画出整个东西。

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

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