[libcamera-devel] libcamera: geometry: Add Size members to clamp to a min/max
diff mbox series

Message ID 20220331120556.2147745-1-kieran.bingham@ideasonboard.com
State Superseded
Headers show
Series
  • [libcamera-devel] libcamera: geometry: Add Size members to clamp to a min/max
Related show

Commit Message

Kieran Bingham March 31, 2022, 12:05 p.m. UTC
Provide two new operations to support clamping a Size to a given
minimum or maximum Size, or returning a const variant of the same
restriction.

Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
---

I was expecting to use this for clamping the block width and height
for the AF hardware restrictions on the IPU3 ... but it turns out to be
not quite appropriate to use a Size there, as the clamped values are
stored in an IPU3 struct directly.

However, having made this - I think it is likely to be useful elsewhere
so posting so it doesn't get lost.

Tests added, so it could be merged already even if there is no current
user yet. I expect it's more likely to get used if it exists, than if it
doesn't ;-)


 include/libcamera/geometry.h | 16 ++++++++++++++++
 src/libcamera/geometry.cpp   | 21 +++++++++++++++++++++
 test/geometry.cpp            | 24 ++++++++++++++++++++++--
 3 files changed, 59 insertions(+), 2 deletions(-)

Comments

Umang Jain April 5, 2022, 1:41 p.m. UTC | #1
Hi Kieran,

Thank you for the patch.

On 3/31/22 17:35, Kieran Bingham via libcamera-devel wrote:
> Provide two new operations to support clamping a Size to a given
> minimum or maximum Size, or returning a const variant of the same
> restriction.
>
> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> ---
>
> I was expecting to use this for clamping the block width and height
> for the AF hardware restrictions on the IPU3 ... but it turns out to be
> not quite appropriate to use a Size there, as the clamped values are
> stored in an IPU3 struct directly.
>
> However, having made this - I think it is likely to be useful elsewhere
> so posting so it doesn't get lost.
>
> Tests added, so it could be merged already even if there is no current
> user yet. I expect it's more likely to get used if it exists, than if it
> doesn't ;-)


+1

>
>
>   include/libcamera/geometry.h | 16 ++++++++++++++++
>   src/libcamera/geometry.cpp   | 21 +++++++++++++++++++++
>   test/geometry.cpp            | 24 ++++++++++++++++++++++--
>   3 files changed, 59 insertions(+), 2 deletions(-)
>
> diff --git a/include/libcamera/geometry.h b/include/libcamera/geometry.h
> index 7838b6793617..a447beb55965 100644
> --- a/include/libcamera/geometry.h
> +++ b/include/libcamera/geometry.h
> @@ -93,6 +93,13 @@ public:
>   		return *this;
>   	}
>   
> +	Size &clamp(const Size &minimum, const Size &maximum)
> +	{
> +		width = std::clamp(width, minimum.width, maximum.width);
> +		height = std::clamp(height, minimum.height, maximum.height);
> +		return *this;
> +	}
> +
>   	Size &growBy(const Size &margins)
>   	{
>   		width += margins.width;
> @@ -141,6 +148,15 @@ public:
>   		};
>   	}
>   
> +	__nodiscard constexpr Size clampedTo(const Size &minimum,
> +					     const Size &maximum) const
> +	{
> +		return {
> +			std::clamp(width, minimum.width, maximum.width),
> +			std::clamp(height, minimum.height, maximum.height)
> +		};
> +	}
> +
>   	__nodiscard constexpr Size grownBy(const Size &margins) const
>   	{
>   		return {
> diff --git a/src/libcamera/geometry.cpp b/src/libcamera/geometry.cpp
> index cb3c2de18d5e..7ac053a536c1 100644
> --- a/src/libcamera/geometry.cpp
> +++ b/src/libcamera/geometry.cpp
> @@ -173,6 +173,18 @@ const std::string Size::toString() const
>    * \return A reference to this object
>    */
>   
> +/**
> + * \fn Size::clamp(const Size &minimum, const Size &maximum)
> + * \brief Restrict the size to be constrained within the \a minimum and \a maximum
> + * \param[in] minimum The minimum size
> + * \param[in] maximum The maximum size
> + *
> + * This function restricts the width and height to the constraints of the \a
> + * minimum and the \a maximum sizes given.
> + *
> + * \return A reference to this object
> + */
> +
>   /**
>    * \fn Size::growBy(const Size &margins)
>    * \brief Grow the size by \a margins in place
> @@ -231,6 +243,15 @@ const std::string Size::toString() const
>    * height of this size and the \a expand size
>    */
>   
> +/**
> + * \fn Size::clampedTo(const Size &minimum, const Size &maximum)
> + * \brief Restrict the size to be constrained within the \a minimum and \a maximum
> + * \param[in] minimum The minimum size
> + * \param[in] maximum The maximum size
> + * \return A size whose width and height match this size within the constraints
> + * of the \a minimum and \a maximum sizes given.
> + */
> +
>   /**
>    * \fn Size::grownBy(const Size &margins)
>    * \brief Grow the size by \a margins
> diff --git a/test/geometry.cpp b/test/geometry.cpp
> index 5125692496b3..1d3d3cad7174 100644
> --- a/test/geometry.cpp
> +++ b/test/geometry.cpp
> @@ -106,7 +106,7 @@ protected:
>   
>   		/*
>   		 * Test alignDownTo(), alignUpTo(), boundTo(), expandTo(),
> -		 * growBy() and shrinkBy()
> +		 * clamp() growBy() and shrinkBy()
>   		 */
>   		Size s(50, 50);
>   
> @@ -134,6 +134,18 @@ protected:
>   			return TestFail;
>   		}
>   
> +		s.clamp({ 80, 120 }, { 160, 240 });


is it okay to ignore the reference returned by .clamp() ? I think so, 
since it's doesn't have __nodiscard anyways,

Reviewed-by: Umang Jain <umang.jain@ideasonboard.com>

> +		if (s != Size(80, 120)) {
> +			cout << "Size::clamp (minium) test failed" << endl;
> +			return TestFail;
> +		}
> +
> +		s.clamp({ 20, 30 }, { 50, 50 });
> +		if (s != Size(50, 50)) {
> +			cout << "Size::clamp (maximum) test failed" << endl;
> +			return TestFail;
> +		}
> +
>   		s.growBy({ 10, 20 });
>   		if (s != Size(60, 70)) {
>   			cout << "Size::growBy() test failed" << endl;
> @@ -162,7 +174,7 @@ protected:
>   
>   		/*
>   		 * Test alignedDownTo(), alignedUpTo(), boundedTo(),
> -		 * expandedTo(), grownBy() and shrunkBy()
> +		 * expandedTo(), clampedTo(), grownBy() and shrunkBy()
>   		 */
>   		if (Size(0, 0).alignedDownTo(16, 8) != Size(0, 0) ||
>   		    Size(1, 1).alignedDownTo(16, 8) != Size(0, 0) ||
> @@ -192,6 +204,14 @@ protected:
>   			return TestFail;
>   		}
>   
> +		if (Size(0, 0).clampedTo({ 60, 40 }, { 80, 90 }) != Size(60, 40) ||
> +		    Size(100, 100).clampedTo({ 60, 40 }, { 80, 90 }) != Size(80, 90) ||
> +		    Size(30, 100).clampedTo({ 60, 40 }, { 80, 90 }) != Size(60, 90) ||
> +		    Size(100, 30).clampedTo({ 60, 40 }, { 80, 90 }) != Size(80, 40)) {
> +			cout << "Size::clampedTo() test failed" << endl;
> +			return TestFail;
> +		}
> +
>   		if (Size(0, 0).grownBy({ 10, 20 }) != Size(10, 20) ||
>   		    Size(200, 50).grownBy({ 10, 20 }) != Size(210, 70)) {
>   			cout << "Size::grownBy() test failed" << endl;
Laurent Pinchart April 5, 2022, 2:18 p.m. UTC | #2
On Tue, Apr 05, 2022 at 07:11:15PM +0530, Umang Jain via libcamera-devel wrote:
> Hi Kieran,
> 
> Thank you for the patch.
> 
> On 3/31/22 17:35, Kieran Bingham via libcamera-devel wrote:
> > Provide two new operations to support clamping a Size to a given
> > minimum or maximum Size, or returning a const variant of the same
> > restriction.

Did you really mean "restriction" here ?

> >
> > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> > ---
> >
> > I was expecting to use this for clamping the block width and height
> > for the AF hardware restrictions on the IPU3 ... but it turns out to be
> > not quite appropriate to use a Size there, as the clamped values are
> > stored in an IPU3 struct directly.
> >
> > However, having made this - I think it is likely to be useful elsewhere
> > so posting so it doesn't get lost.

.clamp(min, max) could be implemented as .expandTo(min).shrinkTo(max),
but Size::clamp() is likely more explicit and better.

> > Tests added, so it could be merged already even if there is no current
> > user yet. I expect it's more likely to get used if it exists, than if it
> > doesn't ;-)
> 
> +1
> 
> >   include/libcamera/geometry.h | 16 ++++++++++++++++
> >   src/libcamera/geometry.cpp   | 21 +++++++++++++++++++++
> >   test/geometry.cpp            | 24 ++++++++++++++++++++++--
> >   3 files changed, 59 insertions(+), 2 deletions(-)
> >
> > diff --git a/include/libcamera/geometry.h b/include/libcamera/geometry.h
> > index 7838b6793617..a447beb55965 100644
> > --- a/include/libcamera/geometry.h
> > +++ b/include/libcamera/geometry.h
> > @@ -93,6 +93,13 @@ public:
> >   		return *this;
> >   	}
> >   
> > +	Size &clamp(const Size &minimum, const Size &maximum)
> > +	{
> > +		width = std::clamp(width, minimum.width, maximum.width);
> > +		height = std::clamp(height, minimum.height, maximum.height);
> > +		return *this;
> > +	}
> > +
> >   	Size &growBy(const Size &margins)
> >   	{
> >   		width += margins.width;
> > @@ -141,6 +148,15 @@ public:
> >   		};
> >   	}
> >   
> > +	__nodiscard constexpr Size clampedTo(const Size &minimum,
> > +					     const Size &maximum) const
> > +	{
> > +		return {
> > +			std::clamp(width, minimum.width, maximum.width),
> > +			std::clamp(height, minimum.height, maximum.height)
> > +		};
> > +	}
> > +
> >   	__nodiscard constexpr Size grownBy(const Size &margins) const
> >   	{
> >   		return {
> > diff --git a/src/libcamera/geometry.cpp b/src/libcamera/geometry.cpp
> > index cb3c2de18d5e..7ac053a536c1 100644
> > --- a/src/libcamera/geometry.cpp
> > +++ b/src/libcamera/geometry.cpp
> > @@ -173,6 +173,18 @@ const std::string Size::toString() const
> >    * \return A reference to this object
> >    */
> >   
> > +/**
> > + * \fn Size::clamp(const Size &minimum, const Size &maximum)
> > + * \brief Restrict the size to be constrained within the \a minimum and \a maximum

"Restrict the size to be constrainted" sounds quite weird to me. Maybe
using the word "clamp" would be better ?

> > + * \param[in] minimum The minimum size
> > + * \param[in] maximum The maximum size
> > + *
> > + * This function restricts the width and height to the constraints of the \a
> > + * minimum and the \a maximum sizes given.

And here too, the help text doesn't make it clear what the function
does. I get more information from the function name than from the
documentation :-)

Same for clampedTo().

> > + *
> > + * \return A reference to this object
> > + */
> > +
> >   /**
> >    * \fn Size::growBy(const Size &margins)
> >    * \brief Grow the size by \a margins in place
> > @@ -231,6 +243,15 @@ const std::string Size::toString() const
> >    * height of this size and the \a expand size
> >    */
> >   
> > +/**
> > + * \fn Size::clampedTo(const Size &minimum, const Size &maximum)
> > + * \brief Restrict the size to be constrained within the \a minimum and \a maximum
> > + * \param[in] minimum The minimum size
> > + * \param[in] maximum The maximum size
> > + * \return A size whose width and height match this size within the constraints
> > + * of the \a minimum and \a maximum sizes given.
> > + */
> > +
> >   /**
> >    * \fn Size::grownBy(const Size &margins)
> >    * \brief Grow the size by \a margins
> > diff --git a/test/geometry.cpp b/test/geometry.cpp
> > index 5125692496b3..1d3d3cad7174 100644
> > --- a/test/geometry.cpp
> > +++ b/test/geometry.cpp
> > @@ -106,7 +106,7 @@ protected:
> >   
> >   		/*
> >   		 * Test alignDownTo(), alignUpTo(), boundTo(), expandTo(),
> > -		 * growBy() and shrinkBy()
> > +		 * clamp() growBy() and shrinkBy()

s/ growBy/, growBy/

> >   		 */
> >   		Size s(50, 50);
> >   
> > @@ -134,6 +134,18 @@ protected:
> >   			return TestFail;
> >   		}
> >   
> > +		s.clamp({ 80, 120 }, { 160, 240 });
> 
> 
> is it okay to ignore the reference returned by .clamp() ? I think so, 
> since it's doesn't have __nodiscard anyways,

The __nodiscard attribute was added to the "-ed" versions of the
functions, to make sure that someone will not accidentally write

	s.clampedTo({ 80, 120 }, { 160, 240 });

when they meant

	s.clamp({ 80, 120 }, { 160, 240 });

clamp() is meant to be called with its return value potentially ignored,
otherwise it would be hard to clamp a Size() in-place.

> Reviewed-by: Umang Jain <umang.jain@ideasonboard.com>
> 
> > +		if (s != Size(80, 120)) {
> > +			cout << "Size::clamp (minium) test failed" << endl;
> > +			return TestFail;
> > +		}
> > +
> > +		s.clamp({ 20, 30 }, { 50, 50 });
> > +		if (s != Size(50, 50)) {
> > +			cout << "Size::clamp (maximum) test failed" << endl;
> > +			return TestFail;
> > +		}
> > +
> >   		s.growBy({ 10, 20 });
> >   		if (s != Size(60, 70)) {
> >   			cout << "Size::growBy() test failed" << endl;
> > @@ -162,7 +174,7 @@ protected:
> >   
> >   		/*
> >   		 * Test alignedDownTo(), alignedUpTo(), boundedTo(),
> > -		 * expandedTo(), grownBy() and shrunkBy()
> > +		 * expandedTo(), clampedTo(), grownBy() and shrunkBy()
> >   		 */
> >   		if (Size(0, 0).alignedDownTo(16, 8) != Size(0, 0) ||
> >   		    Size(1, 1).alignedDownTo(16, 8) != Size(0, 0) ||
> > @@ -192,6 +204,14 @@ protected:
> >   			return TestFail;
> >   		}
> >   
> > +		if (Size(0, 0).clampedTo({ 60, 40 }, { 80, 90 }) != Size(60, 40) ||
> > +		    Size(100, 100).clampedTo({ 60, 40 }, { 80, 90 }) != Size(80, 90) ||
> > +		    Size(30, 100).clampedTo({ 60, 40 }, { 80, 90 }) != Size(60, 90) ||
> > +		    Size(100, 30).clampedTo({ 60, 40 }, { 80, 90 }) != Size(80, 40)) {
> > +			cout << "Size::clampedTo() test failed" << endl;
> > +			return TestFail;
> > +		}
> > +
> >   		if (Size(0, 0).grownBy({ 10, 20 }) != Size(10, 20) ||
> >   		    Size(200, 50).grownBy({ 10, 20 }) != Size(210, 70)) {
> >   			cout << "Size::grownBy() test failed" << endl;
Umang Jain April 5, 2022, 2:24 p.m. UTC | #3
Hi,

On 4/5/22 19:48, Laurent Pinchart wrote:
> On Tue, Apr 05, 2022 at 07:11:15PM +0530, Umang Jain via libcamera-devel wrote:
>> Hi Kieran,
>>
>> Thank you for the patch.
>>
>> On 3/31/22 17:35, Kieran Bingham via libcamera-devel wrote:
>>> Provide two new operations to support clamping a Size to a given
>>> minimum or maximum Size, or returning a const variant of the same
>>> restriction.
> Did you really mean "restriction" here ?
>
>>> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
>>> ---
>>>
>>> I was expecting to use this for clamping the block width and height
>>> for the AF hardware restrictions on the IPU3 ... but it turns out to be
>>> not quite appropriate to use a Size there, as the clamped values are
>>> stored in an IPU3 struct directly.
>>>
>>> However, having made this - I think it is likely to be useful elsewhere
>>> so posting so it doesn't get lost.
> .clamp(min, max) could be implemented as .expandTo(min).shrinkTo(max),
> but Size::clamp() is likely more explicit and better.
>
>>> Tests added, so it could be merged already even if there is no current
>>> user yet. I expect it's more likely to get used if it exists, than if it
>>> doesn't ;-)
>> +1
>>
>>>    include/libcamera/geometry.h | 16 ++++++++++++++++
>>>    src/libcamera/geometry.cpp   | 21 +++++++++++++++++++++
>>>    test/geometry.cpp            | 24 ++++++++++++++++++++++--
>>>    3 files changed, 59 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/include/libcamera/geometry.h b/include/libcamera/geometry.h
>>> index 7838b6793617..a447beb55965 100644
>>> --- a/include/libcamera/geometry.h
>>> +++ b/include/libcamera/geometry.h
>>> @@ -93,6 +93,13 @@ public:
>>>    		return *this;
>>>    	}
>>>    
>>> +	Size &clamp(const Size &minimum, const Size &maximum)
>>> +	{
>>> +		width = std::clamp(width, minimum.width, maximum.width);
>>> +		height = std::clamp(height, minimum.height, maximum.height);
>>> +		return *this;
>>> +	}
>>> +
>>>    	Size &growBy(const Size &margins)
>>>    	{
>>>    		width += margins.width;
>>> @@ -141,6 +148,15 @@ public:
>>>    		};
>>>    	}
>>>    
>>> +	__nodiscard constexpr Size clampedTo(const Size &minimum,
>>> +					     const Size &maximum) const
>>> +	{
>>> +		return {
>>> +			std::clamp(width, minimum.width, maximum.width),
>>> +			std::clamp(height, minimum.height, maximum.height)
>>> +		};
>>> +	}
>>> +
>>>    	__nodiscard constexpr Size grownBy(const Size &margins) const
>>>    	{
>>>    		return {
>>> diff --git a/src/libcamera/geometry.cpp b/src/libcamera/geometry.cpp
>>> index cb3c2de18d5e..7ac053a536c1 100644
>>> --- a/src/libcamera/geometry.cpp
>>> +++ b/src/libcamera/geometry.cpp
>>> @@ -173,6 +173,18 @@ const std::string Size::toString() const
>>>     * \return A reference to this object
>>>     */
>>>    
>>> +/**
>>> + * \fn Size::clamp(const Size &minimum, const Size &maximum)
>>> + * \brief Restrict the size to be constrained within the \a minimum and \a maximum
> "Restrict the size to be constrainted" sounds quite weird to me. Maybe
> using the word "clamp" would be better ?


Now that I am reading it, I agree with Laurent :S

>
>>> + * \param[in] minimum The minimum size
>>> + * \param[in] maximum The maximum size
>>> + *
>>> + * This function restricts the width and height to the constraints of the \a
>>> + * minimum and the \a maximum sizes given.
> And here too, the help text doesn't make it clear what the function
> does. I get more information from the function name than from the
> documentation :-)
>
> Same for clampedTo().
>
>>> + *
>>> + * \return A reference to this object
>>> + */
>>> +
>>>    /**
>>>     * \fn Size::growBy(const Size &margins)
>>>     * \brief Grow the size by \a margins in place
>>> @@ -231,6 +243,15 @@ const std::string Size::toString() const
>>>     * height of this size and the \a expand size
>>>     */
>>>    
>>> +/**
>>> + * \fn Size::clampedTo(const Size &minimum, const Size &maximum)
>>> + * \brief Restrict the size to be constrained within the \a minimum and \a maximum
>>> + * \param[in] minimum The minimum size
>>> + * \param[in] maximum The maximum size
>>> + * \return A size whose width and height match this size within the constraints
>>> + * of the \a minimum and \a maximum sizes given.
>>> + */
>>> +
>>>    /**
>>>     * \fn Size::grownBy(const Size &margins)
>>>     * \brief Grow the size by \a margins
>>> diff --git a/test/geometry.cpp b/test/geometry.cpp
>>> index 5125692496b3..1d3d3cad7174 100644
>>> --- a/test/geometry.cpp
>>> +++ b/test/geometry.cpp
>>> @@ -106,7 +106,7 @@ protected:
>>>    
>>>    		/*
>>>    		 * Test alignDownTo(), alignUpTo(), boundTo(), expandTo(),
>>> -		 * growBy() and shrinkBy()
>>> +		 * clamp() growBy() and shrinkBy()
> s/ growBy/, growBy/
>
>>>    		 */
>>>    		Size s(50, 50);
>>>    
>>> @@ -134,6 +134,18 @@ protected:
>>>    			return TestFail;
>>>    		}
>>>    
>>> +		s.clamp({ 80, 120 }, { 160, 240 });
>>
>> is it okay to ignore the reference returned by .clamp() ? I think so,
>> since it's doesn't have __nodiscard anyways,
> The __nodiscard attribute was added to the "-ed" versions of the
> functions, to make sure that someone will not accidentally write
>
> 	s.clampedTo({ 80, 120 }, { 160, 240 });
>
> when they meant
>
> 	s.clamp({ 80, 120 }, { 160, 240 });


Make sense.

>
> clamp() is meant to be called with its return value potentially ignored,
> otherwise it would be hard to clamp a Size() in-place.


Yeah, I wasn't sure if the complier warns out(or not) when we are 
ignoring the returned references. Yes, the in-place op. makes and I have 
written them a numerous times (with ignoring the reference), but 
something I didn't give a thought about, until now.

>
>> Reviewed-by: Umang Jain <umang.jain@ideasonboard.com>
>>
>>> +		if (s != Size(80, 120)) {
>>> +			cout << "Size::clamp (minium) test failed" << endl;
>>> +			return TestFail;
>>> +		}
>>> +
>>> +		s.clamp({ 20, 30 }, { 50, 50 });
>>> +		if (s != Size(50, 50)) {
>>> +			cout << "Size::clamp (maximum) test failed" << endl;
>>> +			return TestFail;
>>> +		}
>>> +
>>>    		s.growBy({ 10, 20 });
>>>    		if (s != Size(60, 70)) {
>>>    			cout << "Size::growBy() test failed" << endl;
>>> @@ -162,7 +174,7 @@ protected:
>>>    
>>>    		/*
>>>    		 * Test alignedDownTo(), alignedUpTo(), boundedTo(),
>>> -		 * expandedTo(), grownBy() and shrunkBy()
>>> +		 * expandedTo(), clampedTo(), grownBy() and shrunkBy()
>>>    		 */
>>>    		if (Size(0, 0).alignedDownTo(16, 8) != Size(0, 0) ||
>>>    		    Size(1, 1).alignedDownTo(16, 8) != Size(0, 0) ||
>>> @@ -192,6 +204,14 @@ protected:
>>>    			return TestFail;
>>>    		}
>>>    
>>> +		if (Size(0, 0).clampedTo({ 60, 40 }, { 80, 90 }) != Size(60, 40) ||
>>> +		    Size(100, 100).clampedTo({ 60, 40 }, { 80, 90 }) != Size(80, 90) ||
>>> +		    Size(30, 100).clampedTo({ 60, 40 }, { 80, 90 }) != Size(60, 90) ||
>>> +		    Size(100, 30).clampedTo({ 60, 40 }, { 80, 90 }) != Size(80, 40)) {
>>> +			cout << "Size::clampedTo() test failed" << endl;
>>> +			return TestFail;
>>> +		}
>>> +
>>>    		if (Size(0, 0).grownBy({ 10, 20 }) != Size(10, 20) ||
>>>    		    Size(200, 50).grownBy({ 10, 20 }) != Size(210, 70)) {
>>>    			cout << "Size::grownBy() test failed" << endl;
Kieran Bingham April 7, 2022, 2:38 p.m. UTC | #4
Quoting Laurent Pinchart (2022-04-05 15:18:07)
> On Tue, Apr 05, 2022 at 07:11:15PM +0530, Umang Jain via libcamera-devel wrote:
> > Hi Kieran,
> > 
> > Thank you for the patch.
> > 
> > On 3/31/22 17:35, Kieran Bingham via libcamera-devel wrote:
> > > Provide two new operations to support clamping a Size to a given
> > > minimum or maximum Size, or returning a const variant of the same
> > > restriction.
> 
> Did you really mean "restriction" here ?

I did yes. I mean the same restriction of the minimum and maximum Size
but as a const.


> > > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> > > ---
> > >
> > > I was expecting to use this for clamping the block width and height
> > > for the AF hardware restrictions on the IPU3 ... but it turns out to be
> > > not quite appropriate to use a Size there, as the clamped values are
> > > stored in an IPU3 struct directly.
> > >
> > > However, having made this - I think it is likely to be useful elsewhere
> > > so posting so it doesn't get lost.
> 
> .clamp(min, max) could be implemented as .expandTo(min).shrinkTo(max),
> but Size::clamp() is likely more explicit and better.

Yes, a caller could call .expandTo.shrinkTo instead already but I don't
think that should be the implementation detail here.

> 
> > > Tests added, so it could be merged already even if there is no current
> > > user yet. I expect it's more likely to get used if it exists, than if it
> > > doesn't ;-)
> > 
> > +1
> > 
> > >   include/libcamera/geometry.h | 16 ++++++++++++++++
> > >   src/libcamera/geometry.cpp   | 21 +++++++++++++++++++++
> > >   test/geometry.cpp            | 24 ++++++++++++++++++++++--
> > >   3 files changed, 59 insertions(+), 2 deletions(-)
> > >
> > > diff --git a/include/libcamera/geometry.h b/include/libcamera/geometry.h
> > > index 7838b6793617..a447beb55965 100644
> > > --- a/include/libcamera/geometry.h
> > > +++ b/include/libcamera/geometry.h
> > > @@ -93,6 +93,13 @@ public:
> > >             return *this;
> > >     }
> > >   
> > > +   Size &clamp(const Size &minimum, const Size &maximum)
> > > +   {
> > > +           width = std::clamp(width, minimum.width, maximum.width);
> > > +           height = std::clamp(height, minimum.height, maximum.height);
> > > +           return *this;
> > > +   }
> > > +
> > >     Size &growBy(const Size &margins)
> > >     {
> > >             width += margins.width;
> > > @@ -141,6 +148,15 @@ public:
> > >             };
> > >     }
> > >   
> > > +   __nodiscard constexpr Size clampedTo(const Size &minimum,
> > > +                                        const Size &maximum) const
> > > +   {
> > > +           return {
> > > +                   std::clamp(width, minimum.width, maximum.width),
> > > +                   std::clamp(height, minimum.height, maximum.height)
> > > +           };
> > > +   }
> > > +
> > >     __nodiscard constexpr Size grownBy(const Size &margins) const
> > >     {
> > >             return {
> > > diff --git a/src/libcamera/geometry.cpp b/src/libcamera/geometry.cpp
> > > index cb3c2de18d5e..7ac053a536c1 100644
> > > --- a/src/libcamera/geometry.cpp
> > > +++ b/src/libcamera/geometry.cpp
> > > @@ -173,6 +173,18 @@ const std::string Size::toString() const
> > >    * \return A reference to this object
> > >    */
> > >   
> > > +/**
> > > + * \fn Size::clamp(const Size &minimum, const Size &maximum)
> > > + * \brief Restrict the size to be constrained within the \a minimum and \a maximum
> 
> "Restrict the size to be constrainted" sounds quite weird to me. Maybe
> using the word "clamp" would be better ?

I was trying to avoid using the term I'm describing to describe itself
in the description.

Re-reading now it still sounds fine to me, but I can change it.

Do you mean you prefer:

Clamp the size to be constrained within the minimum and maximum

or

Restrict the size to be clamped within the minimum and maximum


> 
> > > + * \param[in] minimum The minimum size
> > > + * \param[in] maximum The maximum size
> > > + *
> > > + * This function restricts the width and height to the constraints of the \a
> > > + * minimum and the \a maximum sizes given.
> 
> And here too, the help text doesn't make it clear what the function
> does. I get more information from the function name than from the
> documentation :-)

Is it because of the word 'constraints' as you mentioned above? or
something else?

Documenting a function called 'clamp' as 'it will clamp the values'
doesn't add any value either.


> 
> Same for clampedTo().
> 
> > > + *
> > > + * \return A reference to this object
> > > + */
> > > +
> > >   /**
> > >    * \fn Size::growBy(const Size &margins)
> > >    * \brief Grow the size by \a margins in place
> > > @@ -231,6 +243,15 @@ const std::string Size::toString() const
> > >    * height of this size and the \a expand size
> > >    */
> > >   
> > > +/**
> > > + * \fn Size::clampedTo(const Size &minimum, const Size &maximum)
> > > + * \brief Restrict the size to be constrained within the \a minimum and \a maximum
> > > + * \param[in] minimum The minimum size
> > > + * \param[in] maximum The maximum size
> > > + * \return A size whose width and height match this size within the constraints
> > > + * of the \a minimum and \a maximum sizes given.
> > > + */
> > > +
> > >   /**
> > >    * \fn Size::grownBy(const Size &margins)
> > >    * \brief Grow the size by \a margins
> > > diff --git a/test/geometry.cpp b/test/geometry.cpp
> > > index 5125692496b3..1d3d3cad7174 100644
> > > --- a/test/geometry.cpp
> > > +++ b/test/geometry.cpp
> > > @@ -106,7 +106,7 @@ protected:
> > >   
> > >             /*
> > >              * Test alignDownTo(), alignUpTo(), boundTo(), expandTo(),
> > > -            * growBy() and shrinkBy()
> > > +            * clamp() growBy() and shrinkBy()
> 
> s/ growBy/, growBy/

Ack.


> 
> > >              */
> > >             Size s(50, 50);
> > >   
> > > @@ -134,6 +134,18 @@ protected:
> > >                     return TestFail;
> > >             }
> > >   
> > > +           s.clamp({ 80, 120 }, { 160, 240 });
> > 
> > 
> > is it okay to ignore the reference returned by .clamp() ? I think so, 
> > since it's doesn't have __nodiscard anyways,
> 
> The __nodiscard attribute was added to the "-ed" versions of the
> functions, to make sure that someone will not accidentally write
> 
>         s.clampedTo({ 80, 120 }, { 160, 240 });
> 
> when they meant
> 
>         s.clamp({ 80, 120 }, { 160, 240 });
> 
> clamp() is meant to be called with its return value potentially ignored,
> otherwise it would be hard to clamp a Size() in-place.
> 
> > Reviewed-by: Umang Jain <umang.jain@ideasonboard.com>
> > 
> > > +           if (s != Size(80, 120)) {
> > > +                   cout << "Size::clamp (minium) test failed" << endl;
> > > +                   return TestFail;
> > > +           }
> > > +
> > > +           s.clamp({ 20, 30 }, { 50, 50 });
> > > +           if (s != Size(50, 50)) {
> > > +                   cout << "Size::clamp (maximum) test failed" << endl;
> > > +                   return TestFail;
> > > +           }
> > > +
> > >             s.growBy({ 10, 20 });
> > >             if (s != Size(60, 70)) {
> > >                     cout << "Size::growBy() test failed" << endl;
> > > @@ -162,7 +174,7 @@ protected:
> > >   
> > >             /*
> > >              * Test alignedDownTo(), alignedUpTo(), boundedTo(),
> > > -            * expandedTo(), grownBy() and shrunkBy()
> > > +            * expandedTo(), clampedTo(), grownBy() and shrunkBy()
> > >              */
> > >             if (Size(0, 0).alignedDownTo(16, 8) != Size(0, 0) ||
> > >                 Size(1, 1).alignedDownTo(16, 8) != Size(0, 0) ||
> > > @@ -192,6 +204,14 @@ protected:
> > >                     return TestFail;
> > >             }
> > >   
> > > +           if (Size(0, 0).clampedTo({ 60, 40 }, { 80, 90 }) != Size(60, 40) ||
> > > +               Size(100, 100).clampedTo({ 60, 40 }, { 80, 90 }) != Size(80, 90) ||
> > > +               Size(30, 100).clampedTo({ 60, 40 }, { 80, 90 }) != Size(60, 90) ||
> > > +               Size(100, 30).clampedTo({ 60, 40 }, { 80, 90 }) != Size(80, 40)) {
> > > +                   cout << "Size::clampedTo() test failed" << endl;
> > > +                   return TestFail;
> > > +           }
> > > +
> > >             if (Size(0, 0).grownBy({ 10, 20 }) != Size(10, 20) ||
> > >                 Size(200, 50).grownBy({ 10, 20 }) != Size(210, 70)) {
> > >                     cout << "Size::grownBy() test failed" << endl;
> 
> -- 
> Regards,
> 
> Laurent Pinchart
Laurent Pinchart April 7, 2022, 2:58 p.m. UTC | #5
Hi Kieran,

On Thu, Apr 07, 2022 at 03:38:56PM +0100, Kieran Bingham wrote:
> Quoting Laurent Pinchart (2022-04-05 15:18:07)
> > On Tue, Apr 05, 2022 at 07:11:15PM +0530, Umang Jain via libcamera-devel wrote:
> > > Hi Kieran,
> > > 
> > > Thank you for the patch.
> > > 
> > > On 3/31/22 17:35, Kieran Bingham via libcamera-devel wrote:
> > > > Provide two new operations to support clamping a Size to a given
> > > > minimum or maximum Size, or returning a const variant of the same
> > > > restriction.
> > 
> > Did you really mean "restriction" here ?
> 
> I did yes. I mean the same restriction of the minimum and maximum Size
> but as a const.
> 
> > > > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> > > > ---
> > > >
> > > > I was expecting to use this for clamping the block width and height
> > > > for the AF hardware restrictions on the IPU3 ... but it turns out to be
> > > > not quite appropriate to use a Size there, as the clamped values are
> > > > stored in an IPU3 struct directly.
> > > >
> > > > However, having made this - I think it is likely to be useful elsewhere
> > > > so posting so it doesn't get lost.
> > 
> > .clamp(min, max) could be implemented as .expandTo(min).shrinkTo(max),
> > but Size::clamp() is likely more explicit and better.
> 
> Yes, a caller could call .expandTo.shrinkTo instead already but I don't
> think that should be the implementation detail here.
> 
> > > > Tests added, so it could be merged already even if there is no current
> > > > user yet. I expect it's more likely to get used if it exists, than if it
> > > > doesn't ;-)
> > > 
> > > +1
> > > 
> > > >   include/libcamera/geometry.h | 16 ++++++++++++++++
> > > >   src/libcamera/geometry.cpp   | 21 +++++++++++++++++++++
> > > >   test/geometry.cpp            | 24 ++++++++++++++++++++++--
> > > >   3 files changed, 59 insertions(+), 2 deletions(-)
> > > >
> > > > diff --git a/include/libcamera/geometry.h b/include/libcamera/geometry.h
> > > > index 7838b6793617..a447beb55965 100644
> > > > --- a/include/libcamera/geometry.h
> > > > +++ b/include/libcamera/geometry.h
> > > > @@ -93,6 +93,13 @@ public:
> > > >             return *this;
> > > >     }
> > > >   
> > > > +   Size &clamp(const Size &minimum, const Size &maximum)
> > > > +   {
> > > > +           width = std::clamp(width, minimum.width, maximum.width);
> > > > +           height = std::clamp(height, minimum.height, maximum.height);
> > > > +           return *this;
> > > > +   }
> > > > +
> > > >     Size &growBy(const Size &margins)
> > > >     {
> > > >             width += margins.width;
> > > > @@ -141,6 +148,15 @@ public:
> > > >             };
> > > >     }
> > > >   
> > > > +   __nodiscard constexpr Size clampedTo(const Size &minimum,
> > > > +                                        const Size &maximum) const
> > > > +   {
> > > > +           return {
> > > > +                   std::clamp(width, minimum.width, maximum.width),
> > > > +                   std::clamp(height, minimum.height, maximum.height)
> > > > +           };
> > > > +   }
> > > > +
> > > >     __nodiscard constexpr Size grownBy(const Size &margins) const
> > > >     {
> > > >             return {
> > > > diff --git a/src/libcamera/geometry.cpp b/src/libcamera/geometry.cpp
> > > > index cb3c2de18d5e..7ac053a536c1 100644
> > > > --- a/src/libcamera/geometry.cpp
> > > > +++ b/src/libcamera/geometry.cpp
> > > > @@ -173,6 +173,18 @@ const std::string Size::toString() const
> > > >    * \return A reference to this object
> > > >    */
> > > >   
> > > > +/**
> > > > + * \fn Size::clamp(const Size &minimum, const Size &maximum)
> > > > + * \brief Restrict the size to be constrained within the \a minimum and \a maximum
> > 
> > "Restrict the size to be constrainted" sounds quite weird to me. Maybe
> > using the word "clamp" would be better ?
> 
> I was trying to avoid using the term I'm describing to describe itself
> in the description.

The fact that the function is named from the term that best describes
its purpose means we picked the right name :-)

> Re-reading now it still sounds fine to me, but I can change it.
> 
> Do you mean you prefer:
> 
> Clamp the size to be constrained within the minimum and maximum

I meant this, yes. Or maybe

Clamp the size to the \a minimum and \a maximum values

(with s/to/between/, and/or adding the word "range" somewhere, if
desired)

> or
> 
> Restrict the size to be clamped within the minimum and maximum
> 
> > > > + * \param[in] minimum The minimum size
> > > > + * \param[in] maximum The maximum size
> > > > + *
> > > > + * This function restricts the width and height to the constraints of the \a
> > > > + * minimum and the \a maximum sizes given.
> > 
> > And here too, the help text doesn't make it clear what the function
> > does. I get more information from the function name than from the
> > documentation :-)
> 
> Is it because of the word 'constraints' as you mentioned above? or
> something else?

Yes, it's "restricts to the constraints" that sounds convoluted to me.

> Documenting a function called 'clamp' as 'it will clamp the values'
> doesn't add any value either.
> 
> > Same for clampedTo().
> > 
> > > > + *
> > > > + * \return A reference to this object
> > > > + */
> > > > +
> > > >   /**
> > > >    * \fn Size::growBy(const Size &margins)
> > > >    * \brief Grow the size by \a margins in place
> > > > @@ -231,6 +243,15 @@ const std::string Size::toString() const
> > > >    * height of this size and the \a expand size
> > > >    */
> > > >   
> > > > +/**
> > > > + * \fn Size::clampedTo(const Size &minimum, const Size &maximum)
> > > > + * \brief Restrict the size to be constrained within the \a minimum and \a maximum
> > > > + * \param[in] minimum The minimum size
> > > > + * \param[in] maximum The maximum size
> > > > + * \return A size whose width and height match this size within the constraints
> > > > + * of the \a minimum and \a maximum sizes given.
> > > > + */
> > > > +
> > > >   /**
> > > >    * \fn Size::grownBy(const Size &margins)
> > > >    * \brief Grow the size by \a margins
> > > > diff --git a/test/geometry.cpp b/test/geometry.cpp
> > > > index 5125692496b3..1d3d3cad7174 100644
> > > > --- a/test/geometry.cpp
> > > > +++ b/test/geometry.cpp
> > > > @@ -106,7 +106,7 @@ protected:
> > > >   
> > > >             /*
> > > >              * Test alignDownTo(), alignUpTo(), boundTo(), expandTo(),
> > > > -            * growBy() and shrinkBy()
> > > > +            * clamp() growBy() and shrinkBy()
> > 
> > s/ growBy/, growBy/
> 
> Ack.
> 
> > > >              */
> > > >             Size s(50, 50);
> > > >   
> > > > @@ -134,6 +134,18 @@ protected:
> > > >                     return TestFail;
> > > >             }
> > > >   
> > > > +           s.clamp({ 80, 120 }, { 160, 240 });
> > > 
> > > 
> > > is it okay to ignore the reference returned by .clamp() ? I think so, 
> > > since it's doesn't have __nodiscard anyways,
> > 
> > The __nodiscard attribute was added to the "-ed" versions of the
> > functions, to make sure that someone will not accidentally write
> > 
> >         s.clampedTo({ 80, 120 }, { 160, 240 });
> > 
> > when they meant
> > 
> >         s.clamp({ 80, 120 }, { 160, 240 });
> > 
> > clamp() is meant to be called with its return value potentially ignored,
> > otherwise it would be hard to clamp a Size() in-place.
> > 
> > > Reviewed-by: Umang Jain <umang.jain@ideasonboard.com>
> > > 
> > > > +           if (s != Size(80, 120)) {
> > > > +                   cout << "Size::clamp (minium) test failed" << endl;
> > > > +                   return TestFail;
> > > > +           }
> > > > +
> > > > +           s.clamp({ 20, 30 }, { 50, 50 });
> > > > +           if (s != Size(50, 50)) {
> > > > +                   cout << "Size::clamp (maximum) test failed" << endl;
> > > > +                   return TestFail;
> > > > +           }
> > > > +
> > > >             s.growBy({ 10, 20 });
> > > >             if (s != Size(60, 70)) {
> > > >                     cout << "Size::growBy() test failed" << endl;
> > > > @@ -162,7 +174,7 @@ protected:
> > > >   
> > > >             /*
> > > >              * Test alignedDownTo(), alignedUpTo(), boundedTo(),
> > > > -            * expandedTo(), grownBy() and shrunkBy()
> > > > +            * expandedTo(), clampedTo(), grownBy() and shrunkBy()
> > > >              */
> > > >             if (Size(0, 0).alignedDownTo(16, 8) != Size(0, 0) ||
> > > >                 Size(1, 1).alignedDownTo(16, 8) != Size(0, 0) ||
> > > > @@ -192,6 +204,14 @@ protected:
> > > >                     return TestFail;
> > > >             }
> > > >   
> > > > +           if (Size(0, 0).clampedTo({ 60, 40 }, { 80, 90 }) != Size(60, 40) ||
> > > > +               Size(100, 100).clampedTo({ 60, 40 }, { 80, 90 }) != Size(80, 90) ||
> > > > +               Size(30, 100).clampedTo({ 60, 40 }, { 80, 90 }) != Size(60, 90) ||
> > > > +               Size(100, 30).clampedTo({ 60, 40 }, { 80, 90 }) != Size(80, 40)) {
> > > > +                   cout << "Size::clampedTo() test failed" << endl;
> > > > +                   return TestFail;
> > > > +           }
> > > > +
> > > >             if (Size(0, 0).grownBy({ 10, 20 }) != Size(10, 20) ||
> > > >                 Size(200, 50).grownBy({ 10, 20 }) != Size(210, 70)) {
> > > >                     cout << "Size::grownBy() test failed" << endl;
Kieran Bingham April 7, 2022, 9:47 p.m. UTC | #6
Quoting Laurent Pinchart (2022-04-07 15:58:09)
> Hi Kieran,
> 
> On Thu, Apr 07, 2022 at 03:38:56PM +0100, Kieran Bingham wrote:
> > Quoting Laurent Pinchart (2022-04-05 15:18:07)
> > > On Tue, Apr 05, 2022 at 07:11:15PM +0530, Umang Jain via libcamera-devel wrote:
> > > > Hi Kieran,
> > > > 
> > > > Thank you for the patch.
> > > > 
> > > > On 3/31/22 17:35, Kieran Bingham via libcamera-devel wrote:
> > > > > Provide two new operations to support clamping a Size to a given
> > > > > minimum or maximum Size, or returning a const variant of the same
> > > > > restriction.
> > > 
> > > Did you really mean "restriction" here ?
> > 
> > I did yes. I mean the same restriction of the minimum and maximum Size
> > but as a const.
> > 
> > > > > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> > > > > ---
> > > > >
> > > > > I was expecting to use this for clamping the block width and height
> > > > > for the AF hardware restrictions on the IPU3 ... but it turns out to be
> > > > > not quite appropriate to use a Size there, as the clamped values are
> > > > > stored in an IPU3 struct directly.
> > > > >
> > > > > However, having made this - I think it is likely to be useful elsewhere
> > > > > so posting so it doesn't get lost.
> > > 
> > > .clamp(min, max) could be implemented as .expandTo(min).shrinkTo(max),
> > > but Size::clamp() is likely more explicit and better.
> > 
> > Yes, a caller could call .expandTo.shrinkTo instead already but I don't
> > think that should be the implementation detail here.
> > 
> > > > > Tests added, so it could be merged already even if there is no current
> > > > > user yet. I expect it's more likely to get used if it exists, than if it
> > > > > doesn't ;-)
> > > > 
> > > > +1
> > > > 
> > > > >   include/libcamera/geometry.h | 16 ++++++++++++++++
> > > > >   src/libcamera/geometry.cpp   | 21 +++++++++++++++++++++
> > > > >   test/geometry.cpp            | 24 ++++++++++++++++++++++--
> > > > >   3 files changed, 59 insertions(+), 2 deletions(-)
> > > > >
> > > > > diff --git a/include/libcamera/geometry.h b/include/libcamera/geometry.h
> > > > > index 7838b6793617..a447beb55965 100644
> > > > > --- a/include/libcamera/geometry.h
> > > > > +++ b/include/libcamera/geometry.h
> > > > > @@ -93,6 +93,13 @@ public:
> > > > >             return *this;
> > > > >     }
> > > > >   
> > > > > +   Size &clamp(const Size &minimum, const Size &maximum)
> > > > > +   {
> > > > > +           width = std::clamp(width, minimum.width, maximum.width);
> > > > > +           height = std::clamp(height, minimum.height, maximum.height);
> > > > > +           return *this;
> > > > > +   }
> > > > > +
> > > > >     Size &growBy(const Size &margins)
> > > > >     {
> > > > >             width += margins.width;
> > > > > @@ -141,6 +148,15 @@ public:
> > > > >             };
> > > > >     }
> > > > >   
> > > > > +   __nodiscard constexpr Size clampedTo(const Size &minimum,
> > > > > +                                        const Size &maximum) const
> > > > > +   {
> > > > > +           return {
> > > > > +                   std::clamp(width, minimum.width, maximum.width),
> > > > > +                   std::clamp(height, minimum.height, maximum.height)
> > > > > +           };
> > > > > +   }
> > > > > +
> > > > >     __nodiscard constexpr Size grownBy(const Size &margins) const
> > > > >     {
> > > > >             return {
> > > > > diff --git a/src/libcamera/geometry.cpp b/src/libcamera/geometry.cpp
> > > > > index cb3c2de18d5e..7ac053a536c1 100644
> > > > > --- a/src/libcamera/geometry.cpp
> > > > > +++ b/src/libcamera/geometry.cpp
> > > > > @@ -173,6 +173,18 @@ const std::string Size::toString() const
> > > > >    * \return A reference to this object
> > > > >    */
> > > > >   
> > > > > +/**
> > > > > + * \fn Size::clamp(const Size &minimum, const Size &maximum)
> > > > > + * \brief Restrict the size to be constrained within the \a minimum and \a maximum
> > > 
> > > "Restrict the size to be constrainted" sounds quite weird to me. Maybe
> > > using the word "clamp" would be better ?
> > 
> > I was trying to avoid using the term I'm describing to describe itself
> > in the description.
> 
> The fact that the function is named from the term that best describes
> its purpose means we picked the right name :-)

Perhaps you only know the term 'clamp' in regards to the coding
implementation.

Describing the function 'clamp()' with the word 'clamp' conveys the
wrong meaning to me. (And maybe other english speakers who have used
clamps?)

But to me - a clamp is a mechanical device used to temporarily fix two
(or more) objects such that they remain in the same position, usually
while you then perform some other operation like screwing the two items
together, or marking them. You 'could' use a clamp to squash something
... but I don't think that quite fits the real definition of
std::clamp() (At least I don't think it can constrain to a non-zero
minimum).

google-translate tells me that a clamp might be known as 'serrer' in
French or 'Morsetto' in Italian, so perhaps 'clamp' is only known as
it's use in code ... but I haven't validated those usages in a sentence
;-) 

Anyway, I presume someone who wants to use this - knows what the coding
term is - but my point is - I don't think the function name is
automatically the best way to describe what it does.

If it was using both the term restrict and contrain in the same sentence
I could offer:


"Constrain the Size to be within the dimensions of both the minimum and
maximum values."

Which also works with 's/Constrain/Restrict/'

> > Re-reading now it still sounds fine to me, but I can change it.
> > 
> > Do you mean you prefer:
> > 
> > Clamp the size to be constrained within the minimum and maximum
> 
> I meant this, yes. Or maybe
> 
> Clamp the size to the \a minimum and \a maximum values
> 
> (with s/to/between/, and/or adding the word "range" somewhere, if
> desired)
> 
> > or
> > 
> > Restrict the size to be clamped within the minimum and maximum
> > 
> > > > > + * \param[in] minimum The minimum size
> > > > > + * \param[in] maximum The maximum size
> > > > > + *
> > > > > + * This function restricts the width and height to the constraints of the \a
> > > > > + * minimum and the \a maximum sizes given.
> > > 
> > > And here too, the help text doesn't make it clear what the function
> > > does. I get more information from the function name than from the
> > > documentation :-)
> > 
> > Is it because of the word 'constraints' as you mentioned above? or
> > something else?
> 
> Yes, it's "restricts to the constraints" that sounds convoluted to me.
> 
> > Documenting a function called 'clamp' as 'it will clamp the values'
> > doesn't add any value either.
> > 
> > > Same for clampedTo().
> > > 
> > > > > + *
> > > > > + * \return A reference to this object
> > > > > + */
> > > > > +
> > > > >   /**
> > > > >    * \fn Size::growBy(const Size &margins)
> > > > >    * \brief Grow the size by \a margins in place
> > > > > @@ -231,6 +243,15 @@ const std::string Size::toString() const
> > > > >    * height of this size and the \a expand size
> > > > >    */
> > > > >   
> > > > > +/**
> > > > > + * \fn Size::clampedTo(const Size &minimum, const Size &maximum)
> > > > > + * \brief Restrict the size to be constrained within the \a minimum and \a maximum
> > > > > + * \param[in] minimum The minimum size
> > > > > + * \param[in] maximum The maximum size
> > > > > + * \return A size whose width and height match this size within the constraints
> > > > > + * of the \a minimum and \a maximum sizes given.
> > > > > + */
> > > > > +
> > > > >   /**
> > > > >    * \fn Size::grownBy(const Size &margins)
> > > > >    * \brief Grow the size by \a margins
> > > > > diff --git a/test/geometry.cpp b/test/geometry.cpp
> > > > > index 5125692496b3..1d3d3cad7174 100644
> > > > > --- a/test/geometry.cpp
> > > > > +++ b/test/geometry.cpp
> > > > > @@ -106,7 +106,7 @@ protected:
> > > > >   
> > > > >             /*
> > > > >              * Test alignDownTo(), alignUpTo(), boundTo(), expandTo(),
> > > > > -            * growBy() and shrinkBy()
> > > > > +            * clamp() growBy() and shrinkBy()
> > > 
> > > s/ growBy/, growBy/
> > 
> > Ack.
> > 
> > > > >              */
> > > > >             Size s(50, 50);
> > > > >   
> > > > > @@ -134,6 +134,18 @@ protected:
> > > > >                     return TestFail;
> > > > >             }
> > > > >   
> > > > > +           s.clamp({ 80, 120 }, { 160, 240 });
> > > > 
> > > > 
> > > > is it okay to ignore the reference returned by .clamp() ? I think so, 
> > > > since it's doesn't have __nodiscard anyways,
> > > 
> > > The __nodiscard attribute was added to the "-ed" versions of the
> > > functions, to make sure that someone will not accidentally write
> > > 
> > >         s.clampedTo({ 80, 120 }, { 160, 240 });
> > > 
> > > when they meant
> > > 
> > >         s.clamp({ 80, 120 }, { 160, 240 });
> > > 
> > > clamp() is meant to be called with its return value potentially ignored,
> > > otherwise it would be hard to clamp a Size() in-place.
> > > 
> > > > Reviewed-by: Umang Jain <umang.jain@ideasonboard.com>
> > > > 
> > > > > +           if (s != Size(80, 120)) {
> > > > > +                   cout << "Size::clamp (minium) test failed" << endl;
> > > > > +                   return TestFail;
> > > > > +           }
> > > > > +
> > > > > +           s.clamp({ 20, 30 }, { 50, 50 });
> > > > > +           if (s != Size(50, 50)) {
> > > > > +                   cout << "Size::clamp (maximum) test failed" << endl;
> > > > > +                   return TestFail;
> > > > > +           }
> > > > > +
> > > > >             s.growBy({ 10, 20 });
> > > > >             if (s != Size(60, 70)) {
> > > > >                     cout << "Size::growBy() test failed" << endl;
> > > > > @@ -162,7 +174,7 @@ protected:
> > > > >   
> > > > >             /*
> > > > >              * Test alignedDownTo(), alignedUpTo(), boundedTo(),
> > > > > -            * expandedTo(), grownBy() and shrunkBy()
> > > > > +            * expandedTo(), clampedTo(), grownBy() and shrunkBy()
> > > > >              */
> > > > >             if (Size(0, 0).alignedDownTo(16, 8) != Size(0, 0) ||
> > > > >                 Size(1, 1).alignedDownTo(16, 8) != Size(0, 0) ||
> > > > > @@ -192,6 +204,14 @@ protected:
> > > > >                     return TestFail;
> > > > >             }
> > > > >   
> > > > > +           if (Size(0, 0).clampedTo({ 60, 40 }, { 80, 90 }) != Size(60, 40) ||
> > > > > +               Size(100, 100).clampedTo({ 60, 40 }, { 80, 90 }) != Size(80, 90) ||
> > > > > +               Size(30, 100).clampedTo({ 60, 40 }, { 80, 90 }) != Size(60, 90) ||
> > > > > +               Size(100, 30).clampedTo({ 60, 40 }, { 80, 90 }) != Size(80, 40)) {
> > > > > +                   cout << "Size::clampedTo() test failed" << endl;
> > > > > +                   return TestFail;
> > > > > +           }
> > > > > +
> > > > >             if (Size(0, 0).grownBy({ 10, 20 }) != Size(10, 20) ||
> > > > >                 Size(200, 50).grownBy({ 10, 20 }) != Size(210, 70)) {
> > > > >                     cout << "Size::grownBy() test failed" << endl;
> 
> -- 
> Regards,
> 
> Laurent Pinchart
Laurent Pinchart April 8, 2022, 7:34 a.m. UTC | #7
Hi Kieran,

On Thu, Apr 07, 2022 at 10:47:40PM +0100, Kieran Bingham wrote:
> Quoting Laurent Pinchart (2022-04-07 15:58:09)
> > On Thu, Apr 07, 2022 at 03:38:56PM +0100, Kieran Bingham wrote:
> > > Quoting Laurent Pinchart (2022-04-05 15:18:07)
> > > > On Tue, Apr 05, 2022 at 07:11:15PM +0530, Umang Jain via libcamera-devel wrote:
> > > > > Hi Kieran,
> > > > > 
> > > > > Thank you for the patch.
> > > > > 
> > > > > On 3/31/22 17:35, Kieran Bingham via libcamera-devel wrote:
> > > > > > Provide two new operations to support clamping a Size to a given
> > > > > > minimum or maximum Size, or returning a const variant of the same
> > > > > > restriction.
> > > > 
> > > > Did you really mean "restriction" here ?
> > > 
> > > I did yes. I mean the same restriction of the minimum and maximum Size
> > > but as a const.
> > > 
> > > > > > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> > > > > > ---
> > > > > >
> > > > > > I was expecting to use this for clamping the block width and height
> > > > > > for the AF hardware restrictions on the IPU3 ... but it turns out to be
> > > > > > not quite appropriate to use a Size there, as the clamped values are
> > > > > > stored in an IPU3 struct directly.
> > > > > >
> > > > > > However, having made this - I think it is likely to be useful elsewhere
> > > > > > so posting so it doesn't get lost.
> > > > 
> > > > .clamp(min, max) could be implemented as .expandTo(min).shrinkTo(max),
> > > > but Size::clamp() is likely more explicit and better.
> > > 
> > > Yes, a caller could call .expandTo.shrinkTo instead already but I don't
> > > think that should be the implementation detail here.
> > > 
> > > > > > Tests added, so it could be merged already even if there is no current
> > > > > > user yet. I expect it's more likely to get used if it exists, than if it
> > > > > > doesn't ;-)
> > > > > 
> > > > > +1
> > > > > 
> > > > > >   include/libcamera/geometry.h | 16 ++++++++++++++++
> > > > > >   src/libcamera/geometry.cpp   | 21 +++++++++++++++++++++
> > > > > >   test/geometry.cpp            | 24 ++++++++++++++++++++++--
> > > > > >   3 files changed, 59 insertions(+), 2 deletions(-)
> > > > > >
> > > > > > diff --git a/include/libcamera/geometry.h b/include/libcamera/geometry.h
> > > > > > index 7838b6793617..a447beb55965 100644
> > > > > > --- a/include/libcamera/geometry.h
> > > > > > +++ b/include/libcamera/geometry.h
> > > > > > @@ -93,6 +93,13 @@ public:
> > > > > >             return *this;
> > > > > >     }
> > > > > >   
> > > > > > +   Size &clamp(const Size &minimum, const Size &maximum)
> > > > > > +   {
> > > > > > +           width = std::clamp(width, minimum.width, maximum.width);
> > > > > > +           height = std::clamp(height, minimum.height, maximum.height);
> > > > > > +           return *this;
> > > > > > +   }
> > > > > > +
> > > > > >     Size &growBy(const Size &margins)
> > > > > >     {
> > > > > >             width += margins.width;
> > > > > > @@ -141,6 +148,15 @@ public:
> > > > > >             };
> > > > > >     }
> > > > > >   
> > > > > > +   __nodiscard constexpr Size clampedTo(const Size &minimum,
> > > > > > +                                        const Size &maximum) const
> > > > > > +   {
> > > > > > +           return {
> > > > > > +                   std::clamp(width, minimum.width, maximum.width),
> > > > > > +                   std::clamp(height, minimum.height, maximum.height)
> > > > > > +           };
> > > > > > +   }
> > > > > > +
> > > > > >     __nodiscard constexpr Size grownBy(const Size &margins) const
> > > > > >     {
> > > > > >             return {
> > > > > > diff --git a/src/libcamera/geometry.cpp b/src/libcamera/geometry.cpp
> > > > > > index cb3c2de18d5e..7ac053a536c1 100644
> > > > > > --- a/src/libcamera/geometry.cpp
> > > > > > +++ b/src/libcamera/geometry.cpp
> > > > > > @@ -173,6 +173,18 @@ const std::string Size::toString() const
> > > > > >    * \return A reference to this object
> > > > > >    */
> > > > > >   
> > > > > > +/**
> > > > > > + * \fn Size::clamp(const Size &minimum, const Size &maximum)
> > > > > > + * \brief Restrict the size to be constrained within the \a minimum and \a maximum
> > > > 
> > > > "Restrict the size to be constrainted" sounds quite weird to me. Maybe
> > > > using the word "clamp" would be better ?
> > > 
> > > I was trying to avoid using the term I'm describing to describe itself
> > > in the description.
> > 
> > The fact that the function is named from the term that best describes
> > its purpose means we picked the right name :-)
> 
> Perhaps you only know the term 'clamp' in regards to the coding
> implementation.
> 
> Describing the function 'clamp()' with the word 'clamp' conveys the
> wrong meaning to me. (And maybe other english speakers who have used
> clamps?)
> 
> But to me - a clamp is a mechanical device used to temporarily fix two
> (or more) objects such that they remain in the same position, usually
> while you then perform some other operation like screwing the two items
> together, or marking them. You 'could' use a clamp to squash something
> ... but I don't think that quite fits the real definition of
> std::clamp() (At least I don't think it can constrain to a non-zero
> minimum).
> 
> google-translate tells me that a clamp might be known as 'serrer' in
> French or 'Morsetto' in Italian, so perhaps 'clamp' is only known as
> it's use in code ... but I haven't validated those usages in a sentence
> ;-) 

We could call the function puristaa() but we will then restrict our
audience.

Jokes aside, the word has multiple meanings, with "to modify a numeric
value so it lies within a specific range" being one of them only.
Perhaps I am more familiar with that meaning than the average developer
without being aware of that (it's widely used in the kernel after all).

> Anyway, I presume someone who wants to use this - knows what the coding
> term is - but my point is - I don't think the function name is
> automatically the best way to describe what it does.
> 
> If it was using both the term restrict and contrain in the same sentence
> I could offer:
> 
> 
> "Constrain the Size to be within the dimensions of both the minimum and
> maximum values."
> 
> Which also works with 's/Constrain/Restrict/'

I think we've spent enough time bikeshedding, I'm fine with this, or any
variation that would be preferred by the team.

> > > Re-reading now it still sounds fine to me, but I can change it.
> > > 
> > > Do you mean you prefer:
> > > 
> > > Clamp the size to be constrained within the minimum and maximum
> > 
> > I meant this, yes. Or maybe
> > 
> > Clamp the size to the \a minimum and \a maximum values
> > 
> > (with s/to/between/, and/or adding the word "range" somewhere, if
> > desired)
> > 
> > > or
> > > 
> > > Restrict the size to be clamped within the minimum and maximum
> > > 
> > > > > > + * \param[in] minimum The minimum size
> > > > > > + * \param[in] maximum The maximum size
> > > > > > + *
> > > > > > + * This function restricts the width and height to the constraints of the \a
> > > > > > + * minimum and the \a maximum sizes given.
> > > > 
> > > > And here too, the help text doesn't make it clear what the function
> > > > does. I get more information from the function name than from the
> > > > documentation :-)
> > > 
> > > Is it because of the word 'constraints' as you mentioned above? or
> > > something else?
> > 
> > Yes, it's "restricts to the constraints" that sounds convoluted to me.
> > 
> > > Documenting a function called 'clamp' as 'it will clamp the values'
> > > doesn't add any value either.
> > > 
> > > > Same for clampedTo().
> > > > 
> > > > > > + *
> > > > > > + * \return A reference to this object
> > > > > > + */
> > > > > > +
> > > > > >   /**
> > > > > >    * \fn Size::growBy(const Size &margins)
> > > > > >    * \brief Grow the size by \a margins in place
> > > > > > @@ -231,6 +243,15 @@ const std::string Size::toString() const
> > > > > >    * height of this size and the \a expand size
> > > > > >    */
> > > > > >   
> > > > > > +/**
> > > > > > + * \fn Size::clampedTo(const Size &minimum, const Size &maximum)
> > > > > > + * \brief Restrict the size to be constrained within the \a minimum and \a maximum
> > > > > > + * \param[in] minimum The minimum size
> > > > > > + * \param[in] maximum The maximum size
> > > > > > + * \return A size whose width and height match this size within the constraints
> > > > > > + * of the \a minimum and \a maximum sizes given.
> > > > > > + */
> > > > > > +
> > > > > >   /**
> > > > > >    * \fn Size::grownBy(const Size &margins)
> > > > > >    * \brief Grow the size by \a margins
> > > > > > diff --git a/test/geometry.cpp b/test/geometry.cpp
> > > > > > index 5125692496b3..1d3d3cad7174 100644
> > > > > > --- a/test/geometry.cpp
> > > > > > +++ b/test/geometry.cpp
> > > > > > @@ -106,7 +106,7 @@ protected:
> > > > > >   
> > > > > >             /*
> > > > > >              * Test alignDownTo(), alignUpTo(), boundTo(), expandTo(),
> > > > > > -            * growBy() and shrinkBy()
> > > > > > +            * clamp() growBy() and shrinkBy()
> > > > 
> > > > s/ growBy/, growBy/
> > > 
> > > Ack.
> > > 
> > > > > >              */
> > > > > >             Size s(50, 50);
> > > > > >   
> > > > > > @@ -134,6 +134,18 @@ protected:
> > > > > >                     return TestFail;
> > > > > >             }
> > > > > >   
> > > > > > +           s.clamp({ 80, 120 }, { 160, 240 });
> > > > > 
> > > > > 
> > > > > is it okay to ignore the reference returned by .clamp() ? I think so, 
> > > > > since it's doesn't have __nodiscard anyways,
> > > > 
> > > > The __nodiscard attribute was added to the "-ed" versions of the
> > > > functions, to make sure that someone will not accidentally write
> > > > 
> > > >         s.clampedTo({ 80, 120 }, { 160, 240 });
> > > > 
> > > > when they meant
> > > > 
> > > >         s.clamp({ 80, 120 }, { 160, 240 });
> > > > 
> > > > clamp() is meant to be called with its return value potentially ignored,
> > > > otherwise it would be hard to clamp a Size() in-place.
> > > > 
> > > > > Reviewed-by: Umang Jain <umang.jain@ideasonboard.com>
> > > > > 
> > > > > > +           if (s != Size(80, 120)) {
> > > > > > +                   cout << "Size::clamp (minium) test failed" << endl;
> > > > > > +                   return TestFail;
> > > > > > +           }
> > > > > > +
> > > > > > +           s.clamp({ 20, 30 }, { 50, 50 });
> > > > > > +           if (s != Size(50, 50)) {
> > > > > > +                   cout << "Size::clamp (maximum) test failed" << endl;
> > > > > > +                   return TestFail;
> > > > > > +           }
> > > > > > +
> > > > > >             s.growBy({ 10, 20 });
> > > > > >             if (s != Size(60, 70)) {
> > > > > >                     cout << "Size::growBy() test failed" << endl;
> > > > > > @@ -162,7 +174,7 @@ protected:
> > > > > >   
> > > > > >             /*
> > > > > >              * Test alignedDownTo(), alignedUpTo(), boundedTo(),
> > > > > > -            * expandedTo(), grownBy() and shrunkBy()
> > > > > > +            * expandedTo(), clampedTo(), grownBy() and shrunkBy()
> > > > > >              */
> > > > > >             if (Size(0, 0).alignedDownTo(16, 8) != Size(0, 0) ||
> > > > > >                 Size(1, 1).alignedDownTo(16, 8) != Size(0, 0) ||
> > > > > > @@ -192,6 +204,14 @@ protected:
> > > > > >                     return TestFail;
> > > > > >             }
> > > > > >   
> > > > > > +           if (Size(0, 0).clampedTo({ 60, 40 }, { 80, 90 }) != Size(60, 40) ||
> > > > > > +               Size(100, 100).clampedTo({ 60, 40 }, { 80, 90 }) != Size(80, 90) ||
> > > > > > +               Size(30, 100).clampedTo({ 60, 40 }, { 80, 90 }) != Size(60, 90) ||
> > > > > > +               Size(100, 30).clampedTo({ 60, 40 }, { 80, 90 }) != Size(80, 40)) {
> > > > > > +                   cout << "Size::clampedTo() test failed" << endl;
> > > > > > +                   return TestFail;
> > > > > > +           }
> > > > > > +
> > > > > >             if (Size(0, 0).grownBy({ 10, 20 }) != Size(10, 20) ||
> > > > > >                 Size(200, 50).grownBy({ 10, 20 }) != Size(210, 70)) {
> > > > > >                     cout << "Size::grownBy() test failed" << endl;

Patch
diff mbox series

diff --git a/include/libcamera/geometry.h b/include/libcamera/geometry.h
index 7838b6793617..a447beb55965 100644
--- a/include/libcamera/geometry.h
+++ b/include/libcamera/geometry.h
@@ -93,6 +93,13 @@  public:
 		return *this;
 	}
 
+	Size &clamp(const Size &minimum, const Size &maximum)
+	{
+		width = std::clamp(width, minimum.width, maximum.width);
+		height = std::clamp(height, minimum.height, maximum.height);
+		return *this;
+	}
+
 	Size &growBy(const Size &margins)
 	{
 		width += margins.width;
@@ -141,6 +148,15 @@  public:
 		};
 	}
 
+	__nodiscard constexpr Size clampedTo(const Size &minimum,
+					     const Size &maximum) const
+	{
+		return {
+			std::clamp(width, minimum.width, maximum.width),
+			std::clamp(height, minimum.height, maximum.height)
+		};
+	}
+
 	__nodiscard constexpr Size grownBy(const Size &margins) const
 	{
 		return {
diff --git a/src/libcamera/geometry.cpp b/src/libcamera/geometry.cpp
index cb3c2de18d5e..7ac053a536c1 100644
--- a/src/libcamera/geometry.cpp
+++ b/src/libcamera/geometry.cpp
@@ -173,6 +173,18 @@  const std::string Size::toString() const
  * \return A reference to this object
  */
 
+/**
+ * \fn Size::clamp(const Size &minimum, const Size &maximum)
+ * \brief Restrict the size to be constrained within the \a minimum and \a maximum
+ * \param[in] minimum The minimum size
+ * \param[in] maximum The maximum size
+ *
+ * This function restricts the width and height to the constraints of the \a
+ * minimum and the \a maximum sizes given.
+ *
+ * \return A reference to this object
+ */
+
 /**
  * \fn Size::growBy(const Size &margins)
  * \brief Grow the size by \a margins in place
@@ -231,6 +243,15 @@  const std::string Size::toString() const
  * height of this size and the \a expand size
  */
 
+/**
+ * \fn Size::clampedTo(const Size &minimum, const Size &maximum)
+ * \brief Restrict the size to be constrained within the \a minimum and \a maximum
+ * \param[in] minimum The minimum size
+ * \param[in] maximum The maximum size
+ * \return A size whose width and height match this size within the constraints
+ * of the \a minimum and \a maximum sizes given.
+ */
+
 /**
  * \fn Size::grownBy(const Size &margins)
  * \brief Grow the size by \a margins
diff --git a/test/geometry.cpp b/test/geometry.cpp
index 5125692496b3..1d3d3cad7174 100644
--- a/test/geometry.cpp
+++ b/test/geometry.cpp
@@ -106,7 +106,7 @@  protected:
 
 		/*
 		 * Test alignDownTo(), alignUpTo(), boundTo(), expandTo(),
-		 * growBy() and shrinkBy()
+		 * clamp() growBy() and shrinkBy()
 		 */
 		Size s(50, 50);
 
@@ -134,6 +134,18 @@  protected:
 			return TestFail;
 		}
 
+		s.clamp({ 80, 120 }, { 160, 240 });
+		if (s != Size(80, 120)) {
+			cout << "Size::clamp (minium) test failed" << endl;
+			return TestFail;
+		}
+
+		s.clamp({ 20, 30 }, { 50, 50 });
+		if (s != Size(50, 50)) {
+			cout << "Size::clamp (maximum) test failed" << endl;
+			return TestFail;
+		}
+
 		s.growBy({ 10, 20 });
 		if (s != Size(60, 70)) {
 			cout << "Size::growBy() test failed" << endl;
@@ -162,7 +174,7 @@  protected:
 
 		/*
 		 * Test alignedDownTo(), alignedUpTo(), boundedTo(),
-		 * expandedTo(), grownBy() and shrunkBy()
+		 * expandedTo(), clampedTo(), grownBy() and shrunkBy()
 		 */
 		if (Size(0, 0).alignedDownTo(16, 8) != Size(0, 0) ||
 		    Size(1, 1).alignedDownTo(16, 8) != Size(0, 0) ||
@@ -192,6 +204,14 @@  protected:
 			return TestFail;
 		}
 
+		if (Size(0, 0).clampedTo({ 60, 40 }, { 80, 90 }) != Size(60, 40) ||
+		    Size(100, 100).clampedTo({ 60, 40 }, { 80, 90 }) != Size(80, 90) ||
+		    Size(30, 100).clampedTo({ 60, 40 }, { 80, 90 }) != Size(60, 90) ||
+		    Size(100, 30).clampedTo({ 60, 40 }, { 80, 90 }) != Size(80, 40)) {
+			cout << "Size::clampedTo() test failed" << endl;
+			return TestFail;
+		}
+
 		if (Size(0, 0).grownBy({ 10, 20 }) != Size(10, 20) ||
 		    Size(200, 50).grownBy({ 10, 20 }) != Size(210, 70)) {
 			cout << "Size::grownBy() test failed" << endl;