From dce1f0205fa48d1b75e9c88b19c7404f58befbd4 Mon Sep 17 00:00:00 2001 From: Laura Fritz Date: Thu, 9 Jun 2022 15:16:26 +0200 Subject: [PATCH] #186 obj import with large coordinates compensation, #156 view planner serialization, changed selection type for selected vp (fixed some bugs), load scene with wrong obj path and reload obj per gui, bug fixes: selection problems for multiple platforms, pan and tilt axes don't move in 3d view --- src/PRo3D.Core/Surface/SurfaceApp.fs | 84 +++- .../Viewplanner/Rover-Model.fs | 414 ++++++++++++++++-- .../Viewplanner/ViewPlanApp.fs | 187 ++++---- src/PRo3D.Viewer/Viewer-Model.fs | 62 ++- src/PRo3D.Viewer/Viewer/SnapshotSg.fs | 16 +- src/PRo3D.Viewer/Viewer/Viewer-IO.fs | 15 +- src/PRo3D.Viewer/Viewer/Viewer-Utils.fs | 34 +- src/PRo3D.Viewer/Viewer/Viewer.fs | 10 +- src/PRo3D.Viewer/Viewer/ViewerGUI.fs | 38 +- 9 files changed, 692 insertions(+), 168 deletions(-) diff --git a/src/PRo3D.Core/Surface/SurfaceApp.fs b/src/PRo3D.Core/Surface/SurfaceApp.fs index e6977d2e..f12fb585 100644 --- a/src/PRo3D.Core/Surface/SurfaceApp.fs +++ b/src/PRo3D.Core/Surface/SurfaceApp.fs @@ -29,24 +29,25 @@ open Adaptify.FSharp.Core open Aardvark.Base.Coder type SurfaceAppAction = -| SurfacePropertiesMessage of SurfaceProperties.Action -| FlyToSurface of Guid -| MakeRelative of Guid -| RemoveSurface of Guid*list -| PickSurface of SceneHit*string -| OpenFolder of Guid -| RebuildKdTrees of Guid -| ToggleActiveFlag of Guid -| ChangeImportDirectory of Guid*string -| ChangeImportDirectories of list -| GroupsMessage of GroupsAppAction -//| PickObject of V3d -| PlaceSurface of V3d -| ScalarsColorLegendMessage of FalseColorLegendApp.Action -| ColorCorrectionMessage of ColorCorrectionProperties.Action -| SetHomePosition -| TranslationMessage of TranslationApp.Action -| SetPreTrafo of string +| SurfacePropertiesMessage of SurfaceProperties.Action +| FlyToSurface of Guid +| MakeRelative of Guid +| RemoveSurface of Guid*list +| PickSurface of SceneHit*string +| OpenFolder of Guid +| RebuildKdTrees of Guid +| ToggleActiveFlag of Guid +| ChangeImportDirectory of Guid*string +| ChangeImportDirectories of list +| ChangeOBJImportDirectories of list +| GroupsMessage of GroupsAppAction +//| PickObject of V3d +| PlaceSurface of V3d +| ScalarsColorLegendMessage of FalseColorLegendApp.Action +| ColorCorrectionMessage of ColorCorrectionProperties.Action +| SetHomePosition +| TranslationMessage of TranslationApp.Action +| SetPreTrafo of string module SurfaceUtils = @@ -187,6 +188,12 @@ module SurfaceUtils = let sghs = surfaces |> IndexList.toList + |> List.filter(fun s -> + let dirExists = File.Exists s.importPath + if dirExists |> not then + Log.error "[Surface.Sg] could not find %s" s.importPath + dirExists + ) |> List.map loadObject let sgObjects = @@ -306,6 +313,38 @@ module SurfaceApp = { model with surfaces = { model.surfaces with flat = flat' } } + let changeOBJImportDirectories (model:SurfaceModel) (selectedPaths:list) = + + let surfaces = + model.surfaces.flat + |> HashMap.toList + |> List.map(fun (_,v) -> + let s = (v |> Leaf.toSurface) + let newPath = + selectedPaths + |> List.map(fun p -> + let name = p |> IO.Path.GetFileName + match name = s.name with + | true -> Some p + | false -> None + ) + |> List.choose( fun np -> np) + match newPath.IsEmpty with + | true -> s + | false -> { s with importPath = newPath.Head } + ) + + let flat' = + surfaces + |> List.choose(fun x -> + let f = (fun _ -> x |> Leaf.Surfaces) + Groups.updateLeaf' x.guid f model.surfaces + |> HashMap.tryFind x.guid + |> Option.map(fun leaf -> (x.guid,leaf) |> hmapsingle)) + |> List.fold HashMap.union HashMap.empty + + { model with surfaces = { model.surfaces with flat = flat' } } + let update @@ -394,7 +433,12 @@ module SurfaceApp = | paths -> let selectedPaths = paths |> List.choose Files.tryDirectoryExists changeImportDirectories model selectedPaths - + | ChangeOBJImportDirectories sl -> + match sl with + | [] -> model + | paths -> + let selectedPaths = paths |> List.choose( fun p -> if File.Exists p then Some p else None) + changeOBJImportDirectories model selectedPaths | GroupsMessage msg -> let groups = GroupsApp.update model.surfaces msg @@ -720,7 +764,7 @@ module SurfaceApp = let isobj = Path.GetExtension path = ".obj" // // yield i - if ((Directory.Exists path) |> not || (path |> Files.isSurfaceFolder |> not)) && (isobj |> not) then + if (((Directory.Exists path) |> not || (path |> Files.isSurfaceFolder |> not)) && (isobj |> not)) || ( isobj && (File.Exists path) |> not ) then yield i [ clazz "exclamation red icon" //Dialogs.onChooseDirectory key ChangeImportDirectory; diff --git a/src/PRo3D.SimulatedViews/Viewplanner/Rover-Model.fs b/src/PRo3D.SimulatedViews/Viewplanner/Rover-Model.fs index bb9a1823..c9d3c9d3 100644 --- a/src/PRo3D.SimulatedViews/Viewplanner/Rover-Model.fs +++ b/src/PRo3D.SimulatedViews/Viewplanner/Rover-Model.fs @@ -32,8 +32,73 @@ type Intrinsics = { vignettingMap : string } +[] +module Intrinsics = + + let current = 0 + let read0 = + json { + let! horizontalFieldOfView = Json.read "horizontalFieldOfView" + let! verticalFieldOfView = Json.read "verticalFieldOfView" + let! horizontalResolution = Json.read "horizontalResolution" + let! verticalResolution = Json.read "verticalResolution" + let! horizontalPrinciplePoint = Json.read "horizontalPrinciplePoint" + let! verticalPrinciplePoint = Json.read "verticalPrinciplePoint" + let! horizontalFocalLengthPerPixel = Json.read "horizontalFocalLengthPerPixel" + let! verticalFocalLengthPerPixel = Json.read "verticalFocalLengthPerPixel" + let! horizontalDistortionMap = Json.read "horizontalDistortionMap" + let! verticalDistortionMap = Json.read "verticalDistortionMap" + let! vignettingMap = Json.read "vignettingMap" + + return + { + // version = current + horizontalFieldOfView = horizontalFieldOfView + verticalFieldOfView = verticalFieldOfView + horizontalResolution = horizontalResolution + verticalResolution = verticalResolution + horizontalPrinciplePoint = horizontalPrinciplePoint + verticalPrinciplePoint = verticalPrinciplePoint + horizontalFocalLengthPerPixel = horizontalFocalLengthPerPixel + verticalFocalLengthPerPixel = verticalFocalLengthPerPixel + horizontalDistortionMap = horizontalDistortionMap + verticalDistortionMap = verticalDistortionMap + vignettingMap = vignettingMap + } + } + +type Intrinsics with + static member FromJson(_ : Intrinsics) = + json { + //let! v = Json.read "version" + //match v with + //| 0 -> return! Intrinsics.read0 + //| _ -> + // return! v + // |> sprintf "don't know version %A of Intrinsics" + // |> Json.error + return! Intrinsics.read0 + } + static member ToJson(x : Intrinsics) = + json { + //do! Json.write "version" x.version + do! Json.write "horizontalFieldOfView" x.horizontalFieldOfView + do! Json.write "verticalFieldOfView" x.verticalFieldOfView + do! Json.write "horizontalResolution" x.horizontalResolution + do! Json.write "verticalResolution" x.verticalResolution + do! Json.write "horizontalPrinciplePoint" x.horizontalPrinciplePoint + do! Json.write "verticalPrinciplePoint" x.verticalPrinciplePoint + do! Json.write "horizontalFocalLengthPerPixel" x.horizontalFocalLengthPerPixel + do! Json.write "verticalFocalLengthPerPixel" x.verticalFocalLengthPerPixel + do! Json.write "horizontalDistortionMap" x.horizontalDistortionMap + do! Json.write "verticalDistortionMap" x.verticalDistortionMap + do! Json.write "vignettingMap" x.vignettingMap + } + [] type Extrinsics = { + //version : int + position : V3d camUp : V3d camLookAt : V3d @@ -41,24 +106,65 @@ type Extrinsics = { } module Extrinsics = + let current = 0 let transformed (t:M44d) (ex:Extrinsics)= { + //version = ex.version position = t.TransformPos ex.position camUp = t.TransformDir ex.camUp camLookAt= t.TransformDir ex.camLookAt box = ex.box.Transformed t } + let read0 = + json { + let! position = Json.read "position" + let! camUp = Json.read "camUp" + let! camLookAt = Json.read "camLookAt" + let! box = Json.read "box" + + return + { + //version = current + position = position |> V3d.Parse + camUp = camUp |> V3d.Parse + camLookAt = camLookAt |> V3d.Parse + box = box |> Box3d.Parse + } + } + +type Extrinsics with + static member FromJson(_ : Extrinsics) = + json { + //let! v = Json.read "version" + //match v with + //| 0 -> return! Extrinsics.read0 + //| _ -> + // return! v + // |> sprintf "don't know version %A of Extrinsics" + // |> Json.error + return! Extrinsics.read0 + } + static member ToJson(x : Extrinsics) = + json { + //do! Json.write "version" x.version + do! Json.write "position" (x.position.ToString()) + do! Json.write "camUp" (x.camUp.ToString()) + do! Json.write "camLookAt" (x.camLookAt.ToString()) + do! Json.write "box" (x.box.ToString()) + } + type InstrumentType = - | WACL // Left Wide Angle Camera (PanCam) - | WACR // Right Wide Angle Camera (PanCam) - | HRC // High Resolution Camera - | WISDOM // penetrating radar - | CLUPI // Close UP Imager microscope - | ISEM // Infrared Spectrometer - | DRILL // Drill - | RIM // Rover Inspection Mirror - | Undefined + | WACL = 0 // Left Wide Angle Camera (PanCam) + | WACR = 1 // Right Wide Angle Camera (PanCam) + | HRC = 2 // High Resolution Camera + | WISDOM = 3 // penetrating radar + | CLUPI = 4 // Close UP Imager microscope + | ISEM = 5 // Infrared Spectrometer + | DRILL = 6 // Drill + | RIM = 7 // Rover Inspection Mirror + | Undefined = 8 + //type ArnoldSnapshot = { // location : V3d // forward : V3d @@ -137,6 +243,7 @@ type InstrumentType = [] type Instrument = { + //version : int id : string iType : InstrumentType calibratedFocalLengths : list @@ -147,6 +254,57 @@ type Instrument = { index : int } +[] +module Instrument = + + let current = 0 + let read0 = + json { + let! id = Json.read "id" + let! iType = Json.read "iType" + let! calibratedFocalLengths = Json.read "calibratedFocalLengths" + let! focal = Json.readWith Ext.fromJson "focal" + let! intrinsics = Json.read "intrinsics" + let! extrinsics = Json.read "extrinsics" + let! index = Json.read "index" + + return + { + //version = current + id = id + iType = iType |> enum + calibratedFocalLengths = calibratedFocalLengths + focal = focal + intrinsics = intrinsics + extrinsics = extrinsics + index = index + } + } + +type Instrument with + static member FromJson(_ : Instrument) = + json { + //let! v = Json.read "version" + //match v with + //| 0 -> return! Instrument.read0 + //| _ -> + // return! v + // |> sprintf "don't know version %A of Instrument" + // |> Json.error + return! Instrument.read0 + } + static member ToJson(x : Instrument) = + json { + //do! Json.write "version" x.version + do! Json.write "id" x.id + do! Json.write "iType" (x.iType |> int) + do! Json.write "calibratedFocalLengths" x.calibratedFocalLengths + do! Json.writeWith Ext.toJson "focal" x.focal + do! Json.write "intrinsics" x.intrinsics + do! Json.write "extrinsics" x.extrinsics + do! Json.write "index" x.index + } + type AxisAngleUpdate = { roverId : string axisId : string @@ -176,6 +334,32 @@ type Axis = { } module Axis = + let current = 0 + let read0 = + json { + let! id = Json.read "id" + let! description = Json.read "description" + let! startPoint = Json.read "startPoint" + let! endPoint = Json.read "endPoint" + let! index = Json.read "index" + let! angle = Json.readWith Ext.fromJson "angle" + let! degreesMapped = Json.read "degreesMapped" + let! degreesNegated = Json.read "degreesNegated" + + return + { + //version = current + id = id + description = description + startPoint = startPoint |> V3d.Parse + endPoint = endPoint |> V3d.Parse + index = index + angle = angle + degreesMapped = degreesMapped + degreesNegated = degreesNegated + } + + } module Mapping = let to180 (min : float) (max : float) (v : float) = @@ -201,7 +385,30 @@ module Axis = else v - +type Axis with + static member FromJson(_ : Axis) = + json { + //let! v = Json.read "version" + //match v with + //| 0 -> return! Axis.read0 + //| _ -> + // return! v + // |> sprintf "don't know version %A of Axis" + // |> Json.error + return! Axis.read0 + } + static member ToJson(x : Axis) = + json { + //do! Json.write "version" x.version + do! Json.write "id" x.id + do! Json.write "description" x.description + do! Json.write "startPoint" (x.startPoint.ToString()) + do! Json.write "endPoint" (x.endPoint.ToString()) + do! Json.write "index" x.index + do! Json.writeWith Ext.toJson "angle" x.angle + do! Json.write "degreesMapped" x.degreesMapped + do! Json.write "degreesNegated" x.degreesNegated + } [] type Rover = { @@ -213,6 +420,55 @@ type Rover = { box : Box3d } +module Rover = + let current = 0 + let read0 = + json { + let! id = Json.read "id" + let! platform2Ground = Json.read "platform2Ground" + let! wheelPositions = Json.readWith Ext.fromJson,Ext> "wheelPositions" + let! inst = Json.read "instruments" + let instruments = inst |> List.map(fun (a : Instrument) -> (a.id, a)) |> HashMap.ofList + let! axes = Json.read "axes" + let axes = axes |> List.map(fun (a : Axis) -> (a.id, a)) |> HashMap.ofList + let! box = Json.read "box" + + return + { + //version = current + id = id + platform2Ground = platform2Ground |> M44d.Parse + wheelPositions = wheelPositions + instruments = instruments + axes = axes + box = box |> Box3d.Parse + } + + } + +type Rover with + static member FromJson(_ : Rover) = + json { + //let! v = Json.read "version" + //match v with + //| 0 -> return! Rover.read0 + //| _ -> + // return! v + // |> sprintf "don't know version %A of Rover" + // |> Json.error + return! Rover.read0 + } + static member ToJson(x : Rover) = + json { + //do! Json.write "version" x.version + do! Json.write "id" x.id + do! Json.write "platform2Ground" (x.platform2Ground.ToString()) + do! Json.writeWith (Ext.toJson,Ext>) "wheelPositions" x.wheelPositions + do! Json.write "instruments" (x.instruments |> HashMap.toList |> List.map snd) + do! Json.write "axes" (x.axes |> HashMap.toList |> List.map snd) + do! Json.write "box" (x.box.ToString()) + } + [] type RoverModel = { rovers : HashMap @@ -398,6 +654,7 @@ type ViewPlan = { module ViewPlan = let current = 0 + let initialAngle = { value = 0.0 min = 0.0 @@ -406,6 +663,83 @@ module ViewPlan = format = "{0:0.0}" } + let read0 = + json { + let! id = Json.read "id" + let! name = Json.read "name" + let! position = Json.read "position" + let! lookAt = Json.read "lookAt" + + let! (viewerState : list) = Json.read "viewerState" + let viewerState = viewerState |> List.map V3d.Parse + let viewerState = CameraView(viewerState.[0],viewerState.[1],viewerState.[2],viewerState.[3], viewerState.[4]) + + let! vectorsVisible = Json.read "vectorsVisible" + let! rover = Json.read "rover" + let! roverTrafo = Json.read "roverTrafo" + let! isVisible = Json.read "isVisible" + + let! selectedInstrument = Json.read "selectedInstrument" + let! selectedAxis = Json.read "selectedAxis" + let! currentAngle = Json.readWith Ext.fromJson "currentAngle" + + return + { + version = current + id = id |> Guid + name = name + + position = position |> V3d.Parse + lookAt = lookAt |> V3d.Parse + viewerState = viewerState + rover = rover + roverTrafo = roverTrafo |> Trafo3d.Parse + + isVisible = isVisible + + vectorsVisible = vectorsVisible + + selectedInstrument = selectedInstrument + selectedAxis = selectedAxis + + currentAngle = currentAngle + } + } + +type ViewPlan with + static member FromJson(_ : ViewPlan) = + json { + let! v = Json.read "version" + match v with + | 0 -> return! ViewPlan.read0 + | _ -> + return! v + |> sprintf "don't know version %A of ViewPlan" + |> Json.error + } + static member ToJson(x : ViewPlan) = + json { + do! Json.write "version" x.version + do! Json.write "id" x.id + do! Json.write "name" x.name + do! Json.write "position" (x.position.ToString()) + do! Json.write "lookAt" (x.lookAt.ToString()) + let camView = x.viewerState + let camView = + [camView.Sky; camView.Location; camView.Forward; camView.Up ; camView.Right] + |> List.map(fun x -> x.ToString()) + do! Json.write "viewerState" camView + + do! Json.write "rover" x.rover + do! Json.write "roverTrafo" (x.roverTrafo.ToString()) + do! Json.write "isVisible" x.isVisible + do! Json.write "vectorsVisible" x.vectorsVisible + + do! Json.write "selectedInstrument" x.selectedInstrument + do! Json.write "selectedAxis" x.selectedAxis + do! Json.writeWith Ext.toJson "currentAngle" x.currentAngle + } + //let initial = { // version = current // id = Guid.Empty @@ -428,7 +762,7 @@ module ViewPlan = type ViewPlanModel = { version : int viewPlans : HashMap - selectedViewPlan : Option + selectedViewPlan : Option working : list // pos + lookAt roverModel : RoverModel instrumentCam : CameraView @@ -438,7 +772,7 @@ type ViewPlanModel = { } module ViewPlanModel = - let current = 0 + let current = 1 let initPixTex = let res = V2i((int)1024, (int)1024) @@ -483,13 +817,37 @@ module ViewPlanModel = } } + let readV1 = + json { + + let! viewPlans = Json.read "viewPlans" + let viewPlans = viewPlans |> List.map(fun (a : ViewPlan) -> (a.id, a)) |> HashMap.ofList + + return { + version = current + viewPlans = viewPlans + selectedViewPlan = None + working = list.Empty + roverModel = RoverModel.initial + instrumentCam = CameraView.lookAt V3d.Zero V3d.One V3d.OOI + instrumentFrustum = Frustum.perspective 60.0 0.1 10000.0 1.0 + footPrint = initFootPrint + } + } + type ViewPlanModel with static member FromJson(_:ViewPlanModel) = json { let! v = Json.read "version" match v with | 0 -> return! ViewPlanModel.readV0 - | _ -> return! v |> sprintf "don't know version %d of Traverse" |> Json.error + | 1 -> return! ViewPlanModel.readV1 + | _ -> return! v |> sprintf "don't know version %d of ViewPlanModel" |> Json.error + } + static member ToJson (x : ViewPlanModel) = + json { + do! Json.write "version" x.version + do! Json.write "viewPlans" (x.viewPlans |> HashMap.toList |> List.map snd) } module FootPrint = @@ -503,11 +861,12 @@ module FootPrint = let fpPath = getFootprintsPath scenePath match vp.selectedViewPlan with - | Some v -> + | Some id -> + let selectedVp = vp.viewPlans |> HashMap.find id let now = DateTime.Now - let roverName = v.rover.id + let roverName = selectedVp.rover.id let instrumentName = - match v.selectedInstrument with + match selectedVp.selectedInstrument with | Some i -> i.id | None -> "" @@ -524,7 +883,7 @@ module FootPrint = ) let width, height = - match v.selectedInstrument with + match selectedVp.selectedInstrument with | Some i -> let horRes = i.intrinsics.horizontalResolution/uint32(2) let vertRes = i.intrinsics.verticalResolution/uint32(2) @@ -541,20 +900,20 @@ module FootPrint = } let calibration = { - instrumentPlatformXmlFileName = v.rover.id + ".xml" + instrumentPlatformXmlFileName = selectedVp.rover.id + ".xml" instrumentPlatformXmlFileVersion = 1.0 } let roverInfo = { - position = v.position - lookAtPosition = v.lookAt - placementTrafo = v.roverTrafo + position = selectedVp.position + lookAtPosition = selectedVp.lookAt + placementTrafo = selectedVp.roverTrafo } - let panAx = v.rover.axes.TryFind "Pan Axis" |> Option.map(fun x -> x.angle.value ) + let panAx = selectedVp.rover.axes.TryFind "Pan Axis" |> Option.map(fun x -> x.angle.value ) let panVal = match panAx with | Some av -> av | None -> 1.0 - let tiltAx = v.rover.axes.TryFind "Tilt Axis" |> Option.map(fun x -> x.angle.value ) + let tiltAx = selectedVp.rover.axes.TryFind "Tilt Axis" |> Option.map(fun x -> x.angle.value ) let tiltVal = match tiltAx with | Some av -> av | None -> 1.0 let angles = { panAxis = panVal @@ -562,7 +921,7 @@ module FootPrint = } let focal = - match v.selectedInstrument with + match selectedVp.selectedInstrument with | Some i -> i.focal.value | None -> 1.0 @@ -601,11 +960,6 @@ module FootPrint = let updateFootprint (instrument:Instrument) (roverpos:V3d) (model:ViewPlanModel) = - let id = - match model.selectedViewPlan with - | Some vp -> Some vp.id - | None -> None - let res = V2i((int)instrument.intrinsics.horizontalResolution, (int)instrument.intrinsics.verticalResolution) //let image = PixImage(Col.Format.RGB,res).ToPixImage(Col.Format.RGB) @@ -619,7 +973,7 @@ module FootPrint = let fp = { - vpId = id + vpId = model.selectedViewPlan isVisible = true projectionMatrix = (model.instrumentFrustum |> Frustum.projTrafo).Forward instViewMatrix = model.instrumentCam.ViewTrafo.Forward diff --git a/src/PRo3D.SimulatedViews/Viewplanner/ViewPlanApp.fs b/src/PRo3D.SimulatedViews/Viewplanner/ViewPlanApp.fs index d137c4c1..4e8fd118 100644 --- a/src/PRo3D.SimulatedViews/Viewplanner/ViewPlanApp.fs +++ b/src/PRo3D.SimulatedViews/Viewplanner/ViewPlanApp.fs @@ -87,14 +87,11 @@ module ViewPlanApp = let m = {vp with roverModel = roverModel} // update selected view plan and viewplans match vp.selectedViewPlan with - | Some v -> - // TODO v5: refactor - let viewPlans = updateViewPlans v (vp.viewPlans) - let m = { m with viewPlans = viewPlans |> HashMap.map (fun _ _ -> v) } - - match roverModel.selectedRover with - | Some r -> { m with selectedViewPlan = Some { v with rover = r }} - | None -> m + | Some id -> + // TODO v5: refactor + let selectedVp = vp.viewPlans |> HashMap.find id + let viewPlans = updateViewPlans selectedVp (vp.viewPlans) + { m with viewPlans = viewPlans |> HashMap.map (fun _ _ -> selectedVp) } | None -> m let getPlaneNormalSign @@ -132,18 +129,22 @@ module ViewPlanApp = adaptive { let! selected = vp.selectedViewPlan let fail = (uint32(1024), uint32(1024)) - + match selected with - | AdaptiveSome v -> - let! selectedI = v.selectedInstrument - match selectedI with - | AdaptiveSome i -> - let! intrinsics = i.intrinsics - let horRes = intrinsics.horizontalResolution - let vertRes = intrinsics.verticalResolution - return (horRes, vertRes) - | AdaptiveNone -> return fail - | AdaptiveNone -> return fail + | Some id -> + let! selectedVp = vp.viewPlans |> AMap.tryFind id + match selectedVp with + | Some v -> + let! selectedI = v.selectedInstrument + match selectedI with + | AdaptiveSome i -> + let! intrinsics = i.intrinsics + let horRes = intrinsics.horizontalResolution + let vertRes = intrinsics.verticalResolution + return (horRes, vertRes) + | AdaptiveNone -> return fail + | None -> return fail + | None -> return fail } let trafoFromTranslatedBase @@ -367,7 +368,7 @@ module ViewPlanApp = { model with viewPlans = HashMap.add newViewPlan.id newViewPlan model.viewPlans working = List.Empty - selectedViewPlan = Some newViewPlan + selectedViewPlan = Some newViewPlan.id } let removeViewPlan @@ -402,15 +403,18 @@ module ViewPlanApp = let view = CameraView.look extr.position extr.camLookAt.Normalized extr.camUp.Normalized let ifrustum = Frustum.perspective (hfov.DegreesFromGons()) frust.near frust.far (resh/resv) + let i' = { i with extrinsics = extr} + let vp' = { vp with selectedInstrument = Some i';} + let m' = { - model with + model with instrumentCam = view // CHECK-merge { model.instrumentCam with view = view } instrumentFrustum = ifrustum } //Log.line "%A" model.instrumentCam.ViewTrafo - let f = FootPrint.updateFootprint i vp.position m' + let f = FootPrint.updateFootprint i' vp.position m' f,m' | None -> @@ -431,31 +435,32 @@ module ViewPlanApp = | Some i -> Some (r.instruments |> HashMap.find i.id) | None -> None - let vp = { vp with rover = r; selectedInstrument = i } - let m = { - model with - selectedViewPlan = Some vp - viewPlans = model.viewPlans |> HashMap.alter vp.id id - } + let vp' = { vp with rover = r; selectedInstrument = i } + let m = { model with + selectedViewPlan = Some vp'.id + viewPlans = model.viewPlans |> HashMap.alter vp'.id (Option.map(fun _ -> vp')) //id + roverModel = roverModel} - let fp, m = updateInstrumentCam vp m fp - fp, { m with roverModel = roverModel } + let fp, m = updateInstrumentCam vp' m fp + fp, m //{ m with roverModel = roverModel } let selectViewplan outerModel _footprint id model = let viewPlanToSelect = model.viewPlans |> HashMap.tryFind id - match viewPlanToSelect with - | Some viewplan -> - match model.selectedViewPlan with - | Some selected when selected.id = viewplan.id -> + match viewPlanToSelect, model.selectedViewPlan with + | Some a, Some b -> + if a.id = b then outerModel, { model with selectedViewPlan = None } - | _ -> + else let footPrint = Optic.get _footprint outerModel - let footPrint, model = updateInstrumentCam viewplan model footPrint + let footPrint, model = updateInstrumentCam a model footPrint let newOuterModel = Optic.set _footprint footPrint outerModel - newOuterModel, { model with selectedViewPlan = Some viewplan } - | None -> - Log.line "[ViewplanApp] viewplan with selected id does not exist" - outerModel, model + newOuterModel, { model with selectedViewPlan = Some a.id } + | Some a, None -> + let footPrint = Optic.get _footprint outerModel + let footPrint, model = updateInstrumentCam a model footPrint + let newOuterModel = Optic.set _footprint footPrint outerModel + newOuterModel, { model with selectedViewPlan = Some a.id } + | None, _ -> outerModel, model //let update (model : ViewPlanModel) (camState:CameraControllerState) (action : Action) = let update @@ -544,7 +549,7 @@ module ViewPlanApp = | RemoveViewPlan id -> let vp' = match model.selectedViewPlan with - | Some v -> if v.id = id then None else Some v + | Some v -> if v = id then None else Some v | None -> None let vps = removeViewPlan model.viewPlans id @@ -552,37 +557,40 @@ module ViewPlanApp = | SelectInstrument i -> match model.selectedViewPlan with - | Some vp -> - let newVp = { vp with selectedInstrument = i } + | Some id-> + let selectedVp = model.viewPlans |> HashMap.find id + let newVp = { selectedVp with selectedInstrument = i } let fp = Optic.get _footprint outerModel let fp', m' = updateInstrumentCam newVp model fp let newOuterModel = Optic.set _footprint fp' outerModel let viewPlans = model.viewPlans |> HashMap.add newVp.id newVp - newOuterModel, { m' with selectedViewPlan = Some newVp; viewPlans = viewPlans } - | None -> outerModel, model + newOuterModel, { m' with viewPlans = viewPlans } + | None -> outerModel, model | SelectAxis a -> match model.selectedViewPlan with - | Some vp -> - let newVp = { vp with selectedAxis = a } + | Some id -> + let selectedVp = model.viewPlans |> HashMap.find id + let newVp = { selectedVp with selectedAxis = a } let viewPlans = model.viewPlans |> HashMap.add newVp.id newVp - outerModel, { model with selectedViewPlan = Some newVp; viewPlans = viewPlans } + outerModel, { model with viewPlans = viewPlans } | None -> outerModel, model | ChangeAngle (id,a) -> match model.selectedViewPlan with - | Some vp -> - match vp.rover.axes.TryFind id with + | Some vpid -> + let selectedVp = model.viewPlans |> HashMap.find vpid + match selectedVp.rover.axes.TryFind id with | Some ax -> let angle = Utilities.PRo3DNumeric.update ax.angle a //printfn "numeric angle: %A" angle let ax' = { ax with angle = angle } - let rover = { vp.rover with axes = (vp.rover.axes |> HashMap.update id (fun _ -> ax')) } - let vp' = { vp with rover = rover; currentAngle = angle; } + let rover = { selectedVp.rover with axes = (selectedVp.rover.axes |> HashMap.update id (fun _ -> ax')) } + let vp' = { selectedVp with rover = rover; currentAngle = angle; } let angleUpdate = { roverId = vp'.rover.id @@ -595,30 +603,33 @@ module ViewPlanApp = let roverModel = RoverApp.updateAnglePlatform angleUpdate model.roverModel let fp = Optic.get _footprint outerModel - let fp, model = updateRovers model roverModel vp' fp + let fp, model' = updateRovers model roverModel vp' fp let newOuterModel = Optic.set _footprint fp outerModel + + //let viewPlans = model.viewPlans |> HashMap.add vp'.id vp' - newOuterModel, model + newOuterModel, model' //{ model with viewPlans = viewPlans } | None -> outerModel, model | None -> outerModel, model | ChangeFocal (id, f) -> match model.selectedViewPlan with - | Some vp -> - match vp.selectedInstrument with + | Some vpid -> + let selectedVp = model.viewPlans |> HashMap.find vpid + match selectedVp.selectedInstrument with | Some inst -> let focal = Numeric.update inst.focal f let inst' = { inst with focal = focal } let instruments' = - vp.rover.instruments + selectedVp.rover.instruments |> HashMap.update id (fun x -> match x with | Some _ -> inst' | None -> failwith "instrument not found") - let rover = { vp.rover with instruments = instruments'} - let vp' = { vp with rover = rover; selectedInstrument = Some inst' } + let rover = { selectedVp.rover with instruments = instruments'} + let vp' = { selectedVp with rover = rover; selectedInstrument = Some inst' } let focusUpdate = { roverId = vp'.rover.id @@ -633,14 +644,15 @@ module ViewPlanApp = newOuterModel, m' | None -> outerModel, model - | None -> outerModel, model + | None -> outerModel, model | SetVPName t -> match model.selectedViewPlan with - | Some vp -> - let vp' = {vp with name = t} + | Some vpid -> + let selectedVp = model.viewPlans |> HashMap.find vpid + let vp' = {selectedVp with name = t} let viewPlans = model.viewPlans |> HashMap.add vp'.id vp' - outerModel, {model with selectedViewPlan = Some vp'; viewPlans = viewPlans } + outerModel, {model with viewPlans = viewPlans } | None -> outerModel, model | ToggleFootprint -> @@ -873,21 +885,24 @@ module ViewPlanApp = let viewPlans = aset { + let! selected' = model.selectedViewPlan for _,vp in model.viewPlans do let! showVectors = vp.isVisible if showVectors then yield drawPlatformCoordinateCross vp near length thickness cam - let! selected = model.selectedViewPlan - let id = vp.id + + let selected = + match selected' with + | Some sel -> sel = vp.id + | None -> false let gg = - match selected with - | AdaptiveSome s -> + if selected then let sg = drawRover vp near length thickness cam model.roverModel - let condition = ~~(s.id = id) - sg |> Sg.onOff (condition) - | AdaptiveNone -> + //let condition = ~~(sid = vp.id) + sg //|> Sg.onOff (condition) + else Sg.empty yield gg } |> Sg.set @@ -924,7 +939,7 @@ module ViewPlanApp = Incremental.div listAttributes ( alist { - + let! selected = m.selectedViewPlan let viewPlans = m.viewPlans |> AMap.toASetValues @@ -947,17 +962,14 @@ module ViewPlanApp = amap { yield clazz "large cube middle aligned icon"; - //TODO refactor broken adaptive behavior, use hashmap lookup instead of viewplan copy - let! selected = m.selectedViewPlan let color = match selected with - | AdaptiveSome sel -> - Log.line "selection changed in view" - (if sel.id = vpid then C4b.VRVisGreen else C4b.White) - | AdaptiveNone -> - C4b.White + | Some sel -> + AVal.constant (if sel = vpid then C4b.VRVisGreen else C4b.Gray) + | None -> AVal.constant C4b.Gray - let bgc = color |> Html.ofC4b |> sprintf "color: %s" + let! c = color + let bgc = sprintf "color: %s" (Html.ofC4b c) yield style bgc yield onClick (fun _ -> SelectViewPlan vpid) @@ -1089,10 +1101,17 @@ module ViewPlanApp = let viewRoverProperties lifter (fpVisible:aval) (model : AdaptiveViewPlanModel) = - model.selectedViewPlan - |> AVal.map(fun x -> - match x with - | AdaptiveSome x -> viewRoverProperties' x.rover x fpVisible |> UI.map lifter - | AdaptiveNone -> div[][] |> UI.map lifter) + adaptive { + let! guid = model.selectedViewPlan + let empty = div[][] |> UI.map lifter + + match guid with + | Some id -> + let! vp = model.viewPlans |> AMap.tryFind id + match vp with + | Some x -> return (viewRoverProperties' x.rover x fpVisible |> UI.map lifter) + | None -> return empty + | None -> return empty + } diff --git a/src/PRo3D.Viewer/Viewer-Model.fs b/src/PRo3D.Viewer/Viewer-Model.fs index 3a4cf922..78e0b03b 100644 --- a/src/PRo3D.Viewer/Viewer-Model.fs +++ b/src/PRo3D.Viewer/Viewer-Model.fs @@ -202,7 +202,8 @@ type Scene = { module Scene = - let current = 2 //20211611 ... added traverse and sequenced bookmarks and comparison app + //let current = 2 //20211611 ... added traverse and sequenced bookmarks and comparison app + let current = 3 //20220306 ... added viewPlans let read0 = json { let! cameraView = Json.readWith Ext.fromJson "cameraView" @@ -356,6 +357,63 @@ module Scene = } } + // added viewPlans + let read3 = + json { + let! cameraView = Json.readWith Ext.fromJson "cameraView" + let! navigationMode = Json.read "navigationMode" + let! exploreCenter = Json.read "exploreCenter" + + let! interactionMode = Json.read "interactionMode" + let! surfaceModel = Json.read "surfaceModel" + let! config = Json.read "config" + let! scenePath = Json.read "scenePath" + let! referenceSystem = Json.read "referenceSystem" + let! bookmarks = Json.read "bookmarks" + let! viewPlans = Json.read "viewPlans" + let! dockConfig = Json.read "dockConfig" + let! (comparisonApp : option) = Json.tryRead "comparisonApp" + let! scaleBars = Json.read "scaleBars" + let! sceneObjectsModel = Json.read "sceneObjectsModel" + let! geologicSurfacesModel = Json.read "geologicSurfacesModel" + let! sequencedBookmarks = Json.tryRead "sequencedBookmarks" + let! screenshotModel = Json.tryRead "screenshotModel" + let! traverse = Json.tryRead "traverses" + //let! viewplans = Json.tryRead "viewplans" + + return + { + version = current + + cameraView = cameraView + navigationMode = navigationMode |> enum + exploreCenter = exploreCenter |> V3d.Parse + + interaction = interactionMode |> enum + surfacesModel = surfaceModel + config = config + scenePath = scenePath + referenceSystem = referenceSystem + bookmarks = bookmarks + + viewPlans = viewPlans + dockConfig = dockConfig |> Serialization.jsonSerializer.UnPickleOfString + closedPages = List.empty + firstImport = false + userFeedback = String.Empty + feedbackThreads = ThreadPool.empty + scaleBars = scaleBars + sceneObjectsModel = sceneObjectsModel + geologicSurfacesModel = geologicSurfacesModel + + traverses = traverse |> Option.defaultValue(TraverseModel.initial) + sequencedBookmarks = if sequencedBookmarks.IsSome then sequencedBookmarks.Value else SequencedBookmarks.initial + comparisonApp = if comparisonApp.IsSome then comparisonApp.Value else ComparisonApp.init + + screenshotModel = screenshotModel |> Option.defaultValue(ScreenshotModel.initial) + } + } + type Scene with static member FromJson (_ : Scene) = json { @@ -364,6 +422,7 @@ type Scene with | 0 -> return! Scene.read0 | 1 -> return! Scene.read1 | 2 -> return! Scene.read2 + | 3 -> return! Scene.read3 | _ -> return! v |> sprintf "don't know version %A of Scene" @@ -383,6 +442,7 @@ type Scene with do! Json.write "scenePath" x.scenePath do! Json.write "referenceSystem" x.referenceSystem do! Json.write "bookmarks" x.bookmarks + do! Json.write "viewPlans" x.viewPlans do! Json.write "comparisonApp" (x.comparisonApp) do! Json.write "dockConfig" (x.dockConfig |> Serialization.jsonSerializer.PickleToString) do! Json.write "scaleBars" x.scaleBars diff --git a/src/PRo3D.Viewer/Viewer/SnapshotSg.fs b/src/PRo3D.Viewer/Viewer/SnapshotSg.fs index b8eca0e8..089b5f7d 100644 --- a/src/PRo3D.Viewer/Viewer/SnapshotSg.fs +++ b/src/PRo3D.Viewer/Viewer/SnapshotSg.fs @@ -73,6 +73,19 @@ open PRo3D.ViewerApp /// PRo3D Sg for batch rendering (snapshots) module SnapshotSg = + + let isViewPlanVisible (m:AdaptiveModel) = + adaptive { + let! id = m.scene.viewPlans.selectedViewPlan + match id with + | Some v -> + let! vp = m.scene.viewPlans.viewPlans |> AMap.tryFind v + match vp with + | Some selVp -> return! selVp.isVisible + | None -> return false + | None -> return false + } + /// creaste simple sg for debugging purposes let createDebugSg (m:AdaptiveModel) = let camera = AVal.map2 (fun v f -> Camera.create v f) m.navigation.camera.view m.frustum @@ -108,6 +121,7 @@ module SnapshotSg = | _ -> true ) + let vpVisible = isViewPlanVisible m let selected = m.scene.surfacesModel.surfaces.singleSelectLeaf let refSystem = m.scene.referenceSystem let grouped = @@ -123,7 +137,7 @@ module SnapshotSg = surface.globalBB refSystem m.footPrint - m.scene.viewPlans.selectedViewPlan + vpVisible usehighlighting filterTexture allowFootprint ) diff --git a/src/PRo3D.Viewer/Viewer/Viewer-IO.fs b/src/PRo3D.Viewer/Viewer/Viewer-IO.fs index 2b6de315..8215a8f1 100644 --- a/src/PRo3D.Viewer/Viewer/Viewer-IO.fs +++ b/src/PRo3D.Viewer/Viewer/Viewer-IO.fs @@ -240,15 +240,18 @@ module ViewerIO = { m with linkingModel = m.linkingModel |> LinkingApp.initFeatures m.minervaModel.data.features } let loadLastFootPrint (m:Model) = - let fp = + let fp, viewPlans' = m.scene.viewPlans.selectedViewPlan |> Option.bind(fun vp -> - vp.selectedInstrument |> Option.map(fun instr -> (instr,vp))) + let selectedVp = m.scene.viewPlans.viewPlans |> HashMap.find vp + selectedVp.selectedInstrument |> Option.map(fun instr -> (instr,vp))) |> Option.map(fun (instr, vp) -> - FootPrint.updateFootprint instr vp.position m.scene.viewPlans) - |> Option.defaultValue ViewPlanModel.initFootPrint - - { m with footPrint = fp } + let selectedVp = m.scene.viewPlans.viewPlans |> HashMap.find vp + let footPrint = FootPrint.updateFootprint instr selectedVp.position m.scene.viewPlans + ViewPlanApp.updateInstrumentCam selectedVp m.scene.viewPlans footPrint) + |> Option.defaultValue (ViewPlanModel.initFootPrint, m.scene.viewPlans) + + { m with scene = {m.scene with viewPlans = viewPlans'}; footPrint = fp } let saveEverything (path:string) (m:Model) = diff --git a/src/PRo3D.Viewer/Viewer/Viewer-Utils.fs b/src/PRo3D.Viewer/Viewer/Viewer-Utils.fs index 4eab1fb8..9a1d313b 100644 --- a/src/PRo3D.Viewer/Viewer/Viewer-Utils.fs +++ b/src/PRo3D.Viewer/Viewer/Viewer-Utils.fs @@ -230,7 +230,7 @@ module ViewerUtils = (globalBB : aval) (refsys : AdaptiveReferenceSystem) (fp : AdaptiveFootPrint) - (vp : aval>) + (vpVisible : aval) (useHighlighting : aval) (filterTexture : aval) (allowFootprint : bool) = @@ -308,15 +308,7 @@ module ViewerUtils = adaptive { if not allowFootprint then return false else - let! vp = vp - match vp with - | AdaptiveSome vp -> - return! vp.isVisible - | _ -> - return false - //let! visible = fp.isVisible - //let! id = fp.vpId - //return (vp.IsSome && visible) + return! vpVisible } let footprintViewProj = @@ -443,7 +435,8 @@ module ViewerUtils = Effect.compose [ PRo3D.Base.Shader.footprintV |> toEffect - stableTrafo |> toEffect + //stableTrafo |> toEffect + PRo3D.Base.Shader.stableTrafo' |> toEffect triangleFilterX |> toEffect Shader.OPCFilter.improvedDiffuseTexture |> toEffect @@ -463,6 +456,18 @@ module ViewerUtils = ] + let isViewPlanVisible (m:AdaptiveModel) = + adaptive { + let! id = m.scene.viewPlans.selectedViewPlan + match id with + | Some v -> + let! vp = m.scene.viewPlans.viewPlans |> AMap.tryFind v + match vp with + | Some selVp -> return! selVp.isVisible + | None -> return false + | None -> return false + } + //TODO TO refactor screenshot specific let getSurfacesScenegraphs (m:AdaptiveModel) = let sgGrouped = m.scene.surfacesModel.sgGrouped @@ -471,6 +476,8 @@ module ViewerUtils = let usehighlighting = true |> AVal.constant //m.scene.config.useSurfaceHighlighting let selected = m.scene.surfacesModel.surfaces.singleSelectLeaf let refSystem = m.scene.referenceSystem + let vpVisible = isViewPlanVisible m + let grouped = sgGrouped |> AList.map( fun x -> ( x @@ -485,7 +492,7 @@ module ViewerUtils = sf.globalBB refSystem m.footPrint - m.scene.viewPlans.selectedViewPlan + vpVisible usehighlighting m.filterTexture true) |> AMap.toASet @@ -536,6 +543,7 @@ module ViewerUtils = | _ -> true ) + let vpVisible = isViewPlanVisible m let selected = m.scene.surfacesModel.surfaces.singleSelectLeaf let refSystem = m.scene.referenceSystem let grouped = @@ -551,7 +559,7 @@ module ViewerUtils = surface.globalBB refSystem m.footPrint - m.scene.viewPlans.selectedViewPlan + vpVisible usehighlighting filterTexture allowFootprint ) diff --git a/src/PRo3D.Viewer/Viewer/Viewer.fs b/src/PRo3D.Viewer/Viewer/Viewer.fs index 53cc8bbd..b3f1a77e 100644 --- a/src/PRo3D.Viewer/Viewer/Viewer.fs +++ b/src/PRo3D.Viewer/Viewer/Viewer.fs @@ -508,7 +508,8 @@ module ViewerApp = let surfaceModel = match msg with - | SurfaceAppAction.ChangeImportDirectories _ -> + | SurfaceAppAction.ChangeImportDirectories _ + | SurfaceAppAction.ChangeOBJImportDirectories _ -> model.scene.surfacesModel |> SceneLoader.prepareSurfaceModel runtime signature model.scene.scenePath | _ -> @@ -623,8 +624,7 @@ module ViewerApp = | RoverMessage msg,_,_ -> let roverModel = RoverApp.update m.scene.viewPlans.roverModel msg - let viewPlanModel = ViewPlanApp.updateViewPlanFroAdaptiveRover roverModel m.scene.viewPlans - { m with scene = { m.scene with viewPlans = viewPlanModel }} + { m with scene = { m.scene with viewPlans = {m.scene.viewPlans with roverModel = roverModel }}} | ViewPlanMessage msg,_,_ -> let model, viewPlanModel = ViewPlanApp.update m.scene.viewPlans msg _navigation _footprint m.scene.scenePath m @@ -641,8 +641,8 @@ module ViewerApp = | _ -> m.animations - { m with - scene = { m.scene with viewPlans = viewPlanModel } + { model with + scene = { model.scene with viewPlans = viewPlanModel } footPrint = model.footPrint animations = animations } diff --git a/src/PRo3D.Viewer/Viewer/ViewerGUI.fs b/src/PRo3D.Viewer/Viewer/ViewerGUI.fs index b0b71ffb..abf81a56 100644 --- a/src/PRo3D.Viewer/Viewer/ViewerGUI.fs +++ b/src/PRo3D.Viewer/Viewer/ViewerGUI.fs @@ -210,14 +210,16 @@ module Gui = let textOverlaysInstrumentView (m : AdaptiveViewPlanModel) = let instrument = adaptive { - let! vp = m.selectedViewPlan - let! inst = - match Adaptify.FSharp.Core.Missing.AdaptiveOption.toOption vp with - | Some v -> AVal.bindAdaptiveOption v.selectedInstrument "No instrument selected" (fun a -> a.id) - | None -> AVal.constant("") - - return inst - } + let! id = m.selectedViewPlan + match id with + | Some v -> + let! vp = m.viewPlans |> AMap.tryFind v + match vp with + | Some selVp -> + return! (AVal.bindAdaptiveOption selVp.selectedInstrument "No instrument selected" (fun a -> a.id)) + | None -> return "" + | None -> return "" + } div [js "oncontextmenu" "event.preventDefault();"] [ yield div [clazz "ui"; style "position: absolute; top: 15px; left: 15px; float:left" ] [ //arrowOverlay @@ -414,6 +416,24 @@ module Gui = ] } + Incremental.div(AttributeMap.Empty) ui |> UI.map SurfaceActions + + let fixAllBrokenOBJPaths = + let jsLocateOBJDialog = + "top.aardvark.dialog.showOpenDialog({title:'Select directory to locate OBJs', filters: [{ name: 'OBJs (*.obj)', extensions: ['obj']}], properties: ['openFile']}).then(result => {top.aardvark.processEvent('__ID__', 'onchoose', result.filePaths);});" + + let ui = + alist { + yield + div [ + clazz "ui item"; + Dialogs.onChooseFiles SurfaceAppAction.ChangeOBJImportDirectories; + clientEvent "onclick" jsLocateOBJDialog + ][ + text "Locate OBJ Surfaces" + ] + } + Incremental.div(AttributeMap.Empty) ui |> UI.map SurfaceActions let jsOpenAnnotationFileDialog = @@ -546,6 +566,8 @@ module Gui = div [ clazz "menu"] [ //fixes all broken surface import paths fixAllBrokenPaths + //fixes all broken obj import paths + fixAllBrokenOBJPaths let jsOpenOldAnnotationsFileDialogue = "top.aardvark.dialog.showOpenDialog({title:'Import legacy annotations from PRo3D 1.0' , filters: [{ name: 'Annotations (*.xml)', extensions: ['xml']},], properties: ['openFile']}).then(result => {top.aardvark.processEvent('__ID__', 'onchoose', result.filePaths);});"