Regex is a powerful (albeit not Turing complete) language, in which it's hard to say what is possible and what isn't: Adding Numbers with Regex.
However, in this case I find it fair to say the task can't be done using regex alone: the way search & replace functionality in regex mode works, is it goes through objects one by one, and applies the regex formula to each name separately. Therefore the task can't be done, not because of limitations of regex, but the limitations of the context available to it. Imagine writing a Python function, which gets an old name and is supposed to return a new name:
def rename(old_name):
new_name = "?"
return new_name
If you can't access variables outside of this function (and regex can't), then even though you're using a Turing complete language (Python), you can't come up with a solution, because while you can extract a number from the old_name
, you don't know a total number of names, so you're unable to do new_number = total_number - old_number
. If you hard-coded the total_number
to the function, assuming that there's always 100 objects for example, then it would work (as long as the assumption holds true). Likewise, if your object numbering, instead of integers, uses float numbers in range [0, 1], e.g. instead of Cube.001, Cube.002, Cube.003, Cube.004, Cube.005
, it uses Cube.0.0, Cube.0.25, Cube.0.50, Cube.0.75, Cube.1.0
, then again, you can make a Python function, not knowing the total number of objects, that still flips their names. Think of a shader that takes a texture and flips it, and what kind of data or coordinate system that shader requires in order to work. Search & Replace just doesn't supply Regex with that (and if it did, it would still be very unpractical to create such complex regex formulas).
The Python script solution can be as simple as:
import bpy
cubes = [o for o in bpy.data.objects if o.name.startswith('Cube.')]
for i, c in enumerate(sorted(cubes, key = lambda c: int(c.name.split('.')[-1]), reverse = True)):
newname = f'Cube.{i:03}'
if newname in bpy.data.objects:
bpy.data.objects[newname].name = f'Renaming...{newname}'
c.name = newname
However, this script has some problems:
- It doesn't touch an object without a number in its name. So if you start with a Cube, and then duplicate it 9 times, it will only rename duplicates (giving the last duplicate number 000), while the first duplicate will become the last (009). You need to manually rename the base Cube to
Cube.000
in order to fix that.
- It assumes every object with its name starting with
Cube.
is the object to be renamed. Even if it's name is Cube.Big
without a number.
- It will make the numbers have 3 digits, even if you rename more than a thousand objects. So the numbering will be:
001, 002, [...] 099, 100, 101, [...], 998, 999, 1000, 1001, [...]
.
- It assumes there's a dot immediately before a number. If there's any object like
Cube.Big005
, the script will fail trying to convert Big005
to an integer, and nothing will be renamed.
- It's case sensitive, so it will rename
Cube.001
but not cube.001
.
Here's a more general script, dealing with those issues:
import bpy
name = 'sphere' # what you search for, exactly. Searching for 'cube' will *not* find 'cube2' or 'cubes' or 'mycube' objects
case_sensitive = False # Don't ignore letter case
ignore_numberless = False # Don't treat an object with proper name but without a number as first in sequence
new_first_numberless = False # Keep the convention of not giving the number to the first object, after reversing
minimum_digits = 3 # use at least that many digits in object numbering (the script will use more if necessary)
# ------------------------------------
objs = bpy.data.objects
_name = name if case_sensitive else name.lower()
to_be_renamed = []
for o in objs:
name2 = o.name if case_sensitive else o.name.lower()
last_part = name2.split('.')[-1]
if not last_part.isdigit():
if not ignore_numberless and _name == name2:
to_be_renamed.append((-1, o))
continue
if _name == name2[:-len(last_part)-1]:
to_be_renamed.append((int(last_part), o))
to_be_renamed.sort(key = lambda x: x[0], reverse = True)
number_len = max(minimum_digits, len(str(len(to_be_renamed)-1)))
format_str = '{}.{:0' + str(number_len) + '}'
for i, (sortkey, object) in enumerate(to_be_renamed):
if i == 0 and new_first_numberless:
newname = name
else:
newname = format_str.format(name, i)
if newname in objs:
objs[newname].name = f'Renaming...{newname}'
object.name = newname
- There are settings in one place for easier script usage.
- If an object's name is the name searched for, without a number, it's number is considered as
-1
(so its order is before 000
)
- Every number has to be at the end, immediately after a dot. An object like
Cube001
will be ignored (but the script will not fail)
- If the last element is
35000
, the first will be 00001
(consistent length of numbers)
Text.001, Text.002, Text.003, Text.004, Text.005
into001.Text, 002.Text, 003.Text, 004.Text, 005.Text
$\endgroup$