Skip to content
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
6ac57c6
WIP calculate surface area of segments
fm3 Sep 16, 2025
61c71a2
fix triangle offset
fm3 Sep 17, 2025
710d52e
Merge branch 'master' into mesh-surface-area
fm3 Sep 29, 2025
40a8ce4
fix vertex index offset
fm3 Sep 29, 2025
00b458a
Merge branch 'master' into mesh-surface-area
fm3 Sep 30, 2025
dbd3335
move to segmentStatistics routes
fm3 Sep 30, 2025
18e2409
add meshFileName, lod and seedPosition to getSegmentSurfaceArea reque…
fm3 Oct 1, 2025
7d56a1a
integrate first version for surface area into front end
philippotto Oct 1, 2025
6dfe14c
also add to segment statistics modal
philippotto Oct 1, 2025
cc95bbe
Fix RPC fetching voxel size for annotation
fm3 Oct 2, 2025
d55e5a2
Merge branch 'master' of github.com:scalableminds/webknossos into mes…
philippotto Oct 7, 2025
bad0855
only show segment stats when explicitly triggered
philippotto Oct 7, 2025
2ea2456
integrate surface area into stats modal and csv export
philippotto Oct 7, 2025
ae62338
remove seed pos
philippotto Oct 7, 2025
ee138b3
Merge branch 'master' into mesh-surface-area
fm3 Oct 8, 2025
9940941
fix voxelSize rpc; remove lod param; check meshfile mapping matches
fm3 Oct 8, 2025
3172698
add token to request, remove unused imports
fm3 Oct 8, 2025
9855846
keep menu open when load stats is clicked
philippotto Oct 8, 2025
0e6f1ea
remove lod
philippotto Oct 8, 2025
9af5488
fix type error
philippotto Oct 8, 2025
82400f7
sort imports
philippotto Oct 8, 2025
b65aa32
Merge branch 'master' into mesh-surface-area
fm3 Oct 9, 2025
7d22add
Merge branch 'master' into mesh-surface-area
fm3 Oct 13, 2025
c61dbca
changelog
fm3 Oct 13, 2025
e9ec82e
migrate new function getSegmentSurfaceArea to use new LayerSourceInfo…
MichaelBuessemeyer Oct 13, 2025
90bcfef
remove now-unused route implementation
fm3 Oct 13, 2025
fa7ef22
remove empty line
MichaelBuessemeyer Oct 13, 2025
00d68aa
deduplicate react key
MichaelBuessemeyer Oct 13, 2025
d1cb2bc
Merge branch 'mesh-surface-area' of github.com:scalableminds/webknoss…
MichaelBuessemeyer Oct 13, 2025
a02d99b
improve dataset.notFound messages
fm3 Oct 13, 2025
299c8e3
fix oversize-segment stats table
MichaelBuessemeyer Oct 13, 2025
10cfcdd
Merge branch 'mesh-surface-area' of github.com:scalableminds/webknoss…
fm3 Oct 13, 2025
f39564b
Merge branch 'master' into mesh-surface-area
fm3 Oct 14, 2025
7b44303
fix after merge
fm3 Oct 14, 2025
1cb016a
add seed position parameter for segment surface area request
MichaelBuessemeyer Oct 14, 2025
2c684cd
fix typo & reuse calculated mapping name
MichaelBuessemeyer Oct 14, 2025
89ed12c
undo adding segment seed position to surface calculation route
MichaelBuessemeyer Oct 14, 2025
b566e1c
remove comment about seed position for FE surface area route as it is…
MichaelBuessemeyer Oct 14, 2025
a8d15ef
remove seed position from request body type
fm3 Oct 14, 2025
c6aa9f2
Merge branch 'mesh-surface-area' of github.com:scalableminds/webknoss…
fm3 Oct 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions app/controllers/WKRemoteDataStoreController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -261,11 +261,10 @@ class WKRemoteDataStoreController @Inject()(
Action.async { implicit request =>
dataStoreService.validateAccess(name, key) { _ =>
for {
dataset <- datasetDAO.findOne(datasetId)(GlobalAccessContext)
dataset <- datasetDAO.findOne(datasetId)(GlobalAccessContext) ?~> "dataset.notFound" ~> NOT_FOUND
dataSource <- datasetService.dataSourceFor(dataset)
} yield Ok(Json.toJson(dataSource))
}

}

def updateDataSource(name: String, key: String, datasetId: ObjectId): Action[DataSource] =
Expand Down
10 changes: 10 additions & 0 deletions app/controllers/WKRemoteTracingStoreController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,16 @@ class WKRemoteTracingStoreController @Inject()(tracingStoreService: TracingStore
}
}

def getDataSource(name: String, key: String, datasetId: ObjectId): Action[AnyContent] =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This route is only used to retrieve the voxelsize If am not incorrect. Maybe we could instead implement a getVoxelSize or so instead of sending all the whole datasource to the tracingstore?

Moreover, maybe the access checking via a DB context and a passed accesstoken in the request could be better. Just as coderabbit is indicating.
Although, keeping the global access context and only returning the voxel size should be fine as this does not leak any crucial imo.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it’s fine. The tracingstore needs to be authenticated anyway, and it doesn’t give any of this info to the user.

Action.async { implicit request =>
tracingStoreService.validateAccess(name, key) { _ =>
for {
dataset <- datasetDAO.findOne(datasetId)(GlobalAccessContext) ?~> "dataset.notFound" ~> NOT_FOUND
dataSource <- datasetService.dataSourceFor(dataset)
} yield Ok(Json.toJson(dataSource))
}
}

def createTracing(name: String,
key: String,
annotationId: ObjectId,
Expand Down
1 change: 1 addition & 0 deletions conf/messages
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ mesh.file.lookup.failed=Failed to look up mesh file “{0}”
mesh.file.readVersion.failed=Failed to read format version from file “{0}”
mesh.file.readMappingName.failed=Failed to read mapping name from mesh file “{0}”
mesh.meshFileName.required=Trying to load mesh from mesh file, but mesh file name was not supplied.
mesh.loadFull.failed=Failed to load full segment mesh.

segmentIndexFile.notFound=Could not find requested segment index file

Expand Down
3 changes: 2 additions & 1 deletion conf/webknossos.latest.routes
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,12 @@ GET /datasets/:datasetId/layers/:layer/thumbnail
POST /datasets/:datasetId/layers/:layer/segmentAnythingMask controllers.DatasetController.segmentAnythingMask(datasetId: ObjectId, layer: String, intensityMin: Option[Float], intensityMax: Option[Float])
PUT /datasets/:datasetId/clearThumbnailCache controllers.DatasetController.removeFromThumbnailCache(datasetId: ObjectId)
GET /datasets/:datasetName/isValidNewName controllers.DatasetController.isValidNewName(datasetName: String)
GET /datasets/:datasetId controllers.DatasetController.read(datasetId: ObjectId, sharingToken: Option[String])
DELETE /datasets/:datasetId/deleteOnDisk controllers.DatasetController.deleteOnDisk(datasetId: ObjectId)
POST /datasets/:datasetId/reserveAttachmentUploadToPath controllers.DatasetController.reserveAttachmentUploadToPath(datasetId: ObjectId)
POST /datasets/:datasetId/finishAttachmentUploadToPath controllers.DatasetController.finishAttachmentUploadToPath(datasetId: ObjectId)
POST /datasets/:datasetId/reserveUploadToPathsForPreliminary controllers.DatasetController.reserveUploadToPathsForPreliminary(datasetId: ObjectId)
POST /datasets/:datasetId/finishUploadToPaths controllers.DatasetController.finishUploadToPaths(datasetId: ObjectId)
GET /datasets/:datasetId controllers.DatasetController.read(datasetId: ObjectId, sharingToken: Option[String])
POST /datasets/compose controllers.DatasetController.compose()
POST /datasets/reserveUploadToPaths controllers.DatasetController.reserveUploadToPaths()

Expand Down Expand Up @@ -152,6 +152,7 @@ GET /tracingstores/:name/datasetId
GET /tracingstores/:name/annotationId controllers.WKRemoteTracingStoreController.annotationIdForTracing(name: String, key: String, tracingId: String)
GET /tracingstores/:name/dataStoreUri/:datasetId controllers.WKRemoteTracingStoreController.dataStoreUriForDataset(name: String, key: String, datasetId: ObjectId)
POST /tracingstores/:name/createTracing controllers.WKRemoteTracingStoreController.createTracing(name: String, key: String, annotationId: ObjectId, previousVersion: Long)
GET /tracingstores/:name/datasources/:datasetId controllers.WKRemoteTracingStoreController.getDataSource(name: String, key: String, datasetId: ObjectId)

# User access tokens for datastore authentication
POST /userToken/generate controllers.UserTokenController.generateTokenForDataStore()
Expand Down
35 changes: 35 additions & 0 deletions frontend/javascripts/admin/rest_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -879,6 +879,41 @@ export function getSegmentVolumes(
);
}

type SegmentStatisticsParametersMeshBased = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rename this to SurfaceAreaParametersMeshBased or so as at least the route sounds like that's whats returned by the route, and not all segment stats

mag: Vector3;
segmentIds: number[];
mappingName?: string | null;
additionalCoordinates?: AdditionalCoordinate[] | null;
meshFileName?: string | null;
};

export function getSegmentSurfaceArea(
layerSourceInfo: LayerSourceInfo,
mag: Vector3,
meshFileName: string | undefined | null,
segmentIds: Array<number>,
additionalCoordinates: AdditionalCoordinate[] | undefined | null,
mappingName: string | null | undefined,
): Promise<number[]> {
const requestUrl = getDataOrTracingStoreUrl(layerSourceInfo);
return doWithToken((token) => {
const data: SegmentStatisticsParametersMeshBased = {
mag,
segmentIds,
mappingName,
additionalCoordinates,
meshFileName,
};
return Request.sendJSONReceiveJSON(
`${requestUrl}/segmentStatistics/surfaceArea?token=${token}`,
{
data,
method: "POST",
},
);
});
}

export function getSegmentBoundingBoxes(
layerSourceInfo: LayerSourceInfo,
mag: Vector3,
Expand Down
2 changes: 1 addition & 1 deletion frontend/javascripts/libs/format_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ export const nmFactorToUnit3D = new Map([
[1e99, "Ym³"],
]);

// Accepts an volume that is interpreted in the given unit and returns a string
// Accepts a volume that is interpreted in the given unit and returns a string
// that uses a readable unit to represent the volume.
// E.g. formatNumberToVolume(0.003, Unit.m) == "3000.0 cm³"
export function formatNumberToVolume(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -947,3 +947,14 @@ export function getReadableNameOfVolumeLayer(
? getReadableNameByVolumeTracingId(tracing, layer.tracingId)
: null;
}

export function getCurrentMappingName(state: WebknossosState) {
const visibleSegmentationLayer = getVisibleSegmentationLayer(state);
const volumeTracing = getActiveSegmentationTracing(state);
if (volumeTracing?.mappingName != null) return volumeTracing?.mappingName;
const mappingInfo = getMappingInfo(
state.temporaryConfiguration.activeMappingByLayer,
visibleSegmentationLayer?.name,
);
return mappingInfo.mappingName;
}
Loading
Loading