@quenty/adorneeutils 3.5.0 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,12 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [3.6.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/adorneeutils@3.5.0...@quenty/adorneeutils@3.6.0) (2026-09-02)
7
+
8
+ ### Features
9
+
10
+ - **adorneeutils:** Add AdorneeUtils.setBoundingBoxCFrame ([d4b0fe8](https://github.com/Quenty/NevermoreEngine/commit/d4b0fe8f04b9e5ce26de76c47e387e6031ad87ec))
11
+
6
12
  # [3.5.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/adorneeutils@3.4.0...@quenty/adorneeutils@3.5.0) (2026-08-28)
7
13
 
8
14
  **Note:** Version bump only for package @quenty/adorneeutils
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quenty/adorneeutils",
3
- "version": "3.5.0",
3
+ "version": "3.6.0",
4
4
  "description": "AdorneeUtils - Generic adornee functions to make attaching to objects in Roblox easier.",
5
5
  "keywords": [
6
6
  "Roblox",
@@ -37,5 +37,5 @@
37
37
  "publishConfig": {
38
38
  "access": "public"
39
39
  },
40
- "gitHead": "346201e192e467cb71160f43029863853be855b5"
40
+ "gitHead": "278f4ca25486a8ba0601704b27a11da8c77499c9"
41
41
  }
@@ -69,6 +69,38 @@ function AdorneeUtils.getBoundingBox(adornee: Instance): (CFrame?, Vector3?)
69
69
  end
70
70
  end
71
71
 
72
+ --[=[
73
+ Moves the adornee so its bounding box sits at the given cframe, keeping every part where it
74
+ sits relative to the others.
75
+
76
+ This is the counterpart to [AdorneeUtils.getBoundingBox], and is not the same as `PivotTo`,
77
+ which moves the adornee by its pivot rather than by the center of its bounds.
78
+
79
+ @param adornee Instance
80
+ @param cframe CFrame -- New bounding box cframe
81
+ @return boolean -- False when the adornee has no bounding box to move
82
+ ]=]
83
+ function AdorneeUtils.setBoundingBoxCFrame(adornee: Instance, cframe: CFrame): boolean
84
+ assert(typeof(adornee) == "Instance", "Adornee must by of type 'Instance'")
85
+ assert(typeof(cframe) == "CFrame", "Bad cframe")
86
+
87
+ local bbCFrame = AdorneeUtils.getBoundingBox(adornee)
88
+ if not bbCFrame then
89
+ return false
90
+ end
91
+
92
+ local relativeCFrames: { [BasePart]: CFrame } = {}
93
+ for _, part in AdorneeUtils.getParts(adornee) do
94
+ relativeCFrames[part] = bbCFrame:ToObjectSpace(part.CFrame)
95
+ end
96
+
97
+ for part, relativeCFrame in relativeCFrames do
98
+ part.CFrame = cframe:ToWorldSpace(relativeCFrame)
99
+ end
100
+
101
+ return true
102
+ end
103
+
72
104
  --[=[
73
105
  Returns whether a part is a part of an adornee
74
106
  @param adornee Instance
@@ -439,6 +439,57 @@ describe("AdorneeUtils.getBoundingBox", function()
439
439
  end)
440
440
  end)
441
441
 
442
+ describe("AdorneeUtils.setBoundingBoxCFrame", function()
443
+ it("puts the bounding box where it was asked to go", function()
444
+ local controller = setup()
445
+ local model = controller.newModel()
446
+
447
+ local target = CFrame.new(20, 8, -4)
448
+ local moved = AdorneeUtils.setBoundingBoxCFrame(model, target)
449
+
450
+ local cframe = AdorneeUtils.getBoundingBox(model)
451
+
452
+ expect(moved).toBe(true)
453
+ expect(cframe).toEqual(target)
454
+
455
+ controller:Destroy()
456
+ end)
457
+
458
+ it("keeps parts where they sit relative to each other", function()
459
+ local controller = setup()
460
+ local model, first, second = controller.newModel()
461
+
462
+ local offset = first.CFrame:ToObjectSpace(second.CFrame)
463
+
464
+ AdorneeUtils.setBoundingBoxCFrame(model, CFrame.Angles(0, math.pi / 2, 0) + Vector3.new(20, 8, -4))
465
+
466
+ expect(first.CFrame:ToObjectSpace(second.CFrame)).toEqual(offset)
467
+
468
+ controller:Destroy()
469
+ end)
470
+
471
+ it("moves a lone part", function()
472
+ local controller = setup()
473
+ local part = controller.newPart(Vector3.new(4, 1, 2), CFrame.new(1, 2, 3))
474
+
475
+ local moved = AdorneeUtils.setBoundingBoxCFrame(part, CFrame.new(7, 7, 7))
476
+
477
+ expect(moved).toBe(true)
478
+ expect(part.CFrame).toEqual(CFrame.new(7, 7, 7))
479
+
480
+ controller:Destroy()
481
+ end)
482
+
483
+ it("reports nothing moved when the adornee has no parts", function()
484
+ local controller = setup()
485
+ local folder = controller.newFolder()
486
+
487
+ expect(AdorneeUtils.setBoundingBoxCFrame(folder, CFrame.new(1, 2, 3))).toBe(false)
488
+
489
+ controller:Destroy()
490
+ end)
491
+ end)
492
+
442
493
  describe("AdorneeUtils.isPartOfAdornee", function()
443
494
  it("returns true when the part is the adornee", function()
444
495
  local controller = setup()