Jump to content
  • Advertisement
Sign in to follow this  
Finalspace

Letterbox for negative ranges (Dimension analysis?)

This topic is 393 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I want to compute a new letterboxed destination rectangle from the following example:

 Source rectangle (-10, -5 to 10, 5)

Destination rectangle (0, 1000, 800, 0)

Aspect ratio is determined by source rectangle sizes (w / h)

 

Source coordinate system is bottom up, target coordinate system is up bottom in Y.

 

My current code which i have used for many years to compute a letterbox rectangle cannot handle such a case:

 

		struct RenderArea {
			Vec2f sourceMin;
			Vec2f sourceMax;
			Vec2f targetMin;
			Vec2f targetMax;
			Vec2f sourceScale;

			inline Vec2f Project(const Vec2f &sourcePos) {
				Vec2f invScale = Vec2f(1.0f / sourceScale.x, 1.0f / sourceScale.y);
				Vec2f result = targetMin + Hadamard(sourcePos - sourceMin, invScale);
				return(result);
			}

			inline Vec2f Unproject(const Vec2f &targetPos) {
				Vec2f result = sourceMin + Vec2f((targetPos.x - targetMin.x) * sourceScale.x, (targetPos.y - targetMin.y) * sourceScale.y);
				return(result);
			}
		};

		inline RenderArea CalculateRenderArea(const Vec2f &sourceMin, const Vec2f &sourceMax, const Vec2f &targetMin, const Vec2f &targetMax, const bool letterBoxed) {
			RenderArea result = {};

			const Vec2f sourceRange = sourceMax - sourceMin;
			Vec2f targetRange = targetMax - targetMin;

			result.sourceMin = sourceMin;
			result.sourceMax = sourceMax;

			if (letterBoxed) {
				const f32 sourceAspect = sourceRange.w / sourceRange.h;
				Vec2f newTargetSize = Vec2f(targetRange.w, targetRange.w / sourceAspect);
				if (newTargetSize.h > targetRange.h) {
					newTargetSize.h = targetRange.h;
					newTargetSize.w = targetRange.h * sourceAspect;
				}
				Vec2f newTargetOffset = Vec2f((targetRange.w - newTargetSize.w) / 2.0f, (targetRange.h - newTargetSize.h) / 2.0f);
				targetRange = newTargetSize;

				result.targetMin = targetMin + newTargetOffset;
				result.targetMax = targetMin + newTargetOffset + newTargetSize;
			} else {
				result.targetMin = targetMin;
				result.targetMax = targetMax;
			}

			f32 scaleX = (sourceRange.x) / (targetRange.x);
			f32 scaleY = (sourceRange.y) / (targetRange.y);
			result.sourceScale = Vec2f(scaleX, scaleY);

			return(result);
		}

 

How can i to get it to work for negative ranges as well?

Edited by Finalspace

Share this post


Link to post
Share on other sites
Advertisement

I figured it out using dimensional analysis.

Basically what i did was to convert the incoming source vector into it a percentage and then convert that percentage lerp that back to the targets min/max.

 

Looks like that - if one is curious:

 

		struct RenderArea {
			Vec2f sourceMin;
			Vec2f sourceMax;
			Vec2f targetMin;
			Vec2f targetMax;
			Vec2f sourceScale;
			Vec2f targetScale;

			inline Vec2f Project(const Vec2f &sourcePos) {
				Vec2f percentage = Vec2f((sourcePos.x - sourceMin.x) * sourceScale.x, (sourcePos.y - sourceMin.y) * sourceScale.y);
				f32 x = Lerp(targetMin.x, percentage.x, targetMax.x);
				f32 y = Lerp(targetMin.y, percentage.y, targetMax.y);
				Vec2f result = Vec2f(x, y);
				return(result);
			}

			inline Vec2f Unproject(const Vec2f &targetPos) {
				Vec2f percentage = Vec2f((targetPos.x - targetMin.x) * targetScale.x, (targetPos.y - targetMin.y) * targetScale.y);
				f32 x = Lerp(sourceMin.x, percentage.x, sourceMax.x);
				f32 y = Lerp(sourceMin.y, percentage.y, sourceMax.y);
				Vec2f result = Vec2f(x, y);
				return(result);
			}
		};

		inline RenderArea CalculateRenderArea(const Vec2f &sourceMin, const Vec2f &sourceMax, const Vec2f &targetMin, const Vec2f &targetMax, const bool letterBoxed) {
			// @TODO: Do we want to allow source min/max to be flipped as well
			assert(sourceMin.x < sourceMax.x);
			assert(sourceMin.y < sourceMax.y);

			RenderArea result = {};

			Vec2f sourceRange = Absolute(sourceMax - sourceMin);
			Vec2f targetRange = Absolute(targetMax - targetMin);

			result.sourceMin = sourceMin;
			result.sourceMax = sourceMax;


			if (letterBoxed) {
				// @NOTE: Letterbox calculation is done in absolute range always
				const f32 sourceAspect = sourceRange.w / sourceRange.h;
				Vec2f newTargetSize = Vec2f(targetRange.w, targetRange.w / sourceAspect);
				if (newTargetSize.h > targetRange.h) {
					newTargetSize.h = targetRange.h;
					newTargetSize.w = targetRange.h * sourceAspect;
				}
				Vec2f newTargetOffset = Vec2f((targetRange.w - newTargetSize.w) / 2.0f, (targetRange.h - newTargetSize.h) / 2.0f);
				targetRange = newTargetSize;

				// Nuisance, but this works
				if (targetMin.x < targetMax.x) {
					result.targetMin.x = targetMin.x + newTargetOffset.x;
					result.targetMax.x = targetMax.x - newTargetOffset.x;
					targetRange.x = newTargetSize.w;
				} else {
					result.targetMin.x = targetMin.x - newTargetOffset.x;
					result.targetMax.x = targetMax.x + newTargetOffset.x;
					targetRange.y = -newTargetSize.h;
				}

				if (targetMin.y < targetMax.y) {
					result.targetMin.y = targetMin.y + newTargetOffset.y;
					result.targetMax.y = targetMax.y - newTargetOffset.y;
					targetRange.y = newTargetSize.h;
				} else {
					result.targetMin.y = targetMin.y - newTargetOffset.y;
					result.targetMax.y = targetMax.y + newTargetOffset.y;
					targetRange.y = -newTargetSize.h;
				}
			} else {
				result.targetMin = targetMin;
				result.targetMax = targetMax;
			}

			result.sourceScale = Vec2f(1.0f / sourceRange.x, 1.0f / sourceRange.y);
			result.targetScale = Vec2f(1.0f / targetRange.x, 1.0f / targetRange.y);

			return(result);
		}

I am pretty sure, that i could go the same thing with a inverse/transponse matrix, but i never tried that. Also i dont have a inverse function...

Edited by Finalspace

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!