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

Allow rectangular and box masks to be rotated #845

Merged
merged 10 commits into from
Mar 7, 2018

Conversation

stinebuu
Copy link
Contributor

This PR adds the optional parameters azimuth_angle and polar_angle to the rectangular and box mask dictionaries so that the masks can be rotated.

This have led to some changes in the boxMask class, most notably with the inside(..) function and when finding the boundary box. When checking if a point is inside the mask, we have to take rotation into account. If we have rotation, we can no longer use lower_left and upper_right to define the boundary box, so we have to calculate the minimum and maximum values of the mask. This is done in create_min_max_values_().

The functions check to see if we have rotation as much as possible, so that we can use lower_left and upper_right and avoid unnecessary rotation calculations if we do not have rotation.

I have also added a test.

@stinebuu stinebuu changed the title Allow rectangle and box masks to be rotated Allow rectangular and box masks to be rotated Oct 25, 2017
@heplesser heplesser added ZC: Model DO NOT USE THIS LABEL I: No breaking change Previously written code will work as before, no one should note anything changing (aside the fix) ZP: PR Created DO NOT USE THIS LABEL S: High Should be handled next T: Enhancement New functionality, model or documentation labels Oct 30, 2017
Copy link
Contributor

@hakonsbm hakonsbm left a comment

Choose a reason for hiding this comment

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

Thank you for this, it looks good. As far as I can see, there are just some minor things to fix, see below.

topology/mask.h Outdated
/**
* Calculate the min/max x, y, z values in case of a rotated box.
*/
void create_min_max_values_();
Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps rename to calculate_min_max_values_?

return ( lower_left_ <= p ) && ( p <= upper_right_ );
}

// If we have a rotated box, We rotate the point down to the unrotated box,
Copy link
Contributor

Choose a reason for hiding this comment

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

Small w.

const double cntr_y = ( upper_right_[ 1 ] + lower_left_[ 1 ] ) * 0.5;
const double cntr_z = ( upper_right_[ 2 ] + lower_left_[ 2 ] ) * 0.5;

const double new_x = ( ( p[ 0 ] - cntr_x ) * azimuth_cos_
Copy link
Contributor

Choose a reason for hiding this comment

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

For efficiency p[ {0|1|2} ] - cntr_{x|y|z} can be precalculated after calculating the centres.


const double new_x = ( ( p[ 0 ] - cntr_x ) * azimuth_cos_
+ ( p[ 1 ] - cntr_y ) * azimuth_sin_ ) * polar_cos_
- ( ( p[ 2 ] - cntr_z ) * polar_sin_ ) + cntr_x;
Copy link
Contributor

Choose a reason for hiding this comment

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

These lines should have the same indentation.

+ ( p[ 1 ] - cntr_y ) * azimuth_cos_ + cntr_y;
const double new_z = ( ( p[ 0 ] - cntr_x ) * azimuth_cos_
+ ( p[ 1 ] - cntr_y ) * azimuth_sin_ ) * polar_sin_
+ ( ( p[ 2 ] - cntr_z ) * polar_cos_ ) + cntr_z;
Copy link
Contributor

Choose a reason for hiding this comment

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

Give lines same indentation here too.

def test_RotatedBoxMaskByAzimuthAndPolarAngle(self):
"""Test rotated box mask with azimuth and polar angle."""

pos = [[x*1., y*1., z*1.] for x in range(-2, 3)
Copy link
Contributor

Choose a reason for hiding this comment

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

Missing whitespace around *-operators.

[38, 39, 44, 58, 59, 64, 69, 70, 84, 89, 90])

def test_RotatedRectangleOutsideOrigo(self):
"""Test rotated rectangle where mask is outside origo."""
Copy link
Contributor

Choose a reason for hiding this comment

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

As it is the mask that contains things, not origo, perhaps something along the lines of "where origo is not in mask"?

def test_RotatedBoxOutsideOrigo(self):
"""Test rotated box where mask is outside origo."""

pos = [[x*1., y*1., z*1.] for x in range(-2, 3)
Copy link
Contributor

Choose a reason for hiding this comment

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

Missing whitespace around *-operators.


44
14 -> 40
36
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like there's something missing with these example connections.

def suite():
suite = unittest.makeSuite(RotatedRectangularMask, 'test')
return suite

Copy link
Contributor

Choose a reason for hiding this comment

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

Two blank lines after the function definition.

calculate_min_max_values, precalculation of some of the values,
indentation, changed some comments.
Copy link
Contributor

@hakonsbm hakonsbm left a comment

Choose a reason for hiding this comment

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

Great, thank you!

Copy link
Contributor

@heplesser heplesser left a comment

Choose a reason for hiding this comment

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

Looks quite fine, but see comments. We should discuss once more about the "eps" that we are adding, just wondering if we are digging some future grave. For 3D, we olny have two angles. Which rotation possibility are we missing?

topology/mask.h Outdated
Position< D > lower_left_;
Position< D > upper_right_;

Position< D > min_values_;
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you add a comment what these are and how they relate to lower_left/upper_right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@heplesser I have added the comment. The min/max values correspond to the lower_left/upper_right of the boundary box around the rotated box. Should I change the names to something like bbox_lower_left_ and bbox_upper_right_ to make it more clear?

* upper_right - Position of upper right corner (array of doubles)
* lower_left - Position of lower left corner (array of doubles)
* upper_right - Position of upper right corner (array of doubles)
* azimuth_angle - Rotation angle in degrees from x-axis (double), optional
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it documented somewhere that the angles are in degrees? Maybe also add that the polar angle does not apply to 2D.

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 have now added a note about the angles in the python documentation for masks.

topology/mask.h Outdated
"polar_angle not defined in 2D." );
}

is_rotated_ = azimuth_angle_ or polar_angle_;
Copy link
Contributor

Choose a reason for hiding this comment

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

Make explicit comparisons to 0!

// We need to add a small epsilon in case of rounding errors.
const double x_length = upper_right_[ 0 ] - lower_left_[ 0 ];
const double y_length = upper_right_[ 1 ] - lower_left_[ 1 ];
const Position< 2 > eps( x_length * 1e-12, y_length * 1e-12 );
Copy link
Contributor

Choose a reason for hiding this comment

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

We can use the cntr_* values to determine eps, saving some operations.


// See https://en.wikipedia.org/wiki/Rotation_matrix for more.

const double cntr_x = ( upper_right_[ 0 ] + lower_left_[ 0 ] ) * 0.5;
Copy link
Contributor

Choose a reason for hiding this comment

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

If I am not entirely off, we need to check quite often if a point is inside a (fixed) box. Wouldn't it make more sense to precompute all values that do not depend on p in the box constructor?


// See https://en.wikipedia.org/wiki/Rotation_matrix for more.

const double cntr_x = ( upper_right_[ 0 ] + lower_left_[ 0 ] ) * 0.5;
Copy link
Contributor

Choose a reason for hiding this comment

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

Same comments apply as for 2D.

Position< 2 > upper_right_cos = ( upper_right_ - cntr ) * azimuth_cos_;
Position< 2 > upper_right_sin = ( upper_right_ - cntr ) * azimuth_sin_;

double rotatedLLx = lower_left_cos[ 0 ] - lower_left_sin[ 1 ] + cntr_x;
Copy link
Contributor

Choose a reason for hiding this comment

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

I suppose these can call be const?

double rotatedLLLx =
( lower_left_cos[ 0 ] - lower_left_sin[ 1 ] ) * polar_cos_
- lower_left_polar_sin + cntr_x;
double rotatedLLLy = lower_left_sin[ 0 ] + lower_left_cos[ 1 ] + cntr_y;
Copy link
Contributor

Choose a reason for hiding this comment

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

Can't these all be const?

position is pre-calculated in constructor, added some const, eps no
longer calculated from length, but just a constant, small number.
@stinebuu
Copy link
Contributor Author

stinebuu commented Dec 8, 2017

@heplesser thank you for your review! I have tried to address your comments, please take a look.

I changed the "eps" to be constant 1e-12 for x, y and z. Having it depend on the center of the box will fail when the center is in the origin, as eps then will be zero.

When it comes to the rotation angles, we are missing rotation from the y-axis to the z-axis (the γ here ). Do you want me to add it? We do not have it for the elliptical mask, but I could add it there as well and make a PR of course.

@terhorstd terhorstd added this to the NEST 2.16 milestone Mar 5, 2018
@heplesser
Copy link
Contributor

@stinebuu Thanks for addressing the issues. I think this ready to merge now. Would you create a follow-up issue to add rotation about the third Euler angle?

@heplesser heplesser merged commit c11bb2c into nest:master Mar 7, 2018
@stinebuu stinebuu deleted the tilted_rectangle_mask branch March 7, 2018 13:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
I: No breaking change Previously written code will work as before, no one should note anything changing (aside the fix) S: High Should be handled next T: Enhancement New functionality, model or documentation ZC: Model DO NOT USE THIS LABEL ZP: PR Created DO NOT USE THIS LABEL
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants