4
$\begingroup$

Official Blender 2.8+ includes add-on called Batch Rename, but I can't find an option to invert numbering order for a bunch of objects (Text.001, Text.002, ..., Text.010).
Number 1 become 10, 2-9, 3-8, 4-7, 5-6.

Manual says

Regular Expressions can be used as a powerful way to tailor the Find/Replace texts and can be enabled using the icon to the right of the text fields.

Question - What is the right "Regular Expression" to add or revert sequential numbering of objects?

Note: There is an awesome script of p2or that does exactly what I need, but I would rather use a script bundled with official version. Thank you for your time :)

$\endgroup$
4
  • 2
    $\begingroup$ Can you write an example of at least 5 names and what you want to turn those names into? The way I understand your question is that you need to turn Text.001, Text.002, Text.003, Text.004, Text.005 into 001.Text, 002.Text, 003.Text, 004.Text, 005.Text $\endgroup$ – Markus von Broady Feb 8 at 21:50
  • $\begingroup$ @MarkusvonBroady Feel free to edit my post if my English is wrong, because I don't want to change location of numbers in a name. I need to rename files like from ascending to descending ... what is number 1 become 10, 2-9, 3-8, 4-7, 5-6. $\endgroup$ – vklidu Feb 8 at 22:07
  • 2
    $\begingroup$ From the perspective of a regex formula, total number of objects is not known, not to mention that it would be hard to make a mathematical equation in regex - so I don't think what you're asking is possible. Outside of regex, by making a Python script, the problem is trivial. $\endgroup$ – Markus von Broady Feb 8 at 22:26
  • $\begingroup$ @MarkusvonBroady If you think it is not a possible to get there with expression - you can write it as answer, I'm fine with that. If you are interested to extend current add-on with this feature even better. If you are "strong" enough to move this version into official release ... I can't pray more :) I just thought - if there is an expression, it would be easier to ask, than to merge new version into official build :) $\endgroup$ – vklidu Feb 9 at 14:55
4
+50
$\begingroup$

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)
$\endgroup$
3
  • $\begingroup$ Great :) Two things - please extend your Answer about impossibility to use expression for this task, so I can accept your answer. Second - can you contact author of Batch Rename to ask him to extend his script with this function? Thank you for your time. $\endgroup$ – vklidu Feb 11 at 11:07
  • $\begingroup$ @vklidu I added the explanation. As for the extension of the Blender's functionality: I think options like that would unnecessarily clutter the UI, for how often it would be needed and so I'd consider it a so-called feature creep. Not only that, but given how many options there are already in the script, and how many more could be added (some people perhaps want to work it also without a dot in the name), I think it's best to leave such problems to the scripting tab, where they can be modified to suit a particular case. I could be wrong, but I won't request something I disagree with. $\endgroup$ – Markus von Broady Feb 11 at 12:17
  • $\begingroup$ OK :) that is fair enough. Thank you. $\endgroup$ – vklidu Feb 11 at 13:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.