# Scale Around Point - similar to Rotate Around

Discussion in 'Scripting' started by De-Panther, Mar 7, 2014.

1. ### De-Panther

Joined:
Dec 27, 2009
Posts:
564
Hey,

So I had a problem, and I solved it, and because I saw lots of questions about it, but without a solution, here is how I solved it:

Unity has Transform.RotateAround
https://docs.unity3d.com/Documentation/ScriptReference/Transform.RotateAround.html
Rotates the transform about axis passing through point in world coordinates by angle degrees.
But it doesn't have ScaleAround

In order to scale around a point in the world, you'll need this formula:

GameObject Local Position
A=Vector3(9,8,7)

New pivot position(relative to object's parent)
B=Vector3(4,5,6)

Old scale Vector3(2,2,2)
New scale Vector3(5,5,5)

Find Object position relative to pivot(It's similar to put the game object as a child of a pivot object, and then get it's localPosition):
A-B=C
Vector3(9,8,7)-Vector3(4,5,6)=Vector3(5,3,1)

Relative Scale(we need to check how much did the object scaled):
RS=New Scale / Old Scale
2.5=5 / 2

Final Position:
RS*C+B=FP

Vector3.Scale(Vector3(2.5,2.5,2.5), Vector3(5,3,1)) + Vector3(4,5,6)
Vector3(12.5,7.5,2.5) + Vector3(4,5,6)=Vector3(16.5,12.5,8.5)
Vector3(16.5,12.5,8.5)

So,
(Relative Scale * Object Position Relative to Pivot) + Pivot Position

Good luck Afroman and amonitzer like this.
2. ### MaRTiDoRe

Joined:
Jul 4, 2011
Posts:
41
I don't understand where do you get:

Old scale Vector3(2,2,2)
New scale Vector3(5,5,5)

Where do you get those values from and why do you have two scale values?

The rest is crystal clear.

Last edited: Jul 3, 2014
3. ### gregroberts

Joined:
Nov 21, 2014
Posts:
25
I've translated De-Panther's pseudocode into working code. Enjoy:

var targetGO : GameObject;
var pivotGO : Transform;

function Start () {
var A : Vector3 = targetGO.transform.position;
var B : Vector3 = pivotGO.transform.position;

var RS : float = 0.2; // relataive scale factor
var startScale = targetGO.transform.localScale;
var endScale = targetGO.transform.localScale *RS;

var C : Vector3 = A-B; // diff from object pivot to desired pivot/origin

// calc final position post-scale
var FP : Vector3 = (C*RS)+B;

// finally, actually perform the scale/translation
targetGO.transform.localScale = endScale;
targetGO.transform.position = FP;
}

4. ### trul

Joined:
Nov 15, 2013
Posts:
9
I've created static method to scale target around the pivot:

Code (CSharp):
1. public static void ScaleAround(Transform target, Transform pivot, Vector3 scale) {
2.         Transform pivotParent = pivot.parent;
3.         Vector3 pivotPos = pivot.position;
4.         pivot.parent = target;
5.         target.localScale = scale;
6.         target.position += pivotPos - pivot.position;
7.         pivot.parent = pivotParent;
8.     }

5. ### KyleBlumreisinger

Joined:
Mar 6, 2017
Posts:
2
So trul's code with parenting wasn't quite what I personally was looking for and I couldn't get it to work, but gregroberts's example code also didn't work for me out of the box, so I eventually made this:
I've translated gregroberts's mostly-working (I'm assuming Java) code translation of De-Panther's pseudocode into fully functioning and tested C#:

Code (CSharp):
1. public void ScaleAround(GameObject target, Vector3 pivot, Vector3 newScale)
2. {
3.     Vector3 A = target.transform.localPosition;
4.     Vector3 B = pivot;
5.
6.     Vector3 C = A - B; // diff from object pivot to desired pivot/origin
7.
8.     float RS = newScale.x / target.transform.localScale.x; // relative scale factor
9.
10.     // calc final position post-scale
11.     Vector3 FP = B + C * RS;
12.
13.     // finally, actually perform the scale/translation
14.     target.transform.localScale = newScale;
15.     target.transform.localPosition = FP;
16. }
Usage:
Code (CSharp):
1. ScaleAround(targetGameObject, scaleCenterPivot, desiredFinalScale);
Alternatively, if you would rather use a Transform / GameObject's position as the pivot, you can do:
Code (CSharp):
1. ScaleAround(targetGameObject, pivotGameObject.transform.position, desiredFinalScale);
Just remember: It is extremely important that the pivot point be in the target's PARENT'S space. So if your target object has no parent, your pivot is in world space. If your target is a child of SomeGameObject, you pivot point must be in SomeGameObject's space. This can be accomplished with
Code (CSharp):
1. targetGameObject.transform.parent.InverseTransformPoint(pivot);
if your pivot is in world space.

Hope this helps someone who needed it as much as I did, thanks to everyone who contributed here, and happy coding!

6. ### De-Panther

Joined:
Dec 27, 2009
Posts:
564
@KyleBlumreisinger , I'm happy that it helped someone after so many years I think that the "target" type on ScaleAround should be Transform.

And maybe we can add a bool that ask if pivot is in parent space or world space.

Also, now that I think about it, it works only if the scaling was unified.
So iguess it shold be:
Vector3 RS= new Vector3(newScale.x / target.transform.localScale.x, newScale.y / target.transform.localScale.y, newScale.z / target.transform.localScale.z);
Vector3 FP = B + C.Scale(RS);

But I'll have to test it.

Diablo404 likes this.
7. ### stenfeio

Joined:
Jan 18, 2015
Posts:
22
This was excellent. It's easy to forget that final positions need to also be handled as we've come so much to rely on parenting...

Last edited: Sep 3, 2018
8. ### _geo__

Joined:
Feb 26, 2014
Posts:
1,137
I know it's an older post but just for completeness and to help others who might come across this.
So here are some versions of this method with some write-up on how they work.
I took the liberty to add @De-Panther s suggestions.

Code (CSharp):
1. /// <summary>
2. /// Scales the target around an arbitrary point by scaleFactor.
3. /// This is relative scaling, meaning using  scale Factor of Vector3.one
4. /// will not change anything and new Vector3(0.5f,0.5f,0.5f) will reduce
5. /// the object size by half.
6. /// The pivot is assumed to be the position in the space of the target.
7. /// Scaling is applied to localScale of target.
8. /// </summary>
9. /// <param name="target">The object to scale.</param>
10. /// <param name="pivot">The point to scale around in space of target.</param>
11. /// <param name="scaleFactor">The factor with which the current localScale of the target will be multiplied with.</param>
12. public static void ScaleAroundRelative(GameObject target, Vector3 pivot, Vector3 scaleFactor)
13. {
14.     // pivot
15.     var pivotDelta = target.transform.localPosition - pivot;
16.     pivotDelta.Scale(scaleFactor);
17.     target.transform.localPosition = pivot + pivotDelta;
18.
19.     // scale
20.     var finalScale = target.transform.localScale;
21.     finalScale.Scale(scaleFactor);
22.     target.transform.localScale = finalScale;
23. }
24.
25. /// <summary>
26. /// Scales the target around an arbitrary pivot.
27. /// This is absolute scaling, meaning using for example a scale factor of
28. /// Vector3.one will set the localScale of target to x=1, y=1 and z=1.
29. /// The pivot is assumed to be the position in the space of the target.
30. /// Scaling is applied to localScale of target.
31. /// </summary>
32. /// <param name="target">The object to scale.</param>
33. /// <param name="pivot">The point to scale around in the space of target.</param>
34. /// <param name="scaleFactor">The new localScale the target object will have after scaling.</param>
35. public static void ScaleAround(GameObject target, Vector3 pivot, Vector3 newScale)
36. {
37.     // pivot
38.     Vector3 pivotDelta = target.transform.localPosition - pivot; // diff from object pivot to desired pivot/origin
39.     Vector3 scaleFactor = new Vector3(
40.         newScale.x / target.transform.localScale.x,
41.         newScale.y / target.transform.localScale.y,
42.         newScale.z / target.transform.localScale.z );
43.     pivotDelta.Scale(scaleFactor);
44.     target.transform.localPosition = pivot + pivotDelta;
45.
46.     //scale
47.     target.transform.localScale = newScale;
48. }
I'd argue that "ScaleAroundRelative()" is the way most people would assume the ScaleAround() methods from above work. Unitys own RotateAround() also works relative (rotate by) instead of absolute (rotate to angle).

Hope it's useful.

Last edited: Jul 23, 2020
LeviLeuthold, doompr, NotaNaN and 9 others like this.