Get perpendicular angle

This is kind of an embarrassing one.

I’m trying to draw a perpendicular line to two given vectors and I can’t work it out for the life of me. I suppose what I’m receiving with atan (or PVector.getAngle) is not what I was expecting… ie a degree returned within 360 degrees.

size(400,400)

nx1, ny1 = 200,100
nx2, ny2 = 100,300

noFill()
strokeWeight(3)
stroke(255,0,0)

line(nx1,ny1, nx2, ny2)

nx3 = nx2-((nx2-nx1)/2)
ny3 = ny2-((ny2-ny1)/2)

ellipse(nx3,ny3,20,20)

stroke(255,255,0)

a = atan2(ny2-ny1, nx2-nx1)

print "angle", a
print "angle deg", degrees(a)

r = 100 # max addition push

a-=90

linex = r * cos(a)
liney = r * sin(a)

line(nx3, ny3, nx3+linex, ny3+liney)
1 Like

try

#a-=90
a-=PI/2


3 Likes

To add to @kll’s comment –

The sin() and cos() functions work with radians. If you really want to work in degrees, you could convert:

#a-=90
a-=radians(90)

But, it’s probably better to get used to working in radians exclusively.

3 Likes

lord above…

thanks for that. Egg. on. my. face :slight_smile:

1 Like

you like vectors?

diagp = True # False

def setup():
    size(400, 400)
    smooth()
    noFill()
    strokeWeight(3)
#    noLoop()

def draw():
    background(0)
    n1 = PVector(200,100)
#    n2 = PVector(100,300)
    n2 = PVector(mouseX,mouseY)
    stroke(255,0,0)
    vline(n1,n2)
    vtext(n1,'1')
    vtext(n2,'2')
    n3 = n2.copy()
    n21= n2.sub(n1) 
    n3 = n3 - n21/2      # midpoint
    vcircle(n3,20)
    vtext(n3,'3')
    a = n21.heading()
    #print 'a', a 
    #___________________________________________________
    r = 100 
#    a-=PI/2              # perpendicular-angle   
#    n4 = n3.copy()
#    n4 = n4.add( PVector( r*cos(a),r*sin(a) ) )
    n31 = n3.copy().sub(n1)
    n34 = n31.setMag(r).rotate(-PI/2)
    n4 = n3.copy().add(n34)
    #___________________________________________________
    stroke(255,255,0)
    vline(n3,n4)
    vtext(n4,'4')
    
# let's make our own Vector primitives
def vline(p1, p2):
    line(p1.x, p1.y, p2.x, p2.y)

def vcircle(p,diam):
    circle(p.x,p.y,diam)    

def vtext(p,t):
    if ( diagp ): text(t,p.x,p.y+15)

1 Like

Hey @kll in Python Mode we also have some nice operator overloading for PVectors:

n21 = n2 - n1
n31 = n3 - n1
n4 = n3 + n34

:smiley:

2 Likes

works great,
but compared to
https://processing.org/reference/PVector.html
python version
https://py.processing.org/reference/PVector.html
is very poor AND says

we cannot simply use traditional addition/multiplication/etc. Instead, we’ll need to do some “vector” math, which is made easy by the methods inside the PVector class.

what you prove wrong AND refs to missing methods list.

This makes me so sad. There is an open issue somewhere to fix these docs. It is on my TODO list to work on it, kind of forever :frowning:

2 Likes

rewritten for Java


// https://discourse.processing.org/t/get-perpendicular-angle/14067/5 

boolean diagp = true; // false

void setup() {
  size(400, 400);
  smooth();
  noFill();
  strokeWeight(3);
  //    noLoop();
}

void draw() {
  background(0);

  PVector  n1 = new PVector(200, 100);
  PVector n2 = new PVector(mouseX, mouseY);

  stroke(255, 0, 0);
  vline(n1, n2);
  vtext(n1, "1");
  vtext(n2, "2");
  PVector n3 = n2.copy();
  PVector n21= n2.sub(n1); 
  n3 = n3.sub(n21.div(2));     // midpoint
  vcircle(n3, 20);
  vtext(n3, "3");

  float a = n21.heading();
  //print 'a', a ;
  //___________________________________________________
  float r = 100 ;
  //    a-=PI/2;              // perpendicular-angle   
  //    n4 = n3.copy();
  //    n4 = n4.add( PVector( r*cos(a),r*sin(a) ) );
  PVector n31 = n3.copy().sub(n1);
  PVector n34 = n31.setMag(r).rotate(-PI/2);
  PVector n4 = n3.copy().add(n34);
  //___________________________________________________
  stroke(255, 255, 0);
  vline(n3, n4);
  vtext(n4, "4");
}

// ----------------------------------------------------------------

// let's make our own Vector primitives
void vline(PVector p1, PVector p2) {
  line(p1.x, p1.y, p2.x, p2.y);
}

void vcircle(PVector p, float diam) {
  ellipse(p.x, p.y, 
    diam, diam);
}

void vtext(PVector p, String t) {
  if ( diagp )
    text(t, p.x, p.y+15);
}
//

thanks @Chrisir,
yes , there is a interesting question too as i actually translated from JAVA to PYTHON

in JAVA i can do

java method overloading

// let's make our own Vector primitives
//void vline(PVector p1, PVector p2) {
void line(PVector p1, PVector p2) {
  line(p1.x, p1.y, p2.x, p2.y);
}

but in PYTHON i did not know how to do so i had to invent a ‘vline’ method

In Python you can define a method in such a way that there are multiple ways to call it.
Given a single method or function, we can specify the number of parameters ourself.
Depending on the function definition, it can be called with zero, one, two or more parameters.

# def vline(p1, p2):
def line(p1, p2):
    line(p1.x, p1.y, p2.x, p2.y)

TypeError: line() takes exactly 2 arguments (4 given)

def line(p1, p2): this.line(p1.x, p1.y, p2.x, p2.y)
def circle(p, diam): this.circle(p.x, p.y, diam)    
def text(p, t, b=True, offset=15): b and this.text(t, p.x, p.y + offset)
1 Like

kinda works,
but if i now call

circle(20,20,20);

get the

#TypeError: circle() takes exactly 2 arguments (3 given)

again, so must use

this.circle(20,20,20);

LOL

  • Python, just like JS and many other languages, functions are stored in variables too.
  • It’s not like in Java where we can have a variable/field/parameter, a function/method and a class/interface sharing the same name within the same context:
void setup() {
  println(same(same) * new same().same); // 60
  exit();
}

char same = 10;

int same(int same) {
  return same / 3;
}

class same {
  short same = 20;
}
  • For our convenience, most of Processing’s API is made available to us under Python Mode via global variables.
  • That is, size, background, color, triangle, etc. are all global variables, having a function reference as their value.
  • However, when we use def, class or even the assign = operator in the global context, if the label already exists, we end up reassigning its old value w/ a new 1.
  • For example, global variable line already exists holding a reference to method PApplet::line().
  • When we issue def line():, we replace the old Java method w/ a new Python 1.
  • But even after that, not all is lost, b/c we still got another global variable named this, which holds a reference to current PApplet sketch instance.
  • And we can still invoke the original PApplet::line() this way: this.line().
  • However, once a PApplet member is globally replaced, only via global variable this in order to reach it again.
2 Likes

We can still implement the new functions in a way they can deal w/ both PVector and original parameters using the varargs * parameter: :partying_face:

# def line(p1, p2): this.line(p1.x, p1.y, p2.x, p2.y)
# def circle(p, diam): this.circle(p.x, p.y, diam)    
# def text(p, t, b=True, offset=15): b and this.text(t, p.x, p.y + offset)

def line(p1, p2, *args):
    if args: this.line(p1, p2, *args)
    elif g.is2D(): this.line(p1.x, p1.y, p2.x, p2.y)
    else: this.line(p1.x, p1.y, p1.z, p2.x, p2.y, p2.z)


def circle(p, diam, *args):
    this.circle(p, diam, *args) if args else this.circle(p.x, p.y, diam)


def text(t, p, *args):
    if args: this.text(t, p, *args)
    elif g.is2D(): this.text(t, p.x, p.y)
    else: this.text(t, p.x, p.y, p.z)
2 Likes