@@ -3,7 +3,9 @@ import { setupWebknossosForTesting, type WebknossosTestContext } from "test/help
33import { getMappingInfo } from "viewer/model/accessors/dataset_accessor" ;
44import {
55 minCutAgglomerateWithPositionAction ,
6+ minCutPartitionsAction ,
67 proofreadMergeAction ,
8+ toggleSegmentInPartitionAction ,
79} from "viewer/model/actions/proofread_actions" ;
810import {
911 setActiveCellAction ,
@@ -29,6 +31,7 @@ import type { Vector3 } from "viewer/constants";
2931import type { MinCutTargetEdge } from "admin/rest_api" ;
3032import _ from "lodash" ;
3133import { delay } from "typed-redux-saga" ;
34+ import { updateUserSettingAction } from "viewer/model/actions/settings_actions" ;
3235
3336describe ( "Proofreading (with mesh actions)" , ( ) => {
3437 beforeEach < WebknossosTestContext > ( async ( context ) => {
@@ -182,7 +185,7 @@ describe("Proofreading (with mesh actions)", () => {
182185 await task . toPromise ( ) ;
183186 } , 8000 ) ;
184187
185- const mockEdgesForAgglomerateMinCut = ( mocks : WebknossosTestContext [ "mocks" ] ) =>
188+ const mockEdgesForNormalAgglomerateMinCut = ( mocks : WebknossosTestContext [ "mocks" ] ) =>
186189 vi . mocked ( mocks . getEdgesForAgglomerateMinCut ) . mockImplementation (
187190 async (
188191 _tracingStoreUrl : string ,
@@ -278,7 +281,7 @@ describe("Proofreading (with mesh actions)", () => {
278281 [ 1338 , 1 ] ,
279282 ] ) ;
280283
281- mockEdgesForAgglomerateMinCut ( mocks ) ;
284+ mockEdgesForNormalAgglomerateMinCut ( mocks ) ;
282285
283286 const { annotation } = Store . getState ( ) ;
284287 const { tracingId } = annotation . volumes [ 0 ] ;
@@ -355,7 +358,7 @@ describe("Proofreading (with mesh actions)", () => {
355358 } ,
356359 ] ) ;
357360
358- mockEdgesForAgglomerateMinCut ( mocks ) ;
361+ mockEdgesForNormalAgglomerateMinCut ( mocks ) ;
359362
360363 const { annotation } = Store . getState ( ) ;
361364 const { tracingId } = annotation . volumes [ 0 ] ;
@@ -402,4 +405,168 @@ describe("Proofreading (with mesh actions)", () => {
402405
403406 await task . toPromise ( ) ;
404407 } , 8000 ) ;
408+
409+ const mockEdgesForPartitionedAgglomerateMinCut = ( mocks : WebknossosTestContext [ "mocks" ] ) =>
410+ vi . mocked ( mocks . getEdgesForAgglomerateMinCut ) . mockImplementation (
411+ async (
412+ _tracingStoreUrl : string ,
413+ _tracingId : string ,
414+ segmentsInfo : {
415+ partition1 : NumberLike [ ] ;
416+ partition2 : NumberLike [ ] ;
417+ mag : Vector3 ;
418+ agglomerateId : NumberLike ;
419+ editableMappingId : string ;
420+ } ,
421+ ) : Promise < Array < MinCutTargetEdge > > => {
422+ const { agglomerateId, partition1, partition2 } = segmentsInfo ;
423+ if (
424+ agglomerateId === 1 &&
425+ _ . isEqual ( partition1 , [ 1 , 2 ] ) &&
426+ _ . isEqual ( partition2 , [ 1337 , 1338 ] )
427+ ) {
428+ return [
429+ {
430+ position1 : [ 1 , 1 , 1 ] ,
431+ position2 : [ 1338 , 1338 , 1338 ] ,
432+ segmentId1 : 1 ,
433+ segmentId2 : 1338 ,
434+ } ,
435+ {
436+ position1 : [ 3 , 3 , 3 ] ,
437+ position2 : [ 1337 , 1337 , 1337 ] ,
438+ segmentId1 : 3 ,
439+ segmentId2 : 1337 ,
440+ } ,
441+ ] ;
442+ }
443+ throw new Error ( "Unexpected min cut request" ) ;
444+ } ,
445+ ) ;
446+
447+ function * simulatePartitionedSplitAgglomeratesViaMeshes (
448+ context : WebknossosTestContext ,
449+ ) : Generator < any , void , any > {
450+ const { api } = context ;
451+ const { tracingId } = yield select ( ( state : WebknossosState ) => state . annotation . volumes [ 0 ] ) ;
452+ const expectedInitialMapping = new Map ( [
453+ [ 1 , 1 ] ,
454+ [ 2 , 1 ] ,
455+ [ 3 , 1 ] ,
456+ [ 4 , 4 ] ,
457+ [ 5 , 4 ] ,
458+ [ 6 , 6 ] ,
459+ [ 7 , 6 ] ,
460+ ] ) ;
461+
462+ yield call ( initializeMappingAndTool , context , tracingId ) ;
463+ const mapping0 = yield select (
464+ ( state ) =>
465+ getMappingInfo ( state . temporaryConfiguration . activeMappingByLayer , tracingId ) . mapping ,
466+ ) ;
467+ expect ( mapping0 ) . toEqual ( expectedInitialMapping ) ;
468+
469+ // Set up the merge-related segment partners. Normally, this would happen
470+ // due to the user's interactions.
471+ yield put ( updateSegmentAction ( 6 , { somePosition : [ 1337 , 1337 , 1337 ] } , tracingId ) ) ;
472+ yield put ( setActiveCellAction ( 6 , undefined , null , 1337 ) ) ;
473+
474+ yield call ( createEditableMapping ) ;
475+
476+ // After making the mapping editable, it should not have changed (as no other user did any update actions in between).
477+ const mapping1 = yield select (
478+ ( state ) =>
479+ getMappingInfo ( state . temporaryConfiguration . activeMappingByLayer , tracingId ) . mapping ,
480+ ) ;
481+ expect ( mapping1 ) . toEqual ( expectedInitialMapping ) ;
482+ // setOthersMayEditForAnnotationAction must be after making the mapping editable as this action is not supported to be integrated.
483+ // TODOM: Support integrating this action, if it originates from this user.
484+ yield put ( setOthersMayEditForAnnotationAction ( true ) ) ;
485+
486+ //Activate Multi-split tool
487+ yield put ( updateUserSettingAction ( "isMultiSplitActive" , true ) ) ;
488+ // Select partition 1
489+ yield put ( toggleSegmentInPartitionAction ( 1 , 1 , 1 ) ) ;
490+ yield put ( toggleSegmentInPartitionAction ( 2 , 1 , 1 ) ) ;
491+ // Select partition 2
492+ yield put ( toggleSegmentInPartitionAction ( 1337 , 2 , 1 ) ) ;
493+ yield put ( toggleSegmentInPartitionAction ( 1338 , 2 , 1 ) ) ;
494+ // Execute the actual merge and wait for the finished mapping.
495+ yield put ( minCutPartitionsAction ( ) ) ;
496+ yield take ( "FINISH_MAPPING_INITIALIZATION" ) ;
497+ // Checking optimistic merge is not necessary as no "foreign" update was injected.
498+ yield call ( ( ) => api . tracing . save ( ) ) ; // Also pulls newest version from backend.
499+ }
500+
501+ it ( "should perform partitioned min-cut correctly" , async ( context : WebknossosTestContext ) => {
502+ const { mocks } = context ;
503+ // Initial mapping should be
504+ // [[1, 1],
505+ // [2, 1],
506+ // [3, 1],
507+ // [4, 4],
508+ // [5, 4],
509+ // [6, 6],
510+ // [7, 6],
511+ // [1337, 1],
512+ // [1338, 1]]
513+ // Thus, there should be the following circle of edges: 1-2-3-1337-1338-1.
514+ const _backendMock = mockInitialBucketAndAgglomerateData ( context , [
515+ [ 1 , 1338 ] ,
516+ [ 3 , 1337 ] ,
517+ ] ) ;
518+
519+ mockEdgesForPartitionedAgglomerateMinCut ( mocks ) ;
520+
521+ const { annotation } = Store . getState ( ) ;
522+ const { tracingId } = annotation . volumes [ 0 ] ;
523+
524+ // TODOM: Test is failing because interfering things are problematic
525+ const task = startSaga ( function * task ( ) : Generator < any , void , any > {
526+ yield simulatePartitionedSplitAgglomeratesViaMeshes ( context ) ;
527+
528+ const mergeSaveActionBatch = context . receivedDataPerSaveRequest . at ( - 1 ) ! [ 0 ] ?. actions ;
529+
530+ expect ( mergeSaveActionBatch ) . toEqual ( [
531+ {
532+ name : "splitAgglomerate" ,
533+ value : {
534+ actionTracingId : "volumeTracingId" ,
535+ agglomerateId : 1 ,
536+ segmentId1 : 1 ,
537+ segmentId2 : 1338 ,
538+ } ,
539+ } ,
540+ {
541+ name : "splitAgglomerate" ,
542+ value : {
543+ actionTracingId : "volumeTracingId" ,
544+ agglomerateId : 1 ,
545+ segmentId1 : 3 ,
546+ segmentId2 : 1337 ,
547+ } ,
548+ } ,
549+ ] ) ;
550+ const finalMapping = yield select (
551+ ( state ) =>
552+ getMappingInfo ( state . temporaryConfiguration . activeMappingByLayer , tracingId ) . mapping ,
553+ ) ;
554+
555+ expect ( finalMapping ) . toEqual (
556+ new Map ( [
557+ [ 1 , 1 ] ,
558+ [ 2 , 1 ] ,
559+ [ 3 , 1 ] ,
560+ [ 4 , 4 ] ,
561+ [ 5 , 4 ] ,
562+ [ 6 , 6 ] ,
563+ [ 7 , 6 ] ,
564+ [ 1337 , 1339 ] ,
565+ [ 1338 , 1339 ] , // TODO: check why this is loaded
566+ ] ) ,
567+ ) ;
568+ } ) ;
569+
570+ await task . toPromise ( ) ;
571+ } ) ;
405572} ) ;
0 commit comments