Calculate maximum length of a vector

I’m trying to achieve the following calculation but i really don’t know what functions and equations to use in this situation.

As you can see in the diagram, I have vector A and vector B and I know angle K and angle J. What I am trying to find out is how to calculate the maximum magnitude of vector C - the point it intersects with vector A. With that maximum length in hand, I’ll generate a random number (10, maxLength) to draw a line from where vector C intersects vector B to the random length using sin(J) * length & cos(J) * length.

I’ve looked at scalar projection and dot product and other vector math stuff and I really haven’t a clue how to tackle this problem. any help would be greatly appreciated.

Screenshot 2021-11-29 at 18.24.06

I have added some info to your drawing, a,b and c are the lengths of the 3 sides of a triangle formed by the intersection of three non-parallel lines. Assuming that you calculate the distance b then you can calculate the other two sides using ‘sine law’.

c = b * sin(K) / sin(J - K)
and
a = b * sin(180 - J) / sin(J - K)

Where J and K are measured in degrees. Obvious should use radians on the computer.

1 Like

This is excellent. I see it now. Just to confirm, those formulas for c & a will give me a length/distance of those lines/sides? I’ll try this tonight.

Yes :+1:

Extra to make the minimum 20 characters needed LOL

Hello,

Some links to help you:

:)

I implemented the formulas, however, it doesn’t appear to be working as expected. the code below is for a snowflake generator. you can see in the output below, where i’ve circled, the lines intersect each other and continue on when they should terminate.

int numStems = (random(1) <= 0.5) ? 6 : 8;
float stemAngle = TWO_PI / numStems;
boolean centerPiece = (random(1) <= 0.5) ? true : false;
int centerType = floor(random(1,7));
boolean midStems;
int numMidStems = 6;
int mainStemLen = 200;
int midStemLen = 150;
int stemOffset = (centerPiece) ? 50 : 0;
int numMainBranches;
int numMidBranches = 4;
float branchAngle = random(PI/6, PI/3);
float spaceBuffer = 20;
float mainBranchSpacer;
float[] mainBranchLen;
PVector[] mainBranchEnd;

void setup() {
  size(650, 850, P3D);
  background(0);
  smooth(8);
  
  if(numStems <= 6){
    midStems = (random(1) <= 0.5) ? true : false;
  } else{ midStems = false; }
  if(centerPiece){
    numMainBranches = floor(random(4,7));
  } else{ numMainBranches = floor(random(5,8)); }
  
  mainBranchSpacer = (mainStemLen - stemOffset - spaceBuffer*2) / numMainBranches;
  mainBranchLen = new float[numMainBranches];
  mainBranchEnd = new PVector[numMainBranches];
  
  noLoop();
}

void draw(){
  background(0);
  translate(width/2, height/2);
  rotate(HALF_PI);

  // draw the stems
    for(int i=0; i<numStems; i++){
      pushMatrix();
        rotate(stemAngle * i);
        stroke(255);
        strokeWeight(5);
        strokeCap(SQUARE);
        line(stemOffset, 0, mainStemLen, 0);
      popMatrix();
    }
    //draw the in-between stems
    if(midStems){
      for(int i=0; i<numMidStems; i++){
        pushMatrix();
          rotate((stemAngle * i) + (PI / numMidStems));
          stroke(255);
          strokeWeight(3);
          strokeCap(SQUARE);
          line(stemOffset, 0, midStemLen, 0);
        popMatrix();
      }
    }

    // draw the branches on main stems
    for(int j=1; j<=numMainBranches; j++){
      float maxLength = ((spaceBuffer + (mainBranchSpacer*j)) * sin(stemAngle/2)) / sin(branchAngle - (stemAngle/2));  //stemAngle/2 is meeting in the middle
      if(j < 3){              //only for the first two
        if(! centerPiece){    //don't do if there is a centerpiece
          mainBranchLen[j-1] = (random(1) <= 0.5) ? random(15, maxLength) : maxLength;      //50% chance they are connected
        } else{ mainBranchLen[j-1] = random(15, maxLength); }
      }
      else if(j == numMainBranches){
          mainBranchLen[j-1] = random(15, 20);    //want the last one to be shorter
      } 
      else{
        mainBranchLen[j-1] = random(35, maxLength/6);
      }
      mainBranchEnd[j-1] = new PVector(cos(branchAngle)*mainBranchLen[j-1], sin(branchAngle)*mainBranchLen[j-1]);
    }
    for(int i=0; i<numStems; i++){
      pushMatrix();
      rotate(stemAngle * i);
      for(int k=0; k<numMainBranches; k++){
        pushMatrix();
          translate(spaceBuffer + stemOffset + (mainBranchSpacer*k), 0);
          stroke(255);
          strokeWeight(3);
          strokeCap(SQUARE);
          line(0, 0, mainBranchEnd[k].x, mainBranchEnd[k].y);
          line(0, 0, mainBranchEnd[k].x, -mainBranchEnd[k].y);
        popMatrix();
      }
      popMatrix();
    }
}

Now that I know your goal is to create snowflakes I can think of several different algorithms for doing this and avoid the intersections. Unfortunately your code is quite complex so I would struggle to find the solution by modifying what you have.

I would start again but this time focus on creating a single branch. This would be a two stage process.

  1. Turn your computer off and get pen and paper and draw what you would want a typical branch to look like. From that, design an algorithm to draw the single branch.
  2. Convert the algorithm into a reusable function with appropriate parameters to control the size and shape of the branch.

You will probably iterate this process several times before you get what you need.

Since snowflakes exhibit 6-fold symmetry can repeatably draw the branches by repeated calls to the function with rotation to get the final snow flake.

1 Like

have already done all that! above is just the part of the code that generates the stems and branches so you can see the implementation of the original question. i know i can consolidate the stem and branch generation into a single, 6x repeating function and will do that, but it still leaves a question of setting a maximum length of a branch to prevent them crossing and continuing instead of terminating. i’m open to any other ways of doing this, but i wanted to give feedback on your solution above to see if it can be fixed.

My answer gave the solution to the original question “Calculate the maximum length of a vector” and the sine law answered that.

I believe that the intersection problem is caused by a logic error when using the sine-law-results in the branch-drawing algorithm that is why I suggested revisiting the algorithm from scratch, it was not a criticism of your approach.

I often have to go back to square one when I am programming some complex algorithm because my original logic was flawed.

It seems to me that there are three possibilities

  1. The sine law has been incorrectly implemented
  2. The vector A is not the mid point between two branches and may impinge on the space required by an adjacent branch. For six-fold symmetry the angle K should be <= 30 degrees
  3. The length of the sub branch is greater than the maximum length calculated using the sine law.

Fair enough. I’ll comb back through the code.

edit: I found the error in the original formula. Per the diagram above, the formula was written:
c = b * sin(K) / sin(J - K)
when it should have been
c = b * sin(K) / sin((180-J) - K)

1 Like

Screenshot 2021-11-29 at 18.24.06
Sine line means that

\frac{a}{sin(180-J)}=\frac{b}{sin(L)}=\frac{c}{sin(K)}

Since we know the angles J and K we can calculate the angle L because the sum of the internal angles of a triangle is 180 degrees. So we have

180 - J + L + K = 180
Subtract 180 from both sides gives
- J + L + K = 0
Adding J to and subtracting K from both sides gives
L = J - K

Substituting back into the sine law gives

\frac{a}{sin(180-J)}=\frac{b}{sin(J-K)}=\frac{c}{sin(K)}

From this you can see that

c = \frac{b * sin(K)}{sin(J - K)}

So although

c = \frac{b * sin(K)}{sin((180-J) - K)}

might solve your intersection problem it is not a valid outcome of the sine law.

I really enjoy playing with algorithms so I decided to start at first principles based on your problem description and came up with a solution that could be adapted to suit other user needs.

So here is my diagram for a main branch with a single sub-branch


The horizontal line represents the main branch. The dotted line represents the limit for any sub-branch and is the mid point between two main branches. For 6-fold symmetery the angle between main branches is 60o so the angle a1 will be 30o. sbAngle is the angle between the sub branch and the main branch (i.e. J in the original drawing)

Using this information I created a basic sketch to draw the branh with this result
sn75
Repeating this six times to make a snowflake gives
sn389
The blue lines represent the area available to each branch.

The sketch code is below and should be self explanatory when read in conjunction with the diagram above

public void setup() {
  size(320, 320);
}

void draw() {
  background(0);
  translate(width/2, height/2);
  // Select single branch or snowflake
  branch(new PVector(), 0, 100, 5, PI/3, PI/6);
  // snowflake(new PVector(), 100, 5, PI/3 + PI/2, PI/6);
}

void snowflake(PVector centre, float mbLength, int nbrSBs, float sbAngle, float a1) {
  translate(centre.x, centre.y);
  pushMatrix();
  for (int i = 0; i < 6; i++) {
    branch(centre, i * PI / 3, mbLength, nbrSBs, sbAngle, a1);
  }
  popMatrix();
}

/**
 * This draws a single branch of the snow flake. To prevent interactions between
 * neighbouring branches the last parameter is the angular limit for all sub branches.
 * For 6-fold symmetry the angle between main branches is PI/3 so the angular limit
 * is PI/6
 * @param start the global position for the start of the branch
 * @param rotAngle the global angle the main branch makes with the x-axis
 * @param mbLength the length of the main branch
 * @param nbrSBs the number of sub-branches from the main branch
 * @param sbAngle the angle between the sub branch makes along length of the main branch
 * @param a1 the limit angle.
 */
public void branch(PVector start, float rotAngle, float mbLength, int nbrSBs, 
  float sbAngle, float a1) {
  pushMatrix();
  // Transform the graphics context so the main branch starts at [0,0] and lies 
  // along x-axis
  translate(start.x, start.y, rotAngle);
  rotate(rotAngle);
  stroke(255);
  strokeWeight(2.5f);
  float spacing = mbLength / (nbrSBs + 1);
  float a2 = PI - sbAngle;  // triangle internal angle
  float a3 = PI - a2 - a1;  // calculate third internal angle
  line(0, 0, mbLength, 0);
  float d1, d2, d3, p3X, p3Y;
  for (int i = 1; i <= nbrSBs; i++) {
    // Find position on main branch to start sub-branch
    d3 = i * spacing;
    // Use the Sine Law to calculate other two side lengths
    d1 = d3 * sin(a1) / sin(a3); // length of the sub branch
    d2 = d3 * sin(a2) / sin(a3); // d2 is not used in this sketch
    // Before drawing the sub branch we have the opportunity to change
    // the length of the sub branch if we wish

    // Using Pythagorus calculate the coordinates for end of
    // the sub branch (p3) based on the length of the sub-branch
    p3X = d3 + d1 * cos(PI - a2);
    p3Y = d1 * sin(PI - a2);
    line(d3, 0, p3X, p3Y);
    line(d3, 0, p3X, -p3Y);
  }
  // Show limit angle (blue lines
  PVector limit = new PVector(cos(a1), sin(a1));
  limit.mult(300);
  stroke(120, 220, 220);
  strokeWeight(1.5f);
  line(0, 0, limit.x, limit.y);
  line(0, 0, limit.x, -limit.y);
  popMatrix();
}

void mouseClicked() {
  save("sn" + frameCount + ".png");
}