如何计算角度以创建矢量偏移来定义平行路径?

How do I calculate angles to create vector offsets to define parallel paths?

我有由一系列点定义的参考路径。给定一个起点和给定方向上的其他点,我需要有效地将这些点创建的路径分成两条不同的路径(方向),以便我可以独立地绘制和处理两条路径。以下屏幕截图显示了 8 个路径示例。 "starting point" 是上面有白色圆圈的那个。如果您将自己从起点定位到​​下一个点,则红色圆圈建议的路径应始终指向给定点定义的路径的 "right"。

在屏幕中,路径2、3、5、6、7是正确的。在路径 1、4 和 8 中,基本上当路径从右侧开始向左移动时,平行路径点的 position 是正确的,但它们在某些情况下被交换(应该是红色的地方是绿色,等等)。

我在某种程度上滥用了 atan2()(我认为)来获得正确的角度和/或计算位置。对于第一个点和最后一个点,我正在计算与相邻点的角度,并绘制与该角度成 90 度偏移的红色和绿色路径点。对于路径中间的点,我正在查看与前一个点的角度和与下一个点的角度,并以平分角度的方式放置这些点。

如何正确计算这些角度以获得参考线右侧的红色和绿色平行路径点?

问题可能出在 TestLine::calculateParallelPoints()

我为此使用了 openFrameworks 0.90。这是代码:

ofApp.h

#pragma once

#include "ofMain.h"
#include "TestLine.h"

class ofApp : public ofBaseApp{

    public:
        void setup();
        void update();
        void draw();

    int sectors_wide;
    int sectors_tall;

    vector<TestLine> testLines;

};

ofApp.cpp

#include "ofApp.h"

//--------------------------------------------------------------
void ofApp::setup(){

    ofSetBackgroundColorHex(0x000000);

    sectors_wide = 4;
    sectors_tall = 2;

    TestLine t1 = TestLine(0,0,sectors_wide,sectors_tall);
    t1.raw_points.push_back(ofPoint(0.9,0.5));
    t1.raw_points.push_back(ofPoint(0.8,0.6));
    t1.raw_points.push_back(ofPoint(0.7,0.4));
    t1.raw_points.push_back(ofPoint(0.6,0.6));
    t1.raw_points.push_back(ofPoint(0.5,0.4));
    t1.raw_points.push_back(ofPoint(0.4,0.4));
    t1.raw_points.push_back(ofPoint(0.3,0.5));
    testLines.push_back(t1);


    TestLine t2 = TestLine(1,0,sectors_wide,sectors_tall);
    t2.raw_points.push_back(ofPoint(0.3,0.5)); //
    t2.raw_points.push_back(ofPoint(0.4,0.4));
    t2.raw_points.push_back(ofPoint(0.5,0.4));
    t2.raw_points.push_back(ofPoint(0.6,0.6));
    t2.raw_points.push_back(ofPoint(0.7,0.4));
    t2.raw_points.push_back(ofPoint(0.8,0.6));
    t2.raw_points.push_back(ofPoint(0.9,0.5));
    testLines.push_back(t2);

    TestLine t3 = TestLine(2,0,sectors_wide,sectors_tall);
    t3.raw_points.push_back(ofPoint(0.1,0.2));
    t3.raw_points.push_back(ofPoint(0.7,0.4));
    t3.raw_points.push_back(ofPoint(0.4,0.45));
    t3.raw_points.push_back(ofPoint(0.6,0.5));
    t3.raw_points.push_back(ofPoint(0.9,0.9));
    testLines.push_back(t3);

    TestLine t4 = TestLine(3,0,sectors_wide,sectors_tall);
    t4.raw_points.push_back(ofPoint(0.5,0.5));
    t4.raw_points.push_back(ofPoint(0.9,0.5));
    t4.raw_points.push_back(ofPoint(0.5,0.1));
    t4.raw_points.push_back(ofPoint(0.1,0.1));
    t4.raw_points.push_back(ofPoint(0.1,0.8));
    t4.raw_points.push_back(ofPoint(0.8,0.6));
    testLines.push_back(t4);

    TestLine t5 = TestLine(0,1,sectors_wide,sectors_tall);
    t5.raw_points.push_back(ofPoint(0.4,0.4));
    t5.raw_points.push_back(ofPoint(0.6,0.5));
    t5.raw_points.push_back(ofPoint(0.8,0.4));
    testLines.push_back(t5);

    TestLine t6 = TestLine(1,1,sectors_wide,sectors_tall);
    t6.raw_points.push_back(ofPoint(0.7,0.1));
    t6.raw_points.push_back(ofPoint(0.2,0.3));
    t6.raw_points.push_back(ofPoint(0.7,0.5));
    testLines.push_back(t6);

    TestLine t7 = TestLine(2,1,sectors_wide,sectors_tall);
    t7.raw_points.push_back(ofPoint(0.2,0.1));
    t7.raw_points.push_back(ofPoint(0.7,0.3));
    t7.raw_points.push_back(ofPoint(0.2,0.5));
    testLines.push_back(t7);

    TestLine t8 = TestLine(3,1,sectors_wide,sectors_tall);

    t8.raw_points.push_back(ofPoint(0.8,0.5));
    t8.raw_points.push_back(ofPoint(0.6,0.4));
    t8.raw_points.push_back(ofPoint(0.4,0.5));
    testLines.push_back(t8);

    // Convert raw points to real points in the grid space
    for (int i = 0; i < testLines.size(); i++) {
        testLines[i].processRawPoints();
        testLines[i].calculateParallelPoints();
    }


}

//--------------------------------------------------------------
void ofApp::update(){

}

//--------------------------------------------------------------
void ofApp::draw(){

    ofSetBackgroundColorHex(0x000000);
    ofSetColor(255, 255, 255);

    for (int i = 0; i < testLines.size(); i++) {
        testLines[i].displayTestLine();
    }

}

TestLine.h

#include "ofMain.h"

class TestLine {
public:
    TestLine(float _x, float _y, int _sec_wide, int _sec_tall);

    void displayTestLine();
    void calculateParallelPoints();
    void processRawPoints();

    float x_coord;
    float y_coord;

    float x_min;
    float x_max;
    float y_min;
    float y_max;

    vector<float>   angles;
    vector<ofPoint> raw_points;
    vector<ofPoint> points;
    vector<ofPoint> forward_points;
    vector<ofPoint> reverse_points;

    ofPolyline line;
    ofPolyline forward_line;
    ofPolyline reverse_line;
};

TestLine.cpp

#include "TestLine.h"

TestLine::TestLine(float _x, float _y, int _sec_wide, int _sec_tall){
    x_coord = _x;
    y_coord = _y;

    int w = ofGetWindowWidth();
    int h = ofGetWindowHeight();

    float allowed_w = (float)w / _sec_wide;
    float allowed_h = (float)h / _sec_tall;

    x_min = x_coord * allowed_w;
    x_max = x_min   + allowed_w;

    y_min = y_coord * allowed_h;
    y_max = y_min   + allowed_h;




}

void TestLine::calculateParallelPoints(){
    for (int i = 0; i < points.size(); i++) {
        if (i == 0) {
            ofVec2f v2 = points[i];
            ofVec2f v1 = points[i+1];

            float angle = ofRadToDeg(atan2(v1.y - v2.y, v1.x - v2.x));
            angles.push_back(angle);

            cout << "Start: " << angle << endl;
        }

        if (i > 0 && i < points.size() - 1) {
            ofVec2f v1 = points[i];
            ofVec2f v2 = points[i-1];

            float back_angle = ofRadToDeg(atan2(v1.y - v2.y, v1.x - v2.x));

            v2 = points[i];
            v1 = points[i+1];

            float front_angle = ofRadToDeg(atan2(v1.y - v2.y, v1.x - v2.x));


            float final_angle = (back_angle + front_angle) / 2;

            cout << "BACK ANGLE: " << back_angle << ", FRONT ANGLE: " << front_angle << ", FINAL ANGLE: " << final_angle << endl;

            float prev_x = points[i-1].x;
            float prev_y = points[i-1].y;
            float this_x = points[i].x;
            float this_y = points[i].y;
            float next_x = points[i+1].x;
            float next_y = points[i+1].y;



            angles.push_back(final_angle);

        }

        if (i == points.size() - 1) {
            ofVec2f v1 = points[i];
            ofVec2f v2 = points[i-1];

            float angle = ofRadToDeg(atan2(v1.y - v2.y, v1.x - v2.x));
            angles.push_back(angle);

            cout << "End: " << angle << endl;
        }

        line.addVertex(points[i]);
    }


    // Now using the points and the angles to calculate the forward and reverse points
    for (int i = 0; i < points.size(); i++) {
        float forward_angle = angles[i] + 90;
        float reverse_angle = angles[i] - 90;
        // cout << forward_angle << ", " << reverse_angle << endl;

        float forward_x = points[i].x + cos(ofDegToRad(forward_angle)) * 8;
        float forward_y = points[i].y + sin(ofDegToRad(forward_angle)) * 8;

        forward_points.push_back(ofPoint(forward_x, forward_y));

        float reverse_x = points[i].x + cos(ofDegToRad(reverse_angle)) * 8;
        float reverse_y = points[i].y + sin(ofDegToRad(reverse_angle)) * 8;

        reverse_points.push_back(ofPoint(reverse_x, reverse_y));

    }
}

void TestLine::processRawPoints(){
    for (int i = 0; i < raw_points.size(); i++) {
        float newx = ofMap(raw_points[i].x, 0, 1, x_min, x_max);
        float newy = ofMap(raw_points[i].y, 0, 1, y_min, y_max);
        points.push_back(ofPoint(newx,newy));
    }
}

void TestLine::displayTestLine(){

    ofSetColor(128,128,128);
    line.draw();

    ofSetColor(255, 255, 255);
    ofDrawCircle(points[0].x, points[0].y, 3);

    ofSetColor(255, 0, 0);

    for (int i = 0; i < forward_points.size(); i++) {
        ofDrawCircle(forward_points[i].x, forward_points[i].y, 2);
    }

    ofSetColor(0, 255, 0);

    for (int i = 0; i < reverse_points.size(); i++) {
        ofDrawCircle(reverse_points[i].x, reverse_points[i].y, 2);
    }
}

我最终用几个条件语句来处理这个问题,尽管我觉得可能有更好的方法来处理它用三角函数。这是修改后的 TestLine.cpp 文件。

TestLine.cpp

#include "TestLine.h"

TestLine::TestLine(float _x, float _y, int _sec_wide, int _sec_tall){
    x_coord = _x;
    y_coord = _y;

    int w = ofGetWindowWidth();
    int h = ofGetWindowHeight();

    float allowed_w = (float)w / _sec_wide;
    float allowed_h = (float)h / _sec_tall;

    x_min = x_coord * allowed_w;
    x_max = x_min   + allowed_w;

    y_min = y_coord * allowed_h;
    y_max = y_min   + allowed_h;
}

void TestLine::calculateParallelPoints(){
    for (int i = 0; i < points.size(); i++) {
        if (i == 0) {
            ofVec2f v2 = points[i];
            ofVec2f v1 = points[i+1];

            float angle = ofRadToDeg(atan2(v1.y - v2.y, v1.x - v2.x));
            angles.push_back(angle);

            cout << endl;
            cout << "Start: " << angle << endl;
        }

        if (i > 0 && i < points.size() - 1) {
            ofVec2f v1 = points[i];
            ofVec2f v2 = points[i-1];

            float back_angle = ofRadToDeg(atan2(v1.y - v2.y, v1.x - v2.x));

            v2 = points[i];
            v1 = points[i+1];

            float front_angle = ofRadToDeg(atan2(v1.y - v2.y, v1.x - v2.x));
            float final_angle = (back_angle + front_angle) / 2; // back_angle + front_angle


            float prev_x = points[i-1].x;
            float prev_y = points[i-1].y;
            float this_x = points[i].x;
            float this_y = points[i].y;
            float next_x = points[i+1].x;
            float next_y = points[i+1].y;

            // Here is the addition that addressed the problem.
            if ((prev_x > this_x && prev_y <= this_y && next_x < this_x && next_y < this_y) ||
                (prev_x > this_x && prev_y > this_y && next_x < this_x && next_y >= this_y)) {
                final_angle += 180;
            }

            cout << "BACK ANGLE: " << back_angle << ", FRONT ANGLE: " << front_angle << ", FINAL ANGLE: " << final_angle << endl;

            angles.push_back(final_angle);

        }

        if (i == points.size() - 1) {
            ofVec2f v1 = points[i];
            ofVec2f v2 = points[i-1];

            float angle = ofRadToDeg(atan2(v1.y - v2.y, v1.x - v2.x));
            angles.push_back(angle);

            cout << "End: " << angle << endl << endl;
        }

        line.addVertex(points[i]);
    }


    // Now using the points and the angles to calculate the forward and reverse points
    for (int i = 0; i < points.size(); i++) {
        float forward_angle = angles[i] + 90;
        float reverse_angle = angles[i] - 90;
        // cout << forward_angle << ", " << reverse_angle << endl;

        float forward_x = points[i].x + cos(ofDegToRad(forward_angle)) * 8;
        float forward_y = points[i].y + sin(ofDegToRad(forward_angle)) * 8;

        forward_points.push_back(ofPoint(forward_x, forward_y));

        float reverse_x = points[i].x + cos(ofDegToRad(reverse_angle)) * 8;
        float reverse_y = points[i].y + sin(ofDegToRad(reverse_angle)) * 8;

        reverse_points.push_back(ofPoint(reverse_x, reverse_y));

    }
}

void TestLine::processRawPoints(){
    for (int i = 0; i < raw_points.size(); i++) {
        float newx = ofMap(raw_points[i].x, 0, 1, x_min, x_max);
        float newy = ofMap(raw_points[i].y, 0, 1, y_min, y_max);
        points.push_back(ofPoint(newx,newy));
    }
}

void TestLine::displayTestLine(){

    ofSetColor(128,128,128);
    line.draw();

    ofSetColor(255, 255, 255);
    ofDrawCircle(points[0].x, points[0].y, 3);

    ofSetColor(255, 0, 0);

    for (int i = 0; i < forward_points.size(); i++) {
        ofDrawCircle(forward_points[i].x, forward_points[i].y, 2);
    }

    ofSetColor(0, 255, 0);

    for (int i = 0; i < reverse_points.size(); i++) {
        ofDrawCircle(reverse_points[i].x, reverse_points[i].y, 2);
    }
}