Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DO NOT MERGE] Calculate bezier curve length #799

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Added continental geotherm from Chapman (1986) for the Temperature Model in Continental Plate \[Alan Yu; 2025-01-21; [#778](https://github.com/GeodynamicWorldBuilder/WorldBuilder/issues/778), [#797](https://github.com/GeodynamicWorldBuilder/WorldBuilder/pull/797)\]

### Changed
- Modify the implementation of the Bezier curve to account for the special case of colinear points. Also added functionality for determining the arclength of the bezier curve. \[Daniel Douglas; 2025-01-23; [#799](https://github.com/GeodynamicWorldBuilder/WorldBuilder/pull/799)\]
- The tian2019 composition model now returns a mass fraction instead of a mass percentage. \[Daniel Douglas; 2024-11-12; [#767](https://github.com/GeodynamicWorldBuilder/WorldBuilder/pull/767)\]
- Only link to MPI libraries if the cmake variable USE_MPI has been set. No longer automatically link to MPI if MPI is found. \[Rene Gassmoeller; 2025-01-20; [#792](https://github.com/GeodynamicWorldBuilder/WorldBuilder/pull/792)\]
- Change the Doxygen documentation design using the Doxygen Awesome theme. Also fix the main README logo so it appears in the doxygen start page. \[Rene Gassmoeller; 2025-01-21; [#807](https://github.com/GeodynamicWorldBuilder/WorldBuilder/pull/807)\]
Expand Down
2 changes: 1 addition & 1 deletion include/world_builder/features/subducting_plate.h
Original file line number Diff line number Diff line change
Expand Up @@ -236,4 +236,4 @@ namespace WorldBuilder
} // namespace Features
} // namespace WorldBuilder

#endif
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are you doing here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what is happening here? This is the same as the current file and I made no changes to it.

#endif
19 changes: 15 additions & 4 deletions include/world_builder/objects/bezier_curve.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,20 +66,31 @@ namespace WorldBuilder
ClosestPointOnCurve closest_point_on_curve_segment(const Point<2> &p, const bool verbose = false) const;

/**
* @brief
* @brief Returns a point that lies on the bezier curve at some interval x between coordinate i and coordinate
* i + 1. If x = 0, returns point i, if x = 1, returns point i + 1.
*
* @param i
* @param x
* @param i The index of the coordinate that defines the bezier curve.
* @param x The value used to determine additional points that lie on the bezier curve between coordinates i
* and i + 1
* @return Point<2>
*/
Point<2> operator()(const size_t i, const double x) const;

/**
* @brief Returns the total arc length of the bezier curve from the first user-provided coordinate to the last
* user-provided coordinate.
*/
double get_total_arclength() const
{
return total_arclength;
}

private:
std::vector<Point<2> > points;
std::vector<std::array<Point<2>,2 > > control_points;
std::vector<double> lengths;
std::vector<double> angles;

double total_arclength; // The total arc length of the bezier curve
};
}

Expand Down
1 change: 0 additions & 1 deletion source/world_builder/features/interface.cc
Original file line number Diff line number Diff line change
Expand Up @@ -206,4 +206,3 @@ namespace WorldBuilder

} // namespace Features
} // namespace WorldBuilder

danieldouglas92 marked this conversation as resolved.
Show resolved Hide resolved
3 changes: 1 addition & 2 deletions source/world_builder/features/subducting_plate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -850,5 +850,4 @@ namespace WorldBuilder
*/
WB_REGISTER_FEATURE(SubductingPlate, subducting plate)
} // namespace Features
} // namespace WorldBuilder

} // namespace WorldBuilder
danieldouglas92 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -78,5 +78,4 @@ namespace WorldBuilder
} // namespace Composition
} // namespace SubductingPlateModels
} // namespace Features
} // namespace WorldBuilder

} // namespace WorldBuilder
127 changes: 120 additions & 7 deletions source/world_builder/objects/bezier_curve.cc
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,14 @@ namespace WorldBuilder
std::vector<double> angle_constraints = angle_constraints_input;
angle_constraints.resize(n_points,NaN::DQNAN);

// Whether points on the line are colinear. If they are, this is a special case for the bezier curve.
// We determine if the points are colinear by calculating the area of the triangle formed by the points.
// If the area is less than epsilon, the points are colinear.
bool points_are_colinear = false;
const unsigned int max_arclength_discretization = 10;
const double epsilon = 1e-9;


// if no angle is provided, compute the angle as the average angle between the previous and next point.
// The first angle points at the second point and the last angle points at the second to last point.
// The check points are set at a distance of 1/10th the line length from the point in the direction of the angle.
Expand All @@ -71,6 +79,13 @@ namespace WorldBuilder
// Calculate the line between the current point and the following point
const Point<2> P3P2 = points[p_i+1]-points[p_i];

// Check if the points are colinear by determining the area of the triangle
// formed by the 3 points. This is a special case of the bezier curve.
if ( std::abs(points[p_i-1][0] * (points[p_i][1] - points[p_i+1][1]) +
points[p_i][0] * (points[p_i+1][1] - points[p_i-1][1]) +
points[p_i+1][0] * (points[p_i-1][1] - points[p_i][1])) < epsilon)
points_are_colinear = true;

// Calculate the angles of the two lines determined above
const double angle_p1p2 = atan2(P1P2[1],P1P2[0]);
const double angle_p3p1 = atan2(P3P2[1],P3P2[0]);
Expand Down Expand Up @@ -115,6 +130,7 @@ namespace WorldBuilder
control_points[0][0][1] = sin(angles[0])*length*fraction_of_length+p1[1];
control_points[0][1][0] = cos(angles[1])*length*fraction_of_length+p2[0];
control_points[0][1][1] = sin(angles[1])*length*fraction_of_length+p2[1];

{
// Determine which side of the line the control points lie on
const bool side_of_line_1 = (p1[0] - p2[0]) * (control_points[0][1][1] - p1[1])
Expand All @@ -123,12 +139,43 @@ namespace WorldBuilder
const bool side_of_line_2 = (p1[0] - p2[0]) * (p3[1] - p1[1])
- (p1[1] - p2[1]) * (p3[0] - p1[0])
< 0;
if (side_of_line_1 == side_of_line_2)

// The points are colinear, so we need to check if the control points are within the line p1p2
if (points_are_colinear)
{
const bool cp_1_within_p1p2 = (std::min(p1[0], p2[0]) - epsilon <= control_points[0][0][0] && control_points[0][0][0] <= std::max(p1[0], p2[0]) + epsilon) &&
(std::min(p1[1], p2[1]) - epsilon <= control_points[0][0][1] && control_points[0][0][1] <= std::max(p1[1], p2[1]) + epsilon);
const bool cp_2_within_p1p2 = (std::min(p1[0], p2[0]) - epsilon <= control_points[0][1][0] && control_points[0][1][0] <= std::max(p1[0], p2[0]) + epsilon) &&
(std::min(p1[1], p2[1]) - epsilon <= control_points[0][1][1] && control_points[0][1][1] <= std::max(p1[1], p2[1]) + epsilon);
// If the control points are not within the line p1p2, we need to move them within the line p1p2
if (!cp_1_within_p1p2)
{
control_points[0][0][0] = cos(angles[0]+Consts::PI)*length*fraction_of_length+p1[0];
control_points[0][0][1] = sin(angles[0]+Consts::PI)*length*fraction_of_length+p1[1];
}
if (!cp_2_within_p1p2)
{
control_points[0][1][0] = cos(angles[0]+Consts::PI)*length*fraction_of_length+p1[0];
control_points[0][1][1] = sin(angles[0]+Consts::PI)*length*fraction_of_length+p1[1];
}
}

else if (side_of_line_1 == side_of_line_2)
{
// use a 180 degree rotated angle to create this control_point
control_points[0][1][0] = cos(angles[1]+Consts::PI)*length*fraction_of_length+p2[0];
control_points[0][1][1] = sin(angles[1]+Consts::PI)*length*fraction_of_length+p2[1];
}

// There is no closed-form analytic way to express the arc-length of a cubic bezier curve. We approximate
// the arc-length by dividing the curve into 10 points and piecewise linearly connect them. We also store the
// length of the bezier curve within each of these intervals. We calculate the points that lie on the bezier
// curve using the operator function below.
for (unsigned int t_value = 1; t_value <= max_arclength_discretization; ++t_value)
{
lengths[0] = (operator()(0, static_cast <double> (t_value)/max_arclength_discretization) - operator()(0, static_cast <double> (t_value - 1)/max_arclength_discretization)).norm();
total_arclength += lengths[0];
}
}
}

Expand All @@ -139,7 +186,6 @@ namespace WorldBuilder
const double length = (points[p_i]-points[p_i+1]).norm(); // can be squared
control_points[p_i][0][0] = cos(angles[p_i])*length*fraction_of_length+p1[0];
control_points[p_i][0][1] = sin(angles[p_i])*length*fraction_of_length+p1[1];

danieldouglas92 marked this conversation as resolved.
Show resolved Hide resolved
{
// Determine which side of the line the control points lie on
const bool side_of_line_1 = (p1[0] - p2[0]) * (control_points[p_i-1][1][1] - p1[1])
Expand All @@ -148,16 +194,43 @@ namespace WorldBuilder
const bool side_of_line_2 = (p1[0] - p2[0]) * (control_points[p_i][0][1] - p1[1])
- (p1[1] - p2[1]) * (control_points[p_i][0][0] - p1[0])
< 0;
if (side_of_line_1 == side_of_line_2)

// The points are colinear, so we need to check if the control points are within the line p1p2
if (points_are_colinear)
{
const bool cp_1_within_p1p2 = (std::min(p1[0], p2[0]) <= control_points[p_i][0][0] && control_points[p_i][0][0] <= std::max(p1[0], p2[0])) &&
(std::min(p1[1], p2[1]) <= control_points[p_i][0][1] && control_points[p_i][0][1] <= std::max(p1[1], p2[1]));
const bool cp_2_within_p1p2 = (std::min(p1[0], p2[0]) <= control_points[p_i][1][0] && control_points[p_i][1][0] <= std::max(p1[0], p2[0])) &&
(std::min(p1[1], p2[1]) <= control_points[p_i][1][1] && control_points[p_i][1][1] <= std::max(p1[1], p2[1]));
// If the control points are not within the line p1p2, we need to move them within the line p1p2
if (!cp_1_within_p1p2)
{
control_points[p_i][0][0] = cos(angles[p_i]+Consts::PI)*length*fraction_of_length+p1[0];
control_points[p_i][0][1] = sin(angles[p_i]+Consts::PI)*length*fraction_of_length+p1[1];
}
if (!cp_2_within_p1p2)
{
control_points[p_i][1][0] = cos(angles[p_i]+Consts::PI)*length*fraction_of_length+p1[0];
control_points[p_i][1][1] = sin(angles[p_i]+Consts::PI)*length*fraction_of_length+p1[1];
}
}

// Check to see if the angles are different. If the angles are the same, points p1, p2, and p3
danieldouglas92 marked this conversation as resolved.
Show resolved Hide resolved
// are colinear, and therefore the control points will also be colinear with p1, p2 and p3. This
// makes determining which 'side' the control points lie meaningless.
else if (side_of_line_1 == side_of_line_2)
{
// use a 180 degree rotated angle to create this control_point
control_points[p_i][0][0] = cos(angles[p_i]+Consts::PI)*length*fraction_of_length+p1[0];
control_points[p_i][0][1] = sin(angles[p_i]+Consts::PI)*length*fraction_of_length+p1[1];
}
}

control_points[p_i][1][0] = cos(angles[p_i+1])*length*fraction_of_length+points[p_i+1][0];
control_points[p_i][1][1] = sin(angles[p_i+1])*length*fraction_of_length+points[p_i+1][1];
if (!points_are_colinear)
{
control_points[p_i][1][0] = cos(angles[p_i+1])*length*fraction_of_length+p2[0];
control_points[p_i][1][1] = sin(angles[p_i+1])*length*fraction_of_length+p2[1];
}

if (p_i+1 < n_points-1)
{
Expand All @@ -168,15 +241,55 @@ namespace WorldBuilder
const bool side_of_line_2 = (p1[0] - p2[0]) * (p3[1] - p1[1])
- (p1[1] - p2[1]) * (p3[0] - p1[0])
< 0;
if (side_of_line_1 == side_of_line_2)
// The points are colinear, so we need to check if the control points are within the line p1p2
if (points_are_colinear)
{
const bool cp_1_within_p1p2 = (std::min(p1[0], p2[0]) <= control_points[p_i][0][0] && control_points[p_i][0][0] <= std::max(p1[0], p2[0])) &&
(std::min(p1[1], p2[1]) <= control_points[p_i][0][1] && control_points[p_i][0][1] <= std::max(p1[1], p2[1]));
const bool cp_2_within_p1p2 = (std::min(p1[0], p2[0]) <= control_points[p_i][1][0] && control_points[p_i][1][0] <= std::max(p1[0], p2[0])) &&
(std::min(p1[1], p2[1]) <= control_points[p_i][1][1] && control_points[p_i][1][1] <= std::max(p1[1], p2[1]));
// If the control points are not within the line p1p2, we need to move them within the line p1p2
if (!cp_1_within_p1p2)
{
control_points[p_i][0][0] = cos(angles[p_i+1]+Consts::PI)*length*fraction_of_length+p1[0];
control_points[p_i][0][1] = sin(angles[p_i+1]+Consts::PI)*length*fraction_of_length+p1[1];
}
if (!cp_2_within_p1p2)
{
control_points[p_i][1][0] = cos(angles[p_i+1]+Consts::PI)*length*fraction_of_length+p1[0];
control_points[p_i][1][1] = sin(angles[p_i+1]+Consts::PI)*length*fraction_of_length+p1[1];
}
}

// Check to see if the angles are different. If the angles are the same, points p1, p2, and p3
danieldouglas92 marked this conversation as resolved.
Show resolved Hide resolved
// are colinear, and therefore the control points will also be colinear with p1, p2 and p3. This
// makes determining which 'side' the control points lie meaningless.
else if (side_of_line_1 == side_of_line_2)
{
// use a 180 degree rotated angle to create this control_point
control_points[p_i][1][0] = cos(angles[p_i+1]+Consts::PI)*length*fraction_of_length+p2[0];
control_points[p_i][1][1] = sin(angles[p_i+1]+Consts::PI)*length*fraction_of_length+p2[1];
}
}

// There is no closed-form analytic way to express the arc-length of a cubic bezier curve. We approximate
// the arc-length by dividing the curve into 10 points and piecewise linearly connect them. We also store the
// length of the bezier curve within each of these intervals. We calculate the points that lie on the bezier
// curve using the operator function below.
for (unsigned int t_value = 1; t_value <= max_arclength_discretization; ++t_value)
{
lengths[p_i] = (operator()(p_i, static_cast <double> (t_value)/max_arclength_discretization) -
operator()(p_i, static_cast <double> ((t_value - 1))/max_arclength_discretization)).norm();
total_arclength += lengths[p_i];
}
}
}

else
{
lengths[0] = (points[0]-points[1]).norm();
total_arclength = lengths[0];
}
}


Expand All @@ -188,7 +301,6 @@ namespace WorldBuilder
return (1-t)*(1-t)*(1-t)*points[i] + 3*(1-t)*(1-t)*t*control_points[i][0] + 3.*(1-t)*t*t*control_points[i][1]+t*t*t*points[i+1];
}


ClosestPointOnCurve
BezierCurve::closest_point_on_curve_segment(const Point<2> &check_point,
const bool verbose) const
Expand Down Expand Up @@ -560,5 +672,6 @@ namespace WorldBuilder
}
return closest_point_on_curve;
}

} // namespace Objects
} // namespace WorldBuilder
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# x y z d g T vx vy vz c0 c1 tag
-20000 90000 990000 10000 20 6.44612 7.44612 8.44612 0 1 0
-20000 90000 990000 10000 20 6.34463 7.34463 8.34463 0 1 0
-20000 580000 990000 10000 20 4 5 6 0 1 0
-20000 910000 990000 10000 20 4 5 6 0 1 0
70000 -30000 990000 10000 10 1 2 3 1 0 0
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# x y z d g T vx vy vz c0 c1 tag
-20000 90000 990000 10000 20 6.44612 7.44612 8.44612 0 1 0
-20000 90000 990000 10000 20 6.34463 7.34463 8.34463 0 1 0
-20000 580000 990000 10000 20 4 5 6 0 1 0
-20000 910000 990000 10000 20 4 5 6 0 1 0
70000 -30000 990000 10000 10 1 2 3 1 0 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,6 @@
-104000 120000 150000 50e3 1622.56 0 0 0 -1
-14000 70000 150000 50e3 1622.56 0 0 0 -1
-163600 124000 -10 200010 1692.16 0 0 0 -1
90000 200000 0 200000 1692.16 0 0 0 -1
90000 200000 0 200000 600 0 0 2 0
90000 180000 0 200000 600 0 0 2 0
86000 0 0 200000 1692.16 0 0 0 -1
8 changes: 4 additions & 4 deletions tests/gwb-dat/smooth_composition_fault/screen-output.log
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# x y z d g T vx vy vz c0 c1 tag
6371000 -24.5 -1. 0 1600 0 0 2 0.145198 0 0
6365000 -23. -1.3 6e3 1602.64 0 0 2 0.728204 0 0
6371000 -24.5 -1. 0 1600 0 0 2 0.999675 0 0
6365000 -23. -1.3 6e3 1602.64 0 0 2 0.728204 0 0
6361000 -23.3 -1.3 10e3 1604.4 0 0 2 0.879318 0 0
6371000 -23. -1. 0 1600 0 0 2 0.999489 0 0
6370000 -23.4 -1.66 1e3 1600.44 0 0 2 0.000138979 0 0
6371000 -23. -1. 0 1600 0 0 2 0.999489 0 0
6370000 -23.4 -1.66 1e3 1600.44 0 0 0 0 0 -1
6370000 -23.4 -1.45 1e3 1600.44 0 0 2 0.271888 0 0
6370000 -23.4 -1.25 1e3 1600.44 0 0 2 0.968088 0 0
6370000 -23.4 -1.0 1e3 1600.44 0 0 2 0.999865 0 0
Original file line number Diff line number Diff line change
Expand Up @@ -27,30 +27,31 @@ TITLE
[23] = 29.3046 19.2632
[24] = 30 20
[25] = 30.4922 20.4417
[26] = -0.944 10
[27] = 0 10
[28] = 1.424 10
[29] = 3.232 10
[30] = 5.328 10
[31] = 7.616 10
[32] = 10 10
[33] = 12.384 10
[34] = 14.672 10
[35] = 16.768 10
[36] = 18.576 10
[37] = 20 10
[38] = 20.944 10
[39] = 19.3866 10.32
[40] = 20 10
[41] = 21.0437 10.28
[42] = 22.3976 11.04
[43] = 23.9419 12.16
[44] = 25.5565 13.52
[45] = 27.1213 15
[46] = 28.5165 16.48
[47] = 29.6219 17.84
[48] = 30.3176 18.96
[49] = 30.4837 19.72
[50] = 30 20
[51] = 28.7466 19.68
[26] = 34.2854 34.2854
[27] = -0.944 10
[28] = 0 10
[29] = 1.424 10
[30] = 3.232 10
[31] = 5.328 10
[32] = 7.616 10
[33] = 10 10
[34] = 12.384 10
[35] = 14.672 10
[36] = 16.768 10
[37] = 18.576 10
[38] = 20 10
[39] = 20.944 10
[40] = 19.3866 10.32
[41] = 20 10
[42] = 21.0437 10.28
[43] = 22.3976 11.04
[44] = 23.9419 12.16
[45] = 25.5565 13.52
[46] = 27.1213 15
[47] = 28.5165 16.48
[48] = 29.6219 17.84
[49] = 30.3176 18.96
[50] = 30.4837 19.72
[51] = 30 20
[52] = 28.7466 19.68

Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ Test

[0] = 7141.78
[1] = 70.7107
[2] = 482.865
[3] = 153.282
[4] = 468.712
[5] = 139.151
[2] = 481.055
[3] = 155.091
[4] = 466.902
[5] = 140.96
[6] = 1070.96
[7] = 6141.53
[8] = inf
Expand Down
Loading
Loading