How to draw rounded corners for line-segments with different angles

edited June 2015 in Questions about Code

I want to have rounded corners for my path segments (straight lines). Doing it with right angles is quite easy, but I have a problem with different angles. I know that the angle of the line joints must somehow be related to the radius of the arc() function, but can’t figure out in which way (after all, I wasn’t very good in math). The more obtuse the angle, the bigger the radius, the more acute, the smaller, I suppose. But then there is also the position of the arc that must change in some (other) way.

I appreciate any ideas. I'm sure someone already wrote a function that solves exactly this kind of problem, it may be a trigonometry kind of solution. Sorry if my code example appears strange to you. I want to do something with L-Systems, so I have to make sure it works with that approach. The rounded corners always appear before the next line drawing inside the F-case and the lines have to make room for the corners, hence all the if-statements.

String todo = "F+F-F-F"; // this is the path in Lindenmayer-grammar
float len = 100; // length of a line
float theta; // the angle between the current line direction and the next one
float  edgeRad; // radius of the rounded edge (arc()-function)
float  edgeAngle; // angle of the rounded edge

void setup() {
  size(400, 400);
}

void draw() {
  background(255);
  noFill();  

  // I played around with these variables to figure out their relationship
  theta = map(mouseX, 0, width, 0, PI);
  edgeAngle = map(mouseY, 0, width, 0, TWO_PI);

  edgeRad = len/8; // maybe this has to change with theta

  pushMatrix();
  translate(10, height/2);
  render();
  popMatrix();

  fill(0);
  println(theta);
  text("angle: "+round(degrees(theta))+"°", 20, 20);
}

void render() {
  // here we draw the lines

  for (int i=0; i<todo.length (); i++) {
    char c = todo.charAt(i); // look up each char and apply the replacement command

    if (c == 'F') { // F is the replacement char for a straight line and translation call

      // the if's here are just there to make sure everything applies correctly inside the String order
      float startEdgeRad = 0;
      float endEdgeRad = 0;

      if (todo.length() > 1) { // if at least 2 chars & we're not at the first one
        if (i < todo.length()-2) { // if we're not at the last char
          // check if a direction change coming up, so we can subtract the radius of the edge
          if (todo.charAt(i+1) == '+' || todo.charAt(i+1) == '-') {
            endEdgeRad = edgeRad;
          }
        }
        // if there was a direction change before, draw a round corner at start
        if (i > 0 && todo.charAt(i-1) == '+') startEdgeRad = drawRoundCorner('+');
        else if (i > 0 && todo.charAt(i-1) == '-') startEdgeRad = drawRoundCorner('-');
      }

      line(startEdgeRad, 0, len-endEdgeRad, 0); // the radius of the round edges affect the line position and length
      translate(len, 0);
    } else if (c == '+') { // + is the replacement for left rotation
      rotate(-theta);
    } else if (c == '-') { // - is the replacement for right rotation
      rotate(theta);
    }
  }
}

float drawRoundCorner(char dir) {    
  // This draws the rounded corners
  // Maybe I have to completely rewrite this, I just wrote it by intuition

  pushMatrix();
  if (dir == '+') translate(edgeRad, -edgeRad);
  else if (dir == '-') translate(edgeRad, edgeRad);

  noFill();
  float aStart = PI;
  float aStop = aStart+edgeAngle/2;
  if (dir == '+') {
    aStart = PI/2;
    aStop = aStart+edgeAngle/2;
  }

  fill(255, 0, 0, 40);
  arc(0, 0, edgeRad*2, edgeRad*2, aStart, aStop);

  stroke(255, 0, 0);
  strokeWeight(3);
  point(0, 0);
  stroke(0);

  popMatrix();
  strokeWeight(1);

  return edgeRad;
}
Sign In or Register to comment.