Hi, not a trivial task via code. If you are running a full version and are able to use Python (PyRx). You can try a KD-Tree to quickly search and adjust text clusters. This is just a proof of concept, you may have to add in some math since you have text rotation
import traceback
from pyrx import Ap, Db, Ed, Ge
from itertools import pairwise
# hold text data, __it__, so we can presort by Y
class MT:
def __init__(self, id: Db.ObjectId):
mt = Db.MText(id)
self.id = id
self.ext = mt.getGeomExtents()
# change this value to increase or decrease the search
self.rad = (self.ext.maxPoint() - self.ext.minPoint()).length() * 0.5
self.lay = mt.layer()
def __lt__(self, other):
return self.ext.maxPoint().y > other.ext.maxPoint().y
# get the data
def getData(ss: Ed.SelectionSet):
mtdata: list[MT] = []
for id in ss.objectIds():
mtdata.append(MT(id))
return sorted(mtdata)
# return the new ext so we can update
def moveText(id, point):
mt = Db.MText(id, Db.OpenMode.kForWrite)
mt.setLocation(point)
return mt.getGeomExtents()
@Ap.Command()
def doit():
try:
# fill up the data
ps, ss = Ed.Editor.select([(0, "MTEXT")])
if ps != Ed.PromptStatus.eOk:
raise RuntimeError("Selection Error! {}: ".format(ps))
mtdata = getData(ss)
# build the kd-tree from extents mid points
pnts = Ge.Point3dArray()
for mt in mtdata:
pnts.append(mt.ext.midPoint())
tree = Ge.Point3dTree(pnts)
# search and group
groups = set()
for mt in mtdata:
subgroup = []
_idxs, _dists = tree.radiusSearch(mt.ext.midPoint(), mt.rad)
for _i in _idxs:
subgroup.append(_i)
groups.add(tuple(sorted(subgroup)))
# do something with the the groups
for items in groups:
for i, j in pairwise(items):
mtdata[j].ext = moveText(mtdata[j].id, mtdata[i].ext.minPoint())
except Exception as err:
traceback.print_exception(err)

Python for AutoCAD, Python wrappers for ARX https://github.com/CEXT-Dan/PyRx