MXS:运行 我的 'delete duplicates' 脚本时 3ds max 崩溃

MXS: 3ds max crashed when running my 'delete duplicates' script

3ds max 在我 运行 我的脚本时崩溃。生成一个 windows 错误消息框(由进程 "Windows Problem Reporting")。 Maxscript 中没有产生任何错误(侦听器在脚本 运行-time 期间不显示任何错误)。 CPU 和 RAM 使用在脚本 运行 期间处于可接受的水平。我的脚本在没有崩溃的情况下运行,并在只有标准原始对象的较小场景(10-20 个对象)上产生了所需的结果。 error/crash 出现在包含 4051 个多边形对象的较大场景中。 该脚本会查看所有对象,找到相同的对象并删除除了一个相同对象之外的所有对象,从而为您留下一个没有任何重复对象的场景。

当运行在包含 4051 个对象的场景上运行脚本时 windows 给我一个错误消息框; “3ds max 已停止工作 - 一个问题导致程序停止正常工作。Windows 将关闭程序并在有可用解决方案时通知您。” 在脚本期间 运行-time RAM 和 CPU 使用 3ds max 仍然可以接受(根据任务管理器)(30-40% CPU (i5-6600k) 和 3-4GB RAM 16GB).

'Windows Problem Reporting' 错误消息框的图像:

进程在任务管理器中的样子

CPU/RAM 在脚本 运行 时间 期间使用 3ds max 的图像:

注意:出现错误消息框后,3ds max 的 RAM/CPU/DISK 使用率下降到接近零值。出现错误消息框后 windows 除了关闭 3ds max,我别无选择。

我已经让脚本 运行 多次检查错误消息是否每次都出现在同一个对象上 运行,但事实并非如此,错误消息出现在每次我 运行 脚本时,都会有不同的对象(当脚本正在处理不同的对象时)。当脚本将场景中的第一个对象 ("Mesh_000") 与场景中网格数介于“700”和“900”之间的另一个对象进行比较时,脚本往往会崩溃。以下数组包含我的脚本在我执行的一些测试中崩溃的网格索引:#(805,832,733,766,877)。脚本 运行s 持续 10-20 分钟后崩溃。

导致错误的.max场景可以在这里找到: larger .max scene which causes error

脚本是这样运行的(伪代码):

duplicate_objs = #()
meshes_array = Collect all meshes in scene
for mesh in meshes_array do(
    Compare mesh to all other meshes in meshes_array
    if an identical mesh is found do(append duplicate_objs identical_mesh)
)
--Delete all the duplicate meshes:
delete duplicate_objs    

我的脚本代码: fn fn_construct_objs_array x = ( 清除selection() select几何 deselect 帮手 objs = selection 作为数组 checked_objs_list = #() items_to_delete_from_objs = #() )

fn fn_delete_duplicates equal_pos_boolean =( --打印"DELETING DUPLICATES" obj_dups = #()

    ConvertTo objs[1] Editable_Poly
    PolyCount_src = polyop.getNumFaces objs[1]
    TriCount_src = (objs[1].mesh.numFaces)
    VertCount_src = (getnumverts objs[1])    
    Position_src = objs[1].pos

    for i in 1 to objs.count do(
        if i <= objs.count and objs[i] != objs[1] do(
            format "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n--> Comp. original obj (%) to comparison-obj(%) \n" objs[1] objs[i]
            ConvertTo objs[i] Editable_Poly
            PolyCount = polyop.getNumFaces objs[i]
            Tricount = (objs[i].mesh.numFaces)
            Position = objs[i].pos
            VertCount = (getnumverts objs[i])

            if(Tricount == Tricount_src and PolyCount == PolyCount_src and VertCount == VertCount_src and ( if equal_pos_boolean then( Position == Position_src)else(true) )) do(
                if((finditem checked_objs_list (objs[i]) == 0) ) do(
                    appendifunique obj_dups objs[i]; --print "||||||||||||||||||||| FOUND DUPLICATE OBJECT |||||||||||||||||||||| \n"
                    appendifunique checked_objs_list objs[i]
                    append items_to_delete_from_objs objs[i]
                    )-- end if(finditem checked_objs_array objs[i] == 0 ) do(
                )-- end if(Tricount == Tricount_src and PolyCount == PolyCount_src and VertCount == VertCount_src and ( if equal_pos_boolean then( Position == Position_src)else(true) )) do(

            )-- end if i <= objs.count(
    )
    for item in items_to_delete_from_objs do(
        deleteitem objs (finditem objs item)
        )
    items_to_delete_from_objs = #()
    deleteitem objs 1
for duplicate in obj_dups do(delete duplicate)

if objs.count > 0 do (
    --format "objs left to process = % \n" objs.count ; 
    fn_delete_duplicates equal_pos_boolean
    )--end if objs.count > 0 do(
)--end fn_delete_duplicates

fn_construct_objs_array 1
fn_delete_duplicates true

注意:网格相同(在此上下文中)定义为具有相同多边形数、垂直数、边数、位置的网格。

粗略地看一下,我会警告不要使用 objs[i].mesh.numFaces,因为它会在内存中创建新的网格实例,而您不会调用 dispose 来删除它 - 如果您选择这样做并比较两者polycount 和 tricount,一定要首先进行转换并在循环外收集网格(最好也使用 snapshotAsMes)。此外,您可能想使用 getPolygonCount <node> 来为您提供节点多边形计数(如果堆栈的结果是多边形对象)和顶点计数。

我已经根据 Swordslayer 的建议编辑了我的代码;网格信息的收集现在发生在循环之外,我正在使用 getpolygonCount 来获取多边形和垂直计数。

我原来的问题中提到的包含 4051 个标准原始对象的场景不再导致崩溃(而且速度明显更快),但是另一个包含导入对象的场景现在导致 'Stack Overflow Error' inside 3ds max... st运行ge 的事情是这个错误只发生在我第一次 运行 启动 3ds max 后的脚本。当 运行 在发生堆栈溢出错误后第二次(或第三次等)运行脚本时,脚本执行时没有任何错误(完成大约需要 10-20 秒)并且脚本正常运行(因为它删除了重复的对象)。每次我 运行 刚启动 3ds max 后原始场景上的脚本时,都会发生堆栈溢出错误。

另一个 st运行ge 事情(对我来说)是即使发生堆栈溢出错误,脚本确实在产生所述错误之前删除了正确的对象(重复对象)。

3ds max占用内存before/after系统异常错误如下: 3ds max 场景打开,在 运行ning 脚本之前:RAM 使用 = 大约 1800MB 在脚本 运行 期间,在弹出错误消息框之前:RAM 使用量从最初的 1800 增加到最终值 2132.6MB 出现系统异常错误消息框:RAM usage = 2132.6MB 在错误消息框上单击“确定”后:RAM 使用率 = 2132.6MB (注意:正如我原来的问题中提到的,我有 16GB 的内存)

我现在的代码:

checked_objs_list; items_to_delete_from_objs; theMeshes; equal_pos_bool ; mesh_data

fn construct_arrays x =(    
    max select none ; select geometry ; deselect helpers
    theMeshes = selection as array
    mesh_info = for theMesh in theMeshes collect ( #(theMesh, getpolygoncount theMesh, theMesh.pos) )  --collects #(tricount, vertcount)    for all meshes in 'getpolycount_meshes'
    max select none     
    --Declare initial values of array variables
    checked_objs_list = #()
    items_to_delete_from_objs = #()
    return mesh_info
    )--end fn construct_arrays

fn delete_duplicates equal_pos_bool =(
    obj_dups = #()
        appendifunique items_to_delete_from_objs mesh_data[1]
        if i <= mesh_data.count and i != 1 do(
            if(mesh_data[i][2][1] == mesh_data[1][2][1] and mesh_data[i][2][2] == mesh_data[1][2][2] and ( if equal_pos_bool then( mesh_data[i][3] == mesh_data[1][3])else(true) )) do(
                if((finditem checked_objs_list (mesh_data[i]) == 0) ) do(
                    appendifunique obj_dups mesh_data[i][1] ; --print "||||||||||||||||||||| FOUND DUPLICATE OBJECT |||||||||||||||||||||| \n"
                    appendifunique checked_objs_list mesh_data[i]
                    appendifunique items_to_delete_from_objs mesh_data[i]
                    )-- end if(finditem checked_objs_array objs[i] == 0 ) do(
                )-- end if(Tricount == Tricount_src and PolyCount == PolyCount_src and VertCount == VertCount_src and ( if equal_pos_boolean then( Position == Position_src)else(true) )) do(
            )-- end if i <= objs.count(
        ) --end for i in 1 to mesh_data[1].count do(

    for item in items_to_delete_from_objs do(
            deleteitem mesh_data (finditem mesh_data item)
            )

    items_to_delete_from_objs = #()
    for duplicate in obj_dups do(delete duplicate)

    if mesh_data.count > 0 do (
        delete_duplicates equal_pos_bool
        )
    )

mesh_data = construct_arrays 1 --call construct_arrays function and save        returned data in variable 'mesh_data'
delete_duplicates false --function call

该问题似乎与场景中 objects/polygons 的数量无关;我 运行 包含 16204 个标准原始对象的场景上的脚本这个场景由 11,418,736 个多边形和 5,780,404 个顶点组成,而导致堆栈溢出错误的导入对象的场景仅由 703,737 个多边形和 769,886 个顶点组成(这是显着降低)。

我让堆栈溢出错误发生多次,每次都产生一个略有不同的错误消息框,所有这些都可以在下面看到:

第一次崩溃时产生的错误:

第二次崩溃时产生的错误:

第 3 次崩溃时产生的错误:

侦听器 window 中显示的错误告诉我第 1001 行 'Macro_SceneExplorer.mcr' 中发生的错误,该行包含以下代码: sceneexplorermanager.ExplorerIsOpen ~SE_SCENE_EXPLORER_NAME~

它还告诉我这行代码是从我的 'delete_duplicates.ms' 脚本的第 44 行调用的,其中包含以下代码: for duplicate in obj_dups do(delete duplicate)

这是发生堆栈溢出错误后在侦听器 window 中产生的错误消息的一部分(完整的错误和堆栈跟踪可在此处找到:https://drive.google.com/open?id=1s-BplnpiM_sz3yfN4XVySmhcsK5zraeX) :

-- Error occurred in anonymous codeblock; filename: C:\Program     Files\Autodeskds Max 2018\MacroScripts\Macro_SceneExplorer.mcr; position: 29880; line: 1001
-- MAXScript MacroScript Error Exception:
-- Known system exception
--       ########################################################################
-- Address: 0x6745e920; nCode: 0x00000000C00000FD
-- Desc: EXCEPTION_STACK_OVERFLOW The thread used up its stack.
-- ########################################################################
-- MAXScript callstack:
--  thread data: threadID:5300
--  ------------------------------------------------------
--  [stack level: 0]
--  In isChecked(); filename: C:\Program Files\Autodeskds Max 2018\MacroScripts\Macro_SceneExplorer.mcr; position: 29881; line: 1001
--  member of: codeblock macroScript: Scene Explorer_SESceneExplorer
--      Locals:
--      Externals:
--          owner: <CodeBlock:Scene Explorer_SESceneExplorer>
--          Scene Explorer_SESceneExplorer: <CodeBlock:Scene Explorer_SESceneExplorer>
--  ------------------------------------------------------
--  [stack level: 1]
--  called from codeblock macroScript: Scene Explorer_SESceneExplorer;     filename: C:\Users\Philips\Desktop\ninjaripper1.7.1\tools dmax_maxscript_importer[=11=]00_delete duplicates (pre-construct_arrays) 9  (noprint) 2.ms; position: 2755; line: 44
--      Locals:
--      Externals:
--  ------------------------------------------------------
--  [stack level: 2]
--  called from duplicate loop; filename: C:\Users\Philips\Desktop\ninjaripper1.7.1\toolsdmax_maxscript_importer[=11=]00_delete duplicates (pre-construct_arrays) 9 (noprint) 2.ms; position: 2755; line: 44
--      Parameters:
--          duplicate: <Deleted scene node>
--      Locals:
--          duplicate: <Deleted scene node>
--      Externals:
--          owner: undefined
--  ------------------------------------------------------
--  [stack level: 3]
--  called from delete_duplicates(); filename: C:\Users\Philips\Desktop\ninjaripper1.7.1\toolsdmax_maxscript_importer[=11=]00_delete duplicates (pre-construct_arrays) 9 (noprint) 2.ms; position: 2755; line: 44
--      Parameters:
--          equal_pos_bool: false
--      Locals:
--          equal_pos_bool: false
--          obj_dups: #(<Deleted scene node>)
--      Externals:
--          owner: undefined
--          delete_duplicates: Global:delete_duplicates : delete_duplicates()
--          mesh_data: Global:mesh_data : #(#($Editable_Mesh:Mesh_2184 @ [0.000000,0.000000,0.000000], #(128, 28), [0,0,0]), #($Editable_Mesh:Mesh_2185 @ [0.000000,0.000000,0.000000], #(128, 28), [0,0,0]), #($Editable_Mesh:Mesh_2256 @ [0.000000,0.000000,0.000000], #(256, 768), [0,0,0]), #($Editable_Mesh:Mesh_2258 @ [0.000000,0.000000,0.000000], #(2048, 1057), [0,0,0]), #($Editable_Mesh:Mesh_2261 @ [0.000000,0.000000,0.000000], #(512, 273), [0,0,0]), #($Editable_Mesh:Mesh_2262 @ [0.000000,0.000000,0.000000], #(512, 281), [0,0,0]), #($Editable_Mesh:Mesh_2263 @ [0.000000,0.000000,0.000000], #(2048, 1057), [0,0,0]), #($Editable_Mesh:Mesh_2265 @ [0.000000,0.000000,0.000000], #(32, 23), [0,0,0]), #($Editable_Mesh:Mesh_2266 @ [0.000000,0.000000,0.000000], #(32, 23), [0,0,0]), #($Editable_Mesh:Mesh_2267 @ [0.000000,0.000000,0.000000], #(128, 73), [0,0,0]), #($Editable_Mesh:Mesh_2268 @ [0.000000,0.000000,0.000000], #(128, 77), [0,0,0]), #($Editable_Mesh:Mesh_2269 @ [0.000000,0.000000,0.000000], #(512, 273), [0,0,0]), #($Editable_Mesh:Mesh_2270 @ [0.000000,0.000000,0.000000], #(2048, 1041), [0,0,0]), #($Editable_Mesh:Mesh_2271 @ [0.000000,0.000000,0.000000], #(32, 21), [0,0,0]), #($Editable_Mesh:Mesh_2272 @ [0.000000,0.000000,0.000000], #(32, 23), [0,0,0]), #($Editable_Mesh:Mesh_2273 @ [0.000000,0.000000,0.000000], #(128, 73), [0,0,0]), #($Editable_Mesh:Mesh_2274 @ [0.000000,0.000000,0.000000], #(512, 265), [0,0,0]), #($Editable_Mesh:Mesh_2276 @ [0.000000,0.000000,0.000000], #(32, 23), [0,0,0]), #($Editable_Mesh:Mesh_2277 @ [0.000000,0.000000,0.000000], #(128, 73), [0,0,0]), #($Editable_Mesh:Mesh_2278 @ [0.000000,0.000000,0.000000], #(50, 60), [0,0,0]), ...)
--          items_to_delete_from_objs: Global:items_to_delete_from_objs : #()
--  ------------------------------------------------------
--  [stack level: 4]
--  called from delete_duplicates(); filename: C:\Users\Philips\Desktop\ninjaripper1.7.1\toolsdmax_maxscript_importer[=11=]00_delete duplicates (pre-construct_arrays) 9 (noprint) 2.ms; position: 2958; line: 49
--      Parameters:
--          equal_pos_bool: false
--      Locals:
--          equal_pos_bool: false
--          obj_dups: #()
--      Externals:
--          owner: undefined
--          delete_duplicates: Global:delete_duplicates : delete_duplicates()
--          mesh_data: Global:mesh_data : #(#($Editable_Mesh:Mesh_2184 @ [0.000000,0.000000,0.000000], #(128, 28), [0,0,0]), #($Editable_Mesh:Mesh_2185 @ [0.000000,0.000000,0.000000], #(128, 28), [0,0,0]), #($Editable_Mesh:Mesh_2256 @ [0.000000,0.000000,0.000000], #(256, 768), [0,0,0]), #($Editable_Mesh:Mesh_2258 @ [0.000000,0.000000,0.000000], #(2048, 1057), [0,0,0]), #($Editable_Mesh:Mesh_2261 @ [0.000000,0.000000,0.000000], #(512, 273), [0,0,0]), #($Editable_Mesh:Mesh_2262 @ [0.000000,0.000000,0.000000], #(512, 281), [0,0,0]), #($Editable_Mesh:Mesh_2263 @ [0.000000,0.000000,0.000000], #(2048, 1057), [0,0,0]), #($Editable_Mesh:Mesh_2265 @ [0.000000,0.000000,0.000000], #(32, 23), [0,0,0]), #($Editable_Mesh:Mesh_2266 @ [0.000000,0.000000,0.000000], #(32, 23), [0,0,0]), #($Editable_Mesh:Mesh_2267 @ [0.000000,0.000000,0.000000], #(128, 73), [0,0,0]), #($Editable_Mesh:Mesh_2268 @ [0.000000,0.000000,0.000000], #(128, 77), [0,0,0]), #($Editable_Mesh:Mesh_2269 @ [0.000000,0.000000,0.000000], #(512, 273), [0,0,0]), #($Editable_Mesh:Mesh_2270 @ [0.000000,0.000000,0.000000], #(2048, 1041), [0,0,0]), #($Editable_Mesh:Mesh_2271 @ [0.000000,0.000000,0.000000], #(32, 21), [0,0,0]), #($Editable_Mesh:Mesh_2272 @ [0.000000,0.000000,0.000000], #(32, 23), [0,0,0]), #($Editable_Mesh:Mesh_2273 @ [0.000000,0.000000,0.000000], #(128, 73), [0,0,0]), #($Editable_Mesh:Mesh_2274 @ [0.000000,0.000000,0.000000], #(512, 265), [0,0,0]), #($Editable_Mesh:Mesh_2276 @ [0.000000,0.000000,0.000000], #(32, 23), [0,0,0]), #($Editable_Mesh:Mesh_2277 @ [0.000000,0.000000,0.000000], #(128, 73), [0,0,0]), #($Editable_Mesh:Mesh_2278 @ [0.000000,0.000000,0.000000], #(50, 60), [0,0,0]), ...)
--          items_to_delete_from_objs: Global:items_to_delete_from_objs :   #()

错误一直持续到堆栈级别 386...错误的其余部分看起来类似于下面显示的部分(再次错误持续很长时间,请参阅前面提到的 .txt 文件对于完整的错误):

--  [stack level: 387]
--  called from top-level
--   ########################################################################
-- C++ callstack:
-- (ucrtbase): (filename not available): malloc_base
-- (maxutil): (filename not available): GetWindowTextW
-- (MAXScrpt): (filename not available): InterfaceFunction::val_to_FPValue
-- (MAXScrpt): (filename not available): Generic::apply
-- (MAXScrpt): (filename not available): CodeTree::eval
-- (MAXScrpt): (filename not available): Generic::apply
-- (MAXScrpt): (filename not available): SourceFileWrapper::apply
-- (MAXScrpt): (filename not available): SourcePositionWrapper::apply_no_alloc_frame
-- (MAXScrpt): (filename not available): MacroEntry::DisableCompileGuard::DisableCompileGuard
-- (MAXScrpt): (filename not available): InitMacroScriptDir
-- (core): (filename not available): MacroButtonData::SetLabel
-- (core): (filename not available): MaxSDK::QMaxMultiResIconManager::trUtf8
-- (Qt5Core): (filename not available): QMetaObject::activate
-- (core): (filename not available): MaxSDK::QMaxMacroButtonUpdater::qt_static_metacall
-- (core): (filename not available): MaxSDK::QMaxMacroButtonUpdater::updateAll
-- (MAXScrpt): (filename not available): collect_nodes
-- (MAXScrpt): (filename not available): Primitive::apply
-- (MAXScrpt): (filename not available): CodeTree::eval
-- (MAXScrpt): (filename not available): Primitive::apply
-- (MAXScrpt): (filename not available): Array::map
-- (MAXScrpt): (filename not available): clear_error_source_data
-- (MAXScrpt): (filename not available): Primitive::apply
-- (MAXScrpt): (filename not available): CodeTree::eval
-- (MAXScrpt): (filename not available): Primitive::apply
-- (MAXScrpt): (filename not available): CodeTree::eval
-- (MAXScrpt): (filename not available): SourceFileWrapper::eval
-- (MAXScrpt): (filename not available): Generic::apply
-- (MAXScrpt): (filename not available): CodeTree::eval
-- (MAXScrpt): (filename not available): clear_error_source_data
-- (MAXScrpt): (filename not available): Primitive::apply
-- (MAXScrpt): (filename not available): CodeTree::eval
-- (MAXScrpt): (filename not available): Primitive::apply
-- (MAXScrpt): (filename not available): CodeTree::eval
-- (MAXScrpt): (filename not available): SourceFileWrapper::eval
-- (MAXScrpt): (filename not available): Generic::apply
-- (MAXScrpt): (filename not available): CodeTree::eval
-- (MAXScrpt): (filename not available): clear_error_source_data
-- (MAXScrpt): (filename not available): Primitive::apply
-- (MAXScrpt): (filename not available): CodeTree::eval
-- (MAXScrpt): (filename not available): Primitive::apply
-- (MAXScrpt): (filename not available): CodeTree::eval
-- (MAXScrpt): (filename not available): SourceFileWrapper::eval
-- (MAXScrpt): (filename not available): Generic::apply
-- (MAXScrpt): (filename not available): CodeTree::eval
-- (MAXScrpt): (filename not available): clear_error_source_data
-- (MAXScrpt): (filename not available): Primitive::apply
-- (MAXScrpt): (filename not available): CodeTree::eval

有时,我得到的不是错误消息框 'Stack Overflow Error',而是错误消息框 'Unknown System Exception'(大多数时候我得到的是堆栈溢出错误消息框,我无法找出原因另一个尚未发生)。其他时候 3ds max 不会产生错误消息框,windows 问题报告会给我一个消息框,说 3ds max 已停止工作。

我尝试使用 window 的事件查看器查找有关“3ds max 已停止工作”错误的更多信息:

文件:

脚本完美运行的包含 4000 多个标准原始对象的场景:https://drive.google.com/open?id=1ittqWwvqgJjrsNcNkbhM_mOHUnz1W-SH

包含导入对象的场景导致上述堆栈overflow/unknown系统异常错误: https://drive.google.com/open?id=1JpncS9pJcYxGeZU4UI_uzmLIPnB9r0uJ

发生堆栈溢出错误消息后的侦听器输出(错误): https://drive.google.com/open?id=1s-BplnpiM_sz3yfN4XVySmhcsK5zraeX

发生未知系统异常错误消息后的侦听器输出(错误): https://drive.google.com/open?id=1QPl0e0RHcqKIPkMaorMRJm-Q_LyWvG1E

'delete_duplicates.ms' maxscript 代码: https://drive.google.com/open?id=1FCpJNlgII6ouLYsh0ySjHXuTiKmfkWEK

我用于测试的另一个 .max 场景产生了类似的错误: https://drive.google.com/open?id=142JOth12rATD3sW4LlTmMX2vVzCLnmJs

任何关于我正在做的事情的建议 wrong/what 可能是这些错误的原因将不胜感激,谢谢!

我不擅长调试别人的代码,所以再次快速浏览一下 - 我肯定会避免 for duplicate in obj_dups do(delete duplicate) 中的 for 循环,delete 是一个映射函数,你可以传递一个集合作为它的论据。那样的话,场景资源管理器的更新就不会那么多了,应该会快很多。我也更喜欢迭代而不是递归。总而言之,我会这样写:

struct objInfo
(
    obj, polyCount, vertCount, pos,

    fn isSamePos pos1 pos2 eps:1e-6 =
    (
        local diff = pos2 - pos1
        dot diff diff < eps
    ),

    fn isEqual info checkPos:off =
    (
        this.vertCount == info.vertCount and
        this.polyCount == info.polyCount and
        (not checkPos or isSamePos this.pos info.pos)
    ),

    on create do
    (
        local polyVertCount = getPolygonCount obj
        polyCount = polyVertCount[1]
        vertCount = polyVertCount[2]
        pos = obj.pos
    )
)

fn collectDuplicates checkPos:off =
(
    items = for obj in geometry collect objInfo obj:obj
    itemCount = items.count
    collected = #{}
    duplicates = #()

    for item = 1 to itemCount where not collected[item] do
    (
        local current = items[item]

        for nextItem = item + 1 to itemCount
            where not collected[nextItem] and
            (
                local next = items[nextItem]
                next.isEqual current checkPos:checkPos
            )
            do
            (
                append duplicates next.obj
                append collected nextItem
            )
    )

    return duplicates
)

delete (collectDuplicates checkPos:off)