Repository: Chaf-Libraries/Ilum Branch: main Commit: 1d26a51ac02f Files: 546 Total size: 5.4 MB Directory structure: gitextract_7jr9t7gk/ ├── .clang-format ├── .clang-tidy ├── .github/ │ └── workflows/ │ ├── windows_dev.yml │ └── windows_main.yml ├── .gitignore ├── .gitmodules ├── Asset/ │ ├── BuildIn/ │ │ ├── ImGui.spv.asset │ │ ├── MaterialBall.asset │ │ ├── animation.icon.asset │ │ ├── equirectangular_to_cubemap.shader.asset │ │ ├── mesh.preview.asset │ │ ├── pipeline.icon.asset │ │ ├── prefab.icon.asset │ │ └── scene.icon.asset │ ├── Meta/ │ │ └── README.md │ ├── MustacheTemplate/ │ │ └── Reflection.mustache │ ├── NRD_Data/ │ │ ├── NRD.rg │ │ └── Sample/ │ │ ├── Texture2D_89.dds │ │ ├── blur_buffer.csv │ │ ├── gIn_Diff.dds │ │ ├── gIn_Diff_History.dds │ │ ├── gIn_Normal_Roughness.dds │ │ ├── gIn_ObjectMotion.dds │ │ ├── gIn_Prev_AccumSpeeds_MaterialID.dds │ │ ├── gIn_Prev_Normal_Roughness.dds │ │ ├── gIn_Prev_ViewZ.dds │ │ ├── gIn_ViewZ.dds │ │ ├── history_fix_buffer.csv │ │ ├── post_blur_buffer.csv │ │ └── temporal_accumulate_buffer.csv │ └── SPD/ │ └── metals/ │ ├── Ag.eta.spd │ ├── Ag.k.spd │ ├── Al.eta.spd │ ├── Al.k.spd │ ├── AlAs.eta.spd │ ├── AlAs.k.spd │ ├── AlSb.eta.spd │ ├── AlSb.k.spd │ ├── Au.eta.spd │ ├── Au.k.spd │ ├── Be.eta.spd │ ├── Be.k.spd │ ├── Cr.eta.spd │ ├── Cr.k.spd │ ├── CsI.eta.spd │ ├── CsI.k.spd │ ├── Cu.eta.spd │ ├── Cu.k.spd │ ├── Cu2O.eta.spd │ ├── Cu2O.k.spd │ ├── CuO.eta.spd │ ├── CuO.k.spd │ ├── Hg.eta.spd │ ├── Hg.k.spd │ ├── HgTe.eta.spd │ ├── HgTe.k.spd │ ├── Ir.eta.spd │ ├── Ir.k.spd │ ├── K.eta.spd │ ├── K.k.spd │ ├── KBr.eta.spd │ ├── KBr.k.spd │ ├── KCl.eta.spd │ ├── KCl.k.spd │ ├── Li.eta.spd │ ├── Li.k.spd │ ├── MgO.eta.spd │ ├── MgO.k.spd │ ├── Mo.eta.spd │ ├── Mo.k.spd │ ├── NaCl.eta.spd │ ├── NaCl.k.spd │ ├── Nb.eta.spd │ ├── Nb.k.spd │ ├── Rh.eta.spd │ ├── Rh.k.spd │ ├── Se-e.eta.spd │ ├── Se-e.k.spd │ ├── Se.eta.spd │ ├── Se.k.spd │ ├── SiC.eta.spd │ ├── SiC.k.spd │ ├── SiO.eta.spd │ ├── SiO.k.spd │ ├── SnTe.eta.spd │ ├── SnTe.k.spd │ ├── Ta.eta.spd │ ├── Ta.k.spd │ ├── Te-e.eta.spd │ ├── Te-e.k.spd │ ├── Te.eta.spd │ ├── Te.k.spd │ ├── ThF4.eta.spd │ ├── ThF4.k.spd │ ├── TiC.eta.spd │ ├── TiC.k.spd │ ├── TiN.eta.spd │ ├── TiN.k.spd │ ├── TiO2-e.eta.spd │ ├── TiO2-e.k.spd │ ├── TiO2.eta.spd │ ├── TiO2.k.spd │ ├── VC.eta.spd │ ├── VC.k.spd │ ├── VN.eta.spd │ ├── VN.k.spd │ ├── W.eta.spd │ ├── W.k.spd │ ├── a-C.eta.spd │ ├── a-C.k.spd │ ├── a-SiH.eta.spd │ ├── a-SiH.k.spd │ ├── d-C.eta.spd │ └── d-C.k.spd ├── Doc/ │ ├── Material.drawio │ ├── RHI.drawio │ ├── RenderGraph.drawio │ ├── Resource.drawio │ ├── Scene.drawio │ └── Shader.drawio ├── LICENSE.txt ├── README.md ├── Source/ │ ├── Editor/ │ │ ├── Editor.cpp │ │ ├── Editor.hpp │ │ ├── ImGui/ │ │ │ ├── ImGuiContext.cpp │ │ │ └── ImGuiContext.hpp │ │ ├── Precompile.hpp │ │ └── Widget.hpp │ ├── Engine/ │ │ ├── Engine.cpp │ │ ├── Engine.hpp │ │ ├── Precompile.hpp │ │ └── main.cpp │ ├── External/ │ │ ├── dxc/ │ │ │ └── inc/ │ │ │ ├── d3d12shader.h │ │ │ ├── dxcapi.h │ │ │ ├── dxcerrors.h │ │ │ └── dxcisense.h │ │ ├── imgui_tools/ │ │ │ ├── IconsFontAwesome/ │ │ │ │ └── IconsFontAwesome5.h │ │ │ ├── imgui_impl_glfw.cpp │ │ │ ├── imgui_impl_glfw.h │ │ │ ├── imguizmo/ │ │ │ │ ├── GraphEditor.cpp │ │ │ │ ├── GraphEditor.h │ │ │ │ ├── ImCurveEdit.cpp │ │ │ │ ├── ImCurveEdit.h │ │ │ │ ├── ImGradient.cpp │ │ │ │ ├── ImGradient.h │ │ │ │ ├── ImGuizmo.cpp │ │ │ │ ├── ImGuizmo.h │ │ │ │ ├── ImSequencer.cpp │ │ │ │ ├── ImSequencer.h │ │ │ │ └── ImZoomSlider.h │ │ │ ├── imnodes/ │ │ │ │ ├── imnodes.cpp │ │ │ │ ├── imnodes.h │ │ │ │ └── imnodes_internal.h │ │ │ ├── implot/ │ │ │ │ ├── implot.cpp │ │ │ │ ├── implot.h │ │ │ │ ├── implot_demo.cpp │ │ │ │ ├── implot_internal.h │ │ │ │ └── implot_items.cpp │ │ │ └── xmake.lua │ │ ├── mustache/ │ │ │ └── mustache.hpp │ │ ├── slang/ │ │ │ ├── docs/ │ │ │ │ ├── 64bit-type-support.md │ │ │ │ ├── README.md │ │ │ │ ├── api-users-guide.md │ │ │ │ ├── building.md │ │ │ │ ├── command-line-slangc.md │ │ │ │ ├── cpu-target.md │ │ │ │ ├── cuda-target.md │ │ │ │ ├── doc-system.md │ │ │ │ ├── faq.md │ │ │ │ ├── language-guide.md │ │ │ │ ├── layout.md │ │ │ │ ├── linking.md │ │ │ │ ├── nvapi-support.md │ │ │ │ ├── repro.md │ │ │ │ ├── shader-playground.md │ │ │ │ ├── stdlib-doc.md │ │ │ │ ├── target-compatibility.md │ │ │ │ └── wave-intrinsics.md │ │ │ ├── prelude/ │ │ │ │ ├── slang-cpp-host-prelude.h │ │ │ │ ├── slang-cpp-prelude.h │ │ │ │ ├── slang-cpp-scalar-intrinsics.h │ │ │ │ ├── slang-cpp-types.h │ │ │ │ ├── slang-cuda-prelude.h │ │ │ │ ├── slang-hlsl-prelude.h │ │ │ │ └── slang-llvm.h │ │ │ ├── slang-com-helper.h │ │ │ ├── slang-com-ptr.h │ │ │ ├── slang-gfx.h │ │ │ ├── slang-tag-version.h │ │ │ └── slang.h │ │ └── xmake.lua │ ├── Plugin/ │ │ ├── Editor/ │ │ │ ├── AnimationEditor.cpp │ │ │ ├── Console.cpp │ │ │ ├── Hierarchy.cpp │ │ │ ├── Inspector.cpp │ │ │ ├── MainMenu.cpp │ │ │ ├── MaterialGraphEditor.cpp │ │ │ ├── MeshEditor.cpp │ │ │ ├── RenderGraphEditor.cpp │ │ │ ├── ResourceBrowser.cpp │ │ │ ├── SceneView.cpp │ │ │ ├── ShaderToy.cpp │ │ │ └── xmake.lua │ │ ├── Geometry/ │ │ │ ├── Parameterization/ │ │ │ │ ├── MinimumSurface.cpp │ │ │ │ └── Tutte.cpp │ │ │ ├── Subdivision/ │ │ │ │ └── Loop.cpp │ │ │ └── xmake.lua │ │ ├── Importer/ │ │ │ ├── Assimp.cpp │ │ │ ├── DDS.cpp │ │ │ ├── STB.cpp │ │ │ └── xmake.lua │ │ ├── MaterialNode/ │ │ │ ├── BSDF/ │ │ │ │ ├── ConductorMaterial.cpp │ │ │ │ ├── DielectricMaterial.cpp │ │ │ │ ├── DiffuseMaterial.cpp │ │ │ │ ├── DisneyMaterial.cpp │ │ │ │ ├── MaskedMaterial.cpp │ │ │ │ ├── MixMaterial.cpp │ │ │ │ ├── PrincipledMaterial.cpp │ │ │ │ ├── ThinDielectricMaterial.cpp │ │ │ │ └── Utils/ │ │ │ │ └── SPDLoader.hpp │ │ │ ├── Converter/ │ │ │ │ ├── Calculate.cpp │ │ │ │ ├── SRGBToLinear.cpp │ │ │ │ ├── VectorCalculate.cpp │ │ │ │ ├── VectorMerge.cpp │ │ │ │ └── VectorSplit.cpp │ │ │ ├── IMaterialNode.hpp │ │ │ ├── Input/ │ │ │ │ ├── RGB.cpp │ │ │ │ └── SurfaceInteraction.cpp │ │ │ ├── Output/ │ │ │ │ └── MaterialOutput.cpp │ │ │ ├── Texture/ │ │ │ │ └── ImageTexture.cpp │ │ │ └── xmake.lua │ │ ├── RHI/ │ │ │ ├── CUDA/ │ │ │ │ ├── Buffer.cpp │ │ │ │ ├── Buffer.hpp │ │ │ │ ├── CUDA.cpp │ │ │ │ ├── Command.cpp │ │ │ │ ├── Command.hpp │ │ │ │ ├── Descriptor.cpp │ │ │ │ ├── Descriptor.hpp │ │ │ │ ├── Device.cpp │ │ │ │ ├── Device.hpp │ │ │ │ ├── Frame.cpp │ │ │ │ ├── Frame.hpp │ │ │ │ ├── Fwd.hpp │ │ │ │ ├── PipelineState.cpp │ │ │ │ ├── PipelineState.hpp │ │ │ │ ├── Profiler.cpp │ │ │ │ ├── Profiler.hpp │ │ │ │ ├── Queue.cpp │ │ │ │ ├── Queue.hpp │ │ │ │ ├── Sampler.cpp │ │ │ │ ├── Sampler.hpp │ │ │ │ ├── Shader.cpp │ │ │ │ ├── Shader.hpp │ │ │ │ ├── Synchronization.cpp │ │ │ │ ├── Synchronization.hpp │ │ │ │ ├── Texture.cpp │ │ │ │ └── Texture.hpp │ │ │ ├── Vulkan/ │ │ │ │ ├── AccelerationStructure.cpp │ │ │ │ ├── AccelerationStructure.hpp │ │ │ │ ├── Buffer.cpp │ │ │ │ ├── Buffer.hpp │ │ │ │ ├── Command.cpp │ │ │ │ ├── Command.hpp │ │ │ │ ├── Definitions.cpp │ │ │ │ ├── Definitions.hpp │ │ │ │ ├── Descriptor.cpp │ │ │ │ ├── Descriptor.hpp │ │ │ │ ├── Device.cpp │ │ │ │ ├── Device.hpp │ │ │ │ ├── Frame.cpp │ │ │ │ ├── Frame.hpp │ │ │ │ ├── Fwd.cpp │ │ │ │ ├── Fwd.hpp │ │ │ │ ├── PipelineState.cpp │ │ │ │ ├── PipelineState.hpp │ │ │ │ ├── Profiler.cpp │ │ │ │ ├── Profiler.hpp │ │ │ │ ├── Queue.cpp │ │ │ │ ├── Queue.hpp │ │ │ │ ├── RenderTarget.cpp │ │ │ │ ├── RenderTarget.hpp │ │ │ │ ├── Sampler.cpp │ │ │ │ ├── Sampler.hpp │ │ │ │ ├── Shader.cpp │ │ │ │ ├── Shader.hpp │ │ │ │ ├── Swapchain.cpp │ │ │ │ ├── Swapchain.hpp │ │ │ │ ├── Synchronization.cpp │ │ │ │ ├── Synchronization.hpp │ │ │ │ ├── Texture.cpp │ │ │ │ ├── Texture.hpp │ │ │ │ ├── Vulkan.cpp │ │ │ │ └── vk_mem_alloc.h │ │ │ └── xmake.lua │ │ ├── RenderPass/ │ │ │ ├── Bloom.cpp │ │ │ ├── CompositePass.cpp │ │ │ ├── CopyImageRGBA16.cpp │ │ │ ├── Debug.cpp │ │ │ ├── Deferred.cpp │ │ │ ├── FXAA.cpp │ │ │ ├── Forward.cpp │ │ │ ├── ForwardPlus.cpp │ │ │ ├── IBL.cpp │ │ │ ├── IPass.hpp │ │ │ ├── PassData.hpp │ │ │ ├── PathTracing.cpp │ │ │ ├── Present.cpp │ │ │ ├── RayTracedAO.cpp │ │ │ ├── RayTracingReflection.cpp │ │ │ ├── RayTracingShadow.cpp │ │ │ ├── SSAO.cpp │ │ │ ├── ShadowMapPass.cpp │ │ │ ├── SkyboxPass.cpp │ │ │ ├── Tonemapping.cpp │ │ │ ├── Triangle.cpp │ │ │ ├── VisibilityBufferVisualization.cpp │ │ │ ├── VisibilityGeometryPass.cpp │ │ │ ├── VisibilityLightingPass.cpp │ │ │ └── xmake.lua │ │ └── xmake.lua │ ├── Runtime/ │ │ ├── Core/ │ │ │ ├── Private/ │ │ │ │ ├── JobSystem.cpp │ │ │ │ ├── Log.cpp │ │ │ │ ├── Path.cpp │ │ │ │ ├── Plugin.cpp │ │ │ │ ├── Time.cpp │ │ │ │ ├── Variant.cpp │ │ │ │ └── Window.cpp │ │ │ ├── Public/ │ │ │ │ └── Core/ │ │ │ │ ├── API.hpp │ │ │ │ ├── Container.hpp │ │ │ │ ├── Core.hpp │ │ │ │ ├── Delegates.hpp │ │ │ │ ├── Hash.hpp │ │ │ │ ├── Input.hpp │ │ │ │ ├── JobSystem.hpp │ │ │ │ ├── Log.hpp │ │ │ │ ├── PRECOMPILE.HPP │ │ │ │ ├── Path.hpp │ │ │ │ ├── Plugin.hpp │ │ │ │ ├── Time.hpp │ │ │ │ ├── Variant.hpp │ │ │ │ └── Window.hpp │ │ │ └── xmake.lua │ │ ├── Geometry/ │ │ │ ├── Private/ │ │ │ │ ├── AABB.cpp │ │ │ │ ├── Mesh/ │ │ │ │ │ ├── EMesh.cpp │ │ │ │ │ ├── FMesh.cpp │ │ │ │ │ ├── HEMesh.cpp │ │ │ │ │ └── Mesh.cpp │ │ │ │ └── MeshProcess.cpp │ │ │ ├── Public/ │ │ │ │ └── Geometry/ │ │ │ │ ├── AABB.hpp │ │ │ │ ├── Mesh/ │ │ │ │ │ ├── EMesh.hpp │ │ │ │ │ ├── FMesh.hpp │ │ │ │ │ ├── HEMesh.hpp │ │ │ │ │ └── Mesh.hpp │ │ │ │ ├── MeshProcess.hpp │ │ │ │ ├── Meshlet.hpp │ │ │ │ └── Precompile.hpp │ │ │ └── xmake.lua │ │ ├── RHI/ │ │ │ ├── Private/ │ │ │ │ ├── RHIAccelerationStructure.cpp │ │ │ │ ├── RHIBuffer.cpp │ │ │ │ ├── RHICommand.cpp │ │ │ │ ├── RHIContext.cpp │ │ │ │ ├── RHIDescriptor.cpp │ │ │ │ ├── RHIDevice.cpp │ │ │ │ ├── RHIFrame.cpp │ │ │ │ ├── RHIPipelineState.cpp │ │ │ │ ├── RHIProfiler.cpp │ │ │ │ ├── RHIQueue.cpp │ │ │ │ ├── RHIRenderTarget.cpp │ │ │ │ ├── RHISampler.cpp │ │ │ │ ├── RHIShader.cpp │ │ │ │ ├── RHISwapchain.cpp │ │ │ │ ├── RHISynchronization.cpp │ │ │ │ └── RHITexture.cpp │ │ │ └── Public/ │ │ │ └── RHI/ │ │ │ ├── Fwd.hpp │ │ │ ├── RHIAccelerationStructure.hpp │ │ │ ├── RHIBuffer.hpp │ │ │ ├── RHICommand.hpp │ │ │ ├── RHIContext.hpp │ │ │ ├── RHIDefinitions.hpp │ │ │ ├── RHIDescriptor.hpp │ │ │ ├── RHIDevice.hpp │ │ │ ├── RHIFrame.hpp │ │ │ ├── RHIPipelineState.hpp │ │ │ ├── RHIProfiler.hpp │ │ │ ├── RHIQueue.hpp │ │ │ ├── RHIRenderTarget.hpp │ │ │ ├── RHISampler.hpp │ │ │ ├── RHIShader.hpp │ │ │ ├── RHISwapchain.hpp │ │ │ ├── RHISynchronization.hpp │ │ │ └── RHITexture.hpp │ │ ├── Render/ │ │ │ ├── Material/ │ │ │ │ ├── Private/ │ │ │ │ │ └── Material/ │ │ │ │ │ ├── MaterialCompiler.cpp │ │ │ │ │ ├── MaterialData.cpp │ │ │ │ │ ├── MaterialGraph.cpp │ │ │ │ │ ├── MaterialNode.cpp │ │ │ │ │ └── Spectrum.cpp │ │ │ │ └── Public/ │ │ │ │ └── Material/ │ │ │ │ ├── MaterialCompiler.hpp │ │ │ │ ├── MaterialData.hpp │ │ │ │ ├── MaterialGraph.hpp │ │ │ │ ├── MaterialNode.hpp │ │ │ │ └── Spectrum.hpp │ │ │ ├── RenderGraph/ │ │ │ │ ├── Private/ │ │ │ │ │ ├── RenderGraph.cpp │ │ │ │ │ ├── RenderGraphBlackboard.cpp │ │ │ │ │ ├── RenderGraphBuilder.cpp │ │ │ │ │ └── RenderPass.cpp │ │ │ │ └── Public/ │ │ │ │ └── RenderGraph/ │ │ │ │ ├── Impl/ │ │ │ │ │ └── RenderGraphBuilder.inl │ │ │ │ ├── Precompile.hpp │ │ │ │ ├── RenderGraph.hpp │ │ │ │ ├── RenderGraphBlackboard.hpp │ │ │ │ ├── RenderGraphBuilder.hpp │ │ │ │ └── RenderPass.hpp │ │ │ ├── Renderer/ │ │ │ │ ├── Private/ │ │ │ │ │ └── Renderer.cpp │ │ │ │ └── Public/ │ │ │ │ └── Renderer/ │ │ │ │ ├── RenderData.hpp │ │ │ │ └── Renderer.hpp │ │ │ ├── ShaderCompiler/ │ │ │ │ ├── Private/ │ │ │ │ │ ├── ShaderBuilder.cpp │ │ │ │ │ ├── ShaderCompiler.cpp │ │ │ │ │ └── SpirvReflection.cpp │ │ │ │ └── Public/ │ │ │ │ └── ShaderCompiler/ │ │ │ │ ├── Precompile.hpp │ │ │ │ ├── ShaderBuilder.hpp │ │ │ │ ├── ShaderCompiler.hpp │ │ │ │ └── SpirvReflection.hpp │ │ │ └── xmake.lua │ │ ├── Resource/ │ │ │ ├── Private/ │ │ │ │ └── Resource/ │ │ │ │ ├── Importer.cpp │ │ │ │ ├── Resource/ │ │ │ │ │ ├── Animation.cpp │ │ │ │ │ ├── Material.cpp │ │ │ │ │ ├── Mesh.cpp │ │ │ │ │ ├── Prefab.cpp │ │ │ │ │ ├── RenderPipeline.cpp │ │ │ │ │ ├── Scene.cpp │ │ │ │ │ ├── SkinnedMesh.cpp │ │ │ │ │ ├── Texture2D.cpp │ │ │ │ │ └── TextureCube.cpp │ │ │ │ ├── Resource.cpp │ │ │ │ └── ResourceManager.cpp │ │ │ └── Public/ │ │ │ └── Resource/ │ │ │ ├── Importer.hpp │ │ │ ├── Resource/ │ │ │ │ ├── Animation.hpp │ │ │ │ ├── Material.hpp │ │ │ │ ├── Mesh.hpp │ │ │ │ ├── Prefab.hpp │ │ │ │ ├── RenderPipeline.hpp │ │ │ │ ├── Scene.hpp │ │ │ │ ├── SkinnedMesh.hpp │ │ │ │ ├── Texture2D.hpp │ │ │ │ └── TextureCube.hpp │ │ │ ├── Resource.hpp │ │ │ └── ResourceManager.hpp │ │ ├── Scene/ │ │ │ ├── Private/ │ │ │ │ └── Scene/ │ │ │ │ ├── Component.cpp │ │ │ │ ├── Components/ │ │ │ │ │ ├── AllComponents.cpp │ │ │ │ │ ├── Camera/ │ │ │ │ │ │ ├── Camera.cpp │ │ │ │ │ │ ├── OrthographicCamera.cpp │ │ │ │ │ │ └── PerspectiveCamera.cpp │ │ │ │ │ ├── Light/ │ │ │ │ │ │ ├── DirectionalLight.cpp │ │ │ │ │ │ ├── EnvironmentLight.cpp │ │ │ │ │ │ ├── Light.cpp │ │ │ │ │ │ ├── PointLight.cpp │ │ │ │ │ │ ├── RectLight.cpp │ │ │ │ │ │ └── SpotLight.cpp │ │ │ │ │ ├── Renderable/ │ │ │ │ │ │ ├── MeshRenderer.cpp │ │ │ │ │ │ ├── Renderable.cpp │ │ │ │ │ │ └── SkinnedMeshRenderer.cpp │ │ │ │ │ └── Transform.cpp │ │ │ │ ├── Node.cpp │ │ │ │ └── Scene.cpp │ │ │ └── Public/ │ │ │ └── Scene/ │ │ │ ├── Component.hpp │ │ │ ├── Components/ │ │ │ │ ├── AllComponents.hpp │ │ │ │ ├── Camera/ │ │ │ │ │ ├── Camera.hpp │ │ │ │ │ ├── OrthographicCamera.hpp │ │ │ │ │ └── PerspectiveCamera.hpp │ │ │ │ ├── Light/ │ │ │ │ │ ├── DirectionalLight.hpp │ │ │ │ │ ├── EnvironmentLight.hpp │ │ │ │ │ ├── Light.hpp │ │ │ │ │ ├── PointLight.hpp │ │ │ │ │ ├── RectLight.hpp │ │ │ │ │ └── SpotLight.hpp │ │ │ │ ├── Renderable/ │ │ │ │ │ ├── MeshRenderer.hpp │ │ │ │ │ ├── Renderable.hpp │ │ │ │ │ └── SkinnedMeshRenderer.hpp │ │ │ │ └── Transform.hpp │ │ │ ├── Node.hpp │ │ │ └── Scene.hpp │ │ └── xmake.lua │ ├── Shaders/ │ │ ├── AmbientOcclusion/ │ │ │ ├── RayTracedAO.hlsl │ │ │ └── SSAO.hlsl │ │ ├── Attribute.hlsli │ │ ├── Common.hlsli │ │ ├── Complex.hlsli │ │ ├── Editor/ │ │ │ ├── AnimationEditor/ │ │ │ │ └── DrawSkeleton.hlsl │ │ │ ├── AnimationEditor.hlsl │ │ │ ├── MaterialEditor.hlsl │ │ │ ├── MeshEditor.hlsl │ │ │ └── Preview/ │ │ │ ├── Material.hlsl │ │ │ └── Mesh.hlsl │ │ ├── GlobalIllumination/ │ │ │ └── DDGI.hlsl │ │ ├── ImGui.hlsl │ │ ├── Interaction.hlsli │ │ ├── Light.hlsli │ │ ├── Material/ │ │ │ ├── BSDF/ │ │ │ │ ├── BSDF.hlsli │ │ │ │ ├── ConductorBSDF.hlsli │ │ │ │ ├── DielectricBSDF.hlsli │ │ │ │ ├── DiffuseBSDF.hlsli │ │ │ │ ├── DisneyBSDF.hlsli │ │ │ │ ├── LayeredBSDF.hlsli │ │ │ │ └── ThinDielectricBSDF.hlsli │ │ │ ├── Fresnel.hlsli │ │ │ ├── Material/ │ │ │ │ ├── ConductorMaterial.hlsli │ │ │ │ ├── DielectricMaterial.hlsli │ │ │ │ ├── DiffuseMaterial.hlsli │ │ │ │ ├── DisneyMaterial.hlsli │ │ │ │ ├── MaskedMaterial.hlsli │ │ │ │ ├── MixMaterial.hlsli │ │ │ │ └── ThinDielectricMaterial.hlsli │ │ │ ├── Material.hlsli │ │ │ ├── Scattering.hlsli │ │ │ └── Template.material.hlsli │ │ ├── Material.hlsli │ │ ├── MaterialResource.hlsli │ │ ├── Math.hlsli │ │ ├── PostProcess/ │ │ │ ├── Bloom.hlsl │ │ │ ├── FXAA.hlsl │ │ │ └── Tonemapping.hlsl │ │ ├── PreProcess/ │ │ │ ├── EquirectangularToCubemap.hlsl │ │ │ └── GGXBRDFLUT.hlsl │ │ ├── Random.hlsli │ │ ├── RayTracing/ │ │ │ ├── BidirectionalPathTracing.hlsl │ │ │ └── PathTracing.hlsl │ │ ├── Reflection/ │ │ │ └── RayTracedReflection.hlsl │ │ ├── RenderPath/ │ │ │ ├── VisibilityBufferVisualization.hlsl │ │ │ ├── VisibilityGeometryPass.hlsl │ │ │ └── VisibilityLightingPass.hlsl │ │ ├── Shading/ │ │ │ ├── Composite.hlsl │ │ │ ├── IBL.hlsl │ │ │ └── Skybox.hlsl │ │ ├── Shadow/ │ │ │ ├── CascadeShadowMap.hlsl │ │ │ ├── OmniShadowMap.hlsl │ │ │ ├── RayTracedShadow.hlsl │ │ │ └── ShadowMap.hlsl │ │ ├── SphericalHarmonic.hlsli │ │ ├── Tonemapper.hlsli │ │ └── UpdateBoneMatrics.hlsl │ └── xmake.lua ├── imgui.ini ├── xmake/ │ ├── module.lua │ ├── plugin.lua │ ├── rule.lua │ └── shader.lua └── xmake.lua ================================================ FILE CONTENTS ================================================ ================================================ FILE: .clang-format ================================================ --- Language: Cpp # BasedOnStyle: LLVM AccessModifierOffset: -2 AlignAfterOpenBracket: Align AlignConsecutiveAssignments: true AlignConsecutiveDeclarations: true AlignEscapedNewlines: Left AlignOperands: true AlignTrailingComments: true AllowAllParametersOfDeclarationOnNextLine: true AllowShortBlocksOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: None AllowShortIfStatementsOnASingleLine: false AllowShortLoopsOnASingleLine: false AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: true BinPackArguments: true BinPackParameters: true BraceWrapping: AfterClass: true AfterControlStatement: true AfterEnum: true AfterFunction: true AfterNamespace: true AfterObjCDeclaration: true AfterStruct: true AfterUnion: true AfterExternBlock: true BeforeCatch: true BeforeElse: true IndentBraces: false SplitEmptyFunction: false SplitEmptyRecord: false SplitEmptyNamespace: false BreakBeforeBinaryOperators: None BreakBeforeBraces: Custom BreakBeforeInheritanceComma: false BreakBeforeTernaryOperators: false BreakConstructorInitializersBeforeComma: false BreakConstructorInitializers: AfterColon BreakAfterJavaFieldAnnotations: false BreakStringLiterals: true ColumnLimit: 0 CommentPragmas: '^ IWYU pragma:' CompactNamespaces: false ConstructorInitializerAllOnOneLineOrOnePerLine: true ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 Cpp11BracedListStyle: true DerivePointerAlignment: false DisableFormat: false ExperimentalAutoDetectBinPacking: false FixNamespaceComments: true ForEachMacros: - foreach - Q_FOREACH - BOOST_FOREACH IncludeBlocks: Preserve IncludeIsMainRegex: '(Test)?$' IndentCaseLabels: true IndentPPDirectives: AfterHash IndentWidth: 4 IndentWrappedFunctionNames: true JavaScriptQuotes: Leave JavaScriptWrapImports: true KeepEmptyLinesAtTheStartOfBlocks: false MacroBlockBegin: '' MacroBlockEnd: '' MaxEmptyLinesToKeep: 1 NamespaceIndentation: None ObjCBinPackProtocolList: Auto ObjCBlockIndentWidth: 2 ObjCSpaceAfterProperty: false ObjCSpaceBeforeProtocolList: true PenaltyBreakAssignment: 2 PenaltyBreakBeforeFirstCallParameter: 19 PenaltyBreakComment: 300 PenaltyBreakFirstLessLess: 120 PenaltyBreakString: 1000 PenaltyExcessCharacter: 1000000 PenaltyReturnTypeOnItsOwnLine: 60 PointerAlignment: Right ReflowComments: true SortIncludes: true SortUsingDeclarations: true SpaceAfterCStyleCast: true SpaceAfterTemplateKeyword: true SpaceBeforeAssignmentOperators: true SpaceBeforeCtorInitializerColon: true SpaceBeforeInheritanceColon: true SpaceBeforeParens: ControlStatements SpaceBeforeRangeBasedForLoopColon: true SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 8 SpacesInAngles: false SpacesInContainerLiterals: false SpacesInCStyleCastParentheses: false SpacesInParentheses: false SpacesInSquareBrackets: false Standard: Cpp11 TabWidth: 4 UseTab: ForIndentation ... ================================================ FILE: .clang-tidy ================================================ Checks: '-*, misc-throw-by-value-catch-by-reference, misc-misplaced-const, misc-unconventional-assign-operator, misc-redundant-expression, misc-static-assert, misc-unconventional-assign-operator, misc-uniqueptr-reset-release, misc-unused-alias-decls, misc-unused-parameters, misc-unused-using-decls, modernize-avoid-bind, modernize-loop-convert, modernize-make-shared, modernize-make-unique, modernize-raw-string-literal, modernize-redundant-void-arg, modernize-replace-auto-ptr, modernize-replace-random-shuffle, modernize-use-bool-literals, modernize-use-nullptr, modernize-use-using, modernize-use-equals-default, modernize-use-equals-delete, performance-faster-string-find, performance-for-range-copy, performance-implicit-conversion-in-loop, performance-inefficient-algorithm, performance-inefficient-vector-operation, performance-move-constructor-init, performance-no-automatic-move, performance-trivially-destructible, performance-unnecessary-copy-initialization, readability-avoid-const-params-in-decls, readability-const-return-type, readability-container-size-empty, readability-convert-member-functions-to-static, readability-delete-null-pointer, readability-deleted-default, readability-make-member-function-const, readability-misplaced-array-index, readability-non-const-parameter, readability-qualified-auto, readability-redundant-access-specifiers, readability-redundant-control-flow, readability-redundant-function-ptr-dereference, readability-redundant-smartptr-get, readability-redundant-string-cstr, readability-redundant-string-init, readability-static-definition-in-anonymous-namespace, readability-string-compare, readability-uniqueptr-delete-release, readability-redundant-member-init, readability-simplify-subscript-expr, readability-simplify-boolean-expr, readability-inconsistent-declaration-parameter-name, readability-identifier-naming, bugprone-undelegated-constructor, bugprone-argument-comment, bugprone-bad-signal-to-kill-thread, bugprone-bool-pointer-implicit-conversion, bugprone-copy-constructor-init, bugprone-dangling-handle, bugprone-forward-declaration-namespace, bugprone-fold-init-type, bugprone-inaccurate-erase, bugprone-incorrect-roundings, bugprone-infinite-loop, bugprone-integer-division, bugprone-macro-parentheses, bugprone-macro-repeated-side-effects, bugprone-misplaced-operator-in-strlen-in-alloc, bugprone-misplaced-pointer-artithmetic-in-alloc, bugprone-misplaced-widening-cast, bugprone-move-forwarding-reference, bugprone-multiple-statement-macro, bugprone-parent-virtual-call, bugprone-posix-return, bugprone-reserved-identifier, bugprone-signed-char-misuse, bugprone-sizeof-container, bugprone-sizeof-expression, bugprone-string-constructor, bugprone-string-integer-assignment, bugprone-string-literal-with-embedded-nul, bugprone-suspicious-enum-usage, bugprone-suspicious-include, bugprone-suspicious-memset-usage, bugprone-suspicious-missing-comma, bugprone-suspicious-string-compare, bugprone-swapped-arguments, bugprone-terminating-continue, bugprone-throw-keyword-missing, bugprone-too-small-loop-variable, bugprone-undefined-memory-manipulation, bugprone-unhandled-self-assignment, bugprone-unused-raii, bugprone-unused-return-value, bugprone-use-after-move, bugprone-virtual-near-miss, cert-dcl21-cpp, cert-dcl50-cpp, cert-env33-c, cert-err34-c, cert-err52-cpp, cert-flp30-c, cert-mem57-cpp, cert-msc50-cpp, cert-oop58-cpp, google-build-explicit-make-pair, google-build-namespaces, google-default-arguments, google-explicit-constructor, google-readability-casting, google-readability-avoid-underscore-in-googletest-name, google-runtime-int, google-runtime-operator, hicpp-exception-baseclass, clang-analyzer-core.CallAndMessage, clang-analyzer-core.DivideZero, clang-analyzer-core.NonNullParamChecker, clang-analyzer-core.NullDereference, clang-analyzer-core.StackAddressEscape, clang-analyzer-core.UndefinedBinaryOperatorResult, clang-analyzer-core.VLASize, clang-analyzer-core.uninitialized.ArraySubscript, clang-analyzer-core.uninitialized.Assign, clang-analyzer-core.uninitialized.Branch, clang-analyzer-core.uninitialized.CapturedBlockVariable, clang-analyzer-core.uninitialized.UndefReturn, clang-analyzer-cplusplus.InnerPointer, clang-analyzer-cplusplus.NewDelete, clang-analyzer-cplusplus.NewDeleteLeaks, clang-analyzer-cplusplus.PlacementNewChecker, clang-analyzer-cplusplus.SelfAssignment, clang-analyzer-deadcode.DeadStores, clang-analyzer-optin.cplusplus.VirtualCall, clang-analyzer-security.insecureAPI.UncheckedReturn, clang-analyzer-security.insecureAPI.bcmp, clang-analyzer-security.insecureAPI.bcopy, clang-analyzer-security.insecureAPI.bzero, clang-analyzer-security.insecureAPI.getpw, clang-analyzer-security.insecureAPI.gets, clang-analyzer-security.insecureAPI.mkstemp, clang-analyzer-security.insecureAPI.mktemp, clang-analyzer-security.insecureAPI.rand, clang-analyzer-security.insecureAPI.strcpy, clang-analyzer-unix.Malloc, clang-analyzer-unix.MallocSizeof, clang-analyzer-unix.MismatchedDeallocator, clang-analyzer-unix.Vfork, clang-analyzer-unix.cstring.BadSizeArg, clang-analyzer-unix.cstring.NullArg, boost-use-to-string, ' WarningsAsErrors: '*' CheckOptions: - key: readability-identifier-naming.ClassCase value: CamelCase - key: readability-identifier-naming.EnumCase value: CamelCase - key: readability-identifier-naming.LocalVariableCase value: lower_case - key: readability-identifier-naming.StaticConstantCase value: aNy_CasE - key: readability-identifier-naming.MemberCase value: lower_case - key: readability-identifier-naming.ClassMemberPrefix value: m_ - key: readability-identifier-naming.PrivateMemberPrefix value: '' - key: readability-identifier-naming.ProtectedMemberPrefix value: '' - key: readability-identifier-naming.PublicMemberCase value: lower_case - key: readability-identifier-naming.MethodCase value: camelBack - key: readability-identifier-naming.PrivateMethodPrefix value: '' - key: readability-identifier-naming.ProtectedMethodPrefix value: '' - key: readability-identifier-naming.ParameterPackCase value: lower_case - key: readability-identifier-naming.StructCase value: CamelCase - key: readability-identifier-naming.TemplateTemplateParameterCase value: CamelCase - key: readability-identifier-naming.TemplateUsingCase value: lower_case - key: readability-identifier-naming.TypeTemplateParameterCase value: CamelCase - key: readability-identifier-naming.TypedefCase value: CamelCase - key: readability-identifier-naming.UnionCase value: CamelCase - key: readability-identifier-naming.UsingCase value: CamelCase ================================================ FILE: .github/workflows/windows_dev.yml ================================================ name: Windows_dev on: pull_request: types: [dev] push: branches: [dev] jobs: build: runs-on: "windows-latest" steps: - uses: actions/checkout@v2 with: submodules: "recursive" - uses: Jimver/cuda-toolkit@v0.2.8 id: cuda-toolkit with: cuda: '11.7.0' sub-packages: '["nvcc", "visual_studio_integration", "cudart"]' - uses: xmake-io/github-action-setup-xmake@v1 with: xmake-version: latest actions-cache-folder: ".xmake-cache" - name: Build run: xmake -y ================================================ FILE: .github/workflows/windows_main.yml ================================================ name: Windows_main on: pull_request: types: [main] push: branches: [main] jobs: build: runs-on: "windows-latest" steps: - uses: actions/checkout@v2 with: submodules: "recursive" - uses: Jimver/cuda-toolkit@v0.2.8 id: cuda-toolkit with: cuda: '11.7.0' sub-packages: '["nvcc", "visual_studio_integration", "cudart"]' - uses: xmake-io/github-action-setup-xmake@v1 with: xmake-version: latest actions-cache-folder: ".xmake-cache" - name: Build run: xmake -y ================================================ FILE: .gitignore ================================================ # Prerequisites *.d # Compiled Object files *.slo *.lo *.o # Precompiled Headers *.gch *.pch # Compiled Dynamic libraries *.so *.dylib *.dll # Fortran module files *.mod *.smod # Compiled Static libraries *.lai *.la *.a *.lib # Executables *.exe *.out *.app *.spv # Folders /build/* /bin/* /lib/* /shared/* **/Archive/* *.meta .vscode .xmake/ build/ vsxmake** ================================================ FILE: .gitmodules ================================================ [submodule "Source/Dependencies"] path = Source/Dependencies url = https://github.com/Chaf-Libraries/Dependencies ================================================ FILE: Asset/Meta/README.md ================================================ ================================================ FILE: Asset/MustacheTemplate/Reflection.mustache ================================================ {{#Header}} {{&Include}} {{/Header}} namespace Ilum_{{Hash}} { RTTR_REGISTRATION { {{#Enum}} rttr::registration::enumeration<{{EnumQualifiedName}}>("{{EnumName}}") ( {{#EnumValue}} rttr::value("{{EnumValueName}}", {{EnumValueQualifiedName}}){{^IsLast}},{{/IsLast}} {{/EnumValue}} ); {{/Enum}} {{#Class}} rttr::registration::class_<{{ClassQualifiedName}}>("{{ClassName}}"){{#Meta}}({{#MetaData}}rttr::metadata("{{&Key}}", {{&Value}}){{^IsLast}}, {{/IsLast}}{{/MetaData}}){{/Meta}} {{#Field}} .property("{{FieldName}}", &{{FieldQualifiedName}}, rttr::registration::{{#Public}}public_access{{/Public}}{{#Private}}private_access{{/Private}}{{#Protected}}protected_access{{/Protected}}){{#Meta}}({{#MetaData}}rttr::metadata("{{&Key}}", {{&Value}}){{^IsLast}}, {{/IsLast}}{{/MetaData}}){{/Meta}} {{/Field}} {{#Constructor}} {{^Static}}.constructor<{{#Params}}{{&Param}}{{^IsLast}}, {{/IsLast}}{{/Params}}>(){{/Static}}{{#Static}}.constructor(&{{MethodQualifiedName}}){{/Static}}(rttr::policy::ctor::as_object) {{/Constructor}} {{#NoConstructor}} .constructor<>()(rttr::policy::ctor::as_object) {{/NoConstructor}} {{#Method}} .method("{{&MethodName}}", {{^Overload}}&{{&MethodQualifiedName}}{{/Overload}}{{#Overload}}{{^Const}}rttr::select_overload<{{&ReturnType}}({{#Params}}{{&Param}}{{^IsLast}}, {{/IsLast}}{{/Params}})>{{/Const}}{{#Const}}rttr::select_const<{{&ClassQualifiedName}}, {{&ReturnType}}, {{#Params}}{{&Param}}{{^IsLast}}, {{/IsLast}}{{/Params}}>{{/Const}}(&{{&MethodQualifiedName}}){{/Overload}}, rttr::registration::{{#Public}}public_access{{/Public}}{{#Private}}private_access{{/Private}}{{#Protected}}protected_access{{/Protected}}){{#Meta}}({{#MetaData}}rttr::metadata("{{&Key}}", {{&Value}}){{^IsLast}}, {{/IsLast}}{{/MetaData}}){{/Meta}} {{/Method}} ; {{/Class}} } } ================================================ FILE: Asset/NRD_Data/Sample/blur_buffer.csv ================================================ gViewToClip,"[1.0000, 0.0000, 0.0000, 0.0000] [0.0000, 1.7778, 0.0000, 0.0000] [-0.0000, -0.0000, 1.0000, 1.0000] [0.0000, 0.0000, -0.0010, 0.0000]" gViewToWorld,"[-0.9997, -0.0236, -0.0000, 0.0000] [0.0031, -0.1315, 0.9913, 0.0000] [0.0234, -0.9910, -0.1315, 0.0000] [0.0000, 0.0000, 0.0000, 1.0000]" gFrustum,"[-1.0000, 0.5625, 2.0000, -1.1250]" gHitDistParams,"[3.0000, 0.1000, 20.0000, -25.0000]" gViewVectorWorld,"[-0.0234, 0.9910, 0.1315, 0.0000]" gViewVectorWorldPrev,"[-0.0234, 0.9910, 0.1315, 0.0000]" gInvScreenSize,"[0.0008, 0.0014]" gScreenSize,"[1280.0000, 720.0000]" gInvRectSize,"[0.0008, 0.0014]" gRectSize,"[1280.0000, 720.0000]" gRectSizePrev,"[1280.0000, 720.0000]" gResolutionScale,"[1.0000, 1.0000]" gRectOffset,"[0.0000, 0.0000]" gSensitivityToDarkness,"[0.0100, 0.1000]" gRectOrigin,"[0, 0]" gReference,0.0000 gOrthoMode,0.0000 gUnproject,0.0016 gDebug,0.0000 gDenoisingRange,60.6960 gPlaneDistSensitivity,0.0050 gFramerateScale,0.5000 gBlurRadius,30.0000 gMaxAccumulatedFrameNum,1.0000 gMaxFastAccumulatedFrameNum,0.0000 gAntiFirefly,0.0000 gMinConvergedStateBaseRadiusScale,0.2500 gLobeAngleFraction,0.1000 gRoughnessFraction,0.0500 gResponsiveAccumulationRoughnessThreshold,0.0000 gDiffPrepassBlurRadius,30.0000 gSpecPrepassBlurRadius,50.0000 gIsWorldSpaceMotionEnabled,1 gFrameIndex,42372 gResetHistory,0 gDiffMaterialMask,1 gSpecMaterialMask,0 gRotator,"[0.2651, -0.9642, 0.9642, 0.2651]" gSpecLobeTrimmingParams,"[0.9500, 0.0400, 0.1100]" gBlurRadiusScale,5.0000 ================================================ FILE: Asset/NRD_Data/Sample/history_fix_buffer.csv ================================================ gViewToClip,"[1.0000, 0.0000, 0.0000, 0.0000] [0.0000, 1.7778, 0.0000, 0.0000] [-0.0000, -0.0000, 1.0000, 1.0000] [0.0000, 0.0000, -0.0010, 0.0000]",- gViewToWorld,"[-0.9997, -0.0236, -0.0000, 0.0000] [0.0031, -0.1315, 0.9913, 0.0000] [0.0234, -0.9910, -0.1315, 0.0000] [0.0000, 0.0000, 0.0000, 1.0000]",- gFrustum,"[-1.0000, 0.5625, 2.0000, -1.1250]",- gHitDistParams,"[3.0000, 0.1000, 20.0000, -25.0000]",- gViewVectorWorld,"[-0.0234, 0.9910, 0.1315, 0.0000]",- gViewVectorWorldPrev,"[-0.0234, 0.9910, 0.1315, 0.0000]",- gInvScreenSize,"[0.0008, 0.0014]",- gScreenSize,"[1280.0000, 720.0000]",- gInvRectSize,"[0.0008, 0.0014]",- gRectSize,"[1280.0000, 720.0000]",- gRectSizePrev,"[1280.0000, 720.0000]",- gResolutionScale,"[1.0000, 1.0000]",- gRectOffset,"[0.0000, 0.0000]",- gSensitivityToDarkness,"[0.0100, 0.1000]",- gRectOrigin,"[0, 0]",- gReference,0.0000,- gOrthoMode,0.0000,- gUnproject,0.0016,- gDebug,0.0000,- gDenoisingRange,60.6960,- gPlaneDistSensitivity,0.0050,- gFramerateScale,0.5000,- gBlurRadius,30.0000,- gMaxAccumulatedFrameNum,1.0000,- gMaxFastAccumulatedFrameNum,0.0000,- gAntiFirefly,0.0000,- gMinConvergedStateBaseRadiusScale,0.2500,- gLobeAngleFraction,0.1000,- gRoughnessFraction,0.0500,- gResponsiveAccumulationRoughnessThreshold,0.0000,- gDiffPrepassBlurRadius,30.0000,- gSpecPrepassBlurRadius,50.0000,- gIsWorldSpaceMotionEnabled,1,- gFrameIndex,42372,- gResetHistory,0,- gDiffMaterialMask,1,- gSpecMaterialMask,0,- gHistoryFixStrength,1.0000,- ================================================ FILE: Asset/NRD_Data/Sample/post_blur_buffer.csv ================================================ gViewToClip,gViewToWorld,gFrustum,gHitDistParams,gViewVectorWorld,gViewVectorWorldPrev,gInvScreenSize,gScreenSize,gInvRectSize,gRectSize,gRectSizePrev,gResolutionScale,gRectOffset,gSensitivityToDarkness,gRectOrigin,gReference,gOrthoMode,gUnproject,gDebug,gDenoisingRange,gPlaneDistSensitivity,gFramerateScale,gBlurRadius,gMaxAccumulatedFrameNum,gMaxFastAccumulatedFrameNum,gAntiFirefly,gMinConvergedStateBaseRadiusScale,gLobeAngleFraction,gRoughnessFraction,gResponsiveAccumulationRoughnessThreshold,gDiffPrepassBlurRadius,gSpecPrepassBlurRadius,gIsWorldSpaceMotionEnabled,gFrameIndex,gResetHistory,gDiffMaterialMask,gSpecMaterialMask,gRotator,gSpecLobeTrimmingParams,gBlurRadiusScale "[1.0000, 0.0000, 0.0000, 0.0000] [0.0000, 1.7778, 0.0000, 0.0000] [-0.0000, -0.0000, 1.0000, 1.0000] [0.0000, 0.0000, -0.0010, 0.0000]","[-0.9997, -0.0236, -0.0000, 0.0000] [0.0031, -0.1315, 0.9913, 0.0000] [0.0234, -0.9910, -0.1315, 0.0000] [0.0000, 0.0000, 0.0000, 1.0000]","[-1.0000, 0.5625, 2.0000, -1.1250]","[3.0000, 0.1000, 20.0000, -25.0000]","[-0.0234, 0.9910, 0.1315, 0.0000]","[-0.0234, 0.9910, 0.1315, 0.0000]","[0.0008, 0.0014]","[1280.0000, 720.0000]","[0.0008, 0.0014]","[1280.0000, 720.0000]","[1280.0000, 720.0000]","[1.0000, 1.0000]","[0.0000, 0.0000]","[0.0100, 0.1000]","[0, 0]",0.0000,0.0000,0.0016,0.0000,60.6960,0.0050,0.5000,30.0000,1.0000,0.0000,0.0000,0.2500,0.1000,0.0500,0.0000,30.0000,50.0000,1,42372,0,1,0,"[-0.9642, -0.2651, 0.2651, -0.9642]","[0.9500, 0.0400, 0.1100]",5.0000 ================================================ FILE: Asset/NRD_Data/Sample/temporal_accumulate_buffer.csv ================================================ gViewToClip,"[1.0000, 0.0000, 0.0000, 0.0000] [0.0000, 1.7778, 0.0000, 0.0000] [-0.0000, -0.0000, 1.0000, 1.0000] [0.0000, 0.0000, -0.0010, 0.0000]",- gViewToWorld,"[-0.9997, -0.0236, -0.0000, 0.0000] [0.0031, -0.1315, 0.9913, 0.0000] [0.0234, -0.9910, -0.1315, 0.0000] [0.0000, 0.0000, 0.0000, 1.0000]",- gFrustum,"[-1.0000, 0.5625, 2.0000, -1.1250]",- gHitDistParams,"[3.0000, 0.1000, 20.0000, -25.0000]",- gViewVectorWorld,"[-0.0234, 0.9910, 0.1315, 0.0000]",- gViewVectorWorldPrev,"[-0.0234, 0.9910, 0.1315, 0.0000]",- gInvScreenSize,"[0.0008, 0.0014]",- gScreenSize,"[1280.0000, 720.0000]",- gInvRectSize,"[0.0008, 0.0014]",- gRectSize,"[1280.0000, 720.0000]",- gRectSizePrev,"[1280.0000, 720.0000]",- gResolutionScale,"[1.0000, 1.0000]",- gRectOffset,"[0.0000, 0.0000]",- gSensitivityToDarkness,"[0.0100, 0.1000]",- gRectOrigin,"[0, 0]",- gReference,0.0000,- gOrthoMode,0.0000,- gUnproject,0.0016,- gDebug,0.0000,- gDenoisingRange,60.6960,- gPlaneDistSensitivity,0.0050,- gFramerateScale,0.5000,- gBlurRadius,30.0000,- gMaxAccumulatedFrameNum,1.0000,- gMaxFastAccumulatedFrameNum,0.0000,- gAntiFirefly,0.0000,- gMinConvergedStateBaseRadiusScale,0.2500,- gLobeAngleFraction,0.1000,- gRoughnessFraction,0.0500,- gResponsiveAccumulationRoughnessThreshold,0.0000,- gDiffPrepassBlurRadius,30.0000,- gSpecPrepassBlurRadius,50.0000,- gIsWorldSpaceMotionEnabled,1,- gFrameIndex,42372,- gResetHistory,0,- gDiffMaterialMask,1,- gSpecMaterialMask,0,- gWorldToViewPrev,"[-0.9997, 0.0031, 0.0234, 0.0000] [-0.0236, -0.1315, -0.9910, 0.0000] [-0.0000, 0.9913, -0.1315, 0.0000] [0.0000, -0.0000, -0.0000, 1.0000]",- gWorldToClipPrev,"[-0.9997, 0.0055, 0.0234, 0.0234] [-0.0236, -0.2337, -0.9910, -0.9910] [0.0000, 1.7623, -0.1315, -0.1315] [0.0000, 0.0000, -0.0010, 0.0000]",- gWorldToClip,"[-0.9997, 0.0055, 0.0234, 0.0234] [-0.0236, -0.2337, -0.9910, -0.9910] [0.0000, 1.7623, -0.1315, -0.1315] [0.0000, 0.0000, -0.0010, 0.0000]",- gWorldPrevToWorld,"[1.0000, 0.0000, 0.0000, 0.0000] [0.0000, 1.0000, 0.0000, 0.0000] [0.0000, 0.0000, 1.0000, 0.0000] [0.0000, 0.0000, 0.0000, 1.0000]",- gFrustumPrev,"[-1.0000, 0.5625, 2.0000, -1.1250]",- gCameraDelta,"[0.0000, 0.0000, 0.0000, 0.0000]",- gRotator,"[-0.5868, 0.8098, -0.8098, -0.5868]",- gMotionVectorScale,"[1.0000, 1.0000]",- gCheckerboardResolveAccumSpeed,0.6447,- gDisocclusionThreshold,0.0071,- gDiffCheckerboard,2,- gSpecCheckerboard,2,- gIsPrepassEnabled,0,- ================================================ FILE: Asset/SPD/metals/Ag.eta.spd ================================================ 298.757050 1.519000 302.400421 1.496000 306.133759 1.432500 309.960449 1.323000 313.884003 1.142062 317.908142 0.932000 322.036835 0.719062 326.274139 0.526000 330.624481 0.388125 335.092377 0.294000 339.682678 0.253313 344.400482 0.238000 349.251221 0.221438 354.240509 0.209000 359.374420 0.194813 364.659332 0.186000 370.102020 0.192063 375.709625 0.200000 381.489777 0.198063 387.450562 0.192000 393.600555 0.182000 399.948975 0.173000 406.505493 0.172625 413.280579 0.173000 420.285339 0.166688 427.531647 0.160000 435.032196 0.158500 442.800629 0.157000 450.851562 0.151063 459.200653 0.144000 467.864838 0.137313 476.862213 0.132000 486.212463 0.130250 495.936707 0.130000 506.057861 0.129938 516.600769 0.130000 527.592224 0.130063 539.061646 0.129000 551.040771 0.124375 563.564453 0.120000 576.670593 0.119313 590.400818 0.121000 604.800842 0.125500 619.920898 0.131000 635.816284 0.136125 652.548279 0.140000 670.184753 0.140063 688.800964 0.140000 708.481018 0.144313 729.318665 0.148000 751.419250 0.145875 774.901123 0.143000 799.897949 0.142563 826.561157 0.145000 855.063293 0.151938 885.601257 0.163000 ================================================ FILE: Asset/SPD/metals/Ag.k.spd ================================================ 298.757050 1.080000 302.400421 0.882000 306.133759 0.761063 309.960449 0.647000 313.884003 0.550875 317.908142 0.504000 322.036835 0.554375 326.274139 0.663000 330.624481 0.818563 335.092377 0.986000 339.682678 1.120687 344.400482 1.240000 349.251221 1.345250 354.240509 1.440000 359.374420 1.533750 364.659332 1.610000 370.102020 1.641875 375.709625 1.670000 381.489777 1.735000 387.450562 1.810000 393.600555 1.878750 399.948975 1.950000 406.505493 2.029375 413.280579 2.110000 420.285339 2.186250 427.531647 2.260000 435.032196 2.329375 442.800629 2.400000 450.851562 2.478750 459.200653 2.560000 467.864838 2.640000 476.862213 2.720000 486.212463 2.798125 495.936707 2.880000 506.057861 2.973750 516.600769 3.070000 527.592224 3.159375 539.061646 3.250000 551.040771 3.348125 563.564453 3.450000 576.670593 3.553750 590.400818 3.660000 604.800842 3.766250 619.920898 3.880000 635.816284 4.010625 652.548279 4.150000 670.184753 4.293125 688.800964 4.440000 708.481018 4.586250 729.318665 4.740000 751.419250 4.908125 774.901123 5.090000 799.897949 5.288750 826.561157 5.500000 855.063293 5.720624 885.601257 5.950000 ================================================ FILE: Asset/SPD/metals/Al.eta.spd ================================================ 298.757050 0.273375 302.400421 0.280000 306.133759 0.286813 309.960449 0.294000 313.884003 0.301875 317.908142 0.310000 322.036835 0.317875 326.274139 0.326000 330.624481 0.334750 335.092377 0.344000 339.682678 0.353813 344.400482 0.364000 349.251221 0.374375 354.240509 0.385000 359.374420 0.395750 364.659332 0.407000 370.102020 0.419125 375.709625 0.432000 381.489777 0.445688 387.450562 0.460000 393.600555 0.474688 399.948975 0.490000 406.505493 0.506188 413.280579 0.523000 420.285339 0.540063 427.531647 0.558000 435.032196 0.577313 442.800629 0.598000 450.851562 0.620313 459.200653 0.644000 467.864838 0.668625 476.862213 0.695000 486.212463 0.723750 495.936707 0.755000 506.057861 0.789000 516.600769 0.826000 527.592224 0.867000 539.061646 0.912000 551.040771 0.963000 563.564453 1.020000 576.670593 1.080000 590.400818 1.150000 604.800842 1.220000 619.920898 1.300000 635.816284 1.390000 652.548279 1.490000 670.184753 1.600000 688.800964 1.740000 708.481018 1.910000 729.318665 2.140000 751.419250 2.410000 774.901123 2.630000 799.897949 2.800000 826.561157 2.740000 855.063293 2.580000 885.601257 2.240000 ================================================ FILE: Asset/SPD/metals/Al.k.spd ================================================ 298.757050 3.593750 302.400421 3.640000 306.133759 3.689375 309.960449 3.740000 313.884003 3.789375 317.908142 3.840000 322.036835 3.894375 326.274139 3.950000 330.624481 4.005000 335.092377 4.060000 339.682678 4.113750 344.400482 4.170000 349.251221 4.233750 354.240509 4.300000 359.374420 4.365000 364.659332 4.430000 370.102020 4.493750 375.709625 4.560000 381.489777 4.633750 387.450562 4.710000 393.600555 4.784375 399.948975 4.860000 406.505493 4.938125 413.280579 5.020000 420.285339 5.108750 427.531647 5.200000 435.032196 5.290000 442.800629 5.380000 450.851562 5.480000 459.200653 5.580000 467.864838 5.690000 476.862213 5.800000 486.212463 5.915000 495.936707 6.030000 506.057861 6.150000 516.600769 6.280000 527.592224 6.420000 539.061646 6.550000 551.040771 6.700000 563.564453 6.850000 576.670593 7.000000 590.400818 7.150000 604.800842 7.310000 619.920898 7.480000 635.816284 7.650000 652.548279 7.820000 670.184753 8.010000 688.800964 8.210000 708.481018 8.390000 729.318665 8.570000 751.419250 8.620000 774.901123 8.600000 799.897949 8.450000 826.561157 8.310000 855.063293 8.210000 885.601257 8.210000 ================================================ FILE: Asset/SPD/metals/AlAs.eta.spd ================================================ # data from www.luxpop.com database # ; Optical constants for cubic AlAs # ; # ; taken from: # ; # ; 'Handbook of Optical Constants of Solids II', Ed. by Edward D. Palik, # ; Academic Press, Inc., 1991. # ; # ; Lambda (A) n k # ;----------------------------------------- 302.389984 4.470000 309.950012 4.920000 317.897003 5.350000 326.263000 5.390000 335.081024 4.990000 344.389008 4.720000 354.229004 4.480000 364.647003 4.300000 375.696991 4.130000 387.437988 3.930000 393.587006 0.000000 397.372009 0.000000 399.935028 0.000000 403.187012 0.000000 406.492004 0.000000 409.850983 0.000000 413.266998 0.000000 416.739990 0.000000 420.270996 0.000000 423.863007 0.000000 427.516998 0.000000 431.235016 0.000000 435.018036 0.000000 442.785980 3.703000 459.185028 3.570000 476.846008 3.472000 495.920013 3.394000 516.583008 3.329000 539.044006 3.273000 563.545044 3.225000 590.381042 3.183000 619.900024 3.145000 652.526001 3.112000 688.778015 3.082000 729.294006 3.056000 774.875000 3.032000 826.533020 3.010000 885.570984 2.990000 ================================================ FILE: Asset/SPD/metals/AlAs.k.spd ================================================ # data from www.luxpop.com database # ; Optical constants for cubic AlAs # ; # ; taken from: # ; # ; 'Handbook of Optical Constants of Solids II', Ed. by Edward D. Palik, # ; Academic Press, Inc., 1991. # ; # ; Lambda (A) n k # ;----------------------------------------- 302.389984 2.680000 309.950012 2.490000 317.897003 2.150000 326.263000 1.300000 335.081024 0.752000 344.389008 0.519000 354.229004 0.334000 364.647003 0.233000 375.696991 0.139000 387.437988 0.115000 393.587006 0.119000 397.372009 0.115000 399.935028 0.113000 403.187012 0.110000 406.492004 0.106000 409.850983 0.099800 413.266998 0.063800 416.739990 0.031500 420.270996 0.018900 423.863007 0.011800 427.516998 0.010500 431.235016 0.010300 435.018036 0.009930 442.785980 0.001610 459.185028 0.001560 476.846008 0.001530 495.920013 0.001250 516.583008 0.000682 539.044006 0.000275 563.545044 0.000040 590.381042 0.000002 619.900024 0.000000 652.526001 0.000000 688.778015 0.000000 729.294006 0.000000 774.875000 0.000000 826.533020 0.000000 885.570984 0.000000 ================================================ FILE: Asset/SPD/metals/AlSb.eta.spd ================================================ # data from www.luxpop.com database # ; Optical constants for cubic AlSb # ; # ; taken from: # ; # ; 'Handbook of Optical Constants of Solids II', Ed. by Edward D. Palik, # ; Academic Press, Inc., 1991. # ; # ; Lambda (A) n k # ;----------------------------------------- 302.389984 3.150000 309.950012 3.500000 317.897003 3.710000 326.263000 3.810000 335.081024 3.930000 344.389008 3.960000 354.229004 3.950000 364.647003 3.970000 375.696991 4.140000 387.437988 4.510000 399.935028 4.570000 413.266998 4.520000 427.516998 4.660000 442.785980 5.270000 459.185028 5.080000 476.846008 4.810000 495.920013 4.610000 516.583008 4.440000 539.044006 4.310000 563.545044 4.200000 590.381042 4.010000 ================================================ FILE: Asset/SPD/metals/AlSb.k.spd ================================================ # data from www.luxpop.com database # ; Optical constants for cubic AlSb # ; # ; taken from: # ; # ; 'Handbook of Optical Constants of Solids II', Ed. by Edward D. Palik, # ; Academic Press, Inc., 1991. # ; # ; Lambda (A) n k # ;----------------------------------------- 302.389984 4.000000 309.950012 3.710000 317.897003 3.400000 326.263000 3.160000 335.081024 2.970000 344.389008 2.810000 354.229004 2.690000 364.647003 2.640000 375.696991 2.690000 387.437988 2.470000 399.935028 2.120000 413.266998 1.970000 427.516998 2.060000 442.785980 1.580000 459.185028 0.920000 476.846008 0.630000 495.920013 0.460000 516.583008 0.330000 539.044006 0.240000 563.545044 0.010000 590.381042 0.006000 ================================================ FILE: Asset/SPD/metals/Au.eta.spd ================================================ 298.757050 1.795000 302.400421 1.812000 306.133759 1.822625 309.960449 1.830000 313.884003 1.837125 317.908142 1.840000 322.036835 1.834250 326.274139 1.824000 330.624481 1.812000 335.092377 1.798000 339.682678 1.782000 344.400482 1.766000 349.251221 1.752500 354.240509 1.740000 359.374420 1.727625 364.659332 1.716000 370.102020 1.705875 375.709625 1.696000 381.489777 1.684750 387.450562 1.674000 393.600555 1.666000 399.948975 1.658000 406.505493 1.647250 413.280579 1.636000 420.285339 1.628000 427.531647 1.616000 435.032196 1.596250 442.800629 1.562000 450.851562 1.502125 459.200653 1.426000 467.864838 1.345875 476.862213 1.242000 486.212463 1.086750 495.936707 0.916000 506.057861 0.754500 516.600769 0.608000 527.592224 0.491750 539.061646 0.402000 551.040771 0.345500 563.564453 0.306000 576.670593 0.267625 590.400818 0.236000 604.800842 0.212375 619.920898 0.194000 635.816284 0.177750 652.548279 0.166000 670.184753 0.161000 688.800964 0.160000 708.481018 0.160875 729.318665 0.164000 751.419250 0.169500 774.901123 0.176000 799.897949 0.181375 826.561157 0.188000 855.063293 0.198125 885.601257 0.210000 ================================================ FILE: Asset/SPD/metals/Au.k.spd ================================================ 298.757050 1.920375 302.400421 1.920000 306.133759 1.918875 309.960449 1.916000 313.884003 1.911375 317.908142 1.904000 322.036835 1.891375 326.274139 1.878000 330.624481 1.868250 335.092377 1.860000 339.682678 1.851750 344.400482 1.846000 349.251221 1.845250 354.240509 1.848000 359.374420 1.852375 364.659332 1.862000 370.102020 1.883000 375.709625 1.906000 381.489777 1.922500 387.450562 1.936000 393.600555 1.947750 399.948975 1.956000 406.505493 1.959375 413.280579 1.958000 420.285339 1.951375 427.531647 1.940000 435.032196 1.924500 442.800629 1.904000 450.851562 1.875875 459.200653 1.846000 467.864838 1.814625 476.862213 1.796000 486.212463 1.797375 495.936707 1.840000 506.057861 1.956500 516.600769 2.120000 527.592224 2.326250 539.061646 2.540000 551.040771 2.730625 563.564453 2.880000 576.670593 2.940625 590.400818 2.970000 604.800842 3.015000 619.920898 3.060000 635.816284 3.070000 652.548279 3.150000 670.184753 3.445812 688.800964 3.800000 708.481018 4.087687 729.318665 4.357000 751.419250 4.610188 774.901123 4.860000 799.897949 5.125813 826.561157 5.390000 855.063293 5.631250 885.601257 5.880000 ================================================ FILE: Asset/SPD/metals/Be.eta.spd ================================================ # data from www.luxpop.com database # ; Optical constants for polycrystalline Be # ; # ; Concatenation of: # ; # ; Be_llnl_cxro + Be_palik # ; # ; Lambda (A) n k # ;----------------------------------------- 310.000000 2.470000 326.300018 2.550000 344.399994 2.640000 364.600006 2.730000 387.399994 2.840000 413.300018 2.950000 442.800018 3.070000 476.800018 3.190000 516.600037 3.300000 563.500000 3.390000 619.900024 3.460000 688.799988 3.470000 774.900024 3.440000 885.600037 3.350000 ================================================ FILE: Asset/SPD/metals/Be.k.spd ================================================ # data from www.luxpop.com database # ; Optical constants for polycrystalline Be # ; # ; Concatenation of: # ; # ; Be_llnl_cxro + Be_palik # ; # ; Lambda (A) n k # ;----------------------------------------- 310.000000 3.080000 326.300018 3.080000 344.399994 3.080000 364.600006 3.100000 387.399994 3.120000 413.300018 3.140000 442.800018 3.160000 476.800018 3.160000 516.600037 3.180000 563.500000 3.170000 619.900024 3.180000 688.799988 3.230000 774.900024 3.350000 885.600037 3.550000 ================================================ FILE: Asset/SPD/metals/Cr.eta.spd ================================================ # data from www.luxpop.com database # ; Optical constants for Cr # ; # ; Concatenation of: # ; # ; Cr_llnl_cxro + Cr_palik # ; # ; Lambda (A) n k # ;----------------------------------------- 300.194000 0.980000 307.643005 1.020000 316.276001 1.060000 323.708008 1.120000 333.279999 1.180000 341.542999 1.260000 351.217987 1.330000 362.514984 1.390000 372.312012 1.430000 385.031006 1.440000 396.102020 1.480000 409.175018 1.540000 424.589020 1.650000 438.092010 1.800000 455.808990 1.990000 471.406982 2.220000 490.040009 2.490000 512.314026 2.750000 532.102966 2.980000 558.468018 3.180000 582.066040 3.340000 610.739014 3.480000 700.452026 3.840000 815.658020 4.230000 826.533020 4.270000 849.178040 4.310000 860.971985 4.330000 885.570984 4.380000 ================================================ FILE: Asset/SPD/metals/Cr.k.spd ================================================ # data from www.luxpop.com database # ; Optical constants for Cr # ; # ; Concatenation of: # ; # ; Cr_llnl_cxro + Cr_palik # ; # ; Lambda (A) n k # ;----------------------------------------- 300.194000 2.670000 307.643005 2.760000 316.276001 2.850000 323.708008 2.950000 333.279999 3.040000 341.542999 3.120000 351.217987 3.180000 362.514984 3.240000 372.312012 3.310000 385.031006 3.400000 396.102020 3.540000 409.175018 3.710000 424.589020 3.890000 438.092010 4.060000 455.808990 4.220000 471.406982 4.360000 490.040009 4.440000 512.314026 4.460000 532.102966 4.450000 558.468018 4.410000 582.066040 4.380000 610.739014 4.360000 700.452026 4.370000 815.658020 4.340000 826.533020 4.330000 849.178040 4.320000 860.971985 4.320000 885.570984 4.310000 ================================================ FILE: Asset/SPD/metals/CsI.eta.spd ================================================ # data from www.luxpop.com database # ; Optical constants for CsI # ; # ; Concatenation of: # ; # ; CsI_llnl_cxro + CsI_palik # ; # ; Lambda (A) n k # ;----------------------------------------- 304.993988 1.967310 309.950012 1.956510 314.989990 1.946580 319.947998 1.937410 324.980011 1.928930 329.997009 1.921060 334.990997 1.913730 339.951019 1.906900 344.963989 1.900510 350.028015 1.894530 354.937988 1.888910 359.988007 1.883630 364.968994 1.878660 369.979004 1.873970 375.014984 1.869540 379.957001 1.865350 385.031006 1.861380 389.997009 1.857620 394.967010 1.854040 399.935028 1.850640 409.987030 1.844330 419.985992 1.838590 430.037994 1.833360 439.957001 1.828570 450.018036 1.824170 460.037018 1.820120 469.977020 1.816370 479.985016 1.812910 490.040009 1.809700 499.919006 1.806720 509.996002 1.803930 520.049988 1.801340 530.056030 1.798910 539.983032 1.796640 550.044006 1.794510 559.981995 1.792500 570.023010 1.790620 579.888000 1.788840 590.100037 1.787170 600.097046 1.785590 619.900024 1.782680 640.062012 1.780060 659.819031 1.777700 680.088013 1.775570 700.056030 1.773630 719.976990 1.771860 740.179016 1.770250 760.147034 1.768770 779.747986 1.767410 799.871033 1.766150 819.974060 1.765000 839.973083 1.763920 859.778015 1.762930 879.915039 1.762010 899.709961 1.761150 ================================================ FILE: Asset/SPD/metals/CsI.k.spd ================================================ # data from www.luxpop.com database # ; Optical constants for CsI # ; # ; Concatenation of: # ; # ; CsI_llnl_cxro + CsI_palik # ; # ; Lambda (A) n k # ;----------------------------------------- 304.993988 0.000000 309.950012 0.000000 314.989990 0.000000 319.947998 0.000000 324.980011 0.000000 329.997009 0.000000 334.990997 0.000000 339.951019 0.000000 344.963989 0.000000 350.028015 0.000000 354.937988 0.000000 359.988007 0.000000 364.968994 0.000000 369.979004 0.000000 375.014984 0.000000 379.957001 0.000000 385.031006 0.000000 389.997009 0.000000 394.967010 0.000000 399.935028 0.000000 409.987030 0.000000 419.985992 0.000000 430.037994 0.000000 439.957001 0.000000 450.018036 0.000000 460.037018 0.000000 469.977020 0.000000 479.985016 0.000000 490.040009 0.000000 499.919006 0.000000 509.996002 0.000000 520.049988 0.000000 530.056030 0.000000 539.983032 0.000000 550.044006 0.000000 559.981995 0.000000 570.023010 0.000000 579.888000 0.000000 590.100037 0.000000 600.097046 0.000000 619.900024 0.000000 640.062012 0.000000 659.819031 0.000000 680.088013 0.000000 700.056030 0.000000 719.976990 0.000000 740.179016 0.000000 760.147034 0.000000 779.747986 0.000000 799.871033 0.000000 819.974060 0.000000 839.973083 0.000000 859.778015 0.000000 879.915039 0.000000 899.709961 0.000000 ================================================ FILE: Asset/SPD/metals/Cu.eta.spd ================================================ 298.757050 1.400313 302.400421 1.380000 306.133759 1.358438 309.960449 1.340000 313.884003 1.329063 317.908142 1.325000 322.036835 1.332500 326.274139 1.340000 330.624481 1.334375 335.092377 1.325000 339.682678 1.317812 344.400482 1.310000 349.251221 1.300313 354.240509 1.290000 359.374420 1.281563 364.659332 1.270000 370.102020 1.249062 375.709625 1.225000 381.489777 1.200000 387.450562 1.180000 393.600555 1.174375 399.948975 1.175000 406.505493 1.177500 413.280579 1.180000 420.285339 1.178125 427.531647 1.175000 435.032196 1.172812 442.800629 1.170000 450.851562 1.165312 459.200653 1.160000 467.864838 1.155312 476.862213 1.150000 486.212463 1.142812 495.936707 1.135000 506.057861 1.131562 516.600769 1.120000 527.592224 1.092437 539.061646 1.040000 551.040771 0.950375 563.564453 0.826000 576.670593 0.645875 590.400818 0.468000 604.800842 0.351250 619.920898 0.272000 635.816284 0.230813 652.548279 0.214000 670.184753 0.209250 688.800964 0.213000 708.481018 0.216250 729.318665 0.223000 751.419250 0.236500 774.901123 0.250000 799.897949 0.254188 826.561157 0.260000 855.063293 0.280000 885.601257 0.300000 ================================================ FILE: Asset/SPD/metals/Cu.k.spd ================================================ 298.757050 1.662125 302.400421 1.687000 306.133759 1.703313 309.960449 1.720000 313.884003 1.744563 317.908142 1.770000 322.036835 1.791625 326.274139 1.810000 330.624481 1.822125 335.092377 1.834000 339.682678 1.851750 344.400482 1.872000 349.251221 1.894250 354.240509 1.916000 359.374420 1.931688 364.659332 1.950000 370.102020 1.972438 375.709625 2.015000 381.489777 2.121562 387.450562 2.210000 393.600555 2.177188 399.948975 2.130000 406.505493 2.160063 413.280579 2.210000 420.285339 2.249938 427.531647 2.289000 435.032196 2.326000 442.800629 2.362000 450.851562 2.397625 459.200653 2.433000 467.864838 2.469187 476.862213 2.504000 486.212463 2.535875 495.936707 2.564000 506.057861 2.589625 516.600769 2.605000 527.592224 2.595562 539.061646 2.583000 551.040771 2.576500 563.564453 2.599000 576.670593 2.678062 590.400818 2.809000 604.800842 3.010750 619.920898 3.240000 635.816284 3.458187 652.548279 3.670000 670.184753 3.863125 688.800964 4.050000 708.481018 4.239563 729.318665 4.430000 751.419250 4.619563 774.901123 4.817000 799.897949 5.034125 826.561157 5.260000 855.063293 5.485625 885.601257 5.717000 ================================================ FILE: Asset/SPD/metals/Cu2O.eta.spd ================================================ # data from www.luxpop.com database # ; Optical constants for Cu2O # ; # ; Concatenation of: # ; # ; Cu2O_llnl_cxro + Cu2O_palik # ; # ; Lambda (A) n k # ;----------------------------------------- 350.028015 2.400000 399.935028 2.800000 449.854980 3.060000 499.919006 3.120000 549.799988 3.100000 599.806030 3.020000 649.789978 2.900000 700.056030 2.830000 750.029968 2.770000 799.871033 2.700000 849.759949 2.660000 899.709961 2.630000 ================================================ FILE: Asset/SPD/metals/Cu2O.k.spd ================================================ # data from www.luxpop.com database # ; Optical constants for Cu2O # ; # ; Concatenation of: # ; # ; Cu2O_llnl_cxro + Cu2O_palik # ; # ; Lambda (A) n k # ;----------------------------------------- 350.028015 1.440000 399.935028 0.990000 449.854980 0.600000 499.919006 0.350000 549.799988 0.190000 599.806030 0.130000 649.789978 0.100000 700.056030 0.083000 750.029968 0.070000 799.871033 0.060000 849.759949 0.053000 899.709961 0.048000 ================================================ FILE: Asset/SPD/metals/CuO.eta.spd ================================================ # data from www.luxpop.com database # ; Optical constants for CuO # ; # ; Concatenation of: # ; # ; CuO_llnl_cxro + CuO_palik # ; # ; Lambda (A) n k # ;----------------------------------------- 350.028015 2.240000 399.935028 2.340000 449.854980 2.450000 499.919006 2.540000 549.799988 2.580000 599.806030 2.650000 649.789978 2.720000 700.056030 2.880000 750.029968 2.970000 799.871033 2.940000 849.759949 2.810000 899.709961 2.740000 ================================================ FILE: Asset/SPD/metals/CuO.k.spd ================================================ # data from www.luxpop.com database # ; Optical constants for CuO # ; # ; Concatenation of: # ; # ; CuO_llnl_cxro + CuO_palik # ; # ; Lambda (A) n k # ;----------------------------------------- 350.028015 1.030000 399.935028 0.870000 449.854980 0.770000 499.919006 0.680000 549.799988 0.590000 599.806030 0.500000 649.789978 0.400000 700.056030 0.310000 750.029968 0.220000 799.871033 0.110000 849.759949 0.040000 899.709961 0.030000 ================================================ FILE: Asset/SPD/metals/Hg.eta.spd ================================================ # data from www.luxpop.com database # ; Optical constants for Hg # ; # ; Concatenation of: # ; # ; Hg_llnl_cxro + Hg_palik # ; # ; Lambda (A) n k # ;----------------------------------------- 309.950012 0.542000 326.263000 0.589000 344.389008 0.644000 364.647003 0.713000 387.437988 0.798000 413.266998 0.898000 442.785980 1.027000 476.846008 1.186000 516.583008 1.384000 563.545044 1.620000 619.900024 1.910000 688.778015 2.284000 774.875000 2.746000 885.570984 3.324000 ================================================ FILE: Asset/SPD/metals/Hg.k.spd ================================================ # data from www.luxpop.com database # ; Optical constants for Hg # ; # ; Concatenation of: # ; # ; Hg_llnl_cxro + Hg_palik # ; # ; Lambda (A) n k # ;----------------------------------------- 309.950012 2.502000 326.263000 2.665000 344.389008 2.860000 364.647003 3.074000 387.437988 3.294000 413.266998 3.538000 442.785980 3.802000 476.846008 4.090000 516.583008 4.407000 563.545044 4.751000 619.900024 5.150000 688.778015 5.582000 774.875000 6.054000 885.570984 6.558000 ================================================ FILE: Asset/SPD/metals/HgTe.eta.spd ================================================ # data from www.luxpop.com database # ; Optical constants for HgTe # ; # ; taken from: # ; # ; 'Handbook of Optical Constants of Solids II', Ed. by Edward D. Palik, # ; Academic Press, Inc., 1991. # ; # ; Lambda (A) n k # ;----------------------------------------- 300.921997 2.481000 302.243011 2.480000 303.575012 2.479000 304.919006 2.478000 306.274994 2.476000 307.643005 2.475000 309.023010 2.474000 310.415985 2.473000 311.821014 2.471000 313.238983 2.470000 314.670013 2.470000 316.113983 2.469000 317.571991 2.468000 319.042999 2.468000 320.610016 2.467000 322.110016 2.467000 323.623016 2.467000 325.151001 2.467000 326.692993 2.467000 328.250000 2.466000 329.821991 2.467000 331.409027 2.467000 333.011017 2.467000 334.628998 2.467000 336.263000 2.468000 337.912018 2.469000 339.578003 2.470000 341.261017 2.470000 343.055023 2.472000 344.772003 2.473000 346.506012 2.474000 348.258026 2.476000 350.028015 2.478000 351.816010 2.480000 353.622009 2.481000 355.446991 2.484000 357.290985 2.487000 359.154022 2.489000 361.037018 2.492000 362.938995 2.495000 364.862030 2.499000 366.805023 2.502000 368.878021 2.506000 370.864990 2.510000 372.872009 2.515000 374.902008 2.520000 376.953003 2.525000 379.028015 2.530000 381.125000 2.536000 383.246002 2.542000 385.389984 2.548000 387.559021 2.555000 389.752014 2.563000 391.970001 2.570000 394.212982 2.578000 396.482025 2.587000 398.906006 2.596000 401.230011 2.606000 403.581024 2.616000 405.959015 2.627000 408.365997 2.639000 410.802002 2.651000 413.266998 2.664000 415.760986 2.678000 418.285980 2.694000 420.842010 2.710000 423.429016 2.727000 426.048004 2.746000 428.700012 2.768000 431.535004 2.790000 434.256012 2.816000 437.010986 2.844000 439.800995 2.874000 442.627991 2.908000 445.489990 2.945000 448.391022 2.983000 451.329010 3.020000 454.306000 3.056000 457.322021 3.088000 460.378998 3.115000 463.477020 3.138000 466.616028 3.159000 469.799042 3.175000 473.206024 3.190000 476.479980 3.203000 479.799042 3.214000 483.164032 3.223000 486.577972 3.231000 490.040009 3.239000 493.550995 3.245000 497.113007 3.252000 500.727020 3.259000 504.394012 3.265000 508.114990 3.272000 511.891022 3.279000 515.724060 3.287000 519.614014 3.294000 523.785034 3.303000 527.799011 3.313000 531.875000 3.324000 536.014038 3.335000 540.218018 3.349000 544.487976 3.364000 548.827026 3.382000 553.235046 3.402000 557.715027 3.425000 562.268005 3.454000 566.895020 3.488000 571.600037 3.532000 576.382996 3.586000 581.247009 3.650000 586.471008 3.719000 591.507996 3.785000 596.631042 3.838000 601.845032 3.876000 607.150024 3.901000 612.549011 3.917000 618.046021 3.925000 623.642029 3.929000 629.340027 3.929000 635.143005 3.924000 641.054993 3.919000 647.077026 3.911000 653.214050 3.903000 659.468018 3.892000 666.200989 3.882000 672.708008 3.870000 679.341980 3.860000 686.110046 3.848000 693.013000 3.837000 700.056030 3.826000 707.245056 3.815000 714.581970 3.805000 722.072998 3.795000 729.723022 3.786000 737.537048 3.776000 745.520020 3.767000 753.677979 3.756000 762.016052 3.747000 771.020020 3.738000 779.747986 3.729000 788.677002 3.719000 797.812012 3.711000 807.161011 3.702000 816.733032 3.691000 826.533020 3.685000 ================================================ FILE: Asset/SPD/metals/HgTe.k.spd ================================================ # data from www.luxpop.com database # ; Optical constants for HgTe # ; # ; taken from: # ; # ; 'Handbook of Optical Constants of Solids II', Ed. by Edward D. Palik, # ; Academic Press, Inc., 1991. # ; # ; Lambda (A) n k # ;----------------------------------------- 300.921997 1.782000 302.243011 1.771000 303.575012 1.761000 304.919006 1.752000 306.274994 1.744000 307.643005 1.736000 309.023010 1.729000 310.415985 1.722000 311.821014 1.716000 313.238983 1.711000 314.670013 1.706000 316.113983 1.701000 317.571991 1.697000 319.042999 1.693000 320.610016 1.690000 322.110016 1.687000 323.623016 1.684000 325.151001 1.681000 326.692993 1.679000 328.250000 1.677000 329.821991 1.675000 331.409027 1.674000 333.011017 1.673000 334.628998 1.672000 336.263000 1.672000 337.912018 1.671000 339.578003 1.671000 341.261017 1.671000 343.055023 1.672000 344.772003 1.673000 346.506012 1.674000 348.258026 1.675000 350.028015 1.677000 351.816010 1.679000 353.622009 1.681000 355.446991 1.683000 357.290985 1.686000 359.154022 1.689000 361.037018 1.692000 362.938995 1.696000 364.862030 1.699000 366.805023 1.703000 368.878021 1.707000 370.864990 1.712000 372.872009 1.716000 374.902008 1.722000 376.953003 1.727000 379.028015 1.732000 381.125000 1.738000 383.246002 1.743000 385.389984 1.750000 387.559021 1.756000 389.752014 1.763000 391.970001 1.770000 394.212982 1.777000 396.482025 1.784000 398.906006 1.792000 401.230011 1.800000 403.581024 1.808000 405.959015 1.816000 408.365997 1.825000 410.802002 1.834000 413.266998 1.844000 415.760986 1.853000 418.285980 1.863000 420.842010 1.873000 423.429016 1.883000 426.048004 1.893000 428.700012 1.904000 431.535004 1.914000 434.256012 1.924000 437.010986 1.933000 439.800995 1.940000 442.627991 1.945000 445.489990 1.946000 448.391022 1.942000 451.329010 1.932000 454.306000 1.917000 457.322021 1.898000 460.378998 1.877000 463.477020 1.856000 466.616028 1.833000 469.799042 1.812000 473.206024 1.791000 476.479980 1.771000 479.799042 1.753000 483.164032 1.736000 486.577972 1.721000 490.040009 1.707000 493.550995 1.695000 497.113007 1.683000 500.727020 1.674000 504.394012 1.665000 508.114990 1.658000 511.891022 1.653000 515.724060 1.647000 519.614014 1.644000 523.785034 1.641000 527.799011 1.641000 531.875000 1.640000 536.014038 1.642000 540.218018 1.645000 544.487976 1.649000 548.827026 1.653000 553.235046 1.660000 557.715027 1.668000 562.268005 1.677000 566.895020 1.687000 571.600037 1.696000 576.382996 1.700000 581.247009 1.694000 586.471008 1.670000 591.507996 1.626000 596.631042 1.569000 601.845032 1.507000 607.150024 1.443000 612.549011 1.382000 618.046021 1.325000 623.642029 1.272000 629.340027 1.222000 635.143005 1.176000 641.054993 1.134000 647.077026 1.094000 653.214050 1.058000 659.468018 1.024000 666.200989 0.992300 672.708008 0.964200 679.341980 0.935800 686.110046 0.910700 693.013000 0.886300 700.056030 0.863300 707.245056 0.841900 714.581970 0.822600 722.072998 0.802400 729.723022 0.782800 737.537048 0.764400 745.520020 0.747400 753.677979 0.735100 762.016052 0.722500 771.020020 0.709700 779.747986 0.695700 788.677002 0.686400 797.812012 0.671600 807.161011 0.660000 816.733032 0.655200 826.533020 0.638200 ================================================ FILE: Asset/SPD/metals/Ir.eta.spd ================================================ # data from www.luxpop.com database # ; Optical constants for Ir # ; # ; Concatenation of: # ; # ; Ir_llnl_cxro + Ir_palik # ; # ; Lambda (A) n k # ;----------------------------------------- 302.389984 1.620000 309.950012 1.640000 317.897003 1.640000 326.263000 1.610000 335.081024 1.570000 344.389008 1.520000 354.229004 1.500000 364.647003 1.530000 375.696991 1.570000 387.437988 1.620000 399.935028 1.680000 413.266998 1.730000 427.516998 1.770000 442.785980 1.810000 459.185028 1.850000 476.846008 1.910000 495.920013 1.980000 516.583008 2.070000 539.044006 2.180000 563.545044 2.290000 590.381042 2.400000 619.900024 2.500000 652.526001 2.570000 688.778015 2.640000 729.294006 2.690000 774.875000 2.680000 826.533020 2.650000 885.570984 2.720000 ================================================ FILE: Asset/SPD/metals/Ir.k.spd ================================================ # data from www.luxpop.com database # ; Optical constants for Ir # ; # ; Concatenation of: # ; # ; Ir_llnl_cxro + Ir_palik # ; # ; Lambda (A) n k # ;----------------------------------------- 302.389984 2.700000 309.950012 2.680000 317.897003 2.670000 326.263000 2.690000 335.081024 2.720000 344.389008 2.810000 354.229004 2.930000 364.647003 3.050000 375.696991 3.150000 387.437988 3.260000 399.935028 3.350000 413.266998 3.430000 427.516998 3.510000 442.785980 3.610000 459.185028 3.730000 476.846008 3.860000 495.920013 4.000000 516.583008 4.140000 539.044006 4.260000 563.545044 4.380000 590.381042 4.480000 619.900024 4.570000 652.526001 4.680000 688.778015 4.810000 729.294006 4.920000 774.875000 5.080000 826.533020 5.390000 885.570984 5.740000 ================================================ FILE: Asset/SPD/metals/K.eta.spd ================================================ # data from www.luxpop.com database # ; Optical constants for polycrystalline K # ; # ; taken from: # ; # ; 'Handbook of Optical Constants of Solids II', Ed. by Edward D. Palik, # ; Academic Press, Inc., 1991. # ; # ; Lambda (A) n k # ;----------------------------------------- 304.993988 0.380000 309.950012 0.340000 312.291992 0.287000 334.178009 0.089000 364.647003 0.052000 405.162994 0.041000 420.270996 0.041000 439.645020 0.043000 469.621002 0.043000 506.041016 0.046000 546.166992 0.049000 598.937012 0.053000 662.995056 0.050000 751.393982 0.044000 860.971985 0.040000 ================================================ FILE: Asset/SPD/metals/K.k.spd ================================================ # data from www.luxpop.com database # ; Optical constants for polycrystalline K # ; # ; taken from: # ; # ; 'Handbook of Optical Constants of Solids II', Ed. by Edward D. Palik, # ; Academic Press, Inc., 1991. # ; # ; Lambda (A) n k # ;----------------------------------------- 304.993988 0.070000 309.950012 0.080000 312.291992 0.091000 334.178009 0.288000 364.647003 0.549000 405.162994 0.799000 420.270996 0.898000 439.645020 1.020000 469.621002 1.140000 506.041016 1.280000 546.166992 1.430000 598.937012 1.620000 662.995056 1.840000 751.393982 2.190000 860.971985 2.560000 ================================================ FILE: Asset/SPD/metals/KBr.eta.spd ================================================ # data from www.luxpop.com database # ; Optical constants for cubic KBr # ; # ; taken from: # ; # ; 'Handbook of Optical Constants of Solids II', Ed. by Edward D. Palik, # ; Academic Press, Inc., 1991. # ; # ; Lambda (A) n k # ;----------------------------------------- 310.027008 1.638860 320.031006 1.630710 329.997009 1.623570 339.951019 1.617270 350.028015 1.611670 359.988007 1.606680 369.979004 1.602200 379.957001 1.598160 389.997009 1.594500 399.935028 1.591170 419.985992 1.585370 439.957001 1.580480 460.037018 1.576320 479.985016 1.572740 499.919006 1.569640 520.049988 1.566940 539.983032 1.564560 559.981995 1.562470 579.888000 1.560600 600.097046 1.558940 619.900024 1.557440 640.062012 1.556100 659.819031 1.554880 680.088013 1.553780 700.056030 1.552780 719.976990 1.551860 740.179016 1.551020 760.147034 1.550250 779.747986 1.549540 799.871033 1.548880 819.974060 1.548280 839.973083 1.547720 859.778015 1.547200 879.915039 1.546710 899.709961 1.546260 ================================================ FILE: Asset/SPD/metals/KBr.k.spd ================================================ # data from www.luxpop.com database # ; Optical constants for cubic KBr # ; # ; taken from: # ; # ; 'Handbook of Optical Constants of Solids II', Ed. by Edward D. Palik, # ; Academic Press, Inc., 1991. # ; # ; Lambda (A) n k # ;----------------------------------------- 310.027008 0.000000 320.031006 0.000000 329.997009 0.000000 339.951019 0.000000 350.028015 0.000000 359.988007 0.000000 369.979004 0.000000 379.957001 0.000000 389.997009 0.000000 399.935028 0.000000 419.985992 0.000000 439.957001 0.000000 460.037018 0.000000 479.985016 0.000000 499.919006 0.000000 520.049988 0.000000 539.983032 0.000000 559.981995 0.000000 579.888000 0.000000 600.097046 0.000000 619.900024 0.000000 640.062012 0.000000 659.819031 0.000000 680.088013 0.000000 700.056030 0.000000 719.976990 0.000000 740.179016 0.000000 760.147034 0.000000 779.747986 0.000000 799.871033 0.000000 819.974060 0.000000 839.973083 0.000000 859.778015 0.000000 879.915039 0.000000 899.709961 0.000000 ================================================ FILE: Asset/SPD/metals/KCl.eta.spd ================================================ # data from www.luxpop.com database # ; Optical constants for KCl # ; # ; taken from: # ; # ; 'Handbook of Optical Constants of Solids', Ed. by Edward D. Palik, # ; Academic Press, Inc., 1985. # ; # ; Lambda (A) n k # ;----------------------------------------- 310.027008 1.540050 320.031006 1.535180 329.997009 1.530870 339.951019 1.527030 350.028015 1.523580 359.988007 1.520490 369.979004 1.517690 379.957001 1.515150 389.997009 1.512830 399.935028 1.510720 419.985992 1.507010 439.957001 1.503860 460.037018 1.501150 479.985016 1.498820 499.919006 1.496790 520.049988 1.495010 539.983032 1.493440 559.981995 1.492050 579.888000 1.490810 600.097046 1.489690 619.900024 1.488690 640.062012 1.487790 659.819031 1.486970 680.088013 1.486230 700.056030 1.485550 719.976990 1.484930 740.179016 1.484360 760.147034 1.483840 779.747986 1.483360 799.871033 1.482910 819.974060 1.482500 839.973083 1.482120 859.778015 1.481760 879.915039 1.481430 899.709961 1.481110 ================================================ FILE: Asset/SPD/metals/KCl.k.spd ================================================ # data from www.luxpop.com database # ; Optical constants for KCl # ; # ; taken from: # ; # ; 'Handbook of Optical Constants of Solids', Ed. by Edward D. Palik, # ; Academic Press, Inc., 1985. # ; # ; Lambda (A) n k # ;----------------------------------------- 310.027008 0.000000 320.031006 0.000000 329.997009 0.000000 339.951019 0.000000 350.028015 0.000000 359.988007 0.000000 369.979004 0.000000 379.957001 0.000000 389.997009 0.000000 399.935028 0.000000 419.985992 0.000000 439.957001 0.000000 460.037018 0.000000 479.985016 0.000000 499.919006 0.000000 520.049988 0.000000 539.983032 0.000000 559.981995 0.000000 579.888000 0.000000 600.097046 0.000000 619.900024 0.000000 640.062012 0.000000 659.819031 0.000000 680.088013 0.000000 700.056030 0.000000 719.976990 0.000000 740.179016 0.000000 760.147034 0.000000 779.747986 0.000000 799.871033 0.000000 819.974060 0.000000 839.973083 0.000000 859.778015 0.000000 879.915039 0.000000 899.709961 0.000000 ================================================ FILE: Asset/SPD/metals/Li.eta.spd ================================================ # data from www.luxpop.com database # ; Optical constants for Li # ; # ; taken from: # ; # ; 'Handbook of Optical Constants of Solids II', Ed. by Edward D. Palik, # ; Academic Press, Inc., 1991. # ; # ; Lambda (A) n k # ;----------------------------------------- 306.123016 0.346000 330.613007 0.345000 359.362030 0.334000 393.587006 0.304000 435.018036 0.247000 486.196014 0.217000 551.022034 0.206000 635.795044 0.221000 751.393982 0.265000 ================================================ FILE: Asset/SPD/metals/Li.k.spd ================================================ # data from www.luxpop.com database # ; Optical constants for Li # ; # ; taken from: # ; # ; 'Handbook of Optical Constants of Solids II', Ed. by Edward D. Palik, # ; Academic Press, Inc., 1991. # ; # ; Lambda (A) n k # ;----------------------------------------- 306.123016 1.210000 330.613007 1.320000 359.362030 1.450000 393.587006 1.600000 435.018036 1.820000 486.196014 2.110000 551.022034 2.480000 635.795044 2.940000 751.393982 3.550000 ================================================ FILE: Asset/SPD/metals/MgO.eta.spd ================================================ # data from www.luxpop.com database # ; Optical constants for MgO # ; # ; Concatenation of: # ; # ; MgO_llnl_cxro + MgO_palik # ; # ; Lambda (A) n k # ;----------------------------------------- 309.950012 1.798000 330.613007 1.785000 351.118988 1.776800 355.549011 1.775500 360.932007 1.773200 361.141998 1.773180 364.968994 1.771860 382.065002 1.766800 386.712006 1.765500 393.337982 1.763800 404.634003 1.761040 430.935028 1.755700 435.781982 1.754710 457.829010 1.751200 477.949036 1.748300 486.004974 1.747110 487.918030 1.746900 499.919006 1.745400 502.350006 1.745300 508.531982 1.744460 514.440002 1.743900 546.166992 1.740770 589.258972 1.737370 632.874023 1.734600 643.718018 1.734000 656.325989 1.733350 667.635986 1.732770 690.695984 1.731910 706.439026 1.731010 767.677979 1.728720 ================================================ FILE: Asset/SPD/metals/MgO.k.spd ================================================ # data from www.luxpop.com database # ; Optical constants for MgO # ; # ; Concatenation of: # ; # ; MgO_llnl_cxro + MgO_palik # ; # ; Lambda (A) n k # ;----------------------------------------- 309.950012 0.000000 330.613007 0.000000 351.118988 0.000000 355.549011 0.000001 360.932007 0.000001 361.141998 0.000001 364.968994 0.000000 382.065002 0.000000 386.712006 0.000000 393.337982 0.000000 404.634003 0.000000 430.935028 0.000000 435.781982 0.000000 457.829010 0.000000 477.949036 0.000000 486.004974 0.000000 487.918030 0.000000 499.919006 0.000000 502.350006 0.000000 508.531982 0.000000 514.440002 0.000000 546.166992 0.000000 589.258972 0.000000 632.874023 0.000000 643.718018 0.000000 656.325989 0.000000 667.635986 0.000000 690.695984 0.000000 706.439026 0.000000 767.677979 0.000000 ================================================ FILE: Asset/SPD/metals/Mo.eta.spd ================================================ # data from www.luxpop.com database # ; Optical constants for # ; # ; Concatenation of: # ; # ; Mo_llnl_cxro + Mo_palik # ; # ; Lambda (A) n k # ;----------------------------------------- 302.389984 2.910000 309.950012 3.010000 317.897003 3.040000 326.263000 3.040000 335.081024 3.040000 344.389008 3.050000 354.229004 3.060000 364.647003 3.060000 375.696991 3.060000 387.437988 3.050000 399.935028 3.030000 413.266998 3.040000 427.516998 3.050000 442.785980 3.080000 459.185028 3.130000 476.846008 3.220000 495.920013 3.360000 516.583008 3.590000 539.044006 3.790000 563.545044 3.760000 590.381042 3.680000 619.900024 3.680000 652.526001 3.740000 688.778015 3.810000 729.294006 3.840000 774.875000 3.770000 826.533020 3.530000 885.570984 3.150000 ================================================ FILE: Asset/SPD/metals/Mo.k.spd ================================================ # data from www.luxpop.com database # ; Optical constants for # ; # ; Concatenation of: # ; # ; Mo_llnl_cxro + Mo_palik # ; # ; Lambda (A) n k # ;----------------------------------------- 302.389984 3.670000 309.950012 3.510000 317.897003 3.400000 326.263000 3.310000 335.081024 3.270000 344.389008 3.240000 354.229004 3.210000 364.647003 3.190000 375.696991 3.180000 387.437988 3.180000 399.935028 3.220000 413.266998 3.270000 427.516998 3.330000 442.785980 3.420000 459.185028 3.510000 476.846008 3.610000 495.920013 3.730000 516.583008 3.780000 539.044006 3.610000 563.545044 3.410000 590.381042 3.450000 619.900024 3.520000 652.526001 3.580000 688.778015 3.580000 729.294006 3.510000 774.875000 3.410000 826.533020 3.300000 885.570984 3.400000 ================================================ FILE: Asset/SPD/metals/NaCl.eta.spd ================================================ # data from www.luxpop.com database # ; Optical constants for NaCl # ; # ; taken from: # ; # ; 'Handbook of Optical Constants of Solids', Ed. by Edward D. Palik, # ; Academic Press, Inc., 1985. # ; # ; Lambda (A) n k # ;----------------------------------------- 319.947998 1.595060 339.951019 1.585900 359.988007 1.578520 379.957001 1.572480 399.935028 1.567460 419.985992 1.563240 439.957001 1.559650 460.037018 1.556570 479.985016 1.553900 499.919006 1.551570 520.049988 1.549530 539.983032 1.547730 559.981995 1.546130 579.888000 1.544710 600.097046 1.543430 619.900024 1.542280 640.062012 1.541240 659.819031 1.540300 680.088013 1.539440 700.056030 1.538650 719.976990 1.537940 740.179016 1.537280 760.147034 1.536670 779.747986 1.536110 799.871033 1.535600 819.974060 1.535120 839.973083 1.534670 859.778015 1.534260 879.915039 1.533870 899.709961 1.533510 ================================================ FILE: Asset/SPD/metals/NaCl.k.spd ================================================ # data from www.luxpop.com database # ; Optical constants for NaCl # ; # ; taken from: # ; # ; 'Handbook of Optical Constants of Solids', Ed. by Edward D. Palik, # ; Academic Press, Inc., 1985. # ; # ; Lambda (A) n k # ;----------------------------------------- 319.947998 0.000000 339.951019 0.000000 359.988007 0.000000 379.957001 0.000000 399.935028 0.000000 419.985992 0.000000 439.957001 0.000000 460.037018 0.000000 479.985016 0.000000 499.919006 0.000000 520.049988 0.000000 539.983032 0.000000 559.981995 0.000000 579.888000 0.000000 600.097046 0.000000 619.900024 0.000000 640.062012 0.000000 659.819031 0.000000 680.088013 0.000000 700.056030 0.000000 719.976990 0.000000 740.179016 0.000000 760.147034 0.000000 779.747986 0.000000 799.871033 0.000000 819.974060 0.000000 839.973083 0.000000 859.778015 0.000000 879.915039 0.000000 899.709961 0.000000 ================================================ FILE: Asset/SPD/metals/Nb.eta.spd ================================================ # data from www.luxpop.com database # ; Optical constants for Nb # ; # ; Concatenation of: # ; # ; Nb_llnl_cxro + Nb_palik # ; # ; Lambda (A) n k # ;----------------------------------------- 309.950012 2.640000 317.897003 2.620000 326.263000 2.590000 335.081024 2.560000 344.389008 2.520000 354.229004 2.480000 364.647003 2.460000 375.696991 2.440000 387.437988 2.450000 399.935028 2.480000 413.266998 2.510000 435.018036 2.580000 450.835999 2.660000 467.849030 2.740000 486.196014 2.830000 506.041016 2.890000 527.575012 2.920000 551.022034 2.930000 576.651001 2.920000 604.781006 2.890000 635.795044 2.820000 670.162048 2.690000 708.456970 2.540000 751.393982 2.360000 799.871033 2.150000 855.033997 1.950000 ================================================ FILE: Asset/SPD/metals/Nb.k.spd ================================================ # data from www.luxpop.com database # ; Optical constants for Nb # ; # ; Concatenation of: # ; # ; Nb_llnl_cxro + Nb_palik # ; # ; Lambda (A) n k # ;----------------------------------------- 309.950012 2.330000 317.897003 2.290000 326.263000 2.280000 335.081024 2.270000 344.389008 2.290000 354.229004 2.330000 364.647003 2.380000 375.696991 2.450000 387.437988 2.530000 399.935028 2.600000 413.266998 2.680000 435.018036 2.800000 450.835999 2.860000 467.849030 2.900000 486.196014 2.920000 506.041016 2.900000 527.575012 2.880000 551.022034 2.870000 576.651001 2.870000 604.781006 2.870000 635.795044 2.860000 670.162048 2.890000 708.456970 2.990000 751.393982 3.130000 799.871033 3.370000 855.033997 3.680000 ================================================ FILE: Asset/SPD/metals/Rh.eta.spd ================================================ # data from www.luxpop.com database # ; Optical constants for Rh # ; # ; Concatenation of: # ; # ; Rh_llnl_cxro + Rh_palik # ; # ; Lambda (A) n k # ;----------------------------------------- 302.389984 0.840000 309.950012 0.860000 317.897003 0.880000 326.263000 0.910000 335.081024 0.950000 344.389008 0.990000 354.229004 1.040000 364.647003 1.110000 375.696991 1.200000 387.437988 1.300000 399.935028 1.410000 413.266998 1.530000 427.516998 1.630000 459.185028 1.800000 476.846008 1.850000 495.920013 1.880000 516.583008 1.900000 539.044006 1.940000 563.545044 2.000000 590.381042 2.050000 619.900024 2.120000 652.526001 2.200000 688.778015 2.300000 729.294006 2.420000 774.875000 2.600000 826.533020 2.780000 885.570984 3.010000 ================================================ FILE: Asset/SPD/metals/Rh.k.spd ================================================ # data from www.luxpop.com database # ; Optical constants for Rh # ; # ; Concatenation of: # ; # ; Rh_llnl_cxro + Rh_palik # ; # ; Lambda (A) n k # ;----------------------------------------- 302.389984 3.030000 309.950012 3.120000 317.897003 3.230000 326.263000 3.340000 335.081024 3.450000 344.389008 3.580000 354.229004 3.710000 364.647003 3.840000 375.696991 3.970000 387.437988 4.090000 399.935028 4.200000 413.266998 4.290000 427.516998 4.360000 459.185028 4.490000 476.846008 4.550000 495.920013 4.650000 516.583008 4.780000 539.044006 4.940000 563.545044 5.110000 590.381042 5.300000 619.900024 5.510000 652.526001 5.760000 688.778015 6.020000 729.294006 6.330000 774.875000 6.640000 826.533020 6.970000 885.570984 7.310000 ================================================ FILE: Asset/SPD/metals/Se-e.eta.spd ================================================ # data from www.luxpop.com database # ; Optical constants for Se, extraordinary ray # ; # ; taken from: # ; # ; 'Handbook of Optical Constants of Solids II', Ed. by Edward D. Palik, # ; Academic Press, Inc., 1991. # ; # ; Lambda (A) n k # ;----------------------------------------- 344.389008 3.390000 364.647003 3.690000 387.437988 3.920000 413.266998 4.440000 442.785980 4.590000 476.846008 4.400000 516.583008 4.280000 563.545044 4.490000 619.900024 4.790000 688.778015 4.460000 ================================================ FILE: Asset/SPD/metals/Se-e.k.spd ================================================ # data from www.luxpop.com database # ; Optical constants for Se, extraordinary ray # ; # ; taken from: # ; # ; 'Handbook of Optical Constants of Solids II', Ed. by Edward D. Palik, # ; Academic Press, Inc., 1991. # ; # ; Lambda (A) n k # ;----------------------------------------- 344.389008 3.010000 364.647003 2.760000 387.437988 2.590000 413.266998 2.290000 442.785980 1.700000 476.846008 1.320000 516.583008 1.210000 563.545044 1.190000 619.900024 0.760000 688.778015 0.022000 ================================================ FILE: Asset/SPD/metals/Se.eta.spd ================================================ # data from www.luxpop.com database # ; Optical constants for Se, ordinary ray # ; # ; taken from: # ; # ; 'Handbook of Optical Constants of Solids II', Ed. by Edward D. Palik, # ; Academic Press, Inc., 1991. # ; # ; Lambda (A) n k # ;----------------------------------------- 309.950012 2.670000 326.263000 2.840000 344.389008 3.060000 364.647003 3.220000 387.437988 3.350000 413.266998 3.300000 442.785980 3.120000 476.846008 3.000000 516.583008 2.930000 563.545044 3.070000 619.900024 3.380000 688.778015 3.320000 ================================================ FILE: Asset/SPD/metals/Se.k.spd ================================================ # data from www.luxpop.com database # ; Optical constants for Se, ordinary ray # ; # ; taken from: # ; # ; 'Handbook of Optical Constants of Solids II', Ed. by Edward D. Palik, # ; Academic Press, Inc., 1991. # ; # ; Lambda (A) n k # ;----------------------------------------- 309.950012 1.990000 326.263000 1.660000 344.389008 1.470000 364.647003 1.240000 387.437988 1.010000 413.266998 0.700000 442.785980 0.580000 476.846008 0.530000 516.583008 0.610000 563.545044 0.730000 619.900024 0.650000 688.778015 0.110000 ================================================ FILE: Asset/SPD/metals/SiC.eta.spd ================================================ # data from www.luxpop.com database # ; Optical constants for crystalline SiC # ; # ; Concatenation of: # ; # ; SiC_llnl_cxro + SiC_palik # ; # ; Lambda (A) n k # ;----------------------------------------- 309.950012 2.850000 317.897003 2.923000 326.263000 2.899000 335.081024 2.877000 344.389008 2.856000 354.229004 2.836000 364.647003 2.817000 375.696991 2.799000 387.437988 2.781000 399.935028 2.765000 413.266998 2.750000 435.781982 2.730500 466.968018 2.707400 495.920013 2.684000 497.912018 2.687000 515.080994 2.678900 546.166992 2.663100 567.934021 2.655700 577.995056 2.651100 588.979004 2.648800 589.539001 2.647500 615.897034 2.641100 619.900024 2.634000 656.325989 2.629600 691.080994 2.624300 826.533020 2.598000 ================================================ FILE: Asset/SPD/metals/SiC.k.spd ================================================ # data from www.luxpop.com database # ; Optical constants for crystalline SiC # ; # ; Concatenation of: # ; # ; SiC_llnl_cxro + SiC_palik # ; # ; Lambda (A) n k # ;----------------------------------------- 309.950012 0.174000 317.897003 0.007650 326.263000 0.005980 335.081024 0.004930 344.389008 0.003750 354.229004 0.002890 364.647003 0.002040 375.696991 0.001380 387.437988 0.000839 399.935028 0.000191 413.266998 0.000044 435.781982 0.000000 466.968018 0.000000 495.920013 0.000012 497.912018 0.000000 515.080994 0.000000 546.166992 0.000000 567.934021 0.000000 577.995056 0.000000 588.979004 0.000000 589.539001 0.000000 615.897034 0.000000 619.900024 0.000000 656.325989 0.000000 691.080994 0.000000 826.533020 0.000000 ================================================ FILE: Asset/SPD/metals/SiO.eta.spd ================================================ # data from www.luxpop.com database # ; Optical constants for SiO # ; # ; Concatenation of: # ; # ; SiO_llnl_cxro + SiO_palik # ; # ; Lambda (A) n k # ;----------------------------------------- 309.950012 2.141000 326.263000 2.157000 344.389008 2.162000 364.647003 2.160000 387.437988 2.144000 413.266998 2.116000 442.785980 2.085000 476.846008 2.053000 516.583008 2.021000 563.545044 1.994000 619.900024 1.969000 688.778015 1.948000 774.875000 1.929000 885.570984 1.913000 ================================================ FILE: Asset/SPD/metals/SiO.k.spd ================================================ # data from www.luxpop.com database # ; Optical constants for SiO # ; # ; Concatenation of: # ; # ; SiO_llnl_cxro + SiO_palik # ; # ; Lambda (A) n k # ;----------------------------------------- 309.950012 0.400600 326.263000 0.345300 344.389008 0.287200 364.647003 0.228700 387.437988 0.170600 413.266998 0.121100 442.785980 0.083740 476.846008 0.055440 516.583008 0.035330 563.545044 0.021530 619.900024 0.011750 688.778015 0.005230 774.875000 0.001510 885.570984 0.000000 ================================================ FILE: Asset/SPD/metals/SnTe.eta.spd ================================================ # data from www.luxpop.com database # ; Optical constants for SnTe # ; # ; taken from: # ; # ; 'Handbook of Optical Constants of Solids II', Ed. by Edward D. Palik, # ; Academic Press, Inc., 1991. # ; # ; Lambda (A) n k # ;----------------------------------------- 311.586029 0.967000 332.118988 1.007000 361.037018 1.103000 390.242004 1.228000 414.372040 1.364000 453.308990 1.551000 498.312012 1.702000 533.476990 1.813000 548.099060 1.878000 550.533020 1.989000 552.987976 2.135000 558.216980 2.442000 566.119019 2.719000 582.339050 2.991000 605.963013 3.273000 621.454041 3.479000 648.091980 3.701000 681.208984 3.912000 748.671021 4.285000 804.020996 4.541000 867.478027 4.803000 ================================================ FILE: Asset/SPD/metals/SnTe.k.spd ================================================ # data from www.luxpop.com database # ; Optical constants for SnTe # ; # ; taken from: # ; # ; 'Handbook of Optical Constants of Solids II', Ed. by Edward D. Palik, # ; Academic Press, Inc., 1991. # ; # ; Lambda (A) n k # ;----------------------------------------- 311.586029 0.000000 332.118988 0.000000 361.037018 0.000000 390.242004 0.000000 414.372040 0.000000 453.308990 0.000000 498.312012 0.000000 533.476990 0.000000 548.099060 0.000000 550.533020 0.000000 552.987976 0.000000 558.216980 0.000000 566.119019 0.000000 582.339050 0.000000 605.963013 0.000000 621.454041 0.000000 648.091980 0.000000 681.208984 0.000000 748.671021 0.000000 804.020996 0.000000 867.478027 0.000000 ================================================ FILE: Asset/SPD/metals/Ta.eta.spd ================================================ # data from www.luxpop.com database # ; Optical constants for Ta # ; # ; Concatenation of: # ; # ; Ta_llnl_cxro + Ta_palik # ; # ; Lambda (A) n k # ;----------------------------------------- 309.950012 2.360000 326.263000 2.400000 344.389008 2.490000 364.647003 2.610000 387.437988 2.730000 413.266998 2.810000 427.516998 2.840000 442.785980 2.850000 459.185028 2.840000 476.846008 2.800000 495.920013 2.750000 516.583008 2.680000 539.044006 2.560000 563.545044 2.360000 590.381042 2.100000 619.900024 1.830000 652.526001 1.570000 688.778015 1.350000 729.294006 1.240000 774.875000 1.150000 826.533020 1.090000 885.570984 1.040000 ================================================ FILE: Asset/SPD/metals/Ta.k.spd ================================================ # data from www.luxpop.com database # ; Optical constants for Ta # ; # ; Concatenation of: # ; # ; Ta_llnl_cxro + Ta_palik # ; # ; Lambda (A) n k # ;----------------------------------------- 309.950012 2.140000 326.263000 2.220000 344.389008 2.300000 364.647003 2.330000 387.437988 2.310000 413.266998 2.240000 427.516998 2.200000 442.785980 2.140000 459.185028 2.080000 476.846008 2.020000 495.920013 1.980000 516.583008 1.920000 539.044006 1.860000 563.545044 1.810000 590.381042 1.840000 619.900024 1.990000 652.526001 2.240000 688.778015 2.600000 729.294006 2.950000 774.875000 3.330000 826.533020 3.730000 885.570984 4.150000 ================================================ FILE: Asset/SPD/metals/Te-e.eta.spd ================================================ # data from www.luxpop.com database # ; Optical constants for trigonal Te, extraordinary ray # ; # ; taken from: # ; # ; 'Handbook of Optical Constants of Solids II', Ed. by Edward D. Palik, # ; Academic Press, Inc., 1991. # ; # ; Lambda (A) n k # ;----------------------------------------- 309.950012 1.330000 330.613007 1.440000 354.229004 1.690000 381.477020 1.920000 403.975006 2.220000 413.266998 2.300000 416.599030 2.350000 430.037994 2.540000 442.785980 2.730000 444.373016 2.760000 459.696014 2.980000 476.114014 3.210000 476.846008 3.250000 489.266022 3.460000 498.512024 3.630000 507.906982 3.810000 516.583008 3.940000 517.877991 3.990000 528.024048 4.200000 538.809021 4.390000 549.799988 4.650000 561.249023 4.910000 563.545044 4.940000 573.450989 5.170000 599.226990 5.760000 612.851990 6.000000 619.900024 4.670000 627.429016 6.210000 642.382996 6.400000 658.416992 6.580000 674.904968 6.770000 688.778015 6.890000 692.625977 6.850000 710.895020 6.860000 730.583008 6.800000 750.939026 6.780000 772.943054 6.740000 774.875000 6.750000 796.275024 6.730000 820.516052 6.810000 846.858032 6.950000 874.330017 7.140000 885.570984 7.110000 ================================================ FILE: Asset/SPD/metals/Te-e.k.spd ================================================ # data from www.luxpop.com database # ; Optical constants for trigonal Te, extraordinary ray # ; # ; taken from: # ; # ; 'Handbook of Optical Constants of Solids II', Ed. by Edward D. Palik, # ; Academic Press, Inc., 1991. # ; # ; Lambda (A) n k # ;----------------------------------------- 309.950012 2.640000 330.613007 3.060000 354.229004 3.440000 381.477020 3.780000 403.975006 3.960000 413.266998 4.160000 416.599030 4.150000 430.037994 4.270000 442.785980 4.420000 444.373016 4.390000 459.696014 4.520000 476.114014 4.710000 476.846008 4.770000 489.266022 4.850000 498.512024 4.930000 507.906982 5.010000 516.583008 5.080000 517.877991 5.080000 528.024048 5.160000 538.809021 5.200000 549.799988 5.210000 561.249023 5.200000 563.545044 5.160000 573.450989 5.160000 599.226990 4.920000 612.851990 4.770000 619.900024 4.670000 627.429016 4.600000 642.382996 4.360000 658.416992 4.100000 674.904968 3.860000 688.778015 3.700000 692.625977 3.650000 710.895020 3.470000 730.583008 3.180000 750.939026 3.030000 772.943054 2.930000 774.875000 2.910000 796.275024 2.890000 820.516052 2.860000 846.858032 2.800000 874.330017 2.560000 885.570984 2.460000 ================================================ FILE: Asset/SPD/metals/Te.eta.spd ================================================ # data from www.luxpop.com database # ; Optical constants for trigonal Te,ordinary ray # ; # ; taken from: # ; # ; 'Handbook of Optical Constants of Solids II', Ed. by Edward D. Palik, # ; Academic Press, Inc., 1991. # ; # ; Lambda (A) n k # ;----------------------------------------- 309.950012 1.320000 330.613007 1.500000 354.229004 1.720000 381.477020 2.020000 403.975006 2.370000 413.266998 2.510000 416.599030 2.560000 430.037994 2.770000 442.785980 3.030000 444.373016 3.010000 459.696014 3.270000 476.114014 3.550000 476.846008 3.570000 489.266022 3.760000 498.512024 3.910000 507.906982 4.070000 516.583008 4.240000 517.877991 4.250000 528.024048 4.430000 538.809021 4.620000 549.799988 4.810000 561.249023 5.010000 563.545044 5.100000 573.450989 5.260000 599.226990 5.670000 612.851990 5.820000 619.900024 5.940000 627.429016 5.970000 642.382996 6.070000 658.416992 6.110000 674.904968 6.120000 688.778015 6.100000 692.625977 6.090000 710.895020 6.040000 730.583008 5.950000 750.939026 5.870000 772.943054 5.860000 774.875000 5.880000 796.275024 5.840000 820.516052 5.780000 846.858032 5.680000 874.330017 5.590000 885.570984 5.560000 ================================================ FILE: Asset/SPD/metals/Te.k.spd ================================================ # data from www.luxpop.com database # ; Optical constants for trigonal Te,ordinary ray # ; # ; taken from: # ; # ; 'Handbook of Optical Constants of Solids II', Ed. by Edward D. Palik, # ; Academic Press, Inc., 1991. # ; # ; Lambda (A) n k # ;----------------------------------------- 309.950012 2.010000 330.613007 2.370000 354.229004 2.700000 381.477020 3.030000 403.975006 3.290000 413.266998 3.390000 416.599030 3.390000 430.037994 3.470000 442.785980 3.630000 444.373016 3.570000 459.696014 3.660000 476.114014 3.720000 476.846008 3.750000 489.266022 3.760000 498.512024 3.770000 507.906982 3.770000 516.583008 3.770000 517.877991 3.780000 528.024048 3.760000 538.809021 3.690000 549.799988 3.630000 561.249023 3.570000 563.545044 3.610000 573.450989 3.520000 599.226990 3.160000 612.851990 2.940000 619.900024 2.690000 627.429016 2.700000 642.382996 2.450000 658.416992 2.250000 674.904968 2.020000 688.778015 1.800000 692.625977 1.720000 710.895020 1.520000 730.583008 1.360000 750.939026 1.260000 772.943054 1.170000 774.875000 1.150000 796.275024 1.060000 820.516052 0.895000 846.858032 0.760000 874.330017 0.659000 885.570984 0.630000 ================================================ FILE: Asset/SPD/metals/ThF4.eta.spd ================================================ # data from www.luxpop.com database # ; Optical constants for polycrystalline ThF4 # ; # ; taken from: # ; # ; 'Handbook of Optical Constants of Solids II', Ed. by Edward D. Palik, # ; Academic Press, Inc., 1991. # ; # ; Lambda (A) n k # ;----------------------------------------- 309.950012 1.545000 319.947998 1.542000 329.997009 1.540000 340.044006 1.538000 349.929016 1.536000 359.988007 1.534000 369.979004 1.532000 379.957001 1.531000 389.997009 1.530000 399.935028 1.529000 409.987030 1.528000 419.985992 1.527000 430.037994 1.526000 435.781982 1.533000 439.957001 1.526000 450.018036 1.525000 460.037018 1.524000 469.977020 1.524000 479.985016 1.523000 490.040009 1.523000 499.919006 1.523000 509.996002 1.522000 520.049988 1.522000 539.983032 1.521000 545.927002 1.521000 559.981995 1.521000 579.888000 1.520000 600.097046 1.520000 619.900024 1.520000 640.062012 1.519000 659.819031 1.519000 700.056030 1.519000 739.737000 1.518000 779.747986 1.518000 799.871033 1.518000 ================================================ FILE: Asset/SPD/metals/ThF4.k.spd ================================================ # data from www.luxpop.com database # ; Optical constants for polycrystalline ThF4 # ; # ; taken from: # ; # ; 'Handbook of Optical Constants of Solids II', Ed. by Edward D. Palik, # ; Academic Press, Inc., 1991. # ; # ; Lambda (A) n k # ;----------------------------------------- 309.950012 0.000000 319.947998 0.000000 329.997009 0.000000 340.044006 0.000000 349.929016 0.000000 359.988007 0.000000 369.979004 0.000000 379.957001 0.000000 389.997009 0.000000 399.935028 0.000000 409.987030 0.000000 419.985992 0.000000 430.037994 0.000000 435.781982 0.000000 439.957001 0.000000 450.018036 0.000000 460.037018 0.000000 469.977020 0.000000 479.985016 0.000000 490.040009 0.000000 499.919006 0.000000 509.996002 0.000000 520.049988 0.000000 539.983032 0.000000 545.927002 0.000000 559.981995 0.000000 579.888000 0.000000 600.097046 0.000000 619.900024 0.000000 640.062012 0.000000 659.819031 0.000000 700.056030 0.000000 739.737000 0.000000 779.747986 0.000000 799.871033 0.000000 ================================================ FILE: Asset/SPD/metals/TiC.eta.spd ================================================ # data from www.luxpop.com database # ; Optical constants for TiC # ; # ; Concatenation of: # ; # ; TiC_llnl_cxro + TiC_palik # ; # ; Lambda (A) n k # ;----------------------------------------- 309.950012 2.300000 354.229004 2.570000 413.266998 2.780000 495.920013 2.950000 619.900024 3.050000 826.533020 3.510000 ================================================ FILE: Asset/SPD/metals/TiC.k.spd ================================================ # data from www.luxpop.com database # ; Optical constants for TiC # ; # ; Concatenation of: # ; # ; TiC_llnl_cxro + TiC_palik # ; # ; Lambda (A) n k # ;----------------------------------------- 309.950012 2.330000 354.229004 2.340000 413.266998 2.430000 495.920013 2.380000 619.900024 2.670000 826.533020 3.070000 ================================================ FILE: Asset/SPD/metals/TiN.eta.spd ================================================ # data from www.luxpop.com database # ; Optical constants for TiN # ; # ; Concatenation of: # ; # ; TiN_llnl_cxro + TiN_palik # ; # ; Lambda (A) n k # ;----------------------------------------- 309.950012 2.270000 354.229004 2.140000 413.266998 1.740000 495.920013 1.200000 619.900024 1.320000 826.533020 1.820000 ================================================ FILE: Asset/SPD/metals/TiN.k.spd ================================================ # data from www.luxpop.com database # ; Optical constants for TiN # ; # ; Concatenation of: # ; # ; TiN_llnl_cxro + TiN_palik # ; # ; Lambda (A) n k # ;----------------------------------------- 309.950012 1.230000 354.229004 1.060000 413.266998 1.040000 495.920013 1.650000 619.900024 2.690000 826.533020 3.810000 ================================================ FILE: Asset/SPD/metals/TiO2-e.eta.spd ================================================ # data from www.luxpop.com database # ; Optical constants for tetragonal TiO2, extraordinary ray # ; # ; taken from: # ; # ; 'Handbook of Optical Constants of Solids', Ed. by Edward D. Palik, # ; Academic Press, Inc., 1985. # ; # ; Lambda (A) n k # ;----------------------------------------- 307.033020 3.420000 317.979004 4.000000 327.037994 3.870000 344.007019 4.300000 358.013000 3.380000 388.044006 2.880000 399.935028 3.000000 402.010010 3.000000 419.985992 2.910000 439.957001 2.840000 460.037018 2.790000 479.985016 2.750000 499.919006 2.710000 520.049988 2.680000 539.983032 2.660000 559.981995 2.640000 579.888000 2.620000 600.097046 2.600000 619.900024 2.590000 640.062012 2.580000 659.819031 2.570000 680.088013 2.560000 700.056030 2.550000 719.976990 2.540000 740.179016 2.540000 760.147034 2.530000 779.747986 2.520000 799.871033 2.520000 819.974060 2.520000 839.973083 2.510000 859.778015 2.510000 879.915039 2.500000 899.709961 2.500000 ================================================ FILE: Asset/SPD/metals/TiO2-e.k.spd ================================================ # data from www.luxpop.com database # ; Optical constants for tetragonal TiO2, extraordinary ray # ; # ; taken from: # ; # ; 'Handbook of Optical Constants of Solids', Ed. by Edward D. Palik, # ; Academic Press, Inc., 1985. # ; # ; Lambda (A) n k # ;----------------------------------------- 307.033020 1.610000 317.979004 1.790000 327.037994 0.810000 344.007019 0.000000 358.013000 0.117000 388.044006 0.000000 399.935028 0.000000 402.010010 0.008000 419.985992 0.000000 439.957001 0.000000 460.037018 0.000000 479.985016 0.000000 499.919006 0.000000 520.049988 0.000000 539.983032 0.000000 559.981995 0.000000 579.888000 0.000000 600.097046 0.000000 619.900024 0.000000 640.062012 0.000000 659.819031 0.000000 680.088013 0.000000 700.056030 0.000000 719.976990 0.000000 740.179016 0.000000 760.147034 0.000000 779.747986 0.000000 799.871033 0.000000 819.974060 0.000000 839.973083 0.000000 859.778015 0.000000 879.915039 0.000000 899.709961 0.000000 ================================================ FILE: Asset/SPD/metals/TiO2.eta.spd ================================================ # data from www.luxpop.com database # ; Optical constants for tetragonal TiO2, ordinary ray # ; # ; Concatenation of: # ; # ; TiO2_llnl_cxro + TiO2_palik # ; # ; Lambda (A) n k # ;----------------------------------------- 305.972015 3.840000 317.979004 5.380000 334.990997 4.220000 344.007019 4.360000 359.988007 3.870000 388.044006 3.490000 399.935028 3.400000 412.031006 3.240000 419.985992 3.290000 439.957001 3.200000 460.037018 3.130000 479.985016 3.080000 499.919006 3.030000 520.049988 3.000000 539.044006 2.950000 539.983032 2.970000 559.981995 2.940000 579.888000 2.920000 600.097046 2.900000 619.900024 2.880000 640.062012 2.870000 659.819031 2.850000 680.088013 2.840000 700.056030 2.830000 719.976990 2.820000 740.179016 2.810000 760.147034 2.810000 779.747986 2.800000 799.871033 2.790000 819.974060 2.790000 839.973083 2.780000 859.778015 2.780000 879.915039 2.770000 899.709961 2.770000 ================================================ FILE: Asset/SPD/metals/TiO2.k.spd ================================================ # data from www.luxpop.com database # ; Optical constants for tetragonal TiO2, ordinary ray # ; # ; Concatenation of: # ; # ; TiO2_llnl_cxro + TiO2_palik # ; # ; Lambda (A) n k # ;----------------------------------------- 305.972015 1.950000 317.979004 2.180000 334.990997 0.788000 344.007019 0.000000 359.988007 0.251000 388.044006 0.000000 399.935028 0.000000 412.031006 0.022000 419.985992 0.000000 439.957001 0.000000 460.037018 0.000000 479.985016 0.000000 499.919006 0.000000 520.049988 0.000000 539.044006 0.000000 539.983032 0.000000 559.981995 0.000000 579.888000 0.000000 600.097046 0.000000 619.900024 0.000000 640.062012 0.000000 659.819031 0.000000 680.088013 0.000000 700.056030 0.000000 719.976990 0.000000 740.179016 0.000000 760.147034 0.000000 779.747986 0.000000 799.871033 0.000000 819.974060 0.000000 839.973083 0.000000 859.778015 0.000000 879.915039 0.000000 899.709961 0.000000 ================================================ FILE: Asset/SPD/metals/VC.eta.spd ================================================ # data from www.luxpop.com database # ; Optical constants for VC # ; # ; Concatenation of: # ; # ; VC_llnl_cxro + VC_palik # ; # ; Lambda (A) n k # ;----------------------------------------- 309.950012 2.470000 354.229004 2.590000 413.266998 2.770000 495.920013 2.840000 619.900024 3.010000 826.533020 3.370000 ================================================ FILE: Asset/SPD/metals/VC.k.spd ================================================ # data from www.luxpop.com database # ; Optical constants for VC # ; # ; Concatenation of: # ; # ; VC_llnl_cxro + VC_palik # ; # ; Lambda (A) n k # ;----------------------------------------- 309.950012 2.150000 354.229004 2.120000 413.266998 2.160000 495.920013 2.210000 619.900024 2.510000 826.533020 2.880000 ================================================ FILE: Asset/SPD/metals/VN.eta.spd ================================================ # data from www.luxpop.com database # ; Optical constants for VN # ; # ; Concatenation of: # ; # ; VN_llnl_cxro + VN_palik # ; # ; Lambda (A) n k # ;----------------------------------------- 309.950012 2.200000 354.229004 2.180000 413.266998 2.130000 495.920013 2.170000 619.900024 2.350000 826.533020 2.730000 ================================================ FILE: Asset/SPD/metals/VN.k.spd ================================================ # data from www.luxpop.com database # ; Optical constants for VN # ; # ; Concatenation of: # ; # ; VN_llnl_cxro + VN_palik # ; # ; Lambda (A) n k # ;----------------------------------------- 309.950012 1.540000 354.229004 1.580000 413.266998 1.700000 495.920013 1.980000 619.900024 2.470000 826.533020 2.920000 ================================================ FILE: Asset/SPD/metals/W.eta.spd ================================================ # data from www.luxpop.com database # ; Optical constants for W # ; # ; Concatenation of: # ; # ; W_llnl_cxro + W_palik # ; # ; Lambda (A) n k # ;----------------------------------------- 302.389984 2.970000 309.950012 2.950000 313.873016 2.950000 317.897003 2.960000 322.026001 2.980000 326.263000 2.990000 330.613007 3.020000 335.081024 3.050000 339.670990 3.090000 344.389008 3.130000 349.238983 3.180000 354.229004 3.240000 359.362030 3.320000 364.647003 3.390000 370.089996 3.430000 375.696991 3.450000 381.477020 3.450000 387.437988 3.430000 393.587006 3.410000 399.935028 3.390000 406.492004 3.370000 413.266998 3.350000 420.270996 3.330000 427.516998 3.320000 435.018036 3.310000 442.785980 3.300000 450.835999 3.310000 459.185028 3.310000 467.849030 3.320000 476.846008 3.340000 486.196014 3.350000 495.920013 3.380000 506.041016 3.420000 516.583008 3.450000 527.575012 3.480000 539.044006 3.500000 551.022034 3.500000 563.545044 3.490000 576.651001 3.510000 590.381042 3.540000 604.781006 3.570000 619.900024 3.600000 635.795044 3.650000 652.526001 3.700000 670.162048 3.760000 688.778015 3.820000 708.456970 3.850000 729.294006 3.840000 751.393982 3.780000 774.875000 3.670000 799.871033 3.560000 826.533020 3.480000 855.033997 3.380000 885.570984 3.290000 ================================================ FILE: Asset/SPD/metals/W.k.spd ================================================ # data from www.luxpop.com database # ; Optical constants for W # ; # ; Concatenation of: # ; # ; W_llnl_cxro + W_palik # ; # ; Lambda (A) n k # ;----------------------------------------- 302.389984 2.370000 309.950012 2.430000 313.873016 2.460000 317.897003 2.500000 322.026001 2.530000 326.263000 2.560000 330.613007 2.600000 335.081024 2.620000 339.670990 2.650000 344.389008 2.670000 349.238983 2.690000 354.229004 2.700000 359.362030 2.700000 364.647003 2.660000 370.089996 2.600000 375.696991 2.550000 381.477020 2.490000 387.437988 2.450000 393.587006 2.430000 399.935028 2.410000 406.492004 2.420000 413.266998 2.420000 420.270996 2.430000 427.516998 2.450000 435.018036 2.470000 442.785980 2.490000 450.835999 2.530000 459.185028 2.550000 467.849030 2.590000 476.846008 2.620000 486.196014 2.640000 495.920013 2.680000 506.041016 2.710000 516.583008 2.720000 527.575012 2.720000 539.044006 2.720000 551.022034 2.730000 563.545044 2.750000 576.651001 2.810000 590.381042 2.840000 604.781006 2.860000 619.900024 2.890000 635.795044 2.920000 652.526001 2.940000 670.162048 2.950000 688.778015 2.910000 708.456970 2.860000 729.294006 2.780000 751.393982 2.720000 774.875000 2.680000 799.871033 2.730000 826.533020 2.790000 855.033997 2.850000 885.570984 2.960000 ================================================ FILE: Asset/SPD/metals/a-C.eta.spd ================================================ # data from www.luxpop.com database # ; Optical constants for amorphous carbon # ; # ; Concatenation of: # ; # ; a-C_llnl_cxro + a-C_windt88 + a-C_palik # ; # ; # ; Lambda (A) n k # ;----------------------------------------- 309.950012 1.840000 326.263000 1.900000 344.389008 1.940000 364.647003 2.000000 387.437988 2.060000 413.266998 2.110000 442.785980 2.170000 476.846008 2.240000 516.583008 2.300000 563.545044 2.380000 619.900024 2.430000 688.778015 2.430000 774.875000 2.330000 885.570984 2.240000 ================================================ FILE: Asset/SPD/metals/a-C.k.spd ================================================ # data from www.luxpop.com database # ; Optical constants for amorphous carbon # ; # ; Concatenation of: # ; # ; a-C_llnl_cxro + a-C_windt88 + a-C_palik # ; # ; # ; Lambda (A) n k # ;----------------------------------------- 309.950012 0.808000 326.263000 0.910000 344.389008 0.920000 364.647003 0.920000 387.437988 0.910000 413.266998 0.900000 442.785980 0.890000 476.846008 0.880000 516.583008 0.870000 563.545044 0.820000 619.900024 0.750000 688.778015 0.700000 774.875000 0.710000 885.570984 0.800000 ================================================ FILE: Asset/SPD/metals/a-SiH.eta.spd ================================================ # data from www.luxpop.com database # ; Optical constants for a-Si:H, amorphous glow-discharge Si onto # ; 400 C substrates, ~4 at.% H # ; # ; taken from: # ; # ; 'Handbook of Optical Constants of Solids', Ed. by Edward D. Palik, # ; Academic Press, Inc., 1985. # ; # ; Lambda (A) n k # ;----------------------------------------- 309.950012 3.360000 354.229004 4.590000 413.266998 5.430000 495.920013 5.250000 619.900024 4.710000 826.533020 4.130000 ================================================ FILE: Asset/SPD/metals/a-SiH.k.spd ================================================ # data from www.luxpop.com database # ; Optical constants for a-Si:H, amorphous glow-discharge Si onto # ; 400 C substrates, ~4 at.% H # ; # ; taken from: # ; # ; 'Handbook of Optical Constants of Solids', Ed. by Edward D. Palik, # ; Academic Press, Inc., 1985. # ; # ; Lambda (A) n k # ;----------------------------------------- 309.950012 3.920000 354.229004 3.380000 413.266998 2.190000 495.920013 0.992000 619.900024 0.217000 826.533020 0.000000 ================================================ FILE: Asset/SPD/metals/d-C.eta.spd ================================================ # data from www.luxpop.com database # ; Optical constants for diamond # ; # ; Concatenation of: # ; # ; d-C_llnl_cxro + d-C_palik # ; # ; Lambda (A) n k # ;----------------------------------------- 346.700012 2.495400 361.037018 2.485400 404.634003 2.462600 467.795990 2.440800 486.120026 2.435400 508.552002 2.430600 546.047058 2.423500 588.979004 2.417500 643.818054 2.411100 656.257019 2.410400 ================================================ FILE: Asset/SPD/metals/d-C.k.spd ================================================ # data from www.luxpop.com database # ; Optical constants for diamond # ; # ; Concatenation of: # ; # ; d-C_llnl_cxro + d-C_palik # ; # ; Lambda (A) n k # ;----------------------------------------- 346.700012 0.000000 361.037018 0.000000 404.634003 0.000000 467.795990 0.000000 486.120026 0.000000 508.552002 0.000000 546.047058 0.000000 588.979004 0.000000 643.818054 0.000000 656.257019 0.000000 ================================================ FILE: Doc/Material.drawio ================================================ ================================================ FILE: Doc/RHI.drawio ================================================ ================================================ FILE: Doc/RenderGraph.drawio ================================================ ================================================ FILE: Doc/Resource.drawio ================================================ ================================================ FILE: Doc/Scene.drawio ================================================ ================================================ FILE: Doc/Shader.drawio ================================================ ================================================ FILE: LICENSE.txt ================================================ MIT License Copyright (c) 2021 Chaf-Graphics Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # Ilum(WIP) [![Windows](https://github.com/Chaf-Libraries/Ilum/actions/workflows/windows_main.yml/badge.svg)](https://github.com/Chaf-Libraries/Ilum/actions/workflows/windows.yml) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/b0cb3a2729ee4be783dd5feb2cc67eb6)](https://www.codacy.com/gh/Chaf-Libraries/IlumEngine/dashboard?utm_source=github.com&utm_medium=referral&utm_content=Chaf-Libraries/IlumEngine&utm_campaign=Badge_Grade) Ilum Graphics Playground, name after *Planet Ilum* from [star wars](https://starwars.fandom.com/wiki/Ilum). A framework for computer graphics learning and practice (It's not a game engine!) [bilibili: Project demo video](https://www.bilibili.com/video/BV1Zy4y1f7PX/?spm_id_from=333.1007.top_right_bar_window_history.content.click&vd_source=80dfe42ae16a11fb93eda40958202849) ![image-20230212023548756](Doc/Img/image-20230212023548756.png) ## Install **Platform** * Windows 10 * Visual Studio 2022 * xmake >= v2.7.5 * [optional] CUDA >= 11.7 **Build** Ilum uses [xmake](https://xmake.io/#/) for building. You can compile the whole project by simply run command: ```shell xmake -y ``` or you can open the project in Visual Studio by generating `.sln` file: ```shell xmake project -k vsxmake ``` ## Feature ### Cross-Platform RHI * Vulkan * Mesh Shading * Dynamic Rendering * Ray Tracing Pipeline * Draw/Dispatch Indirect * Bindless Resource * CUDA * CUDA & Vulkan Interop ### Resource Manager ![](./Doc/Img/Resource.png) ### Scene Graph ![](./Doc/Img/Scene.png) ### Shader Compilation ![shader](Doc/Img/shader.png) ### Render Graph * Static compilation * Automatic resource state tracking and transition * Texture memory aliasing * Seamless interoperation between Graphics API and CUDA * Easy to customize render pass **Render Pass** * Visibility Deferred Shading Pipeline * Visibility Geometry Pass * Mesh shading with meshlet frustum culling (if device support `mesh_shader`) * Usual rasterization without optimization * Visibility Buffer Visualization * Visualize visibility buffer: instance and primitive * Visibility Buffer Lighting Pass * Indirect dispatch to support multiple material graphs * Generate lighting result and GBuffer * Shadow * Classic Shadow Map (Spot Light) * Omnidirectional Shadow Map (Point Light) * Cascade Shadow Map (Directional Light) * Ambient Occlusion * SSAO * IBL * Spherical Harmonic Encoding Diffuse * Pre-Filter For Specular * Ray Tracing * Path Tracing * Post Process * Tone Mapping * Bloom * FXAA **TODO** * Resource Pool * Runtime compilation maybe * Multi-threading ### Material Graph **Feature** * Static compilation and HLSL generation * Easy to customize material node * Support usual BSDF models ### Plugins You can extend the renderer features by adding these plugins: * RHI * Render Pass * Material Node * Importer * Editor #### Customize Render Pass #### Customize Material Node ## Gallery ### Render Graph Editor ![image-20230210193225994](./Doc/Img/image-20230210193225994.png) ### Material Graph Editor ![image-20230210194028080](./Doc/Img/image-20230210194028080.png) ### Path Tracing ![](./Doc/Img/material.png) ## Reference * [https://www.pbr-book.org/](https://www.pbr-book.org/) * [http://www.realtimerendering.com/](http://www.realtimerendering.com/) * [https://learnopengl-cn.github.io/](https://learnopengl-cn.github.io/) * [https://hazelengine.com/](https://hazelengine.com/) * [https://advances.realtimerendering.com/s2015/aaltonenhaar_siggraph2015_combined_final_footer_220dpi.pdf](https://advances.realtimerendering.com/s2015/aaltonenhaar_siggraph2015_combined_final_footer_220dpi.pdf) * [https://www.gdcvault.com/play/1024612/FrameGraph-Extensible-Rendering-Architecture-in](https://www.gdcvault.com/play/1024612/FrameGraph-Extensible-Rendering-Architecture-in) * [https://github.com/SaschaWillems/Vulkan](https://github.com/SaschaWillems/Vulkan) * [https://github.com/KhronosGroup/Vulkan-Samples](https://github.com/KhronosGroup/Vulkan-Samples) * [https://github.com/wdas/brdf](https://github.com/wdas/brdf) * [http://blog.selfshadow.com/publications/s2015-shading-course/burley/s2015_pbs_disney_bsdf_notes.pdf.](http://blog.selfshadow.com/publications/s2015-shading-course/burley/s2015_pbs_disney_bsdf_notes.pdf.) * [https://www.froyok.fr/blog/2021-12-ue4-custom-bloom/](https://www.froyok.fr/blog/2021-12-ue4-custom-bloom/) * [https://filmicworlds.com/blog/visibility-buffer-rendering-with-material-graphs](https://filmicworlds.com/blog/visibility-buffer-rendering-with-material-graphs) ================================================ FILE: Source/Editor/Editor.cpp ================================================ #include "Editor.hpp" #include "ImGui/ImGuiContext.hpp" #include "Widget.hpp" #include #include #include #include #include #include namespace Ilum { struct Editor::Impl { std::unique_ptr imgui_context = nullptr; RHIContext *rhi_context = nullptr; Renderer *renderer = nullptr; std::vector> widgets; Node *select = nullptr; Cmpt::Camera *main_camera = nullptr; }; Editor::Editor(Window *window, RHIContext *rhi_context, Renderer *renderer) { m_impl = new Impl; m_impl->imgui_context = std::make_unique(rhi_context, window); m_impl->renderer = renderer; m_impl->rhi_context = rhi_context; for (const auto &file : std::filesystem::directory_iterator("shared/Editor/")) { m_impl->widgets.emplace_back(std::unique_ptr(std::move(PluginManager::GetInstance().Call(file.path().string(), "Create", this, ImGui::GetCurrentContext())))); } } Editor::~Editor() { m_impl->imgui_context.reset(); delete m_impl; } void Editor::PreTick() { m_impl->imgui_context->BeginFrame(); } void Editor::Tick() { if (ImGui::BeginMainMenuBar()) { if (ImGui::BeginMenu("Widget")) { for (auto &widget : m_impl->widgets) { ImGui::MenuItem(widget->GetName().c_str(), nullptr, &widget->GetActive()); } ImGui::EndMenu(); } ImGui::EndMainMenuBar(); } for (auto &widget : m_impl->widgets) { if (widget->GetActive()) { widget->Tick(); } } } void Editor::PostTick() { m_impl->imgui_context->EndFrame(); m_impl->imgui_context->Render(); } Renderer *Editor::GetRenderer() const { return m_impl->renderer; } RHIContext *Editor::GetRHIContext() const { return m_impl->rhi_context; } Window *Editor::GetWindow() const { return m_impl->imgui_context->GetWindow(); } void Editor::SelectNode(Node *node) { m_impl->select = node; } void Editor::SetMainCamera(Cmpt::Camera *camera) { m_impl->main_camera = camera; } Node *Editor::GetSelectedNode() const { return m_impl->select; } Cmpt::Camera *Editor::GetMainCamera() const { return m_impl->main_camera; } } // namespace Ilum ================================================ FILE: Source/Editor/Editor.hpp ================================================ #pragma once #include #include #include namespace Ilum { class Window; class RHIContext; class GuiContext; class Widget; class Renderer; class Node; namespace Cmpt { class Camera; } class EXPORT_API Editor { public: Editor(Window *window, RHIContext *rhi_context, Renderer *renderer); ~Editor(); void PreTick(); void Tick(); void PostTick(); Renderer *GetRenderer() const; RHIContext *GetRHIContext() const; Window *GetWindow() const; void SelectNode(Node *node = nullptr); void SetMainCamera(Cmpt::Camera *camera = nullptr); Node *GetSelectedNode() const; Cmpt::Camera *GetMainCamera() const; private: struct Impl; Impl* m_impl = nullptr; }; } // namespace Ilum ================================================ FILE: Source/Editor/ImGui/ImGuiContext.cpp ================================================ #include "ImGuiContext.hpp" #include #include #include #include #include #include #include #include #define GLFW_EXPOSE_NATIVE_WIN32 #include #undef CreateSemaphore namespace Ilum { static RHIContext *gContext = nullptr; static Window *gWindow = nullptr; static RHIPipelineState *gPipelineState = nullptr; static RHIDescriptor *gDescriptor = nullptr; static RHISampler *gSampler = nullptr; static RHIRenderTarget *gRenderTarget = nullptr; struct ConstantBlock { glm::vec2 scale; glm::vec2 translate; }; struct ViewportResources { ViewportResources(const std::string &name = "subwindow") { uniform_buffer = gContext->CreateBuffer(BufferDesc{ "imgui_uniform_buffer - " + name + std::to_string((uint64_t) this), RHIBufferUsage::ConstantBuffer, RHIMemoryUsage::CPU_TO_GPU, sizeof(ConstantBlock), }); for (uint32_t i = 0; i < 3; i++) { render_completes[i] = gContext->CreateSemaphore(); present_completes[i] = gContext->CreateSemaphore(); } } std::unique_ptr vertex_buffer = nullptr; std::unique_ptr index_buffer = nullptr; std::unique_ptr uniform_buffer = nullptr; std::array, 3> render_completes; std::array, 3> present_completes; std::vector cmd_buffers; uint32_t vertex_count = 0; uint32_t index_count = 0; uint32_t frame_index = 0; }; struct WindowData { std::unique_ptr viewport_data; std::unique_ptr swapchain; }; static std::unique_ptr gResource; GuiContext::GuiContext(RHIContext *context, Window *window) : p_context(context), p_window(window) { gContext = p_context; gWindow = p_window; gResource = std::make_unique("mainwindows"); ImGui::CreateContext(); SetStyle(); ImGui_ImplGlfw_InitForOther(window->GetHandle(), true); m_pipeline_state = context->CreatePipelineState(); m_render_target = context->CreateRenderTarget(); m_sampler = context->CreateSampler(SamplerDesc{ RHIFilter::Linear, RHIFilter::Linear, RHIAddressMode::Clamp_To_Edge, RHIAddressMode::Clamp_To_Edge, RHIAddressMode::Clamp_To_Edge, RHIMipmapMode::Linear, RHISamplerBorderColor::Float_Opaque_White}); // Setup pipeline state DepthStencilState depth_stencil_state = {}; depth_stencil_state.depth_test_enable = false; depth_stencil_state.depth_write_enable = false; depth_stencil_state.compare = RHICompareOp::Always; RasterizationState rasterization_state = {}; rasterization_state.cull_mode = RHICullMode::None; rasterization_state.polygon_mode = RHIPolygonMode::Solid; rasterization_state.front_face = RHIFrontFace::Clockwise; InputAssemblyState input_assembly_state = {}; input_assembly_state.topology = RHIPrimitiveTopology::Triangle; BlendState blend_state = {}; blend_state.enable = false; blend_state.attachment_states.push_back(BlendState::AttachmentState{ true, RHIBlendFactor::Src_Alpha, RHIBlendFactor::One_Minus_Src_Alpha, RHIBlendOp::Add, RHIBlendFactor::One, RHIBlendFactor::One_Minus_Src_Alpha, RHIBlendOp::Add, 1 | 2 | 4 | 8}); VertexInputState vertex_input_state = {}; vertex_input_state.input_attributes = { VertexInputState::InputAttribute{RHIVertexSemantics::Position, 0, 0, RHIFormat::R32G32_FLOAT, offsetof(ImDrawVert, pos)}, VertexInputState::InputAttribute{RHIVertexSemantics::Texcoord, 1, 0, RHIFormat::R32G32_FLOAT, offsetof(ImDrawVert, uv)}, VertexInputState::InputAttribute{RHIVertexSemantics::Texcoord, 2, 0, RHIFormat::R8G8B8A8_UNORM, offsetof(ImDrawVert, col)}, }; vertex_input_state.input_bindings = { VertexInputState::InputBinding{0, sizeof(ImDrawVert), RHIVertexInputRate::Vertex}}; /*std::vector raw_shader; Path::GetInstance().Read("./Source/Shaders/ImGui.hlsl", raw_shader); std::string shader_source; shader_source.resize(raw_shader.size()); std::memcpy(shader_source.data(), raw_shader.data(), raw_shader.size()); shader_source += "\n"; ShaderDesc vertex_shader_desc = {}; vertex_shader_desc.entry_point = "VSmain"; vertex_shader_desc.stage = RHIShaderStage::Vertex; vertex_shader_desc.source = ShaderSource::HLSL; vertex_shader_desc.target = ShaderTarget::SPIRV; vertex_shader_desc.code = shader_source; vertex_shader_desc.macros = {"VULKAN_BACKEND"}; ShaderDesc fragment_shader_desc = {}; fragment_shader_desc.entry_point = "PSmain"; fragment_shader_desc.stage = RHIShaderStage::Fragment; fragment_shader_desc.source = ShaderSource::HLSL; fragment_shader_desc.target = ShaderTarget::SPIRV; fragment_shader_desc.code = shader_source; fragment_shader_desc.macros = {"VULKAN_BACKEND"}; ShaderMeta vertex_meta = {}; ShaderMeta fragment_meta = {}; auto vertex_shader_spirv = ShaderCompiler::GetInstance().Compile(vertex_shader_desc, vertex_meta); auto fragment_shader_spirv = ShaderCompiler::GetInstance().Compile(fragment_shader_desc, fragment_meta); m_vertex_shader = p_context->CreateShader("VSmain", vertex_shader_spirv); m_fragment_shader = p_context->CreateShader("PSmain", fragment_shader_spirv); ShaderMeta shader_meta= vertex_meta; shader_meta += fragment_meta;*/ ShaderMeta shader_meta; std::vector vertex_shader_spirv; std::vector fragment_shader_spirv; DESERIALIZE("Asset/BuildIn/ImGui.spv.asset", vertex_shader_spirv, fragment_shader_spirv, shader_meta); m_vertex_shader = p_context->CreateShader("VSmain", vertex_shader_spirv); m_fragment_shader = p_context->CreateShader("PSmain", fragment_shader_spirv); m_descriptor = p_context->CreateDescriptor(shader_meta); m_pipeline_state->SetDepthStencilState(depth_stencil_state); m_pipeline_state->SetRasterizationState(rasterization_state); m_pipeline_state->SetVertexInputState(vertex_input_state); m_pipeline_state->SetBlendState(blend_state); m_pipeline_state->SetInputAssemblyState(input_assembly_state); m_pipeline_state->SetShader(RHIShaderStage::Vertex, m_vertex_shader.get()); m_pipeline_state->SetShader(RHIShaderStage::Fragment, m_fragment_shader.get()); // Font atlas unsigned char *pixels; int32_t atlas_width, atlas_height, bpp; auto &io = ImGui::GetIO(); io.Fonts->GetTexDataAsRGBA32(&pixels, &atlas_width, &atlas_height, &bpp); const uint32_t size = atlas_width * atlas_height * bpp; auto buffer = p_context->CreateBuffer(static_cast(size), RHIBufferUsage::Transfer, RHIMemoryUsage::CPU_TO_GPU); std::memcpy(buffer->Map(), pixels, size); buffer->Unmap(); m_font_atlas = p_context->CreateTexture2D(atlas_width, atlas_height, RHIFormat::R8G8B8A8_UNORM, RHITextureUsage::ShaderResource | RHITextureUsage::Transfer, false); auto *cmd_buffer = p_context->CreateCommand(RHIQueueFamily::Graphics); cmd_buffer->Begin(); cmd_buffer->BeginMarker("UI"); cmd_buffer->ResourceStateTransition({TextureStateTransition{ m_font_atlas.get(), RHIResourceState::Undefined, RHIResourceState::TransferDest, TextureRange{RHITextureDimension::Texture2D, 0, 1, 0, 1}}}, {}); cmd_buffer->CopyBufferToTexture(buffer.get(), m_font_atlas.get(), 0, 0, 1); cmd_buffer->ResourceStateTransition({TextureStateTransition{ m_font_atlas.get(), RHIResourceState::TransferDest, RHIResourceState::ShaderResource, TextureRange{RHITextureDimension::Texture2D, 0, 1, 0, 1}}}, {}); cmd_buffer->EndMarker(); cmd_buffer->End(); p_context->Execute(cmd_buffer); io.Fonts->TexID = static_cast(m_font_atlas.get()); gPipelineState = m_pipeline_state.get(); gDescriptor = m_descriptor; gSampler = m_sampler; gRenderTarget = m_render_target.get(); InitializePlatformInterface(); } GuiContext::~GuiContext() { ImGui_ImplGlfw_Shutdown(); ImGui::DestroyContext(); gContext = nullptr; gWindow = nullptr; gPipelineState = nullptr; gDescriptor = nullptr; gSampler = nullptr; gRenderTarget = nullptr; gResource.reset(); } void GuiContext::BeginFrame() { ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDocking; window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove; window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus; ImGuiViewport *viewport = ImGui::GetMainViewport(); ImGui::SetNextWindowPos(viewport->WorkPos); ImGui::SetNextWindowSize(viewport->WorkSize); ImGui::SetNextWindowViewport(viewport->ID); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); ImGui::Begin("DockSpace", (bool *) 1, window_flags); ImGui::PopStyleVar(); ImGui::PopStyleVar(2); ImGuiIO &io = ImGui::GetIO(); if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable) { ImGuiID dockspace_id = ImGui::GetID("DockSpace"); ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), ImGuiDockNodeFlags_None); } } void GuiContext::EndFrame() { ImGui::End(); ImGuiIO &io = ImGui::GetIO(); io.DisplaySize = ImVec2(static_cast(gWindow->GetWidth()), static_cast(gWindow->GetHeight())); ImGui::EndFrame(); ImGui::Render(); if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { ImGui::UpdatePlatformWindows(); ImGui::RenderPlatformWindowsDefault(); } } void GuiContext::SetStyle() { ImGuiIO &io = ImGui::GetIO(); (void) io; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // Set fonts static const ImWchar icon_ranges[] = {ICON_MIN_FA, ICON_MAX_FA, 0}; ImFontConfig config; config.MergeMode = true; config.GlyphMinAdvanceX = 13.0f; io.Fonts->AddFontFromFileTTF("./Asset/Font/ArialUnicodeMS.ttf", 20.0f, NULL, io.Fonts->GetGlyphRangesChineseFull()); io.Fonts->AddFontFromFileTTF("./Asset/Font/fontawesome-webfont.ttf", 15.0f, &config, icon_ranges); // When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones. ImGuiStyle &style = ImGui::GetStyle(); auto colors = style.Colors; if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { style.WindowRounding = 0.0f; style.Colors[ImGuiCol_WindowBg].w = 1.0f; } colors[ImGuiCol_Text] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); colors[ImGuiCol_TextDisabled] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f); colors[ImGuiCol_WindowBg] = ImVec4(0.06f, 0.06f, 0.06f, 0.94f); colors[ImGuiCol_ChildBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); colors[ImGuiCol_PopupBg] = ImVec4(0.08f, 0.08f, 0.08f, 0.94f); colors[ImGuiCol_Border] = ImVec4(0.43f, 0.43f, 0.50f, 0.50f); colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); colors[ImGuiCol_FrameBg] = ImVec4(0.44f, 0.44f, 0.44f, 0.60f); colors[ImGuiCol_FrameBgHovered] = ImVec4(0.57f, 0.57f, 0.57f, 0.70f); colors[ImGuiCol_FrameBgActive] = ImVec4(0.76f, 0.76f, 0.76f, 0.80f); colors[ImGuiCol_TitleBg] = ImVec4(0.04f, 0.04f, 0.04f, 1.00f); colors[ImGuiCol_TitleBgActive] = ImVec4(0.16f, 0.16f, 0.16f, 1.00f); colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.00f, 0.00f, 0.00f, 0.60f); colors[ImGuiCol_MenuBarBg] = ImVec4(0.14f, 0.14f, 0.14f, 1.00f); colors[ImGuiCol_ScrollbarBg] = ImVec4(0.02f, 0.02f, 0.02f, 0.53f); colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.31f, 0.31f, 0.31f, 1.00f); colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.41f, 0.41f, 0.41f, 1.00f); colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.51f, 0.51f, 0.51f, 1.00f); colors[ImGuiCol_CheckMark] = ImVec4(0.13f, 0.75f, 0.55f, 0.80f); colors[ImGuiCol_SliderGrab] = ImVec4(0.13f, 0.75f, 0.75f, 0.80f); colors[ImGuiCol_SliderGrabActive] = ImVec4(0.13f, 0.75f, 1.00f, 0.80f); colors[ImGuiCol_Button] = ImVec4(0.13f, 0.75f, 0.55f, 0.40f); colors[ImGuiCol_ButtonHovered] = ImVec4(0.13f, 0.75f, 0.75f, 0.60f); colors[ImGuiCol_ButtonActive] = ImVec4(0.13f, 0.75f, 1.00f, 0.80f); colors[ImGuiCol_Header] = ImVec4(0.13f, 0.75f, 0.55f, 0.40f); colors[ImGuiCol_HeaderHovered] = ImVec4(0.13f, 0.75f, 0.75f, 0.60f); colors[ImGuiCol_HeaderActive] = ImVec4(0.13f, 0.75f, 1.00f, 0.80f); colors[ImGuiCol_Separator] = ImVec4(0.13f, 0.75f, 0.55f, 0.40f); colors[ImGuiCol_SeparatorHovered] = ImVec4(0.13f, 0.75f, 0.75f, 0.60f); colors[ImGuiCol_SeparatorActive] = ImVec4(0.13f, 0.75f, 1.00f, 0.80f); colors[ImGuiCol_ResizeGrip] = ImVec4(0.13f, 0.75f, 0.55f, 0.40f); colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.13f, 0.75f, 0.75f, 0.60f); colors[ImGuiCol_ResizeGripActive] = ImVec4(0.13f, 0.75f, 1.00f, 0.80f); colors[ImGuiCol_Tab] = ImVec4(0.13f, 0.75f, 0.55f, 0.80f); colors[ImGuiCol_TabHovered] = ImVec4(0.13f, 0.75f, 0.75f, 0.80f); colors[ImGuiCol_TabActive] = ImVec4(0.13f, 0.75f, 1.00f, 0.80f); colors[ImGuiCol_TabUnfocused] = ImVec4(0.18f, 0.18f, 0.18f, 1.00f); colors[ImGuiCol_TabUnfocusedActive] = ImVec4(0.36f, 0.36f, 0.36f, 0.54f); colors[ImGuiCol_DockingPreview] = ImVec4(0.13f, 0.75f, 0.55f, 0.80f); colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.13f, 0.13f, 0.13f, 0.80f); colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f); colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f); colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f); colors[ImGuiCol_TableHeaderBg] = ImVec4(0.19f, 0.19f, 0.20f, 1.00f); colors[ImGuiCol_TableBorderStrong] = ImVec4(0.31f, 0.31f, 0.35f, 1.00f); colors[ImGuiCol_TableBorderLight] = ImVec4(0.23f, 0.23f, 0.25f, 1.00f); colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.07f); colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f); colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f); colors[ImGuiCol_NavHighlight] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f); colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f); colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f); style.WindowPadding = ImVec2(8.00f, 8.00f); style.FramePadding = ImVec2(5.00f, 2.00f); style.CellPadding = ImVec2(6.00f, 6.00f); style.ItemSpacing = ImVec2(6.00f, 6.00f); style.ItemInnerSpacing = ImVec2(6.00f, 6.00f); style.TouchExtraPadding = ImVec2(0.00f, 0.00f); style.IndentSpacing = 25; style.ScrollbarSize = 15; style.GrabMinSize = 10; style.WindowBorderSize = 1; style.ChildBorderSize = 1; style.PopupBorderSize = 1; style.FrameBorderSize = 1; style.TabBorderSize = 1; style.WindowRounding = 7; style.ChildRounding = 4; style.FrameRounding = 3; style.PopupRounding = 4; style.ScrollbarRounding = 9; style.GrabRounding = 3; style.LogSliderDeadzone = 4; style.TabRounding = 4; } static void RHI_Render(ImDrawData *draw_data, WindowData *window_data = nullptr) { size_t vertex_buffer_size = draw_data->TotalVtxCount * sizeof(ImDrawVert); size_t index_buffer_size = draw_data->TotalIdxCount * sizeof(ImDrawIdx); if (draw_data->DisplaySize.x == 0 || draw_data->DisplaySize.y == 0) { return; } bool is_child_window = window_data != nullptr; RHISwapchain *swapchain = is_child_window ? window_data->swapchain.get() : gContext->GetSwapchain(); ViewportResources *resources = is_child_window ? window_data->viewport_data.get() : gResource.get(); auto &vertex_buffer = resources->vertex_buffer; auto &index_buffer = resources->index_buffer; auto &uniform_buffer = resources->uniform_buffer; auto &vertex_count = resources->vertex_count; auto &index_count = resources->index_count; if (vertex_buffer == nullptr || vertex_count != static_cast(draw_data->TotalVtxCount)) { if (vertex_buffer_size != 0) { vertex_buffer.reset(); vertex_buffer = gContext->CreateBuffer(vertex_buffer_size, RHIBufferUsage::Vertex, RHIMemoryUsage::CPU_TO_GPU); vertex_count = draw_data->TotalVtxCount; vertex_buffer->Map(); } } if (index_buffer == nullptr || index_count < static_cast(draw_data->TotalIdxCount)) { if (index_buffer_size != 0) { index_buffer.reset(); index_buffer = gContext->CreateBuffer(index_buffer_size, RHIBufferUsage::Index, RHIMemoryUsage::CPU_TO_GPU); index_count = draw_data->TotalIdxCount; index_buffer->Map(); } } ImDrawVert *vtx_dst = vertex_buffer ? (ImDrawVert *) vertex_buffer->Map() : nullptr; ImDrawIdx *idx_dst = index_buffer ? (ImDrawIdx *) index_buffer->Map() : nullptr; for (int n = 0; n < draw_data->CmdListsCount; n++) { const ImDrawList *cmd_list = draw_data->CmdLists[n]; if (vtx_dst) { memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert)); } if (idx_dst) { memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx)); } vtx_dst += cmd_list->VtxBuffer.Size; idx_dst += cmd_list->IdxBuffer.Size; } if (vertex_buffer) { vertex_buffer->Flush(0, vertex_buffer->GetDesc().size); } if (index_buffer) { index_buffer->Flush(0, index_buffer->GetDesc().size); } int32_t fb_width = (int32_t) (draw_data->DisplaySize.x * draw_data->FramebufferScale.x); int32_t fb_height = (int32_t) (draw_data->DisplaySize.y * draw_data->FramebufferScale.y); if (fb_width <= 0 || fb_height <= 0) { return; } ConstantBlock constant_block = {}; constant_block.scale = glm::vec2(2.0f / draw_data->DisplaySize.x, 2.0f / draw_data->DisplaySize.y); constant_block.translate = glm::vec2(-1.0f - draw_data->DisplayPos.x * constant_block.scale[0], -1.0f - draw_data->DisplayPos.y * constant_block.scale[1]); std::memcpy(uniform_buffer->Map(), &constant_block, sizeof(constant_block)); gDescriptor->BindSampler("fontSampler", gSampler); gDescriptor->BindBuffer("constant", uniform_buffer.get()); gRenderTarget->Clear(); ColorAttachment attachment = {}; attachment.clear_value[3] = 1.f; gRenderTarget->Set(0, swapchain->GetCurrentTexture(), TextureRange{RHITextureDimension::Texture2D, 0, 1, 0, 1}, ColorAttachment{}); auto *cmd_buffer = gContext->CreateCommand(RHIQueueFamily::Graphics); cmd_buffer->Begin(); cmd_buffer->BeginMarker("UI"); cmd_buffer->ResourceStateTransition({TextureStateTransition{ swapchain->GetCurrentTexture(), RHIResourceState::Present, RHIResourceState::RenderTarget, TextureRange{ RHITextureDimension::Texture2D, 0, 1, 0, 1}}}, {}); cmd_buffer->SetViewport(draw_data->DisplaySize.x, draw_data->DisplaySize.y); cmd_buffer->BeginRenderPass(gRenderTarget); int32_t global_vtx_offset = 0; int32_t global_idx_offset = 0; const ImVec2 &clip_off = draw_data->DisplayPos; ImVec2 clip_scale = draw_data->FramebufferScale; void *current_texture = nullptr; for (int32_t i = 0; i < draw_data->CmdListsCount; i++) { cmd_buffer->BindVertexBuffer(0, vertex_buffer.get()); cmd_buffer->BindIndexBuffer(index_buffer.get(), true); ImDrawList *cmd_list_imgui = draw_data->CmdLists[i]; for (int32_t cmd_i = 0; cmd_i < cmd_list_imgui->CmdBuffer.Size; cmd_i++) { const ImDrawCmd *pcmd = &cmd_list_imgui->CmdBuffer[cmd_i]; if (pcmd->UserCallback != nullptr) { pcmd->UserCallback(cmd_list_imgui, pcmd); } else { ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y); ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y); if (clip_min.x < 0.0f) { clip_min.x = 0.0f; } if (clip_min.y < 0.0f) { clip_min.y = 0.0f; } if (clip_max.x > fb_width) { clip_max.x = (float) fb_width; } if (clip_max.y > fb_height) { clip_max.y = (float) fb_height; } if (clip_max.x < clip_min.x || clip_max.y < clip_min.y) { continue; } cmd_buffer->SetScissor((uint32_t) (clip_max.x - clip_min.x), (uint32_t) (clip_max.y - clip_min.y), (int32_t) (clip_min.x), (int32_t) (clip_min.y)); if (current_texture != pcmd->TextureId) { auto texture = static_cast(pcmd->TextureId); gDescriptor->BindTexture("fontTexture", static_cast(pcmd->TextureId), TextureRange{RHITextureDimension::Texture2D, 0, 1, 0, 1}); cmd_buffer->BindDescriptor(gDescriptor); cmd_buffer->BindPipelineState(gPipelineState); current_texture = pcmd->TextureId; } cmd_buffer->DrawIndexed(pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0); } } global_idx_offset += cmd_list_imgui->IdxBuffer.Size; global_vtx_offset += cmd_list_imgui->VtxBuffer.Size; } cmd_buffer->EndRenderPass(); cmd_buffer->ResourceStateTransition({TextureStateTransition{ swapchain->GetCurrentTexture(), RHIResourceState::RenderTarget, RHIResourceState::Present, TextureRange{ RHITextureDimension::Texture2D, 0, 1, 0, 1}}}, {}); cmd_buffer->EndMarker(); cmd_buffer->End(); if (is_child_window) { resources->cmd_buffers.push_back(cmd_buffer); } else { gContext->Submit({cmd_buffer}, {}, {}); } } static void ImGuiWindowCreate(ImGuiViewport *viewport) { WindowData *window = new WindowData(); window->swapchain = gContext->CreateSwapchain( #ifdef _WIN32 glfwGetWin32Window((GLFWwindow *) viewport->PlatformHandle), #endif // _WIN32 static_cast(viewport->Size.x), static_cast(viewport->Size.y), gContext->IsVsync()); window->viewport_data = std::make_unique(); viewport->RendererUserData = window; } static void ImGuiWindowDestroy(ImGuiViewport *viewport) { if (WindowData *window = static_cast(viewport->RendererUserData)) { gContext->WaitIdle(); delete window; } viewport->RendererUserData = nullptr; } static void ImGuiWindowSetSize(ImGuiViewport *viewport, const ImVec2 size) { static_cast(viewport->RendererUserData)->swapchain->Resize(static_cast(size.x), static_cast(size.y), gContext->IsVsync()); } static void ImGuiWindowRender(ImGuiViewport *viewport, void *) { WindowData *window_data = static_cast(viewport->RendererUserData); window_data->swapchain->AcquireNextTexture(window_data->viewport_data->present_completes.at(window_data->viewport_data->frame_index).get(), nullptr); RHI_Render(viewport->DrawData, window_data); } static void ImGuiWindowPresent(ImGuiViewport *viewport, void *) { WindowData *window_data = static_cast(viewport->RendererUserData); gContext->Execute( std::move(window_data->viewport_data->cmd_buffers), {window_data->viewport_data->present_completes.at(window_data->viewport_data->frame_index).get()}, {window_data->viewport_data->render_completes.at(window_data->viewport_data->frame_index).get()}, gContext->CreateFrameFence()); window_data->viewport_data->cmd_buffers.clear(); window_data->swapchain->Present(window_data->viewport_data->render_completes.at(window_data->viewport_data->frame_index).get()); window_data->viewport_data->frame_index = (window_data->viewport_data->frame_index + 1) % 3; } void GuiContext::Render() { RHI_Render(ImGui::GetDrawData()); } Window *GuiContext::GetWindow() const { return p_window; } void GuiContext::InitializePlatformInterface() { ImGuiPlatformIO &platform_io = ImGui::GetPlatformIO(); platform_io.Renderer_CreateWindow = ImGuiWindowCreate; platform_io.Renderer_DestroyWindow = ImGuiWindowDestroy; platform_io.Renderer_SetWindowSize = ImGuiWindowSetSize; platform_io.Renderer_RenderWindow = ImGuiWindowRender; platform_io.Renderer_SwapBuffers = ImGuiWindowPresent; } } // namespace Ilum ================================================ FILE: Source/Editor/ImGui/ImGuiContext.hpp ================================================ #pragma once #include namespace Ilum { class GuiContext { public: GuiContext(RHIContext *context, Window *window); ~GuiContext(); void BeginFrame(); void EndFrame(); void Render(); Window *GetWindow() const; private: void SetStyle(); void InitializePlatformInterface(); private: RHIContext *p_context = nullptr; Window *p_window = nullptr; std::unique_ptr m_pipeline_state = nullptr; RHIDescriptor* m_descriptor = nullptr; RHISampler* m_sampler = nullptr; std::unique_ptr m_vertex_shader = nullptr; std::unique_ptr m_fragment_shader = nullptr; std::unique_ptr m_render_target = nullptr; std::unique_ptr m_font_atlas = nullptr; size_t m_vertex_count = 0; size_t m_index_count = 0; }; } // namespace Ilum ================================================ FILE: Source/Editor/Precompile.hpp ================================================ #pragma once #include ================================================ FILE: Source/Editor/Widget.hpp ================================================ #pragma once #include namespace Ilum { class Editor; class Widget { public: Widget(const std::string &name, Editor *editor) : m_name(name), p_editor(editor) { } virtual ~Widget() = default; virtual void Tick() = 0; inline bool &GetActive() { return m_active; } const std::string& GetName() const { return m_name; } protected: Editor *p_editor = nullptr; std::string m_name; bool m_active = true; }; } // namespace Ilum ================================================ FILE: Source/Engine/Engine.cpp ================================================ #include "Engine.hpp" #include #include #include #include #include #include #include namespace Ilum { Engine::Engine() { m_window = std::make_unique("Ilum", "Asset/Icon/logo.bmp"); m_rhi_context = std::make_unique(m_window.get(), "Vulkan", true); m_resource_manager = std::make_unique(m_rhi_context.get()); m_scene = std::make_unique("Default Scene"); m_renderer = std::make_unique(m_rhi_context.get(), m_scene.get(), m_resource_manager.get()); m_editor = std::make_unique(m_window.get(), m_rhi_context.get(), m_renderer.get()); Path::GetInstance().SetCurrent("./"); } Engine::~Engine() { m_scene.reset(); m_editor.reset(); m_renderer.reset(); m_resource_manager.reset(); m_rhi_context.reset(); m_window.reset(); } void Engine::Tick() { while (m_window->Tick()) { m_timer.Tick(); if (m_window->GetWidth() != 0 && m_window->GetHeight() != 0) { m_rhi_context->BeginFrame(); // Render loop m_renderer->Tick(); // Update resource manager m_resource_manager->Tick(); // Render UI m_editor->PreTick(); m_editor->Tick(); m_editor->PostTick(); m_rhi_context->EndFrame(); } else { std::this_thread::sleep_for(std::chrono::milliseconds(16)); } m_window->SetTitle(fmt::format("IlumEngine v{} | Scene - {} | FPS - {}", "1.0Beta", m_scene->GetName(), m_timer.FrameRate())); } } } // namespace Ilum ================================================ FILE: Source/Engine/Engine.hpp ================================================ #pragma once #include namespace Ilum { class Window; class RHIContext; class Editor; class ResourceManager; class Renderer; class Scene; class Engine { public: Engine(); ~Engine(); void Tick(); private: std::unique_ptr m_window = nullptr; std::unique_ptr m_rhi_context = nullptr; std::unique_ptr m_resource_manager = nullptr; std::unique_ptr m_scene = nullptr; std::unique_ptr m_renderer = nullptr; std::unique_ptr m_editor = nullptr; Timer m_timer; }; } // namespace Ilum ================================================ FILE: Source/Engine/Precompile.hpp ================================================ #pragma once #include ================================================ FILE: Source/Engine/main.cpp ================================================ #include "Engine.hpp" #include int main() { Ilum::Engine engine; //Ilum::ShaderCompiler::GetInstance(); engine.Tick(); return 0; } ================================================ FILE: Source/External/dxc/inc/d3d12shader.h ================================================ ////////////////////////////////////////////////////////////////////////////// // // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. // // File: D3D12Shader.h // Content: D3D12 Shader Types and APIs // ////////////////////////////////////////////////////////////////////////////// #ifndef __D3D12SHADER_H__ #define __D3D12SHADER_H__ #include "d3dcommon.h" typedef enum D3D12_SHADER_VERSION_TYPE { D3D12_SHVER_PIXEL_SHADER = 0, D3D12_SHVER_VERTEX_SHADER = 1, D3D12_SHVER_GEOMETRY_SHADER = 2, // D3D11 Shaders D3D12_SHVER_HULL_SHADER = 3, D3D12_SHVER_DOMAIN_SHADER = 4, D3D12_SHVER_COMPUTE_SHADER = 5, // D3D12 Shaders D3D12_SHVER_LIBRARY = 6, D3D12_SHVER_RAY_GENERATION_SHADER = 7, D3D12_SHVER_INTERSECTION_SHADER = 8, D3D12_SHVER_ANY_HIT_SHADER = 9, D3D12_SHVER_CLOSEST_HIT_SHADER = 10, D3D12_SHVER_MISS_SHADER = 11, D3D12_SHVER_CALLABLE_SHADER = 12, D3D12_SHVER_MESH_SHADER = 13, D3D12_SHVER_AMPLIFICATION_SHADER = 14, D3D12_SHVER_RESERVED0 = 0xFFF0, } D3D12_SHADER_VERSION_TYPE; #define D3D12_SHVER_GET_TYPE(_Version) \ (((_Version) >> 16) & 0xffff) #define D3D12_SHVER_GET_MAJOR(_Version) \ (((_Version) >> 4) & 0xf) #define D3D12_SHVER_GET_MINOR(_Version) \ (((_Version) >> 0) & 0xf) // Slot ID for library function return #define D3D_RETURN_PARAMETER_INDEX (-1) typedef D3D_RESOURCE_RETURN_TYPE D3D12_RESOURCE_RETURN_TYPE; typedef D3D_CBUFFER_TYPE D3D12_CBUFFER_TYPE; typedef struct _D3D12_SIGNATURE_PARAMETER_DESC { LPCSTR SemanticName; // Name of the semantic UINT SemanticIndex; // Index of the semantic UINT Register; // Number of member variables D3D_NAME SystemValueType;// A predefined system value, or D3D_NAME_UNDEFINED if not applicable D3D_REGISTER_COMPONENT_TYPE ComponentType; // Scalar type (e.g. uint, float, etc.) BYTE Mask; // Mask to indicate which components of the register // are used (combination of D3D10_COMPONENT_MASK values) BYTE ReadWriteMask; // Mask to indicate whether a given component is // never written (if this is an output signature) or // always read (if this is an input signature). // (combination of D3D_MASK_* values) UINT Stream; // Stream index D3D_MIN_PRECISION MinPrecision; // Minimum desired interpolation precision } D3D12_SIGNATURE_PARAMETER_DESC; typedef struct _D3D12_SHADER_BUFFER_DESC { LPCSTR Name; // Name of the constant buffer D3D_CBUFFER_TYPE Type; // Indicates type of buffer content UINT Variables; // Number of member variables UINT Size; // Size of CB (in bytes) UINT uFlags; // Buffer description flags } D3D12_SHADER_BUFFER_DESC; typedef struct _D3D12_SHADER_VARIABLE_DESC { LPCSTR Name; // Name of the variable UINT StartOffset; // Offset in constant buffer's backing store UINT Size; // Size of variable (in bytes) UINT uFlags; // Variable flags LPVOID DefaultValue; // Raw pointer to default value UINT StartTexture; // First texture index (or -1 if no textures used) UINT TextureSize; // Number of texture slots possibly used. UINT StartSampler; // First sampler index (or -1 if no textures used) UINT SamplerSize; // Number of sampler slots possibly used. } D3D12_SHADER_VARIABLE_DESC; typedef struct _D3D12_SHADER_TYPE_DESC { D3D_SHADER_VARIABLE_CLASS Class; // Variable class (e.g. object, matrix, etc.) D3D_SHADER_VARIABLE_TYPE Type; // Variable type (e.g. float, sampler, etc.) UINT Rows; // Number of rows (for matrices, 1 for other numeric, 0 if not applicable) UINT Columns; // Number of columns (for vectors & matrices, 1 for other numeric, 0 if not applicable) UINT Elements; // Number of elements (0 if not an array) UINT Members; // Number of members (0 if not a structure) UINT Offset; // Offset from the start of structure (0 if not a structure member) LPCSTR Name; // Name of type, can be NULL } D3D12_SHADER_TYPE_DESC; typedef D3D_TESSELLATOR_DOMAIN D3D12_TESSELLATOR_DOMAIN; typedef D3D_TESSELLATOR_PARTITIONING D3D12_TESSELLATOR_PARTITIONING; typedef D3D_TESSELLATOR_OUTPUT_PRIMITIVE D3D12_TESSELLATOR_OUTPUT_PRIMITIVE; typedef struct _D3D12_SHADER_DESC { UINT Version; // Shader version LPCSTR Creator; // Creator string UINT Flags; // Shader compilation/parse flags UINT ConstantBuffers; // Number of constant buffers UINT BoundResources; // Number of bound resources UINT InputParameters; // Number of parameters in the input signature UINT OutputParameters; // Number of parameters in the output signature UINT InstructionCount; // Number of emitted instructions UINT TempRegisterCount; // Number of temporary registers used UINT TempArrayCount; // Number of temporary arrays used UINT DefCount; // Number of constant defines UINT DclCount; // Number of declarations (input + output) UINT TextureNormalInstructions; // Number of non-categorized texture instructions UINT TextureLoadInstructions; // Number of texture load instructions UINT TextureCompInstructions; // Number of texture comparison instructions UINT TextureBiasInstructions; // Number of texture bias instructions UINT TextureGradientInstructions; // Number of texture gradient instructions UINT FloatInstructionCount; // Number of floating point arithmetic instructions used UINT IntInstructionCount; // Number of signed integer arithmetic instructions used UINT UintInstructionCount; // Number of unsigned integer arithmetic instructions used UINT StaticFlowControlCount; // Number of static flow control instructions used UINT DynamicFlowControlCount; // Number of dynamic flow control instructions used UINT MacroInstructionCount; // Number of macro instructions used UINT ArrayInstructionCount; // Number of array instructions used UINT CutInstructionCount; // Number of cut instructions used UINT EmitInstructionCount; // Number of emit instructions used D3D_PRIMITIVE_TOPOLOGY GSOutputTopology; // Geometry shader output topology UINT GSMaxOutputVertexCount; // Geometry shader maximum output vertex count D3D_PRIMITIVE InputPrimitive; // GS/HS input primitive UINT PatchConstantParameters; // Number of parameters in the patch constant signature UINT cGSInstanceCount; // Number of Geometry shader instances UINT cControlPoints; // Number of control points in the HS->DS stage D3D_TESSELLATOR_OUTPUT_PRIMITIVE HSOutputPrimitive; // Primitive output by the tessellator D3D_TESSELLATOR_PARTITIONING HSPartitioning; // Partitioning mode of the tessellator D3D_TESSELLATOR_DOMAIN TessellatorDomain; // Domain of the tessellator (quad, tri, isoline) // instruction counts UINT cBarrierInstructions; // Number of barrier instructions in a compute shader UINT cInterlockedInstructions; // Number of interlocked instructions UINT cTextureStoreInstructions; // Number of texture writes } D3D12_SHADER_DESC; typedef struct _D3D12_SHADER_INPUT_BIND_DESC { LPCSTR Name; // Name of the resource D3D_SHADER_INPUT_TYPE Type; // Type of resource (e.g. texture, cbuffer, etc.) UINT BindPoint; // Starting bind point UINT BindCount; // Number of contiguous bind points (for arrays) UINT uFlags; // Input binding flags D3D_RESOURCE_RETURN_TYPE ReturnType; // Return type (if texture) D3D_SRV_DIMENSION Dimension; // Dimension (if texture) UINT NumSamples; // Number of samples (0 if not MS texture) UINT Space; // Register space UINT uID; // Range ID in the bytecode } D3D12_SHADER_INPUT_BIND_DESC; #define D3D_SHADER_REQUIRES_DOUBLES 0x00000001 #define D3D_SHADER_REQUIRES_EARLY_DEPTH_STENCIL 0x00000002 #define D3D_SHADER_REQUIRES_UAVS_AT_EVERY_STAGE 0x00000004 #define D3D_SHADER_REQUIRES_64_UAVS 0x00000008 #define D3D_SHADER_REQUIRES_MINIMUM_PRECISION 0x00000010 #define D3D_SHADER_REQUIRES_11_1_DOUBLE_EXTENSIONS 0x00000020 #define D3D_SHADER_REQUIRES_11_1_SHADER_EXTENSIONS 0x00000040 #define D3D_SHADER_REQUIRES_LEVEL_9_COMPARISON_FILTERING 0x00000080 #define D3D_SHADER_REQUIRES_TILED_RESOURCES 0x00000100 #define D3D_SHADER_REQUIRES_STENCIL_REF 0x00000200 #define D3D_SHADER_REQUIRES_INNER_COVERAGE 0x00000400 #define D3D_SHADER_REQUIRES_TYPED_UAV_LOAD_ADDITIONAL_FORMATS 0x00000800 #define D3D_SHADER_REQUIRES_ROVS 0x00001000 #define D3D_SHADER_REQUIRES_VIEWPORT_AND_RT_ARRAY_INDEX_FROM_ANY_SHADER_FEEDING_RASTERIZER 0x00002000 #define D3D_SHADER_REQUIRES_WAVE_OPS 0x00004000 #define D3D_SHADER_REQUIRES_INT64_OPS 0x00008000 #define D3D_SHADER_REQUIRES_VIEW_ID 0x00010000 #define D3D_SHADER_REQUIRES_BARYCENTRICS 0x00020000 #define D3D_SHADER_REQUIRES_NATIVE_16BIT_OPS 0x00040000 #define D3D_SHADER_REQUIRES_SHADING_RATE 0x00080000 #define D3D_SHADER_REQUIRES_RAYTRACING_TIER_1_1 0x00100000 #define D3D_SHADER_REQUIRES_SAMPLER_FEEDBACK 0x00200000 #define D3D_SHADER_REQUIRES_ATOMIC_INT64_ON_TYPED_RESOURCE 0x00400000 #define D3D_SHADER_REQUIRES_ATOMIC_INT64_ON_GROUP_SHARED 0x00800000 #define D3D_SHADER_REQUIRES_DERIVATIVES_IN_MESH_AND_AMPLIFICATION_SHADERS 0x01000000 #define D3D_SHADER_REQUIRES_RESOURCE_DESCRIPTOR_HEAP_INDEXING 0x02000000 #define D3D_SHADER_REQUIRES_SAMPLER_DESCRIPTOR_HEAP_INDEXING 0x04000000 #define D3D_SHADER_REQUIRES_WAVE_MMA 0x08000000 #define D3D_SHADER_REQUIRES_ATOMIC_INT64_ON_DESCRIPTOR_HEAP_RESOURCE 0x10000000 typedef struct _D3D12_LIBRARY_DESC { LPCSTR Creator; // The name of the originator of the library. UINT Flags; // Compilation flags. UINT FunctionCount; // Number of functions exported from the library. } D3D12_LIBRARY_DESC; typedef struct _D3D12_FUNCTION_DESC { UINT Version; // Shader version LPCSTR Creator; // Creator string UINT Flags; // Shader compilation/parse flags UINT ConstantBuffers; // Number of constant buffers UINT BoundResources; // Number of bound resources UINT InstructionCount; // Number of emitted instructions UINT TempRegisterCount; // Number of temporary registers used UINT TempArrayCount; // Number of temporary arrays used UINT DefCount; // Number of constant defines UINT DclCount; // Number of declarations (input + output) UINT TextureNormalInstructions; // Number of non-categorized texture instructions UINT TextureLoadInstructions; // Number of texture load instructions UINT TextureCompInstructions; // Number of texture comparison instructions UINT TextureBiasInstructions; // Number of texture bias instructions UINT TextureGradientInstructions; // Number of texture gradient instructions UINT FloatInstructionCount; // Number of floating point arithmetic instructions used UINT IntInstructionCount; // Number of signed integer arithmetic instructions used UINT UintInstructionCount; // Number of unsigned integer arithmetic instructions used UINT StaticFlowControlCount; // Number of static flow control instructions used UINT DynamicFlowControlCount; // Number of dynamic flow control instructions used UINT MacroInstructionCount; // Number of macro instructions used UINT ArrayInstructionCount; // Number of array instructions used UINT MovInstructionCount; // Number of mov instructions used UINT MovcInstructionCount; // Number of movc instructions used UINT ConversionInstructionCount; // Number of type conversion instructions used UINT BitwiseInstructionCount; // Number of bitwise arithmetic instructions used D3D_FEATURE_LEVEL MinFeatureLevel; // Min target of the function byte code UINT64 RequiredFeatureFlags; // Required feature flags LPCSTR Name; // Function name INT FunctionParameterCount; // Number of logical parameters in the function signature (not including return) BOOL HasReturn; // TRUE, if function returns a value, false - it is a subroutine BOOL Has10Level9VertexShader; // TRUE, if there is a 10L9 VS blob BOOL Has10Level9PixelShader; // TRUE, if there is a 10L9 PS blob } D3D12_FUNCTION_DESC; typedef struct _D3D12_PARAMETER_DESC { LPCSTR Name; // Parameter name. LPCSTR SemanticName; // Parameter semantic name (+index). D3D_SHADER_VARIABLE_TYPE Type; // Element type. D3D_SHADER_VARIABLE_CLASS Class; // Scalar/Vector/Matrix. UINT Rows; // Rows are for matrix parameters. UINT Columns; // Components or Columns in matrix. D3D_INTERPOLATION_MODE InterpolationMode; // Interpolation mode. D3D_PARAMETER_FLAGS Flags; // Parameter modifiers. UINT FirstInRegister; // The first input register for this parameter. UINT FirstInComponent; // The first input register component for this parameter. UINT FirstOutRegister; // The first output register for this parameter. UINT FirstOutComponent; // The first output register component for this parameter. } D3D12_PARAMETER_DESC; ////////////////////////////////////////////////////////////////////////////// // Interfaces //////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// typedef interface ID3D12ShaderReflectionType ID3D12ShaderReflectionType; typedef interface ID3D12ShaderReflectionType *LPD3D12SHADERREFLECTIONTYPE; typedef interface ID3D12ShaderReflectionVariable ID3D12ShaderReflectionVariable; typedef interface ID3D12ShaderReflectionVariable *LPD3D12SHADERREFLECTIONVARIABLE; typedef interface ID3D12ShaderReflectionConstantBuffer ID3D12ShaderReflectionConstantBuffer; typedef interface ID3D12ShaderReflectionConstantBuffer *LPD3D12SHADERREFLECTIONCONSTANTBUFFER; typedef interface ID3D12ShaderReflection ID3D12ShaderReflection; typedef interface ID3D12ShaderReflection *LPD3D12SHADERREFLECTION; typedef interface ID3D12LibraryReflection ID3D12LibraryReflection; typedef interface ID3D12LibraryReflection *LPD3D12LIBRARYREFLECTION; typedef interface ID3D12FunctionReflection ID3D12FunctionReflection; typedef interface ID3D12FunctionReflection *LPD3D12FUNCTIONREFLECTION; typedef interface ID3D12FunctionParameterReflection ID3D12FunctionParameterReflection; typedef interface ID3D12FunctionParameterReflection *LPD3D12FUNCTIONPARAMETERREFLECTION; // {E913C351-783D-48CA-A1D1-4F306284AD56} interface DECLSPEC_UUID("E913C351-783D-48CA-A1D1-4F306284AD56") ID3D12ShaderReflectionType; DEFINE_GUID(IID_ID3D12ShaderReflectionType, 0xe913c351, 0x783d, 0x48ca, 0xa1, 0xd1, 0x4f, 0x30, 0x62, 0x84, 0xad, 0x56); #undef INTERFACE #define INTERFACE ID3D12ShaderReflectionType DECLARE_INTERFACE(ID3D12ShaderReflectionType) { STDMETHOD(GetDesc)(THIS_ _Out_ D3D12_SHADER_TYPE_DESC *pDesc) PURE; STDMETHOD_(ID3D12ShaderReflectionType*, GetMemberTypeByIndex)(THIS_ _In_ UINT Index) PURE; STDMETHOD_(ID3D12ShaderReflectionType*, GetMemberTypeByName)(THIS_ _In_ LPCSTR Name) PURE; STDMETHOD_(LPCSTR, GetMemberTypeName)(THIS_ _In_ UINT Index) PURE; STDMETHOD(IsEqual)(THIS_ _In_ ID3D12ShaderReflectionType* pType) PURE; STDMETHOD_(ID3D12ShaderReflectionType*, GetSubType)(THIS) PURE; STDMETHOD_(ID3D12ShaderReflectionType*, GetBaseClass)(THIS) PURE; STDMETHOD_(UINT, GetNumInterfaces)(THIS) PURE; STDMETHOD_(ID3D12ShaderReflectionType*, GetInterfaceByIndex)(THIS_ _In_ UINT uIndex) PURE; STDMETHOD(IsOfType)(THIS_ _In_ ID3D12ShaderReflectionType* pType) PURE; STDMETHOD(ImplementsInterface)(THIS_ _In_ ID3D12ShaderReflectionType* pBase) PURE; }; // {8337A8A6-A216-444A-B2F4-314733A73AEA} interface DECLSPEC_UUID("8337A8A6-A216-444A-B2F4-314733A73AEA") ID3D12ShaderReflectionVariable; DEFINE_GUID(IID_ID3D12ShaderReflectionVariable, 0x8337a8a6, 0xa216, 0x444a, 0xb2, 0xf4, 0x31, 0x47, 0x33, 0xa7, 0x3a, 0xea); #undef INTERFACE #define INTERFACE ID3D12ShaderReflectionVariable DECLARE_INTERFACE(ID3D12ShaderReflectionVariable) { STDMETHOD(GetDesc)(THIS_ _Out_ D3D12_SHADER_VARIABLE_DESC *pDesc) PURE; STDMETHOD_(ID3D12ShaderReflectionType*, GetType)(THIS) PURE; STDMETHOD_(ID3D12ShaderReflectionConstantBuffer*, GetBuffer)(THIS) PURE; STDMETHOD_(UINT, GetInterfaceSlot)(THIS_ _In_ UINT uArrayIndex) PURE; }; // {C59598B4-48B3-4869-B9B1-B1618B14A8B7} interface DECLSPEC_UUID("C59598B4-48B3-4869-B9B1-B1618B14A8B7") ID3D12ShaderReflectionConstantBuffer; DEFINE_GUID(IID_ID3D12ShaderReflectionConstantBuffer, 0xc59598b4, 0x48b3, 0x4869, 0xb9, 0xb1, 0xb1, 0x61, 0x8b, 0x14, 0xa8, 0xb7); #undef INTERFACE #define INTERFACE ID3D12ShaderReflectionConstantBuffer DECLARE_INTERFACE(ID3D12ShaderReflectionConstantBuffer) { STDMETHOD(GetDesc)(THIS_ D3D12_SHADER_BUFFER_DESC *pDesc) PURE; STDMETHOD_(ID3D12ShaderReflectionVariable*, GetVariableByIndex)(THIS_ _In_ UINT Index) PURE; STDMETHOD_(ID3D12ShaderReflectionVariable*, GetVariableByName)(THIS_ _In_ LPCSTR Name) PURE; }; // The ID3D12ShaderReflection IID may change from SDK version to SDK version // if the reflection API changes. This prevents new code with the new API // from working with an old binary. Recompiling with the new header // will pick up the new IID. // {5A58797D-A72C-478D-8BA2-EFC6B0EFE88E} interface DECLSPEC_UUID("5A58797D-A72C-478D-8BA2-EFC6B0EFE88E") ID3D12ShaderReflection; DEFINE_GUID(IID_ID3D12ShaderReflection, 0x5a58797d, 0xa72c, 0x478d, 0x8b, 0xa2, 0xef, 0xc6, 0xb0, 0xef, 0xe8, 0x8e); #undef INTERFACE #define INTERFACE ID3D12ShaderReflection DECLARE_INTERFACE_(ID3D12ShaderReflection, IUnknown) { STDMETHOD(QueryInterface)(THIS_ _In_ REFIID iid, _Out_ LPVOID *ppv) PURE; STDMETHOD_(ULONG, AddRef)(THIS) PURE; STDMETHOD_(ULONG, Release)(THIS) PURE; STDMETHOD(GetDesc)(THIS_ _Out_ D3D12_SHADER_DESC *pDesc) PURE; STDMETHOD_(ID3D12ShaderReflectionConstantBuffer*, GetConstantBufferByIndex)(THIS_ _In_ UINT Index) PURE; STDMETHOD_(ID3D12ShaderReflectionConstantBuffer*, GetConstantBufferByName)(THIS_ _In_ LPCSTR Name) PURE; STDMETHOD(GetResourceBindingDesc)(THIS_ _In_ UINT ResourceIndex, _Out_ D3D12_SHADER_INPUT_BIND_DESC *pDesc) PURE; STDMETHOD(GetInputParameterDesc)(THIS_ _In_ UINT ParameterIndex, _Out_ D3D12_SIGNATURE_PARAMETER_DESC *pDesc) PURE; STDMETHOD(GetOutputParameterDesc)(THIS_ _In_ UINT ParameterIndex, _Out_ D3D12_SIGNATURE_PARAMETER_DESC *pDesc) PURE; STDMETHOD(GetPatchConstantParameterDesc)(THIS_ _In_ UINT ParameterIndex, _Out_ D3D12_SIGNATURE_PARAMETER_DESC *pDesc) PURE; STDMETHOD_(ID3D12ShaderReflectionVariable*, GetVariableByName)(THIS_ _In_ LPCSTR Name) PURE; STDMETHOD(GetResourceBindingDescByName)(THIS_ _In_ LPCSTR Name, _Out_ D3D12_SHADER_INPUT_BIND_DESC *pDesc) PURE; STDMETHOD_(UINT, GetMovInstructionCount)(THIS) PURE; STDMETHOD_(UINT, GetMovcInstructionCount)(THIS) PURE; STDMETHOD_(UINT, GetConversionInstructionCount)(THIS) PURE; STDMETHOD_(UINT, GetBitwiseInstructionCount)(THIS) PURE; STDMETHOD_(D3D_PRIMITIVE, GetGSInputPrimitive)(THIS) PURE; STDMETHOD_(BOOL, IsSampleFrequencyShader)(THIS) PURE; STDMETHOD_(UINT, GetNumInterfaceSlots)(THIS) PURE; STDMETHOD(GetMinFeatureLevel)(THIS_ _Out_ enum D3D_FEATURE_LEVEL* pLevel) PURE; STDMETHOD_(UINT, GetThreadGroupSize)(THIS_ _Out_opt_ UINT* pSizeX, _Out_opt_ UINT* pSizeY, _Out_opt_ UINT* pSizeZ) PURE; STDMETHOD_(UINT64, GetRequiresFlags)(THIS) PURE; }; // {8E349D19-54DB-4A56-9DC9-119D87BDB804} interface DECLSPEC_UUID("8E349D19-54DB-4A56-9DC9-119D87BDB804") ID3D12LibraryReflection; DEFINE_GUID(IID_ID3D12LibraryReflection, 0x8e349d19, 0x54db, 0x4a56, 0x9d, 0xc9, 0x11, 0x9d, 0x87, 0xbd, 0xb8, 0x4); #undef INTERFACE #define INTERFACE ID3D12LibraryReflection DECLARE_INTERFACE_(ID3D12LibraryReflection, IUnknown) { STDMETHOD(QueryInterface)(THIS_ _In_ REFIID iid, _Out_ LPVOID * ppv) PURE; STDMETHOD_(ULONG, AddRef)(THIS) PURE; STDMETHOD_(ULONG, Release)(THIS) PURE; STDMETHOD(GetDesc)(THIS_ _Out_ D3D12_LIBRARY_DESC * pDesc) PURE; STDMETHOD_(ID3D12FunctionReflection *, GetFunctionByIndex)(THIS_ _In_ INT FunctionIndex) PURE; }; // {1108795C-2772-4BA9-B2A8-D464DC7E2799} interface DECLSPEC_UUID("1108795C-2772-4BA9-B2A8-D464DC7E2799") ID3D12FunctionReflection; DEFINE_GUID(IID_ID3D12FunctionReflection, 0x1108795c, 0x2772, 0x4ba9, 0xb2, 0xa8, 0xd4, 0x64, 0xdc, 0x7e, 0x27, 0x99); #undef INTERFACE #define INTERFACE ID3D12FunctionReflection DECLARE_INTERFACE(ID3D12FunctionReflection) { STDMETHOD(GetDesc)(THIS_ _Out_ D3D12_FUNCTION_DESC * pDesc) PURE; STDMETHOD_(ID3D12ShaderReflectionConstantBuffer *, GetConstantBufferByIndex)(THIS_ _In_ UINT BufferIndex) PURE; STDMETHOD_(ID3D12ShaderReflectionConstantBuffer *, GetConstantBufferByName)(THIS_ _In_ LPCSTR Name) PURE; STDMETHOD(GetResourceBindingDesc)(THIS_ _In_ UINT ResourceIndex, _Out_ D3D12_SHADER_INPUT_BIND_DESC * pDesc) PURE; STDMETHOD_(ID3D12ShaderReflectionVariable *, GetVariableByName)(THIS_ _In_ LPCSTR Name) PURE; STDMETHOD(GetResourceBindingDescByName)(THIS_ _In_ LPCSTR Name, _Out_ D3D12_SHADER_INPUT_BIND_DESC * pDesc) PURE; // Use D3D_RETURN_PARAMETER_INDEX to get description of the return value. STDMETHOD_(ID3D12FunctionParameterReflection *, GetFunctionParameter)(THIS_ _In_ INT ParameterIndex) PURE; }; // {EC25F42D-7006-4F2B-B33E-02CC3375733F} interface DECLSPEC_UUID("EC25F42D-7006-4F2B-B33E-02CC3375733F") ID3D12FunctionParameterReflection; DEFINE_GUID(IID_ID3D12FunctionParameterReflection, 0xec25f42d, 0x7006, 0x4f2b, 0xb3, 0x3e, 0x2, 0xcc, 0x33, 0x75, 0x73, 0x3f); #undef INTERFACE #define INTERFACE ID3D12FunctionParameterReflection DECLARE_INTERFACE(ID3D12FunctionParameterReflection) { STDMETHOD(GetDesc)(THIS_ _Out_ D3D12_PARAMETER_DESC * pDesc) PURE; }; ////////////////////////////////////////////////////////////////////////////// // APIs ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// #ifdef __cplusplus extern "C" { #endif //__cplusplus #ifdef __cplusplus } #endif //__cplusplus #endif //__D3D12SHADER_H__ ================================================ FILE: Source/External/dxc/inc/dxcapi.h ================================================ /////////////////////////////////////////////////////////////////////////////// // // // dxcapi.h // // Copyright (C) Microsoft Corporation. All rights reserved. // // This file is distributed under the University of Illinois Open Source // // License. See LICENSE.TXT for details. // // // // Provides declarations for the DirectX Compiler API entry point. // // // /////////////////////////////////////////////////////////////////////////////// #ifndef __DXC_API__ #define __DXC_API__ #ifdef _WIN32 #ifndef DXC_API_IMPORT #define DXC_API_IMPORT __declspec(dllimport) #endif #else #ifndef DXC_API_IMPORT #define DXC_API_IMPORT __attribute__ ((visibility ("default"))) #endif #endif #ifdef _WIN32 #ifndef CROSS_PLATFORM_UUIDOF // Warning: This macro exists in WinAdapter.h as well #define CROSS_PLATFORM_UUIDOF(interface, spec) \ struct __declspec(uuid(spec)) interface; #endif #else #include #include "dxc/Support/WinAdapter.h" #endif struct IMalloc; struct IDxcIncludeHandler; typedef HRESULT (__stdcall *DxcCreateInstanceProc)( _In_ REFCLSID rclsid, _In_ REFIID riid, _Out_ LPVOID* ppv ); typedef HRESULT(__stdcall *DxcCreateInstance2Proc)( _In_ IMalloc *pMalloc, _In_ REFCLSID rclsid, _In_ REFIID riid, _Out_ LPVOID* ppv ); /// /// Creates a single uninitialized object of the class associated with a specified CLSID. /// /// /// The CLSID associated with the data and code that will be used to create the object. /// /// /// A reference to the identifier of the interface to be used to communicate /// with the object. /// /// /// Address of pointer variable that receives the interface pointer requested /// in riid. Upon successful return, *ppv contains the requested interface /// pointer. Upon failure, *ppv contains NULL. /// /// While this function is similar to CoCreateInstance, there is no COM involvement. /// extern "C" DXC_API_IMPORT HRESULT __stdcall DxcCreateInstance( _In_ REFCLSID rclsid, _In_ REFIID riid, _Out_ LPVOID* ppv ); extern "C" DXC_API_IMPORT HRESULT __stdcall DxcCreateInstance2( _In_ IMalloc *pMalloc, _In_ REFCLSID rclsid, _In_ REFIID riid, _Out_ LPVOID* ppv ); // For convenience, equivalent definitions to CP_UTF8 and CP_UTF16. #define DXC_CP_UTF8 65001 #define DXC_CP_UTF16 1200 #define DXC_CP_UTF32 12000 // Use DXC_CP_ACP for: Binary; ANSI Text; Autodetect UTF with BOM #define DXC_CP_ACP 0 #ifdef _WIN32 #define DXC_CP_WIDE DXC_CP_UTF16 #else #define DXC_CP_WIDE DXC_CP_UTF32 #endif // This flag indicates that the shader hash was computed taking into account source information (-Zss) #define DXC_HASHFLAG_INCLUDES_SOURCE 1 // Hash digest type for ShaderHash typedef struct DxcShaderHash { UINT32 Flags; // DXC_HASHFLAG_* BYTE HashDigest[16]; } DxcShaderHash; #define DXC_FOURCC(ch0, ch1, ch2, ch3) ( \ (UINT32)(UINT8)(ch0) | (UINT32)(UINT8)(ch1) << 8 | \ (UINT32)(UINT8)(ch2) << 16 | (UINT32)(UINT8)(ch3) << 24 \ ) #define DXC_PART_PDB DXC_FOURCC('I', 'L', 'D', 'B') #define DXC_PART_PDB_NAME DXC_FOURCC('I', 'L', 'D', 'N') #define DXC_PART_PRIVATE_DATA DXC_FOURCC('P', 'R', 'I', 'V') #define DXC_PART_ROOT_SIGNATURE DXC_FOURCC('R', 'T', 'S', '0') #define DXC_PART_DXIL DXC_FOURCC('D', 'X', 'I', 'L') #define DXC_PART_REFLECTION_DATA DXC_FOURCC('S', 'T', 'A', 'T') #define DXC_PART_SHADER_HASH DXC_FOURCC('H', 'A', 'S', 'H') #define DXC_PART_INPUT_SIGNATURE DXC_FOURCC('I', 'S', 'G', '1') #define DXC_PART_OUTPUT_SIGNATURE DXC_FOURCC('O', 'S', 'G', '1') #define DXC_PART_PATCH_CONSTANT_SIGNATURE DXC_FOURCC('P', 'S', 'G', '1') // Some option arguments are defined here for continuity with D3DCompile interface #define DXC_ARG_DEBUG L"-Zi" #define DXC_ARG_SKIP_VALIDATION L"-Vd" #define DXC_ARG_SKIP_OPTIMIZATIONS L"-Od" #define DXC_ARG_PACK_MATRIX_ROW_MAJOR L"-Zpr" #define DXC_ARG_PACK_MATRIX_COLUMN_MAJOR L"-Zpc" #define DXC_ARG_AVOID_FLOW_CONTROL L"-Gfa" #define DXC_ARG_PREFER_FLOW_CONTROL L"-Gfp" #define DXC_ARG_ENABLE_STRICTNESS L"-Ges" #define DXC_ARG_ENABLE_BACKWARDS_COMPATIBILITY L"-Gec" #define DXC_ARG_IEEE_STRICTNESS L"-Gis" #define DXC_ARG_OPTIMIZATION_LEVEL0 L"-O0" #define DXC_ARG_OPTIMIZATION_LEVEL1 L"-O1" #define DXC_ARG_OPTIMIZATION_LEVEL2 L"-O2" #define DXC_ARG_OPTIMIZATION_LEVEL3 L"-O3" #define DXC_ARG_WARNINGS_ARE_ERRORS L"-WX" #define DXC_ARG_RESOURCES_MAY_ALIAS L"-res_may_alias" #define DXC_ARG_ALL_RESOURCES_BOUND L"-all_resources_bound" #define DXC_ARG_DEBUG_NAME_FOR_SOURCE L"-Zss" #define DXC_ARG_DEBUG_NAME_FOR_BINARY L"-Zsb" // IDxcBlob is an alias of ID3D10Blob and ID3DBlob CROSS_PLATFORM_UUIDOF(IDxcBlob, "8BA5FB08-5195-40e2-AC58-0D989C3A0102") struct IDxcBlob : public IUnknown { public: virtual LPVOID STDMETHODCALLTYPE GetBufferPointer(void) = 0; virtual SIZE_T STDMETHODCALLTYPE GetBufferSize(void) = 0; }; CROSS_PLATFORM_UUIDOF(IDxcBlobEncoding, "7241d424-2646-4191-97c0-98e96e42fc68") struct IDxcBlobEncoding : public IDxcBlob { public: virtual HRESULT STDMETHODCALLTYPE GetEncoding(_Out_ BOOL *pKnown, _Out_ UINT32 *pCodePage) = 0; }; // Notes on IDxcBlobWide and IDxcBlobUtf8 // These guarantee null-terminated text and eithre utf8 or the native wide char encoding. // GetBufferSize() will return the size in bytes, including null-terminator // GetStringLength() will return the length in characters, excluding the null-terminator // Name strings will use IDxcBlobWide, while other string output blobs, // such as errors/warnings, preprocessed HLSL, or other text will be based // on the -encoding option. // The API will use this interface for output name strings CROSS_PLATFORM_UUIDOF(IDxcBlobWide, "A3F84EAB-0FAA-497E-A39C-EE6ED60B2D84") struct IDxcBlobWide : public IDxcBlobEncoding { public: virtual LPCWSTR STDMETHODCALLTYPE GetStringPointer(void) = 0; virtual SIZE_T STDMETHODCALLTYPE GetStringLength(void) = 0; }; CROSS_PLATFORM_UUIDOF(IDxcBlobUtf8, "3DA636C9-BA71-4024-A301-30CBF125305B") struct IDxcBlobUtf8 : public IDxcBlobEncoding { public: virtual LPCSTR STDMETHODCALLTYPE GetStringPointer(void) = 0; virtual SIZE_T STDMETHODCALLTYPE GetStringLength(void) = 0; }; // Define legacy name IDxcBlobUtf16 as IDxcBlobWide for Win32 #ifdef _WIN32 typedef IDxcBlobWide IDxcBlobUtf16; #endif CROSS_PLATFORM_UUIDOF(IDxcIncludeHandler, "7f61fc7d-950d-467f-b3e3-3c02fb49187c") struct IDxcIncludeHandler : public IUnknown { virtual HRESULT STDMETHODCALLTYPE LoadSource( _In_z_ LPCWSTR pFilename, // Candidate filename. _COM_Outptr_result_maybenull_ IDxcBlob **ppIncludeSource // Resultant source object for included file, nullptr if not found. ) = 0; }; // Structure for supplying bytes or text input to Dxc APIs. // Use Encoding = 0 for non-text bytes, ANSI text, or unknown with BOM. typedef struct DxcBuffer { LPCVOID Ptr; SIZE_T Size; UINT Encoding; } DxcText; struct DxcDefine { LPCWSTR Name; _Maybenull_ LPCWSTR Value; }; CROSS_PLATFORM_UUIDOF(IDxcCompilerArgs, "73EFFE2A-70DC-45F8-9690-EFF64C02429D") struct IDxcCompilerArgs : public IUnknown { // Pass GetArguments() and GetCount() to Compile virtual LPCWSTR* STDMETHODCALLTYPE GetArguments() = 0; virtual UINT32 STDMETHODCALLTYPE GetCount() = 0; // Add additional arguments or defines here, if desired. virtual HRESULT STDMETHODCALLTYPE AddArguments( _In_opt_count_(argCount) LPCWSTR *pArguments, // Array of pointers to arguments to add _In_ UINT32 argCount // Number of arguments to add ) = 0; virtual HRESULT STDMETHODCALLTYPE AddArgumentsUTF8( _In_opt_count_(argCount)LPCSTR *pArguments, // Array of pointers to UTF-8 arguments to add _In_ UINT32 argCount // Number of arguments to add ) = 0; virtual HRESULT STDMETHODCALLTYPE AddDefines( _In_count_(defineCount) const DxcDefine *pDefines, // Array of defines _In_ UINT32 defineCount // Number of defines ) = 0; }; ////////////////////////// // Legacy Interfaces ///////////////////////// // NOTE: IDxcUtils replaces IDxcLibrary CROSS_PLATFORM_UUIDOF(IDxcLibrary, "e5204dc7-d18c-4c3c-bdfb-851673980fe7") struct IDxcLibrary : public IUnknown { virtual HRESULT STDMETHODCALLTYPE SetMalloc(_In_opt_ IMalloc *pMalloc) = 0; virtual HRESULT STDMETHODCALLTYPE CreateBlobFromBlob( _In_ IDxcBlob *pBlob, UINT32 offset, UINT32 length, _COM_Outptr_ IDxcBlob **ppResult) = 0; virtual HRESULT STDMETHODCALLTYPE CreateBlobFromFile( _In_z_ LPCWSTR pFileName, _In_opt_ UINT32* codePage, _COM_Outptr_ IDxcBlobEncoding **pBlobEncoding) = 0; virtual HRESULT STDMETHODCALLTYPE CreateBlobWithEncodingFromPinned( _In_bytecount_(size) LPCVOID pText, UINT32 size, UINT32 codePage, _COM_Outptr_ IDxcBlobEncoding **pBlobEncoding) = 0; virtual HRESULT STDMETHODCALLTYPE CreateBlobWithEncodingOnHeapCopy( _In_bytecount_(size) LPCVOID pText, UINT32 size, UINT32 codePage, _COM_Outptr_ IDxcBlobEncoding **pBlobEncoding) = 0; virtual HRESULT STDMETHODCALLTYPE CreateBlobWithEncodingOnMalloc( _In_bytecount_(size) LPCVOID pText, IMalloc *pIMalloc, UINT32 size, UINT32 codePage, _COM_Outptr_ IDxcBlobEncoding **pBlobEncoding) = 0; virtual HRESULT STDMETHODCALLTYPE CreateIncludeHandler( _COM_Outptr_ IDxcIncludeHandler **ppResult) = 0; virtual HRESULT STDMETHODCALLTYPE CreateStreamFromBlobReadOnly( _In_ IDxcBlob *pBlob, _COM_Outptr_ IStream **ppStream) = 0; virtual HRESULT STDMETHODCALLTYPE GetBlobAsUtf8( _In_ IDxcBlob *pBlob, _COM_Outptr_ IDxcBlobEncoding **pBlobEncoding) = 0; // Renamed from GetBlobAsUtf16 to GetBlobAsWide virtual HRESULT STDMETHODCALLTYPE GetBlobAsWide( _In_ IDxcBlob *pBlob, _COM_Outptr_ IDxcBlobEncoding **pBlobEncoding) = 0; #ifdef _WIN32 // Alias to GetBlobAsWide on Win32 inline HRESULT GetBlobAsUtf16( _In_ IDxcBlob *pBlob, _COM_Outptr_ IDxcBlobEncoding **pBlobEncoding) { return this->GetBlobAsWide(pBlob, pBlobEncoding); } #endif }; // NOTE: IDxcResult replaces IDxcOperationResult CROSS_PLATFORM_UUIDOF(IDxcOperationResult, "CEDB484A-D4E9-445A-B991-CA21CA157DC2") struct IDxcOperationResult : public IUnknown { virtual HRESULT STDMETHODCALLTYPE GetStatus(_Out_ HRESULT *pStatus) = 0; // GetResult returns the main result of the operation. // This corresponds to: // DXC_OUT_OBJECT - Compile() with shader or library target // DXC_OUT_DISASSEMBLY - Disassemble() // DXC_OUT_HLSL - Compile() with -P // DXC_OUT_ROOT_SIGNATURE - Compile() with rootsig_* target virtual HRESULT STDMETHODCALLTYPE GetResult(_COM_Outptr_result_maybenull_ IDxcBlob **ppResult) = 0; // GetErrorBuffer Corresponds to DXC_OUT_ERRORS. virtual HRESULT STDMETHODCALLTYPE GetErrorBuffer(_COM_Outptr_result_maybenull_ IDxcBlobEncoding **ppErrors) = 0; }; // NOTE: IDxcCompiler3 replaces IDxcCompiler and IDxcCompiler2 CROSS_PLATFORM_UUIDOF(IDxcCompiler, "8c210bf3-011f-4422-8d70-6f9acb8db617") struct IDxcCompiler : public IUnknown { // Compile a single entry point to the target shader model virtual HRESULT STDMETHODCALLTYPE Compile( _In_ IDxcBlob *pSource, // Source text to compile _In_opt_z_ LPCWSTR pSourceName, // Optional file name for pSource. Used in errors and include handlers. _In_opt_z_ LPCWSTR pEntryPoint, // entry point name _In_z_ LPCWSTR pTargetProfile, // shader profile to compile _In_opt_count_(argCount) LPCWSTR *pArguments, // Array of pointers to arguments _In_ UINT32 argCount, // Number of arguments _In_count_(defineCount) const DxcDefine *pDefines, // Array of defines _In_ UINT32 defineCount, // Number of defines _In_opt_ IDxcIncludeHandler *pIncludeHandler, // user-provided interface to handle #include directives (optional) _COM_Outptr_ IDxcOperationResult **ppResult // Compiler output status, buffer, and errors ) = 0; // Preprocess source text virtual HRESULT STDMETHODCALLTYPE Preprocess( _In_ IDxcBlob *pSource, // Source text to preprocess _In_opt_z_ LPCWSTR pSourceName, // Optional file name for pSource. Used in errors and include handlers. _In_opt_count_(argCount) LPCWSTR *pArguments, // Array of pointers to arguments _In_ UINT32 argCount, // Number of arguments _In_count_(defineCount) const DxcDefine *pDefines, // Array of defines _In_ UINT32 defineCount, // Number of defines _In_opt_ IDxcIncludeHandler *pIncludeHandler, // user-provided interface to handle #include directives (optional) _COM_Outptr_ IDxcOperationResult **ppResult // Preprocessor output status, buffer, and errors ) = 0; // Disassemble a program. virtual HRESULT STDMETHODCALLTYPE Disassemble( _In_ IDxcBlob *pSource, // Program to disassemble. _COM_Outptr_ IDxcBlobEncoding **ppDisassembly // Disassembly text. ) = 0; }; // NOTE: IDxcCompiler3 replaces IDxcCompiler and IDxcCompiler2 CROSS_PLATFORM_UUIDOF(IDxcCompiler2, "A005A9D9-B8BB-4594-B5C9-0E633BEC4D37") struct IDxcCompiler2 : public IDxcCompiler { // Compile a single entry point to the target shader model with debug information. virtual HRESULT STDMETHODCALLTYPE CompileWithDebug( _In_ IDxcBlob *pSource, // Source text to compile _In_opt_z_ LPCWSTR pSourceName, // Optional file name for pSource. Used in errors and include handlers. _In_opt_z_ LPCWSTR pEntryPoint, // Entry point name _In_z_ LPCWSTR pTargetProfile, // Shader profile to compile _In_opt_count_(argCount) LPCWSTR *pArguments, // Array of pointers to arguments _In_ UINT32 argCount, // Number of arguments _In_count_(defineCount) const DxcDefine *pDefines, // Array of defines _In_ UINT32 defineCount, // Number of defines _In_opt_ IDxcIncludeHandler *pIncludeHandler, // user-provided interface to handle #include directives (optional) _COM_Outptr_ IDxcOperationResult **ppResult, // Compiler output status, buffer, and errors _Outptr_opt_result_z_ LPWSTR *ppDebugBlobName,// Suggested file name for debug blob. (Must be CoTaskMemFree()'d!) _COM_Outptr_opt_ IDxcBlob **ppDebugBlob // Debug blob ) = 0; }; CROSS_PLATFORM_UUIDOF(IDxcLinker, "F1B5BE2A-62DD-4327-A1C2-42AC1E1E78E6") struct IDxcLinker : public IUnknown { public: // Register a library with name to ref it later. virtual HRESULT RegisterLibrary( _In_opt_ LPCWSTR pLibName, // Name of the library. _In_ IDxcBlob *pLib // Library blob. ) = 0; // Links the shader and produces a shader blob that the Direct3D runtime can // use. virtual HRESULT STDMETHODCALLTYPE Link( _In_opt_ LPCWSTR pEntryName, // Entry point name _In_ LPCWSTR pTargetProfile, // shader profile to link _In_count_(libCount) const LPCWSTR *pLibNames, // Array of library names to link _In_ UINT32 libCount, // Number of libraries to link _In_opt_count_(argCount) const LPCWSTR *pArguments, // Array of pointers to arguments _In_ UINT32 argCount, // Number of arguments _COM_Outptr_ IDxcOperationResult **ppResult // Linker output status, buffer, and errors ) = 0; }; ///////////////////////// // Latest interfaces. Please use these //////////////////////// // NOTE: IDxcUtils replaces IDxcLibrary CROSS_PLATFORM_UUIDOF(IDxcUtils, "4605C4CB-2019-492A-ADA4-65F20BB7D67F") struct IDxcUtils : public IUnknown { // Create a sub-blob that holds a reference to the outer blob and points to its memory. virtual HRESULT STDMETHODCALLTYPE CreateBlobFromBlob( _In_ IDxcBlob *pBlob, UINT32 offset, UINT32 length, _COM_Outptr_ IDxcBlob **ppResult) = 0; // For codePage, use 0 (or DXC_CP_ACP) for raw binary or ANSI code page // Creates a blob referencing existing memory, with no copy. // User must manage the memory lifetime separately. // (was: CreateBlobWithEncodingFromPinned) virtual HRESULT STDMETHODCALLTYPE CreateBlobFromPinned( _In_bytecount_(size) LPCVOID pData, UINT32 size, UINT32 codePage, _COM_Outptr_ IDxcBlobEncoding **pBlobEncoding) = 0; // Create blob, taking ownership of memory allocated with supplied allocator. // (was: CreateBlobWithEncodingOnMalloc) virtual HRESULT STDMETHODCALLTYPE MoveToBlob( _In_bytecount_(size) LPCVOID pData, IMalloc *pIMalloc, UINT32 size, UINT32 codePage, _COM_Outptr_ IDxcBlobEncoding **pBlobEncoding) = 0; //// // New blobs and copied contents are allocated with the current allocator // Copy blob contents to memory owned by the new blob. // (was: CreateBlobWithEncodingOnHeapCopy) virtual HRESULT STDMETHODCALLTYPE CreateBlob( _In_bytecount_(size) LPCVOID pData, UINT32 size, UINT32 codePage, _COM_Outptr_ IDxcBlobEncoding **pBlobEncoding) = 0; // (was: CreateBlobFromFile) virtual HRESULT STDMETHODCALLTYPE LoadFile( _In_z_ LPCWSTR pFileName, _In_opt_ UINT32* pCodePage, _COM_Outptr_ IDxcBlobEncoding **pBlobEncoding) = 0; virtual HRESULT STDMETHODCALLTYPE CreateReadOnlyStreamFromBlob( _In_ IDxcBlob *pBlob, _COM_Outptr_ IStream **ppStream) = 0; // Create default file-based include handler virtual HRESULT STDMETHODCALLTYPE CreateDefaultIncludeHandler( _COM_Outptr_ IDxcIncludeHandler **ppResult) = 0; // Convert or return matching encoded text blobs virtual HRESULT STDMETHODCALLTYPE GetBlobAsUtf8( _In_ IDxcBlob *pBlob, _COM_Outptr_ IDxcBlobUtf8 **pBlobEncoding) = 0; // Renamed from GetBlobAsUtf16 to GetBlobAsWide virtual HRESULT STDMETHODCALLTYPE GetBlobAsWide( _In_ IDxcBlob *pBlob, _COM_Outptr_ IDxcBlobWide **pBlobEncoding) = 0; #ifdef _WIN32 // Alias to GetBlobAsWide on Win32 inline HRESULT GetBlobAsUtf16( _In_ IDxcBlob *pBlob, _COM_Outptr_ IDxcBlobWide **pBlobEncoding) { return this->GetBlobAsWide(pBlob, pBlobEncoding); } #endif virtual HRESULT STDMETHODCALLTYPE GetDxilContainerPart( _In_ const DxcBuffer *pShader, _In_ UINT32 DxcPart, _Outptr_result_nullonfailure_ void **ppPartData, _Out_ UINT32 *pPartSizeInBytes) = 0; // Create reflection interface from serialized Dxil container, or DXC_PART_REFLECTION_DATA. // TBD: Require part header for RDAT? (leaning towards yes) virtual HRESULT STDMETHODCALLTYPE CreateReflection( _In_ const DxcBuffer *pData, REFIID iid, void **ppvReflection) = 0; virtual HRESULT STDMETHODCALLTYPE BuildArguments( _In_opt_z_ LPCWSTR pSourceName, // Optional file name for pSource. Used in errors and include handlers. _In_opt_z_ LPCWSTR pEntryPoint, // Entry point name. (-E) _In_z_ LPCWSTR pTargetProfile, // Shader profile to compile. (-T) _In_opt_count_(argCount) LPCWSTR *pArguments, // Array of pointers to arguments _In_ UINT32 argCount, // Number of arguments _In_count_(defineCount) const DxcDefine *pDefines, // Array of defines _In_ UINT32 defineCount, // Number of defines _COM_Outptr_ IDxcCompilerArgs **ppArgs // Arguments you can use with Compile() method ) = 0; // Takes the shader PDB and returns the hash and the container inside it virtual HRESULT STDMETHODCALLTYPE GetPDBContents( _In_ IDxcBlob *pPDBBlob, _COM_Outptr_ IDxcBlob **ppHash, _COM_Outptr_ IDxcBlob **ppContainer) = 0; }; // For use with IDxcResult::[Has|Get]Output dxcOutKind argument // Note: text outputs returned from version 2 APIs are UTF-8 or UTF-16 based on -encoding option typedef enum DXC_OUT_KIND { DXC_OUT_NONE = 0, DXC_OUT_OBJECT = 1, // IDxcBlob - Shader or library object DXC_OUT_ERRORS = 2, // IDxcBlobUtf8 or IDxcBlobWide DXC_OUT_PDB = 3, // IDxcBlob DXC_OUT_SHADER_HASH = 4, // IDxcBlob - DxcShaderHash of shader or shader with source info (-Zsb/-Zss) DXC_OUT_DISASSEMBLY = 5, // IDxcBlobUtf8 or IDxcBlobWide - from Disassemble DXC_OUT_HLSL = 6, // IDxcBlobUtf8 or IDxcBlobWide - from Preprocessor or Rewriter DXC_OUT_TEXT = 7, // IDxcBlobUtf8 or IDxcBlobWide - other text, such as -ast-dump or -Odump DXC_OUT_REFLECTION = 8, // IDxcBlob - RDAT part with reflection data DXC_OUT_ROOT_SIGNATURE = 9, // IDxcBlob - Serialized root signature output DXC_OUT_EXTRA_OUTPUTS = 10,// IDxcExtraResults - Extra outputs DXC_OUT_REMARKS = 11, // IDxcBlobUtf8 or IDxcBlobWide - text directed at stdout DXC_OUT_TIME_REPORT = 12, // IDxcBlobUtf8 or IDxcBlobWide - text directed at stdout DXC_OUT_TIME_TRACE = 13, // IDxcBlobUtf8 or IDxcBlobWide - text directed at stdout DXC_OUT_LAST = DXC_OUT_TIME_TRACE, // Last value for a counter DXC_OUT_NUM_ENUMS, DXC_OUT_FORCE_DWORD = 0xFFFFFFFF } DXC_OUT_KIND; static_assert(DXC_OUT_NUM_ENUMS == DXC_OUT_LAST + 1, "DXC_OUT_* Enum added and last value not updated."); CROSS_PLATFORM_UUIDOF(IDxcResult, "58346CDA-DDE7-4497-9461-6F87AF5E0659") struct IDxcResult : public IDxcOperationResult { virtual BOOL STDMETHODCALLTYPE HasOutput(_In_ DXC_OUT_KIND dxcOutKind) = 0; virtual HRESULT STDMETHODCALLTYPE GetOutput(_In_ DXC_OUT_KIND dxcOutKind, _In_ REFIID iid, _COM_Outptr_opt_result_maybenull_ void **ppvObject, _COM_Outptr_ IDxcBlobWide **ppOutputName) = 0; virtual UINT32 GetNumOutputs() = 0; virtual DXC_OUT_KIND GetOutputByIndex(UINT32 Index) = 0; virtual DXC_OUT_KIND PrimaryOutput() = 0; }; // Special names for extra output that should get written to specific streams #define DXC_EXTRA_OUTPUT_NAME_STDOUT L"*stdout*" #define DXC_EXTRA_OUTPUT_NAME_STDERR L"*stderr*" CROSS_PLATFORM_UUIDOF(IDxcExtraOutputs, "319b37a2-a5c2-494a-a5de-4801b2faf989") struct IDxcExtraOutputs : public IUnknown { virtual UINT32 STDMETHODCALLTYPE GetOutputCount() = 0; virtual HRESULT STDMETHODCALLTYPE GetOutput(_In_ UINT32 uIndex, _In_ REFIID iid, _COM_Outptr_opt_result_maybenull_ void **ppvObject, _COM_Outptr_opt_result_maybenull_ IDxcBlobWide **ppOutputType, _COM_Outptr_opt_result_maybenull_ IDxcBlobWide **ppOutputName) = 0; }; CROSS_PLATFORM_UUIDOF(IDxcCompiler3, "228B4687-5A6A-4730-900C-9702B2203F54") struct IDxcCompiler3 : public IUnknown { // Compile a single entry point to the target shader model, // Compile a library to a library target (-T lib_*), // Compile a root signature (-T rootsig_*), or // Preprocess HLSL source (-P) virtual HRESULT STDMETHODCALLTYPE Compile( _In_ const DxcBuffer *pSource, // Source text to compile _In_opt_count_(argCount) LPCWSTR *pArguments, // Array of pointers to arguments _In_ UINT32 argCount, // Number of arguments _In_opt_ IDxcIncludeHandler *pIncludeHandler, // user-provided interface to handle #include directives (optional) _In_ REFIID riid, _Out_ LPVOID *ppResult // IDxcResult: status, buffer, and errors ) = 0; // Disassemble a program. virtual HRESULT STDMETHODCALLTYPE Disassemble( _In_ const DxcBuffer *pObject, // Program to disassemble: dxil container or bitcode. _In_ REFIID riid, _Out_ LPVOID *ppResult // IDxcResult: status, disassembly text, and errors ) = 0; }; static const UINT32 DxcValidatorFlags_Default = 0; static const UINT32 DxcValidatorFlags_InPlaceEdit = 1; // Validator is allowed to update shader blob in-place. static const UINT32 DxcValidatorFlags_RootSignatureOnly = 2; static const UINT32 DxcValidatorFlags_ModuleOnly = 4; static const UINT32 DxcValidatorFlags_ValidMask = 0x7; CROSS_PLATFORM_UUIDOF(IDxcValidator, "A6E82BD2-1FD7-4826-9811-2857E797F49A") struct IDxcValidator : public IUnknown { // Validate a shader. virtual HRESULT STDMETHODCALLTYPE Validate( _In_ IDxcBlob *pShader, // Shader to validate. _In_ UINT32 Flags, // Validation flags. _COM_Outptr_ IDxcOperationResult **ppResult // Validation output status, buffer, and errors ) = 0; }; CROSS_PLATFORM_UUIDOF(IDxcValidator2, "458e1fd1-b1b2-4750-a6e1-9c10f03bed92") struct IDxcValidator2 : public IDxcValidator { // Validate a shader. virtual HRESULT STDMETHODCALLTYPE ValidateWithDebug( _In_ IDxcBlob *pShader, // Shader to validate. _In_ UINT32 Flags, // Validation flags. _In_opt_ DxcBuffer *pOptDebugBitcode, // Optional debug module bitcode to provide line numbers _COM_Outptr_ IDxcOperationResult **ppResult // Validation output status, buffer, and errors ) = 0; }; CROSS_PLATFORM_UUIDOF(IDxcContainerBuilder, "334b1f50-2292-4b35-99a1-25588d8c17fe") struct IDxcContainerBuilder : public IUnknown { virtual HRESULT STDMETHODCALLTYPE Load(_In_ IDxcBlob *pDxilContainerHeader) = 0; // Loads DxilContainer to the builder virtual HRESULT STDMETHODCALLTYPE AddPart(_In_ UINT32 fourCC, _In_ IDxcBlob *pSource) = 0; // Part to add to the container virtual HRESULT STDMETHODCALLTYPE RemovePart(_In_ UINT32 fourCC) = 0; // Remove the part with fourCC virtual HRESULT STDMETHODCALLTYPE SerializeContainer(_Out_ IDxcOperationResult **ppResult) = 0; // Builds a container of the given container builder state }; CROSS_PLATFORM_UUIDOF(IDxcAssembler, "091f7a26-1c1f-4948-904b-e6e3a8a771d5") struct IDxcAssembler : public IUnknown { // Assemble dxil in ll or llvm bitcode to DXIL container. virtual HRESULT STDMETHODCALLTYPE AssembleToContainer( _In_ IDxcBlob *pShader, // Shader to assemble. _COM_Outptr_ IDxcOperationResult **ppResult // Assembly output status, buffer, and errors ) = 0; }; CROSS_PLATFORM_UUIDOF(IDxcContainerReflection, "d2c21b26-8350-4bdc-976a-331ce6f4c54c") struct IDxcContainerReflection : public IUnknown { virtual HRESULT STDMETHODCALLTYPE Load(_In_ IDxcBlob *pContainer) = 0; // Container to load. virtual HRESULT STDMETHODCALLTYPE GetPartCount(_Out_ UINT32 *pResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetPartKind(UINT32 idx, _Out_ UINT32 *pResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetPartContent(UINT32 idx, _COM_Outptr_ IDxcBlob **ppResult) = 0; virtual HRESULT STDMETHODCALLTYPE FindFirstPartKind(UINT32 kind, _Out_ UINT32 *pResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetPartReflection(UINT32 idx, REFIID iid, void **ppvObject) = 0; }; CROSS_PLATFORM_UUIDOF(IDxcOptimizerPass, "AE2CD79F-CC22-453F-9B6B-B124E7A5204C") struct IDxcOptimizerPass : public IUnknown { virtual HRESULT STDMETHODCALLTYPE GetOptionName(_COM_Outptr_ LPWSTR *ppResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetDescription(_COM_Outptr_ LPWSTR *ppResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetOptionArgCount(_Out_ UINT32 *pCount) = 0; virtual HRESULT STDMETHODCALLTYPE GetOptionArgName(UINT32 argIndex, _COM_Outptr_ LPWSTR *ppResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetOptionArgDescription(UINT32 argIndex, _COM_Outptr_ LPWSTR *ppResult) = 0; }; CROSS_PLATFORM_UUIDOF(IDxcOptimizer, "25740E2E-9CBA-401B-9119-4FB42F39F270") struct IDxcOptimizer : public IUnknown { virtual HRESULT STDMETHODCALLTYPE GetAvailablePassCount(_Out_ UINT32 *pCount) = 0; virtual HRESULT STDMETHODCALLTYPE GetAvailablePass(UINT32 index, _COM_Outptr_ IDxcOptimizerPass** ppResult) = 0; virtual HRESULT STDMETHODCALLTYPE RunOptimizer(IDxcBlob *pBlob, _In_count_(optionCount) LPCWSTR *ppOptions, UINT32 optionCount, _COM_Outptr_ IDxcBlob **pOutputModule, _COM_Outptr_opt_ IDxcBlobEncoding **ppOutputText) = 0; }; static const UINT32 DxcVersionInfoFlags_None = 0; static const UINT32 DxcVersionInfoFlags_Debug = 1; // Matches VS_FF_DEBUG static const UINT32 DxcVersionInfoFlags_Internal = 2; // Internal Validator (non-signing) CROSS_PLATFORM_UUIDOF(IDxcVersionInfo, "b04f5b50-2059-4f12-a8ff-a1e0cde1cc7e") struct IDxcVersionInfo : public IUnknown { virtual HRESULT STDMETHODCALLTYPE GetVersion(_Out_ UINT32 *pMajor, _Out_ UINT32 *pMinor) = 0; virtual HRESULT STDMETHODCALLTYPE GetFlags(_Out_ UINT32 *pFlags) = 0; }; CROSS_PLATFORM_UUIDOF(IDxcVersionInfo2, "fb6904c4-42f0-4b62-9c46-983af7da7c83") struct IDxcVersionInfo2 : public IDxcVersionInfo { virtual HRESULT STDMETHODCALLTYPE GetCommitInfo( _Out_ UINT32 *pCommitCount, // The total number commits. _Outptr_result_z_ char **pCommitHash // The SHA of the latest commit. (Must be CoTaskMemFree()'d!) ) = 0; }; CROSS_PLATFORM_UUIDOF(IDxcVersionInfo3, "5e13e843-9d25-473c-9ad2-03b2d0b44b1e") struct IDxcVersionInfo3 : public IUnknown { virtual HRESULT STDMETHODCALLTYPE GetCustomVersionString( _Outptr_result_z_ char **pVersionString // Custom version string for compiler. (Must be CoTaskMemFree()'d!) ) = 0; }; struct DxcArgPair { const WCHAR *pName; const WCHAR *pValue; }; CROSS_PLATFORM_UUIDOF(IDxcPdbUtils, "E6C9647E-9D6A-4C3B-B94C-524B5A6C343D") struct IDxcPdbUtils : public IUnknown { virtual HRESULT STDMETHODCALLTYPE Load(_In_ IDxcBlob *pPdbOrDxil) = 0; virtual HRESULT STDMETHODCALLTYPE GetSourceCount(_Out_ UINT32 *pCount) = 0; virtual HRESULT STDMETHODCALLTYPE GetSource(_In_ UINT32 uIndex, _COM_Outptr_ IDxcBlobEncoding **ppResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetSourceName(_In_ UINT32 uIndex, _Outptr_result_z_ BSTR *pResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetFlagCount(_Out_ UINT32 *pCount) = 0; virtual HRESULT STDMETHODCALLTYPE GetFlag(_In_ UINT32 uIndex, _Outptr_result_z_ BSTR *pResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetArgCount(_Out_ UINT32 *pCount) = 0; virtual HRESULT STDMETHODCALLTYPE GetArg(_In_ UINT32 uIndex, _Outptr_result_z_ BSTR *pResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetArgPairCount(_Out_ UINT32 *pCount) = 0; virtual HRESULT STDMETHODCALLTYPE GetArgPair(_In_ UINT32 uIndex, _Outptr_result_z_ BSTR *pName, _Outptr_result_z_ BSTR *pValue) = 0; virtual HRESULT STDMETHODCALLTYPE GetDefineCount(_Out_ UINT32 *pCount) = 0; virtual HRESULT STDMETHODCALLTYPE GetDefine(_In_ UINT32 uIndex, _Outptr_result_z_ BSTR *pResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetTargetProfile(_Outptr_result_z_ BSTR *pResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetEntryPoint(_Outptr_result_z_ BSTR *pResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetMainFileName(_Outptr_result_z_ BSTR *pResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetHash(_COM_Outptr_ IDxcBlob **ppResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetName(_Outptr_result_z_ BSTR *pResult) = 0; virtual BOOL STDMETHODCALLTYPE IsFullPDB() = 0; virtual HRESULT STDMETHODCALLTYPE GetFullPDB(_COM_Outptr_ IDxcBlob **ppFullPDB) = 0; virtual HRESULT STDMETHODCALLTYPE GetVersionInfo(_COM_Outptr_ IDxcVersionInfo **ppVersionInfo) = 0; virtual HRESULT STDMETHODCALLTYPE SetCompiler(_In_ IDxcCompiler3 *pCompiler) = 0; virtual HRESULT STDMETHODCALLTYPE CompileForFullPDB(_COM_Outptr_ IDxcResult **ppResult) = 0; virtual HRESULT STDMETHODCALLTYPE OverrideArgs(_In_ DxcArgPair *pArgPairs, UINT32 uNumArgPairs) = 0; virtual HRESULT STDMETHODCALLTYPE OverrideRootSignature(_In_ const WCHAR *pRootSignature) = 0; }; CROSS_PLATFORM_UUIDOF(IDxcPdbUtils2, "4315D938-F369-4F93-95A2-252017CC3807") struct IDxcPdbUtils2 : public IUnknown { virtual HRESULT STDMETHODCALLTYPE Load(_In_ IDxcBlob *pPdbOrDxil) = 0; virtual HRESULT STDMETHODCALLTYPE GetSourceCount(_Out_ UINT32 *pCount) = 0; virtual HRESULT STDMETHODCALLTYPE GetSource(_In_ UINT32 uIndex, _COM_Outptr_ IDxcBlobEncoding **ppResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetSourceName(_In_ UINT32 uIndex, _COM_Outptr_ IDxcBlobWide **ppResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetLibraryPDBCount(UINT32 *pCount) = 0; virtual HRESULT STDMETHODCALLTYPE GetLibraryPDB(_In_ UINT32 uIndex, _COM_Outptr_ IDxcPdbUtils2 **ppOutPdbUtils, _COM_Outptr_opt_result_maybenull_ IDxcBlobWide **ppLibraryName) = 0; virtual HRESULT STDMETHODCALLTYPE GetFlagCount(_Out_ UINT32 *pCount) = 0; virtual HRESULT STDMETHODCALLTYPE GetFlag(_In_ UINT32 uIndex, _COM_Outptr_ IDxcBlobWide **ppResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetArgCount(_Out_ UINT32 *pCount) = 0; virtual HRESULT STDMETHODCALLTYPE GetArg(_In_ UINT32 uIndex, _COM_Outptr_ IDxcBlobWide **ppResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetArgPairCount(_Out_ UINT32 *pCount) = 0; virtual HRESULT STDMETHODCALLTYPE GetArgPair(_In_ UINT32 uIndex, _COM_Outptr_result_maybenull_ IDxcBlobWide **ppName, _COM_Outptr_result_maybenull_ IDxcBlobWide **ppValue) = 0; virtual HRESULT STDMETHODCALLTYPE GetDefineCount(_Out_ UINT32 *pCount) = 0; virtual HRESULT STDMETHODCALLTYPE GetDefine(_In_ UINT32 uIndex, _COM_Outptr_ IDxcBlobWide **ppResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetTargetProfile(_COM_Outptr_result_maybenull_ IDxcBlobWide **ppResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetEntryPoint(_COM_Outptr_result_maybenull_ IDxcBlobWide **ppResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetMainFileName(_COM_Outptr_result_maybenull_ IDxcBlobWide **ppResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetHash(_COM_Outptr_result_maybenull_ IDxcBlob **ppResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetName(_COM_Outptr_result_maybenull_ IDxcBlobWide **ppResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetVersionInfo(_COM_Outptr_result_maybenull_ IDxcVersionInfo **ppVersionInfo) = 0; virtual HRESULT STDMETHODCALLTYPE GetCustomToolchainID(_Out_ UINT32 *pID) = 0; virtual HRESULT STDMETHODCALLTYPE GetCustomToolchainData(_COM_Outptr_result_maybenull_ IDxcBlob **ppBlob) = 0; virtual HRESULT STDMETHODCALLTYPE GetWholeDxil(_COM_Outptr_result_maybenull_ IDxcBlob **ppResult) = 0; virtual BOOL STDMETHODCALLTYPE IsFullPDB() = 0; virtual BOOL STDMETHODCALLTYPE IsPDBRef() = 0; }; // Note: __declspec(selectany) requires 'extern' // On Linux __declspec(selectany) is removed and using 'extern' results in link error. #ifdef _MSC_VER #define CLSID_SCOPE __declspec(selectany) extern #else #define CLSID_SCOPE #endif CLSID_SCOPE const CLSID CLSID_DxcCompiler = { 0x73e22d93, 0xe6ce, 0x47f3, {0xb5, 0xbf, 0xf0, 0x66, 0x4f, 0x39, 0xc1, 0xb0}}; // {EF6A8087-B0EA-4D56-9E45-D07E1A8B7806} CLSID_SCOPE const GUID CLSID_DxcLinker = { 0xef6a8087, 0xb0ea, 0x4d56, {0x9e, 0x45, 0xd0, 0x7e, 0x1a, 0x8b, 0x78, 0x6}}; // {CD1F6B73-2AB0-484D-8EDC-EBE7A43CA09F} CLSID_SCOPE const CLSID CLSID_DxcDiaDataSource = { 0xcd1f6b73, 0x2ab0, 0x484d, {0x8e, 0xdc, 0xeb, 0xe7, 0xa4, 0x3c, 0xa0, 0x9f}}; // {3E56AE82-224D-470F-A1A1-FE3016EE9F9D} CLSID_SCOPE const CLSID CLSID_DxcCompilerArgs = { 0x3e56ae82, 0x224d, 0x470f, {0xa1, 0xa1, 0xfe, 0x30, 0x16, 0xee, 0x9f, 0x9d}}; // {6245D6AF-66E0-48FD-80B4-4D271796748C} CLSID_SCOPE const GUID CLSID_DxcLibrary = { 0x6245d6af, 0x66e0, 0x48fd, {0x80, 0xb4, 0x4d, 0x27, 0x17, 0x96, 0x74, 0x8c}}; CLSID_SCOPE const GUID CLSID_DxcUtils = CLSID_DxcLibrary; // {8CA3E215-F728-4CF3-8CDD-88AF917587A1} CLSID_SCOPE const GUID CLSID_DxcValidator = { 0x8ca3e215, 0xf728, 0x4cf3, {0x8c, 0xdd, 0x88, 0xaf, 0x91, 0x75, 0x87, 0xa1}}; // {D728DB68-F903-4F80-94CD-DCCF76EC7151} CLSID_SCOPE const GUID CLSID_DxcAssembler = { 0xd728db68, 0xf903, 0x4f80, {0x94, 0xcd, 0xdc, 0xcf, 0x76, 0xec, 0x71, 0x51}}; // {b9f54489-55b8-400c-ba3a-1675e4728b91} CLSID_SCOPE const GUID CLSID_DxcContainerReflection = { 0xb9f54489, 0x55b8, 0x400c, {0xba, 0x3a, 0x16, 0x75, 0xe4, 0x72, 0x8b, 0x91}}; // {AE2CD79F-CC22-453F-9B6B-B124E7A5204C} CLSID_SCOPE const GUID CLSID_DxcOptimizer = { 0xae2cd79f, 0xcc22, 0x453f, {0x9b, 0x6b, 0xb1, 0x24, 0xe7, 0xa5, 0x20, 0x4c}}; // {94134294-411f-4574-b4d0-8741e25240d2} CLSID_SCOPE const GUID CLSID_DxcContainerBuilder = { 0x94134294, 0x411f, 0x4574, {0xb4, 0xd0, 0x87, 0x41, 0xe2, 0x52, 0x40, 0xd2}}; // {54621dfb-f2ce-457e-ae8c-ec355faeec7c} CLSID_SCOPE const GUID CLSID_DxcPdbUtils = { 0x54621dfb, 0xf2ce, 0x457e, {0xae, 0x8c, 0xec, 0x35, 0x5f, 0xae, 0xec, 0x7c}}; #endif ================================================ FILE: Source/External/dxc/inc/dxcerrors.h ================================================ /////////////////////////////////////////////////////////////////////////////// // // // dxcerror.h // // Copyright (C) Microsoft Corporation. All rights reserved. // // This file is distributed under the University of Illinois Open Source // // License. See LICENSE.TXT for details. // // // // Provides definition of error codes. // // // /////////////////////////////////////////////////////////////////////////////// #ifndef __DXC_ERRORS__ #define __DXC_ERRORS__ #ifndef FACILITY_GRAPHICS #define FACILITY_GRAPHICS 36 #endif #define DXC_EXCEPTION_CODE(name, status) \ static constexpr DWORD EXCEPTION_##name = \ (0xc0000000u | (FACILITY_GRAPHICS << 16) | (0xff00u | (status & 0xffu))); DXC_EXCEPTION_CODE(LOAD_LIBRARY_FAILED, 0x00u) DXC_EXCEPTION_CODE(NO_HMODULE, 0x01u) DXC_EXCEPTION_CODE(GET_PROC_FAILED, 0x02u) #undef DXC_EXCEPTION_CODE #endif ================================================ FILE: Source/External/dxc/inc/dxcisense.h ================================================ /////////////////////////////////////////////////////////////////////////////// // // // dxcisense.h // // Copyright (C) Microsoft Corporation. All rights reserved. // // This file is distributed under the University of Illinois Open Source // // License. See LICENSE.TXT for details. // // // // Provides declarations for the DirectX Compiler IntelliSense component. // // // /////////////////////////////////////////////////////////////////////////////// #ifndef __DXC_ISENSE__ #define __DXC_ISENSE__ #include "dxcapi.h" #ifndef _WIN32 #include "Support/WinAdapter.h" #endif typedef enum DxcGlobalOptions { DxcGlobalOpt_None = 0x0, DxcGlobalOpt_ThreadBackgroundPriorityForIndexing = 0x1, DxcGlobalOpt_ThreadBackgroundPriorityForEditing = 0x2, DxcGlobalOpt_ThreadBackgroundPriorityForAll = DxcGlobalOpt_ThreadBackgroundPriorityForIndexing | DxcGlobalOpt_ThreadBackgroundPriorityForEditing } DxcGlobalOptions; typedef enum DxcTokenKind { DxcTokenKind_Punctuation = 0, // A token that contains some kind of punctuation. DxcTokenKind_Keyword = 1, // A language keyword. DxcTokenKind_Identifier = 2, // An identifier (that is not a keyword). DxcTokenKind_Literal = 3, // A numeric, string, or character literal. DxcTokenKind_Comment = 4, // A comment. DxcTokenKind_Unknown = 5, // An unknown token (possibly known to a future version). DxcTokenKind_BuiltInType = 6, // A built-in type like int, void or float3. } DxcTokenKind; typedef enum DxcTypeKind { DxcTypeKind_Invalid = 0, // Reprents an invalid type (e.g., where no type is available). DxcTypeKind_Unexposed = 1, // A type whose specific kind is not exposed via this interface. // Builtin types DxcTypeKind_Void = 2, DxcTypeKind_Bool = 3, DxcTypeKind_Char_U = 4, DxcTypeKind_UChar = 5, DxcTypeKind_Char16 = 6, DxcTypeKind_Char32 = 7, DxcTypeKind_UShort = 8, DxcTypeKind_UInt = 9, DxcTypeKind_ULong = 10, DxcTypeKind_ULongLong = 11, DxcTypeKind_UInt128 = 12, DxcTypeKind_Char_S = 13, DxcTypeKind_SChar = 14, DxcTypeKind_WChar = 15, DxcTypeKind_Short = 16, DxcTypeKind_Int = 17, DxcTypeKind_Long = 18, DxcTypeKind_LongLong = 19, DxcTypeKind_Int128 = 20, DxcTypeKind_Float = 21, DxcTypeKind_Double = 22, DxcTypeKind_LongDouble = 23, DxcTypeKind_NullPtr = 24, DxcTypeKind_Overload = 25, DxcTypeKind_Dependent = 26, DxcTypeKind_ObjCId = 27, DxcTypeKind_ObjCClass = 28, DxcTypeKind_ObjCSel = 29, DxcTypeKind_FirstBuiltin = DxcTypeKind_Void, DxcTypeKind_LastBuiltin = DxcTypeKind_ObjCSel, DxcTypeKind_Complex = 100, DxcTypeKind_Pointer = 101, DxcTypeKind_BlockPointer = 102, DxcTypeKind_LValueReference = 103, DxcTypeKind_RValueReference = 104, DxcTypeKind_Record = 105, DxcTypeKind_Enum = 106, DxcTypeKind_Typedef = 107, DxcTypeKind_ObjCInterface = 108, DxcTypeKind_ObjCObjectPointer = 109, DxcTypeKind_FunctionNoProto = 110, DxcTypeKind_FunctionProto = 111, DxcTypeKind_ConstantArray = 112, DxcTypeKind_Vector = 113, DxcTypeKind_IncompleteArray = 114, DxcTypeKind_VariableArray = 115, DxcTypeKind_DependentSizedArray = 116, DxcTypeKind_MemberPointer = 117 } DxcTypeKind; // Describes the severity of a particular diagnostic. typedef enum DxcDiagnosticSeverity { // A diagnostic that has been suppressed, e.g., by a command-line option. DxcDiagnostic_Ignored = 0, // This diagnostic is a note that should be attached to the previous (non-note) diagnostic. DxcDiagnostic_Note = 1, // This diagnostic indicates suspicious code that may not be wrong. DxcDiagnostic_Warning = 2, // This diagnostic indicates that the code is ill-formed. DxcDiagnostic_Error = 3, // This diagnostic indicates that the code is ill-formed such that future // parser rec unlikely to produce useful results. DxcDiagnostic_Fatal = 4 } DxcDiagnosticSeverity; // Options to control the display of diagnostics. typedef enum DxcDiagnosticDisplayOptions { // Display the source-location information where the diagnostic was located. DxcDiagnostic_DisplaySourceLocation = 0x01, // If displaying the source-location information of the diagnostic, // also include the column number. DxcDiagnostic_DisplayColumn = 0x02, // If displaying the source-location information of the diagnostic, // also include information about source ranges in a machine-parsable format. DxcDiagnostic_DisplaySourceRanges = 0x04, // Display the option name associated with this diagnostic, if any. DxcDiagnostic_DisplayOption = 0x08, // Display the category number associated with this diagnostic, if any. DxcDiagnostic_DisplayCategoryId = 0x10, // Display the category name associated with this diagnostic, if any. DxcDiagnostic_DisplayCategoryName = 0x20, // Display the severity of the diagnostic message. DxcDiagnostic_DisplaySeverity = 0x200 } DxcDiagnosticDisplayOptions; typedef enum DxcTranslationUnitFlags { // Used to indicate that no special translation-unit options are needed. DxcTranslationUnitFlags_None = 0x0, // Used to indicate that the parser should construct a "detailed" // preprocessing record, including all macro definitions and instantiations. DxcTranslationUnitFlags_DetailedPreprocessingRecord = 0x01, // Used to indicate that the translation unit is incomplete. DxcTranslationUnitFlags_Incomplete = 0x02, // Used to indicate that the translation unit should be built with an // implicit precompiled header for the preamble. DxcTranslationUnitFlags_PrecompiledPreamble = 0x04, // Used to indicate that the translation unit should cache some // code-completion results with each reparse of the source file. DxcTranslationUnitFlags_CacheCompletionResults = 0x08, // Used to indicate that the translation unit will be serialized with // SaveTranslationUnit. DxcTranslationUnitFlags_ForSerialization = 0x10, // DEPRECATED DxcTranslationUnitFlags_CXXChainedPCH = 0x20, // Used to indicate that function/method bodies should be skipped while parsing. DxcTranslationUnitFlags_SkipFunctionBodies = 0x40, // Used to indicate that brief documentation comments should be // included into the set of code completions returned from this translation // unit. DxcTranslationUnitFlags_IncludeBriefCommentsInCodeCompletion = 0x80, // Used to indicate that compilation should occur on the caller's thread. DxcTranslationUnitFlags_UseCallerThread = 0x800 } DxcTranslationUnitFlags; typedef enum DxcCursorFormatting { DxcCursorFormatting_Default = 0x0, // Default rules, language-insensitive formatting. DxcCursorFormatting_UseLanguageOptions = 0x1, // Language-sensitive formatting. DxcCursorFormatting_SuppressSpecifiers = 0x2, // Supresses type specifiers. DxcCursorFormatting_SuppressTagKeyword = 0x4, // Suppressed tag keyword (eg, 'class'). DxcCursorFormatting_IncludeNamespaceKeyword = 0x8, // Include namespace keyword. } DxcCursorFormatting; enum DxcCursorKind { /* Declarations */ DxcCursor_UnexposedDecl = 1, // A declaration whose specific kind is not exposed via this interface. DxcCursor_StructDecl = 2, // A C or C++ struct. DxcCursor_UnionDecl = 3, // A C or C++ union. DxcCursor_ClassDecl = 4, // A C++ class. DxcCursor_EnumDecl = 5, // An enumeration. DxcCursor_FieldDecl = 6, // A field (in C) or non-static data member (in C++) in a struct, union, or C++ class. DxcCursor_EnumConstantDecl = 7, // An enumerator constant. DxcCursor_FunctionDecl = 8, // A function. DxcCursor_VarDecl = 9, // A variable. DxcCursor_ParmDecl = 10, // A function or method parameter. DxcCursor_ObjCInterfaceDecl = 11, // An Objective-C interface. DxcCursor_ObjCCategoryDecl = 12, // An Objective-C interface for a category. DxcCursor_ObjCProtocolDecl = 13, // An Objective-C protocol declaration. DxcCursor_ObjCPropertyDecl = 14, // An Objective-C property declaration. DxcCursor_ObjCIvarDecl = 15, // An Objective-C instance variable. DxcCursor_ObjCInstanceMethodDecl = 16, // An Objective-C instance method. DxcCursor_ObjCClassMethodDecl = 17, // An Objective-C class method. DxcCursor_ObjCImplementationDecl = 18, // An Objective-C \@implementation. DxcCursor_ObjCCategoryImplDecl = 19, // An Objective-C \@implementation for a category. DxcCursor_TypedefDecl = 20, // A typedef DxcCursor_CXXMethod = 21, // A C++ class method. DxcCursor_Namespace = 22, // A C++ namespace. DxcCursor_LinkageSpec = 23, // A linkage specification, e.g. 'extern "C"'. DxcCursor_Constructor = 24, // A C++ constructor. DxcCursor_Destructor = 25, // A C++ destructor. DxcCursor_ConversionFunction = 26, // A C++ conversion function. DxcCursor_TemplateTypeParameter = 27, // A C++ template type parameter. DxcCursor_NonTypeTemplateParameter = 28, // A C++ non-type template parameter. DxcCursor_TemplateTemplateParameter = 29, // A C++ template template parameter. DxcCursor_FunctionTemplate = 30, // A C++ function template. DxcCursor_ClassTemplate = 31, // A C++ class template. DxcCursor_ClassTemplatePartialSpecialization = 32, // A C++ class template partial specialization. DxcCursor_NamespaceAlias = 33, // A C++ namespace alias declaration. DxcCursor_UsingDirective = 34, // A C++ using directive. DxcCursor_UsingDeclaration = 35, // A C++ using declaration. DxcCursor_TypeAliasDecl = 36, // A C++ alias declaration DxcCursor_ObjCSynthesizeDecl = 37, // An Objective-C \@synthesize definition. DxcCursor_ObjCDynamicDecl = 38, // An Objective-C \@dynamic definition. DxcCursor_CXXAccessSpecifier = 39, // An access specifier. DxcCursor_FirstDecl = DxcCursor_UnexposedDecl, DxcCursor_LastDecl = DxcCursor_CXXAccessSpecifier, /* References */ DxcCursor_FirstRef = 40, /* Decl references */ DxcCursor_ObjCSuperClassRef = 40, DxcCursor_ObjCProtocolRef = 41, DxcCursor_ObjCClassRef = 42, /** * \brief A reference to a type declaration. * * A type reference occurs anywhere where a type is named but not * declared. For example, given: * * \code * typedef unsigned size_type; * size_type size; * \endcode * * The typedef is a declaration of size_type (DxcCursor_TypedefDecl), * while the type of the variable "size" is referenced. The cursor * referenced by the type of size is the typedef for size_type. */ DxcCursor_TypeRef = 43, // A reference to a type declaration. DxcCursor_CXXBaseSpecifier = 44, DxcCursor_TemplateRef = 45, // A reference to a class template, function template, template template parameter, or class template partial specialization. DxcCursor_NamespaceRef = 46, // A reference to a namespace or namespace alias. DxcCursor_MemberRef = 47, // A reference to a member of a struct, union, or class that occurs in some non-expression context, e.g., a designated initializer. /** * \brief A reference to a labeled statement. * * This cursor kind is used to describe the jump to "start_over" in the * goto statement in the following example: * * \code * start_over: * ++counter; * * goto start_over; * \endcode * * A label reference cursor refers to a label statement. */ DxcCursor_LabelRef = 48, // A reference to a labeled statement. // A reference to a set of overloaded functions or function templates // that has not yet been resolved to a specific function or function template. // // An overloaded declaration reference cursor occurs in C++ templates where // a dependent name refers to a function. DxcCursor_OverloadedDeclRef = 49, DxcCursor_VariableRef = 50, // A reference to a variable that occurs in some non-expression context, e.g., a C++ lambda capture list. DxcCursor_LastRef = DxcCursor_VariableRef, /* Error conditions */ DxcCursor_FirstInvalid = 70, DxcCursor_InvalidFile = 70, DxcCursor_NoDeclFound = 71, DxcCursor_NotImplemented = 72, DxcCursor_InvalidCode = 73, DxcCursor_LastInvalid = DxcCursor_InvalidCode, /* Expressions */ DxcCursor_FirstExpr = 100, /** * \brief An expression whose specific kind is not exposed via this * interface. * * Unexposed expressions have the same operations as any other kind * of expression; one can extract their location information, * spelling, children, etc. However, the specific kind of the * expression is not reported. */ DxcCursor_UnexposedExpr = 100, // An expression whose specific kind is not exposed via this interface. DxcCursor_DeclRefExpr = 101, // An expression that refers to some value declaration, such as a function, varible, or enumerator. DxcCursor_MemberRefExpr = 102, // An expression that refers to a member of a struct, union, class, Objective-C class, etc. DxcCursor_CallExpr = 103, // An expression that calls a function. DxcCursor_ObjCMessageExpr = 104, // An expression that sends a message to an Objective-C object or class. DxcCursor_BlockExpr = 105, // An expression that represents a block literal. DxcCursor_IntegerLiteral = 106, // An integer literal. DxcCursor_FloatingLiteral = 107, // A floating point number literal. DxcCursor_ImaginaryLiteral = 108, // An imaginary number literal. DxcCursor_StringLiteral = 109, // A string literal. DxcCursor_CharacterLiteral = 110, // A character literal. DxcCursor_ParenExpr = 111, // A parenthesized expression, e.g. "(1)". This AST node is only formed if full location information is requested. DxcCursor_UnaryOperator = 112, // This represents the unary-expression's (except sizeof and alignof). DxcCursor_ArraySubscriptExpr = 113, // [C99 6.5.2.1] Array Subscripting. DxcCursor_BinaryOperator = 114, // A builtin binary operation expression such as "x + y" or "x <= y". DxcCursor_CompoundAssignOperator = 115, // Compound assignment such as "+=". DxcCursor_ConditionalOperator = 116, // The ?: ternary operator. DxcCursor_CStyleCastExpr = 117, // An explicit cast in C (C99 6.5.4) or a C-style cast in C++ (C++ [expr.cast]), which uses the syntax (Type)expr, eg: (int)f. DxcCursor_CompoundLiteralExpr = 118, // [C99 6.5.2.5] DxcCursor_InitListExpr = 119, // Describes an C or C++ initializer list. DxcCursor_AddrLabelExpr = 120, // The GNU address of label extension, representing &&label. DxcCursor_StmtExpr = 121, // This is the GNU Statement Expression extension: ({int X=4; X;}) DxcCursor_GenericSelectionExpr = 122, // Represents a C11 generic selection. /** \brief Implements the GNU __null extension, which is a name for a null * pointer constant that has integral type (e.g., int or long) and is the same * size and alignment as a pointer. * * The __null extension is typically only used by system headers, which define * NULL as __null in C++ rather than using 0 (which is an integer that may not * match the size of a pointer). */ DxcCursor_GNUNullExpr = 123, DxcCursor_CXXStaticCastExpr = 124, // C++'s static_cast<> expression. DxcCursor_CXXDynamicCastExpr = 125, // C++'s dynamic_cast<> expression. DxcCursor_CXXReinterpretCastExpr = 126, // C++'s reinterpret_cast<> expression. DxcCursor_CXXConstCastExpr = 127, // C++'s const_cast<> expression. /** \brief Represents an explicit C++ type conversion that uses "functional" * notion (C++ [expr.type.conv]). * * Example: * \code * x = int(0.5); * \endcode */ DxcCursor_CXXFunctionalCastExpr = 128, DxcCursor_CXXTypeidExpr = 129, // A C++ typeid expression (C++ [expr.typeid]). DxcCursor_CXXBoolLiteralExpr = 130, // [C++ 2.13.5] C++ Boolean Literal. DxcCursor_CXXNullPtrLiteralExpr = 131, // [C++0x 2.14.7] C++ Pointer Literal. DxcCursor_CXXThisExpr = 132, // Represents the "this" expression in C++ DxcCursor_CXXThrowExpr = 133, // [C++ 15] C++ Throw Expression, both 'throw' and 'throw' assignment-expression. DxcCursor_CXXNewExpr = 134, // A new expression for memory allocation and constructor calls, e.g: "new CXXNewExpr(foo)". DxcCursor_CXXDeleteExpr = 135, // A delete expression for memory deallocation and destructor calls, e.g. "delete[] pArray". DxcCursor_UnaryExpr = 136, // A unary expression. DxcCursor_ObjCStringLiteral = 137, // An Objective-C string literal i.e. @"foo". DxcCursor_ObjCEncodeExpr = 138, // An Objective-C \@encode expression. DxcCursor_ObjCSelectorExpr = 139, // An Objective-C \@selector expression. DxcCursor_ObjCProtocolExpr = 140, // An Objective-C \@protocol expression. /** \brief An Objective-C "bridged" cast expression, which casts between * Objective-C pointers and C pointers, transferring ownership in the process. * * \code * NSString *str = (__bridge_transfer NSString *)CFCreateString(); * \endcode */ DxcCursor_ObjCBridgedCastExpr = 141, /** \brief Represents a C++0x pack expansion that produces a sequence of * expressions. * * A pack expansion expression contains a pattern (which itself is an * expression) followed by an ellipsis. For example: * * \code * template * void forward(F f, Types &&...args) { * f(static_cast(args)...); * } * \endcode */ DxcCursor_PackExpansionExpr = 142, /** \brief Represents an expression that computes the length of a parameter * pack. * * \code * template * struct count { * static const unsigned value = sizeof...(Types); * }; * \endcode */ DxcCursor_SizeOfPackExpr = 143, /* \brief Represents a C++ lambda expression that produces a local function * object. * * \code * void abssort(float *x, unsigned N) { * std::sort(x, x + N, * [](float a, float b) { * return std::abs(a) < std::abs(b); * }); * } * \endcode */ DxcCursor_LambdaExpr = 144, DxcCursor_ObjCBoolLiteralExpr = 145, // Objective-c Boolean Literal. DxcCursor_ObjCSelfExpr = 146, // Represents the "self" expression in a ObjC method. DxcCursor_LastExpr = DxcCursor_ObjCSelfExpr, /* Statements */ DxcCursor_FirstStmt = 200, /** * \brief A statement whose specific kind is not exposed via this * interface. * * Unexposed statements have the same operations as any other kind of * statement; one can extract their location information, spelling, * children, etc. However, the specific kind of the statement is not * reported. */ DxcCursor_UnexposedStmt = 200, /** \brief A labelled statement in a function. * * This cursor kind is used to describe the "start_over:" label statement in * the following example: * * \code * start_over: * ++counter; * \endcode * */ DxcCursor_LabelStmt = 201, DxcCursor_CompoundStmt = 202, // A group of statements like { stmt stmt }. This cursor kind is used to describe compound statements, e.g. function bodies. DxcCursor_CaseStmt = 203, // A case statement. DxcCursor_DefaultStmt = 204, // A default statement. DxcCursor_IfStmt = 205, // An if statement DxcCursor_SwitchStmt = 206, // A switch statement. DxcCursor_WhileStmt = 207, // A while statement. DxcCursor_DoStmt = 208, // A do statement. DxcCursor_ForStmt = 209, // A for statement. DxcCursor_GotoStmt = 210, // A goto statement. DxcCursor_IndirectGotoStmt = 211, // An indirect goto statement. DxcCursor_ContinueStmt = 212, // A continue statement. DxcCursor_BreakStmt = 213, // A break statement. DxcCursor_ReturnStmt = 214, // A return statement. DxcCursor_GCCAsmStmt = 215, // A GCC inline assembly statement extension. DxcCursor_AsmStmt = DxcCursor_GCCAsmStmt, DxcCursor_ObjCAtTryStmt = 216, // Objective-C's overall \@try-\@catch-\@finally statement. DxcCursor_ObjCAtCatchStmt = 217, // Objective-C's \@catch statement. DxcCursor_ObjCAtFinallyStmt = 218, // Objective-C's \@finally statement. DxcCursor_ObjCAtThrowStmt = 219, // Objective-C's \@throw statement. DxcCursor_ObjCAtSynchronizedStmt = 220, // Objective-C's \@synchronized statement. DxcCursor_ObjCAutoreleasePoolStmt = 221, // Objective-C's autorelease pool statement. DxcCursor_ObjCForCollectionStmt = 222, // Objective-C's collection statement. DxcCursor_CXXCatchStmt = 223, // C++'s catch statement. DxcCursor_CXXTryStmt = 224, // C++'s try statement. DxcCursor_CXXForRangeStmt = 225, // C++'s for (* : *) statement. DxcCursor_SEHTryStmt = 226, // Windows Structured Exception Handling's try statement. DxcCursor_SEHExceptStmt = 227, // Windows Structured Exception Handling's except statement. DxcCursor_SEHFinallyStmt = 228, // Windows Structured Exception Handling's finally statement. DxcCursor_MSAsmStmt = 229, // A MS inline assembly statement extension. DxcCursor_NullStmt = 230, // The null satement ";": C99 6.8.3p3. DxcCursor_DeclStmt = 231, // Adaptor class for mixing declarations with statements and expressions. DxcCursor_OMPParallelDirective = 232, // OpenMP parallel directive. DxcCursor_OMPSimdDirective = 233, // OpenMP SIMD directive. DxcCursor_OMPForDirective = 234, // OpenMP for directive. DxcCursor_OMPSectionsDirective = 235, // OpenMP sections directive. DxcCursor_OMPSectionDirective = 236, // OpenMP section directive. DxcCursor_OMPSingleDirective = 237, // OpenMP single directive. DxcCursor_OMPParallelForDirective = 238, // OpenMP parallel for directive. DxcCursor_OMPParallelSectionsDirective = 239, // OpenMP parallel sections directive. DxcCursor_OMPTaskDirective = 240, // OpenMP task directive. DxcCursor_OMPMasterDirective = 241, // OpenMP master directive. DxcCursor_OMPCriticalDirective = 242, // OpenMP critical directive. DxcCursor_OMPTaskyieldDirective = 243, // OpenMP taskyield directive. DxcCursor_OMPBarrierDirective = 244, // OpenMP barrier directive. DxcCursor_OMPTaskwaitDirective = 245, // OpenMP taskwait directive. DxcCursor_OMPFlushDirective = 246, // OpenMP flush directive. DxcCursor_SEHLeaveStmt = 247, // Windows Structured Exception Handling's leave statement. DxcCursor_OMPOrderedDirective = 248, // OpenMP ordered directive. DxcCursor_OMPAtomicDirective = 249, // OpenMP atomic directive. DxcCursor_OMPForSimdDirective = 250, // OpenMP for SIMD directive. DxcCursor_OMPParallelForSimdDirective = 251, // OpenMP parallel for SIMD directive. DxcCursor_OMPTargetDirective = 252, // OpenMP target directive. DxcCursor_OMPTeamsDirective = 253, // OpenMP teams directive. DxcCursor_OMPTaskgroupDirective = 254, // OpenMP taskgroup directive. DxcCursor_OMPCancellationPointDirective = 255, // OpenMP cancellation point directive. DxcCursor_OMPCancelDirective = 256, // OpenMP cancel directive. DxcCursor_LastStmt = DxcCursor_OMPCancelDirective, DxcCursor_TranslationUnit = 300, // Cursor that represents the translation unit itself. /* Attributes */ DxcCursor_FirstAttr = 400, /** * \brief An attribute whose specific kind is not exposed via this * interface. */ DxcCursor_UnexposedAttr = 400, DxcCursor_IBActionAttr = 401, DxcCursor_IBOutletAttr = 402, DxcCursor_IBOutletCollectionAttr = 403, DxcCursor_CXXFinalAttr = 404, DxcCursor_CXXOverrideAttr = 405, DxcCursor_AnnotateAttr = 406, DxcCursor_AsmLabelAttr = 407, DxcCursor_PackedAttr = 408, DxcCursor_PureAttr = 409, DxcCursor_ConstAttr = 410, DxcCursor_NoDuplicateAttr = 411, DxcCursor_CUDAConstantAttr = 412, DxcCursor_CUDADeviceAttr = 413, DxcCursor_CUDAGlobalAttr = 414, DxcCursor_CUDAHostAttr = 415, DxcCursor_CUDASharedAttr = 416, DxcCursor_LastAttr = DxcCursor_CUDASharedAttr, /* Preprocessing */ DxcCursor_PreprocessingDirective = 500, DxcCursor_MacroDefinition = 501, DxcCursor_MacroExpansion = 502, DxcCursor_MacroInstantiation = DxcCursor_MacroExpansion, DxcCursor_InclusionDirective = 503, DxcCursor_FirstPreprocessing = DxcCursor_PreprocessingDirective, DxcCursor_LastPreprocessing = DxcCursor_InclusionDirective, /* Extra Declarations */ /** * \brief A module import declaration. */ DxcCursor_ModuleImportDecl = 600, DxcCursor_FirstExtraDecl = DxcCursor_ModuleImportDecl, DxcCursor_LastExtraDecl = DxcCursor_ModuleImportDecl }; enum DxcCursorKindFlags { DxcCursorKind_None = 0, DxcCursorKind_Declaration = 0x1, DxcCursorKind_Reference = 0x2, DxcCursorKind_Expression = 0x4, DxcCursorKind_Statement = 0x8, DxcCursorKind_Attribute = 0x10, DxcCursorKind_Invalid = 0x20, DxcCursorKind_TranslationUnit = 0x40, DxcCursorKind_Preprocessing = 0x80, DxcCursorKind_Unexposed = 0x100, }; enum DxcCodeCompleteFlags { DxcCodeCompleteFlags_None = 0, DxcCodeCompleteFlags_IncludeMacros = 0x1, DxcCodeCompleteFlags_IncludeCodePatterns = 0x2, DxcCodeCompleteFlags_IncludeBriefComments = 0x4, }; enum DxcCompletionChunkKind { DxcCompletionChunk_Optional = 0, DxcCompletionChunk_TypedText = 1, DxcCompletionChunk_Text = 2, DxcCompletionChunk_Placeholder = 3, DxcCompletionChunk_Informative = 4, DxcCompletionChunk_CurrentParameter = 5, DxcCompletionChunk_LeftParen = 6, DxcCompletionChunk_RightParen = 7, DxcCompletionChunk_LeftBracket = 8, DxcCompletionChunk_RightBracket = 9, DxcCompletionChunk_LeftBrace = 10, DxcCompletionChunk_RightBrace = 11, DxcCompletionChunk_LeftAngle = 12, DxcCompletionChunk_RightAngle = 13, DxcCompletionChunk_Comma = 14, DxcCompletionChunk_ResultType = 15, DxcCompletionChunk_Colon = 16, DxcCompletionChunk_SemiColon = 17, DxcCompletionChunk_Equal = 18, DxcCompletionChunk_HorizontalSpace = 19, DxcCompletionChunk_VerticalSpace = 20, }; struct IDxcCursor; struct IDxcDiagnostic; struct IDxcFile; struct IDxcInclusion; struct IDxcIntelliSense; struct IDxcIndex; struct IDxcSourceLocation; struct IDxcSourceRange; struct IDxcToken; struct IDxcTranslationUnit; struct IDxcType; struct IDxcUnsavedFile; struct IDxcCodeCompleteResults; struct IDxcCompletionResult; struct IDxcCompletionString; CROSS_PLATFORM_UUIDOF(IDxcCursor, "1467b985-288d-4d2a-80c1-ef89c42c40bc") struct IDxcCursor : public IUnknown { virtual HRESULT STDMETHODCALLTYPE GetExtent(_Outptr_result_nullonfailure_ IDxcSourceRange** pRange) = 0; virtual HRESULT STDMETHODCALLTYPE GetLocation(_Outptr_result_nullonfailure_ IDxcSourceLocation** pResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetKind(_Out_ DxcCursorKind* pResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetKindFlags(_Out_ DxcCursorKindFlags* pResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetSemanticParent(_Outptr_result_nullonfailure_ IDxcCursor** pResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetLexicalParent(_Outptr_result_nullonfailure_ IDxcCursor** pResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetCursorType(_Outptr_result_nullonfailure_ IDxcType** pResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetNumArguments(_Out_ int* pResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetArgumentAt(int index, _Outptr_result_nullonfailure_ IDxcCursor** pResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetReferencedCursor(_Outptr_result_nullonfailure_ IDxcCursor** pResult) = 0; /// For a cursor that is either a reference to or a declaration of some entity, retrieve a cursor that describes the definition of that entity. /// Some entities can be declared multiple times within a translation unit, but only one of those declarations can also be a definition. /// A cursor to the definition of this entity; nullptr if there is no definition in this translation unit. virtual HRESULT STDMETHODCALLTYPE GetDefinitionCursor(_Outptr_result_nullonfailure_ IDxcCursor** pResult) = 0; virtual HRESULT STDMETHODCALLTYPE FindReferencesInFile( _In_ IDxcFile* file, unsigned skip, unsigned top, _Out_ unsigned* pResultLength, _Outptr_result_buffer_maybenull_(*pResultLength) IDxcCursor*** pResult) = 0; /// Gets the name for the entity references by the cursor, e.g. foo for an 'int foo' variable. virtual HRESULT STDMETHODCALLTYPE GetSpelling(_Outptr_result_maybenull_ LPSTR* pResult) = 0; virtual HRESULT STDMETHODCALLTYPE IsEqualTo(_In_ IDxcCursor* other, _Out_ BOOL* pResult) = 0; virtual HRESULT STDMETHODCALLTYPE IsNull(_Out_ BOOL* pResult) = 0; virtual HRESULT STDMETHODCALLTYPE IsDefinition(_Out_ BOOL* pResult) = 0; /// Gets the display name for the cursor, including e.g. parameter types for a function. virtual HRESULT STDMETHODCALLTYPE GetDisplayName(_Out_ BSTR* pResult) = 0; /// Gets the qualified name for the symbol the cursor refers to. virtual HRESULT STDMETHODCALLTYPE GetQualifiedName(BOOL includeTemplateArgs, _Outptr_result_maybenull_ BSTR* pResult) = 0; /// Gets a name for the cursor, applying the specified formatting flags. virtual HRESULT STDMETHODCALLTYPE GetFormattedName(DxcCursorFormatting formatting , _Outptr_result_maybenull_ BSTR* pResult) = 0; /// Gets children in pResult up to top elements. virtual HRESULT STDMETHODCALLTYPE GetChildren( unsigned skip, unsigned top, _Out_ unsigned* pResultLength, _Outptr_result_buffer_maybenull_(*pResultLength) IDxcCursor*** pResult) = 0; /// Gets the cursor following a location within a compound cursor. virtual HRESULT STDMETHODCALLTYPE GetSnappedChild(_In_ IDxcSourceLocation* location, _Outptr_result_maybenull_ IDxcCursor** pResult) = 0; }; CROSS_PLATFORM_UUIDOF(IDxcDiagnostic, "4f76b234-3659-4d33-99b0-3b0db994b564") struct IDxcDiagnostic : public IUnknown { virtual HRESULT STDMETHODCALLTYPE FormatDiagnostic( DxcDiagnosticDisplayOptions options, _Outptr_result_maybenull_ LPSTR* pResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetSeverity(_Out_ DxcDiagnosticSeverity* pResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetLocation(_Outptr_result_nullonfailure_ IDxcSourceLocation** pResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetSpelling(_Outptr_result_maybenull_ LPSTR* pResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetCategoryText(_Outptr_result_maybenull_ LPSTR* pResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetNumRanges(_Out_ unsigned* pResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetRangeAt(unsigned index, _Outptr_result_nullonfailure_ IDxcSourceRange** pResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetNumFixIts(_Out_ unsigned* pResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetFixItAt(unsigned index, _Outptr_result_nullonfailure_ IDxcSourceRange** pReplacementRange, _Outptr_result_maybenull_ LPSTR* pText) = 0; }; CROSS_PLATFORM_UUIDOF(IDxcFile, "bb2fca9e-1478-47ba-b08c-2c502ada4895") struct IDxcFile : public IUnknown { /// Gets the file name for this file. virtual HRESULT STDMETHODCALLTYPE GetName(_Outptr_result_maybenull_ LPSTR* pResult) = 0; /// Checks whether this file is equal to the other specified file. virtual HRESULT STDMETHODCALLTYPE IsEqualTo(_In_ IDxcFile* other, _Out_ BOOL* pResult) = 0; }; CROSS_PLATFORM_UUIDOF(IDxcInclusion, "0c364d65-df44-4412-888e-4e552fc5e3d6") struct IDxcInclusion : public IUnknown { virtual HRESULT STDMETHODCALLTYPE GetIncludedFile(_Outptr_result_nullonfailure_ IDxcFile** pResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetStackLength(_Out_ unsigned *pResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetStackItem(unsigned index, _Outptr_result_nullonfailure_ IDxcSourceLocation **pResult) = 0; }; CROSS_PLATFORM_UUIDOF(IDxcIntelliSense, "b1f99513-46d6-4112-8169-dd0d6053f17d") struct IDxcIntelliSense : public IUnknown { virtual HRESULT STDMETHODCALLTYPE CreateIndex(_Outptr_result_nullonfailure_ IDxcIndex** index) = 0; virtual HRESULT STDMETHODCALLTYPE GetNullLocation(_Outptr_result_nullonfailure_ IDxcSourceLocation** location) = 0; virtual HRESULT STDMETHODCALLTYPE GetNullRange(_Outptr_result_nullonfailure_ IDxcSourceRange** location) = 0; virtual HRESULT STDMETHODCALLTYPE GetRange( _In_ IDxcSourceLocation* start, _In_ IDxcSourceLocation* end, _Outptr_result_nullonfailure_ IDxcSourceRange** location) = 0; virtual HRESULT STDMETHODCALLTYPE GetDefaultDiagnosticDisplayOptions( _Out_ DxcDiagnosticDisplayOptions* pValue) = 0; virtual HRESULT STDMETHODCALLTYPE GetDefaultEditingTUOptions(_Out_ DxcTranslationUnitFlags* pValue) = 0; virtual HRESULT STDMETHODCALLTYPE CreateUnsavedFile(_In_ LPCSTR fileName, _In_ LPCSTR contents, unsigned contentLength, _Outptr_result_nullonfailure_ IDxcUnsavedFile** pResult) = 0; }; CROSS_PLATFORM_UUIDOF(IDxcIndex, "937824a0-7f5a-4815-9ba7-7fc0424f4173") struct IDxcIndex : public IUnknown { virtual HRESULT STDMETHODCALLTYPE SetGlobalOptions(DxcGlobalOptions options) = 0; virtual HRESULT STDMETHODCALLTYPE GetGlobalOptions(_Out_ DxcGlobalOptions* options) = 0; virtual HRESULT STDMETHODCALLTYPE ParseTranslationUnit( _In_z_ const char *source_filename, _In_count_(num_command_line_args) const char * const *command_line_args, int num_command_line_args, _In_count_(num_unsaved_files) IDxcUnsavedFile** unsaved_files, unsigned num_unsaved_files, DxcTranslationUnitFlags options, _Out_ IDxcTranslationUnit** pTranslationUnit) = 0; }; CROSS_PLATFORM_UUIDOF(IDxcSourceLocation, "8e7ddf1c-d7d3-4d69-b286-85fccba1e0cf") struct IDxcSourceLocation : public IUnknown { virtual HRESULT STDMETHODCALLTYPE IsEqualTo(_In_ IDxcSourceLocation* other, _Out_ BOOL* pResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetSpellingLocation( _Outptr_opt_ IDxcFile** pFile, _Out_opt_ unsigned* pLine, _Out_opt_ unsigned* pCol, _Out_opt_ unsigned* pOffset) = 0; virtual HRESULT STDMETHODCALLTYPE IsNull(_Out_ BOOL* pResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetPresumedLocation( _Outptr_opt_ LPSTR* pFilename, _Out_opt_ unsigned* pLine, _Out_opt_ unsigned* pCol) = 0; }; CROSS_PLATFORM_UUIDOF(IDxcSourceRange, "f1359b36-a53f-4e81-b514-b6b84122a13f") struct IDxcSourceRange : public IUnknown { virtual HRESULT STDMETHODCALLTYPE IsNull(_Out_ BOOL* pValue) = 0; virtual HRESULT STDMETHODCALLTYPE GetStart(_Out_ IDxcSourceLocation** pValue) = 0; virtual HRESULT STDMETHODCALLTYPE GetEnd(_Out_ IDxcSourceLocation** pValue) = 0; virtual HRESULT STDMETHODCALLTYPE GetOffsets(_Out_ unsigned* startOffset, _Out_ unsigned* endOffset) = 0; }; CROSS_PLATFORM_UUIDOF(IDxcToken, "7f90b9ff-a275-4932-97d8-3cfd234482a2") struct IDxcToken : public IUnknown { virtual HRESULT STDMETHODCALLTYPE GetKind(_Out_ DxcTokenKind* pValue) = 0; virtual HRESULT STDMETHODCALLTYPE GetLocation(_Out_ IDxcSourceLocation** pValue) = 0; virtual HRESULT STDMETHODCALLTYPE GetExtent(_Out_ IDxcSourceRange** pValue) = 0; virtual HRESULT STDMETHODCALLTYPE GetSpelling(_Out_ LPSTR* pValue) = 0; }; CROSS_PLATFORM_UUIDOF(IDxcTranslationUnit, "9677dee0-c0e5-46a1-8b40-3db3168be63d") struct IDxcTranslationUnit : public IUnknown { virtual HRESULT STDMETHODCALLTYPE GetCursor(_Out_ IDxcCursor** pCursor) = 0; virtual HRESULT STDMETHODCALLTYPE Tokenize( _In_ IDxcSourceRange* range, _Outptr_result_buffer_maybenull_(*pTokenCount) IDxcToken*** pTokens, _Out_ unsigned* pTokenCount) = 0; virtual HRESULT STDMETHODCALLTYPE GetLocation( _In_ IDxcFile* file, unsigned line, unsigned column, _Outptr_result_nullonfailure_ IDxcSourceLocation** pResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetNumDiagnostics(_Out_ unsigned* pValue) = 0; virtual HRESULT STDMETHODCALLTYPE GetDiagnostic(unsigned index, _Outptr_result_nullonfailure_ IDxcDiagnostic** pValue) = 0; virtual HRESULT STDMETHODCALLTYPE GetFile(_In_ const char* name, _Outptr_result_nullonfailure_ IDxcFile** pResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetFileName(_Outptr_result_maybenull_ LPSTR* pResult) = 0; virtual HRESULT STDMETHODCALLTYPE Reparse( _In_count_(num_unsaved_files) IDxcUnsavedFile** unsaved_files, unsigned num_unsaved_files) = 0; virtual HRESULT STDMETHODCALLTYPE GetCursorForLocation(_In_ IDxcSourceLocation* location, _Outptr_result_nullonfailure_ IDxcCursor** pResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetLocationForOffset(_In_ IDxcFile* file, unsigned offset, _Outptr_result_nullonfailure_ IDxcSourceLocation** pResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetSkippedRanges(_In_ IDxcFile* file, _Out_ unsigned* pResultCount, _Outptr_result_buffer_(*pResultCount) IDxcSourceRange*** pResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetDiagnosticDetails(unsigned index, DxcDiagnosticDisplayOptions options, _Out_ unsigned* errorCode, _Out_ unsigned* errorLine, _Out_ unsigned* errorColumn, _Out_ BSTR* errorFile, _Out_ unsigned* errorOffset, _Out_ unsigned* errorLength, _Out_ BSTR* errorMessage) = 0; virtual HRESULT STDMETHODCALLTYPE GetInclusionList(_Out_ unsigned* pResultCount, _Outptr_result_buffer_(*pResultCount) IDxcInclusion*** pResult) = 0; virtual HRESULT STDMETHODCALLTYPE CodeCompleteAt( _In_ const char *fileName, unsigned line, unsigned column, _In_ IDxcUnsavedFile** pUnsavedFiles, unsigned numUnsavedFiles, _In_ DxcCodeCompleteFlags options, _Outptr_result_nullonfailure_ IDxcCodeCompleteResults **pResult) = 0; }; CROSS_PLATFORM_UUIDOF(IDxcType, "2ec912fd-b144-4a15-ad0d-1c5439c81e46") struct IDxcType : public IUnknown { virtual HRESULT STDMETHODCALLTYPE GetSpelling(_Outptr_result_z_ LPSTR* pResult) = 0; virtual HRESULT STDMETHODCALLTYPE IsEqualTo(_In_ IDxcType* other, _Out_ BOOL* pResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetKind(_Out_ DxcTypeKind* pResult) = 0; }; CROSS_PLATFORM_UUIDOF(IDxcUnsavedFile, "8ec00f98-07d0-4e60-9d7c-5a50b5b0017f") struct IDxcUnsavedFile : public IUnknown { virtual HRESULT STDMETHODCALLTYPE GetFileName(_Outptr_result_z_ LPSTR* pFileName) = 0; virtual HRESULT STDMETHODCALLTYPE GetContents(_Outptr_result_z_ LPSTR* pContents) = 0; virtual HRESULT STDMETHODCALLTYPE GetLength(_Out_ unsigned* pLength) = 0; }; CROSS_PLATFORM_UUIDOF(IDxcCodeCompleteResults, "1E06466A-FD8B-45F3-A78F-8A3F76EBB552") struct IDxcCodeCompleteResults : public IUnknown { virtual HRESULT STDMETHODCALLTYPE GetNumResults(_Out_ unsigned* pResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetResultAt(unsigned index, _Outptr_result_nullonfailure_ IDxcCompletionResult** pResult) = 0; }; CROSS_PLATFORM_UUIDOF(IDxcCompletionResult, "943C0588-22D0-4784-86FC-701F802AC2B6") struct IDxcCompletionResult : public IUnknown { virtual HRESULT STDMETHODCALLTYPE GetCursorKind(_Out_ DxcCursorKind* pResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetCompletionString(_Outptr_result_nullonfailure_ IDxcCompletionString** pResult) = 0; }; CROSS_PLATFORM_UUIDOF(IDxcCompletionString, "06B51E0F-A605-4C69-A110-CD6E14B58EEC") struct IDxcCompletionString : public IUnknown { virtual HRESULT STDMETHODCALLTYPE GetNumCompletionChunks(_Out_ unsigned* pResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetCompletionChunkKind(unsigned chunkNumber, _Out_ DxcCompletionChunkKind* pResult) = 0; virtual HRESULT STDMETHODCALLTYPE GetCompletionChunkText(unsigned chunkNumber, _Out_ LPSTR* pResult) = 0; }; // Fun fact: 'extern' is required because const is by default static in C++, so // CLSID_DxcIntelliSense is not visible externally (this is OK in C, since const is // not by default static in C) #ifdef _MSC_VER #define CLSID_SCOPE __declspec(selectany) extern #else #define CLSID_SCOPE #endif CLSID_SCOPE const CLSID CLSID_DxcIntelliSense = {/* 3047833c-d1c0-4b8e-9d40-102878605985 */ 0x3047833c, 0xd1c0, 0x4b8e, {0x9d, 0x40, 0x10, 0x28, 0x78, 0x60, 0x59, 0x85}}; #endif ================================================ FILE: Source/External/imgui_tools/IconsFontAwesome/IconsFontAwesome5.h ================================================ // Generated by https://github.com/juliettef/IconFontCppHeaders script GenerateIconFontCppHeaders.py for languages C and C++ // from https://github.com/FortAwesome/Font-Awesome/raw/5.x/metadata/icons.yml // for use with https://github.com/FortAwesome/Font-Awesome/blob/5.x/webfonts/fa-regular-400.ttf, https://github.com/FortAwesome/Font-Awesome/blob/5.x/webfonts/fa-solid-900.ttf #pragma once #define FONT_ICON_FILE_NAME_FAR "fa-regular-400.ttf" #define FONT_ICON_FILE_NAME_FAS "fa-solid-900.ttf" #define ICON_MIN_FA 0xe005 #define ICON_MAX_16_FA 0xf8ff #define ICON_MAX_FA 0xf8ff #define ICON_FA_AD "\xef\x99\x81" // U+f641 #define ICON_FA_ADDRESS_BOOK "\xef\x8a\xb9" // U+f2b9 #define ICON_FA_ADDRESS_CARD "\xef\x8a\xbb" // U+f2bb #define ICON_FA_ADJUST "\xef\x81\x82" // U+f042 #define ICON_FA_AIR_FRESHENER "\xef\x97\x90" // U+f5d0 #define ICON_FA_ALIGN_CENTER "\xef\x80\xb7" // U+f037 #define ICON_FA_ALIGN_JUSTIFY "\xef\x80\xb9" // U+f039 #define ICON_FA_ALIGN_LEFT "\xef\x80\xb6" // U+f036 #define ICON_FA_ALIGN_RIGHT "\xef\x80\xb8" // U+f038 #define ICON_FA_ALLERGIES "\xef\x91\xa1" // U+f461 #define ICON_FA_AMBULANCE "\xef\x83\xb9" // U+f0f9 #define ICON_FA_AMERICAN_SIGN_LANGUAGE_INTERPRETING "\xef\x8a\xa3" // U+f2a3 #define ICON_FA_ANCHOR "\xef\x84\xbd" // U+f13d #define ICON_FA_ANGLE_DOUBLE_DOWN "\xef\x84\x83" // U+f103 #define ICON_FA_ANGLE_DOUBLE_LEFT "\xef\x84\x80" // U+f100 #define ICON_FA_ANGLE_DOUBLE_RIGHT "\xef\x84\x81" // U+f101 #define ICON_FA_ANGLE_DOUBLE_UP "\xef\x84\x82" // U+f102 #define ICON_FA_ANGLE_DOWN "\xef\x84\x87" // U+f107 #define ICON_FA_ANGLE_LEFT "\xef\x84\x84" // U+f104 #define ICON_FA_ANGLE_RIGHT "\xef\x84\x85" // U+f105 #define ICON_FA_ANGLE_UP "\xef\x84\x86" // U+f106 #define ICON_FA_ANGRY "\xef\x95\x96" // U+f556 #define ICON_FA_ANKH "\xef\x99\x84" // U+f644 #define ICON_FA_APPLE_ALT "\xef\x97\x91" // U+f5d1 #define ICON_FA_ARCHIVE "\xef\x86\x87" // U+f187 #define ICON_FA_ARCHWAY "\xef\x95\x97" // U+f557 #define ICON_FA_ARROW_ALT_CIRCLE_DOWN "\xef\x8d\x98" // U+f358 #define ICON_FA_ARROW_ALT_CIRCLE_LEFT "\xef\x8d\x99" // U+f359 #define ICON_FA_ARROW_ALT_CIRCLE_RIGHT "\xef\x8d\x9a" // U+f35a #define ICON_FA_ARROW_ALT_CIRCLE_UP "\xef\x8d\x9b" // U+f35b #define ICON_FA_ARROW_CIRCLE_DOWN "\xef\x82\xab" // U+f0ab #define ICON_FA_ARROW_CIRCLE_LEFT "\xef\x82\xa8" // U+f0a8 #define ICON_FA_ARROW_CIRCLE_RIGHT "\xef\x82\xa9" // U+f0a9 #define ICON_FA_ARROW_CIRCLE_UP "\xef\x82\xaa" // U+f0aa #define ICON_FA_ARROW_DOWN "\xef\x81\xa3" // U+f063 #define ICON_FA_ARROW_LEFT "\xef\x81\xa0" // U+f060 #define ICON_FA_ARROW_RIGHT "\xef\x81\xa1" // U+f061 #define ICON_FA_ARROW_UP "\xef\x81\xa2" // U+f062 #define ICON_FA_ARROWS_ALT "\xef\x82\xb2" // U+f0b2 #define ICON_FA_ARROWS_ALT_H "\xef\x8c\xb7" // U+f337 #define ICON_FA_ARROWS_ALT_V "\xef\x8c\xb8" // U+f338 #define ICON_FA_ASSISTIVE_LISTENING_SYSTEMS "\xef\x8a\xa2" // U+f2a2 #define ICON_FA_ASTERISK "\xef\x81\xa9" // U+f069 #define ICON_FA_AT "\xef\x87\xba" // U+f1fa #define ICON_FA_ATLAS "\xef\x95\x98" // U+f558 #define ICON_FA_ATOM "\xef\x97\x92" // U+f5d2 #define ICON_FA_AUDIO_DESCRIPTION "\xef\x8a\x9e" // U+f29e #define ICON_FA_AWARD "\xef\x95\x99" // U+f559 #define ICON_FA_BABY "\xef\x9d\xbc" // U+f77c #define ICON_FA_BABY_CARRIAGE "\xef\x9d\xbd" // U+f77d #define ICON_FA_BACKSPACE "\xef\x95\x9a" // U+f55a #define ICON_FA_BACKWARD "\xef\x81\x8a" // U+f04a #define ICON_FA_BACON "\xef\x9f\xa5" // U+f7e5 #define ICON_FA_BACTERIA "\xee\x81\x99" // U+e059 #define ICON_FA_BACTERIUM "\xee\x81\x9a" // U+e05a #define ICON_FA_BAHAI "\xef\x99\xa6" // U+f666 #define ICON_FA_BALANCE_SCALE "\xef\x89\x8e" // U+f24e #define ICON_FA_BALANCE_SCALE_LEFT "\xef\x94\x95" // U+f515 #define ICON_FA_BALANCE_SCALE_RIGHT "\xef\x94\x96" // U+f516 #define ICON_FA_BAN "\xef\x81\x9e" // U+f05e #define ICON_FA_BAND_AID "\xef\x91\xa2" // U+f462 #define ICON_FA_BARCODE "\xef\x80\xaa" // U+f02a #define ICON_FA_BARS "\xef\x83\x89" // U+f0c9 #define ICON_FA_BASEBALL_BALL "\xef\x90\xb3" // U+f433 #define ICON_FA_BASKETBALL_BALL "\xef\x90\xb4" // U+f434 #define ICON_FA_BATH "\xef\x8b\x8d" // U+f2cd #define ICON_FA_BATTERY_EMPTY "\xef\x89\x84" // U+f244 #define ICON_FA_BATTERY_FULL "\xef\x89\x80" // U+f240 #define ICON_FA_BATTERY_HALF "\xef\x89\x82" // U+f242 #define ICON_FA_BATTERY_QUARTER "\xef\x89\x83" // U+f243 #define ICON_FA_BATTERY_THREE_QUARTERS "\xef\x89\x81" // U+f241 #define ICON_FA_BED "\xef\x88\xb6" // U+f236 #define ICON_FA_BEER "\xef\x83\xbc" // U+f0fc #define ICON_FA_BELL "\xef\x83\xb3" // U+f0f3 #define ICON_FA_BELL_SLASH "\xef\x87\xb6" // U+f1f6 #define ICON_FA_BEZIER_CURVE "\xef\x95\x9b" // U+f55b #define ICON_FA_BIBLE "\xef\x99\x87" // U+f647 #define ICON_FA_BICYCLE "\xef\x88\x86" // U+f206 #define ICON_FA_BIKING "\xef\xa1\x8a" // U+f84a #define ICON_FA_BINOCULARS "\xef\x87\xa5" // U+f1e5 #define ICON_FA_BIOHAZARD "\xef\x9e\x80" // U+f780 #define ICON_FA_BIRTHDAY_CAKE "\xef\x87\xbd" // U+f1fd #define ICON_FA_BLENDER "\xef\x94\x97" // U+f517 #define ICON_FA_BLENDER_PHONE "\xef\x9a\xb6" // U+f6b6 #define ICON_FA_BLIND "\xef\x8a\x9d" // U+f29d #define ICON_FA_BLOG "\xef\x9e\x81" // U+f781 #define ICON_FA_BOLD "\xef\x80\xb2" // U+f032 #define ICON_FA_BOLT "\xef\x83\xa7" // U+f0e7 #define ICON_FA_BOMB "\xef\x87\xa2" // U+f1e2 #define ICON_FA_BONE "\xef\x97\x97" // U+f5d7 #define ICON_FA_BONG "\xef\x95\x9c" // U+f55c #define ICON_FA_BOOK "\xef\x80\xad" // U+f02d #define ICON_FA_BOOK_DEAD "\xef\x9a\xb7" // U+f6b7 #define ICON_FA_BOOK_MEDICAL "\xef\x9f\xa6" // U+f7e6 #define ICON_FA_BOOK_OPEN "\xef\x94\x98" // U+f518 #define ICON_FA_BOOK_READER "\xef\x97\x9a" // U+f5da #define ICON_FA_BOOKMARK "\xef\x80\xae" // U+f02e #define ICON_FA_BORDER_ALL "\xef\xa1\x8c" // U+f84c #define ICON_FA_BORDER_NONE "\xef\xa1\x90" // U+f850 #define ICON_FA_BORDER_STYLE "\xef\xa1\x93" // U+f853 #define ICON_FA_BOWLING_BALL "\xef\x90\xb6" // U+f436 #define ICON_FA_BOX "\xef\x91\xa6" // U+f466 #define ICON_FA_BOX_OPEN "\xef\x92\x9e" // U+f49e #define ICON_FA_BOX_TISSUE "\xee\x81\x9b" // U+e05b #define ICON_FA_BOXES "\xef\x91\xa8" // U+f468 #define ICON_FA_BRAILLE "\xef\x8a\xa1" // U+f2a1 #define ICON_FA_BRAIN "\xef\x97\x9c" // U+f5dc #define ICON_FA_BREAD_SLICE "\xef\x9f\xac" // U+f7ec #define ICON_FA_BRIEFCASE "\xef\x82\xb1" // U+f0b1 #define ICON_FA_BRIEFCASE_MEDICAL "\xef\x91\xa9" // U+f469 #define ICON_FA_BROADCAST_TOWER "\xef\x94\x99" // U+f519 #define ICON_FA_BROOM "\xef\x94\x9a" // U+f51a #define ICON_FA_BRUSH "\xef\x95\x9d" // U+f55d #define ICON_FA_BUG "\xef\x86\x88" // U+f188 #define ICON_FA_BUILDING "\xef\x86\xad" // U+f1ad #define ICON_FA_BULLHORN "\xef\x82\xa1" // U+f0a1 #define ICON_FA_BULLSEYE "\xef\x85\x80" // U+f140 #define ICON_FA_BURN "\xef\x91\xaa" // U+f46a #define ICON_FA_BUS "\xef\x88\x87" // U+f207 #define ICON_FA_BUS_ALT "\xef\x95\x9e" // U+f55e #define ICON_FA_BUSINESS_TIME "\xef\x99\x8a" // U+f64a #define ICON_FA_CALCULATOR "\xef\x87\xac" // U+f1ec #define ICON_FA_CALENDAR "\xef\x84\xb3" // U+f133 #define ICON_FA_CALENDAR_ALT "\xef\x81\xb3" // U+f073 #define ICON_FA_CALENDAR_CHECK "\xef\x89\xb4" // U+f274 #define ICON_FA_CALENDAR_DAY "\xef\x9e\x83" // U+f783 #define ICON_FA_CALENDAR_MINUS "\xef\x89\xb2" // U+f272 #define ICON_FA_CALENDAR_PLUS "\xef\x89\xb1" // U+f271 #define ICON_FA_CALENDAR_TIMES "\xef\x89\xb3" // U+f273 #define ICON_FA_CALENDAR_WEEK "\xef\x9e\x84" // U+f784 #define ICON_FA_CAMERA "\xef\x80\xb0" // U+f030 #define ICON_FA_CAMERA_RETRO "\xef\x82\x83" // U+f083 #define ICON_FA_CAMPGROUND "\xef\x9a\xbb" // U+f6bb #define ICON_FA_CANDY_CANE "\xef\x9e\x86" // U+f786 #define ICON_FA_CANNABIS "\xef\x95\x9f" // U+f55f #define ICON_FA_CAPSULES "\xef\x91\xab" // U+f46b #define ICON_FA_CAR "\xef\x86\xb9" // U+f1b9 #define ICON_FA_CAR_ALT "\xef\x97\x9e" // U+f5de #define ICON_FA_CAR_BATTERY "\xef\x97\x9f" // U+f5df #define ICON_FA_CAR_CRASH "\xef\x97\xa1" // U+f5e1 #define ICON_FA_CAR_SIDE "\xef\x97\xa4" // U+f5e4 #define ICON_FA_CARAVAN "\xef\xa3\xbf" // U+f8ff #define ICON_FA_CARET_DOWN "\xef\x83\x97" // U+f0d7 #define ICON_FA_CARET_LEFT "\xef\x83\x99" // U+f0d9 #define ICON_FA_CARET_RIGHT "\xef\x83\x9a" // U+f0da #define ICON_FA_CARET_SQUARE_DOWN "\xef\x85\x90" // U+f150 #define ICON_FA_CARET_SQUARE_LEFT "\xef\x86\x91" // U+f191 #define ICON_FA_CARET_SQUARE_RIGHT "\xef\x85\x92" // U+f152 #define ICON_FA_CARET_SQUARE_UP "\xef\x85\x91" // U+f151 #define ICON_FA_CARET_UP "\xef\x83\x98" // U+f0d8 #define ICON_FA_CARROT "\xef\x9e\x87" // U+f787 #define ICON_FA_CART_ARROW_DOWN "\xef\x88\x98" // U+f218 #define ICON_FA_CART_PLUS "\xef\x88\x97" // U+f217 #define ICON_FA_CASH_REGISTER "\xef\x9e\x88" // U+f788 #define ICON_FA_CAT "\xef\x9a\xbe" // U+f6be #define ICON_FA_CERTIFICATE "\xef\x82\xa3" // U+f0a3 #define ICON_FA_CHAIR "\xef\x9b\x80" // U+f6c0 #define ICON_FA_CHALKBOARD "\xef\x94\x9b" // U+f51b #define ICON_FA_CHALKBOARD_TEACHER "\xef\x94\x9c" // U+f51c #define ICON_FA_CHARGING_STATION "\xef\x97\xa7" // U+f5e7 #define ICON_FA_CHART_AREA "\xef\x87\xbe" // U+f1fe #define ICON_FA_CHART_BAR "\xef\x82\x80" // U+f080 #define ICON_FA_CHART_LINE "\xef\x88\x81" // U+f201 #define ICON_FA_CHART_PIE "\xef\x88\x80" // U+f200 #define ICON_FA_CHECK "\xef\x80\x8c" // U+f00c #define ICON_FA_CHECK_CIRCLE "\xef\x81\x98" // U+f058 #define ICON_FA_CHECK_DOUBLE "\xef\x95\xa0" // U+f560 #define ICON_FA_CHECK_SQUARE "\xef\x85\x8a" // U+f14a #define ICON_FA_CHEESE "\xef\x9f\xaf" // U+f7ef #define ICON_FA_CHESS "\xef\x90\xb9" // U+f439 #define ICON_FA_CHESS_BISHOP "\xef\x90\xba" // U+f43a #define ICON_FA_CHESS_BOARD "\xef\x90\xbc" // U+f43c #define ICON_FA_CHESS_KING "\xef\x90\xbf" // U+f43f #define ICON_FA_CHESS_KNIGHT "\xef\x91\x81" // U+f441 #define ICON_FA_CHESS_PAWN "\xef\x91\x83" // U+f443 #define ICON_FA_CHESS_QUEEN "\xef\x91\x85" // U+f445 #define ICON_FA_CHESS_ROOK "\xef\x91\x87" // U+f447 #define ICON_FA_CHEVRON_CIRCLE_DOWN "\xef\x84\xba" // U+f13a #define ICON_FA_CHEVRON_CIRCLE_LEFT "\xef\x84\xb7" // U+f137 #define ICON_FA_CHEVRON_CIRCLE_RIGHT "\xef\x84\xb8" // U+f138 #define ICON_FA_CHEVRON_CIRCLE_UP "\xef\x84\xb9" // U+f139 #define ICON_FA_CHEVRON_DOWN "\xef\x81\xb8" // U+f078 #define ICON_FA_CHEVRON_LEFT "\xef\x81\x93" // U+f053 #define ICON_FA_CHEVRON_RIGHT "\xef\x81\x94" // U+f054 #define ICON_FA_CHEVRON_UP "\xef\x81\xb7" // U+f077 #define ICON_FA_CHILD "\xef\x86\xae" // U+f1ae #define ICON_FA_CHURCH "\xef\x94\x9d" // U+f51d #define ICON_FA_CIRCLE "\xef\x84\x91" // U+f111 #define ICON_FA_CIRCLE_NOTCH "\xef\x87\x8e" // U+f1ce #define ICON_FA_CITY "\xef\x99\x8f" // U+f64f #define ICON_FA_CLINIC_MEDICAL "\xef\x9f\xb2" // U+f7f2 #define ICON_FA_CLIPBOARD "\xef\x8c\xa8" // U+f328 #define ICON_FA_CLIPBOARD_CHECK "\xef\x91\xac" // U+f46c #define ICON_FA_CLIPBOARD_LIST "\xef\x91\xad" // U+f46d #define ICON_FA_CLOCK "\xef\x80\x97" // U+f017 #define ICON_FA_CLONE "\xef\x89\x8d" // U+f24d #define ICON_FA_CLOSED_CAPTIONING "\xef\x88\x8a" // U+f20a #define ICON_FA_CLOUD "\xef\x83\x82" // U+f0c2 #define ICON_FA_CLOUD_DOWNLOAD_ALT "\xef\x8e\x81" // U+f381 #define ICON_FA_CLOUD_MEATBALL "\xef\x9c\xbb" // U+f73b #define ICON_FA_CLOUD_MOON "\xef\x9b\x83" // U+f6c3 #define ICON_FA_CLOUD_MOON_RAIN "\xef\x9c\xbc" // U+f73c #define ICON_FA_CLOUD_RAIN "\xef\x9c\xbd" // U+f73d #define ICON_FA_CLOUD_SHOWERS_HEAVY "\xef\x9d\x80" // U+f740 #define ICON_FA_CLOUD_SUN "\xef\x9b\x84" // U+f6c4 #define ICON_FA_CLOUD_SUN_RAIN "\xef\x9d\x83" // U+f743 #define ICON_FA_CLOUD_UPLOAD_ALT "\xef\x8e\x82" // U+f382 #define ICON_FA_COCKTAIL "\xef\x95\xa1" // U+f561 #define ICON_FA_CODE "\xef\x84\xa1" // U+f121 #define ICON_FA_CODE_BRANCH "\xef\x84\xa6" // U+f126 #define ICON_FA_COFFEE "\xef\x83\xb4" // U+f0f4 #define ICON_FA_COG "\xef\x80\x93" // U+f013 #define ICON_FA_COGS "\xef\x82\x85" // U+f085 #define ICON_FA_COINS "\xef\x94\x9e" // U+f51e #define ICON_FA_COLUMNS "\xef\x83\x9b" // U+f0db #define ICON_FA_COMMENT "\xef\x81\xb5" // U+f075 #define ICON_FA_COMMENT_ALT "\xef\x89\xba" // U+f27a #define ICON_FA_COMMENT_DOLLAR "\xef\x99\x91" // U+f651 #define ICON_FA_COMMENT_DOTS "\xef\x92\xad" // U+f4ad #define ICON_FA_COMMENT_MEDICAL "\xef\x9f\xb5" // U+f7f5 #define ICON_FA_COMMENT_SLASH "\xef\x92\xb3" // U+f4b3 #define ICON_FA_COMMENTS "\xef\x82\x86" // U+f086 #define ICON_FA_COMMENTS_DOLLAR "\xef\x99\x93" // U+f653 #define ICON_FA_COMPACT_DISC "\xef\x94\x9f" // U+f51f #define ICON_FA_COMPASS "\xef\x85\x8e" // U+f14e #define ICON_FA_COMPRESS "\xef\x81\xa6" // U+f066 #define ICON_FA_COMPRESS_ALT "\xef\x90\xa2" // U+f422 #define ICON_FA_COMPRESS_ARROWS_ALT "\xef\x9e\x8c" // U+f78c #define ICON_FA_CONCIERGE_BELL "\xef\x95\xa2" // U+f562 #define ICON_FA_COOKIE "\xef\x95\xa3" // U+f563 #define ICON_FA_COOKIE_BITE "\xef\x95\xa4" // U+f564 #define ICON_FA_COPY "\xef\x83\x85" // U+f0c5 #define ICON_FA_COPYRIGHT "\xef\x87\xb9" // U+f1f9 #define ICON_FA_COUCH "\xef\x92\xb8" // U+f4b8 #define ICON_FA_CREDIT_CARD "\xef\x82\x9d" // U+f09d #define ICON_FA_CROP "\xef\x84\xa5" // U+f125 #define ICON_FA_CROP_ALT "\xef\x95\xa5" // U+f565 #define ICON_FA_CROSS "\xef\x99\x94" // U+f654 #define ICON_FA_CROSSHAIRS "\xef\x81\x9b" // U+f05b #define ICON_FA_CROW "\xef\x94\xa0" // U+f520 #define ICON_FA_CROWN "\xef\x94\xa1" // U+f521 #define ICON_FA_CRUTCH "\xef\x9f\xb7" // U+f7f7 #define ICON_FA_CUBE "\xef\x86\xb2" // U+f1b2 #define ICON_FA_CUBES "\xef\x86\xb3" // U+f1b3 #define ICON_FA_CUT "\xef\x83\x84" // U+f0c4 #define ICON_FA_DATABASE "\xef\x87\x80" // U+f1c0 #define ICON_FA_DEAF "\xef\x8a\xa4" // U+f2a4 #define ICON_FA_DEMOCRAT "\xef\x9d\x87" // U+f747 #define ICON_FA_DESKTOP "\xef\x84\x88" // U+f108 #define ICON_FA_DHARMACHAKRA "\xef\x99\x95" // U+f655 #define ICON_FA_DIAGNOSES "\xef\x91\xb0" // U+f470 #define ICON_FA_DICE "\xef\x94\xa2" // U+f522 #define ICON_FA_DICE_D20 "\xef\x9b\x8f" // U+f6cf #define ICON_FA_DICE_D6 "\xef\x9b\x91" // U+f6d1 #define ICON_FA_DICE_FIVE "\xef\x94\xa3" // U+f523 #define ICON_FA_DICE_FOUR "\xef\x94\xa4" // U+f524 #define ICON_FA_DICE_ONE "\xef\x94\xa5" // U+f525 #define ICON_FA_DICE_SIX "\xef\x94\xa6" // U+f526 #define ICON_FA_DICE_THREE "\xef\x94\xa7" // U+f527 #define ICON_FA_DICE_TWO "\xef\x94\xa8" // U+f528 #define ICON_FA_DIGITAL_TACHOGRAPH "\xef\x95\xa6" // U+f566 #define ICON_FA_DIRECTIONS "\xef\x97\xab" // U+f5eb #define ICON_FA_DISEASE "\xef\x9f\xba" // U+f7fa #define ICON_FA_DIVIDE "\xef\x94\xa9" // U+f529 #define ICON_FA_DIZZY "\xef\x95\xa7" // U+f567 #define ICON_FA_DNA "\xef\x91\xb1" // U+f471 #define ICON_FA_DOG "\xef\x9b\x93" // U+f6d3 #define ICON_FA_DOLLAR_SIGN "\xef\x85\x95" // U+f155 #define ICON_FA_DOLLY "\xef\x91\xb2" // U+f472 #define ICON_FA_DOLLY_FLATBED "\xef\x91\xb4" // U+f474 #define ICON_FA_DONATE "\xef\x92\xb9" // U+f4b9 #define ICON_FA_DOOR_CLOSED "\xef\x94\xaa" // U+f52a #define ICON_FA_DOOR_OPEN "\xef\x94\xab" // U+f52b #define ICON_FA_DOT_CIRCLE "\xef\x86\x92" // U+f192 #define ICON_FA_DOVE "\xef\x92\xba" // U+f4ba #define ICON_FA_DOWNLOAD "\xef\x80\x99" // U+f019 #define ICON_FA_DRAFTING_COMPASS "\xef\x95\xa8" // U+f568 #define ICON_FA_DRAGON "\xef\x9b\x95" // U+f6d5 #define ICON_FA_DRAW_POLYGON "\xef\x97\xae" // U+f5ee #define ICON_FA_DRUM "\xef\x95\xa9" // U+f569 #define ICON_FA_DRUM_STEELPAN "\xef\x95\xaa" // U+f56a #define ICON_FA_DRUMSTICK_BITE "\xef\x9b\x97" // U+f6d7 #define ICON_FA_DUMBBELL "\xef\x91\x8b" // U+f44b #define ICON_FA_DUMPSTER "\xef\x9e\x93" // U+f793 #define ICON_FA_DUMPSTER_FIRE "\xef\x9e\x94" // U+f794 #define ICON_FA_DUNGEON "\xef\x9b\x99" // U+f6d9 #define ICON_FA_EDIT "\xef\x81\x84" // U+f044 #define ICON_FA_EGG "\xef\x9f\xbb" // U+f7fb #define ICON_FA_EJECT "\xef\x81\x92" // U+f052 #define ICON_FA_ELLIPSIS_H "\xef\x85\x81" // U+f141 #define ICON_FA_ELLIPSIS_V "\xef\x85\x82" // U+f142 #define ICON_FA_ENVELOPE "\xef\x83\xa0" // U+f0e0 #define ICON_FA_ENVELOPE_OPEN "\xef\x8a\xb6" // U+f2b6 #define ICON_FA_ENVELOPE_OPEN_TEXT "\xef\x99\x98" // U+f658 #define ICON_FA_ENVELOPE_SQUARE "\xef\x86\x99" // U+f199 #define ICON_FA_EQUALS "\xef\x94\xac" // U+f52c #define ICON_FA_ERASER "\xef\x84\xad" // U+f12d #define ICON_FA_ETHERNET "\xef\x9e\x96" // U+f796 #define ICON_FA_EURO_SIGN "\xef\x85\x93" // U+f153 #define ICON_FA_EXCHANGE_ALT "\xef\x8d\xa2" // U+f362 #define ICON_FA_EXCLAMATION "\xef\x84\xaa" // U+f12a #define ICON_FA_EXCLAMATION_CIRCLE "\xef\x81\xaa" // U+f06a #define ICON_FA_EXCLAMATION_TRIANGLE "\xef\x81\xb1" // U+f071 #define ICON_FA_EXPAND "\xef\x81\xa5" // U+f065 #define ICON_FA_EXPAND_ALT "\xef\x90\xa4" // U+f424 #define ICON_FA_EXPAND_ARROWS_ALT "\xef\x8c\x9e" // U+f31e #define ICON_FA_EXTERNAL_LINK_ALT "\xef\x8d\x9d" // U+f35d #define ICON_FA_EXTERNAL_LINK_SQUARE_ALT "\xef\x8d\xa0" // U+f360 #define ICON_FA_EYE "\xef\x81\xae" // U+f06e #define ICON_FA_EYE_DROPPER "\xef\x87\xbb" // U+f1fb #define ICON_FA_EYE_SLASH "\xef\x81\xb0" // U+f070 #define ICON_FA_FAN "\xef\xa1\xa3" // U+f863 #define ICON_FA_FAST_BACKWARD "\xef\x81\x89" // U+f049 #define ICON_FA_FAST_FORWARD "\xef\x81\x90" // U+f050 #define ICON_FA_FAUCET "\xee\x80\x85" // U+e005 #define ICON_FA_FAX "\xef\x86\xac" // U+f1ac #define ICON_FA_FEATHER "\xef\x94\xad" // U+f52d #define ICON_FA_FEATHER_ALT "\xef\x95\xab" // U+f56b #define ICON_FA_FEMALE "\xef\x86\x82" // U+f182 #define ICON_FA_FIGHTER_JET "\xef\x83\xbb" // U+f0fb #define ICON_FA_FILE "\xef\x85\x9b" // U+f15b #define ICON_FA_FILE_ALT "\xef\x85\x9c" // U+f15c #define ICON_FA_FILE_ARCHIVE "\xef\x87\x86" // U+f1c6 #define ICON_FA_FILE_AUDIO "\xef\x87\x87" // U+f1c7 #define ICON_FA_FILE_CODE "\xef\x87\x89" // U+f1c9 #define ICON_FA_FILE_CONTRACT "\xef\x95\xac" // U+f56c #define ICON_FA_FILE_CSV "\xef\x9b\x9d" // U+f6dd #define ICON_FA_FILE_DOWNLOAD "\xef\x95\xad" // U+f56d #define ICON_FA_FILE_EXCEL "\xef\x87\x83" // U+f1c3 #define ICON_FA_FILE_EXPORT "\xef\x95\xae" // U+f56e #define ICON_FA_FILE_IMAGE "\xef\x87\x85" // U+f1c5 #define ICON_FA_FILE_IMPORT "\xef\x95\xaf" // U+f56f #define ICON_FA_FILE_INVOICE "\xef\x95\xb0" // U+f570 #define ICON_FA_FILE_INVOICE_DOLLAR "\xef\x95\xb1" // U+f571 #define ICON_FA_FILE_MEDICAL "\xef\x91\xb7" // U+f477 #define ICON_FA_FILE_MEDICAL_ALT "\xef\x91\xb8" // U+f478 #define ICON_FA_FILE_PDF "\xef\x87\x81" // U+f1c1 #define ICON_FA_FILE_POWERPOINT "\xef\x87\x84" // U+f1c4 #define ICON_FA_FILE_PRESCRIPTION "\xef\x95\xb2" // U+f572 #define ICON_FA_FILE_SIGNATURE "\xef\x95\xb3" // U+f573 #define ICON_FA_FILE_UPLOAD "\xef\x95\xb4" // U+f574 #define ICON_FA_FILE_VIDEO "\xef\x87\x88" // U+f1c8 #define ICON_FA_FILE_WORD "\xef\x87\x82" // U+f1c2 #define ICON_FA_FILL "\xef\x95\xb5" // U+f575 #define ICON_FA_FILL_DRIP "\xef\x95\xb6" // U+f576 #define ICON_FA_FILM "\xef\x80\x88" // U+f008 #define ICON_FA_FILTER "\xef\x82\xb0" // U+f0b0 #define ICON_FA_FINGERPRINT "\xef\x95\xb7" // U+f577 #define ICON_FA_FIRE "\xef\x81\xad" // U+f06d #define ICON_FA_FIRE_ALT "\xef\x9f\xa4" // U+f7e4 #define ICON_FA_FIRE_EXTINGUISHER "\xef\x84\xb4" // U+f134 #define ICON_FA_FIRST_AID "\xef\x91\xb9" // U+f479 #define ICON_FA_FISH "\xef\x95\xb8" // U+f578 #define ICON_FA_FIST_RAISED "\xef\x9b\x9e" // U+f6de #define ICON_FA_FLAG "\xef\x80\xa4" // U+f024 #define ICON_FA_FLAG_CHECKERED "\xef\x84\x9e" // U+f11e #define ICON_FA_FLAG_USA "\xef\x9d\x8d" // U+f74d #define ICON_FA_FLASK "\xef\x83\x83" // U+f0c3 #define ICON_FA_FLUSHED "\xef\x95\xb9" // U+f579 #define ICON_FA_FOLDER "\xef\x81\xbb" // U+f07b #define ICON_FA_FOLDER_MINUS "\xef\x99\x9d" // U+f65d #define ICON_FA_FOLDER_OPEN "\xef\x81\xbc" // U+f07c #define ICON_FA_FOLDER_PLUS "\xef\x99\x9e" // U+f65e #define ICON_FA_FONT "\xef\x80\xb1" // U+f031 #define ICON_FA_FONT_AWESOME_LOGO_FULL "\xef\x93\xa6" // U+f4e6 #define ICON_FA_FOOTBALL_BALL "\xef\x91\x8e" // U+f44e #define ICON_FA_FORWARD "\xef\x81\x8e" // U+f04e #define ICON_FA_FROG "\xef\x94\xae" // U+f52e #define ICON_FA_FROWN "\xef\x84\x99" // U+f119 #define ICON_FA_FROWN_OPEN "\xef\x95\xba" // U+f57a #define ICON_FA_FUNNEL_DOLLAR "\xef\x99\xa2" // U+f662 #define ICON_FA_FUTBOL "\xef\x87\xa3" // U+f1e3 #define ICON_FA_GAMEPAD "\xef\x84\x9b" // U+f11b #define ICON_FA_GAS_PUMP "\xef\x94\xaf" // U+f52f #define ICON_FA_GAVEL "\xef\x83\xa3" // U+f0e3 #define ICON_FA_GEM "\xef\x8e\xa5" // U+f3a5 #define ICON_FA_GENDERLESS "\xef\x88\xad" // U+f22d #define ICON_FA_GHOST "\xef\x9b\xa2" // U+f6e2 #define ICON_FA_GIFT "\xef\x81\xab" // U+f06b #define ICON_FA_GIFTS "\xef\x9e\x9c" // U+f79c #define ICON_FA_GLASS_CHEERS "\xef\x9e\x9f" // U+f79f #define ICON_FA_GLASS_MARTINI "\xef\x80\x80" // U+f000 #define ICON_FA_GLASS_MARTINI_ALT "\xef\x95\xbb" // U+f57b #define ICON_FA_GLASS_WHISKEY "\xef\x9e\xa0" // U+f7a0 #define ICON_FA_GLASSES "\xef\x94\xb0" // U+f530 #define ICON_FA_GLOBE "\xef\x82\xac" // U+f0ac #define ICON_FA_GLOBE_AFRICA "\xef\x95\xbc" // U+f57c #define ICON_FA_GLOBE_AMERICAS "\xef\x95\xbd" // U+f57d #define ICON_FA_GLOBE_ASIA "\xef\x95\xbe" // U+f57e #define ICON_FA_GLOBE_EUROPE "\xef\x9e\xa2" // U+f7a2 #define ICON_FA_GOLF_BALL "\xef\x91\x90" // U+f450 #define ICON_FA_GOPURAM "\xef\x99\xa4" // U+f664 #define ICON_FA_GRADUATION_CAP "\xef\x86\x9d" // U+f19d #define ICON_FA_GREATER_THAN "\xef\x94\xb1" // U+f531 #define ICON_FA_GREATER_THAN_EQUAL "\xef\x94\xb2" // U+f532 #define ICON_FA_GRIMACE "\xef\x95\xbf" // U+f57f #define ICON_FA_GRIN "\xef\x96\x80" // U+f580 #define ICON_FA_GRIN_ALT "\xef\x96\x81" // U+f581 #define ICON_FA_GRIN_BEAM "\xef\x96\x82" // U+f582 #define ICON_FA_GRIN_BEAM_SWEAT "\xef\x96\x83" // U+f583 #define ICON_FA_GRIN_HEARTS "\xef\x96\x84" // U+f584 #define ICON_FA_GRIN_SQUINT "\xef\x96\x85" // U+f585 #define ICON_FA_GRIN_SQUINT_TEARS "\xef\x96\x86" // U+f586 #define ICON_FA_GRIN_STARS "\xef\x96\x87" // U+f587 #define ICON_FA_GRIN_TEARS "\xef\x96\x88" // U+f588 #define ICON_FA_GRIN_TONGUE "\xef\x96\x89" // U+f589 #define ICON_FA_GRIN_TONGUE_SQUINT "\xef\x96\x8a" // U+f58a #define ICON_FA_GRIN_TONGUE_WINK "\xef\x96\x8b" // U+f58b #define ICON_FA_GRIN_WINK "\xef\x96\x8c" // U+f58c #define ICON_FA_GRIP_HORIZONTAL "\xef\x96\x8d" // U+f58d #define ICON_FA_GRIP_LINES "\xef\x9e\xa4" // U+f7a4 #define ICON_FA_GRIP_LINES_VERTICAL "\xef\x9e\xa5" // U+f7a5 #define ICON_FA_GRIP_VERTICAL "\xef\x96\x8e" // U+f58e #define ICON_FA_GUITAR "\xef\x9e\xa6" // U+f7a6 #define ICON_FA_H_SQUARE "\xef\x83\xbd" // U+f0fd #define ICON_FA_HAMBURGER "\xef\xa0\x85" // U+f805 #define ICON_FA_HAMMER "\xef\x9b\xa3" // U+f6e3 #define ICON_FA_HAMSA "\xef\x99\xa5" // U+f665 #define ICON_FA_HAND_HOLDING "\xef\x92\xbd" // U+f4bd #define ICON_FA_HAND_HOLDING_HEART "\xef\x92\xbe" // U+f4be #define ICON_FA_HAND_HOLDING_MEDICAL "\xee\x81\x9c" // U+e05c #define ICON_FA_HAND_HOLDING_USD "\xef\x93\x80" // U+f4c0 #define ICON_FA_HAND_HOLDING_WATER "\xef\x93\x81" // U+f4c1 #define ICON_FA_HAND_LIZARD "\xef\x89\x98" // U+f258 #define ICON_FA_HAND_MIDDLE_FINGER "\xef\xa0\x86" // U+f806 #define ICON_FA_HAND_PAPER "\xef\x89\x96" // U+f256 #define ICON_FA_HAND_PEACE "\xef\x89\x9b" // U+f25b #define ICON_FA_HAND_POINT_DOWN "\xef\x82\xa7" // U+f0a7 #define ICON_FA_HAND_POINT_LEFT "\xef\x82\xa5" // U+f0a5 #define ICON_FA_HAND_POINT_RIGHT "\xef\x82\xa4" // U+f0a4 #define ICON_FA_HAND_POINT_UP "\xef\x82\xa6" // U+f0a6 #define ICON_FA_HAND_POINTER "\xef\x89\x9a" // U+f25a #define ICON_FA_HAND_ROCK "\xef\x89\x95" // U+f255 #define ICON_FA_HAND_SCISSORS "\xef\x89\x97" // U+f257 #define ICON_FA_HAND_SPARKLES "\xee\x81\x9d" // U+e05d #define ICON_FA_HAND_SPOCK "\xef\x89\x99" // U+f259 #define ICON_FA_HANDS "\xef\x93\x82" // U+f4c2 #define ICON_FA_HANDS_HELPING "\xef\x93\x84" // U+f4c4 #define ICON_FA_HANDS_WASH "\xee\x81\x9e" // U+e05e #define ICON_FA_HANDSHAKE "\xef\x8a\xb5" // U+f2b5 #define ICON_FA_HANDSHAKE_ALT_SLASH "\xee\x81\x9f" // U+e05f #define ICON_FA_HANDSHAKE_SLASH "\xee\x81\xa0" // U+e060 #define ICON_FA_HANUKIAH "\xef\x9b\xa6" // U+f6e6 #define ICON_FA_HARD_HAT "\xef\xa0\x87" // U+f807 #define ICON_FA_HASHTAG "\xef\x8a\x92" // U+f292 #define ICON_FA_HAT_COWBOY "\xef\xa3\x80" // U+f8c0 #define ICON_FA_HAT_COWBOY_SIDE "\xef\xa3\x81" // U+f8c1 #define ICON_FA_HAT_WIZARD "\xef\x9b\xa8" // U+f6e8 #define ICON_FA_HDD "\xef\x82\xa0" // U+f0a0 #define ICON_FA_HEAD_SIDE_COUGH "\xee\x81\xa1" // U+e061 #define ICON_FA_HEAD_SIDE_COUGH_SLASH "\xee\x81\xa2" // U+e062 #define ICON_FA_HEAD_SIDE_MASK "\xee\x81\xa3" // U+e063 #define ICON_FA_HEAD_SIDE_VIRUS "\xee\x81\xa4" // U+e064 #define ICON_FA_HEADING "\xef\x87\x9c" // U+f1dc #define ICON_FA_HEADPHONES "\xef\x80\xa5" // U+f025 #define ICON_FA_HEADPHONES_ALT "\xef\x96\x8f" // U+f58f #define ICON_FA_HEADSET "\xef\x96\x90" // U+f590 #define ICON_FA_HEART "\xef\x80\x84" // U+f004 #define ICON_FA_HEART_BROKEN "\xef\x9e\xa9" // U+f7a9 #define ICON_FA_HEARTBEAT "\xef\x88\x9e" // U+f21e #define ICON_FA_HELICOPTER "\xef\x94\xb3" // U+f533 #define ICON_FA_HIGHLIGHTER "\xef\x96\x91" // U+f591 #define ICON_FA_HIKING "\xef\x9b\xac" // U+f6ec #define ICON_FA_HIPPO "\xef\x9b\xad" // U+f6ed #define ICON_FA_HISTORY "\xef\x87\x9a" // U+f1da #define ICON_FA_HOCKEY_PUCK "\xef\x91\x93" // U+f453 #define ICON_FA_HOLLY_BERRY "\xef\x9e\xaa" // U+f7aa #define ICON_FA_HOME "\xef\x80\x95" // U+f015 #define ICON_FA_HORSE "\xef\x9b\xb0" // U+f6f0 #define ICON_FA_HORSE_HEAD "\xef\x9e\xab" // U+f7ab #define ICON_FA_HOSPITAL "\xef\x83\xb8" // U+f0f8 #define ICON_FA_HOSPITAL_ALT "\xef\x91\xbd" // U+f47d #define ICON_FA_HOSPITAL_SYMBOL "\xef\x91\xbe" // U+f47e #define ICON_FA_HOSPITAL_USER "\xef\xa0\x8d" // U+f80d #define ICON_FA_HOT_TUB "\xef\x96\x93" // U+f593 #define ICON_FA_HOTDOG "\xef\xa0\x8f" // U+f80f #define ICON_FA_HOTEL "\xef\x96\x94" // U+f594 #define ICON_FA_HOURGLASS "\xef\x89\x94" // U+f254 #define ICON_FA_HOURGLASS_END "\xef\x89\x93" // U+f253 #define ICON_FA_HOURGLASS_HALF "\xef\x89\x92" // U+f252 #define ICON_FA_HOURGLASS_START "\xef\x89\x91" // U+f251 #define ICON_FA_HOUSE_DAMAGE "\xef\x9b\xb1" // U+f6f1 #define ICON_FA_HOUSE_USER "\xee\x81\xa5" // U+e065 #define ICON_FA_HRYVNIA "\xef\x9b\xb2" // U+f6f2 #define ICON_FA_I_CURSOR "\xef\x89\x86" // U+f246 #define ICON_FA_ICE_CREAM "\xef\xa0\x90" // U+f810 #define ICON_FA_ICICLES "\xef\x9e\xad" // U+f7ad #define ICON_FA_ICONS "\xef\xa1\xad" // U+f86d #define ICON_FA_ID_BADGE "\xef\x8b\x81" // U+f2c1 #define ICON_FA_ID_CARD "\xef\x8b\x82" // U+f2c2 #define ICON_FA_ID_CARD_ALT "\xef\x91\xbf" // U+f47f #define ICON_FA_IGLOO "\xef\x9e\xae" // U+f7ae #define ICON_FA_IMAGE "\xef\x80\xbe" // U+f03e #define ICON_FA_IMAGES "\xef\x8c\x82" // U+f302 #define ICON_FA_INBOX "\xef\x80\x9c" // U+f01c #define ICON_FA_INDENT "\xef\x80\xbc" // U+f03c #define ICON_FA_INDUSTRY "\xef\x89\xb5" // U+f275 #define ICON_FA_INFINITY "\xef\x94\xb4" // U+f534 #define ICON_FA_INFO "\xef\x84\xa9" // U+f129 #define ICON_FA_INFO_CIRCLE "\xef\x81\x9a" // U+f05a #define ICON_FA_ITALIC "\xef\x80\xb3" // U+f033 #define ICON_FA_JEDI "\xef\x99\xa9" // U+f669 #define ICON_FA_JOINT "\xef\x96\x95" // U+f595 #define ICON_FA_JOURNAL_WHILLS "\xef\x99\xaa" // U+f66a #define ICON_FA_KAABA "\xef\x99\xab" // U+f66b #define ICON_FA_KEY "\xef\x82\x84" // U+f084 #define ICON_FA_KEYBOARD "\xef\x84\x9c" // U+f11c #define ICON_FA_KHANDA "\xef\x99\xad" // U+f66d #define ICON_FA_KISS "\xef\x96\x96" // U+f596 #define ICON_FA_KISS_BEAM "\xef\x96\x97" // U+f597 #define ICON_FA_KISS_WINK_HEART "\xef\x96\x98" // U+f598 #define ICON_FA_KIWI_BIRD "\xef\x94\xb5" // U+f535 #define ICON_FA_LANDMARK "\xef\x99\xaf" // U+f66f #define ICON_FA_LANGUAGE "\xef\x86\xab" // U+f1ab #define ICON_FA_LAPTOP "\xef\x84\x89" // U+f109 #define ICON_FA_LAPTOP_CODE "\xef\x97\xbc" // U+f5fc #define ICON_FA_LAPTOP_HOUSE "\xee\x81\xa6" // U+e066 #define ICON_FA_LAPTOP_MEDICAL "\xef\xa0\x92" // U+f812 #define ICON_FA_LAUGH "\xef\x96\x99" // U+f599 #define ICON_FA_LAUGH_BEAM "\xef\x96\x9a" // U+f59a #define ICON_FA_LAUGH_SQUINT "\xef\x96\x9b" // U+f59b #define ICON_FA_LAUGH_WINK "\xef\x96\x9c" // U+f59c #define ICON_FA_LAYER_GROUP "\xef\x97\xbd" // U+f5fd #define ICON_FA_LEAF "\xef\x81\xac" // U+f06c #define ICON_FA_LEMON "\xef\x82\x94" // U+f094 #define ICON_FA_LESS_THAN "\xef\x94\xb6" // U+f536 #define ICON_FA_LESS_THAN_EQUAL "\xef\x94\xb7" // U+f537 #define ICON_FA_LEVEL_DOWN_ALT "\xef\x8e\xbe" // U+f3be #define ICON_FA_LEVEL_UP_ALT "\xef\x8e\xbf" // U+f3bf #define ICON_FA_LIFE_RING "\xef\x87\x8d" // U+f1cd #define ICON_FA_LIGHTBULB "\xef\x83\xab" // U+f0eb #define ICON_FA_LINK "\xef\x83\x81" // U+f0c1 #define ICON_FA_LIRA_SIGN "\xef\x86\x95" // U+f195 #define ICON_FA_LIST "\xef\x80\xba" // U+f03a #define ICON_FA_LIST_ALT "\xef\x80\xa2" // U+f022 #define ICON_FA_LIST_OL "\xef\x83\x8b" // U+f0cb #define ICON_FA_LIST_UL "\xef\x83\x8a" // U+f0ca #define ICON_FA_LOCATION_ARROW "\xef\x84\xa4" // U+f124 #define ICON_FA_LOCK "\xef\x80\xa3" // U+f023 #define ICON_FA_LOCK_OPEN "\xef\x8f\x81" // U+f3c1 #define ICON_FA_LONG_ARROW_ALT_DOWN "\xef\x8c\x89" // U+f309 #define ICON_FA_LONG_ARROW_ALT_LEFT "\xef\x8c\x8a" // U+f30a #define ICON_FA_LONG_ARROW_ALT_RIGHT "\xef\x8c\x8b" // U+f30b #define ICON_FA_LONG_ARROW_ALT_UP "\xef\x8c\x8c" // U+f30c #define ICON_FA_LOW_VISION "\xef\x8a\xa8" // U+f2a8 #define ICON_FA_LUGGAGE_CART "\xef\x96\x9d" // U+f59d #define ICON_FA_LUNGS "\xef\x98\x84" // U+f604 #define ICON_FA_LUNGS_VIRUS "\xee\x81\xa7" // U+e067 #define ICON_FA_MAGIC "\xef\x83\x90" // U+f0d0 #define ICON_FA_MAGNET "\xef\x81\xb6" // U+f076 #define ICON_FA_MAIL_BULK "\xef\x99\xb4" // U+f674 #define ICON_FA_MALE "\xef\x86\x83" // U+f183 #define ICON_FA_MAP "\xef\x89\xb9" // U+f279 #define ICON_FA_MAP_MARKED "\xef\x96\x9f" // U+f59f #define ICON_FA_MAP_MARKED_ALT "\xef\x96\xa0" // U+f5a0 #define ICON_FA_MAP_MARKER "\xef\x81\x81" // U+f041 #define ICON_FA_MAP_MARKER_ALT "\xef\x8f\x85" // U+f3c5 #define ICON_FA_MAP_PIN "\xef\x89\xb6" // U+f276 #define ICON_FA_MAP_SIGNS "\xef\x89\xb7" // U+f277 #define ICON_FA_MARKER "\xef\x96\xa1" // U+f5a1 #define ICON_FA_MARS "\xef\x88\xa2" // U+f222 #define ICON_FA_MARS_DOUBLE "\xef\x88\xa7" // U+f227 #define ICON_FA_MARS_STROKE "\xef\x88\xa9" // U+f229 #define ICON_FA_MARS_STROKE_H "\xef\x88\xab" // U+f22b #define ICON_FA_MARS_STROKE_V "\xef\x88\xaa" // U+f22a #define ICON_FA_MASK "\xef\x9b\xba" // U+f6fa #define ICON_FA_MEDAL "\xef\x96\xa2" // U+f5a2 #define ICON_FA_MEDKIT "\xef\x83\xba" // U+f0fa #define ICON_FA_MEH "\xef\x84\x9a" // U+f11a #define ICON_FA_MEH_BLANK "\xef\x96\xa4" // U+f5a4 #define ICON_FA_MEH_ROLLING_EYES "\xef\x96\xa5" // U+f5a5 #define ICON_FA_MEMORY "\xef\x94\xb8" // U+f538 #define ICON_FA_MENORAH "\xef\x99\xb6" // U+f676 #define ICON_FA_MERCURY "\xef\x88\xa3" // U+f223 #define ICON_FA_METEOR "\xef\x9d\x93" // U+f753 #define ICON_FA_MICROCHIP "\xef\x8b\x9b" // U+f2db #define ICON_FA_MICROPHONE "\xef\x84\xb0" // U+f130 #define ICON_FA_MICROPHONE_ALT "\xef\x8f\x89" // U+f3c9 #define ICON_FA_MICROPHONE_ALT_SLASH "\xef\x94\xb9" // U+f539 #define ICON_FA_MICROPHONE_SLASH "\xef\x84\xb1" // U+f131 #define ICON_FA_MICROSCOPE "\xef\x98\x90" // U+f610 #define ICON_FA_MINUS "\xef\x81\xa8" // U+f068 #define ICON_FA_MINUS_CIRCLE "\xef\x81\x96" // U+f056 #define ICON_FA_MINUS_SQUARE "\xef\x85\x86" // U+f146 #define ICON_FA_MITTEN "\xef\x9e\xb5" // U+f7b5 #define ICON_FA_MOBILE "\xef\x84\x8b" // U+f10b #define ICON_FA_MOBILE_ALT "\xef\x8f\x8d" // U+f3cd #define ICON_FA_MONEY_BILL "\xef\x83\x96" // U+f0d6 #define ICON_FA_MONEY_BILL_ALT "\xef\x8f\x91" // U+f3d1 #define ICON_FA_MONEY_BILL_WAVE "\xef\x94\xba" // U+f53a #define ICON_FA_MONEY_BILL_WAVE_ALT "\xef\x94\xbb" // U+f53b #define ICON_FA_MONEY_CHECK "\xef\x94\xbc" // U+f53c #define ICON_FA_MONEY_CHECK_ALT "\xef\x94\xbd" // U+f53d #define ICON_FA_MONUMENT "\xef\x96\xa6" // U+f5a6 #define ICON_FA_MOON "\xef\x86\x86" // U+f186 #define ICON_FA_MORTAR_PESTLE "\xef\x96\xa7" // U+f5a7 #define ICON_FA_MOSQUE "\xef\x99\xb8" // U+f678 #define ICON_FA_MOTORCYCLE "\xef\x88\x9c" // U+f21c #define ICON_FA_MOUNTAIN "\xef\x9b\xbc" // U+f6fc #define ICON_FA_MOUSE "\xef\xa3\x8c" // U+f8cc #define ICON_FA_MOUSE_POINTER "\xef\x89\x85" // U+f245 #define ICON_FA_MUG_HOT "\xef\x9e\xb6" // U+f7b6 #define ICON_FA_MUSIC "\xef\x80\x81" // U+f001 #define ICON_FA_NETWORK_WIRED "\xef\x9b\xbf" // U+f6ff #define ICON_FA_NEUTER "\xef\x88\xac" // U+f22c #define ICON_FA_NEWSPAPER "\xef\x87\xaa" // U+f1ea #define ICON_FA_NOT_EQUAL "\xef\x94\xbe" // U+f53e #define ICON_FA_NOTES_MEDICAL "\xef\x92\x81" // U+f481 #define ICON_FA_OBJECT_GROUP "\xef\x89\x87" // U+f247 #define ICON_FA_OBJECT_UNGROUP "\xef\x89\x88" // U+f248 #define ICON_FA_OIL_CAN "\xef\x98\x93" // U+f613 #define ICON_FA_OM "\xef\x99\xb9" // U+f679 #define ICON_FA_OTTER "\xef\x9c\x80" // U+f700 #define ICON_FA_OUTDENT "\xef\x80\xbb" // U+f03b #define ICON_FA_PAGER "\xef\xa0\x95" // U+f815 #define ICON_FA_PAINT_BRUSH "\xef\x87\xbc" // U+f1fc #define ICON_FA_PAINT_ROLLER "\xef\x96\xaa" // U+f5aa #define ICON_FA_PALETTE "\xef\x94\xbf" // U+f53f #define ICON_FA_PALLET "\xef\x92\x82" // U+f482 #define ICON_FA_PAPER_PLANE "\xef\x87\x98" // U+f1d8 #define ICON_FA_PAPERCLIP "\xef\x83\x86" // U+f0c6 #define ICON_FA_PARACHUTE_BOX "\xef\x93\x8d" // U+f4cd #define ICON_FA_PARAGRAPH "\xef\x87\x9d" // U+f1dd #define ICON_FA_PARKING "\xef\x95\x80" // U+f540 #define ICON_FA_PASSPORT "\xef\x96\xab" // U+f5ab #define ICON_FA_PASTAFARIANISM "\xef\x99\xbb" // U+f67b #define ICON_FA_PASTE "\xef\x83\xaa" // U+f0ea #define ICON_FA_PAUSE "\xef\x81\x8c" // U+f04c #define ICON_FA_PAUSE_CIRCLE "\xef\x8a\x8b" // U+f28b #define ICON_FA_PAW "\xef\x86\xb0" // U+f1b0 #define ICON_FA_PEACE "\xef\x99\xbc" // U+f67c #define ICON_FA_PEN "\xef\x8c\x84" // U+f304 #define ICON_FA_PEN_ALT "\xef\x8c\x85" // U+f305 #define ICON_FA_PEN_FANCY "\xef\x96\xac" // U+f5ac #define ICON_FA_PEN_NIB "\xef\x96\xad" // U+f5ad #define ICON_FA_PEN_SQUARE "\xef\x85\x8b" // U+f14b #define ICON_FA_PENCIL_ALT "\xef\x8c\x83" // U+f303 #define ICON_FA_PENCIL_RULER "\xef\x96\xae" // U+f5ae #define ICON_FA_PEOPLE_ARROWS "\xee\x81\xa8" // U+e068 #define ICON_FA_PEOPLE_CARRY "\xef\x93\x8e" // U+f4ce #define ICON_FA_PEPPER_HOT "\xef\xa0\x96" // U+f816 #define ICON_FA_PERCENT "\xef\x8a\x95" // U+f295 #define ICON_FA_PERCENTAGE "\xef\x95\x81" // U+f541 #define ICON_FA_PERSON_BOOTH "\xef\x9d\x96" // U+f756 #define ICON_FA_PHONE "\xef\x82\x95" // U+f095 #define ICON_FA_PHONE_ALT "\xef\xa1\xb9" // U+f879 #define ICON_FA_PHONE_SLASH "\xef\x8f\x9d" // U+f3dd #define ICON_FA_PHONE_SQUARE "\xef\x82\x98" // U+f098 #define ICON_FA_PHONE_SQUARE_ALT "\xef\xa1\xbb" // U+f87b #define ICON_FA_PHONE_VOLUME "\xef\x8a\xa0" // U+f2a0 #define ICON_FA_PHOTO_VIDEO "\xef\xa1\xbc" // U+f87c #define ICON_FA_PIGGY_BANK "\xef\x93\x93" // U+f4d3 #define ICON_FA_PILLS "\xef\x92\x84" // U+f484 #define ICON_FA_PIZZA_SLICE "\xef\xa0\x98" // U+f818 #define ICON_FA_PLACE_OF_WORSHIP "\xef\x99\xbf" // U+f67f #define ICON_FA_PLANE "\xef\x81\xb2" // U+f072 #define ICON_FA_PLANE_ARRIVAL "\xef\x96\xaf" // U+f5af #define ICON_FA_PLANE_DEPARTURE "\xef\x96\xb0" // U+f5b0 #define ICON_FA_PLANE_SLASH "\xee\x81\xa9" // U+e069 #define ICON_FA_PLAY "\xef\x81\x8b" // U+f04b #define ICON_FA_PLAY_CIRCLE "\xef\x85\x84" // U+f144 #define ICON_FA_PLUG "\xef\x87\xa6" // U+f1e6 #define ICON_FA_PLUS "\xef\x81\xa7" // U+f067 #define ICON_FA_PLUS_CIRCLE "\xef\x81\x95" // U+f055 #define ICON_FA_PLUS_SQUARE "\xef\x83\xbe" // U+f0fe #define ICON_FA_PODCAST "\xef\x8b\x8e" // U+f2ce #define ICON_FA_POLL "\xef\x9a\x81" // U+f681 #define ICON_FA_POLL_H "\xef\x9a\x82" // U+f682 #define ICON_FA_POO "\xef\x8b\xbe" // U+f2fe #define ICON_FA_POO_STORM "\xef\x9d\x9a" // U+f75a #define ICON_FA_POOP "\xef\x98\x99" // U+f619 #define ICON_FA_PORTRAIT "\xef\x8f\xa0" // U+f3e0 #define ICON_FA_POUND_SIGN "\xef\x85\x94" // U+f154 #define ICON_FA_POWER_OFF "\xef\x80\x91" // U+f011 #define ICON_FA_PRAY "\xef\x9a\x83" // U+f683 #define ICON_FA_PRAYING_HANDS "\xef\x9a\x84" // U+f684 #define ICON_FA_PRESCRIPTION "\xef\x96\xb1" // U+f5b1 #define ICON_FA_PRESCRIPTION_BOTTLE "\xef\x92\x85" // U+f485 #define ICON_FA_PRESCRIPTION_BOTTLE_ALT "\xef\x92\x86" // U+f486 #define ICON_FA_PRINT "\xef\x80\xaf" // U+f02f #define ICON_FA_PROCEDURES "\xef\x92\x87" // U+f487 #define ICON_FA_PROJECT_DIAGRAM "\xef\x95\x82" // U+f542 #define ICON_FA_PUMP_MEDICAL "\xee\x81\xaa" // U+e06a #define ICON_FA_PUMP_SOAP "\xee\x81\xab" // U+e06b #define ICON_FA_PUZZLE_PIECE "\xef\x84\xae" // U+f12e #define ICON_FA_QRCODE "\xef\x80\xa9" // U+f029 #define ICON_FA_QUESTION "\xef\x84\xa8" // U+f128 #define ICON_FA_QUESTION_CIRCLE "\xef\x81\x99" // U+f059 #define ICON_FA_QUIDDITCH "\xef\x91\x98" // U+f458 #define ICON_FA_QUOTE_LEFT "\xef\x84\x8d" // U+f10d #define ICON_FA_QUOTE_RIGHT "\xef\x84\x8e" // U+f10e #define ICON_FA_QURAN "\xef\x9a\x87" // U+f687 #define ICON_FA_RADIATION "\xef\x9e\xb9" // U+f7b9 #define ICON_FA_RADIATION_ALT "\xef\x9e\xba" // U+f7ba #define ICON_FA_RAINBOW "\xef\x9d\x9b" // U+f75b #define ICON_FA_RANDOM "\xef\x81\xb4" // U+f074 #define ICON_FA_RECEIPT "\xef\x95\x83" // U+f543 #define ICON_FA_RECORD_VINYL "\xef\xa3\x99" // U+f8d9 #define ICON_FA_RECYCLE "\xef\x86\xb8" // U+f1b8 #define ICON_FA_REDO "\xef\x80\x9e" // U+f01e #define ICON_FA_REDO_ALT "\xef\x8b\xb9" // U+f2f9 #define ICON_FA_REGISTERED "\xef\x89\x9d" // U+f25d #define ICON_FA_REMOVE_FORMAT "\xef\xa1\xbd" // U+f87d #define ICON_FA_REPLY "\xef\x8f\xa5" // U+f3e5 #define ICON_FA_REPLY_ALL "\xef\x84\xa2" // U+f122 #define ICON_FA_REPUBLICAN "\xef\x9d\x9e" // U+f75e #define ICON_FA_RESTROOM "\xef\x9e\xbd" // U+f7bd #define ICON_FA_RETWEET "\xef\x81\xb9" // U+f079 #define ICON_FA_RIBBON "\xef\x93\x96" // U+f4d6 #define ICON_FA_RING "\xef\x9c\x8b" // U+f70b #define ICON_FA_ROAD "\xef\x80\x98" // U+f018 #define ICON_FA_ROBOT "\xef\x95\x84" // U+f544 #define ICON_FA_ROCKET "\xef\x84\xb5" // U+f135 #define ICON_FA_ROUTE "\xef\x93\x97" // U+f4d7 #define ICON_FA_RSS "\xef\x82\x9e" // U+f09e #define ICON_FA_RSS_SQUARE "\xef\x85\x83" // U+f143 #define ICON_FA_RUBLE_SIGN "\xef\x85\x98" // U+f158 #define ICON_FA_RULER "\xef\x95\x85" // U+f545 #define ICON_FA_RULER_COMBINED "\xef\x95\x86" // U+f546 #define ICON_FA_RULER_HORIZONTAL "\xef\x95\x87" // U+f547 #define ICON_FA_RULER_VERTICAL "\xef\x95\x88" // U+f548 #define ICON_FA_RUNNING "\xef\x9c\x8c" // U+f70c #define ICON_FA_RUPEE_SIGN "\xef\x85\x96" // U+f156 #define ICON_FA_SAD_CRY "\xef\x96\xb3" // U+f5b3 #define ICON_FA_SAD_TEAR "\xef\x96\xb4" // U+f5b4 #define ICON_FA_SATELLITE "\xef\x9e\xbf" // U+f7bf #define ICON_FA_SATELLITE_DISH "\xef\x9f\x80" // U+f7c0 #define ICON_FA_SAVE "\xef\x83\x87" // U+f0c7 #define ICON_FA_SCHOOL "\xef\x95\x89" // U+f549 #define ICON_FA_SCREWDRIVER "\xef\x95\x8a" // U+f54a #define ICON_FA_SCROLL "\xef\x9c\x8e" // U+f70e #define ICON_FA_SD_CARD "\xef\x9f\x82" // U+f7c2 #define ICON_FA_SEARCH "\xef\x80\x82" // U+f002 #define ICON_FA_SEARCH_DOLLAR "\xef\x9a\x88" // U+f688 #define ICON_FA_SEARCH_LOCATION "\xef\x9a\x89" // U+f689 #define ICON_FA_SEARCH_MINUS "\xef\x80\x90" // U+f010 #define ICON_FA_SEARCH_PLUS "\xef\x80\x8e" // U+f00e #define ICON_FA_SEEDLING "\xef\x93\x98" // U+f4d8 #define ICON_FA_SERVER "\xef\x88\xb3" // U+f233 #define ICON_FA_SHAPES "\xef\x98\x9f" // U+f61f #define ICON_FA_SHARE "\xef\x81\xa4" // U+f064 #define ICON_FA_SHARE_ALT "\xef\x87\xa0" // U+f1e0 #define ICON_FA_SHARE_ALT_SQUARE "\xef\x87\xa1" // U+f1e1 #define ICON_FA_SHARE_SQUARE "\xef\x85\x8d" // U+f14d #define ICON_FA_SHEKEL_SIGN "\xef\x88\x8b" // U+f20b #define ICON_FA_SHIELD_ALT "\xef\x8f\xad" // U+f3ed #define ICON_FA_SHIELD_VIRUS "\xee\x81\xac" // U+e06c #define ICON_FA_SHIP "\xef\x88\x9a" // U+f21a #define ICON_FA_SHIPPING_FAST "\xef\x92\x8b" // U+f48b #define ICON_FA_SHOE_PRINTS "\xef\x95\x8b" // U+f54b #define ICON_FA_SHOPPING_BAG "\xef\x8a\x90" // U+f290 #define ICON_FA_SHOPPING_BASKET "\xef\x8a\x91" // U+f291 #define ICON_FA_SHOPPING_CART "\xef\x81\xba" // U+f07a #define ICON_FA_SHOWER "\xef\x8b\x8c" // U+f2cc #define ICON_FA_SHUTTLE_VAN "\xef\x96\xb6" // U+f5b6 #define ICON_FA_SIGN "\xef\x93\x99" // U+f4d9 #define ICON_FA_SIGN_IN_ALT "\xef\x8b\xb6" // U+f2f6 #define ICON_FA_SIGN_LANGUAGE "\xef\x8a\xa7" // U+f2a7 #define ICON_FA_SIGN_OUT_ALT "\xef\x8b\xb5" // U+f2f5 #define ICON_FA_SIGNAL "\xef\x80\x92" // U+f012 #define ICON_FA_SIGNATURE "\xef\x96\xb7" // U+f5b7 #define ICON_FA_SIM_CARD "\xef\x9f\x84" // U+f7c4 #define ICON_FA_SINK "\xee\x81\xad" // U+e06d #define ICON_FA_SITEMAP "\xef\x83\xa8" // U+f0e8 #define ICON_FA_SKATING "\xef\x9f\x85" // U+f7c5 #define ICON_FA_SKIING "\xef\x9f\x89" // U+f7c9 #define ICON_FA_SKIING_NORDIC "\xef\x9f\x8a" // U+f7ca #define ICON_FA_SKULL "\xef\x95\x8c" // U+f54c #define ICON_FA_SKULL_CROSSBONES "\xef\x9c\x94" // U+f714 #define ICON_FA_SLASH "\xef\x9c\x95" // U+f715 #define ICON_FA_SLEIGH "\xef\x9f\x8c" // U+f7cc #define ICON_FA_SLIDERS_H "\xef\x87\x9e" // U+f1de #define ICON_FA_SMILE "\xef\x84\x98" // U+f118 #define ICON_FA_SMILE_BEAM "\xef\x96\xb8" // U+f5b8 #define ICON_FA_SMILE_WINK "\xef\x93\x9a" // U+f4da #define ICON_FA_SMOG "\xef\x9d\x9f" // U+f75f #define ICON_FA_SMOKING "\xef\x92\x8d" // U+f48d #define ICON_FA_SMOKING_BAN "\xef\x95\x8d" // U+f54d #define ICON_FA_SMS "\xef\x9f\x8d" // U+f7cd #define ICON_FA_SNOWBOARDING "\xef\x9f\x8e" // U+f7ce #define ICON_FA_SNOWFLAKE "\xef\x8b\x9c" // U+f2dc #define ICON_FA_SNOWMAN "\xef\x9f\x90" // U+f7d0 #define ICON_FA_SNOWPLOW "\xef\x9f\x92" // U+f7d2 #define ICON_FA_SOAP "\xee\x81\xae" // U+e06e #define ICON_FA_SOCKS "\xef\x9a\x96" // U+f696 #define ICON_FA_SOLAR_PANEL "\xef\x96\xba" // U+f5ba #define ICON_FA_SORT "\xef\x83\x9c" // U+f0dc #define ICON_FA_SORT_ALPHA_DOWN "\xef\x85\x9d" // U+f15d #define ICON_FA_SORT_ALPHA_DOWN_ALT "\xef\xa2\x81" // U+f881 #define ICON_FA_SORT_ALPHA_UP "\xef\x85\x9e" // U+f15e #define ICON_FA_SORT_ALPHA_UP_ALT "\xef\xa2\x82" // U+f882 #define ICON_FA_SORT_AMOUNT_DOWN "\xef\x85\xa0" // U+f160 #define ICON_FA_SORT_AMOUNT_DOWN_ALT "\xef\xa2\x84" // U+f884 #define ICON_FA_SORT_AMOUNT_UP "\xef\x85\xa1" // U+f161 #define ICON_FA_SORT_AMOUNT_UP_ALT "\xef\xa2\x85" // U+f885 #define ICON_FA_SORT_DOWN "\xef\x83\x9d" // U+f0dd #define ICON_FA_SORT_NUMERIC_DOWN "\xef\x85\xa2" // U+f162 #define ICON_FA_SORT_NUMERIC_DOWN_ALT "\xef\xa2\x86" // U+f886 #define ICON_FA_SORT_NUMERIC_UP "\xef\x85\xa3" // U+f163 #define ICON_FA_SORT_NUMERIC_UP_ALT "\xef\xa2\x87" // U+f887 #define ICON_FA_SORT_UP "\xef\x83\x9e" // U+f0de #define ICON_FA_SPA "\xef\x96\xbb" // U+f5bb #define ICON_FA_SPACE_SHUTTLE "\xef\x86\x97" // U+f197 #define ICON_FA_SPELL_CHECK "\xef\xa2\x91" // U+f891 #define ICON_FA_SPIDER "\xef\x9c\x97" // U+f717 #define ICON_FA_SPINNER "\xef\x84\x90" // U+f110 #define ICON_FA_SPLOTCH "\xef\x96\xbc" // U+f5bc #define ICON_FA_SPRAY_CAN "\xef\x96\xbd" // U+f5bd #define ICON_FA_SQUARE "\xef\x83\x88" // U+f0c8 #define ICON_FA_SQUARE_FULL "\xef\x91\x9c" // U+f45c #define ICON_FA_SQUARE_ROOT_ALT "\xef\x9a\x98" // U+f698 #define ICON_FA_STAMP "\xef\x96\xbf" // U+f5bf #define ICON_FA_STAR "\xef\x80\x85" // U+f005 #define ICON_FA_STAR_AND_CRESCENT "\xef\x9a\x99" // U+f699 #define ICON_FA_STAR_HALF "\xef\x82\x89" // U+f089 #define ICON_FA_STAR_HALF_ALT "\xef\x97\x80" // U+f5c0 #define ICON_FA_STAR_OF_DAVID "\xef\x9a\x9a" // U+f69a #define ICON_FA_STAR_OF_LIFE "\xef\x98\xa1" // U+f621 #define ICON_FA_STEP_BACKWARD "\xef\x81\x88" // U+f048 #define ICON_FA_STEP_FORWARD "\xef\x81\x91" // U+f051 #define ICON_FA_STETHOSCOPE "\xef\x83\xb1" // U+f0f1 #define ICON_FA_STICKY_NOTE "\xef\x89\x89" // U+f249 #define ICON_FA_STOP "\xef\x81\x8d" // U+f04d #define ICON_FA_STOP_CIRCLE "\xef\x8a\x8d" // U+f28d #define ICON_FA_STOPWATCH "\xef\x8b\xb2" // U+f2f2 #define ICON_FA_STOPWATCH_20 "\xee\x81\xaf" // U+e06f #define ICON_FA_STORE "\xef\x95\x8e" // U+f54e #define ICON_FA_STORE_ALT "\xef\x95\x8f" // U+f54f #define ICON_FA_STORE_ALT_SLASH "\xee\x81\xb0" // U+e070 #define ICON_FA_STORE_SLASH "\xee\x81\xb1" // U+e071 #define ICON_FA_STREAM "\xef\x95\x90" // U+f550 #define ICON_FA_STREET_VIEW "\xef\x88\x9d" // U+f21d #define ICON_FA_STRIKETHROUGH "\xef\x83\x8c" // U+f0cc #define ICON_FA_STROOPWAFEL "\xef\x95\x91" // U+f551 #define ICON_FA_SUBSCRIPT "\xef\x84\xac" // U+f12c #define ICON_FA_SUBWAY "\xef\x88\xb9" // U+f239 #define ICON_FA_SUITCASE "\xef\x83\xb2" // U+f0f2 #define ICON_FA_SUITCASE_ROLLING "\xef\x97\x81" // U+f5c1 #define ICON_FA_SUN "\xef\x86\x85" // U+f185 #define ICON_FA_SUPERSCRIPT "\xef\x84\xab" // U+f12b #define ICON_FA_SURPRISE "\xef\x97\x82" // U+f5c2 #define ICON_FA_SWATCHBOOK "\xef\x97\x83" // U+f5c3 #define ICON_FA_SWIMMER "\xef\x97\x84" // U+f5c4 #define ICON_FA_SWIMMING_POOL "\xef\x97\x85" // U+f5c5 #define ICON_FA_SYNAGOGUE "\xef\x9a\x9b" // U+f69b #define ICON_FA_SYNC "\xef\x80\xa1" // U+f021 #define ICON_FA_SYNC_ALT "\xef\x8b\xb1" // U+f2f1 #define ICON_FA_SYRINGE "\xef\x92\x8e" // U+f48e #define ICON_FA_TABLE "\xef\x83\x8e" // U+f0ce #define ICON_FA_TABLE_TENNIS "\xef\x91\x9d" // U+f45d #define ICON_FA_TABLET "\xef\x84\x8a" // U+f10a #define ICON_FA_TABLET_ALT "\xef\x8f\xba" // U+f3fa #define ICON_FA_TABLETS "\xef\x92\x90" // U+f490 #define ICON_FA_TACHOMETER_ALT "\xef\x8f\xbd" // U+f3fd #define ICON_FA_TAG "\xef\x80\xab" // U+f02b #define ICON_FA_TAGS "\xef\x80\xac" // U+f02c #define ICON_FA_TAPE "\xef\x93\x9b" // U+f4db #define ICON_FA_TASKS "\xef\x82\xae" // U+f0ae #define ICON_FA_TAXI "\xef\x86\xba" // U+f1ba #define ICON_FA_TEETH "\xef\x98\xae" // U+f62e #define ICON_FA_TEETH_OPEN "\xef\x98\xaf" // U+f62f #define ICON_FA_TEMPERATURE_HIGH "\xef\x9d\xa9" // U+f769 #define ICON_FA_TEMPERATURE_LOW "\xef\x9d\xab" // U+f76b #define ICON_FA_TENGE "\xef\x9f\x97" // U+f7d7 #define ICON_FA_TERMINAL "\xef\x84\xa0" // U+f120 #define ICON_FA_TEXT_HEIGHT "\xef\x80\xb4" // U+f034 #define ICON_FA_TEXT_WIDTH "\xef\x80\xb5" // U+f035 #define ICON_FA_TH "\xef\x80\x8a" // U+f00a #define ICON_FA_TH_LARGE "\xef\x80\x89" // U+f009 #define ICON_FA_TH_LIST "\xef\x80\x8b" // U+f00b #define ICON_FA_THEATER_MASKS "\xef\x98\xb0" // U+f630 #define ICON_FA_THERMOMETER "\xef\x92\x91" // U+f491 #define ICON_FA_THERMOMETER_EMPTY "\xef\x8b\x8b" // U+f2cb #define ICON_FA_THERMOMETER_FULL "\xef\x8b\x87" // U+f2c7 #define ICON_FA_THERMOMETER_HALF "\xef\x8b\x89" // U+f2c9 #define ICON_FA_THERMOMETER_QUARTER "\xef\x8b\x8a" // U+f2ca #define ICON_FA_THERMOMETER_THREE_QUARTERS "\xef\x8b\x88" // U+f2c8 #define ICON_FA_THUMBS_DOWN "\xef\x85\xa5" // U+f165 #define ICON_FA_THUMBS_UP "\xef\x85\xa4" // U+f164 #define ICON_FA_THUMBTACK "\xef\x82\x8d" // U+f08d #define ICON_FA_TICKET_ALT "\xef\x8f\xbf" // U+f3ff #define ICON_FA_TIMES "\xef\x80\x8d" // U+f00d #define ICON_FA_TIMES_CIRCLE "\xef\x81\x97" // U+f057 #define ICON_FA_TINT "\xef\x81\x83" // U+f043 #define ICON_FA_TINT_SLASH "\xef\x97\x87" // U+f5c7 #define ICON_FA_TIRED "\xef\x97\x88" // U+f5c8 #define ICON_FA_TOGGLE_OFF "\xef\x88\x84" // U+f204 #define ICON_FA_TOGGLE_ON "\xef\x88\x85" // U+f205 #define ICON_FA_TOILET "\xef\x9f\x98" // U+f7d8 #define ICON_FA_TOILET_PAPER "\xef\x9c\x9e" // U+f71e #define ICON_FA_TOILET_PAPER_SLASH "\xee\x81\xb2" // U+e072 #define ICON_FA_TOOLBOX "\xef\x95\x92" // U+f552 #define ICON_FA_TOOLS "\xef\x9f\x99" // U+f7d9 #define ICON_FA_TOOTH "\xef\x97\x89" // U+f5c9 #define ICON_FA_TORAH "\xef\x9a\xa0" // U+f6a0 #define ICON_FA_TORII_GATE "\xef\x9a\xa1" // U+f6a1 #define ICON_FA_TRACTOR "\xef\x9c\xa2" // U+f722 #define ICON_FA_TRADEMARK "\xef\x89\x9c" // U+f25c #define ICON_FA_TRAFFIC_LIGHT "\xef\x98\xb7" // U+f637 #define ICON_FA_TRAILER "\xee\x81\x81" // U+e041 #define ICON_FA_TRAIN "\xef\x88\xb8" // U+f238 #define ICON_FA_TRAM "\xef\x9f\x9a" // U+f7da #define ICON_FA_TRANSGENDER "\xef\x88\xa4" // U+f224 #define ICON_FA_TRANSGENDER_ALT "\xef\x88\xa5" // U+f225 #define ICON_FA_TRASH "\xef\x87\xb8" // U+f1f8 #define ICON_FA_TRASH_ALT "\xef\x8b\xad" // U+f2ed #define ICON_FA_TRASH_RESTORE "\xef\xa0\xa9" // U+f829 #define ICON_FA_TRASH_RESTORE_ALT "\xef\xa0\xaa" // U+f82a #define ICON_FA_TREE "\xef\x86\xbb" // U+f1bb #define ICON_FA_TROPHY "\xef\x82\x91" // U+f091 #define ICON_FA_TRUCK "\xef\x83\x91" // U+f0d1 #define ICON_FA_TRUCK_LOADING "\xef\x93\x9e" // U+f4de #define ICON_FA_TRUCK_MONSTER "\xef\x98\xbb" // U+f63b #define ICON_FA_TRUCK_MOVING "\xef\x93\x9f" // U+f4df #define ICON_FA_TRUCK_PICKUP "\xef\x98\xbc" // U+f63c #define ICON_FA_TSHIRT "\xef\x95\x93" // U+f553 #define ICON_FA_TTY "\xef\x87\xa4" // U+f1e4 #define ICON_FA_TV "\xef\x89\xac" // U+f26c #define ICON_FA_UMBRELLA "\xef\x83\xa9" // U+f0e9 #define ICON_FA_UMBRELLA_BEACH "\xef\x97\x8a" // U+f5ca #define ICON_FA_UNDERLINE "\xef\x83\x8d" // U+f0cd #define ICON_FA_UNDO "\xef\x83\xa2" // U+f0e2 #define ICON_FA_UNDO_ALT "\xef\x8b\xaa" // U+f2ea #define ICON_FA_UNIVERSAL_ACCESS "\xef\x8a\x9a" // U+f29a #define ICON_FA_UNIVERSITY "\xef\x86\x9c" // U+f19c #define ICON_FA_UNLINK "\xef\x84\xa7" // U+f127 #define ICON_FA_UNLOCK "\xef\x82\x9c" // U+f09c #define ICON_FA_UNLOCK_ALT "\xef\x84\xbe" // U+f13e #define ICON_FA_UPLOAD "\xef\x82\x93" // U+f093 #define ICON_FA_USER "\xef\x80\x87" // U+f007 #define ICON_FA_USER_ALT "\xef\x90\x86" // U+f406 #define ICON_FA_USER_ALT_SLASH "\xef\x93\xba" // U+f4fa #define ICON_FA_USER_ASTRONAUT "\xef\x93\xbb" // U+f4fb #define ICON_FA_USER_CHECK "\xef\x93\xbc" // U+f4fc #define ICON_FA_USER_CIRCLE "\xef\x8a\xbd" // U+f2bd #define ICON_FA_USER_CLOCK "\xef\x93\xbd" // U+f4fd #define ICON_FA_USER_COG "\xef\x93\xbe" // U+f4fe #define ICON_FA_USER_EDIT "\xef\x93\xbf" // U+f4ff #define ICON_FA_USER_FRIENDS "\xef\x94\x80" // U+f500 #define ICON_FA_USER_GRADUATE "\xef\x94\x81" // U+f501 #define ICON_FA_USER_INJURED "\xef\x9c\xa8" // U+f728 #define ICON_FA_USER_LOCK "\xef\x94\x82" // U+f502 #define ICON_FA_USER_MD "\xef\x83\xb0" // U+f0f0 #define ICON_FA_USER_MINUS "\xef\x94\x83" // U+f503 #define ICON_FA_USER_NINJA "\xef\x94\x84" // U+f504 #define ICON_FA_USER_NURSE "\xef\xa0\xaf" // U+f82f #define ICON_FA_USER_PLUS "\xef\x88\xb4" // U+f234 #define ICON_FA_USER_SECRET "\xef\x88\x9b" // U+f21b #define ICON_FA_USER_SHIELD "\xef\x94\x85" // U+f505 #define ICON_FA_USER_SLASH "\xef\x94\x86" // U+f506 #define ICON_FA_USER_TAG "\xef\x94\x87" // U+f507 #define ICON_FA_USER_TIE "\xef\x94\x88" // U+f508 #define ICON_FA_USER_TIMES "\xef\x88\xb5" // U+f235 #define ICON_FA_USERS "\xef\x83\x80" // U+f0c0 #define ICON_FA_USERS_COG "\xef\x94\x89" // U+f509 #define ICON_FA_USERS_SLASH "\xee\x81\xb3" // U+e073 #define ICON_FA_UTENSIL_SPOON "\xef\x8b\xa5" // U+f2e5 #define ICON_FA_UTENSILS "\xef\x8b\xa7" // U+f2e7 #define ICON_FA_VECTOR_SQUARE "\xef\x97\x8b" // U+f5cb #define ICON_FA_VENUS "\xef\x88\xa1" // U+f221 #define ICON_FA_VENUS_DOUBLE "\xef\x88\xa6" // U+f226 #define ICON_FA_VENUS_MARS "\xef\x88\xa8" // U+f228 #define ICON_FA_VEST "\xee\x82\x85" // U+e085 #define ICON_FA_VEST_PATCHES "\xee\x82\x86" // U+e086 #define ICON_FA_VIAL "\xef\x92\x92" // U+f492 #define ICON_FA_VIALS "\xef\x92\x93" // U+f493 #define ICON_FA_VIDEO "\xef\x80\xbd" // U+f03d #define ICON_FA_VIDEO_SLASH "\xef\x93\xa2" // U+f4e2 #define ICON_FA_VIHARA "\xef\x9a\xa7" // U+f6a7 #define ICON_FA_VIRUS "\xee\x81\xb4" // U+e074 #define ICON_FA_VIRUS_SLASH "\xee\x81\xb5" // U+e075 #define ICON_FA_VIRUSES "\xee\x81\xb6" // U+e076 #define ICON_FA_VOICEMAIL "\xef\xa2\x97" // U+f897 #define ICON_FA_VOLLEYBALL_BALL "\xef\x91\x9f" // U+f45f #define ICON_FA_VOLUME_DOWN "\xef\x80\xa7" // U+f027 #define ICON_FA_VOLUME_MUTE "\xef\x9a\xa9" // U+f6a9 #define ICON_FA_VOLUME_OFF "\xef\x80\xa6" // U+f026 #define ICON_FA_VOLUME_UP "\xef\x80\xa8" // U+f028 #define ICON_FA_VOTE_YEA "\xef\x9d\xb2" // U+f772 #define ICON_FA_VR_CARDBOARD "\xef\x9c\xa9" // U+f729 #define ICON_FA_WALKING "\xef\x95\x94" // U+f554 #define ICON_FA_WALLET "\xef\x95\x95" // U+f555 #define ICON_FA_WAREHOUSE "\xef\x92\x94" // U+f494 #define ICON_FA_WATER "\xef\x9d\xb3" // U+f773 #define ICON_FA_WAVE_SQUARE "\xef\xa0\xbe" // U+f83e #define ICON_FA_WEIGHT "\xef\x92\x96" // U+f496 #define ICON_FA_WEIGHT_HANGING "\xef\x97\x8d" // U+f5cd #define ICON_FA_WHEELCHAIR "\xef\x86\x93" // U+f193 #define ICON_FA_WIFI "\xef\x87\xab" // U+f1eb #define ICON_FA_WIND "\xef\x9c\xae" // U+f72e #define ICON_FA_WINDOW_CLOSE "\xef\x90\x90" // U+f410 #define ICON_FA_WINDOW_MAXIMIZE "\xef\x8b\x90" // U+f2d0 #define ICON_FA_WINDOW_MINIMIZE "\xef\x8b\x91" // U+f2d1 #define ICON_FA_WINDOW_RESTORE "\xef\x8b\x92" // U+f2d2 #define ICON_FA_WINE_BOTTLE "\xef\x9c\xaf" // U+f72f #define ICON_FA_WINE_GLASS "\xef\x93\xa3" // U+f4e3 #define ICON_FA_WINE_GLASS_ALT "\xef\x97\x8e" // U+f5ce #define ICON_FA_WON_SIGN "\xef\x85\x99" // U+f159 #define ICON_FA_WRENCH "\xef\x82\xad" // U+f0ad #define ICON_FA_X_RAY "\xef\x92\x97" // U+f497 #define ICON_FA_YEN_SIGN "\xef\x85\x97" // U+f157 #define ICON_FA_YIN_YANG "\xef\x9a\xad" // U+f6ad ================================================ FILE: Source/External/imgui_tools/imgui_impl_glfw.cpp ================================================ // dear imgui: Platform Backend for GLFW // This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan, WebGPU..) // (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) // (Requires: GLFW 3.1+. Prefer GLFW 3.3+ for full feature support.) // Implemented features: // [X] Platform: Clipboard support. // [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLFW_KEY_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set] // [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange' (note: the resizing cursors requires GLFW 3.4+). // [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. // Issues: // [ ] Platform: Multi-viewport support: ParentViewportID not honored, and so io.ConfigViewportsNoDefaultParent has no effect (minor). // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. // Read online: https://github.com/ocornut/imgui/tree/master/docs // CHANGELOG // (minor and older changes stripped away, please see git history for details) // 2022-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. // 2022-11-22: Perform a dummy glfwGetError() read to cancel missing names with glfwGetKeyName(). (#5908) // 2022-10-18: Perform a dummy glfwGetError() read to cancel missing mouse cursors errors. Using GLFW_VERSION_COMBINED directly. (#5785) // 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11. // 2022-09-26: Inputs: Renamed ImGuiKey_ModXXX introduced in 1.87 to ImGuiMod_XXX (old names still supported). // 2022-09-01: Inputs: Honor GLFW_CURSOR_DISABLED by not setting mouse position. // 2022-04-30: Inputs: Fixed ImGui_ImplGlfw_TranslateUntranslatedKey() for lower case letters on OSX. // 2022-03-23: Inputs: Fixed a regression in 1.87 which resulted in keyboard modifiers events being reported incorrectly on Linux/X11. // 2022-02-07: Added ImGui_ImplGlfw_InstallCallbacks()/ImGui_ImplGlfw_RestoreCallbacks() helpers to facilitate user installing callbacks after initializing backend. // 2022-01-26: Inputs: replaced short-lived io.AddKeyModsEvent() (added two weeks ago) with io.AddKeyEvent() using ImGuiKey_ModXXX flags. Sorry for the confusion. // 2021-01-20: Inputs: calling new io.AddKeyAnalogEvent() for gamepad support, instead of writing directly to io.NavInputs[]. // 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+). // 2022-01-17: Inputs: always update key mods next and before key event (not in NewFrame) to fix input queue with very low framerates. // 2022-01-12: *BREAKING CHANGE*: Now using glfwSetCursorPosCallback(). If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetCursorPosCallback() and forward it to the backend via ImGui_ImplGlfw_CursorPosCallback(). // 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2022-01-05: Inputs: Converting GLFW untranslated keycodes back to translated keycodes (in the ImGui_ImplGlfw_KeyCallback() function) in order to match the behavior of every other backend, and facilitate the use of GLFW with lettered-shortcuts API. // 2021-08-17: *BREAKING CHANGE*: Now using glfwSetWindowFocusCallback() to calling io.AddFocusEvent(). If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetWindowFocusCallback() and forward it to the backend via ImGui_ImplGlfw_WindowFocusCallback(). // 2021-07-29: *BREAKING CHANGE*: Now using glfwSetCursorEnterCallback(). MousePos is correctly reported when the host platform window is hovered but not focused. If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetWindowFocusCallback() callback and forward it to the backend via ImGui_ImplGlfw_CursorEnterCallback(). // 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). // 2020-01-17: Inputs: Disable error callback while assigning mouse cursors because some X11 setup don't have them and it generates errors. // 2019-12-05: Inputs: Added support for new mouse cursors added in GLFW 3.4+ (resizing cursors, not allowed cursor). // 2019-10-18: Misc: Previously installed user callbacks are now restored on shutdown. // 2019-07-21: Inputs: Added mapping for ImGuiKey_KeyPadEnter. // 2019-05-11: Inputs: Don't filter value from character callback before calling AddInputCharacter(). // 2019-03-12: Misc: Preserve DisplayFramebufferScale when main window is minimized. // 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window. // 2018-11-07: Inputs: When installing our GLFW callbacks, we save user's previously installed ones - if any - and chain call them. // 2018-08-01: Inputs: Workaround for Emscripten which doesn't seem to handle focus related calls. // 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor. // 2018-06-08: Misc: Extracted imgui_impl_glfw.cpp/.h away from the old combined GLFW+OpenGL/Vulkan examples. // 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors flag + honor ImGuiConfigFlags_NoMouseCursorChange flag. // 2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value, passed to glfwSetCursor()). // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. // 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. // 2018-01-25: Inputs: Added gamepad support if ImGuiConfigFlags_NavEnableGamepad is set. // 2018-01-25: Inputs: Honoring the io.WantSetMousePos by repositioning the mouse (when using navigation and ImGuiConfigFlags_NavMoveMouse is set). // 2018-01-20: Inputs: Added Horizontal Mouse Wheel support. // 2018-01-18: Inputs: Added mapping for ImGuiKey_Insert. // 2017-08-25: Inputs: MousePos set to -FLT_MAX,-FLT_MAX when mouse is unavailable/missing (instead of -1,-1). // 2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers. #include "imgui.h" #include "imgui_impl_glfw.h" // Clang warnings with -Weverything #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast #pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness #endif // GLFW #include #ifdef _WIN32 #undef APIENTRY #define GLFW_EXPOSE_NATIVE_WIN32 #include // for glfwGetWin32Window() #endif #ifdef __APPLE__ #define GLFW_EXPOSE_NATIVE_COCOA #include // for glfwGetCocoaWindow() #endif // We gather version tests as define in order to easily see which features are version-dependent. #define GLFW_VERSION_COMBINED (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 + GLFW_VERSION_REVISION) #define GLFW_HAS_WINDOW_TOPMOST (GLFW_VERSION_COMBINED >= 3200) // 3.2+ GLFW_FLOATING #define GLFW_HAS_WINDOW_HOVERED (GLFW_VERSION_COMBINED >= 3300) // 3.3+ GLFW_HOVERED #define GLFW_HAS_WINDOW_ALPHA (GLFW_VERSION_COMBINED >= 3300) // 3.3+ glfwSetWindowOpacity #define GLFW_HAS_PER_MONITOR_DPI (GLFW_VERSION_COMBINED >= 3300) // 3.3+ glfwGetMonitorContentScale #define GLFW_HAS_VULKAN (GLFW_VERSION_COMBINED >= 3200) // 3.2+ glfwCreateWindowSurface #define GLFW_HAS_FOCUS_WINDOW (GLFW_VERSION_COMBINED >= 3200) // 3.2+ glfwFocusWindow #define GLFW_HAS_FOCUS_ON_SHOW (GLFW_VERSION_COMBINED >= 3300) // 3.3+ GLFW_FOCUS_ON_SHOW #define GLFW_HAS_MONITOR_WORK_AREA (GLFW_VERSION_COMBINED >= 3300) // 3.3+ glfwGetMonitorWorkarea #define GLFW_HAS_OSX_WINDOW_POS_FIX (GLFW_VERSION_COMBINED >= 3301) // 3.3.1+ Fixed: Resizing window repositions it on MacOS #1553 #ifdef GLFW_RESIZE_NESW_CURSOR // Let's be nice to people who pulled GLFW between 2019-04-16 (3.4 define) and 2019-11-29 (cursors defines) // FIXME: Remove when GLFW 3.4 is released? #define GLFW_HAS_NEW_CURSORS (GLFW_VERSION_COMBINED >= 3400) // 3.4+ GLFW_RESIZE_ALL_CURSOR, GLFW_RESIZE_NESW_CURSOR, GLFW_RESIZE_NWSE_CURSOR, GLFW_NOT_ALLOWED_CURSOR #else #define GLFW_HAS_NEW_CURSORS (0) #endif #ifdef GLFW_MOUSE_PASSTHROUGH // Let's be nice to people who pulled GLFW between 2019-04-16 (3.4 define) and 2020-07-17 (passthrough) #define GLFW_HAS_MOUSE_PASSTHROUGH (GLFW_VERSION_COMBINED >= 3400) // 3.4+ GLFW_MOUSE_PASSTHROUGH #else #define GLFW_HAS_MOUSE_PASSTHROUGH (0) #endif #define GLFW_HAS_GAMEPAD_API (GLFW_VERSION_COMBINED >= 3300) // 3.3+ glfwGetGamepadState() new api #define GLFW_HAS_GETKEYNAME (GLFW_VERSION_COMBINED >= 3200) // 3.2+ glfwGetKeyName() // GLFW data enum GlfwClientApi { GlfwClientApi_Unknown, GlfwClientApi_OpenGL, GlfwClientApi_Vulkan }; struct ImGui_ImplGlfw_Data { GLFWwindow* Window; GlfwClientApi ClientApi; double Time; GLFWwindow* MouseWindow; GLFWcursor* MouseCursors[ImGuiMouseCursor_COUNT]; ImVec2 LastValidMousePos; GLFWwindow* KeyOwnerWindows[GLFW_KEY_LAST]; bool InstalledCallbacks; bool WantUpdateMonitors; #ifdef _WIN32 WNDPROC GlfwWndProc; #endif // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. GLFWwindowfocusfun PrevUserCallbackWindowFocus; GLFWcursorposfun PrevUserCallbackCursorPos; GLFWcursorenterfun PrevUserCallbackCursorEnter; GLFWmousebuttonfun PrevUserCallbackMousebutton; GLFWscrollfun PrevUserCallbackScroll; GLFWkeyfun PrevUserCallbackKey; GLFWcharfun PrevUserCallbackChar; GLFWmonitorfun PrevUserCallbackMonitor; ImGui_ImplGlfw_Data() { memset((void*)this, 0, sizeof(*this)); } }; // Backend data stored in io.BackendPlatformUserData to allow support for multiple Dear ImGui contexts // It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts. // FIXME: multi-context support is not well tested and probably dysfunctional in this backend. // - Because glfwPollEvents() process all windows and some events may be called outside of it, you will need to register your own callbacks // (passing install_callbacks=false in ImGui_ImplGlfw_InitXXX functions), set the current dear imgui context and then call our callbacks. // - Otherwise we may need to store a GLFWWindow* -> ImGuiContext* map and handle this in the backend, adding a little bit of extra complexity to it. // FIXME: some shared resources (mouse cursor shape, gamepad) are mishandled when using multi-context. static ImGui_ImplGlfw_Data* ImGui_ImplGlfw_GetBackendData() { return ImGui::GetCurrentContext() ? (ImGui_ImplGlfw_Data*)ImGui::GetIO().BackendPlatformUserData : nullptr; } // Forward Declarations static void ImGui_ImplGlfw_UpdateMonitors(); static void ImGui_ImplGlfw_InitPlatformInterface(); static void ImGui_ImplGlfw_ShutdownPlatformInterface(); // Functions static const char* ImGui_ImplGlfw_GetClipboardText(void* user_data) { return glfwGetClipboardString((GLFWwindow*)user_data); } static void ImGui_ImplGlfw_SetClipboardText(void* user_data, const char* text) { glfwSetClipboardString((GLFWwindow*)user_data, text); } static ImGuiKey ImGui_ImplGlfw_KeyToImGuiKey(int key) { switch (key) { case GLFW_KEY_TAB: return ImGuiKey_Tab; case GLFW_KEY_LEFT: return ImGuiKey_LeftArrow; case GLFW_KEY_RIGHT: return ImGuiKey_RightArrow; case GLFW_KEY_UP: return ImGuiKey_UpArrow; case GLFW_KEY_DOWN: return ImGuiKey_DownArrow; case GLFW_KEY_PAGE_UP: return ImGuiKey_PageUp; case GLFW_KEY_PAGE_DOWN: return ImGuiKey_PageDown; case GLFW_KEY_HOME: return ImGuiKey_Home; case GLFW_KEY_END: return ImGuiKey_End; case GLFW_KEY_INSERT: return ImGuiKey_Insert; case GLFW_KEY_DELETE: return ImGuiKey_Delete; case GLFW_KEY_BACKSPACE: return ImGuiKey_Backspace; case GLFW_KEY_SPACE: return ImGuiKey_Space; case GLFW_KEY_ENTER: return ImGuiKey_Enter; case GLFW_KEY_ESCAPE: return ImGuiKey_Escape; case GLFW_KEY_APOSTROPHE: return ImGuiKey_Apostrophe; case GLFW_KEY_COMMA: return ImGuiKey_Comma; case GLFW_KEY_MINUS: return ImGuiKey_Minus; case GLFW_KEY_PERIOD: return ImGuiKey_Period; case GLFW_KEY_SLASH: return ImGuiKey_Slash; case GLFW_KEY_SEMICOLON: return ImGuiKey_Semicolon; case GLFW_KEY_EQUAL: return ImGuiKey_Equal; case GLFW_KEY_LEFT_BRACKET: return ImGuiKey_LeftBracket; case GLFW_KEY_BACKSLASH: return ImGuiKey_Backslash; case GLFW_KEY_RIGHT_BRACKET: return ImGuiKey_RightBracket; case GLFW_KEY_GRAVE_ACCENT: return ImGuiKey_GraveAccent; case GLFW_KEY_CAPS_LOCK: return ImGuiKey_CapsLock; case GLFW_KEY_SCROLL_LOCK: return ImGuiKey_ScrollLock; case GLFW_KEY_NUM_LOCK: return ImGuiKey_NumLock; case GLFW_KEY_PRINT_SCREEN: return ImGuiKey_PrintScreen; case GLFW_KEY_PAUSE: return ImGuiKey_Pause; case GLFW_KEY_KP_0: return ImGuiKey_Keypad0; case GLFW_KEY_KP_1: return ImGuiKey_Keypad1; case GLFW_KEY_KP_2: return ImGuiKey_Keypad2; case GLFW_KEY_KP_3: return ImGuiKey_Keypad3; case GLFW_KEY_KP_4: return ImGuiKey_Keypad4; case GLFW_KEY_KP_5: return ImGuiKey_Keypad5; case GLFW_KEY_KP_6: return ImGuiKey_Keypad6; case GLFW_KEY_KP_7: return ImGuiKey_Keypad7; case GLFW_KEY_KP_8: return ImGuiKey_Keypad8; case GLFW_KEY_KP_9: return ImGuiKey_Keypad9; case GLFW_KEY_KP_DECIMAL: return ImGuiKey_KeypadDecimal; case GLFW_KEY_KP_DIVIDE: return ImGuiKey_KeypadDivide; case GLFW_KEY_KP_MULTIPLY: return ImGuiKey_KeypadMultiply; case GLFW_KEY_KP_SUBTRACT: return ImGuiKey_KeypadSubtract; case GLFW_KEY_KP_ADD: return ImGuiKey_KeypadAdd; case GLFW_KEY_KP_ENTER: return ImGuiKey_KeypadEnter; case GLFW_KEY_KP_EQUAL: return ImGuiKey_KeypadEqual; case GLFW_KEY_LEFT_SHIFT: return ImGuiKey_LeftShift; case GLFW_KEY_LEFT_CONTROL: return ImGuiKey_LeftCtrl; case GLFW_KEY_LEFT_ALT: return ImGuiKey_LeftAlt; case GLFW_KEY_LEFT_SUPER: return ImGuiKey_LeftSuper; case GLFW_KEY_RIGHT_SHIFT: return ImGuiKey_RightShift; case GLFW_KEY_RIGHT_CONTROL: return ImGuiKey_RightCtrl; case GLFW_KEY_RIGHT_ALT: return ImGuiKey_RightAlt; case GLFW_KEY_RIGHT_SUPER: return ImGuiKey_RightSuper; case GLFW_KEY_MENU: return ImGuiKey_Menu; case GLFW_KEY_0: return ImGuiKey_0; case GLFW_KEY_1: return ImGuiKey_1; case GLFW_KEY_2: return ImGuiKey_2; case GLFW_KEY_3: return ImGuiKey_3; case GLFW_KEY_4: return ImGuiKey_4; case GLFW_KEY_5: return ImGuiKey_5; case GLFW_KEY_6: return ImGuiKey_6; case GLFW_KEY_7: return ImGuiKey_7; case GLFW_KEY_8: return ImGuiKey_8; case GLFW_KEY_9: return ImGuiKey_9; case GLFW_KEY_A: return ImGuiKey_A; case GLFW_KEY_B: return ImGuiKey_B; case GLFW_KEY_C: return ImGuiKey_C; case GLFW_KEY_D: return ImGuiKey_D; case GLFW_KEY_E: return ImGuiKey_E; case GLFW_KEY_F: return ImGuiKey_F; case GLFW_KEY_G: return ImGuiKey_G; case GLFW_KEY_H: return ImGuiKey_H; case GLFW_KEY_I: return ImGuiKey_I; case GLFW_KEY_J: return ImGuiKey_J; case GLFW_KEY_K: return ImGuiKey_K; case GLFW_KEY_L: return ImGuiKey_L; case GLFW_KEY_M: return ImGuiKey_M; case GLFW_KEY_N: return ImGuiKey_N; case GLFW_KEY_O: return ImGuiKey_O; case GLFW_KEY_P: return ImGuiKey_P; case GLFW_KEY_Q: return ImGuiKey_Q; case GLFW_KEY_R: return ImGuiKey_R; case GLFW_KEY_S: return ImGuiKey_S; case GLFW_KEY_T: return ImGuiKey_T; case GLFW_KEY_U: return ImGuiKey_U; case GLFW_KEY_V: return ImGuiKey_V; case GLFW_KEY_W: return ImGuiKey_W; case GLFW_KEY_X: return ImGuiKey_X; case GLFW_KEY_Y: return ImGuiKey_Y; case GLFW_KEY_Z: return ImGuiKey_Z; case GLFW_KEY_F1: return ImGuiKey_F1; case GLFW_KEY_F2: return ImGuiKey_F2; case GLFW_KEY_F3: return ImGuiKey_F3; case GLFW_KEY_F4: return ImGuiKey_F4; case GLFW_KEY_F5: return ImGuiKey_F5; case GLFW_KEY_F6: return ImGuiKey_F6; case GLFW_KEY_F7: return ImGuiKey_F7; case GLFW_KEY_F8: return ImGuiKey_F8; case GLFW_KEY_F9: return ImGuiKey_F9; case GLFW_KEY_F10: return ImGuiKey_F10; case GLFW_KEY_F11: return ImGuiKey_F11; case GLFW_KEY_F12: return ImGuiKey_F12; default: return ImGuiKey_None; } } static int ImGui_ImplGlfw_KeyToModifier(int key) { if (key == GLFW_KEY_LEFT_CONTROL || key == GLFW_KEY_RIGHT_CONTROL) return GLFW_MOD_CONTROL; if (key == GLFW_KEY_LEFT_SHIFT || key == GLFW_KEY_RIGHT_SHIFT) return GLFW_MOD_SHIFT; if (key == GLFW_KEY_LEFT_ALT || key == GLFW_KEY_RIGHT_ALT) return GLFW_MOD_ALT; if (key == GLFW_KEY_LEFT_SUPER || key == GLFW_KEY_RIGHT_SUPER) return GLFW_MOD_SUPER; return 0; } static void ImGui_ImplGlfw_UpdateKeyModifiers(int mods) { ImGuiIO& io = ImGui::GetIO(); io.AddKeyEvent(ImGuiMod_Ctrl, (mods & GLFW_MOD_CONTROL) != 0); io.AddKeyEvent(ImGuiMod_Shift, (mods & GLFW_MOD_SHIFT) != 0); io.AddKeyEvent(ImGuiMod_Alt, (mods & GLFW_MOD_ALT) != 0); io.AddKeyEvent(ImGuiMod_Super, (mods & GLFW_MOD_SUPER) != 0); } void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods) { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); if (bd->PrevUserCallbackMousebutton != nullptr && window == bd->Window) bd->PrevUserCallbackMousebutton(window, button, action, mods); ImGui_ImplGlfw_UpdateKeyModifiers(mods); ImGuiIO& io = ImGui::GetIO(); if (button >= 0 && button < ImGuiMouseButton_COUNT) io.AddMouseButtonEvent(button, action == GLFW_PRESS); } void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset) { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); if (bd->PrevUserCallbackScroll != nullptr && window == bd->Window) bd->PrevUserCallbackScroll(window, xoffset, yoffset); ImGuiIO& io = ImGui::GetIO(); io.AddMouseWheelEvent((float)xoffset, (float)yoffset); } static int ImGui_ImplGlfw_TranslateUntranslatedKey(int key, int scancode) { #if GLFW_HAS_GETKEYNAME && !defined(__EMSCRIPTEN__) // GLFW 3.1+ attempts to "untranslate" keys, which goes the opposite of what every other framework does, making using lettered shortcuts difficult. // (It had reasons to do so: namely GLFW is/was more likely to be used for WASD-type game controls rather than lettered shortcuts, but IHMO the 3.1 change could have been done differently) // See https://github.com/glfw/glfw/issues/1502 for details. // Adding a workaround to undo this (so our keys are translated->untranslated->translated, likely a lossy process). // This won't cover edge cases but this is at least going to cover common cases. if (key >= GLFW_KEY_KP_0 && key <= GLFW_KEY_KP_EQUAL) return key; GLFWerrorfun prev_error_callback = glfwSetErrorCallback(nullptr); const char* key_name = glfwGetKeyName(key, scancode); glfwSetErrorCallback(prev_error_callback); #if (GLFW_VERSION_COMBINED >= 3300) // Eat errors (see #5908) (void)glfwGetError(NULL); #endif if (key_name && key_name[0] != 0 && key_name[1] == 0) { const char char_names[] = "`-=[]\\,;\'./"; const int char_keys[] = { GLFW_KEY_GRAVE_ACCENT, GLFW_KEY_MINUS, GLFW_KEY_EQUAL, GLFW_KEY_LEFT_BRACKET, GLFW_KEY_RIGHT_BRACKET, GLFW_KEY_BACKSLASH, GLFW_KEY_COMMA, GLFW_KEY_SEMICOLON, GLFW_KEY_APOSTROPHE, GLFW_KEY_PERIOD, GLFW_KEY_SLASH, 0 }; IM_ASSERT(IM_ARRAYSIZE(char_names) == IM_ARRAYSIZE(char_keys)); if (key_name[0] >= '0' && key_name[0] <= '9') { key = GLFW_KEY_0 + (key_name[0] - '0'); } else if (key_name[0] >= 'A' && key_name[0] <= 'Z') { key = GLFW_KEY_A + (key_name[0] - 'A'); } else if (key_name[0] >= 'a' && key_name[0] <= 'z') { key = GLFW_KEY_A + (key_name[0] - 'a'); } else if (const char* p = strchr(char_names, key_name[0])) { key = char_keys[p - char_names]; } } // if (action == GLFW_PRESS) printf("key %d scancode %d name '%s'\n", key, scancode, key_name); #else IM_UNUSED(scancode); #endif return key; } void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int keycode, int scancode, int action, int mods) { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); if (bd->PrevUserCallbackKey != nullptr && window == bd->Window) bd->PrevUserCallbackKey(window, keycode, scancode, action, mods); if (action != GLFW_PRESS && action != GLFW_RELEASE) return; // Workaround: X11 does not include current pressed/released modifier key in 'mods' flags. https://github.com/glfw/glfw/issues/1630 if (int keycode_to_mod = ImGui_ImplGlfw_KeyToModifier(keycode)) mods = (action == GLFW_PRESS) ? (mods | keycode_to_mod) : (mods & ~keycode_to_mod); ImGui_ImplGlfw_UpdateKeyModifiers(mods); if (keycode >= 0 && keycode < IM_ARRAYSIZE(bd->KeyOwnerWindows)) bd->KeyOwnerWindows[keycode] = (action == GLFW_PRESS) ? window : nullptr; keycode = ImGui_ImplGlfw_TranslateUntranslatedKey(keycode, scancode); ImGuiIO& io = ImGui::GetIO(); ImGuiKey imgui_key = ImGui_ImplGlfw_KeyToImGuiKey(keycode); io.AddKeyEvent(imgui_key, (action == GLFW_PRESS)); io.SetKeyEventNativeData(imgui_key, keycode, scancode); // To support legacy indexing (<1.87 user code) } void ImGui_ImplGlfw_WindowFocusCallback(GLFWwindow* window, int focused) { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); if (bd->PrevUserCallbackWindowFocus != nullptr && window == bd->Window) bd->PrevUserCallbackWindowFocus(window, focused); ImGuiIO& io = ImGui::GetIO(); io.AddFocusEvent(focused != 0); } void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y) { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); if (bd->PrevUserCallbackCursorPos != nullptr && window == bd->Window) bd->PrevUserCallbackCursorPos(window, x, y); if (glfwGetInputMode(window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED) return; ImGuiIO& io = ImGui::GetIO(); if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { int window_x, window_y; glfwGetWindowPos(window, &window_x, &window_y); x += window_x; y += window_y; } io.AddMousePosEvent((float)x, (float)y); bd->LastValidMousePos = ImVec2((float)x, (float)y); } // Workaround: X11 seems to send spurious Leave/Enter events which would make us lose our position, // so we back it up and restore on Leave/Enter (see https://github.com/ocornut/imgui/issues/4984) void ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered) { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); if (bd->PrevUserCallbackCursorEnter != nullptr && window == bd->Window) bd->PrevUserCallbackCursorEnter(window, entered); if (glfwGetInputMode(window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED) return; ImGuiIO& io = ImGui::GetIO(); if (entered) { bd->MouseWindow = window; io.AddMousePosEvent(bd->LastValidMousePos.x, bd->LastValidMousePos.y); } else if (!entered && bd->MouseWindow == window) { bd->LastValidMousePos = io.MousePos; bd->MouseWindow = nullptr; io.AddMousePosEvent(-FLT_MAX, -FLT_MAX); } } void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c) { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); if (bd->PrevUserCallbackChar != nullptr && window == bd->Window) bd->PrevUserCallbackChar(window, c); ImGuiIO& io = ImGui::GetIO(); io.AddInputCharacter(c); } void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor*, int) { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); bd->WantUpdateMonitors = true; } void ImGui_ImplGlfw_InstallCallbacks(GLFWwindow* window) { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); IM_ASSERT(bd->InstalledCallbacks == false && "Callbacks already installed!"); IM_ASSERT(bd->Window == window); bd->PrevUserCallbackWindowFocus = glfwSetWindowFocusCallback(window, ImGui_ImplGlfw_WindowFocusCallback); bd->PrevUserCallbackCursorEnter = glfwSetCursorEnterCallback(window, ImGui_ImplGlfw_CursorEnterCallback); bd->PrevUserCallbackCursorPos = glfwSetCursorPosCallback(window, ImGui_ImplGlfw_CursorPosCallback); bd->PrevUserCallbackMousebutton = glfwSetMouseButtonCallback(window, ImGui_ImplGlfw_MouseButtonCallback); bd->PrevUserCallbackScroll = glfwSetScrollCallback(window, ImGui_ImplGlfw_ScrollCallback); bd->PrevUserCallbackKey = glfwSetKeyCallback(window, ImGui_ImplGlfw_KeyCallback); bd->PrevUserCallbackChar = glfwSetCharCallback(window, ImGui_ImplGlfw_CharCallback); bd->PrevUserCallbackMonitor = glfwSetMonitorCallback(ImGui_ImplGlfw_MonitorCallback); bd->InstalledCallbacks = true; } void ImGui_ImplGlfw_RestoreCallbacks(GLFWwindow* window) { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); IM_ASSERT(bd->InstalledCallbacks == true && "Callbacks not installed!"); IM_ASSERT(bd->Window == window); glfwSetWindowFocusCallback(window, bd->PrevUserCallbackWindowFocus); glfwSetCursorEnterCallback(window, bd->PrevUserCallbackCursorEnter); glfwSetCursorPosCallback(window, bd->PrevUserCallbackCursorPos); glfwSetMouseButtonCallback(window, bd->PrevUserCallbackMousebutton); glfwSetScrollCallback(window, bd->PrevUserCallbackScroll); glfwSetKeyCallback(window, bd->PrevUserCallbackKey); glfwSetCharCallback(window, bd->PrevUserCallbackChar); glfwSetMonitorCallback(bd->PrevUserCallbackMonitor); bd->InstalledCallbacks = false; bd->PrevUserCallbackWindowFocus = nullptr; bd->PrevUserCallbackCursorEnter = nullptr; bd->PrevUserCallbackCursorPos = nullptr; bd->PrevUserCallbackMousebutton = nullptr; bd->PrevUserCallbackScroll = nullptr; bd->PrevUserCallbackKey = nullptr; bd->PrevUserCallbackChar = nullptr; bd->PrevUserCallbackMonitor = nullptr; } static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, GlfwClientApi client_api) { ImGuiIO& io = ImGui::GetIO(); IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!"); //printf("GLFW_VERSION: %d.%d.%d (%d)", GLFW_VERSION_MAJOR, GLFW_VERSION_MINOR, GLFW_VERSION_REVISION, GLFW_VERSION_COMBINED); // Setup backend capabilities flags ImGui_ImplGlfw_Data* bd = IM_NEW(ImGui_ImplGlfw_Data)(); io.BackendPlatformUserData = (void*)bd; io.BackendPlatformName = "imgui_impl_glfw"; io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) #if GLFW_HAS_MOUSE_PASSTHROUGH || (GLFW_HAS_WINDOW_HOVERED && defined(_WIN32)) io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can call io.AddMouseViewportEvent() with correct data (optional) #endif bd->Window = window; bd->Time = 0.0; bd->WantUpdateMonitors = true; io.SetClipboardTextFn = ImGui_ImplGlfw_SetClipboardText; io.GetClipboardTextFn = ImGui_ImplGlfw_GetClipboardText; io.ClipboardUserData = bd->Window; // Create mouse cursors // (By design, on X11 cursors are user configurable and some cursors may be missing. When a cursor doesn't exist, // GLFW will emit an error which will often be printed by the app, so we temporarily disable error reporting. // Missing cursors will return nullptr and our _UpdateMouseCursor() function will use the Arrow cursor instead.) GLFWerrorfun prev_error_callback = glfwSetErrorCallback(nullptr); bd->MouseCursors[ImGuiMouseCursor_Arrow] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); bd->MouseCursors[ImGuiMouseCursor_TextInput] = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR); bd->MouseCursors[ImGuiMouseCursor_ResizeNS] = glfwCreateStandardCursor(GLFW_VRESIZE_CURSOR); bd->MouseCursors[ImGuiMouseCursor_ResizeEW] = glfwCreateStandardCursor(GLFW_HRESIZE_CURSOR); bd->MouseCursors[ImGuiMouseCursor_Hand] = glfwCreateStandardCursor(GLFW_HAND_CURSOR); #if GLFW_HAS_NEW_CURSORS bd->MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_RESIZE_ALL_CURSOR); bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_RESIZE_NESW_CURSOR); bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_RESIZE_NWSE_CURSOR); bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_NOT_ALLOWED_CURSOR); #else bd->MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); #endif glfwSetErrorCallback(prev_error_callback); #if (GLFW_VERSION_COMBINED >= 3300) // Eat errors (see #5785) (void)glfwGetError(NULL); #endif // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. if (install_callbacks) ImGui_ImplGlfw_InstallCallbacks(window); // Update monitors the first time (note: monitor callback are broken in GLFW 3.2 and earlier, see github.com/glfw/glfw/issues/784) ImGui_ImplGlfw_UpdateMonitors(); glfwSetMonitorCallback(ImGui_ImplGlfw_MonitorCallback); // Our mouse update function expect PlatformHandle to be filled for the main viewport ImGuiViewport* main_viewport = ImGui::GetMainViewport(); main_viewport->PlatformHandle = (void*)bd->Window; #ifdef _WIN32 main_viewport->PlatformHandleRaw = glfwGetWin32Window(bd->Window); #elif defined(__APPLE__) main_viewport->PlatformHandleRaw = (void*)glfwGetCocoaWindow(bd->Window); #endif if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) ImGui_ImplGlfw_InitPlatformInterface(); bd->ClientApi = client_api; return true; } bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks) { return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_OpenGL); } bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks) { return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_Vulkan); } bool ImGui_ImplGlfw_InitForOther(GLFWwindow* window, bool install_callbacks) { return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_Unknown); } void ImGui_ImplGlfw_Shutdown() { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?"); ImGuiIO& io = ImGui::GetIO(); ImGui_ImplGlfw_ShutdownPlatformInterface(); if (bd->InstalledCallbacks) ImGui_ImplGlfw_RestoreCallbacks(bd->Window); for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++) glfwDestroyCursor(bd->MouseCursors[cursor_n]); io.BackendPlatformName = nullptr; io.BackendPlatformUserData = nullptr; IM_DELETE(bd); } static void ImGui_ImplGlfw_UpdateMouseData() { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); ImGuiIO& io = ImGui::GetIO(); ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); if (glfwGetInputMode(bd->Window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED) { io.AddMousePosEvent(-FLT_MAX, -FLT_MAX); return; } ImGuiID mouse_viewport_id = 0; const ImVec2 mouse_pos_prev = io.MousePos; for (int n = 0; n < platform_io.Viewports.Size; n++) { ImGuiViewport* viewport = platform_io.Viewports[n]; GLFWwindow* window = (GLFWwindow*)viewport->PlatformHandle; #ifdef __EMSCRIPTEN__ const bool is_window_focused = true; #else const bool is_window_focused = glfwGetWindowAttrib(window, GLFW_FOCUSED) != 0; #endif if (is_window_focused) { // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) // When multi-viewports are enabled, all Dear ImGui positions are same as OS positions. if (io.WantSetMousePos) glfwSetCursorPos(window, (double)(mouse_pos_prev.x - viewport->Pos.x), (double)(mouse_pos_prev.y - viewport->Pos.y)); // (Optional) Fallback to provide mouse position when focused (ImGui_ImplGlfw_CursorPosCallback already provides this when hovered or captured) if (bd->MouseWindow == nullptr) { double mouse_x, mouse_y; glfwGetCursorPos(window, &mouse_x, &mouse_y); if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { // Single viewport mode: mouse position in client window coordinates (io.MousePos is (0,0) when the mouse is on the upper-left corner of the app window) // Multi-viewport mode: mouse position in OS absolute coordinates (io.MousePos is (0,0) when the mouse is on the upper-left of the primary monitor) int window_x, window_y; glfwGetWindowPos(window, &window_x, &window_y); mouse_x += window_x; mouse_y += window_y; } bd->LastValidMousePos = ImVec2((float)mouse_x, (float)mouse_y); io.AddMousePosEvent((float)mouse_x, (float)mouse_y); } } // (Optional) When using multiple viewports: call io.AddMouseViewportEvent() with the viewport the OS mouse cursor is hovering. // If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the backend, Dear imGui will ignore this field and infer the information using its flawed heuristic. // - [X] GLFW >= 3.3 backend ON WINDOWS ONLY does correctly ignore viewports with the _NoInputs flag. // - [!] GLFW <= 3.2 backend CANNOT correctly ignore viewports with the _NoInputs flag, and CANNOT reported Hovered Viewport because of mouse capture. // Some backend are not able to handle that correctly. If a backend report an hovered viewport that has the _NoInputs flag (e.g. when dragging a window // for docking, the viewport has the _NoInputs flag in order to allow us to find the viewport under), then Dear ImGui is forced to ignore the value reported // by the backend, and use its flawed heuristic to guess the viewport behind. // - [X] GLFW backend correctly reports this regardless of another viewport behind focused and dragged from (we need this to find a useful drag and drop target). // FIXME: This is currently only correct on Win32. See what we do below with the WM_NCHITTEST, missing an equivalent for other systems. // See https://github.com/glfw/glfw/issues/1236 if you want to help in making this a GLFW feature. #if GLFW_HAS_MOUSE_PASSTHROUGH || (GLFW_HAS_WINDOW_HOVERED && defined(_WIN32)) const bool window_no_input = (viewport->Flags & ImGuiViewportFlags_NoInputs) != 0; #if GLFW_HAS_MOUSE_PASSTHROUGH glfwSetWindowAttrib(window, GLFW_MOUSE_PASSTHROUGH, window_no_input); #endif if (glfwGetWindowAttrib(window, GLFW_HOVERED) && !window_no_input) mouse_viewport_id = viewport->ID; #else // We cannot use bd->MouseWindow maintained from CursorEnter/Leave callbacks, because it is locked to the window capturing mouse. #endif } if (io.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport) io.AddMouseViewportEvent(mouse_viewport_id); } static void ImGui_ImplGlfw_UpdateMouseCursor() { ImGuiIO& io = ImGui::GetIO(); ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); if ((io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) || glfwGetInputMode(bd->Window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED) return; ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); for (int n = 0; n < platform_io.Viewports.Size; n++) { GLFWwindow* window = (GLFWwindow*)platform_io.Viewports[n]->PlatformHandle; if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor) { // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); } else { // Show OS mouse cursor // FIXME-PLATFORM: Unfocused windows seems to fail changing the mouse cursor with GLFW 3.2, but 3.3 works here. glfwSetCursor(window, bd->MouseCursors[imgui_cursor] ? bd->MouseCursors[imgui_cursor] : bd->MouseCursors[ImGuiMouseCursor_Arrow]); glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); } } } // Update gamepad inputs static inline float Saturate(float v) { return v < 0.0f ? 0.0f : v > 1.0f ? 1.0f : v; } static void ImGui_ImplGlfw_UpdateGamepads() { ImGuiIO& io = ImGui::GetIO(); if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) // FIXME: Technically feeding gamepad shouldn't depend on this now that they are regular inputs. return; io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; #if GLFW_HAS_GAMEPAD_API GLFWgamepadstate gamepad; if (!glfwGetGamepadState(GLFW_JOYSTICK_1, &gamepad)) return; #define MAP_BUTTON(KEY_NO, BUTTON_NO, _UNUSED) do { io.AddKeyEvent(KEY_NO, gamepad.buttons[BUTTON_NO] != 0); } while (0) #define MAP_ANALOG(KEY_NO, AXIS_NO, _UNUSED, V0, V1) do { float v = gamepad.axes[AXIS_NO]; v = (v - V0) / (V1 - V0); io.AddKeyAnalogEvent(KEY_NO, v > 0.10f, Saturate(v)); } while (0) #else int axes_count = 0, buttons_count = 0; const float* axes = glfwGetJoystickAxes(GLFW_JOYSTICK_1, &axes_count); const unsigned char* buttons = glfwGetJoystickButtons(GLFW_JOYSTICK_1, &buttons_count); if (axes_count == 0 || buttons_count == 0) return; #define MAP_BUTTON(KEY_NO, _UNUSED, BUTTON_NO) do { io.AddKeyEvent(KEY_NO, (buttons_count > BUTTON_NO && buttons[BUTTON_NO] == GLFW_PRESS)); } while (0) #define MAP_ANALOG(KEY_NO, _UNUSED, AXIS_NO, V0, V1) do { float v = (axes_count > AXIS_NO) ? axes[AXIS_NO] : V0; v = (v - V0) / (V1 - V0); io.AddKeyAnalogEvent(KEY_NO, v > 0.10f, Saturate(v)); } while (0) #endif io.BackendFlags |= ImGuiBackendFlags_HasGamepad; MAP_BUTTON(ImGuiKey_GamepadStart, GLFW_GAMEPAD_BUTTON_START, 7); MAP_BUTTON(ImGuiKey_GamepadBack, GLFW_GAMEPAD_BUTTON_BACK, 6); MAP_BUTTON(ImGuiKey_GamepadFaceLeft, GLFW_GAMEPAD_BUTTON_X, 2); // Xbox X, PS Square MAP_BUTTON(ImGuiKey_GamepadFaceRight, GLFW_GAMEPAD_BUTTON_B, 1); // Xbox B, PS Circle MAP_BUTTON(ImGuiKey_GamepadFaceUp, GLFW_GAMEPAD_BUTTON_Y, 3); // Xbox Y, PS Triangle MAP_BUTTON(ImGuiKey_GamepadFaceDown, GLFW_GAMEPAD_BUTTON_A, 0); // Xbox A, PS Cross MAP_BUTTON(ImGuiKey_GamepadDpadLeft, GLFW_GAMEPAD_BUTTON_DPAD_LEFT, 13); MAP_BUTTON(ImGuiKey_GamepadDpadRight, GLFW_GAMEPAD_BUTTON_DPAD_RIGHT, 11); MAP_BUTTON(ImGuiKey_GamepadDpadUp, GLFW_GAMEPAD_BUTTON_DPAD_UP, 10); MAP_BUTTON(ImGuiKey_GamepadDpadDown, GLFW_GAMEPAD_BUTTON_DPAD_DOWN, 12); MAP_BUTTON(ImGuiKey_GamepadL1, GLFW_GAMEPAD_BUTTON_LEFT_BUMPER, 4); MAP_BUTTON(ImGuiKey_GamepadR1, GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER, 5); MAP_ANALOG(ImGuiKey_GamepadL2, GLFW_GAMEPAD_AXIS_LEFT_TRIGGER, 4, -0.75f, +1.0f); MAP_ANALOG(ImGuiKey_GamepadR2, GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER, 5, -0.75f, +1.0f); MAP_BUTTON(ImGuiKey_GamepadL3, GLFW_GAMEPAD_BUTTON_LEFT_THUMB, 8); MAP_BUTTON(ImGuiKey_GamepadR3, GLFW_GAMEPAD_BUTTON_RIGHT_THUMB, 9); MAP_ANALOG(ImGuiKey_GamepadLStickLeft, GLFW_GAMEPAD_AXIS_LEFT_X, 0, -0.25f, -1.0f); MAP_ANALOG(ImGuiKey_GamepadLStickRight, GLFW_GAMEPAD_AXIS_LEFT_X, 0, +0.25f, +1.0f); MAP_ANALOG(ImGuiKey_GamepadLStickUp, GLFW_GAMEPAD_AXIS_LEFT_Y, 1, -0.25f, -1.0f); MAP_ANALOG(ImGuiKey_GamepadLStickDown, GLFW_GAMEPAD_AXIS_LEFT_Y, 1, +0.25f, +1.0f); MAP_ANALOG(ImGuiKey_GamepadRStickLeft, GLFW_GAMEPAD_AXIS_RIGHT_X, 2, -0.25f, -1.0f); MAP_ANALOG(ImGuiKey_GamepadRStickRight, GLFW_GAMEPAD_AXIS_RIGHT_X, 2, +0.25f, +1.0f); MAP_ANALOG(ImGuiKey_GamepadRStickUp, GLFW_GAMEPAD_AXIS_RIGHT_Y, 3, -0.25f, -1.0f); MAP_ANALOG(ImGuiKey_GamepadRStickDown, GLFW_GAMEPAD_AXIS_RIGHT_Y, 3, +0.25f, +1.0f); #undef MAP_BUTTON #undef MAP_ANALOG } static void ImGui_ImplGlfw_UpdateMonitors() { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); int monitors_count = 0; GLFWmonitor** glfw_monitors = glfwGetMonitors(&monitors_count); platform_io.Monitors.resize(0); for (int n = 0; n < monitors_count; n++) { ImGuiPlatformMonitor monitor; int x, y; glfwGetMonitorPos(glfw_monitors[n], &x, &y); const GLFWvidmode* vid_mode = glfwGetVideoMode(glfw_monitors[n]); monitor.MainPos = monitor.WorkPos = ImVec2((float)x, (float)y); monitor.MainSize = monitor.WorkSize = ImVec2((float)vid_mode->width, (float)vid_mode->height); #if GLFW_HAS_MONITOR_WORK_AREA int w, h; glfwGetMonitorWorkarea(glfw_monitors[n], &x, &y, &w, &h); if (w > 0 && h > 0) // Workaround a small GLFW issue reporting zero on monitor changes: https://github.com/glfw/glfw/pull/1761 { monitor.WorkPos = ImVec2((float)x, (float)y); monitor.WorkSize = ImVec2((float)w, (float)h); } #endif #if GLFW_HAS_PER_MONITOR_DPI // Warning: the validity of monitor DPI information on Windows depends on the application DPI awareness settings, which generally needs to be set in the manifest or at runtime. float x_scale, y_scale; glfwGetMonitorContentScale(glfw_monitors[n], &x_scale, &y_scale); monitor.DpiScale = x_scale; #endif platform_io.Monitors.push_back(monitor); } bd->WantUpdateMonitors = false; } void ImGui_ImplGlfw_NewFrame() { ImGuiIO& io = ImGui::GetIO(); ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); IM_ASSERT(bd != nullptr && "Did you call ImGui_ImplGlfw_InitForXXX()?"); // Setup display size (every frame to accommodate for window resizing) int w, h; int display_w, display_h; glfwGetWindowSize(bd->Window, &w, &h); glfwGetFramebufferSize(bd->Window, &display_w, &display_h); io.DisplaySize = ImVec2((float)w, (float)h); if (w > 0 && h > 0) io.DisplayFramebufferScale = ImVec2((float)display_w / (float)w, (float)display_h / (float)h); if (bd->WantUpdateMonitors) ImGui_ImplGlfw_UpdateMonitors(); // Setup time step double current_time = glfwGetTime(); io.DeltaTime = bd->Time > 0.0 ? (float)(current_time - bd->Time) : (float)(1.0f / 60.0f); bd->Time = current_time; ImGui_ImplGlfw_UpdateMouseData(); ImGui_ImplGlfw_UpdateMouseCursor(); // Update game controllers (if enabled and available) ImGui_ImplGlfw_UpdateGamepads(); } //-------------------------------------------------------------------------------------------------------- // MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT // This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously. // If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. //-------------------------------------------------------------------------------------------------------- // Helper structure we store in the void* RenderUserData field of each ImGuiViewport to easily retrieve our backend data. struct ImGui_ImplGlfw_ViewportData { GLFWwindow* Window; bool WindowOwned; int IgnoreWindowPosEventFrame; int IgnoreWindowSizeEventFrame; ImGui_ImplGlfw_ViewportData() { Window = nullptr; WindowOwned = false; IgnoreWindowSizeEventFrame = IgnoreWindowPosEventFrame = -1; } ~ImGui_ImplGlfw_ViewportData() { IM_ASSERT(Window == nullptr); } }; static void ImGui_ImplGlfw_WindowCloseCallback(GLFWwindow* window) { if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle(window)) viewport->PlatformRequestClose = true; } // GLFW may dispatch window pos/size events after calling glfwSetWindowPos()/glfwSetWindowSize(). // However: depending on the platform the callback may be invoked at different time: // - on Windows it appears to be called within the glfwSetWindowPos()/glfwSetWindowSize() call // - on Linux it is queued and invoked during glfwPollEvents() // Because the event doesn't always fire on glfwSetWindowXXX() we use a frame counter tag to only // ignore recent glfwSetWindowXXX() calls. static void ImGui_ImplGlfw_WindowPosCallback(GLFWwindow* window, int, int) { if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle(window)) { if (ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData) { bool ignore_event = (ImGui::GetFrameCount() <= vd->IgnoreWindowPosEventFrame + 1); //data->IgnoreWindowPosEventFrame = -1; if (ignore_event) return; } viewport->PlatformRequestMove = true; } } static void ImGui_ImplGlfw_WindowSizeCallback(GLFWwindow* window, int, int) { if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle(window)) { if (ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData) { bool ignore_event = (ImGui::GetFrameCount() <= vd->IgnoreWindowSizeEventFrame + 1); //data->IgnoreWindowSizeEventFrame = -1; if (ignore_event) return; } viewport->PlatformRequestResize = true; } } static void ImGui_ImplGlfw_CreateWindow(ImGuiViewport* viewport) { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); ImGui_ImplGlfw_ViewportData* vd = IM_NEW(ImGui_ImplGlfw_ViewportData)(); viewport->PlatformUserData = vd; // GLFW 3.2 unfortunately always set focus on glfwCreateWindow() if GLFW_VISIBLE is set, regardless of GLFW_FOCUSED // With GLFW 3.3, the hint GLFW_FOCUS_ON_SHOW fixes this problem glfwWindowHint(GLFW_VISIBLE, false); glfwWindowHint(GLFW_FOCUSED, false); #if GLFW_HAS_FOCUS_ON_SHOW glfwWindowHint(GLFW_FOCUS_ON_SHOW, false); #endif glfwWindowHint(GLFW_DECORATED, (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? false : true); #if GLFW_HAS_WINDOW_TOPMOST glfwWindowHint(GLFW_FLOATING, (viewport->Flags & ImGuiViewportFlags_TopMost) ? true : false); #endif GLFWwindow* share_window = (bd->ClientApi == GlfwClientApi_OpenGL) ? bd->Window : nullptr; vd->Window = glfwCreateWindow((int)viewport->Size.x, (int)viewport->Size.y, "No Title Yet", nullptr, share_window); vd->WindowOwned = true; viewport->PlatformHandle = (void*)vd->Window; #ifdef _WIN32 viewport->PlatformHandleRaw = glfwGetWin32Window(vd->Window); #elif defined(__APPLE__) viewport->PlatformHandleRaw = (void*)glfwGetCocoaWindow(vd->Window); #endif glfwSetWindowPos(vd->Window, (int)viewport->Pos.x, (int)viewport->Pos.y); // Install GLFW callbacks for secondary viewports glfwSetWindowFocusCallback(vd->Window, ImGui_ImplGlfw_WindowFocusCallback); glfwSetCursorEnterCallback(vd->Window, ImGui_ImplGlfw_CursorEnterCallback); glfwSetCursorPosCallback(vd->Window, ImGui_ImplGlfw_CursorPosCallback); glfwSetMouseButtonCallback(vd->Window, ImGui_ImplGlfw_MouseButtonCallback); glfwSetScrollCallback(vd->Window, ImGui_ImplGlfw_ScrollCallback); glfwSetKeyCallback(vd->Window, ImGui_ImplGlfw_KeyCallback); glfwSetCharCallback(vd->Window, ImGui_ImplGlfw_CharCallback); glfwSetWindowCloseCallback(vd->Window, ImGui_ImplGlfw_WindowCloseCallback); glfwSetWindowPosCallback(vd->Window, ImGui_ImplGlfw_WindowPosCallback); glfwSetWindowSizeCallback(vd->Window, ImGui_ImplGlfw_WindowSizeCallback); if (bd->ClientApi == GlfwClientApi_OpenGL) { glfwMakeContextCurrent(vd->Window); glfwSwapInterval(0); } } static void ImGui_ImplGlfw_DestroyWindow(ImGuiViewport* viewport) { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); if (ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData) { if (vd->WindowOwned) { #if !GLFW_HAS_MOUSE_PASSTHROUGH && GLFW_HAS_WINDOW_HOVERED && defined(_WIN32) HWND hwnd = (HWND)viewport->PlatformHandleRaw; ::RemovePropA(hwnd, "IMGUI_VIEWPORT"); #endif // Release any keys that were pressed in the window being destroyed and are still held down, // because we will not receive any release events after window is destroyed. for (int i = 0; i < IM_ARRAYSIZE(bd->KeyOwnerWindows); i++) if (bd->KeyOwnerWindows[i] == vd->Window) ImGui_ImplGlfw_KeyCallback(vd->Window, i, 0, GLFW_RELEASE, 0); // Later params are only used for main viewport, on which this function is never called. glfwDestroyWindow(vd->Window); } vd->Window = nullptr; IM_DELETE(vd); } viewport->PlatformUserData = viewport->PlatformHandle = nullptr; } // We have submitted https://github.com/glfw/glfw/pull/1568 to allow GLFW to support "transparent inputs". // In the meanwhile we implement custom per-platform workarounds here (FIXME-VIEWPORT: Implement same work-around for Linux/OSX!) #if !GLFW_HAS_MOUSE_PASSTHROUGH && GLFW_HAS_WINDOW_HOVERED && defined(_WIN32) static LRESULT CALLBACK WndProcNoInputs(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); if (msg == WM_NCHITTEST) { // Let mouse pass-through the window. This will allow the backend to call io.AddMouseViewportEvent() properly (which is OPTIONAL). // The ImGuiViewportFlags_NoInputs flag is set while dragging a viewport, as want to detect the window behind the one we are dragging. // If you cannot easily access those viewport flags from your windowing/event code: you may manually synchronize its state e.g. in // your main loop after calling UpdatePlatformWindows(). Iterate all viewports/platform windows and pass the flag to your windowing system. ImGuiViewport* viewport = (ImGuiViewport*)::GetPropA(hWnd, "IMGUI_VIEWPORT"); if (viewport->Flags & ImGuiViewportFlags_NoInputs) return HTTRANSPARENT; } return ::CallWindowProc(bd->GlfwWndProc, hWnd, msg, wParam, lParam); } #endif static void ImGui_ImplGlfw_ShowWindow(ImGuiViewport* viewport) { ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; #if defined(_WIN32) // GLFW hack: Hide icon from task bar HWND hwnd = (HWND)viewport->PlatformHandleRaw; if (viewport->Flags & ImGuiViewportFlags_NoTaskBarIcon) { LONG ex_style = ::GetWindowLong(hwnd, GWL_EXSTYLE); ex_style &= ~WS_EX_APPWINDOW; ex_style |= WS_EX_TOOLWINDOW; ::SetWindowLong(hwnd, GWL_EXSTYLE, ex_style); } // GLFW hack: install hook for WM_NCHITTEST message handler #if !GLFW_HAS_MOUSE_PASSTHROUGH && GLFW_HAS_WINDOW_HOVERED && defined(_WIN32) ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); ::SetPropA(hwnd, "IMGUI_VIEWPORT", viewport); if (bd->GlfwWndProc == nullptr) bd->GlfwWndProc = (WNDPROC)::GetWindowLongPtr(hwnd, GWLP_WNDPROC); ::SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)WndProcNoInputs); #endif #if !GLFW_HAS_FOCUS_ON_SHOW // GLFW hack: GLFW 3.2 has a bug where glfwShowWindow() also activates/focus the window. // The fix was pushed to GLFW repository on 2018/01/09 and should be included in GLFW 3.3 via a GLFW_FOCUS_ON_SHOW window attribute. // See https://github.com/glfw/glfw/issues/1189 // FIXME-VIEWPORT: Implement same work-around for Linux/OSX in the meanwhile. if (viewport->Flags & ImGuiViewportFlags_NoFocusOnAppearing) { ::ShowWindow(hwnd, SW_SHOWNA); return; } #endif #endif glfwShowWindow(vd->Window); } static ImVec2 ImGui_ImplGlfw_GetWindowPos(ImGuiViewport* viewport) { ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; int x = 0, y = 0; glfwGetWindowPos(vd->Window, &x, &y); return ImVec2((float)x, (float)y); } static void ImGui_ImplGlfw_SetWindowPos(ImGuiViewport* viewport, ImVec2 pos) { ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; vd->IgnoreWindowPosEventFrame = ImGui::GetFrameCount(); glfwSetWindowPos(vd->Window, (int)pos.x, (int)pos.y); } static ImVec2 ImGui_ImplGlfw_GetWindowSize(ImGuiViewport* viewport) { ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; int w = 0, h = 0; glfwGetWindowSize(vd->Window, &w, &h); return ImVec2((float)w, (float)h); } static void ImGui_ImplGlfw_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) { ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; #if __APPLE__ && !GLFW_HAS_OSX_WINDOW_POS_FIX // Native OS windows are positioned from the bottom-left corner on macOS, whereas on other platforms they are // positioned from the upper-left corner. GLFW makes an effort to convert macOS style coordinates, however it // doesn't handle it when changing size. We are manually moving the window in order for changes of size to be based // on the upper-left corner. int x, y, width, height; glfwGetWindowPos(vd->Window, &x, &y); glfwGetWindowSize(vd->Window, &width, &height); glfwSetWindowPos(vd->Window, x, y - height + size.y); #endif vd->IgnoreWindowSizeEventFrame = ImGui::GetFrameCount(); glfwSetWindowSize(vd->Window, (int)size.x, (int)size.y); } static void ImGui_ImplGlfw_SetWindowTitle(ImGuiViewport* viewport, const char* title) { ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; glfwSetWindowTitle(vd->Window, title); } static void ImGui_ImplGlfw_SetWindowFocus(ImGuiViewport* viewport) { #if GLFW_HAS_FOCUS_WINDOW ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; glfwFocusWindow(vd->Window); #else // FIXME: What are the effect of not having this function? At the moment imgui doesn't actually call SetWindowFocus - we set that up ahead, will answer that question later. (void)viewport; #endif } static bool ImGui_ImplGlfw_GetWindowFocus(ImGuiViewport* viewport) { ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; return glfwGetWindowAttrib(vd->Window, GLFW_FOCUSED) != 0; } static bool ImGui_ImplGlfw_GetWindowMinimized(ImGuiViewport* viewport) { ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; return glfwGetWindowAttrib(vd->Window, GLFW_ICONIFIED) != 0; } #if GLFW_HAS_WINDOW_ALPHA static void ImGui_ImplGlfw_SetWindowAlpha(ImGuiViewport* viewport, float alpha) { ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; glfwSetWindowOpacity(vd->Window, alpha); } #endif static void ImGui_ImplGlfw_RenderWindow(ImGuiViewport* viewport, void*) { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; if (bd->ClientApi == GlfwClientApi_OpenGL) glfwMakeContextCurrent(vd->Window); } static void ImGui_ImplGlfw_SwapBuffers(ImGuiViewport* viewport, void*) { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; if (bd->ClientApi == GlfwClientApi_OpenGL) { glfwMakeContextCurrent(vd->Window); glfwSwapBuffers(vd->Window); } } //-------------------------------------------------------------------------------------------------------- // Vulkan support (the Vulkan renderer needs to call a platform-side support function to create the surface) //-------------------------------------------------------------------------------------------------------- // Avoid including so we can build without it #if GLFW_HAS_VULKAN #ifndef VULKAN_H_ #define VK_DEFINE_HANDLE(object) typedef struct object##_T* object; #if defined(__LP64__) || defined(_WIN64) || defined(__x86_64__) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) #define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef struct object##_T *object; #else #define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object; #endif VK_DEFINE_HANDLE(VkInstance) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSurfaceKHR) struct VkAllocationCallbacks; enum VkResult { VK_RESULT_MAX_ENUM = 0x7FFFFFFF }; #endif // VULKAN_H_ extern "C" { extern GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface); } static int ImGui_ImplGlfw_CreateVkSurface(ImGuiViewport* viewport, ImU64 vk_instance, const void* vk_allocator, ImU64* out_vk_surface) { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; IM_UNUSED(bd); IM_ASSERT(bd->ClientApi == GlfwClientApi_Vulkan); VkResult err = glfwCreateWindowSurface((VkInstance)vk_instance, vd->Window, (const VkAllocationCallbacks*)vk_allocator, (VkSurfaceKHR*)out_vk_surface); return (int)err; } #endif // GLFW_HAS_VULKAN static void ImGui_ImplGlfw_InitPlatformInterface() { // Register platform interface (will be coupled with a renderer interface) ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); platform_io.Platform_CreateWindow = ImGui_ImplGlfw_CreateWindow; platform_io.Platform_DestroyWindow = ImGui_ImplGlfw_DestroyWindow; platform_io.Platform_ShowWindow = ImGui_ImplGlfw_ShowWindow; platform_io.Platform_SetWindowPos = ImGui_ImplGlfw_SetWindowPos; platform_io.Platform_GetWindowPos = ImGui_ImplGlfw_GetWindowPos; platform_io.Platform_SetWindowSize = ImGui_ImplGlfw_SetWindowSize; platform_io.Platform_GetWindowSize = ImGui_ImplGlfw_GetWindowSize; platform_io.Platform_SetWindowFocus = ImGui_ImplGlfw_SetWindowFocus; platform_io.Platform_GetWindowFocus = ImGui_ImplGlfw_GetWindowFocus; platform_io.Platform_GetWindowMinimized = ImGui_ImplGlfw_GetWindowMinimized; platform_io.Platform_SetWindowTitle = ImGui_ImplGlfw_SetWindowTitle; platform_io.Platform_RenderWindow = ImGui_ImplGlfw_RenderWindow; platform_io.Platform_SwapBuffers = ImGui_ImplGlfw_SwapBuffers; #if GLFW_HAS_WINDOW_ALPHA platform_io.Platform_SetWindowAlpha = ImGui_ImplGlfw_SetWindowAlpha; #endif #if GLFW_HAS_VULKAN platform_io.Platform_CreateVkSurface = ImGui_ImplGlfw_CreateVkSurface; #endif // Register main window handle (which is owned by the main application, not by us) // This is mostly for simplicity and consistency, so that our code (e.g. mouse handling etc.) can use same logic for main and secondary viewports. ImGuiViewport* main_viewport = ImGui::GetMainViewport(); ImGui_ImplGlfw_ViewportData* vd = IM_NEW(ImGui_ImplGlfw_ViewportData)(); vd->Window = bd->Window; vd->WindowOwned = false; main_viewport->PlatformUserData = vd; main_viewport->PlatformHandle = (void*)bd->Window; } static void ImGui_ImplGlfw_ShutdownPlatformInterface() { ImGui::DestroyPlatformWindows(); } #if defined(__clang__) #pragma clang diagnostic pop #endif ================================================ FILE: Source/External/imgui_tools/imgui_impl_glfw.h ================================================ // dear imgui: Platform Backend for GLFW // This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan, WebGPU..) // (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) // (Requires: GLFW 3.1+. Prefer GLFW 3.3+ for full feature support.) // Implemented features: // [X] Platform: Clipboard support. // [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLFW_KEY_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set] // [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // [x] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange' (note: the resizing cursors requires GLFW 3.4+). // [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. // Issues: // [ ] Platform: Multi-viewport support: ParentViewportID not honored, and so io.ConfigViewportsNoDefaultParent has no effect (minor). // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. // Read online: https://github.com/ocornut/imgui/tree/master/docs #pragma once #include "imgui.h" // IMGUI_IMPL_API struct GLFWwindow; struct GLFWmonitor; IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks); IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks); IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOther(GLFWwindow* window, bool install_callbacks); IMGUI_IMPL_API void ImGui_ImplGlfw_Shutdown(); IMGUI_IMPL_API void ImGui_ImplGlfw_NewFrame(); // GLFW callbacks (installer) // - When calling Init with 'install_callbacks=true': ImGui_ImplGlfw_InstallCallbacks() is called. GLFW callbacks will be installed for you. They will chain-call user's previously installed callbacks, if any. // - When calling Init with 'install_callbacks=false': GLFW callbacks won't be installed. You will need to call individual function yourself from your own GLFW callbacks. IMGUI_IMPL_API void ImGui_ImplGlfw_InstallCallbacks(GLFWwindow* window); IMGUI_IMPL_API void ImGui_ImplGlfw_RestoreCallbacks(GLFWwindow* window); // GLFW callbacks (individual callbacks to call if you didn't install callbacks) IMGUI_IMPL_API void ImGui_ImplGlfw_WindowFocusCallback(GLFWwindow* window, int focused); // Since 1.84 IMGUI_IMPL_API void ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered); // Since 1.84 IMGUI_IMPL_API void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y); // Since 1.87 IMGUI_IMPL_API void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods); IMGUI_IMPL_API void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset); IMGUI_IMPL_API void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods); IMGUI_IMPL_API void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c); IMGUI_IMPL_API void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor* monitor, int event); ================================================ FILE: Source/External/imgui_tools/imguizmo/GraphEditor.cpp ================================================ // https://github.com/CedricGuillemet/ImGuizmo // v 1.89 WIP // // The MIT License(MIT) // // Copyright(c) 2021 Cedric Guillemet // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files(the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and / or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions : // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #define IMGUI_DEFINE_MATH_OPERATORS #include "imgui.h" #include "imgui_internal.h" #include #include #include #include #include "GraphEditor.h" namespace GraphEditor { static inline float Distance(const ImVec2& a, const ImVec2& b) { return sqrtf((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)); } static inline float sign(float v) { return (v >= 0.f) ? 1.f : -1.f; } static ImVec2 GetInputSlotPos(Delegate& delegate, const Node& node, SlotIndex slotIndex, float factor) { ImVec2 Size = node.mRect.GetSize() * factor; size_t InputsCount = delegate.GetTemplate(node.mTemplateIndex).mInputCount; return ImVec2(node.mRect.Min.x * factor, node.mRect.Min.y * factor + Size.y * ((float)slotIndex + 1) / ((float)InputsCount + 1) + 8.f); } static ImVec2 GetOutputSlotPos(Delegate& delegate, const Node& node, SlotIndex slotIndex, float factor) { ImVec2 Size = node.mRect.GetSize() * factor; size_t OutputsCount = delegate.GetTemplate(node.mTemplateIndex).mOutputCount; return ImVec2(node.mRect.Min.x * factor + Size.x, node.mRect.Min.y * factor + Size.y * ((float)slotIndex + 1) / ((float)OutputsCount + 1) + 8.f); } static ImRect GetNodeRect(const Node& node, float factor) { ImVec2 Size = node.mRect.GetSize() * factor; return ImRect(node.mRect.Min * factor, node.mRect.Min * factor + Size); } static ImVec2 editingNodeSource; static bool editingInput = false; static ImVec2 captureOffset; enum NodeOperation { NO_None, NO_EditingLink, NO_QuadSelecting, NO_MovingNodes, NO_EditInput, NO_PanView, }; static NodeOperation nodeOperation = NO_None; static void HandleZoomScroll(ImRect regionRect, ViewState& viewState, const Options& options) { ImGuiIO& io = ImGui::GetIO(); if (regionRect.Contains(io.MousePos)) { if (io.MouseWheel < -FLT_EPSILON) { viewState.mFactorTarget *= 1.f - options.mZoomRatio; } if (io.MouseWheel > FLT_EPSILON) { viewState.mFactorTarget *= 1.0f + options.mZoomRatio; } } ImVec2 mouseWPosPre = (io.MousePos - ImGui::GetCursorScreenPos()) / viewState.mFactor; viewState.mFactorTarget = ImClamp(viewState.mFactorTarget, options.mMinZoom, options.mMaxZoom); viewState.mFactor = ImLerp(viewState.mFactor, viewState.mFactorTarget, options.mZoomLerpFactor); ImVec2 mouseWPosPost = (io.MousePos - ImGui::GetCursorScreenPos()) / viewState.mFactor; if (ImGui::IsMousePosValid()) { viewState.mPosition += mouseWPosPost - mouseWPosPre; } } void GraphEditorClear() { nodeOperation = NO_None; } static void FitNodes(Delegate& delegate, ViewState& viewState, const ImVec2 viewSize, bool selectedNodesOnly) { const size_t nodeCount = delegate.GetNodeCount(); if (!nodeCount) { return; } bool validNode = false; ImVec2 min(FLT_MAX, FLT_MAX); ImVec2 max(-FLT_MAX, -FLT_MAX); for (NodeIndex nodeIndex = 0; nodeIndex < nodeCount; nodeIndex++) { const Node& node = delegate.GetNode(nodeIndex); if (selectedNodesOnly && !node.mSelected) { continue; } min = ImMin(min, node.mRect.Min); min = ImMin(min, node.mRect.Max); max = ImMax(max, node.mRect.Min); max = ImMax(max, node.mRect.Max); validNode = true; } if (!validNode) { return; } min -= viewSize * 0.05f; max += viewSize * 0.05f; ImVec2 nodesSize = max - min; ImVec2 nodeCenter = (max + min) * 0.5f; float ratioY = viewSize.y / nodesSize.y; float ratioX = viewSize.x / nodesSize.x; viewState.mFactor = viewState.mFactorTarget = ImMin(ImMin(ratioY, ratioX), 1.f); viewState.mPosition = ImVec2(-nodeCenter.x, -nodeCenter.y) + (viewSize * 0.5f) / viewState.mFactorTarget; } static void DisplayLinks(Delegate& delegate, ImDrawList* drawList, const ImVec2 offset, const float factor, const ImRect regionRect, NodeIndex hoveredNode, const Options& options) { const size_t linkCount = delegate.GetLinkCount(); for (LinkIndex linkIndex = 0; linkIndex < linkCount; linkIndex++) { const auto link = delegate.GetLink(linkIndex); const auto nodeInput = delegate.GetNode(link.mInputNodeIndex); const auto nodeOutput = delegate.GetNode(link.mOutputNodeIndex); ImVec2 p1 = offset + GetOutputSlotPos(delegate, nodeInput, link.mInputSlotIndex, factor); ImVec2 p2 = offset + GetInputSlotPos(delegate, nodeOutput, link.mOutputSlotIndex, factor); // con. view clipping if ((p1.y < 0.f && p2.y < 0.f) || (p1.y > regionRect.Max.y && p2.y > regionRect.Max.y) || (p1.x < 0.f && p2.x < 0.f) || (p1.x > regionRect.Max.x && p2.x > regionRect.Max.x)) continue; bool highlightCons = hoveredNode == link.mInputNodeIndex || hoveredNode == link.mOutputNodeIndex; uint32_t col = delegate.GetTemplate(nodeInput.mTemplateIndex).mHeaderColor | (highlightCons ? 0xF0F0F0 : 0); if (options.mDisplayLinksAsCurves) { // curves drawList->AddBezierCubic(p1, p1 + ImVec2(50, 0) * factor, p2 + ImVec2(-50, 0) * factor, p2, 0xFF000000, options.mLineThickness * 1.5f * factor); drawList->AddBezierCubic(p1, p1 + ImVec2(50, 0) * factor, p2 + ImVec2(-50, 0) * factor, p2, col, options.mLineThickness * 1.5f * factor); /* ImVec2 p10 = p1 + ImVec2(20.f * factor, 0.f); ImVec2 p20 = p2 - ImVec2(20.f * factor, 0.f); ImVec2 dif = p20 - p10; ImVec2 p1a, p1b; if (fabsf(dif.x) > fabsf(dif.y)) { p1a = p10 + ImVec2(fabsf(fabsf(dif.x) - fabsf(dif.y)) * 0.5 * sign(dif.x), 0.f); p1b = p1a + ImVec2(fabsf(dif.y) * sign(dif.x) , dif.y); } else { p1a = p10 + ImVec2(0.f, fabsf(fabsf(dif.y) - fabsf(dif.x)) * 0.5 * sign(dif.y)); p1b = p1a + ImVec2(dif.x, fabsf(dif.x) * sign(dif.y)); } drawList->AddLine(p1, p10, col, 3.f * factor); drawList->AddLine(p10, p1a, col, 3.f * factor); drawList->AddLine(p1a, p1b, col, 3.f * factor); drawList->AddLine(p1b, p20, col, 3.f * factor); drawList->AddLine(p20, p2, col, 3.f * factor); */ } else { // straight lines std::array pts; int ptCount = 0; ImVec2 dif = p2 - p1; ImVec2 p1a, p1b; const float limitx = 12.f * factor; if (dif.x < limitx) { ImVec2 p10 = p1 + ImVec2(limitx, 0.f); ImVec2 p20 = p2 - ImVec2(limitx, 0.f); dif = p20 - p10; p1a = p10 + ImVec2(0.f, dif.y * 0.5f); p1b = p1a + ImVec2(dif.x, 0.f); pts = { p1, p10, p1a, p1b, p20, p2 }; ptCount = 6; } else { if (fabsf(dif.y) < 1.f) { pts = { p1, (p1 + p2) * 0.5f, p2 }; ptCount = 3; } else { if (fabsf(dif.y) < 10.f) { if (fabsf(dif.x) > fabsf(dif.y)) { p1a = p1 + ImVec2(fabsf(fabsf(dif.x) - fabsf(dif.y)) * 0.5f * sign(dif.x), 0.f); p1b = p1a + ImVec2(fabsf(dif.y) * sign(dif.x), dif.y); } else { p1a = p1 + ImVec2(0.f, fabsf(fabsf(dif.y) - fabsf(dif.x)) * 0.5f * sign(dif.y)); p1b = p1a + ImVec2(dif.x, fabsf(dif.x) * sign(dif.y)); } } else { if (fabsf(dif.x) > fabsf(dif.y)) { float d = fabsf(dif.y) * sign(dif.x) * 0.5f; p1a = p1 + ImVec2(d, dif.y * 0.5f); p1b = p1a + ImVec2(fabsf(fabsf(dif.x) - fabsf(d) * 2.f) * sign(dif.x), 0.f); } else { float d = fabsf(dif.x) * sign(dif.y) * 0.5f; p1a = p1 + ImVec2(dif.x * 0.5f, d); p1b = p1a + ImVec2(0.f, fabsf(fabsf(dif.y) - fabsf(d) * 2.f) * sign(dif.y)); } } pts = { p1, p1a, p1b, p2 }; ptCount = 4; } } float highLightFactor = factor * (highlightCons ? 2.0f : 1.f); for (int pass = 0; pass < 2; pass++) { drawList->AddPolyline(pts.data(), ptCount, pass ? col : 0xFF000000, false, (pass ? options.mLineThickness : (options.mLineThickness * 1.5f)) * highLightFactor); } } } } static void HandleQuadSelection(Delegate& delegate, ImDrawList* drawList, const ImVec2 offset, const float factor, ImRect contentRect, const Options& options) { if (!options.mAllowQuadSelection) { return; } ImGuiIO& io = ImGui::GetIO(); static ImVec2 quadSelectPos; //auto& nodes = delegate->GetNodes(); auto nodeCount = delegate.GetNodeCount(); if (nodeOperation == NO_QuadSelecting && ImGui::IsWindowFocused()) { const ImVec2 bmin = ImMin(quadSelectPos, io.MousePos); const ImVec2 bmax = ImMax(quadSelectPos, io.MousePos); drawList->AddRectFilled(bmin, bmax, options.mQuadSelection, 1.f); drawList->AddRect(bmin, bmax, options.mQuadSelectionBorder, 1.f); if (!io.MouseDown[0]) { if (!io.KeyCtrl && !io.KeyShift) { for (size_t nodeIndex = 0; nodeIndex < nodeCount; nodeIndex++) { delegate.SelectNode(nodeIndex, false); } } nodeOperation = NO_None; ImRect selectionRect(bmin, bmax); for (int nodeIndex = 0; nodeIndex < nodeCount; nodeIndex++) { const auto node = delegate.GetNode(nodeIndex); ImVec2 nodeRectangleMin = offset + node.mRect.Min * factor; ImVec2 nodeRectangleMax = nodeRectangleMin + node.mRect.GetSize() * factor; if (selectionRect.Overlaps(ImRect(nodeRectangleMin, nodeRectangleMax))) { if (io.KeyCtrl) { delegate.SelectNode(nodeIndex, false); } else { delegate.SelectNode(nodeIndex, true); } } else { if (!io.KeyShift) { delegate.SelectNode(nodeIndex, false); } } } } } else if (nodeOperation == NO_None && io.MouseDown[0] && ImGui::IsWindowFocused() && contentRect.Contains(io.MousePos)) { nodeOperation = NO_QuadSelecting; quadSelectPos = io.MousePos; } } static bool HandleConnections(ImDrawList* drawList, NodeIndex nodeIndex, const ImVec2 offset, const float factor, Delegate& delegate, const Options& options, bool bDrawOnly, SlotIndex& inputSlotOver, SlotIndex& outputSlotOver, const bool inMinimap) { static NodeIndex editingNodeIndex; static SlotIndex editingSlotIndex; ImGuiIO& io = ImGui::GetIO(); const auto node = delegate.GetNode(nodeIndex); const auto nodeTemplate = delegate.GetTemplate(node.mTemplateIndex); const auto linkCount = delegate.GetLinkCount(); size_t InputsCount = nodeTemplate.mInputCount; size_t OutputsCount = nodeTemplate.mOutputCount; inputSlotOver = -1; outputSlotOver = -1; // draw/use inputs/outputs bool hoverSlot = false; for (int i = 0; i < 2; i++) { float closestDistance = FLT_MAX; SlotIndex closestConn = -1; ImVec2 closestTextPos; ImVec2 closestPos; const size_t slotCount[2] = {InputsCount, OutputsCount}; for (SlotIndex slotIndex = 0; slotIndex < slotCount[i]; slotIndex++) { const char** con = i ? nodeTemplate.mOutputNames : nodeTemplate.mInputNames; const char* conText = (con && con[slotIndex]) ? con[slotIndex] : ""; ImVec2 p = offset + (i ? GetOutputSlotPos(delegate, node, slotIndex, factor) : GetInputSlotPos(delegate, node, slotIndex, factor)); float distance = Distance(p, io.MousePos); bool overCon = (nodeOperation == NO_None || nodeOperation == NO_EditingLink) && (distance < options.mNodeSlotRadius * 2.f) && (distance < closestDistance); ImVec2 textSize; textSize = ImGui::CalcTextSize(conText); ImVec2 textPos = p + ImVec2(-options.mNodeSlotRadius * (i ? -1.f : 1.f) * (overCon ? 3.f : 2.f) - (i ? 0 : textSize.x), -textSize.y / 2); ImRect nodeRect = GetNodeRect(node, factor); if (!inMinimap && (overCon || (nodeRect.Contains(io.MousePos - offset) && closestConn == -1 && (editingInput == (i != 0)) && nodeOperation == NO_EditingLink))) { closestDistance = distance; closestConn = slotIndex; closestTextPos = textPos; closestPos = p; if (i) { outputSlotOver = slotIndex; } else { inputSlotOver = slotIndex; } } else { const ImU32* slotColorSource = i ? nodeTemplate.mOutputColors : nodeTemplate.mInputColors; const ImU32 slotColor = slotColorSource ? slotColorSource[slotIndex] : options.mDefaultSlotColor; drawList->AddCircleFilled(p, options.mNodeSlotRadius, IM_COL32(0, 0, 0, 200)); drawList->AddCircleFilled(p, options.mNodeSlotRadius * 0.75f, slotColor); if (!options.mDrawIONameOnHover) { drawList->AddText(io.FontDefault, 14, textPos + ImVec2(2, 2), IM_COL32(0, 0, 0, 255), conText); drawList->AddText(io.FontDefault, 14, textPos, IM_COL32(150, 150, 150, 255), conText); } } } if (closestConn != -1) { const char** con = i ? nodeTemplate.mOutputNames : nodeTemplate.mInputNames; const char* conText = (con && con[closestConn]) ? con[closestConn] : ""; const ImU32* slotColorSource = i ? nodeTemplate.mOutputColors : nodeTemplate.mInputColors; const ImU32 slotColor = slotColorSource ? slotColorSource[closestConn] : options.mDefaultSlotColor; hoverSlot = true; drawList->AddCircleFilled(closestPos, options.mNodeSlotRadius * options.mNodeSlotHoverFactor * 0.75f, IM_COL32(0, 0, 0, 200)); drawList->AddCircleFilled(closestPos, options.mNodeSlotRadius * options.mNodeSlotHoverFactor, slotColor); drawList->AddText(io.FontDefault, 16, closestTextPos + ImVec2(1, 1), IM_COL32(0, 0, 0, 255), conText); drawList->AddText(io.FontDefault, 16, closestTextPos, IM_COL32(250, 250, 250, 255), conText); bool inputToOutput = (!editingInput && !i) || (editingInput && i); if (nodeOperation == NO_EditingLink && !io.MouseDown[0] && !bDrawOnly) { if (inputToOutput) { // check loopback Link nl; if (editingInput) nl = Link{nodeIndex, closestConn, editingNodeIndex, editingSlotIndex}; else nl = Link{editingNodeIndex, editingSlotIndex, nodeIndex, closestConn}; if (!delegate.AllowedLink(nl.mOutputNodeIndex, nl.mInputNodeIndex)) { break; } bool alreadyExisting = false; for (size_t linkIndex = 0; linkIndex < linkCount; linkIndex++) { const auto link = delegate.GetLink(linkIndex); if (!memcmp(&link, &nl, sizeof(Link))) { alreadyExisting = true; break; } } if (!alreadyExisting) { for (int linkIndex = 0; linkIndex < linkCount; linkIndex++) { const auto link = delegate.GetLink(linkIndex); if (link.mOutputNodeIndex == nl.mOutputNodeIndex && link.mOutputSlotIndex == nl.mOutputSlotIndex) { delegate.DelLink(linkIndex); break; } } delegate.AddLink(nl.mInputNodeIndex, nl.mInputSlotIndex, nl.mOutputNodeIndex, nl.mOutputSlotIndex); } } } // when ImGui::IsWindowHovered() && !ImGui::IsAnyItemActive() is uncommented, one can't click the node // input/output when mouse is over the node itself. if (nodeOperation == NO_None && /*ImGui::IsWindowHovered() && !ImGui::IsAnyItemActive() &&*/ io.MouseClicked[0] && !bDrawOnly) { nodeOperation = NO_EditingLink; editingInput = i == 0; editingNodeSource = closestPos; editingNodeIndex = nodeIndex; editingSlotIndex = closestConn; if (editingInput) { // remove existing link for (int linkIndex = 0; linkIndex < linkCount; linkIndex++) { const auto link = delegate.GetLink(linkIndex); if (link.mOutputNodeIndex == nodeIndex && link.mOutputSlotIndex == closestConn) { delegate.DelLink(linkIndex); break; } } } } } } return hoverSlot; } static void DrawGrid(ImDrawList* drawList, ImVec2 windowPos, const ViewState& viewState, const ImVec2 canvasSize, ImU32 gridColor, ImU32 gridColor2, float gridSize) { float gridSpace = gridSize * viewState.mFactor; int divx = static_cast(-viewState.mPosition.x / gridSize); int divy = static_cast(-viewState.mPosition.y / gridSize); for (float x = fmodf(viewState.mPosition.x * viewState.mFactor, gridSpace); x < canvasSize.x; x += gridSpace, divx ++) { bool tenth = !(divx % 10); drawList->AddLine(ImVec2(x, 0.0f) + windowPos, ImVec2(x, canvasSize.y) + windowPos, tenth ? gridColor2 : gridColor); } for (float y = fmodf(viewState.mPosition.y * viewState.mFactor, gridSpace); y < canvasSize.y; y += gridSpace, divy ++) { bool tenth = !(divy % 10); drawList->AddLine(ImVec2(0.0f, y) + windowPos, ImVec2(canvasSize.x, y) + windowPos, tenth ? gridColor2 : gridColor); } } // return true if node is hovered static bool DrawNode(ImDrawList* drawList, NodeIndex nodeIndex, const ImVec2 offset, const float factor, Delegate& delegate, bool overInput, const Options& options, const bool inMinimap, const ImRect& viewPort) { ImGuiIO& io = ImGui::GetIO(); const auto node = delegate.GetNode(nodeIndex); IM_ASSERT((node.mRect.GetWidth() != 0.f) && (node.mRect.GetHeight() != 0.f) && "Nodes must have a non-zero rect."); const auto nodeTemplate = delegate.GetTemplate(node.mTemplateIndex); const ImVec2 nodeRectangleMin = offset + node.mRect.Min * factor; const bool old_any_active = ImGui::IsAnyItemActive(); ImGui::SetCursorScreenPos(nodeRectangleMin); const ImVec2 nodeSize = node.mRect.GetSize() * factor; // test nested IO drawList->ChannelsSetCurrent(1); // Background const size_t InputsCount = nodeTemplate.mInputCount; const size_t OutputsCount = nodeTemplate.mOutputCount; /* for (int i = 0; i < 2; i++) { const size_t slotCount[2] = {InputsCount, OutputsCount}; for (size_t slotIndex = 0; slotIndex < slotCount[i]; slotIndex++) { const char* con = i ? nodeTemplate.mOutputNames[slotIndex] : nodeTemplate.mInputNames[slotIndex];//node.mOutputs[slot_idx] : node->mInputs[slot_idx]; if (!delegate->IsIOPinned(nodeIndex, slot_idx, i == 1)) { } continue; ImVec2 p = offset + (i ? GetOutputSlotPos(delegate, node, slotIndex, factor) : GetInputSlotPos(delegate, node, slotIndex, factor)); const float arc = 28.f * (float(i) * 0.3f + 1.0f) * (i ? 1.f : -1.f); const float ofs = 0.f; ImVec2 pts[3] = {p + ImVec2(arc + ofs, 0.f), p + ImVec2(0.f + ofs, -arc), p + ImVec2(0.f + ofs, arc)}; drawList->AddTriangleFilled(pts[0], pts[1], pts[2], i ? 0xFFAA5030 : 0xFF30AA50); drawList->AddTriangle(pts[0], pts[1], pts[2], 0xFF000000, 2.f); } } */ ImGui::SetCursorScreenPos(nodeRectangleMin); float maxHeight = ImMin(viewPort.Max.y, nodeRectangleMin.y + nodeSize.y) - nodeRectangleMin.y; float maxWidth = ImMin(viewPort.Max.x, nodeRectangleMin.x + nodeSize.x) - nodeRectangleMin.x; ImGui::InvisibleButton("node", ImVec2(maxWidth, maxHeight)); // must be called right after creating the control we want to be able to move bool nodeMovingActive = ImGui::IsItemActive(); // Save the size of what we have emitted and whether any of the widgets are being used bool nodeWidgetsActive = (!old_any_active && ImGui::IsAnyItemActive()); ImVec2 nodeRectangleMax = nodeRectangleMin + nodeSize; bool nodeHovered = false; if (ImGui::IsItemHovered() && nodeOperation == NO_None && !overInput) { nodeHovered = true; } if (ImGui::IsWindowFocused()) { if ((nodeWidgetsActive || nodeMovingActive) && !inMinimap) { if (!node.mSelected) { if (!io.KeyShift) { const auto nodeCount = delegate.GetNodeCount(); for (size_t i = 0; i < nodeCount; i++) { delegate.SelectNode(i, false); } } delegate.SelectNode(nodeIndex, true); } } } if (nodeMovingActive && io.MouseDown[0] && nodeHovered && !inMinimap) { if (nodeOperation != NO_MovingNodes) { nodeOperation = NO_MovingNodes; } } const bool currentSelectedNode = node.mSelected; const ImU32 node_bg_color = nodeHovered ? nodeTemplate.mBackgroundColorOver : nodeTemplate.mBackgroundColor; drawList->AddRect(nodeRectangleMin, nodeRectangleMax, currentSelectedNode ? options.mSelectedNodeBorderColor : options.mNodeBorderColor, options.mRounding, ImDrawFlags_RoundCornersAll, currentSelectedNode ? options.mBorderSelectionThickness : options.mBorderThickness); ImVec2 imgPos = nodeRectangleMin + ImVec2(14, 25); ImVec2 imgSize = nodeRectangleMax + ImVec2(-5, -5) - imgPos; float imgSizeComp = std::min(imgSize.x, imgSize.y); drawList->AddRectFilled(nodeRectangleMin, nodeRectangleMax, node_bg_color, options.mRounding); /*float progress = delegate->NodeProgress(nodeIndex); if (progress > FLT_EPSILON && progress < 1.f - FLT_EPSILON) { ImVec2 progressLineA = nodeRectangleMax - ImVec2(nodeSize.x - 2.f, 3.f); ImVec2 progressLineB = progressLineA + ImVec2(nodeSize.x * factor - 4.f, 0.f); drawList->AddLine(progressLineA, progressLineB, 0xFF400000, 3.f); drawList->AddLine(progressLineA, ImLerp(progressLineA, progressLineB, progress), 0xFFFF0000, 3.f); }*/ ImVec2 imgPosMax = imgPos + ImVec2(imgSizeComp, imgSizeComp); //ImVec2 imageSize = delegate->GetEvaluationSize(nodeIndex); /*float imageRatio = 1.f; if (imageSize.x > 0.f && imageSize.y > 0.f) { imageRatio = imageSize.y / imageSize.x; } ImVec2 quadSize = imgPosMax - imgPos; ImVec2 marge(0.f, 0.f); if (imageRatio > 1.f) { marge.x = (quadSize.x - quadSize.y / imageRatio) * 0.5f; } else { marge.y = (quadSize.y - quadSize.y * imageRatio) * 0.5f; }*/ //delegate->DrawNodeImage(drawList, ImRect(imgPos, imgPosMax), marge, nodeIndex); drawList->AddRectFilled(nodeRectangleMin, ImVec2(nodeRectangleMax.x, nodeRectangleMin.y + 20), nodeTemplate.mHeaderColor, options.mRounding); drawList->PushClipRect(nodeRectangleMin, ImVec2(nodeRectangleMax.x, nodeRectangleMin.y + 20), true); drawList->AddText(nodeRectangleMin + ImVec2(2, 2), IM_COL32(0, 0, 0, 255), node.mName); drawList->PopClipRect(); ImRect customDrawRect(nodeRectangleMin + ImVec2(options.mRounding, 20 + options.mRounding), nodeRectangleMax - ImVec2(options.mRounding, options.mRounding)); if (customDrawRect.Max.y > customDrawRect.Min.y && customDrawRect.Max.x > customDrawRect.Min.x) { delegate.CustomDraw(drawList, customDrawRect, nodeIndex); } /* const ImTextureID bmpInfo = (ImTextureID)(uint64_t)delegate->GetBitmapInfo(nodeIndex).idx; if (bmpInfo) { ImVec2 bmpInfoPos(nodeRectangleMax - ImVec2(26, 12)); ImVec2 bmpInfoSize(20, 20); if (delegate->NodeIsCompute(nodeIndex)) { drawList->AddImageQuad(bmpInfo, bmpInfoPos, bmpInfoPos + ImVec2(bmpInfoSize.x, 0.f), bmpInfoPos + bmpInfoSize, bmpInfoPos + ImVec2(0., bmpInfoSize.y)); } else if (delegate->NodeIs2D(nodeIndex)) { drawList->AddImageQuad(bmpInfo, bmpInfoPos, bmpInfoPos + ImVec2(bmpInfoSize.x, 0.f), bmpInfoPos + bmpInfoSize, bmpInfoPos + ImVec2(0., bmpInfoSize.y)); } else if (delegate->NodeIsCubemap(nodeIndex)) { drawList->AddImageQuad(bmpInfo, bmpInfoPos + ImVec2(0., bmpInfoSize.y), bmpInfoPos + bmpInfoSize, bmpInfoPos + ImVec2(bmpInfoSize.x, 0.f), bmpInfoPos); } }*/ return nodeHovered; } bool DrawMiniMap(ImDrawList* drawList, Delegate& delegate, ViewState& viewState, const Options& options, const ImVec2 windowPos, const ImVec2 canvasSize) { if (Distance(options.mMinimap.Min, options.mMinimap.Max) <= FLT_EPSILON) { return false; } const size_t nodeCount = delegate.GetNodeCount(); if (!nodeCount) { return false; } ImVec2 min(FLT_MAX, FLT_MAX); ImVec2 max(-FLT_MAX, -FLT_MAX); const ImVec2 margin(50, 50); for (NodeIndex nodeIndex = 0; nodeIndex < nodeCount; nodeIndex++) { const Node& node = delegate.GetNode(nodeIndex); min = ImMin(min, node.mRect.Min - margin); min = ImMin(min, node.mRect.Max + margin); max = ImMax(max, node.mRect.Min - margin); max = ImMax(max, node.mRect.Max + margin); } // add view in world space const ImVec2 worldSizeView = canvasSize / viewState.mFactor; const ImVec2 viewMin(-viewState.mPosition.x, -viewState.mPosition.y); const ImVec2 viewMax = viewMin + worldSizeView; min = ImMin(min, viewMin); max = ImMax(max, viewMax); const ImVec2 nodesSize = max - min; const ImVec2 middleWorld = (min + max) * 0.5f; const ImVec2 minScreen = windowPos + options.mMinimap.Min * canvasSize; const ImVec2 maxScreen = windowPos + options.mMinimap.Max * canvasSize; const ImVec2 viewSize = maxScreen - minScreen; const ImVec2 middleScreen = (minScreen + maxScreen) * 0.5f; const float ratioY = viewSize.y / nodesSize.y; const float ratioX = viewSize.x / nodesSize.x; const float factor = ImMin(ImMin(ratioY, ratioX), 1.f); drawList->AddRectFilled(minScreen, maxScreen, IM_COL32(30, 30, 30, 200), 3, ImDrawFlags_RoundCornersAll); for (NodeIndex nodeIndex = 0; nodeIndex < nodeCount; nodeIndex++) { const Node& node = delegate.GetNode(nodeIndex); const auto nodeTemplate = delegate.GetTemplate(node.mTemplateIndex); ImRect rect = node.mRect; rect.Min -= middleWorld; rect.Min *= factor; rect.Min += middleScreen; rect.Max -= middleWorld; rect.Max *= factor; rect.Max += middleScreen; drawList->AddRectFilled(rect.Min, rect.Max, nodeTemplate.mBackgroundColor, 1, ImDrawFlags_RoundCornersAll); if (node.mSelected) { drawList->AddRect(rect.Min, rect.Max, options.mSelectedNodeBorderColor, 1, ImDrawFlags_RoundCornersAll); } } // add view ImVec2 viewMinScreen = (viewMin - middleWorld) * factor + middleScreen; ImVec2 viewMaxScreen = (viewMax - middleWorld) * factor + middleScreen; drawList->AddRectFilled(viewMinScreen, viewMaxScreen, IM_COL32(255, 255, 255, 32), 1, ImDrawFlags_RoundCornersAll); drawList->AddRect(viewMinScreen, viewMaxScreen, IM_COL32(255, 255, 255, 128), 1, ImDrawFlags_RoundCornersAll); ImGuiIO& io = ImGui::GetIO(); const bool mouseInMinimap = ImRect(minScreen, maxScreen).Contains(io.MousePos); if (mouseInMinimap && io.MouseClicked[0]) { const ImVec2 clickedRatio = (io.MousePos - minScreen) / viewSize; const ImVec2 worldPosCenter = ImVec2(ImLerp(min.x, max.x, clickedRatio.x), ImLerp(min.y, max.y, clickedRatio.y)); ImVec2 worldPosViewMin = worldPosCenter - worldSizeView * 0.5; ImVec2 worldPosViewMax = worldPosCenter + worldSizeView * 0.5; if (worldPosViewMin.x < min.x) { worldPosViewMin.x = min.x; worldPosViewMax.x = worldPosViewMin.x + worldSizeView.x; } if (worldPosViewMin.y < min.y) { worldPosViewMin.y = min.y; worldPosViewMax.y = worldPosViewMin.y + worldSizeView.y; } if (worldPosViewMax.x > max.x) { worldPosViewMax.x = max.x; worldPosViewMin.x = worldPosViewMax.x - worldSizeView.x; } if (worldPosViewMax.y > max.y) { worldPosViewMax.y = max.y; worldPosViewMin.y = worldPosViewMax.y - worldSizeView.y; } viewState.mPosition = ImVec2(-worldPosViewMin.x, -worldPosViewMin.y); } return mouseInMinimap; } void Show(Delegate& delegate, const Options& options, ViewState& viewState, bool enabled, FitOnScreen* fit) { ImGui::PushStyleVar(ImGuiStyleVar_ChildBorderSize, 0.f); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.f, 0.f)); ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.f); const ImVec2 windowPos = ImGui::GetCursorScreenPos(); const ImVec2 canvasSize = ImGui::GetContentRegionAvail(); const ImVec2 scrollRegionLocalPos(0, 0); ImRect regionRect(windowPos, windowPos + canvasSize); HandleZoomScroll(regionRect, viewState, options); ImVec2 offset = ImGui::GetCursorScreenPos() + viewState.mPosition * viewState.mFactor; captureOffset = viewState.mPosition * viewState.mFactor; //ImGui::InvisibleButton("GraphEditorButton", canvasSize); ImGui::BeginChildFrame(71711, canvasSize); ImGui::SetCursorPos(windowPos); ImGui::BeginGroup(); ImGuiIO& io = ImGui::GetIO(); // Create our child canvas ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(1, 1)); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); ImGui::PushStyleColor(ImGuiCol_ChildBg, IM_COL32(30, 30, 30, 200)); ImDrawList* drawList = ImGui::GetWindowDrawList(); ImGui::PushClipRect(regionRect.Min, regionRect.Max, true); drawList->AddRectFilled(windowPos, windowPos + canvasSize, options.mBackgroundColor); // Background or Display grid if (options.mRenderGrid) { DrawGrid(drawList, windowPos, viewState, canvasSize, options.mGridColor, options.mGridColor2, options.mGridSize); } // Fit view if (fit && ((*fit == Fit_AllNodes) || (*fit == Fit_SelectedNodes))) { FitNodes(delegate, viewState, canvasSize, (*fit == Fit_SelectedNodes)); } if (enabled) { static NodeIndex hoveredNode = -1; // Display links drawList->ChannelsSplit(3); // minimap drawList->ChannelsSetCurrent(2); // minimap const bool inMinimap = DrawMiniMap(drawList, delegate, viewState, options, windowPos, canvasSize); // Focus rectangle if (ImGui::IsWindowFocused()) { drawList->AddRect(regionRect.Min, regionRect.Max, options.mFrameFocus, 1.f, 0, 2.f); } drawList->ChannelsSetCurrent(1); // Background // Links DisplayLinks(delegate, drawList, offset, viewState.mFactor, regionRect, hoveredNode, options); // edit node link if (nodeOperation == NO_EditingLink) { ImVec2 p1 = editingNodeSource; ImVec2 p2 = io.MousePos; drawList->AddLine(p1, p2, IM_COL32(200, 200, 200, 255), 3.0f); } // Display nodes drawList->PushClipRect(regionRect.Min, regionRect.Max, true); hoveredNode = -1; SlotIndex inputSlotOver = -1; SlotIndex outputSlotOver = -1; NodeIndex nodeOver = -1; const auto nodeCount = delegate.GetNodeCount(); for (int i = 0; i < 2; i++) { for (NodeIndex nodeIndex = 0; nodeIndex < nodeCount; nodeIndex++) { //const auto* node = &nodes[nodeIndex]; const auto node = delegate.GetNode(nodeIndex); if (node.mSelected != (i != 0)) { continue; } // node view clipping ImRect nodeRect = GetNodeRect(node, viewState.mFactor); nodeRect.Min += offset; nodeRect.Max += offset; if (!regionRect.Overlaps(nodeRect)) { continue; } ImGui::PushID((int)nodeIndex); SlotIndex inputSlot = -1; SlotIndex outputSlot = -1; bool overInput = (!inMinimap) && HandleConnections(drawList, nodeIndex, offset, viewState.mFactor, delegate, options, false, inputSlot, outputSlot, inMinimap); // shadow /* ImVec2 shadowOffset = ImVec2(30, 30); ImVec2 shadowPivot = (nodeRect.Min + nodeRect.Max) /2.f; ImVec2 shadowPointMiddle = shadowPivot + shadowOffset; ImVec2 shadowPointTop = ImVec2(shadowPivot.x, nodeRect.Min.y) + shadowOffset; ImVec2 shadowPointBottom = ImVec2(shadowPivot.x, nodeRect.Max.y) + shadowOffset; ImVec2 shadowPointLeft = ImVec2(nodeRect.Min.x, shadowPivot.y) + shadowOffset; ImVec2 shadowPointRight = ImVec2(nodeRect.Max.x, shadowPivot.y) + shadowOffset; // top left drawList->AddRectFilledMultiColor(nodeRect.Min + shadowOffset, shadowPointMiddle, IM_COL32(0 ,0, 0, 0), IM_COL32(0,0,0,0), IM_COL32(0, 0, 0, 255), IM_COL32(0, 0, 0, 0)); // top right drawList->AddRectFilledMultiColor(shadowPointTop, shadowPointRight, IM_COL32(0 ,0, 0, 0), IM_COL32(0,0,0,0), IM_COL32(0, 0, 0, 0), IM_COL32(0, 0, 0, 255)); // bottom left drawList->AddRectFilledMultiColor(shadowPointLeft, shadowPointBottom, IM_COL32(0 ,0, 0, 0), IM_COL32(0, 0, 0, 255), IM_COL32(0, 0, 0, 0), IM_COL32(0,0,0,0)); // bottom right drawList->AddRectFilledMultiColor(shadowPointMiddle, nodeRect.Max + shadowOffset, IM_COL32(0, 0, 0, 255), IM_COL32(0 ,0, 0, 0), IM_COL32(0,0,0,0), IM_COL32(0, 0, 0, 0)); */ if (DrawNode(drawList, nodeIndex, offset, viewState.mFactor, delegate, overInput, options, inMinimap, regionRect)) { hoveredNode = nodeIndex; } HandleConnections(drawList, nodeIndex, offset, viewState.mFactor, delegate, options, true, inputSlot, outputSlot, inMinimap); if (inputSlot != -1 || outputSlot != -1) { inputSlotOver = inputSlot; outputSlotOver = outputSlot; nodeOver = nodeIndex; } ImGui::PopID(); } } drawList->PopClipRect(); if (nodeOperation == NO_MovingNodes) { if (ImGui::IsMouseDragging(0, 1)) { ImVec2 delta = io.MouseDelta / viewState.mFactor; if (fabsf(delta.x) >= 1.f || fabsf(delta.y) >= 1.f) { delegate.MoveSelectedNodes(delta); } } } drawList->ChannelsSetCurrent(0); // quad selection if (!inMinimap) { HandleQuadSelection(delegate, drawList, offset, viewState.mFactor, regionRect, options); } drawList->ChannelsMerge(); // releasing mouse button means it's done in any operation if (nodeOperation == NO_PanView) { if (!io.MouseDown[2]) { nodeOperation = NO_None; } } else if (nodeOperation != NO_None && !io.MouseDown[0]) { nodeOperation = NO_None; } // right click if (!inMinimap && nodeOperation == NO_None && regionRect.Contains(io.MousePos) && (ImGui::IsMouseClicked(1) /*|| (ImGui::IsWindowFocused() && ImGui::IsKeyPressedMap(ImGuiKey_Tab))*/)) { delegate.RightClick(nodeOver, inputSlotOver, outputSlotOver); } // Scrolling if (ImGui::IsWindowHovered() && !ImGui::IsAnyItemActive() && io.MouseClicked[2] && nodeOperation == NO_None) { nodeOperation = NO_PanView; } if (nodeOperation == NO_PanView) { viewState.mPosition += io.MouseDelta / viewState.mFactor; } } ImGui::PopClipRect(); ImGui::PopStyleColor(1); ImGui::PopStyleVar(2); ImGui::EndGroup(); ImGui::EndChildFrame(); ImGui::PopStyleVar(3); // change fit to none if (fit) { *fit = Fit_None; } } bool EditOptions(Options& options) { bool updated = false; if (ImGui::CollapsingHeader("Colors", nullptr)) { ImColor backgroundColor(options.mBackgroundColor); ImColor gridColor(options.mGridColor); ImColor selectedNodeBorderColor(options.mSelectedNodeBorderColor); ImColor nodeBorderColor(options.mNodeBorderColor); ImColor quadSelection(options.mQuadSelection); ImColor quadSelectionBorder(options.mQuadSelectionBorder); ImColor defaultSlotColor(options.mDefaultSlotColor); ImColor frameFocus(options.mFrameFocus); updated |= ImGui::ColorEdit4("Background", (float*)&backgroundColor); updated |= ImGui::ColorEdit4("Grid", (float*)&gridColor); updated |= ImGui::ColorEdit4("Selected Node Border", (float*)&selectedNodeBorderColor); updated |= ImGui::ColorEdit4("Node Border", (float*)&nodeBorderColor); updated |= ImGui::ColorEdit4("Quad Selection", (float*)&quadSelection); updated |= ImGui::ColorEdit4("Quad Selection Border", (float*)&quadSelectionBorder); updated |= ImGui::ColorEdit4("Default Slot", (float*)&defaultSlotColor); updated |= ImGui::ColorEdit4("Frame when has focus", (float*)&frameFocus); options.mBackgroundColor = backgroundColor; options.mGridColor = gridColor; options.mSelectedNodeBorderColor = selectedNodeBorderColor; options.mNodeBorderColor = nodeBorderColor; options.mQuadSelection = quadSelection; options.mQuadSelectionBorder = quadSelectionBorder; options.mDefaultSlotColor = defaultSlotColor; options.mFrameFocus = frameFocus; } if (ImGui::CollapsingHeader("Options", nullptr)) { updated |= ImGui::InputFloat4("Minimap", &options.mMinimap.Min.x); updated |= ImGui::InputFloat("Line Thickness", &options.mLineThickness); updated |= ImGui::InputFloat("Grid Size", &options.mGridSize); updated |= ImGui::InputFloat("Rounding", &options.mRounding); updated |= ImGui::InputFloat("Zoom Ratio", &options.mZoomRatio); updated |= ImGui::InputFloat("Zoom Lerp Factor", &options.mZoomLerpFactor); updated |= ImGui::InputFloat("Border Selection Thickness", &options.mBorderSelectionThickness); updated |= ImGui::InputFloat("Border Thickness", &options.mBorderThickness); updated |= ImGui::InputFloat("Slot Radius", &options.mNodeSlotRadius); updated |= ImGui::InputFloat("Slot Hover Factor", &options.mNodeSlotHoverFactor); updated |= ImGui::InputFloat2("Zoom min/max", &options.mMinZoom); updated |= ImGui::InputFloat("Slot Hover Factor", &options.mSnap); if (ImGui::RadioButton("Curved Links", options.mDisplayLinksAsCurves)) { options.mDisplayLinksAsCurves = !options.mDisplayLinksAsCurves; updated = true; } if (ImGui::RadioButton("Straight Links", !options.mDisplayLinksAsCurves)) { options.mDisplayLinksAsCurves = !options.mDisplayLinksAsCurves; updated = true; } updated |= ImGui::Checkbox("Allow Quad Selection", &options.mAllowQuadSelection); updated |= ImGui::Checkbox("Render Grid", &options.mRenderGrid); updated |= ImGui::Checkbox("Draw IO names on hover", &options.mDrawIONameOnHover); } return updated; } } // namespace ================================================ FILE: Source/External/imgui_tools/imguizmo/GraphEditor.h ================================================ // https://github.com/CedricGuillemet/ImGuizmo // v 1.89 WIP // // The MIT License(MIT) // // Copyright(c) 2021 Cedric Guillemet // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files(the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and / or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions : // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #pragma once #include #include #include #include "imgui.h" #include "imgui_internal.h" namespace GraphEditor { typedef size_t NodeIndex; typedef size_t SlotIndex; typedef size_t LinkIndex; typedef size_t TemplateIndex; // Force the view to be respositionned and zoom to fit nodes with Show function. // Parameter value will be changed to Fit_None by the function. enum FitOnScreen { Fit_None, Fit_AllNodes, Fit_SelectedNodes }; // Display options and colors struct Options { ImRect mMinimap{{0.75f, 0.8f, 0.99f, 0.99f}}; // rectangle coordinates of minimap ImU32 mBackgroundColor{ IM_COL32(40, 40, 40, 255) }; // full background color ImU32 mGridColor{ IM_COL32(0, 0, 0, 60) }; // grid lines color ImU32 mGridColor2{ IM_COL32(0, 0, 0, 160) }; // grid lines color every 10th ImU32 mSelectedNodeBorderColor{ IM_COL32(255, 130, 30, 255) }; // node border color when it's selected ImU32 mNodeBorderColor{ IM_COL32(100, 100, 100, 0) }; // node border color when it's not selected ImU32 mQuadSelection{ IM_COL32(255, 32, 32, 64) }; // quad selection inside color ImU32 mQuadSelectionBorder{ IM_COL32(255, 32, 32, 255) }; // quad selection border color ImU32 mDefaultSlotColor{ IM_COL32(128, 128, 128, 255) }; // when no color is provided in node template, use this value ImU32 mFrameFocus{ IM_COL32(64, 128, 255, 255) }; // rectangle border when graph editor has focus float mLineThickness{ 5 }; // links width in pixels when zoom value is 1 float mGridSize{ 64.f }; // background grid size in pixels when zoom value is 1 float mRounding{ 3.f }; // rounding at node corners float mZoomRatio{ 0.1f }; // factor per mouse wheel delta float mZoomLerpFactor{ 0.25f }; // the smaller, the smoother float mBorderSelectionThickness{ 6.f }; // thickness of selection border around nodes float mBorderThickness{ 6.f }; // thickness of selection border around nodes float mNodeSlotRadius{ 8.f }; // circle radius for inputs and outputs float mNodeSlotHoverFactor{ 1.2f }; // increase size when hovering float mMinZoom{ 0.2f }, mMaxZoom { 1.1f }; float mSnap{ 5.f }; bool mDisplayLinksAsCurves{ true }; // false is straight and 45deg lines bool mAllowQuadSelection{ true }; // multiple selection using drag and drop bool mRenderGrid{ true }; // grid or nothing bool mDrawIONameOnHover{ true }; // only draw node input/output when hovering }; // View state: scroll position and zoom factor struct ViewState { ImVec2 mPosition{0.0f, 0.0f}; // scroll position float mFactor{ 1.0f }; // current zoom factor float mFactorTarget{ 1.0f }; // targeted zoom factor interpolated using Options.mZoomLerpFactor }; struct Template { ImU32 mHeaderColor; ImU32 mBackgroundColor; ImU32 mBackgroundColorOver; ImU8 mInputCount; const char** mInputNames; // can be nullptr. No text displayed. ImU32* mInputColors; // can be nullptr, default slot color will be used. ImU8 mOutputCount; const char** mOutputNames; // can be nullptr. No text displayed. ImU32* mOutputColors; // can be nullptr, default slot color will be used. }; struct Node { const char* mName; TemplateIndex mTemplateIndex; ImRect mRect; bool mSelected{ false }; }; struct Link { NodeIndex mInputNodeIndex; SlotIndex mInputSlotIndex; NodeIndex mOutputNodeIndex; SlotIndex mOutputSlotIndex; }; struct Delegate { virtual bool AllowedLink(NodeIndex from, NodeIndex to) = 0; virtual void SelectNode(NodeIndex nodeIndex, bool selected) = 0; virtual void MoveSelectedNodes(const ImVec2 delta) = 0; virtual void AddLink(NodeIndex inputNodeIndex, SlotIndex inputSlotIndex, NodeIndex outputNodeIndex, SlotIndex outputSlotIndex) = 0; virtual void DelLink(LinkIndex linkIndex) = 0; // user is responsible for clipping virtual void CustomDraw(ImDrawList* drawList, ImRect rectangle, NodeIndex nodeIndex) = 0; // use mouse position to open context menu // if nodeIndex != -1, right click happens on the specified node virtual void RightClick(NodeIndex nodeIndex, SlotIndex slotIndexInput, SlotIndex slotIndexOutput) = 0; virtual const size_t GetTemplateCount() = 0; virtual const Template GetTemplate(TemplateIndex index) = 0; virtual const size_t GetNodeCount() = 0; virtual const Node GetNode(NodeIndex index) = 0; virtual const size_t GetLinkCount() = 0; virtual const Link GetLink(LinkIndex index) = 0; }; void Show(Delegate& delegate, const Options& options, ViewState& viewState, bool enabled, FitOnScreen* fit = nullptr); void GraphEditorClear(); bool EditOptions(Options& options); } // namespace ================================================ FILE: Source/External/imgui_tools/imguizmo/ImCurveEdit.cpp ================================================ // https://github.com/CedricGuillemet/ImGuizmo // v 1.89 WIP // // The MIT License(MIT) // // Copyright(c) 2021 Cedric Guillemet // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files(the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and / or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions : // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #include "ImCurveEdit.h" #include "imgui.h" #include "imgui_internal.h" #include #include #include #if defined(_MSC_VER) || defined(__MINGW32__) #include #endif #if !defined(_MSC_VER) && !defined(__MINGW64_VERSION_MAJOR) #define _malloca(x) alloca(x) #define _freea(x) #endif namespace ImCurveEdit { #ifndef IMGUI_DEFINE_MATH_OPERATORS static ImVec2 operator+(const ImVec2& a, const ImVec2& b) { return ImVec2(a.x + b.x, a.y + b.y); } static ImVec2 operator-(const ImVec2& a, const ImVec2& b) { return ImVec2(a.x - b.x, a.y - b.y); } static ImVec2 operator*(const ImVec2& a, const ImVec2& b) { return ImVec2(a.x * b.x, a.y * b.y); } static ImVec2 operator/(const ImVec2& a, const ImVec2& b) { return ImVec2(a.x / b.x, a.y / b.y); } static ImVec2 operator*(const ImVec2& a, const float b) { return ImVec2(a.x * b, a.y * b); } #endif static float smoothstep(float edge0, float edge1, float x) { x = ImClamp((x - edge0) / (edge1 - edge0), 0.0f, 1.0f); return x * x * (3 - 2 * x); } static float distance(float x, float y, float x1, float y1, float x2, float y2) { float A = x - x1; float B = y - y1; float C = x2 - x1; float D = y2 - y1; float dot = A * C + B * D; float len_sq = C * C + D * D; float param = -1.f; if (len_sq > FLT_EPSILON) param = dot / len_sq; float xx, yy; if (param < 0.f) { xx = x1; yy = y1; } else if (param > 1.f) { xx = x2; yy = y2; } else { xx = x1 + param * C; yy = y1 + param * D; } float dx = x - xx; float dy = y - yy; return sqrtf(dx * dx + dy * dy); } static int DrawPoint(ImDrawList* draw_list, ImVec2 pos, const ImVec2 size, const ImVec2 offset, bool edited) { int ret = 0; ImGuiIO& io = ImGui::GetIO(); static const ImVec2 localOffsets[4] = { ImVec2(1,0), ImVec2(0,1), ImVec2(-1,0), ImVec2(0,-1) }; ImVec2 offsets[4]; for (int i = 0; i < 4; i++) { offsets[i] = pos * size + localOffsets[i] * 4.5f + offset; } const ImVec2 center = pos * size + offset; const ImRect anchor(center - ImVec2(5, 5), center + ImVec2(5, 5)); draw_list->AddConvexPolyFilled(offsets, 4, 0xFF000000); if (anchor.Contains(io.MousePos)) { ret = 1; if (io.MouseDown[0]) ret = 2; } if (edited) draw_list->AddPolyline(offsets, 4, 0xFFFFFFFF, true, 3.0f); else if (ret) draw_list->AddPolyline(offsets, 4, 0xFF80B0FF, true, 2.0f); else draw_list->AddPolyline(offsets, 4, 0xFF0080FF, true, 2.0f); return ret; } int Edit(Delegate& delegate, const ImVec2& size, unsigned int id, const ImRect* clippingRect, ImVector* selectedPoints) { static bool selectingQuad = false; static ImVec2 quadSelection; static int overCurve = -1; static int movingCurve = -1; static bool scrollingV = false; static std::set selection; static bool overSelectedPoint = false; int ret = 0; ImGuiIO& io = ImGui::GetIO(); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); ImGui::PushStyleColor(ImGuiCol_Border, 0); ImGui::BeginChildFrame(id, size); delegate.focused = ImGui::IsWindowFocused(); ImDrawList* draw_list = ImGui::GetWindowDrawList(); if (clippingRect) draw_list->PushClipRect(clippingRect->Min, clippingRect->Max, true); const ImVec2 offset = ImGui::GetCursorScreenPos() + ImVec2(0.f, size.y); const ImVec2 ssize(size.x, -size.y); const ImRect container(offset + ImVec2(0.f, ssize.y), offset + ImVec2(ssize.x, 0.f)); ImVec2& min = delegate.GetMin(); ImVec2& max = delegate.GetMax(); // handle zoom and VScroll if (container.Contains(io.MousePos)) { if (fabsf(io.MouseWheel) > FLT_EPSILON) { const float r = (io.MousePos.y - offset.y) / ssize.y; float ratioY = ImLerp(min.y, max.y, r); auto scaleValue = [&](float v) { v -= ratioY; v *= (1.f - io.MouseWheel * 0.05f); v += ratioY; return v; }; min.y = scaleValue(min.y); max.y = scaleValue(max.y); } if (!scrollingV && ImGui::IsMouseDown(2)) { scrollingV = true; } } ImVec2 range = max - min + ImVec2(1.f, 0.f); // +1 because of inclusive last frame const ImVec2 viewSize(size.x, -size.y); const ImVec2 sizeOfPixel = ImVec2(1.f, 1.f) / viewSize; const size_t curveCount = delegate.GetCurveCount(); if (scrollingV) { float deltaH = io.MouseDelta.y * range.y * sizeOfPixel.y; min.y -= deltaH; max.y -= deltaH; if (!ImGui::IsMouseDown(2)) scrollingV = false; } draw_list->AddRectFilled(offset, offset + ssize, delegate.GetBackgroundColor()); auto pointToRange = [&](ImVec2 pt) { return (pt - min) / range; }; auto rangeToPoint = [&](ImVec2 pt) { return (pt * range) + min; }; draw_list->AddLine(ImVec2(-1.f, -min.y / range.y) * viewSize + offset, ImVec2(1.f, -min.y / range.y) * viewSize + offset, 0xFF000000, 1.5f); bool overCurveOrPoint = false; int localOverCurve = -1; // make sure highlighted curve is rendered last int* curvesIndex = (int*)_malloca(sizeof(int) * curveCount); for (size_t c = 0; c < curveCount; c++) curvesIndex[c] = int(c); int highLightedCurveIndex = -1; if (overCurve != -1 && curveCount) { ImSwap(curvesIndex[overCurve], curvesIndex[curveCount - 1]); highLightedCurveIndex = overCurve; } for (size_t cur = 0; cur < curveCount; cur++) { int c = curvesIndex[cur]; if (!delegate.IsVisible(c)) continue; const size_t ptCount = delegate.GetPointCount(c); if (ptCount < 1) continue; CurveType curveType = delegate.GetCurveType(c); if (curveType == CurveNone) continue; const ImVec2* pts = delegate.GetPoints(c); uint32_t curveColor = delegate.GetCurveColor(c); if ((c == highLightedCurveIndex && selection.empty() && !selectingQuad) || movingCurve == c) curveColor = 0xFFFFFFFF; for (size_t p = 0; p < ptCount - 1; p++) { const ImVec2 p1 = pointToRange(pts[p]); const ImVec2 p2 = pointToRange(pts[p + 1]); if (curveType == CurveSmooth || curveType == CurveLinear) { size_t subStepCount = (curveType == CurveSmooth) ? 20 : 2; float step = 1.f / float(subStepCount - 1); for (size_t substep = 0; substep < subStepCount - 1; substep++) { float t = float(substep) * step; const ImVec2 sp1 = ImLerp(p1, p2, t); const ImVec2 sp2 = ImLerp(p1, p2, t + step); const float rt1 = smoothstep(p1.x, p2.x, sp1.x); const float rt2 = smoothstep(p1.x, p2.x, sp2.x); const ImVec2 pos1 = ImVec2(sp1.x, ImLerp(p1.y, p2.y, rt1)) * viewSize + offset; const ImVec2 pos2 = ImVec2(sp2.x, ImLerp(p1.y, p2.y, rt2)) * viewSize + offset; if (distance(io.MousePos.x, io.MousePos.y, pos1.x, pos1.y, pos2.x, pos2.y) < 8.f && !scrollingV) { localOverCurve = int(c); overCurve = int(c); overCurveOrPoint = true; } draw_list->AddLine(pos1, pos2, curveColor, 1.3f); } // substep } else if (curveType == CurveDiscrete) { ImVec2 dp1 = p1 * viewSize + offset; ImVec2 dp2 = ImVec2(p2.x, p1.y) * viewSize + offset; ImVec2 dp3 = p2 * viewSize + offset; draw_list->AddLine(dp1, dp2, curveColor, 1.3f); draw_list->AddLine(dp2, dp3, curveColor, 1.3f); if ((distance(io.MousePos.x, io.MousePos.y, dp1.x, dp1.y, dp3.x, dp1.y) < 8.f || distance(io.MousePos.x, io.MousePos.y, dp3.x, dp1.y, dp3.x, dp3.y) < 8.f) /*&& localOverCurve == -1*/) { localOverCurve = int(c); overCurve = int(c); overCurveOrPoint = true; } } } // point loop for (size_t p = 0; p < ptCount; p++) { const int drawState = DrawPoint(draw_list, pointToRange(pts[p]), viewSize, offset, (selection.find({ int(c), int(p) }) != selection.end() && movingCurve == -1 && !scrollingV)); if (drawState && movingCurve == -1 && !selectingQuad) { overCurveOrPoint = true; overSelectedPoint = true; overCurve = -1; if (drawState == 2) { if (!io.KeyShift && selection.find({ int(c), int(p) }) == selection.end()) selection.clear(); selection.insert({ int(c), int(p) }); } } } } // curves loop if (localOverCurve == -1) overCurve = -1; // move selection static bool pointsMoved = false; static ImVec2 mousePosOrigin; static std::vector originalPoints; if (overSelectedPoint && io.MouseDown[0]) { if ((fabsf(io.MouseDelta.x) > 0.f || fabsf(io.MouseDelta.y) > 0.f) && !selection.empty()) { if (!pointsMoved) { delegate.BeginEdit(0); mousePosOrigin = io.MousePos; originalPoints.resize(selection.size()); int index = 0; for (auto& sel : selection) { const ImVec2* pts = delegate.GetPoints(sel.curveIndex); originalPoints[index++] = pts[sel.pointIndex]; } } pointsMoved = true; ret = 1; auto prevSelection = selection; int originalIndex = 0; for (auto& sel : prevSelection) { const ImVec2 p = rangeToPoint(pointToRange(originalPoints[originalIndex]) + (io.MousePos - mousePosOrigin) * sizeOfPixel); const int newIndex = delegate.EditPoint(sel.curveIndex, sel.pointIndex, p); if (newIndex != sel.pointIndex) { selection.erase(sel); selection.insert({ sel.curveIndex, newIndex }); } originalIndex++; } } } if (overSelectedPoint && !io.MouseDown[0]) { overSelectedPoint = false; if (pointsMoved) { pointsMoved = false; delegate.EndEdit(); } } // add point if (overCurve != -1 && io.MouseDoubleClicked[0]) { const ImVec2 np = rangeToPoint((io.MousePos - offset) / viewSize); delegate.BeginEdit(overCurve); delegate.AddPoint(overCurve, np); delegate.EndEdit(); ret = 1; } // move curve if (movingCurve != -1) { const size_t ptCount = delegate.GetPointCount(movingCurve); const ImVec2* pts = delegate.GetPoints(movingCurve); if (!pointsMoved) { mousePosOrigin = io.MousePos; pointsMoved = true; originalPoints.resize(ptCount); for (size_t index = 0; index < ptCount; index++) { originalPoints[index] = pts[index]; } } if (ptCount >= 1) { for (size_t p = 0; p < ptCount; p++) { delegate.EditPoint(movingCurve, int(p), rangeToPoint(pointToRange(originalPoints[p]) + (io.MousePos - mousePosOrigin) * sizeOfPixel)); } ret = 1; } if (!io.MouseDown[0]) { movingCurve = -1; pointsMoved = false; delegate.EndEdit(); } } if (movingCurve == -1 && overCurve != -1 && ImGui::IsMouseClicked(0) && selection.empty() && !selectingQuad) { movingCurve = overCurve; delegate.BeginEdit(overCurve); } // quad selection if (selectingQuad) { const ImVec2 bmin = ImMin(quadSelection, io.MousePos); const ImVec2 bmax = ImMax(quadSelection, io.MousePos); draw_list->AddRectFilled(bmin, bmax, 0x40FF0000, 1.f); draw_list->AddRect(bmin, bmax, 0xFFFF0000, 1.f); const ImRect selectionQuad(bmin, bmax); if (!io.MouseDown[0]) { if (!io.KeyShift) selection.clear(); // select everythnig is quad for (size_t c = 0; c < curveCount; c++) { if (!delegate.IsVisible(c)) continue; const size_t ptCount = delegate.GetPointCount(c); if (ptCount < 1) continue; const ImVec2* pts = delegate.GetPoints(c); for (size_t p = 0; p < ptCount; p++) { const ImVec2 center = pointToRange(pts[p]) * viewSize + offset; if (selectionQuad.Contains(center)) selection.insert({ int(c), int(p) }); } } // done selectingQuad = false; } } if (!overCurveOrPoint && ImGui::IsMouseClicked(0) && !selectingQuad && movingCurve == -1 && !overSelectedPoint && container.Contains(io.MousePos)) { selectingQuad = true; quadSelection = io.MousePos; } if (clippingRect) draw_list->PopClipRect(); ImGui::EndChildFrame(); ImGui::PopStyleVar(); ImGui::PopStyleColor(1); if (selectedPoints) { selectedPoints->resize(int(selection.size())); int index = 0; for (auto& point : selection) (*selectedPoints)[index++] = point; } return ret; } } ================================================ FILE: Source/External/imgui_tools/imguizmo/ImCurveEdit.h ================================================ // https://github.com/CedricGuillemet/ImGuizmo // v 1.89 WIP // // The MIT License(MIT) // // Copyright(c) 2021 Cedric Guillemet // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files(the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and / or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions : // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #pragma once #include #include "imgui.h" struct ImRect; namespace ImCurveEdit { enum CurveType { CurveNone, CurveDiscrete, CurveLinear, CurveSmooth, CurveBezier, }; struct EditPoint { int curveIndex; int pointIndex; bool operator <(const EditPoint& other) const { if (curveIndex < other.curveIndex) return true; if (curveIndex > other.curveIndex) return false; if (pointIndex < other.pointIndex) return true; return false; } }; struct Delegate { bool focused = false; virtual size_t GetCurveCount() = 0; virtual bool IsVisible(size_t /*curveIndex*/) { return true; } virtual CurveType GetCurveType(size_t /*curveIndex*/) const { return CurveLinear; } virtual ImVec2& GetMin() = 0; virtual ImVec2& GetMax() = 0; virtual size_t GetPointCount(size_t curveIndex) = 0; virtual uint32_t GetCurveColor(size_t curveIndex) = 0; virtual ImVec2* GetPoints(size_t curveIndex) = 0; virtual int EditPoint(size_t curveIndex, int pointIndex, ImVec2 value) = 0; virtual void AddPoint(size_t curveIndex, ImVec2 value) = 0; virtual unsigned int GetBackgroundColor() { return 0xFF202020; } // handle undo/redo thru this functions virtual void BeginEdit(int /*index*/) {} virtual void EndEdit() {} }; int Edit(Delegate& delegate, const ImVec2& size, unsigned int id, const ImRect* clippingRect = NULL, ImVector* selectedPoints = NULL); } ================================================ FILE: Source/External/imgui_tools/imguizmo/ImGradient.cpp ================================================ // https://github.com/CedricGuillemet/ImGuizmo // v 1.89 WIP // // The MIT License(MIT) // // Copyright(c) 2021 Cedric Guillemet // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files(the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and / or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions : // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #include "ImGradient.h" #include "imgui.h" #include "imgui_internal.h" namespace ImGradient { #ifndef IMGUI_DEFINE_MATH_OPERATORS static inline ImVec2 operator*(const ImVec2& lhs, const float rhs) { return ImVec2(lhs.x * rhs, lhs.y * rhs); } static inline ImVec2 operator/(const ImVec2& lhs, const float rhs) { return ImVec2(lhs.x / rhs, lhs.y / rhs); } static inline ImVec2 operator+(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x + rhs.x, lhs.y + rhs.y); } static inline ImVec2 operator-(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x - rhs.x, lhs.y - rhs.y); } static inline ImVec2 operator*(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x * rhs.x, lhs.y * rhs.y); } static inline ImVec2 operator/(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x / rhs.x, lhs.y / rhs.y); } #endif static int DrawPoint(ImDrawList* draw_list, ImVec4 color, const ImVec2 size, bool editing, ImVec2 pos) { ImGuiIO& io = ImGui::GetIO(); ImVec2 p1 = ImLerp(pos, ImVec2(pos + ImVec2(size.x - size.y, 0.f)), color.w) + ImVec2(3, 3); ImVec2 p2 = ImLerp(pos + ImVec2(size.y, size.y), ImVec2(pos + size), color.w) - ImVec2(3, 3); ImRect rc(p1, p2); color.w = 1.f; draw_list->AddRectFilled(p1, p2, ImColor(color)); if (editing) draw_list->AddRect(p1, p2, 0xFFFFFFFF, 2.f, 15, 2.5f); else draw_list->AddRect(p1, p2, 0x80FFFFFF, 2.f, 15, 1.25f); if (rc.Contains(io.MousePos)) { if (io.MouseClicked[0]) return 2; return 1; } return 0; } bool Edit(Delegate& delegate, const ImVec2& size, int& selection) { bool ret = false; ImGuiIO& io = ImGui::GetIO(); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); ImGui::BeginChildFrame(137, size); ImDrawList* draw_list = ImGui::GetWindowDrawList(); const ImVec2 offset = ImGui::GetCursorScreenPos(); const ImVec4* pts = delegate.GetPoints(); static int currentSelection = -1; static int movingPt = -1; if (currentSelection >= int(delegate.GetPointCount())) currentSelection = -1; if (movingPt != -1) { ImVec4 current = pts[movingPt]; current.w += io.MouseDelta.x / size.x; current.w = ImClamp(current.w, 0.f, 1.f); delegate.EditPoint(movingPt, current); ret = true; if (!io.MouseDown[0]) movingPt = -1; } for (size_t i = 0; i < delegate.GetPointCount(); i++) { int ptSel = DrawPoint(draw_list, pts[i], size, i == currentSelection, offset); if (ptSel == 2) { currentSelection = int(i); ret = true; } if (ptSel == 1 && io.MouseDown[0] && movingPt == -1) { movingPt = int(i); } } ImRect rc(offset, offset + size); if (rc.Contains(io.MousePos) && io.MouseDoubleClicked[0]) { float t = (io.MousePos.x - offset.x) / size.x; delegate.AddPoint(delegate.GetPoint(t)); ret = true; } ImGui::EndChildFrame(); ImGui::PopStyleVar(); selection = currentSelection; return ret; } } ================================================ FILE: Source/External/imgui_tools/imguizmo/ImGradient.h ================================================ // https://github.com/CedricGuillemet/ImGuizmo // v 1.89 WIP // // The MIT License(MIT) // // Copyright(c) 2021 Cedric Guillemet // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files(the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and / or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions : // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #pragma once #include struct ImVec4; struct ImVec2; namespace ImGradient { struct Delegate { virtual size_t GetPointCount() = 0; virtual ImVec4* GetPoints() = 0; virtual int EditPoint(int pointIndex, ImVec4 value) = 0; virtual ImVec4 GetPoint(float t) = 0; virtual void AddPoint(ImVec4 value) = 0; }; bool Edit(Delegate& delegate, const ImVec2& size, int& selection); } ================================================ FILE: Source/External/imgui_tools/imguizmo/ImGuizmo.cpp ================================================ // https://github.com/CedricGuillemet/ImGuizmo // v 1.89 WIP // // The MIT License(MIT) // // Copyright(c) 2021 Cedric Guillemet // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files(the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and / or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions : // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #ifndef IMGUI_DEFINE_MATH_OPERATORS #define IMGUI_DEFINE_MATH_OPERATORS #endif #include "imgui.h" #include "imgui_internal.h" #include "ImGuizmo.h" #if defined(_MSC_VER) || defined(__MINGW32__) #include #endif #if !defined(_MSC_VER) && !defined(__MINGW64_VERSION_MAJOR) #define _malloca(x) alloca(x) #define _freea(x) #endif // includes patches for multiview from // https://github.com/CedricGuillemet/ImGuizmo/issues/15 namespace IMGUIZMO_NAMESPACE { static const float ZPI = 3.14159265358979323846f; static const float RAD2DEG = (180.f / ZPI); static const float DEG2RAD = (ZPI / 180.f); const float screenRotateSize = 0.06f; // scale a bit so translate axis do not touch when in universal const float rotationDisplayFactor = 1.2f; static OPERATION operator&(OPERATION lhs, OPERATION rhs) { return static_cast(static_cast(lhs) & static_cast(rhs)); } static bool operator!=(OPERATION lhs, int rhs) { return static_cast(lhs) != rhs; } static bool Intersects(OPERATION lhs, OPERATION rhs) { return (lhs & rhs) != 0; } // True if lhs contains rhs static bool Contains(OPERATION lhs, OPERATION rhs) { return (lhs & rhs) == rhs; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // utility and math void FPU_MatrixF_x_MatrixF(const float* a, const float* b, float* r) { r[0] = a[0] * b[0] + a[1] * b[4] + a[2] * b[8] + a[3] * b[12]; r[1] = a[0] * b[1] + a[1] * b[5] + a[2] * b[9] + a[3] * b[13]; r[2] = a[0] * b[2] + a[1] * b[6] + a[2] * b[10] + a[3] * b[14]; r[3] = a[0] * b[3] + a[1] * b[7] + a[2] * b[11] + a[3] * b[15]; r[4] = a[4] * b[0] + a[5] * b[4] + a[6] * b[8] + a[7] * b[12]; r[5] = a[4] * b[1] + a[5] * b[5] + a[6] * b[9] + a[7] * b[13]; r[6] = a[4] * b[2] + a[5] * b[6] + a[6] * b[10] + a[7] * b[14]; r[7] = a[4] * b[3] + a[5] * b[7] + a[6] * b[11] + a[7] * b[15]; r[8] = a[8] * b[0] + a[9] * b[4] + a[10] * b[8] + a[11] * b[12]; r[9] = a[8] * b[1] + a[9] * b[5] + a[10] * b[9] + a[11] * b[13]; r[10] = a[8] * b[2] + a[9] * b[6] + a[10] * b[10] + a[11] * b[14]; r[11] = a[8] * b[3] + a[9] * b[7] + a[10] * b[11] + a[11] * b[15]; r[12] = a[12] * b[0] + a[13] * b[4] + a[14] * b[8] + a[15] * b[12]; r[13] = a[12] * b[1] + a[13] * b[5] + a[14] * b[9] + a[15] * b[13]; r[14] = a[12] * b[2] + a[13] * b[6] + a[14] * b[10] + a[15] * b[14]; r[15] = a[12] * b[3] + a[13] * b[7] + a[14] * b[11] + a[15] * b[15]; } void Frustum(float left, float right, float bottom, float top, float znear, float zfar, float* m16) { float temp, temp2, temp3, temp4; temp = 2.0f * znear; temp2 = right - left; temp3 = top - bottom; temp4 = zfar - znear; m16[0] = temp / temp2; m16[1] = 0.0; m16[2] = 0.0; m16[3] = 0.0; m16[4] = 0.0; m16[5] = temp / temp3; m16[6] = 0.0; m16[7] = 0.0; m16[8] = (right + left) / temp2; m16[9] = (top + bottom) / temp3; m16[10] = (-zfar - znear) / temp4; m16[11] = -1.0f; m16[12] = 0.0; m16[13] = 0.0; m16[14] = (-temp * zfar) / temp4; m16[15] = 0.0; } void Perspective(float fovyInDegrees, float aspectRatio, float znear, float zfar, float* m16) { float ymax, xmax; ymax = znear * tanf(fovyInDegrees * DEG2RAD); xmax = ymax * aspectRatio; Frustum(-xmax, xmax, -ymax, ymax, znear, zfar, m16); } void Cross(const float* a, const float* b, float* r) { r[0] = a[1] * b[2] - a[2] * b[1]; r[1] = a[2] * b[0] - a[0] * b[2]; r[2] = a[0] * b[1] - a[1] * b[0]; } float Dot(const float* a, const float* b) { return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; } void Normalize(const float* a, float* r) { float il = 1.f / (sqrtf(Dot(a, a)) + FLT_EPSILON); r[0] = a[0] * il; r[1] = a[1] * il; r[2] = a[2] * il; } void LookAt(const float* eye, const float* at, const float* up, float* m16) { float X[3], Y[3], Z[3], tmp[3]; tmp[0] = eye[0] - at[0]; tmp[1] = eye[1] - at[1]; tmp[2] = eye[2] - at[2]; Normalize(tmp, Z); Normalize(up, Y); Cross(Y, Z, tmp); Normalize(tmp, X); Cross(Z, X, tmp); Normalize(tmp, Y); m16[0] = X[0]; m16[1] = Y[0]; m16[2] = Z[0]; m16[3] = 0.0f; m16[4] = X[1]; m16[5] = Y[1]; m16[6] = Z[1]; m16[7] = 0.0f; m16[8] = X[2]; m16[9] = Y[2]; m16[10] = Z[2]; m16[11] = 0.0f; m16[12] = -Dot(X, eye); m16[13] = -Dot(Y, eye); m16[14] = -Dot(Z, eye); m16[15] = 1.0f; } template T Clamp(T x, T y, T z) { return ((x < y) ? y : ((x > z) ? z : x)); } template T max(T x, T y) { return (x > y) ? x : y; } template T min(T x, T y) { return (x < y) ? x : y; } template bool IsWithin(T x, T y, T z) { return (x >= y) && (x <= z); } struct matrix_t; struct vec_t { public: float x, y, z, w; void Lerp(const vec_t& v, float t) { x += (v.x - x) * t; y += (v.y - y) * t; z += (v.z - z) * t; w += (v.w - w) * t; } void Set(float v) { x = y = z = w = v; } void Set(float _x, float _y, float _z = 0.f, float _w = 0.f) { x = _x; y = _y; z = _z; w = _w; } vec_t& operator -= (const vec_t& v) { x -= v.x; y -= v.y; z -= v.z; w -= v.w; return *this; } vec_t& operator += (const vec_t& v) { x += v.x; y += v.y; z += v.z; w += v.w; return *this; } vec_t& operator *= (const vec_t& v) { x *= v.x; y *= v.y; z *= v.z; w *= v.w; return *this; } vec_t& operator *= (float v) { x *= v; y *= v; z *= v; w *= v; return *this; } vec_t operator * (float f) const; vec_t operator - () const; vec_t operator - (const vec_t& v) const; vec_t operator + (const vec_t& v) const; vec_t operator * (const vec_t& v) const; const vec_t& operator + () const { return (*this); } float Length() const { return sqrtf(x * x + y * y + z * z); }; float LengthSq() const { return (x * x + y * y + z * z); }; vec_t Normalize() { (*this) *= (1.f / ( Length() > FLT_EPSILON ? Length() : FLT_EPSILON ) ); return (*this); } vec_t Normalize(const vec_t& v) { this->Set(v.x, v.y, v.z, v.w); this->Normalize(); return (*this); } vec_t Abs() const; void Cross(const vec_t& v) { vec_t res; res.x = y * v.z - z * v.y; res.y = z * v.x - x * v.z; res.z = x * v.y - y * v.x; x = res.x; y = res.y; z = res.z; w = 0.f; } void Cross(const vec_t& v1, const vec_t& v2) { x = v1.y * v2.z - v1.z * v2.y; y = v1.z * v2.x - v1.x * v2.z; z = v1.x * v2.y - v1.y * v2.x; w = 0.f; } float Dot(const vec_t& v) const { return (x * v.x) + (y * v.y) + (z * v.z) + (w * v.w); } float Dot3(const vec_t& v) const { return (x * v.x) + (y * v.y) + (z * v.z); } void Transform(const matrix_t& matrix); void Transform(const vec_t& s, const matrix_t& matrix); void TransformVector(const matrix_t& matrix); void TransformPoint(const matrix_t& matrix); void TransformVector(const vec_t& v, const matrix_t& matrix) { (*this) = v; this->TransformVector(matrix); } void TransformPoint(const vec_t& v, const matrix_t& matrix) { (*this) = v; this->TransformPoint(matrix); } float& operator [] (size_t index) { return ((float*)&x)[index]; } const float& operator [] (size_t index) const { return ((float*)&x)[index]; } bool operator!=(const vec_t& other) const { return memcmp(this, &other, sizeof(vec_t)) != 0; } }; vec_t makeVect(float _x, float _y, float _z = 0.f, float _w = 0.f) { vec_t res; res.x = _x; res.y = _y; res.z = _z; res.w = _w; return res; } vec_t makeVect(ImVec2 v) { vec_t res; res.x = v.x; res.y = v.y; res.z = 0.f; res.w = 0.f; return res; } vec_t vec_t::operator * (float f) const { return makeVect(x * f, y * f, z * f, w * f); } vec_t vec_t::operator - () const { return makeVect(-x, -y, -z, -w); } vec_t vec_t::operator - (const vec_t& v) const { return makeVect(x - v.x, y - v.y, z - v.z, w - v.w); } vec_t vec_t::operator + (const vec_t& v) const { return makeVect(x + v.x, y + v.y, z + v.z, w + v.w); } vec_t vec_t::operator * (const vec_t& v) const { return makeVect(x * v.x, y * v.y, z * v.z, w * v.w); } vec_t vec_t::Abs() const { return makeVect(fabsf(x), fabsf(y), fabsf(z)); } vec_t Normalized(const vec_t& v) { vec_t res; res = v; res.Normalize(); return res; } vec_t Cross(const vec_t& v1, const vec_t& v2) { vec_t res; res.x = v1.y * v2.z - v1.z * v2.y; res.y = v1.z * v2.x - v1.x * v2.z; res.z = v1.x * v2.y - v1.y * v2.x; res.w = 0.f; return res; } float Dot(const vec_t& v1, const vec_t& v2) { return (v1.x * v2.x) + (v1.y * v2.y) + (v1.z * v2.z); } vec_t BuildPlan(const vec_t& p_point1, const vec_t& p_normal) { vec_t normal, res; normal.Normalize(p_normal); res.w = normal.Dot(p_point1); res.x = normal.x; res.y = normal.y; res.z = normal.z; return res; } struct matrix_t { public: union { float m[4][4]; float m16[16]; struct { vec_t right, up, dir, position; } v; vec_t component[4]; }; operator float* () { return m16; } operator const float* () const { return m16; } void Translation(float _x, float _y, float _z) { this->Translation(makeVect(_x, _y, _z)); } void Translation(const vec_t& vt) { v.right.Set(1.f, 0.f, 0.f, 0.f); v.up.Set(0.f, 1.f, 0.f, 0.f); v.dir.Set(0.f, 0.f, 1.f, 0.f); v.position.Set(vt.x, vt.y, vt.z, 1.f); } void Scale(float _x, float _y, float _z) { v.right.Set(_x, 0.f, 0.f, 0.f); v.up.Set(0.f, _y, 0.f, 0.f); v.dir.Set(0.f, 0.f, _z, 0.f); v.position.Set(0.f, 0.f, 0.f, 1.f); } void Scale(const vec_t& s) { Scale(s.x, s.y, s.z); } matrix_t& operator *= (const matrix_t& mat) { matrix_t tmpMat; tmpMat = *this; tmpMat.Multiply(mat); *this = tmpMat; return *this; } matrix_t operator * (const matrix_t& mat) const { matrix_t matT; matT.Multiply(*this, mat); return matT; } void Multiply(const matrix_t& matrix) { matrix_t tmp; tmp = *this; FPU_MatrixF_x_MatrixF((float*)&tmp, (float*)&matrix, (float*)this); } void Multiply(const matrix_t& m1, const matrix_t& m2) { FPU_MatrixF_x_MatrixF((float*)&m1, (float*)&m2, (float*)this); } float GetDeterminant() const { return m[0][0] * m[1][1] * m[2][2] + m[0][1] * m[1][2] * m[2][0] + m[0][2] * m[1][0] * m[2][1] - m[0][2] * m[1][1] * m[2][0] - m[0][1] * m[1][0] * m[2][2] - m[0][0] * m[1][2] * m[2][1]; } float Inverse(const matrix_t& srcMatrix, bool affine = false); void SetToIdentity() { v.right.Set(1.f, 0.f, 0.f, 0.f); v.up.Set(0.f, 1.f, 0.f, 0.f); v.dir.Set(0.f, 0.f, 1.f, 0.f); v.position.Set(0.f, 0.f, 0.f, 1.f); } void Transpose() { matrix_t tmpm; for (int l = 0; l < 4; l++) { for (int c = 0; c < 4; c++) { tmpm.m[l][c] = m[c][l]; } } (*this) = tmpm; } void RotationAxis(const vec_t& axis, float angle); void OrthoNormalize() { v.right.Normalize(); v.up.Normalize(); v.dir.Normalize(); } }; void vec_t::Transform(const matrix_t& matrix) { vec_t out; out.x = x * matrix.m[0][0] + y * matrix.m[1][0] + z * matrix.m[2][0] + w * matrix.m[3][0]; out.y = x * matrix.m[0][1] + y * matrix.m[1][1] + z * matrix.m[2][1] + w * matrix.m[3][1]; out.z = x * matrix.m[0][2] + y * matrix.m[1][2] + z * matrix.m[2][2] + w * matrix.m[3][2]; out.w = x * matrix.m[0][3] + y * matrix.m[1][3] + z * matrix.m[2][3] + w * matrix.m[3][3]; x = out.x; y = out.y; z = out.z; w = out.w; } void vec_t::Transform(const vec_t& s, const matrix_t& matrix) { *this = s; Transform(matrix); } void vec_t::TransformPoint(const matrix_t& matrix) { vec_t out; out.x = x * matrix.m[0][0] + y * matrix.m[1][0] + z * matrix.m[2][0] + matrix.m[3][0]; out.y = x * matrix.m[0][1] + y * matrix.m[1][1] + z * matrix.m[2][1] + matrix.m[3][1]; out.z = x * matrix.m[0][2] + y * matrix.m[1][2] + z * matrix.m[2][2] + matrix.m[3][2]; out.w = x * matrix.m[0][3] + y * matrix.m[1][3] + z * matrix.m[2][3] + matrix.m[3][3]; x = out.x; y = out.y; z = out.z; w = out.w; } void vec_t::TransformVector(const matrix_t& matrix) { vec_t out; out.x = x * matrix.m[0][0] + y * matrix.m[1][0] + z * matrix.m[2][0]; out.y = x * matrix.m[0][1] + y * matrix.m[1][1] + z * matrix.m[2][1]; out.z = x * matrix.m[0][2] + y * matrix.m[1][2] + z * matrix.m[2][2]; out.w = x * matrix.m[0][3] + y * matrix.m[1][3] + z * matrix.m[2][3]; x = out.x; y = out.y; z = out.z; w = out.w; } float matrix_t::Inverse(const matrix_t& srcMatrix, bool affine) { float det = 0; if (affine) { det = GetDeterminant(); float s = 1 / det; m[0][0] = (srcMatrix.m[1][1] * srcMatrix.m[2][2] - srcMatrix.m[1][2] * srcMatrix.m[2][1]) * s; m[0][1] = (srcMatrix.m[2][1] * srcMatrix.m[0][2] - srcMatrix.m[2][2] * srcMatrix.m[0][1]) * s; m[0][2] = (srcMatrix.m[0][1] * srcMatrix.m[1][2] - srcMatrix.m[0][2] * srcMatrix.m[1][1]) * s; m[1][0] = (srcMatrix.m[1][2] * srcMatrix.m[2][0] - srcMatrix.m[1][0] * srcMatrix.m[2][2]) * s; m[1][1] = (srcMatrix.m[2][2] * srcMatrix.m[0][0] - srcMatrix.m[2][0] * srcMatrix.m[0][2]) * s; m[1][2] = (srcMatrix.m[0][2] * srcMatrix.m[1][0] - srcMatrix.m[0][0] * srcMatrix.m[1][2]) * s; m[2][0] = (srcMatrix.m[1][0] * srcMatrix.m[2][1] - srcMatrix.m[1][1] * srcMatrix.m[2][0]) * s; m[2][1] = (srcMatrix.m[2][0] * srcMatrix.m[0][1] - srcMatrix.m[2][1] * srcMatrix.m[0][0]) * s; m[2][2] = (srcMatrix.m[0][0] * srcMatrix.m[1][1] - srcMatrix.m[0][1] * srcMatrix.m[1][0]) * s; m[3][0] = -(m[0][0] * srcMatrix.m[3][0] + m[1][0] * srcMatrix.m[3][1] + m[2][0] * srcMatrix.m[3][2]); m[3][1] = -(m[0][1] * srcMatrix.m[3][0] + m[1][1] * srcMatrix.m[3][1] + m[2][1] * srcMatrix.m[3][2]); m[3][2] = -(m[0][2] * srcMatrix.m[3][0] + m[1][2] * srcMatrix.m[3][1] + m[2][2] * srcMatrix.m[3][2]); } else { // transpose matrix float src[16]; for (int i = 0; i < 4; ++i) { src[i] = srcMatrix.m16[i * 4]; src[i + 4] = srcMatrix.m16[i * 4 + 1]; src[i + 8] = srcMatrix.m16[i * 4 + 2]; src[i + 12] = srcMatrix.m16[i * 4 + 3]; } // calculate pairs for first 8 elements (cofactors) float tmp[12]; // temp array for pairs tmp[0] = src[10] * src[15]; tmp[1] = src[11] * src[14]; tmp[2] = src[9] * src[15]; tmp[3] = src[11] * src[13]; tmp[4] = src[9] * src[14]; tmp[5] = src[10] * src[13]; tmp[6] = src[8] * src[15]; tmp[7] = src[11] * src[12]; tmp[8] = src[8] * src[14]; tmp[9] = src[10] * src[12]; tmp[10] = src[8] * src[13]; tmp[11] = src[9] * src[12]; // calculate first 8 elements (cofactors) m16[0] = (tmp[0] * src[5] + tmp[3] * src[6] + tmp[4] * src[7]) - (tmp[1] * src[5] + tmp[2] * src[6] + tmp[5] * src[7]); m16[1] = (tmp[1] * src[4] + tmp[6] * src[6] + tmp[9] * src[7]) - (tmp[0] * src[4] + tmp[7] * src[6] + tmp[8] * src[7]); m16[2] = (tmp[2] * src[4] + tmp[7] * src[5] + tmp[10] * src[7]) - (tmp[3] * src[4] + tmp[6] * src[5] + tmp[11] * src[7]); m16[3] = (tmp[5] * src[4] + tmp[8] * src[5] + tmp[11] * src[6]) - (tmp[4] * src[4] + tmp[9] * src[5] + tmp[10] * src[6]); m16[4] = (tmp[1] * src[1] + tmp[2] * src[2] + tmp[5] * src[3]) - (tmp[0] * src[1] + tmp[3] * src[2] + tmp[4] * src[3]); m16[5] = (tmp[0] * src[0] + tmp[7] * src[2] + tmp[8] * src[3]) - (tmp[1] * src[0] + tmp[6] * src[2] + tmp[9] * src[3]); m16[6] = (tmp[3] * src[0] + tmp[6] * src[1] + tmp[11] * src[3]) - (tmp[2] * src[0] + tmp[7] * src[1] + tmp[10] * src[3]); m16[7] = (tmp[4] * src[0] + tmp[9] * src[1] + tmp[10] * src[2]) - (tmp[5] * src[0] + tmp[8] * src[1] + tmp[11] * src[2]); // calculate pairs for second 8 elements (cofactors) tmp[0] = src[2] * src[7]; tmp[1] = src[3] * src[6]; tmp[2] = src[1] * src[7]; tmp[3] = src[3] * src[5]; tmp[4] = src[1] * src[6]; tmp[5] = src[2] * src[5]; tmp[6] = src[0] * src[7]; tmp[7] = src[3] * src[4]; tmp[8] = src[0] * src[6]; tmp[9] = src[2] * src[4]; tmp[10] = src[0] * src[5]; tmp[11] = src[1] * src[4]; // calculate second 8 elements (cofactors) m16[8] = (tmp[0] * src[13] + tmp[3] * src[14] + tmp[4] * src[15]) - (tmp[1] * src[13] + tmp[2] * src[14] + tmp[5] * src[15]); m16[9] = (tmp[1] * src[12] + tmp[6] * src[14] + tmp[9] * src[15]) - (tmp[0] * src[12] + tmp[7] * src[14] + tmp[8] * src[15]); m16[10] = (tmp[2] * src[12] + tmp[7] * src[13] + tmp[10] * src[15]) - (tmp[3] * src[12] + tmp[6] * src[13] + tmp[11] * src[15]); m16[11] = (tmp[5] * src[12] + tmp[8] * src[13] + tmp[11] * src[14]) - (tmp[4] * src[12] + tmp[9] * src[13] + tmp[10] * src[14]); m16[12] = (tmp[2] * src[10] + tmp[5] * src[11] + tmp[1] * src[9]) - (tmp[4] * src[11] + tmp[0] * src[9] + tmp[3] * src[10]); m16[13] = (tmp[8] * src[11] + tmp[0] * src[8] + tmp[7] * src[10]) - (tmp[6] * src[10] + tmp[9] * src[11] + tmp[1] * src[8]); m16[14] = (tmp[6] * src[9] + tmp[11] * src[11] + tmp[3] * src[8]) - (tmp[10] * src[11] + tmp[2] * src[8] + tmp[7] * src[9]); m16[15] = (tmp[10] * src[10] + tmp[4] * src[8] + tmp[9] * src[9]) - (tmp[8] * src[9] + tmp[11] * src[10] + tmp[5] * src[8]); // calculate determinant det = src[0] * m16[0] + src[1] * m16[1] + src[2] * m16[2] + src[3] * m16[3]; // calculate matrix inverse float invdet = 1 / det; for (int j = 0; j < 16; ++j) { m16[j] *= invdet; } } return det; } void matrix_t::RotationAxis(const vec_t& axis, float angle) { float length2 = axis.LengthSq(); if (length2 < FLT_EPSILON) { SetToIdentity(); return; } vec_t n = axis * (1.f / sqrtf(length2)); float s = sinf(angle); float c = cosf(angle); float k = 1.f - c; float xx = n.x * n.x * k + c; float yy = n.y * n.y * k + c; float zz = n.z * n.z * k + c; float xy = n.x * n.y * k; float yz = n.y * n.z * k; float zx = n.z * n.x * k; float xs = n.x * s; float ys = n.y * s; float zs = n.z * s; m[0][0] = xx; m[0][1] = xy + zs; m[0][2] = zx - ys; m[0][3] = 0.f; m[1][0] = xy - zs; m[1][1] = yy; m[1][2] = yz + xs; m[1][3] = 0.f; m[2][0] = zx + ys; m[2][1] = yz - xs; m[2][2] = zz; m[2][3] = 0.f; m[3][0] = 0.f; m[3][1] = 0.f; m[3][2] = 0.f; m[3][3] = 1.f; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // enum MOVETYPE { MT_NONE, MT_MOVE_X, MT_MOVE_Y, MT_MOVE_Z, MT_MOVE_YZ, MT_MOVE_ZX, MT_MOVE_XY, MT_MOVE_SCREEN, MT_ROTATE_X, MT_ROTATE_Y, MT_ROTATE_Z, MT_ROTATE_SCREEN, MT_SCALE_X, MT_SCALE_Y, MT_SCALE_Z, MT_SCALE_XYZ }; static bool IsTranslateType(int type) { return type >= MT_MOVE_X && type <= MT_MOVE_SCREEN; } static bool IsRotateType(int type) { return type >= MT_ROTATE_X && type <= MT_ROTATE_SCREEN; } static bool IsScaleType(int type) { return type >= MT_SCALE_X && type <= MT_SCALE_XYZ; } // Matches MT_MOVE_AB order static const OPERATION TRANSLATE_PLANS[3] = { TRANSLATE_Y | TRANSLATE_Z, TRANSLATE_X | TRANSLATE_Z, TRANSLATE_X | TRANSLATE_Y }; Style::Style() { // default values TranslationLineThickness = 3.0f; TranslationLineArrowSize = 6.0f; RotationLineThickness = 2.0f; RotationOuterLineThickness = 3.0f; ScaleLineThickness = 3.0f; ScaleLineCircleSize = 6.0f; HatchedAxisLineThickness = 6.0f; CenterCircleSize = 6.0f; // initialize default colors Colors[DIRECTION_X] = ImVec4(0.666f, 0.000f, 0.000f, 1.000f); Colors[DIRECTION_Y] = ImVec4(0.000f, 0.666f, 0.000f, 1.000f); Colors[DIRECTION_Z] = ImVec4(0.000f, 0.000f, 0.666f, 1.000f); Colors[PLANE_X] = ImVec4(0.666f, 0.000f, 0.000f, 0.380f); Colors[PLANE_Y] = ImVec4(0.000f, 0.666f, 0.000f, 0.380f); Colors[PLANE_Z] = ImVec4(0.000f, 0.000f, 0.666f, 0.380f); Colors[SELECTION] = ImVec4(1.000f, 0.500f, 0.062f, 0.541f); Colors[INACTIVE] = ImVec4(0.600f, 0.600f, 0.600f, 0.600f); Colors[TRANSLATION_LINE] = ImVec4(0.666f, 0.666f, 0.666f, 0.666f); Colors[SCALE_LINE] = ImVec4(0.250f, 0.250f, 0.250f, 1.000f); Colors[ROTATION_USING_BORDER] = ImVec4(1.000f, 0.500f, 0.062f, 1.000f); Colors[ROTATION_USING_FILL] = ImVec4(1.000f, 0.500f, 0.062f, 0.500f); Colors[HATCHED_AXIS_LINES] = ImVec4(0.000f, 0.000f, 0.000f, 0.500f); Colors[TEXT] = ImVec4(1.000f, 1.000f, 1.000f, 1.000f); Colors[TEXT_SHADOW] = ImVec4(0.000f, 0.000f, 0.000f, 1.000f); } struct Context { Context() : mbUsing(false), mbEnable(true), mbUsingBounds(false) { } ImDrawList* mDrawList; Style mStyle; MODE mMode; matrix_t mViewMat; matrix_t mProjectionMat; matrix_t mModel; matrix_t mModelLocal; // orthonormalized model matrix_t mModelInverse; matrix_t mModelSource; matrix_t mModelSourceInverse; matrix_t mMVP; matrix_t mMVPLocal; // MVP with full model matrix whereas mMVP's model matrix might only be translation in case of World space edition matrix_t mViewProjection; vec_t mModelScaleOrigin; vec_t mCameraEye; vec_t mCameraRight; vec_t mCameraDir; vec_t mCameraUp; vec_t mRayOrigin; vec_t mRayVector; float mRadiusSquareCenter; ImVec2 mScreenSquareCenter; ImVec2 mScreenSquareMin; ImVec2 mScreenSquareMax; float mScreenFactor; vec_t mRelativeOrigin; bool mbUsing; bool mbEnable; bool mbMouseOver; bool mReversed; // reversed projection matrix // translation vec_t mTranslationPlan; vec_t mTranslationPlanOrigin; vec_t mMatrixOrigin; vec_t mTranslationLastDelta; // rotation vec_t mRotationVectorSource; float mRotationAngle; float mRotationAngleOrigin; //vec_t mWorldToLocalAxis; // scale vec_t mScale; vec_t mScaleValueOrigin; vec_t mScaleLast; float mSaveMousePosx; // save axis factor when using gizmo bool mBelowAxisLimit[3]; bool mBelowPlaneLimit[3]; float mAxisFactor[3]; // bounds stretching vec_t mBoundsPivot; vec_t mBoundsAnchor; vec_t mBoundsPlan; vec_t mBoundsLocalPivot; int mBoundsBestAxis; int mBoundsAxis[2]; bool mbUsingBounds; matrix_t mBoundsMatrix; // int mCurrentOperation; float mX = 0.f; float mY = 0.f; float mWidth = 0.f; float mHeight = 0.f; float mXMax = 0.f; float mYMax = 0.f; float mDisplayRatio = 1.f; bool mIsOrthographic = false; int mActualID = -1; int mEditingID = -1; OPERATION mOperation = OPERATION(-1); bool mAllowAxisFlip = true; float mGizmoSizeClipSpace = 0.1f; }; static Context gContext; static const vec_t directionUnary[3] = { makeVect(1.f, 0.f, 0.f), makeVect(0.f, 1.f, 0.f), makeVect(0.f, 0.f, 1.f) }; static const char* translationInfoMask[] = { "X : %5.3f", "Y : %5.3f", "Z : %5.3f", "Y : %5.3f Z : %5.3f", "X : %5.3f Z : %5.3f", "X : %5.3f Y : %5.3f", "X : %5.3f Y : %5.3f Z : %5.3f" }; static const char* scaleInfoMask[] = { "X : %5.2f", "Y : %5.2f", "Z : %5.2f", "XYZ : %5.2f" }; static const char* rotationInfoMask[] = { "X : %5.2f deg %5.2f rad", "Y : %5.2f deg %5.2f rad", "Z : %5.2f deg %5.2f rad", "Screen : %5.2f deg %5.2f rad" }; static const int translationInfoIndex[] = { 0,0,0, 1,0,0, 2,0,0, 1,2,0, 0,2,0, 0,1,0, 0,1,2 }; static const float quadMin = 0.5f; static const float quadMax = 0.8f; static const float quadUV[8] = { quadMin, quadMin, quadMin, quadMax, quadMax, quadMax, quadMax, quadMin }; static const int halfCircleSegmentCount = 64; static const float snapTension = 0.5f; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // static int GetMoveType(OPERATION op, vec_t* gizmoHitProportion); static int GetRotateType(OPERATION op); static int GetScaleType(OPERATION op); Style& GetStyle() { return gContext.mStyle; } static ImU32 GetColorU32(int idx) { IM_ASSERT(idx < COLOR::COUNT); return ImGui::ColorConvertFloat4ToU32(gContext.mStyle.Colors[idx]); } static ImVec2 worldToPos(const vec_t& worldPos, const matrix_t& mat, ImVec2 position = ImVec2(gContext.mX, gContext.mY), ImVec2 size = ImVec2(gContext.mWidth, gContext.mHeight)) { vec_t trans; trans.TransformPoint(worldPos, mat); trans *= 0.5f / trans.w; trans += makeVect(0.5f, 0.5f); trans.y = 1.f - trans.y; trans.x *= size.x; trans.y *= size.y; trans.x += position.x; trans.y += position.y; return ImVec2(trans.x, trans.y); } static void ComputeCameraRay(vec_t& rayOrigin, vec_t& rayDir, ImVec2 position = ImVec2(gContext.mX, gContext.mY), ImVec2 size = ImVec2(gContext.mWidth, gContext.mHeight)) { ImGuiIO& io = ImGui::GetIO(); matrix_t mViewProjInverse; mViewProjInverse.Inverse(gContext.mViewMat * gContext.mProjectionMat); const float mox = ((io.MousePos.x - position.x) / size.x) * 2.f - 1.f; const float moy = (1.f - ((io.MousePos.y - position.y) / size.y)) * 2.f - 1.f; const float zNear = gContext.mReversed ? (1.f - FLT_EPSILON) : 0.f; const float zFar = gContext.mReversed ? 0.f : (1.f - FLT_EPSILON); rayOrigin.Transform(makeVect(mox, moy, zNear, 1.f), mViewProjInverse); rayOrigin *= 1.f / rayOrigin.w; vec_t rayEnd; rayEnd.Transform(makeVect(mox, moy, zFar, 1.f), mViewProjInverse); rayEnd *= 1.f / rayEnd.w; rayDir = Normalized(rayEnd - rayOrigin); } static float GetSegmentLengthClipSpace(const vec_t& start, const vec_t& end, const bool localCoordinates = false) { vec_t startOfSegment = start; const matrix_t& mvp = localCoordinates ? gContext.mMVPLocal : gContext.mMVP; startOfSegment.TransformPoint(mvp); if (fabsf(startOfSegment.w) > FLT_EPSILON) // check for axis aligned with camera direction { startOfSegment *= 1.f / startOfSegment.w; } vec_t endOfSegment = end; endOfSegment.TransformPoint(mvp); if (fabsf(endOfSegment.w) > FLT_EPSILON) // check for axis aligned with camera direction { endOfSegment *= 1.f / endOfSegment.w; } vec_t clipSpaceAxis = endOfSegment - startOfSegment; clipSpaceAxis.y /= gContext.mDisplayRatio; float segmentLengthInClipSpace = sqrtf(clipSpaceAxis.x * clipSpaceAxis.x + clipSpaceAxis.y * clipSpaceAxis.y); return segmentLengthInClipSpace; } static float GetParallelogram(const vec_t& ptO, const vec_t& ptA, const vec_t& ptB) { vec_t pts[] = { ptO, ptA, ptB }; for (unsigned int i = 0; i < 3; i++) { pts[i].TransformPoint(gContext.mMVP); if (fabsf(pts[i].w) > FLT_EPSILON) // check for axis aligned with camera direction { pts[i] *= 1.f / pts[i].w; } } vec_t segA = pts[1] - pts[0]; vec_t segB = pts[2] - pts[0]; segA.y /= gContext.mDisplayRatio; segB.y /= gContext.mDisplayRatio; vec_t segAOrtho = makeVect(-segA.y, segA.x); segAOrtho.Normalize(); float dt = segAOrtho.Dot3(segB); float surface = sqrtf(segA.x * segA.x + segA.y * segA.y) * fabsf(dt); return surface; } inline vec_t PointOnSegment(const vec_t& point, const vec_t& vertPos1, const vec_t& vertPos2) { vec_t c = point - vertPos1; vec_t V; V.Normalize(vertPos2 - vertPos1); float d = (vertPos2 - vertPos1).Length(); float t = V.Dot3(c); if (t < 0.f) { return vertPos1; } if (t > d) { return vertPos2; } return vertPos1 + V * t; } static float IntersectRayPlane(const vec_t& rOrigin, const vec_t& rVector, const vec_t& plan) { const float numer = plan.Dot3(rOrigin) - plan.w; const float denom = plan.Dot3(rVector); if (fabsf(denom) < FLT_EPSILON) // normal is orthogonal to vector, cant intersect { return -1.0f; } return -(numer / denom); } static float DistanceToPlane(const vec_t& point, const vec_t& plan) { return plan.Dot3(point) + plan.w; } static bool IsInContextRect(ImVec2 p) { return IsWithin(p.x, gContext.mX, gContext.mXMax) && IsWithin(p.y, gContext.mY, gContext.mYMax); } static bool IsHoveringWindow() { ImGuiContext& g = *ImGui::GetCurrentContext(); ImGuiWindow* window = ImGui::FindWindowByName(gContext.mDrawList->_OwnerName); if (g.HoveredWindow == window) // Mouse hovering drawlist window return true; if (g.HoveredWindow != NULL) // Any other window is hovered return false; if (ImGui::IsMouseHoveringRect(window->InnerRect.Min, window->InnerRect.Max, false)) // Hovering drawlist window rect, while no other window is hovered (for _NoInputs windows) return true; return false; } void SetRect(float x, float y, float width, float height) { gContext.mX = x; gContext.mY = y; gContext.mWidth = width; gContext.mHeight = height; gContext.mXMax = gContext.mX + gContext.mWidth; gContext.mYMax = gContext.mY + gContext.mXMax; gContext.mDisplayRatio = width / height; } void SetOrthographic(bool isOrthographic) { gContext.mIsOrthographic = isOrthographic; } void SetDrawlist(ImDrawList* drawlist) { gContext.mDrawList = drawlist ? drawlist : ImGui::GetWindowDrawList(); } void SetImGuiContext(ImGuiContext* ctx) { ImGui::SetCurrentContext(ctx); } void BeginFrame() { const ImU32 flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoBringToFrontOnFocus; #ifdef IMGUI_HAS_VIEWPORT ImGui::SetNextWindowSize(ImGui::GetMainViewport()->Size); ImGui::SetNextWindowPos(ImGui::GetMainViewport()->Pos); #else ImGuiIO& io = ImGui::GetIO(); ImGui::SetNextWindowSize(io.DisplaySize); ImGui::SetNextWindowPos(ImVec2(0, 0)); #endif ImGui::PushStyleColor(ImGuiCol_WindowBg, 0); ImGui::PushStyleColor(ImGuiCol_Border, 0); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); ImGui::Begin("gizmo", NULL, flags); gContext.mDrawList = ImGui::GetWindowDrawList(); ImGui::End(); ImGui::PopStyleVar(); ImGui::PopStyleColor(2); } bool IsUsing() { return gContext.mbUsing || gContext.mbUsingBounds; } bool IsOver() { return (Intersects(gContext.mOperation, TRANSLATE) && GetMoveType(gContext.mOperation, NULL) != MT_NONE) || (Intersects(gContext.mOperation, ROTATE) && GetRotateType(gContext.mOperation) != MT_NONE) || (Intersects(gContext.mOperation, SCALE) && GetScaleType(gContext.mOperation) != MT_NONE) || IsUsing(); } bool IsOver(OPERATION op) { if(IsUsing()) { return true; } if(Intersects(op, SCALE) && GetScaleType(op) != MT_NONE) { return true; } if(Intersects(op, ROTATE) && GetRotateType(op) != MT_NONE) { return true; } if(Intersects(op, TRANSLATE) && GetMoveType(op, NULL) != MT_NONE) { return true; } return false; } void Enable(bool enable) { gContext.mbEnable = enable; if (!enable) { gContext.mbUsing = false; gContext.mbUsingBounds = false; } } static void ComputeContext(const float* view, const float* projection, float* matrix, MODE mode) { gContext.mMode = mode; gContext.mViewMat = *(matrix_t*)view; gContext.mProjectionMat = *(matrix_t*)projection; gContext.mbMouseOver = IsHoveringWindow(); gContext.mModelLocal = *(matrix_t*)matrix; gContext.mModelLocal.OrthoNormalize(); if (mode == LOCAL) { gContext.mModel = gContext.mModelLocal; } else { gContext.mModel.Translation(((matrix_t*)matrix)->v.position); } gContext.mModelSource = *(matrix_t*)matrix; gContext.mModelScaleOrigin.Set(gContext.mModelSource.v.right.Length(), gContext.mModelSource.v.up.Length(), gContext.mModelSource.v.dir.Length()); gContext.mModelInverse.Inverse(gContext.mModel); gContext.mModelSourceInverse.Inverse(gContext.mModelSource); gContext.mViewProjection = gContext.mViewMat * gContext.mProjectionMat; gContext.mMVP = gContext.mModel * gContext.mViewProjection; gContext.mMVPLocal = gContext.mModelLocal * gContext.mViewProjection; matrix_t viewInverse; viewInverse.Inverse(gContext.mViewMat); gContext.mCameraDir = viewInverse.v.dir; gContext.mCameraEye = viewInverse.v.position; gContext.mCameraRight = viewInverse.v.right; gContext.mCameraUp = viewInverse.v.up; // projection reverse vec_t nearPos, farPos; nearPos.Transform(makeVect(0, 0, 1.f, 1.f), gContext.mProjectionMat); farPos.Transform(makeVect(0, 0, 2.f, 1.f), gContext.mProjectionMat); gContext.mReversed = (nearPos.z/nearPos.w) > (farPos.z / farPos.w); // compute scale from the size of camera right vector projected on screen at the matrix position vec_t pointRight = viewInverse.v.right; pointRight.TransformPoint(gContext.mViewProjection); gContext.mScreenFactor = gContext.mGizmoSizeClipSpace / (pointRight.x / pointRight.w - gContext.mMVP.v.position.x / gContext.mMVP.v.position.w); vec_t rightViewInverse = viewInverse.v.right; rightViewInverse.TransformVector(gContext.mModelInverse); float rightLength = GetSegmentLengthClipSpace(makeVect(0.f, 0.f), rightViewInverse); gContext.mScreenFactor = gContext.mGizmoSizeClipSpace / rightLength; ImVec2 centerSSpace = worldToPos(makeVect(0.f, 0.f), gContext.mMVP); gContext.mScreenSquareCenter = centerSSpace; gContext.mScreenSquareMin = ImVec2(centerSSpace.x - 10.f, centerSSpace.y - 10.f); gContext.mScreenSquareMax = ImVec2(centerSSpace.x + 10.f, centerSSpace.y + 10.f); ComputeCameraRay(gContext.mRayOrigin, gContext.mRayVector); } static void ComputeColors(ImU32* colors, int type, OPERATION operation) { if (gContext.mbEnable) { ImU32 selectionColor = GetColorU32(SELECTION); switch (operation) { case TRANSLATE: colors[0] = (type == MT_MOVE_SCREEN) ? selectionColor : IM_COL32_WHITE; for (int i = 0; i < 3; i++) { colors[i + 1] = (type == (int)(MT_MOVE_X + i)) ? selectionColor : GetColorU32(DIRECTION_X + i); colors[i + 4] = (type == (int)(MT_MOVE_YZ + i)) ? selectionColor : GetColorU32(PLANE_X + i); colors[i + 4] = (type == MT_MOVE_SCREEN) ? selectionColor : colors[i + 4]; } break; case ROTATE: colors[0] = (type == MT_ROTATE_SCREEN) ? selectionColor : IM_COL32_WHITE; for (int i = 0; i < 3; i++) { colors[i + 1] = (type == (int)(MT_ROTATE_X + i)) ? selectionColor : GetColorU32(DIRECTION_X + i); } break; case SCALEU: case SCALE: colors[0] = (type == MT_SCALE_XYZ) ? selectionColor : IM_COL32_WHITE; for (int i = 0; i < 3; i++) { colors[i + 1] = (type == (int)(MT_SCALE_X + i)) ? selectionColor : GetColorU32(DIRECTION_X + i); } break; // note: this internal function is only called with three possible values for operation default: break; } } else { ImU32 inactiveColor = GetColorU32(INACTIVE); for (int i = 0; i < 7; i++) { colors[i] = inactiveColor; } } } static void ComputeTripodAxisAndVisibility(const int axisIndex, vec_t& dirAxis, vec_t& dirPlaneX, vec_t& dirPlaneY, bool& belowAxisLimit, bool& belowPlaneLimit, const bool localCoordinates = false) { dirAxis = directionUnary[axisIndex]; dirPlaneX = directionUnary[(axisIndex + 1) % 3]; dirPlaneY = directionUnary[(axisIndex + 2) % 3]; if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID)) { // when using, use stored factors so the gizmo doesn't flip when we translate belowAxisLimit = gContext.mBelowAxisLimit[axisIndex]; belowPlaneLimit = gContext.mBelowPlaneLimit[axisIndex]; dirAxis *= gContext.mAxisFactor[axisIndex]; dirPlaneX *= gContext.mAxisFactor[(axisIndex + 1) % 3]; dirPlaneY *= gContext.mAxisFactor[(axisIndex + 2) % 3]; } else { // new method float lenDir = GetSegmentLengthClipSpace(makeVect(0.f, 0.f, 0.f), dirAxis, localCoordinates); float lenDirMinus = GetSegmentLengthClipSpace(makeVect(0.f, 0.f, 0.f), -dirAxis, localCoordinates); float lenDirPlaneX = GetSegmentLengthClipSpace(makeVect(0.f, 0.f, 0.f), dirPlaneX, localCoordinates); float lenDirMinusPlaneX = GetSegmentLengthClipSpace(makeVect(0.f, 0.f, 0.f), -dirPlaneX, localCoordinates); float lenDirPlaneY = GetSegmentLengthClipSpace(makeVect(0.f, 0.f, 0.f), dirPlaneY, localCoordinates); float lenDirMinusPlaneY = GetSegmentLengthClipSpace(makeVect(0.f, 0.f, 0.f), -dirPlaneY, localCoordinates); // For readability bool & allowFlip = gContext.mAllowAxisFlip; float mulAxis = (allowFlip && lenDir < lenDirMinus&& fabsf(lenDir - lenDirMinus) > FLT_EPSILON) ? -1.f : 1.f; float mulAxisX = (allowFlip && lenDirPlaneX < lenDirMinusPlaneX&& fabsf(lenDirPlaneX - lenDirMinusPlaneX) > FLT_EPSILON) ? -1.f : 1.f; float mulAxisY = (allowFlip && lenDirPlaneY < lenDirMinusPlaneY&& fabsf(lenDirPlaneY - lenDirMinusPlaneY) > FLT_EPSILON) ? -1.f : 1.f; dirAxis *= mulAxis; dirPlaneX *= mulAxisX; dirPlaneY *= mulAxisY; // for axis float axisLengthInClipSpace = GetSegmentLengthClipSpace(makeVect(0.f, 0.f, 0.f), dirAxis * gContext.mScreenFactor, localCoordinates); float paraSurf = GetParallelogram(makeVect(0.f, 0.f, 0.f), dirPlaneX * gContext.mScreenFactor, dirPlaneY * gContext.mScreenFactor); belowPlaneLimit = (paraSurf > 0.0025f); belowAxisLimit = (axisLengthInClipSpace > 0.02f); // and store values gContext.mAxisFactor[axisIndex] = mulAxis; gContext.mAxisFactor[(axisIndex + 1) % 3] = mulAxisX; gContext.mAxisFactor[(axisIndex + 2) % 3] = mulAxisY; gContext.mBelowAxisLimit[axisIndex] = belowAxisLimit; gContext.mBelowPlaneLimit[axisIndex] = belowPlaneLimit; } } static void ComputeSnap(float* value, float snap) { if (snap <= FLT_EPSILON) { return; } float modulo = fmodf(*value, snap); float moduloRatio = fabsf(modulo) / snap; if (moduloRatio < snapTension) { *value -= modulo; } else if (moduloRatio > (1.f - snapTension)) { *value = *value - modulo + snap * ((*value < 0.f) ? -1.f : 1.f); } } static void ComputeSnap(vec_t& value, const float* snap) { for (int i = 0; i < 3; i++) { ComputeSnap(&value[i], snap[i]); } } static float ComputeAngleOnPlan() { const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan); vec_t localPos = Normalized(gContext.mRayOrigin + gContext.mRayVector * len - gContext.mModel.v.position); vec_t perpendicularVector; perpendicularVector.Cross(gContext.mRotationVectorSource, gContext.mTranslationPlan); perpendicularVector.Normalize(); float acosAngle = Clamp(Dot(localPos, gContext.mRotationVectorSource), -1.f, 1.f); float angle = acosf(acosAngle); angle *= (Dot(localPos, perpendicularVector) < 0.f) ? 1.f : -1.f; return angle; } static void DrawRotationGizmo(OPERATION op, int type) { if(!Intersects(op, ROTATE)) { return; } ImDrawList* drawList = gContext.mDrawList; // colors ImU32 colors[7]; ComputeColors(colors, type, ROTATE); vec_t cameraToModelNormalized; if (gContext.mIsOrthographic) { matrix_t viewInverse; viewInverse.Inverse(*(matrix_t*)&gContext.mViewMat); cameraToModelNormalized = -viewInverse.v.dir; } else { cameraToModelNormalized = Normalized(gContext.mModel.v.position - gContext.mCameraEye); } cameraToModelNormalized.TransformVector(gContext.mModelInverse); gContext.mRadiusSquareCenter = screenRotateSize * gContext.mHeight; bool hasRSC = Intersects(op, ROTATE_SCREEN); for (int axis = 0; axis < 3; axis++) { if(!Intersects(op, static_cast(ROTATE_Z >> axis))) { continue; } const bool usingAxis = (gContext.mbUsing && type == MT_ROTATE_Z - axis); const int circleMul = (hasRSC && !usingAxis ) ? 1 : 2; ImVec2* circlePos = (ImVec2*)alloca(sizeof(ImVec2) * (circleMul * halfCircleSegmentCount + 1)); float angleStart = atan2f(cameraToModelNormalized[(4 - axis) % 3], cameraToModelNormalized[(3 - axis) % 3]) + ZPI * 0.5f; for (int i = 0; i < circleMul * halfCircleSegmentCount + 1; i++) { float ng = angleStart + (float)circleMul * ZPI * ((float)i / (float)halfCircleSegmentCount); vec_t axisPos = makeVect(cosf(ng), sinf(ng), 0.f); vec_t pos = makeVect(axisPos[axis], axisPos[(axis + 1) % 3], axisPos[(axis + 2) % 3]) * gContext.mScreenFactor * rotationDisplayFactor; circlePos[i] = worldToPos(pos, gContext.mMVP); } if (!gContext.mbUsing || usingAxis) { drawList->AddPolyline(circlePos, circleMul* halfCircleSegmentCount + 1, colors[3 - axis], false, gContext.mStyle.RotationLineThickness); } float radiusAxis = sqrtf((ImLengthSqr(worldToPos(gContext.mModel.v.position, gContext.mViewProjection) - circlePos[0]))); if (radiusAxis > gContext.mRadiusSquareCenter) { gContext.mRadiusSquareCenter = radiusAxis; } } if(hasRSC && (!gContext.mbUsing || type == MT_ROTATE_SCREEN)) { drawList->AddCircle(worldToPos(gContext.mModel.v.position, gContext.mViewProjection), gContext.mRadiusSquareCenter, colors[0], 64, gContext.mStyle.RotationOuterLineThickness); } if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID) && IsRotateType(type)) { ImVec2 circlePos[halfCircleSegmentCount + 1]; circlePos[0] = worldToPos(gContext.mModel.v.position, gContext.mViewProjection); for (unsigned int i = 1; i < halfCircleSegmentCount; i++) { float ng = gContext.mRotationAngle * ((float)(i - 1) / (float)(halfCircleSegmentCount - 1)); matrix_t rotateVectorMatrix; rotateVectorMatrix.RotationAxis(gContext.mTranslationPlan, ng); vec_t pos; pos.TransformPoint(gContext.mRotationVectorSource, rotateVectorMatrix); pos *= gContext.mScreenFactor * rotationDisplayFactor; circlePos[i] = worldToPos(pos + gContext.mModel.v.position, gContext.mViewProjection); } drawList->AddConvexPolyFilled(circlePos, halfCircleSegmentCount, GetColorU32(ROTATION_USING_FILL)); drawList->AddPolyline(circlePos, halfCircleSegmentCount, GetColorU32(ROTATION_USING_BORDER), true, gContext.mStyle.RotationLineThickness); ImVec2 destinationPosOnScreen = circlePos[1]; char tmps[512]; ImFormatString(tmps, sizeof(tmps), rotationInfoMask[type - MT_ROTATE_X], (gContext.mRotationAngle / ZPI) * 180.f, gContext.mRotationAngle); drawList->AddText(ImVec2(destinationPosOnScreen.x + 15, destinationPosOnScreen.y + 15), GetColorU32(TEXT_SHADOW), tmps); drawList->AddText(ImVec2(destinationPosOnScreen.x + 14, destinationPosOnScreen.y + 14), GetColorU32(TEXT), tmps); } } static void DrawHatchedAxis(const vec_t& axis) { if (gContext.mStyle.HatchedAxisLineThickness <= 0.0f) { return; } for (int j = 1; j < 10; j++) { ImVec2 baseSSpace2 = worldToPos(axis * 0.05f * (float)(j * 2) * gContext.mScreenFactor, gContext.mMVP); ImVec2 worldDirSSpace2 = worldToPos(axis * 0.05f * (float)(j * 2 + 1) * gContext.mScreenFactor, gContext.mMVP); gContext.mDrawList->AddLine(baseSSpace2, worldDirSSpace2, GetColorU32(HATCHED_AXIS_LINES), gContext.mStyle.HatchedAxisLineThickness); } } static void DrawScaleGizmo(OPERATION op, int type) { ImDrawList* drawList = gContext.mDrawList; if(!Intersects(op, SCALE)) { return; } // colors ImU32 colors[7]; ComputeColors(colors, type, SCALE); // draw vec_t scaleDisplay = { 1.f, 1.f, 1.f, 1.f }; if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID)) { scaleDisplay = gContext.mScale; } for (int i = 0; i < 3; i++) { if(!Intersects(op, static_cast(SCALE_X << i))) { continue; } const bool usingAxis = (gContext.mbUsing && type == MT_SCALE_X + i); if (!gContext.mbUsing || usingAxis) { vec_t dirPlaneX, dirPlaneY, dirAxis; bool belowAxisLimit, belowPlaneLimit; ComputeTripodAxisAndVisibility(i, dirAxis, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit, true); // draw axis if (belowAxisLimit) { bool hasTranslateOnAxis = Contains(op, static_cast(TRANSLATE_X << i)); float markerScale = hasTranslateOnAxis ? 1.4f : 1.0f; ImVec2 baseSSpace = worldToPos(dirAxis * 0.1f * gContext.mScreenFactor, gContext.mMVP); ImVec2 worldDirSSpaceNoScale = worldToPos(dirAxis * markerScale * gContext.mScreenFactor, gContext.mMVP); ImVec2 worldDirSSpace = worldToPos((dirAxis * markerScale * scaleDisplay[i]) * gContext.mScreenFactor, gContext.mMVP); if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID)) { ImU32 scaleLineColor = GetColorU32(SCALE_LINE); drawList->AddLine(baseSSpace, worldDirSSpaceNoScale, scaleLineColor, gContext.mStyle.ScaleLineThickness); drawList->AddCircleFilled(worldDirSSpaceNoScale, gContext.mStyle.ScaleLineCircleSize, scaleLineColor); } if (!hasTranslateOnAxis || gContext.mbUsing) { drawList->AddLine(baseSSpace, worldDirSSpace, colors[i + 1], gContext.mStyle.ScaleLineThickness); } drawList->AddCircleFilled(worldDirSSpace, gContext.mStyle.ScaleLineCircleSize, colors[i + 1]); if (gContext.mAxisFactor[i] < 0.f) { DrawHatchedAxis(dirAxis * scaleDisplay[i]); } } } } // draw screen cirle drawList->AddCircleFilled(gContext.mScreenSquareCenter, gContext.mStyle.CenterCircleSize, colors[0], 32); if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID) && IsScaleType(type)) { //ImVec2 sourcePosOnScreen = worldToPos(gContext.mMatrixOrigin, gContext.mViewProjection); ImVec2 destinationPosOnScreen = worldToPos(gContext.mModel.v.position, gContext.mViewProjection); /*vec_t dif(destinationPosOnScreen.x - sourcePosOnScreen.x, destinationPosOnScreen.y - sourcePosOnScreen.y); dif.Normalize(); dif *= 5.f; drawList->AddCircle(sourcePosOnScreen, 6.f, translationLineColor); drawList->AddCircle(destinationPosOnScreen, 6.f, translationLineColor); drawList->AddLine(ImVec2(sourcePosOnScreen.x + dif.x, sourcePosOnScreen.y + dif.y), ImVec2(destinationPosOnScreen.x - dif.x, destinationPosOnScreen.y - dif.y), translationLineColor, 2.f); */ char tmps[512]; //vec_t deltaInfo = gContext.mModel.v.position - gContext.mMatrixOrigin; int componentInfoIndex = (type - MT_SCALE_X) * 3; ImFormatString(tmps, sizeof(tmps), scaleInfoMask[type - MT_SCALE_X], scaleDisplay[translationInfoIndex[componentInfoIndex]]); drawList->AddText(ImVec2(destinationPosOnScreen.x + 15, destinationPosOnScreen.y + 15), GetColorU32(TEXT_SHADOW), tmps); drawList->AddText(ImVec2(destinationPosOnScreen.x + 14, destinationPosOnScreen.y + 14), GetColorU32(TEXT), tmps); } } static void DrawScaleUniveralGizmo(OPERATION op, int type) { ImDrawList* drawList = gContext.mDrawList; if (!Intersects(op, SCALEU)) { return; } // colors ImU32 colors[7]; ComputeColors(colors, type, SCALEU); // draw vec_t scaleDisplay = { 1.f, 1.f, 1.f, 1.f }; if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID)) { scaleDisplay = gContext.mScale; } for (int i = 0; i < 3; i++) { if (!Intersects(op, static_cast(SCALE_XU << i))) { continue; } const bool usingAxis = (gContext.mbUsing && type == MT_SCALE_X + i); if (!gContext.mbUsing || usingAxis) { vec_t dirPlaneX, dirPlaneY, dirAxis; bool belowAxisLimit, belowPlaneLimit; ComputeTripodAxisAndVisibility(i, dirAxis, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit, true); // draw axis if (belowAxisLimit) { bool hasTranslateOnAxis = Contains(op, static_cast(TRANSLATE_X << i)); float markerScale = hasTranslateOnAxis ? 1.4f : 1.0f; //ImVec2 baseSSpace = worldToPos(dirAxis * 0.1f * gContext.mScreenFactor, gContext.mMVPLocal); //ImVec2 worldDirSSpaceNoScale = worldToPos(dirAxis * markerScale * gContext.mScreenFactor, gContext.mMVP); ImVec2 worldDirSSpace = worldToPos((dirAxis * markerScale * scaleDisplay[i]) * gContext.mScreenFactor, gContext.mMVPLocal); #if 0 if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID)) { drawList->AddLine(baseSSpace, worldDirSSpaceNoScale, IM_COL32(0x40, 0x40, 0x40, 0xFF), 3.f); drawList->AddCircleFilled(worldDirSSpaceNoScale, 6.f, IM_COL32(0x40, 0x40, 0x40, 0xFF)); } /* if (!hasTranslateOnAxis || gContext.mbUsing) { drawList->AddLine(baseSSpace, worldDirSSpace, colors[i + 1], 3.f); } */ #endif drawList->AddCircleFilled(worldDirSSpace, 12.f, colors[i + 1]); } } } // draw screen cirle drawList->AddCircle(gContext.mScreenSquareCenter, 20.f, colors[0], 32, gContext.mStyle.CenterCircleSize); if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID) && IsScaleType(type)) { //ImVec2 sourcePosOnScreen = worldToPos(gContext.mMatrixOrigin, gContext.mViewProjection); ImVec2 destinationPosOnScreen = worldToPos(gContext.mModel.v.position, gContext.mViewProjection); /*vec_t dif(destinationPosOnScreen.x - sourcePosOnScreen.x, destinationPosOnScreen.y - sourcePosOnScreen.y); dif.Normalize(); dif *= 5.f; drawList->AddCircle(sourcePosOnScreen, 6.f, translationLineColor); drawList->AddCircle(destinationPosOnScreen, 6.f, translationLineColor); drawList->AddLine(ImVec2(sourcePosOnScreen.x + dif.x, sourcePosOnScreen.y + dif.y), ImVec2(destinationPosOnScreen.x - dif.x, destinationPosOnScreen.y - dif.y), translationLineColor, 2.f); */ char tmps[512]; //vec_t deltaInfo = gContext.mModel.v.position - gContext.mMatrixOrigin; int componentInfoIndex = (type - MT_SCALE_X) * 3; ImFormatString(tmps, sizeof(tmps), scaleInfoMask[type - MT_SCALE_X], scaleDisplay[translationInfoIndex[componentInfoIndex]]); drawList->AddText(ImVec2(destinationPosOnScreen.x + 15, destinationPosOnScreen.y + 15), GetColorU32(TEXT_SHADOW), tmps); drawList->AddText(ImVec2(destinationPosOnScreen.x + 14, destinationPosOnScreen.y + 14), GetColorU32(TEXT), tmps); } } static void DrawTranslationGizmo(OPERATION op, int type) { ImDrawList* drawList = gContext.mDrawList; if (!drawList) { return; } if(!Intersects(op, TRANSLATE)) { return; } // colors ImU32 colors[7]; ComputeColors(colors, type, TRANSLATE); const ImVec2 origin = worldToPos(gContext.mModel.v.position, gContext.mViewProjection); // draw bool belowAxisLimit = false; bool belowPlaneLimit = false; for (int i = 0; i < 3; ++i) { vec_t dirPlaneX, dirPlaneY, dirAxis; ComputeTripodAxisAndVisibility(i, dirAxis, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit); if (!gContext.mbUsing || (gContext.mbUsing && type == MT_MOVE_X + i)) { // draw axis if (belowAxisLimit && Intersects(op, static_cast(TRANSLATE_X << i))) { ImVec2 baseSSpace = worldToPos(dirAxis * 0.1f * gContext.mScreenFactor, gContext.mMVP); ImVec2 worldDirSSpace = worldToPos(dirAxis * gContext.mScreenFactor, gContext.mMVP); drawList->AddLine(baseSSpace, worldDirSSpace, colors[i + 1], gContext.mStyle.TranslationLineThickness); // Arrow head begin ImVec2 dir(origin - worldDirSSpace); float d = sqrtf(ImLengthSqr(dir)); dir /= d; // Normalize dir *= gContext.mStyle.TranslationLineArrowSize; ImVec2 ortogonalDir(dir.y, -dir.x); // Perpendicular vector ImVec2 a(worldDirSSpace + dir); drawList->AddTriangleFilled(worldDirSSpace - dir, a + ortogonalDir, a - ortogonalDir, colors[i + 1]); // Arrow head end if (gContext.mAxisFactor[i] < 0.f) { DrawHatchedAxis(dirAxis); } } } // draw plane if (!gContext.mbUsing || (gContext.mbUsing && type == MT_MOVE_YZ + i)) { if (belowPlaneLimit && Contains(op, TRANSLATE_PLANS[i])) { ImVec2 screenQuadPts[4]; for (int j = 0; j < 4; ++j) { vec_t cornerWorldPos = (dirPlaneX * quadUV[j * 2] + dirPlaneY * quadUV[j * 2 + 1]) * gContext.mScreenFactor; screenQuadPts[j] = worldToPos(cornerWorldPos, gContext.mMVP); } drawList->AddPolyline(screenQuadPts, 4, GetColorU32(DIRECTION_X + i), true, 1.0f); drawList->AddConvexPolyFilled(screenQuadPts, 4, colors[i + 4]); } } } drawList->AddCircleFilled(gContext.mScreenSquareCenter, gContext.mStyle.CenterCircleSize, colors[0], 32); if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID) && IsTranslateType(type)) { ImU32 translationLineColor = GetColorU32(TRANSLATION_LINE); ImVec2 sourcePosOnScreen = worldToPos(gContext.mMatrixOrigin, gContext.mViewProjection); ImVec2 destinationPosOnScreen = worldToPos(gContext.mModel.v.position, gContext.mViewProjection); vec_t dif = { destinationPosOnScreen.x - sourcePosOnScreen.x, destinationPosOnScreen.y - sourcePosOnScreen.y, 0.f, 0.f }; dif.Normalize(); dif *= 5.f; drawList->AddCircle(sourcePosOnScreen, 6.f, translationLineColor); drawList->AddCircle(destinationPosOnScreen, 6.f, translationLineColor); drawList->AddLine(ImVec2(sourcePosOnScreen.x + dif.x, sourcePosOnScreen.y + dif.y), ImVec2(destinationPosOnScreen.x - dif.x, destinationPosOnScreen.y - dif.y), translationLineColor, 2.f); char tmps[512]; vec_t deltaInfo = gContext.mModel.v.position - gContext.mMatrixOrigin; int componentInfoIndex = (type - MT_MOVE_X) * 3; ImFormatString(tmps, sizeof(tmps), translationInfoMask[type - MT_MOVE_X], deltaInfo[translationInfoIndex[componentInfoIndex]], deltaInfo[translationInfoIndex[componentInfoIndex + 1]], deltaInfo[translationInfoIndex[componentInfoIndex + 2]]); drawList->AddText(ImVec2(destinationPosOnScreen.x + 15, destinationPosOnScreen.y + 15), GetColorU32(TEXT_SHADOW), tmps); drawList->AddText(ImVec2(destinationPosOnScreen.x + 14, destinationPosOnScreen.y + 14), GetColorU32(TEXT), tmps); } } static bool CanActivate() { if (ImGui::IsMouseClicked(0) && !ImGui::IsAnyItemHovered() && !ImGui::IsAnyItemActive()) { return true; } return false; } static void HandleAndDrawLocalBounds(const float* bounds, matrix_t* matrix, const float* snapValues, OPERATION operation) { ImGuiIO& io = ImGui::GetIO(); ImDrawList* drawList = gContext.mDrawList; // compute best projection axis vec_t axesWorldDirections[3]; vec_t bestAxisWorldDirection = { 0.0f, 0.0f, 0.0f, 0.0f }; int axes[3]; unsigned int numAxes = 1; axes[0] = gContext.mBoundsBestAxis; int bestAxis = axes[0]; if (!gContext.mbUsingBounds) { numAxes = 0; float bestDot = 0.f; for (int i = 0; i < 3; i++) { vec_t dirPlaneNormalWorld; dirPlaneNormalWorld.TransformVector(directionUnary[i], gContext.mModelSource); dirPlaneNormalWorld.Normalize(); float dt = fabsf(Dot(Normalized(gContext.mCameraEye - gContext.mModelSource.v.position), dirPlaneNormalWorld)); if (dt >= bestDot) { bestDot = dt; bestAxis = i; bestAxisWorldDirection = dirPlaneNormalWorld; } if (dt >= 0.1f) { axes[numAxes] = i; axesWorldDirections[numAxes] = dirPlaneNormalWorld; ++numAxes; } } } if (numAxes == 0) { axes[0] = bestAxis; axesWorldDirections[0] = bestAxisWorldDirection; numAxes = 1; } else if (bestAxis != axes[0]) { unsigned int bestIndex = 0; for (unsigned int i = 0; i < numAxes; i++) { if (axes[i] == bestAxis) { bestIndex = i; break; } } int tempAxis = axes[0]; axes[0] = axes[bestIndex]; axes[bestIndex] = tempAxis; vec_t tempDirection = axesWorldDirections[0]; axesWorldDirections[0] = axesWorldDirections[bestIndex]; axesWorldDirections[bestIndex] = tempDirection; } for (unsigned int axisIndex = 0; axisIndex < numAxes; ++axisIndex) { bestAxis = axes[axisIndex]; bestAxisWorldDirection = axesWorldDirections[axisIndex]; // corners vec_t aabb[4]; int secondAxis = (bestAxis + 1) % 3; int thirdAxis = (bestAxis + 2) % 3; for (int i = 0; i < 4; i++) { aabb[i][3] = aabb[i][bestAxis] = 0.f; aabb[i][secondAxis] = bounds[secondAxis + 3 * (i >> 1)]; aabb[i][thirdAxis] = bounds[thirdAxis + 3 * ((i >> 1) ^ (i & 1))]; } // draw bounds unsigned int anchorAlpha = gContext.mbEnable ? IM_COL32_BLACK : IM_COL32(0, 0, 0, 0x80); matrix_t boundsMVP = gContext.mModelSource * gContext.mViewProjection; for (int i = 0; i < 4; i++) { ImVec2 worldBound1 = worldToPos(aabb[i], boundsMVP); ImVec2 worldBound2 = worldToPos(aabb[(i + 1) % 4], boundsMVP); if (!IsInContextRect(worldBound1) || !IsInContextRect(worldBound2)) { continue; } float boundDistance = sqrtf(ImLengthSqr(worldBound1 - worldBound2)); int stepCount = (int)(boundDistance / 10.f); stepCount = min(stepCount, 1000); for (int j = 0; j < stepCount; j++) { float stepLength = 1.f / (float)stepCount; float t1 = (float)j * stepLength; float t2 = (float)j * stepLength + stepLength * 0.5f; ImVec2 worldBoundSS1 = ImLerp(worldBound1, worldBound2, ImVec2(t1, t1)); ImVec2 worldBoundSS2 = ImLerp(worldBound1, worldBound2, ImVec2(t2, t2)); //drawList->AddLine(worldBoundSS1, worldBoundSS2, IM_COL32(0, 0, 0, 0) + anchorAlpha, 3.f); drawList->AddLine(worldBoundSS1, worldBoundSS2, IM_COL32(0xAA, 0xAA, 0xAA, 0) + anchorAlpha, 2.f); } vec_t midPoint = (aabb[i] + aabb[(i + 1) % 4]) * 0.5f; ImVec2 midBound = worldToPos(midPoint, boundsMVP); static const float AnchorBigRadius = 8.f; static const float AnchorSmallRadius = 6.f; bool overBigAnchor = ImLengthSqr(worldBound1 - io.MousePos) <= (AnchorBigRadius * AnchorBigRadius); bool overSmallAnchor = ImLengthSqr(midBound - io.MousePos) <= (AnchorBigRadius * AnchorBigRadius); int type = MT_NONE; vec_t gizmoHitProportion; if(Intersects(operation, TRANSLATE)) { type = GetMoveType(operation, &gizmoHitProportion); } if(Intersects(operation, ROTATE) && type == MT_NONE) { type = GetRotateType(operation); } if(Intersects(operation, SCALE) && type == MT_NONE) { type = GetScaleType(operation); } if (type != MT_NONE) { overBigAnchor = false; overSmallAnchor = false; } ImU32 selectionColor = GetColorU32(SELECTION); unsigned int bigAnchorColor = overBigAnchor ? selectionColor : (IM_COL32(0xAA, 0xAA, 0xAA, 0) + anchorAlpha); unsigned int smallAnchorColor = overSmallAnchor ? selectionColor : (IM_COL32(0xAA, 0xAA, 0xAA, 0) + anchorAlpha); drawList->AddCircleFilled(worldBound1, AnchorBigRadius, IM_COL32_BLACK); drawList->AddCircleFilled(worldBound1, AnchorBigRadius - 1.2f, bigAnchorColor); drawList->AddCircleFilled(midBound, AnchorSmallRadius, IM_COL32_BLACK); drawList->AddCircleFilled(midBound, AnchorSmallRadius - 1.2f, smallAnchorColor); int oppositeIndex = (i + 2) % 4; // big anchor on corners if (!gContext.mbUsingBounds && gContext.mbEnable && overBigAnchor && CanActivate()) { gContext.mBoundsPivot.TransformPoint(aabb[(i + 2) % 4], gContext.mModelSource); gContext.mBoundsAnchor.TransformPoint(aabb[i], gContext.mModelSource); gContext.mBoundsPlan = BuildPlan(gContext.mBoundsAnchor, bestAxisWorldDirection); gContext.mBoundsBestAxis = bestAxis; gContext.mBoundsAxis[0] = secondAxis; gContext.mBoundsAxis[1] = thirdAxis; gContext.mBoundsLocalPivot.Set(0.f); gContext.mBoundsLocalPivot[secondAxis] = aabb[oppositeIndex][secondAxis]; gContext.mBoundsLocalPivot[thirdAxis] = aabb[oppositeIndex][thirdAxis]; gContext.mbUsingBounds = true; gContext.mEditingID = gContext.mActualID; gContext.mBoundsMatrix = gContext.mModelSource; } // small anchor on middle of segment if (!gContext.mbUsingBounds && gContext.mbEnable && overSmallAnchor && CanActivate()) { vec_t midPointOpposite = (aabb[(i + 2) % 4] + aabb[(i + 3) % 4]) * 0.5f; gContext.mBoundsPivot.TransformPoint(midPointOpposite, gContext.mModelSource); gContext.mBoundsAnchor.TransformPoint(midPoint, gContext.mModelSource); gContext.mBoundsPlan = BuildPlan(gContext.mBoundsAnchor, bestAxisWorldDirection); gContext.mBoundsBestAxis = bestAxis; int indices[] = { secondAxis , thirdAxis }; gContext.mBoundsAxis[0] = indices[i % 2]; gContext.mBoundsAxis[1] = -1; gContext.mBoundsLocalPivot.Set(0.f); gContext.mBoundsLocalPivot[gContext.mBoundsAxis[0]] = aabb[oppositeIndex][indices[i % 2]];// bounds[gContext.mBoundsAxis[0]] * (((i + 1) & 2) ? 1.f : -1.f); gContext.mbUsingBounds = true; gContext.mEditingID = gContext.mActualID; gContext.mBoundsMatrix = gContext.mModelSource; } } if (gContext.mbUsingBounds && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID)) { matrix_t scale; scale.SetToIdentity(); // compute projected mouse position on plan const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mBoundsPlan); vec_t newPos = gContext.mRayOrigin + gContext.mRayVector * len; // compute a reference and delta vectors base on mouse move vec_t deltaVector = (newPos - gContext.mBoundsPivot).Abs(); vec_t referenceVector = (gContext.mBoundsAnchor - gContext.mBoundsPivot).Abs(); // for 1 or 2 axes, compute a ratio that's used for scale and snap it based on resulting length for (int i = 0; i < 2; i++) { int axisIndex1 = gContext.mBoundsAxis[i]; if (axisIndex1 == -1) { continue; } float ratioAxis = 1.f; vec_t axisDir = gContext.mBoundsMatrix.component[axisIndex1].Abs(); float dtAxis = axisDir.Dot(referenceVector); float boundSize = bounds[axisIndex1 + 3] - bounds[axisIndex1]; if (dtAxis > FLT_EPSILON) { ratioAxis = axisDir.Dot(deltaVector) / dtAxis; } if (snapValues) { float length = boundSize * ratioAxis; ComputeSnap(&length, snapValues[axisIndex1]); if (boundSize > FLT_EPSILON) { ratioAxis = length / boundSize; } } scale.component[axisIndex1] *= ratioAxis; } // transform matrix matrix_t preScale, postScale; preScale.Translation(-gContext.mBoundsLocalPivot); postScale.Translation(gContext.mBoundsLocalPivot); matrix_t res = preScale * scale * postScale * gContext.mBoundsMatrix; *matrix = res; // info text char tmps[512]; ImVec2 destinationPosOnScreen = worldToPos(gContext.mModel.v.position, gContext.mViewProjection); ImFormatString(tmps, sizeof(tmps), "X: %.2f Y: %.2f Z: %.2f" , (bounds[3] - bounds[0]) * gContext.mBoundsMatrix.component[0].Length() * scale.component[0].Length() , (bounds[4] - bounds[1]) * gContext.mBoundsMatrix.component[1].Length() * scale.component[1].Length() , (bounds[5] - bounds[2]) * gContext.mBoundsMatrix.component[2].Length() * scale.component[2].Length() ); drawList->AddText(ImVec2(destinationPosOnScreen.x + 15, destinationPosOnScreen.y + 15), GetColorU32(TEXT_SHADOW), tmps); drawList->AddText(ImVec2(destinationPosOnScreen.x + 14, destinationPosOnScreen.y + 14), GetColorU32(TEXT), tmps); } if (!io.MouseDown[0]) { gContext.mbUsingBounds = false; gContext.mEditingID = -1; } if (gContext.mbUsingBounds) { break; } } } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // static int GetScaleType(OPERATION op) { if (gContext.mbUsing) { return MT_NONE; } ImGuiIO& io = ImGui::GetIO(); int type = MT_NONE; // screen if (io.MousePos.x >= gContext.mScreenSquareMin.x && io.MousePos.x <= gContext.mScreenSquareMax.x && io.MousePos.y >= gContext.mScreenSquareMin.y && io.MousePos.y <= gContext.mScreenSquareMax.y && Contains(op, SCALE)) { type = MT_SCALE_XYZ; } // compute for (int i = 0; i < 3 && type == MT_NONE; i++) { if(!Intersects(op, static_cast(SCALE_X << i))) { continue; } vec_t dirPlaneX, dirPlaneY, dirAxis; bool belowAxisLimit, belowPlaneLimit; ComputeTripodAxisAndVisibility(i, dirAxis, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit, true); dirAxis.TransformVector(gContext.mModelLocal); dirPlaneX.TransformVector(gContext.mModelLocal); dirPlaneY.TransformVector(gContext.mModelLocal); const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, BuildPlan(gContext.mModelLocal.v.position, dirAxis)); vec_t posOnPlan = gContext.mRayOrigin + gContext.mRayVector * len; const float startOffset = Contains(op, static_cast(TRANSLATE_X << i)) ? 1.0f : 0.1f; const float endOffset = Contains(op, static_cast(TRANSLATE_X << i)) ? 1.4f : 1.0f; const ImVec2 posOnPlanScreen = worldToPos(posOnPlan, gContext.mViewProjection); const ImVec2 axisStartOnScreen = worldToPos(gContext.mModelLocal.v.position + dirAxis * gContext.mScreenFactor * startOffset, gContext.mViewProjection); const ImVec2 axisEndOnScreen = worldToPos(gContext.mModelLocal.v.position + dirAxis * gContext.mScreenFactor * endOffset, gContext.mViewProjection); vec_t closestPointOnAxis = PointOnSegment(makeVect(posOnPlanScreen), makeVect(axisStartOnScreen), makeVect(axisEndOnScreen)); if ((closestPointOnAxis - makeVect(posOnPlanScreen)).Length() < 12.f) // pixel size { type = MT_SCALE_X + i; } } // universal vec_t deltaScreen = { io.MousePos.x - gContext.mScreenSquareCenter.x, io.MousePos.y - gContext.mScreenSquareCenter.y, 0.f, 0.f }; float dist = deltaScreen.Length(); if (Contains(op, SCALEU) && dist >= 17.0f && dist < 23.0f) { type = MT_SCALE_XYZ; } for (int i = 0; i < 3 && type == MT_NONE; i++) { if (!Intersects(op, static_cast(SCALE_XU << i))) { continue; } vec_t dirPlaneX, dirPlaneY, dirAxis; bool belowAxisLimit, belowPlaneLimit; ComputeTripodAxisAndVisibility(i, dirAxis, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit, true); // draw axis if (belowAxisLimit) { bool hasTranslateOnAxis = Contains(op, static_cast(TRANSLATE_X << i)); float markerScale = hasTranslateOnAxis ? 1.4f : 1.0f; //ImVec2 baseSSpace = worldToPos(dirAxis * 0.1f * gContext.mScreenFactor, gContext.mMVPLocal); //ImVec2 worldDirSSpaceNoScale = worldToPos(dirAxis * markerScale * gContext.mScreenFactor, gContext.mMVP); ImVec2 worldDirSSpace = worldToPos((dirAxis * markerScale) * gContext.mScreenFactor, gContext.mMVPLocal); float distance = sqrtf(ImLengthSqr(worldDirSSpace - io.MousePos)); if (distance < 12.f) { type = MT_SCALE_X + i; } } } return type; } static int GetRotateType(OPERATION op) { if (gContext.mbUsing) { return MT_NONE; } ImGuiIO& io = ImGui::GetIO(); int type = MT_NONE; vec_t deltaScreen = { io.MousePos.x - gContext.mScreenSquareCenter.x, io.MousePos.y - gContext.mScreenSquareCenter.y, 0.f, 0.f }; float dist = deltaScreen.Length(); if (Intersects(op, ROTATE_SCREEN) && dist >= (gContext.mRadiusSquareCenter - 4.0f) && dist < (gContext.mRadiusSquareCenter + 4.0f)) { type = MT_ROTATE_SCREEN; } const vec_t planNormals[] = { gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir }; vec_t modelViewPos; modelViewPos.TransformPoint(gContext.mModel.v.position, gContext.mViewMat); for (int i = 0; i < 3 && type == MT_NONE; i++) { if(!Intersects(op, static_cast(ROTATE_X << i))) { continue; } // pickup plan vec_t pickupPlan = BuildPlan(gContext.mModel.v.position, planNormals[i]); const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, pickupPlan); const vec_t intersectWorldPos = gContext.mRayOrigin + gContext.mRayVector * len; vec_t intersectViewPos; intersectViewPos.TransformPoint(intersectWorldPos, gContext.mViewMat); if (ImAbs(modelViewPos.z) - ImAbs(intersectViewPos.z) < -FLT_EPSILON) { continue; } const vec_t localPos = intersectWorldPos - gContext.mModel.v.position; vec_t idealPosOnCircle = Normalized(localPos); idealPosOnCircle.TransformVector(gContext.mModelInverse); const ImVec2 idealPosOnCircleScreen = worldToPos(idealPosOnCircle * rotationDisplayFactor * gContext.mScreenFactor, gContext.mMVP); //gContext.mDrawList->AddCircle(idealPosOnCircleScreen, 5.f, IM_COL32_WHITE); const ImVec2 distanceOnScreen = idealPosOnCircleScreen - io.MousePos; const float distance = makeVect(distanceOnScreen).Length(); if (distance < 8.f) // pixel size { type = MT_ROTATE_X + i; } } return type; } static int GetMoveType(OPERATION op, vec_t* gizmoHitProportion) { if(!Intersects(op, TRANSLATE) || gContext.mbUsing || !gContext.mbMouseOver) { return MT_NONE; } ImGuiIO& io = ImGui::GetIO(); int type = MT_NONE; // screen if (io.MousePos.x >= gContext.mScreenSquareMin.x && io.MousePos.x <= gContext.mScreenSquareMax.x && io.MousePos.y >= gContext.mScreenSquareMin.y && io.MousePos.y <= gContext.mScreenSquareMax.y && Contains(op, TRANSLATE)) { type = MT_MOVE_SCREEN; } const vec_t screenCoord = makeVect(io.MousePos - ImVec2(gContext.mX, gContext.mY)); // compute for (int i = 0; i < 3 && type == MT_NONE; i++) { vec_t dirPlaneX, dirPlaneY, dirAxis; bool belowAxisLimit, belowPlaneLimit; ComputeTripodAxisAndVisibility(i, dirAxis, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit); dirAxis.TransformVector(gContext.mModel); dirPlaneX.TransformVector(gContext.mModel); dirPlaneY.TransformVector(gContext.mModel); const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, BuildPlan(gContext.mModel.v.position, dirAxis)); vec_t posOnPlan = gContext.mRayOrigin + gContext.mRayVector * len; const ImVec2 axisStartOnScreen = worldToPos(gContext.mModel.v.position + dirAxis * gContext.mScreenFactor * 0.1f, gContext.mViewProjection) - ImVec2(gContext.mX, gContext.mY); const ImVec2 axisEndOnScreen = worldToPos(gContext.mModel.v.position + dirAxis * gContext.mScreenFactor, gContext.mViewProjection) - ImVec2(gContext.mX, gContext.mY); vec_t closestPointOnAxis = PointOnSegment(screenCoord, makeVect(axisStartOnScreen), makeVect(axisEndOnScreen)); if ((closestPointOnAxis - screenCoord).Length() < 12.f && Intersects(op, static_cast(TRANSLATE_X << i))) // pixel size { type = MT_MOVE_X + i; } const float dx = dirPlaneX.Dot3((posOnPlan - gContext.mModel.v.position) * (1.f / gContext.mScreenFactor)); const float dy = dirPlaneY.Dot3((posOnPlan - gContext.mModel.v.position) * (1.f / gContext.mScreenFactor)); if (belowPlaneLimit && dx >= quadUV[0] && dx <= quadUV[4] && dy >= quadUV[1] && dy <= quadUV[3] && Contains(op, TRANSLATE_PLANS[i])) { type = MT_MOVE_YZ + i; } if (gizmoHitProportion) { *gizmoHitProportion = makeVect(dx, dy, 0.f); } } return type; } static bool HandleTranslation(float* matrix, float* deltaMatrix, OPERATION op, int& type, const float* snap) { if(!Intersects(op, TRANSLATE) || type != MT_NONE) { return false; } const ImGuiIO& io = ImGui::GetIO(); const bool applyRotationLocaly = gContext.mMode == LOCAL || type == MT_MOVE_SCREEN; bool modified = false; // move if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID) && IsTranslateType(gContext.mCurrentOperation)) { #if IMGUI_VERSION_NUM >= 18723 ImGui::SetNextFrameWantCaptureMouse(true); #else ImGui::CaptureMouseFromApp(); #endif const float signedLength = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan); const float len = fabsf(signedLength); // near plan const vec_t newPos = gContext.mRayOrigin + gContext.mRayVector * len; // compute delta const vec_t newOrigin = newPos - gContext.mRelativeOrigin * gContext.mScreenFactor; vec_t delta = newOrigin - gContext.mModel.v.position; // 1 axis constraint if (gContext.mCurrentOperation >= MT_MOVE_X && gContext.mCurrentOperation <= MT_MOVE_Z) { const int axisIndex = gContext.mCurrentOperation - MT_MOVE_X; const vec_t& axisValue = *(vec_t*)&gContext.mModel.m[axisIndex]; const float lengthOnAxis = Dot(axisValue, delta); delta = axisValue * lengthOnAxis; } // snap if (snap) { vec_t cumulativeDelta = gContext.mModel.v.position + delta - gContext.mMatrixOrigin; if (applyRotationLocaly) { matrix_t modelSourceNormalized = gContext.mModelSource; modelSourceNormalized.OrthoNormalize(); matrix_t modelSourceNormalizedInverse; modelSourceNormalizedInverse.Inverse(modelSourceNormalized); cumulativeDelta.TransformVector(modelSourceNormalizedInverse); ComputeSnap(cumulativeDelta, snap); cumulativeDelta.TransformVector(modelSourceNormalized); } else { ComputeSnap(cumulativeDelta, snap); } delta = gContext.mMatrixOrigin + cumulativeDelta - gContext.mModel.v.position; } if (delta != gContext.mTranslationLastDelta) { modified = true; } gContext.mTranslationLastDelta = delta; // compute matrix & delta matrix_t deltaMatrixTranslation; deltaMatrixTranslation.Translation(delta); if (deltaMatrix) { memcpy(deltaMatrix, deltaMatrixTranslation.m16, sizeof(float) * 16); } const matrix_t res = gContext.mModelSource * deltaMatrixTranslation; *(matrix_t*)matrix = res; if (!io.MouseDown[0]) { gContext.mbUsing = false; } type = gContext.mCurrentOperation; } else { // find new possible way to move vec_t gizmoHitProportion; type = GetMoveType(op, &gizmoHitProportion); if (type != MT_NONE) { #if IMGUI_VERSION_NUM >= 18723 ImGui::SetNextFrameWantCaptureMouse(true); #else ImGui::CaptureMouseFromApp(); #endif } if (CanActivate() && type != MT_NONE) { gContext.mbUsing = true; gContext.mEditingID = gContext.mActualID; gContext.mCurrentOperation = type; vec_t movePlanNormal[] = { gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir, gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir, -gContext.mCameraDir }; vec_t cameraToModelNormalized = Normalized(gContext.mModel.v.position - gContext.mCameraEye); for (unsigned int i = 0; i < 3; i++) { vec_t orthoVector = Cross(movePlanNormal[i], cameraToModelNormalized); movePlanNormal[i].Cross(orthoVector); movePlanNormal[i].Normalize(); } // pickup plan gContext.mTranslationPlan = BuildPlan(gContext.mModel.v.position, movePlanNormal[type - MT_MOVE_X]); const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan); gContext.mTranslationPlanOrigin = gContext.mRayOrigin + gContext.mRayVector * len; gContext.mMatrixOrigin = gContext.mModel.v.position; gContext.mRelativeOrigin = (gContext.mTranslationPlanOrigin - gContext.mModel.v.position) * (1.f / gContext.mScreenFactor); } } return modified; } static bool HandleScale(float* matrix, float* deltaMatrix, OPERATION op, int& type, const float* snap) { if((!Intersects(op, SCALE) && !Intersects(op, SCALEU)) || type != MT_NONE || !gContext.mbMouseOver) { return false; } ImGuiIO& io = ImGui::GetIO(); bool modified = false; if (!gContext.mbUsing) { // find new possible way to scale type = GetScaleType(op); if (type != MT_NONE) { #if IMGUI_VERSION_NUM >= 18723 ImGui::SetNextFrameWantCaptureMouse(true); #else ImGui::CaptureMouseFromApp(); #endif } if (CanActivate() && type != MT_NONE) { gContext.mbUsing = true; gContext.mEditingID = gContext.mActualID; gContext.mCurrentOperation = type; const vec_t movePlanNormal[] = { gContext.mModel.v.up, gContext.mModel.v.dir, gContext.mModel.v.right, gContext.mModel.v.dir, gContext.mModel.v.up, gContext.mModel.v.right, -gContext.mCameraDir }; // pickup plan gContext.mTranslationPlan = BuildPlan(gContext.mModel.v.position, movePlanNormal[type - MT_SCALE_X]); const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan); gContext.mTranslationPlanOrigin = gContext.mRayOrigin + gContext.mRayVector * len; gContext.mMatrixOrigin = gContext.mModel.v.position; gContext.mScale.Set(1.f, 1.f, 1.f); gContext.mRelativeOrigin = (gContext.mTranslationPlanOrigin - gContext.mModel.v.position) * (1.f / gContext.mScreenFactor); gContext.mScaleValueOrigin = makeVect(gContext.mModelSource.v.right.Length(), gContext.mModelSource.v.up.Length(), gContext.mModelSource.v.dir.Length()); gContext.mSaveMousePosx = io.MousePos.x; } } // scale if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID) && IsScaleType(gContext.mCurrentOperation)) { #if IMGUI_VERSION_NUM >= 18723 ImGui::SetNextFrameWantCaptureMouse(true); #else ImGui::CaptureMouseFromApp(); #endif const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan); vec_t newPos = gContext.mRayOrigin + gContext.mRayVector * len; vec_t newOrigin = newPos - gContext.mRelativeOrigin * gContext.mScreenFactor; vec_t delta = newOrigin - gContext.mModelLocal.v.position; // 1 axis constraint if (gContext.mCurrentOperation >= MT_SCALE_X && gContext.mCurrentOperation <= MT_SCALE_Z) { int axisIndex = gContext.mCurrentOperation - MT_SCALE_X; const vec_t& axisValue = *(vec_t*)&gContext.mModelLocal.m[axisIndex]; float lengthOnAxis = Dot(axisValue, delta); delta = axisValue * lengthOnAxis; vec_t baseVector = gContext.mTranslationPlanOrigin - gContext.mModelLocal.v.position; float ratio = Dot(axisValue, baseVector + delta) / Dot(axisValue, baseVector); gContext.mScale[axisIndex] = max(ratio, 0.001f); } else { float scaleDelta = (io.MousePos.x - gContext.mSaveMousePosx) * 0.01f; gContext.mScale.Set(max(1.f + scaleDelta, 0.001f)); } // snap if (snap) { float scaleSnap[] = { snap[0], snap[0], snap[0] }; ComputeSnap(gContext.mScale, scaleSnap); } // no 0 allowed for (int i = 0; i < 3; i++) gContext.mScale[i] = max(gContext.mScale[i], 0.001f); if (gContext.mScaleLast != gContext.mScale) { modified = true; } gContext.mScaleLast = gContext.mScale; // compute matrix & delta matrix_t deltaMatrixScale; deltaMatrixScale.Scale(gContext.mScale * gContext.mScaleValueOrigin); matrix_t res = deltaMatrixScale * gContext.mModelLocal; *(matrix_t*)matrix = res; if (deltaMatrix) { vec_t deltaScale = gContext.mScale * gContext.mScaleValueOrigin; vec_t originalScaleDivider; originalScaleDivider.x = 1 / gContext.mModelScaleOrigin.x; originalScaleDivider.y = 1 / gContext.mModelScaleOrigin.y; originalScaleDivider.z = 1 / gContext.mModelScaleOrigin.z; deltaScale = deltaScale * originalScaleDivider; deltaMatrixScale.Scale(deltaScale); memcpy(deltaMatrix, deltaMatrixScale.m16, sizeof(float) * 16); } if (!io.MouseDown[0]) { gContext.mbUsing = false; gContext.mScale.Set(1.f, 1.f, 1.f); } type = gContext.mCurrentOperation; } return modified; } static bool HandleRotation(float* matrix, float* deltaMatrix, OPERATION op, int& type, const float* snap) { if(!Intersects(op, ROTATE) || type != MT_NONE || !gContext.mbMouseOver) { return false; } ImGuiIO& io = ImGui::GetIO(); bool applyRotationLocaly = gContext.mMode == LOCAL; bool modified = false; if (!gContext.mbUsing) { type = GetRotateType(op); if (type != MT_NONE) { #if IMGUI_VERSION_NUM >= 18723 ImGui::SetNextFrameWantCaptureMouse(true); #else ImGui::CaptureMouseFromApp(); #endif } if (type == MT_ROTATE_SCREEN) { applyRotationLocaly = true; } if (CanActivate() && type != MT_NONE) { gContext.mbUsing = true; gContext.mEditingID = gContext.mActualID; gContext.mCurrentOperation = type; const vec_t rotatePlanNormal[] = { gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir, -gContext.mCameraDir }; // pickup plan if (applyRotationLocaly) { gContext.mTranslationPlan = BuildPlan(gContext.mModel.v.position, rotatePlanNormal[type - MT_ROTATE_X]); } else { gContext.mTranslationPlan = BuildPlan(gContext.mModelSource.v.position, directionUnary[type - MT_ROTATE_X]); } const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan); vec_t localPos = gContext.mRayOrigin + gContext.mRayVector * len - gContext.mModel.v.position; gContext.mRotationVectorSource = Normalized(localPos); gContext.mRotationAngleOrigin = ComputeAngleOnPlan(); } } // rotation if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID) && IsRotateType(gContext.mCurrentOperation)) { #if IMGUI_VERSION_NUM >= 18723 ImGui::SetNextFrameWantCaptureMouse(true); #else ImGui::CaptureMouseFromApp(); #endif gContext.mRotationAngle = ComputeAngleOnPlan(); if (snap) { float snapInRadian = snap[0] * DEG2RAD; ComputeSnap(&gContext.mRotationAngle, snapInRadian); } vec_t rotationAxisLocalSpace; rotationAxisLocalSpace.TransformVector(makeVect(gContext.mTranslationPlan.x, gContext.mTranslationPlan.y, gContext.mTranslationPlan.z, 0.f), gContext.mModelInverse); rotationAxisLocalSpace.Normalize(); matrix_t deltaRotation; deltaRotation.RotationAxis(rotationAxisLocalSpace, gContext.mRotationAngle - gContext.mRotationAngleOrigin); if (gContext.mRotationAngle != gContext.mRotationAngleOrigin) { modified = true; } gContext.mRotationAngleOrigin = gContext.mRotationAngle; matrix_t scaleOrigin; scaleOrigin.Scale(gContext.mModelScaleOrigin); if (applyRotationLocaly) { *(matrix_t*)matrix = scaleOrigin * deltaRotation * gContext.mModelLocal; } else { matrix_t res = gContext.mModelSource; res.v.position.Set(0.f); *(matrix_t*)matrix = res * deltaRotation; ((matrix_t*)matrix)->v.position = gContext.mModelSource.v.position; } if (deltaMatrix) { *(matrix_t*)deltaMatrix = gContext.mModelInverse * deltaRotation * gContext.mModel; } if (!io.MouseDown[0]) { gContext.mbUsing = false; gContext.mEditingID = -1; } type = gContext.mCurrentOperation; } return modified; } void DecomposeMatrixToComponents(const float* matrix, float* translation, float* rotation, float* scale) { matrix_t mat = *(matrix_t*)matrix; scale[0] = mat.v.right.Length(); scale[1] = mat.v.up.Length(); scale[2] = mat.v.dir.Length(); mat.OrthoNormalize(); rotation[0] = RAD2DEG * atan2f(mat.m[1][2], mat.m[2][2]); rotation[1] = RAD2DEG * atan2f(-mat.m[0][2], sqrtf(mat.m[1][2] * mat.m[1][2] + mat.m[2][2] * mat.m[2][2])); rotation[2] = RAD2DEG * atan2f(mat.m[0][1], mat.m[0][0]); translation[0] = mat.v.position.x; translation[1] = mat.v.position.y; translation[2] = mat.v.position.z; } void RecomposeMatrixFromComponents(const float* translation, const float* rotation, const float* scale, float* matrix) { matrix_t& mat = *(matrix_t*)matrix; matrix_t rot[3]; for (int i = 0; i < 3; i++) { rot[i].RotationAxis(directionUnary[i], rotation[i] * DEG2RAD); } mat = rot[0] * rot[1] * rot[2]; float validScale[3]; for (int i = 0; i < 3; i++) { if (fabsf(scale[i]) < FLT_EPSILON) { validScale[i] = 0.001f; } else { validScale[i] = scale[i]; } } mat.v.right *= validScale[0]; mat.v.up *= validScale[1]; mat.v.dir *= validScale[2]; mat.v.position.Set(translation[0], translation[1], translation[2], 1.f); } void SetID(int id) { gContext.mActualID = id; } void AllowAxisFlip(bool value) { gContext.mAllowAxisFlip = value; } bool Manipulate(const float* view, const float* projection, OPERATION operation, MODE mode, float* matrix, float* deltaMatrix, const float* snap, const float* localBounds, const float* boundsSnap) { // Scale is always local or matrix will be skewed when applying world scale or oriented matrix ComputeContext(view, projection, matrix, (operation & SCALE) ? LOCAL : mode); // set delta to identity if (deltaMatrix) { ((matrix_t*)deltaMatrix)->SetToIdentity(); } // behind camera vec_t camSpacePosition; camSpacePosition.TransformPoint(makeVect(0.f, 0.f, 0.f), gContext.mMVP); if (!gContext.mIsOrthographic && camSpacePosition.z < 0.001f) { return false; } // -- int type = MT_NONE; bool manipulated = false; if (gContext.mbEnable) { if (!gContext.mbUsingBounds) { manipulated = HandleTranslation(matrix, deltaMatrix, operation, type, snap) || HandleScale(matrix, deltaMatrix, operation, type, snap) || HandleRotation(matrix, deltaMatrix, operation, type, snap); } } if (localBounds && !gContext.mbUsing) { HandleAndDrawLocalBounds(localBounds, (matrix_t*)matrix, boundsSnap, operation); } gContext.mOperation = operation; if (!gContext.mbUsingBounds) { DrawRotationGizmo(operation, type); DrawTranslationGizmo(operation, type); DrawScaleGizmo(operation, type); DrawScaleUniveralGizmo(operation, type); } return manipulated; } void SetGizmoSizeClipSpace(float value) { gContext.mGizmoSizeClipSpace = value; } /////////////////////////////////////////////////////////////////////////////////////////////////// void ComputeFrustumPlanes(vec_t* frustum, const float* clip) { frustum[0].x = clip[3] - clip[0]; frustum[0].y = clip[7] - clip[4]; frustum[0].z = clip[11] - clip[8]; frustum[0].w = clip[15] - clip[12]; frustum[1].x = clip[3] + clip[0]; frustum[1].y = clip[7] + clip[4]; frustum[1].z = clip[11] + clip[8]; frustum[1].w = clip[15] + clip[12]; frustum[2].x = clip[3] + clip[1]; frustum[2].y = clip[7] + clip[5]; frustum[2].z = clip[11] + clip[9]; frustum[2].w = clip[15] + clip[13]; frustum[3].x = clip[3] - clip[1]; frustum[3].y = clip[7] - clip[5]; frustum[3].z = clip[11] - clip[9]; frustum[3].w = clip[15] - clip[13]; frustum[4].x = clip[3] - clip[2]; frustum[4].y = clip[7] - clip[6]; frustum[4].z = clip[11] - clip[10]; frustum[4].w = clip[15] - clip[14]; frustum[5].x = clip[3] + clip[2]; frustum[5].y = clip[7] + clip[6]; frustum[5].z = clip[11] + clip[10]; frustum[5].w = clip[15] + clip[14]; for (int i = 0; i < 6; i++) { frustum[i].Normalize(); } } void DrawCubes(const float* view, const float* projection, const float* matrices, int matrixCount) { matrix_t viewInverse; viewInverse.Inverse(*(matrix_t*)view); struct CubeFace { float z; ImVec2 faceCoordsScreen[4]; ImU32 color; }; CubeFace* faces = (CubeFace*)_malloca(sizeof(CubeFace) * matrixCount * 6); if (!faces) { return; } vec_t frustum[6]; matrix_t viewProjection = *(matrix_t*)view * *(matrix_t*)projection; ComputeFrustumPlanes(frustum, viewProjection.m16); int cubeFaceCount = 0; for (int cube = 0; cube < matrixCount; cube++) { const float* matrix = &matrices[cube * 16]; matrix_t res = *(matrix_t*)matrix * *(matrix_t*)view * *(matrix_t*)projection; for (int iFace = 0; iFace < 6; iFace++) { const int normalIndex = (iFace % 3); const int perpXIndex = (normalIndex + 1) % 3; const int perpYIndex = (normalIndex + 2) % 3; const float invert = (iFace > 2) ? -1.f : 1.f; const vec_t faceCoords[4] = { directionUnary[normalIndex] + directionUnary[perpXIndex] + directionUnary[perpYIndex], directionUnary[normalIndex] + directionUnary[perpXIndex] - directionUnary[perpYIndex], directionUnary[normalIndex] - directionUnary[perpXIndex] - directionUnary[perpYIndex], directionUnary[normalIndex] - directionUnary[perpXIndex] + directionUnary[perpYIndex], }; // clipping /* bool skipFace = false; for (unsigned int iCoord = 0; iCoord < 4; iCoord++) { vec_t camSpacePosition; camSpacePosition.TransformPoint(faceCoords[iCoord] * 0.5f * invert, res); if (camSpacePosition.z < 0.001f) { skipFace = true; break; } } if (skipFace) { continue; } */ vec_t centerPosition, centerPositionVP; centerPosition.TransformPoint(directionUnary[normalIndex] * 0.5f * invert, *(matrix_t*)matrix); centerPositionVP.TransformPoint(directionUnary[normalIndex] * 0.5f * invert, res); bool inFrustum = true; for (int iFrustum = 0; iFrustum < 6; iFrustum++) { float dist = DistanceToPlane(centerPosition, frustum[iFrustum]); if (dist < 0.f) { inFrustum = false; break; } } if (!inFrustum) { continue; } CubeFace& cubeFace = faces[cubeFaceCount]; // 3D->2D //ImVec2 faceCoordsScreen[4]; for (unsigned int iCoord = 0; iCoord < 4; iCoord++) { cubeFace.faceCoordsScreen[iCoord] = worldToPos(faceCoords[iCoord] * 0.5f * invert, res); } ImU32 directionColor = GetColorU32(DIRECTION_X + normalIndex); cubeFace.color = directionColor | IM_COL32(0x80, 0x80, 0x80, 0); cubeFace.z = centerPositionVP.z / centerPositionVP.w; cubeFaceCount++; } } qsort(faces, cubeFaceCount, sizeof(CubeFace), [](void const* _a, void const* _b) { CubeFace* a = (CubeFace*)_a; CubeFace* b = (CubeFace*)_b; if (a->z < b->z) { return 1; } return -1; }); // draw face with lighter color for (int iFace = 0; iFace < cubeFaceCount; iFace++) { const CubeFace& cubeFace = faces[iFace]; gContext.mDrawList->AddConvexPolyFilled(cubeFace.faceCoordsScreen, 4, cubeFace.color); } _freea(faces); } void DrawGrid(const float* view, const float* projection, const float* matrix, const float gridSize) { matrix_t viewProjection = *(matrix_t*)view * *(matrix_t*)projection; vec_t frustum[6]; ComputeFrustumPlanes(frustum, viewProjection.m16); matrix_t res = *(matrix_t*)matrix * viewProjection; for (float f = -gridSize; f <= gridSize; f += 1.f) { for (int dir = 0; dir < 2; dir++) { vec_t ptA = makeVect(dir ? -gridSize : f, 0.f, dir ? f : -gridSize); vec_t ptB = makeVect(dir ? gridSize : f, 0.f, dir ? f : gridSize); bool visible = true; for (int i = 0; i < 6; i++) { float dA = DistanceToPlane(ptA, frustum[i]); float dB = DistanceToPlane(ptB, frustum[i]); if (dA < 0.f && dB < 0.f) { visible = false; break; } if (dA > 0.f && dB > 0.f) { continue; } if (dA < 0.f) { float len = fabsf(dA - dB); float t = fabsf(dA) / len; ptA.Lerp(ptB, t); } if (dB < 0.f) { float len = fabsf(dB - dA); float t = fabsf(dB) / len; ptB.Lerp(ptA, t); } } if (visible) { ImU32 col = IM_COL32(0x80, 0x80, 0x80, 0xFF); col = (fmodf(fabsf(f), 10.f) < FLT_EPSILON) ? IM_COL32(0x90, 0x90, 0x90, 0xFF) : col; col = (fabsf(f) < FLT_EPSILON) ? IM_COL32(0x40, 0x40, 0x40, 0xFF): col; float thickness = 1.f; thickness = (fmodf(fabsf(f), 10.f) < FLT_EPSILON) ? 1.5f : thickness; thickness = (fabsf(f) < FLT_EPSILON) ? 2.3f : thickness; gContext.mDrawList->AddLine(worldToPos(ptA, res), worldToPos(ptB, res), col, thickness); } } } } void ViewManipulate(float* view, const float* projection, OPERATION operation, MODE mode, float* matrix, float length, ImVec2 position, ImVec2 size, ImU32 backgroundColor) { // Scale is always local or matrix will be skewed when applying world scale or oriented matrix ComputeContext(view, projection, matrix, (operation & SCALE) ? LOCAL : mode); ViewManipulate(view, length, position, size, backgroundColor); } void ViewManipulate(float* view, float length, ImVec2 position, ImVec2 size, ImU32 backgroundColor) { static bool isDraging = false; static bool isClicking = false; static bool isInside = false; static vec_t interpolationUp; static vec_t interpolationDir; static int interpolationFrames = 0; const vec_t referenceUp = makeVect(0.f, 1.f, 0.f); matrix_t svgView, svgProjection; svgView = gContext.mViewMat; svgProjection = gContext.mProjectionMat; ImGuiIO& io = ImGui::GetIO(); gContext.mDrawList->AddRectFilled(position, position + size, backgroundColor); matrix_t viewInverse; viewInverse.Inverse(*(matrix_t*)view); const vec_t camTarget = viewInverse.v.position - viewInverse.v.dir * length; // view/projection matrices const float distance = 3.f; matrix_t cubeProjection, cubeView; float fov = acosf(distance / (sqrtf(distance * distance + 3.f))) * RAD2DEG; Perspective(fov / sqrtf(2.f), size.x / size.y, 0.01f, 1000.f, cubeProjection.m16); vec_t dir = makeVect(viewInverse.m[2][0], viewInverse.m[2][1], viewInverse.m[2][2]); vec_t up = makeVect(viewInverse.m[1][0], viewInverse.m[1][1], viewInverse.m[1][2]); vec_t eye = dir * distance; vec_t zero = makeVect(0.f, 0.f); LookAt(&eye.x, &zero.x, &up.x, cubeView.m16); // set context gContext.mViewMat = cubeView; gContext.mProjectionMat = cubeProjection; ComputeCameraRay(gContext.mRayOrigin, gContext.mRayVector, position, size); const matrix_t res = cubeView * cubeProjection; // panels static const ImVec2 panelPosition[9] = { ImVec2(0.75f,0.75f), ImVec2(0.25f, 0.75f), ImVec2(0.f, 0.75f), ImVec2(0.75f, 0.25f), ImVec2(0.25f, 0.25f), ImVec2(0.f, 0.25f), ImVec2(0.75f, 0.f), ImVec2(0.25f, 0.f), ImVec2(0.f, 0.f) }; static const ImVec2 panelSize[9] = { ImVec2(0.25f,0.25f), ImVec2(0.5f, 0.25f), ImVec2(0.25f, 0.25f), ImVec2(0.25f, 0.5f), ImVec2(0.5f, 0.5f), ImVec2(0.25f, 0.5f), ImVec2(0.25f, 0.25f), ImVec2(0.5f, 0.25f), ImVec2(0.25f, 0.25f) }; // tag faces bool boxes[27]{}; static int overBox = -1; for (int iPass = 0; iPass < 2; iPass++) { for (int iFace = 0; iFace < 6; iFace++) { const int normalIndex = (iFace % 3); const int perpXIndex = (normalIndex + 1) % 3; const int perpYIndex = (normalIndex + 2) % 3; const float invert = (iFace > 2) ? -1.f : 1.f; const vec_t indexVectorX = directionUnary[perpXIndex] * invert; const vec_t indexVectorY = directionUnary[perpYIndex] * invert; const vec_t boxOrigin = directionUnary[normalIndex] * -invert - indexVectorX - indexVectorY; // plan local space const vec_t n = directionUnary[normalIndex] * invert; vec_t viewSpaceNormal = n; vec_t viewSpacePoint = n * 0.5f; viewSpaceNormal.TransformVector(cubeView); viewSpaceNormal.Normalize(); viewSpacePoint.TransformPoint(cubeView); const vec_t viewSpaceFacePlan = BuildPlan(viewSpacePoint, viewSpaceNormal); // back face culling if (viewSpaceFacePlan.w > 0.f) { continue; } const vec_t facePlan = BuildPlan(n * 0.5f, n); const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, facePlan); vec_t posOnPlan = gContext.mRayOrigin + gContext.mRayVector * len - (n * 0.5f); float localx = Dot(directionUnary[perpXIndex], posOnPlan) * invert + 0.5f; float localy = Dot(directionUnary[perpYIndex], posOnPlan) * invert + 0.5f; // panels const vec_t dx = directionUnary[perpXIndex]; const vec_t dy = directionUnary[perpYIndex]; const vec_t origin = directionUnary[normalIndex] - dx - dy; for (int iPanel = 0; iPanel < 9; iPanel++) { vec_t boxCoord = boxOrigin + indexVectorX * float(iPanel % 3) + indexVectorY * float(iPanel / 3) + makeVect(1.f, 1.f, 1.f); const ImVec2 p = panelPosition[iPanel] * 2.f; const ImVec2 s = panelSize[iPanel] * 2.f; ImVec2 faceCoordsScreen[4]; vec_t panelPos[4] = { dx * p.x + dy * p.y, dx * p.x + dy * (p.y + s.y), dx * (p.x + s.x) + dy * (p.y + s.y), dx * (p.x + s.x) + dy * p.y }; for (unsigned int iCoord = 0; iCoord < 4; iCoord++) { faceCoordsScreen[iCoord] = worldToPos((panelPos[iCoord] + origin) * 0.5f * invert, res, position, size); } const ImVec2 panelCorners[2] = { panelPosition[iPanel], panelPosition[iPanel] + panelSize[iPanel] }; bool insidePanel = localx > panelCorners[0].x && localx < panelCorners[1].x && localy > panelCorners[0].y && localy < panelCorners[1].y; int boxCoordInt = int(boxCoord.x * 9.f + boxCoord.y * 3.f + boxCoord.z); IM_ASSERT(boxCoordInt < 27); boxes[boxCoordInt] |= insidePanel && (!isDraging) && gContext.mbMouseOver; // draw face with lighter color if (iPass) { ImU32 directionColor = GetColorU32(DIRECTION_X + normalIndex); gContext.mDrawList->AddConvexPolyFilled(faceCoordsScreen, 4, (directionColor | IM_COL32(0x80, 0x80, 0x80, 0x80)) | (isInside ? IM_COL32(0x08, 0x08, 0x08, 0) : 0)); if (boxes[boxCoordInt]) { gContext.mDrawList->AddConvexPolyFilled(faceCoordsScreen, 4, IM_COL32(0xF0, 0xA0, 0x60, 0x80)); if (io.MouseDown[0] && !isClicking && !isDraging) { overBox = boxCoordInt; isClicking = true; isDraging = true; } } } } } } if (interpolationFrames) { interpolationFrames--; vec_t newDir = viewInverse.v.dir; newDir.Lerp(interpolationDir, 0.2f); newDir.Normalize(); vec_t newUp = viewInverse.v.up; newUp.Lerp(interpolationUp, 0.3f); newUp.Normalize(); newUp = interpolationUp; vec_t newEye = camTarget + newDir * length; LookAt(&newEye.x, &camTarget.x, &newUp.x, view); } isInside = gContext.mbMouseOver && ImRect(position, position + size).Contains(io.MousePos); if (io.MouseDown[0] && (fabsf(io.MouseDelta[0]) || fabsf(io.MouseDelta[1])) && isClicking) { isClicking = false; } if (!io.MouseDown[0]) { if (isClicking) { // apply new view direction int cx = overBox / 9; int cy = (overBox - cx * 9) / 3; int cz = overBox % 3; interpolationDir = makeVect(1.f - (float)cx, 1.f - (float)cy, 1.f - (float)cz); interpolationDir.Normalize(); if (fabsf(Dot(interpolationDir, referenceUp)) > 1.0f - 0.01f) { vec_t right = viewInverse.v.right; if (fabsf(right.x) > fabsf(right.z)) { right.z = 0.f; } else { right.x = 0.f; } right.Normalize(); interpolationUp = Cross(interpolationDir, right); interpolationUp.Normalize(); } else { interpolationUp = referenceUp; } interpolationFrames = 40; } isClicking = false; isDraging = false; } if (isDraging) { matrix_t rx, ry, roll; rx.RotationAxis(referenceUp, -io.MouseDelta.x * 0.01f); ry.RotationAxis(viewInverse.v.right, -io.MouseDelta.y * 0.01f); roll = rx * ry; vec_t newDir = viewInverse.v.dir; newDir.TransformVector(roll); newDir.Normalize(); // clamp vec_t planDir = Cross(viewInverse.v.right, referenceUp); planDir.y = 0.f; planDir.Normalize(); float dt = Dot(planDir, newDir); if (dt < 0.0f) { newDir += planDir * dt; newDir.Normalize(); } vec_t newEye = camTarget + newDir * length; LookAt(&newEye.x, &camTarget.x, &referenceUp.x, view); } // restore view/projection because it was used to compute ray ComputeContext(svgView.m16, svgProjection.m16, gContext.mModelSource.m16, gContext.mMode); } }; ================================================ FILE: Source/External/imgui_tools/imguizmo/ImGuizmo.h ================================================ // https://github.com/CedricGuillemet/ImGuizmo // v 1.89 WIP // // The MIT License(MIT) // // Copyright(c) 2021 Cedric Guillemet // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files(the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and / or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions : // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // // ------------------------------------------------------------------------------------------- // History : // 2019/11/03 View gizmo // 2016/09/11 Behind camera culling. Scaling Delta matrix not multiplied by source matrix scales. local/world rotation and translation fixed. Display message is incorrect (X: ... Y:...) in local mode. // 2016/09/09 Hatched negative axis. Snapping. Documentation update. // 2016/09/04 Axis switch and translation plan autohiding. Scale transform stability improved // 2016/09/01 Mogwai changed to Manipulate. Draw debug cube. Fixed inverted scale. Mixing scale and translation/rotation gives bad results. // 2016/08/31 First version // // ------------------------------------------------------------------------------------------- // Future (no order): // // - Multi view // - display rotation/translation/scale infos in local/world space and not only local // - finish local/world matrix application // - OPERATION as bitmask // // ------------------------------------------------------------------------------------------- // Example #if 0 void EditTransform(const Camera& camera, matrix_t& matrix) { static ImGuizmo::OPERATION mCurrentGizmoOperation(ImGuizmo::ROTATE); static ImGuizmo::MODE mCurrentGizmoMode(ImGuizmo::WORLD); if (ImGui::IsKeyPressed(90)) mCurrentGizmoOperation = ImGuizmo::TRANSLATE; if (ImGui::IsKeyPressed(69)) mCurrentGizmoOperation = ImGuizmo::ROTATE; if (ImGui::IsKeyPressed(82)) // r Key mCurrentGizmoOperation = ImGuizmo::SCALE; if (ImGui::RadioButton("Translate", mCurrentGizmoOperation == ImGuizmo::TRANSLATE)) mCurrentGizmoOperation = ImGuizmo::TRANSLATE; ImGui::SameLine(); if (ImGui::RadioButton("Rotate", mCurrentGizmoOperation == ImGuizmo::ROTATE)) mCurrentGizmoOperation = ImGuizmo::ROTATE; ImGui::SameLine(); if (ImGui::RadioButton("Scale", mCurrentGizmoOperation == ImGuizmo::SCALE)) mCurrentGizmoOperation = ImGuizmo::SCALE; float matrixTranslation[3], matrixRotation[3], matrixScale[3]; ImGuizmo::DecomposeMatrixToComponents(matrix.m16, matrixTranslation, matrixRotation, matrixScale); ImGui::InputFloat3("Tr", matrixTranslation, 3); ImGui::InputFloat3("Rt", matrixRotation, 3); ImGui::InputFloat3("Sc", matrixScale, 3); ImGuizmo::RecomposeMatrixFromComponents(matrixTranslation, matrixRotation, matrixScale, matrix.m16); if (mCurrentGizmoOperation != ImGuizmo::SCALE) { if (ImGui::RadioButton("Local", mCurrentGizmoMode == ImGuizmo::LOCAL)) mCurrentGizmoMode = ImGuizmo::LOCAL; ImGui::SameLine(); if (ImGui::RadioButton("World", mCurrentGizmoMode == ImGuizmo::WORLD)) mCurrentGizmoMode = ImGuizmo::WORLD; } static bool useSnap(false); if (ImGui::IsKeyPressed(83)) useSnap = !useSnap; ImGui::Checkbox("", &useSnap); ImGui::SameLine(); vec_t snap; switch (mCurrentGizmoOperation) { case ImGuizmo::TRANSLATE: snap = config.mSnapTranslation; ImGui::InputFloat3("Snap", &snap.x); break; case ImGuizmo::ROTATE: snap = config.mSnapRotation; ImGui::InputFloat("Angle Snap", &snap.x); break; case ImGuizmo::SCALE: snap = config.mSnapScale; ImGui::InputFloat("Scale Snap", &snap.x); break; } ImGuiIO& io = ImGui::GetIO(); ImGuizmo::SetRect(0, 0, io.DisplaySize.x, io.DisplaySize.y); ImGuizmo::Manipulate(camera.mView.m16, camera.mProjection.m16, mCurrentGizmoOperation, mCurrentGizmoMode, matrix.m16, NULL, useSnap ? &snap.x : NULL); } #endif #pragma once #ifdef USE_IMGUI_API #include "imconfig.h" #endif #ifndef IMGUI_API #define IMGUI_API #endif #ifndef IMGUIZMO_NAMESPACE #define IMGUIZMO_NAMESPACE ImGuizmo #endif namespace IMGUIZMO_NAMESPACE { // call inside your own window and before Manipulate() in order to draw gizmo to that window. // Or pass a specific ImDrawList to draw to (e.g. ImGui::GetForegroundDrawList()). IMGUI_API void SetDrawlist(ImDrawList* drawlist = nullptr); // call BeginFrame right after ImGui_XXXX_NewFrame(); IMGUI_API void BeginFrame(); // this is necessary because when imguizmo is compiled into a dll, and imgui into another // globals are not shared between them. // More details at https://stackoverflow.com/questions/19373061/what-happens-to-global-and-static-variables-in-a-shared-library-when-it-is-dynam // expose method to set imgui context IMGUI_API void SetImGuiContext(ImGuiContext* ctx); // return true if mouse cursor is over any gizmo control (axis, plan or screen component) IMGUI_API bool IsOver(); // return true if mouse IsOver or if the gizmo is in moving state IMGUI_API bool IsUsing(); // enable/disable the gizmo. Stay in the state until next call to Enable. // gizmo is rendered with gray half transparent color when disabled IMGUI_API void Enable(bool enable); // helper functions for manualy editing translation/rotation/scale with an input float // translation, rotation and scale float points to 3 floats each // Angles are in degrees (more suitable for human editing) // example: // float matrixTranslation[3], matrixRotation[3], matrixScale[3]; // ImGuizmo::DecomposeMatrixToComponents(gizmoMatrix.m16, matrixTranslation, matrixRotation, matrixScale); // ImGui::InputFloat3("Tr", matrixTranslation, 3); // ImGui::InputFloat3("Rt", matrixRotation, 3); // ImGui::InputFloat3("Sc", matrixScale, 3); // ImGuizmo::RecomposeMatrixFromComponents(matrixTranslation, matrixRotation, matrixScale, gizmoMatrix.m16); // // These functions have some numerical stability issues for now. Use with caution. IMGUI_API void DecomposeMatrixToComponents(const float* matrix, float* translation, float* rotation, float* scale); IMGUI_API void RecomposeMatrixFromComponents(const float* translation, const float* rotation, const float* scale, float* matrix); IMGUI_API void SetRect(float x, float y, float width, float height); // default is false IMGUI_API void SetOrthographic(bool isOrthographic); // Render a cube with face color corresponding to face normal. Usefull for debug/tests IMGUI_API void DrawCubes(const float* view, const float* projection, const float* matrices, int matrixCount); IMGUI_API void DrawGrid(const float* view, const float* projection, const float* matrix, const float gridSize); // call it when you want a gizmo // Needs view and projection matrices. // matrix parameter is the source matrix (where will be gizmo be drawn) and might be transformed by the function. Return deltaMatrix is optional // translation is applied in world space enum OPERATION { TRANSLATE_X = (1u << 0), TRANSLATE_Y = (1u << 1), TRANSLATE_Z = (1u << 2), ROTATE_X = (1u << 3), ROTATE_Y = (1u << 4), ROTATE_Z = (1u << 5), ROTATE_SCREEN = (1u << 6), SCALE_X = (1u << 7), SCALE_Y = (1u << 8), SCALE_Z = (1u << 9), BOUNDS = (1u << 10), SCALE_XU = (1u << 11), SCALE_YU = (1u << 12), SCALE_ZU = (1u << 13), TRANSLATE = TRANSLATE_X | TRANSLATE_Y | TRANSLATE_Z, ROTATE = ROTATE_X | ROTATE_Y | ROTATE_Z | ROTATE_SCREEN, SCALE = SCALE_X | SCALE_Y | SCALE_Z, SCALEU = SCALE_XU | SCALE_YU | SCALE_ZU, // universal UNIVERSAL = TRANSLATE | ROTATE | SCALEU }; inline OPERATION operator|(OPERATION lhs, OPERATION rhs) { return static_cast(static_cast(lhs) | static_cast(rhs)); } enum MODE { LOCAL, WORLD }; IMGUI_API bool Manipulate(const float* view, const float* projection, OPERATION operation, MODE mode, float* matrix, float* deltaMatrix = NULL, const float* snap = NULL, const float* localBounds = NULL, const float* boundsSnap = NULL); // // Please note that this cubeview is patented by Autodesk : https://patents.google.com/patent/US7782319B2/en // It seems to be a defensive patent in the US. I don't think it will bring troubles using it as // other software are using the same mechanics. But just in case, you are now warned! // IMGUI_API void ViewManipulate(float* view, float length, ImVec2 position, ImVec2 size, ImU32 backgroundColor); // use this version if you did not call Manipulate before and you are just using ViewManipulate IMGUI_API void ViewManipulate(float* view, const float* projection, OPERATION operation, MODE mode, float* matrix, float length, ImVec2 position, ImVec2 size, ImU32 backgroundColor); IMGUI_API void SetID(int id); // return true if the cursor is over the operation's gizmo IMGUI_API bool IsOver(OPERATION op); IMGUI_API void SetGizmoSizeClipSpace(float value); // Allow axis to flip // When true (default), the guizmo axis flip for better visibility // When false, they always stay along the positive world/local axis IMGUI_API void AllowAxisFlip(bool value); enum COLOR { DIRECTION_X, // directionColor[0] DIRECTION_Y, // directionColor[1] DIRECTION_Z, // directionColor[2] PLANE_X, // planeColor[0] PLANE_Y, // planeColor[1] PLANE_Z, // planeColor[2] SELECTION, // selectionColor INACTIVE, // inactiveColor TRANSLATION_LINE, // translationLineColor SCALE_LINE, ROTATION_USING_BORDER, ROTATION_USING_FILL, HATCHED_AXIS_LINES, TEXT, TEXT_SHADOW, COUNT }; struct Style { IMGUI_API Style(); float TranslationLineThickness; // Thickness of lines for translation gizmo float TranslationLineArrowSize; // Size of arrow at the end of lines for translation gizmo float RotationLineThickness; // Thickness of lines for rotation gizmo float RotationOuterLineThickness; // Thickness of line surrounding the rotation gizmo float ScaleLineThickness; // Thickness of lines for scale gizmo float ScaleLineCircleSize; // Size of circle at the end of lines for scale gizmo float HatchedAxisLineThickness; // Thickness of hatched axis lines float CenterCircleSize; // Size of circle at the center of the translate/scale gizmo ImVec4 Colors[COLOR::COUNT]; }; IMGUI_API Style& GetStyle(); } ================================================ FILE: Source/External/imgui_tools/imguizmo/ImSequencer.cpp ================================================ // https://github.com/CedricGuillemet/ImGuizmo // v 1.89 WIP // // The MIT License(MIT) // // Copyright(c) 2021 Cedric Guillemet // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files(the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and / or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions : // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #include "ImSequencer.h" #include "imgui.h" #include "imgui_internal.h" #include namespace ImSequencer { #ifndef IMGUI_DEFINE_MATH_OPERATORS static ImVec2 operator+(const ImVec2& a, const ImVec2& b) { return ImVec2(a.x + b.x, a.y + b.y); } #endif static bool SequencerAddDelButton(ImDrawList* draw_list, ImVec2 pos, bool add = true) { ImGuiIO& io = ImGui::GetIO(); ImRect btnRect(pos, ImVec2(pos.x + 16, pos.y + 16)); bool overBtn = btnRect.Contains(io.MousePos); bool containedClick = overBtn && btnRect.Contains(io.MouseClickedPos[0]); bool clickedBtn = containedClick && io.MouseReleased[0]; int btnColor = overBtn ? 0xAAEAFFAA : 0x77A3B2AA; if (containedClick && io.MouseDownDuration[0] > 0) btnRect.Expand(2.0f); float midy = pos.y + 16 / 2 - 0.5f; float midx = pos.x + 16 / 2 - 0.5f; draw_list->AddRect(btnRect.Min, btnRect.Max, btnColor, 4); draw_list->AddLine(ImVec2(btnRect.Min.x + 3, midy), ImVec2(btnRect.Max.x - 3, midy), btnColor, 2); if (add) draw_list->AddLine(ImVec2(midx, btnRect.Min.y + 3), ImVec2(midx, btnRect.Max.y - 3), btnColor, 2); return clickedBtn; } bool Sequencer(SequenceInterface* sequence, int* currentFrame, bool* expanded, int* selectedEntry, int* firstFrame, int sequenceOptions) { bool ret = false; ImGuiIO& io = ImGui::GetIO(); int cx = (int)(io.MousePos.x); int cy = (int)(io.MousePos.y); static float framePixelWidth = 10.f; static float framePixelWidthTarget = 10.f; int legendWidth = 200; static int movingEntry = -1; static int movingPos = -1; static int movingPart = -1; int delEntry = -1; int dupEntry = -1; int ItemHeight = 20; bool popupOpened = false; int sequenceCount = sequence->GetItemCount(); if (!sequenceCount) return false; ImGui::BeginGroup(); ImDrawList* draw_list = ImGui::GetWindowDrawList(); ImVec2 canvas_pos = ImGui::GetCursorScreenPos(); // ImDrawList API uses screen coordinates! ImVec2 canvas_size = ImGui::GetContentRegionAvail(); // Resize canvas to what's available int firstFrameUsed = firstFrame ? *firstFrame : 0; int controlHeight = sequenceCount * ItemHeight; for (int i = 0; i < sequenceCount; i++) controlHeight += int(sequence->GetCustomHeight(i)); int frameCount = ImMax(sequence->GetFrameMax() - sequence->GetFrameMin(), 1); static bool MovingScrollBar = false; static bool MovingCurrentFrame = false; struct CustomDraw { int index; ImRect customRect; ImRect legendRect; ImRect clippingRect; ImRect legendClippingRect; }; ImVector customDraws; ImVector compactCustomDraws; // zoom in/out const int visibleFrameCount = (int)floorf((canvas_size.x - legendWidth) / framePixelWidth); const float barWidthRatio = ImMin(visibleFrameCount / (float)frameCount, 1.f); const float barWidthInPixels = barWidthRatio * (canvas_size.x - legendWidth); ImRect regionRect(canvas_pos, canvas_pos + canvas_size); static bool panningView = false; static ImVec2 panningViewSource; static int panningViewFrame; if (ImGui::IsWindowFocused() && io.KeyAlt && io.MouseDown[2]) { if (!panningView) { panningViewSource = io.MousePos; panningView = true; panningViewFrame = *firstFrame; } *firstFrame = panningViewFrame - int((io.MousePos.x - panningViewSource.x) / framePixelWidth); *firstFrame = ImClamp(*firstFrame, sequence->GetFrameMin(), sequence->GetFrameMax() - visibleFrameCount); } if (panningView && !io.MouseDown[2]) { panningView = false; } framePixelWidthTarget = ImClamp(framePixelWidthTarget, 0.1f, 50.f); framePixelWidth = ImLerp(framePixelWidth, framePixelWidthTarget, 0.33f); frameCount = sequence->GetFrameMax() - sequence->GetFrameMin(); if (visibleFrameCount >= frameCount && firstFrame) *firstFrame = sequence->GetFrameMin(); // -- if (expanded && !*expanded) { ImGui::InvisibleButton("canvas", ImVec2(canvas_size.x - canvas_pos.x, (float)ItemHeight)); draw_list->AddRectFilled(canvas_pos, ImVec2(canvas_size.x + canvas_pos.x, canvas_pos.y + ItemHeight), 0xFF3D3837, 0); char tmps[512]; ImFormatString(tmps, IM_ARRAYSIZE(tmps), sequence->GetCollapseFmt(), frameCount, sequenceCount); draw_list->AddText(ImVec2(canvas_pos.x + 26, canvas_pos.y + 2), 0xFFFFFFFF, tmps); } else { bool hasScrollBar(true); /* int framesPixelWidth = int(frameCount * framePixelWidth); if ((framesPixelWidth + legendWidth) >= canvas_size.x) { hasScrollBar = true; } */ // test scroll area ImVec2 headerSize(canvas_size.x, (float)ItemHeight); ImVec2 scrollBarSize(canvas_size.x, 14.f); ImGui::InvisibleButton("topBar", headerSize); draw_list->AddRectFilled(canvas_pos, canvas_pos + headerSize, 0xFFFF0000, 0); ImVec2 childFramePos = ImGui::GetCursorScreenPos(); ImVec2 childFrameSize(canvas_size.x, canvas_size.y - 8.f - headerSize.y - (hasScrollBar ? scrollBarSize.y : 0)); ImGui::PushStyleColor(ImGuiCol_FrameBg, 0); ImGui::BeginChildFrame(889, childFrameSize); sequence->focused = ImGui::IsWindowFocused(); ImGui::InvisibleButton("contentBar", ImVec2(canvas_size.x, float(controlHeight))); const ImVec2 contentMin = ImGui::GetItemRectMin(); const ImVec2 contentMax = ImGui::GetItemRectMax(); const ImRect contentRect(contentMin, contentMax); const float contentHeight = contentMax.y - contentMin.y; // full background draw_list->AddRectFilled(canvas_pos, canvas_pos + canvas_size, 0xFF242424, 0); // current frame top ImRect topRect(ImVec2(canvas_pos.x + legendWidth, canvas_pos.y), ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + ItemHeight)); if (!MovingCurrentFrame && !MovingScrollBar && movingEntry == -1 && sequenceOptions & SEQUENCER_CHANGE_FRAME && currentFrame && *currentFrame >= 0 && topRect.Contains(io.MousePos) && io.MouseDown[0]) { MovingCurrentFrame = true; } if (MovingCurrentFrame) { if (frameCount) { *currentFrame = (int)((io.MousePos.x - topRect.Min.x) / framePixelWidth) + firstFrameUsed; if (*currentFrame < sequence->GetFrameMin()) *currentFrame = sequence->GetFrameMin(); if (*currentFrame >= sequence->GetFrameMax()) *currentFrame = sequence->GetFrameMax(); } if (!io.MouseDown[0]) MovingCurrentFrame = false; } //header draw_list->AddRectFilled(canvas_pos, ImVec2(canvas_size.x + canvas_pos.x, canvas_pos.y + ItemHeight), 0xFF3D3837, 0); if (sequenceOptions & SEQUENCER_ADD) { if (SequencerAddDelButton(draw_list, ImVec2(canvas_pos.x + legendWidth - ItemHeight, canvas_pos.y + 2), true)) ImGui::OpenPopup("addEntry"); if (ImGui::BeginPopup("addEntry")) { for (int i = 0; i < sequence->GetItemTypeCount(); i++) if (ImGui::Selectable(sequence->GetItemTypeName(i))) { sequence->Add(i); *selectedEntry = sequence->GetItemCount() - 1; } ImGui::EndPopup(); popupOpened = true; } } //header frame number and lines int modFrameCount = 10; int frameStep = 1; while ((modFrameCount * framePixelWidth) < 150) { modFrameCount *= 2; frameStep *= 2; }; int halfModFrameCount = modFrameCount / 2; auto drawLine = [&](int i, int regionHeight) { bool baseIndex = ((i % modFrameCount) == 0) || (i == sequence->GetFrameMax() || i == sequence->GetFrameMin()); bool halfIndex = (i % halfModFrameCount) == 0; int px = (int)canvas_pos.x + int(i * framePixelWidth) + legendWidth - int(firstFrameUsed * framePixelWidth); int tiretStart = baseIndex ? 4 : (halfIndex ? 10 : 14); int tiretEnd = baseIndex ? regionHeight : ItemHeight; if (px <= (canvas_size.x + canvas_pos.x) && px >= (canvas_pos.x + legendWidth)) { draw_list->AddLine(ImVec2((float)px, canvas_pos.y + (float)tiretStart), ImVec2((float)px, canvas_pos.y + (float)tiretEnd - 1), 0xFF606060, 1); draw_list->AddLine(ImVec2((float)px, canvas_pos.y + (float)ItemHeight), ImVec2((float)px, canvas_pos.y + (float)regionHeight - 1), 0x30606060, 1); } if (baseIndex && px > (canvas_pos.x + legendWidth)) { char tmps[512]; ImFormatString(tmps, IM_ARRAYSIZE(tmps), "%d", i); draw_list->AddText(ImVec2((float)px + 3.f, canvas_pos.y), 0xFFBBBBBB, tmps); } }; auto drawLineContent = [&](int i, int /*regionHeight*/) { int px = (int)canvas_pos.x + int(i * framePixelWidth) + legendWidth - int(firstFrameUsed * framePixelWidth); int tiretStart = int(contentMin.y); int tiretEnd = int(contentMax.y); if (px <= (canvas_size.x + canvas_pos.x) && px >= (canvas_pos.x + legendWidth)) { //draw_list->AddLine(ImVec2((float)px, canvas_pos.y + (float)tiretStart), ImVec2((float)px, canvas_pos.y + (float)tiretEnd - 1), 0xFF606060, 1); draw_list->AddLine(ImVec2(float(px), float(tiretStart)), ImVec2(float(px), float(tiretEnd)), 0x30606060, 1); } }; for (int i = sequence->GetFrameMin(); i <= sequence->GetFrameMax(); i += frameStep) { drawLine(i, ItemHeight); } drawLine(sequence->GetFrameMin(), ItemHeight); drawLine(sequence->GetFrameMax(), ItemHeight); /* draw_list->AddLine(canvas_pos, ImVec2(canvas_pos.x, canvas_pos.y + controlHeight), 0xFF000000, 1); draw_list->AddLine(ImVec2(canvas_pos.x, canvas_pos.y + ItemHeight), ImVec2(canvas_size.x, canvas_pos.y + ItemHeight), 0xFF000000, 1); */ // clip content draw_list->PushClipRect(childFramePos, childFramePos + childFrameSize, true); // draw item names in the legend rect on the left size_t customHeight = 0; for (int i = 0; i < sequenceCount; i++) { int type; sequence->Get(i, NULL, NULL, &type, NULL); ImVec2 tpos(contentMin.x + 3, contentMin.y + i * ItemHeight + 2 + customHeight); draw_list->AddText(tpos, 0xFFFFFFFF, sequence->GetItemLabel(i)); if (sequenceOptions & SEQUENCER_DEL) { if (SequencerAddDelButton(draw_list, ImVec2(contentMin.x + legendWidth - ItemHeight + 2 - 10, tpos.y + 2), false)) delEntry = i; if (SequencerAddDelButton(draw_list, ImVec2(contentMin.x + legendWidth - ItemHeight - ItemHeight + 2 - 10, tpos.y + 2), true)) dupEntry = i; } customHeight += sequence->GetCustomHeight(i); } // slots background customHeight = 0; for (int i = 0; i < sequenceCount; i++) { unsigned int col = (i & 1) ? 0xFF3A3636 : 0xFF413D3D; size_t localCustomHeight = sequence->GetCustomHeight(i); ImVec2 pos = ImVec2(contentMin.x + legendWidth, contentMin.y + ItemHeight * i + 1 + customHeight); ImVec2 sz = ImVec2(canvas_size.x + canvas_pos.x, pos.y + ItemHeight - 1 + localCustomHeight); if (!popupOpened && cy >= pos.y && cy < pos.y + (ItemHeight + localCustomHeight) && movingEntry == -1 && cx>contentMin.x && cx < contentMin.x + canvas_size.x) { col += 0x80201008; pos.x -= legendWidth; } draw_list->AddRectFilled(pos, sz, col, 0); customHeight += localCustomHeight; } draw_list->PushClipRect(childFramePos + ImVec2(float(legendWidth), 0.f), childFramePos + childFrameSize, true); // vertical frame lines in content area for (int i = sequence->GetFrameMin(); i <= sequence->GetFrameMax(); i += frameStep) { drawLineContent(i, int(contentHeight)); } drawLineContent(sequence->GetFrameMin(), int(contentHeight)); drawLineContent(sequence->GetFrameMax(), int(contentHeight)); // selection bool selected = selectedEntry && (*selectedEntry >= 0); if (selected) { customHeight = 0; for (int i = 0; i < *selectedEntry; i++) customHeight += sequence->GetCustomHeight(i); draw_list->AddRectFilled(ImVec2(contentMin.x, contentMin.y + ItemHeight * *selectedEntry + customHeight), ImVec2(contentMin.x + canvas_size.x, contentMin.y + ItemHeight * (*selectedEntry + 1) + customHeight), 0x801080FF, 1.f); } // slots customHeight = 0; for (int i = 0; i < sequenceCount; i++) { int* start, * end; unsigned int color; sequence->Get(i, &start, &end, NULL, &color); size_t localCustomHeight = sequence->GetCustomHeight(i); ImVec2 pos = ImVec2(contentMin.x + legendWidth - firstFrameUsed * framePixelWidth, contentMin.y + ItemHeight * i + 1 + customHeight); ImVec2 slotP1(pos.x + *start * framePixelWidth, pos.y + 2); ImVec2 slotP2(pos.x + *end * framePixelWidth + framePixelWidth, pos.y + ItemHeight - 2); ImVec2 slotP3(pos.x + *end * framePixelWidth + framePixelWidth, pos.y + ItemHeight - 2 + localCustomHeight); unsigned int slotColor = color | 0xFF000000; unsigned int slotColorHalf = (color & 0xFFFFFF) | 0x40000000; if (slotP1.x <= (canvas_size.x + contentMin.x) && slotP2.x >= (contentMin.x + legendWidth)) { draw_list->AddRectFilled(slotP1, slotP3, slotColorHalf, 2); draw_list->AddRectFilled(slotP1, slotP2, slotColor, 2); } if (ImRect(slotP1, slotP2).Contains(io.MousePos) && io.MouseDoubleClicked[0]) { sequence->DoubleClick(i); } // Ensure grabbable handles const float max_handle_width = slotP2.x - slotP1.x / 3.0f; const float min_handle_width = ImMin(10.0f, max_handle_width); const float handle_width = ImClamp(framePixelWidth / 2.0f, min_handle_width, max_handle_width); ImRect rects[3] = { ImRect(slotP1, ImVec2(slotP1.x + handle_width, slotP2.y)) , ImRect(ImVec2(slotP2.x - handle_width, slotP1.y), slotP2) , ImRect(slotP1, slotP2) }; const unsigned int quadColor[] = { 0xFFFFFFFF, 0xFFFFFFFF, slotColor + (selected ? 0 : 0x202020) }; if (movingEntry == -1 && (sequenceOptions & SEQUENCER_EDIT_STARTEND))// TODOFOCUS && backgroundRect.Contains(io.MousePos)) { for (int j = 2; j >= 0; j--) { ImRect& rc = rects[j]; if (!rc.Contains(io.MousePos)) continue; draw_list->AddRectFilled(rc.Min, rc.Max, quadColor[j], 2); } for (int j = 0; j < 3; j++) { ImRect& rc = rects[j]; if (!rc.Contains(io.MousePos)) continue; if (!ImRect(childFramePos, childFramePos + childFrameSize).Contains(io.MousePos)) continue; if (ImGui::IsMouseClicked(0) && !MovingScrollBar && !MovingCurrentFrame) { movingEntry = i; movingPos = cx; movingPart = j + 1; sequence->BeginEdit(movingEntry); break; } } } // custom draw if (localCustomHeight > 0) { ImVec2 rp(canvas_pos.x, contentMin.y + ItemHeight * i + 1 + customHeight); ImRect customRect(rp + ImVec2(legendWidth - (firstFrameUsed - sequence->GetFrameMin() - 0.5f) * framePixelWidth, float(ItemHeight)), rp + ImVec2(legendWidth + (sequence->GetFrameMax() - firstFrameUsed - 0.5f + 2.f) * framePixelWidth, float(localCustomHeight + ItemHeight))); ImRect clippingRect(rp + ImVec2(float(legendWidth), float(ItemHeight)), rp + ImVec2(canvas_size.x, float(localCustomHeight + ItemHeight))); ImRect legendRect(rp + ImVec2(0.f, float(ItemHeight)), rp + ImVec2(float(legendWidth), float(localCustomHeight))); ImRect legendClippingRect(canvas_pos + ImVec2(0.f, float(ItemHeight)), canvas_pos + ImVec2(float(legendWidth), float(localCustomHeight + ItemHeight))); customDraws.push_back({ i, customRect, legendRect, clippingRect, legendClippingRect }); } else { ImVec2 rp(canvas_pos.x, contentMin.y + ItemHeight * i + customHeight); ImRect customRect(rp + ImVec2(legendWidth - (firstFrameUsed - sequence->GetFrameMin() - 0.5f) * framePixelWidth, float(0.f)), rp + ImVec2(legendWidth + (sequence->GetFrameMax() - firstFrameUsed - 0.5f + 2.f) * framePixelWidth, float(ItemHeight))); ImRect clippingRect(rp + ImVec2(float(legendWidth), float(0.f)), rp + ImVec2(canvas_size.x, float(ItemHeight))); compactCustomDraws.push_back({ i, customRect, ImRect(), clippingRect, ImRect() }); } customHeight += localCustomHeight; } // moving if (/*backgroundRect.Contains(io.MousePos) && */movingEntry >= 0) { #if IMGUI_VERSION_NUM >= 18723 ImGui::SetNextFrameWantCaptureMouse(true); #else ImGui::CaptureMouseFromApp(); #endif int diffFrame = int((cx - movingPos) / framePixelWidth); if (std::abs(diffFrame) > 0) { int* start, * end; sequence->Get(movingEntry, &start, &end, NULL, NULL); if (selectedEntry) *selectedEntry = movingEntry; int& l = *start; int& r = *end; if (movingPart & 1) l += diffFrame; if (movingPart & 2) r += diffFrame; if (l < 0) { if (movingPart & 2) r -= l; l = 0; } if (movingPart & 1 && l > r) l = r; if (movingPart & 2 && r < l) r = l; movingPos += int(diffFrame * framePixelWidth); } if (!io.MouseDown[0]) { // single select if (!diffFrame && movingPart && selectedEntry) { *selectedEntry = movingEntry; ret = true; } movingEntry = -1; sequence->EndEdit(); } } // cursor if (currentFrame && firstFrame && *currentFrame >= *firstFrame && *currentFrame <= sequence->GetFrameMax()) { static const float cursorWidth = 8.f; float cursorOffset = contentMin.x + legendWidth + (*currentFrame - firstFrameUsed) * framePixelWidth + framePixelWidth / 2 - cursorWidth * 0.5f; draw_list->AddLine(ImVec2(cursorOffset, canvas_pos.y), ImVec2(cursorOffset, contentMax.y), 0xA02A2AFF, cursorWidth); char tmps[512]; ImFormatString(tmps, IM_ARRAYSIZE(tmps), "%d", *currentFrame); draw_list->AddText(ImVec2(cursorOffset + 10, canvas_pos.y + 2), 0xFF2A2AFF, tmps); } draw_list->PopClipRect(); draw_list->PopClipRect(); for (auto& customDraw : customDraws) sequence->CustomDraw(customDraw.index, draw_list, customDraw.customRect, customDraw.legendRect, customDraw.clippingRect, customDraw.legendClippingRect); for (auto& customDraw : compactCustomDraws) sequence->CustomDrawCompact(customDraw.index, draw_list, customDraw.customRect, customDraw.clippingRect); // copy paste if (sequenceOptions & SEQUENCER_COPYPASTE) { ImRect rectCopy(ImVec2(contentMin.x + 100, canvas_pos.y + 2) , ImVec2(contentMin.x + 100 + 30, canvas_pos.y + ItemHeight - 2)); bool inRectCopy = rectCopy.Contains(io.MousePos); unsigned int copyColor = inRectCopy ? 0xFF1080FF : 0xFF000000; draw_list->AddText(rectCopy.Min, copyColor, "Copy"); ImRect rectPaste(ImVec2(contentMin.x + 140, canvas_pos.y + 2) , ImVec2(contentMin.x + 140 + 30, canvas_pos.y + ItemHeight - 2)); bool inRectPaste = rectPaste.Contains(io.MousePos); unsigned int pasteColor = inRectPaste ? 0xFF1080FF : 0xFF000000; draw_list->AddText(rectPaste.Min, pasteColor, "Paste"); if (inRectCopy && io.MouseReleased[0]) { sequence->Copy(); } if (inRectPaste && io.MouseReleased[0]) { sequence->Paste(); } } // ImGui::EndChildFrame(); ImGui::PopStyleColor(); if (hasScrollBar) { ImGui::InvisibleButton("scrollBar", scrollBarSize); ImVec2 scrollBarMin = ImGui::GetItemRectMin(); ImVec2 scrollBarMax = ImGui::GetItemRectMax(); // ratio = number of frames visible in control / number to total frames float startFrameOffset = ((float)(firstFrameUsed - sequence->GetFrameMin()) / (float)frameCount) * (canvas_size.x - legendWidth); ImVec2 scrollBarA(scrollBarMin.x + legendWidth, scrollBarMin.y - 2); ImVec2 scrollBarB(scrollBarMin.x + canvas_size.x, scrollBarMax.y - 1); draw_list->AddRectFilled(scrollBarA, scrollBarB, 0xFF222222, 0); ImRect scrollBarRect(scrollBarA, scrollBarB); bool inScrollBar = scrollBarRect.Contains(io.MousePos); draw_list->AddRectFilled(scrollBarA, scrollBarB, 0xFF101010, 8); ImVec2 scrollBarC(scrollBarMin.x + legendWidth + startFrameOffset, scrollBarMin.y); ImVec2 scrollBarD(scrollBarMin.x + legendWidth + barWidthInPixels + startFrameOffset, scrollBarMax.y - 2); draw_list->AddRectFilled(scrollBarC, scrollBarD, (inScrollBar || MovingScrollBar) ? 0xFF606060 : 0xFF505050, 6); ImRect barHandleLeft(scrollBarC, ImVec2(scrollBarC.x + 14, scrollBarD.y)); ImRect barHandleRight(ImVec2(scrollBarD.x - 14, scrollBarC.y), scrollBarD); bool onLeft = barHandleLeft.Contains(io.MousePos); bool onRight = barHandleRight.Contains(io.MousePos); static bool sizingRBar = false; static bool sizingLBar = false; draw_list->AddRectFilled(barHandleLeft.Min, barHandleLeft.Max, (onLeft || sizingLBar) ? 0xFFAAAAAA : 0xFF666666, 6); draw_list->AddRectFilled(barHandleRight.Min, barHandleRight.Max, (onRight || sizingRBar) ? 0xFFAAAAAA : 0xFF666666, 6); ImRect scrollBarThumb(scrollBarC, scrollBarD); static const float MinBarWidth = 44.f; if (sizingRBar) { if (!io.MouseDown[0]) { sizingRBar = false; } else { float barNewWidth = ImMax(barWidthInPixels + io.MouseDelta.x, MinBarWidth); float barRatio = barNewWidth / barWidthInPixels; framePixelWidthTarget = framePixelWidth = framePixelWidth / barRatio; int newVisibleFrameCount = int((canvas_size.x - legendWidth) / framePixelWidthTarget); int lastFrame = *firstFrame + newVisibleFrameCount; if (lastFrame > sequence->GetFrameMax()) { framePixelWidthTarget = framePixelWidth = (canvas_size.x - legendWidth) / float(sequence->GetFrameMax() - *firstFrame); } } } else if (sizingLBar) { if (!io.MouseDown[0]) { sizingLBar = false; } else { if (fabsf(io.MouseDelta.x) > FLT_EPSILON) { float barNewWidth = ImMax(barWidthInPixels - io.MouseDelta.x, MinBarWidth); float barRatio = barNewWidth / barWidthInPixels; float previousFramePixelWidthTarget = framePixelWidthTarget; framePixelWidthTarget = framePixelWidth = framePixelWidth / barRatio; int newVisibleFrameCount = int(visibleFrameCount / barRatio); int newFirstFrame = *firstFrame + newVisibleFrameCount - visibleFrameCount; newFirstFrame = ImClamp(newFirstFrame, sequence->GetFrameMin(), ImMax(sequence->GetFrameMax() - visibleFrameCount, sequence->GetFrameMin())); if (newFirstFrame == *firstFrame) { framePixelWidth = framePixelWidthTarget = previousFramePixelWidthTarget; } else { *firstFrame = newFirstFrame; } } } } else { if (MovingScrollBar) { if (!io.MouseDown[0]) { MovingScrollBar = false; } else { float framesPerPixelInBar = barWidthInPixels / (float)visibleFrameCount; *firstFrame = int((io.MousePos.x - panningViewSource.x) / framesPerPixelInBar) - panningViewFrame; *firstFrame = ImClamp(*firstFrame, sequence->GetFrameMin(), ImMax(sequence->GetFrameMax() - visibleFrameCount, sequence->GetFrameMin())); } } else { if (scrollBarThumb.Contains(io.MousePos) && ImGui::IsMouseClicked(0) && firstFrame && !MovingCurrentFrame && movingEntry == -1) { MovingScrollBar = true; panningViewSource = io.MousePos; panningViewFrame = -*firstFrame; } if (!sizingRBar && onRight && ImGui::IsMouseClicked(0)) sizingRBar = true; if (!sizingLBar && onLeft && ImGui::IsMouseClicked(0)) sizingLBar = true; } } } } ImGui::EndGroup(); if (regionRect.Contains(io.MousePos)) { bool overCustomDraw = false; for (auto& custom : customDraws) { if (custom.customRect.Contains(io.MousePos)) { overCustomDraw = true; } } if (overCustomDraw) { } else { #if 0 frameOverCursor = *firstFrame + (int)(visibleFrameCount * ((io.MousePos.x - (float)legendWidth - canvas_pos.x) / (canvas_size.x - legendWidth))); //frameOverCursor = max(min(*firstFrame - visibleFrameCount / 2, frameCount - visibleFrameCount), 0); /**firstFrame -= frameOverCursor; *firstFrame *= framePixelWidthTarget / framePixelWidth; *firstFrame += frameOverCursor;*/ if (io.MouseWheel < -FLT_EPSILON) { *firstFrame -= frameOverCursor; *firstFrame = int(*firstFrame * 1.1f); framePixelWidthTarget *= 0.9f; *firstFrame += frameOverCursor; } if (io.MouseWheel > FLT_EPSILON) { *firstFrame -= frameOverCursor; *firstFrame = int(*firstFrame * 0.9f); framePixelWidthTarget *= 1.1f; *firstFrame += frameOverCursor; } #endif } } if (expanded) { if (SequencerAddDelButton(draw_list, ImVec2(canvas_pos.x + 2, canvas_pos.y + 2), !*expanded)) *expanded = !*expanded; } if (delEntry != -1) { sequence->Del(delEntry); if (selectedEntry && (*selectedEntry == delEntry || *selectedEntry >= sequence->GetItemCount())) *selectedEntry = -1; } if (dupEntry != -1) { sequence->Duplicate(dupEntry); } return ret; } } ================================================ FILE: Source/External/imgui_tools/imguizmo/ImSequencer.h ================================================ // https://github.com/CedricGuillemet/ImGuizmo // v 1.89 WIP // // The MIT License(MIT) // // Copyright(c) 2021 Cedric Guillemet // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files(the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and / or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions : // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #pragma once #include struct ImDrawList; struct ImRect; namespace ImSequencer { enum SEQUENCER_OPTIONS { SEQUENCER_EDIT_NONE = 0, SEQUENCER_EDIT_STARTEND = 1 << 1, SEQUENCER_CHANGE_FRAME = 1 << 3, SEQUENCER_ADD = 1 << 4, SEQUENCER_DEL = 1 << 5, SEQUENCER_COPYPASTE = 1 << 6, SEQUENCER_EDIT_ALL = SEQUENCER_EDIT_STARTEND | SEQUENCER_CHANGE_FRAME }; struct SequenceInterface { bool focused = false; virtual int GetFrameMin() const = 0; virtual int GetFrameMax() const = 0; virtual int GetItemCount() const = 0; virtual void BeginEdit(int /*index*/) {} virtual void EndEdit() {} virtual int GetItemTypeCount() const { return 0; } virtual const char* GetItemTypeName(int /*typeIndex*/) const { return ""; } virtual const char* GetItemLabel(int /*index*/) const { return ""; } virtual const char* GetCollapseFmt() const { return "%d Frames / %d entries"; } virtual void Get(int index, int** start, int** end, int* type, unsigned int* color) = 0; virtual void Add(int /*type*/) {} virtual void Del(int /*index*/) {} virtual void Duplicate(int /*index*/) {} virtual void Copy() {} virtual void Paste() {} virtual size_t GetCustomHeight(int /*index*/) { return 0; } virtual void DoubleClick(int /*index*/) {} virtual void CustomDraw(int /*index*/, ImDrawList* /*draw_list*/, const ImRect& /*rc*/, const ImRect& /*legendRect*/, const ImRect& /*clippingRect*/, const ImRect& /*legendClippingRect*/) {} virtual void CustomDrawCompact(int /*index*/, ImDrawList* /*draw_list*/, const ImRect& /*rc*/, const ImRect& /*clippingRect*/) {} }; // return true if selection is made bool Sequencer(SequenceInterface* sequence, int* currentFrame, bool* expanded, int* selectedEntry, int* firstFrame, int sequenceOptions); } ================================================ FILE: Source/External/imgui_tools/imguizmo/ImZoomSlider.h ================================================ // https://github.com/CedricGuillemet/ImGuizmo // v 1.89 WIP // // The MIT License(MIT) // // Copyright(c) 2021 Cedric Guillemet // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files(the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and / or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions : // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #pragma once namespace ImZoomSlider { typedef int ImGuiZoomSliderFlags; enum ImGuiPopupFlags_ { ImGuiZoomSliderFlags_None = 0, ImGuiZoomSliderFlags_Vertical = 1, ImGuiZoomSliderFlags_NoAnchors = 2, ImGuiZoomSliderFlags_NoMiddleCarets = 4, ImGuiZoomSliderFlags_NoWheel = 8, }; template bool ImZoomSlider(const T lower, const T higher, T& viewLower, T& viewHigher, float wheelRatio = 0.01f, ImGuiZoomSliderFlags flags = ImGuiZoomSliderFlags_None) { bool interacted = false; ImGuiIO& io = ImGui::GetIO(); ImDrawList* draw_list = ImGui::GetWindowDrawList(); static const float handleSize = 12; static const float roundRadius = 3.f; static const char* controlName = "ImZoomSlider"; static bool movingScrollBarSvg = false; static bool sizingRBarSvg = false; static bool sizingLBarSvg = false; static ImGuiID editingId = (ImGuiID)-1; static float scrollingSource = 0.f; static float saveViewLower; static float saveViewHigher; const bool isVertical = flags & ImGuiZoomSliderFlags_Vertical; const ImVec2 canvasPos = ImGui::GetCursorScreenPos(); const ImVec2 canvasSize = ImGui::GetContentRegionAvail(); const float canvasSizeLength = isVertical ? ImGui::GetItemRectSize().y : canvasSize.x; const ImVec2 scrollBarSize = isVertical ? ImVec2(14.f, canvasSizeLength) : ImVec2(canvasSizeLength, 14.f); ImGui::InvisibleButton(controlName, scrollBarSize); const ImGuiID currentId = ImGui::GetID(controlName); const bool usingEditingId = currentId == editingId; const bool canUseControl = usingEditingId || editingId == -1; const bool movingScrollBar = usingEditingId ? movingScrollBarSvg : false; const bool sizingRBar = usingEditingId ? sizingRBarSvg : false; const bool sizingLBar = usingEditingId ? sizingLBarSvg : false; const int componentIndex = isVertical ? 1 : 0; const ImVec2 scrollBarMin = ImGui::GetItemRectMin(); const ImVec2 scrollBarMax = ImGui::GetItemRectMax(); const ImVec2 scrollBarA = ImVec2(scrollBarMin.x, scrollBarMin.y) - (isVertical ? ImVec2(2,0) : ImVec2(0,2)); const ImVec2 scrollBarB = isVertical ? ImVec2(scrollBarMax.x - 1.f, scrollBarMin.y + canvasSizeLength) : ImVec2(scrollBarMin.x + canvasSizeLength, scrollBarMax.y - 1.f); const float scrollStart = ((viewLower - lower) / (higher - lower)) * canvasSizeLength + scrollBarMin[componentIndex]; const float scrollEnd = ((viewHigher - lower) / (higher - lower)) * canvasSizeLength + scrollBarMin[componentIndex]; const float screenSize = scrollEnd - scrollStart; const ImVec2 scrollTopLeft = isVertical ? ImVec2(scrollBarMin.x, scrollStart) : ImVec2(scrollStart, scrollBarMin.y); const ImVec2 scrollBottomRight = isVertical ? ImVec2(scrollBarMax.x - 2.f, scrollEnd) : ImVec2(scrollEnd, scrollBarMax.y - 2.f); const bool inScrollBar = canUseControl && ImRect(scrollTopLeft, scrollBottomRight).Contains(io.MousePos); const ImRect scrollBarRect(scrollBarA, scrollBarB); const float deltaScreen = io.MousePos[componentIndex] - scrollingSource; const float deltaView = ((higher - lower) / canvasSizeLength) * deltaScreen; const uint32_t barColor = ImGui::GetColorU32((inScrollBar || movingScrollBar) ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); const float middleCoord = (scrollStart + scrollEnd) * 0.5f; const bool insideControl = canUseControl && ImRect(scrollBarMin, scrollBarMax).Contains(io.MousePos); const bool hasAnchors = !(flags & ImGuiZoomSliderFlags_NoAnchors); const float viewMinSize = ((3.f * handleSize) / canvasSizeLength) * (higher - lower); const auto ClipView = [lower, higher, &viewLower, &viewHigher]() { if (viewLower < lower) { const float deltaClip = lower - viewLower; viewLower += deltaClip; viewHigher += deltaClip; } if (viewHigher > higher) { const float deltaClip = viewHigher - higher; viewLower -= deltaClip; viewHigher -= deltaClip; } }; bool onLeft = false; bool onRight = false; draw_list->AddRectFilled(scrollBarA, scrollBarB, 0xFF101010, roundRadius); draw_list->AddRectFilled(scrollBarA, scrollBarB, 0xFF222222, 0); draw_list->AddRectFilled(scrollTopLeft, scrollBottomRight, barColor, roundRadius); if (!(flags & ImGuiZoomSliderFlags_NoMiddleCarets)) { for (float i = 0.5f; i < 3.f; i += 1.f) { const float coordA = middleCoord - handleSize * 0.5f; const float coordB = middleCoord + handleSize * 0.5f; ImVec2 base = scrollBarMin; base.x += scrollBarSize.x * 0.25f * i; base.y += scrollBarSize.y * 0.25f * i; if (isVertical) { draw_list->AddLine(ImVec2(base.x, coordA), ImVec2(base.x, coordB), ImGui::GetColorU32(ImGuiCol_SliderGrab)); } else { draw_list->AddLine(ImVec2(coordA, base.y), ImVec2(coordB, base.y), ImGui::GetColorU32(ImGuiCol_SliderGrab)); } } } // Mouse wheel if (io.MouseClicked[0] && insideControl && !inScrollBar) { const float ratio = (io.MousePos[componentIndex] - scrollBarMin[componentIndex]) / (scrollBarMax[componentIndex] - scrollBarMin[componentIndex]); const float size = (higher - lower); const float halfViewSize = (viewHigher - viewLower) * 0.5f; const float middle = ratio * size + lower; viewLower = middle - halfViewSize; viewHigher = middle + halfViewSize; ClipView(); interacted = true; } if (!(flags & ImGuiZoomSliderFlags_NoWheel) && inScrollBar && fabsf(io.MouseWheel) > 0.f) { const float ratio = (io.MousePos[componentIndex] - scrollStart) / (scrollEnd - scrollStart); const float amount = io.MouseWheel * wheelRatio * (viewHigher - viewLower); viewLower -= ratio * amount; viewHigher += (1.f - ratio) * amount; ClipView(); interacted = true; } if (screenSize > handleSize * 2.f && hasAnchors) { const ImRect barHandleLeft(scrollTopLeft, isVertical ? ImVec2(scrollBottomRight.x, scrollTopLeft.y + handleSize) : ImVec2(scrollTopLeft.x + handleSize, scrollBottomRight.y)); const ImRect barHandleRight(isVertical ? ImVec2(scrollTopLeft.x, scrollBottomRight.y - handleSize) : ImVec2(scrollBottomRight.x - handleSize, scrollTopLeft.y), scrollBottomRight); onLeft = barHandleLeft.Contains(io.MousePos); onRight = barHandleRight.Contains(io.MousePos); draw_list->AddRectFilled(barHandleLeft.Min, barHandleLeft.Max, ImGui::GetColorU32((onLeft || sizingLBar) ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), roundRadius); draw_list->AddRectFilled(barHandleRight.Min, barHandleRight.Max, ImGui::GetColorU32((onRight || sizingRBar) ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), roundRadius); } if (sizingRBar) { if (!io.MouseDown[0]) { sizingRBarSvg = false; editingId = (ImGuiID)-1; } else { viewHigher = ImMin(saveViewHigher + deltaView, higher); } } else if (sizingLBar) { if (!io.MouseDown[0]) { sizingLBarSvg = false; editingId = (ImGuiID)-1; } else { viewLower = ImMax(saveViewLower + deltaView, lower); } } else { if (movingScrollBar) { if (!io.MouseDown[0]) { movingScrollBarSvg = false; editingId = (ImGuiID)-1; } else { viewLower = saveViewLower + deltaView; viewHigher = saveViewHigher + deltaView; ClipView(); } } else { if (inScrollBar && ImGui::IsMouseClicked(0)) { movingScrollBarSvg = true; scrollingSource = io.MousePos[componentIndex]; saveViewLower = viewLower; saveViewHigher = viewHigher; editingId = currentId; } if (!sizingRBar && onRight && ImGui::IsMouseClicked(0) && hasAnchors) { sizingRBarSvg = true; editingId = currentId; } if (!sizingLBar && onLeft && ImGui::IsMouseClicked(0) && hasAnchors) { sizingLBarSvg = true; editingId = currentId; } } } // minimal size check if ((viewHigher - viewLower) < viewMinSize) { const float middle = (viewLower + viewHigher) * 0.5f; viewLower = middle - viewMinSize * 0.5f; viewHigher = middle + viewMinSize * 0.5f; ClipView(); } return movingScrollBar || sizingRBar || sizingLBar || interacted; } } // namespace ================================================ FILE: Source/External/imgui_tools/imnodes/imnodes.cpp ================================================ // the structure of this file: // // [SECTION] bezier curve helpers // [SECTION] draw list helper // [SECTION] ui state logic // [SECTION] render helpers // [SECTION] API implementation #define IMGUI_DEFINE_MATH_OPERATORS #include "imnodes.h" #include "imnodes_internal.h" #include // Check minimum ImGui version #define MINIMUM_COMPATIBLE_IMGUI_VERSION 17400 #if IMGUI_VERSION_NUM < MINIMUM_COMPATIBLE_IMGUI_VERSION #error "Minimum ImGui version requirement not met -- please use a newer version!" #endif #include #include #include #include #include // for fwrite, ssprintf, sscanf #include #include // strlen, strncmp // Use secure CRT function variants to avoid MSVC compiler errors #ifdef _MSC_VER #define sscanf sscanf_s #endif ImNodesContext* GImNodes = NULL; namespace IMNODES_NAMESPACE { namespace { // [SECTION] bezier curve helpers struct CubicBezier { ImVec2 P0, P1, P2, P3; int NumSegments; }; inline ImVec2 EvalCubicBezier( const float t, const ImVec2& P0, const ImVec2& P1, const ImVec2& P2, const ImVec2& P3) { // B(t) = (1-t)**3 p0 + 3(1 - t)**2 t P1 + 3(1-t)t**2 P2 + t**3 P3 const float u = 1.0f - t; const float b0 = u * u * u; const float b1 = 3 * u * u * t; const float b2 = 3 * u * t * t; const float b3 = t * t * t; return ImVec2( b0 * P0.x + b1 * P1.x + b2 * P2.x + b3 * P3.x, b0 * P0.y + b1 * P1.y + b2 * P2.y + b3 * P3.y); } // Calculates the closest point along each bezier curve segment. ImVec2 GetClosestPointOnCubicBezier(const int num_segments, const ImVec2& p, const CubicBezier& cb) { IM_ASSERT(num_segments > 0); ImVec2 p_last = cb.P0; ImVec2 p_closest; float p_closest_dist = FLT_MAX; float t_step = 1.0f / (float)num_segments; for (int i = 1; i <= num_segments; ++i) { ImVec2 p_current = EvalCubicBezier(t_step * i, cb.P0, cb.P1, cb.P2, cb.P3); ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p); float dist = ImLengthSqr(p - p_line); if (dist < p_closest_dist) { p_closest = p_line; p_closest_dist = dist; } p_last = p_current; } return p_closest; } inline float GetDistanceToCubicBezier( const ImVec2& pos, const CubicBezier& cubic_bezier, const int num_segments) { const ImVec2 point_on_curve = GetClosestPointOnCubicBezier(num_segments, pos, cubic_bezier); const ImVec2 to_curve = point_on_curve - pos; return ImSqrt(ImLengthSqr(to_curve)); } inline ImRect GetContainingRectForCubicBezier(const CubicBezier& cb) { const ImVec2 min = ImVec2(ImMin(cb.P0.x, cb.P3.x), ImMin(cb.P0.y, cb.P3.y)); const ImVec2 max = ImVec2(ImMax(cb.P0.x, cb.P3.x), ImMax(cb.P0.y, cb.P3.y)); const float hover_distance = GImNodes->Style.LinkHoverDistance; ImRect rect(min, max); rect.Add(cb.P1); rect.Add(cb.P2); rect.Expand(ImVec2(hover_distance, hover_distance)); return rect; } inline CubicBezier GetCubicBezier( ImVec2 start, ImVec2 end, const ImNodesAttributeType start_type, const float line_segments_per_length) { IM_ASSERT( (start_type == ImNodesAttributeType_Input) || (start_type == ImNodesAttributeType_Output)); if (start_type == ImNodesAttributeType_Input) { ImSwap(start, end); } const float link_length = ImSqrt(ImLengthSqr(end - start)); const ImVec2 offset = ImVec2(0.25f * link_length, 0.f); CubicBezier cubic_bezier; cubic_bezier.P0 = start; cubic_bezier.P1 = start + offset; cubic_bezier.P2 = end - offset; cubic_bezier.P3 = end; cubic_bezier.NumSegments = ImMax(static_cast(link_length * line_segments_per_length), 1); return cubic_bezier; } inline float EvalImplicitLineEq(const ImVec2& p1, const ImVec2& p2, const ImVec2& p) { return (p2.y - p1.y) * p.x + (p1.x - p2.x) * p.y + (p2.x * p1.y - p1.x * p2.y); } inline int Sign(float val) { return int(val > 0.0f) - int(val < 0.0f); } inline bool RectangleOverlapsLineSegment(const ImRect& rect, const ImVec2& p1, const ImVec2& p2) { // Trivial case: rectangle contains an endpoint if (rect.Contains(p1) || rect.Contains(p2)) { return true; } // Flip rectangle if necessary ImRect flip_rect = rect; if (flip_rect.Min.x > flip_rect.Max.x) { ImSwap(flip_rect.Min.x, flip_rect.Max.x); } if (flip_rect.Min.y > flip_rect.Max.y) { ImSwap(flip_rect.Min.y, flip_rect.Max.y); } // Trivial case: line segment lies to one particular side of rectangle if ((p1.x < flip_rect.Min.x && p2.x < flip_rect.Min.x) || (p1.x > flip_rect.Max.x && p2.x > flip_rect.Max.x) || (p1.y < flip_rect.Min.y && p2.y < flip_rect.Min.y) || (p1.y > flip_rect.Max.y && p2.y > flip_rect.Max.y)) { return false; } const int corner_signs[4] = { Sign(EvalImplicitLineEq(p1, p2, flip_rect.Min)), Sign(EvalImplicitLineEq(p1, p2, ImVec2(flip_rect.Max.x, flip_rect.Min.y))), Sign(EvalImplicitLineEq(p1, p2, ImVec2(flip_rect.Min.x, flip_rect.Max.y))), Sign(EvalImplicitLineEq(p1, p2, flip_rect.Max))}; int sum = 0; int sum_abs = 0; for (int i = 0; i < 4; ++i) { sum += corner_signs[i]; sum_abs += abs(corner_signs[i]); } // At least one corner of rectangle lies on a different side of line segment return abs(sum) != sum_abs; } inline bool RectangleOverlapsBezier(const ImRect& rectangle, const CubicBezier& cubic_bezier) { ImVec2 current = EvalCubicBezier(0.f, cubic_bezier.P0, cubic_bezier.P1, cubic_bezier.P2, cubic_bezier.P3); const float dt = 1.0f / cubic_bezier.NumSegments; for (int s = 0; s < cubic_bezier.NumSegments; ++s) { ImVec2 next = EvalCubicBezier( static_cast((s + 1) * dt), cubic_bezier.P0, cubic_bezier.P1, cubic_bezier.P2, cubic_bezier.P3); if (RectangleOverlapsLineSegment(rectangle, current, next)) { return true; } current = next; } return false; } inline bool RectangleOverlapsLink( const ImRect& rectangle, const ImVec2& start, const ImVec2& end, const ImNodesAttributeType start_type) { // First level: simple rejection test via rectangle overlap: ImRect lrect = ImRect(start, end); if (lrect.Min.x > lrect.Max.x) { ImSwap(lrect.Min.x, lrect.Max.x); } if (lrect.Min.y > lrect.Max.y) { ImSwap(lrect.Min.y, lrect.Max.y); } if (rectangle.Overlaps(lrect)) { // First, check if either one or both endpoinds are trivially contained // in the rectangle if (rectangle.Contains(start) || rectangle.Contains(end)) { return true; } // Second level of refinement: do a more expensive test against the // link const CubicBezier cubic_bezier = GetCubicBezier(start, end, start_type, GImNodes->Style.LinkLineSegmentsPerLength); return RectangleOverlapsBezier(rectangle, cubic_bezier); } return false; } // [SECTION] coordinate space conversion helpers inline ImVec2 ScreenSpaceToGridSpace(const ImNodesEditorContext& editor, const ImVec2& v) { return v - GImNodes->CanvasOriginScreenSpace - editor.Panning; } inline ImRect ScreenSpaceToGridSpace(const ImNodesEditorContext& editor, const ImRect& r) { return ImRect(ScreenSpaceToGridSpace(editor, r.Min), ScreenSpaceToGridSpace(editor, r.Max)); } inline ImVec2 GridSpaceToScreenSpace(const ImNodesEditorContext& editor, const ImVec2& v) { return v + GImNodes->CanvasOriginScreenSpace + editor.Panning; } inline ImVec2 GridSpaceToEditorSpace(const ImNodesEditorContext& editor, const ImVec2& v) { return v + editor.Panning; } inline ImVec2 EditorSpaceToGridSpace(const ImNodesEditorContext& editor, const ImVec2& v) { return v - editor.Panning; } inline ImVec2 EditorSpaceToScreenSpace(const ImVec2& v) { return GImNodes->CanvasOriginScreenSpace + v; } inline ImVec2 MiniMapSpaceToGridSpace(const ImNodesEditorContext& editor, const ImVec2& v) { return (v - editor.MiniMapContentScreenSpace.Min) / editor.MiniMapScaling + editor.GridContentBounds.Min; } inline ImVec2 ScreenSpaceToMiniMapSpace(const ImNodesEditorContext& editor, const ImVec2& v) { return (ScreenSpaceToGridSpace(editor, v) - editor.GridContentBounds.Min) * editor.MiniMapScaling + editor.MiniMapContentScreenSpace.Min; } inline ImRect ScreenSpaceToMiniMapSpace(const ImNodesEditorContext& editor, const ImRect& r) { return ImRect( ScreenSpaceToMiniMapSpace(editor, r.Min), ScreenSpaceToMiniMapSpace(editor, r.Max)); } // [SECTION] draw list helper void ImDrawListGrowChannels(ImDrawList* draw_list, const int num_channels) { ImDrawListSplitter& splitter = draw_list->_Splitter; if (splitter._Count == 1) { splitter.Split(draw_list, num_channels + 1); return; } // NOTE: this logic has been lifted from ImDrawListSplitter::Split with slight modifications // to allow nested splits. The main modification is that we only create new ImDrawChannel // instances after splitter._Count, instead of over the whole splitter._Channels array like // the regular ImDrawListSplitter::Split method does. const int old_channel_capacity = splitter._Channels.Size; // NOTE: _Channels is not resized down, and therefore _Count <= _Channels.size()! const int old_channel_count = splitter._Count; const int requested_channel_count = old_channel_count + num_channels; if (old_channel_capacity < old_channel_count + num_channels) { splitter._Channels.resize(requested_channel_count); } splitter._Count = requested_channel_count; for (int i = old_channel_count; i < requested_channel_count; ++i) { ImDrawChannel& channel = splitter._Channels[i]; // If we're inside the old capacity region of the array, we need to reuse the existing // memory of the command and index buffers. if (i < old_channel_capacity) { channel._CmdBuffer.resize(0); channel._IdxBuffer.resize(0); } // Else, we need to construct new draw channels. else { IM_PLACEMENT_NEW(&channel) ImDrawChannel(); } { ImDrawCmd draw_cmd; draw_cmd.ClipRect = draw_list->_ClipRectStack.back(); draw_cmd.TextureId = draw_list->_TextureIdStack.back(); channel._CmdBuffer.push_back(draw_cmd); } } } void ImDrawListSplitterSwapChannels( ImDrawListSplitter& splitter, const int lhs_idx, const int rhs_idx) { if (lhs_idx == rhs_idx) { return; } IM_ASSERT(lhs_idx >= 0 && lhs_idx < splitter._Count); IM_ASSERT(rhs_idx >= 0 && rhs_idx < splitter._Count); ImDrawChannel& lhs_channel = splitter._Channels[lhs_idx]; ImDrawChannel& rhs_channel = splitter._Channels[rhs_idx]; lhs_channel._CmdBuffer.swap(rhs_channel._CmdBuffer); lhs_channel._IdxBuffer.swap(rhs_channel._IdxBuffer); const int current_channel = splitter._Current; if (current_channel == lhs_idx) { splitter._Current = rhs_idx; } else if (current_channel == rhs_idx) { splitter._Current = lhs_idx; } } void DrawListSet(ImDrawList* window_draw_list) { GImNodes->CanvasDrawList = window_draw_list; GImNodes->NodeIdxToSubmissionIdx.Clear(); GImNodes->NodeIdxSubmissionOrder.clear(); } // The draw list channels are structured as follows. First we have our base channel, the canvas grid // on which we render the grid lines in BeginNodeEditor(). The base channel is the reason // draw_list_submission_idx_to_background_channel_idx offsets the index by one. Each BeginNode() // call appends two new draw channels, for the node background and foreground. The node foreground // is the channel into which the node's ImGui content is rendered. Finally, in EndNodeEditor() we // append one last draw channel for rendering the selection box and the incomplete link on top of // everything else. // // +----------+----------+----------+----------+----------+----------+ // | | | | | | | // |canvas |node |node |... |... |click | // |grid |background|foreground| | |interaction // | | | | | | | // +----------+----------+----------+----------+----------+----------+ // | | // | submission idx | // | | // ----------------------- void DrawListAddNode(const int node_idx) { GImNodes->NodeIdxToSubmissionIdx.SetInt( static_cast(node_idx), GImNodes->NodeIdxSubmissionOrder.Size); GImNodes->NodeIdxSubmissionOrder.push_back(node_idx); ImDrawListGrowChannels(GImNodes->CanvasDrawList, 2); } void DrawListAppendClickInteractionChannel() { // NOTE: don't use this function outside of EndNodeEditor. Using this before all nodes have been // added will screw up the node draw order. ImDrawListGrowChannels(GImNodes->CanvasDrawList, 1); } int DrawListSubmissionIdxToBackgroundChannelIdx(const int submission_idx) { // NOTE: the first channel is the canvas background, i.e. the grid return 1 + 2 * submission_idx; } int DrawListSubmissionIdxToForegroundChannelIdx(const int submission_idx) { return DrawListSubmissionIdxToBackgroundChannelIdx(submission_idx) + 1; } void DrawListActivateClickInteractionChannel() { GImNodes->CanvasDrawList->_Splitter.SetCurrentChannel( GImNodes->CanvasDrawList, GImNodes->CanvasDrawList->_Splitter._Count - 1); } void DrawListActivateCurrentNodeForeground() { const int foreground_channel_idx = DrawListSubmissionIdxToForegroundChannelIdx(GImNodes->NodeIdxSubmissionOrder.Size - 1); GImNodes->CanvasDrawList->_Splitter.SetCurrentChannel( GImNodes->CanvasDrawList, foreground_channel_idx); } void DrawListActivateNodeBackground(const int node_idx) { const int submission_idx = GImNodes->NodeIdxToSubmissionIdx.GetInt(static_cast(node_idx), -1); // There is a discrepancy in the submitted node count and the rendered node count! Did you call // one of the following functions // * EditorContextMoveToNode // * SetNodeScreenSpacePos // * SetNodeGridSpacePos // * SetNodeDraggable // after the BeginNode/EndNode function calls? IM_ASSERT(submission_idx != -1); const int background_channel_idx = DrawListSubmissionIdxToBackgroundChannelIdx(submission_idx); GImNodes->CanvasDrawList->_Splitter.SetCurrentChannel( GImNodes->CanvasDrawList, background_channel_idx); } void DrawListSwapSubmissionIndices(const int lhs_idx, const int rhs_idx) { IM_ASSERT(lhs_idx != rhs_idx); const int lhs_foreground_channel_idx = DrawListSubmissionIdxToForegroundChannelIdx(lhs_idx); const int lhs_background_channel_idx = DrawListSubmissionIdxToBackgroundChannelIdx(lhs_idx); const int rhs_foreground_channel_idx = DrawListSubmissionIdxToForegroundChannelIdx(rhs_idx); const int rhs_background_channel_idx = DrawListSubmissionIdxToBackgroundChannelIdx(rhs_idx); ImDrawListSplitterSwapChannels( GImNodes->CanvasDrawList->_Splitter, lhs_background_channel_idx, rhs_background_channel_idx); ImDrawListSplitterSwapChannels( GImNodes->CanvasDrawList->_Splitter, lhs_foreground_channel_idx, rhs_foreground_channel_idx); } void DrawListSortChannelsByDepth(const ImVector& node_idx_depth_order) { if (GImNodes->NodeIdxToSubmissionIdx.Data.Size < 2) { return; } IM_ASSERT(node_idx_depth_order.Size == GImNodes->NodeIdxSubmissionOrder.Size); int start_idx = node_idx_depth_order.Size - 1; while (node_idx_depth_order[start_idx] == GImNodes->NodeIdxSubmissionOrder[start_idx]) { if (--start_idx == 0) { // early out if submission order and depth order are the same return; } } // TODO: this is an O(N^2) algorithm. It might be worthwhile revisiting this to see if the time // complexity can be reduced. for (int depth_idx = start_idx; depth_idx > 0; --depth_idx) { const int node_idx = node_idx_depth_order[depth_idx]; // Find the current index of the node_idx in the submission order array int submission_idx = -1; for (int i = 0; i < GImNodes->NodeIdxSubmissionOrder.Size; ++i) { if (GImNodes->NodeIdxSubmissionOrder[i] == node_idx) { submission_idx = i; break; } } IM_ASSERT(submission_idx >= 0); if (submission_idx == depth_idx) { continue; } for (int j = submission_idx; j < depth_idx; ++j) { DrawListSwapSubmissionIndices(j, j + 1); ImSwap(GImNodes->NodeIdxSubmissionOrder[j], GImNodes->NodeIdxSubmissionOrder[j + 1]); } } } // [SECTION] ui state logic ImVec2 GetScreenSpacePinCoordinates( const ImRect& node_rect, const ImRect& attribute_rect, const ImNodesAttributeType type) { IM_ASSERT(type == ImNodesAttributeType_Input || type == ImNodesAttributeType_Output); const float x = type == ImNodesAttributeType_Input ? (node_rect.Min.x - GImNodes->Style.PinOffset) : (node_rect.Max.x + GImNodes->Style.PinOffset); return ImVec2(x, 0.5f * (attribute_rect.Min.y + attribute_rect.Max.y)); } ImVec2 GetScreenSpacePinCoordinates(const ImNodesEditorContext& editor, const ImPinData& pin) { const ImRect& parent_node_rect = editor.Nodes.Pool[pin.ParentNodeIdx].Rect; return GetScreenSpacePinCoordinates(parent_node_rect, pin.AttributeRect, pin.Type); } bool MouseInCanvas() { // This flag should be true either when hovering or clicking something in the canvas. const bool is_window_hovered_or_focused = ImGui::IsWindowHovered() || ImGui::IsWindowFocused(); return is_window_hovered_or_focused && GImNodes->CanvasRectScreenSpace.Contains(ImGui::GetMousePos()); } void BeginNodeSelection(ImNodesEditorContext& editor, const int node_idx) { // Don't start selecting a node if we are e.g. already creating and dragging // a new link! New link creation can happen when the mouse is clicked over // a node, but within the hover radius of a pin. if (editor.ClickInteraction.Type != ImNodesClickInteractionType_None) { return; } editor.ClickInteraction.Type = ImNodesClickInteractionType_Node; // If the node is not already contained in the selection, then we want only // the interaction node to be selected, effective immediately. // // If the multiple selection modifier is active, we want to add this node // to the current list of selected nodes. // // Otherwise, we want to allow for the possibility of multiple nodes to be // moved at once. if (!editor.SelectedNodeIndices.contains(node_idx)) { editor.SelectedLinkIndices.clear(); if (!GImNodes->MultipleSelectModifier) { editor.SelectedNodeIndices.clear(); } editor.SelectedNodeIndices.push_back(node_idx); // Ensure that individually selected nodes get rendered on top ImVector& depth_stack = editor.NodeDepthOrder; const int* const elem = depth_stack.find(node_idx); IM_ASSERT(elem != depth_stack.end()); depth_stack.erase(elem); depth_stack.push_back(node_idx); } // Deselect a previously-selected node else if (GImNodes->MultipleSelectModifier) { const int* const node_ptr = editor.SelectedNodeIndices.find(node_idx); editor.SelectedNodeIndices.erase(node_ptr); // Don't allow dragging after deselecting editor.ClickInteraction.Type = ImNodesClickInteractionType_None; } // To support snapping of multiple nodes, we need to store the offset of // each node in the selection to the origin of the dragged node. const ImVec2 ref_origin = editor.Nodes.Pool[node_idx].Origin; editor.PrimaryNodeOffset = ref_origin + GImNodes->CanvasOriginScreenSpace + editor.Panning - GImNodes->MousePos; editor.SelectedNodeOffsets.clear(); for (int idx = 0; idx < editor.SelectedNodeIndices.Size; idx++) { const int node = editor.SelectedNodeIndices[idx]; const ImVec2 node_origin = editor.Nodes.Pool[node].Origin - ref_origin; editor.SelectedNodeOffsets.push_back(node_origin); } } void BeginLinkSelection(ImNodesEditorContext& editor, const int link_idx) { editor.ClickInteraction.Type = ImNodesClickInteractionType_Link; // When a link is selected, clear all other selections, and insert the link // as the sole selection. editor.SelectedNodeIndices.clear(); editor.SelectedLinkIndices.clear(); editor.SelectedLinkIndices.push_back(link_idx); } void BeginLinkDetach(ImNodesEditorContext& editor, const int link_idx, const int detach_pin_idx) { const ImLinkData& link = editor.Links.Pool[link_idx]; ImClickInteractionState& state = editor.ClickInteraction; state.Type = ImNodesClickInteractionType_LinkCreation; state.LinkCreation.EndPinIdx.Reset(); state.LinkCreation.StartPinIdx = detach_pin_idx == link.StartPinIdx ? link.EndPinIdx : link.StartPinIdx; GImNodes->DeletedLinkIdx = link_idx; } void BeginLinkCreation(ImNodesEditorContext& editor, const int hovered_pin_idx) { editor.ClickInteraction.Type = ImNodesClickInteractionType_LinkCreation; editor.ClickInteraction.LinkCreation.StartPinIdx = hovered_pin_idx; editor.ClickInteraction.LinkCreation.EndPinIdx.Reset(); editor.ClickInteraction.LinkCreation.Type = ImNodesLinkCreationType_Standard; GImNodes->ImNodesUIState |= ImNodesUIState_LinkStarted; } void BeginLinkInteraction( ImNodesEditorContext& editor, const int link_idx, const ImOptionalIndex pin_idx = ImOptionalIndex()) { // Check if we are clicking the link with the modifier pressed. // This will in a link detach via clicking. const bool modifier_pressed = GImNodes->Io.LinkDetachWithModifierClick.Modifier == NULL ? false : *GImNodes->Io.LinkDetachWithModifierClick.Modifier; if (modifier_pressed) { const ImLinkData& link = editor.Links.Pool[link_idx]; const ImPinData& start_pin = editor.Pins.Pool[link.StartPinIdx]; const ImPinData& end_pin = editor.Pins.Pool[link.EndPinIdx]; const ImVec2& mouse_pos = GImNodes->MousePos; const float dist_to_start = ImLengthSqr(start_pin.Pos - mouse_pos); const float dist_to_end = ImLengthSqr(end_pin.Pos - mouse_pos); const int closest_pin_idx = dist_to_start < dist_to_end ? link.StartPinIdx : link.EndPinIdx; editor.ClickInteraction.Type = ImNodesClickInteractionType_LinkCreation; BeginLinkDetach(editor, link_idx, closest_pin_idx); editor.ClickInteraction.LinkCreation.Type = ImNodesLinkCreationType_FromDetach; } else { if (pin_idx.HasValue()) { const int hovered_pin_flags = editor.Pins.Pool[pin_idx.Value()].Flags; // Check the 'click and drag to detach' case. if (hovered_pin_flags & ImNodesAttributeFlags_EnableLinkDetachWithDragClick) { BeginLinkDetach(editor, link_idx, pin_idx.Value()); editor.ClickInteraction.LinkCreation.Type = ImNodesLinkCreationType_FromDetach; } else { BeginLinkCreation(editor, pin_idx.Value()); } } else { BeginLinkSelection(editor, link_idx); } } } static inline bool IsMiniMapHovered(); void BeginCanvasInteraction(ImNodesEditorContext& editor) { const bool any_ui_element_hovered = GImNodes->HoveredNodeIdx.HasValue() || GImNodes->HoveredLinkIdx.HasValue() || GImNodes->HoveredPinIdx.HasValue() || ImGui::IsAnyItemHovered(); const bool mouse_not_in_canvas = !MouseInCanvas(); if (editor.ClickInteraction.Type != ImNodesClickInteractionType_None || any_ui_element_hovered || mouse_not_in_canvas) { return; } const bool started_panning = GImNodes->AltMouseClicked; if (started_panning) { editor.ClickInteraction.Type = ImNodesClickInteractionType_Panning; } else if (GImNodes->LeftMouseClicked) { editor.ClickInteraction.Type = ImNodesClickInteractionType_BoxSelection; editor.ClickInteraction.BoxSelector.Rect.Min = ScreenSpaceToGridSpace(editor, GImNodes->MousePos); } } void BoxSelectorUpdateSelection(ImNodesEditorContext& editor, ImRect box_rect) { // Invert box selector coordinates as needed if (box_rect.Min.x > box_rect.Max.x) { ImSwap(box_rect.Min.x, box_rect.Max.x); } if (box_rect.Min.y > box_rect.Max.y) { ImSwap(box_rect.Min.y, box_rect.Max.y); } // Update node selection editor.SelectedNodeIndices.clear(); // Test for overlap against node rectangles for (int node_idx = 0; node_idx < editor.Nodes.Pool.size(); ++node_idx) { if (editor.Nodes.InUse[node_idx]) { ImNodeData& node = editor.Nodes.Pool[node_idx]; if (box_rect.Overlaps(node.Rect)) { editor.SelectedNodeIndices.push_back(node_idx); } } } // Update link selection editor.SelectedLinkIndices.clear(); // Test for overlap against links for (int link_idx = 0; link_idx < editor.Links.Pool.size(); ++link_idx) { if (editor.Links.InUse[link_idx]) { const ImLinkData& link = editor.Links.Pool[link_idx]; const ImPinData& pin_start = editor.Pins.Pool[link.StartPinIdx]; const ImPinData& pin_end = editor.Pins.Pool[link.EndPinIdx]; const ImRect& node_start_rect = editor.Nodes.Pool[pin_start.ParentNodeIdx].Rect; const ImRect& node_end_rect = editor.Nodes.Pool[pin_end.ParentNodeIdx].Rect; const ImVec2 start = GetScreenSpacePinCoordinates( node_start_rect, pin_start.AttributeRect, pin_start.Type); const ImVec2 end = GetScreenSpacePinCoordinates(node_end_rect, pin_end.AttributeRect, pin_end.Type); // Test if (RectangleOverlapsLink(box_rect, start, end, pin_start.Type)) { editor.SelectedLinkIndices.push_back(link_idx); } } } } ImVec2 SnapOriginToGrid(ImVec2 origin) { if (GImNodes->Style.Flags & ImNodesStyleFlags_GridSnapping) { const float spacing = GImNodes->Style.GridSpacing; const float spacing2 = spacing * 0.5f; // Snap the origin to the nearest grid point in any direction float modx = fmodf(fabsf(origin.x) + spacing2, spacing) - spacing2; float mody = fmodf(fabsf(origin.y) + spacing2, spacing) - spacing2; origin.x += (origin.x < 0.f) ? modx : -modx; origin.y += (origin.y < 0.f) ? mody : -mody; } return origin; } void TranslateSelectedNodes(ImNodesEditorContext& editor) { if (GImNodes->LeftMouseDragging) { // If we have grid snap enabled, don't start moving nodes until we've moved the mouse // slightly const bool shouldTranslate = (GImNodes->Style.Flags & ImNodesStyleFlags_GridSnapping) ? ImGui::GetIO().MouseDragMaxDistanceSqr[0] > 5.0 : true; const ImVec2 origin = SnapOriginToGrid( GImNodes->MousePos - GImNodes->CanvasOriginScreenSpace - editor.Panning + editor.PrimaryNodeOffset); for (int i = 0; i < editor.SelectedNodeIndices.size(); ++i) { const ImVec2 node_rel = editor.SelectedNodeOffsets[i]; const int node_idx = editor.SelectedNodeIndices[i]; ImNodeData& node = editor.Nodes.Pool[node_idx]; if (node.Draggable && shouldTranslate) { node.Origin = origin + node_rel + editor.AutoPanningDelta; } } } } struct LinkPredicate { bool operator()(const ImLinkData& lhs, const ImLinkData& rhs) const { // Do a unique compare by sorting the pins' addresses. // This catches duplicate links, whether they are in the // same direction or not. // Sorting by pin index should have the uniqueness guarantees as sorting // by id -- each unique id will get one slot in the link pool array. int lhs_start = lhs.StartPinIdx; int lhs_end = lhs.EndPinIdx; int rhs_start = rhs.StartPinIdx; int rhs_end = rhs.EndPinIdx; if (lhs_start > lhs_end) { ImSwap(lhs_start, lhs_end); } if (rhs_start > rhs_end) { ImSwap(rhs_start, rhs_end); } return lhs_start == rhs_start && lhs_end == rhs_end; } }; ImOptionalIndex FindDuplicateLink( const ImNodesEditorContext& editor, const int start_pin_idx, const int end_pin_idx) { ImLinkData test_link(0); test_link.StartPinIdx = start_pin_idx; test_link.EndPinIdx = end_pin_idx; for (int link_idx = 0; link_idx < editor.Links.Pool.size(); ++link_idx) { const ImLinkData& link = editor.Links.Pool[link_idx]; if (LinkPredicate()(test_link, link) && editor.Links.InUse[link_idx]) { return ImOptionalIndex(link_idx); } } return ImOptionalIndex(); } bool ShouldLinkSnapToPin( const ImNodesEditorContext& editor, const ImPinData& start_pin, const int hovered_pin_idx, const ImOptionalIndex duplicate_link) { const ImPinData& end_pin = editor.Pins.Pool[hovered_pin_idx]; // The end pin must be in a different node if (start_pin.ParentNodeIdx == end_pin.ParentNodeIdx) { return false; } // The end pin must be of a different type if (start_pin.Type == end_pin.Type) { return false; } // The link to be created must not be a duplicate, unless it is the link which was created on // snap. In that case we want to snap, since we want it to appear visually as if the created // link remains snapped to the pin. if (duplicate_link.HasValue() && !(duplicate_link == GImNodes->SnapLinkIdx)) { return false; } return true; } void ClickInteractionUpdate(ImNodesEditorContext& editor) { switch (editor.ClickInteraction.Type) { case ImNodesClickInteractionType_BoxSelection: { editor.ClickInteraction.BoxSelector.Rect.Max = ScreenSpaceToGridSpace(editor, GImNodes->MousePos); ImRect box_rect = editor.ClickInteraction.BoxSelector.Rect; box_rect.Min = GridSpaceToScreenSpace(editor, box_rect.Min); box_rect.Max = GridSpaceToScreenSpace(editor, box_rect.Max); BoxSelectorUpdateSelection(editor, box_rect); const ImU32 box_selector_color = GImNodes->Style.Colors[ImNodesCol_BoxSelector]; const ImU32 box_selector_outline = GImNodes->Style.Colors[ImNodesCol_BoxSelectorOutline]; GImNodes->CanvasDrawList->AddRectFilled(box_rect.Min, box_rect.Max, box_selector_color); GImNodes->CanvasDrawList->AddRect(box_rect.Min, box_rect.Max, box_selector_outline); if (GImNodes->LeftMouseReleased) { ImVector& depth_stack = editor.NodeDepthOrder; const ImVector& selected_idxs = editor.SelectedNodeIndices; // Bump the selected node indices, in order, to the top of the depth stack. // NOTE: this algorithm has worst case time complexity of O(N^2), if the node selection // is ~ N (due to selected_idxs.contains()). if ((selected_idxs.Size > 0) && (selected_idxs.Size < depth_stack.Size)) { int num_moved = 0; // The number of indices moved. Stop after selected_idxs.Size for (int i = 0; i < depth_stack.Size - selected_idxs.Size; ++i) { for (int node_idx = depth_stack[i]; selected_idxs.contains(node_idx); node_idx = depth_stack[i]) { depth_stack.erase(depth_stack.begin() + static_cast(i)); depth_stack.push_back(node_idx); ++num_moved; } if (num_moved == selected_idxs.Size) { break; } } } editor.ClickInteraction.Type = ImNodesClickInteractionType_None; } } break; case ImNodesClickInteractionType_Node: { TranslateSelectedNodes(editor); if (GImNodes->LeftMouseReleased) { editor.ClickInteraction.Type = ImNodesClickInteractionType_None; } } break; case ImNodesClickInteractionType_Link: { if (GImNodes->LeftMouseReleased) { editor.ClickInteraction.Type = ImNodesClickInteractionType_None; } } break; case ImNodesClickInteractionType_LinkCreation: { const ImPinData& start_pin = editor.Pins.Pool[editor.ClickInteraction.LinkCreation.StartPinIdx]; const ImOptionalIndex maybe_duplicate_link_idx = GImNodes->HoveredPinIdx.HasValue() ? FindDuplicateLink( editor, editor.ClickInteraction.LinkCreation.StartPinIdx, GImNodes->HoveredPinIdx.Value()) : ImOptionalIndex(); const bool should_snap = GImNodes->HoveredPinIdx.HasValue() && ShouldLinkSnapToPin( editor, start_pin, GImNodes->HoveredPinIdx.Value(), maybe_duplicate_link_idx); // If we created on snap and the hovered pin is empty or changed, then we need signal that // the link's state has changed. const bool snapping_pin_changed = editor.ClickInteraction.LinkCreation.EndPinIdx.HasValue() && !(GImNodes->HoveredPinIdx == editor.ClickInteraction.LinkCreation.EndPinIdx); // Detach the link that was created by this link event if it's no longer in snap range if (snapping_pin_changed && GImNodes->SnapLinkIdx.HasValue()) { BeginLinkDetach( editor, GImNodes->SnapLinkIdx.Value(), editor.ClickInteraction.LinkCreation.EndPinIdx.Value()); } const ImVec2 start_pos = GetScreenSpacePinCoordinates(editor, start_pin); // If we are within the hover radius of a receiving pin, snap the link // endpoint to it const ImVec2 end_pos = should_snap ? GetScreenSpacePinCoordinates( editor, editor.Pins.Pool[GImNodes->HoveredPinIdx.Value()]) : GImNodes->MousePos; const CubicBezier cubic_bezier = GetCubicBezier( start_pos, end_pos, start_pin.Type, GImNodes->Style.LinkLineSegmentsPerLength); #if IMGUI_VERSION_NUM < 18000 GImNodes->CanvasDrawList->AddBezierCurve( #else GImNodes->CanvasDrawList->AddBezierCubic( #endif cubic_bezier.P0, cubic_bezier.P1, cubic_bezier.P2, cubic_bezier.P3, GImNodes->Style.Colors[ImNodesCol_Link], GImNodes->Style.LinkThickness, cubic_bezier.NumSegments); const bool link_creation_on_snap = GImNodes->HoveredPinIdx.HasValue() && (editor.Pins.Pool[GImNodes->HoveredPinIdx.Value()].Flags & ImNodesAttributeFlags_EnableLinkCreationOnSnap); if (!should_snap) { editor.ClickInteraction.LinkCreation.EndPinIdx.Reset(); } const bool create_link = should_snap && (GImNodes->LeftMouseReleased || link_creation_on_snap); if (create_link && !maybe_duplicate_link_idx.HasValue()) { // Avoid send OnLinkCreated() events every frame if the snap link is not saved // (only applies for EnableLinkCreationOnSnap) if (!GImNodes->LeftMouseReleased && editor.ClickInteraction.LinkCreation.EndPinIdx == GImNodes->HoveredPinIdx) { break; } GImNodes->ImNodesUIState |= ImNodesUIState_LinkCreated; editor.ClickInteraction.LinkCreation.EndPinIdx = GImNodes->HoveredPinIdx.Value(); } if (GImNodes->LeftMouseReleased) { editor.ClickInteraction.Type = ImNodesClickInteractionType_None; if (!create_link) { GImNodes->ImNodesUIState |= ImNodesUIState_LinkDropped; } } } break; case ImNodesClickInteractionType_Panning: { const bool dragging = GImNodes->AltMouseDragging; if (dragging) { editor.Panning += ImGui::GetIO().MouseDelta; } else { editor.ClickInteraction.Type = ImNodesClickInteractionType_None; } } break; case ImNodesClickInteractionType_ImGuiItem: { if (GImNodes->LeftMouseReleased) { editor.ClickInteraction.Type = ImNodesClickInteractionType_None; } } case ImNodesClickInteractionType_None: break; default: IM_ASSERT(!"Unreachable code!"); break; } } void ResolveOccludedPins(const ImNodesEditorContext& editor, ImVector& occluded_pin_indices) { const ImVector& depth_stack = editor.NodeDepthOrder; occluded_pin_indices.resize(0); if (depth_stack.Size < 2) { return; } // For each node in the depth stack for (int depth_idx = 0; depth_idx < (depth_stack.Size - 1); ++depth_idx) { const ImNodeData& node_below = editor.Nodes.Pool[depth_stack[depth_idx]]; // Iterate over the rest of the depth stack to find nodes overlapping the pins for (int next_depth_idx = depth_idx + 1; next_depth_idx < depth_stack.Size; ++next_depth_idx) { const ImRect& rect_above = editor.Nodes.Pool[depth_stack[next_depth_idx]].Rect; // Iterate over each pin for (int idx = 0; idx < node_below.PinIndices.Size; ++idx) { const int pin_idx = node_below.PinIndices[idx]; const ImVec2& pin_pos = editor.Pins.Pool[pin_idx].Pos; if (rect_above.Contains(pin_pos)) { occluded_pin_indices.push_back(pin_idx); } } } } } ImOptionalIndex ResolveHoveredPin( const ImObjectPool& pins, const ImVector& occluded_pin_indices) { float smallest_distance = FLT_MAX; ImOptionalIndex pin_idx_with_smallest_distance; const float hover_radius_sqr = GImNodes->Style.PinHoverRadius * GImNodes->Style.PinHoverRadius; for (int idx = 0; idx < pins.Pool.Size; ++idx) { if (!pins.InUse[idx]) { continue; } if (occluded_pin_indices.contains(idx)) { continue; } const ImVec2& pin_pos = pins.Pool[idx].Pos; const float distance_sqr = ImLengthSqr(pin_pos - GImNodes->MousePos); // TODO: GImNodes->Style.PinHoverRadius needs to be copied into pin data and the pin-local // value used here. This is no longer called in BeginAttribute/EndAttribute scope and the // detected pin might have a different hover radius than what the user had when calling // BeginAttribute/EndAttribute. if (distance_sqr < hover_radius_sqr && distance_sqr < smallest_distance) { smallest_distance = distance_sqr; pin_idx_with_smallest_distance = idx; } } return pin_idx_with_smallest_distance; } ImOptionalIndex ResolveHoveredNode(const ImVector& depth_stack) { if (GImNodes->NodeIndicesOverlappingWithMouse.size() == 0) { return ImOptionalIndex(); } if (GImNodes->NodeIndicesOverlappingWithMouse.size() == 1) { return ImOptionalIndex(GImNodes->NodeIndicesOverlappingWithMouse[0]); } int largest_depth_idx = -1; int node_idx_on_top = -1; for (int i = 0; i < GImNodes->NodeIndicesOverlappingWithMouse.size(); ++i) { const int node_idx = GImNodes->NodeIndicesOverlappingWithMouse[i]; for (int depth_idx = 0; depth_idx < depth_stack.size(); ++depth_idx) { if (depth_stack[depth_idx] == node_idx && (depth_idx > largest_depth_idx)) { largest_depth_idx = depth_idx; node_idx_on_top = node_idx; } } } IM_ASSERT(node_idx_on_top != -1); return ImOptionalIndex(node_idx_on_top); } ImOptionalIndex ResolveHoveredLink( const ImObjectPool& links, const ImObjectPool& pins) { float smallest_distance = FLT_MAX; ImOptionalIndex link_idx_with_smallest_distance; // There are two ways a link can be detected as "hovered". // 1. The link is within hover distance to the mouse. The closest such link is selected as being // hovered over. // 2. If the link is connected to the currently hovered pin. // // The latter is a requirement for link detaching with drag click to work, as both a link and // pin are required to be hovered over for the feature to work. for (int idx = 0; idx < links.Pool.Size; ++idx) { if (!links.InUse[idx]) { continue; } const ImLinkData& link = links.Pool[idx]; const ImPinData& start_pin = pins.Pool[link.StartPinIdx]; const ImPinData& end_pin = pins.Pool[link.EndPinIdx]; // If there is a hovered pin links can only be considered hovered if they use that pin if (GImNodes->HoveredPinIdx.HasValue()) { if (GImNodes->HoveredPinIdx == link.StartPinIdx || GImNodes->HoveredPinIdx == link.EndPinIdx) { return idx; } continue; } // TODO: the calculated CubicBeziers could be cached since we generate them again when // rendering the links const CubicBezier cubic_bezier = GetCubicBezier( start_pin.Pos, end_pin.Pos, start_pin.Type, GImNodes->Style.LinkLineSegmentsPerLength); // The distance test { const ImRect link_rect = GetContainingRectForCubicBezier(cubic_bezier); // First, do a simple bounding box test against the box containing the link // to see whether calculating the distance to the link is worth doing. if (link_rect.Contains(GImNodes->MousePos)) { const float distance = GetDistanceToCubicBezier( GImNodes->MousePos, cubic_bezier, cubic_bezier.NumSegments); // TODO: GImNodes->Style.LinkHoverDistance could be also copied into ImLinkData, // since we're not calling this function in the same scope as ImNodes::Link(). The // rendered/detected link might have a different hover distance than what the user // had specified when calling Link() if (distance < GImNodes->Style.LinkHoverDistance && distance < smallest_distance) { smallest_distance = distance; link_idx_with_smallest_distance = idx; } } } } return link_idx_with_smallest_distance; } // [SECTION] render helpers inline ImRect GetItemRect() { return ImRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax()); } inline ImVec2 GetNodeTitleBarOrigin(const ImNodeData& node) { return node.Origin + node.LayoutStyle.Padding; } inline ImVec2 GetNodeContentOrigin(const ImNodeData& node) { const ImVec2 title_bar_height = ImVec2(0.f, node.TitleBarContentRect.GetHeight() + 2.0f * node.LayoutStyle.Padding.y); return node.Origin + title_bar_height + node.LayoutStyle.Padding; } inline ImRect GetNodeTitleRect(const ImNodeData& node) { ImRect expanded_title_rect = node.TitleBarContentRect; expanded_title_rect.Expand(node.LayoutStyle.Padding); return ImRect( expanded_title_rect.Min, expanded_title_rect.Min + ImVec2(node.Rect.GetWidth(), 0.f) + ImVec2(0.f, expanded_title_rect.GetHeight())); } void DrawGrid(ImNodesEditorContext& editor, const ImVec2& canvas_size) { const ImVec2 offset = editor.Panning; ImU32 line_color = GImNodes->Style.Colors[ImNodesCol_GridLine]; ImU32 line_color_prim = GImNodes->Style.Colors[ImNodesCol_GridLinePrimary]; bool draw_primary = GImNodes->Style.Flags & ImNodesStyleFlags_GridLinesPrimary; for (float x = fmodf(offset.x, GImNodes->Style.GridSpacing); x < canvas_size.x; x += GImNodes->Style.GridSpacing) { GImNodes->CanvasDrawList->AddLine( EditorSpaceToScreenSpace(ImVec2(x, 0.0f)), EditorSpaceToScreenSpace(ImVec2(x, canvas_size.y)), offset.x - x == 0.f && draw_primary ? line_color_prim : line_color); } for (float y = fmodf(offset.y, GImNodes->Style.GridSpacing); y < canvas_size.y; y += GImNodes->Style.GridSpacing) { GImNodes->CanvasDrawList->AddLine( EditorSpaceToScreenSpace(ImVec2(0.0f, y)), EditorSpaceToScreenSpace(ImVec2(canvas_size.x, y)), offset.y - y == 0.f && draw_primary ? line_color_prim : line_color); } } struct QuadOffsets { ImVec2 TopLeft, BottomLeft, BottomRight, TopRight; }; QuadOffsets CalculateQuadOffsets(const float side_length) { const float half_side = 0.5f * side_length; QuadOffsets offset; offset.TopLeft = ImVec2(-half_side, half_side); offset.BottomLeft = ImVec2(-half_side, -half_side); offset.BottomRight = ImVec2(half_side, -half_side); offset.TopRight = ImVec2(half_side, half_side); return offset; } struct TriangleOffsets { ImVec2 TopLeft, BottomLeft, Right; }; TriangleOffsets CalculateTriangleOffsets(const float side_length) { // Calculates the Vec2 offsets from an equilateral triangle's midpoint to // its vertices. Here is how the left_offset and right_offset are // calculated. // // For an equilateral triangle of side length s, the // triangle's height, h, is h = s * sqrt(3) / 2. // // The length from the base to the midpoint is (1 / 3) * h. The length from // the midpoint to the triangle vertex is (2 / 3) * h. const float sqrt_3 = sqrtf(3.0f); const float left_offset = -0.1666666666667f * sqrt_3 * side_length; const float right_offset = 0.333333333333f * sqrt_3 * side_length; const float vertical_offset = 0.5f * side_length; TriangleOffsets offset; offset.TopLeft = ImVec2(left_offset, vertical_offset); offset.BottomLeft = ImVec2(left_offset, -vertical_offset); offset.Right = ImVec2(right_offset, 0.f); return offset; } void DrawPinShape(const ImVec2& pin_pos, const ImPinData& pin, const ImU32 pin_color) { static const int CIRCLE_NUM_SEGMENTS = 8; switch (pin.Shape) { case ImNodesPinShape_Circle: { GImNodes->CanvasDrawList->AddCircle( pin_pos, GImNodes->Style.PinCircleRadius, pin_color, CIRCLE_NUM_SEGMENTS, GImNodes->Style.PinLineThickness); } break; case ImNodesPinShape_CircleFilled: { GImNodes->CanvasDrawList->AddCircleFilled( pin_pos, GImNodes->Style.PinCircleRadius, pin_color, CIRCLE_NUM_SEGMENTS); } break; case ImNodesPinShape_Quad: { const QuadOffsets offset = CalculateQuadOffsets(GImNodes->Style.PinQuadSideLength); GImNodes->CanvasDrawList->AddQuad( pin_pos + offset.TopLeft, pin_pos + offset.BottomLeft, pin_pos + offset.BottomRight, pin_pos + offset.TopRight, pin_color, GImNodes->Style.PinLineThickness); } break; case ImNodesPinShape_QuadFilled: { const QuadOffsets offset = CalculateQuadOffsets(GImNodes->Style.PinQuadSideLength); GImNodes->CanvasDrawList->AddQuadFilled( pin_pos + offset.TopLeft, pin_pos + offset.BottomLeft, pin_pos + offset.BottomRight, pin_pos + offset.TopRight, pin_color); } break; case ImNodesPinShape_Triangle: { const TriangleOffsets offset = CalculateTriangleOffsets(GImNodes->Style.PinTriangleSideLength); GImNodes->CanvasDrawList->AddTriangle( pin_pos + offset.TopLeft, pin_pos + offset.BottomLeft, pin_pos + offset.Right, pin_color, // NOTE: for some weird reason, the line drawn by AddTriangle is // much thinner than the lines drawn by AddCircle or AddQuad. // Multiplying the line thickness by two seemed to solve the // problem at a few different thickness values. 2.f * GImNodes->Style.PinLineThickness); } break; case ImNodesPinShape_TriangleFilled: { const TriangleOffsets offset = CalculateTriangleOffsets(GImNodes->Style.PinTriangleSideLength); GImNodes->CanvasDrawList->AddTriangleFilled( pin_pos + offset.TopLeft, pin_pos + offset.BottomLeft, pin_pos + offset.Right, pin_color); } break; default: IM_ASSERT(!"Invalid PinShape value!"); break; } } void DrawPin(ImNodesEditorContext& editor, const int pin_idx) { ImPinData& pin = editor.Pins.Pool[pin_idx]; const ImRect& parent_node_rect = editor.Nodes.Pool[pin.ParentNodeIdx].Rect; pin.Pos = GetScreenSpacePinCoordinates(parent_node_rect, pin.AttributeRect, pin.Type); ImU32 pin_color = pin.ColorStyle.Background; if (GImNodes->HoveredPinIdx == pin_idx) { pin_color = pin.ColorStyle.Hovered; } DrawPinShape(pin.Pos, pin, pin_color); } void DrawNode(ImNodesEditorContext& editor, const int node_idx) { const ImNodeData& node = editor.Nodes.Pool[node_idx]; ImGui::SetCursorPos(node.Origin + editor.Panning); const bool node_hovered = GImNodes->HoveredNodeIdx == node_idx && editor.ClickInteraction.Type != ImNodesClickInteractionType_BoxSelection; ImU32 node_background = node.ColorStyle.Background; ImU32 titlebar_background = node.ColorStyle.Titlebar; if (editor.SelectedNodeIndices.contains(node_idx)) { node_background = node.ColorStyle.BackgroundSelected; titlebar_background = node.ColorStyle.TitlebarSelected; } else if (node_hovered) { node_background = node.ColorStyle.BackgroundHovered; titlebar_background = node.ColorStyle.TitlebarHovered; } { // node base GImNodes->CanvasDrawList->AddRectFilled( node.Rect.Min, node.Rect.Max, node_background, node.LayoutStyle.CornerRounding); // title bar: if (node.TitleBarContentRect.GetHeight() > 0.f) { ImRect title_bar_rect = GetNodeTitleRect(node); #if IMGUI_VERSION_NUM < 18200 GImNodes->CanvasDrawList->AddRectFilled( title_bar_rect.Min, title_bar_rect.Max, titlebar_background, node.LayoutStyle.CornerRounding, ImDrawCornerFlags_Top); #else GImNodes->CanvasDrawList->AddRectFilled( title_bar_rect.Min, title_bar_rect.Max, titlebar_background, node.LayoutStyle.CornerRounding, ImDrawFlags_RoundCornersTop); #endif } if ((GImNodes->Style.Flags & ImNodesStyleFlags_NodeOutline) != 0) { #if IMGUI_VERSION_NUM < 18200 GImNodes->CanvasDrawList->AddRect( node.Rect.Min, node.Rect.Max, node.ColorStyle.Outline, node.LayoutStyle.CornerRounding, ImDrawCornerFlags_All, node.LayoutStyle.BorderThickness); #else GImNodes->CanvasDrawList->AddRect( node.Rect.Min, node.Rect.Max, node.ColorStyle.Outline, node.LayoutStyle.CornerRounding, ImDrawFlags_RoundCornersAll, node.LayoutStyle.BorderThickness); #endif } } for (int i = 0; i < node.PinIndices.size(); ++i) { DrawPin(editor, node.PinIndices[i]); } if (node_hovered) { GImNodes->HoveredNodeIdx = node_idx; } } void DrawLink(ImNodesEditorContext& editor, const int link_idx) { const ImLinkData& link = editor.Links.Pool[link_idx]; const ImPinData& start_pin = editor.Pins.Pool[link.StartPinIdx]; const ImPinData& end_pin = editor.Pins.Pool[link.EndPinIdx]; const CubicBezier cubic_bezier = GetCubicBezier( start_pin.Pos, end_pin.Pos, start_pin.Type, GImNodes->Style.LinkLineSegmentsPerLength); const bool link_hovered = GImNodes->HoveredLinkIdx == link_idx && editor.ClickInteraction.Type != ImNodesClickInteractionType_BoxSelection; if (link_hovered) { GImNodes->HoveredLinkIdx = link_idx; } // It's possible for a link to be deleted in begin_link_interaction. A user // may detach a link, resulting in the link wire snapping to the mouse // position. // // In other words, skip rendering the link if it was deleted. if (GImNodes->DeletedLinkIdx == link_idx) { return; } ImU32 link_color = link.ColorStyle.Base; if (editor.SelectedLinkIndices.contains(link_idx)) { link_color = link.ColorStyle.Selected; } else if (link_hovered) { link_color = link.ColorStyle.Hovered; } #if IMGUI_VERSION_NUM < 18000 GImNodes->CanvasDrawList->AddBezierCurve( #else GImNodes->CanvasDrawList->AddBezierCubic( #endif cubic_bezier.P0, cubic_bezier.P1, cubic_bezier.P2, cubic_bezier.P3, link_color, GImNodes->Style.LinkThickness, cubic_bezier.NumSegments); } void BeginPinAttribute( const int id, const ImNodesAttributeType type, const ImNodesPinShape shape, const int node_idx) { // Make sure to call BeginNode() before calling // BeginAttribute() IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_Node); GImNodes->CurrentScope = ImNodesScope_Attribute; ImGui::BeginGroup(); ImGui::PushID(id); ImNodesEditorContext& editor = EditorContextGet(); GImNodes->CurrentAttributeId = id; const int pin_idx = ObjectPoolFindOrCreateIndex(editor.Pins, id); GImNodes->CurrentPinIdx = pin_idx; ImPinData& pin = editor.Pins.Pool[pin_idx]; pin.Id = id; pin.ParentNodeIdx = node_idx; pin.Type = type; pin.Shape = shape; pin.Flags = GImNodes->CurrentAttributeFlags; pin.ColorStyle.Background = GImNodes->Style.Colors[ImNodesCol_Pin]; pin.ColorStyle.Hovered = GImNodes->Style.Colors[ImNodesCol_PinHovered]; } void EndPinAttribute() { IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_Attribute); GImNodes->CurrentScope = ImNodesScope_Node; ImGui::PopID(); ImGui::EndGroup(); if (ImGui::IsItemActive()) { GImNodes->ActiveAttribute = true; GImNodes->ActiveAttributeId = GImNodes->CurrentAttributeId; } ImNodesEditorContext& editor = EditorContextGet(); ImPinData& pin = editor.Pins.Pool[GImNodes->CurrentPinIdx]; ImNodeData& node = editor.Nodes.Pool[GImNodes->CurrentNodeIdx]; pin.AttributeRect = GetItemRect(); node.PinIndices.push_back(GImNodes->CurrentPinIdx); } void Initialize(ImNodesContext* context) { context->CanvasOriginScreenSpace = ImVec2(0.0f, 0.0f); context->CanvasRectScreenSpace = ImRect(ImVec2(0.f, 0.f), ImVec2(0.f, 0.f)); context->CurrentScope = ImNodesScope_None; context->CurrentPinIdx = INT_MAX; context->CurrentNodeIdx = INT_MAX; context->DefaultEditorCtx = EditorContextCreate(); context->EditorCtx = context->DefaultEditorCtx; context->CurrentAttributeFlags = ImNodesAttributeFlags_None; context->AttributeFlagStack.push_back(GImNodes->CurrentAttributeFlags); StyleColorsDark(&context->Style); } void Shutdown(ImNodesContext* ctx) { EditorContextFree(ctx->DefaultEditorCtx); } // [SECTION] minimap static inline bool IsMiniMapActive() { ImNodesEditorContext& editor = EditorContextGet(); return editor.MiniMapEnabled && editor.MiniMapSizeFraction > 0.0f; } static inline bool IsMiniMapHovered() { ImNodesEditorContext& editor = EditorContextGet(); return IsMiniMapActive() && ImGui::IsMouseHoveringRect( editor.MiniMapRectScreenSpace.Min, editor.MiniMapRectScreenSpace.Max); } static inline void CalcMiniMapLayout() { ImNodesEditorContext& editor = EditorContextGet(); const ImVec2 offset = GImNodes->Style.MiniMapOffset; const ImVec2 border = GImNodes->Style.MiniMapPadding; const ImRect editor_rect = GImNodes->CanvasRectScreenSpace; // Compute the size of the mini-map area ImVec2 mini_map_size; float mini_map_scaling; { const ImVec2 max_size = ImFloor(editor_rect.GetSize() * editor.MiniMapSizeFraction - border * 2.0f); const float max_size_aspect_ratio = max_size.x / max_size.y; const ImVec2 grid_content_size = editor.GridContentBounds.IsInverted() ? max_size : ImFloor(editor.GridContentBounds.GetSize()); const float grid_content_aspect_ratio = grid_content_size.x / grid_content_size.y; mini_map_size = ImFloor( grid_content_aspect_ratio > max_size_aspect_ratio ? ImVec2(max_size.x, max_size.x / grid_content_aspect_ratio) : ImVec2(max_size.y * grid_content_aspect_ratio, max_size.y)); mini_map_scaling = mini_map_size.x / grid_content_size.x; } // Compute location of the mini-map ImVec2 mini_map_pos; { ImVec2 align; switch (editor.MiniMapLocation) { case ImNodesMiniMapLocation_BottomRight: align.x = 1.0f; align.y = 1.0f; break; case ImNodesMiniMapLocation_BottomLeft: align.x = 0.0f; align.y = 1.0f; break; case ImNodesMiniMapLocation_TopRight: align.x = 1.0f; align.y = 0.0f; break; case ImNodesMiniMapLocation_TopLeft: // [[fallthrough]] default: align.x = 0.0f; align.y = 0.0f; break; } const ImVec2 top_left_pos = editor_rect.Min + offset + border; const ImVec2 bottom_right_pos = editor_rect.Max - offset - border - mini_map_size; mini_map_pos = ImFloor(ImLerp(top_left_pos, bottom_right_pos, align)); } editor.MiniMapRectScreenSpace = ImRect(mini_map_pos - border, mini_map_pos + mini_map_size + border); editor.MiniMapContentScreenSpace = ImRect(mini_map_pos, mini_map_pos + mini_map_size); editor.MiniMapScaling = mini_map_scaling; } static void MiniMapDrawNode(ImNodesEditorContext& editor, const int node_idx) { const ImNodeData& node = editor.Nodes.Pool[node_idx]; const ImRect node_rect = ScreenSpaceToMiniMapSpace(editor, node.Rect); // Round to near whole pixel value for corner-rounding to prevent visual glitches const float mini_map_node_rounding = floorf(node.LayoutStyle.CornerRounding * editor.MiniMapScaling); ImU32 mini_map_node_background; if (editor.ClickInteraction.Type == ImNodesClickInteractionType_None && ImGui::IsMouseHoveringRect(node_rect.Min, node_rect.Max)) { mini_map_node_background = GImNodes->Style.Colors[ImNodesCol_MiniMapNodeBackgroundHovered]; // Run user callback when hovering a mini-map node if (editor.MiniMapNodeHoveringCallback) { editor.MiniMapNodeHoveringCallback(node.Id, editor.MiniMapNodeHoveringCallbackUserData); } } else if (editor.SelectedNodeIndices.contains(node_idx)) { mini_map_node_background = GImNodes->Style.Colors[ImNodesCol_MiniMapNodeBackgroundSelected]; } else { mini_map_node_background = GImNodes->Style.Colors[ImNodesCol_MiniMapNodeBackground]; } const ImU32 mini_map_node_outline = GImNodes->Style.Colors[ImNodesCol_MiniMapNodeOutline]; GImNodes->CanvasDrawList->AddRectFilled( node_rect.Min, node_rect.Max, mini_map_node_background, mini_map_node_rounding); GImNodes->CanvasDrawList->AddRect( node_rect.Min, node_rect.Max, mini_map_node_outline, mini_map_node_rounding); } static void MiniMapDrawLink(ImNodesEditorContext& editor, const int link_idx) { const ImLinkData& link = editor.Links.Pool[link_idx]; const ImPinData& start_pin = editor.Pins.Pool[link.StartPinIdx]; const ImPinData& end_pin = editor.Pins.Pool[link.EndPinIdx]; const CubicBezier cubic_bezier = GetCubicBezier( ScreenSpaceToMiniMapSpace(editor, start_pin.Pos), ScreenSpaceToMiniMapSpace(editor, end_pin.Pos), start_pin.Type, GImNodes->Style.LinkLineSegmentsPerLength / editor.MiniMapScaling); // It's possible for a link to be deleted in begin_link_interaction. A user // may detach a link, resulting in the link wire snapping to the mouse // position. // // In other words, skip rendering the link if it was deleted. if (GImNodes->DeletedLinkIdx == link_idx) { return; } const ImU32 link_color = GImNodes->Style.Colors [editor.SelectedLinkIndices.contains(link_idx) ? ImNodesCol_MiniMapLinkSelected : ImNodesCol_MiniMapLink]; #if IMGUI_VERSION_NUM < 18000 GImNodes->CanvasDrawList->AddBezierCurve( #else GImNodes->CanvasDrawList->AddBezierCubic( #endif cubic_bezier.P0, cubic_bezier.P1, cubic_bezier.P2, cubic_bezier.P3, link_color, GImNodes->Style.LinkThickness * editor.MiniMapScaling, cubic_bezier.NumSegments); } static void MiniMapUpdate() { ImNodesEditorContext& editor = EditorContextGet(); ImU32 mini_map_background; if (IsMiniMapHovered()) { mini_map_background = GImNodes->Style.Colors[ImNodesCol_MiniMapBackgroundHovered]; } else { mini_map_background = GImNodes->Style.Colors[ImNodesCol_MiniMapBackground]; } // Create a child window bellow mini-map, so it blocks all mouse interaction on canvas. int flags = ImGuiWindowFlags_NoBackground; ImGui::SetCursorScreenPos(editor.MiniMapRectScreenSpace.Min); ImGui::BeginChild("minimap", editor.MiniMapRectScreenSpace.GetSize(), false, flags); const ImRect& mini_map_rect = editor.MiniMapRectScreenSpace; // Draw minimap background and border GImNodes->CanvasDrawList->AddRectFilled( mini_map_rect.Min, mini_map_rect.Max, mini_map_background); GImNodes->CanvasDrawList->AddRect( mini_map_rect.Min, mini_map_rect.Max, GImNodes->Style.Colors[ImNodesCol_MiniMapOutline]); // Clip draw list items to mini-map rect (after drawing background/outline) GImNodes->CanvasDrawList->PushClipRect( mini_map_rect.Min, mini_map_rect.Max, true /* intersect with editor clip-rect */); // Draw links first so they appear under nodes, and we can use the same draw channel for (int link_idx = 0; link_idx < editor.Links.Pool.size(); ++link_idx) { if (editor.Links.InUse[link_idx]) { MiniMapDrawLink(editor, link_idx); } } for (int node_idx = 0; node_idx < editor.Nodes.Pool.size(); ++node_idx) { if (editor.Nodes.InUse[node_idx]) { MiniMapDrawNode(editor, node_idx); } } // Draw editor canvas rect inside mini-map { const ImU32 canvas_color = GImNodes->Style.Colors[ImNodesCol_MiniMapCanvas]; const ImU32 outline_color = GImNodes->Style.Colors[ImNodesCol_MiniMapCanvasOutline]; const ImRect rect = ScreenSpaceToMiniMapSpace(editor, GImNodes->CanvasRectScreenSpace); GImNodes->CanvasDrawList->AddRectFilled(rect.Min, rect.Max, canvas_color); GImNodes->CanvasDrawList->AddRect(rect.Min, rect.Max, outline_color); } // Have to pop mini-map clip rect GImNodes->CanvasDrawList->PopClipRect(); bool mini_map_is_hovered = ImGui::IsWindowHovered(); ImGui::EndChild(); bool center_on_click = mini_map_is_hovered && ImGui::IsMouseDown(ImGuiMouseButton_Left) && editor.ClickInteraction.Type == ImNodesClickInteractionType_None && !GImNodes->NodeIdxSubmissionOrder.empty(); if (center_on_click) { ImVec2 target = MiniMapSpaceToGridSpace(editor, ImGui::GetMousePos()); ImVec2 center = GImNodes->CanvasRectScreenSpace.GetSize() * 0.5f; editor.Panning = ImFloor(center - target); } // Reset callback info after use editor.MiniMapNodeHoveringCallback = NULL; editor.MiniMapNodeHoveringCallbackUserData = NULL; } // [SECTION] selection helpers template void SelectObject(const ImObjectPool& objects, ImVector& selected_indices, const int id) { const int idx = ObjectPoolFind(objects, id); IM_ASSERT(idx >= 0); IM_ASSERT(selected_indices.find(idx) == selected_indices.end()); selected_indices.push_back(idx); } template void ClearObjectSelection( const ImObjectPool& objects, ImVector& selected_indices, const int id) { const int idx = ObjectPoolFind(objects, id); IM_ASSERT(idx >= 0); IM_ASSERT(selected_indices.find(idx) != selected_indices.end()); selected_indices.find_erase_unsorted(idx); } template bool IsObjectSelected(const ImObjectPool& objects, ImVector& selected_indices, const int id) { const int idx = ObjectPoolFind(objects, id); return selected_indices.find(idx) != selected_indices.end(); } } // namespace } // namespace IMNODES_NAMESPACE // [SECTION] API implementation ImNodesIO::EmulateThreeButtonMouse::EmulateThreeButtonMouse() : Modifier(NULL) {} ImNodesIO::LinkDetachWithModifierClick::LinkDetachWithModifierClick() : Modifier(NULL) {} ImNodesIO::MultipleSelectModifier::MultipleSelectModifier() : Modifier(NULL) {} ImNodesIO::ImNodesIO() : EmulateThreeButtonMouse(), LinkDetachWithModifierClick(), AltMouseButton(ImGuiMouseButton_Middle), AutoPanningSpeed(1000.0f) { } ImNodesStyle::ImNodesStyle() : GridSpacing(24.f), NodeCornerRounding(4.f), NodePadding(8.f, 8.f), NodeBorderThickness(1.f), LinkThickness(3.f), LinkLineSegmentsPerLength(0.1f), LinkHoverDistance(10.f), PinCircleRadius(4.f), PinQuadSideLength(7.f), PinTriangleSideLength(9.5), PinLineThickness(1.f), PinHoverRadius(10.f), PinOffset(0.f), MiniMapPadding(8.0f, 8.0f), MiniMapOffset(4.0f, 4.0f), Flags(ImNodesStyleFlags_NodeOutline | ImNodesStyleFlags_GridLines), Colors() { } namespace IMNODES_NAMESPACE { ImNodesContext* CreateContext() { ImNodesContext* ctx = IM_NEW(ImNodesContext)(); if (GImNodes == NULL) SetCurrentContext(ctx); Initialize(ctx); return ctx; } void DestroyContext(ImNodesContext* ctx) { if (ctx == NULL) ctx = GImNodes; Shutdown(ctx); if (GImNodes == ctx) SetCurrentContext(NULL); IM_DELETE(ctx); } ImNodesContext* GetCurrentContext() { return GImNodes; } void SetCurrentContext(ImNodesContext* ctx) { GImNodes = ctx; } ImNodesEditorContext* EditorContextCreate() { void* mem = ImGui::MemAlloc(sizeof(ImNodesEditorContext)); new (mem) ImNodesEditorContext(); return (ImNodesEditorContext*)mem; } void EditorContextFree(ImNodesEditorContext* ctx) { ctx->~ImNodesEditorContext(); ImGui::MemFree(ctx); } void EditorContextSet(ImNodesEditorContext* ctx) { GImNodes->EditorCtx = ctx; } ImVec2 EditorContextGetPanning() { const ImNodesEditorContext& editor = EditorContextGet(); return editor.Panning; } void EditorContextResetPanning(const ImVec2& pos) { ImNodesEditorContext& editor = EditorContextGet(); editor.Panning = pos; } void EditorContextMoveToNode(const int node_id) { ImNodesEditorContext& editor = EditorContextGet(); ImNodeData& node = ObjectPoolFindOrCreateObject(editor.Nodes, node_id); editor.Panning.x = -node.Origin.x; editor.Panning.y = -node.Origin.y; } void SetImGuiContext(ImGuiContext* ctx) { ImGui::SetCurrentContext(ctx); } ImNodesIO& GetIO() { return GImNodes->Io; } ImNodesStyle& GetStyle() { return GImNodes->Style; } void StyleColorsDark(ImNodesStyle* dest) { if (dest == nullptr) { dest = &GImNodes->Style; } dest->Colors[ImNodesCol_NodeBackground] = IM_COL32(50, 50, 50, 255); dest->Colors[ImNodesCol_NodeBackgroundHovered] = IM_COL32(75, 75, 75, 255); dest->Colors[ImNodesCol_NodeBackgroundSelected] = IM_COL32(75, 75, 75, 255); dest->Colors[ImNodesCol_NodeOutline] = IM_COL32(100, 100, 100, 255); // title bar colors match ImGui's titlebg colors dest->Colors[ImNodesCol_TitleBar] = IM_COL32(41, 74, 122, 255); dest->Colors[ImNodesCol_TitleBarHovered] = IM_COL32(66, 150, 250, 255); dest->Colors[ImNodesCol_TitleBarSelected] = IM_COL32(66, 150, 250, 255); // link colors match ImGui's slider grab colors dest->Colors[ImNodesCol_Link] = IM_COL32(61, 133, 224, 200); dest->Colors[ImNodesCol_LinkHovered] = IM_COL32(66, 150, 250, 255); dest->Colors[ImNodesCol_LinkSelected] = IM_COL32(66, 150, 250, 255); // pin colors match ImGui's button colors dest->Colors[ImNodesCol_Pin] = IM_COL32(53, 150, 250, 180); dest->Colors[ImNodesCol_PinHovered] = IM_COL32(53, 150, 250, 255); dest->Colors[ImNodesCol_BoxSelector] = IM_COL32(61, 133, 224, 30); dest->Colors[ImNodesCol_BoxSelectorOutline] = IM_COL32(61, 133, 224, 150); dest->Colors[ImNodesCol_GridBackground] = IM_COL32(40, 40, 50, 200); dest->Colors[ImNodesCol_GridLine] = IM_COL32(200, 200, 200, 40); dest->Colors[ImNodesCol_GridLinePrimary] = IM_COL32(240, 240, 240, 60); // minimap colors dest->Colors[ImNodesCol_MiniMapBackground] = IM_COL32(25, 25, 25, 150); dest->Colors[ImNodesCol_MiniMapBackgroundHovered] = IM_COL32(25, 25, 25, 200); dest->Colors[ImNodesCol_MiniMapOutline] = IM_COL32(150, 150, 150, 100); dest->Colors[ImNodesCol_MiniMapOutlineHovered] = IM_COL32(150, 150, 150, 200); dest->Colors[ImNodesCol_MiniMapNodeBackground] = IM_COL32(200, 200, 200, 100); dest->Colors[ImNodesCol_MiniMapNodeBackgroundHovered] = IM_COL32(200, 200, 200, 255); dest->Colors[ImNodesCol_MiniMapNodeBackgroundSelected] = dest->Colors[ImNodesCol_MiniMapNodeBackgroundHovered]; dest->Colors[ImNodesCol_MiniMapNodeOutline] = IM_COL32(200, 200, 200, 100); dest->Colors[ImNodesCol_MiniMapLink] = dest->Colors[ImNodesCol_Link]; dest->Colors[ImNodesCol_MiniMapLinkSelected] = dest->Colors[ImNodesCol_LinkSelected]; dest->Colors[ImNodesCol_MiniMapCanvas] = IM_COL32(200, 200, 200, 25); dest->Colors[ImNodesCol_MiniMapCanvasOutline] = IM_COL32(200, 200, 200, 200); } void StyleColorsClassic(ImNodesStyle* dest) { if (dest == nullptr) { dest = &GImNodes->Style; } dest->Colors[ImNodesCol_NodeBackground] = IM_COL32(50, 50, 50, 255); dest->Colors[ImNodesCol_NodeBackgroundHovered] = IM_COL32(75, 75, 75, 255); dest->Colors[ImNodesCol_NodeBackgroundSelected] = IM_COL32(75, 75, 75, 255); dest->Colors[ImNodesCol_NodeOutline] = IM_COL32(100, 100, 100, 255); dest->Colors[ImNodesCol_TitleBar] = IM_COL32(69, 69, 138, 255); dest->Colors[ImNodesCol_TitleBarHovered] = IM_COL32(82, 82, 161, 255); dest->Colors[ImNodesCol_TitleBarSelected] = IM_COL32(82, 82, 161, 255); dest->Colors[ImNodesCol_Link] = IM_COL32(255, 255, 255, 100); dest->Colors[ImNodesCol_LinkHovered] = IM_COL32(105, 99, 204, 153); dest->Colors[ImNodesCol_LinkSelected] = IM_COL32(105, 99, 204, 153); dest->Colors[ImNodesCol_Pin] = IM_COL32(89, 102, 156, 170); dest->Colors[ImNodesCol_PinHovered] = IM_COL32(102, 122, 179, 200); dest->Colors[ImNodesCol_BoxSelector] = IM_COL32(82, 82, 161, 100); dest->Colors[ImNodesCol_BoxSelectorOutline] = IM_COL32(82, 82, 161, 255); dest->Colors[ImNodesCol_GridBackground] = IM_COL32(40, 40, 50, 200); dest->Colors[ImNodesCol_GridLine] = IM_COL32(200, 200, 200, 40); dest->Colors[ImNodesCol_GridLinePrimary] = IM_COL32(240, 240, 240, 60); // minimap colors dest->Colors[ImNodesCol_MiniMapBackground] = IM_COL32(25, 25, 25, 100); dest->Colors[ImNodesCol_MiniMapBackgroundHovered] = IM_COL32(25, 25, 25, 200); dest->Colors[ImNodesCol_MiniMapOutline] = IM_COL32(150, 150, 150, 100); dest->Colors[ImNodesCol_MiniMapOutlineHovered] = IM_COL32(150, 150, 150, 200); dest->Colors[ImNodesCol_MiniMapNodeBackground] = IM_COL32(200, 200, 200, 100); dest->Colors[ImNodesCol_MiniMapNodeBackgroundSelected] = dest->Colors[ImNodesCol_MiniMapNodeBackgroundHovered]; dest->Colors[ImNodesCol_MiniMapNodeBackgroundSelected] = IM_COL32(200, 200, 240, 255); dest->Colors[ImNodesCol_MiniMapNodeOutline] = IM_COL32(200, 200, 200, 100); dest->Colors[ImNodesCol_MiniMapLink] = dest->Colors[ImNodesCol_Link]; dest->Colors[ImNodesCol_MiniMapLinkSelected] = dest->Colors[ImNodesCol_LinkSelected]; dest->Colors[ImNodesCol_MiniMapCanvas] = IM_COL32(200, 200, 200, 25); dest->Colors[ImNodesCol_MiniMapCanvasOutline] = IM_COL32(200, 200, 200, 200); } void StyleColorsLight(ImNodesStyle* dest) { if (dest == nullptr) { dest = &GImNodes->Style; } dest->Colors[ImNodesCol_NodeBackground] = IM_COL32(240, 240, 240, 255); dest->Colors[ImNodesCol_NodeBackgroundHovered] = IM_COL32(240, 240, 240, 255); dest->Colors[ImNodesCol_NodeBackgroundSelected] = IM_COL32(240, 240, 240, 255); dest->Colors[ImNodesCol_NodeOutline] = IM_COL32(100, 100, 100, 255); dest->Colors[ImNodesCol_TitleBar] = IM_COL32(248, 248, 248, 255); dest->Colors[ImNodesCol_TitleBarHovered] = IM_COL32(209, 209, 209, 255); dest->Colors[ImNodesCol_TitleBarSelected] = IM_COL32(209, 209, 209, 255); // original imgui values: 66, 150, 250 dest->Colors[ImNodesCol_Link] = IM_COL32(66, 150, 250, 100); // original imgui values: 117, 138, 204 dest->Colors[ImNodesCol_LinkHovered] = IM_COL32(66, 150, 250, 242); dest->Colors[ImNodesCol_LinkSelected] = IM_COL32(66, 150, 250, 242); // original imgui values: 66, 150, 250 dest->Colors[ImNodesCol_Pin] = IM_COL32(66, 150, 250, 160); dest->Colors[ImNodesCol_PinHovered] = IM_COL32(66, 150, 250, 255); dest->Colors[ImNodesCol_BoxSelector] = IM_COL32(90, 170, 250, 30); dest->Colors[ImNodesCol_BoxSelectorOutline] = IM_COL32(90, 170, 250, 150); dest->Colors[ImNodesCol_GridBackground] = IM_COL32(225, 225, 225, 255); dest->Colors[ImNodesCol_GridLine] = IM_COL32(180, 180, 180, 100); dest->Colors[ImNodesCol_GridLinePrimary] = IM_COL32(120, 120, 120, 100); // minimap colors dest->Colors[ImNodesCol_MiniMapBackground] = IM_COL32(25, 25, 25, 100); dest->Colors[ImNodesCol_MiniMapBackgroundHovered] = IM_COL32(25, 25, 25, 200); dest->Colors[ImNodesCol_MiniMapOutline] = IM_COL32(150, 150, 150, 100); dest->Colors[ImNodesCol_MiniMapOutlineHovered] = IM_COL32(150, 150, 150, 200); dest->Colors[ImNodesCol_MiniMapNodeBackground] = IM_COL32(200, 200, 200, 100); dest->Colors[ImNodesCol_MiniMapNodeBackgroundSelected] = dest->Colors[ImNodesCol_MiniMapNodeBackgroundHovered]; dest->Colors[ImNodesCol_MiniMapNodeBackgroundSelected] = IM_COL32(200, 200, 240, 255); dest->Colors[ImNodesCol_MiniMapNodeOutline] = IM_COL32(200, 200, 200, 100); dest->Colors[ImNodesCol_MiniMapLink] = dest->Colors[ImNodesCol_Link]; dest->Colors[ImNodesCol_MiniMapLinkSelected] = dest->Colors[ImNodesCol_LinkSelected]; dest->Colors[ImNodesCol_MiniMapCanvas] = IM_COL32(200, 200, 200, 25); dest->Colors[ImNodesCol_MiniMapCanvasOutline] = IM_COL32(200, 200, 200, 200); } void BeginNodeEditor() { IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_None); GImNodes->CurrentScope = ImNodesScope_Editor; // Reset state from previous pass ImNodesEditorContext& editor = EditorContextGet(); editor.AutoPanningDelta = ImVec2(0, 0); editor.GridContentBounds = ImRect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX); editor.MiniMapEnabled = false; ObjectPoolReset(editor.Nodes); ObjectPoolReset(editor.Pins); ObjectPoolReset(editor.Links); GImNodes->HoveredNodeIdx.Reset(); GImNodes->HoveredLinkIdx.Reset(); GImNodes->HoveredPinIdx.Reset(); GImNodes->DeletedLinkIdx.Reset(); GImNodes->SnapLinkIdx.Reset(); GImNodes->NodeIndicesOverlappingWithMouse.clear(); GImNodes->ImNodesUIState = ImNodesUIState_None; GImNodes->MousePos = ImGui::GetIO().MousePos; GImNodes->LeftMouseClicked = ImGui::IsMouseClicked(0); GImNodes->LeftMouseReleased = ImGui::IsMouseReleased(0); GImNodes->LeftMouseDragging = ImGui::IsMouseDragging(0, 0.0f); GImNodes->AltMouseClicked = (GImNodes->Io.EmulateThreeButtonMouse.Modifier != NULL && *GImNodes->Io.EmulateThreeButtonMouse.Modifier && GImNodes->LeftMouseClicked) || ImGui::IsMouseClicked(GImNodes->Io.AltMouseButton); GImNodes->AltMouseDragging = (GImNodes->Io.EmulateThreeButtonMouse.Modifier != NULL && GImNodes->LeftMouseDragging && (*GImNodes->Io.EmulateThreeButtonMouse.Modifier)) || ImGui::IsMouseDragging(GImNodes->Io.AltMouseButton, 0.0f); GImNodes->AltMouseScrollDelta = ImGui::GetIO().MouseWheel; GImNodes->MultipleSelectModifier = (GImNodes->Io.MultipleSelectModifier.Modifier != NULL ? *GImNodes->Io.MultipleSelectModifier.Modifier : ImGui::GetIO().KeyCtrl); GImNodes->ActiveAttribute = false; ImGui::BeginGroup(); { ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(1.f, 1.f)); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.f, 0.f)); ImGui::PushStyleColor(ImGuiCol_ChildBg, GImNodes->Style.Colors[ImNodesCol_GridBackground]); ImGui::BeginChild( "scrolling_region", ImVec2(0.f, 0.f), true, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollWithMouse); GImNodes->CanvasOriginScreenSpace = ImGui::GetCursorScreenPos(); // NOTE: we have to fetch the canvas draw list *after* we call // BeginChild(), otherwise the ImGui UI elements are going to be // rendered into the parent window draw list. DrawListSet(ImGui::GetWindowDrawList()); { const ImVec2 canvas_size = ImGui::GetWindowSize(); GImNodes->CanvasRectScreenSpace = ImRect( EditorSpaceToScreenSpace(ImVec2(0.f, 0.f)), EditorSpaceToScreenSpace(canvas_size)); if (GImNodes->Style.Flags & ImNodesStyleFlags_GridLines) { DrawGrid(editor, canvas_size); } } } } void EndNodeEditor() { IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_Editor); GImNodes->CurrentScope = ImNodesScope_None; ImNodesEditorContext& editor = EditorContextGet(); bool no_grid_content = editor.GridContentBounds.IsInverted(); if (no_grid_content) { editor.GridContentBounds = ScreenSpaceToGridSpace(editor, GImNodes->CanvasRectScreenSpace); } // Detect ImGui interaction first, because it blocks interaction with the rest of the UI if (GImNodes->LeftMouseClicked && ImGui::IsAnyItemActive()) { editor.ClickInteraction.Type = ImNodesClickInteractionType_ImGuiItem; } // Detect which UI element is being hovered over. Detection is done in a hierarchical fashion, // because a UI element being hovered excludes any other as being hovered over. // Don't do hovering detection for nodes/links/pins when interacting with the mini-map, since // its an *overlay* with its own interaction behavior and must have precedence during mouse // interaction. if ((editor.ClickInteraction.Type == ImNodesClickInteractionType_None || editor.ClickInteraction.Type == ImNodesClickInteractionType_LinkCreation) && MouseInCanvas() && !IsMiniMapHovered()) { // Pins needs some special care. We need to check the depth stack to see which pins are // being occluded by other nodes. ResolveOccludedPins(editor, GImNodes->OccludedPinIndices); GImNodes->HoveredPinIdx = ResolveHoveredPin(editor.Pins, GImNodes->OccludedPinIndices); if (!GImNodes->HoveredPinIdx.HasValue()) { // Resolve which node is actually on top and being hovered using the depth stack. GImNodes->HoveredNodeIdx = ResolveHoveredNode(editor.NodeDepthOrder); } // We don't check for hovered pins here, because if we want to detach a link by clicking and // dragging, we need to have both a link and pin hovered. if (!GImNodes->HoveredNodeIdx.HasValue()) { GImNodes->HoveredLinkIdx = ResolveHoveredLink(editor.Links, editor.Pins); } } for (int node_idx = 0; node_idx < editor.Nodes.Pool.size(); ++node_idx) { if (editor.Nodes.InUse[node_idx]) { DrawListActivateNodeBackground(node_idx); DrawNode(editor, node_idx); } } // In order to render the links underneath the nodes, we want to first select the bottom draw // channel. GImNodes->CanvasDrawList->ChannelsSetCurrent(0); for (int link_idx = 0; link_idx < editor.Links.Pool.size(); ++link_idx) { if (editor.Links.InUse[link_idx]) { DrawLink(editor, link_idx); } } // Render the click interaction UI elements (partial links, box selector) on top of everything // else. DrawListAppendClickInteractionChannel(); DrawListActivateClickInteractionChannel(); if (IsMiniMapActive()) { CalcMiniMapLayout(); MiniMapUpdate(); } // Handle node graph interaction if (!IsMiniMapHovered()) { if (GImNodes->LeftMouseClicked && GImNodes->HoveredLinkIdx.HasValue()) { BeginLinkInteraction(editor, GImNodes->HoveredLinkIdx.Value(), GImNodes->HoveredPinIdx); } else if (GImNodes->LeftMouseClicked && GImNodes->HoveredPinIdx.HasValue()) { BeginLinkCreation(editor, GImNodes->HoveredPinIdx.Value()); } else if (GImNodes->LeftMouseClicked && GImNodes->HoveredNodeIdx.HasValue()) { BeginNodeSelection(editor, GImNodes->HoveredNodeIdx.Value()); } else if ( GImNodes->LeftMouseClicked || GImNodes->LeftMouseReleased || GImNodes->AltMouseClicked || GImNodes->AltMouseScrollDelta != 0.f) { BeginCanvasInteraction(editor); } bool should_auto_pan = editor.ClickInteraction.Type == ImNodesClickInteractionType_BoxSelection || editor.ClickInteraction.Type == ImNodesClickInteractionType_LinkCreation || editor.ClickInteraction.Type == ImNodesClickInteractionType_Node; if (should_auto_pan && !MouseInCanvas()) { ImVec2 mouse = ImGui::GetMousePos(); ImVec2 center = GImNodes->CanvasRectScreenSpace.GetCenter(); ImVec2 direction = (center - mouse); direction = direction * ImInvLength(direction, 0.0); editor.AutoPanningDelta = direction * ImGui::GetIO().DeltaTime * GImNodes->Io.AutoPanningSpeed; editor.Panning += editor.AutoPanningDelta; } } ClickInteractionUpdate(editor); // At this point, draw commands have been issued for all nodes (and pins). Update the node pool // to detect unused node slots and remove those indices from the depth stack before sorting the // node draw commands by depth. ObjectPoolUpdate(editor.Nodes); ObjectPoolUpdate(editor.Pins); DrawListSortChannelsByDepth(editor.NodeDepthOrder); // After the links have been rendered, the link pool can be updated as well. ObjectPoolUpdate(editor.Links); // Finally, merge the draw channels GImNodes->CanvasDrawList->ChannelsMerge(); // pop style ImGui::EndChild(); // end scrolling region ImGui::PopStyleColor(); // pop child window background color ImGui::PopStyleVar(); // pop window padding ImGui::PopStyleVar(); // pop frame padding ImGui::EndGroup(); } void MiniMap( const float minimap_size_fraction, const ImNodesMiniMapLocation location, const ImNodesMiniMapNodeHoveringCallback node_hovering_callback, const ImNodesMiniMapNodeHoveringCallbackUserData node_hovering_callback_data) { // Check that editor size fraction is sane; must be in the range (0, 1] IM_ASSERT(minimap_size_fraction > 0.f && minimap_size_fraction <= 1.f); // Remember to call before EndNodeEditor IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_Editor); ImNodesEditorContext& editor = EditorContextGet(); editor.MiniMapEnabled = true; editor.MiniMapSizeFraction = minimap_size_fraction; editor.MiniMapLocation = location; // Set node hovering callback information editor.MiniMapNodeHoveringCallback = node_hovering_callback; editor.MiniMapNodeHoveringCallbackUserData = node_hovering_callback_data; // Actual drawing/updating of the MiniMap is done in EndNodeEditor so that // mini map is draw over everything and all pin/link positions are updated // correctly relative to their respective nodes. Hence, we must store some of // of the state for the mini map in GImNodes for the actual drawing/updating } void BeginNode(const int node_id) { // Remember to call BeginNodeEditor before calling BeginNode IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_Editor); GImNodes->CurrentScope = ImNodesScope_Node; ImNodesEditorContext& editor = EditorContextGet(); const int node_idx = ObjectPoolFindOrCreateIndex(editor.Nodes, node_id); GImNodes->CurrentNodeIdx = node_idx; ImNodeData& node = editor.Nodes.Pool[node_idx]; node.ColorStyle.Background = GImNodes->Style.Colors[ImNodesCol_NodeBackground]; node.ColorStyle.BackgroundHovered = GImNodes->Style.Colors[ImNodesCol_NodeBackgroundHovered]; node.ColorStyle.BackgroundSelected = GImNodes->Style.Colors[ImNodesCol_NodeBackgroundSelected]; node.ColorStyle.Outline = GImNodes->Style.Colors[ImNodesCol_NodeOutline]; node.ColorStyle.Titlebar = GImNodes->Style.Colors[ImNodesCol_TitleBar]; node.ColorStyle.TitlebarHovered = GImNodes->Style.Colors[ImNodesCol_TitleBarHovered]; node.ColorStyle.TitlebarSelected = GImNodes->Style.Colors[ImNodesCol_TitleBarSelected]; node.LayoutStyle.CornerRounding = GImNodes->Style.NodeCornerRounding; node.LayoutStyle.Padding = GImNodes->Style.NodePadding; node.LayoutStyle.BorderThickness = GImNodes->Style.NodeBorderThickness; // ImGui::SetCursorPos sets the cursor position, local to the current widget // (in this case, the child object started in BeginNodeEditor). Use // ImGui::SetCursorScreenPos to set the screen space coordinates directly. ImGui::SetCursorPos(GridSpaceToEditorSpace(editor, GetNodeTitleBarOrigin(node))); DrawListAddNode(node_idx); DrawListActivateCurrentNodeForeground(); ImGui::PushID(node.Id); ImGui::BeginGroup(); } void EndNode() { IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_Node); GImNodes->CurrentScope = ImNodesScope_Editor; ImNodesEditorContext& editor = EditorContextGet(); // The node's rectangle depends on the ImGui UI group size. ImGui::EndGroup(); ImGui::PopID(); ImNodeData& node = editor.Nodes.Pool[GImNodes->CurrentNodeIdx]; node.Rect = GetItemRect(); node.Rect.Expand(node.LayoutStyle.Padding); editor.GridContentBounds.Add(node.Origin); editor.GridContentBounds.Add(node.Origin + node.Rect.GetSize()); if (node.Rect.Contains(GImNodes->MousePos)) { GImNodes->NodeIndicesOverlappingWithMouse.push_back(GImNodes->CurrentNodeIdx); } } ImVec2 GetNodeDimensions(int node_id) { ImNodesEditorContext& editor = EditorContextGet(); const int node_idx = ObjectPoolFind(editor.Nodes, node_id); IM_ASSERT(node_idx != -1); // invalid node_id const ImNodeData& node = editor.Nodes.Pool[node_idx]; return node.Rect.GetSize(); } void BeginNodeTitleBar() { IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_Node); ImGui::BeginGroup(); } void EndNodeTitleBar() { IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_Node); ImGui::EndGroup(); ImNodesEditorContext& editor = EditorContextGet(); ImNodeData& node = editor.Nodes.Pool[GImNodes->CurrentNodeIdx]; node.TitleBarContentRect = GetItemRect(); ImGui::ItemAdd(GetNodeTitleRect(node), ImGui::GetID("title_bar")); ImGui::SetCursorPos(GridSpaceToEditorSpace(editor, GetNodeContentOrigin(node))); } void BeginInputAttribute(const int id, const ImNodesPinShape shape) { BeginPinAttribute(id, ImNodesAttributeType_Input, shape, GImNodes->CurrentNodeIdx); } void EndInputAttribute() { EndPinAttribute(); } void BeginOutputAttribute(const int id, const ImNodesPinShape shape) { BeginPinAttribute(id, ImNodesAttributeType_Output, shape, GImNodes->CurrentNodeIdx); } void EndOutputAttribute() { EndPinAttribute(); } void BeginStaticAttribute(const int id) { // Make sure to call BeginNode() before calling BeginAttribute() IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_Node); GImNodes->CurrentScope = ImNodesScope_Attribute; GImNodes->CurrentAttributeId = id; ImGui::BeginGroup(); ImGui::PushID(id); } void EndStaticAttribute() { // Make sure to call BeginNode() before calling BeginAttribute() IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_Attribute); GImNodes->CurrentScope = ImNodesScope_Node; ImGui::PopID(); ImGui::EndGroup(); if (ImGui::IsItemActive()) { GImNodes->ActiveAttribute = true; GImNodes->ActiveAttributeId = GImNodes->CurrentAttributeId; } } void PushAttributeFlag(const ImNodesAttributeFlags flag) { GImNodes->CurrentAttributeFlags |= flag; GImNodes->AttributeFlagStack.push_back(GImNodes->CurrentAttributeFlags); } void PopAttributeFlag() { // PopAttributeFlag called without a matching PushAttributeFlag! // The bottom value is always the default value, pushed in Initialize(). IM_ASSERT(GImNodes->AttributeFlagStack.size() > 1); GImNodes->AttributeFlagStack.pop_back(); GImNodes->CurrentAttributeFlags = GImNodes->AttributeFlagStack.back(); } void Link(const int id, const int start_attr_id, const int end_attr_id) { IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_Editor); ImNodesEditorContext& editor = EditorContextGet(); ImLinkData& link = ObjectPoolFindOrCreateObject(editor.Links, id); link.Id = id; link.StartPinIdx = ObjectPoolFindOrCreateIndex(editor.Pins, start_attr_id); link.EndPinIdx = ObjectPoolFindOrCreateIndex(editor.Pins, end_attr_id); link.ColorStyle.Base = GImNodes->Style.Colors[ImNodesCol_Link]; link.ColorStyle.Hovered = GImNodes->Style.Colors[ImNodesCol_LinkHovered]; link.ColorStyle.Selected = GImNodes->Style.Colors[ImNodesCol_LinkSelected]; // Check if this link was created by the current link event if ((editor.ClickInteraction.Type == ImNodesClickInteractionType_LinkCreation && editor.Pins.Pool[link.EndPinIdx].Flags & ImNodesAttributeFlags_EnableLinkCreationOnSnap && editor.ClickInteraction.LinkCreation.StartPinIdx == link.StartPinIdx && editor.ClickInteraction.LinkCreation.EndPinIdx == link.EndPinIdx) || (editor.ClickInteraction.LinkCreation.StartPinIdx == link.EndPinIdx && editor.ClickInteraction.LinkCreation.EndPinIdx == link.StartPinIdx)) { GImNodes->SnapLinkIdx = ObjectPoolFindOrCreateIndex(editor.Links, id); } } void PushColorStyle(const ImNodesCol item, unsigned int color) { GImNodes->ColorModifierStack.push_back(ImNodesColElement(GImNodes->Style.Colors[item], item)); GImNodes->Style.Colors[item] = color; } void PopColorStyle() { IM_ASSERT(GImNodes->ColorModifierStack.size() > 0); const ImNodesColElement elem = GImNodes->ColorModifierStack.back(); GImNodes->Style.Colors[elem.Item] = elem.Color; GImNodes->ColorModifierStack.pop_back(); } struct ImNodesStyleVarInfo { ImGuiDataType Type; ImU32 Count; ImU32 Offset; void* GetVarPtr(ImNodesStyle* style) const { return (void*)((unsigned char*)style + Offset); } }; static const ImNodesStyleVarInfo GStyleVarInfo[] = { // ImNodesStyleVar_GridSpacing {ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImNodesStyle, GridSpacing)}, // ImNodesStyleVar_NodeCornerRounding {ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImNodesStyle, NodeCornerRounding)}, // ImNodesStyleVar_NodePadding {ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImNodesStyle, NodePadding)}, // ImNodesStyleVar_NodeBorderThickness {ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImNodesStyle, NodeBorderThickness)}, // ImNodesStyleVar_LinkThickness {ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImNodesStyle, LinkThickness)}, // ImNodesStyleVar_LinkLineSegmentsPerLength {ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImNodesStyle, LinkLineSegmentsPerLength)}, // ImNodesStyleVar_LinkHoverDistance {ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImNodesStyle, LinkHoverDistance)}, // ImNodesStyleVar_PinCircleRadius {ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImNodesStyle, PinCircleRadius)}, // ImNodesStyleVar_PinQuadSideLength {ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImNodesStyle, PinQuadSideLength)}, // ImNodesStyleVar_PinTriangleSideLength {ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImNodesStyle, PinTriangleSideLength)}, // ImNodesStyleVar_PinLineThickness {ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImNodesStyle, PinLineThickness)}, // ImNodesStyleVar_PinHoverRadius {ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImNodesStyle, PinHoverRadius)}, // ImNodesStyleVar_PinOffset {ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImNodesStyle, PinOffset)}, // ImNodesStyleVar_MiniMapPadding {ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImNodesStyle, MiniMapPadding)}, // ImNodesStyleVar_MiniMapOffset {ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImNodesStyle, MiniMapOffset)}, }; static const ImNodesStyleVarInfo* GetStyleVarInfo(ImNodesStyleVar idx) { IM_ASSERT(idx >= 0 && idx < ImNodesStyleVar_COUNT); IM_ASSERT(IM_ARRAYSIZE(GStyleVarInfo) == ImNodesStyleVar_COUNT); return &GStyleVarInfo[idx]; } void PushStyleVar(const ImNodesStyleVar item, const float value) { const ImNodesStyleVarInfo* var_info = GetStyleVarInfo(item); if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1) { float& style_var = *(float*)var_info->GetVarPtr(&GImNodes->Style); GImNodes->StyleModifierStack.push_back(ImNodesStyleVarElement(item, style_var)); style_var = value; return; } IM_ASSERT(0 && "Called PushStyleVar() float variant but variable is not a float!"); } void PushStyleVar(const ImNodesStyleVar item, const ImVec2& value) { const ImNodesStyleVarInfo* var_info = GetStyleVarInfo(item); if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2) { ImVec2& style_var = *(ImVec2*)var_info->GetVarPtr(&GImNodes->Style); GImNodes->StyleModifierStack.push_back(ImNodesStyleVarElement(item, style_var)); style_var = value; return; } IM_ASSERT(0 && "Called PushStyleVar() ImVec2 variant but variable is not a ImVec2!"); } void PopStyleVar(int count) { while (count > 0) { IM_ASSERT(GImNodes->StyleModifierStack.size() > 0); const ImNodesStyleVarElement style_backup = GImNodes->StyleModifierStack.back(); GImNodes->StyleModifierStack.pop_back(); const ImNodesStyleVarInfo* var_info = GetStyleVarInfo(style_backup.Item); void* style_var = var_info->GetVarPtr(&GImNodes->Style); if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1) { ((float*)style_var)[0] = style_backup.FloatValue[0]; } else if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2) { ((float*)style_var)[0] = style_backup.FloatValue[0]; ((float*)style_var)[1] = style_backup.FloatValue[1]; } count--; } } void SetNodeScreenSpacePos(const int node_id, const ImVec2& screen_space_pos) { ImNodesEditorContext& editor = EditorContextGet(); ImNodeData& node = ObjectPoolFindOrCreateObject(editor.Nodes, node_id); node.Origin = ScreenSpaceToGridSpace(editor, screen_space_pos); } void SetNodeEditorSpacePos(const int node_id, const ImVec2& editor_space_pos) { ImNodesEditorContext& editor = EditorContextGet(); ImNodeData& node = ObjectPoolFindOrCreateObject(editor.Nodes, node_id); node.Origin = EditorSpaceToGridSpace(editor, editor_space_pos); } void SetNodeGridSpacePos(const int node_id, const ImVec2& grid_pos) { ImNodesEditorContext& editor = EditorContextGet(); ImNodeData& node = ObjectPoolFindOrCreateObject(editor.Nodes, node_id); node.Origin = grid_pos; } void SetNodeDraggable(const int node_id, const bool draggable) { ImNodesEditorContext& editor = EditorContextGet(); ImNodeData& node = ObjectPoolFindOrCreateObject(editor.Nodes, node_id); node.Draggable = draggable; } ImVec2 GetNodeScreenSpacePos(const int node_id) { ImNodesEditorContext& editor = EditorContextGet(); const int node_idx = ObjectPoolFind(editor.Nodes, node_id); IM_ASSERT(node_idx != -1); ImNodeData& node = editor.Nodes.Pool[node_idx]; return GridSpaceToScreenSpace(editor, node.Origin); } ImVec2 GetNodeEditorSpacePos(const int node_id) { ImNodesEditorContext& editor = EditorContextGet(); const int node_idx = ObjectPoolFind(editor.Nodes, node_id); IM_ASSERT(node_idx != -1); ImNodeData& node = editor.Nodes.Pool[node_idx]; return GridSpaceToEditorSpace(editor, node.Origin); } ImVec2 GetNodeGridSpacePos(const int node_id) { ImNodesEditorContext& editor = EditorContextGet(); const int node_idx = ObjectPoolFind(editor.Nodes, node_id); IM_ASSERT(node_idx != -1); ImNodeData& node = editor.Nodes.Pool[node_idx]; return node.Origin; } void SnapNodeToGrid(int node_id) { ImNodesEditorContext& editor = EditorContextGet(); ImNodeData& node = ObjectPoolFindOrCreateObject(editor.Nodes, node_id); node.Origin = SnapOriginToGrid(node.Origin); } bool IsEditorHovered() { return MouseInCanvas(); } bool IsNodeHovered(int* const node_id) { IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_None); IM_ASSERT(node_id != NULL); const bool is_hovered = GImNodes->HoveredNodeIdx.HasValue(); if (is_hovered) { const ImNodesEditorContext& editor = EditorContextGet(); *node_id = editor.Nodes.Pool[GImNodes->HoveredNodeIdx.Value()].Id; } return is_hovered; } bool IsLinkHovered(int* const link_id) { IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_None); IM_ASSERT(link_id != NULL); const bool is_hovered = GImNodes->HoveredLinkIdx.HasValue(); if (is_hovered) { const ImNodesEditorContext& editor = EditorContextGet(); *link_id = editor.Links.Pool[GImNodes->HoveredLinkIdx.Value()].Id; } return is_hovered; } bool IsPinHovered(int* const attr) { IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_None); IM_ASSERT(attr != NULL); const bool is_hovered = GImNodes->HoveredPinIdx.HasValue(); if (is_hovered) { const ImNodesEditorContext& editor = EditorContextGet(); *attr = editor.Pins.Pool[GImNodes->HoveredPinIdx.Value()].Id; } return is_hovered; } int NumSelectedNodes() { IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_None); const ImNodesEditorContext& editor = EditorContextGet(); return editor.SelectedNodeIndices.size(); } int NumSelectedLinks() { IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_None); const ImNodesEditorContext& editor = EditorContextGet(); return editor.SelectedLinkIndices.size(); } void GetSelectedNodes(int* node_ids) { IM_ASSERT(node_ids != NULL); const ImNodesEditorContext& editor = EditorContextGet(); for (int i = 0; i < editor.SelectedNodeIndices.size(); ++i) { const int node_idx = editor.SelectedNodeIndices[i]; node_ids[i] = editor.Nodes.Pool[node_idx].Id; } } void GetSelectedLinks(int* link_ids) { IM_ASSERT(link_ids != NULL); const ImNodesEditorContext& editor = EditorContextGet(); for (int i = 0; i < editor.SelectedLinkIndices.size(); ++i) { const int link_idx = editor.SelectedLinkIndices[i]; link_ids[i] = editor.Links.Pool[link_idx].Id; } } void ClearNodeSelection() { ImNodesEditorContext& editor = EditorContextGet(); editor.SelectedNodeIndices.clear(); } void ClearNodeSelection(int node_id) { ImNodesEditorContext& editor = EditorContextGet(); ClearObjectSelection(editor.Nodes, editor.SelectedNodeIndices, node_id); } void ClearLinkSelection() { ImNodesEditorContext& editor = EditorContextGet(); editor.SelectedLinkIndices.clear(); } void ClearLinkSelection(int link_id) { ImNodesEditorContext& editor = EditorContextGet(); ClearObjectSelection(editor.Links, editor.SelectedLinkIndices, link_id); } void SelectNode(int node_id) { ImNodesEditorContext& editor = EditorContextGet(); SelectObject(editor.Nodes, editor.SelectedNodeIndices, node_id); } void SelectLink(int link_id) { ImNodesEditorContext& editor = EditorContextGet(); SelectObject(editor.Links, editor.SelectedLinkIndices, link_id); } bool IsNodeSelected(int node_id) { ImNodesEditorContext& editor = EditorContextGet(); return IsObjectSelected(editor.Nodes, editor.SelectedNodeIndices, node_id); } bool IsLinkSelected(int link_id) { ImNodesEditorContext& editor = EditorContextGet(); return IsObjectSelected(editor.Links, editor.SelectedLinkIndices, link_id); } bool IsAttributeActive() { IM_ASSERT((GImNodes->CurrentScope & ImNodesScope_Node) != 0); if (!GImNodes->ActiveAttribute) { return false; } return GImNodes->ActiveAttributeId == GImNodes->CurrentAttributeId; } bool IsAnyAttributeActive(int* const attribute_id) { IM_ASSERT((GImNodes->CurrentScope & (ImNodesScope_Node | ImNodesScope_Attribute)) == 0); if (!GImNodes->ActiveAttribute) { return false; } if (attribute_id != NULL) { *attribute_id = GImNodes->ActiveAttributeId; } return true; } bool IsLinkStarted(int* const started_at_id) { // Call this function after EndNodeEditor()! IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_None); IM_ASSERT(started_at_id != NULL); const bool is_started = (GImNodes->ImNodesUIState & ImNodesUIState_LinkStarted) != 0; if (is_started) { const ImNodesEditorContext& editor = EditorContextGet(); const int pin_idx = editor.ClickInteraction.LinkCreation.StartPinIdx; *started_at_id = editor.Pins.Pool[pin_idx].Id; } return is_started; } bool IsLinkDropped(int* const started_at_id, const bool including_detached_links) { // Call this function after EndNodeEditor()! IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_None); const ImNodesEditorContext& editor = EditorContextGet(); const bool link_dropped = (GImNodes->ImNodesUIState & ImNodesUIState_LinkDropped) != 0 && (including_detached_links || editor.ClickInteraction.LinkCreation.Type != ImNodesLinkCreationType_FromDetach); if (link_dropped && started_at_id) { const int pin_idx = editor.ClickInteraction.LinkCreation.StartPinIdx; *started_at_id = editor.Pins.Pool[pin_idx].Id; } return link_dropped; } bool IsLinkCreated( int* const started_at_pin_id, int* const ended_at_pin_id, bool* const created_from_snap) { IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_None); IM_ASSERT(started_at_pin_id != NULL); IM_ASSERT(ended_at_pin_id != NULL); const bool is_created = (GImNodes->ImNodesUIState & ImNodesUIState_LinkCreated) != 0; if (is_created) { const ImNodesEditorContext& editor = EditorContextGet(); const int start_idx = editor.ClickInteraction.LinkCreation.StartPinIdx; const int end_idx = editor.ClickInteraction.LinkCreation.EndPinIdx.Value(); const ImPinData& start_pin = editor.Pins.Pool[start_idx]; const ImPinData& end_pin = editor.Pins.Pool[end_idx]; if (start_pin.Type == ImNodesAttributeType_Output) { *started_at_pin_id = start_pin.Id; *ended_at_pin_id = end_pin.Id; } else { *started_at_pin_id = end_pin.Id; *ended_at_pin_id = start_pin.Id; } if (created_from_snap) { *created_from_snap = editor.ClickInteraction.Type == ImNodesClickInteractionType_LinkCreation; } } return is_created; } bool IsLinkCreated( int* started_at_node_id, int* started_at_pin_id, int* ended_at_node_id, int* ended_at_pin_id, bool* created_from_snap) { IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_None); IM_ASSERT(started_at_node_id != NULL); IM_ASSERT(started_at_pin_id != NULL); IM_ASSERT(ended_at_node_id != NULL); IM_ASSERT(ended_at_pin_id != NULL); const bool is_created = (GImNodes->ImNodesUIState & ImNodesUIState_LinkCreated) != 0; if (is_created) { const ImNodesEditorContext& editor = EditorContextGet(); const int start_idx = editor.ClickInteraction.LinkCreation.StartPinIdx; const int end_idx = editor.ClickInteraction.LinkCreation.EndPinIdx.Value(); const ImPinData& start_pin = editor.Pins.Pool[start_idx]; const ImNodeData& start_node = editor.Nodes.Pool[start_pin.ParentNodeIdx]; const ImPinData& end_pin = editor.Pins.Pool[end_idx]; const ImNodeData& end_node = editor.Nodes.Pool[end_pin.ParentNodeIdx]; if (start_pin.Type == ImNodesAttributeType_Output) { *started_at_pin_id = start_pin.Id; *started_at_node_id = start_node.Id; *ended_at_pin_id = end_pin.Id; *ended_at_node_id = end_node.Id; } else { *started_at_pin_id = end_pin.Id; *started_at_node_id = end_node.Id; *ended_at_pin_id = start_pin.Id; *ended_at_node_id = start_node.Id; } if (created_from_snap) { *created_from_snap = editor.ClickInteraction.Type == ImNodesClickInteractionType_LinkCreation; } } return is_created; } bool IsLinkDestroyed(int* const link_id) { IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_None); const bool link_destroyed = GImNodes->DeletedLinkIdx.HasValue(); if (link_destroyed) { const ImNodesEditorContext& editor = EditorContextGet(); const int link_idx = GImNodes->DeletedLinkIdx.Value(); *link_id = editor.Links.Pool[link_idx].Id; } return link_destroyed; } namespace { void NodeLineHandler(ImNodesEditorContext& editor, const char* const line) { int id; int x, y; if (sscanf(line, "[node.%i", &id) == 1) { const int node_idx = ObjectPoolFindOrCreateIndex(editor.Nodes, id); GImNodes->CurrentNodeIdx = node_idx; ImNodeData& node = editor.Nodes.Pool[node_idx]; node.Id = id; } else if (sscanf(line, "origin=%i,%i", &x, &y) == 2) { ImNodeData& node = editor.Nodes.Pool[GImNodes->CurrentNodeIdx]; node.Origin = SnapOriginToGrid(ImVec2((float)x, (float)y)); } } void EditorLineHandler(ImNodesEditorContext& editor, const char* const line) { (void)sscanf(line, "panning=%f,%f", &editor.Panning.x, &editor.Panning.y); } } // namespace const char* SaveCurrentEditorStateToIniString(size_t* const data_size) { return SaveEditorStateToIniString(&EditorContextGet(), data_size); } const char* SaveEditorStateToIniString( const ImNodesEditorContext* const editor_ptr, size_t* const data_size) { IM_ASSERT(editor_ptr != NULL); const ImNodesEditorContext& editor = *editor_ptr; GImNodes->TextBuffer.clear(); // TODO: check to make sure that the estimate is the upper bound of element GImNodes->TextBuffer.reserve(64 * editor.Nodes.Pool.size()); GImNodes->TextBuffer.appendf( "[editor]\npanning=%i,%i\n", (int)editor.Panning.x, (int)editor.Panning.y); for (int i = 0; i < editor.Nodes.Pool.size(); i++) { if (editor.Nodes.InUse[i]) { const ImNodeData& node = editor.Nodes.Pool[i]; GImNodes->TextBuffer.appendf("\n[node.%d]\n", node.Id); GImNodes->TextBuffer.appendf("origin=%i,%i\n", (int)node.Origin.x, (int)node.Origin.y); } } if (data_size != NULL) { *data_size = GImNodes->TextBuffer.size(); } return GImNodes->TextBuffer.c_str(); } void LoadCurrentEditorStateFromIniString(const char* const data, const size_t data_size) { LoadEditorStateFromIniString(&EditorContextGet(), data, data_size); } void LoadEditorStateFromIniString( ImNodesEditorContext* const editor_ptr, const char* const data, const size_t data_size) { if (data_size == 0u) { return; } ImNodesEditorContext& editor = editor_ptr == NULL ? EditorContextGet() : *editor_ptr; char* buf = (char*)ImGui::MemAlloc(data_size + 1); const char* buf_end = buf + data_size; memcpy(buf, data, data_size); buf[data_size] = 0; void (*line_handler)(ImNodesEditorContext&, const char*); line_handler = NULL; char* line_end = NULL; for (char* line = buf; line < buf_end; line = line_end + 1) { while (*line == '\n' || *line == '\r') { line++; } line_end = line; while (line_end < buf_end && *line_end != '\n' && *line_end != '\r') { line_end++; } line_end[0] = 0; if (*line == ';' || *line == '\0') { continue; } if (line[0] == '[' && line_end[-1] == ']') { line_end[-1] = 0; if (strncmp(line + 1, "node", 4) == 0) { line_handler = NodeLineHandler; } else if (strcmp(line + 1, "editor") == 0) { line_handler = EditorLineHandler; } } if (line_handler != NULL) { line_handler(editor, line); } } ImGui::MemFree(buf); } void SaveCurrentEditorStateToIniFile(const char* const file_name) { SaveEditorStateToIniFile(&EditorContextGet(), file_name); } void SaveEditorStateToIniFile(const ImNodesEditorContext* const editor, const char* const file_name) { size_t data_size = 0u; const char* data = SaveEditorStateToIniString(editor, &data_size); FILE* file = ImFileOpen(file_name, "wt"); if (!file) { return; } fwrite(data, sizeof(char), data_size, file); fclose(file); } void LoadCurrentEditorStateFromIniFile(const char* const file_name) { LoadEditorStateFromIniFile(&EditorContextGet(), file_name); } void LoadEditorStateFromIniFile(ImNodesEditorContext* const editor, const char* const file_name) { size_t data_size = 0u; char* file_data = (char*)ImFileLoadToMemory(file_name, "rb", &data_size); if (!file_data) { return; } LoadEditorStateFromIniString(editor, file_data, data_size); ImGui::MemFree(file_data); } } // namespace IMNODES_NAMESPACE ================================================ FILE: Source/External/imgui_tools/imnodes/imnodes.h ================================================ #pragma once #include #include #ifdef IMNODES_USER_CONFIG #include IMNODES_USER_CONFIG #endif #ifndef IMNODES_NAMESPACE #define IMNODES_NAMESPACE ImNodes #endif typedef int ImNodesCol; // -> enum ImNodesCol_ typedef int ImNodesStyleVar; // -> enum ImNodesStyleVar_ typedef int ImNodesStyleFlags; // -> enum ImNodesStyleFlags_ typedef int ImNodesPinShape; // -> enum ImNodesPinShape_ typedef int ImNodesAttributeFlags; // -> enum ImNodesAttributeFlags_ typedef int ImNodesMiniMapLocation; // -> enum ImNodesMiniMapLocation_ enum ImNodesCol_ { ImNodesCol_NodeBackground = 0, ImNodesCol_NodeBackgroundHovered, ImNodesCol_NodeBackgroundSelected, ImNodesCol_NodeOutline, ImNodesCol_TitleBar, ImNodesCol_TitleBarHovered, ImNodesCol_TitleBarSelected, ImNodesCol_Link, ImNodesCol_LinkHovered, ImNodesCol_LinkSelected, ImNodesCol_Pin, ImNodesCol_PinHovered, ImNodesCol_BoxSelector, ImNodesCol_BoxSelectorOutline, ImNodesCol_GridBackground, ImNodesCol_GridLine, ImNodesCol_GridLinePrimary, ImNodesCol_MiniMapBackground, ImNodesCol_MiniMapBackgroundHovered, ImNodesCol_MiniMapOutline, ImNodesCol_MiniMapOutlineHovered, ImNodesCol_MiniMapNodeBackground, ImNodesCol_MiniMapNodeBackgroundHovered, ImNodesCol_MiniMapNodeBackgroundSelected, ImNodesCol_MiniMapNodeOutline, ImNodesCol_MiniMapLink, ImNodesCol_MiniMapLinkSelected, ImNodesCol_MiniMapCanvas, ImNodesCol_MiniMapCanvasOutline, ImNodesCol_COUNT }; enum ImNodesStyleVar_ { ImNodesStyleVar_GridSpacing = 0, ImNodesStyleVar_NodeCornerRounding, ImNodesStyleVar_NodePadding, ImNodesStyleVar_NodeBorderThickness, ImNodesStyleVar_LinkThickness, ImNodesStyleVar_LinkLineSegmentsPerLength, ImNodesStyleVar_LinkHoverDistance, ImNodesStyleVar_PinCircleRadius, ImNodesStyleVar_PinQuadSideLength, ImNodesStyleVar_PinTriangleSideLength, ImNodesStyleVar_PinLineThickness, ImNodesStyleVar_PinHoverRadius, ImNodesStyleVar_PinOffset, ImNodesStyleVar_MiniMapPadding, ImNodesStyleVar_MiniMapOffset, ImNodesStyleVar_COUNT }; enum ImNodesStyleFlags_ { ImNodesStyleFlags_None = 0, ImNodesStyleFlags_NodeOutline = 1 << 0, ImNodesStyleFlags_GridLines = 1 << 2, ImNodesStyleFlags_GridLinesPrimary = 1 << 3, ImNodesStyleFlags_GridSnapping = 1 << 4 }; enum ImNodesPinShape_ { ImNodesPinShape_Circle, ImNodesPinShape_CircleFilled, ImNodesPinShape_Triangle, ImNodesPinShape_TriangleFilled, ImNodesPinShape_Quad, ImNodesPinShape_QuadFilled }; // This enum controls the way the attribute pins behave. enum ImNodesAttributeFlags_ { ImNodesAttributeFlags_None = 0, // Allow detaching a link by left-clicking and dragging the link at a pin it is connected to. // NOTE: the user has to actually delete the link for this to work. A deleted link can be // detected by calling IsLinkDestroyed() after EndNodeEditor(). ImNodesAttributeFlags_EnableLinkDetachWithDragClick = 1 << 0, // Visual snapping of an in progress link will trigger IsLink Created/Destroyed events. Allows // for previewing the creation of a link while dragging it across attributes. See here for demo: // https://github.com/Nelarius/imnodes/issues/41#issuecomment-647132113 NOTE: the user has to // actually delete the link for this to work. A deleted link can be detected by calling // IsLinkDestroyed() after EndNodeEditor(). ImNodesAttributeFlags_EnableLinkCreationOnSnap = 1 << 1 }; struct ImNodesIO { struct EmulateThreeButtonMouse { EmulateThreeButtonMouse(); // The keyboard modifier to use in combination with mouse left click to pan the editor view. // Set to NULL by default. To enable this feature, set the modifier to point to a boolean // indicating the state of a modifier. For example, // // ImNodes::GetIO().EmulateThreeButtonMouse.Modifier = &ImGui::GetIO().KeyAlt; const bool* Modifier; } EmulateThreeButtonMouse; struct LinkDetachWithModifierClick { LinkDetachWithModifierClick(); // Pointer to a boolean value indicating when the desired modifier is pressed. Set to NULL // by default. To enable the feature, set the modifier to point to a boolean indicating the // state of a modifier. For example, // // ImNodes::GetIO().LinkDetachWithModifierClick.Modifier = &ImGui::GetIO().KeyCtrl; // // Left-clicking a link with this modifier pressed will detach that link. NOTE: the user has // to actually delete the link for this to work. A deleted link can be detected by calling // IsLinkDestroyed() after EndNodeEditor(). const bool* Modifier; } LinkDetachWithModifierClick; struct MultipleSelectModifier { MultipleSelectModifier(); // Pointer to a boolean value indicating when the desired modifier is pressed. Set to NULL // by default. To enable the feature, set the modifier to point to a boolean indicating the // state of a modifier. For example, // // ImNodes::GetIO().MultipleSelectModifier.Modifier = &ImGui::GetIO().KeyCtrl; // // Left-clicking a node with this modifier pressed will add the node to the list of // currently selected nodes. If this value is NULL, the Ctrl key will be used. const bool* Modifier; } MultipleSelectModifier; // Holding alt mouse button pans the node area, by default middle mouse button will be used // Set based on ImGuiMouseButton values int AltMouseButton; // Panning speed when dragging an element and mouse is outside the main editor view. float AutoPanningSpeed; ImNodesIO(); }; struct ImNodesStyle { float GridSpacing; float NodeCornerRounding; ImVec2 NodePadding; float NodeBorderThickness; float LinkThickness; float LinkLineSegmentsPerLength; float LinkHoverDistance; // The following variables control the look and behavior of the pins. The default size of each // pin shape is balanced to occupy approximately the same surface area on the screen. // The circle radius used when the pin shape is either ImNodesPinShape_Circle or // ImNodesPinShape_CircleFilled. float PinCircleRadius; // The quad side length used when the shape is either ImNodesPinShape_Quad or // ImNodesPinShape_QuadFilled. float PinQuadSideLength; // The equilateral triangle side length used when the pin shape is either // ImNodesPinShape_Triangle or ImNodesPinShape_TriangleFilled. float PinTriangleSideLength; // The thickness of the line used when the pin shape is not filled. float PinLineThickness; // The radius from the pin's center position inside of which it is detected as being hovered // over. float PinHoverRadius; // Offsets the pins' positions from the edge of the node to the outside of the node. float PinOffset; // Mini-map padding size between mini-map edge and mini-map content. ImVec2 MiniMapPadding; // Mini-map offset from the screen side. ImVec2 MiniMapOffset; // By default, ImNodesStyleFlags_NodeOutline and ImNodesStyleFlags_Gridlines are enabled. ImNodesStyleFlags Flags; // Set these mid-frame using Push/PopColorStyle. You can index this color array with with a // ImNodesCol value. unsigned int Colors[ImNodesCol_COUNT]; ImNodesStyle(); }; enum ImNodesMiniMapLocation_ { ImNodesMiniMapLocation_BottomLeft, ImNodesMiniMapLocation_BottomRight, ImNodesMiniMapLocation_TopLeft, ImNodesMiniMapLocation_TopRight, }; struct ImGuiContext; struct ImVec2; struct ImNodesContext; // An editor context corresponds to a set of nodes in a single workspace (created with a single // Begin/EndNodeEditor pair) // // By default, the library creates an editor context behind the scenes, so using any of the imnodes // functions doesn't require you to explicitly create a context. struct ImNodesEditorContext; // Callback type used to specify special behavior when hovering a node in the minimap #ifndef ImNodesMiniMapNodeHoveringCallback typedef void (*ImNodesMiniMapNodeHoveringCallback)(int, void*); #endif #ifndef ImNodesMiniMapNodeHoveringCallbackUserData typedef void* ImNodesMiniMapNodeHoveringCallbackUserData; #endif namespace IMNODES_NAMESPACE { // Call this function if you are compiling imnodes in to a dll, separate from ImGui. Calling this // function sets the GImGui global variable, which is not shared across dll boundaries. void SetImGuiContext(ImGuiContext* ctx); ImNodesContext* CreateContext(); void DestroyContext(ImNodesContext* ctx = NULL); // NULL = destroy current context ImNodesContext* GetCurrentContext(); void SetCurrentContext(ImNodesContext* ctx); ImNodesEditorContext* EditorContextCreate(); void EditorContextFree(ImNodesEditorContext*); void EditorContextSet(ImNodesEditorContext*); ImVec2 EditorContextGetPanning(); void EditorContextResetPanning(const ImVec2& pos); void EditorContextMoveToNode(const int node_id); ImNodesIO& GetIO(); // Returns the global style struct. See the struct declaration for default values. ImNodesStyle& GetStyle(); // Style presets matching the dear imgui styles of the same name. If dest is NULL, the active // context's ImNodesStyle instance will be used as the destination. void StyleColorsDark(ImNodesStyle* dest = NULL); // on by default void StyleColorsClassic(ImNodesStyle* dest = NULL); void StyleColorsLight(ImNodesStyle* dest = NULL); // The top-level function call. Call this before calling BeginNode/EndNode. Calling this function // will result the node editor grid workspace being rendered. void BeginNodeEditor(); void EndNodeEditor(); // Add a navigable minimap to the editor; call before EndNodeEditor after all // nodes and links have been specified void MiniMap( const float minimap_size_fraction = 0.2f, const ImNodesMiniMapLocation location = ImNodesMiniMapLocation_TopLeft, const ImNodesMiniMapNodeHoveringCallback node_hovering_callback = NULL, const ImNodesMiniMapNodeHoveringCallbackUserData node_hovering_callback_data = NULL); // Use PushColorStyle and PopColorStyle to modify ImNodesStyle::Colors mid-frame. void PushColorStyle(ImNodesCol item, unsigned int color); void PopColorStyle(); void PushStyleVar(ImNodesStyleVar style_item, float value); void PushStyleVar(ImNodesStyleVar style_item, const ImVec2& value); void PopStyleVar(int count = 1); // id can be any positive or negative integer, but INT_MIN is currently reserved for internal use. void BeginNode(int id); void EndNode(); ImVec2 GetNodeDimensions(int id); // Place your node title bar content (such as the node title, using ImGui::Text) between the // following function calls. These functions have to be called before adding any attributes, or the // layout of the node will be incorrect. void BeginNodeTitleBar(); void EndNodeTitleBar(); // Attributes are ImGui UI elements embedded within the node. Attributes can have pin shapes // rendered next to them. Links are created between pins. // // The activity status of an attribute can be checked via the IsAttributeActive() and // IsAnyAttributeActive() function calls. This is one easy way of checking for any changes made to // an attribute's drag float UI, for instance. // // Each attribute id must be unique. // Create an input attribute block. The pin is rendered on left side. void BeginInputAttribute(int id, ImNodesPinShape shape = ImNodesPinShape_CircleFilled); void EndInputAttribute(); // Create an output attribute block. The pin is rendered on the right side. void BeginOutputAttribute(int id, ImNodesPinShape shape = ImNodesPinShape_CircleFilled); void EndOutputAttribute(); // Create a static attribute block. A static attribute has no pin, and therefore can't be linked to // anything. However, you can still use IsAttributeActive() and IsAnyAttributeActive() to check for // attribute activity. void BeginStaticAttribute(int id); void EndStaticAttribute(); // Push a single AttributeFlags value. By default, only AttributeFlags_None is set. void PushAttributeFlag(ImNodesAttributeFlags flag); void PopAttributeFlag(); // Render a link between attributes. // The attributes ids used here must match the ids used in Begin(Input|Output)Attribute function // calls. The order of start_attr and end_attr doesn't make a difference for rendering the link. void Link(int id, int start_attribute_id, int end_attribute_id); // Enable or disable the ability to click and drag a specific node. void SetNodeDraggable(int node_id, const bool draggable); // The node's position can be expressed in three coordinate systems: // * screen space coordinates, -- the origin is the upper left corner of the window. // * editor space coordinates -- the origin is the upper left corner of the node editor window // * grid space coordinates, -- the origin is the upper left corner of the node editor window, // translated by the current editor panning vector (see EditorContextGetPanning() and // EditorContextResetPanning()) // Use the following functions to get and set the node's coordinates in these coordinate systems. void SetNodeScreenSpacePos(int node_id, const ImVec2& screen_space_pos); void SetNodeEditorSpacePos(int node_id, const ImVec2& editor_space_pos); void SetNodeGridSpacePos(int node_id, const ImVec2& grid_pos); ImVec2 GetNodeScreenSpacePos(const int node_id); ImVec2 GetNodeEditorSpacePos(const int node_id); ImVec2 GetNodeGridSpacePos(const int node_id); // If ImNodesStyleFlags_GridSnapping is enabled, snap the specified node's origin to the grid. void SnapNodeToGrid(int node_id); // Returns true if the current node editor canvas is being hovered over by the mouse, and is not // blocked by any other windows. bool IsEditorHovered(); // The following functions return true if a UI element is being hovered over by the mouse cursor. // Assigns the id of the UI element being hovered over to the function argument. Use these functions // after EndNodeEditor() has been called. bool IsNodeHovered(int* node_id); bool IsLinkHovered(int* link_id); bool IsPinHovered(int* attribute_id); // Use The following two functions to query the number of selected nodes or links in the current // editor. Use after calling EndNodeEditor(). int NumSelectedNodes(); int NumSelectedLinks(); // Get the selected node/link ids. The pointer argument should point to an integer array with at // least as many elements as the respective NumSelectedNodes/NumSelectedLinks function call // returned. void GetSelectedNodes(int* node_ids); void GetSelectedLinks(int* link_ids); // Clears the list of selected nodes/links. Useful if you want to delete a selected node or link. void ClearNodeSelection(); void ClearLinkSelection(); // Use the following functions to add or remove individual nodes or links from the current editors // selection. Note that all functions require the id to be an existing valid id for this editor. // Select-functions has the precondition that the object is currently considered unselected. // Clear-functions has the precondition that the object is currently considered selected. // Preconditions listed above can be checked via IsNodeSelected/IsLinkSelected if not already // known. void SelectNode(int node_id); void ClearNodeSelection(int node_id); bool IsNodeSelected(int node_id); void SelectLink(int link_id); void ClearLinkSelection(int link_id); bool IsLinkSelected(int link_id); // Was the previous attribute active? This will continuously return true while the left mouse button // is being pressed over the UI content of the attribute. bool IsAttributeActive(); // Was any attribute active? If so, sets the active attribute id to the output function argument. bool IsAnyAttributeActive(int* attribute_id = NULL); // Use the following functions to query a change of state for an existing link, or new link. Call // these after EndNodeEditor(). // Did the user start dragging a new link from a pin? bool IsLinkStarted(int* started_at_attribute_id); // Did the user drop the dragged link before attaching it to a pin? // There are two different kinds of situations to consider when handling this event: // 1) a link which is created at a pin and then dropped // 2) an existing link which is detached from a pin and then dropped // Use the including_detached_links flag to control whether this function triggers when the user // detaches a link and drops it. bool IsLinkDropped(int* started_at_attribute_id = NULL, bool including_detached_links = true); // Did the user finish creating a new link? bool IsLinkCreated( int* started_at_attribute_id, int* ended_at_attribute_id, bool* created_from_snap = NULL); bool IsLinkCreated( int* started_at_node_id, int* started_at_attribute_id, int* ended_at_node_id, int* ended_at_attribute_id, bool* created_from_snap = NULL); // Was an existing link detached from a pin by the user? The detached link's id is assigned to the // output argument link_id. bool IsLinkDestroyed(int* link_id); // Use the following functions to write the editor context's state to a string, or directly to a // file. The editor context is serialized in the INI file format. const char* SaveCurrentEditorStateToIniString(size_t* data_size = NULL); const char* SaveEditorStateToIniString( const ImNodesEditorContext* editor, size_t* data_size = NULL); void LoadCurrentEditorStateFromIniString(const char* data, size_t data_size); void LoadEditorStateFromIniString(ImNodesEditorContext* editor, const char* data, size_t data_size); void SaveCurrentEditorStateToIniFile(const char* file_name); void SaveEditorStateToIniFile(const ImNodesEditorContext* editor, const char* file_name); void LoadCurrentEditorStateFromIniFile(const char* file_name); void LoadEditorStateFromIniFile(ImNodesEditorContext* editor, const char* file_name); } // namespace IMNODES_NAMESPACE ================================================ FILE: Source/External/imgui_tools/imnodes/imnodes_internal.h ================================================ #pragma once #define IMGUI_DEFINE_MATH_OPERATORS #include "imnodes.h" #include #include #include // the structure of this file: // // [SECTION] internal enums // [SECTION] internal data structures // [SECTION] global and editor context structs // [SECTION] object pool implementation struct ImNodesContext; extern ImNodesContext* GImNodes; // [SECTION] internal enums typedef int ImNodesScope; typedef int ImNodesAttributeType; typedef int ImNodesUIState; typedef int ImNodesClickInteractionType; typedef int ImNodesLinkCreationType; enum ImNodesScope_ { ImNodesScope_None = 1, ImNodesScope_Editor = 1 << 1, ImNodesScope_Node = 1 << 2, ImNodesScope_Attribute = 1 << 3 }; enum ImNodesAttributeType_ { ImNodesAttributeType_None, ImNodesAttributeType_Input, ImNodesAttributeType_Output }; enum ImNodesUIState_ { ImNodesUIState_None = 0, ImNodesUIState_LinkStarted = 1 << 0, ImNodesUIState_LinkDropped = 1 << 1, ImNodesUIState_LinkCreated = 1 << 2 }; enum ImNodesClickInteractionType_ { ImNodesClickInteractionType_Node, ImNodesClickInteractionType_Link, ImNodesClickInteractionType_LinkCreation, ImNodesClickInteractionType_Panning, ImNodesClickInteractionType_BoxSelection, ImNodesClickInteractionType_ImGuiItem, ImNodesClickInteractionType_None }; enum ImNodesLinkCreationType_ { ImNodesLinkCreationType_Standard, ImNodesLinkCreationType_FromDetach }; // [SECTION] internal data structures // The object T must have the following interface: // // struct T // { // T(); // // int id; // }; template struct ImObjectPool { ImVector Pool; ImVector InUse; ImVector FreeList; ImGuiStorage IdMap; ImObjectPool() : Pool(), InUse(), FreeList(), IdMap() {} }; // Emulates std::optional using the sentinel value `INVALID_INDEX`. struct ImOptionalIndex { ImOptionalIndex() : _Index(INVALID_INDEX) {} ImOptionalIndex(const int value) : _Index(value) {} // Observers inline bool HasValue() const { return _Index != INVALID_INDEX; } inline int Value() const { IM_ASSERT(HasValue()); return _Index; } // Modifiers inline ImOptionalIndex& operator=(const int value) { _Index = value; return *this; } inline void Reset() { _Index = INVALID_INDEX; } inline bool operator==(const ImOptionalIndex& rhs) const { return _Index == rhs._Index; } inline bool operator==(const int rhs) const { return _Index == rhs; } inline bool operator!=(const ImOptionalIndex& rhs) const { return _Index != rhs._Index; } inline bool operator!=(const int rhs) const { return _Index != rhs; } static const int INVALID_INDEX = -1; private: int _Index; }; struct ImNodeData { int Id; ImVec2 Origin; // The node origin is in editor space ImRect TitleBarContentRect; ImRect Rect; struct { ImU32 Background, BackgroundHovered, BackgroundSelected, Outline, Titlebar, TitlebarHovered, TitlebarSelected; } ColorStyle; struct { float CornerRounding; ImVec2 Padding; float BorderThickness; } LayoutStyle; ImVector PinIndices; bool Draggable; ImNodeData(const int node_id) : Id(node_id), Origin(0.0f, 0.0f), TitleBarContentRect(), Rect(ImVec2(0.0f, 0.0f), ImVec2(0.0f, 0.0f)), ColorStyle(), LayoutStyle(), PinIndices(), Draggable(true) { } ~ImNodeData() { Id = INT_MIN; } }; struct ImPinData { int Id; int ParentNodeIdx; ImRect AttributeRect; ImNodesAttributeType Type; ImNodesPinShape Shape; ImVec2 Pos; // screen-space coordinates int Flags; struct { ImU32 Background, Hovered; } ColorStyle; ImPinData(const int pin_id) : Id(pin_id), ParentNodeIdx(), AttributeRect(), Type(ImNodesAttributeType_None), Shape(ImNodesPinShape_CircleFilled), Pos(), Flags(ImNodesAttributeFlags_None), ColorStyle() { } }; struct ImLinkData { int Id; int StartPinIdx, EndPinIdx; struct { ImU32 Base, Hovered, Selected; } ColorStyle; ImLinkData(const int link_id) : Id(link_id), StartPinIdx(), EndPinIdx(), ColorStyle() {} }; struct ImClickInteractionState { ImNodesClickInteractionType Type; struct { int StartPinIdx; ImOptionalIndex EndPinIdx; ImNodesLinkCreationType Type; } LinkCreation; struct { ImRect Rect; // Coordinates in grid space } BoxSelector; ImClickInteractionState() : Type(ImNodesClickInteractionType_None) {} }; struct ImNodesColElement { ImU32 Color; ImNodesCol Item; ImNodesColElement(const ImU32 c, const ImNodesCol s) : Color(c), Item(s) {} }; struct ImNodesStyleVarElement { ImNodesStyleVar Item; float FloatValue[2]; ImNodesStyleVarElement(const ImNodesStyleVar variable, const float value) : Item(variable) { FloatValue[0] = value; } ImNodesStyleVarElement(const ImNodesStyleVar variable, const ImVec2 value) : Item(variable) { FloatValue[0] = value.x; FloatValue[1] = value.y; } }; // [SECTION] global and editor context structs struct ImNodesEditorContext { ImObjectPool Nodes; ImObjectPool Pins; ImObjectPool Links; ImVector NodeDepthOrder; // ui related fields ImVec2 Panning; ImVec2 AutoPanningDelta; // Minimum and maximum extents of all content in grid space. Valid after final // ImNodes::EndNode() call. ImRect GridContentBounds; ImVector SelectedNodeIndices; ImVector SelectedLinkIndices; // Relative origins of selected nodes for snapping of dragged nodes ImVector SelectedNodeOffsets; // Offset of the primary node origin relative to the mouse cursor. ImVec2 PrimaryNodeOffset; ImClickInteractionState ClickInteraction; // Mini-map state set by MiniMap() bool MiniMapEnabled; ImNodesMiniMapLocation MiniMapLocation; float MiniMapSizeFraction; ImNodesMiniMapNodeHoveringCallback MiniMapNodeHoveringCallback; ImNodesMiniMapNodeHoveringCallbackUserData MiniMapNodeHoveringCallbackUserData; // Mini-map state set during EndNodeEditor() call ImRect MiniMapRectScreenSpace; ImRect MiniMapContentScreenSpace; float MiniMapScaling; ImNodesEditorContext() : Nodes(), Pins(), Links(), Panning(0.f, 0.f), SelectedNodeIndices(), SelectedLinkIndices(), SelectedNodeOffsets(), PrimaryNodeOffset(0.f, 0.f), ClickInteraction(), MiniMapEnabled(false), MiniMapSizeFraction(0.0f), MiniMapNodeHoveringCallback(NULL), MiniMapNodeHoveringCallbackUserData(NULL), MiniMapScaling(0.0f) { } }; struct ImNodesContext { ImNodesEditorContext* DefaultEditorCtx; ImNodesEditorContext* EditorCtx; // Canvas draw list and helper state ImDrawList* CanvasDrawList; ImGuiStorage NodeIdxToSubmissionIdx; ImVector NodeIdxSubmissionOrder; ImVector NodeIndicesOverlappingWithMouse; ImVector OccludedPinIndices; // Canvas extents ImVec2 CanvasOriginScreenSpace; ImRect CanvasRectScreenSpace; // Debug helpers ImNodesScope CurrentScope; // Configuration state ImNodesIO Io; ImNodesStyle Style; ImVector ColorModifierStack; ImVector StyleModifierStack; ImGuiTextBuffer TextBuffer; int CurrentAttributeFlags; ImVector AttributeFlagStack; // UI element state int CurrentNodeIdx; int CurrentPinIdx; int CurrentAttributeId; ImOptionalIndex HoveredNodeIdx; ImOptionalIndex HoveredLinkIdx; ImOptionalIndex HoveredPinIdx; ImOptionalIndex DeletedLinkIdx; ImOptionalIndex SnapLinkIdx; // Event helper state // TODO: this should be a part of a state machine, and not a member of the global struct. // Unclear what parts of the code this relates to. int ImNodesUIState; int ActiveAttributeId; bool ActiveAttribute; // ImGui::IO cache ImVec2 MousePos; bool LeftMouseClicked; bool LeftMouseReleased; bool AltMouseClicked; bool LeftMouseDragging; bool AltMouseDragging; float AltMouseScrollDelta; bool MultipleSelectModifier; }; namespace IMNODES_NAMESPACE { static inline ImNodesEditorContext& EditorContextGet() { // No editor context was set! Did you forget to call ImNodes::CreateContext()? IM_ASSERT(GImNodes->EditorCtx != NULL); return *GImNodes->EditorCtx; } // [SECTION] ObjectPool implementation template static inline int ObjectPoolFind(const ImObjectPool& objects, const int id) { const int index = objects.IdMap.GetInt(static_cast(id), -1); return index; } template static inline void ObjectPoolUpdate(ImObjectPool& objects) { for (int i = 0; i < objects.InUse.size(); ++i) { const int id = objects.Pool[i].Id; if (!objects.InUse[i] && objects.IdMap.GetInt(id, -1) == i) { objects.IdMap.SetInt(id, -1); objects.FreeList.push_back(i); (objects.Pool.Data + i)->~T(); } } } template<> inline void ObjectPoolUpdate(ImObjectPool& nodes) { for (int i = 0; i < nodes.InUse.size(); ++i) { if (nodes.InUse[i]) { nodes.Pool[i].PinIndices.clear(); } else { const int id = nodes.Pool[i].Id; if (nodes.IdMap.GetInt(id, -1) == i) { // Remove node idx form depth stack the first time we detect that this idx slot is // unused ImVector& depth_stack = EditorContextGet().NodeDepthOrder; const int* const elem = depth_stack.find(i); IM_ASSERT(elem != depth_stack.end()); depth_stack.erase(elem); nodes.IdMap.SetInt(id, -1); nodes.FreeList.push_back(i); (nodes.Pool.Data + i)->~ImNodeData(); } } } } template static inline void ObjectPoolReset(ImObjectPool& objects) { if (!objects.InUse.empty()) { memset(objects.InUse.Data, 0, objects.InUse.size_in_bytes()); } } template static inline int ObjectPoolFindOrCreateIndex(ImObjectPool& objects, const int id) { int index = objects.IdMap.GetInt(static_cast(id), -1); // Construct new object if (index == -1) { if (objects.FreeList.empty()) { index = objects.Pool.size(); IM_ASSERT(objects.Pool.size() == objects.InUse.size()); const int new_size = objects.Pool.size() + 1; objects.Pool.resize(new_size); objects.InUse.resize(new_size); } else { index = objects.FreeList.back(); objects.FreeList.pop_back(); } IM_PLACEMENT_NEW(objects.Pool.Data + index) T(id); objects.IdMap.SetInt(static_cast(id), index); } // Flag it as used objects.InUse[index] = true; return index; } template<> inline int ObjectPoolFindOrCreateIndex(ImObjectPool& nodes, const int node_id) { int node_idx = nodes.IdMap.GetInt(static_cast(node_id), -1); // Construct new node if (node_idx == -1) { if (nodes.FreeList.empty()) { node_idx = nodes.Pool.size(); IM_ASSERT(nodes.Pool.size() == nodes.InUse.size()); const int new_size = nodes.Pool.size() + 1; nodes.Pool.resize(new_size); nodes.InUse.resize(new_size); } else { node_idx = nodes.FreeList.back(); nodes.FreeList.pop_back(); } IM_PLACEMENT_NEW(nodes.Pool.Data + node_idx) ImNodeData(node_id); nodes.IdMap.SetInt(static_cast(node_id), node_idx); ImNodesEditorContext& editor = EditorContextGet(); editor.NodeDepthOrder.push_back(node_idx); } // Flag node as used nodes.InUse[node_idx] = true; return node_idx; } template static inline T& ObjectPoolFindOrCreateObject(ImObjectPool& objects, const int id) { const int index = ObjectPoolFindOrCreateIndex(objects, id); return objects.Pool[index]; } } // namespace IMNODES_NAMESPACE ================================================ FILE: Source/External/imgui_tools/implot/implot.cpp ================================================ // MIT License // Copyright (c) 2022 Evan Pezent // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // ImPlot v0.14 /* API BREAKING CHANGES ==================== Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix. Below is a change-log of API breaking changes only. If you are using one of the functions listed, expect to have to fix some code. When you are not sure about a old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all implot files. You can read releases logs https://github.com/epezent/implot/releases for more details. - 2022/06/19 (0.14) - The signature of ColormapScale has changed to accommodate a new ImPlotColormapScaleFlags parameter - 2022/06/17 (0.14) - **IMPORTANT** All PlotX functions now take an ImPlotX_Flags `flags` parameter. Where applicable, it is located before the existing `offset` and `stride` parameters. If you were providing offset and stride values, you will need to update your function call to include a `flags` value. If you fail to do this, you will likely see unexpected results or crashes without a compiler warning since these three are all default args. We apologize for the inconvenience, but this was a necessary evil. - PlotBarsH has been removed; use PlotBars + ImPlotBarsFlags_Horizontal instead - PlotErrorBarsH has been removed; use PlotErrorBars + ImPlotErrorBarsFlags_Horizontal - PlotHistogram/PlotHistogram2D signatures changed; `cumulative`, `density`, and `outliers` options now specified via ImPlotHistogramFlags - PlotPieChart signature changed; `normalize` option now specified via ImPlotPieChartFlags - PlotText signature changes; `vertical` option now specified via `ImPlotTextFlags_Vertical` - `PlotVLines` and `PlotHLines` replaced with `PlotInfLines` (+ ImPlotInfLinesFlags_Horizontal ) - arguments of ImPlotGetter have been reversed to be consistent with other API callbacks - SetupAxisScale + ImPlotScale have replaced ImPlotAxisFlags_LogScale and ImPlotAxisFlags_Time flags - ImPlotFormatters should now return an int indicating the size written - the signature of ImPlotGetter has been reversed so that void* user_data is the last argument and consistent with other callbacks - 2021/10/19 (0.13) - MAJOR API OVERHAUL! See #168 and #272 - TRIVIAL RENAME: - ImPlotLimits -> ImPlotRect - ImPlotYAxis_ -> ImAxis_ - SetPlotYAxis -> SetAxis - BeginDragDropTarget -> BeginDragDropTargetPlot - BeginDragDropSource -> BeginDragDropSourcePlot - ImPlotFlags_NoMousePos -> ImPlotFlags_NoMouseText - SetNextPlotLimits -> SetNextAxesLimits - SetMouseTextLocation -> SetupMouseText - SIGNATURE MODIFIED: - PixelsToPlot/PlotToPixels -> added optional X-Axis arg - GetPlotMousePos -> added optional X-Axis arg - GetPlotLimits -> added optional X-Axis arg - GetPlotSelection -> added optional X-Axis arg - DragLineX/Y/DragPoint -> now takes int id; removed labels (render with Annotation/Tag instead) - REPLACED: - IsPlotXAxisHovered/IsPlotXYAxisHovered -> IsAxisHovered(ImAxis) - BeginDragDropTargetX/BeginDragDropTargetY -> BeginDragDropTargetAxis(ImAxis) - BeginDragDropSourceX/BeginDragDropSourceY -> BeginDragDropSourceAxis(ImAxis) - ImPlotCol_XAxis, ImPlotCol_YAxis1, etc. -> ImPlotCol_AxisText (push/pop this around SetupAxis to style individual axes) - ImPlotCol_XAxisGrid, ImPlotCol_Y1AxisGrid -> ImPlotCol_AxisGrid (push/pop this around SetupAxis to style individual axes) - SetNextPlotLimitsX/Y -> SetNextAxisLimits(ImAxis) - LinkNextPlotLimits -> SetNextAxisLinks(ImAxis) - FitNextPlotAxes -> SetNextAxisToFit(ImAxis)/SetNextAxesToFit - SetLegendLocation -> SetupLegend - ImPlotFlags_NoHighlight -> ImPlotLegendFlags_NoHighlight - ImPlotOrientation -> ImPlotLegendFlags_Horizontal - Annotate -> Annotation - REMOVED: - GetPlotQuery, SetPlotQuery, IsPlotQueried -> use DragRect - SetNextPlotTicksX, SetNextPlotTicksY -> use SetupAxisTicks - SetNextPlotFormatX, SetNextPlotFormatY -> use SetupAxisFormat - AnnotateClamped -> use Annotation(bool clamp = true) - OBSOLETED: - BeginPlot (original signature) -> use simplified signature + Setup API - 2021/07/30 (0.12) - The offset argument of `PlotXG` functions was been removed. Implement offsetting in your getter callback instead. - 2021/03/08 (0.9) - SetColormap and PushColormap(ImVec4*) were removed. Use AddColormap for custom colormap support. LerpColormap was changed to SampleColormap. ShowColormapScale was changed to ColormapScale and requires additional arguments. - 2021/03/07 (0.9) - The signature of ShowColormapScale was modified to accept a ImVec2 size. - 2021/02/28 (0.9) - BeginLegendDragDropSource was changed to BeginDragDropSourceItem with a number of other drag and drop improvements. - 2021/01/18 (0.9) - The default behavior for opening context menus was change from double right-click to single right-click. ImPlotInputMap and related functions were moved to implot_internal.h due to its immaturity. - 2020/10/16 (0.8) - ImPlotStyleVar_InfoPadding was changed to ImPlotStyleVar_MousePosPadding - 2020/09/10 (0.8) - The single array versions of PlotLine, PlotScatter, PlotStems, and PlotShaded were given additional arguments for x-scale and x0. - 2020/09/07 (0.8) - Plotting functions which accept a custom getter function pointer have been post-fixed with a G (e.g. PlotLineG) - 2020/09/06 (0.7) - Several flags under ImPlotFlags and ImPlotAxisFlags were inverted (e.g. ImPlotFlags_Legend -> ImPlotFlags_NoLegend) so that the default flagset is simply 0. This more closely matches ImGui's style and makes it easier to enable non-default but commonly used flags (e.g. ImPlotAxisFlags_Time). - 2020/08/28 (0.5) - ImPlotMarker_ can no longer be combined with bitwise OR, |. This features caused unecessary slow-down, and almost no one used it. - 2020/08/25 (0.5) - ImPlotAxisFlags_Scientific was removed. Logarithmic axes automatically uses scientific notation. - 2020/08/17 (0.5) - PlotText was changed so that text is centered horizontally and vertically about the desired point. - 2020/08/16 (0.5) - An ImPlotContext must be explicitly created and destroyed now with `CreateContext` and `DestroyContext`. Previously, the context was statically initialized in this source file. - 2020/06/13 (0.4) - The flags `ImPlotAxisFlag_Adaptive` and `ImPlotFlags_Cull` were removed. Both are now done internally by default. - 2020/06/03 (0.3) - The signature and behavior of PlotPieChart was changed so that data with sum less than 1 can optionally be normalized. The label format can now be specified as well. - 2020/06/01 (0.3) - SetPalette was changed to `SetColormap` for consistency with other plotting libraries. `RestorePalette` was removed. Use `SetColormap(ImPlotColormap_Default)`. - 2020/05/31 (0.3) - Plot functions taking custom ImVec2* getters were removed. Use the ImPlotPoint* getter versions instead. - 2020/05/29 (0.3) - The signature of ImPlotLimits::Contains was changed to take two doubles instead of ImVec2 - 2020/05/16 (0.2) - All plotting functions were reverted to being prefixed with "Plot" to maintain a consistent VerbNoun style. `Plot` was split into `PlotLine` and `PlotScatter` (however, `PlotLine` can still be used to plot scatter points as `Plot` did before.). `Bar` is not `PlotBars`, to indicate that multiple bars will be plotted. - 2020/05/13 (0.2) - `ImMarker` was change to `ImPlotMarker` and `ImAxisFlags` was changed to `ImPlotAxisFlags`. - 2020/05/11 (0.2) - `ImPlotFlags_Selection` was changed to `ImPlotFlags_BoxSelect` - 2020/05/11 (0.2) - The namespace ImGui:: was replaced with ImPlot::. As a result, the following additional changes were made: - Functions that were prefixed or decorated with the word "Plot" have been truncated. E.g., `ImGui::PlotBars` is now just `ImPlot::Bar`. It should be fairly obvious what was what. - Some functions have been given names that would have otherwise collided with the ImGui namespace. This has been done to maintain a consistent style with ImGui. E.g., 'ImGui::PushPlotStyleVar` is now 'ImPlot::PushStyleVar'. - 2020/05/10 (0.2) - The following function/struct names were changes: - ImPlotRange -> ImPlotLimits - GetPlotRange() -> GetPlotLimits() - SetNextPlotRange -> SetNextPlotLimits - SetNextPlotRangeX -> SetNextPlotLimitsX - SetNextPlotRangeY -> SetNextPlotLimitsY - 2020/05/10 (0.2) - Plot queries are pixel based by default. Query rects that maintain relative plot position have been removed. This was done to support multi-y-axis. */ #define IMGUI_DEFINE_MATH_OPERATORS #include "implot.h" #include "implot_internal.h" #include // Support for pre-1.82 versions. Users on 1.82+ can use 0 (default) flags to mean "all corners" but in order to support older versions we are more explicit. #if (IMGUI_VERSION_NUM < 18102) && !defined(ImDrawFlags_RoundCornersAll) #define ImDrawFlags_RoundCornersAll ImDrawCornerFlags_All #endif // Support for pre-1.89.7 versions. #if (IMGUI_VERSION_NUM < 18966) #define ImGuiButtonFlags_AllowOverlap ImGuiButtonFlags_AllowItemOverlap #endif // Visual Studio warnings #ifdef _MSC_VER #pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen #endif // Clang/GCC warnings with -Weverything #if defined(__clang__) #pragma clang diagnostic ignored "-Wformat-nonliteral" // warning: format string is not a string literal #elif defined(__GNUC__) #pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked #endif // Global plot context #ifndef GImPlot ImPlotContext* GImPlot = nullptr; #endif //----------------------------------------------------------------------------- // Struct Implementations //----------------------------------------------------------------------------- ImPlotInputMap::ImPlotInputMap() { ImPlot::MapInputDefault(this); } ImPlotStyle::ImPlotStyle() { LineWeight = 1; Marker = ImPlotMarker_None; MarkerSize = 4; MarkerWeight = 1; FillAlpha = 1; ErrorBarSize = 5; ErrorBarWeight = 1.5f; DigitalBitHeight = 8; DigitalBitGap = 4; PlotBorderSize = 1; MinorAlpha = 0.25f; MajorTickLen = ImVec2(10,10); MinorTickLen = ImVec2(5,5); MajorTickSize = ImVec2(1,1); MinorTickSize = ImVec2(1,1); MajorGridSize = ImVec2(1,1); MinorGridSize = ImVec2(1,1); PlotPadding = ImVec2(10,10); LabelPadding = ImVec2(5,5); LegendPadding = ImVec2(10,10); LegendInnerPadding = ImVec2(5,5); LegendSpacing = ImVec2(5,0); MousePosPadding = ImVec2(10,10); AnnotationPadding = ImVec2(2,2); FitPadding = ImVec2(0,0); PlotDefaultSize = ImVec2(400,300); PlotMinSize = ImVec2(200,150); ImPlot::StyleColorsAuto(this); Colormap = ImPlotColormap_Deep; UseLocalTime = false; Use24HourClock = false; UseISO8601 = false; } //----------------------------------------------------------------------------- // Style //----------------------------------------------------------------------------- namespace ImPlot { const char* GetStyleColorName(ImPlotCol col) { static const char* col_names[ImPlotCol_COUNT] = { "Line", "Fill", "MarkerOutline", "MarkerFill", "ErrorBar", "FrameBg", "PlotBg", "PlotBorder", "LegendBg", "LegendBorder", "LegendText", "TitleText", "InlayText", "AxisText", "AxisGrid", "AxisTick", "AxisBg", "AxisBgHovered", "AxisBgActive", "Selection", "Crosshairs" }; return col_names[col]; } const char* GetMarkerName(ImPlotMarker marker) { switch (marker) { case ImPlotMarker_None: return "None"; case ImPlotMarker_Circle: return "Circle"; case ImPlotMarker_Square: return "Square"; case ImPlotMarker_Diamond: return "Diamond"; case ImPlotMarker_Up: return "Up"; case ImPlotMarker_Down: return "Down"; case ImPlotMarker_Left: return "Left"; case ImPlotMarker_Right: return "Right"; case ImPlotMarker_Cross: return "Cross"; case ImPlotMarker_Plus: return "Plus"; case ImPlotMarker_Asterisk: return "Asterisk"; default: return ""; } } ImVec4 GetAutoColor(ImPlotCol idx) { ImVec4 col(0,0,0,1); switch(idx) { case ImPlotCol_Line: return col; // these are plot dependent! case ImPlotCol_Fill: return col; // these are plot dependent! case ImPlotCol_MarkerOutline: return col; // these are plot dependent! case ImPlotCol_MarkerFill: return col; // these are plot dependent! case ImPlotCol_ErrorBar: return ImGui::GetStyleColorVec4(ImGuiCol_Text); case ImPlotCol_FrameBg: return ImGui::GetStyleColorVec4(ImGuiCol_FrameBg); case ImPlotCol_PlotBg: return ImGui::GetStyleColorVec4(ImGuiCol_WindowBg); case ImPlotCol_PlotBorder: return ImGui::GetStyleColorVec4(ImGuiCol_Border); case ImPlotCol_LegendBg: return ImGui::GetStyleColorVec4(ImGuiCol_PopupBg); case ImPlotCol_LegendBorder: return GetStyleColorVec4(ImPlotCol_PlotBorder); case ImPlotCol_LegendText: return GetStyleColorVec4(ImPlotCol_InlayText); case ImPlotCol_TitleText: return ImGui::GetStyleColorVec4(ImGuiCol_Text); case ImPlotCol_InlayText: return ImGui::GetStyleColorVec4(ImGuiCol_Text); case ImPlotCol_AxisText: return ImGui::GetStyleColorVec4(ImGuiCol_Text); case ImPlotCol_AxisGrid: return GetStyleColorVec4(ImPlotCol_AxisText) * ImVec4(1,1,1,0.25f); case ImPlotCol_AxisTick: return GetStyleColorVec4(ImPlotCol_AxisGrid); case ImPlotCol_AxisBg: return ImVec4(0,0,0,0); case ImPlotCol_AxisBgHovered: return ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered); case ImPlotCol_AxisBgActive: return ImGui::GetStyleColorVec4(ImGuiCol_ButtonActive); case ImPlotCol_Selection: return ImVec4(1,1,0,1); case ImPlotCol_Crosshairs: return GetStyleColorVec4(ImPlotCol_PlotBorder); default: return col; } } struct ImPlotStyleVarInfo { ImGuiDataType Type; ImU32 Count; ImU32 Offset; void* GetVarPtr(ImPlotStyle* style) const { return (void*)((unsigned char*)style + Offset); } }; static const ImPlotStyleVarInfo GPlotStyleVarInfo[] = { { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, LineWeight) }, // ImPlotStyleVar_LineWeight { ImGuiDataType_S32, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, Marker) }, // ImPlotStyleVar_Marker { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, MarkerSize) }, // ImPlotStyleVar_MarkerSize { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, MarkerWeight) }, // ImPlotStyleVar_MarkerWeight { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, FillAlpha) }, // ImPlotStyleVar_FillAlpha { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, ErrorBarSize) }, // ImPlotStyleVar_ErrorBarSize { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, ErrorBarWeight) }, // ImPlotStyleVar_ErrorBarWeight { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, DigitalBitHeight) }, // ImPlotStyleVar_DigitalBitHeight { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, DigitalBitGap) }, // ImPlotStyleVar_DigitalBitGap { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, PlotBorderSize) }, // ImPlotStyleVar_PlotBorderSize { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, MinorAlpha) }, // ImPlotStyleVar_MinorAlpha { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MajorTickLen) }, // ImPlotStyleVar_MajorTickLen { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MinorTickLen) }, // ImPlotStyleVar_MinorTickLen { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MajorTickSize) }, // ImPlotStyleVar_MajorTickSize { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MinorTickSize) }, // ImPlotStyleVar_MinorTickSize { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MajorGridSize) }, // ImPlotStyleVar_MajorGridSize { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MinorGridSize) }, // ImPlotStyleVar_MinorGridSize { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, PlotPadding) }, // ImPlotStyleVar_PlotPadding { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, LabelPadding) }, // ImPlotStyleVar_LabelPaddine { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, LegendPadding) }, // ImPlotStyleVar_LegendPadding { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, LegendInnerPadding) }, // ImPlotStyleVar_LegendInnerPadding { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, LegendSpacing) }, // ImPlotStyleVar_LegendSpacing { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MousePosPadding) }, // ImPlotStyleVar_MousePosPadding { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, AnnotationPadding) }, // ImPlotStyleVar_AnnotationPadding { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, FitPadding) }, // ImPlotStyleVar_FitPadding { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, PlotDefaultSize) }, // ImPlotStyleVar_PlotDefaultSize { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, PlotMinSize) } // ImPlotStyleVar_PlotMinSize }; static const ImPlotStyleVarInfo* GetPlotStyleVarInfo(ImPlotStyleVar idx) { IM_ASSERT(idx >= 0 && idx < ImPlotStyleVar_COUNT); IM_ASSERT(IM_ARRAYSIZE(GPlotStyleVarInfo) == ImPlotStyleVar_COUNT); return &GPlotStyleVarInfo[idx]; } //----------------------------------------------------------------------------- // Generic Helpers //----------------------------------------------------------------------------- void AddTextVertical(ImDrawList *DrawList, ImVec2 pos, ImU32 col, const char *text_begin, const char* text_end) { // the code below is based loosely on ImFont::RenderText if (!text_end) text_end = text_begin + strlen(text_begin); ImGuiContext& g = *GImGui; ImFont* font = g.Font; // Align to be pixel perfect pos.x = IM_FLOOR(pos.x); pos.y = IM_FLOOR(pos.y); const float scale = g.FontSize / font->FontSize; const char* s = text_begin; int chars_exp = (int)(text_end - s); int chars_rnd = 0; const int vtx_count_max = chars_exp * 4; const int idx_count_max = chars_exp * 6; DrawList->PrimReserve(idx_count_max, vtx_count_max); while (s < text_end) { unsigned int c = (unsigned int)*s; if (c < 0x80) { s += 1; } else { s += ImTextCharFromUtf8(&c, s, text_end); if (c == 0) // Malformed UTF-8? break; } const ImFontGlyph * glyph = font->FindGlyph((ImWchar)c); if (glyph == nullptr) { continue; } DrawList->PrimQuadUV(pos + ImVec2(glyph->Y0, -glyph->X0) * scale, pos + ImVec2(glyph->Y0, -glyph->X1) * scale, pos + ImVec2(glyph->Y1, -glyph->X1) * scale, pos + ImVec2(glyph->Y1, -glyph->X0) * scale, ImVec2(glyph->U0, glyph->V0), ImVec2(glyph->U1, glyph->V0), ImVec2(glyph->U1, glyph->V1), ImVec2(glyph->U0, glyph->V1), col); pos.y -= glyph->AdvanceX * scale; chars_rnd++; } // Give back unused vertices int chars_skp = chars_exp-chars_rnd; DrawList->PrimUnreserve(chars_skp*6, chars_skp*4); } void AddTextCentered(ImDrawList* DrawList, ImVec2 top_center, ImU32 col, const char* text_begin, const char* text_end) { float txt_ht = ImGui::GetTextLineHeight(); const char* title_end = ImGui::FindRenderedTextEnd(text_begin, text_end); ImVec2 text_size; float y = 0; while (const char* tmp = (const char*)memchr(text_begin, '\n', title_end-text_begin)) { text_size = ImGui::CalcTextSize(text_begin,tmp,true); DrawList->AddText(ImVec2(top_center.x - text_size.x * 0.5f, top_center.y+y),col,text_begin,tmp); text_begin = tmp + 1; y += txt_ht; } text_size = ImGui::CalcTextSize(text_begin,title_end,true); DrawList->AddText(ImVec2(top_center.x - text_size.x * 0.5f, top_center.y+y),col,text_begin,title_end); } double NiceNum(double x, bool round) { double f; double nf; int expv = (int)floor(ImLog10(x)); f = x / ImPow(10.0, (double)expv); if (round) if (f < 1.5) nf = 1; else if (f < 3) nf = 2; else if (f < 7) nf = 5; else nf = 10; else if (f <= 1) nf = 1; else if (f <= 2) nf = 2; else if (f <= 5) nf = 5; else nf = 10; return nf * ImPow(10.0, expv); } //----------------------------------------------------------------------------- // Context Utils //----------------------------------------------------------------------------- void SetImGuiContext(ImGuiContext* ctx) { ImGui::SetCurrentContext(ctx); } ImPlotContext* CreateContext() { ImPlotContext* ctx = IM_NEW(ImPlotContext)(); Initialize(ctx); if (GImPlot == nullptr) SetCurrentContext(ctx); return ctx; } void DestroyContext(ImPlotContext* ctx) { if (ctx == nullptr) ctx = GImPlot; if (GImPlot == ctx) SetCurrentContext(nullptr); IM_DELETE(ctx); } ImPlotContext* GetCurrentContext() { return GImPlot; } void SetCurrentContext(ImPlotContext* ctx) { GImPlot = ctx; } #define IMPLOT_APPEND_CMAP(name, qual) ctx->ColormapData.Append(#name, name, sizeof(name)/sizeof(ImU32), qual) #define IM_RGB(r,g,b) IM_COL32(r,g,b,255) void Initialize(ImPlotContext* ctx) { ResetCtxForNextPlot(ctx); ResetCtxForNextAlignedPlots(ctx); ResetCtxForNextSubplot(ctx); const ImU32 Deep[] = {4289753676, 4283598045, 4285048917, 4283584196, 4289950337, 4284512403, 4291005402, 4287401100, 4285839820, 4291671396 }; const ImU32 Dark[] = {4280031972, 4290281015, 4283084621, 4288892568, 4278222847, 4281597951, 4280833702, 4290740727, 4288256409 }; const ImU32 Pastel[] = {4289639675, 4293119411, 4291161036, 4293184478, 4289124862, 4291624959, 4290631909, 4293712637, 4294111986 }; const ImU32 Paired[] = {4293119554, 4290017311, 4287291314, 4281114675, 4288256763, 4280031971, 4285513725, 4278222847, 4292260554, 4288298346, 4288282623, 4280834481}; const ImU32 Viridis[] = {4283695428, 4285867080, 4287054913, 4287455029, 4287526954, 4287402273, 4286883874, 4285579076, 4283552122, 4280737725, 4280674301 }; const ImU32 Plasma[] = {4287039501, 4288480321, 4289200234, 4288941455, 4287638193, 4286072780, 4284638433, 4283139314, 4281771772, 4280667900, 4280416752 }; const ImU32 Hot[] = {4278190144, 4278190208, 4278190271, 4278190335, 4278206719, 4278223103, 4278239231, 4278255615, 4283826175, 4289396735, 4294967295 }; const ImU32 Cool[] = {4294967040, 4294960666, 4294954035, 4294947661, 4294941030, 4294934656, 4294928025, 4294921651, 4294915020, 4294908646, 4294902015 }; const ImU32 Pink[] = {4278190154, 4282532475, 4284308894, 4285690554, 4286879686, 4287870160, 4288794330, 4289651940, 4291685869, 4293392118, 4294967295 }; const ImU32 Jet[] = {4289331200, 4294901760, 4294923520, 4294945280, 4294967040, 4289396565, 4283826090, 4278255615, 4278233855, 4278212095, 4278190335 }; const ImU32 Twilight[] = {IM_RGB(226,217,226),IM_RGB(166,191,202),IM_RGB(109,144,192),IM_RGB(95,88,176),IM_RGB(83,30,124),IM_RGB(47,20,54),IM_RGB(100,25,75),IM_RGB(159,60,80),IM_RGB(192,117,94),IM_RGB(208,179,158),IM_RGB(226,217,226)}; const ImU32 RdBu[] = {IM_RGB(103,0,31),IM_RGB(178,24,43),IM_RGB(214,96,77),IM_RGB(244,165,130),IM_RGB(253,219,199),IM_RGB(247,247,247),IM_RGB(209,229,240),IM_RGB(146,197,222),IM_RGB(67,147,195),IM_RGB(33,102,172),IM_RGB(5,48,97)}; const ImU32 BrBG[] = {IM_RGB(84,48,5),IM_RGB(140,81,10),IM_RGB(191,129,45),IM_RGB(223,194,125),IM_RGB(246,232,195),IM_RGB(245,245,245),IM_RGB(199,234,229),IM_RGB(128,205,193),IM_RGB(53,151,143),IM_RGB(1,102,94),IM_RGB(0,60,48)}; const ImU32 PiYG[] = {IM_RGB(142,1,82),IM_RGB(197,27,125),IM_RGB(222,119,174),IM_RGB(241,182,218),IM_RGB(253,224,239),IM_RGB(247,247,247),IM_RGB(230,245,208),IM_RGB(184,225,134),IM_RGB(127,188,65),IM_RGB(77,146,33),IM_RGB(39,100,25)}; const ImU32 Spectral[] = {IM_RGB(158,1,66),IM_RGB(213,62,79),IM_RGB(244,109,67),IM_RGB(253,174,97),IM_RGB(254,224,139),IM_RGB(255,255,191),IM_RGB(230,245,152),IM_RGB(171,221,164),IM_RGB(102,194,165),IM_RGB(50,136,189),IM_RGB(94,79,162)}; const ImU32 Greys[] = {IM_COL32_WHITE, IM_COL32_BLACK }; IMPLOT_APPEND_CMAP(Deep, true); IMPLOT_APPEND_CMAP(Dark, true); IMPLOT_APPEND_CMAP(Pastel, true); IMPLOT_APPEND_CMAP(Paired, true); IMPLOT_APPEND_CMAP(Viridis, false); IMPLOT_APPEND_CMAP(Plasma, false); IMPLOT_APPEND_CMAP(Hot, false); IMPLOT_APPEND_CMAP(Cool, false); IMPLOT_APPEND_CMAP(Pink, false); IMPLOT_APPEND_CMAP(Jet, false); IMPLOT_APPEND_CMAP(Twilight, false); IMPLOT_APPEND_CMAP(RdBu, false); IMPLOT_APPEND_CMAP(BrBG, false); IMPLOT_APPEND_CMAP(PiYG, false); IMPLOT_APPEND_CMAP(Spectral, false); IMPLOT_APPEND_CMAP(Greys, false); } void ResetCtxForNextPlot(ImPlotContext* ctx) { // end child window if it was made if (ctx->ChildWindowMade) ImGui::EndChild(); ctx->ChildWindowMade = false; // reset the next plot/item data ctx->NextPlotData.Reset(); ctx->NextItemData.Reset(); // reset labels ctx->Annotations.Reset(); ctx->Tags.Reset(); // reset extents/fit ctx->OpenContextThisFrame = false; // reset digital plot items count ctx->DigitalPlotItemCnt = 0; ctx->DigitalPlotOffset = 0; // nullify plot ctx->CurrentPlot = nullptr; ctx->CurrentItem = nullptr; ctx->PreviousItem = nullptr; } void ResetCtxForNextAlignedPlots(ImPlotContext* ctx) { ctx->CurrentAlignmentH = nullptr; ctx->CurrentAlignmentV = nullptr; } void ResetCtxForNextSubplot(ImPlotContext* ctx) { ctx->CurrentSubplot = nullptr; ctx->CurrentAlignmentH = nullptr; ctx->CurrentAlignmentV = nullptr; } //----------------------------------------------------------------------------- // Plot Utils //----------------------------------------------------------------------------- ImPlotPlot* GetPlot(const char* title) { ImGuiWindow* Window = GImGui->CurrentWindow; const ImGuiID ID = Window->GetID(title); return GImPlot->Plots.GetByKey(ID); } ImPlotPlot* GetCurrentPlot() { return GImPlot->CurrentPlot; } void BustPlotCache() { ImPlotContext& gp = *GImPlot; gp.Plots.Clear(); gp.Subplots.Clear(); } //----------------------------------------------------------------------------- // Legend Utils //----------------------------------------------------------------------------- ImVec2 GetLocationPos(const ImRect& outer_rect, const ImVec2& inner_size, ImPlotLocation loc, const ImVec2& pad) { ImVec2 pos; if (ImHasFlag(loc, ImPlotLocation_West) && !ImHasFlag(loc, ImPlotLocation_East)) pos.x = outer_rect.Min.x + pad.x; else if (!ImHasFlag(loc, ImPlotLocation_West) && ImHasFlag(loc, ImPlotLocation_East)) pos.x = outer_rect.Max.x - pad.x - inner_size.x; else pos.x = outer_rect.GetCenter().x - inner_size.x * 0.5f; // legend reference point y if (ImHasFlag(loc, ImPlotLocation_North) && !ImHasFlag(loc, ImPlotLocation_South)) pos.y = outer_rect.Min.y + pad.y; else if (!ImHasFlag(loc, ImPlotLocation_North) && ImHasFlag(loc, ImPlotLocation_South)) pos.y = outer_rect.Max.y - pad.y - inner_size.y; else pos.y = outer_rect.GetCenter().y - inner_size.y * 0.5f; pos.x = IM_ROUND(pos.x); pos.y = IM_ROUND(pos.y); return pos; } ImVec2 CalcLegendSize(ImPlotItemGroup& items, const ImVec2& pad, const ImVec2& spacing, bool vertical) { // vars const int nItems = items.GetLegendCount(); const float txt_ht = ImGui::GetTextLineHeight(); const float icon_size = txt_ht; // get label max width float max_label_width = 0; float sum_label_width = 0; for (int i = 0; i < nItems; ++i) { const char* label = items.GetLegendLabel(i); const float label_width = ImGui::CalcTextSize(label, nullptr, true).x; max_label_width = label_width > max_label_width ? label_width : max_label_width; sum_label_width += label_width; } // calc legend size const ImVec2 legend_size = vertical ? ImVec2(pad.x * 2 + icon_size + max_label_width, pad.y * 2 + nItems * txt_ht + (nItems - 1) * spacing.y) : ImVec2(pad.x * 2 + icon_size * nItems + sum_label_width + (nItems - 1) * spacing.x, pad.y * 2 + txt_ht); return legend_size; } int LegendSortingComp(const void* _a, const void* _b) { ImPlotItemGroup* items = GImPlot->SortItems; const int a = *(const int*)_a; const int b = *(const int*)_b; const char* label_a = items->GetLegendLabel(a); const char* label_b = items->GetLegendLabel(b); return strcmp(label_a,label_b); } bool ShowLegendEntries(ImPlotItemGroup& items, const ImRect& legend_bb, bool hovered, const ImVec2& pad, const ImVec2& spacing, bool vertical, ImDrawList& DrawList) { // vars const float txt_ht = ImGui::GetTextLineHeight(); const float icon_size = txt_ht; const float icon_shrink = 2; ImU32 col_txt = GetStyleColorU32(ImPlotCol_LegendText); ImU32 col_txt_dis = ImAlphaU32(col_txt, 0.25f); // render each legend item float sum_label_width = 0; bool any_item_hovered = false; const int num_items = items.GetLegendCount(); if (num_items < 1) return hovered; // build render order ImPlotContext& gp = *GImPlot; ImVector& indices = gp.TempInt1; indices.resize(num_items); for (int i = 0; i < num_items; ++i) indices[i] = i; if (ImHasFlag(items.Legend.Flags, ImPlotLegendFlags_Sort) && num_items > 1) { gp.SortItems = &items; qsort(indices.Data, num_items, sizeof(int), LegendSortingComp); } // render for (int i = 0; i < num_items; ++i) { const int idx = indices[i]; ImPlotItem* item = items.GetLegendItem(idx); const char* label = items.GetLegendLabel(idx); const float label_width = ImGui::CalcTextSize(label, nullptr, true).x; const ImVec2 top_left = vertical ? legend_bb.Min + pad + ImVec2(0, i * (txt_ht + spacing.y)) : legend_bb.Min + pad + ImVec2(i * (icon_size + spacing.x) + sum_label_width, 0); sum_label_width += label_width; ImRect icon_bb; icon_bb.Min = top_left + ImVec2(icon_shrink,icon_shrink); icon_bb.Max = top_left + ImVec2(icon_size - icon_shrink, icon_size - icon_shrink); ImRect label_bb; label_bb.Min = top_left; label_bb.Max = top_left + ImVec2(label_width + icon_size, icon_size); ImU32 col_txt_hl; ImU32 col_item = ImAlphaU32(item->Color,1); ImRect button_bb(icon_bb.Min, label_bb.Max); ImGui::KeepAliveID(item->ID); bool item_hov = false; bool item_hld = false; bool item_clk = ImHasFlag(items.Legend.Flags, ImPlotLegendFlags_NoButtons) ? false : ImGui::ButtonBehavior(button_bb, item->ID, &item_hov, &item_hld); if (item_clk) item->Show = !item->Show; const bool can_hover = (item_hov) && (!ImHasFlag(items.Legend.Flags, ImPlotLegendFlags_NoHighlightItem) || !ImHasFlag(items.Legend.Flags, ImPlotLegendFlags_NoHighlightAxis)); if (can_hover) { item->LegendHoverRect.Min = icon_bb.Min; item->LegendHoverRect.Max = label_bb.Max; item->LegendHovered = true; col_txt_hl = ImMixU32(col_txt, col_item, 64); any_item_hovered = true; } else { col_txt_hl = ImGui::GetColorU32(col_txt); } ImU32 col_icon; if (item_hld) col_icon = item->Show ? ImAlphaU32(col_item,0.5f) : ImGui::GetColorU32(ImGuiCol_TextDisabled, 0.5f); else if (item_hov) col_icon = item->Show ? ImAlphaU32(col_item,0.75f) : ImGui::GetColorU32(ImGuiCol_TextDisabled, 0.75f); else col_icon = item->Show ? col_item : col_txt_dis; DrawList.AddRectFilled(icon_bb.Min, icon_bb.Max, col_icon); const char* text_display_end = ImGui::FindRenderedTextEnd(label, nullptr); if (label != text_display_end) DrawList.AddText(top_left + ImVec2(icon_size, 0), item->Show ? col_txt_hl : col_txt_dis, label, text_display_end); } return hovered && !any_item_hovered; } //----------------------------------------------------------------------------- // Locators //----------------------------------------------------------------------------- static const float TICK_FILL_X = 0.8f; static const float TICK_FILL_Y = 1.0f; void Locator_Default(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data) { if (range.Min == range.Max) return; const int nMinor = 10; const int nMajor = ImMax(2, (int)IM_ROUND(pixels / (vertical ? 300.0f : 400.0f))); const double nice_range = NiceNum(range.Size() * 0.99, false); const double interval = NiceNum(nice_range / (nMajor - 1), true); const double graphmin = floor(range.Min / interval) * interval; const double graphmax = ceil(range.Max / interval) * interval; bool first_major_set = false; int first_major_idx = 0; const int idx0 = ticker.TickCount(); // ticker may have user custom ticks ImVec2 total_size(0,0); for (double major = graphmin; major < graphmax + 0.5 * interval; major += interval) { // is this zero? combat zero formatting issues if (major-interval < 0 && major+interval > 0) major = 0; if (range.Contains(major)) { if (!first_major_set) { first_major_idx = ticker.TickCount(); first_major_set = true; } total_size += ticker.AddTick(major, true, 0, true, formatter, formatter_data).LabelSize; } for (int i = 1; i < nMinor; ++i) { double minor = major + i * interval / nMinor; if (range.Contains(minor)) { total_size += ticker.AddTick(minor, false, 0, true, formatter, formatter_data).LabelSize; } } } // prune if necessary if ((!vertical && total_size.x > pixels*TICK_FILL_X) || (vertical && total_size.y > pixels*TICK_FILL_Y)) { for (int i = first_major_idx-1; i >= idx0; i -= 2) ticker.Ticks[i].ShowLabel = false; for (int i = first_major_idx+1; i < ticker.TickCount(); i += 2) ticker.Ticks[i].ShowLabel = false; } } bool CalcLogarithmicExponents(const ImPlotRange& range, float pix, bool vertical, int& exp_min, int& exp_max, int& exp_step) { if (range.Min * range.Max > 0) { const int nMajor = vertical ? ImMax(2, (int)IM_ROUND(pix * 0.02f)) : ImMax(2, (int)IM_ROUND(pix * 0.01f)); // TODO: magic numbers double log_min = ImLog10(ImAbs(range.Min)); double log_max = ImLog10(ImAbs(range.Max)); double log_a = ImMin(log_min,log_max); double log_b = ImMax(log_min,log_max); exp_step = ImMax(1,(int)(log_b - log_a) / nMajor); exp_min = (int)log_a; exp_max = (int)log_b; if (exp_step != 1) { while(exp_step % 3 != 0) exp_step++; // make step size multiple of three while(exp_min % exp_step != 0) exp_min--; // decrease exp_min until exp_min + N * exp_step will be 0 } return true; } return false; } void AddTicksLogarithmic(const ImPlotRange& range, int exp_min, int exp_max, int exp_step, ImPlotTicker& ticker, ImPlotFormatter formatter, void* data) { const double sign = ImSign(range.Max); for (int e = exp_min - exp_step; e < (exp_max + exp_step); e += exp_step) { double major1 = sign*ImPow(10, (double)(e)); double major2 = sign*ImPow(10, (double)(e + 1)); double interval = (major2 - major1) / 9; if (major1 >= (range.Min - DBL_EPSILON) && major1 <= (range.Max + DBL_EPSILON)) ticker.AddTick(major1, true, 0, true, formatter, data); for (int j = 0; j < exp_step; ++j) { major1 = sign*ImPow(10, (double)(e+j)); major2 = sign*ImPow(10, (double)(e+j+1)); interval = (major2 - major1) / 9; for (int i = 1; i < (9 + (int)(j < (exp_step - 1))); ++i) { double minor = major1 + i * interval; if (minor >= (range.Min - DBL_EPSILON) && minor <= (range.Max + DBL_EPSILON)) ticker.AddTick(minor, false, 0, false, formatter, data); } } } } void Locator_Log10(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data) { int exp_min, exp_max, exp_step; if (CalcLogarithmicExponents(range, pixels, vertical, exp_min, exp_max, exp_step)) AddTicksLogarithmic(range, exp_min, exp_max, exp_step, ticker, formatter, formatter_data); } float CalcSymLogPixel(double plt, const ImPlotRange& range, float pixels) { double scaleToPixels = pixels / range.Size(); double scaleMin = TransformForward_SymLog(range.Min,nullptr); double scaleMax = TransformForward_SymLog(range.Max,nullptr); double s = TransformForward_SymLog(plt, nullptr); double t = (s - scaleMin) / (scaleMax - scaleMin); plt = range.Min + range.Size() * t; return (float)(0 + scaleToPixels * (plt - range.Min)); } void Locator_SymLog(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data) { if (range.Min >= -1 && range.Max <= 1) { Locator_Default(ticker, range, pixels, vertical, formatter, formatter_data); } else if (range.Min * range.Max < 0) { // cross zero const float pix_min = 0; const float pix_max = pixels; const float pix_p1 = CalcSymLogPixel(1, range, pixels); const float pix_n1 = CalcSymLogPixel(-1, range, pixels); int exp_min_p, exp_max_p, exp_step_p; int exp_min_n, exp_max_n, exp_step_n; CalcLogarithmicExponents(ImPlotRange(1,range.Max), ImAbs(pix_max-pix_p1),vertical,exp_min_p,exp_max_p,exp_step_p); CalcLogarithmicExponents(ImPlotRange(range.Min,-1),ImAbs(pix_n1-pix_min),vertical,exp_min_n,exp_max_n,exp_step_n); int exp_step = ImMax(exp_step_n, exp_step_p); ticker.AddTick(0,true,0,true,formatter,formatter_data); AddTicksLogarithmic(ImPlotRange(1,range.Max), exp_min_p,exp_max_p,exp_step,ticker,formatter,formatter_data); AddTicksLogarithmic(ImPlotRange(range.Min,-1),exp_min_n,exp_max_n,exp_step,ticker,formatter,formatter_data); } else { Locator_Log10(ticker, range, pixels, vertical, formatter, formatter_data); } } void AddTicksCustom(const double* values, const char* const labels[], int n, ImPlotTicker& ticker, ImPlotFormatter formatter, void* data) { for (int i = 0; i < n; ++i) { if (labels != nullptr) ticker.AddTick(values[i], false, 0, true, labels[i]); else ticker.AddTick(values[i], false, 0, true, formatter, data); } } //----------------------------------------------------------------------------- // Time Ticks and Utils //----------------------------------------------------------------------------- // this may not be thread safe? static const double TimeUnitSpans[ImPlotTimeUnit_COUNT] = { 0.000001, 0.001, 1, 60, 3600, 86400, 2629800, 31557600 }; inline ImPlotTimeUnit GetUnitForRange(double range) { static double cutoffs[ImPlotTimeUnit_COUNT] = {0.001, 1, 60, 3600, 86400, 2629800, 31557600, IMPLOT_MAX_TIME}; for (int i = 0; i < ImPlotTimeUnit_COUNT; ++i) { if (range <= cutoffs[i]) return (ImPlotTimeUnit)i; } return ImPlotTimeUnit_Yr; } inline int LowerBoundStep(int max_divs, const int* divs, const int* step, int size) { if (max_divs < divs[0]) return 0; for (int i = 1; i < size; ++i) { if (max_divs < divs[i]) return step[i-1]; } return step[size-1]; } inline int GetTimeStep(int max_divs, ImPlotTimeUnit unit) { if (unit == ImPlotTimeUnit_Ms || unit == ImPlotTimeUnit_Us) { static const int step[] = {500,250,200,100,50,25,20,10,5,2,1}; static const int divs[] = {2,4,5,10,20,40,50,100,200,500,1000}; return LowerBoundStep(max_divs, divs, step, 11); } if (unit == ImPlotTimeUnit_S || unit == ImPlotTimeUnit_Min) { static const int step[] = {30,15,10,5,1}; static const int divs[] = {2,4,6,12,60}; return LowerBoundStep(max_divs, divs, step, 5); } else if (unit == ImPlotTimeUnit_Hr) { static const int step[] = {12,6,3,2,1}; static const int divs[] = {2,4,8,12,24}; return LowerBoundStep(max_divs, divs, step, 5); } else if (unit == ImPlotTimeUnit_Day) { static const int step[] = {14,7,2,1}; static const int divs[] = {2,4,14,28}; return LowerBoundStep(max_divs, divs, step, 4); } else if (unit == ImPlotTimeUnit_Mo) { static const int step[] = {6,3,2,1}; static const int divs[] = {2,4,6,12}; return LowerBoundStep(max_divs, divs, step, 4); } return 0; } ImPlotTime MkGmtTime(struct tm *ptm) { ImPlotTime t; #ifdef _WIN32 t.S = _mkgmtime(ptm); #else t.S = timegm(ptm); #endif if (t.S < 0) t.S = 0; return t; } tm* GetGmtTime(const ImPlotTime& t, tm* ptm) { #ifdef _WIN32 if (gmtime_s(ptm, &t.S) == 0) return ptm; else return nullptr; #else return gmtime_r(&t.S, ptm); #endif } ImPlotTime MkLocTime(struct tm *ptm) { ImPlotTime t; t.S = mktime(ptm); if (t.S < 0) t.S = 0; return t; } tm* GetLocTime(const ImPlotTime& t, tm* ptm) { #ifdef _WIN32 if (localtime_s(ptm, &t.S) == 0) return ptm; else return nullptr; #else return localtime_r(&t.S, ptm); #endif } inline ImPlotTime MkTime(struct tm *ptm) { if (GetStyle().UseLocalTime) return MkLocTime(ptm); else return MkGmtTime(ptm); } inline tm* GetTime(const ImPlotTime& t, tm* ptm) { if (GetStyle().UseLocalTime) return GetLocTime(t,ptm); else return GetGmtTime(t,ptm); } ImPlotTime MakeTime(int year, int month, int day, int hour, int min, int sec, int us) { tm& Tm = GImPlot->Tm; int yr = year - 1900; if (yr < 0) yr = 0; sec = sec + us / 1000000; us = us % 1000000; Tm.tm_sec = sec; Tm.tm_min = min; Tm.tm_hour = hour; Tm.tm_mday = day; Tm.tm_mon = month; Tm.tm_year = yr; ImPlotTime t = MkTime(&Tm); t.Us = us; return t; } int GetYear(const ImPlotTime& t) { tm& Tm = GImPlot->Tm; GetTime(t, &Tm); return Tm.tm_year + 1900; } ImPlotTime AddTime(const ImPlotTime& t, ImPlotTimeUnit unit, int count) { tm& Tm = GImPlot->Tm; ImPlotTime t_out = t; switch(unit) { case ImPlotTimeUnit_Us: t_out.Us += count; break; case ImPlotTimeUnit_Ms: t_out.Us += count * 1000; break; case ImPlotTimeUnit_S: t_out.S += count; break; case ImPlotTimeUnit_Min: t_out.S += count * 60; break; case ImPlotTimeUnit_Hr: t_out.S += count * 3600; break; case ImPlotTimeUnit_Day: t_out.S += count * 86400; break; case ImPlotTimeUnit_Mo: for (int i = 0; i < abs(count); ++i) { GetTime(t_out, &Tm); if (count > 0) t_out.S += 86400 * GetDaysInMonth(Tm.tm_year + 1900, Tm.tm_mon); else if (count < 0) t_out.S -= 86400 * GetDaysInMonth(Tm.tm_year + 1900 - (Tm.tm_mon == 0 ? 1 : 0), Tm.tm_mon == 0 ? 11 : Tm.tm_mon - 1); // NOT WORKING } break; case ImPlotTimeUnit_Yr: for (int i = 0; i < abs(count); ++i) { if (count > 0) t_out.S += 86400 * (365 + (int)IsLeapYear(GetYear(t_out))); else if (count < 0) t_out.S -= 86400 * (365 + (int)IsLeapYear(GetYear(t_out) - 1)); // this is incorrect if leap year and we are past Feb 28 } break; default: break; } t_out.RollOver(); return t_out; } ImPlotTime FloorTime(const ImPlotTime& t, ImPlotTimeUnit unit) { ImPlotContext& gp = *GImPlot; GetTime(t, &gp.Tm); switch (unit) { case ImPlotTimeUnit_S: return ImPlotTime(t.S, 0); case ImPlotTimeUnit_Ms: return ImPlotTime(t.S, (t.Us / 1000) * 1000); case ImPlotTimeUnit_Us: return t; case ImPlotTimeUnit_Yr: gp.Tm.tm_mon = 0; // fall-through case ImPlotTimeUnit_Mo: gp.Tm.tm_mday = 1; // fall-through case ImPlotTimeUnit_Day: gp.Tm.tm_hour = 0; // fall-through case ImPlotTimeUnit_Hr: gp.Tm.tm_min = 0; // fall-through case ImPlotTimeUnit_Min: gp.Tm.tm_sec = 0; break; default: return t; } return MkTime(&gp.Tm); } ImPlotTime CeilTime(const ImPlotTime& t, ImPlotTimeUnit unit) { return AddTime(FloorTime(t, unit), unit, 1); } ImPlotTime RoundTime(const ImPlotTime& t, ImPlotTimeUnit unit) { ImPlotTime t1 = FloorTime(t, unit); ImPlotTime t2 = AddTime(t1,unit,1); if (t1.S == t2.S) return t.Us - t1.Us < t2.Us - t.Us ? t1 : t2; return t.S - t1.S < t2.S - t.S ? t1 : t2; } ImPlotTime CombineDateTime(const ImPlotTime& date_part, const ImPlotTime& tod_part) { ImPlotContext& gp = *GImPlot; tm& Tm = gp.Tm; GetTime(date_part, &gp.Tm); int y = Tm.tm_year; int m = Tm.tm_mon; int d = Tm.tm_mday; GetTime(tod_part, &gp.Tm); Tm.tm_year = y; Tm.tm_mon = m; Tm.tm_mday = d; ImPlotTime t = MkTime(&Tm); t.Us = tod_part.Us; return t; } // TODO: allow users to define these static const char* MONTH_NAMES[] = {"January","February","March","April","May","June","July","August","September","October","November","December"}; static const char* WD_ABRVS[] = {"Su","Mo","Tu","We","Th","Fr","Sa"}; static const char* MONTH_ABRVS[] = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"}; int FormatTime(const ImPlotTime& t, char* buffer, int size, ImPlotTimeFmt fmt, bool use_24_hr_clk) { tm& Tm = GImPlot->Tm; GetTime(t, &Tm); const int us = t.Us % 1000; const int ms = t.Us / 1000; const int sec = Tm.tm_sec; const int min = Tm.tm_min; if (use_24_hr_clk) { const int hr = Tm.tm_hour; switch(fmt) { case ImPlotTimeFmt_Us: return ImFormatString(buffer, size, ".%03d %03d", ms, us); case ImPlotTimeFmt_SUs: return ImFormatString(buffer, size, ":%02d.%03d %03d", sec, ms, us); case ImPlotTimeFmt_SMs: return ImFormatString(buffer, size, ":%02d.%03d", sec, ms); case ImPlotTimeFmt_S: return ImFormatString(buffer, size, ":%02d", sec); case ImPlotTimeFmt_MinSMs: return ImFormatString(buffer, size, ":%02d:%02d.%03d", min, sec, ms); case ImPlotTimeFmt_HrMinSMs: return ImFormatString(buffer, size, "%02d:%02d:%02d.%03d", hr, min, sec, ms); case ImPlotTimeFmt_HrMinS: return ImFormatString(buffer, size, "%02d:%02d:%02d", hr, min, sec); case ImPlotTimeFmt_HrMin: return ImFormatString(buffer, size, "%02d:%02d", hr, min); case ImPlotTimeFmt_Hr: return ImFormatString(buffer, size, "%02d:00", hr); default: return 0; } } else { const char* ap = Tm.tm_hour < 12 ? "am" : "pm"; const int hr = (Tm.tm_hour == 0 || Tm.tm_hour == 12) ? 12 : Tm.tm_hour % 12; switch(fmt) { case ImPlotTimeFmt_Us: return ImFormatString(buffer, size, ".%03d %03d", ms, us); case ImPlotTimeFmt_SUs: return ImFormatString(buffer, size, ":%02d.%03d %03d", sec, ms, us); case ImPlotTimeFmt_SMs: return ImFormatString(buffer, size, ":%02d.%03d", sec, ms); case ImPlotTimeFmt_S: return ImFormatString(buffer, size, ":%02d", sec); case ImPlotTimeFmt_MinSMs: return ImFormatString(buffer, size, ":%02d:%02d.%03d", min, sec, ms); case ImPlotTimeFmt_HrMinSMs: return ImFormatString(buffer, size, "%d:%02d:%02d.%03d%s", hr, min, sec, ms, ap); case ImPlotTimeFmt_HrMinS: return ImFormatString(buffer, size, "%d:%02d:%02d%s", hr, min, sec, ap); case ImPlotTimeFmt_HrMin: return ImFormatString(buffer, size, "%d:%02d%s", hr, min, ap); case ImPlotTimeFmt_Hr: return ImFormatString(buffer, size, "%d%s", hr, ap); default: return 0; } } } int FormatDate(const ImPlotTime& t, char* buffer, int size, ImPlotDateFmt fmt, bool use_iso_8601) { tm& Tm = GImPlot->Tm; GetTime(t, &Tm); const int day = Tm.tm_mday; const int mon = Tm.tm_mon + 1; const int year = Tm.tm_year + 1900; const int yr = year % 100; if (use_iso_8601) { switch (fmt) { case ImPlotDateFmt_DayMo: return ImFormatString(buffer, size, "--%02d-%02d", mon, day); case ImPlotDateFmt_DayMoYr: return ImFormatString(buffer, size, "%d-%02d-%02d", year, mon, day); case ImPlotDateFmt_MoYr: return ImFormatString(buffer, size, "%d-%02d", year, mon); case ImPlotDateFmt_Mo: return ImFormatString(buffer, size, "--%02d", mon); case ImPlotDateFmt_Yr: return ImFormatString(buffer, size, "%d", year); default: return 0; } } else { switch (fmt) { case ImPlotDateFmt_DayMo: return ImFormatString(buffer, size, "%d/%d", mon, day); case ImPlotDateFmt_DayMoYr: return ImFormatString(buffer, size, "%d/%d/%02d", mon, day, yr); case ImPlotDateFmt_MoYr: return ImFormatString(buffer, size, "%s %d", MONTH_ABRVS[Tm.tm_mon], year); case ImPlotDateFmt_Mo: return ImFormatString(buffer, size, "%s", MONTH_ABRVS[Tm.tm_mon]); case ImPlotDateFmt_Yr: return ImFormatString(buffer, size, "%d", year); default: return 0; } } } int FormatDateTime(const ImPlotTime& t, char* buffer, int size, ImPlotDateTimeSpec fmt) { int written = 0; if (fmt.Date != ImPlotDateFmt_None) written += FormatDate(t, buffer, size, fmt.Date, fmt.UseISO8601); if (fmt.Time != ImPlotTimeFmt_None) { if (fmt.Date != ImPlotDateFmt_None) buffer[written++] = ' '; written += FormatTime(t, &buffer[written], size - written, fmt.Time, fmt.Use24HourClock); } return written; } inline float GetDateTimeWidth(ImPlotDateTimeSpec fmt) { static const ImPlotTime t_max_width = MakeTime(2888, 12, 22, 12, 58, 58, 888888); // best guess at time that maximizes pixel width char buffer[32]; FormatDateTime(t_max_width, buffer, 32, fmt); return ImGui::CalcTextSize(buffer).x; } inline bool TimeLabelSame(const char* l1, const char* l2) { size_t len1 = strlen(l1); size_t len2 = strlen(l2); size_t n = len1 < len2 ? len1 : len2; return strcmp(l1 + len1 - n, l2 + len2 - n) == 0; } static const ImPlotDateTimeSpec TimeFormatLevel0[ImPlotTimeUnit_COUNT] = { ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_Us), ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_SMs), ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_S), ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_HrMin), ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_Hr), ImPlotDateTimeSpec(ImPlotDateFmt_DayMo, ImPlotTimeFmt_None), ImPlotDateTimeSpec(ImPlotDateFmt_Mo, ImPlotTimeFmt_None), ImPlotDateTimeSpec(ImPlotDateFmt_Yr, ImPlotTimeFmt_None) }; static const ImPlotDateTimeSpec TimeFormatLevel1[ImPlotTimeUnit_COUNT] = { ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_HrMin), ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_HrMinS), ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_HrMin), ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_HrMin), ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_None), ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_None), ImPlotDateTimeSpec(ImPlotDateFmt_Yr, ImPlotTimeFmt_None), ImPlotDateTimeSpec(ImPlotDateFmt_Yr, ImPlotTimeFmt_None) }; static const ImPlotDateTimeSpec TimeFormatLevel1First[ImPlotTimeUnit_COUNT] = { ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_HrMinS), ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_HrMinS), ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_HrMin), ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_HrMin), ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_None), ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_None), ImPlotDateTimeSpec(ImPlotDateFmt_Yr, ImPlotTimeFmt_None), ImPlotDateTimeSpec(ImPlotDateFmt_Yr, ImPlotTimeFmt_None) }; static const ImPlotDateTimeSpec TimeFormatMouseCursor[ImPlotTimeUnit_COUNT] = { ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_Us), ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_SUs), ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_SMs), ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_HrMinS), ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_HrMin), ImPlotDateTimeSpec(ImPlotDateFmt_DayMo, ImPlotTimeFmt_Hr), ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_None), ImPlotDateTimeSpec(ImPlotDateFmt_MoYr, ImPlotTimeFmt_None) }; inline ImPlotDateTimeSpec GetDateTimeFmt(const ImPlotDateTimeSpec* ctx, ImPlotTimeUnit idx) { ImPlotStyle& style = GetStyle(); ImPlotDateTimeSpec fmt = ctx[idx]; fmt.UseISO8601 = style.UseISO8601; fmt.Use24HourClock = style.Use24HourClock; return fmt; } void Locator_Time(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data) { IM_ASSERT_USER_ERROR(vertical == false, "Cannot locate Time ticks on vertical axis!"); (void)vertical; // get units for level 0 and level 1 labels const ImPlotTimeUnit unit0 = GetUnitForRange(range.Size() / (pixels / 100)); // level = 0 (top) const ImPlotTimeUnit unit1 = ImClamp(unit0 + 1, 0, ImPlotTimeUnit_COUNT-1); // level = 1 (bottom) // get time format specs const ImPlotDateTimeSpec fmt0 = GetDateTimeFmt(TimeFormatLevel0, unit0); const ImPlotDateTimeSpec fmt1 = GetDateTimeFmt(TimeFormatLevel1, unit1); const ImPlotDateTimeSpec fmtf = GetDateTimeFmt(TimeFormatLevel1First, unit1); // min max times const ImPlotTime t_min = ImPlotTime::FromDouble(range.Min); const ImPlotTime t_max = ImPlotTime::FromDouble(range.Max); // maximum allowable density of labels const float max_density = 0.5f; // book keeping int last_major_offset = -1; // formatter data Formatter_Time_Data ftd; ftd.UserFormatter = formatter; ftd.UserFormatterData = formatter_data; if (unit0 != ImPlotTimeUnit_Yr) { // pixels per major (level 1) division const float pix_per_major_div = pixels / (float)(range.Size() / TimeUnitSpans[unit1]); // nominal pixels taken up by labels const float fmt0_width = GetDateTimeWidth(fmt0); const float fmt1_width = GetDateTimeWidth(fmt1); const float fmtf_width = GetDateTimeWidth(fmtf); // the maximum number of minor (level 0) labels that can fit between major (level 1) divisions const int minor_per_major = (int)(max_density * pix_per_major_div / fmt0_width); // the minor step size (level 0) const int step = GetTimeStep(minor_per_major, unit0); // generate ticks ImPlotTime t1 = FloorTime(ImPlotTime::FromDouble(range.Min), unit1); while (t1 < t_max) { // get next major const ImPlotTime t2 = AddTime(t1, unit1, 1); // add major tick if (t1 >= t_min && t1 <= t_max) { // minor level 0 tick ftd.Time = t1; ftd.Spec = fmt0; ticker.AddTick(t1.ToDouble(), true, 0, true, Formatter_Time, &ftd); // major level 1 tick ftd.Time = t1; ftd.Spec = last_major_offset < 0 ? fmtf : fmt1; ImPlotTick& tick_maj = ticker.AddTick(t1.ToDouble(), true, 1, true, Formatter_Time, &ftd); const char* this_major = ticker.GetText(tick_maj); if (last_major_offset >= 0 && TimeLabelSame(ticker.TextBuffer.Buf.Data + last_major_offset, this_major)) tick_maj.ShowLabel = false; last_major_offset = tick_maj.TextOffset; } // add minor ticks up until next major if (minor_per_major > 1 && (t_min <= t2 && t1 <= t_max)) { ImPlotTime t12 = AddTime(t1, unit0, step); while (t12 < t2) { float px_to_t2 = (float)((t2 - t12).ToDouble()/range.Size()) * pixels; if (t12 >= t_min && t12 <= t_max) { ftd.Time = t12; ftd.Spec = fmt0; ticker.AddTick(t12.ToDouble(), false, 0, px_to_t2 >= fmt0_width, Formatter_Time, &ftd); if (last_major_offset < 0 && px_to_t2 >= fmt0_width && px_to_t2 >= (fmt1_width + fmtf_width) / 2) { ftd.Time = t12; ftd.Spec = fmtf; ImPlotTick& tick_maj = ticker.AddTick(t12.ToDouble(), true, 1, true, Formatter_Time, &ftd); last_major_offset = tick_maj.TextOffset; } } t12 = AddTime(t12, unit0, step); } } t1 = t2; } } else { const ImPlotDateTimeSpec fmty = GetDateTimeFmt(TimeFormatLevel0, ImPlotTimeUnit_Yr); const float label_width = GetDateTimeWidth(fmty); const int max_labels = (int)(max_density * pixels / label_width); const int year_min = GetYear(t_min); const int year_max = GetYear(CeilTime(t_max, ImPlotTimeUnit_Yr)); const double nice_range = NiceNum((year_max - year_min)*0.99,false); const double interval = NiceNum(nice_range / (max_labels - 1), true); const int graphmin = (int)(floor(year_min / interval) * interval); const int graphmax = (int)(ceil(year_max / interval) * interval); const int step = (int)interval <= 0 ? 1 : (int)interval; for (int y = graphmin; y < graphmax; y += step) { ImPlotTime t = MakeTime(y); if (t >= t_min && t <= t_max) { ftd.Time = t; ftd.Spec = fmty; ticker.AddTick(t.ToDouble(), true, 0, true, Formatter_Time, &ftd); } } } } //----------------------------------------------------------------------------- // Context Menu //----------------------------------------------------------------------------- template bool DragFloat(const char*, F*, float, F, F) { return false; } template <> bool DragFloat(const char* label, double* v, float v_speed, double v_min, double v_max) { return ImGui::DragScalar(label, ImGuiDataType_Double, v, v_speed, &v_min, &v_max, "%.3f", 1); } template <> bool DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max) { return ImGui::DragScalar(label, ImGuiDataType_Float, v, v_speed, &v_min, &v_max, "%.3f", 1); } inline void BeginDisabledControls(bool cond) { if (cond) { ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.25f); } } inline void EndDisabledControls(bool cond) { if (cond) { ImGui::PopItemFlag(); ImGui::PopStyleVar(); } } void ShowAxisContextMenu(ImPlotAxis& axis, ImPlotAxis* equal_axis, bool /*time_allowed*/) { ImGui::PushItemWidth(75); bool always_locked = axis.IsRangeLocked() || axis.IsAutoFitting(); bool label = axis.HasLabel(); bool grid = axis.HasGridLines(); bool ticks = axis.HasTickMarks(); bool labels = axis.HasTickLabels(); double drag_speed = (axis.Range.Size() <= DBL_EPSILON) ? DBL_EPSILON * 1.0e+13 : 0.01 * axis.Range.Size(); // recover from almost equal axis limits. if (axis.Scale == ImPlotScale_Time) { ImPlotTime tmin = ImPlotTime::FromDouble(axis.Range.Min); ImPlotTime tmax = ImPlotTime::FromDouble(axis.Range.Max); BeginDisabledControls(always_locked); ImGui::CheckboxFlags("##LockMin", (unsigned int*)&axis.Flags, ImPlotAxisFlags_LockMin); EndDisabledControls(always_locked); ImGui::SameLine(); BeginDisabledControls(axis.IsLockedMin() || always_locked); if (ImGui::BeginMenu("Min Time")) { if (ShowTimePicker("mintime", &tmin)) { if (tmin >= tmax) tmax = AddTime(tmin, ImPlotTimeUnit_S, 1); axis.SetRange(tmin.ToDouble(),tmax.ToDouble()); } ImGui::Separator(); if (ShowDatePicker("mindate",&axis.PickerLevel,&axis.PickerTimeMin,&tmin,&tmax)) { tmin = CombineDateTime(axis.PickerTimeMin, tmin); if (tmin >= tmax) tmax = AddTime(tmin, ImPlotTimeUnit_S, 1); axis.SetRange(tmin.ToDouble(), tmax.ToDouble()); } ImGui::EndMenu(); } EndDisabledControls(axis.IsLockedMin() || always_locked); BeginDisabledControls(always_locked); ImGui::CheckboxFlags("##LockMax", (unsigned int*)&axis.Flags, ImPlotAxisFlags_LockMax); EndDisabledControls(always_locked); ImGui::SameLine(); BeginDisabledControls(axis.IsLockedMax() || always_locked); if (ImGui::BeginMenu("Max Time")) { if (ShowTimePicker("maxtime", &tmax)) { if (tmax <= tmin) tmin = AddTime(tmax, ImPlotTimeUnit_S, -1); axis.SetRange(tmin.ToDouble(),tmax.ToDouble()); } ImGui::Separator(); if (ShowDatePicker("maxdate",&axis.PickerLevel,&axis.PickerTimeMax,&tmin,&tmax)) { tmax = CombineDateTime(axis.PickerTimeMax, tmax); if (tmax <= tmin) tmin = AddTime(tmax, ImPlotTimeUnit_S, -1); axis.SetRange(tmin.ToDouble(), tmax.ToDouble()); } ImGui::EndMenu(); } EndDisabledControls(axis.IsLockedMax() || always_locked); } else { BeginDisabledControls(always_locked); ImGui::CheckboxFlags("##LockMin", (unsigned int*)&axis.Flags, ImPlotAxisFlags_LockMin); EndDisabledControls(always_locked); ImGui::SameLine(); BeginDisabledControls(axis.IsLockedMin() || always_locked); double temp_min = axis.Range.Min; if (DragFloat("Min", &temp_min, (float)drag_speed, -HUGE_VAL, axis.Range.Max - DBL_EPSILON)) { axis.SetMin(temp_min,true); if (equal_axis != nullptr) equal_axis->SetAspect(axis.GetAspect()); } EndDisabledControls(axis.IsLockedMin() || always_locked); BeginDisabledControls(always_locked); ImGui::CheckboxFlags("##LockMax", (unsigned int*)&axis.Flags, ImPlotAxisFlags_LockMax); EndDisabledControls(always_locked); ImGui::SameLine(); BeginDisabledControls(axis.IsLockedMax() || always_locked); double temp_max = axis.Range.Max; if (DragFloat("Max", &temp_max, (float)drag_speed, axis.Range.Min + DBL_EPSILON, HUGE_VAL)) { axis.SetMax(temp_max,true); if (equal_axis != nullptr) equal_axis->SetAspect(axis.GetAspect()); } EndDisabledControls(axis.IsLockedMax() || always_locked); } ImGui::Separator(); ImGui::CheckboxFlags("Auto-Fit",(unsigned int*)&axis.Flags, ImPlotAxisFlags_AutoFit); // TODO // BeginDisabledControls(axis.IsTime() && time_allowed); // ImGui::CheckboxFlags("Log Scale",(unsigned int*)&axis.Flags, ImPlotAxisFlags_LogScale); // EndDisabledControls(axis.IsTime() && time_allowed); // if (time_allowed) { // BeginDisabledControls(axis.IsLog() || axis.IsSymLog()); // ImGui::CheckboxFlags("Time",(unsigned int*)&axis.Flags, ImPlotAxisFlags_Time); // EndDisabledControls(axis.IsLog() || axis.IsSymLog()); // } ImGui::Separator(); ImGui::CheckboxFlags("Invert",(unsigned int*)&axis.Flags, ImPlotAxisFlags_Invert); ImGui::CheckboxFlags("Opposite",(unsigned int*)&axis.Flags, ImPlotAxisFlags_Opposite); ImGui::Separator(); BeginDisabledControls(axis.LabelOffset == -1); if (ImGui::Checkbox("Label", &label)) ImFlipFlag(axis.Flags, ImPlotAxisFlags_NoLabel); EndDisabledControls(axis.LabelOffset == -1); if (ImGui::Checkbox("Grid Lines", &grid)) ImFlipFlag(axis.Flags, ImPlotAxisFlags_NoGridLines); if (ImGui::Checkbox("Tick Marks", &ticks)) ImFlipFlag(axis.Flags, ImPlotAxisFlags_NoTickMarks); if (ImGui::Checkbox("Tick Labels", &labels)) ImFlipFlag(axis.Flags, ImPlotAxisFlags_NoTickLabels); } bool ShowLegendContextMenu(ImPlotLegend& legend, bool visible) { const float s = ImGui::GetFrameHeight(); bool ret = false; if (ImGui::Checkbox("Show",&visible)) ret = true; if (legend.CanGoInside) ImGui::CheckboxFlags("Outside",(unsigned int*)&legend.Flags, ImPlotLegendFlags_Outside); if (ImGui::RadioButton("H", ImHasFlag(legend.Flags, ImPlotLegendFlags_Horizontal))) legend.Flags |= ImPlotLegendFlags_Horizontal; ImGui::SameLine(); if (ImGui::RadioButton("V", !ImHasFlag(legend.Flags, ImPlotLegendFlags_Horizontal))) legend.Flags &= ~ImPlotLegendFlags_Horizontal; ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(2,2)); if (ImGui::Button("NW",ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_NorthWest; } ImGui::SameLine(); if (ImGui::Button("N", ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_North; } ImGui::SameLine(); if (ImGui::Button("NE",ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_NorthEast; } if (ImGui::Button("W", ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_West; } ImGui::SameLine(); if (ImGui::InvisibleButton("C", ImVec2(1.5f*s,s))) { } ImGui::SameLine(); if (ImGui::Button("E", ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_East; } if (ImGui::Button("SW",ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_SouthWest; } ImGui::SameLine(); if (ImGui::Button("S", ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_South; } ImGui::SameLine(); if (ImGui::Button("SE",ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_SouthEast; } ImGui::PopStyleVar(); return ret; } void ShowSubplotsContextMenu(ImPlotSubplot& subplot) { if ((ImGui::BeginMenu("Linking"))) { if (ImGui::MenuItem("Link Rows",nullptr,ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkRows))) ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_LinkRows); if (ImGui::MenuItem("Link Cols",nullptr,ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkCols))) ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_LinkCols); if (ImGui::MenuItem("Link All X",nullptr,ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkAllX))) ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_LinkAllX); if (ImGui::MenuItem("Link All Y",nullptr,ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkAllY))) ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_LinkAllY); ImGui::EndMenu(); } if ((ImGui::BeginMenu("Settings"))) { BeginDisabledControls(!subplot.HasTitle); if (ImGui::MenuItem("Title",nullptr,subplot.HasTitle && !ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoTitle))) ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_NoTitle); EndDisabledControls(!subplot.HasTitle); if (ImGui::MenuItem("Resizable",nullptr,!ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoResize))) ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_NoResize); if (ImGui::MenuItem("Align",nullptr,!ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoAlign))) ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_NoAlign); if (ImGui::MenuItem("Share Items",nullptr,ImHasFlag(subplot.Flags, ImPlotSubplotFlags_ShareItems))) ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_ShareItems); ImGui::EndMenu(); } } void ShowPlotContextMenu(ImPlotPlot& plot) { ImPlotContext& gp = *GImPlot; const bool owns_legend = gp.CurrentItems == &plot.Items; const bool equal = ImHasFlag(plot.Flags, ImPlotFlags_Equal); char buf[16] = {}; for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { ImPlotAxis& x_axis = plot.XAxis(i); if (!x_axis.Enabled || !x_axis.HasMenus()) continue; ImGui::PushID(i); ImFormatString(buf, sizeof(buf) - 1, i == 0 ? "X-Axis" : "X-Axis %d", i + 1); if (ImGui::BeginMenu(x_axis.HasLabel() ? plot.GetAxisLabel(x_axis) : buf)) { ShowAxisContextMenu(x_axis, equal ? x_axis.OrthoAxis : nullptr, false); ImGui::EndMenu(); } ImGui::PopID(); } for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { ImPlotAxis& y_axis = plot.YAxis(i); if (!y_axis.Enabled || !y_axis.HasMenus()) continue; ImGui::PushID(i); ImFormatString(buf, sizeof(buf) - 1, i == 0 ? "Y-Axis" : "Y-Axis %d", i + 1); if (ImGui::BeginMenu(y_axis.HasLabel() ? plot.GetAxisLabel(y_axis) : buf)) { ShowAxisContextMenu(y_axis, equal ? y_axis.OrthoAxis : nullptr, false); ImGui::EndMenu(); } ImGui::PopID(); } ImGui::Separator(); if (!ImHasFlag(gp.CurrentItems->Legend.Flags, ImPlotLegendFlags_NoMenus)) { if ((ImGui::BeginMenu("Legend"))) { if (owns_legend) { if (ShowLegendContextMenu(plot.Items.Legend, !ImHasFlag(plot.Flags, ImPlotFlags_NoLegend))) ImFlipFlag(plot.Flags, ImPlotFlags_NoLegend); } else if (gp.CurrentSubplot != nullptr) { if (ShowLegendContextMenu(gp.CurrentSubplot->Items.Legend, !ImHasFlag(gp.CurrentSubplot->Flags, ImPlotSubplotFlags_NoLegend))) ImFlipFlag(gp.CurrentSubplot->Flags, ImPlotSubplotFlags_NoLegend); } ImGui::EndMenu(); } } if ((ImGui::BeginMenu("Settings"))) { if (ImGui::MenuItem("Equal", nullptr, ImHasFlag(plot.Flags, ImPlotFlags_Equal))) ImFlipFlag(plot.Flags, ImPlotFlags_Equal); if (ImGui::MenuItem("Box Select",nullptr,!ImHasFlag(plot.Flags, ImPlotFlags_NoBoxSelect))) ImFlipFlag(plot.Flags, ImPlotFlags_NoBoxSelect); BeginDisabledControls(plot.TitleOffset == -1); if (ImGui::MenuItem("Title",nullptr,plot.HasTitle())) ImFlipFlag(plot.Flags, ImPlotFlags_NoTitle); EndDisabledControls(plot.TitleOffset == -1); if (ImGui::MenuItem("Mouse Position",nullptr,!ImHasFlag(plot.Flags, ImPlotFlags_NoMouseText))) ImFlipFlag(plot.Flags, ImPlotFlags_NoMouseText); if (ImGui::MenuItem("Crosshairs",nullptr,ImHasFlag(plot.Flags, ImPlotFlags_Crosshairs))) ImFlipFlag(plot.Flags, ImPlotFlags_Crosshairs); ImGui::EndMenu(); } if (gp.CurrentSubplot != nullptr && !ImHasFlag(gp.CurrentPlot->Flags, ImPlotSubplotFlags_NoMenus)) { ImGui::Separator(); if ((ImGui::BeginMenu("Subplots"))) { ShowSubplotsContextMenu(*gp.CurrentSubplot); ImGui::EndMenu(); } } } //----------------------------------------------------------------------------- // Axis Utils //----------------------------------------------------------------------------- static inline int AxisPrecision(const ImPlotAxis& axis) { const double range = axis.Ticker.TickCount() > 1 ? (axis.Ticker.Ticks[1].PlotPos - axis.Ticker.Ticks[0].PlotPos) : axis.Range.Size(); return Precision(range); } static inline double RoundAxisValue(const ImPlotAxis& axis, double value) { return RoundTo(value, AxisPrecision(axis)); } void LabelAxisValue(const ImPlotAxis& axis, double value, char* buff, int size, bool round) { ImPlotContext& gp = *GImPlot; // TODO: We shouldn't explicitly check that the axis is Time here. Ideally, // Formatter_Time would handle the formatting for us, but the code below // needs additional arguments which are not currently available in ImPlotFormatter if (axis.Locator == Locator_Time) { ImPlotTimeUnit unit = axis.Vertical ? GetUnitForRange(axis.Range.Size() / (gp.CurrentPlot->PlotRect.GetHeight() / 100)) // TODO: magic value! : GetUnitForRange(axis.Range.Size() / (gp.CurrentPlot->PlotRect.GetWidth() / 100)); // TODO: magic value! FormatDateTime(ImPlotTime::FromDouble(value), buff, size, GetDateTimeFmt(TimeFormatMouseCursor, unit)); } else { if (round) value = RoundAxisValue(axis, value); axis.Formatter(value, buff, size, axis.FormatterData); } } void UpdateAxisColors(ImPlotAxis& axis) { const ImVec4 col_grid = GetStyleColorVec4(ImPlotCol_AxisGrid); axis.ColorMaj = ImGui::GetColorU32(col_grid); axis.ColorMin = ImGui::GetColorU32(col_grid*ImVec4(1,1,1,GImPlot->Style.MinorAlpha)); axis.ColorTick = GetStyleColorU32(ImPlotCol_AxisTick); axis.ColorTxt = GetStyleColorU32(ImPlotCol_AxisText); axis.ColorBg = GetStyleColorU32(ImPlotCol_AxisBg); axis.ColorHov = GetStyleColorU32(ImPlotCol_AxisBgHovered); axis.ColorAct = GetStyleColorU32(ImPlotCol_AxisBgActive); // axis.ColorHiLi = IM_COL32_BLACK_TRANS; } void PadAndDatumAxesX(ImPlotPlot& plot, float& pad_T, float& pad_B, ImPlotAlignmentData* align) { ImPlotContext& gp = *GImPlot; const float T = ImGui::GetTextLineHeight(); const float P = gp.Style.LabelPadding.y; const float K = gp.Style.MinorTickLen.x; int count_T = 0; int count_B = 0; float last_T = plot.AxesRect.Min.y; float last_B = plot.AxesRect.Max.y; for (int i = IMPLOT_NUM_X_AXES; i-- > 0;) { // FYI: can iterate forward ImPlotAxis& axis = plot.XAxis(i); if (!axis.Enabled) continue; const bool label = axis.HasLabel(); const bool ticks = axis.HasTickLabels(); const bool opp = axis.IsOpposite(); const bool time = axis.Scale == ImPlotScale_Time; if (opp) { if (count_T++ > 0) pad_T += K + P; if (label) pad_T += T + P; if (ticks) pad_T += ImMax(T, axis.Ticker.MaxSize.y) + P + (time ? T + P : 0); axis.Datum1 = plot.CanvasRect.Min.y + pad_T; axis.Datum2 = last_T; last_T = axis.Datum1; } else { if (count_B++ > 0) pad_B += K + P; if (label) pad_B += T + P; if (ticks) pad_B += ImMax(T, axis.Ticker.MaxSize.y) + P + (time ? T + P : 0); axis.Datum1 = plot.CanvasRect.Max.y - pad_B; axis.Datum2 = last_B; last_B = axis.Datum1; } } if (align) { count_T = count_B = 0; float delta_T, delta_B; align->Update(pad_T,pad_B,delta_T,delta_B); for (int i = IMPLOT_NUM_X_AXES; i-- > 0;) { ImPlotAxis& axis = plot.XAxis(i); if (!axis.Enabled) continue; if (axis.IsOpposite()) { axis.Datum1 += delta_T; axis.Datum2 += count_T++ > 1 ? delta_T : 0; } else { axis.Datum1 -= delta_B; axis.Datum2 -= count_B++ > 1 ? delta_B : 0; } } } } void PadAndDatumAxesY(ImPlotPlot& plot, float& pad_L, float& pad_R, ImPlotAlignmentData* align) { // [ pad_L ] [ pad_R ] // .................CanvasRect................ // :TPWPK.PTPWP _____PlotRect____ PWPTP.KPWPT: // :A # |- A # |- -| # A -| # A: // :X | X | | X | x: // :I # |- I # |- -| # I -| # I: // :S | S | | S | S: // :3 # |- 0 # |-_______________-| # 1 -| # 2: // :.........................................: // // T = text height // P = label padding // K = minor tick length // W = label width ImPlotContext& gp = *GImPlot; const float T = ImGui::GetTextLineHeight(); const float P = gp.Style.LabelPadding.x; const float K = gp.Style.MinorTickLen.y; int count_L = 0; int count_R = 0; float last_L = plot.AxesRect.Min.x; float last_R = plot.AxesRect.Max.x; for (int i = IMPLOT_NUM_Y_AXES; i-- > 0;) { // FYI: can iterate forward ImPlotAxis& axis = plot.YAxis(i); if (!axis.Enabled) continue; const bool label = axis.HasLabel(); const bool ticks = axis.HasTickLabels(); const bool opp = axis.IsOpposite(); if (opp) { if (count_R++ > 0) pad_R += K + P; if (label) pad_R += T + P; if (ticks) pad_R += axis.Ticker.MaxSize.x + P; axis.Datum1 = plot.CanvasRect.Max.x - pad_R; axis.Datum2 = last_R; last_R = axis.Datum1; } else { if (count_L++ > 0) pad_L += K + P; if (label) pad_L += T + P; if (ticks) pad_L += axis.Ticker.MaxSize.x + P; axis.Datum1 = plot.CanvasRect.Min.x + pad_L; axis.Datum2 = last_L; last_L = axis.Datum1; } } plot.PlotRect.Min.x = plot.CanvasRect.Min.x + pad_L; plot.PlotRect.Max.x = plot.CanvasRect.Max.x - pad_R; if (align) { count_L = count_R = 0; float delta_L, delta_R; align->Update(pad_L,pad_R,delta_L,delta_R); for (int i = IMPLOT_NUM_Y_AXES; i-- > 0;) { ImPlotAxis& axis = plot.YAxis(i); if (!axis.Enabled) continue; if (axis.IsOpposite()) { axis.Datum1 -= delta_R; axis.Datum2 -= count_R++ > 1 ? delta_R : 0; } else { axis.Datum1 += delta_L; axis.Datum2 += count_L++ > 1 ? delta_L : 0; } } } } //----------------------------------------------------------------------------- // RENDERING //----------------------------------------------------------------------------- static inline void RenderGridLinesX(ImDrawList& DrawList, const ImPlotTicker& ticker, const ImRect& rect, ImU32 col_maj, ImU32 col_min, float size_maj, float size_min) { const float density = ticker.TickCount() / rect.GetWidth(); ImVec4 col_min4 = ImGui::ColorConvertU32ToFloat4(col_min); col_min4.w *= ImClamp(ImRemap(density, 0.1f, 0.2f, 1.0f, 0.0f), 0.0f, 1.0f); col_min = ImGui::ColorConvertFloat4ToU32(col_min4); for (int t = 0; t < ticker.TickCount(); t++) { const ImPlotTick& xt = ticker.Ticks[t]; if (xt.PixelPos < rect.Min.x || xt.PixelPos > rect.Max.x) continue; if (xt.Level == 0) { if (xt.Major) DrawList.AddLine(ImVec2(xt.PixelPos, rect.Min.y), ImVec2(xt.PixelPos, rect.Max.y), col_maj, size_maj); else if (density < 0.2f) DrawList.AddLine(ImVec2(xt.PixelPos, rect.Min.y), ImVec2(xt.PixelPos, rect.Max.y), col_min, size_min); } } } static inline void RenderGridLinesY(ImDrawList& DrawList, const ImPlotTicker& ticker, const ImRect& rect, ImU32 col_maj, ImU32 col_min, float size_maj, float size_min) { const float density = ticker.TickCount() / rect.GetHeight(); ImVec4 col_min4 = ImGui::ColorConvertU32ToFloat4(col_min); col_min4.w *= ImClamp(ImRemap(density, 0.1f, 0.2f, 1.0f, 0.0f), 0.0f, 1.0f); col_min = ImGui::ColorConvertFloat4ToU32(col_min4); for (int t = 0; t < ticker.TickCount(); t++) { const ImPlotTick& yt = ticker.Ticks[t]; if (yt.PixelPos < rect.Min.y || yt.PixelPos > rect.Max.y) continue; if (yt.Major) DrawList.AddLine(ImVec2(rect.Min.x, yt.PixelPos), ImVec2(rect.Max.x, yt.PixelPos), col_maj, size_maj); else if (density < 0.2f) DrawList.AddLine(ImVec2(rect.Min.x, yt.PixelPos), ImVec2(rect.Max.x, yt.PixelPos), col_min, size_min); } } static inline void RenderSelectionRect(ImDrawList& DrawList, const ImVec2& p_min, const ImVec2& p_max, const ImVec4& col) { const ImU32 col_bg = ImGui::GetColorU32(col * ImVec4(1,1,1,0.25f)); const ImU32 col_bd = ImGui::GetColorU32(col); DrawList.AddRectFilled(p_min, p_max, col_bg); DrawList.AddRect(p_min, p_max, col_bd); } //----------------------------------------------------------------------------- // Input Handling //----------------------------------------------------------------------------- static const float MOUSE_CURSOR_DRAG_THRESHOLD = 5.0f; static const float BOX_SELECT_DRAG_THRESHOLD = 4.0f; bool UpdateInput(ImPlotPlot& plot) { bool changed = false; ImPlotContext& gp = *GImPlot; ImGuiIO& IO = ImGui::GetIO(); // BUTTON STATE ----------------------------------------------------------- const ImGuiButtonFlags plot_button_flags = ImGuiButtonFlags_AllowOverlap | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick | ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight | ImGuiButtonFlags_MouseButtonMiddle; const ImGuiButtonFlags axis_button_flags = ImGuiButtonFlags_FlattenChildren | plot_button_flags; const bool plot_clicked = ImGui::ButtonBehavior(plot.PlotRect,plot.ID,&plot.Hovered,&plot.Held,plot_button_flags); #if (IMGUI_VERSION_NUM < 18966) ImGui::SetItemAllowOverlap(); // Handled by ButtonBehavior() #endif if (plot_clicked) { if (!ImHasFlag(plot.Flags, ImPlotFlags_NoBoxSelect) && IO.MouseClicked[gp.InputMap.Select] && ImHasFlag(IO.KeyMods, gp.InputMap.SelectMod)) { plot.Selecting = true; plot.SelectStart = IO.MousePos; plot.SelectRect = ImRect(0,0,0,0); } if (IO.MouseDoubleClicked[gp.InputMap.Fit]) { plot.FitThisFrame = true; for (int i = 0; i < ImAxis_COUNT; ++i) plot.Axes[i].FitThisFrame = true; } } const bool can_pan = IO.MouseDown[gp.InputMap.Pan] && ImHasFlag(IO.KeyMods, gp.InputMap.PanMod); plot.Held = plot.Held && can_pan; bool x_click[IMPLOT_NUM_X_AXES] = {false}; bool x_held[IMPLOT_NUM_X_AXES] = {false}; bool x_hov[IMPLOT_NUM_X_AXES] = {false}; bool y_click[IMPLOT_NUM_Y_AXES] = {false}; bool y_held[IMPLOT_NUM_Y_AXES] = {false}; bool y_hov[IMPLOT_NUM_Y_AXES] = {false}; for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) { ImPlotAxis& xax = plot.XAxis(i); if (xax.Enabled) { ImGui::KeepAliveID(xax.ID); x_click[i] = ImGui::ButtonBehavior(xax.HoverRect,xax.ID,&xax.Hovered,&xax.Held,axis_button_flags); if (x_click[i] && IO.MouseDoubleClicked[gp.InputMap.Fit]) plot.FitThisFrame = xax.FitThisFrame = true; xax.Held = xax.Held && can_pan; x_hov[i] = xax.Hovered || plot.Hovered; x_held[i] = xax.Held || plot.Held; } } for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) { ImPlotAxis& yax = plot.YAxis(i); if (yax.Enabled) { ImGui::KeepAliveID(yax.ID); y_click[i] = ImGui::ButtonBehavior(yax.HoverRect,yax.ID,&yax.Hovered,&yax.Held,axis_button_flags); if (y_click[i] && IO.MouseDoubleClicked[gp.InputMap.Fit]) plot.FitThisFrame = yax.FitThisFrame = true; yax.Held = yax.Held && can_pan; y_hov[i] = yax.Hovered || plot.Hovered; y_held[i] = yax.Held || plot.Held; } } // cancel due to DND activity if (GImGui->DragDropActive || (IO.KeyMods == gp.InputMap.OverrideMod && gp.InputMap.OverrideMod != 0)) return false; // STATE ------------------------------------------------------------------- const bool axis_equal = ImHasFlag(plot.Flags, ImPlotFlags_Equal); const bool any_x_hov = plot.Hovered || AnyAxesHovered(&plot.Axes[ImAxis_X1], IMPLOT_NUM_X_AXES); const bool any_x_held = plot.Held || AnyAxesHeld(&plot.Axes[ImAxis_X1], IMPLOT_NUM_X_AXES); const bool any_y_hov = plot.Hovered || AnyAxesHovered(&plot.Axes[ImAxis_Y1], IMPLOT_NUM_Y_AXES); const bool any_y_held = plot.Held || AnyAxesHeld(&plot.Axes[ImAxis_Y1], IMPLOT_NUM_Y_AXES); const bool any_hov = any_x_hov || any_y_hov; const bool any_held = any_x_held || any_y_held; const ImVec2 select_drag = ImGui::GetMouseDragDelta(gp.InputMap.Select); const ImVec2 pan_drag = ImGui::GetMouseDragDelta(gp.InputMap.Pan); const float select_drag_sq = ImLengthSqr(select_drag); const float pan_drag_sq = ImLengthSqr(pan_drag); const bool selecting = plot.Selecting && select_drag_sq > MOUSE_CURSOR_DRAG_THRESHOLD; const bool panning = any_held && pan_drag_sq > MOUSE_CURSOR_DRAG_THRESHOLD; // CONTEXT MENU ----------------------------------------------------------- if (IO.MouseReleased[gp.InputMap.Menu] && !plot.ContextLocked) gp.OpenContextThisFrame = true; if (selecting || panning) plot.ContextLocked = true; else if (!(IO.MouseDown[gp.InputMap.Menu] || IO.MouseReleased[gp.InputMap.Menu])) plot.ContextLocked = false; // DRAG INPUT ------------------------------------------------------------- if (any_held && !plot.Selecting) { int drag_direction = 0; for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { ImPlotAxis& x_axis = plot.XAxis(i); if (x_held[i] && !x_axis.IsInputLocked()) { drag_direction |= (1 << 1); bool increasing = x_axis.IsInverted() ? IO.MouseDelta.x > 0 : IO.MouseDelta.x < 0; if (IO.MouseDelta.x != 0 && !x_axis.IsPanLocked(increasing)) { const double plot_l = x_axis.PixelsToPlot(plot.PlotRect.Min.x - IO.MouseDelta.x); const double plot_r = x_axis.PixelsToPlot(plot.PlotRect.Max.x - IO.MouseDelta.x); x_axis.SetMin(x_axis.IsInverted() ? plot_r : plot_l); x_axis.SetMax(x_axis.IsInverted() ? plot_l : plot_r); if (axis_equal && x_axis.OrthoAxis != nullptr) x_axis.OrthoAxis->SetAspect(x_axis.GetAspect()); changed = true; } } } for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { ImPlotAxis& y_axis = plot.YAxis(i); if (y_held[i] && !y_axis.IsInputLocked()) { drag_direction |= (1 << 2); bool increasing = y_axis.IsInverted() ? IO.MouseDelta.y < 0 : IO.MouseDelta.y > 0; if (IO.MouseDelta.y != 0 && !y_axis.IsPanLocked(increasing)) { const double plot_t = y_axis.PixelsToPlot(plot.PlotRect.Min.y - IO.MouseDelta.y); const double plot_b = y_axis.PixelsToPlot(plot.PlotRect.Max.y - IO.MouseDelta.y); y_axis.SetMin(y_axis.IsInverted() ? plot_t : plot_b); y_axis.SetMax(y_axis.IsInverted() ? plot_b : plot_t); if (axis_equal && y_axis.OrthoAxis != nullptr) y_axis.OrthoAxis->SetAspect(y_axis.GetAspect()); changed = true; } } } if (IO.MouseDragMaxDistanceSqr[gp.InputMap.Pan] > MOUSE_CURSOR_DRAG_THRESHOLD) { switch (drag_direction) { case 0 : ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed); break; case (1 << 1) : ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); break; case (1 << 2) : ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS); break; default : ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll); break; } } } // SCROLL INPUT ----------------------------------------------------------- if (any_hov && IO.MouseWheel != 0 && ImHasFlag(IO.KeyMods, gp.InputMap.ZoomMod)) { float zoom_rate = gp.InputMap.ZoomRate; if (IO.MouseWheel > 0) zoom_rate = (-zoom_rate) / (1.0f + (2.0f * zoom_rate)); ImVec2 rect_size = plot.PlotRect.GetSize(); float tx = ImRemap(IO.MousePos.x, plot.PlotRect.Min.x, plot.PlotRect.Max.x, 0.0f, 1.0f); float ty = ImRemap(IO.MousePos.y, plot.PlotRect.Min.y, plot.PlotRect.Max.y, 0.0f, 1.0f); for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { ImPlotAxis& x_axis = plot.XAxis(i); const bool equal_zoom = axis_equal && x_axis.OrthoAxis != nullptr; const bool equal_locked = (equal_zoom != false) && x_axis.OrthoAxis->IsInputLocked(); if (x_hov[i] && !x_axis.IsInputLocked() && !equal_locked) { float correction = (plot.Hovered && equal_zoom) ? 0.5f : 1.0f; const double plot_l = x_axis.PixelsToPlot(plot.PlotRect.Min.x - rect_size.x * tx * zoom_rate * correction); const double plot_r = x_axis.PixelsToPlot(plot.PlotRect.Max.x + rect_size.x * (1 - tx) * zoom_rate * correction); x_axis.SetMin(x_axis.IsInverted() ? plot_r : plot_l); x_axis.SetMax(x_axis.IsInverted() ? plot_l : plot_r); if (axis_equal && x_axis.OrthoAxis != nullptr) x_axis.OrthoAxis->SetAspect(x_axis.GetAspect()); changed = true; } } for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { ImPlotAxis& y_axis = plot.YAxis(i); const bool equal_zoom = axis_equal && y_axis.OrthoAxis != nullptr; const bool equal_locked = equal_zoom && y_axis.OrthoAxis->IsInputLocked(); if (y_hov[i] && !y_axis.IsInputLocked() && !equal_locked) { float correction = (plot.Hovered && equal_zoom) ? 0.5f : 1.0f; const double plot_t = y_axis.PixelsToPlot(plot.PlotRect.Min.y - rect_size.y * ty * zoom_rate * correction); const double plot_b = y_axis.PixelsToPlot(plot.PlotRect.Max.y + rect_size.y * (1 - ty) * zoom_rate * correction); y_axis.SetMin(y_axis.IsInverted() ? plot_t : plot_b); y_axis.SetMax(y_axis.IsInverted() ? plot_b : plot_t); if (axis_equal && y_axis.OrthoAxis != nullptr) y_axis.OrthoAxis->SetAspect(y_axis.GetAspect()); changed = true; } } } // BOX-SELECTION ---------------------------------------------------------- if (plot.Selecting) { const ImVec2 d = plot.SelectStart - IO.MousePos; const bool x_can_change = !ImHasFlag(IO.KeyMods,gp.InputMap.SelectHorzMod) && ImFabs(d.x) > 2; const bool y_can_change = !ImHasFlag(IO.KeyMods,gp.InputMap.SelectVertMod) && ImFabs(d.y) > 2; // confirm if (IO.MouseReleased[gp.InputMap.Select]) { for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { ImPlotAxis& x_axis = plot.XAxis(i); if (!x_axis.IsInputLocked() && x_can_change) { const double p1 = x_axis.PixelsToPlot(plot.SelectStart.x); const double p2 = x_axis.PixelsToPlot(IO.MousePos.x); x_axis.SetMin(ImMin(p1, p2)); x_axis.SetMax(ImMax(p1, p2)); changed = true; } } for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { ImPlotAxis& y_axis = plot.YAxis(i); if (!y_axis.IsInputLocked() && y_can_change) { const double p1 = y_axis.PixelsToPlot(plot.SelectStart.y); const double p2 = y_axis.PixelsToPlot(IO.MousePos.y); y_axis.SetMin(ImMin(p1, p2)); y_axis.SetMax(ImMax(p1, p2)); changed = true; } } if (x_can_change || y_can_change || (ImHasFlag(IO.KeyMods,gp.InputMap.SelectHorzMod) && ImHasFlag(IO.KeyMods,gp.InputMap.SelectVertMod))) gp.OpenContextThisFrame = false; plot.Selected = plot.Selecting = false; } // cancel else if (IO.MouseReleased[gp.InputMap.SelectCancel]) { plot.Selected = plot.Selecting = false; gp.OpenContextThisFrame = false; } else if (ImLengthSqr(d) > BOX_SELECT_DRAG_THRESHOLD) { // bad selection if (plot.IsInputLocked()) { ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed); gp.OpenContextThisFrame = false; plot.Selected = false; } else { // TODO: Handle only min or max locked cases const bool full_width = ImHasFlag(IO.KeyMods, gp.InputMap.SelectHorzMod) || AllAxesInputLocked(&plot.Axes[ImAxis_X1], IMPLOT_NUM_X_AXES); const bool full_height = ImHasFlag(IO.KeyMods, gp.InputMap.SelectVertMod) || AllAxesInputLocked(&plot.Axes[ImAxis_Y1], IMPLOT_NUM_Y_AXES); plot.SelectRect.Min.x = full_width ? plot.PlotRect.Min.x : ImMin(plot.SelectStart.x, IO.MousePos.x); plot.SelectRect.Max.x = full_width ? plot.PlotRect.Max.x : ImMax(plot.SelectStart.x, IO.MousePos.x); plot.SelectRect.Min.y = full_height ? plot.PlotRect.Min.y : ImMin(plot.SelectStart.y, IO.MousePos.y); plot.SelectRect.Max.y = full_height ? plot.PlotRect.Max.y : ImMax(plot.SelectStart.y, IO.MousePos.y); plot.SelectRect.Min -= plot.PlotRect.Min; plot.SelectRect.Max -= plot.PlotRect.Min; plot.Selected = true; } } else { plot.Selected = false; } } return changed; } //----------------------------------------------------------------------------- // Next Plot Data (Legacy) //----------------------------------------------------------------------------- void ApplyNextPlotData(ImAxis idx) { ImPlotContext& gp = *GImPlot; ImPlotPlot& plot = *gp.CurrentPlot; ImPlotAxis& axis = plot.Axes[idx]; if (!axis.Enabled) return; double* npd_lmin = gp.NextPlotData.LinkedMin[idx]; double* npd_lmax = gp.NextPlotData.LinkedMax[idx]; bool npd_rngh = gp.NextPlotData.HasRange[idx]; ImPlotCond npd_rngc = gp.NextPlotData.RangeCond[idx]; ImPlotRange npd_rngv = gp.NextPlotData.Range[idx]; axis.LinkedMin = npd_lmin; axis.LinkedMax = npd_lmax; axis.PullLinks(); if (npd_rngh) { if (!plot.Initialized || npd_rngc == ImPlotCond_Always) axis.SetRange(npd_rngv); } axis.HasRange = npd_rngh; axis.RangeCond = npd_rngc; } //----------------------------------------------------------------------------- // Setup //----------------------------------------------------------------------------- void SetupAxis(ImAxis idx, const char* label, ImPlotAxisFlags flags) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked, "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); // get plot and axis ImPlotPlot& plot = *gp.CurrentPlot; ImPlotAxis& axis = plot.Axes[idx]; // set ID axis.ID = plot.ID + idx + 1; // check and set flags if (plot.JustCreated || flags != axis.PreviousFlags) axis.Flags = flags; axis.PreviousFlags = flags; // enable axis axis.Enabled = true; // set label plot.SetAxisLabel(axis,label); // cache colors UpdateAxisColors(axis); } void SetupAxisLimits(ImAxis idx, double min_lim, double max_lim, ImPlotCond cond) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked, "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); // get plot and axis ImPlotPlot& plot = *gp.CurrentPlot; ImPlotAxis& axis = plot.Axes[idx]; IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); if (!plot.Initialized || cond == ImPlotCond_Always) axis.SetRange(min_lim, max_lim); axis.HasRange = true; axis.RangeCond = cond; } void SetupAxisFormat(ImAxis idx, const char* fmt) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked, "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); ImPlotPlot& plot = *gp.CurrentPlot; ImPlotAxis& axis = plot.Axes[idx]; IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); axis.HasFormatSpec = fmt != nullptr; if (fmt != nullptr) ImStrncpy(axis.FormatSpec,fmt,sizeof(axis.FormatSpec)); } void SetupAxisLinks(ImAxis idx, double* min_lnk, double* max_lnk) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked, "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); ImPlotPlot& plot = *gp.CurrentPlot; ImPlotAxis& axis = plot.Axes[idx]; IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); axis.LinkedMin = min_lnk; axis.LinkedMax = max_lnk; axis.PullLinks(); } void SetupAxisFormat(ImAxis idx, ImPlotFormatter formatter, void* data) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked, "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); ImPlotPlot& plot = *gp.CurrentPlot; ImPlotAxis& axis = plot.Axes[idx]; IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); axis.Formatter = formatter; axis.FormatterData = data; } void SetupAxisTicks(ImAxis idx, const double* values, int n_ticks, const char* const labels[], bool show_default) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked, "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); ImPlotPlot& plot = *gp.CurrentPlot; ImPlotAxis& axis = plot.Axes[idx]; IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); axis.ShowDefaultTicks = show_default; AddTicksCustom(values, labels, n_ticks, axis.Ticker, axis.Formatter ? axis.Formatter : Formatter_Default, (axis.Formatter && axis.FormatterData) ? axis.FormatterData : axis.HasFormatSpec ? axis.FormatSpec : (void*)IMPLOT_LABEL_FORMAT); } void SetupAxisTicks(ImAxis idx, double v_min, double v_max, int n_ticks, const char* const labels[], bool show_default) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked, "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); n_ticks = n_ticks < 2 ? 2 : n_ticks; FillRange(gp.TempDouble1, n_ticks, v_min, v_max); SetupAxisTicks(idx, gp.TempDouble1.Data, n_ticks, labels, show_default); } void SetupAxisScale(ImAxis idx, ImPlotScale scale) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked, "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); ImPlotPlot& plot = *gp.CurrentPlot; ImPlotAxis& axis = plot.Axes[idx]; IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); axis.Scale = scale; switch (scale) { case ImPlotScale_Time: axis.TransformForward = nullptr; axis.TransformInverse = nullptr; axis.TransformData = nullptr; axis.Locator = Locator_Time; axis.ConstraintRange = ImPlotRange(IMPLOT_MIN_TIME, IMPLOT_MAX_TIME); axis.Ticker.Levels = 2; break; case ImPlotScale_Log10: axis.TransformForward = TransformForward_Log10; axis.TransformInverse = TransformInverse_Log10; axis.TransformData = nullptr; axis.Locator = Locator_Log10; axis.ConstraintRange = ImPlotRange(DBL_MIN, INFINITY); break; case ImPlotScale_SymLog: axis.TransformForward = TransformForward_SymLog; axis.TransformInverse = TransformInverse_SymLog; axis.TransformData = nullptr; axis.Locator = Locator_SymLog; axis.ConstraintRange = ImPlotRange(-INFINITY, INFINITY); break; default: axis.TransformForward = nullptr; axis.TransformInverse = nullptr; axis.TransformData = nullptr; axis.Locator = nullptr; axis.ConstraintRange = ImPlotRange(-INFINITY, INFINITY); break; } } void SetupAxisScale(ImAxis idx, ImPlotTransform fwd, ImPlotTransform inv, void* data) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked, "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); ImPlotPlot& plot = *gp.CurrentPlot; ImPlotAxis& axis = plot.Axes[idx]; IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); axis.Scale = IMPLOT_AUTO; axis.TransformForward = fwd; axis.TransformInverse = inv; axis.TransformData = data; } void SetupAxisLimitsConstraints(ImAxis idx, double v_min, double v_max) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked, "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); ImPlotPlot& plot = *gp.CurrentPlot; ImPlotAxis& axis = plot.Axes[idx]; IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); axis.ConstraintRange.Min = v_min; axis.ConstraintRange.Max = v_max; } void SetupAxisZoomConstraints(ImAxis idx, double z_min, double z_max) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked, "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); ImPlotPlot& plot = *gp.CurrentPlot; ImPlotAxis& axis = plot.Axes[idx]; IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); axis.ConstraintZoom.Min = z_min; axis.ConstraintZoom.Max = z_max; } void SetupAxes(const char* x_label, const char* y_label, ImPlotAxisFlags x_flags, ImPlotAxisFlags y_flags) { SetupAxis(ImAxis_X1, x_label, x_flags); SetupAxis(ImAxis_Y1, y_label, y_flags); } void SetupAxesLimits(double x_min, double x_max, double y_min, double y_max, ImPlotCond cond) { SetupAxisLimits(ImAxis_X1, x_min, x_max, cond); SetupAxisLimits(ImAxis_Y1, y_min, y_max, cond); } void SetupLegend(ImPlotLocation location, ImPlotLegendFlags flags) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked, "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); IM_ASSERT_USER_ERROR(gp.CurrentItems != nullptr, "SetupLegend() needs to be called within an itemized context!"); ImPlotLegend& legend = gp.CurrentItems->Legend; // check and set location if (location != legend.PreviousLocation) legend.Location = location; legend.PreviousLocation = location; // check and set flags if (flags != legend.PreviousFlags) legend.Flags = flags; legend.PreviousFlags = flags; } void SetupMouseText(ImPlotLocation location, ImPlotMouseTextFlags flags) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked, "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); gp.CurrentPlot->MouseTextLocation = location; gp.CurrentPlot->MouseTextFlags = flags; } //----------------------------------------------------------------------------- // SetNext //----------------------------------------------------------------------------- void SetNextAxisLimits(ImAxis axis, double v_min, double v_max, ImPlotCond cond) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot == nullptr, "SetNextAxisLimits() needs to be called before BeginPlot()!"); IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. gp.NextPlotData.HasRange[axis] = true; gp.NextPlotData.RangeCond[axis] = cond; gp.NextPlotData.Range[axis].Min = v_min; gp.NextPlotData.Range[axis].Max = v_max; } void SetNextAxisLinks(ImAxis axis, double* link_min, double* link_max) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot == nullptr, "SetNextAxisLinks() needs to be called before BeginPlot()!"); gp.NextPlotData.LinkedMin[axis] = link_min; gp.NextPlotData.LinkedMax[axis] = link_max; } void SetNextAxisToFit(ImAxis axis) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot == nullptr, "SetNextAxisToFit() needs to be called before BeginPlot()!"); gp.NextPlotData.Fit[axis] = true; } void SetNextAxesLimits(double x_min, double x_max, double y_min, double y_max, ImPlotCond cond) { SetNextAxisLimits(ImAxis_X1, x_min, x_max, cond); SetNextAxisLimits(ImAxis_Y1, y_min, y_max, cond); } void SetNextAxesToFit() { for (int i = 0; i < ImAxis_COUNT; ++i) SetNextAxisToFit(i); } //----------------------------------------------------------------------------- // BeginPlot //----------------------------------------------------------------------------- bool BeginPlot(const char* title_id, const ImVec2& size, ImPlotFlags flags) { IM_ASSERT_USER_ERROR(GImPlot != nullptr, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot == nullptr, "Mismatched BeginPlot()/EndPlot()!"); // FRONT MATTER ----------------------------------------------------------- if (gp.CurrentSubplot != nullptr) ImGui::PushID(gp.CurrentSubplot->CurrentIdx); // get globals ImGuiContext &G = *GImGui; ImGuiWindow* Window = G.CurrentWindow; // skip if needed if (Window->SkipItems && !gp.CurrentSubplot) { ResetCtxForNextPlot(GImPlot); return false; } // ID and age (TODO: keep track of plot age in frames) const ImGuiID ID = Window->GetID(title_id); const bool just_created = gp.Plots.GetByKey(ID) == nullptr; gp.CurrentPlot = gp.Plots.GetOrAddByKey(ID); ImPlotPlot &plot = *gp.CurrentPlot; plot.ID = ID; plot.Items.ID = ID - 1; plot.JustCreated = just_created; plot.SetupLocked = false; // check flags if (plot.JustCreated) plot.Flags = flags; else if (flags != plot.PreviousFlags) plot.Flags = flags; plot.PreviousFlags = flags; // setup default axes if (plot.JustCreated) { SetupAxis(ImAxis_X1); SetupAxis(ImAxis_Y1); } // reset axes for (int i = 0; i < ImAxis_COUNT; ++i) { plot.Axes[i].Reset(); UpdateAxisColors(plot.Axes[i]); } // ensure first axes enabled plot.Axes[ImAxis_X1].Enabled = true; plot.Axes[ImAxis_Y1].Enabled = true; // set initial axes plot.CurrentX = ImAxis_X1; plot.CurrentY = ImAxis_Y1; // process next plot data (legacy) for (int i = 0; i < ImAxis_COUNT; ++i) ApplyNextPlotData(i); // capture scroll with a child region if (!ImHasFlag(plot.Flags, ImPlotFlags_NoChild)) { ImVec2 child_size; if (gp.CurrentSubplot != nullptr) child_size = gp.CurrentSubplot->CellSize; else child_size = ImVec2(size.x == 0 ? gp.Style.PlotDefaultSize.x : size.x, size.y == 0 ? gp.Style.PlotDefaultSize.y : size.y); ImGui::BeginChild(title_id, child_size, false, ImGuiWindowFlags_NoScrollbar); Window = ImGui::GetCurrentWindow(); Window->ScrollMax.y = 1.0f; gp.ChildWindowMade = true; } else { gp.ChildWindowMade = false; } // clear text buffers plot.ClearTextBuffer(); plot.SetTitle(title_id); // set frame size ImVec2 frame_size; if (gp.CurrentSubplot != nullptr) frame_size = gp.CurrentSubplot->CellSize; else frame_size = ImGui::CalcItemSize(size, gp.Style.PlotDefaultSize.x, gp.Style.PlotDefaultSize.y); if (frame_size.x < gp.Style.PlotMinSize.x && (size.x < 0.0f || gp.CurrentSubplot != nullptr)) frame_size.x = gp.Style.PlotMinSize.x; if (frame_size.y < gp.Style.PlotMinSize.y && (size.y < 0.0f || gp.CurrentSubplot != nullptr)) frame_size.y = gp.Style.PlotMinSize.y; plot.FrameRect = ImRect(Window->DC.CursorPos, Window->DC.CursorPos + frame_size); ImGui::ItemSize(plot.FrameRect); if (!ImGui::ItemAdd(plot.FrameRect, plot.ID, &plot.FrameRect) && !gp.CurrentSubplot) { ResetCtxForNextPlot(GImPlot); return false; } // setup items (or dont) if (gp.CurrentItems == nullptr) gp.CurrentItems = &plot.Items; return true; } //----------------------------------------------------------------------------- // SetupFinish //----------------------------------------------------------------------------- void SetupFinish() { IM_ASSERT_USER_ERROR(GImPlot != nullptr, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "SetupFinish needs to be called after BeginPlot!"); ImGuiContext& G = *GImGui; ImDrawList& DrawList = *G.CurrentWindow->DrawList; const ImGuiStyle& Style = G.Style; ImPlotPlot &plot = *gp.CurrentPlot; // lock setup plot.SetupLocked = true; // finalize axes and set default formatter/locator for (int i = 0; i < ImAxis_COUNT; ++i) { ImPlotAxis& axis = plot.Axes[i]; if (axis.Enabled) { axis.Constrain(); if (!plot.Initialized && axis.CanInitFit()) plot.FitThisFrame = axis.FitThisFrame = true; } if (axis.Formatter == nullptr) { axis.Formatter = Formatter_Default; if (axis.HasFormatSpec) axis.FormatterData = axis.FormatSpec; else axis.FormatterData = (void*)IMPLOT_LABEL_FORMAT; } if (axis.Locator == nullptr) { axis.Locator = Locator_Default; } } // setup nullptr orthogonal axes const bool axis_equal = ImHasFlag(plot.Flags, ImPlotFlags_Equal); for (int ix = ImAxis_X1, iy = ImAxis_Y1; ix < ImAxis_Y1 || iy < ImAxis_COUNT; ++ix, ++iy) { ImPlotAxis& x_axis = plot.Axes[ix]; ImPlotAxis& y_axis = plot.Axes[iy]; if (x_axis.Enabled && y_axis.Enabled) { if (x_axis.OrthoAxis == nullptr) x_axis.OrthoAxis = &y_axis; if (y_axis.OrthoAxis == nullptr) y_axis.OrthoAxis = &x_axis; } else if (x_axis.Enabled) { if (x_axis.OrthoAxis == nullptr && !axis_equal) x_axis.OrthoAxis = &plot.Axes[ImAxis_Y1]; } else if (y_axis.Enabled) { if (y_axis.OrthoAxis == nullptr && !axis_equal) y_axis.OrthoAxis = &plot.Axes[ImAxis_X1]; } } // canvas/axes bb plot.CanvasRect = ImRect(plot.FrameRect.Min + gp.Style.PlotPadding, plot.FrameRect.Max - gp.Style.PlotPadding); plot.AxesRect = plot.FrameRect; // outside legend adjustments if (!ImHasFlag(plot.Flags, ImPlotFlags_NoLegend) && plot.Items.GetLegendCount() > 0 && ImHasFlag(plot.Items.Legend.Flags, ImPlotLegendFlags_Outside)) { ImPlotLegend& legend = plot.Items.Legend; const bool horz = ImHasFlag(legend.Flags, ImPlotLegendFlags_Horizontal); const ImVec2 legend_size = CalcLegendSize(plot.Items, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !horz); const bool west = ImHasFlag(legend.Location, ImPlotLocation_West) && !ImHasFlag(legend.Location, ImPlotLocation_East); const bool east = ImHasFlag(legend.Location, ImPlotLocation_East) && !ImHasFlag(legend.Location, ImPlotLocation_West); const bool north = ImHasFlag(legend.Location, ImPlotLocation_North) && !ImHasFlag(legend.Location, ImPlotLocation_South); const bool south = ImHasFlag(legend.Location, ImPlotLocation_South) && !ImHasFlag(legend.Location, ImPlotLocation_North); if ((west && !horz) || (west && horz && !north && !south)) { plot.CanvasRect.Min.x += (legend_size.x + gp.Style.LegendPadding.x); plot.AxesRect.Min.x += (legend_size.x + gp.Style.PlotPadding.x); } if ((east && !horz) || (east && horz && !north && !south)) { plot.CanvasRect.Max.x -= (legend_size.x + gp.Style.LegendPadding.x); plot.AxesRect.Max.x -= (legend_size.x + gp.Style.PlotPadding.x); } if ((north && horz) || (north && !horz && !west && !east)) { plot.CanvasRect.Min.y += (legend_size.y + gp.Style.LegendPadding.y); plot.AxesRect.Min.y += (legend_size.y + gp.Style.PlotPadding.y); } if ((south && horz) || (south && !horz && !west && !east)) { plot.CanvasRect.Max.y -= (legend_size.y + gp.Style.LegendPadding.y); plot.AxesRect.Max.y -= (legend_size.y + gp.Style.PlotPadding.y); } } // plot bb float pad_top = 0, pad_bot = 0, pad_left = 0, pad_right = 0; // (0) calc top padding form title ImVec2 title_size(0.0f, 0.0f); if (plot.HasTitle()) title_size = ImGui::CalcTextSize(plot.GetTitle(), nullptr, true); if (title_size.x > 0) { pad_top += title_size.y + gp.Style.LabelPadding.y; plot.AxesRect.Min.y += gp.Style.PlotPadding.y + pad_top; } // (1) calc addition top padding and bot padding PadAndDatumAxesX(plot,pad_top,pad_bot,gp.CurrentAlignmentH); const float plot_height = plot.CanvasRect.GetHeight() - pad_top - pad_bot; // (2) get y tick labels (needed for left/right pad) for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { ImPlotAxis& axis = plot.YAxis(i); if (axis.WillRender() && axis.ShowDefaultTicks) { axis.Locator(axis.Ticker, axis.Range, plot_height, true, axis.Formatter, axis.FormatterData); } } // (3) calc left/right pad PadAndDatumAxesY(plot,pad_left,pad_right,gp.CurrentAlignmentV); const float plot_width = plot.CanvasRect.GetWidth() - pad_left - pad_right; // (4) get x ticks for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { ImPlotAxis& axis = plot.XAxis(i); if (axis.WillRender() && axis.ShowDefaultTicks) { axis.Locator(axis.Ticker, axis.Range, plot_width, false, axis.Formatter, axis.FormatterData); } } // (5) calc plot bb plot.PlotRect = ImRect(plot.CanvasRect.Min + ImVec2(pad_left, pad_top), plot.CanvasRect.Max - ImVec2(pad_right, pad_bot)); // HOVER------------------------------------------------------------ // axes hover rect, pixel ranges for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) { ImPlotAxis& xax = plot.XAxis(i); xax.HoverRect = ImRect(ImVec2(plot.PlotRect.Min.x, ImMin(xax.Datum1,xax.Datum2)), ImVec2(plot.PlotRect.Max.x, ImMax(xax.Datum1,xax.Datum2))); xax.PixelMin = xax.IsInverted() ? plot.PlotRect.Max.x : plot.PlotRect.Min.x; xax.PixelMax = xax.IsInverted() ? plot.PlotRect.Min.x : plot.PlotRect.Max.x; xax.UpdateTransformCache(); } for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) { ImPlotAxis& yax = plot.YAxis(i); yax.HoverRect = ImRect(ImVec2(ImMin(yax.Datum1,yax.Datum2),plot.PlotRect.Min.y), ImVec2(ImMax(yax.Datum1,yax.Datum2),plot.PlotRect.Max.y)); yax.PixelMin = yax.IsInverted() ? plot.PlotRect.Min.y : plot.PlotRect.Max.y; yax.PixelMax = yax.IsInverted() ? plot.PlotRect.Max.y : plot.PlotRect.Min.y; yax.UpdateTransformCache(); } // Equal axis constraint. Must happen after we set Pixels // constrain equal axes for primary x and y if not approximately equal // constrains x to y since x pixel size depends on y labels width, and causes feedback loops in opposite case if (axis_equal) { for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) { ImPlotAxis& x_axis = plot.XAxis(i); if (x_axis.OrthoAxis == nullptr) continue; double xar = x_axis.GetAspect(); double yar = x_axis.OrthoAxis->GetAspect(); // edge case: user has set x range this frame, so fit y to x so that we honor their request for x range // NB: because of feedback across several frames, the user's x request may not be perfectly honored if (x_axis.HasRange) x_axis.OrthoAxis->SetAspect(xar); else if (!ImAlmostEqual(xar,yar) && !x_axis.OrthoAxis->IsInputLocked()) x_axis.SetAspect(yar); } } // INPUT ------------------------------------------------------------------ if (!ImHasFlag(plot.Flags, ImPlotFlags_NoInputs)) UpdateInput(plot); // fit from FitNextPlotAxes or auto fit for (int i = 0; i < ImAxis_COUNT; ++i) { if (gp.NextPlotData.Fit[i] || plot.Axes[i].IsAutoFitting()) { plot.FitThisFrame = true; plot.Axes[i].FitThisFrame = true; } } // RENDER ----------------------------------------------------------------- const float txt_height = ImGui::GetTextLineHeight(); // render frame if (!ImHasFlag(plot.Flags, ImPlotFlags_NoFrame)) ImGui::RenderFrame(plot.FrameRect.Min, plot.FrameRect.Max, GetStyleColorU32(ImPlotCol_FrameBg), true, Style.FrameRounding); // grid bg DrawList.AddRectFilled(plot.PlotRect.Min, plot.PlotRect.Max, GetStyleColorU32(ImPlotCol_PlotBg)); // transform ticks for (int i = 0; i < ImAxis_COUNT; i++) { ImPlotAxis& axis = plot.Axes[i]; if (axis.WillRender()) { for (int t = 0; t < axis.Ticker.TickCount(); t++) { ImPlotTick& tk = axis.Ticker.Ticks[t]; tk.PixelPos = IM_ROUND(axis.PlotToPixels(tk.PlotPos)); } } } // render grid (background) for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { ImPlotAxis& x_axis = plot.XAxis(i); if (x_axis.Enabled && x_axis.HasGridLines() && !x_axis.IsForeground()) RenderGridLinesX(DrawList, x_axis.Ticker, plot.PlotRect, x_axis.ColorMaj, x_axis.ColorMin, gp.Style.MajorGridSize.x, gp.Style.MinorGridSize.x); } for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { ImPlotAxis& y_axis = plot.YAxis(i); if (y_axis.Enabled && y_axis.HasGridLines() && !y_axis.IsForeground()) RenderGridLinesY(DrawList, y_axis.Ticker, plot.PlotRect, y_axis.ColorMaj, y_axis.ColorMin, gp.Style.MajorGridSize.y, gp.Style.MinorGridSize.y); } // render x axis button, label, tick labels for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { ImPlotAxis& ax = plot.XAxis(i); if (!ax.Enabled) continue; if ((ax.Hovered || ax.Held) && !plot.Held && !ImHasFlag(ax.Flags, ImPlotAxisFlags_NoHighlight)) DrawList.AddRectFilled(ax.HoverRect.Min, ax.HoverRect.Max, ax.Held ? ax.ColorAct : ax.ColorHov); else if (ax.ColorHiLi != IM_COL32_BLACK_TRANS) { DrawList.AddRectFilled(ax.HoverRect.Min, ax.HoverRect.Max, ax.ColorHiLi); ax.ColorHiLi = IM_COL32_BLACK_TRANS; } else if (ax.ColorBg != IM_COL32_BLACK_TRANS) { DrawList.AddRectFilled(ax.HoverRect.Min, ax.HoverRect.Max, ax.ColorBg); } const ImPlotTicker& tkr = ax.Ticker; const bool opp = ax.IsOpposite(); if (ax.HasLabel()) { const char* label = plot.GetAxisLabel(ax); const ImVec2 label_size = ImGui::CalcTextSize(label); const float label_offset = (ax.HasTickLabels() ? tkr.MaxSize.y + gp.Style.LabelPadding.y : 0.0f) + (tkr.Levels - 1) * (txt_height + gp.Style.LabelPadding.y) + gp.Style.LabelPadding.y; const ImVec2 label_pos(plot.PlotRect.GetCenter().x - label_size.x * 0.5f, opp ? ax.Datum1 - label_offset - label_size.y : ax.Datum1 + label_offset); DrawList.AddText(label_pos, ax.ColorTxt, label); } if (ax.HasTickLabels()) { for (int j = 0; j < tkr.TickCount(); ++j) { const ImPlotTick& tk = tkr.Ticks[j]; const float datum = ax.Datum1 + (opp ? (-gp.Style.LabelPadding.y -txt_height -tk.Level * (txt_height + gp.Style.LabelPadding.y)) : gp.Style.LabelPadding.y + tk.Level * (txt_height + gp.Style.LabelPadding.y)); if (tk.ShowLabel && tk.PixelPos >= plot.PlotRect.Min.x - 1 && tk.PixelPos <= plot.PlotRect.Max.x + 1) { ImVec2 start(tk.PixelPos - 0.5f * tk.LabelSize.x, datum); DrawList.AddText(start, ax.ColorTxt, tkr.GetText(j)); } } } } // render y axis button, label, tick labels for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { ImPlotAxis& ax = plot.YAxis(i); if (!ax.Enabled) continue; if ((ax.Hovered || ax.Held) && !plot.Held && !ImHasFlag(ax.Flags, ImPlotAxisFlags_NoHighlight)) DrawList.AddRectFilled(ax.HoverRect.Min, ax.HoverRect.Max, ax.Held ? ax.ColorAct : ax.ColorHov); else if (ax.ColorHiLi != IM_COL32_BLACK_TRANS) { DrawList.AddRectFilled(ax.HoverRect.Min, ax.HoverRect.Max, ax.ColorHiLi); ax.ColorHiLi = IM_COL32_BLACK_TRANS; } else if (ax.ColorBg != IM_COL32_BLACK_TRANS) { DrawList.AddRectFilled(ax.HoverRect.Min, ax.HoverRect.Max, ax.ColorBg); } const ImPlotTicker& tkr = ax.Ticker; const bool opp = ax.IsOpposite(); if (ax.HasLabel()) { const char* label = plot.GetAxisLabel(ax); const ImVec2 label_size = CalcTextSizeVertical(label); const float label_offset = (ax.HasTickLabels() ? tkr.MaxSize.x + gp.Style.LabelPadding.x : 0.0f) + gp.Style.LabelPadding.x; const ImVec2 label_pos(opp ? ax.Datum1 + label_offset : ax.Datum1 - label_offset - label_size.x, plot.PlotRect.GetCenter().y + label_size.y * 0.5f); AddTextVertical(&DrawList, label_pos, ax.ColorTxt, label); } if (ax.HasTickLabels()) { for (int j = 0; j < tkr.TickCount(); ++j) { const ImPlotTick& tk = tkr.Ticks[j]; const float datum = ax.Datum1 + (opp ? gp.Style.LabelPadding.x : (-gp.Style.LabelPadding.x - tk.LabelSize.x)); if (tk.ShowLabel && tk.PixelPos >= plot.PlotRect.Min.y - 1 && tk.PixelPos <= plot.PlotRect.Max.y + 1) { ImVec2 start(datum, tk.PixelPos - 0.5f * tk.LabelSize.y); DrawList.AddText(start, ax.ColorTxt, tkr.GetText(j)); } } } } // clear legend (TODO: put elsewhere) plot.Items.Legend.Reset(); // push ID to set item hashes (NB: !!!THIS PROBABLY NEEDS TO BE IN BEGIN PLOT!!!!) ImGui::PushOverrideID(gp.CurrentItems->ID); } //----------------------------------------------------------------------------- // EndPlot() //----------------------------------------------------------------------------- void EndPlot() { IM_ASSERT_USER_ERROR(GImPlot != nullptr, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "Mismatched BeginPlot()/EndPlot()!"); SetupLock(); ImGuiContext &G = *GImGui; ImPlotPlot &plot = *gp.CurrentPlot; ImGuiWindow * Window = G.CurrentWindow; ImDrawList & DrawList = *Window->DrawList; const ImGuiIO & IO = ImGui::GetIO(); // FINAL RENDER ----------------------------------------------------------- const bool render_border = gp.Style.PlotBorderSize > 0 && gp.Style.Colors[ImPlotCol_PlotBorder].w > 0; const bool any_x_held = plot.Held || AnyAxesHeld(&plot.Axes[ImAxis_X1], IMPLOT_NUM_X_AXES); const bool any_y_held = plot.Held || AnyAxesHeld(&plot.Axes[ImAxis_Y1], IMPLOT_NUM_Y_AXES); ImGui::PushClipRect(plot.FrameRect.Min, plot.FrameRect.Max, true); // render grid (foreground) for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { ImPlotAxis& x_axis = plot.XAxis(i); if (x_axis.Enabled && x_axis.HasGridLines() && x_axis.IsForeground()) RenderGridLinesX(DrawList, x_axis.Ticker, plot.PlotRect, x_axis.ColorMaj, x_axis.ColorMin, gp.Style.MajorGridSize.x, gp.Style.MinorGridSize.x); } for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { ImPlotAxis& y_axis = plot.YAxis(i); if (y_axis.Enabled && y_axis.HasGridLines() && y_axis.IsForeground()) RenderGridLinesY(DrawList, y_axis.Ticker, plot.PlotRect, y_axis.ColorMaj, y_axis.ColorMin, gp.Style.MajorGridSize.y, gp.Style.MinorGridSize.y); } // render title if (plot.HasTitle()) { ImU32 col = GetStyleColorU32(ImPlotCol_TitleText); AddTextCentered(&DrawList,ImVec2(plot.PlotRect.GetCenter().x, plot.CanvasRect.Min.y),col,plot.GetTitle()); } // render x ticks int count_B = 0, count_T = 0; for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { const ImPlotAxis& ax = plot.XAxis(i); if (!ax.Enabled) continue; const ImPlotTicker& tkr = ax.Ticker; const bool opp = ax.IsOpposite(); const bool aux = ((opp && count_T > 0)||(!opp && count_B > 0)); if (ax.HasTickMarks()) { const float direction = opp ? 1.0f : -1.0f; for (int j = 0; j < tkr.TickCount(); ++j) { const ImPlotTick& tk = tkr.Ticks[j]; if (tk.Level != 0 || tk.PixelPos < plot.PlotRect.Min.x || tk.PixelPos > plot.PlotRect.Max.x) continue; const ImVec2 start(tk.PixelPos, ax.Datum1); const float len = (!aux && tk.Major) ? gp.Style.MajorTickLen.x : gp.Style.MinorTickLen.x; const float thk = (!aux && tk.Major) ? gp.Style.MajorTickSize.x : gp.Style.MinorTickSize.x; DrawList.AddLine(start, start + ImVec2(0,direction*len), ax.ColorTick, thk); } if (aux || !render_border) DrawList.AddLine(ImVec2(plot.PlotRect.Min.x,ax.Datum1), ImVec2(plot.PlotRect.Max.x,ax.Datum1), ax.ColorTick, gp.Style.MinorTickSize.x); } count_B += !opp; count_T += opp; } // render y ticks int count_L = 0, count_R = 0; for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { const ImPlotAxis& ax = plot.YAxis(i); if (!ax.Enabled) continue; const ImPlotTicker& tkr = ax.Ticker; const bool opp = ax.IsOpposite(); const bool aux = ((opp && count_R > 0)||(!opp && count_L > 0)); if (ax.HasTickMarks()) { const float direction = opp ? -1.0f : 1.0f; for (int j = 0; j < tkr.TickCount(); ++j) { const ImPlotTick& tk = tkr.Ticks[j]; if (tk.Level != 0 || tk.PixelPos < plot.PlotRect.Min.y || tk.PixelPos > plot.PlotRect.Max.y) continue; const ImVec2 start(ax.Datum1, tk.PixelPos); const float len = (!aux && tk.Major) ? gp.Style.MajorTickLen.y : gp.Style.MinorTickLen.y; const float thk = (!aux && tk.Major) ? gp.Style.MajorTickSize.y : gp.Style.MinorTickSize.y; DrawList.AddLine(start, start + ImVec2(direction*len,0), ax.ColorTick, thk); } if (aux || !render_border) DrawList.AddLine(ImVec2(ax.Datum1, plot.PlotRect.Min.y), ImVec2(ax.Datum1, plot.PlotRect.Max.y), ax.ColorTick, gp.Style.MinorTickSize.y); } count_L += !opp; count_R += opp; } ImGui::PopClipRect(); // render annotations PushPlotClipRect(); for (int i = 0; i < gp.Annotations.Size; ++i) { const char* txt = gp.Annotations.GetText(i); ImPlotAnnotation& an = gp.Annotations.Annotations[i]; const ImVec2 txt_size = ImGui::CalcTextSize(txt); const ImVec2 size = txt_size + gp.Style.AnnotationPadding * 2; ImVec2 pos = an.Pos; if (an.Offset.x == 0) pos.x -= size.x / 2; else if (an.Offset.x > 0) pos.x += an.Offset.x; else pos.x -= size.x - an.Offset.x; if (an.Offset.y == 0) pos.y -= size.y / 2; else if (an.Offset.y > 0) pos.y += an.Offset.y; else pos.y -= size.y - an.Offset.y; if (an.Clamp) pos = ClampLabelPos(pos, size, plot.PlotRect.Min, plot.PlotRect.Max); ImRect rect(pos,pos+size); if (an.Offset.x != 0 || an.Offset.y != 0) { ImVec2 corners[4] = {rect.GetTL(), rect.GetTR(), rect.GetBR(), rect.GetBL()}; int min_corner = 0; float min_len = FLT_MAX; for (int c = 0; c < 4; ++c) { float len = ImLengthSqr(an.Pos - corners[c]); if (len < min_len) { min_corner = c; min_len = len; } } DrawList.AddLine(an.Pos, corners[min_corner], an.ColorBg); } DrawList.AddRectFilled(rect.Min, rect.Max, an.ColorBg); DrawList.AddText(pos + gp.Style.AnnotationPadding, an.ColorFg, txt); } // render selection if (plot.Selected) RenderSelectionRect(DrawList, plot.SelectRect.Min + plot.PlotRect.Min, plot.SelectRect.Max + plot.PlotRect.Min, GetStyleColorVec4(ImPlotCol_Selection)); // render crosshairs if (ImHasFlag(plot.Flags, ImPlotFlags_Crosshairs) && plot.Hovered && !(any_x_held || any_y_held) && !plot.Selecting && !plot.Items.Legend.Hovered) { ImGui::SetMouseCursor(ImGuiMouseCursor_None); ImVec2 xy = IO.MousePos; ImVec2 h1(plot.PlotRect.Min.x, xy.y); ImVec2 h2(xy.x - 5, xy.y); ImVec2 h3(xy.x + 5, xy.y); ImVec2 h4(plot.PlotRect.Max.x, xy.y); ImVec2 v1(xy.x, plot.PlotRect.Min.y); ImVec2 v2(xy.x, xy.y - 5); ImVec2 v3(xy.x, xy.y + 5); ImVec2 v4(xy.x, plot.PlotRect.Max.y); ImU32 col = GetStyleColorU32(ImPlotCol_Crosshairs); DrawList.AddLine(h1, h2, col); DrawList.AddLine(h3, h4, col); DrawList.AddLine(v1, v2, col); DrawList.AddLine(v3, v4, col); } // render mouse pos if (!ImHasFlag(plot.Flags, ImPlotFlags_NoMouseText) && (plot.Hovered || ImHasFlag(plot.MouseTextFlags, ImPlotMouseTextFlags_ShowAlways))) { const bool no_aux = ImHasFlag(plot.MouseTextFlags, ImPlotMouseTextFlags_NoAuxAxes); const bool no_fmt = ImHasFlag(plot.MouseTextFlags, ImPlotMouseTextFlags_NoFormat); ImGuiTextBuffer& builder = gp.MousePosStringBuilder; builder.Buf.shrink(0); char buff[IMPLOT_LABEL_MAX_SIZE]; const int num_x = no_aux ? 1 : IMPLOT_NUM_X_AXES; for (int i = 0; i < num_x; ++i) { ImPlotAxis& x_axis = plot.XAxis(i); if (!x_axis.Enabled) continue; if (i > 0) builder.append(", ("); double v = x_axis.PixelsToPlot(IO.MousePos.x); if (no_fmt) Formatter_Default(v,buff,IMPLOT_LABEL_MAX_SIZE,(void*)IMPLOT_LABEL_FORMAT); else LabelAxisValue(x_axis,v,buff,IMPLOT_LABEL_MAX_SIZE,true); builder.append(buff); if (i > 0) builder.append(")"); } builder.append(", "); const int num_y = no_aux ? 1 : IMPLOT_NUM_Y_AXES; for (int i = 0; i < num_y; ++i) { ImPlotAxis& y_axis = plot.YAxis(i); if (!y_axis.Enabled) continue; if (i > 0) builder.append(", ("); double v = y_axis.PixelsToPlot(IO.MousePos.y); if (no_fmt) Formatter_Default(v,buff,IMPLOT_LABEL_MAX_SIZE,(void*)IMPLOT_LABEL_FORMAT); else LabelAxisValue(y_axis,v,buff,IMPLOT_LABEL_MAX_SIZE,true); builder.append(buff); if (i > 0) builder.append(")"); } if (!builder.empty()) { const ImVec2 size = ImGui::CalcTextSize(builder.c_str()); const ImVec2 pos = GetLocationPos(plot.PlotRect, size, plot.MouseTextLocation, gp.Style.MousePosPadding); DrawList.AddText(pos, GetStyleColorU32(ImPlotCol_InlayText), builder.c_str()); } } PopPlotClipRect(); // axis side switch if (!plot.Held) { ImVec2 mouse_pos = ImGui::GetIO().MousePos; ImRect trigger_rect = plot.PlotRect; trigger_rect.Expand(-10); for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) { ImPlotAxis& x_axis = plot.XAxis(i); if (ImHasFlag(x_axis.Flags, ImPlotAxisFlags_NoSideSwitch)) continue; if (x_axis.Held && plot.PlotRect.Contains(mouse_pos)) { const bool opp = ImHasFlag(x_axis.Flags, ImPlotAxisFlags_Opposite); if (!opp) { ImRect rect(plot.PlotRect.Min.x - 5, plot.PlotRect.Min.y - 5, plot.PlotRect.Max.x + 5, plot.PlotRect.Min.y + 5); if (mouse_pos.y < plot.PlotRect.Max.y - 10) DrawList.AddRectFilled(rect.Min, rect.Max, x_axis.ColorHov); if (rect.Contains(mouse_pos)) x_axis.Flags |= ImPlotAxisFlags_Opposite; } else { ImRect rect(plot.PlotRect.Min.x - 5, plot.PlotRect.Max.y - 5, plot.PlotRect.Max.x + 5, plot.PlotRect.Max.y + 5); if (mouse_pos.y > plot.PlotRect.Min.y + 10) DrawList.AddRectFilled(rect.Min, rect.Max, x_axis.ColorHov); if (rect.Contains(mouse_pos)) x_axis.Flags &= ~ImPlotAxisFlags_Opposite; } } } for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) { ImPlotAxis& y_axis = plot.YAxis(i); if (ImHasFlag(y_axis.Flags, ImPlotAxisFlags_NoSideSwitch)) continue; if (y_axis.Held && plot.PlotRect.Contains(mouse_pos)) { const bool opp = ImHasFlag(y_axis.Flags, ImPlotAxisFlags_Opposite); if (!opp) { ImRect rect(plot.PlotRect.Max.x - 5, plot.PlotRect.Min.y - 5, plot.PlotRect.Max.x + 5, plot.PlotRect.Max.y + 5); if (mouse_pos.x > plot.PlotRect.Min.x + 10) DrawList.AddRectFilled(rect.Min, rect.Max, y_axis.ColorHov); if (rect.Contains(mouse_pos)) y_axis.Flags |= ImPlotAxisFlags_Opposite; } else { ImRect rect(plot.PlotRect.Min.x - 5, plot.PlotRect.Min.y - 5, plot.PlotRect.Min.x + 5, plot.PlotRect.Max.y + 5); if (mouse_pos.x < plot.PlotRect.Max.x - 10) DrawList.AddRectFilled(rect.Min, rect.Max, y_axis.ColorHov); if (rect.Contains(mouse_pos)) y_axis.Flags &= ~ImPlotAxisFlags_Opposite; } } } } // reset legend hovers plot.Items.Legend.Hovered = false; for (int i = 0; i < plot.Items.GetItemCount(); ++i) plot.Items.GetItemByIndex(i)->LegendHovered = false; // render legend if (!ImHasFlag(plot.Flags, ImPlotFlags_NoLegend) && plot.Items.GetLegendCount() > 0) { ImPlotLegend& legend = plot.Items.Legend; const bool legend_out = ImHasFlag(legend.Flags, ImPlotLegendFlags_Outside); const bool legend_horz = ImHasFlag(legend.Flags, ImPlotLegendFlags_Horizontal); const ImVec2 legend_size = CalcLegendSize(plot.Items, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !legend_horz); const ImVec2 legend_pos = GetLocationPos(legend_out ? plot.FrameRect : plot.PlotRect, legend_size, legend.Location, legend_out ? gp.Style.PlotPadding : gp.Style.LegendPadding); legend.Rect = ImRect(legend_pos, legend_pos + legend_size); // test hover legend.Hovered = ImGui::IsWindowHovered() && legend.Rect.Contains(IO.MousePos); if (legend_out) ImGui::PushClipRect(plot.FrameRect.Min, plot.FrameRect.Max, true); else PushPlotClipRect(); ImU32 col_bg = GetStyleColorU32(ImPlotCol_LegendBg); ImU32 col_bd = GetStyleColorU32(ImPlotCol_LegendBorder); DrawList.AddRectFilled(legend.Rect.Min, legend.Rect.Max, col_bg); DrawList.AddRect(legend.Rect.Min, legend.Rect.Max, col_bd); bool legend_contextable = ShowLegendEntries(plot.Items, legend.Rect, legend.Hovered, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !legend_horz, DrawList) && !ImHasFlag(legend.Flags, ImPlotLegendFlags_NoMenus); // main ctx menu if (gp.OpenContextThisFrame && legend_contextable && !ImHasFlag(plot.Flags, ImPlotFlags_NoMenus)) ImGui::OpenPopup("##LegendContext"); ImGui::PopClipRect(); if (ImGui::BeginPopup("##LegendContext")) { ImGui::Text("Legend"); ImGui::Separator(); if (ShowLegendContextMenu(legend, !ImHasFlag(plot.Flags, ImPlotFlags_NoLegend))) ImFlipFlag(plot.Flags, ImPlotFlags_NoLegend); ImGui::EndPopup(); } } else { plot.Items.Legend.Rect = ImRect(); } // render border if (render_border) DrawList.AddRect(plot.PlotRect.Min, plot.PlotRect.Max, GetStyleColorU32(ImPlotCol_PlotBorder), 0, ImDrawFlags_RoundCornersAll, gp.Style.PlotBorderSize); // render tags for (int i = 0; i < gp.Tags.Size; ++i) { ImPlotTag& tag = gp.Tags.Tags[i]; ImPlotAxis& axis = plot.Axes[tag.Axis]; if (!axis.Enabled || !axis.Range.Contains(tag.Value)) continue; const char* txt = gp.Tags.GetText(i); ImVec2 text_size = ImGui::CalcTextSize(txt); ImVec2 size = text_size + gp.Style.AnnotationPadding * 2; ImVec2 pos; axis.Ticker.OverrideSizeLate(size); float pix = IM_ROUND(axis.PlotToPixels(tag.Value)); if (axis.Vertical) { if (axis.IsOpposite()) { pos = ImVec2(axis.Datum1 + gp.Style.LabelPadding.x, pix - size.y * 0.5f); DrawList.AddTriangleFilled(ImVec2(axis.Datum1,pix), pos, pos + ImVec2(0,size.y), tag.ColorBg); } else { pos = ImVec2(axis.Datum1 - size.x - gp.Style.LabelPadding.x, pix - size.y * 0.5f); DrawList.AddTriangleFilled(pos + ImVec2(size.x,0), ImVec2(axis.Datum1,pix), pos+size, tag.ColorBg); } } else { if (axis.IsOpposite()) { pos = ImVec2(pix - size.x * 0.5f, axis.Datum1 - size.y - gp.Style.LabelPadding.y ); DrawList.AddTriangleFilled(pos + ImVec2(0,size.y), pos + size, ImVec2(pix,axis.Datum1), tag.ColorBg); } else { pos = ImVec2(pix - size.x * 0.5f, axis.Datum1 + gp.Style.LabelPadding.y); DrawList.AddTriangleFilled(pos, ImVec2(pix,axis.Datum1), pos + ImVec2(size.x, 0), tag.ColorBg); } } DrawList.AddRectFilled(pos,pos+size,tag.ColorBg); DrawList.AddText(pos+gp.Style.AnnotationPadding,tag.ColorFg,txt); } // FIT DATA -------------------------------------------------------------- const bool axis_equal = ImHasFlag(plot.Flags, ImPlotFlags_Equal); if (plot.FitThisFrame) { for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { ImPlotAxis& x_axis = plot.XAxis(i); if (x_axis.FitThisFrame) { x_axis.ApplyFit(gp.Style.FitPadding.x); if (axis_equal && x_axis.OrthoAxis != nullptr) { double aspect = x_axis.GetAspect(); ImPlotAxis& y_axis = *x_axis.OrthoAxis; if (y_axis.FitThisFrame) { y_axis.ApplyFit(gp.Style.FitPadding.y); y_axis.FitThisFrame = false; aspect = ImMax(aspect, y_axis.GetAspect()); } x_axis.SetAspect(aspect); y_axis.SetAspect(aspect); } } } for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { ImPlotAxis& y_axis = plot.YAxis(i); if (y_axis.FitThisFrame) { y_axis.ApplyFit(gp.Style.FitPadding.y); if (axis_equal && y_axis.OrthoAxis != nullptr) { double aspect = y_axis.GetAspect(); ImPlotAxis& x_axis = *y_axis.OrthoAxis; if (x_axis.FitThisFrame) { x_axis.ApplyFit(gp.Style.FitPadding.x); x_axis.FitThisFrame = false; aspect = ImMax(x_axis.GetAspect(), aspect); } x_axis.SetAspect(aspect); y_axis.SetAspect(aspect); } } } plot.FitThisFrame = false; } // CONTEXT MENUS ----------------------------------------------------------- ImGui::PushOverrideID(plot.ID); const bool can_ctx = gp.OpenContextThisFrame && !ImHasFlag(plot.Flags, ImPlotFlags_NoMenus) && !plot.Items.Legend.Hovered; // main ctx menu if (can_ctx && plot.Hovered) ImGui::OpenPopup("##PlotContext"); if (ImGui::BeginPopup("##PlotContext")) { ShowPlotContextMenu(plot); ImGui::EndPopup(); } // axes ctx menus for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) { ImGui::PushID(i); ImPlotAxis& x_axis = plot.XAxis(i); if (can_ctx && x_axis.Hovered && x_axis.HasMenus()) ImGui::OpenPopup("##XContext"); if (ImGui::BeginPopup("##XContext")) { ImGui::Text(x_axis.HasLabel() ? plot.GetAxisLabel(x_axis) : i == 0 ? "X-Axis" : "X-Axis %d", i + 1); ImGui::Separator(); ShowAxisContextMenu(x_axis, axis_equal ? x_axis.OrthoAxis : nullptr, true); ImGui::EndPopup(); } ImGui::PopID(); } for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) { ImGui::PushID(i); ImPlotAxis& y_axis = plot.YAxis(i); if (can_ctx && y_axis.Hovered && y_axis.HasMenus()) ImGui::OpenPopup("##YContext"); if (ImGui::BeginPopup("##YContext")) { ImGui::Text(y_axis.HasLabel() ? plot.GetAxisLabel(y_axis) : i == 0 ? "Y-Axis" : "Y-Axis %d", i + 1); ImGui::Separator(); ShowAxisContextMenu(y_axis, axis_equal ? y_axis.OrthoAxis : nullptr, false); ImGui::EndPopup(); } ImGui::PopID(); } ImGui::PopID(); // LINKED AXES ------------------------------------------------------------ for (int i = 0; i < ImAxis_COUNT; ++i) plot.Axes[i].PushLinks(); // CLEANUP ---------------------------------------------------------------- // remove items if (gp.CurrentItems == &plot.Items) gp.CurrentItems = nullptr; // reset the plot items for the next frame for (int i = 0; i < plot.Items.GetItemCount(); ++i) { plot.Items.GetItemByIndex(i)->SeenThisFrame = false; } // mark the plot as initialized, i.e. having made it through one frame completely plot.Initialized = true; // Pop ImGui::PushID at the end of BeginPlot ImGui::PopID(); // Reset context for next plot ResetCtxForNextPlot(GImPlot); // setup next subplot if (gp.CurrentSubplot != nullptr) { ImGui::PopID(); SubplotNextCell(); } } //----------------------------------------------------------------------------- // BEGIN/END SUBPLOT //----------------------------------------------------------------------------- static const float SUBPLOT_BORDER_SIZE = 1.0f; static const float SUBPLOT_SPLITTER_HALF_THICKNESS = 4.0f; static const float SUBPLOT_SPLITTER_FEEDBACK_TIMER = 0.06f; void SubplotSetCell(int row, int col) { ImPlotContext& gp = *GImPlot; ImPlotSubplot& subplot = *gp.CurrentSubplot; if (row >= subplot.Rows || col >= subplot.Cols) return; float xoff = 0; float yoff = 0; for (int c = 0; c < col; ++c) xoff += subplot.ColRatios[c]; for (int r = 0; r < row; ++r) yoff += subplot.RowRatios[r]; const ImVec2 grid_size = subplot.GridRect.GetSize(); ImVec2 cpos = subplot.GridRect.Min + ImVec2(xoff*grid_size.x,yoff*grid_size.y); cpos.x = IM_ROUND(cpos.x); cpos.y = IM_ROUND(cpos.y); ImGui::GetCurrentWindow()->DC.CursorPos = cpos; // set cell size subplot.CellSize.x = IM_ROUND(subplot.GridRect.GetWidth() * subplot.ColRatios[col]); subplot.CellSize.y = IM_ROUND(subplot.GridRect.GetHeight() * subplot.RowRatios[row]); // setup links const bool lx = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkAllX); const bool ly = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkAllY); const bool lr = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkRows); const bool lc = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkCols); SetNextAxisLinks(ImAxis_X1, lx ? &subplot.ColLinkData[0].Min : lc ? &subplot.ColLinkData[col].Min : nullptr, lx ? &subplot.ColLinkData[0].Max : lc ? &subplot.ColLinkData[col].Max : nullptr); SetNextAxisLinks(ImAxis_Y1, ly ? &subplot.RowLinkData[0].Min : lr ? &subplot.RowLinkData[row].Min : nullptr, ly ? &subplot.RowLinkData[0].Max : lr ? &subplot.RowLinkData[row].Max : nullptr); // setup alignment if (!ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoAlign)) { gp.CurrentAlignmentH = &subplot.RowAlignmentData[row]; gp.CurrentAlignmentV = &subplot.ColAlignmentData[col]; } // set idx if (ImHasFlag(subplot.Flags, ImPlotSubplotFlags_ColMajor)) subplot.CurrentIdx = col * subplot.Rows + row; else subplot.CurrentIdx = row * subplot.Cols + col; } void SubplotSetCell(int idx) { ImPlotContext& gp = *GImPlot; ImPlotSubplot& subplot = *gp.CurrentSubplot; if (idx >= subplot.Rows * subplot.Cols) return; int row = 0, col = 0; if (ImHasFlag(subplot.Flags, ImPlotSubplotFlags_ColMajor)) { row = idx % subplot.Rows; col = idx / subplot.Rows; } else { row = idx / subplot.Cols; col = idx % subplot.Cols; } return SubplotSetCell(row, col); } void SubplotNextCell() { ImPlotContext& gp = *GImPlot; ImPlotSubplot& subplot = *gp.CurrentSubplot; SubplotSetCell(++subplot.CurrentIdx); } bool BeginSubplots(const char* title, int rows, int cols, const ImVec2& size, ImPlotSubplotFlags flags, float* row_sizes, float* col_sizes) { IM_ASSERT_USER_ERROR(rows > 0 && cols > 0, "Invalid sizing arguments!"); IM_ASSERT_USER_ERROR(GImPlot != nullptr, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentSubplot == nullptr, "Mismatched BeginSubplots()/EndSubplots()!"); ImGuiContext &G = *GImGui; ImGuiWindow * Window = G.CurrentWindow; if (Window->SkipItems) return false; const ImGuiID ID = Window->GetID(title); bool just_created = gp.Subplots.GetByKey(ID) == nullptr; gp.CurrentSubplot = gp.Subplots.GetOrAddByKey(ID); ImPlotSubplot& subplot = *gp.CurrentSubplot; subplot.ID = ID; subplot.Items.ID = ID - 1; subplot.HasTitle = ImGui::FindRenderedTextEnd(title, nullptr) != title; // push ID ImGui::PushID(ID); if (just_created) subplot.Flags = flags; else if (flags != subplot.PreviousFlags) subplot.Flags = flags; subplot.PreviousFlags = flags; // check for change in rows and cols if (subplot.Rows != rows || subplot.Cols != cols) { subplot.RowAlignmentData.resize(rows); subplot.RowLinkData.resize(rows); subplot.RowRatios.resize(rows); for (int r = 0; r < rows; ++r) { subplot.RowAlignmentData[r].Reset(); subplot.RowLinkData[r] = ImPlotRange(0,1); subplot.RowRatios[r] = 1.0f / rows; } subplot.ColAlignmentData.resize(cols); subplot.ColLinkData.resize(cols); subplot.ColRatios.resize(cols); for (int c = 0; c < cols; ++c) { subplot.ColAlignmentData[c].Reset(); subplot.ColLinkData[c] = ImPlotRange(0,1); subplot.ColRatios[c] = 1.0f / cols; } } // check incoming size requests float row_sum = 0, col_sum = 0; if (row_sizes != nullptr) { row_sum = ImSum(row_sizes, rows); for (int r = 0; r < rows; ++r) subplot.RowRatios[r] = row_sizes[r] / row_sum; } if (col_sizes != nullptr) { col_sum = ImSum(col_sizes, cols); for (int c = 0; c < cols; ++c) subplot.ColRatios[c] = col_sizes[c] / col_sum; } subplot.Rows = rows; subplot.Cols = cols; // calc plot frame sizes ImVec2 title_size(0.0f, 0.0f); if (!ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoTitle)) title_size = ImGui::CalcTextSize(title, nullptr, true); const float pad_top = title_size.x > 0.0f ? title_size.y + gp.Style.LabelPadding.y : 0; const ImVec2 half_pad = gp.Style.PlotPadding/2; const ImVec2 frame_size = ImGui::CalcItemSize(size, gp.Style.PlotDefaultSize.x, gp.Style.PlotDefaultSize.y); subplot.FrameRect = ImRect(Window->DC.CursorPos, Window->DC.CursorPos + frame_size); subplot.GridRect.Min = subplot.FrameRect.Min + half_pad + ImVec2(0,pad_top); subplot.GridRect.Max = subplot.FrameRect.Max - half_pad; subplot.FrameHovered = subplot.FrameRect.Contains(ImGui::GetMousePos()) && ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows); // outside legend adjustments (TODO: make function) const bool share_items = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_ShareItems); if (share_items) gp.CurrentItems = &subplot.Items; if (share_items && !ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoLegend) && subplot.Items.GetLegendCount() > 0) { ImPlotLegend& legend = subplot.Items.Legend; const bool horz = ImHasFlag(legend.Flags, ImPlotLegendFlags_Horizontal); const ImVec2 legend_size = CalcLegendSize(subplot.Items, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !horz); const bool west = ImHasFlag(legend.Location, ImPlotLocation_West) && !ImHasFlag(legend.Location, ImPlotLocation_East); const bool east = ImHasFlag(legend.Location, ImPlotLocation_East) && !ImHasFlag(legend.Location, ImPlotLocation_West); const bool north = ImHasFlag(legend.Location, ImPlotLocation_North) && !ImHasFlag(legend.Location, ImPlotLocation_South); const bool south = ImHasFlag(legend.Location, ImPlotLocation_South) && !ImHasFlag(legend.Location, ImPlotLocation_North); if ((west && !horz) || (west && horz && !north && !south)) subplot.GridRect.Min.x += (legend_size.x + gp.Style.LegendPadding.x); if ((east && !horz) || (east && horz && !north && !south)) subplot.GridRect.Max.x -= (legend_size.x + gp.Style.LegendPadding.x); if ((north && horz) || (north && !horz && !west && !east)) subplot.GridRect.Min.y += (legend_size.y + gp.Style.LegendPadding.y); if ((south && horz) || (south && !horz && !west && !east)) subplot.GridRect.Max.y -= (legend_size.y + gp.Style.LegendPadding.y); } // render single background frame ImGui::RenderFrame(subplot.FrameRect.Min, subplot.FrameRect.Max, GetStyleColorU32(ImPlotCol_FrameBg), true, ImGui::GetStyle().FrameRounding); // render title if (title_size.x > 0.0f && !ImHasFlag(subplot.Flags, ImPlotFlags_NoTitle)) { const ImU32 col = GetStyleColorU32(ImPlotCol_TitleText); AddTextCentered(ImGui::GetWindowDrawList(),ImVec2(subplot.GridRect.GetCenter().x, subplot.GridRect.Min.y - pad_top + half_pad.y),col,title); } // render splitters if (!ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoResize)) { ImDrawList& DrawList = *ImGui::GetWindowDrawList(); const ImU32 hov_col = ImGui::ColorConvertFloat4ToU32(GImGui->Style.Colors[ImGuiCol_SeparatorHovered]); const ImU32 act_col = ImGui::ColorConvertFloat4ToU32(GImGui->Style.Colors[ImGuiCol_SeparatorActive]); float xpos = subplot.GridRect.Min.x; float ypos = subplot.GridRect.Min.y; int separator = 1; // bool pass = false; for (int r = 0; r < subplot.Rows-1; ++r) { ypos += subplot.RowRatios[r] * subplot.GridRect.GetHeight(); const ImGuiID sep_id = subplot.ID + separator; ImGui::KeepAliveID(sep_id); const ImRect sep_bb = ImRect(subplot.GridRect.Min.x, ypos-SUBPLOT_SPLITTER_HALF_THICKNESS, subplot.GridRect.Max.x, ypos+SUBPLOT_SPLITTER_HALF_THICKNESS); bool sep_hov = false, sep_hld = false; const bool sep_clk = ImGui::ButtonBehavior(sep_bb, sep_id, &sep_hov, &sep_hld, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick); if ((sep_hov && G.HoveredIdTimer > SUBPLOT_SPLITTER_FEEDBACK_TIMER) || sep_hld) { if (sep_clk && ImGui::IsMouseDoubleClicked(0)) { float p = (subplot.RowRatios[r] + subplot.RowRatios[r+1])/2; subplot.RowRatios[r] = subplot.RowRatios[r+1] = p; } if (sep_clk) { subplot.TempSizes[0] = subplot.RowRatios[r]; subplot.TempSizes[1] = subplot.RowRatios[r+1]; } if (sep_hld) { float dp = ImGui::GetMouseDragDelta(0).y / subplot.GridRect.GetHeight(); if (subplot.TempSizes[0] + dp > 0.1f && subplot.TempSizes[1] - dp > 0.1f) { subplot.RowRatios[r] = subplot.TempSizes[0] + dp; subplot.RowRatios[r+1] = subplot.TempSizes[1] - dp; } } DrawList.AddLine(ImVec2(IM_ROUND(subplot.GridRect.Min.x),IM_ROUND(ypos)), ImVec2(IM_ROUND(subplot.GridRect.Max.x),IM_ROUND(ypos)), sep_hld ? act_col : hov_col, SUBPLOT_BORDER_SIZE); ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS); } separator++; } for (int c = 0; c < subplot.Cols-1; ++c) { xpos += subplot.ColRatios[c] * subplot.GridRect.GetWidth(); const ImGuiID sep_id = subplot.ID + separator; ImGui::KeepAliveID(sep_id); const ImRect sep_bb = ImRect(xpos-SUBPLOT_SPLITTER_HALF_THICKNESS, subplot.GridRect.Min.y, xpos+SUBPLOT_SPLITTER_HALF_THICKNESS, subplot.GridRect.Max.y); bool sep_hov = false, sep_hld = false; const bool sep_clk = ImGui::ButtonBehavior(sep_bb, sep_id, &sep_hov, &sep_hld, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick); if ((sep_hov && G.HoveredIdTimer > SUBPLOT_SPLITTER_FEEDBACK_TIMER) || sep_hld) { if (sep_clk && ImGui::IsMouseDoubleClicked(0)) { float p = (subplot.ColRatios[c] + subplot.ColRatios[c+1])/2; subplot.ColRatios[c] = subplot.ColRatios[c+1] = p; } if (sep_clk) { subplot.TempSizes[0] = subplot.ColRatios[c]; subplot.TempSizes[1] = subplot.ColRatios[c+1]; } if (sep_hld) { float dp = ImGui::GetMouseDragDelta(0).x / subplot.GridRect.GetWidth(); if (subplot.TempSizes[0] + dp > 0.1f && subplot.TempSizes[1] - dp > 0.1f) { subplot.ColRatios[c] = subplot.TempSizes[0] + dp; subplot.ColRatios[c+1] = subplot.TempSizes[1] - dp; } } DrawList.AddLine(ImVec2(IM_ROUND(xpos),IM_ROUND(subplot.GridRect.Min.y)), ImVec2(IM_ROUND(xpos),IM_ROUND(subplot.GridRect.Max.y)), sep_hld ? act_col : hov_col, SUBPLOT_BORDER_SIZE); ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); } separator++; } } // set outgoing sizes if (row_sizes != nullptr) { for (int r = 0; r < rows; ++r) row_sizes[r] = subplot.RowRatios[r] * row_sum; } if (col_sizes != nullptr) { for (int c = 0; c < cols; ++c) col_sizes[c] = subplot.ColRatios[c] * col_sum; } // push styling PushStyleColor(ImPlotCol_FrameBg, IM_COL32_BLACK_TRANS); PushStyleVar(ImPlotStyleVar_PlotPadding, half_pad); PushStyleVar(ImPlotStyleVar_PlotMinSize, ImVec2(0,0)); ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize,0); // set initial cursor pos Window->DC.CursorPos = subplot.GridRect.Min; // begin alignments for (int r = 0; r < subplot.Rows; ++r) subplot.RowAlignmentData[r].Begin(); for (int c = 0; c < subplot.Cols; ++c) subplot.ColAlignmentData[c].Begin(); // clear legend data subplot.Items.Legend.Reset(); // Setup first subplot SubplotSetCell(0,0); return true; } void EndSubplots() { IM_ASSERT_USER_ERROR(GImPlot != nullptr, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentSubplot != nullptr, "Mismatched BeginSubplots()/EndSubplots()!"); ImPlotSubplot& subplot = *gp.CurrentSubplot; // set alignments for (int r = 0; r < subplot.Rows; ++r) subplot.RowAlignmentData[r].End(); for (int c = 0; c < subplot.Cols; ++c) subplot.ColAlignmentData[c].End(); // pop styling PopStyleColor(); PopStyleVar(); PopStyleVar(); ImGui::PopStyleVar(); // legend subplot.Items.Legend.Hovered = false; for (int i = 0; i < subplot.Items.GetItemCount(); ++i) subplot.Items.GetItemByIndex(i)->LegendHovered = false; // render legend const bool share_items = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_ShareItems); ImDrawList& DrawList = *ImGui::GetWindowDrawList(); if (share_items && !ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoLegend) && subplot.Items.GetLegendCount() > 0) { const bool legend_horz = ImHasFlag(subplot.Items.Legend.Flags, ImPlotLegendFlags_Horizontal); const ImVec2 legend_size = CalcLegendSize(subplot.Items, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !legend_horz); const ImVec2 legend_pos = GetLocationPos(subplot.FrameRect, legend_size, subplot.Items.Legend.Location, gp.Style.PlotPadding); subplot.Items.Legend.Rect = ImRect(legend_pos, legend_pos + legend_size); subplot.Items.Legend.Hovered = subplot.FrameHovered && subplot.Items.Legend.Rect.Contains(ImGui::GetIO().MousePos); ImGui::PushClipRect(subplot.FrameRect.Min, subplot.FrameRect.Max, true); ImU32 col_bg = GetStyleColorU32(ImPlotCol_LegendBg); ImU32 col_bd = GetStyleColorU32(ImPlotCol_LegendBorder); DrawList.AddRectFilled(subplot.Items.Legend.Rect.Min, subplot.Items.Legend.Rect.Max, col_bg); DrawList.AddRect(subplot.Items.Legend.Rect.Min, subplot.Items.Legend.Rect.Max, col_bd); bool legend_contextable = ShowLegendEntries(subplot.Items, subplot.Items.Legend.Rect, subplot.Items.Legend.Hovered, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !legend_horz, DrawList) && !ImHasFlag(subplot.Items.Legend.Flags, ImPlotLegendFlags_NoMenus); if (legend_contextable && !ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoMenus) && ImGui::GetIO().MouseReleased[gp.InputMap.Menu]) ImGui::OpenPopup("##LegendContext"); ImGui::PopClipRect(); if (ImGui::BeginPopup("##LegendContext")) { ImGui::Text("Legend"); ImGui::Separator(); if (ShowLegendContextMenu(subplot.Items.Legend, !ImHasFlag(subplot.Flags, ImPlotFlags_NoLegend))) ImFlipFlag(subplot.Flags, ImPlotFlags_NoLegend); ImGui::EndPopup(); } } else { subplot.Items.Legend.Rect = ImRect(); } // remove items if (gp.CurrentItems == &subplot.Items) gp.CurrentItems = nullptr; // reset the plot items for the next frame (TODO: put this elswhere) for (int i = 0; i < subplot.Items.GetItemCount(); ++i) { subplot.Items.GetItemByIndex(i)->SeenThisFrame = false; } // pop id ImGui::PopID(); // set DC back correctly GImGui->CurrentWindow->DC.CursorPos = subplot.FrameRect.Min; ImGui::Dummy(subplot.FrameRect.GetSize()); ResetCtxForNextSubplot(GImPlot); } //----------------------------------------------------------------------------- // [SECTION] Plot Utils //----------------------------------------------------------------------------- void SetAxis(ImAxis axis) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "SetAxis() needs to be called between BeginPlot() and EndPlot()!"); IM_ASSERT_USER_ERROR(axis >= ImAxis_X1 && axis < ImAxis_COUNT, "Axis index out of bounds!"); IM_ASSERT_USER_ERROR(gp.CurrentPlot->Axes[axis].Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); SetupLock(); if (axis < ImAxis_Y1) gp.CurrentPlot->CurrentX = axis; else gp.CurrentPlot->CurrentY = axis; } void SetAxes(ImAxis x_idx, ImAxis y_idx) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "SetAxes() needs to be called between BeginPlot() and EndPlot()!"); IM_ASSERT_USER_ERROR(x_idx >= ImAxis_X1 && x_idx < ImAxis_Y1, "X-Axis index out of bounds!"); IM_ASSERT_USER_ERROR(y_idx >= ImAxis_Y1 && y_idx < ImAxis_COUNT, "Y-Axis index out of bounds!"); IM_ASSERT_USER_ERROR(gp.CurrentPlot->Axes[x_idx].Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); IM_ASSERT_USER_ERROR(gp.CurrentPlot->Axes[y_idx].Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); SetupLock(); gp.CurrentPlot->CurrentX = x_idx; gp.CurrentPlot->CurrentY = y_idx; } ImPlotPoint PixelsToPlot(float x, float y, ImAxis x_idx, ImAxis y_idx) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "PixelsToPlot() needs to be called between BeginPlot() and EndPlot()!"); IM_ASSERT_USER_ERROR(x_idx == IMPLOT_AUTO || (x_idx >= ImAxis_X1 && x_idx < ImAxis_Y1), "X-Axis index out of bounds!"); IM_ASSERT_USER_ERROR(y_idx == IMPLOT_AUTO || (y_idx >= ImAxis_Y1 && y_idx < ImAxis_COUNT), "Y-Axis index out of bounds!"); SetupLock(); ImPlotPlot& plot = *gp.CurrentPlot; ImPlotAxis& x_axis = x_idx == IMPLOT_AUTO ? plot.Axes[plot.CurrentX] : plot.Axes[x_idx]; ImPlotAxis& y_axis = y_idx == IMPLOT_AUTO ? plot.Axes[plot.CurrentY] : plot.Axes[y_idx]; return ImPlotPoint( x_axis.PixelsToPlot(x), y_axis.PixelsToPlot(y) ); } ImPlotPoint PixelsToPlot(const ImVec2& pix, ImAxis x_idx, ImAxis y_idx) { return PixelsToPlot(pix.x, pix.y, x_idx, y_idx); } ImVec2 PlotToPixels(double x, double y, ImAxis x_idx, ImAxis y_idx) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "PlotToPixels() needs to be called between BeginPlot() and EndPlot()!"); IM_ASSERT_USER_ERROR(x_idx == IMPLOT_AUTO || (x_idx >= ImAxis_X1 && x_idx < ImAxis_Y1), "X-Axis index out of bounds!"); IM_ASSERT_USER_ERROR(y_idx == IMPLOT_AUTO || (y_idx >= ImAxis_Y1 && y_idx < ImAxis_COUNT), "Y-Axis index out of bounds!"); SetupLock(); ImPlotPlot& plot = *gp.CurrentPlot; ImPlotAxis& x_axis = x_idx == IMPLOT_AUTO ? plot.Axes[plot.CurrentX] : plot.Axes[x_idx]; ImPlotAxis& y_axis = y_idx == IMPLOT_AUTO ? plot.Axes[plot.CurrentY] : plot.Axes[y_idx]; return ImVec2( x_axis.PlotToPixels(x), y_axis.PlotToPixels(y) ); } ImVec2 PlotToPixels(const ImPlotPoint& plt, ImAxis x_idx, ImAxis y_idx) { return PlotToPixels(plt.x, plt.y, x_idx, y_idx); } ImVec2 GetPlotPos() { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "GetPlotPos() needs to be called between BeginPlot() and EndPlot()!"); SetupLock(); return gp.CurrentPlot->PlotRect.Min; } ImVec2 GetPlotSize() { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "GetPlotSize() needs to be called between BeginPlot() and EndPlot()!"); SetupLock(); return gp.CurrentPlot->PlotRect.GetSize(); } ImPlotPoint GetPlotMousePos(ImAxis x_idx, ImAxis y_idx) { IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != nullptr, "GetPlotMousePos() needs to be called between BeginPlot() and EndPlot()!"); SetupLock(); return PixelsToPlot(ImGui::GetMousePos(), x_idx, y_idx); } ImPlotRect GetPlotLimits(ImAxis x_idx, ImAxis y_idx) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "GetPlotLimits() needs to be called between BeginPlot() and EndPlot()!"); IM_ASSERT_USER_ERROR(x_idx == IMPLOT_AUTO || (x_idx >= ImAxis_X1 && x_idx < ImAxis_Y1), "X-Axis index out of bounds!"); IM_ASSERT_USER_ERROR(y_idx == IMPLOT_AUTO || (y_idx >= ImAxis_Y1 && y_idx < ImAxis_COUNT), "Y-Axis index out of bounds!"); SetupLock(); ImPlotPlot& plot = *gp.CurrentPlot; ImPlotAxis& x_axis = x_idx == IMPLOT_AUTO ? plot.Axes[plot.CurrentX] : plot.Axes[x_idx]; ImPlotAxis& y_axis = y_idx == IMPLOT_AUTO ? plot.Axes[plot.CurrentY] : plot.Axes[y_idx]; ImPlotRect limits; limits.X = x_axis.Range; limits.Y = y_axis.Range; return limits; } bool IsPlotHovered() { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "IsPlotHovered() needs to be called between BeginPlot() and EndPlot()!"); SetupLock(); return gp.CurrentPlot->Hovered; } bool IsAxisHovered(ImAxis axis) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "IsPlotXAxisHovered() needs to be called between BeginPlot() and EndPlot()!"); SetupLock(); return gp.CurrentPlot->Axes[axis].Hovered; } bool IsSubplotsHovered() { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentSubplot != nullptr, "IsSubplotsHovered() needs to be called between BeginSubplots() and EndSubplots()!"); return gp.CurrentSubplot->FrameHovered; } bool IsPlotSelected() { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "IsPlotSelected() needs to be called between BeginPlot() and EndPlot()!"); SetupLock(); return gp.CurrentPlot->Selected; } ImPlotRect GetPlotSelection(ImAxis x_idx, ImAxis y_idx) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "GetPlotSelection() needs to be called between BeginPlot() and EndPlot()!"); SetupLock(); ImPlotPlot& plot = *gp.CurrentPlot; if (!plot.Selected) return ImPlotRect(0,0,0,0); ImPlotPoint p1 = PixelsToPlot(plot.SelectRect.Min + plot.PlotRect.Min, x_idx, y_idx); ImPlotPoint p2 = PixelsToPlot(plot.SelectRect.Max + plot.PlotRect.Min, x_idx, y_idx); ImPlotRect result; result.X.Min = ImMin(p1.x, p2.x); result.X.Max = ImMax(p1.x, p2.x); result.Y.Min = ImMin(p1.y, p2.y); result.Y.Max = ImMax(p1.y, p2.y); return result; } void CancelPlotSelection() { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "CancelPlotSelection() needs to be called between BeginPlot() and EndPlot()!"); SetupLock(); ImPlotPlot& plot = *gp.CurrentPlot; if (plot.Selected) plot.Selected = plot.Selecting = false; } void HideNextItem(bool hidden, ImPlotCond cond) { ImPlotContext& gp = *GImPlot; gp.NextItemData.HasHidden = true; gp.NextItemData.Hidden = hidden; gp.NextItemData.HiddenCond = cond; } //----------------------------------------------------------------------------- // [SECTION] Plot Tools //----------------------------------------------------------------------------- void Annotation(double x, double y, const ImVec4& col, const ImVec2& offset, bool clamp, bool round) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "Annotation() needs to be called between BeginPlot() and EndPlot()!"); SetupLock(); char x_buff[IMPLOT_LABEL_MAX_SIZE]; char y_buff[IMPLOT_LABEL_MAX_SIZE]; ImPlotAxis& x_axis = gp.CurrentPlot->Axes[gp.CurrentPlot->CurrentX]; ImPlotAxis& y_axis = gp.CurrentPlot->Axes[gp.CurrentPlot->CurrentY]; LabelAxisValue(x_axis, x, x_buff, sizeof(x_buff), round); LabelAxisValue(y_axis, y, y_buff, sizeof(y_buff), round); Annotation(x,y,col,offset,clamp,"%s, %s",x_buff,y_buff); } void AnnotationV(double x, double y, const ImVec4& col, const ImVec2& offset, bool clamp, const char* fmt, va_list args) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "Annotation() needs to be called between BeginPlot() and EndPlot()!"); SetupLock(); ImVec2 pos = PlotToPixels(x,y,IMPLOT_AUTO,IMPLOT_AUTO); ImU32 bg = ImGui::GetColorU32(col); ImU32 fg = col.w == 0 ? GetStyleColorU32(ImPlotCol_InlayText) : CalcTextColor(col); gp.Annotations.AppendV(pos, offset, bg, fg, clamp, fmt, args); } void Annotation(double x, double y, const ImVec4& col, const ImVec2& offset, bool clamp, const char* fmt, ...) { va_list args; va_start(args, fmt); AnnotationV(x,y,col,offset,clamp,fmt,args); va_end(args); } void TagV(ImAxis axis, double v, const ImVec4& col, const char* fmt, va_list args) { ImPlotContext& gp = *GImPlot; SetupLock(); ImU32 bg = ImGui::GetColorU32(col); ImU32 fg = col.w == 0 ? GetStyleColorU32(ImPlotCol_AxisText) : CalcTextColor(col); gp.Tags.AppendV(axis,v,bg,fg,fmt,args); } void Tag(ImAxis axis, double v, const ImVec4& col, const char* fmt, ...) { va_list args; va_start(args, fmt); TagV(axis,v,col,fmt,args); va_end(args); } void Tag(ImAxis axis, double v, const ImVec4& color, bool round) { ImPlotContext& gp = *GImPlot; SetupLock(); char buff[IMPLOT_LABEL_MAX_SIZE]; ImPlotAxis& ax = gp.CurrentPlot->Axes[axis]; LabelAxisValue(ax, v, buff, sizeof(buff), round); Tag(axis,v,color,"%s",buff); } IMPLOT_API void TagX(double x, const ImVec4& color, bool round) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "TagX() needs to be called between BeginPlot() and EndPlot()!"); Tag(gp.CurrentPlot->CurrentX, x, color, round); } IMPLOT_API void TagX(double x, const ImVec4& color, const char* fmt, ...) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "TagX() needs to be called between BeginPlot() and EndPlot()!"); va_list args; va_start(args, fmt); TagV(gp.CurrentPlot->CurrentX,x,color,fmt,args); va_end(args); } IMPLOT_API void TagXV(double x, const ImVec4& color, const char* fmt, va_list args) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "TagX() needs to be called between BeginPlot() and EndPlot()!"); TagV(gp.CurrentPlot->CurrentX, x, color, fmt, args); } IMPLOT_API void TagY(double y, const ImVec4& color, bool round) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "TagY() needs to be called between BeginPlot() and EndPlot()!"); Tag(gp.CurrentPlot->CurrentY, y, color, round); } IMPLOT_API void TagY(double y, const ImVec4& color, const char* fmt, ...) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "TagY() needs to be called between BeginPlot() and EndPlot()!"); va_list args; va_start(args, fmt); TagV(gp.CurrentPlot->CurrentY,y,color,fmt,args); va_end(args); } IMPLOT_API void TagYV(double y, const ImVec4& color, const char* fmt, va_list args) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "TagY() needs to be called between BeginPlot() and EndPlot()!"); TagV(gp.CurrentPlot->CurrentY, y, color, fmt, args); } static const float DRAG_GRAB_HALF_SIZE = 4.0f; bool DragPoint(int n_id, double* x, double* y, const ImVec4& col, float radius, ImPlotDragToolFlags flags) { ImGui::PushID("#IMPLOT_DRAG_POINT"); IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != nullptr, "DragPoint() needs to be called between BeginPlot() and EndPlot()!"); SetupLock(); if (!ImHasFlag(flags,ImPlotDragToolFlags_NoFit) && FitThisFrame()) { FitPoint(ImPlotPoint(*x,*y)); } const bool input = !ImHasFlag(flags, ImPlotDragToolFlags_NoInputs); const bool show_curs = !ImHasFlag(flags, ImPlotDragToolFlags_NoCursors); const bool no_delay = !ImHasFlag(flags, ImPlotDragToolFlags_Delayed); const float grab_half_size = ImMax(DRAG_GRAB_HALF_SIZE, radius); const ImVec4 color = IsColorAuto(col) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : col; const ImU32 col32 = ImGui::ColorConvertFloat4ToU32(color); ImVec2 pos = PlotToPixels(*x,*y,IMPLOT_AUTO,IMPLOT_AUTO); const ImGuiID id = ImGui::GetCurrentWindow()->GetID(n_id); ImRect rect(pos.x-grab_half_size,pos.y-grab_half_size,pos.x+grab_half_size,pos.y+grab_half_size); bool hovered = false, held = false; ImGui::KeepAliveID(id); if (input) ImGui::ButtonBehavior(rect,id,&hovered,&held); bool dragging = false; if (held && ImGui::IsMouseDragging(0)) { *x = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x; *y = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y; dragging = true; } PushPlotClipRect(); ImDrawList& DrawList = *GetPlotDrawList(); if ((hovered || held) && show_curs) ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); if (dragging && no_delay) pos = PlotToPixels(*x,*y,IMPLOT_AUTO,IMPLOT_AUTO); DrawList.AddCircleFilled(pos, radius, col32); PopPlotClipRect(); ImGui::PopID(); return dragging; } bool DragLineX(int n_id, double* value, const ImVec4& col, float thickness, ImPlotDragToolFlags flags) { // ImGui::PushID("#IMPLOT_DRAG_LINE_X"); ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "DragLineX() needs to be called between BeginPlot() and EndPlot()!"); SetupLock(); if (!ImHasFlag(flags,ImPlotDragToolFlags_NoFit) && FitThisFrame()) { FitPointX(*value); } const bool input = !ImHasFlag(flags, ImPlotDragToolFlags_NoInputs); const bool show_curs = !ImHasFlag(flags, ImPlotDragToolFlags_NoCursors); const bool no_delay = !ImHasFlag(flags, ImPlotDragToolFlags_Delayed); const float grab_half_size = ImMax(DRAG_GRAB_HALF_SIZE, thickness/2); float yt = gp.CurrentPlot->PlotRect.Min.y; float yb = gp.CurrentPlot->PlotRect.Max.y; float x = IM_ROUND(PlotToPixels(*value,0,IMPLOT_AUTO,IMPLOT_AUTO).x); const ImGuiID id = ImGui::GetCurrentWindow()->GetID(n_id); ImRect rect(x-grab_half_size,yt,x+grab_half_size,yb); bool hovered = false, held = false; ImGui::KeepAliveID(id); if (input) ImGui::ButtonBehavior(rect,id,&hovered,&held); if ((hovered || held) && show_curs) ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); float len = gp.Style.MajorTickLen.x; ImVec4 color = IsColorAuto(col) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : col; ImU32 col32 = ImGui::ColorConvertFloat4ToU32(color); bool dragging = false; if (held && ImGui::IsMouseDragging(0)) { *value = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x; dragging = true; } PushPlotClipRect(); ImDrawList& DrawList = *GetPlotDrawList(); if (dragging && no_delay) x = IM_ROUND(PlotToPixels(*value,0,IMPLOT_AUTO,IMPLOT_AUTO).x); DrawList.AddLine(ImVec2(x,yt), ImVec2(x,yb), col32, thickness); DrawList.AddLine(ImVec2(x,yt), ImVec2(x,yt+len), col32, 3*thickness); DrawList.AddLine(ImVec2(x,yb), ImVec2(x,yb-len), col32, 3*thickness); PopPlotClipRect(); // ImGui::PopID(); return dragging; } bool DragLineY(int n_id, double* value, const ImVec4& col, float thickness, ImPlotDragToolFlags flags) { ImGui::PushID("#IMPLOT_DRAG_LINE_Y"); ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "DragLineY() needs to be called between BeginPlot() and EndPlot()!"); SetupLock(); if (!ImHasFlag(flags,ImPlotDragToolFlags_NoFit) && FitThisFrame()) { FitPointY(*value); } const bool input = !ImHasFlag(flags, ImPlotDragToolFlags_NoInputs); const bool show_curs = !ImHasFlag(flags, ImPlotDragToolFlags_NoCursors); const bool no_delay = !ImHasFlag(flags, ImPlotDragToolFlags_Delayed); const float grab_half_size = ImMax(DRAG_GRAB_HALF_SIZE, thickness/2); float xl = gp.CurrentPlot->PlotRect.Min.x; float xr = gp.CurrentPlot->PlotRect.Max.x; float y = IM_ROUND(PlotToPixels(0, *value,IMPLOT_AUTO,IMPLOT_AUTO).y); const ImGuiID id = ImGui::GetCurrentWindow()->GetID(n_id); ImRect rect(xl,y-grab_half_size,xr,y+grab_half_size); bool hovered = false, held = false; ImGui::KeepAliveID(id); if (input) ImGui::ButtonBehavior(rect,id,&hovered,&held); if ((hovered || held) && show_curs) ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS); float len = gp.Style.MajorTickLen.y; ImVec4 color = IsColorAuto(col) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : col; ImU32 col32 = ImGui::ColorConvertFloat4ToU32(color); bool dragging = false; if (held && ImGui::IsMouseDragging(0)) { *value = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y; dragging = true; } PushPlotClipRect(); ImDrawList& DrawList = *GetPlotDrawList(); if (dragging && no_delay) y = IM_ROUND(PlotToPixels(0, *value,IMPLOT_AUTO,IMPLOT_AUTO).y); DrawList.AddLine(ImVec2(xl,y), ImVec2(xr,y), col32, thickness); DrawList.AddLine(ImVec2(xl,y), ImVec2(xl+len,y), col32, 3*thickness); DrawList.AddLine(ImVec2(xr,y), ImVec2(xr-len,y), col32, 3*thickness); PopPlotClipRect(); ImGui::PopID(); return dragging; } bool DragRect(int n_id, double* x_min, double* y_min, double* x_max, double* y_max, const ImVec4& col, ImPlotDragToolFlags flags) { ImGui::PushID("#IMPLOT_DRAG_RECT"); IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != nullptr, "DragRect() needs to be called between BeginPlot() and EndPlot()!"); SetupLock(); if (!ImHasFlag(flags,ImPlotDragToolFlags_NoFit) && FitThisFrame()) { FitPoint(ImPlotPoint(*x_min,*y_min)); FitPoint(ImPlotPoint(*x_max,*y_max)); } const bool input = !ImHasFlag(flags, ImPlotDragToolFlags_NoInputs); const bool show_curs = !ImHasFlag(flags, ImPlotDragToolFlags_NoCursors); const bool no_delay = !ImHasFlag(flags, ImPlotDragToolFlags_Delayed); bool h[] = {true,false,true,false}; double* x[] = {x_min,x_max,x_max,x_min}; double* y[] = {y_min,y_min,y_max,y_max}; ImVec2 p[4]; for (int i = 0; i < 4; ++i) p[i] = PlotToPixels(*x[i],*y[i],IMPLOT_AUTO,IMPLOT_AUTO); ImVec2 pc = PlotToPixels((*x_min+*x_max)/2,(*y_min+*y_max)/2,IMPLOT_AUTO,IMPLOT_AUTO); ImRect rect(ImMin(p[0],p[2]),ImMax(p[0],p[2])); ImRect rect_grab = rect; rect_grab.Expand(DRAG_GRAB_HALF_SIZE); ImGuiMouseCursor cur[4]; if (show_curs) { cur[0] = (rect.Min.x == p[0].x && rect.Min.y == p[0].y) || (rect.Max.x == p[0].x && rect.Max.y == p[0].y) ? ImGuiMouseCursor_ResizeNWSE : ImGuiMouseCursor_ResizeNESW; cur[1] = cur[0] == ImGuiMouseCursor_ResizeNWSE ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE; cur[2] = cur[1] == ImGuiMouseCursor_ResizeNWSE ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE; cur[3] = cur[2] == ImGuiMouseCursor_ResizeNWSE ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE; } ImVec4 color = IsColorAuto(col) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : col; ImU32 col32 = ImGui::ColorConvertFloat4ToU32(color); color.w *= 0.25f; ImU32 col32_a = ImGui::ColorConvertFloat4ToU32(color); const ImGuiID id = ImGui::GetCurrentWindow()->GetID(n_id); bool dragging = false; bool hovered = false, held = false; ImRect b_rect(pc.x-DRAG_GRAB_HALF_SIZE,pc.y-DRAG_GRAB_HALF_SIZE,pc.x+DRAG_GRAB_HALF_SIZE,pc.y+DRAG_GRAB_HALF_SIZE); ImGui::KeepAliveID(id); if (input) ImGui::ButtonBehavior(b_rect,id,&hovered,&held); if ((hovered || held) && show_curs) ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll); if (held && ImGui::IsMouseDragging(0)) { for (int i = 0; i < 4; ++i) { ImPlotPoint pp = PixelsToPlot(p[i] + ImGui::GetIO().MouseDelta,IMPLOT_AUTO,IMPLOT_AUTO); *y[i] = pp.y; *x[i] = pp.x; } dragging = true; } for (int i = 0; i < 4; ++i) { // points b_rect = ImRect(p[i].x-DRAG_GRAB_HALF_SIZE,p[i].y-DRAG_GRAB_HALF_SIZE,p[i].x+DRAG_GRAB_HALF_SIZE,p[i].y+DRAG_GRAB_HALF_SIZE); ImGuiID p_id = id + i + 1; ImGui::KeepAliveID(p_id); if (input) ImGui::ButtonBehavior(b_rect,p_id,&hovered,&held); if ((hovered || held) && show_curs) ImGui::SetMouseCursor(cur[i]); if (held && ImGui::IsMouseDragging(0)) { *x[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x; *y[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y; dragging = true; } // edges ImVec2 e_min = ImMin(p[i],p[(i+1)%4]); ImVec2 e_max = ImMax(p[i],p[(i+1)%4]); b_rect = h[i] ? ImRect(e_min.x + DRAG_GRAB_HALF_SIZE, e_min.y - DRAG_GRAB_HALF_SIZE, e_max.x - DRAG_GRAB_HALF_SIZE, e_max.y + DRAG_GRAB_HALF_SIZE) : ImRect(e_min.x - DRAG_GRAB_HALF_SIZE, e_min.y + DRAG_GRAB_HALF_SIZE, e_max.x + DRAG_GRAB_HALF_SIZE, e_max.y - DRAG_GRAB_HALF_SIZE); ImGuiID e_id = id + i + 5; ImGui::KeepAliveID(e_id); if (input) ImGui::ButtonBehavior(b_rect,e_id,&hovered,&held); if ((hovered || held) && show_curs) h[i] ? ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS) : ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); if (held && ImGui::IsMouseDragging(0)) { if (h[i]) *y[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y; else *x[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x; dragging = true; } if (hovered && ImGui::IsMouseDoubleClicked(0)) { ImPlotRect b = GetPlotLimits(IMPLOT_AUTO,IMPLOT_AUTO); if (h[i]) *y[i] = ((y[i] == y_min && *y_min < *y_max) || (y[i] == y_max && *y_max < *y_min)) ? b.Y.Min : b.Y.Max; else *x[i] = ((x[i] == x_min && *x_min < *x_max) || (x[i] == x_max && *x_max < *x_min)) ? b.X.Min : b.X.Max; dragging = true; } } PushPlotClipRect(); ImDrawList& DrawList = *GetPlotDrawList(); if (dragging && no_delay) { for (int i = 0; i < 4; ++i) p[i] = PlotToPixels(*x[i],*y[i],IMPLOT_AUTO,IMPLOT_AUTO); pc = PlotToPixels((*x_min+*x_max)/2,(*y_min+*y_max)/2,IMPLOT_AUTO,IMPLOT_AUTO); rect = ImRect(ImMin(p[0],p[2]),ImMax(p[0],p[2])); } DrawList.AddRectFilled(rect.Min, rect.Max, col32_a); DrawList.AddRect(rect.Min, rect.Max, col32); if (input && (dragging || rect_grab.Contains(ImGui::GetMousePos()))) { DrawList.AddCircleFilled(pc,DRAG_GRAB_HALF_SIZE,col32); for (int i = 0; i < 4; ++i) DrawList.AddCircleFilled(p[i],DRAG_GRAB_HALF_SIZE,col32); } PopPlotClipRect(); ImGui::PopID(); return dragging; } bool DragRect(int id, ImPlotRect* bounds, const ImVec4& col, ImPlotDragToolFlags flags) { return DragRect(id, &bounds->X.Min, &bounds->Y.Min,&bounds->X.Max, &bounds->Y.Max, col, flags); } //----------------------------------------------------------------------------- // [SECTION] Legend Utils and Tools //----------------------------------------------------------------------------- bool IsLegendEntryHovered(const char* label_id) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentItems != nullptr, "IsPlotItemHighlight() needs to be called within an itemized context!"); SetupLock(); ImGuiID id = ImGui::GetIDWithSeed(label_id, nullptr, gp.CurrentItems->ID); ImPlotItem* item = gp.CurrentItems->GetItem(id); return item && item->LegendHovered; } bool BeginLegendPopup(const char* label_id, ImGuiMouseButton mouse_button) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentItems != nullptr, "BeginLegendPopup() needs to be called within an itemized context!"); SetupLock(); ImGuiWindow* window = GImGui->CurrentWindow; if (window->SkipItems) return false; ImGuiID id = ImGui::GetIDWithSeed(label_id, nullptr, gp.CurrentItems->ID); if (ImGui::IsMouseReleased(mouse_button)) { ImPlotItem* item = gp.CurrentItems->GetItem(id); if (item && item->LegendHovered) ImGui::OpenPopupEx(id); } return ImGui::BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings); } void EndLegendPopup() { SetupLock(); ImGui::EndPopup(); } void ShowAltLegend(const char* title_id, bool vertical, const ImVec2 size, bool interactable) { ImPlotContext& gp = *GImPlot; ImGuiContext &G = *GImGui; ImGuiWindow * Window = G.CurrentWindow; if (Window->SkipItems) return; ImDrawList &DrawList = *Window->DrawList; ImPlotPlot* plot = GetPlot(title_id); ImVec2 legend_size; ImVec2 default_size = gp.Style.LegendPadding * 2; if (plot != nullptr) { legend_size = CalcLegendSize(plot->Items, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, vertical); default_size = legend_size + gp.Style.LegendPadding * 2; } ImVec2 frame_size = ImGui::CalcItemSize(size, default_size.x, default_size.y); ImRect bb_frame = ImRect(Window->DC.CursorPos, Window->DC.CursorPos + frame_size); ImGui::ItemSize(bb_frame); if (!ImGui::ItemAdd(bb_frame, 0, &bb_frame)) return; ImGui::RenderFrame(bb_frame.Min, bb_frame.Max, GetStyleColorU32(ImPlotCol_FrameBg), true, G.Style.FrameRounding); DrawList.PushClipRect(bb_frame.Min, bb_frame.Max, true); if (plot != nullptr) { const ImVec2 legend_pos = GetLocationPos(bb_frame, legend_size, 0, gp.Style.LegendPadding); const ImRect legend_bb(legend_pos, legend_pos + legend_size); interactable = interactable && bb_frame.Contains(ImGui::GetIO().MousePos); // render legend box ImU32 col_bg = GetStyleColorU32(ImPlotCol_LegendBg); ImU32 col_bd = GetStyleColorU32(ImPlotCol_LegendBorder); DrawList.AddRectFilled(legend_bb.Min, legend_bb.Max, col_bg); DrawList.AddRect(legend_bb.Min, legend_bb.Max, col_bd); // render entries ShowLegendEntries(plot->Items, legend_bb, interactable, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, vertical, DrawList); } DrawList.PopClipRect(); } //----------------------------------------------------------------------------- // [SECTION] Drag and Drop Utils //----------------------------------------------------------------------------- bool BeginDragDropTargetPlot() { SetupLock(); ImPlotContext& gp = *GImPlot; ImRect rect = gp.CurrentPlot->PlotRect; return ImGui::BeginDragDropTargetCustom(rect, gp.CurrentPlot->ID); } bool BeginDragDropTargetAxis(ImAxis axis) { SetupLock(); ImPlotPlot& plot = *GImPlot->CurrentPlot; ImPlotAxis& ax = plot.Axes[axis]; ImRect rect = ax.HoverRect; rect.Expand(-3.5f); return ImGui::BeginDragDropTargetCustom(rect, ax.ID); } bool BeginDragDropTargetLegend() { SetupLock(); ImPlotItemGroup& items = *GImPlot->CurrentItems; ImRect rect = items.Legend.Rect; return ImGui::BeginDragDropTargetCustom(rect, items.ID); } void EndDragDropTarget() { SetupLock(); ImGui::EndDragDropTarget(); } bool BeginDragDropSourcePlot(ImGuiDragDropFlags flags) { SetupLock(); ImPlotContext& gp = *GImPlot; ImPlotPlot* plot = gp.CurrentPlot; if (GImGui->IO.KeyMods == gp.InputMap.OverrideMod || GImGui->DragDropPayload.SourceId == plot->ID) return ImGui::ItemAdd(plot->PlotRect, plot->ID) && ImGui::BeginDragDropSource(flags); return false; } bool BeginDragDropSourceAxis(ImAxis idx, ImGuiDragDropFlags flags) { SetupLock(); ImPlotContext& gp = *GImPlot; ImPlotAxis& axis = gp.CurrentPlot->Axes[idx]; if (GImGui->IO.KeyMods == gp.InputMap.OverrideMod || GImGui->DragDropPayload.SourceId == axis.ID) return ImGui::ItemAdd(axis.HoverRect, axis.ID) && ImGui::BeginDragDropSource(flags); return false; } bool BeginDragDropSourceItem(const char* label_id, ImGuiDragDropFlags flags) { SetupLock(); ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentItems != nullptr, "BeginDragDropSourceItem() needs to be called within an itemized context!"); ImGuiID item_id = ImGui::GetIDWithSeed(label_id, nullptr, gp.CurrentItems->ID); ImPlotItem* item = gp.CurrentItems->GetItem(item_id); if (item != nullptr) { return ImGui::ItemAdd(item->LegendHoverRect, item->ID) && ImGui::BeginDragDropSource(flags); } return false; } void EndDragDropSource() { SetupLock(); ImGui::EndDragDropSource(); } //----------------------------------------------------------------------------- // [SECTION] Aligned Plots //----------------------------------------------------------------------------- bool BeginAlignedPlots(const char* group_id, bool vertical) { IM_ASSERT_USER_ERROR(GImPlot != nullptr, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentAlignmentH == nullptr && gp.CurrentAlignmentV == nullptr, "Mismatched BeginAlignedPlots()/EndAlignedPlots()!"); ImGuiContext &G = *GImGui; ImGuiWindow * Window = G.CurrentWindow; if (Window->SkipItems) return false; const ImGuiID ID = Window->GetID(group_id); ImPlotAlignmentData* alignment = gp.AlignmentData.GetOrAddByKey(ID); if (vertical) gp.CurrentAlignmentV = alignment; else gp.CurrentAlignmentH = alignment; if (alignment->Vertical != vertical) alignment->Reset(); alignment->Vertical = vertical; alignment->Begin(); return true; } void EndAlignedPlots() { IM_ASSERT_USER_ERROR(GImPlot != nullptr, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentAlignmentH != nullptr || gp.CurrentAlignmentV != nullptr, "Mismatched BeginAlignedPlots()/EndAlignedPlots()!"); ImPlotAlignmentData* alignment = gp.CurrentAlignmentH != nullptr ? gp.CurrentAlignmentH : (gp.CurrentAlignmentV != nullptr ? gp.CurrentAlignmentV : nullptr); if (alignment) alignment->End(); ResetCtxForNextAlignedPlots(GImPlot); } //----------------------------------------------------------------------------- // [SECTION] Plot and Item Styling //----------------------------------------------------------------------------- ImPlotStyle& GetStyle() { IM_ASSERT_USER_ERROR(GImPlot != nullptr, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); ImPlotContext& gp = *GImPlot; return gp.Style; } void PushStyleColor(ImPlotCol idx, ImU32 col) { ImPlotContext& gp = *GImPlot; ImGuiColorMod backup; backup.Col = (ImGuiCol)idx; backup.BackupValue = gp.Style.Colors[idx]; gp.ColorModifiers.push_back(backup); gp.Style.Colors[idx] = ImGui::ColorConvertU32ToFloat4(col); } void PushStyleColor(ImPlotCol idx, const ImVec4& col) { ImPlotContext& gp = *GImPlot; ImGuiColorMod backup; backup.Col = (ImGuiCol)idx; backup.BackupValue = gp.Style.Colors[idx]; gp.ColorModifiers.push_back(backup); gp.Style.Colors[idx] = col; } void PopStyleColor(int count) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(count <= gp.ColorModifiers.Size, "You can't pop more modifiers than have been pushed!"); while (count > 0) { ImGuiColorMod& backup = gp.ColorModifiers.back(); gp.Style.Colors[backup.Col] = backup.BackupValue; gp.ColorModifiers.pop_back(); count--; } } void PushStyleVar(ImPlotStyleVar idx, float val) { ImPlotContext& gp = *GImPlot; const ImPlotStyleVarInfo* var_info = GetPlotStyleVarInfo(idx); if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1) { float* pvar = (float*)var_info->GetVarPtr(&gp.Style); gp.StyleModifiers.push_back(ImGuiStyleMod((ImGuiStyleVar)idx, *pvar)); *pvar = val; return; } IM_ASSERT(0 && "Called PushStyleVar() float variant but variable is not a float!"); } void PushStyleVar(ImPlotStyleVar idx, int val) { ImPlotContext& gp = *GImPlot; const ImPlotStyleVarInfo* var_info = GetPlotStyleVarInfo(idx); if (var_info->Type == ImGuiDataType_S32 && var_info->Count == 1) { int* pvar = (int*)var_info->GetVarPtr(&gp.Style); gp.StyleModifiers.push_back(ImGuiStyleMod((ImGuiStyleVar)idx, *pvar)); *pvar = val; return; } else if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1) { float* pvar = (float*)var_info->GetVarPtr(&gp.Style); gp.StyleModifiers.push_back(ImGuiStyleMod((ImGuiStyleVar)idx, *pvar)); *pvar = (float)val; return; } IM_ASSERT(0 && "Called PushStyleVar() int variant but variable is not a int!"); } void PushStyleVar(ImPlotStyleVar idx, const ImVec2& val) { ImPlotContext& gp = *GImPlot; const ImPlotStyleVarInfo* var_info = GetPlotStyleVarInfo(idx); if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2) { ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&gp.Style); gp.StyleModifiers.push_back(ImGuiStyleMod((ImGuiStyleVar)idx, *pvar)); *pvar = val; return; } IM_ASSERT(0 && "Called PushStyleVar() ImVec2 variant but variable is not a ImVec2!"); } void PopStyleVar(int count) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(count <= gp.StyleModifiers.Size, "You can't pop more modifiers than have been pushed!"); while (count > 0) { ImGuiStyleMod& backup = gp.StyleModifiers.back(); const ImPlotStyleVarInfo* info = GetPlotStyleVarInfo(backup.VarIdx); void* data = info->GetVarPtr(&gp.Style); if (info->Type == ImGuiDataType_Float && info->Count == 1) { ((float*)data)[0] = backup.BackupFloat[0]; } else if (info->Type == ImGuiDataType_Float && info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; } else if (info->Type == ImGuiDataType_S32 && info->Count == 1) { ((int*)data)[0] = backup.BackupInt[0]; } gp.StyleModifiers.pop_back(); count--; } } //------------------------------------------------------------------------------ // [Section] Colormaps //------------------------------------------------------------------------------ ImPlotColormap AddColormap(const char* name, const ImVec4* colormap, int size, bool qual) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(size > 1, "The colormap size must be greater than 1!"); IM_ASSERT_USER_ERROR(gp.ColormapData.GetIndex(name) == -1, "The colormap name has already been used!"); ImVector buffer; buffer.resize(size); for (int i = 0; i < size; ++i) buffer[i] = ImGui::ColorConvertFloat4ToU32(colormap[i]); return gp.ColormapData.Append(name, buffer.Data, size, qual); } ImPlotColormap AddColormap(const char* name, const ImU32* colormap, int size, bool qual) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(size > 1, "The colormap size must be greater than 1!"); IM_ASSERT_USER_ERROR(gp.ColormapData.GetIndex(name) == -1, "The colormap name has already be used!"); return gp.ColormapData.Append(name, colormap, size, qual); } int GetColormapCount() { ImPlotContext& gp = *GImPlot; return gp.ColormapData.Count; } const char* GetColormapName(ImPlotColormap colormap) { ImPlotContext& gp = *GImPlot; return gp.ColormapData.GetName(colormap); } ImPlotColormap GetColormapIndex(const char* name) { ImPlotContext& gp = *GImPlot; return gp.ColormapData.GetIndex(name); } void PushColormap(ImPlotColormap colormap) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(colormap >= 0 && colormap < gp.ColormapData.Count, "The colormap index is invalid!"); gp.ColormapModifiers.push_back(gp.Style.Colormap); gp.Style.Colormap = colormap; } void PushColormap(const char* name) { ImPlotContext& gp = *GImPlot; ImPlotColormap idx = gp.ColormapData.GetIndex(name); IM_ASSERT_USER_ERROR(idx != -1, "The colormap name is invalid!"); PushColormap(idx); } void PopColormap(int count) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(count <= gp.ColormapModifiers.Size, "You can't pop more modifiers than have been pushed!"); while (count > 0) { const ImPlotColormap& backup = gp.ColormapModifiers.back(); gp.Style.Colormap = backup; gp.ColormapModifiers.pop_back(); count--; } } ImU32 NextColormapColorU32() { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentItems != nullptr, "NextColormapColor() needs to be called between BeginPlot() and EndPlot()!"); int idx = gp.CurrentItems->ColormapIdx % gp.ColormapData.GetKeyCount(gp.Style.Colormap); ImU32 col = gp.ColormapData.GetKeyColor(gp.Style.Colormap, idx); gp.CurrentItems->ColormapIdx++; return col; } ImVec4 NextColormapColor() { return ImGui::ColorConvertU32ToFloat4(NextColormapColorU32()); } int GetColormapSize(ImPlotColormap cmap) { ImPlotContext& gp = *GImPlot; cmap = cmap == IMPLOT_AUTO ? gp.Style.Colormap : cmap; IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, "Invalid colormap index!"); return gp.ColormapData.GetKeyCount(cmap); } ImU32 GetColormapColorU32(int idx, ImPlotColormap cmap) { ImPlotContext& gp = *GImPlot; cmap = cmap == IMPLOT_AUTO ? gp.Style.Colormap : cmap; IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, "Invalid colormap index!"); idx = idx % gp.ColormapData.GetKeyCount(cmap); return gp.ColormapData.GetKeyColor(cmap, idx); } ImVec4 GetColormapColor(int idx, ImPlotColormap cmap) { return ImGui::ColorConvertU32ToFloat4(GetColormapColorU32(idx,cmap)); } ImU32 SampleColormapU32(float t, ImPlotColormap cmap) { ImPlotContext& gp = *GImPlot; cmap = cmap == IMPLOT_AUTO ? gp.Style.Colormap : cmap; IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, "Invalid colormap index!"); return gp.ColormapData.LerpTable(cmap, t); } ImVec4 SampleColormap(float t, ImPlotColormap cmap) { return ImGui::ColorConvertU32ToFloat4(SampleColormapU32(t,cmap)); } void RenderColorBar(const ImU32* colors, int size, ImDrawList& DrawList, const ImRect& bounds, bool vert, bool reversed, bool continuous) { const int n = continuous ? size - 1 : size; ImU32 col1, col2; if (vert) { const float step = bounds.GetHeight() / n; ImRect rect(bounds.Min.x, bounds.Min.y, bounds.Max.x, bounds.Min.y + step); for (int i = 0; i < n; ++i) { if (reversed) { col1 = colors[size-i-1]; col2 = continuous ? colors[size-i-2] : col1; } else { col1 = colors[i]; col2 = continuous ? colors[i+1] : col1; } DrawList.AddRectFilledMultiColor(rect.Min, rect.Max, col1, col1, col2, col2); rect.TranslateY(step); } } else { const float step = bounds.GetWidth() / n; ImRect rect(bounds.Min.x, bounds.Min.y, bounds.Min.x + step, bounds.Max.y); for (int i = 0; i < n; ++i) { if (reversed) { col1 = colors[size-i-1]; col2 = continuous ? colors[size-i-2] : col1; } else { col1 = colors[i]; col2 = continuous ? colors[i+1] : col1; } DrawList.AddRectFilledMultiColor(rect.Min, rect.Max, col1, col2, col2, col1); rect.TranslateX(step); } } } void ColormapScale(const char* label, double scale_min, double scale_max, const ImVec2& size, const char* format, ImPlotColormapScaleFlags flags, ImPlotColormap cmap) { ImGuiContext &G = *GImGui; ImGuiWindow * Window = G.CurrentWindow; if (Window->SkipItems) return; const ImGuiID ID = Window->GetID(label); ImVec2 label_size(0,0); if (!ImHasFlag(flags, ImPlotColormapScaleFlags_NoLabel)) { label_size = ImGui::CalcTextSize(label,nullptr,true); } ImPlotContext& gp = *GImPlot; cmap = cmap == IMPLOT_AUTO ? gp.Style.Colormap : cmap; IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, "Invalid colormap index!"); ImVec2 frame_size = ImGui::CalcItemSize(size, 0, gp.Style.PlotDefaultSize.y); if (frame_size.y < gp.Style.PlotMinSize.y && size.y < 0.0f) frame_size.y = gp.Style.PlotMinSize.y; ImPlotRange range(ImMin(scale_min,scale_max), ImMax(scale_min,scale_max)); gp.CTicker.Reset(); Locator_Default(gp.CTicker, range, frame_size.y, true, Formatter_Default, (void*)format); const bool rend_label = label_size.x > 0; const float txt_off = gp.Style.LabelPadding.x; const float pad = txt_off + gp.CTicker.MaxSize.x + (rend_label ? txt_off + label_size.y : 0); float bar_w = 20; if (frame_size.x == 0) frame_size.x = bar_w + pad + 2 * gp.Style.PlotPadding.x; else { bar_w = frame_size.x - (pad + 2 * gp.Style.PlotPadding.x); if (bar_w < gp.Style.MajorTickLen.y) bar_w = gp.Style.MajorTickLen.y; } ImDrawList &DrawList = *Window->DrawList; ImRect bb_frame = ImRect(Window->DC.CursorPos, Window->DC.CursorPos + frame_size); ImGui::ItemSize(bb_frame); if (!ImGui::ItemAdd(bb_frame, ID, &bb_frame)) return; ImGui::RenderFrame(bb_frame.Min, bb_frame.Max, GetStyleColorU32(ImPlotCol_FrameBg), true, G.Style.FrameRounding); const bool opposite = ImHasFlag(flags, ImPlotColormapScaleFlags_Opposite); const bool inverted = ImHasFlag(flags, ImPlotColormapScaleFlags_Invert); const bool reversed = scale_min > scale_max; float bb_grad_shift = opposite ? pad : 0; ImRect bb_grad(bb_frame.Min + gp.Style.PlotPadding + ImVec2(bb_grad_shift, 0), bb_frame.Min + ImVec2(bar_w + gp.Style.PlotPadding.x + bb_grad_shift, frame_size.y - gp.Style.PlotPadding.y)); ImGui::PushClipRect(bb_frame.Min, bb_frame.Max, true); const ImU32 col_text = ImGui::GetColorU32(ImGuiCol_Text); const bool invert_scale = inverted ? (reversed ? false : true) : (reversed ? true : false); const float y_min = invert_scale ? bb_grad.Max.y : bb_grad.Min.y; const float y_max = invert_scale ? bb_grad.Min.y : bb_grad.Max.y; RenderColorBar(gp.ColormapData.GetKeys(cmap), gp.ColormapData.GetKeyCount(cmap), DrawList, bb_grad, true, !inverted, !gp.ColormapData.IsQual(cmap)); for (int i = 0; i < gp.CTicker.TickCount(); ++i) { const double y_pos_plt = gp.CTicker.Ticks[i].PlotPos; const float y_pos = ImRemap((float)y_pos_plt, (float)range.Max, (float)range.Min, y_min, y_max); const float tick_width = gp.CTicker.Ticks[i].Major ? gp.Style.MajorTickLen.y : gp.Style.MinorTickLen.y; const float tick_thick = gp.CTicker.Ticks[i].Major ? gp.Style.MajorTickSize.y : gp.Style.MinorTickSize.y; const float tick_t = (float)((y_pos_plt - scale_min) / (scale_max - scale_min)); const ImU32 tick_col = CalcTextColor(gp.ColormapData.LerpTable(cmap,tick_t)); if (y_pos < bb_grad.Max.y - 2 && y_pos > bb_grad.Min.y + 2) { DrawList.AddLine(opposite ? ImVec2(bb_grad.Min.x+1, y_pos) : ImVec2(bb_grad.Max.x-1, y_pos), opposite ? ImVec2(bb_grad.Min.x + tick_width, y_pos) : ImVec2(bb_grad.Max.x - tick_width, y_pos), tick_col, tick_thick); } const float txt_x = opposite ? bb_grad.Min.x - txt_off - gp.CTicker.Ticks[i].LabelSize.x : bb_grad.Max.x + txt_off; const float txt_y = y_pos - gp.CTicker.Ticks[i].LabelSize.y * 0.5f; DrawList.AddText(ImVec2(txt_x, txt_y), col_text, gp.CTicker.GetText(i)); } if (rend_label) { const float pos_x = opposite ? bb_frame.Min.x + gp.Style.PlotPadding.x : bb_grad.Max.x + 2 * txt_off + gp.CTicker.MaxSize.x; const float pos_y = bb_grad.GetCenter().y + label_size.x * 0.5f; const char* label_end = ImGui::FindRenderedTextEnd(label); AddTextVertical(&DrawList,ImVec2(pos_x,pos_y),col_text,label,label_end); } DrawList.AddRect(bb_grad.Min, bb_grad.Max, GetStyleColorU32(ImPlotCol_PlotBorder)); ImGui::PopClipRect(); } bool ColormapSlider(const char* label, float* t, ImVec4* out, const char* format, ImPlotColormap cmap) { *t = ImClamp(*t,0.0f,1.0f); ImGuiContext &G = *GImGui; ImGuiWindow * Window = G.CurrentWindow; if (Window->SkipItems) return false; ImPlotContext& gp = *GImPlot; cmap = cmap == IMPLOT_AUTO ? gp.Style.Colormap : cmap; IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, "Invalid colormap index!"); const ImU32* keys = gp.ColormapData.GetKeys(cmap); const int count = gp.ColormapData.GetKeyCount(cmap); const bool qual = gp.ColormapData.IsQual(cmap); const ImVec2 pos = ImGui::GetCurrentWindow()->DC.CursorPos; const float w = ImGui::CalcItemWidth(); const float h = ImGui::GetFrameHeight(); const ImRect rect = ImRect(pos.x,pos.y,pos.x+w,pos.y+h); RenderColorBar(keys,count,*ImGui::GetWindowDrawList(),rect,false,false,!qual); const ImU32 grab = CalcTextColor(gp.ColormapData.LerpTable(cmap,*t)); // const ImU32 text = CalcTextColor(gp.ColormapData.LerpTable(cmap,0.5f)); ImGui::PushStyleColor(ImGuiCol_FrameBg,IM_COL32_BLACK_TRANS); ImGui::PushStyleColor(ImGuiCol_FrameBgActive,IM_COL32_BLACK_TRANS); ImGui::PushStyleColor(ImGuiCol_FrameBgHovered,ImVec4(1,1,1,0.1f)); ImGui::PushStyleColor(ImGuiCol_SliderGrab,grab); ImGui::PushStyleColor(ImGuiCol_SliderGrabActive, grab); ImGui::PushStyleVar(ImGuiStyleVar_GrabMinSize,2); ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding,0); const bool changed = ImGui::SliderFloat(label,t,0,1,format); ImGui::PopStyleColor(5); ImGui::PopStyleVar(2); if (out != nullptr) *out = ImGui::ColorConvertU32ToFloat4(gp.ColormapData.LerpTable(cmap,*t)); return changed; } bool ColormapButton(const char* label, const ImVec2& size_arg, ImPlotColormap cmap) { ImGuiContext &G = *GImGui; const ImGuiStyle& style = G.Style; ImGuiWindow * Window = G.CurrentWindow; if (Window->SkipItems) return false; ImPlotContext& gp = *GImPlot; cmap = cmap == IMPLOT_AUTO ? gp.Style.Colormap : cmap; IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, "Invalid colormap index!"); const ImU32* keys = gp.ColormapData.GetKeys(cmap); const int count = gp.ColormapData.GetKeyCount(cmap); const bool qual = gp.ColormapData.IsQual(cmap); const ImVec2 pos = ImGui::GetCurrentWindow()->DC.CursorPos; const ImVec2 label_size = ImGui::CalcTextSize(label, nullptr, true); ImVec2 size = ImGui::CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f); const ImRect rect = ImRect(pos.x,pos.y,pos.x+size.x,pos.y+size.y); RenderColorBar(keys,count,*ImGui::GetWindowDrawList(),rect,false,false,!qual); const ImU32 text = CalcTextColor(gp.ColormapData.LerpTable(cmap,G.Style.ButtonTextAlign.x)); ImGui::PushStyleColor(ImGuiCol_Button,IM_COL32_BLACK_TRANS); ImGui::PushStyleColor(ImGuiCol_ButtonHovered,ImVec4(1,1,1,0.1f)); ImGui::PushStyleColor(ImGuiCol_ButtonActive,ImVec4(1,1,1,0.2f)); ImGui::PushStyleColor(ImGuiCol_Text,text); ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding,0); const bool pressed = ImGui::Button(label,size); ImGui::PopStyleColor(4); ImGui::PopStyleVar(1); return pressed; } //----------------------------------------------------------------------------- // [Section] Miscellaneous //----------------------------------------------------------------------------- ImPlotInputMap& GetInputMap() { IM_ASSERT_USER_ERROR(GImPlot != nullptr, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); ImPlotContext& gp = *GImPlot; return gp.InputMap; } void MapInputDefault(ImPlotInputMap* dst) { ImPlotInputMap& map = dst ? *dst : GetInputMap(); map.Pan = ImGuiMouseButton_Left; map.PanMod = ImGuiMod_None; map.Fit = ImGuiMouseButton_Left; map.Menu = ImGuiMouseButton_Right; map.Select = ImGuiMouseButton_Right; map.SelectMod = ImGuiMod_None; map.SelectCancel = ImGuiMouseButton_Left; map.SelectHorzMod = ImGuiMod_Alt; map.SelectVertMod = ImGuiMod_Shift; map.OverrideMod = ImGuiMod_Ctrl; map.ZoomMod = ImGuiMod_None; map.ZoomRate = 0.1f; } void MapInputReverse(ImPlotInputMap* dst) { ImPlotInputMap& map = dst ? *dst : GetInputMap(); map.Pan = ImGuiMouseButton_Right; map.PanMod = ImGuiMod_None; map.Fit = ImGuiMouseButton_Left; map.Menu = ImGuiMouseButton_Right; map.Select = ImGuiMouseButton_Left; map.SelectMod = ImGuiMod_None; map.SelectCancel = ImGuiMouseButton_Right; map.SelectHorzMod = ImGuiMod_Alt; map.SelectVertMod = ImGuiMod_Shift; map.OverrideMod = ImGuiMod_Ctrl; map.ZoomMod = ImGuiMod_None; map.ZoomRate = 0.1f; } //----------------------------------------------------------------------------- // [Section] Miscellaneous //----------------------------------------------------------------------------- void ItemIcon(const ImVec4& col) { ItemIcon(ImGui::ColorConvertFloat4ToU32(col)); } void ItemIcon(ImU32 col) { const float txt_size = ImGui::GetTextLineHeight(); ImVec2 size(txt_size-4,txt_size); ImGuiWindow* window = ImGui::GetCurrentWindow(); ImVec2 pos = window->DC.CursorPos; ImGui::GetWindowDrawList()->AddRectFilled(pos + ImVec2(0,2), pos + size - ImVec2(0,2), col); ImGui::Dummy(size); } void ColormapIcon(ImPlotColormap cmap) { ImPlotContext& gp = *GImPlot; const float txt_size = ImGui::GetTextLineHeight(); ImVec2 size(txt_size-4,txt_size); ImGuiWindow* window = ImGui::GetCurrentWindow(); ImVec2 pos = window->DC.CursorPos; ImRect rect(pos+ImVec2(0,2),pos+size-ImVec2(0,2)); ImDrawList& DrawList = *ImGui::GetWindowDrawList(); RenderColorBar(gp.ColormapData.GetKeys(cmap),gp.ColormapData.GetKeyCount(cmap),DrawList,rect,false,false,!gp.ColormapData.IsQual(cmap)); ImGui::Dummy(size); } ImDrawList* GetPlotDrawList() { return ImGui::GetWindowDrawList(); } void PushPlotClipRect(float expand) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "PushPlotClipRect() needs to be called between BeginPlot() and EndPlot()!"); SetupLock(); ImRect rect = gp.CurrentPlot->PlotRect; rect.Expand(expand); ImGui::PushClipRect(rect.Min, rect.Max, true); } void PopPlotClipRect() { SetupLock(); ImGui::PopClipRect(); } static void HelpMarker(const char* desc) { ImGui::TextDisabled("(?)"); if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); ImGui::TextUnformatted(desc); ImGui::PopTextWrapPos(); ImGui::EndTooltip(); } } bool ShowStyleSelector(const char* label) { static int style_idx = -1; if (ImGui::Combo(label, &style_idx, "Auto\0Classic\0Dark\0Light\0")) { switch (style_idx) { case 0: StyleColorsAuto(); break; case 1: StyleColorsClassic(); break; case 2: StyleColorsDark(); break; case 3: StyleColorsLight(); break; } return true; } return false; } bool ShowColormapSelector(const char* label) { ImPlotContext& gp = *GImPlot; bool set = false; if (ImGui::BeginCombo(label, gp.ColormapData.GetName(gp.Style.Colormap))) { for (int i = 0; i < gp.ColormapData.Count; ++i) { const char* name = gp.ColormapData.GetName(i); if (ImGui::Selectable(name, gp.Style.Colormap == i)) { gp.Style.Colormap = i; ImPlot::BustItemCache(); set = true; } } ImGui::EndCombo(); } return set; } bool ShowInputMapSelector(const char* label) { static int map_idx = -1; if (ImGui::Combo(label, &map_idx, "Default\0Reversed\0")) { switch (map_idx) { case 0: MapInputDefault(); break; case 1: MapInputReverse(); break; } return true; } return false; } void ShowStyleEditor(ImPlotStyle* ref) { ImPlotContext& gp = *GImPlot; ImPlotStyle& style = GetStyle(); static ImPlotStyle ref_saved_style; // Default to using internal storage as reference static bool init = true; if (init && ref == nullptr) ref_saved_style = style; init = false; if (ref == nullptr) ref = &ref_saved_style; if (ImPlot::ShowStyleSelector("Colors##Selector")) ref_saved_style = style; // Save/Revert button if (ImGui::Button("Save Ref")) *ref = ref_saved_style = style; ImGui::SameLine(); if (ImGui::Button("Revert Ref")) style = *ref; ImGui::SameLine(); HelpMarker("Save/Revert in local non-persistent storage. Default Colors definition are not affected. " "Use \"Export\" below to save them somewhere."); if (ImGui::BeginTabBar("##StyleEditor")) { if (ImGui::BeginTabItem("Variables")) { ImGui::Text("Item Styling"); ImGui::SliderFloat("LineWeight", &style.LineWeight, 0.0f, 5.0f, "%.1f"); ImGui::SliderFloat("MarkerSize", &style.MarkerSize, 2.0f, 10.0f, "%.1f"); ImGui::SliderFloat("MarkerWeight", &style.MarkerWeight, 0.0f, 5.0f, "%.1f"); ImGui::SliderFloat("FillAlpha", &style.FillAlpha, 0.0f, 1.0f, "%.2f"); ImGui::SliderFloat("ErrorBarSize", &style.ErrorBarSize, 0.0f, 10.0f, "%.1f"); ImGui::SliderFloat("ErrorBarWeight", &style.ErrorBarWeight, 0.0f, 5.0f, "%.1f"); ImGui::SliderFloat("DigitalBitHeight", &style.DigitalBitHeight, 0.0f, 20.0f, "%.1f"); ImGui::SliderFloat("DigitalBitGap", &style.DigitalBitGap, 0.0f, 20.0f, "%.1f"); ImGui::Text("Plot Styling"); ImGui::SliderFloat("PlotBorderSize", &style.PlotBorderSize, 0.0f, 2.0f, "%.0f"); ImGui::SliderFloat("MinorAlpha", &style.MinorAlpha, 0.0f, 1.0f, "%.2f"); ImGui::SliderFloat2("MajorTickLen", (float*)&style.MajorTickLen, 0.0f, 20.0f, "%.0f"); ImGui::SliderFloat2("MinorTickLen", (float*)&style.MinorTickLen, 0.0f, 20.0f, "%.0f"); ImGui::SliderFloat2("MajorTickSize", (float*)&style.MajorTickSize, 0.0f, 2.0f, "%.1f"); ImGui::SliderFloat2("MinorTickSize", (float*)&style.MinorTickSize, 0.0f, 2.0f, "%.1f"); ImGui::SliderFloat2("MajorGridSize", (float*)&style.MajorGridSize, 0.0f, 2.0f, "%.1f"); ImGui::SliderFloat2("MinorGridSize", (float*)&style.MinorGridSize, 0.0f, 2.0f, "%.1f"); ImGui::SliderFloat2("PlotDefaultSize", (float*)&style.PlotDefaultSize, 0.0f, 1000, "%.0f"); ImGui::SliderFloat2("PlotMinSize", (float*)&style.PlotMinSize, 0.0f, 300, "%.0f"); ImGui::Text("Plot Padding"); ImGui::SliderFloat2("PlotPadding", (float*)&style.PlotPadding, 0.0f, 20.0f, "%.0f"); ImGui::SliderFloat2("LabelPadding", (float*)&style.LabelPadding, 0.0f, 20.0f, "%.0f"); ImGui::SliderFloat2("LegendPadding", (float*)&style.LegendPadding, 0.0f, 20.0f, "%.0f"); ImGui::SliderFloat2("LegendInnerPadding", (float*)&style.LegendInnerPadding, 0.0f, 10.0f, "%.0f"); ImGui::SliderFloat2("LegendSpacing", (float*)&style.LegendSpacing, 0.0f, 5.0f, "%.0f"); ImGui::SliderFloat2("MousePosPadding", (float*)&style.MousePosPadding, 0.0f, 20.0f, "%.0f"); ImGui::SliderFloat2("AnnotationPadding", (float*)&style.AnnotationPadding, 0.0f, 5.0f, "%.0f"); ImGui::SliderFloat2("FitPadding", (float*)&style.FitPadding, 0, 0.2f, "%.2f"); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Colors")) { static int output_dest = 0; static bool output_only_modified = false; if (ImGui::Button("Export", ImVec2(75,0))) { if (output_dest == 0) ImGui::LogToClipboard(); else ImGui::LogToTTY(); ImGui::LogText("ImVec4* colors = ImPlot::GetStyle().Colors;\n"); for (int i = 0; i < ImPlotCol_COUNT; i++) { const ImVec4& col = style.Colors[i]; const char* name = ImPlot::GetStyleColorName(i); if (!output_only_modified || memcmp(&col, &ref->Colors[i], sizeof(ImVec4)) != 0) { if (IsColorAuto(i)) ImGui::LogText("colors[ImPlotCol_%s]%*s= IMPLOT_AUTO_COL;\n",name,14 - (int)strlen(name), ""); else ImGui::LogText("colors[ImPlotCol_%s]%*s= ImVec4(%.2ff, %.2ff, %.2ff, %.2ff);\n", name, 14 - (int)strlen(name), "", col.x, col.y, col.z, col.w); } } ImGui::LogFinish(); } ImGui::SameLine(); ImGui::SetNextItemWidth(120); ImGui::Combo("##output_type", &output_dest, "To Clipboard\0To TTY\0"); ImGui::SameLine(); ImGui::Checkbox("Only Modified Colors", &output_only_modified); static ImGuiTextFilter filter; filter.Draw("Filter colors", ImGui::GetFontSize() * 16); static ImGuiColorEditFlags alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf; if (ImGui::RadioButton("Opaque", alpha_flags == ImGuiColorEditFlags_None)) { alpha_flags = ImGuiColorEditFlags_None; } ImGui::SameLine(); if (ImGui::RadioButton("Alpha", alpha_flags == ImGuiColorEditFlags_AlphaPreview)) { alpha_flags = ImGuiColorEditFlags_AlphaPreview; } ImGui::SameLine(); if (ImGui::RadioButton("Both", alpha_flags == ImGuiColorEditFlags_AlphaPreviewHalf)) { alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf; } ImGui::SameLine(); HelpMarker( "In the color list:\n" "Left-click on colored square to open color picker,\n" "Right-click to open edit options menu."); ImGui::Separator(); ImGui::PushItemWidth(-160); for (int i = 0; i < ImPlotCol_COUNT; i++) { const char* name = ImPlot::GetStyleColorName(i); if (!filter.PassFilter(name)) continue; ImGui::PushID(i); ImVec4 temp = GetStyleColorVec4(i); const bool is_auto = IsColorAuto(i); if (!is_auto) ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.25f); if (ImGui::Button("Auto")) { if (is_auto) style.Colors[i] = temp; else style.Colors[i] = IMPLOT_AUTO_COL; BustItemCache(); } if (!is_auto) ImGui::PopStyleVar(); ImGui::SameLine(); if (ImGui::ColorEdit4(name, &temp.x, ImGuiColorEditFlags_NoInputs | alpha_flags)) { style.Colors[i] = temp; BustItemCache(); } if (memcmp(&style.Colors[i], &ref->Colors[i], sizeof(ImVec4)) != 0) { ImGui::SameLine(175); if (ImGui::Button("Save")) { ref->Colors[i] = style.Colors[i]; } ImGui::SameLine(); if (ImGui::Button("Revert")) { style.Colors[i] = ref->Colors[i]; BustItemCache(); } } ImGui::PopID(); } ImGui::PopItemWidth(); ImGui::Separator(); ImGui::Text("Colors that are set to Auto (i.e. IMPLOT_AUTO_COL) will\n" "be automatically deduced from your ImGui style or the\n" "current ImPlot Colormap. If you want to style individual\n" "plot items, use Push/PopStyleColor around its function."); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Colormaps")) { static int output_dest = 0; if (ImGui::Button("Export", ImVec2(75,0))) { if (output_dest == 0) ImGui::LogToClipboard(); else ImGui::LogToTTY(); int size = GetColormapSize(); const char* name = GetColormapName(gp.Style.Colormap); ImGui::LogText("static const ImU32 %s_Data[%d] = {\n", name, size); for (int i = 0; i < size; ++i) { ImU32 col = GetColormapColorU32(i,gp.Style.Colormap); ImGui::LogText(" %u%s\n", col, i == size - 1 ? "" : ","); } ImGui::LogText("};\nImPlotColormap %s = ImPlot::AddColormap(\"%s\", %s_Data, %d);", name, name, name, size); ImGui::LogFinish(); } ImGui::SameLine(); ImGui::SetNextItemWidth(120); ImGui::Combo("##output_type", &output_dest, "To Clipboard\0To TTY\0"); ImGui::SameLine(); static bool edit = false; ImGui::Checkbox("Edit Mode",&edit); // built-in/added ImGui::Separator(); for (int i = 0; i < gp.ColormapData.Count; ++i) { ImGui::PushID(i); int size = gp.ColormapData.GetKeyCount(i); bool selected = i == gp.Style.Colormap; const char* name = GetColormapName(i); if (!selected) ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.25f); if (ImGui::Button(name, ImVec2(100,0))) { gp.Style.Colormap = i; BustItemCache(); } if (!selected) ImGui::PopStyleVar(); ImGui::SameLine(); ImGui::BeginGroup(); if (edit) { for (int c = 0; c < size; ++c) { ImGui::PushID(c); ImVec4 col4 = ImGui::ColorConvertU32ToFloat4(gp.ColormapData.GetKeyColor(i,c)); if (ImGui::ColorEdit4("",&col4.x,ImGuiColorEditFlags_NoInputs)) { ImU32 col32 = ImGui::ColorConvertFloat4ToU32(col4); gp.ColormapData.SetKeyColor(i,c,col32); BustItemCache(); } if ((c + 1) % 12 != 0 && c != size -1) ImGui::SameLine(); ImGui::PopID(); } } else { if (ImPlot::ColormapButton("##",ImVec2(-1,0),i)) edit = true; } ImGui::EndGroup(); ImGui::PopID(); } static ImVector custom; if (custom.Size == 0) { custom.push_back(ImVec4(1,0,0,1)); custom.push_back(ImVec4(0,1,0,1)); custom.push_back(ImVec4(0,0,1,1)); } ImGui::Separator(); ImGui::BeginGroup(); static char name[16] = "MyColormap"; if (ImGui::Button("+", ImVec2((100 - ImGui::GetStyle().ItemSpacing.x)/2,0))) custom.push_back(ImVec4(0,0,0,1)); ImGui::SameLine(); if (ImGui::Button("-", ImVec2((100 - ImGui::GetStyle().ItemSpacing.x)/2,0)) && custom.Size > 2) custom.pop_back(); ImGui::SetNextItemWidth(100); ImGui::InputText("##Name",name,16,ImGuiInputTextFlags_CharsNoBlank); static bool qual = true; ImGui::Checkbox("Qualitative",&qual); if (ImGui::Button("Add", ImVec2(100, 0)) && gp.ColormapData.GetIndex(name)==-1) AddColormap(name,custom.Data,custom.Size,qual); ImGui::EndGroup(); ImGui::SameLine(); ImGui::BeginGroup(); for (int c = 0; c < custom.Size; ++c) { ImGui::PushID(c); if (ImGui::ColorEdit4("##Col1", &custom[c].x, ImGuiColorEditFlags_NoInputs)) { } if ((c + 1) % 12 != 0) ImGui::SameLine(); ImGui::PopID(); } ImGui::EndGroup(); ImGui::EndTabItem(); } ImGui::EndTabBar(); } } void ShowUserGuide() { ImGui::BulletText("Left-click drag within the plot area to pan X and Y axes."); ImGui::Indent(); ImGui::BulletText("Left-click drag on axis labels to pan an individual axis."); ImGui::Unindent(); ImGui::BulletText("Scroll in the plot area to zoom both X any Y axes."); ImGui::Indent(); ImGui::BulletText("Scroll on axis labels to zoom an individual axis."); ImGui::Unindent(); ImGui::BulletText("Right-click drag to box select data."); ImGui::Indent(); ImGui::BulletText("Hold Alt to expand box selection horizontally."); ImGui::BulletText("Hold Shift to expand box selection vertically."); ImGui::BulletText("Left-click while box selecting to cancel the selection."); ImGui::Unindent(); ImGui::BulletText("Double left-click to fit all visible data."); ImGui::Indent(); ImGui::BulletText("Double left-click axis labels to fit the individual axis."); ImGui::Unindent(); ImGui::BulletText("Right-click open the full plot context menu."); ImGui::Indent(); ImGui::BulletText("Right-click axis labels to open an individual axis context menu."); ImGui::Unindent(); ImGui::BulletText("Click legend label icons to show/hide plot items."); } void ShowTicksMetrics(const ImPlotTicker& ticker) { ImGui::BulletText("Size: %d", ticker.TickCount()); ImGui::BulletText("MaxSize: [%f,%f]", ticker.MaxSize.x, ticker.MaxSize.y); } void ShowAxisMetrics(const ImPlotPlot& plot, const ImPlotAxis& axis) { ImGui::BulletText("Label: %s", axis.LabelOffset == -1 ? "[none]" : plot.GetAxisLabel(axis)); ImGui::BulletText("Flags: 0x%08X", axis.Flags); ImGui::BulletText("Range: [%f,%f]",axis.Range.Min, axis.Range.Max); ImGui::BulletText("Pixels: %f", axis.PixelSize()); ImGui::BulletText("Aspect: %f", axis.GetAspect()); ImGui::BulletText(axis.OrthoAxis == nullptr ? "OrtherAxis: NULL" : "OrthoAxis: 0x%08X", axis.OrthoAxis->ID); ImGui::BulletText("LinkedMin: %p", (void*)axis.LinkedMin); ImGui::BulletText("LinkedMax: %p", (void*)axis.LinkedMax); ImGui::BulletText("HasRange: %s", axis.HasRange ? "true" : "false"); ImGui::BulletText("Hovered: %s", axis.Hovered ? "true" : "false"); ImGui::BulletText("Held: %s", axis.Held ? "true" : "false"); if (ImGui::TreeNode("Transform")) { ImGui::BulletText("PixelMin: %f", axis.PixelMin); ImGui::BulletText("PixelMax: %f", axis.PixelMax); ImGui::BulletText("ScaleToPixel: %f", axis.ScaleToPixel); ImGui::BulletText("ScaleMax: %f", axis.ScaleMax); ImGui::TreePop(); } if (ImGui::TreeNode("Ticks")) { ShowTicksMetrics(axis.Ticker); ImGui::TreePop(); } } void ShowMetricsWindow(bool* p_popen) { static bool show_plot_rects = false; static bool show_axes_rects = false; static bool show_axis_rects = false; static bool show_canvas_rects = false; static bool show_frame_rects = false; static bool show_subplot_frame_rects = false; static bool show_subplot_grid_rects = false; ImDrawList& fg = *ImGui::GetForegroundDrawList(); ImPlotContext& gp = *GImPlot; // ImGuiContext& g = *GImGui; ImGuiIO& io = ImGui::GetIO(); ImGui::Begin("ImPlot Metrics", p_popen); ImGui::Text("ImPlot " IMPLOT_VERSION); ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); ImGui::Text("Mouse Position: [%.0f,%.0f]", io.MousePos.x, io.MousePos.y); ImGui::Separator(); if (ImGui::TreeNode("Tools")) { if (ImGui::Button("Bust Plot Cache")) BustPlotCache(); ImGui::SameLine(); if (ImGui::Button("Bust Item Cache")) BustItemCache(); ImGui::Checkbox("Show Frame Rects", &show_frame_rects); ImGui::Checkbox("Show Canvas Rects",&show_canvas_rects); ImGui::Checkbox("Show Plot Rects", &show_plot_rects); ImGui::Checkbox("Show Axes Rects", &show_axes_rects); ImGui::Checkbox("Show Axis Rects", &show_axis_rects); ImGui::Checkbox("Show Subplot Frame Rects", &show_subplot_frame_rects); ImGui::Checkbox("Show Subplot Grid Rects", &show_subplot_grid_rects); ImGui::TreePop(); } const int n_plots = gp.Plots.GetBufSize(); const int n_subplots = gp.Subplots.GetBufSize(); // render rects for (int p = 0; p < n_plots; ++p) { ImPlotPlot* plot = gp.Plots.GetByIndex(p); if (show_frame_rects) fg.AddRect(plot->FrameRect.Min, plot->FrameRect.Max, IM_COL32(255,0,255,255)); if (show_canvas_rects) fg.AddRect(plot->CanvasRect.Min, plot->CanvasRect.Max, IM_COL32(0,255,255,255)); if (show_plot_rects) fg.AddRect(plot->PlotRect.Min, plot->PlotRect.Max, IM_COL32(255,255,0,255)); if (show_axes_rects) fg.AddRect(plot->AxesRect.Min, plot->AxesRect.Max, IM_COL32(0,255,128,255)); if (show_axis_rects) { for (int i = 0; i < ImAxis_COUNT; ++i) { if (plot->Axes[i].Enabled) fg.AddRect(plot->Axes[i].HoverRect.Min, plot->Axes[i].HoverRect.Max, IM_COL32(0,255,0,255)); } } } for (int p = 0; p < n_subplots; ++p) { ImPlotSubplot* subplot = gp.Subplots.GetByIndex(p); if (show_subplot_frame_rects) fg.AddRect(subplot->FrameRect.Min, subplot->FrameRect.Max, IM_COL32(255,0,0,255)); if (show_subplot_grid_rects) fg.AddRect(subplot->GridRect.Min, subplot->GridRect.Max, IM_COL32(0,0,255,255)); } if (ImGui::TreeNode("Plots","Plots (%d)", n_plots)) { for (int p = 0; p < n_plots; ++p) { // plot ImPlotPlot& plot = *gp.Plots.GetByIndex(p); ImGui::PushID(p); if (ImGui::TreeNode("Plot", "Plot [0x%08X]", plot.ID)) { int n_items = plot.Items.GetItemCount(); if (ImGui::TreeNode("Items", "Items (%d)", n_items)) { for (int i = 0; i < n_items; ++i) { ImPlotItem* item = plot.Items.GetItemByIndex(i); ImGui::PushID(i); if (ImGui::TreeNode("Item", "Item [0x%08X]", item->ID)) { ImGui::Bullet(); ImGui::Checkbox("Show", &item->Show); ImGui::Bullet(); ImVec4 temp = ImGui::ColorConvertU32ToFloat4(item->Color); if (ImGui::ColorEdit4("Color",&temp.x, ImGuiColorEditFlags_NoInputs)) item->Color = ImGui::ColorConvertFloat4ToU32(temp); ImGui::BulletText("NameOffset: %d",item->NameOffset); ImGui::BulletText("Name: %s", item->NameOffset != -1 ? plot.Items.Legend.Labels.Buf.Data + item->NameOffset : "N/A"); ImGui::BulletText("Hovered: %s",item->LegendHovered ? "true" : "false"); ImGui::TreePop(); } ImGui::PopID(); } ImGui::TreePop(); } char buff[16]; for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) { ImFormatString(buff,16,"X-Axis %d", i+1); if (plot.XAxis(i).Enabled && ImGui::TreeNode(buff, "X-Axis %d [0x%08X]", i+1, plot.XAxis(i).ID)) { ShowAxisMetrics(plot, plot.XAxis(i)); ImGui::TreePop(); } } for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) { ImFormatString(buff,16,"Y-Axis %d", i+1); if (plot.YAxis(i).Enabled && ImGui::TreeNode(buff, "Y-Axis %d [0x%08X]", i+1, plot.YAxis(i).ID)) { ShowAxisMetrics(plot, plot.YAxis(i)); ImGui::TreePop(); } } ImGui::BulletText("Title: %s", plot.HasTitle() ? plot.GetTitle() : "none"); ImGui::BulletText("Flags: 0x%08X", plot.Flags); ImGui::BulletText("Initialized: %s", plot.Initialized ? "true" : "false"); ImGui::BulletText("Selecting: %s", plot.Selecting ? "true" : "false"); ImGui::BulletText("Selected: %s", plot.Selected ? "true" : "false"); ImGui::BulletText("Hovered: %s", plot.Hovered ? "true" : "false"); ImGui::BulletText("Held: %s", plot.Held ? "true" : "false"); ImGui::BulletText("LegendHovered: %s", plot.Items.Legend.Hovered ? "true" : "false"); ImGui::BulletText("ContextLocked: %s", plot.ContextLocked ? "true" : "false"); ImGui::TreePop(); } ImGui::PopID(); } ImGui::TreePop(); } if (ImGui::TreeNode("Subplots","Subplots (%d)", n_subplots)) { for (int p = 0; p < n_subplots; ++p) { // plot ImPlotSubplot& plot = *gp.Subplots.GetByIndex(p); ImGui::PushID(p); if (ImGui::TreeNode("Subplot", "Subplot [0x%08X]", plot.ID)) { int n_items = plot.Items.GetItemCount(); if (ImGui::TreeNode("Items", "Items (%d)", n_items)) { for (int i = 0; i < n_items; ++i) { ImPlotItem* item = plot.Items.GetItemByIndex(i); ImGui::PushID(i); if (ImGui::TreeNode("Item", "Item [0x%08X]", item->ID)) { ImGui::Bullet(); ImGui::Checkbox("Show", &item->Show); ImGui::Bullet(); ImVec4 temp = ImGui::ColorConvertU32ToFloat4(item->Color); if (ImGui::ColorEdit4("Color",&temp.x, ImGuiColorEditFlags_NoInputs)) item->Color = ImGui::ColorConvertFloat4ToU32(temp); ImGui::BulletText("NameOffset: %d",item->NameOffset); ImGui::BulletText("Name: %s", item->NameOffset != -1 ? plot.Items.Legend.Labels.Buf.Data + item->NameOffset : "N/A"); ImGui::BulletText("Hovered: %s",item->LegendHovered ? "true" : "false"); ImGui::TreePop(); } ImGui::PopID(); } ImGui::TreePop(); } ImGui::BulletText("Flags: 0x%08X", plot.Flags); ImGui::BulletText("FrameHovered: %s", plot.FrameHovered ? "true" : "false"); ImGui::BulletText("LegendHovered: %s", plot.Items.Legend.Hovered ? "true" : "false"); ImGui::TreePop(); } ImGui::PopID(); } ImGui::TreePop(); } if (ImGui::TreeNode("Colormaps")) { ImGui::BulletText("Colormaps: %d", gp.ColormapData.Count); ImGui::BulletText("Memory: %d bytes", gp.ColormapData.Tables.Size * 4); if (ImGui::TreeNode("Data")) { for (int m = 0; m < gp.ColormapData.Count; ++m) { if (ImGui::TreeNode(gp.ColormapData.GetName(m))) { int count = gp.ColormapData.GetKeyCount(m); int size = gp.ColormapData.GetTableSize(m); bool qual = gp.ColormapData.IsQual(m); ImGui::BulletText("Qualitative: %s", qual ? "true" : "false"); ImGui::BulletText("Key Count: %d", count); ImGui::BulletText("Table Size: %d", size); ImGui::Indent(); static float t = 0.5; ImVec4 samp; float wid = 32 * 10 - ImGui::GetFrameHeight() - ImGui::GetStyle().ItemSpacing.x; ImGui::SetNextItemWidth(wid); ImPlot::ColormapSlider("##Sample",&t,&samp,"%.3f",m); ImGui::SameLine(); ImGui::ColorButton("Sampler",samp); ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0,0,0,0)); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0,0)); for (int c = 0; c < size; ++c) { ImVec4 col = ImGui::ColorConvertU32ToFloat4(gp.ColormapData.GetTableColor(m,c)); ImGui::PushID(m*1000+c); ImGui::ColorButton("",col,0,ImVec2(10,10)); ImGui::PopID(); if ((c + 1) % 32 != 0 && c != size - 1) ImGui::SameLine(); } ImGui::PopStyleVar(); ImGui::PopStyleColor(); ImGui::Unindent(); ImGui::TreePop(); } } ImGui::TreePop(); } ImGui::TreePop(); } ImGui::End(); } bool ShowDatePicker(const char* id, int* level, ImPlotTime* t, const ImPlotTime* t1, const ImPlotTime* t2) { ImGui::PushID(id); ImGui::BeginGroup(); ImGuiStyle& style = ImGui::GetStyle(); ImVec4 col_txt = style.Colors[ImGuiCol_Text]; ImVec4 col_dis = style.Colors[ImGuiCol_TextDisabled]; ImVec4 col_btn = style.Colors[ImGuiCol_Button]; ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0,0,0,0)); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0,0)); const float ht = ImGui::GetFrameHeight(); ImVec2 cell_size(ht*1.25f,ht); char buff[32]; bool clk = false; tm& Tm = GImPlot->Tm; const int min_yr = 1970; const int max_yr = 2999; // t1 parts int t1_mo = 0; int t1_md = 0; int t1_yr = 0; if (t1 != nullptr) { GetTime(*t1,&Tm); t1_mo = Tm.tm_mon; t1_md = Tm.tm_mday; t1_yr = Tm.tm_year + 1900; } // t2 parts int t2_mo = 0; int t2_md = 0; int t2_yr = 0; if (t2 != nullptr) { GetTime(*t2,&Tm); t2_mo = Tm.tm_mon; t2_md = Tm.tm_mday; t2_yr = Tm.tm_year + 1900; } // day widget if (*level == 0) { *t = FloorTime(*t, ImPlotTimeUnit_Day); GetTime(*t, &Tm); const int this_year = Tm.tm_year + 1900; const int last_year = this_year - 1; const int next_year = this_year + 1; const int this_mon = Tm.tm_mon; const int last_mon = this_mon == 0 ? 11 : this_mon - 1; const int next_mon = this_mon == 11 ? 0 : this_mon + 1; const int days_this_mo = GetDaysInMonth(this_year, this_mon); const int days_last_mo = GetDaysInMonth(this_mon == 0 ? last_year : this_year, last_mon); ImPlotTime t_first_mo = FloorTime(*t,ImPlotTimeUnit_Mo); GetTime(t_first_mo,&Tm); const int first_wd = Tm.tm_wday; // month year ImFormatString(buff, 32, "%s %d", MONTH_NAMES[this_mon], this_year); if (ImGui::Button(buff)) *level = 1; ImGui::SameLine(5*cell_size.x); BeginDisabledControls(this_year <= min_yr && this_mon == 0); if (ImGui::ArrowButtonEx("##Up",ImGuiDir_Up,cell_size)) *t = AddTime(*t, ImPlotTimeUnit_Mo, -1); EndDisabledControls(this_year <= min_yr && this_mon == 0); ImGui::SameLine(); BeginDisabledControls(this_year >= max_yr && this_mon == 11); if (ImGui::ArrowButtonEx("##Down",ImGuiDir_Down,cell_size)) *t = AddTime(*t, ImPlotTimeUnit_Mo, 1); EndDisabledControls(this_year >= max_yr && this_mon == 11); // render weekday abbreviations ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); for (int i = 0; i < 7; ++i) { ImGui::Button(WD_ABRVS[i],cell_size); if (i != 6) { ImGui::SameLine(); } } ImGui::PopItemFlag(); // 0 = last mo, 1 = this mo, 2 = next mo int mo = first_wd > 0 ? 0 : 1; int day = mo == 1 ? 1 : days_last_mo - first_wd + 1; for (int i = 0; i < 6; ++i) { for (int j = 0; j < 7; ++j) { if (mo == 0 && day > days_last_mo) { mo = 1; day = 1; } else if (mo == 1 && day > days_this_mo) { mo = 2; day = 1; } const int now_yr = (mo == 0 && this_mon == 0) ? last_year : ((mo == 2 && this_mon == 11) ? next_year : this_year); const int now_mo = mo == 0 ? last_mon : (mo == 1 ? this_mon : next_mon); const int now_md = day; const bool off_mo = mo == 0 || mo == 2; const bool t1_or_t2 = (t1 != nullptr && t1_mo == now_mo && t1_yr == now_yr && t1_md == now_md) || (t2 != nullptr && t2_mo == now_mo && t2_yr == now_yr && t2_md == now_md); if (off_mo) ImGui::PushStyleColor(ImGuiCol_Text, col_dis); if (t1_or_t2) { ImGui::PushStyleColor(ImGuiCol_Button, col_btn); ImGui::PushStyleColor(ImGuiCol_Text, col_txt); } ImGui::PushID(i*7+j); ImFormatString(buff,32,"%d",day); if (now_yr == min_yr-1 || now_yr == max_yr+1) { ImGui::Dummy(cell_size); } else if (ImGui::Button(buff,cell_size) && !clk) { *t = MakeTime(now_yr, now_mo, now_md); clk = true; } ImGui::PopID(); if (t1_or_t2) ImGui::PopStyleColor(2); if (off_mo) ImGui::PopStyleColor(); if (j != 6) ImGui::SameLine(); day++; } } } // month widget else if (*level == 1) { *t = FloorTime(*t, ImPlotTimeUnit_Mo); GetTime(*t, &Tm); int this_yr = Tm.tm_year + 1900; ImFormatString(buff, 32, "%d", this_yr); if (ImGui::Button(buff)) *level = 2; BeginDisabledControls(this_yr <= min_yr); ImGui::SameLine(5*cell_size.x); if (ImGui::ArrowButtonEx("##Up",ImGuiDir_Up,cell_size)) *t = AddTime(*t, ImPlotTimeUnit_Yr, -1); EndDisabledControls(this_yr <= min_yr); ImGui::SameLine(); BeginDisabledControls(this_yr >= max_yr); if (ImGui::ArrowButtonEx("##Down",ImGuiDir_Down,cell_size)) *t = AddTime(*t, ImPlotTimeUnit_Yr, 1); EndDisabledControls(this_yr >= max_yr); // ImGui::Dummy(cell_size); cell_size.x *= 7.0f/4.0f; cell_size.y *= 7.0f/3.0f; int mo = 0; for (int i = 0; i < 3; ++i) { for (int j = 0; j < 4; ++j) { const bool t1_or_t2 = (t1 != nullptr && t1_yr == this_yr && t1_mo == mo) || (t2 != nullptr && t2_yr == this_yr && t2_mo == mo); if (t1_or_t2) ImGui::PushStyleColor(ImGuiCol_Button, col_btn); if (ImGui::Button(MONTH_ABRVS[mo],cell_size) && !clk) { *t = MakeTime(this_yr, mo); *level = 0; } if (t1_or_t2) ImGui::PopStyleColor(); if (j != 3) ImGui::SameLine(); mo++; } } } else if (*level == 2) { *t = FloorTime(*t, ImPlotTimeUnit_Yr); int this_yr = GetYear(*t); int yr = this_yr - this_yr % 20; ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); ImFormatString(buff,32,"%d-%d",yr,yr+19); ImGui::Button(buff); ImGui::PopItemFlag(); ImGui::SameLine(5*cell_size.x); BeginDisabledControls(yr <= min_yr); if (ImGui::ArrowButtonEx("##Up",ImGuiDir_Up,cell_size)) *t = MakeTime(yr-20); EndDisabledControls(yr <= min_yr); ImGui::SameLine(); BeginDisabledControls(yr + 20 >= max_yr); if (ImGui::ArrowButtonEx("##Down",ImGuiDir_Down,cell_size)) *t = MakeTime(yr+20); EndDisabledControls(yr+ 20 >= max_yr); // ImGui::Dummy(cell_size); cell_size.x *= 7.0f/4.0f; cell_size.y *= 7.0f/5.0f; for (int i = 0; i < 5; ++i) { for (int j = 0; j < 4; ++j) { const bool t1_or_t2 = (t1 != nullptr && t1_yr == yr) || (t2 != nullptr && t2_yr == yr); if (t1_or_t2) ImGui::PushStyleColor(ImGuiCol_Button, col_btn); ImFormatString(buff,32,"%d",yr); if (yr<1970||yr>3000) { ImGui::Dummy(cell_size); } else if (ImGui::Button(buff,cell_size)) { *t = MakeTime(yr); *level = 1; } if (t1_or_t2) ImGui::PopStyleColor(); if (j != 3) ImGui::SameLine(); yr++; } } } ImGui::PopStyleVar(); ImGui::PopStyleColor(); ImGui::EndGroup(); ImGui::PopID(); return clk; } bool ShowTimePicker(const char* id, ImPlotTime* t) { ImPlotContext& gp = *GImPlot; ImGui::PushID(id); tm& Tm = gp.Tm; GetTime(*t,&Tm); static const char* nums[] = { "00","01","02","03","04","05","06","07","08","09", "10","11","12","13","14","15","16","17","18","19", "20","21","22","23","24","25","26","27","28","29", "30","31","32","33","34","35","36","37","38","39", "40","41","42","43","44","45","46","47","48","49", "50","51","52","53","54","55","56","57","58","59"}; static const char* am_pm[] = {"am","pm"}; bool hour24 = gp.Style.Use24HourClock; int hr = hour24 ? Tm.tm_hour : ((Tm.tm_hour == 0 || Tm.tm_hour == 12) ? 12 : Tm.tm_hour % 12); int min = Tm.tm_min; int sec = Tm.tm_sec; int ap = Tm.tm_hour < 12 ? 0 : 1; bool changed = false; ImVec2 spacing = ImGui::GetStyle().ItemSpacing; spacing.x = 0; float width = ImGui::CalcTextSize("888").x; float height = ImGui::GetFrameHeight(); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, spacing); ImGui::PushStyleVar(ImGuiStyleVar_ScrollbarSize,2.0f); ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0,0,0,0)); ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0,0,0,0)); ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered)); ImGui::SetNextItemWidth(width); if (ImGui::BeginCombo("##hr",nums[hr],ImGuiComboFlags_NoArrowButton)) { const int ia = hour24 ? 0 : 1; const int ib = hour24 ? 24 : 13; for (int i = ia; i < ib; ++i) { if (ImGui::Selectable(nums[i],i==hr)) { hr = i; changed = true; } } ImGui::EndCombo(); } ImGui::SameLine(); ImGui::Text(":"); ImGui::SameLine(); ImGui::SetNextItemWidth(width); if (ImGui::BeginCombo("##min",nums[min],ImGuiComboFlags_NoArrowButton)) { for (int i = 0; i < 60; ++i) { if (ImGui::Selectable(nums[i],i==min)) { min = i; changed = true; } } ImGui::EndCombo(); } ImGui::SameLine(); ImGui::Text(":"); ImGui::SameLine(); ImGui::SetNextItemWidth(width); if (ImGui::BeginCombo("##sec",nums[sec],ImGuiComboFlags_NoArrowButton)) { for (int i = 0; i < 60; ++i) { if (ImGui::Selectable(nums[i],i==sec)) { sec = i; changed = true; } } ImGui::EndCombo(); } if (!hour24) { ImGui::SameLine(); if (ImGui::Button(am_pm[ap],ImVec2(0,height))) { ap = 1 - ap; changed = true; } } ImGui::PopStyleColor(3); ImGui::PopStyleVar(2); ImGui::PopID(); if (changed) { if (!hour24) hr = hr % 12 + ap * 12; Tm.tm_hour = hr; Tm.tm_min = min; Tm.tm_sec = sec; *t = MkTime(&Tm); } return changed; } void StyleColorsAuto(ImPlotStyle* dst) { ImPlotStyle* style = dst ? dst : &ImPlot::GetStyle(); ImVec4* colors = style->Colors; style->MinorAlpha = 0.25f; colors[ImPlotCol_Line] = IMPLOT_AUTO_COL; colors[ImPlotCol_Fill] = IMPLOT_AUTO_COL; colors[ImPlotCol_MarkerOutline] = IMPLOT_AUTO_COL; colors[ImPlotCol_MarkerFill] = IMPLOT_AUTO_COL; colors[ImPlotCol_ErrorBar] = IMPLOT_AUTO_COL; colors[ImPlotCol_FrameBg] = IMPLOT_AUTO_COL; colors[ImPlotCol_PlotBg] = IMPLOT_AUTO_COL; colors[ImPlotCol_PlotBorder] = IMPLOT_AUTO_COL; colors[ImPlotCol_LegendBg] = IMPLOT_AUTO_COL; colors[ImPlotCol_LegendBorder] = IMPLOT_AUTO_COL; colors[ImPlotCol_LegendText] = IMPLOT_AUTO_COL; colors[ImPlotCol_TitleText] = IMPLOT_AUTO_COL; colors[ImPlotCol_InlayText] = IMPLOT_AUTO_COL; colors[ImPlotCol_PlotBorder] = IMPLOT_AUTO_COL; colors[ImPlotCol_AxisText] = IMPLOT_AUTO_COL; colors[ImPlotCol_AxisGrid] = IMPLOT_AUTO_COL; colors[ImPlotCol_AxisTick] = IMPLOT_AUTO_COL; colors[ImPlotCol_AxisBg] = IMPLOT_AUTO_COL; colors[ImPlotCol_AxisBgHovered] = IMPLOT_AUTO_COL; colors[ImPlotCol_AxisBgActive] = IMPLOT_AUTO_COL; colors[ImPlotCol_Selection] = IMPLOT_AUTO_COL; colors[ImPlotCol_Crosshairs] = IMPLOT_AUTO_COL; } void StyleColorsClassic(ImPlotStyle* dst) { ImPlotStyle* style = dst ? dst : &ImPlot::GetStyle(); ImVec4* colors = style->Colors; style->MinorAlpha = 0.5f; colors[ImPlotCol_Line] = IMPLOT_AUTO_COL; colors[ImPlotCol_Fill] = IMPLOT_AUTO_COL; colors[ImPlotCol_MarkerOutline] = IMPLOT_AUTO_COL; colors[ImPlotCol_MarkerFill] = IMPLOT_AUTO_COL; colors[ImPlotCol_ErrorBar] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f); colors[ImPlotCol_FrameBg] = ImVec4(0.43f, 0.43f, 0.43f, 0.39f); colors[ImPlotCol_PlotBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.35f); colors[ImPlotCol_PlotBorder] = ImVec4(0.50f, 0.50f, 0.50f, 0.50f); colors[ImPlotCol_LegendBg] = ImVec4(0.11f, 0.11f, 0.14f, 0.92f); colors[ImPlotCol_LegendBorder] = ImVec4(0.50f, 0.50f, 0.50f, 0.50f); colors[ImPlotCol_LegendText] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f); colors[ImPlotCol_TitleText] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f); colors[ImPlotCol_InlayText] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f); colors[ImPlotCol_AxisText] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f); colors[ImPlotCol_AxisGrid] = ImVec4(0.90f, 0.90f, 0.90f, 0.25f); colors[ImPlotCol_AxisTick] = IMPLOT_AUTO_COL; // TODO colors[ImPlotCol_AxisBg] = IMPLOT_AUTO_COL; // TODO colors[ImPlotCol_AxisBgHovered] = IMPLOT_AUTO_COL; // TODO colors[ImPlotCol_AxisBgActive] = IMPLOT_AUTO_COL; // TODO colors[ImPlotCol_Selection] = ImVec4(0.97f, 0.97f, 0.39f, 1.00f); colors[ImPlotCol_Crosshairs] = ImVec4(0.50f, 0.50f, 0.50f, 0.75f); } void StyleColorsDark(ImPlotStyle* dst) { ImPlotStyle* style = dst ? dst : &ImPlot::GetStyle(); ImVec4* colors = style->Colors; style->MinorAlpha = 0.25f; colors[ImPlotCol_Line] = IMPLOT_AUTO_COL; colors[ImPlotCol_Fill] = IMPLOT_AUTO_COL; colors[ImPlotCol_MarkerOutline] = IMPLOT_AUTO_COL; colors[ImPlotCol_MarkerFill] = IMPLOT_AUTO_COL; colors[ImPlotCol_ErrorBar] = IMPLOT_AUTO_COL; colors[ImPlotCol_FrameBg] = ImVec4(1.00f, 1.00f, 1.00f, 0.07f); colors[ImPlotCol_PlotBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.50f); colors[ImPlotCol_PlotBorder] = ImVec4(0.43f, 0.43f, 0.50f, 0.50f); colors[ImPlotCol_LegendBg] = ImVec4(0.08f, 0.08f, 0.08f, 0.94f); colors[ImPlotCol_LegendBorder] = ImVec4(0.43f, 0.43f, 0.50f, 0.50f); colors[ImPlotCol_LegendText] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); colors[ImPlotCol_TitleText] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); colors[ImPlotCol_InlayText] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); colors[ImPlotCol_AxisText] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); colors[ImPlotCol_AxisGrid] = ImVec4(1.00f, 1.00f, 1.00f, 0.25f); colors[ImPlotCol_AxisTick] = IMPLOT_AUTO_COL; // TODO colors[ImPlotCol_AxisBg] = IMPLOT_AUTO_COL; // TODO colors[ImPlotCol_AxisBgHovered] = IMPLOT_AUTO_COL; // TODO colors[ImPlotCol_AxisBgActive] = IMPLOT_AUTO_COL; // TODO colors[ImPlotCol_Selection] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f); colors[ImPlotCol_Crosshairs] = ImVec4(1.00f, 1.00f, 1.00f, 0.50f); } void StyleColorsLight(ImPlotStyle* dst) { ImPlotStyle* style = dst ? dst : &ImPlot::GetStyle(); ImVec4* colors = style->Colors; style->MinorAlpha = 1.0f; colors[ImPlotCol_Line] = IMPLOT_AUTO_COL; colors[ImPlotCol_Fill] = IMPLOT_AUTO_COL; colors[ImPlotCol_MarkerOutline] = IMPLOT_AUTO_COL; colors[ImPlotCol_MarkerFill] = IMPLOT_AUTO_COL; colors[ImPlotCol_ErrorBar] = IMPLOT_AUTO_COL; colors[ImPlotCol_FrameBg] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); colors[ImPlotCol_PlotBg] = ImVec4(0.42f, 0.57f, 1.00f, 0.13f); colors[ImPlotCol_PlotBorder] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); colors[ImPlotCol_LegendBg] = ImVec4(1.00f, 1.00f, 1.00f, 0.98f); colors[ImPlotCol_LegendBorder] = ImVec4(0.82f, 0.82f, 0.82f, 0.80f); colors[ImPlotCol_LegendText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); colors[ImPlotCol_TitleText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); colors[ImPlotCol_InlayText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); colors[ImPlotCol_AxisText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); colors[ImPlotCol_AxisGrid] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); colors[ImPlotCol_AxisTick] = ImVec4(0.00f, 0.00f, 0.00f, 0.25f); colors[ImPlotCol_AxisBg] = IMPLOT_AUTO_COL; // TODO colors[ImPlotCol_AxisBgHovered] = IMPLOT_AUTO_COL; // TODO colors[ImPlotCol_AxisBgActive] = IMPLOT_AUTO_COL; // TODO colors[ImPlotCol_Selection] = ImVec4(0.82f, 0.64f, 0.03f, 1.00f); colors[ImPlotCol_Crosshairs] = ImVec4(0.00f, 0.00f, 0.00f, 0.50f); } //----------------------------------------------------------------------------- // [SECTION] Obsolete Functions/Types //----------------------------------------------------------------------------- #ifndef IMPLOT_DISABLE_OBSOLETE_FUNCTIONS bool BeginPlot(const char* title, const char* x_label, const char* y1_label, const ImVec2& size, ImPlotFlags flags, ImPlotAxisFlags x_flags, ImPlotAxisFlags y1_flags, ImPlotAxisFlags y2_flags, ImPlotAxisFlags y3_flags, const char* y2_label, const char* y3_label) { if (!BeginPlot(title, size, flags)) return false; SetupAxis(ImAxis_X1, x_label, x_flags); SetupAxis(ImAxis_Y1, y1_label, y1_flags); if (ImHasFlag(flags, ImPlotFlags_YAxis2)) SetupAxis(ImAxis_Y2, y2_label, y2_flags); if (ImHasFlag(flags, ImPlotFlags_YAxis3)) SetupAxis(ImAxis_Y3, y3_label, y3_flags); return true; } #endif } // namespace ImPlot ================================================ FILE: Source/External/imgui_tools/implot/implot.h ================================================ // MIT License // Copyright (c) 2022 Evan Pezent // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // ImPlot v0.14 // Table of Contents: // // [SECTION] Macros and Defines // [SECTION] Enums and Types // [SECTION] Callbacks // [SECTION] Contexts // [SECTION] Begin/End Plot // [SECTION] Begin/End Subplot // [SECTION] Setup // [SECTION] SetNext // [SECTION] Plot Items // [SECTION] Plot Tools // [SECTION] Plot Utils // [SECTION] Legend Utils // [SECTION] Drag and Drop // [SECTION] Styling // [SECTION] Colormaps // [SECTION] Input Mapping // [SECTION] Miscellaneous // [SECTION] Demo // [SECTION] Obsolete API #pragma once #include "imgui.h" //----------------------------------------------------------------------------- // [SECTION] Macros and Defines //----------------------------------------------------------------------------- // Define attributes of all API symbols declarations (e.g. for DLL under Windows) // Using ImPlot via a shared library is not recommended, because we don't guarantee // backward nor forward ABI compatibility and also function call overhead. If you // do use ImPlot as a DLL, be sure to call SetImGuiContext (see Miscellanous section). #ifndef IMPLOT_API #define IMPLOT_API #endif // ImPlot version string. #define IMPLOT_VERSION "0.14" // Indicates variable should deduced automatically. #define IMPLOT_AUTO -1 // Special color used to indicate that a color should be deduced automatically. #define IMPLOT_AUTO_COL ImVec4(0,0,0,-1) // Macro for templated plotting functions; keeps header clean. #define IMPLOT_TMP template IMPLOT_API //----------------------------------------------------------------------------- // [SECTION] Enums and Types //----------------------------------------------------------------------------- // Forward declarations struct ImPlotContext; // ImPlot context (opaque struct, see implot_internal.h) // Enums/Flags typedef int ImAxis; // -> enum ImAxis_ typedef int ImPlotFlags; // -> enum ImPlotFlags_ typedef int ImPlotAxisFlags; // -> enum ImPlotAxisFlags_ typedef int ImPlotSubplotFlags; // -> enum ImPlotSubplotFlags_ typedef int ImPlotLegendFlags; // -> enum ImPlotLegendFlags_ typedef int ImPlotMouseTextFlags; // -> enum ImPlotMouseTextFlags_ typedef int ImPlotDragToolFlags; // -> ImPlotDragToolFlags_ typedef int ImPlotColormapScaleFlags; // -> ImPlotColormapScaleFlags_ typedef int ImPlotItemFlags; // -> ImPlotItemFlags_ typedef int ImPlotLineFlags; // -> ImPlotLineFlags_ typedef int ImPlotScatterFlags; // -> ImPlotScatterFlags typedef int ImPlotStairsFlags; // -> ImPlotStairsFlags_ typedef int ImPlotShadedFlags; // -> ImPlotShadedFlags_ typedef int ImPlotBarsFlags; // -> ImPlotBarsFlags_ typedef int ImPlotBarGroupsFlags; // -> ImPlotBarGroupsFlags_ typedef int ImPlotErrorBarsFlags; // -> ImPlotErrorBarsFlags_ typedef int ImPlotStemsFlags; // -> ImPlotStemsFlags_ typedef int ImPlotInfLinesFlags; // -> ImPlotInfLinesFlags_ typedef int ImPlotPieChartFlags; // -> ImPlotPieChartFlags_ typedef int ImPlotHeatmapFlags; // -> ImPlotHeatmapFlags_ typedef int ImPlotHistogramFlags; // -> ImPlotHistogramFlags_ typedef int ImPlotDigitalFlags; // -> ImPlotDigitalFlags_ typedef int ImPlotImageFlags; // -> ImPlotImageFlags_ typedef int ImPlotTextFlags; // -> ImPlotTextFlags_ typedef int ImPlotDummyFlags; // -> ImPlotDummyFlags_ typedef int ImPlotCond; // -> enum ImPlotCond_ typedef int ImPlotCol; // -> enum ImPlotCol_ typedef int ImPlotStyleVar; // -> enum ImPlotStyleVar_ typedef int ImPlotScale; // -> enum ImPlotScale_ typedef int ImPlotMarker; // -> enum ImPlotMarker_ typedef int ImPlotColormap; // -> enum ImPlotColormap_ typedef int ImPlotLocation; // -> enum ImPlotLocation_ typedef int ImPlotBin; // -> enum ImPlotBin_ // Axis indices. The values assigned may change; NEVER hardcode these. enum ImAxis_ { // horizontal axes ImAxis_X1 = 0, // enabled by default ImAxis_X2, // disabled by default ImAxis_X3, // disabled by default // vertical axes ImAxis_Y1, // enabled by default ImAxis_Y2, // disabled by default ImAxis_Y3, // disabled by default // bookeeping ImAxis_COUNT }; // Options for plots (see BeginPlot). enum ImPlotFlags_ { ImPlotFlags_None = 0, // default ImPlotFlags_NoTitle = 1 << 0, // the plot title will not be displayed (titles are also hidden if preceeded by double hashes, e.g. "##MyPlot") ImPlotFlags_NoLegend = 1 << 1, // the legend will not be displayed ImPlotFlags_NoMouseText = 1 << 2, // the mouse position, in plot coordinates, will not be displayed inside of the plot ImPlotFlags_NoInputs = 1 << 3, // the user will not be able to interact with the plot ImPlotFlags_NoMenus = 1 << 4, // the user will not be able to open context menus ImPlotFlags_NoBoxSelect = 1 << 5, // the user will not be able to box-select ImPlotFlags_NoChild = 1 << 6, // a child window region will not be used to capture mouse scroll (can boost performance for single ImGui window applications) ImPlotFlags_NoFrame = 1 << 7, // the ImGui frame will not be rendered ImPlotFlags_Equal = 1 << 8, // x and y axes pairs will be constrained to have the same units/pixel ImPlotFlags_Crosshairs = 1 << 9, // the default mouse cursor will be replaced with a crosshair when hovered ImPlotFlags_CanvasOnly = ImPlotFlags_NoTitle | ImPlotFlags_NoLegend | ImPlotFlags_NoMenus | ImPlotFlags_NoBoxSelect | ImPlotFlags_NoMouseText }; // Options for plot axes (see SetupAxis). enum ImPlotAxisFlags_ { ImPlotAxisFlags_None = 0, // default ImPlotAxisFlags_NoLabel = 1 << 0, // the axis label will not be displayed (axis labels are also hidden if the supplied string name is nullptr) ImPlotAxisFlags_NoGridLines = 1 << 1, // no grid lines will be displayed ImPlotAxisFlags_NoTickMarks = 1 << 2, // no tick marks will be displayed ImPlotAxisFlags_NoTickLabels = 1 << 3, // no text labels will be displayed ImPlotAxisFlags_NoInitialFit = 1 << 4, // axis will not be initially fit to data extents on the first rendered frame ImPlotAxisFlags_NoMenus = 1 << 5, // the user will not be able to open context menus with right-click ImPlotAxisFlags_NoSideSwitch = 1 << 6, // the user will not be able to switch the axis side by dragging it ImPlotAxisFlags_NoHighlight = 1 << 7, // the axis will not have its background highlighted when hovered or held ImPlotAxisFlags_Opposite = 1 << 8, // axis ticks and labels will be rendered on the conventionally opposite side (i.e, right or top) ImPlotAxisFlags_Foreground = 1 << 9, // grid lines will be displayed in the foreground (i.e. on top of data) instead of the background ImPlotAxisFlags_Invert = 1 << 10, // the axis will be inverted ImPlotAxisFlags_AutoFit = 1 << 11, // axis will be auto-fitting to data extents ImPlotAxisFlags_RangeFit = 1 << 12, // axis will only fit points if the point is in the visible range of the **orthogonal** axis ImPlotAxisFlags_PanStretch = 1 << 13, // panning in a locked or constrained state will cause the axis to stretch if possible ImPlotAxisFlags_LockMin = 1 << 14, // the axis minimum value will be locked when panning/zooming ImPlotAxisFlags_LockMax = 1 << 15, // the axis maximum value will be locked when panning/zooming ImPlotAxisFlags_Lock = ImPlotAxisFlags_LockMin | ImPlotAxisFlags_LockMax, ImPlotAxisFlags_NoDecorations = ImPlotAxisFlags_NoLabel | ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_NoTickMarks | ImPlotAxisFlags_NoTickLabels, ImPlotAxisFlags_AuxDefault = ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_Opposite }; // Options for subplots (see BeginSubplot) enum ImPlotSubplotFlags_ { ImPlotSubplotFlags_None = 0, // default ImPlotSubplotFlags_NoTitle = 1 << 0, // the subplot title will not be displayed (titles are also hidden if preceeded by double hashes, e.g. "##MySubplot") ImPlotSubplotFlags_NoLegend = 1 << 1, // the legend will not be displayed (only applicable if ImPlotSubplotFlags_ShareItems is enabled) ImPlotSubplotFlags_NoMenus = 1 << 2, // the user will not be able to open context menus with right-click ImPlotSubplotFlags_NoResize = 1 << 3, // resize splitters between subplot cells will be not be provided ImPlotSubplotFlags_NoAlign = 1 << 4, // subplot edges will not be aligned vertically or horizontally ImPlotSubplotFlags_ShareItems = 1 << 5, // items across all subplots will be shared and rendered into a single legend entry ImPlotSubplotFlags_LinkRows = 1 << 6, // link the y-axis limits of all plots in each row (does not apply to auxiliary axes) ImPlotSubplotFlags_LinkCols = 1 << 7, // link the x-axis limits of all plots in each column (does not apply to auxiliary axes) ImPlotSubplotFlags_LinkAllX = 1 << 8, // link the x-axis limits in every plot in the subplot (does not apply to auxiliary axes) ImPlotSubplotFlags_LinkAllY = 1 << 9, // link the y-axis limits in every plot in the subplot (does not apply to auxiliary axes) ImPlotSubplotFlags_ColMajor = 1 << 10 // subplots are added in column major order instead of the default row major order }; // Options for legends (see SetupLegend) enum ImPlotLegendFlags_ { ImPlotLegendFlags_None = 0, // default ImPlotLegendFlags_NoButtons = 1 << 0, // legend icons will not function as hide/show buttons ImPlotLegendFlags_NoHighlightItem = 1 << 1, // plot items will not be highlighted when their legend entry is hovered ImPlotLegendFlags_NoHighlightAxis = 1 << 2, // axes will not be highlighted when legend entries are hovered (only relevant if x/y-axis count > 1) ImPlotLegendFlags_NoMenus = 1 << 3, // the user will not be able to open context menus with right-click ImPlotLegendFlags_Outside = 1 << 4, // legend will be rendered outside of the plot area ImPlotLegendFlags_Horizontal = 1 << 5, // legend entries will be displayed horizontally ImPlotLegendFlags_Sort = 1 << 6, // legend entries will be displayed in alphabetical order }; // Options for mouse hover text (see SetupMouseText) enum ImPlotMouseTextFlags_ { ImPlotMouseTextFlags_None = 0, // default ImPlotMouseTextFlags_NoAuxAxes = 1 << 0, // only show the mouse position for primary axes ImPlotMouseTextFlags_NoFormat = 1 << 1, // axes label formatters won't be used to render text ImPlotMouseTextFlags_ShowAlways = 1 << 2, // always display mouse position even if plot not hovered }; // Options for DragPoint, DragLine, DragRect enum ImPlotDragToolFlags_ { ImPlotDragToolFlags_None = 0, // default ImPlotDragToolFlags_NoCursors = 1 << 0, // drag tools won't change cursor icons when hovered or held ImPlotDragToolFlags_NoFit = 1 << 1, // the drag tool won't be considered for plot fits ImPlotDragToolFlags_NoInputs = 1 << 2, // lock the tool from user inputs ImPlotDragToolFlags_Delayed = 1 << 3, // tool rendering will be delayed one frame; useful when applying position-constraints }; // Flags for ColormapScale enum ImPlotColormapScaleFlags_ { ImPlotColormapScaleFlags_None = 0, // default ImPlotColormapScaleFlags_NoLabel = 1 << 0, // the colormap axis label will not be displayed ImPlotColormapScaleFlags_Opposite = 1 << 1, // render the colormap label and tick labels on the opposite side ImPlotColormapScaleFlags_Invert = 1 << 2, // invert the colormap bar and axis scale (this only affects rendering; if you only want to reverse the scale mapping, make scale_min > scale_max) }; // Flags for ANY PlotX function enum ImPlotItemFlags_ { ImPlotItemFlags_None = 0, ImPlotItemFlags_NoLegend = 1 << 0, // the item won't have a legend entry displayed ImPlotItemFlags_NoFit = 1 << 1, // the item won't be considered for plot fits }; // Flags for PlotLine enum ImPlotLineFlags_ { ImPlotLineFlags_None = 0, // default ImPlotLineFlags_Segments = 1 << 10, // a line segment will be rendered from every two consecutive points ImPlotLineFlags_Loop = 1 << 11, // the last and first point will be connected to form a closed loop ImPlotLineFlags_SkipNaN = 1 << 12, // NaNs values will be skipped instead of rendered as missing data ImPlotLineFlags_NoClip = 1 << 13, // markers (if displayed) on the edge of a plot will not be clipped ImPlotLineFlags_Shaded = 1 << 14, // a filled region between the line and horizontal origin will be rendered; use PlotShaded for more advanced cases }; // Flags for PlotScatter enum ImPlotScatterFlags_ { ImPlotScatterFlags_None = 0, // default ImPlotScatterFlags_NoClip = 1 << 10, // markers on the edge of a plot will not be clipped }; // Flags for PlotStairs enum ImPlotStairsFlags_ { ImPlotStairsFlags_None = 0, // default ImPlotStairsFlags_PreStep = 1 << 10, // the y value is continued constantly to the left from every x position, i.e. the interval (x[i-1], x[i]] has the value y[i] ImPlotStairsFlags_Shaded = 1 << 11 // a filled region between the stairs and horizontal origin will be rendered; use PlotShaded for more advanced cases }; // Flags for PlotShaded (placeholder) enum ImPlotShadedFlags_ { ImPlotShadedFlags_None = 0 // default }; // Flags for PlotBars enum ImPlotBarsFlags_ { ImPlotBarsFlags_None = 0, // default ImPlotBarsFlags_Horizontal = 1 << 10, // bars will be rendered horizontally on the current y-axis }; // Flags for PlotBarGroups enum ImPlotBarGroupsFlags_ { ImPlotBarGroupsFlags_None = 0, // default ImPlotBarGroupsFlags_Horizontal = 1 << 10, // bar groups will be rendered horizontally on the current y-axis ImPlotBarGroupsFlags_Stacked = 1 << 11, // items in a group will be stacked on top of each other }; // Flags for PlotErrorBars enum ImPlotErrorBarsFlags_ { ImPlotErrorBarsFlags_None = 0, // default ImPlotErrorBarsFlags_Horizontal = 1 << 10, // error bars will be rendered horizontally on the current y-axis }; // Flags for PlotStems enum ImPlotStemsFlags_ { ImPlotStemsFlags_None = 0, // default ImPlotStemsFlags_Horizontal = 1 << 10, // stems will be rendered horizontally on the current y-axis }; // Flags for PlotInfLines enum ImPlotInfLinesFlags_ { ImPlotInfLinesFlags_None = 0, // default ImPlotInfLinesFlags_Horizontal = 1 << 10 // lines will be rendered horizontally on the current y-axis }; // Flags for PlotPieChart enum ImPlotPieChartFlags_ { ImPlotPieChartFlags_None = 0, // default ImPlotPieChartFlags_Normalize = 1 << 10 // force normalization of pie chart values (i.e. always make a full circle if sum < 0) }; // Flags for PlotHeatmap enum ImPlotHeatmapFlags_ { ImPlotHeatmapFlags_None = 0, // default ImPlotHeatmapFlags_ColMajor = 1 << 10, // data will be read in column major order }; // Flags for PlotHistogram and PlotHistogram2D enum ImPlotHistogramFlags_ { ImPlotHistogramFlags_None = 0, // default ImPlotHistogramFlags_Horizontal = 1 << 10, // histogram bars will be rendered horizontally (not supported by PlotHistogram2D) ImPlotHistogramFlags_Cumulative = 1 << 11, // each bin will contain its count plus the counts of all previous bins (not supported by PlotHistogram2D) ImPlotHistogramFlags_Density = 1 << 12, // counts will be normalized, i.e. the PDF will be visualized, or the CDF will be visualized if Cumulative is also set ImPlotHistogramFlags_NoOutliers = 1 << 13, // exclude values outside the specifed histogram range from the count toward normalizing and cumulative counts ImPlotHistogramFlags_ColMajor = 1 << 14 // data will be read in column major order (not supported by PlotHistogram) }; // Flags for PlotDigital (placeholder) enum ImPlotDigitalFlags_ { ImPlotDigitalFlags_None = 0 // default }; // Flags for PlotImage (placeholder) enum ImPlotImageFlags_ { ImPlotImageFlags_None = 0 // default }; // Flags for PlotText enum ImPlotTextFlags_ { ImPlotTextFlags_None = 0, // default ImPlotTextFlags_Vertical = 1 << 10 // text will be rendered vertically }; // Flags for PlotDummy (placeholder) enum ImPlotDummyFlags_ { ImPlotDummyFlags_None = 0 // default }; // Represents a condition for SetupAxisLimits etc. (same as ImGuiCond, but we only support a subset of those enums) enum ImPlotCond_ { ImPlotCond_None = ImGuiCond_None, // No condition (always set the variable), same as _Always ImPlotCond_Always = ImGuiCond_Always, // No condition (always set the variable) ImPlotCond_Once = ImGuiCond_Once, // Set the variable once per runtime session (only the first call will succeed) }; // Plot styling colors. enum ImPlotCol_ { // item styling colors ImPlotCol_Line, // plot line/outline color (defaults to next unused color in current colormap) ImPlotCol_Fill, // plot fill color for bars (defaults to the current line color) ImPlotCol_MarkerOutline, // marker outline color (defaults to the current line color) ImPlotCol_MarkerFill, // marker fill color (defaults to the current line color) ImPlotCol_ErrorBar, // error bar color (defaults to ImGuiCol_Text) // plot styling colors ImPlotCol_FrameBg, // plot frame background color (defaults to ImGuiCol_FrameBg) ImPlotCol_PlotBg, // plot area background color (defaults to ImGuiCol_WindowBg) ImPlotCol_PlotBorder, // plot area border color (defaults to ImGuiCol_Border) ImPlotCol_LegendBg, // legend background color (defaults to ImGuiCol_PopupBg) ImPlotCol_LegendBorder, // legend border color (defaults to ImPlotCol_PlotBorder) ImPlotCol_LegendText, // legend text color (defaults to ImPlotCol_InlayText) ImPlotCol_TitleText, // plot title text color (defaults to ImGuiCol_Text) ImPlotCol_InlayText, // color of text appearing inside of plots (defaults to ImGuiCol_Text) ImPlotCol_AxisText, // axis label and tick lables color (defaults to ImGuiCol_Text) ImPlotCol_AxisGrid, // axis grid color (defaults to 25% ImPlotCol_AxisText) ImPlotCol_AxisTick, // axis tick color (defaults to AxisGrid) ImPlotCol_AxisBg, // background color of axis hover region (defaults to transparent) ImPlotCol_AxisBgHovered, // axis hover color (defaults to ImGuiCol_ButtonHovered) ImPlotCol_AxisBgActive, // axis active color (defaults to ImGuiCol_ButtonActive) ImPlotCol_Selection, // box-selection color (defaults to yellow) ImPlotCol_Crosshairs, // crosshairs color (defaults to ImPlotCol_PlotBorder) ImPlotCol_COUNT }; // Plot styling variables. enum ImPlotStyleVar_ { // item styling variables ImPlotStyleVar_LineWeight, // float, plot item line weight in pixels ImPlotStyleVar_Marker, // int, marker specification ImPlotStyleVar_MarkerSize, // float, marker size in pixels (roughly the marker's "radius") ImPlotStyleVar_MarkerWeight, // float, plot outline weight of markers in pixels ImPlotStyleVar_FillAlpha, // float, alpha modifier applied to all plot item fills ImPlotStyleVar_ErrorBarSize, // float, error bar whisker width in pixels ImPlotStyleVar_ErrorBarWeight, // float, error bar whisker weight in pixels ImPlotStyleVar_DigitalBitHeight, // float, digital channels bit height (at 1) in pixels ImPlotStyleVar_DigitalBitGap, // float, digital channels bit padding gap in pixels // plot styling variables ImPlotStyleVar_PlotBorderSize, // float, thickness of border around plot area ImPlotStyleVar_MinorAlpha, // float, alpha multiplier applied to minor axis grid lines ImPlotStyleVar_MajorTickLen, // ImVec2, major tick lengths for X and Y axes ImPlotStyleVar_MinorTickLen, // ImVec2, minor tick lengths for X and Y axes ImPlotStyleVar_MajorTickSize, // ImVec2, line thickness of major ticks ImPlotStyleVar_MinorTickSize, // ImVec2, line thickness of minor ticks ImPlotStyleVar_MajorGridSize, // ImVec2, line thickness of major grid lines ImPlotStyleVar_MinorGridSize, // ImVec2, line thickness of minor grid lines ImPlotStyleVar_PlotPadding, // ImVec2, padding between widget frame and plot area, labels, or outside legends (i.e. main padding) ImPlotStyleVar_LabelPadding, // ImVec2, padding between axes labels, tick labels, and plot edge ImPlotStyleVar_LegendPadding, // ImVec2, legend padding from plot edges ImPlotStyleVar_LegendInnerPadding, // ImVec2, legend inner padding from legend edges ImPlotStyleVar_LegendSpacing, // ImVec2, spacing between legend entries ImPlotStyleVar_MousePosPadding, // ImVec2, padding between plot edge and interior info text ImPlotStyleVar_AnnotationPadding, // ImVec2, text padding around annotation labels ImPlotStyleVar_FitPadding, // ImVec2, additional fit padding as a percentage of the fit extents (e.g. ImVec2(0.1f,0.1f) adds 10% to the fit extents of X and Y) ImPlotStyleVar_PlotDefaultSize, // ImVec2, default size used when ImVec2(0,0) is passed to BeginPlot ImPlotStyleVar_PlotMinSize, // ImVec2, minimum size plot frame can be when shrunk ImPlotStyleVar_COUNT }; // Axis scale enum ImPlotScale_ { ImPlotScale_Linear = 0, // default linear scale ImPlotScale_Time, // date/time scale ImPlotScale_Log10, // base 10 logartithmic scale ImPlotScale_SymLog, // symmetric log scale }; // Marker specifications. enum ImPlotMarker_ { ImPlotMarker_None = -1, // no marker ImPlotMarker_Circle, // a circle marker (default) ImPlotMarker_Square, // a square maker ImPlotMarker_Diamond, // a diamond marker ImPlotMarker_Up, // an upward-pointing triangle marker ImPlotMarker_Down, // an downward-pointing triangle marker ImPlotMarker_Left, // an leftward-pointing triangle marker ImPlotMarker_Right, // an rightward-pointing triangle marker ImPlotMarker_Cross, // a cross marker (not fillable) ImPlotMarker_Plus, // a plus marker (not fillable) ImPlotMarker_Asterisk, // a asterisk marker (not fillable) ImPlotMarker_COUNT }; // Built-in colormaps enum ImPlotColormap_ { ImPlotColormap_Deep = 0, // a.k.a. seaborn deep (qual=true, n=10) (default) ImPlotColormap_Dark = 1, // a.k.a. matplotlib "Set1" (qual=true, n=9 ) ImPlotColormap_Pastel = 2, // a.k.a. matplotlib "Pastel1" (qual=true, n=9 ) ImPlotColormap_Paired = 3, // a.k.a. matplotlib "Paired" (qual=true, n=12) ImPlotColormap_Viridis = 4, // a.k.a. matplotlib "viridis" (qual=false, n=11) ImPlotColormap_Plasma = 5, // a.k.a. matplotlib "plasma" (qual=false, n=11) ImPlotColormap_Hot = 6, // a.k.a. matplotlib/MATLAB "hot" (qual=false, n=11) ImPlotColormap_Cool = 7, // a.k.a. matplotlib/MATLAB "cool" (qual=false, n=11) ImPlotColormap_Pink = 8, // a.k.a. matplotlib/MATLAB "pink" (qual=false, n=11) ImPlotColormap_Jet = 9, // a.k.a. MATLAB "jet" (qual=false, n=11) ImPlotColormap_Twilight = 10, // a.k.a. matplotlib "twilight" (qual=false, n=11) ImPlotColormap_RdBu = 11, // red/blue, Color Brewer (qual=false, n=11) ImPlotColormap_BrBG = 12, // brown/blue-green, Color Brewer (qual=false, n=11) ImPlotColormap_PiYG = 13, // pink/yellow-green, Color Brewer (qual=false, n=11) ImPlotColormap_Spectral = 14, // color spectrum, Color Brewer (qual=false, n=11) ImPlotColormap_Greys = 15, // white/black (qual=false, n=2 ) }; // Used to position items on a plot (e.g. legends, labels, etc.) enum ImPlotLocation_ { ImPlotLocation_Center = 0, // center-center ImPlotLocation_North = 1 << 0, // top-center ImPlotLocation_South = 1 << 1, // bottom-center ImPlotLocation_West = 1 << 2, // center-left ImPlotLocation_East = 1 << 3, // center-right ImPlotLocation_NorthWest = ImPlotLocation_North | ImPlotLocation_West, // top-left ImPlotLocation_NorthEast = ImPlotLocation_North | ImPlotLocation_East, // top-right ImPlotLocation_SouthWest = ImPlotLocation_South | ImPlotLocation_West, // bottom-left ImPlotLocation_SouthEast = ImPlotLocation_South | ImPlotLocation_East // bottom-right }; // Enums for different automatic histogram binning methods (k = bin count or w = bin width) enum ImPlotBin_ { ImPlotBin_Sqrt = -1, // k = sqrt(n) ImPlotBin_Sturges = -2, // k = 1 + log2(n) ImPlotBin_Rice = -3, // k = 2 * cbrt(n) ImPlotBin_Scott = -4, // w = 3.49 * sigma / cbrt(n) }; // Double precision version of ImVec2 used by ImPlot. Extensible by end users. struct ImPlotPoint { double x, y; ImPlotPoint() { x = y = 0.0; } ImPlotPoint(double _x, double _y) { x = _x; y = _y; } ImPlotPoint(const ImVec2& p) { x = p.x; y = p.y; } double operator[] (size_t idx) const { return (&x)[idx]; } double& operator[] (size_t idx) { return (&x)[idx]; } #ifdef IMPLOT_POINT_CLASS_EXTRA IMPLOT_POINT_CLASS_EXTRA // Define additional constructors and implicit cast operators in imconfig.h // to convert back and forth between your math types and ImPlotPoint. #endif }; // Range defined by a min/max value. struct ImPlotRange { double Min, Max; ImPlotRange() { Min = 0; Max = 0; } ImPlotRange(double _min, double _max) { Min = _min; Max = _max; } bool Contains(double value) const { return value >= Min && value <= Max; } double Size() const { return Max - Min; } double Clamp(double value) const { return (value < Min) ? Min : (value > Max) ? Max : value; } }; // Combination of two range limits for X and Y axes. Also an AABB defined by Min()/Max(). struct ImPlotRect { ImPlotRange X, Y; ImPlotRect() { } ImPlotRect(double x_min, double x_max, double y_min, double y_max) { X.Min = x_min; X.Max = x_max; Y.Min = y_min; Y.Max = y_max; } bool Contains(const ImPlotPoint& p) const { return Contains(p.x, p.y); } bool Contains(double x, double y) const { return X.Contains(x) && Y.Contains(y); } ImPlotPoint Size() const { return ImPlotPoint(X.Size(), Y.Size()); } ImPlotPoint Clamp(const ImPlotPoint& p) { return Clamp(p.x, p.y); } ImPlotPoint Clamp(double x, double y) { return ImPlotPoint(X.Clamp(x),Y.Clamp(y)); } ImPlotPoint Min() const { return ImPlotPoint(X.Min, Y.Min); } ImPlotPoint Max() const { return ImPlotPoint(X.Max, Y.Max); } }; // Plot style structure struct ImPlotStyle { // item styling variables float LineWeight; // = 1, item line weight in pixels int Marker; // = ImPlotMarker_None, marker specification float MarkerSize; // = 4, marker size in pixels (roughly the marker's "radius") float MarkerWeight; // = 1, outline weight of markers in pixels float FillAlpha; // = 1, alpha modifier applied to plot fills float ErrorBarSize; // = 5, error bar whisker width in pixels float ErrorBarWeight; // = 1.5, error bar whisker weight in pixels float DigitalBitHeight; // = 8, digital channels bit height (at y = 1.0f) in pixels float DigitalBitGap; // = 4, digital channels bit padding gap in pixels // plot styling variables float PlotBorderSize; // = 1, line thickness of border around plot area float MinorAlpha; // = 0.25 alpha multiplier applied to minor axis grid lines ImVec2 MajorTickLen; // = 10,10 major tick lengths for X and Y axes ImVec2 MinorTickLen; // = 5,5 minor tick lengths for X and Y axes ImVec2 MajorTickSize; // = 1,1 line thickness of major ticks ImVec2 MinorTickSize; // = 1,1 line thickness of minor ticks ImVec2 MajorGridSize; // = 1,1 line thickness of major grid lines ImVec2 MinorGridSize; // = 1,1 line thickness of minor grid lines ImVec2 PlotPadding; // = 10,10 padding between widget frame and plot area, labels, or outside legends (i.e. main padding) ImVec2 LabelPadding; // = 5,5 padding between axes labels, tick labels, and plot edge ImVec2 LegendPadding; // = 10,10 legend padding from plot edges ImVec2 LegendInnerPadding; // = 5,5 legend inner padding from legend edges ImVec2 LegendSpacing; // = 5,0 spacing between legend entries ImVec2 MousePosPadding; // = 10,10 padding between plot edge and interior mouse location text ImVec2 AnnotationPadding; // = 2,2 text padding around annotation labels ImVec2 FitPadding; // = 0,0 additional fit padding as a percentage of the fit extents (e.g. ImVec2(0.1f,0.1f) adds 10% to the fit extents of X and Y) ImVec2 PlotDefaultSize; // = 400,300 default size used when ImVec2(0,0) is passed to BeginPlot ImVec2 PlotMinSize; // = 200,150 minimum size plot frame can be when shrunk // style colors ImVec4 Colors[ImPlotCol_COUNT]; // Array of styling colors. Indexable with ImPlotCol_ enums. // colormap ImPlotColormap Colormap; // The current colormap. Set this to either an ImPlotColormap_ enum or an index returned by AddColormap. // settings/flags bool UseLocalTime; // = false, axis labels will be formatted for your timezone when ImPlotAxisFlag_Time is enabled bool UseISO8601; // = false, dates will be formatted according to ISO 8601 where applicable (e.g. YYYY-MM-DD, YYYY-MM, --MM-DD, etc.) bool Use24HourClock; // = false, times will be formatted using a 24 hour clock IMPLOT_API ImPlotStyle(); }; // Support for legacy versions #if (IMGUI_VERSION_NUM < 18716) // Renamed in 1.88 #define ImGuiMod_None 0 #define ImGuiMod_Ctrl ImGuiKeyModFlags_Ctrl #define ImGuiMod_Shift ImGuiKeyModFlags_Shift #define ImGuiMod_Alt ImGuiKeyModFlags_Alt #define ImGuiMod_Super ImGuiKeyModFlags_Super #elif (IMGUI_VERSION_NUM < 18823) // Renamed in 1.89, sorry #define ImGuiMod_None 0 #define ImGuiMod_Ctrl ImGuiModFlags_Ctrl #define ImGuiMod_Shift ImGuiModFlags_Shift #define ImGuiMod_Alt ImGuiModFlags_Alt #define ImGuiMod_Super ImGuiModFlags_Super #endif // Input mapping structure. Default values listed. See also MapInputDefault, MapInputReverse. struct ImPlotInputMap { ImGuiMouseButton Pan; // LMB enables panning when held, int PanMod; // none optional modifier that must be held for panning/fitting ImGuiMouseButton Fit; // LMB initiates fit when double clicked ImGuiMouseButton Select; // RMB begins box selection when pressed and confirms selection when released ImGuiMouseButton SelectCancel; // LMB cancels active box selection when pressed; cannot be same as Select int SelectMod; // none optional modifier that must be held for box selection int SelectHorzMod; // Alt expands active box selection horizontally to plot edge when held int SelectVertMod; // Shift expands active box selection vertically to plot edge when held ImGuiMouseButton Menu; // RMB opens context menus (if enabled) when clicked int OverrideMod; // Ctrl when held, all input is ignored; used to enable axis/plots as DND sources int ZoomMod; // none optional modifier that must be held for scroll wheel zooming float ZoomRate; // 0.1f zoom rate for scroll (e.g. 0.1f = 10% plot range every scroll click); make negative to invert IMPLOT_API ImPlotInputMap(); }; //----------------------------------------------------------------------------- // [SECTION] Callbacks //----------------------------------------------------------------------------- // Callback signature for axis tick label formatter. typedef int (*ImPlotFormatter)(double value, char* buff, int size, void* user_data); // Callback signature for data getter. typedef ImPlotPoint (*ImPlotGetter)(int idx, void* user_data); // Callback signature for axis transform. typedef double (*ImPlotTransform)(double value, void* user_data); namespace ImPlot { //----------------------------------------------------------------------------- // [SECTION] Contexts //----------------------------------------------------------------------------- // Creates a new ImPlot context. Call this after ImGui::CreateContext. IMPLOT_API ImPlotContext* CreateContext(); // Destroys an ImPlot context. Call this before ImGui::DestroyContext. nullptr = destroy current context. IMPLOT_API void DestroyContext(ImPlotContext* ctx = nullptr); // Returns the current ImPlot context. nullptr if no context has ben set. IMPLOT_API ImPlotContext* GetCurrentContext(); // Sets the current ImPlot context. IMPLOT_API void SetCurrentContext(ImPlotContext* ctx); // Sets the current **ImGui** context. This is ONLY necessary if you are compiling // ImPlot as a DLL (not recommended) separate from your ImGui compilation. It // sets the global variable GImGui, which is not shared across DLL boundaries. // See GImGui documentation in imgui.cpp for more details. IMPLOT_API void SetImGuiContext(ImGuiContext* ctx); //----------------------------------------------------------------------------- // [SECTION] Begin/End Plot //----------------------------------------------------------------------------- // Starts a 2D plotting context. If this function returns true, EndPlot() MUST // be called! You are encouraged to use the following convention: // // if (BeginPlot(...)) { // PlotLine(...); // ... // EndPlot(); // } // // Important notes: // // - #title_id must be unique to the current ImGui ID scope. If you need to avoid ID // collisions or don't want to display a title in the plot, use double hashes // (e.g. "MyPlot##HiddenIdText" or "##NoTitle"). // - #size is the **frame** size of the plot widget, not the plot area. The default // size of plots (i.e. when ImVec2(0,0)) can be modified in your ImPlotStyle. IMPLOT_API bool BeginPlot(const char* title_id, const ImVec2& size=ImVec2(-1,0), ImPlotFlags flags=0); // Only call EndPlot() if BeginPlot() returns true! Typically called at the end // of an if statement conditioned on BeginPlot(). See example above. IMPLOT_API void EndPlot(); //----------------------------------------------------------------------------- // [SECTION] Begin/End Subplots //----------------------------------------------------------------------------- // Starts a subdivided plotting context. If the function returns true, // EndSubplots() MUST be called! Call BeginPlot/EndPlot AT MOST [rows*cols] // times in between the begining and end of the subplot context. Plots are // added in row major order. // // Example: // // if (BeginSubplots("My Subplot",2,3,ImVec2(800,400)) { // for (int i = 0; i < 6; ++i) { // if (BeginPlot(...)) { // ImPlot::PlotLine(...); // ... // EndPlot(); // } // } // EndSubplots(); // } // // Produces: // // [0] | [1] | [2] // ----|-----|---- // [3] | [4] | [5] // // Important notes: // // - #title_id must be unique to the current ImGui ID scope. If you need to avoid ID // collisions or don't want to display a title in the plot, use double hashes // (e.g. "MySubplot##HiddenIdText" or "##NoTitle"). // - #rows and #cols must be greater than 0. // - #size is the size of the entire grid of subplots, not the individual plots // - #row_ratios and #col_ratios must have AT LEAST #rows and #cols elements, // respectively. These are the sizes of the rows and columns expressed in ratios. // If the user adjusts the dimensions, the arrays are updated with new ratios. // // Important notes regarding BeginPlot from inside of BeginSubplots: // // - The #title_id parameter of _BeginPlot_ (see above) does NOT have to be // unique when called inside of a subplot context. Subplot IDs are hashed // for your convenience so you don't have call PushID or generate unique title // strings. Simply pass an empty string to BeginPlot unless you want to title // each subplot. // - The #size parameter of _BeginPlot_ (see above) is ignored when inside of a // subplot context. The actual size of the subplot will be based on the // #size value you pass to _BeginSubplots_ and #row/#col_ratios if provided. IMPLOT_API bool BeginSubplots(const char* title_id, int rows, int cols, const ImVec2& size, ImPlotSubplotFlags flags = 0, float* row_ratios = nullptr, float* col_ratios = nullptr); // Only call EndSubplots() if BeginSubplots() returns true! Typically called at the end // of an if statement conditioned on BeginSublots(). See example above. IMPLOT_API void EndSubplots(); //----------------------------------------------------------------------------- // [SECTION] Setup //----------------------------------------------------------------------------- // The following API allows you to setup and customize various aspects of the // current plot. The functions should be called immediately after BeginPlot // and before any other API calls. Typical usage is as follows: // if (BeginPlot(...)) { 1) begin a new plot // SetupAxis(ImAxis_X1, "My X-Axis"); 2) make Setup calls // SetupAxis(ImAxis_Y1, "My Y-Axis"); // SetupLegend(ImPlotLocation_North); // ... // SetupFinish(); 3) [optional] explicitly finish setup // PlotLine(...); 4) plot items // ... // EndPlot(); 5) end the plot // } // // Important notes: // // - Always call Setup code at the top of your BeginPlot conditional statement. // - Setup is locked once you start plotting or explicitly call SetupFinish. // Do NOT call Setup code after you begin plotting or after you make // any non-Setup API calls (e.g. utils like PlotToPixels also lock Setup) // - Calling SetupFinish is OPTIONAL, but probably good practice. If you do not // call it yourself, then the first subsequent plotting or utility function will // call it for you. // Enables an axis or sets the label and/or flags for an existing axis. Leave #label = nullptr for no label. IMPLOT_API void SetupAxis(ImAxis axis, const char* label=nullptr, ImPlotAxisFlags flags=0); // Sets an axis range limits. If ImPlotCond_Always is used, the axes limits will be locked. IMPLOT_API void SetupAxisLimits(ImAxis axis, double v_min, double v_max, ImPlotCond cond = ImPlotCond_Once); // Links an axis range limits to external values. Set to nullptr for no linkage. The pointer data must remain valid until EndPlot. IMPLOT_API void SetupAxisLinks(ImAxis axis, double* link_min, double* link_max); // Sets the format of numeric axis labels via formater specifier (default="%g"). Formated values will be double (i.e. use %f). IMPLOT_API void SetupAxisFormat(ImAxis axis, const char* fmt); // Sets the format of numeric axis labels via formatter callback. Given #value, write a label into #buff. Optionally pass user data. IMPLOT_API void SetupAxisFormat(ImAxis axis, ImPlotFormatter formatter, void* data=nullptr); // Sets an axis' ticks and optionally the labels. To keep the default ticks, set #keep_default=true. IMPLOT_API void SetupAxisTicks(ImAxis axis, const double* values, int n_ticks, const char* const labels[]=nullptr, bool keep_default=false); // Sets an axis' ticks and optionally the labels for the next plot. To keep the default ticks, set #keep_default=true. IMPLOT_API void SetupAxisTicks(ImAxis axis, double v_min, double v_max, int n_ticks, const char* const labels[]=nullptr, bool keep_default=false); // Sets an axis' scale using built-in options. IMPLOT_API void SetupAxisScale(ImAxis axis, ImPlotScale scale); // Sets an axis' scale using user supplied forward and inverse transfroms. IMPLOT_API void SetupAxisScale(ImAxis axis, ImPlotTransform forward, ImPlotTransform inverse, void* data=nullptr); // Sets an axis' limits constraints. IMPLOT_API void SetupAxisLimitsConstraints(ImAxis axis, double v_min, double v_max); // Sets an axis' zoom constraints. IMPLOT_API void SetupAxisZoomConstraints(ImAxis axis, double z_min, double z_max); // Sets the label and/or flags for primary X and Y axes (shorthand for two calls to SetupAxis). IMPLOT_API void SetupAxes(const char* x_label, const char* y_label, ImPlotAxisFlags x_flags=0, ImPlotAxisFlags y_flags=0); // Sets the primary X and Y axes range limits. If ImPlotCond_Always is used, the axes limits will be locked (shorthand for two calls to SetupAxisLimits). IMPLOT_API void SetupAxesLimits(double x_min, double x_max, double y_min, double y_max, ImPlotCond cond = ImPlotCond_Once); // Sets up the plot legend. IMPLOT_API void SetupLegend(ImPlotLocation location, ImPlotLegendFlags flags=0); // Set the location of the current plot's mouse position text (default = South|East). IMPLOT_API void SetupMouseText(ImPlotLocation location, ImPlotMouseTextFlags flags=0); // Explicitly finalize plot setup. Once you call this, you cannot make anymore Setup calls for the current plot! // Note that calling this function is OPTIONAL; it will be called by the first subsequent setup-locking API call. IMPLOT_API void SetupFinish(); //----------------------------------------------------------------------------- // [SECTION] SetNext //----------------------------------------------------------------------------- // Though you should default to the `Setup` API above, there are some scenarios // where (re)configuring a plot or axis before `BeginPlot` is needed (e.g. if // using a preceding button or slider widget to change the plot limits). In // this case, you can use the `SetNext` API below. While this is not as feature // rich as the Setup API, most common needs are provided. These functions can be // called anwhere except for inside of `Begin/EndPlot`. For example: // if (ImGui::Button("Center Plot")) // ImPlot::SetNextPlotLimits(-1,1,-1,1); // if (ImPlot::BeginPlot(...)) { // ... // ImPlot::EndPlot(); // } // // Important notes: // // - You must still enable non-default axes with SetupAxis for these functions // to work properly. // Sets an upcoming axis range limits. If ImPlotCond_Always is used, the axes limits will be locked. IMPLOT_API void SetNextAxisLimits(ImAxis axis, double v_min, double v_max, ImPlotCond cond = ImPlotCond_Once); // Links an upcoming axis range limits to external values. Set to nullptr for no linkage. The pointer data must remain valid until EndPlot! IMPLOT_API void SetNextAxisLinks(ImAxis axis, double* link_min, double* link_max); // Set an upcoming axis to auto fit to its data. IMPLOT_API void SetNextAxisToFit(ImAxis axis); // Sets the upcoming primary X and Y axes range limits. If ImPlotCond_Always is used, the axes limits will be locked (shorthand for two calls to SetupAxisLimits). IMPLOT_API void SetNextAxesLimits(double x_min, double x_max, double y_min, double y_max, ImPlotCond cond = ImPlotCond_Once); // Sets all upcoming axes to auto fit to their data. IMPLOT_API void SetNextAxesToFit(); //----------------------------------------------------------------------------- // [SECTION] Plot Items //----------------------------------------------------------------------------- // The main plotting API is provied below. Call these functions between // Begin/EndPlot and after any Setup API calls. Each plots data on the current // x and y axes, which can be changed with `SetAxis/Axes`. // // The templated functions are explicitly instantiated in implot_items.cpp. // They are not intended to be used generically with custom types. You will get // a linker error if you try! All functions support the following scalar types: // // float, double, ImS8, ImU8, ImS16, ImU16, ImS32, ImU32, ImS64, ImU64 // // // If you need to plot custom or non-homogenous data you have a few options: // // 1. If your data is a simple struct/class (e.g. Vector2f), you can use striding. // This is the most performant option if applicable. // // struct Vector2f { float X, Y; }; // ... // Vector2f data[42]; // ImPlot::PlotLine("line", &data[0].x, &data[0].y, 42, 0, 0, sizeof(Vector2f)); // // 2. Write a custom getter C function or C++ lambda and pass it and optionally your data to // an ImPlot function post-fixed with a G (e.g. PlotScatterG). This has a slight performance // cost, but probably not enough to worry about unless your data is very large. Examples: // // ImPlotPoint MyDataGetter(void* data, int idx) { // MyData* my_data = (MyData*)data; // ImPlotPoint p; // p.x = my_data->GetTime(idx); // p.y = my_data->GetValue(idx); // return p // } // ... // auto my_lambda = [](int idx, void*) { // double t = idx / 999.0; // return ImPlotPoint(t, 0.5+0.5*std::sin(2*PI*10*t)); // }; // ... // if (ImPlot::BeginPlot("MyPlot")) { // MyData my_data; // ImPlot::PlotScatterG("scatter", MyDataGetter, &my_data, my_data.Size()); // ImPlot::PlotLineG("line", my_lambda, nullptr, 1000); // ImPlot::EndPlot(); // } // // NB: All types are converted to double before plotting. You may lose information // if you try plotting extremely large 64-bit integral types. Proceed with caution! // Plots a standard 2D line plot. IMPLOT_TMP void PlotLine(const char* label_id, const T* values, int count, double xscale=1, double xstart=0, ImPlotLineFlags flags=0, int offset=0, int stride=sizeof(T)); IMPLOT_TMP void PlotLine(const char* label_id, const T* xs, const T* ys, int count, ImPlotLineFlags flags=0, int offset=0, int stride=sizeof(T)); IMPLOT_API void PlotLineG(const char* label_id, ImPlotGetter getter, void* data, int count, ImPlotLineFlags flags=0); // Plots a standard 2D scatter plot. Default marker is ImPlotMarker_Circle. IMPLOT_TMP void PlotScatter(const char* label_id, const T* values, int count, double xscale=1, double xstart=0, ImPlotScatterFlags flags=0, int offset=0, int stride=sizeof(T)); IMPLOT_TMP void PlotScatter(const char* label_id, const T* xs, const T* ys, int count, ImPlotScatterFlags flags=0, int offset=0, int stride=sizeof(T)); IMPLOT_API void PlotScatterG(const char* label_id, ImPlotGetter getter, void* data, int count, ImPlotScatterFlags flags=0); // Plots a a stairstep graph. The y value is continued constantly to the right from every x position, i.e. the interval [x[i], x[i+1]) has the value y[i] IMPLOT_TMP void PlotStairs(const char* label_id, const T* values, int count, double xscale=1, double xstart=0, ImPlotStairsFlags flags=0, int offset=0, int stride=sizeof(T)); IMPLOT_TMP void PlotStairs(const char* label_id, const T* xs, const T* ys, int count, ImPlotStairsFlags flags=0, int offset=0, int stride=sizeof(T)); IMPLOT_API void PlotStairsG(const char* label_id, ImPlotGetter getter, void* data, int count, ImPlotStairsFlags flags=0); // Plots a shaded (filled) region between two lines, or a line and a horizontal reference. Set yref to +/-INFINITY for infinite fill extents. IMPLOT_TMP void PlotShaded(const char* label_id, const T* values, int count, double yref=0, double xscale=1, double xstart=0, ImPlotShadedFlags flags=0, int offset=0, int stride=sizeof(T)); IMPLOT_TMP void PlotShaded(const char* label_id, const T* xs, const T* ys, int count, double yref=0, ImPlotShadedFlags flags=0, int offset=0, int stride=sizeof(T)); IMPLOT_TMP void PlotShaded(const char* label_id, const T* xs, const T* ys1, const T* ys2, int count, ImPlotShadedFlags flags=0, int offset=0, int stride=sizeof(T)); IMPLOT_API void PlotShadedG(const char* label_id, ImPlotGetter getter1, void* data1, ImPlotGetter getter2, void* data2, int count, ImPlotShadedFlags flags=0); // Plots a bar graph. Vertical by default. #bar_size and #shift are in plot units. IMPLOT_TMP void PlotBars(const char* label_id, const T* values, int count, double bar_size=0.67, double shift=0, ImPlotBarsFlags flags=0, int offset=0, int stride=sizeof(T)); IMPLOT_TMP void PlotBars(const char* label_id, const T* xs, const T* ys, int count, double bar_size, ImPlotBarsFlags flags=0, int offset=0, int stride=sizeof(T)); IMPLOT_API void PlotBarsG(const char* label_id, ImPlotGetter getter, void* data, int count, double bar_size, ImPlotBarsFlags flags=0); // Plots a group of bars. #values is a row-major matrix with #item_count rows and #group_count cols. #label_ids should have #item_count elements. IMPLOT_TMP void PlotBarGroups(const char* const label_ids[], const T* values, int item_count, int group_count, double group_size=0.67, double shift=0, ImPlotBarGroupsFlags flags=0); // Plots vertical error bar. The label_id should be the same as the label_id of the associated line or bar plot. IMPLOT_TMP void PlotErrorBars(const char* label_id, const T* xs, const T* ys, const T* err, int count, ImPlotErrorBarsFlags flags=0, int offset=0, int stride=sizeof(T)); IMPLOT_TMP void PlotErrorBars(const char* label_id, const T* xs, const T* ys, const T* neg, const T* pos, int count, ImPlotErrorBarsFlags flags=0, int offset=0, int stride=sizeof(T)); // Plots stems. Vertical by default. IMPLOT_TMP void PlotStems(const char* label_id, const T* values, int count, double ref=0, double scale=1, double start=0, ImPlotStemsFlags flags=0, int offset=0, int stride=sizeof(T)); IMPLOT_TMP void PlotStems(const char* label_id, const T* xs, const T* ys, int count, double ref=0, ImPlotStemsFlags flags=0, int offset=0, int stride=sizeof(T)); // Plots infinite vertical or horizontal lines (e.g. for references or asymptotes). IMPLOT_TMP void PlotInfLines(const char* label_id, const T* values, int count, ImPlotInfLinesFlags flags=0, int offset=0, int stride=sizeof(T)); // Plots a pie chart. Center and radius are in plot units. #label_fmt can be set to nullptr for no labels. IMPLOT_TMP void PlotPieChart(const char* const label_ids[], const T* values, int count, double x, double y, double radius, const char* label_fmt="%.1f", double angle0=90, ImPlotPieChartFlags flags=0); // Plots a 2D heatmap chart. Values are expected to be in row-major order by default. Leave #scale_min and scale_max both at 0 for automatic color scaling, or set them to a predefined range. #label_fmt can be set to nullptr for no labels. IMPLOT_TMP void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, double scale_min=0, double scale_max=0, const char* label_fmt="%.1f", const ImPlotPoint& bounds_min=ImPlotPoint(0,0), const ImPlotPoint& bounds_max=ImPlotPoint(1,1), ImPlotHeatmapFlags flags=0); // Plots a horizontal histogram. #bins can be a positive integer or an ImPlotBin_ method. If #range is left unspecified, the min/max of #values will be used as the range. // Otherwise, outlier values outside of the range are not binned. The largest bin count or density is returned. IMPLOT_TMP double PlotHistogram(const char* label_id, const T* values, int count, int bins=ImPlotBin_Sturges, double bar_scale=1.0, ImPlotRange range=ImPlotRange(), ImPlotHistogramFlags flags=0); // Plots two dimensional, bivariate histogram as a heatmap. #x_bins and #y_bins can be a positive integer or an ImPlotBin. If #range is left unspecified, the min/max of // #xs an #ys will be used as the ranges. Otherwise, outlier values outside of range are not binned. The largest bin count or density is returned. IMPLOT_TMP double PlotHistogram2D(const char* label_id, const T* xs, const T* ys, int count, int x_bins=ImPlotBin_Sturges, int y_bins=ImPlotBin_Sturges, ImPlotRect range=ImPlotRect(), ImPlotHistogramFlags flags=0); // Plots digital data. Digital plots do not respond to y drag or zoom, and are always referenced to the bottom of the plot. IMPLOT_TMP void PlotDigital(const char* label_id, const T* xs, const T* ys, int count, ImPlotDigitalFlags flags=0, int offset=0, int stride=sizeof(T)); IMPLOT_API void PlotDigitalG(const char* label_id, ImPlotGetter getter, void* data, int count, ImPlotDigitalFlags flags=0); // Plots an axis-aligned image. #bounds_min/bounds_max are in plot coordinates (y-up) and #uv0/uv1 are in texture coordinates (y-down). IMPLOT_API void PlotImage(const char* label_id, ImTextureID user_texture_id, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max, const ImVec2& uv0=ImVec2(0,0), const ImVec2& uv1=ImVec2(1,1), const ImVec4& tint_col=ImVec4(1,1,1,1), ImPlotImageFlags flags=0); // Plots a centered text label at point x,y with an optional pixel offset. Text color can be changed with ImPlot::PushStyleColor(ImPlotCol_InlayText, ...). IMPLOT_API void PlotText(const char* text, double x, double y, const ImVec2& pix_offset=ImVec2(0,0), ImPlotTextFlags flags=0); // Plots a dummy item (i.e. adds a legend entry colored by ImPlotCol_Line) IMPLOT_API void PlotDummy(const char* label_id, ImPlotDummyFlags flags=0); //----------------------------------------------------------------------------- // [SECTION] Plot Tools //----------------------------------------------------------------------------- // The following can be used to render interactive elements and/or annotations. // Like the item plotting functions above, they apply to the current x and y // axes, which can be changed with `SetAxis/SetAxes`. // Shows a draggable point at x,y. #col defaults to ImGuiCol_Text. IMPLOT_API bool DragPoint(int id, double* x, double* y, const ImVec4& col, float size = 4, ImPlotDragToolFlags flags=0); // Shows a draggable vertical guide line at an x-value. #col defaults to ImGuiCol_Text. IMPLOT_API bool DragLineX(int id, double* x, const ImVec4& col, float thickness = 1, ImPlotDragToolFlags flags=0); // Shows a draggable horizontal guide line at a y-value. #col defaults to ImGuiCol_Text. IMPLOT_API bool DragLineY(int id, double* y, const ImVec4& col, float thickness = 1, ImPlotDragToolFlags flags=0); // Shows a draggable and resizeable rectangle. IMPLOT_API bool DragRect(int id, double* x1, double* y1, double* x2, double* y2, const ImVec4& col, ImPlotDragToolFlags flags=0); // Shows an annotation callout at a chosen point. Clamping keeps annotations in the plot area. Annotations are always rendered on top. IMPLOT_API void Annotation(double x, double y, const ImVec4& col, const ImVec2& pix_offset, bool clamp, bool round = false); IMPLOT_API void Annotation(double x, double y, const ImVec4& col, const ImVec2& pix_offset, bool clamp, const char* fmt, ...) IM_FMTARGS(6); IMPLOT_API void AnnotationV(double x, double y, const ImVec4& col, const ImVec2& pix_offset, bool clamp, const char* fmt, va_list args) IM_FMTLIST(6); // Shows a x-axis tag at the specified coordinate value. IMPLOT_API void TagX(double x, const ImVec4& col, bool round = false); IMPLOT_API void TagX(double x, const ImVec4& col, const char* fmt, ...) IM_FMTARGS(3); IMPLOT_API void TagXV(double x, const ImVec4& col, const char* fmt, va_list args) IM_FMTLIST(3); // Shows a y-axis tag at the specified coordinate value. IMPLOT_API void TagY(double y, const ImVec4& col, bool round = false); IMPLOT_API void TagY(double y, const ImVec4& col, const char* fmt, ...) IM_FMTARGS(3); IMPLOT_API void TagYV(double y, const ImVec4& col, const char* fmt, va_list args) IM_FMTLIST(3); //----------------------------------------------------------------------------- // [SECTION] Plot Utils //----------------------------------------------------------------------------- // Select which axis/axes will be used for subsequent plot elements. IMPLOT_API void SetAxis(ImAxis axis); IMPLOT_API void SetAxes(ImAxis x_axis, ImAxis y_axis); // Convert pixels to a position in the current plot's coordinate system. Passing IMPLOT_AUTO uses the current axes. IMPLOT_API ImPlotPoint PixelsToPlot(const ImVec2& pix, ImAxis x_axis = IMPLOT_AUTO, ImAxis y_axis = IMPLOT_AUTO); IMPLOT_API ImPlotPoint PixelsToPlot(float x, float y, ImAxis x_axis = IMPLOT_AUTO, ImAxis y_axis = IMPLOT_AUTO); // Convert a position in the current plot's coordinate system to pixels. Passing IMPLOT_AUTO uses the current axes. IMPLOT_API ImVec2 PlotToPixels(const ImPlotPoint& plt, ImAxis x_axis = IMPLOT_AUTO, ImAxis y_axis = IMPLOT_AUTO); IMPLOT_API ImVec2 PlotToPixels(double x, double y, ImAxis x_axis = IMPLOT_AUTO, ImAxis y_axis = IMPLOT_AUTO); // Get the current Plot position (top-left) in pixels. IMPLOT_API ImVec2 GetPlotPos(); // Get the curent Plot size in pixels. IMPLOT_API ImVec2 GetPlotSize(); // Returns the mouse position in x,y coordinates of the current plot. Passing IMPLOT_AUTO uses the current axes. IMPLOT_API ImPlotPoint GetPlotMousePos(ImAxis x_axis = IMPLOT_AUTO, ImAxis y_axis = IMPLOT_AUTO); // Returns the current plot axis range. IMPLOT_API ImPlotRect GetPlotLimits(ImAxis x_axis = IMPLOT_AUTO, ImAxis y_axis = IMPLOT_AUTO); // Returns true if the plot area in the current plot is hovered. IMPLOT_API bool IsPlotHovered(); // Returns true if the axis label area in the current plot is hovered. IMPLOT_API bool IsAxisHovered(ImAxis axis); // Returns true if the bounding frame of a subplot is hovered. IMPLOT_API bool IsSubplotsHovered(); // Returns true if the current plot is being box selected. IMPLOT_API bool IsPlotSelected(); // Returns the current plot box selection bounds. Passing IMPLOT_AUTO uses the current axes. IMPLOT_API ImPlotRect GetPlotSelection(ImAxis x_axis = IMPLOT_AUTO, ImAxis y_axis = IMPLOT_AUTO); // Cancels a the current plot box selection. IMPLOT_API void CancelPlotSelection(); // Hides or shows the next plot item (i.e. as if it were toggled from the legend). // Use ImPlotCond_Always if you need to forcefully set this every frame. IMPLOT_API void HideNextItem(bool hidden = true, ImPlotCond cond = ImPlotCond_Once); // Use the following around calls to Begin/EndPlot to align l/r/t/b padding. // Consider using Begin/EndSubplots first. They are more feature rich and // accomplish the same behaviour by default. The functions below offer lower // level control of plot alignment. // Align axis padding over multiple plots in a single row or column. #group_id must // be unique. If this function returns true, EndAlignedPlots() must be called. IMPLOT_API bool BeginAlignedPlots(const char* group_id, bool vertical = true); // Only call EndAlignedPlots() if BeginAlignedPlots() returns true! IMPLOT_API void EndAlignedPlots(); //----------------------------------------------------------------------------- // [SECTION] Legend Utils //----------------------------------------------------------------------------- // Begin a popup for a legend entry. IMPLOT_API bool BeginLegendPopup(const char* label_id, ImGuiMouseButton mouse_button=1); // End a popup for a legend entry. IMPLOT_API void EndLegendPopup(); // Returns true if a plot item legend entry is hovered. IMPLOT_API bool IsLegendEntryHovered(const char* label_id); //----------------------------------------------------------------------------- // [SECTION] Drag and Drop //----------------------------------------------------------------------------- // Turns the current plot's plotting area into a drag and drop target. Don't forget to call EndDragDropTarget! IMPLOT_API bool BeginDragDropTargetPlot(); // Turns the current plot's X-axis into a drag and drop target. Don't forget to call EndDragDropTarget! IMPLOT_API bool BeginDragDropTargetAxis(ImAxis axis); // Turns the current plot's legend into a drag and drop target. Don't forget to call EndDragDropTarget! IMPLOT_API bool BeginDragDropTargetLegend(); // Ends a drag and drop target (currently just an alias for ImGui::EndDragDropTarget). IMPLOT_API void EndDragDropTarget(); // NB: By default, plot and axes drag and drop *sources* require holding the Ctrl modifier to initiate the drag. // You can change the modifier if desired. If ImGuiMod_None is provided, the axes will be locked from panning. // Turns the current plot's plotting area into a drag and drop source. You must hold Ctrl. Don't forget to call EndDragDropSource! IMPLOT_API bool BeginDragDropSourcePlot(ImGuiDragDropFlags flags=0); // Turns the current plot's X-axis into a drag and drop source. You must hold Ctrl. Don't forget to call EndDragDropSource! IMPLOT_API bool BeginDragDropSourceAxis(ImAxis axis, ImGuiDragDropFlags flags=0); // Turns an item in the current plot's legend into drag and drop source. Don't forget to call EndDragDropSource! IMPLOT_API bool BeginDragDropSourceItem(const char* label_id, ImGuiDragDropFlags flags=0); // Ends a drag and drop source (currently just an alias for ImGui::EndDragDropSource). IMPLOT_API void EndDragDropSource(); //----------------------------------------------------------------------------- // [SECTION] Styling //----------------------------------------------------------------------------- // Styling colors in ImPlot works similarly to styling colors in ImGui, but // with one important difference. Like ImGui, all style colors are stored in an // indexable array in ImPlotStyle. You can permanently modify these values through // GetStyle().Colors, or temporarily modify them with Push/Pop functions below. // However, by default all style colors in ImPlot default to a special color // IMPLOT_AUTO_COL. The behavior of this color depends upon the style color to // which it as applied: // // 1) For style colors associated with plot items (e.g. ImPlotCol_Line), // IMPLOT_AUTO_COL tells ImPlot to color the item with the next unused // color in the current colormap. Thus, every item will have a different // color up to the number of colors in the colormap, at which point the // colormap will roll over. For most use cases, you should not need to // set these style colors to anything but IMPLOT_COL_AUTO; you are // probably better off changing the current colormap. However, if you // need to explicitly color a particular item you may either Push/Pop // the style color around the item in question, or use the SetNextXXXStyle // API below. If you permanently set one of these style colors to a specific // color, or forget to call Pop, then all subsequent items will be styled // with the color you set. // // 2) For style colors associated with plot styling (e.g. ImPlotCol_PlotBg), // IMPLOT_AUTO_COL tells ImPlot to set that color from color data in your // **ImGuiStyle**. The ImGuiCol_ that these style colors default to are // detailed above, and in general have been mapped to produce plots visually // consistent with your current ImGui style. Of course, you are free to // manually set these colors to whatever you like, and further can Push/Pop // them around individual plots for plot-specific styling (e.g. coloring axes). // Provides access to plot style structure for permanant modifications to colors, sizes, etc. IMPLOT_API ImPlotStyle& GetStyle(); // Style plot colors for current ImGui style (default). IMPLOT_API void StyleColorsAuto(ImPlotStyle* dst = nullptr); // Style plot colors for ImGui "Classic". IMPLOT_API void StyleColorsClassic(ImPlotStyle* dst = nullptr); // Style plot colors for ImGui "Dark". IMPLOT_API void StyleColorsDark(ImPlotStyle* dst = nullptr); // Style plot colors for ImGui "Light". IMPLOT_API void StyleColorsLight(ImPlotStyle* dst = nullptr); // Use PushStyleX to temporarily modify your ImPlotStyle. The modification // will last until the matching call to PopStyleX. You MUST call a pop for // every push, otherwise you will leak memory! This behaves just like ImGui. // Temporarily modify a style color. Don't forget to call PopStyleColor! IMPLOT_API void PushStyleColor(ImPlotCol idx, ImU32 col); IMPLOT_API void PushStyleColor(ImPlotCol idx, const ImVec4& col); // Undo temporary style color modification(s). Undo multiple pushes at once by increasing count. IMPLOT_API void PopStyleColor(int count = 1); // Temporarily modify a style variable of float type. Don't forget to call PopStyleVar! IMPLOT_API void PushStyleVar(ImPlotStyleVar idx, float val); // Temporarily modify a style variable of int type. Don't forget to call PopStyleVar! IMPLOT_API void PushStyleVar(ImPlotStyleVar idx, int val); // Temporarily modify a style variable of ImVec2 type. Don't forget to call PopStyleVar! IMPLOT_API void PushStyleVar(ImPlotStyleVar idx, const ImVec2& val); // Undo temporary style variable modification(s). Undo multiple pushes at once by increasing count. IMPLOT_API void PopStyleVar(int count = 1); // The following can be used to modify the style of the next plot item ONLY. They do // NOT require calls to PopStyleX. Leave style attributes you don't want modified to // IMPLOT_AUTO or IMPLOT_AUTO_COL. Automatic styles will be deduced from the current // values in your ImPlotStyle or from Colormap data. // Set the line color and weight for the next item only. IMPLOT_API void SetNextLineStyle(const ImVec4& col = IMPLOT_AUTO_COL, float weight = IMPLOT_AUTO); // Set the fill color for the next item only. IMPLOT_API void SetNextFillStyle(const ImVec4& col = IMPLOT_AUTO_COL, float alpha_mod = IMPLOT_AUTO); // Set the marker style for the next item only. IMPLOT_API void SetNextMarkerStyle(ImPlotMarker marker = IMPLOT_AUTO, float size = IMPLOT_AUTO, const ImVec4& fill = IMPLOT_AUTO_COL, float weight = IMPLOT_AUTO, const ImVec4& outline = IMPLOT_AUTO_COL); // Set the error bar style for the next item only. IMPLOT_API void SetNextErrorBarStyle(const ImVec4& col = IMPLOT_AUTO_COL, float size = IMPLOT_AUTO, float weight = IMPLOT_AUTO); // Gets the last item primary color (i.e. its legend icon color) IMPLOT_API ImVec4 GetLastItemColor(); // Returns the null terminated string name for an ImPlotCol. IMPLOT_API const char* GetStyleColorName(ImPlotCol idx); // Returns the null terminated string name for an ImPlotMarker. IMPLOT_API const char* GetMarkerName(ImPlotMarker idx); //----------------------------------------------------------------------------- // [SECTION] Colormaps //----------------------------------------------------------------------------- // Item styling is based on colormaps when the relevant ImPlotCol_XXX is set to // IMPLOT_AUTO_COL (default). Several built-in colormaps are available. You can // add and then push/pop your own colormaps as well. To permanently set a colormap, // modify the Colormap index member of your ImPlotStyle. // Colormap data will be ignored and a custom color will be used if you have done one of the following: // 1) Modified an item style color in your ImPlotStyle to anything other than IMPLOT_AUTO_COL. // 2) Pushed an item style color using PushStyleColor(). // 3) Set the next item style with a SetNextXXXStyle function. // Add a new colormap. The color data will be copied. The colormap can be used by pushing either the returned index or the // string name with PushColormap. The colormap name must be unique and the size must be greater than 1. You will receive // an assert otherwise! By default colormaps are considered to be qualitative (i.e. discrete). If you want to create a // continuous colormap, set #qual=false. This will treat the colors you provide as keys, and ImPlot will build a linearly // interpolated lookup table. The memory footprint of this table will be exactly ((size-1)*255+1)*4 bytes. IMPLOT_API ImPlotColormap AddColormap(const char* name, const ImVec4* cols, int size, bool qual=true); IMPLOT_API ImPlotColormap AddColormap(const char* name, const ImU32* cols, int size, bool qual=true); // Returns the number of available colormaps (i.e. the built-in + user-added count). IMPLOT_API int GetColormapCount(); // Returns a null terminated string name for a colormap given an index. Returns nullptr if index is invalid. IMPLOT_API const char* GetColormapName(ImPlotColormap cmap); // Returns an index number for a colormap given a valid string name. Returns -1 if name is invalid. IMPLOT_API ImPlotColormap GetColormapIndex(const char* name); // Temporarily switch to one of the built-in (i.e. ImPlotColormap_XXX) or user-added colormaps (i.e. a return value of AddColormap). Don't forget to call PopColormap! IMPLOT_API void PushColormap(ImPlotColormap cmap); // Push a colormap by string name. Use built-in names such as "Default", "Deep", "Jet", etc. or a string you provided to AddColormap. Don't forget to call PopColormap! IMPLOT_API void PushColormap(const char* name); // Undo temporary colormap modification(s). Undo multiple pushes at once by increasing count. IMPLOT_API void PopColormap(int count = 1); // Returns the next color from the current colormap and advances the colormap for the current plot. // Can also be used with no return value to skip colors if desired. You need to call this between Begin/EndPlot! IMPLOT_API ImVec4 NextColormapColor(); // Colormap utils. If cmap = IMPLOT_AUTO (default), the current colormap is assumed. // Pass an explicit colormap index (built-in or user-added) to specify otherwise. // Returns the size of a colormap. IMPLOT_API int GetColormapSize(ImPlotColormap cmap = IMPLOT_AUTO); // Returns a color from a colormap given an index >= 0 (modulo will be performed). IMPLOT_API ImVec4 GetColormapColor(int idx, ImPlotColormap cmap = IMPLOT_AUTO); // Sample a color from the current colormap given t between 0 and 1. IMPLOT_API ImVec4 SampleColormap(float t, ImPlotColormap cmap = IMPLOT_AUTO); // Shows a vertical color scale with linear spaced ticks using the specified color map. Use double hashes to hide label (e.g. "##NoLabel"). If scale_min > scale_max, the scale to color mapping will be reversed. IMPLOT_API void ColormapScale(const char* label, double scale_min, double scale_max, const ImVec2& size = ImVec2(0,0), const char* format = "%g", ImPlotColormapScaleFlags flags = 0, ImPlotColormap cmap = IMPLOT_AUTO); // Shows a horizontal slider with a colormap gradient background. Optionally returns the color sampled at t in [0 1]. IMPLOT_API bool ColormapSlider(const char* label, float* t, ImVec4* out = nullptr, const char* format = "", ImPlotColormap cmap = IMPLOT_AUTO); // Shows a button with a colormap gradient brackground. IMPLOT_API bool ColormapButton(const char* label, const ImVec2& size = ImVec2(0,0), ImPlotColormap cmap = IMPLOT_AUTO); // When items in a plot sample their color from a colormap, the color is cached and does not change // unless explicitly overriden. Therefore, if you change the colormap after the item has already been plotted, // item colors will NOT update. If you need item colors to resample the new colormap, then use this // function to bust the cached colors. If #plot_title_id is nullptr, then every item in EVERY existing plot // will be cache busted. Otherwise only the plot specified by #plot_title_id will be busted. For the // latter, this function must be called in the same ImGui ID scope that the plot is in. You should rarely if ever // need this function, but it is available for applications that require runtime colormap swaps (e.g. Heatmaps demo). IMPLOT_API void BustColorCache(const char* plot_title_id = nullptr); //----------------------------------------------------------------------------- // [SECTION] Input Mapping //----------------------------------------------------------------------------- // Provides access to input mapping structure for permanant modifications to controls for pan, select, etc. IMPLOT_API ImPlotInputMap& GetInputMap(); // Default input mapping: pan = LMB drag, box select = RMB drag, fit = LMB double click, context menu = RMB click, zoom = scroll. IMPLOT_API void MapInputDefault(ImPlotInputMap* dst = nullptr); // Reverse input mapping: pan = RMB drag, box select = LMB drag, fit = LMB double click, context menu = RMB click, zoom = scroll. IMPLOT_API void MapInputReverse(ImPlotInputMap* dst = nullptr); //----------------------------------------------------------------------------- // [SECTION] Miscellaneous //----------------------------------------------------------------------------- // Render icons similar to those that appear in legends (nifty for data lists). IMPLOT_API void ItemIcon(const ImVec4& col); IMPLOT_API void ItemIcon(ImU32 col); IMPLOT_API void ColormapIcon(ImPlotColormap cmap); // Get the plot draw list for custom rendering to the current plot area. Call between Begin/EndPlot. IMPLOT_API ImDrawList* GetPlotDrawList(); // Push clip rect for rendering to current plot area. The rect can be expanded or contracted by #expand pixels. Call between Begin/EndPlot. IMPLOT_API void PushPlotClipRect(float expand=0); // Pop plot clip rect. Call between Begin/EndPlot. IMPLOT_API void PopPlotClipRect(); // Shows ImPlot style selector dropdown menu. IMPLOT_API bool ShowStyleSelector(const char* label); // Shows ImPlot colormap selector dropdown menu. IMPLOT_API bool ShowColormapSelector(const char* label); // Shows ImPlot input map selector dropdown menu. IMPLOT_API bool ShowInputMapSelector(const char* label); // Shows ImPlot style editor block (not a window). IMPLOT_API void ShowStyleEditor(ImPlotStyle* ref = nullptr); // Add basic help/info block for end users (not a window). IMPLOT_API void ShowUserGuide(); // Shows ImPlot metrics/debug information window. IMPLOT_API void ShowMetricsWindow(bool* p_popen = nullptr); //----------------------------------------------------------------------------- // [SECTION] Demo //----------------------------------------------------------------------------- // Shows the ImPlot demo window (add implot_demo.cpp to your sources!) IMPLOT_API void ShowDemoWindow(bool* p_open = nullptr); } // namespace ImPlot //----------------------------------------------------------------------------- // [SECTION] Obsolete API //----------------------------------------------------------------------------- // The following functions will be removed! Keep your copy of implot up to date! // Occasionally set '#define IMPLOT_DISABLE_OBSOLETE_FUNCTIONS' to stay ahead. // If you absolutely must use these functions and do not want to receive compiler // warnings, set '#define IMPLOT_DISABLE_OBSOLETE_WARNINGS'. #ifndef IMPLOT_DISABLE_OBSOLETE_FUNCTIONS #ifndef IMPLOT_DISABLE_DEPRECATED_WARNINGS #if __cplusplus > 201402L #define IMPLOT_DEPRECATED(method) [[deprecated]] method #elif defined( __GNUC__ ) && !defined( __INTEL_COMPILER ) && ( __GNUC__ > 3 || ( __GNUC__ == 3 && __GNUC_MINOR__ >= 1 ) ) #define IMPLOT_DEPRECATED(method) method __attribute__( ( deprecated ) ) #elif defined( _MSC_VER ) #define IMPLOT_DEPRECATED(method) __declspec(deprecated) method #else #define IMPLOT_DEPRECATED(method) method #endif #else #define IMPLOT_DEPRECATED(method) method #endif enum ImPlotFlagsObsolete_ { ImPlotFlags_YAxis2 = 1 << 20, ImPlotFlags_YAxis3 = 1 << 21, }; namespace ImPlot { // OBSOLETED in v0.13 -> PLANNED REMOVAL in v1.0 IMPLOT_DEPRECATED( IMPLOT_API bool BeginPlot(const char* title_id, const char* x_label, // = nullptr, const char* y_label, // = nullptr, const ImVec2& size = ImVec2(-1,0), ImPlotFlags flags = ImPlotFlags_None, ImPlotAxisFlags x_flags = 0, ImPlotAxisFlags y_flags = 0, ImPlotAxisFlags y2_flags = ImPlotAxisFlags_AuxDefault, ImPlotAxisFlags y3_flags = ImPlotAxisFlags_AuxDefault, const char* y2_label = nullptr, const char* y3_label = nullptr) ); } // namespace ImPlot #endif ================================================ FILE: Source/External/imgui_tools/implot/implot_demo.cpp ================================================ // MIT License // Copyright (c) 2022 Evan Pezent // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // ImPlot v0.14 // We define this so that the demo does not accidentally use deprecated API #ifndef IMPLOT_DISABLE_OBSOLETE_FUNCTIONS #define IMPLOT_DISABLE_OBSOLETE_FUNCTIONS #endif #include "implot.h" #include #include #include #include #ifdef _MSC_VER #define sprintf sprintf_s #endif #ifndef PI #define PI 3.14159265358979323846 #endif #define CHECKBOX_FLAG(flags, flag) ImGui::CheckboxFlags(#flag, (unsigned int*)&flags, flag) // Encapsulates examples for customizing ImPlot. namespace MyImPlot { // Example for Custom Data and Getters section. struct Vector2f { Vector2f(float _x, float _y) { x = _x; y = _y; } float x, y; }; // Example for Custom Data and Getters section. struct WaveData { double X, Amp, Freq, Offset; WaveData(double x, double amp, double freq, double offset) { X = x; Amp = amp; Freq = freq; Offset = offset; } }; ImPlotPoint SineWave(int idx, void* wave_data); ImPlotPoint SawWave(int idx, void* wave_data); ImPlotPoint Spiral(int idx, void* wave_data); // Example for Tables section. void Sparkline(const char* id, const float* values, int count, float min_v, float max_v, int offset, const ImVec4& col, const ImVec2& size); // Example for Custom Plotters and Tooltips section. void PlotCandlestick(const char* label_id, const double* xs, const double* opens, const double* closes, const double* lows, const double* highs, int count, bool tooltip = true, float width_percent = 0.25f, ImVec4 bullCol = ImVec4(0,1,0,1), ImVec4 bearCol = ImVec4(1,0,0,1)); // Example for Custom Styles section. void StyleSeaborn(); } // namespace MyImPlot namespace ImPlot { template inline T RandomRange(T min, T max) { T scale = rand() / (T) RAND_MAX; return min + scale * ( max - min ); } ImVec4 RandomColor() { ImVec4 col; col.x = RandomRange(0.0f,1.0f); col.y = RandomRange(0.0f,1.0f); col.z = RandomRange(0.0f,1.0f); col.w = 1.0f; return col; } double RandomGauss() { static double V1, V2, S; static int phase = 0; double X; if(phase == 0) { do { double U1 = (double)rand() / RAND_MAX; double U2 = (double)rand() / RAND_MAX; V1 = 2 * U1 - 1; V2 = 2 * U2 - 1; S = V1 * V1 + V2 * V2; } while(S >= 1 || S == 0); X = V1 * sqrt(-2 * log(S) / S); } else X = V2 * sqrt(-2 * log(S) / S); phase = 1 - phase; return X; } template struct NormalDistribution { NormalDistribution(double mean, double sd) { for (int i = 0; i < N; ++i) Data[i] = RandomGauss()*sd + mean; } double Data[N]; }; // utility structure for realtime plot struct ScrollingBuffer { int MaxSize; int Offset; ImVector Data; ScrollingBuffer(int max_size = 2000) { MaxSize = max_size; Offset = 0; Data.reserve(MaxSize); } void AddPoint(float x, float y) { if (Data.size() < MaxSize) Data.push_back(ImVec2(x,y)); else { Data[Offset] = ImVec2(x,y); Offset = (Offset + 1) % MaxSize; } } void Erase() { if (Data.size() > 0) { Data.shrink(0); Offset = 0; } } }; // utility structure for realtime plot struct RollingBuffer { float Span; ImVector Data; RollingBuffer() { Span = 10.0f; Data.reserve(2000); } void AddPoint(float x, float y) { float xmod = fmodf(x, Span); if (!Data.empty() && xmod < Data.back().x) Data.shrink(0); Data.push_back(ImVec2(xmod, y)); } }; // Huge data used by Time Formatting example (~500 MB allocation!) struct HugeTimeData { HugeTimeData(double min) { Ts = new double[Size]; Ys = new double[Size]; for (int i = 0; i < Size; ++i) { Ts[i] = min + i; Ys[i] = GetY(Ts[i]); } } ~HugeTimeData() { delete[] Ts; delete[] Ys; } static double GetY(double t) { return 0.5 + 0.25 * sin(t/86400/12) + 0.005 * sin(t/3600); } double* Ts; double* Ys; static const int Size = 60*60*24*366; }; //----------------------------------------------------------------------------- // [SECTION] Demo Functions //----------------------------------------------------------------------------- void Demo_Help() { ImGui::Text("ABOUT THIS DEMO:"); ImGui::BulletText("Sections below are demonstrating many aspects of the library."); ImGui::BulletText("The \"Tools\" menu above gives access to: Style Editors (ImPlot/ImGui)\n" "and Metrics (general purpose Dear ImGui debugging tool)."); ImGui::Separator(); ImGui::Text("PROGRAMMER GUIDE:"); ImGui::BulletText("See the ShowDemoWindow() code in implot_demo.cpp. <- you are here!"); ImGui::BulletText("If you see visual artifacts, do one of the following:"); ImGui::Indent(); ImGui::BulletText("Handle ImGuiBackendFlags_RendererHasVtxOffset for 16-bit indices in your backend."); ImGui::BulletText("Or, enable 32-bit indices in imconfig.h."); ImGui::BulletText("Your current configuration is:"); ImGui::Indent(); ImGui::BulletText("ImDrawIdx: %d-bit", (int)(sizeof(ImDrawIdx) * 8)); ImGui::BulletText("ImGuiBackendFlags_RendererHasVtxOffset: %s", (ImGui::GetIO().BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset) ? "True" : "False"); ImGui::Unindent(); ImGui::Unindent(); ImGui::Separator(); ImGui::Text("USER GUIDE:"); ShowUserGuide(); } //----------------------------------------------------------------------------- void ButtonSelector(const char* label, ImGuiMouseButton* b) { ImGui::PushID(label); if (ImGui::RadioButton("LMB",*b == ImGuiMouseButton_Left)) *b = ImGuiMouseButton_Left; ImGui::SameLine(); if (ImGui::RadioButton("RMB",*b == ImGuiMouseButton_Right)) *b = ImGuiMouseButton_Right; ImGui::SameLine(); if (ImGui::RadioButton("MMB",*b == ImGuiMouseButton_Middle)) *b = ImGuiMouseButton_Middle; ImGui::PopID(); } void ModSelector(const char* label, int* k) { ImGui::PushID(label); ImGui::CheckboxFlags("Ctrl", (unsigned int*)k, ImGuiMod_Ctrl); ImGui::SameLine(); ImGui::CheckboxFlags("Shift", (unsigned int*)k, ImGuiMod_Shift); ImGui::SameLine(); ImGui::CheckboxFlags("Alt", (unsigned int*)k, ImGuiMod_Alt); ImGui::SameLine(); ImGui::CheckboxFlags("Super", (unsigned int*)k, ImGuiMod_Super); ImGui::PopID(); } void InputMapping(const char* label, ImGuiMouseButton* b, int* k) { ImGui::LabelText("##","%s",label); if (b != nullptr) { ImGui::SameLine(100); ButtonSelector(label,b); } if (k != nullptr) { ImGui::SameLine(300); ModSelector(label,k); } } void ShowInputMapping() { ImPlotInputMap& map = ImPlot::GetInputMap(); InputMapping("Pan",&map.Pan,&map.PanMod); InputMapping("Fit",&map.Fit,nullptr); InputMapping("Select",&map.Select,&map.SelectMod); InputMapping("SelectHorzMod",nullptr,&map.SelectHorzMod); InputMapping("SelectVertMod",nullptr,&map.SelectVertMod); InputMapping("SelectCancel",&map.SelectCancel,nullptr); InputMapping("Menu",&map.Menu,nullptr); InputMapping("OverrideMod",nullptr,&map.OverrideMod); InputMapping("ZoomMod",nullptr,&map.ZoomMod); ImGui::SliderFloat("ZoomRate",&map.ZoomRate,-1,1); } void Demo_Config() { ImGui::ShowFontSelector("Font"); ImGui::ShowStyleSelector("ImGui Style"); ImPlot::ShowStyleSelector("ImPlot Style"); ImPlot::ShowColormapSelector("ImPlot Colormap"); ImPlot::ShowInputMapSelector("Input Map"); ImGui::Separator(); ImGui::Checkbox("Use Local Time", &ImPlot::GetStyle().UseLocalTime); ImGui::Checkbox("Use ISO 8601", &ImPlot::GetStyle().UseISO8601); ImGui::Checkbox("Use 24 Hour Clock", &ImPlot::GetStyle().Use24HourClock); ImGui::Separator(); if (ImPlot::BeginPlot("Preview")) { static double now = (double)time(nullptr); ImPlot::SetupAxisScale(ImAxis_X1, ImPlotScale_Time); ImPlot::SetupAxisLimits(ImAxis_X1, now, now + 24*3600); for (int i = 0; i < 10; ++i) { double x[2] = {now, now + 24*3600}; double y[2] = {0,i/9.0}; ImGui::PushID(i); ImPlot::PlotLine("##Line",x,y,2); ImGui::PopID(); } ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_LinePlots() { static float xs1[1001], ys1[1001]; for (int i = 0; i < 1001; ++i) { xs1[i] = i * 0.001f; ys1[i] = 0.5f + 0.5f * sinf(50 * (xs1[i] + (float)ImGui::GetTime() / 10)); } static double xs2[20], ys2[20]; for (int i = 0; i < 20; ++i) { xs2[i] = i * 1/19.0f; ys2[i] = xs2[i] * xs2[i]; } if (ImPlot::BeginPlot("Line Plots")) { ImPlot::SetupAxes("x","y"); ImPlot::PlotLine("f(x)", xs1, ys1, 1001); ImPlot::SetNextMarkerStyle(ImPlotMarker_Circle); ImPlot::PlotLine("g(x)", xs2, ys2, 20,ImPlotLineFlags_Segments); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_FilledLinePlots() { static double xs1[101], ys1[101], ys2[101], ys3[101]; srand(0); for (int i = 0; i < 101; ++i) { xs1[i] = (float)i; ys1[i] = RandomRange(400.0,450.0); ys2[i] = RandomRange(275.0,350.0); ys3[i] = RandomRange(150.0,225.0); } static bool show_lines = true; static bool show_fills = true; static float fill_ref = 0; static int shade_mode = 0; static ImPlotShadedFlags flags = 0; ImGui::Checkbox("Lines",&show_lines); ImGui::SameLine(); ImGui::Checkbox("Fills",&show_fills); if (show_fills) { ImGui::SameLine(); if (ImGui::RadioButton("To -INF",shade_mode == 0)) shade_mode = 0; ImGui::SameLine(); if (ImGui::RadioButton("To +INF",shade_mode == 1)) shade_mode = 1; ImGui::SameLine(); if (ImGui::RadioButton("To Ref",shade_mode == 2)) shade_mode = 2; if (shade_mode == 2) { ImGui::SameLine(); ImGui::SetNextItemWidth(100); ImGui::DragFloat("##Ref",&fill_ref, 1, -100, 500); } } if (ImPlot::BeginPlot("Stock Prices")) { ImPlot::SetupAxes("Days","Price"); ImPlot::SetupAxesLimits(0,100,0,500); if (show_fills) { ImPlot::PushStyleVar(ImPlotStyleVar_FillAlpha, 0.25f); ImPlot::PlotShaded("Stock 1", xs1, ys1, 101, shade_mode == 0 ? -INFINITY : shade_mode == 1 ? INFINITY : fill_ref, flags); ImPlot::PlotShaded("Stock 2", xs1, ys2, 101, shade_mode == 0 ? -INFINITY : shade_mode == 1 ? INFINITY : fill_ref, flags); ImPlot::PlotShaded("Stock 3", xs1, ys3, 101, shade_mode == 0 ? -INFINITY : shade_mode == 1 ? INFINITY : fill_ref, flags); ImPlot::PopStyleVar(); } if (show_lines) { ImPlot::PlotLine("Stock 1", xs1, ys1, 101); ImPlot::PlotLine("Stock 2", xs1, ys2, 101); ImPlot::PlotLine("Stock 3", xs1, ys3, 101); } ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_ShadedPlots() { static float xs[1001], ys[1001], ys1[1001], ys2[1001], ys3[1001], ys4[1001]; srand(0); for (int i = 0; i < 1001; ++i) { xs[i] = i * 0.001f; ys[i] = 0.25f + 0.25f * sinf(25 * xs[i]) * sinf(5 * xs[i]) + RandomRange(-0.01f, 0.01f); ys1[i] = ys[i] + RandomRange(0.1f, 0.12f); ys2[i] = ys[i] - RandomRange(0.1f, 0.12f); ys3[i] = 0.75f + 0.2f * sinf(25 * xs[i]); ys4[i] = 0.75f + 0.1f * cosf(25 * xs[i]); } static float alpha = 0.25f; ImGui::DragFloat("Alpha",&alpha,0.01f,0,1); if (ImPlot::BeginPlot("Shaded Plots")) { ImPlot::PushStyleVar(ImPlotStyleVar_FillAlpha, alpha); ImPlot::PlotShaded("Uncertain Data",xs,ys1,ys2,1001); ImPlot::PlotLine("Uncertain Data", xs, ys, 1001); ImPlot::PlotShaded("Overlapping",xs,ys3,ys4,1001); ImPlot::PlotLine("Overlapping",xs,ys3,1001); ImPlot::PlotLine("Overlapping",xs,ys4,1001); ImPlot::PopStyleVar(); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_ScatterPlots() { srand(0); static float xs1[100], ys1[100]; for (int i = 0; i < 100; ++i) { xs1[i] = i * 0.01f; ys1[i] = xs1[i] + 0.1f * ((float)rand() / (float)RAND_MAX); } static float xs2[50], ys2[50]; for (int i = 0; i < 50; i++) { xs2[i] = 0.25f + 0.2f * ((float)rand() / (float)RAND_MAX); ys2[i] = 0.75f + 0.2f * ((float)rand() / (float)RAND_MAX); } if (ImPlot::BeginPlot("Scatter Plot")) { ImPlot::PlotScatter("Data 1", xs1, ys1, 100); ImPlot::PushStyleVar(ImPlotStyleVar_FillAlpha, 0.25f); ImPlot::SetNextMarkerStyle(ImPlotMarker_Square, 6, ImPlot::GetColormapColor(1), IMPLOT_AUTO, ImPlot::GetColormapColor(1)); ImPlot::PlotScatter("Data 2", xs2, ys2, 50); ImPlot::PopStyleVar(); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_StairstepPlots() { static float ys1[21], ys2[21]; for (int i = 0; i < 21; ++i) { ys1[i] = 0.75f + 0.2f * sinf(10 * i * 0.05f); ys2[i] = 0.25f + 0.2f * sinf(10 * i * 0.05f); } static ImPlotStairsFlags flags = 0; CHECKBOX_FLAG(flags, ImPlotStairsFlags_Shaded); if (ImPlot::BeginPlot("Stairstep Plot")) { ImPlot::SetupAxes("x","f(x)"); ImPlot::SetupAxesLimits(0,1,0,1); ImPlot::PushStyleColor(ImPlotCol_Line, ImVec4(0.5f,0.5f,0.5f,1.0f)); ImPlot::PlotLine("##1",ys1,21,0.05f); ImPlot::PlotLine("##2",ys2,21,0.05f); ImPlot::PopStyleColor(); ImPlot::SetNextMarkerStyle(ImPlotMarker_Circle); ImPlot::SetNextFillStyle(IMPLOT_AUTO_COL, 0.25f); ImPlot::PlotStairs("Post Step (default)", ys1, 21, 0.05f, 0, flags); ImPlot::SetNextMarkerStyle(ImPlotMarker_Circle); ImPlot::SetNextFillStyle(IMPLOT_AUTO_COL, 0.25f); ImPlot::PlotStairs("Pre Step", ys2, 21, 0.05f, 0, flags|ImPlotStairsFlags_PreStep); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_BarPlots() { static ImS8 data[10] = {1,2,3,4,5,6,7,8,9,10}; if (ImPlot::BeginPlot("Bar Plot")) { ImPlot::PlotBars("Vertical",data,10,0.7,1); ImPlot::PlotBars("Horizontal",data,10,0.4,1,ImPlotBarsFlags_Horizontal); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_BarGroups() { static ImS8 data[30] = {83, 67, 23, 89, 83, 78, 91, 82, 85, 90, // midterm 80, 62, 56, 99, 55, 78, 88, 78, 90, 100, // final 80, 69, 52, 92, 72, 78, 75, 76, 89, 95}; // course static const char* ilabels[] = {"Midterm Exam","Final Exam","Course Grade"}; static const char* glabels[] = {"S1","S2","S3","S4","S5","S6","S7","S8","S9","S10"}; static const double positions[] = {0,1,2,3,4,5,6,7,8,9}; static int items = 3; static int groups = 10; static float size = 0.67f; static ImPlotBarGroupsFlags flags = 0; static bool horz = false; ImGui::CheckboxFlags("Stacked", (unsigned int*)&flags, ImPlotBarGroupsFlags_Stacked); ImGui::SameLine(); ImGui::Checkbox("Horizontal",&horz); ImGui::SliderInt("Items",&items,1,3); ImGui::SliderFloat("Size",&size,0,1); if (ImPlot::BeginPlot("Bar Group")) { ImPlot::SetupLegend(ImPlotLocation_East, ImPlotLegendFlags_Outside); if (horz) { ImPlot::SetupAxes("Score","Student",ImPlotAxisFlags_AutoFit,ImPlotAxisFlags_AutoFit); ImPlot::SetupAxisTicks(ImAxis_Y1,positions, groups, glabels); ImPlot::PlotBarGroups(ilabels,data,items,groups,size,0,flags|ImPlotBarGroupsFlags_Horizontal); } else { ImPlot::SetupAxes("Student","Score",ImPlotAxisFlags_AutoFit,ImPlotAxisFlags_AutoFit); ImPlot::SetupAxisTicks(ImAxis_X1,positions, groups, glabels); ImPlot::PlotBarGroups(ilabels,data,items,groups,size,0,flags); } ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_BarStacks() { static ImPlotColormap Liars = -1; if (Liars == -1) { static const ImU32 Liars_Data[6] = { 4282515870, 4282609140, 4287357182, 4294630301, 4294945280, 4294921472 }; Liars = ImPlot::AddColormap("Liars", Liars_Data, 6); } static bool diverging = true; ImGui::Checkbox("Diverging",&diverging); static const char* politicians[] = {"Trump","Bachman","Cruz","Gingrich","Palin","Santorum","Walker","Perry","Ryan","McCain","Rubio","Romney","Rand Paul","Christie","Biden","Kasich","Sanders","J Bush","H Clinton","Obama"}; static int data_reg[] = {18,26,7,14,10,8,6,11,4,4,3,8,6,8,6,5,0,3,1,2, // Pants on Fire 43,36,30,21,30,27,25,17,11,22,15,16,16,17,12,12,14,6,13,12, // False 16,13,28,22,15,21,15,18,30,17,24,18,13,10,14,15,17,22,14,12, // Mostly False 17,10,13,25,12,22,19,26,23,17,22,27,20,26,29,17,18,22,21,27, // Half True 5,7,16,10,10,12,23,13,17,20,22,16,23,19,20,26,36,29,27,26, // Mostly True 1,8,6,8,23,10,12,15,15,20,14,15,22,20,19,25,15,18,24,21}; // True static const char* labels_reg[] = {"Pants on Fire","False","Mostly False","Half True","Mostly True","True"}; static int data_div[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // Pants on Fire (dummy, to order legend logically) 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // False (dummy, to order legend logically) 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // Mostly False (dummy, to order legend logically) -16,-13,-28,-22,-15,-21,-15,-18,-30,-17,-24,-18,-13,-10,-14,-15,-17,-22,-14,-12, // Mostly False -43,-36,-30,-21,-30,-27,-25,-17,-11,-22,-15,-16,-16,-17,-12,-12,-14,-6,-13,-12, // False -18,-26,-7,-14,-10,-8,-6,-11,-4,-4,-3,-8,-6,-8,-6,-5,0,-3,-1,-2, // Pants on Fire 17,10,13,25,12,22,19,26,23,17,22,27,20,26,29,17,18,22,21,27, // Half True 5,7,16,10,10,12,23,13,17,20,22,16,23,19,20,26,36,29,27,26, // Mostly True 1,8,6,8,23,10,12,15,15,20,14,15,22,20,19,25,15,18,24,21}; // True static const char* labels_div[] = {"Pants on Fire","False","Mostly False","Mostly False","False","Pants on Fire","Half True","Mostly True","True"}; ImPlot::PushColormap(Liars); if (ImPlot::BeginPlot("PolitiFact: Who Lies More?",ImVec2(-1,400),ImPlotFlags_NoMouseText)) { ImPlot::SetupLegend(ImPlotLocation_South, ImPlotLegendFlags_Outside|ImPlotLegendFlags_Horizontal); ImPlot::SetupAxes(nullptr,nullptr,ImPlotAxisFlags_AutoFit|ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_AutoFit|ImPlotAxisFlags_Invert); ImPlot::SetupAxisTicks(ImAxis_Y1,0,19,20,politicians,false); if (diverging) ImPlot::PlotBarGroups(labels_div,data_div,9,20,0.75,0,ImPlotBarGroupsFlags_Stacked|ImPlotBarGroupsFlags_Horizontal); else ImPlot::PlotBarGroups(labels_reg,data_reg,6,20,0.75,0,ImPlotBarGroupsFlags_Stacked|ImPlotBarGroupsFlags_Horizontal); ImPlot::EndPlot(); } ImPlot::PopColormap(); } //----------------------------------------------------------------------------- void Demo_ErrorBars() { static float xs[5] = {1,2,3,4,5}; static float bar[5] = {1,2,5,3,4}; static float lin1[5] = {8,8,9,7,8}; static float lin2[5] = {6,7,6,9,6}; static float err1[5] = {0.2f, 0.4f, 0.2f, 0.6f, 0.4f}; static float err2[5] = {0.4f, 0.2f, 0.4f, 0.8f, 0.6f}; static float err3[5] = {0.09f, 0.14f, 0.09f, 0.12f, 0.16f}; static float err4[5] = {0.02f, 0.08f, 0.15f, 0.05f, 0.2f}; if (ImPlot::BeginPlot("##ErrorBars")) { ImPlot::SetupAxesLimits(0, 6, 0, 10); ImPlot::PlotBars("Bar", xs, bar, 5, 0.5f); ImPlot::PlotErrorBars("Bar", xs, bar, err1, 5); ImPlot::SetNextErrorBarStyle(ImPlot::GetColormapColor(1), 0); ImPlot::PlotErrorBars("Line", xs, lin1, err1, err2, 5); ImPlot::SetNextMarkerStyle(ImPlotMarker_Square); ImPlot::PlotLine("Line", xs, lin1, 5); ImPlot::PushStyleColor(ImPlotCol_ErrorBar, ImPlot::GetColormapColor(2)); ImPlot::PlotErrorBars("Scatter", xs, lin2, err2, 5); ImPlot::PlotErrorBars("Scatter", xs, lin2, err3, err4, 5, ImPlotErrorBarsFlags_Horizontal); ImPlot::PopStyleColor(); ImPlot::PlotScatter("Scatter", xs, lin2, 5); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_StemPlots() { static double xs[51], ys1[51], ys2[51]; for (int i = 0; i < 51; ++i) { xs[i] = i * 0.02; ys1[i] = 1.0 + 0.5 * sin(25*xs[i])*cos(2*xs[i]); ys2[i] = 0.5 + 0.25 * sin(10*xs[i]) * sin(xs[i]); } if (ImPlot::BeginPlot("Stem Plots")) { ImPlot::SetupAxisLimits(ImAxis_X1,0,1.0); ImPlot::SetupAxisLimits(ImAxis_Y1,0,1.6); ImPlot::PlotStems("Stems 1",xs,ys1,51); ImPlot::SetNextMarkerStyle(ImPlotMarker_Circle); ImPlot::PlotStems("Stems 2", xs, ys2,51); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_InfiniteLines() { static double vals[] = {0.25, 0.5, 0.75}; if (ImPlot::BeginPlot("##Infinite")) { ImPlot::SetupAxes(nullptr,nullptr,ImPlotAxisFlags_NoInitialFit,ImPlotAxisFlags_NoInitialFit); ImPlot::PlotInfLines("Vertical",vals,3); ImPlot::PlotInfLines("Horizontal",vals,3,ImPlotInfLinesFlags_Horizontal); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_PieCharts() { static const char* labels1[] = {"Frogs","Hogs","Dogs","Logs"}; static float data1[] = {0.15f, 0.30f, 0.2f, 0.05f}; static ImPlotPieChartFlags flags = 0; ImGui::SetNextItemWidth(250); ImGui::DragFloat4("Values", data1, 0.01f, 0, 1); if ((data1[0] + data1[1] + data1[2] + data1[3]) < 1) { ImGui::SameLine(); CHECKBOX_FLAG(flags,ImPlotPieChartFlags_Normalize); } if (ImPlot::BeginPlot("##Pie1", ImVec2(250,250), ImPlotFlags_Equal | ImPlotFlags_NoMouseText)) { ImPlot::SetupAxes(nullptr, nullptr, ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations); ImPlot::SetupAxesLimits(0, 1, 0, 1); ImPlot::PlotPieChart(labels1, data1, 4, 0.5, 0.5, 0.4, "%.2f", 90, flags); ImPlot::EndPlot(); } ImGui::SameLine(); static const char* labels2[] = {"A","B","C","D","E"}; static int data2[] = {1,1,2,3,5}; ImPlot::PushColormap(ImPlotColormap_Pastel); if (ImPlot::BeginPlot("##Pie2", ImVec2(250,250), ImPlotFlags_Equal | ImPlotFlags_NoMouseText)) { ImPlot::SetupAxes(nullptr, nullptr, ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations); ImPlot::SetupAxesLimits(0, 1, 0, 1); ImPlot::PlotPieChart(labels2, data2, 5, 0.5, 0.5, 0.4, "%.0f", 180, flags); ImPlot::EndPlot(); } ImPlot::PopColormap(); } //----------------------------------------------------------------------------- void Demo_Heatmaps() { static float values1[7][7] = {{0.8f, 2.4f, 2.5f, 3.9f, 0.0f, 4.0f, 0.0f}, {2.4f, 0.0f, 4.0f, 1.0f, 2.7f, 0.0f, 0.0f}, {1.1f, 2.4f, 0.8f, 4.3f, 1.9f, 4.4f, 0.0f}, {0.6f, 0.0f, 0.3f, 0.0f, 3.1f, 0.0f, 0.0f}, {0.7f, 1.7f, 0.6f, 2.6f, 2.2f, 6.2f, 0.0f}, {1.3f, 1.2f, 0.0f, 0.0f, 0.0f, 3.2f, 5.1f}, {0.1f, 2.0f, 0.0f, 1.4f, 0.0f, 1.9f, 6.3f}}; static float scale_min = 0; static float scale_max = 6.3f; static const char* xlabels[] = {"C1","C2","C3","C4","C5","C6","C7"}; static const char* ylabels[] = {"R1","R2","R3","R4","R5","R6","R7"}; static ImPlotColormap map = ImPlotColormap_Viridis; if (ImPlot::ColormapButton(ImPlot::GetColormapName(map),ImVec2(225,0),map)) { map = (map + 1) % ImPlot::GetColormapCount(); // We bust the color cache of our plots so that item colors will // resample the new colormap in the event that they have already // been created. See documentation in implot.h. BustColorCache("##Heatmap1"); BustColorCache("##Heatmap2"); } ImGui::SameLine(); ImGui::LabelText("##Colormap Index", "%s", "Change Colormap"); ImGui::SetNextItemWidth(225); ImGui::DragFloatRange2("Min / Max",&scale_min, &scale_max, 0.01f, -20, 20); static ImPlotHeatmapFlags hm_flags = 0; ImGui::CheckboxFlags("Column Major", (unsigned int*)&hm_flags, ImPlotHeatmapFlags_ColMajor); static ImPlotAxisFlags axes_flags = ImPlotAxisFlags_Lock | ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_NoTickMarks; ImPlot::PushColormap(map); if (ImPlot::BeginPlot("##Heatmap1",ImVec2(225,225),ImPlotFlags_NoLegend|ImPlotFlags_NoMouseText)) { ImPlot::SetupAxes(nullptr, nullptr, axes_flags, axes_flags); ImPlot::SetupAxisTicks(ImAxis_X1,0 + 1.0/14.0, 1 - 1.0/14.0, 7, xlabels); ImPlot::SetupAxisTicks(ImAxis_Y1,1 - 1.0/14.0, 0 + 1.0/14.0, 7, ylabels); ImPlot::PlotHeatmap("heat",values1[0],7,7,scale_min,scale_max,"%g",ImPlotPoint(0,0),ImPlotPoint(1,1),hm_flags); ImPlot::EndPlot(); } ImGui::SameLine(); ImPlot::ColormapScale("##HeatScale",scale_min, scale_max, ImVec2(60,225)); ImGui::SameLine(); const int size = 80; static double values2[size*size]; srand((unsigned int)(ImGui::GetTime()*1000000)); for (int i = 0; i < size*size; ++i) values2[i] = RandomRange(0.0,1.0); if (ImPlot::BeginPlot("##Heatmap2",ImVec2(225,225))) { ImPlot::SetupAxes(nullptr, nullptr, ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations); ImPlot::SetupAxesLimits(-1,1,-1,1); ImPlot::PlotHeatmap("heat1",values2,size,size,0,1,nullptr); ImPlot::PlotHeatmap("heat2",values2,size,size,0,1,nullptr, ImPlotPoint(-1,-1), ImPlotPoint(0,0)); ImPlot::EndPlot(); } ImPlot::PopColormap(); } //----------------------------------------------------------------------------- void Demo_Histogram() { static ImPlotHistogramFlags hist_flags = ImPlotHistogramFlags_Density; static int bins = 50; static double mu = 5; static double sigma = 2; ImGui::SetNextItemWidth(200); if (ImGui::RadioButton("Sqrt",bins==ImPlotBin_Sqrt)) { bins = ImPlotBin_Sqrt; } ImGui::SameLine(); if (ImGui::RadioButton("Sturges",bins==ImPlotBin_Sturges)) { bins = ImPlotBin_Sturges; } ImGui::SameLine(); if (ImGui::RadioButton("Rice",bins==ImPlotBin_Rice)) { bins = ImPlotBin_Rice; } ImGui::SameLine(); if (ImGui::RadioButton("Scott",bins==ImPlotBin_Scott)) { bins = ImPlotBin_Scott; } ImGui::SameLine(); if (ImGui::RadioButton("N Bins",bins>=0)) { bins = 50; } if (bins>=0) { ImGui::SameLine(); ImGui::SetNextItemWidth(200); ImGui::SliderInt("##Bins", &bins, 1, 100); } ImGui::CheckboxFlags("Horizontal", (unsigned int*)&hist_flags, ImPlotHistogramFlags_Horizontal); ImGui::SameLine(); ImGui::CheckboxFlags("Density", (unsigned int*)&hist_flags, ImPlotHistogramFlags_Density); ImGui::SameLine(); ImGui::CheckboxFlags("Cumulative", (unsigned int*)&hist_flags, ImPlotHistogramFlags_Cumulative); static bool range = false; ImGui::Checkbox("Range", &range); static float rmin = -3; static float rmax = 13; if (range) { ImGui::SameLine(); ImGui::SetNextItemWidth(200); ImGui::DragFloat2("##Range",&rmin,0.1f,-3,13); ImGui::SameLine(); ImGui::CheckboxFlags("Exclude Outliers", (unsigned int*)&hist_flags, ImPlotHistogramFlags_NoOutliers); } static NormalDistribution<10000> dist(mu, sigma); static double x[100]; static double y[100]; if (hist_flags & ImPlotHistogramFlags_Density) { for (int i = 0; i < 100; ++i) { x[i] = -3 + 16 * (double)i/99.0; y[i] = exp( - (x[i]-mu)*(x[i]-mu) / (2*sigma*sigma)) / (sigma * sqrt(2*3.141592653589793238)); } if (hist_flags & ImPlotHistogramFlags_Cumulative) { for (int i = 1; i < 100; ++i) y[i] += y[i-1]; for (int i = 0; i < 100; ++i) y[i] /= y[99]; } } if (ImPlot::BeginPlot("##Histograms")) { ImPlot::SetupAxes(nullptr,nullptr,ImPlotAxisFlags_AutoFit,ImPlotAxisFlags_AutoFit); ImPlot::SetNextFillStyle(IMPLOT_AUTO_COL,0.5f); ImPlot::PlotHistogram("Empirical", dist.Data, 10000, bins, 1.0, range ? ImPlotRange(rmin,rmax) : ImPlotRange(), hist_flags); if ((hist_flags & ImPlotHistogramFlags_Density) && !(hist_flags & ImPlotHistogramFlags_NoOutliers)) { if (hist_flags & ImPlotHistogramFlags_Horizontal) ImPlot::PlotLine("Theoretical",y,x,100); else ImPlot::PlotLine("Theoretical",x,y,100); } ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_Histogram2D() { static int count = 50000; static int xybins[2] = {100,100}; static ImPlotHistogramFlags hist_flags = 0; ImGui::SliderInt("Count",&count,100,100000); ImGui::SliderInt2("Bins",xybins,1,500); ImGui::SameLine(); ImGui::CheckboxFlags("Density", (unsigned int*)&hist_flags, ImPlotHistogramFlags_Density); static NormalDistribution<100000> dist1(1, 2); static NormalDistribution<100000> dist2(1, 1); double max_count = 0; ImPlotAxisFlags flags = ImPlotAxisFlags_AutoFit|ImPlotAxisFlags_Foreground; ImPlot::PushColormap("Hot"); if (ImPlot::BeginPlot("##Hist2D",ImVec2(ImGui::GetContentRegionAvail().x-100-ImGui::GetStyle().ItemSpacing.x,0))) { ImPlot::SetupAxes(nullptr, nullptr, flags, flags); ImPlot::SetupAxesLimits(-6,6,-6,6); max_count = ImPlot::PlotHistogram2D("Hist2D",dist1.Data,dist2.Data,count,xybins[0],xybins[1],ImPlotRect(-6,6,-6,6), hist_flags); ImPlot::EndPlot(); } ImGui::SameLine(); ImPlot::ColormapScale(hist_flags & ImPlotHistogramFlags_Density ? "Density" : "Count",0,max_count,ImVec2(100,0)); ImPlot::PopColormap(); } //----------------------------------------------------------------------------- void Demo_DigitalPlots() { ImGui::BulletText("Digital plots do not respond to Y drag and zoom, so that"); ImGui::Indent(); ImGui::Text("you can drag analog plots over the rising/falling digital edge."); ImGui::Unindent(); static bool paused = false; static ScrollingBuffer dataDigital[2]; static ScrollingBuffer dataAnalog[2]; static bool showDigital[2] = {true, false}; static bool showAnalog[2] = {true, false}; char label[32]; ImGui::Checkbox("digital_0", &showDigital[0]); ImGui::SameLine(); ImGui::Checkbox("digital_1", &showDigital[1]); ImGui::SameLine(); ImGui::Checkbox("analog_0", &showAnalog[0]); ImGui::SameLine(); ImGui::Checkbox("analog_1", &showAnalog[1]); static float t = 0; if (!paused) { t += ImGui::GetIO().DeltaTime; //digital signal values if (showDigital[0]) dataDigital[0].AddPoint(t, sinf(2*t) > 0.45); if (showDigital[1]) dataDigital[1].AddPoint(t, sinf(2*t) < 0.45); //Analog signal values if (showAnalog[0]) dataAnalog[0].AddPoint(t, sinf(2*t)); if (showAnalog[1]) dataAnalog[1].AddPoint(t, cosf(2*t)); } if (ImPlot::BeginPlot("##Digital")) { ImPlot::SetupAxisLimits(ImAxis_X1, t - 10.0, t, paused ? ImGuiCond_Once : ImGuiCond_Always); ImPlot::SetupAxisLimits(ImAxis_Y1, -1, 1); for (int i = 0; i < 2; ++i) { if (showDigital[i] && dataDigital[i].Data.size() > 0) { snprintf(label, sizeof(label), "digital_%d", i); ImPlot::PlotDigital(label, &dataDigital[i].Data[0].x, &dataDigital[i].Data[0].y, dataDigital[i].Data.size(), 0, dataDigital[i].Offset, 2 * sizeof(float)); } } for (int i = 0; i < 2; ++i) { if (showAnalog[i]) { snprintf(label, sizeof(label), "analog_%d", i); if (dataAnalog[i].Data.size() > 0) ImPlot::PlotLine(label, &dataAnalog[i].Data[0].x, &dataAnalog[i].Data[0].y, dataAnalog[i].Data.size(), 0, dataAnalog[i].Offset, 2 * sizeof(float)); } } ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_Images() { ImGui::BulletText("Below we are displaying the font texture, which is the only texture we have\naccess to in this demo."); ImGui::BulletText("Use the 'ImTextureID' type as storage to pass pointers or identifiers to your\nown texture data."); ImGui::BulletText("See ImGui Wiki page 'Image Loading and Displaying Examples'."); static ImVec2 bmin(0,0); static ImVec2 bmax(1,1); static ImVec2 uv0(0,0); static ImVec2 uv1(1,1); static ImVec4 tint(1,1,1,1); ImGui::SliderFloat2("Min", &bmin.x, -2, 2, "%.1f"); ImGui::SliderFloat2("Max", &bmax.x, -2, 2, "%.1f"); ImGui::SliderFloat2("UV0", &uv0.x, -2, 2, "%.1f"); ImGui::SliderFloat2("UV1", &uv1.x, -2, 2, "%.1f"); ImGui::ColorEdit4("Tint",&tint.x); if (ImPlot::BeginPlot("##image")) { ImPlot::PlotImage("my image",ImGui::GetIO().Fonts->TexID, bmin, bmax, uv0, uv1, tint); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_RealtimePlots() { ImGui::BulletText("Move your mouse to change the data!"); ImGui::BulletText("This example assumes 60 FPS. Higher FPS requires larger buffer size."); static ScrollingBuffer sdata1, sdata2; static RollingBuffer rdata1, rdata2; ImVec2 mouse = ImGui::GetMousePos(); static float t = 0; t += ImGui::GetIO().DeltaTime; sdata1.AddPoint(t, mouse.x * 0.0005f); rdata1.AddPoint(t, mouse.x * 0.0005f); sdata2.AddPoint(t, mouse.y * 0.0005f); rdata2.AddPoint(t, mouse.y * 0.0005f); static float history = 10.0f; ImGui::SliderFloat("History",&history,1,30,"%.1f s"); rdata1.Span = history; rdata2.Span = history; static ImPlotAxisFlags flags = ImPlotAxisFlags_NoTickLabels; if (ImPlot::BeginPlot("##Scrolling", ImVec2(-1,150))) { ImPlot::SetupAxes(nullptr, nullptr, flags, flags); ImPlot::SetupAxisLimits(ImAxis_X1,t - history, t, ImGuiCond_Always); ImPlot::SetupAxisLimits(ImAxis_Y1,0,1); ImPlot::SetNextFillStyle(IMPLOT_AUTO_COL,0.5f); ImPlot::PlotShaded("Mouse X", &sdata1.Data[0].x, &sdata1.Data[0].y, sdata1.Data.size(), -INFINITY, 0, sdata1.Offset, 2 * sizeof(float)); ImPlot::PlotLine("Mouse Y", &sdata2.Data[0].x, &sdata2.Data[0].y, sdata2.Data.size(), 0, sdata2.Offset, 2*sizeof(float)); ImPlot::EndPlot(); } if (ImPlot::BeginPlot("##Rolling", ImVec2(-1,150))) { ImPlot::SetupAxes(nullptr, nullptr, flags, flags); ImPlot::SetupAxisLimits(ImAxis_X1,0,history, ImGuiCond_Always); ImPlot::SetupAxisLimits(ImAxis_Y1,0,1); ImPlot::PlotLine("Mouse X", &rdata1.Data[0].x, &rdata1.Data[0].y, rdata1.Data.size(), 0, 0, 2 * sizeof(float)); ImPlot::PlotLine("Mouse Y", &rdata2.Data[0].x, &rdata2.Data[0].y, rdata2.Data.size(), 0, 0, 2 * sizeof(float)); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_MarkersAndText() { static float mk_size = ImPlot::GetStyle().MarkerSize; static float mk_weight = ImPlot::GetStyle().MarkerWeight; ImGui::DragFloat("Marker Size",&mk_size,0.1f,2.0f,10.0f,"%.2f px"); ImGui::DragFloat("Marker Weight", &mk_weight,0.05f,0.5f,3.0f,"%.2f px"); if (ImPlot::BeginPlot("##MarkerStyles", ImVec2(-1,0), ImPlotFlags_CanvasOnly)) { ImPlot::SetupAxes(nullptr, nullptr, ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations); ImPlot::SetupAxesLimits(0, 10, 0, 12); ImS8 xs[2] = {1,4}; ImS8 ys[2] = {10,11}; // filled markers for (int m = 0; m < ImPlotMarker_COUNT; ++m) { ImGui::PushID(m); ImPlot::SetNextMarkerStyle(m, mk_size, IMPLOT_AUTO_COL, mk_weight); ImPlot::PlotLine("##Filled", xs, ys, 2); ImGui::PopID(); ys[0]--; ys[1]--; } xs[0] = 6; xs[1] = 9; ys[0] = 10; ys[1] = 11; // open markers for (int m = 0; m < ImPlotMarker_COUNT; ++m) { ImGui::PushID(m); ImPlot::SetNextMarkerStyle(m, mk_size, ImVec4(0,0,0,0), mk_weight); ImPlot::PlotLine("##Open", xs, ys, 2); ImGui::PopID(); ys[0]--; ys[1]--; } ImPlot::PlotText("Filled Markers", 2.5f, 6.0f); ImPlot::PlotText("Open Markers", 7.5f, 6.0f); ImPlot::PushStyleColor(ImPlotCol_InlayText, ImVec4(1,0,1,1)); ImPlot::PlotText("Vertical Text", 5.0f, 6.0f, ImVec2(0,0), ImPlotTextFlags_Vertical); ImPlot::PopStyleColor(); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_NaNValues() { static bool include_nan = true; static ImPlotLineFlags flags = 0; float data1[5] = {0.0f,0.25f,0.5f,0.75f,1.0f}; float data2[5] = {0.0f,0.25f,0.5f,0.75f,1.0f}; if (include_nan) data1[2] = NAN; ImGui::Checkbox("Include NaN",&include_nan); ImGui::SameLine(); ImGui::CheckboxFlags("Skip NaN", (unsigned int*)&flags, ImPlotLineFlags_SkipNaN); if (ImPlot::BeginPlot("##NaNValues")) { ImPlot::SetNextMarkerStyle(ImPlotMarker_Square); ImPlot::PlotLine("line", data1, data2, 5, flags); ImPlot::PlotBars("bars", data1, 5); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_LogScale() { static double xs[1001], ys1[1001], ys2[1001], ys3[1001]; for (int i = 0; i < 1001; ++i) { xs[i] = i*0.1f; ys1[i] = sin(xs[i]) + 1; ys2[i] = log(xs[i]); ys3[i] = pow(10.0, xs[i]); } if (ImPlot::BeginPlot("Log Plot", ImVec2(-1,0))) { ImPlot::SetupAxisScale(ImAxis_X1, ImPlotScale_Log10); ImPlot::SetupAxesLimits(0.1, 100, 0, 10); ImPlot::PlotLine("f(x) = x", xs, xs, 1001); ImPlot::PlotLine("f(x) = sin(x)+1", xs, ys1, 1001); ImPlot::PlotLine("f(x) = log(x)", xs, ys2, 1001); ImPlot::PlotLine("f(x) = 10^x", xs, ys3, 21); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_SymmetricLogScale() { static double xs[1001], ys1[1001], ys2[1001]; for (int i = 0; i < 1001; ++i) { xs[i] = i*0.1f-50; ys1[i] = sin(xs[i]); ys2[i] = i*0.002 - 1; } if (ImPlot::BeginPlot("SymLog Plot", ImVec2(-1,0))) { ImPlot::SetupAxisScale(ImAxis_X1, ImPlotScale_SymLog); ImPlot::PlotLine("f(x) = a*x+b",xs,ys2,1001); ImPlot::PlotLine("f(x) = sin(x)",xs,ys1,1001); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_TimeScale() { static double t_min = 1609459200; // 01/01/2021 @ 12:00:00am (UTC) static double t_max = 1640995200; // 01/01/2022 @ 12:00:00am (UTC) ImGui::BulletText("When ImPlotAxisFlags_Time is enabled on the X-Axis, values are interpreted as\n" "UNIX timestamps in seconds and axis labels are formated as date/time."); ImGui::BulletText("By default, labels are in UTC time but can be set to use local time instead."); ImGui::Checkbox("Local Time",&ImPlot::GetStyle().UseLocalTime); ImGui::SameLine(); ImGui::Checkbox("ISO 8601",&ImPlot::GetStyle().UseISO8601); ImGui::SameLine(); ImGui::Checkbox("24 Hour Clock",&ImPlot::GetStyle().Use24HourClock); static HugeTimeData* data = nullptr; if (data == nullptr) { ImGui::SameLine(); if (ImGui::Button("Generate Huge Data (~500MB!)")) { static HugeTimeData sdata(t_min); data = &sdata; } } if (ImPlot::BeginPlot("##Time", ImVec2(-1,0))) { ImPlot::SetupAxisScale(ImAxis_X1, ImPlotScale_Time); ImPlot::SetupAxesLimits(t_min,t_max,0,1); if (data != nullptr) { // downsample our data int downsample = (int)ImPlot::GetPlotLimits().X.Size() / 1000 + 1; int start = (int)(ImPlot::GetPlotLimits().X.Min - t_min); start = start < 0 ? 0 : start > HugeTimeData::Size - 1 ? HugeTimeData::Size - 1 : start; int end = (int)(ImPlot::GetPlotLimits().X.Max - t_min) + 1000; end = end < 0 ? 0 : end > HugeTimeData::Size - 1 ? HugeTimeData::Size - 1 : end; int size = (end - start)/downsample; // plot it ImPlot::PlotLine("Time Series", &data->Ts[start], &data->Ys[start], size, 0, 0, sizeof(double)*downsample); } // plot time now double t_now = (double)time(nullptr); double y_now = HugeTimeData::GetY(t_now); ImPlot::PlotScatter("Now",&t_now,&y_now,1); ImPlot::Annotation(t_now,y_now,ImPlot::GetLastItemColor(),ImVec2(10,10),false,"Now"); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- static inline double TransformForward_Sqrt(double v, void*) { return sqrt(v); } static inline double TransformInverse_Sqrt(double v, void*) { return v*v; } void Demo_CustomScale() { static float v[100]; for (int i = 0; i < 100; ++i) { v[i] = i*0.01f; } if (ImPlot::BeginPlot("Sqrt")) { ImPlot::SetupAxis(ImAxis_X1, "Linear"); ImPlot::SetupAxis(ImAxis_Y1, "Sqrt"); ImPlot::SetupAxisScale(ImAxis_Y1, TransformForward_Sqrt, TransformInverse_Sqrt); ImPlot::SetupAxisLimitsConstraints(ImAxis_Y1, 0, INFINITY); ImPlot::PlotLine("##data",v,v,100); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_MultipleAxes() { static float xs[1001], xs2[1001], ys1[1001], ys2[1001], ys3[1001]; for (int i = 0; i < 1001; ++i) { xs[i] = (i*0.1f); xs2[i] = xs[i] + 10.0f; ys1[i] = sinf(xs[i]) * 3 + 1; ys2[i] = cosf(xs[i]) * 0.2f + 0.5f; ys3[i] = sinf(xs[i]+0.5f) * 100 + 200; } static bool x2_axis = true; static bool y2_axis = true; static bool y3_axis = true; ImGui::Checkbox("X-Axis 2", &x2_axis); ImGui::SameLine(); ImGui::Checkbox("Y-Axis 2", &y2_axis); ImGui::SameLine(); ImGui::Checkbox("Y-Axis 3", &y3_axis); ImGui::BulletText("You can drag axes to the opposite side of the plot."); ImGui::BulletText("Hover over legend items to see which axis they are plotted on."); if (ImPlot::BeginPlot("Multi-Axis Plot", ImVec2(-1,0))) { ImPlot::SetupAxes("X-Axis 1", "Y-Axis 1"); ImPlot::SetupAxesLimits(0, 100, 0, 10); if (x2_axis) { ImPlot::SetupAxis(ImAxis_X2, "X-Axis 2",ImPlotAxisFlags_AuxDefault); ImPlot::SetupAxisLimits(ImAxis_X2, 0, 100); } if (y2_axis) { ImPlot::SetupAxis(ImAxis_Y2, "Y-Axis 2",ImPlotAxisFlags_AuxDefault); ImPlot::SetupAxisLimits(ImAxis_Y2, 0, 1); } if (y3_axis) { ImPlot::SetupAxis(ImAxis_Y3, "Y-Axis 3",ImPlotAxisFlags_AuxDefault); ImPlot::SetupAxisLimits(ImAxis_Y3, 0, 300); } ImPlot::PlotLine("f(x) = x", xs, xs, 1001); if (x2_axis) { ImPlot::SetAxes(ImAxis_X2, ImAxis_Y1); ImPlot::PlotLine("f(x) = sin(x)*3+1", xs2, ys1, 1001); } if (y2_axis) { ImPlot::SetAxes(ImAxis_X1, ImAxis_Y2); ImPlot::PlotLine("f(x) = cos(x)*.2+.5", xs, ys2, 1001); } if (y3_axis) { ImPlot::SetAxes(ImAxis_X2, ImAxis_Y3); ImPlot::PlotLine("f(x) = sin(x+.5)*100+200 ", xs2, ys3, 1001); } ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_LinkedAxes() { static ImPlotRect lims(0,1,0,1); static bool linkx = true, linky = true; int data[2] = {0,1}; ImGui::Checkbox("Link X", &linkx); ImGui::SameLine(); ImGui::Checkbox("Link Y", &linky); ImGui::DragScalarN("Limits",ImGuiDataType_Double,&lims.X.Min,4,0.01f); if (BeginAlignedPlots("AlignedGroup")) { if (ImPlot::BeginPlot("Plot A")) { ImPlot::SetupAxisLinks(ImAxis_X1, linkx ? &lims.X.Min : nullptr, linkx ? &lims.X.Max : nullptr); ImPlot::SetupAxisLinks(ImAxis_Y1, linky ? &lims.Y.Min : nullptr, linky ? &lims.Y.Max : nullptr); ImPlot::PlotLine("Line",data,2); ImPlot::EndPlot(); } if (ImPlot::BeginPlot("Plot B")) { ImPlot::SetupAxisLinks(ImAxis_X1, linkx ? &lims.X.Min : nullptr, linkx ? &lims.X.Max : nullptr); ImPlot::SetupAxisLinks(ImAxis_Y1, linky ? &lims.Y.Min : nullptr, linky ? &lims.Y.Max : nullptr); ImPlot::PlotLine("Line",data,2); ImPlot::EndPlot(); } ImPlot::EndAlignedPlots(); } } //----------------------------------------------------------------------------- void Demo_AxisConstraints() { static float constraints[4] = {-10,10,1,20}; static ImPlotAxisFlags flags; ImGui::DragFloat2("Limits Constraints", &constraints[0], 0.01f); ImGui::DragFloat2("Zoom Constraints", &constraints[2], 0.01f); CHECKBOX_FLAG(flags, ImPlotAxisFlags_PanStretch); if (ImPlot::BeginPlot("##AxisConstraints",ImVec2(-1,0))) { ImPlot::SetupAxes("X","Y",flags,flags); ImPlot::SetupAxesLimits(-1,1,-1,1); ImPlot::SetupAxisLimitsConstraints(ImAxis_X1,constraints[0], constraints[1]); ImPlot::SetupAxisZoomConstraints(ImAxis_X1,constraints[2], constraints[3]); ImPlot::SetupAxisLimitsConstraints(ImAxis_Y1,constraints[0], constraints[1]); ImPlot::SetupAxisZoomConstraints(ImAxis_Y1,constraints[2], constraints[3]); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_EqualAxes() { ImGui::BulletText("Equal constraint applies to axis pairs (e.g ImAxis_X1/Y1, ImAxis_X2/Y2)"); static double xs1[360], ys1[360]; for (int i = 0; i < 360; ++i) { double angle = i * 2 * PI / 359.0; xs1[i] = cos(angle); ys1[i] = sin(angle); } float xs2[] = {-1,0,1,0,-1}; float ys2[] = {0,1,0,-1,0}; if (ImPlot::BeginPlot("##EqualAxes",ImVec2(-1,0),ImPlotFlags_Equal)) { ImPlot::SetupAxis(ImAxis_X2, nullptr, ImPlotAxisFlags_AuxDefault); ImPlot::SetupAxis(ImAxis_Y2, nullptr, ImPlotAxisFlags_AuxDefault); ImPlot::PlotLine("Circle",xs1,ys1,360); ImPlot::SetAxes(ImAxis_X2, ImAxis_Y2); ImPlot::PlotLine("Diamond",xs2,ys2,5); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_AutoFittingData() { ImGui::BulletText("The Y-axis has been configured to auto-fit to only the data visible in X-axis range."); ImGui::BulletText("Zoom and pan the X-axis. Disable Stems to see a difference in fit."); ImGui::BulletText("If ImPlotAxisFlags_RangeFit is disabled, the axis will fit ALL data."); static ImPlotAxisFlags xflags = ImPlotAxisFlags_None; static ImPlotAxisFlags yflags = ImPlotAxisFlags_AutoFit|ImPlotAxisFlags_RangeFit; ImGui::TextUnformatted("X: "); ImGui::SameLine(); ImGui::CheckboxFlags("ImPlotAxisFlags_AutoFit##X", (unsigned int*)&xflags, ImPlotAxisFlags_AutoFit); ImGui::SameLine(); ImGui::CheckboxFlags("ImPlotAxisFlags_RangeFit##X", (unsigned int*)&xflags, ImPlotAxisFlags_RangeFit); ImGui::TextUnformatted("Y: "); ImGui::SameLine(); ImGui::CheckboxFlags("ImPlotAxisFlags_AutoFit##Y", (unsigned int*)&yflags, ImPlotAxisFlags_AutoFit); ImGui::SameLine(); ImGui::CheckboxFlags("ImPlotAxisFlags_RangeFit##Y", (unsigned int*)&yflags, ImPlotAxisFlags_RangeFit); static double data[101]; srand(0); for (int i = 0; i < 101; ++i) data[i] = 1 + sin(i/10.0f); if (ImPlot::BeginPlot("##DataFitting")) { ImPlot::SetupAxes("X","Y",xflags,yflags); ImPlot::PlotLine("Line",data,101); ImPlot::PlotStems("Stems",data,101); ImPlot::EndPlot(); }; } //----------------------------------------------------------------------------- ImPlotPoint SinewaveGetter(int i, void* data) { float f = *(float*)data; return ImPlotPoint(i,sinf(f*i)); } void Demo_SubplotsSizing() { static ImPlotSubplotFlags flags = ImPlotSubplotFlags_None; ImGui::CheckboxFlags("ImPlotSubplotFlags_NoResize", (unsigned int*)&flags, ImPlotSubplotFlags_NoResize); ImGui::CheckboxFlags("ImPlotSubplotFlags_NoTitle", (unsigned int*)&flags, ImPlotSubplotFlags_NoTitle); static int rows = 3; static int cols = 3; ImGui::SliderInt("Rows",&rows,1,5); ImGui::SliderInt("Cols",&cols,1,5); static float rratios[] = {5,1,1,1,1,1}; static float cratios[] = {5,1,1,1,1,1}; ImGui::DragScalarN("Row Ratios",ImGuiDataType_Float,rratios,rows,0.01f,nullptr); ImGui::DragScalarN("Col Ratios",ImGuiDataType_Float,cratios,cols,0.01f,nullptr); if (ImPlot::BeginSubplots("My Subplots", rows, cols, ImVec2(-1,400), flags, rratios, cratios)) { for (int i = 0; i < rows*cols; ++i) { if (ImPlot::BeginPlot("",ImVec2(),ImPlotFlags_NoLegend)) { ImPlot::SetupAxes(nullptr,nullptr,ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_NoDecorations); float fi = 0.01f * (i+1); ImPlot::SetNextLineStyle(SampleColormap((float)i/(float)(rows*cols-1),ImPlotColormap_Jet)); ImPlot::PlotLineG("data",SinewaveGetter,&fi,1000); ImPlot::EndPlot(); } } ImPlot::EndSubplots(); } } //----------------------------------------------------------------------------- void Demo_SubplotItemSharing() { static ImPlotSubplotFlags flags = ImPlotSubplotFlags_ShareItems; ImGui::CheckboxFlags("ImPlotSubplotFlags_ShareItems", (unsigned int*)&flags, ImPlotSubplotFlags_ShareItems); ImGui::CheckboxFlags("ImPlotSubplotFlags_ColMajor", (unsigned int*)&flags, ImPlotSubplotFlags_ColMajor); ImGui::BulletText("Drag and drop items from the legend onto plots (except for 'common')"); static int rows = 2; static int cols = 3; static int id[] = {0,1,2,3,4,5}; static int curj = -1; if (ImPlot::BeginSubplots("##ItemSharing", rows, cols, ImVec2(-1,400), flags)) { for (int i = 0; i < rows*cols; ++i) { if (ImPlot::BeginPlot("")) { float fc = 0.01f; ImPlot::PlotLineG("common",SinewaveGetter,&fc,1000); for (int j = 0; j < 6; ++j) { if (id[j] == i) { char label[8]; float fj = 0.01f * (j+2); snprintf(label, sizeof(label), "data%d", j); ImPlot::PlotLineG(label,SinewaveGetter,&fj,1000); if (ImPlot::BeginDragDropSourceItem(label)) { curj = j; ImGui::SetDragDropPayload("MY_DND",nullptr,0); ImPlot::ItemIcon(GetLastItemColor()); ImGui::SameLine(); ImGui::TextUnformatted(label); ImPlot::EndDragDropSource(); } } } if (ImPlot::BeginDragDropTargetPlot()) { if (ImGui::AcceptDragDropPayload("MY_DND")) id[curj] = i; ImPlot::EndDragDropTarget(); } ImPlot::EndPlot(); } } ImPlot::EndSubplots(); } } //----------------------------------------------------------------------------- void Demo_SubplotAxisLinking() { static ImPlotSubplotFlags flags = ImPlotSubplotFlags_LinkRows | ImPlotSubplotFlags_LinkCols; ImGui::CheckboxFlags("ImPlotSubplotFlags_LinkRows", (unsigned int*)&flags, ImPlotSubplotFlags_LinkRows); ImGui::CheckboxFlags("ImPlotSubplotFlags_LinkCols", (unsigned int*)&flags, ImPlotSubplotFlags_LinkCols); ImGui::CheckboxFlags("ImPlotSubplotFlags_LinkAllX", (unsigned int*)&flags, ImPlotSubplotFlags_LinkAllX); ImGui::CheckboxFlags("ImPlotSubplotFlags_LinkAllY", (unsigned int*)&flags, ImPlotSubplotFlags_LinkAllY); static int rows = 2; static int cols = 2; if (ImPlot::BeginSubplots("##AxisLinking", rows, cols, ImVec2(-1,400), flags)) { for (int i = 0; i < rows*cols; ++i) { if (ImPlot::BeginPlot("")) { ImPlot::SetupAxesLimits(0,1000,-1,1); float fc = 0.01f; ImPlot::PlotLineG("common",SinewaveGetter,&fc,1000); ImPlot::EndPlot(); } } ImPlot::EndSubplots(); } } //----------------------------------------------------------------------------- void Demo_LegendOptions() { static ImPlotLocation loc = ImPlotLocation_East; ImGui::CheckboxFlags("North", (unsigned int*)&loc, ImPlotLocation_North); ImGui::SameLine(); ImGui::CheckboxFlags("South", (unsigned int*)&loc, ImPlotLocation_South); ImGui::SameLine(); ImGui::CheckboxFlags("West", (unsigned int*)&loc, ImPlotLocation_West); ImGui::SameLine(); ImGui::CheckboxFlags("East", (unsigned int*)&loc, ImPlotLocation_East); static ImPlotLegendFlags flags = 0; CHECKBOX_FLAG(flags, ImPlotLegendFlags_Horizontal); CHECKBOX_FLAG(flags, ImPlotLegendFlags_Outside); CHECKBOX_FLAG(flags, ImPlotLegendFlags_Sort); ImGui::SliderFloat2("LegendPadding", (float*)&GetStyle().LegendPadding, 0.0f, 20.0f, "%.0f"); ImGui::SliderFloat2("LegendInnerPadding", (float*)&GetStyle().LegendInnerPadding, 0.0f, 10.0f, "%.0f"); ImGui::SliderFloat2("LegendSpacing", (float*)&GetStyle().LegendSpacing, 0.0f, 5.0f, "%.0f"); if (ImPlot::BeginPlot("##Legend",ImVec2(-1,0))) { ImPlot::SetupLegend(loc, flags); static MyImPlot::WaveData data1(0.001, 0.2, 4, 0.2); static MyImPlot::WaveData data2(0.001, 0.2, 4, 0.4); static MyImPlot::WaveData data3(0.001, 0.2, 4, 0.6); static MyImPlot::WaveData data4(0.001, 0.2, 4, 0.8); static MyImPlot::WaveData data5(0.001, 0.2, 4, 1.0); ImPlot::PlotLineG("Item B", MyImPlot::SawWave, &data1, 1000); // "Item B" added to legend ImPlot::PlotLineG("Item A##IDText", MyImPlot::SawWave, &data2, 1000); // "Item A" added to legend, text after ## used for ID only ImPlot::PlotLineG("##NotListed", MyImPlot::SawWave, &data3, 1000); // plotted, but not added to legend ImPlot::PlotLineG("Item C", MyImPlot::SawWave, &data4, 1000); // "Item C" added to legend ImPlot::PlotLineG("Item C", MyImPlot::SawWave, &data5, 1000); // combined with previous "Item C" ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_DragPoints() { ImGui::BulletText("Click and drag each point."); static ImPlotDragToolFlags flags = ImPlotDragToolFlags_None; ImGui::CheckboxFlags("NoCursors", (unsigned int*)&flags, ImPlotDragToolFlags_NoCursors); ImGui::SameLine(); ImGui::CheckboxFlags("NoFit", (unsigned int*)&flags, ImPlotDragToolFlags_NoFit); ImGui::SameLine(); ImGui::CheckboxFlags("NoInput", (unsigned int*)&flags, ImPlotDragToolFlags_NoInputs); ImPlotAxisFlags ax_flags = ImPlotAxisFlags_NoTickLabels | ImPlotAxisFlags_NoTickMarks; if (ImPlot::BeginPlot("##Bezier",ImVec2(-1,0),ImPlotFlags_CanvasOnly)) { ImPlot::SetupAxes(nullptr,nullptr,ax_flags,ax_flags); ImPlot::SetupAxesLimits(0,1,0,1); static ImPlotPoint P[] = {ImPlotPoint(.05f,.05f), ImPlotPoint(0.2,0.4), ImPlotPoint(0.8,0.6), ImPlotPoint(.95f,.95f)}; ImPlot::DragPoint(0,&P[0].x,&P[0].y, ImVec4(0,0.9f,0,1),4,flags); ImPlot::DragPoint(1,&P[1].x,&P[1].y, ImVec4(1,0.5f,1,1),4,flags); ImPlot::DragPoint(2,&P[2].x,&P[2].y, ImVec4(0,0.5f,1,1),4,flags); ImPlot::DragPoint(3,&P[3].x,&P[3].y, ImVec4(0,0.9f,0,1),4,flags); static ImPlotPoint B[100]; for (int i = 0; i < 100; ++i) { double t = i / 99.0; double u = 1 - t; double w1 = u*u*u; double w2 = 3*u*u*t; double w3 = 3*u*t*t; double w4 = t*t*t; B[i] = ImPlotPoint(w1*P[0].x + w2*P[1].x + w3*P[2].x + w4*P[3].x, w1*P[0].y + w2*P[1].y + w3*P[2].y + w4*P[3].y); } ImPlot::SetNextLineStyle(ImVec4(1,0.5f,1,1)); ImPlot::PlotLine("##h1",&P[0].x, &P[0].y, 2, 0, 0, sizeof(ImPlotPoint)); ImPlot::SetNextLineStyle(ImVec4(0,0.5f,1,1)); ImPlot::PlotLine("##h2",&P[2].x, &P[2].y, 2, 0, 0, sizeof(ImPlotPoint)); ImPlot::SetNextLineStyle(ImVec4(0,0.9f,0,1), 2); ImPlot::PlotLine("##bez",&B[0].x, &B[0].y, 100, 0, 0, sizeof(ImPlotPoint)); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_DragLines() { ImGui::BulletText("Click and drag the horizontal and vertical lines."); static double x1 = 0.2; static double x2 = 0.8; static double y1 = 0.25; static double y2 = 0.75; static double f = 0.1; static ImPlotDragToolFlags flags = ImPlotDragToolFlags_None; ImGui::CheckboxFlags("NoCursors", (unsigned int*)&flags, ImPlotDragToolFlags_NoCursors); ImGui::SameLine(); ImGui::CheckboxFlags("NoFit", (unsigned int*)&flags, ImPlotDragToolFlags_NoFit); ImGui::SameLine(); ImGui::CheckboxFlags("NoInput", (unsigned int*)&flags, ImPlotDragToolFlags_NoInputs); if (ImPlot::BeginPlot("##lines",ImVec2(-1,0))) { ImPlot::SetupAxesLimits(0,1,0,1); ImPlot::DragLineX(0,&x1,ImVec4(1,1,1,1),1,flags); ImPlot::DragLineX(1,&x2,ImVec4(1,1,1,1),1,flags); ImPlot::DragLineY(2,&y1,ImVec4(1,1,1,1),1,flags); ImPlot::DragLineY(3,&y2,ImVec4(1,1,1,1),1,flags); double xs[1000], ys[1000]; for (int i = 0; i < 1000; ++i) { xs[i] = (x2+x1)/2+fabs(x2-x1)*(i/1000.0f - 0.5f); ys[i] = (y1+y2)/2+fabs(y2-y1)/2*sin(f*i/10); } ImPlot::PlotLine("Interactive Data", xs, ys, 1000); ImPlot::DragLineY(120482,&f,ImVec4(1,0.5f,1,1),1,flags); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_DragRects() { static float x_data[512]; static float y_data1[512]; static float y_data2[512]; static float y_data3[512]; static float sampling_freq = 44100; static float freq = 500; for (size_t i = 0; i < 512; ++i) { const float t = i / sampling_freq; x_data[i] = t; const float arg = 2 * 3.14f * freq * t; y_data1[i] = sinf(arg); y_data2[i] = y_data1[i] * -0.6f + sinf(2 * arg) * 0.4f; y_data3[i] = y_data2[i] * -0.6f + sinf(3 * arg) * 0.4f; } ImGui::BulletText("Click and drag the edges, corners, and center of the rect."); static ImPlotRect rect(0.0025,0.0045,0,0.5); static ImPlotDragToolFlags flags = ImPlotDragToolFlags_None; ImGui::CheckboxFlags("NoCursors", (unsigned int*)&flags, ImPlotDragToolFlags_NoCursors); ImGui::SameLine(); ImGui::CheckboxFlags("NoFit", (unsigned int*)&flags, ImPlotDragToolFlags_NoFit); ImGui::SameLine(); ImGui::CheckboxFlags("NoInput", (unsigned int*)&flags, ImPlotDragToolFlags_NoInputs); if (ImPlot::BeginPlot("##Main",ImVec2(-1,150))) { ImPlot::SetupAxes(nullptr,nullptr,ImPlotAxisFlags_NoTickLabels,ImPlotAxisFlags_NoTickLabels); ImPlot::SetupAxesLimits(0,0.01,-1,1); ImPlot::PlotLine("Signal 1", x_data, y_data1, 512); ImPlot::PlotLine("Signal 2", x_data, y_data2, 512); ImPlot::PlotLine("Signal 3", x_data, y_data3, 512); ImPlot::DragRect(0,&rect.X.Min,&rect.Y.Min,&rect.X.Max,&rect.Y.Max,ImVec4(1,0,1,1),flags); ImPlot::EndPlot(); } if (ImPlot::BeginPlot("##rect",ImVec2(-1,150), ImPlotFlags_CanvasOnly)) { ImPlot::SetupAxes(nullptr,nullptr,ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_NoDecorations); ImPlot::SetupAxesLimits(rect.X.Min, rect.X.Max, rect.Y.Min, rect.Y.Max, ImGuiCond_Always); ImPlot::PlotLine("Signal 1", x_data, y_data1, 512); ImPlot::PlotLine("Signal 2", x_data, y_data2, 512); ImPlot::PlotLine("Signal 3", x_data, y_data3, 512); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- ImPlotPoint FindCentroid(const ImVector& data, const ImPlotRect& bounds, int& cnt) { cnt = 0; ImPlotPoint avg; ImPlotRect bounds_fixed; bounds_fixed.X.Min = bounds.X.Min < bounds.X.Max ? bounds.X.Min : bounds.X.Max; bounds_fixed.X.Max = bounds.X.Min < bounds.X.Max ? bounds.X.Max : bounds.X.Min; bounds_fixed.Y.Min = bounds.Y.Min < bounds.Y.Max ? bounds.Y.Min : bounds.Y.Max; bounds_fixed.Y.Max = bounds.Y.Min < bounds.Y.Max ? bounds.Y.Max : bounds.Y.Min; for (int i = 0; i < data.size(); ++i) { if (bounds_fixed.Contains(data[i].x, data[i].y)) { avg.x += data[i].x; avg.y += data[i].y; cnt++; } } if (cnt > 0) { avg.x = avg.x / cnt; avg.y = avg.y / cnt; } return avg; } //----------------------------------------------------------------------------- void Demo_Querying() { static ImVector data; static ImVector rects; static ImPlotRect limits, select; static bool init = true; if (init) { for (int i = 0; i < 50; ++i) { double x = RandomRange(0.1, 0.9); double y = RandomRange(0.1, 0.9); data.push_back(ImPlotPoint(x,y)); } init = false; } ImGui::BulletText("Box select and left click mouse to create a new query rect."); ImGui::BulletText("Ctrl + click in the plot area to draw points."); if (ImGui::Button("Clear Queries")) rects.shrink(0); if (ImPlot::BeginPlot("##Centroid")) { ImPlot::SetupAxesLimits(0,1,0,1); if (ImPlot::IsPlotHovered() && ImGui::IsMouseClicked(0) && ImGui::GetIO().KeyCtrl) { ImPlotPoint pt = ImPlot::GetPlotMousePos(); data.push_back(pt); } ImPlot::PlotScatter("Points", &data[0].x, &data[0].y, data.size(), 0, 0, 2 * sizeof(double)); if (ImPlot::IsPlotSelected()) { select = ImPlot::GetPlotSelection(); int cnt; ImPlotPoint centroid = FindCentroid(data,select,cnt); if (cnt > 0) { ImPlot::SetNextMarkerStyle(ImPlotMarker_Square,6); ImPlot::PlotScatter("Centroid", ¢roid.x, ¢roid.y, 1); } if (ImGui::IsMouseClicked(ImPlot::GetInputMap().SelectCancel)) { CancelPlotSelection(); rects.push_back(select); } } for (int i = 0; i < rects.size(); ++i) { int cnt; ImPlotPoint centroid = FindCentroid(data,rects[i],cnt); if (cnt > 0) { ImPlot::SetNextMarkerStyle(ImPlotMarker_Square,6); ImPlot::PlotScatter("Centroid", ¢roid.x, ¢roid.y, 1); } ImPlot::DragRect(i,&rects[i].X.Min,&rects[i].Y.Min,&rects[i].X.Max,&rects[i].Y.Max,ImVec4(1,0,1,1)); } limits = ImPlot::GetPlotLimits(); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_Annotations() { static bool clamp = false; ImGui::Checkbox("Clamp",&clamp); if (ImPlot::BeginPlot("##Annotations")) { ImPlot::SetupAxesLimits(0,2,0,1); static float p[] = {0.25f, 0.25f, 0.75f, 0.75f, 0.25f}; ImPlot::PlotScatter("##Points",&p[0],&p[1],4); ImVec4 col = GetLastItemColor(); ImPlot::Annotation(0.25,0.25,col,ImVec2(-15,15),clamp,"BL"); ImPlot::Annotation(0.75,0.25,col,ImVec2(15,15),clamp,"BR"); ImPlot::Annotation(0.75,0.75,col,ImVec2(15,-15),clamp,"TR"); ImPlot::Annotation(0.25,0.75,col,ImVec2(-15,-15),clamp,"TL"); ImPlot::Annotation(0.5,0.5,col,ImVec2(0,0),clamp,"Center"); ImPlot::Annotation(1.25,0.75,ImVec4(0,1,0,1),ImVec2(0,0),clamp); float bx[] = {1.2f,1.5f,1.8f}; float by[] = {0.25f, 0.5f, 0.75f}; ImPlot::PlotBars("##Bars",bx,by,3,0.2); for (int i = 0; i < 3; ++i) ImPlot::Annotation(bx[i],by[i],ImVec4(0,0,0,0),ImVec2(0,-5),clamp,"B[%d]=%.2f",i,by[i]); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_Tags() { static bool show = true; ImGui::Checkbox("Show Tags",&show); if (ImPlot::BeginPlot("##Tags")) { ImPlot::SetupAxis(ImAxis_X2); ImPlot::SetupAxis(ImAxis_Y2); if (show) { ImPlot::TagX(0.25, ImVec4(1,1,0,1)); ImPlot::TagY(0.75, ImVec4(1,1,0,1)); static double drag_tag = 0.25; ImPlot::DragLineY(0,&drag_tag,ImVec4(1,0,0,1),1,ImPlotDragToolFlags_NoFit); ImPlot::TagY(drag_tag, ImVec4(1,0,0,1), "Drag"); SetAxes(ImAxis_X2, ImAxis_Y2); ImPlot::TagX(0.5, ImVec4(0,1,1,1), "%s", "MyTag"); ImPlot::TagY(0.5, ImVec4(0,1,1,1), "Tag: %d", 42); } ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_DragAndDrop() { ImGui::BulletText("Drag/drop items from the left column."); ImGui::BulletText("Drag/drop items between plots."); ImGui::Indent(); ImGui::BulletText("Plot 1 Targets: Plot, Y-Axes, Legend"); ImGui::BulletText("Plot 1 Sources: Legend Item Labels"); ImGui::BulletText("Plot 2 Targets: Plot, X-Axis, Y-Axis"); ImGui::BulletText("Plot 2 Sources: Plot, X-Axis, Y-Axis (hold Ctrl)"); ImGui::Unindent(); // convenience struct to manage DND items; do this however you like struct MyDndItem { int Idx; int Plt; ImAxis Yax; char Label[16]; ImVector Data; ImVec4 Color; MyDndItem() { static int i = 0; Idx = i++; Plt = 0; Yax = ImAxis_Y1; snprintf(Label, sizeof(Label), "%02d Hz", Idx+1); Color = RandomColor(); Data.reserve(1001); for (int k = 0; k < 1001; ++k) { float t = k * 1.0f / 999; Data.push_back(ImVec2(t, 0.5f + 0.5f * sinf(2*3.14f*t*(Idx+1)))); } } void Reset() { Plt = 0; Yax = ImAxis_Y1; } }; const int k_dnd = 20; static MyDndItem dnd[k_dnd]; static MyDndItem* dndx = nullptr; // for plot 2 static MyDndItem* dndy = nullptr; // for plot 2 // child window to serve as initial source for our DND items ImGui::BeginChild("DND_LEFT",ImVec2(100,400)); if (ImGui::Button("Reset Data")) { for (int k = 0; k < k_dnd; ++k) dnd[k].Reset(); dndx = dndy = nullptr; } for (int k = 0; k < k_dnd; ++k) { if (dnd[k].Plt > 0) continue; ImPlot::ItemIcon(dnd[k].Color); ImGui::SameLine(); ImGui::Selectable(dnd[k].Label, false, 0, ImVec2(100, 0)); if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) { ImGui::SetDragDropPayload("MY_DND", &k, sizeof(int)); ImPlot::ItemIcon(dnd[k].Color); ImGui::SameLine(); ImGui::TextUnformatted(dnd[k].Label); ImGui::EndDragDropSource(); } } ImGui::EndChild(); if (ImGui::BeginDragDropTarget()) { if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_DND")) { int i = *(int*)payload->Data; dnd[i].Reset(); } ImGui::EndDragDropTarget(); } ImGui::SameLine(); ImGui::BeginChild("DND_RIGHT",ImVec2(-1,400)); // plot 1 (time series) ImPlotAxisFlags flags = ImPlotAxisFlags_NoTickLabels | ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_NoHighlight; if (ImPlot::BeginPlot("##DND1", ImVec2(-1,195))) { ImPlot::SetupAxis(ImAxis_X1, nullptr, flags|ImPlotAxisFlags_Lock); ImPlot::SetupAxis(ImAxis_Y1, "[drop here]", flags); ImPlot::SetupAxis(ImAxis_Y2, "[drop here]", flags|ImPlotAxisFlags_Opposite); ImPlot::SetupAxis(ImAxis_Y3, "[drop here]", flags|ImPlotAxisFlags_Opposite); for (int k = 0; k < k_dnd; ++k) { if (dnd[k].Plt == 1 && dnd[k].Data.size() > 0) { ImPlot::SetAxis(dnd[k].Yax); ImPlot::SetNextLineStyle(dnd[k].Color); ImPlot::PlotLine(dnd[k].Label, &dnd[k].Data[0].x, &dnd[k].Data[0].y, dnd[k].Data.size(), 0, 0, 2 * sizeof(float)); // allow legend item labels to be DND sources if (ImPlot::BeginDragDropSourceItem(dnd[k].Label)) { ImGui::SetDragDropPayload("MY_DND", &k, sizeof(int)); ImPlot::ItemIcon(dnd[k].Color); ImGui::SameLine(); ImGui::TextUnformatted(dnd[k].Label); ImPlot::EndDragDropSource(); } } } // allow the main plot area to be a DND target if (ImPlot::BeginDragDropTargetPlot()) { if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_DND")) { int i = *(int*)payload->Data; dnd[i].Plt = 1; dnd[i].Yax = ImAxis_Y1; } ImPlot::EndDragDropTarget(); } // allow each y-axis to be a DND target for (int y = ImAxis_Y1; y <= ImAxis_Y3; ++y) { if (ImPlot::BeginDragDropTargetAxis(y)) { if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_DND")) { int i = *(int*)payload->Data; dnd[i].Plt = 1; dnd[i].Yax = y; } ImPlot::EndDragDropTarget(); } } // allow the legend to be a DND target if (ImPlot::BeginDragDropTargetLegend()) { if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_DND")) { int i = *(int*)payload->Data; dnd[i].Plt = 1; dnd[i].Yax = ImAxis_Y1; } ImPlot::EndDragDropTarget(); } ImPlot::EndPlot(); } // plot 2 (Lissajous) if (ImPlot::BeginPlot("##DND2", ImVec2(-1,195))) { ImPlot::PushStyleColor(ImPlotCol_AxisBg, dndx != nullptr ? dndx->Color : ImPlot::GetStyle().Colors[ImPlotCol_AxisBg]); ImPlot::SetupAxis(ImAxis_X1, dndx == nullptr ? "[drop here]" : dndx->Label, flags); ImPlot::PushStyleColor(ImPlotCol_AxisBg, dndy != nullptr ? dndy->Color : ImPlot::GetStyle().Colors[ImPlotCol_AxisBg]); ImPlot::SetupAxis(ImAxis_Y1, dndy == nullptr ? "[drop here]" : dndy->Label, flags); ImPlot::PopStyleColor(2); if (dndx != nullptr && dndy != nullptr) { ImVec4 mixed((dndx->Color.x + dndy->Color.x)/2,(dndx->Color.y + dndy->Color.y)/2,(dndx->Color.z + dndy->Color.z)/2,(dndx->Color.w + dndy->Color.w)/2); ImPlot::SetNextLineStyle(mixed); ImPlot::PlotLine("##dndxy", &dndx->Data[0].y, &dndy->Data[0].y, dndx->Data.size(), 0, 0, 2 * sizeof(float)); } // allow the x-axis to be a DND target if (ImPlot::BeginDragDropTargetAxis(ImAxis_X1)) { if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_DND")) { int i = *(int*)payload->Data; dndx = &dnd[i]; } ImPlot::EndDragDropTarget(); } // allow the x-axis to be a DND source if (dndx != nullptr && ImPlot::BeginDragDropSourceAxis(ImAxis_X1)) { ImGui::SetDragDropPayload("MY_DND", &dndx->Idx, sizeof(int)); ImPlot::ItemIcon(dndx->Color); ImGui::SameLine(); ImGui::TextUnformatted(dndx->Label); ImPlot::EndDragDropSource(); } // allow the y-axis to be a DND target if (ImPlot::BeginDragDropTargetAxis(ImAxis_Y1)) { if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_DND")) { int i = *(int*)payload->Data; dndy = &dnd[i]; } ImPlot::EndDragDropTarget(); } // allow the y-axis to be a DND source if (dndy != nullptr && ImPlot::BeginDragDropSourceAxis(ImAxis_Y1)) { ImGui::SetDragDropPayload("MY_DND", &dndy->Idx, sizeof(int)); ImPlot::ItemIcon(dndy->Color); ImGui::SameLine(); ImGui::TextUnformatted(dndy->Label); ImPlot::EndDragDropSource(); } // allow the plot area to be a DND target if (ImPlot::BeginDragDropTargetPlot()) { if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_DND")) { int i = *(int*)payload->Data; dndx = dndy = &dnd[i]; } } // allow the plot area to be a DND source if (ImPlot::BeginDragDropSourcePlot()) { ImGui::TextUnformatted("Yes, you can\ndrag this!"); ImPlot::EndDragDropSource(); } ImPlot::EndPlot(); } ImGui::EndChild(); } //----------------------------------------------------------------------------- void Demo_Tables() { #ifdef IMGUI_HAS_TABLE static ImGuiTableFlags flags = ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable; static bool anim = true; static int offset = 0; ImGui::BulletText("Plots can be used inside of ImGui tables as another means of creating subplots."); ImGui::Checkbox("Animate",&anim); if (anim) offset = (offset + 1) % 100; if (ImGui::BeginTable("##table", 3, flags, ImVec2(-1,0))) { ImGui::TableSetupColumn("Electrode", ImGuiTableColumnFlags_WidthFixed, 75.0f); ImGui::TableSetupColumn("Voltage", ImGuiTableColumnFlags_WidthFixed, 75.0f); ImGui::TableSetupColumn("EMG Signal"); ImGui::TableHeadersRow(); ImPlot::PushColormap(ImPlotColormap_Cool); for (int row = 0; row < 10; row++) { ImGui::TableNextRow(); static float data[100]; srand(row); for (int i = 0; i < 100; ++i) data[i] = RandomRange(0.0f,10.0f); ImGui::TableSetColumnIndex(0); ImGui::Text("EMG %d", row); ImGui::TableSetColumnIndex(1); ImGui::Text("%.3f V", data[offset]); ImGui::TableSetColumnIndex(2); ImGui::PushID(row); MyImPlot::Sparkline("##spark",data,100,0,11.0f,offset,ImPlot::GetColormapColor(row),ImVec2(-1, 35)); ImGui::PopID(); } ImPlot::PopColormap(); ImGui::EndTable(); } #else ImGui::BulletText("You need to merge the ImGui 'tables' branch for this section."); #endif } //----------------------------------------------------------------------------- void Demo_OffsetAndStride() { static const int k_circles = 11; static const int k_points_per = 50; static const int k_size = 2 * k_points_per * k_circles; static double interleaved_data[k_size]; for (int p = 0; p < k_points_per; ++p) { for (int c = 0; c < k_circles; ++c) { double r = (double)c / (k_circles - 1) * 0.2 + 0.2; interleaved_data[p*2*k_circles + 2*c + 0] = 0.5 + r * cos((double)p/k_points_per * 6.28); interleaved_data[p*2*k_circles + 2*c + 1] = 0.5 + r * sin((double)p/k_points_per * 6.28); } } static int offset = 0; ImGui::BulletText("Offsetting is useful for realtime plots (see above) and circular buffers."); ImGui::BulletText("Striding is useful for interleaved data (e.g. audio) or plotting structs."); ImGui::BulletText("Here, all circle data is stored in a single interleaved buffer:"); ImGui::BulletText("[c0.x0 c0.y0 ... cn.x0 cn.y0 c0.x1 c0.y1 ... cn.x1 cn.y1 ... cn.xm cn.ym]"); ImGui::BulletText("The offset value indicates which circle point index is considered the first."); ImGui::BulletText("Offsets can be negative and/or larger than the actual data count."); ImGui::SliderInt("Offset", &offset, -2*k_points_per, 2*k_points_per); if (ImPlot::BeginPlot("##strideoffset",ImVec2(-1,0),ImPlotFlags_Equal)) { ImPlot::PushColormap(ImPlotColormap_Jet); char buff[32]; for (int c = 0; c < k_circles; ++c) { snprintf(buff, sizeof(buff), "Circle %d", c); ImPlot::PlotLine(buff, &interleaved_data[c*2 + 0], &interleaved_data[c*2 + 1], k_points_per, 0, offset, 2*k_circles*sizeof(double)); } ImPlot::EndPlot(); ImPlot::PopColormap(); } // offset++; uncomment for animation! } //----------------------------------------------------------------------------- void Demo_CustomDataAndGetters() { ImGui::BulletText("You can plot custom structs using the stride feature."); ImGui::BulletText("Most plotters can also be passed a function pointer for getting data."); ImGui::Indent(); ImGui::BulletText("You can optionally pass user data to be given to your getter function."); ImGui::BulletText("C++ lambdas can be passed as function pointers as well!"); ImGui::Unindent(); MyImPlot::Vector2f vec2_data[2] = { MyImPlot::Vector2f(0,0), MyImPlot::Vector2f(1,1) }; if (ImPlot::BeginPlot("##Custom Data")) { // custom structs using stride example: ImPlot::PlotLine("Vector2f", &vec2_data[0].x, &vec2_data[0].y, 2, 0, 0, sizeof(MyImPlot::Vector2f) /* or sizeof(float) * 2 */); // custom getter example 1: ImPlot::PlotLineG("Spiral", MyImPlot::Spiral, nullptr, 1000); // custom getter example 2: static MyImPlot::WaveData data1(0.001, 0.2, 2, 0.75); static MyImPlot::WaveData data2(0.001, 0.2, 4, 0.25); ImPlot::PlotLineG("Waves", MyImPlot::SineWave, &data1, 1000); ImPlot::PlotLineG("Waves", MyImPlot::SawWave, &data2, 1000); ImPlot::PushStyleVar(ImPlotStyleVar_FillAlpha, 0.25f); ImPlot::PlotShadedG("Waves", MyImPlot::SineWave, &data1, MyImPlot::SawWave, &data2, 1000); ImPlot::PopStyleVar(); // you can also pass C++ lambdas: // auto lamda = [](void* data, int idx) { ... return ImPlotPoint(x,y); }; // ImPlot::PlotLine("My Lambda", lambda, data, 1000); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- int MetricFormatter(double value, char* buff, int size, void* data) { const char* unit = (const char*)data; static double v[] = {1000000000,1000000,1000,1,0.001,0.000001,0.000000001}; static const char* p[] = {"G","M","k","","m","u","n"}; if (value == 0) { return snprintf(buff,size,"0 %s", unit); } for (int i = 0; i < 7; ++i) { if (fabs(value) >= v[i]) { return snprintf(buff,size,"%g %s%s",value/v[i],p[i],unit); } } return snprintf(buff,size,"%g %s%s",value/v[6],p[6],unit); } void Demo_TickLabels() { static bool custom_fmt = true; static bool custom_ticks = false; static bool custom_labels = true; ImGui::Checkbox("Show Custom Format", &custom_fmt); ImGui::SameLine(); ImGui::Checkbox("Show Custom Ticks", &custom_ticks); if (custom_ticks) { ImGui::SameLine(); ImGui::Checkbox("Show Custom Labels", &custom_labels); } const double pi = 3.14; const char* pi_str[] = {"PI"}; static double yticks[] = {100,300,700,900}; static const char* ylabels[] = {"One","Three","Seven","Nine"}; static double yticks_aux[] = {0.2,0.4,0.6}; static const char* ylabels_aux[] = {"A","B","C","D","E","F"}; if (ImPlot::BeginPlot("##Ticks")) { ImPlot::SetupAxesLimits(2.5,5,0,1000); ImPlot::SetupAxis(ImAxis_Y2, nullptr, ImPlotAxisFlags_AuxDefault); ImPlot::SetupAxis(ImAxis_Y3, nullptr, ImPlotAxisFlags_AuxDefault); if (custom_fmt) { ImPlot::SetupAxisFormat(ImAxis_X1, "%g ms"); ImPlot::SetupAxisFormat(ImAxis_Y1, MetricFormatter, (void*)"Hz"); ImPlot::SetupAxisFormat(ImAxis_Y2, "%g dB"); ImPlot::SetupAxisFormat(ImAxis_Y3, MetricFormatter, (void*)"m"); } if (custom_ticks) { ImPlot::SetupAxisTicks(ImAxis_X1, &pi,1,custom_labels ? pi_str : nullptr, true); ImPlot::SetupAxisTicks(ImAxis_Y1, yticks, 4, custom_labels ? ylabels : nullptr, false); ImPlot::SetupAxisTicks(ImAxis_Y2, yticks_aux, 3, custom_labels ? ylabels_aux : nullptr, false); ImPlot::SetupAxisTicks(ImAxis_Y3, 0, 1, 6, custom_labels ? ylabels_aux : nullptr, false); } ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_CustomStyles() { ImPlot::PushColormap(ImPlotColormap_Deep); // normally you wouldn't change the entire style each frame ImPlotStyle backup = ImPlot::GetStyle(); MyImPlot::StyleSeaborn(); if (ImPlot::BeginPlot("seaborn style")) { ImPlot::SetupAxes( "x-axis", "y-axis"); ImPlot::SetupAxesLimits(-0.5f, 9.5f, 0, 10); unsigned int lin[10] = {8,8,9,7,8,8,8,9,7,8}; unsigned int bar[10] = {1,2,5,3,4,1,2,5,3,4}; unsigned int dot[10] = {7,6,6,7,8,5,6,5,8,7}; ImPlot::PlotBars("Bars", bar, 10, 0.5f); ImPlot::PlotLine("Line", lin, 10); ImPlot::NextColormapColor(); // skip green ImPlot::PlotScatter("Scatter", dot, 10); ImPlot::EndPlot(); } ImPlot::GetStyle() = backup; ImPlot::PopColormap(); } //----------------------------------------------------------------------------- void Demo_CustomRendering() { if (ImPlot::BeginPlot("##CustomRend")) { ImVec2 cntr = ImPlot::PlotToPixels(ImPlotPoint(0.5f, 0.5f)); ImVec2 rmin = ImPlot::PlotToPixels(ImPlotPoint(0.25f, 0.75f)); ImVec2 rmax = ImPlot::PlotToPixels(ImPlotPoint(0.75f, 0.25f)); ImPlot::PushPlotClipRect(); ImPlot::GetPlotDrawList()->AddCircleFilled(cntr,20,IM_COL32(255,255,0,255),20); ImPlot::GetPlotDrawList()->AddRect(rmin, rmax, IM_COL32(128,0,255,255)); ImPlot::PopPlotClipRect(); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_LegendPopups() { ImGui::BulletText("You can implement legend context menus to inject per-item controls and widgets."); ImGui::BulletText("Right click the legend label/icon to edit custom item attributes."); static float frequency = 0.1f; static float amplitude = 0.5f; static ImVec4 color = ImVec4(1,1,0,1); static float alpha = 1.0f; static bool line = false; static float thickness = 1; static bool markers = false; static bool shaded = false; static float vals[101]; for (int i = 0; i < 101; ++i) vals[i] = amplitude * sinf(frequency * i); if (ImPlot::BeginPlot("Right Click the Legend")) { ImPlot::SetupAxesLimits(0,100,-1,1); // rendering logic ImPlot::PushStyleVar(ImPlotStyleVar_FillAlpha, alpha); if (!line) { ImPlot::SetNextFillStyle(color); ImPlot::PlotBars("Right Click Me", vals, 101); } else { if (markers) ImPlot::SetNextMarkerStyle(ImPlotMarker_Square); ImPlot::SetNextLineStyle(color, thickness); ImPlot::PlotLine("Right Click Me", vals, 101); if (shaded) ImPlot::PlotShaded("Right Click Me",vals,101); } ImPlot::PopStyleVar(); // custom legend context menu if (ImPlot::BeginLegendPopup("Right Click Me")) { ImGui::SliderFloat("Frequency",&frequency,0,1,"%0.2f"); ImGui::SliderFloat("Amplitude",&litude,0,1,"%0.2f"); ImGui::Separator(); ImGui::ColorEdit3("Color",&color.x); ImGui::SliderFloat("Transparency",&alpha,0,1,"%.2f"); ImGui::Checkbox("Line Plot", &line); if (line) { ImGui::SliderFloat("Thickness", &thickness, 0, 5); ImGui::Checkbox("Markers", &markers); ImGui::Checkbox("Shaded",&shaded); } ImPlot::EndLegendPopup(); } ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- void Demo_ColormapWidgets() { static int cmap = ImPlotColormap_Viridis; if (ImPlot::ColormapButton("Button",ImVec2(0,0),cmap)) { cmap = (cmap + 1) % ImPlot::GetColormapCount(); } static float t = 0.5f; static ImVec4 col; ImGui::ColorButton("##Display",col,ImGuiColorEditFlags_NoInputs); ImGui::SameLine(); ImPlot::ColormapSlider("Slider", &t, &col, "%.3f", cmap); ImPlot::ColormapIcon(cmap); ImGui::SameLine(); ImGui::Text("Icon"); static ImPlotColormapScaleFlags flags = 0; static float scale[2] = {0, 100}; ImPlot::ColormapScale("Scale",scale[0],scale[1],ImVec2(0,0),"%g dB",flags,cmap); ImGui::InputFloat2("Scale",scale); CHECKBOX_FLAG(flags, ImPlotColormapScaleFlags_NoLabel); CHECKBOX_FLAG(flags, ImPlotColormapScaleFlags_Opposite); CHECKBOX_FLAG(flags, ImPlotColormapScaleFlags_Invert); } //----------------------------------------------------------------------------- void Demo_CustomPlottersAndTooltips() { ImGui::BulletText("You can create custom plotters or extend ImPlot using implot_internal.h."); double dates[] = {1546300800,1546387200,1546473600,1546560000,1546819200,1546905600,1546992000,1547078400,1547164800,1547424000,1547510400,1547596800,1547683200,1547769600,1547942400,1548028800,1548115200,1548201600,1548288000,1548374400,1548633600,1548720000,1548806400,1548892800,1548979200,1549238400,1549324800,1549411200,1549497600,1549584000,1549843200,1549929600,1550016000,1550102400,1550188800,1550361600,1550448000,1550534400,1550620800,1550707200,1550793600,1551052800,1551139200,1551225600,1551312000,1551398400,1551657600,1551744000,1551830400,1551916800,1552003200,1552262400,1552348800,1552435200,1552521600,1552608000,1552867200,1552953600,1553040000,1553126400,1553212800,1553472000,1553558400,1553644800,1553731200,1553817600,1554076800,1554163200,1554249600,1554336000,1554422400,1554681600,1554768000,1554854400,1554940800,1555027200,1555286400,1555372800,1555459200,1555545600,1555632000,1555891200,1555977600,1556064000,1556150400,1556236800,1556496000,1556582400,1556668800,1556755200,1556841600,1557100800,1557187200,1557273600,1557360000,1557446400,1557705600,1557792000,1557878400,1557964800,1558051200,1558310400,1558396800,1558483200,1558569600,1558656000,1558828800,1558915200,1559001600,1559088000,1559174400,1559260800,1559520000,1559606400,1559692800,1559779200,1559865600,1560124800,1560211200,1560297600,1560384000,1560470400,1560729600,1560816000,1560902400,1560988800,1561075200,1561334400,1561420800,1561507200,1561593600,1561680000,1561939200,1562025600,1562112000,1562198400,1562284800,1562544000,1562630400,1562716800,1562803200,1562889600,1563148800,1563235200,1563321600,1563408000,1563494400,1563753600,1563840000,1563926400,1564012800,1564099200,1564358400,1564444800,1564531200,1564617600,1564704000,1564963200,1565049600,1565136000,1565222400,1565308800,1565568000,1565654400,1565740800,1565827200,1565913600,1566172800,1566259200,1566345600,1566432000,1566518400,1566777600,1566864000,1566950400,1567036800,1567123200,1567296000,1567382400,1567468800,1567555200,1567641600,1567728000,1567987200,1568073600,1568160000,1568246400,1568332800,1568592000,1568678400,1568764800,1568851200,1568937600,1569196800,1569283200,1569369600,1569456000,1569542400,1569801600,1569888000,1569974400,1570060800,1570147200,1570406400,1570492800,1570579200,1570665600,1570752000,1571011200,1571097600,1571184000,1571270400,1571356800,1571616000,1571702400,1571788800,1571875200,1571961600}; double opens[] = {1284.7,1319.9,1318.7,1328,1317.6,1321.6,1314.3,1325,1319.3,1323.1,1324.7,1321.3,1323.5,1322,1281.3,1281.95,1311.1,1315,1314,1313.1,1331.9,1334.2,1341.3,1350.6,1349.8,1346.4,1343.4,1344.9,1335.6,1337.9,1342.5,1337,1338.6,1337,1340.4,1324.65,1324.35,1349.5,1371.3,1367.9,1351.3,1357.8,1356.1,1356,1347.6,1339.1,1320.6,1311.8,1314,1312.4,1312.3,1323.5,1319.1,1327.2,1332.1,1320.3,1323.1,1328,1330.9,1338,1333,1335.3,1345.2,1341.1,1332.5,1314,1314.4,1310.7,1314,1313.1,1315,1313.7,1320,1326.5,1329.2,1314.2,1312.3,1309.5,1297.4,1293.7,1277.9,1295.8,1295.2,1290.3,1294.2,1298,1306.4,1299.8,1302.3,1297,1289.6,1302,1300.7,1303.5,1300.5,1303.2,1306,1318.7,1315,1314.5,1304.1,1294.7,1293.7,1291.2,1290.2,1300.4,1284.2,1284.25,1301.8,1295.9,1296.2,1304.4,1323.1,1340.9,1341,1348,1351.4,1351.4,1343.5,1342.3,1349,1357.6,1357.1,1354.7,1361.4,1375.2,1403.5,1414.7,1433.2,1438,1423.6,1424.4,1418,1399.5,1435.5,1421.25,1434.1,1412.4,1409.8,1412.2,1433.4,1418.4,1429,1428.8,1420.6,1441,1460.4,1441.7,1438.4,1431,1439.3,1427.4,1431.9,1439.5,1443.7,1425.6,1457.5,1451.2,1481.1,1486.7,1512.1,1515.9,1509.2,1522.3,1513,1526.6,1533.9,1523,1506.3,1518.4,1512.4,1508.8,1545.4,1537.3,1551.8,1549.4,1536.9,1535.25,1537.95,1535.2,1556,1561.4,1525.6,1516.4,1507,1493.9,1504.9,1506.5,1513.1,1506.5,1509.7,1502,1506.8,1521.5,1529.8,1539.8,1510.9,1511.8,1501.7,1478,1485.4,1505.6,1511.6,1518.6,1498.7,1510.9,1510.8,1498.3,1492,1497.7,1484.8,1494.2,1495.6,1495.6,1487.5,1491.1,1495.1,1506.4}; double highs[] = {1284.75,1320.6,1327,1330.8,1326.8,1321.6,1326,1328,1325.8,1327.1,1326,1326,1323.5,1322.1,1282.7,1282.95,1315.8,1316.3,1314,1333.2,1334.7,1341.7,1353.2,1354.6,1352.2,1346.4,1345.7,1344.9,1340.7,1344.2,1342.7,1342.1,1345.2,1342,1350,1324.95,1330.75,1369.6,1374.3,1368.4,1359.8,1359,1357,1356,1353.4,1340.6,1322.3,1314.1,1316.1,1312.9,1325.7,1323.5,1326.3,1336,1332.1,1330.1,1330.4,1334.7,1341.1,1344.2,1338.8,1348.4,1345.6,1342.8,1334.7,1322.3,1319.3,1314.7,1316.6,1316.4,1315,1325.4,1328.3,1332.2,1329.2,1316.9,1312.3,1309.5,1299.6,1296.9,1277.9,1299.5,1296.2,1298.4,1302.5,1308.7,1306.4,1305.9,1307,1297.2,1301.7,1305,1305.3,1310.2,1307,1308,1319.8,1321.7,1318.7,1316.2,1305.9,1295.8,1293.8,1293.7,1304.2,1302,1285.15,1286.85,1304,1302,1305.2,1323,1344.1,1345.2,1360.1,1355.3,1363.8,1353,1344.7,1353.6,1358,1373.6,1358.2,1369.6,1377.6,1408.9,1425.5,1435.9,1453.7,1438,1426,1439.1,1418,1435,1452.6,1426.65,1437.5,1421.5,1414.1,1433.3,1441.3,1431.4,1433.9,1432.4,1440.8,1462.3,1467,1443.5,1444,1442.9,1447,1437.6,1440.8,1445.7,1447.8,1458.2,1461.9,1481.8,1486.8,1522.7,1521.3,1521.1,1531.5,1546.1,1534.9,1537.7,1538.6,1523.6,1518.8,1518.4,1514.6,1540.3,1565,1554.5,1556.6,1559.8,1541.9,1542.9,1540.05,1558.9,1566.2,1561.9,1536.2,1523.8,1509.1,1506.2,1532.2,1516.6,1519.7,1515,1519.5,1512.1,1524.5,1534.4,1543.3,1543.3,1542.8,1519.5,1507.2,1493.5,1511.4,1525.8,1522.2,1518.8,1515.3,1518,1522.3,1508,1501.5,1503,1495.5,1501.1,1497.9,1498.7,1492.1,1499.4,1506.9,1520.9}; double lows[] = {1282.85,1315,1318.7,1309.6,1317.6,1312.9,1312.4,1319.1,1319,1321,1318.1,1321.3,1319.9,1312,1280.5,1276.15,1308,1309.9,1308.5,1312.3,1329.3,1333.1,1340.2,1347,1345.9,1338,1340.8,1335,1332,1337.9,1333,1336.8,1333.2,1329.9,1340.4,1323.85,1324.05,1349,1366.3,1351.2,1349.1,1352.4,1350.7,1344.3,1338.9,1316.3,1308.4,1306.9,1309.6,1306.7,1312.3,1315.4,1319,1327.2,1317.2,1320,1323,1328,1323,1327.8,1331.7,1335.3,1336.6,1331.8,1311.4,1310,1309.5,1308,1310.6,1302.8,1306.6,1313.7,1320,1322.8,1311,1312.1,1303.6,1293.9,1293.5,1291,1277.9,1294.1,1286,1289.1,1293.5,1296.9,1298,1299.6,1292.9,1285.1,1288.5,1296.3,1297.2,1298.4,1298.6,1302,1300.3,1312,1310.8,1301.9,1292,1291.1,1286.3,1289.2,1289.9,1297.4,1283.65,1283.25,1292.9,1295.9,1290.8,1304.2,1322.7,1336.1,1341,1343.5,1345.8,1340.3,1335.1,1341.5,1347.6,1352.8,1348.2,1353.7,1356.5,1373.3,1398,1414.7,1427,1416.4,1412.7,1420.1,1396.4,1398.8,1426.6,1412.85,1400.7,1406,1399.8,1404.4,1415.5,1417.2,1421.9,1415,1413.7,1428.1,1434,1435.7,1427.5,1429.4,1423.9,1425.6,1427.5,1434.8,1422.3,1412.1,1442.5,1448.8,1468.2,1484.3,1501.6,1506.2,1498.6,1488.9,1504.5,1518.3,1513.9,1503.3,1503,1506.5,1502.1,1503,1534.8,1535.3,1541.4,1528.6,1525.6,1535.25,1528.15,1528,1542.6,1514.3,1510.7,1505.5,1492.1,1492.9,1496.8,1493.1,1503.4,1500.9,1490.7,1496.3,1505.3,1505.3,1517.9,1507.4,1507.1,1493.3,1470.5,1465,1480.5,1501.7,1501.4,1493.3,1492.1,1505.1,1495.7,1478,1487.1,1480.8,1480.6,1487,1488.3,1484.8,1484,1490.7,1490.4,1503.1}; double closes[] = {1283.35,1315.3,1326.1,1317.4,1321.5,1317.4,1323.5,1319.2,1321.3,1323.3,1319.7,1325.1,1323.6,1313.8,1282.05,1279.05,1314.2,1315.2,1310.8,1329.1,1334.5,1340.2,1340.5,1350,1347.1,1344.3,1344.6,1339.7,1339.4,1343.7,1337,1338.9,1340.1,1338.7,1346.8,1324.25,1329.55,1369.6,1372.5,1352.4,1357.6,1354.2,1353.4,1346,1341,1323.8,1311.9,1309.1,1312.2,1310.7,1324.3,1315.7,1322.4,1333.8,1319.4,1327.1,1325.8,1330.9,1325.8,1331.6,1336.5,1346.7,1339.2,1334.7,1313.3,1316.5,1312.4,1313.4,1313.3,1312.2,1313.7,1319.9,1326.3,1331.9,1311.3,1313.4,1309.4,1295.2,1294.7,1294.1,1277.9,1295.8,1291.2,1297.4,1297.7,1306.8,1299.4,1303.6,1302.2,1289.9,1299.2,1301.8,1303.6,1299.5,1303.2,1305.3,1319.5,1313.6,1315.1,1303.5,1293,1294.6,1290.4,1291.4,1302.7,1301,1284.15,1284.95,1294.3,1297.9,1304.1,1322.6,1339.3,1340.1,1344.9,1354,1357.4,1340.7,1342.7,1348.2,1355.1,1355.9,1354.2,1362.1,1360.1,1408.3,1411.2,1429.5,1430.1,1426.8,1423.4,1425.1,1400.8,1419.8,1432.9,1423.55,1412.1,1412.2,1412.8,1424.9,1419.3,1424.8,1426.1,1423.6,1435.9,1440.8,1439.4,1439.7,1434.5,1436.5,1427.5,1432.2,1433.3,1441.8,1437.8,1432.4,1457.5,1476.5,1484.2,1519.6,1509.5,1508.5,1517.2,1514.1,1527.8,1531.2,1523.6,1511.6,1515.7,1515.7,1508.5,1537.6,1537.2,1551.8,1549.1,1536.9,1529.4,1538.05,1535.15,1555.9,1560.4,1525.5,1515.5,1511.1,1499.2,1503.2,1507.4,1499.5,1511.5,1513.4,1515.8,1506.2,1515.1,1531.5,1540.2,1512.3,1515.2,1506.4,1472.9,1489,1507.9,1513.8,1512.9,1504.4,1503.9,1512.8,1500.9,1488.7,1497.6,1483.5,1494,1498.3,1494.1,1488.1,1487.5,1495.7,1504.7,1505.3}; static bool tooltip = true; ImGui::Checkbox("Show Tooltip", &tooltip); ImGui::SameLine(); static ImVec4 bullCol = ImVec4(0.000f, 1.000f, 0.441f, 1.000f); static ImVec4 bearCol = ImVec4(0.853f, 0.050f, 0.310f, 1.000f); ImGui::SameLine(); ImGui::ColorEdit4("##Bull", &bullCol.x, ImGuiColorEditFlags_NoInputs); ImGui::SameLine(); ImGui::ColorEdit4("##Bear", &bearCol.x, ImGuiColorEditFlags_NoInputs); ImPlot::GetStyle().UseLocalTime = false; if (ImPlot::BeginPlot("Candlestick Chart",ImVec2(-1,0))) { ImPlot::SetupAxes(nullptr,nullptr,0,ImPlotAxisFlags_AutoFit|ImPlotAxisFlags_RangeFit); ImPlot::SetupAxesLimits(1546300800, 1571961600, 1250, 1600); ImPlot::SetupAxisScale(ImAxis_X1, ImPlotScale_Time); ImPlot::SetupAxisLimitsConstraints(ImAxis_X1, 1546300800, 1571961600); ImPlot::SetupAxisZoomConstraints(ImAxis_X1, 60*60*24*14, 1571961600-1546300800); ImPlot::SetupAxisFormat(ImAxis_Y1, "$%.0f"); MyImPlot::PlotCandlestick("GOOGL",dates, opens, closes, lows, highs, 218, tooltip, 0.25f, bullCol, bearCol); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- // DEMO WINDOW //----------------------------------------------------------------------------- void DemoHeader(const char* label, void(*demo)()) { if (ImGui::TreeNodeEx(label)) { demo(); ImGui::TreePop(); } } void ShowDemoWindow(bool* p_open) { static bool show_implot_metrics = false; static bool show_implot_style_editor = false; static bool show_imgui_metrics = false; static bool show_imgui_style_editor = false; static bool show_imgui_demo = false; if (show_implot_metrics) { ImPlot::ShowMetricsWindow(&show_implot_metrics); } if (show_implot_style_editor) { ImGui::SetNextWindowSize(ImVec2(415,762), ImGuiCond_Appearing); ImGui::Begin("Style Editor (ImPlot)", &show_implot_style_editor); ImPlot::ShowStyleEditor(); ImGui::End(); } if (show_imgui_style_editor) { ImGui::Begin("Style Editor (ImGui)", &show_imgui_style_editor); ImGui::ShowStyleEditor(); ImGui::End(); } if (show_imgui_metrics) { ImGui::ShowMetricsWindow(&show_imgui_metrics); } if (show_imgui_demo) { ImGui::ShowDemoWindow(&show_imgui_demo); } ImGui::SetNextWindowPos(ImVec2(50, 50), ImGuiCond_FirstUseEver); ImGui::SetNextWindowSize(ImVec2(600, 750), ImGuiCond_FirstUseEver); ImGui::Begin("ImPlot Demo", p_open, ImGuiWindowFlags_MenuBar); if (ImGui::BeginMenuBar()) { if (ImGui::BeginMenu("Tools")) { ImGui::MenuItem("Metrics", nullptr, &show_implot_metrics); ImGui::MenuItem("Style Editor", nullptr, &show_implot_style_editor); ImGui::Separator(); ImGui::MenuItem("ImGui Metrics", nullptr, &show_imgui_metrics); ImGui::MenuItem("ImGui Style Editor", nullptr, &show_imgui_style_editor); ImGui::MenuItem("ImGui Demo", nullptr, &show_imgui_demo); ImGui::EndMenu(); } ImGui::EndMenuBar(); } //------------------------------------------------------------------------- ImGui::Text("ImPlot says hello. (%s)", IMPLOT_VERSION); // display warning about 16-bit indices static bool showWarning = sizeof(ImDrawIdx)*8 == 16 && (ImGui::GetIO().BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset) == false; if (showWarning) { ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1,1,0,1)); ImGui::TextWrapped("WARNING: ImDrawIdx is 16-bit and ImGuiBackendFlags_RendererHasVtxOffset is false. Expect visual glitches and artifacts! See README for more information."); ImGui::PopStyleColor(); } ImGui::Spacing(); if (ImGui::BeginTabBar("ImPlotDemoTabs")) { if (ImGui::BeginTabItem("Plots")) { DemoHeader("Line Plots", Demo_LinePlots); DemoHeader("Filled Line Plots", Demo_FilledLinePlots); DemoHeader("Shaded Plots##", Demo_ShadedPlots); DemoHeader("Scatter Plots", Demo_ScatterPlots); DemoHeader("Realtime Plots", Demo_RealtimePlots); DemoHeader("Stairstep Plots", Demo_StairstepPlots); DemoHeader("Bar Plots", Demo_BarPlots); DemoHeader("Bar Groups", Demo_BarGroups); DemoHeader("Bar Stacks", Demo_BarStacks); DemoHeader("Error Bars", Demo_ErrorBars); DemoHeader("Stem Plots##", Demo_StemPlots); DemoHeader("Infinite Lines", Demo_InfiniteLines); DemoHeader("Pie Charts", Demo_PieCharts); DemoHeader("Heatmaps", Demo_Heatmaps); DemoHeader("Histogram", Demo_Histogram); DemoHeader("Histogram 2D", Demo_Histogram2D); DemoHeader("Digital Plots", Demo_DigitalPlots); DemoHeader("Images", Demo_Images); DemoHeader("Markers and Text", Demo_MarkersAndText); DemoHeader("NaN Values", Demo_NaNValues); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Subplots")) { DemoHeader("Sizing", Demo_SubplotsSizing); DemoHeader("Item Sharing", Demo_SubplotItemSharing); DemoHeader("Axis Linking", Demo_SubplotAxisLinking); DemoHeader("Tables", Demo_Tables); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Axes")) { DemoHeader("Log Scale", Demo_LogScale); DemoHeader("Symmetric Log Scale", Demo_SymmetricLogScale); DemoHeader("Time Scale", Demo_TimeScale); DemoHeader("Custom Scale", Demo_CustomScale); DemoHeader("Multiple Axes", Demo_MultipleAxes); DemoHeader("Tick Labels", Demo_TickLabels); DemoHeader("Linked Axes", Demo_LinkedAxes); DemoHeader("Axis Constraints", Demo_AxisConstraints); DemoHeader("Equal Axes", Demo_EqualAxes); DemoHeader("Auto-Fitting Data", Demo_AutoFittingData); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Tools")) { DemoHeader("Offset and Stride", Demo_OffsetAndStride); DemoHeader("Drag Points", Demo_DragPoints); DemoHeader("Drag Lines", Demo_DragLines); DemoHeader("Drag Rects", Demo_DragRects); DemoHeader("Querying", Demo_Querying); DemoHeader("Annotations", Demo_Annotations); DemoHeader("Tags", Demo_Tags); DemoHeader("Drag and Drop", Demo_DragAndDrop); DemoHeader("Legend Options", Demo_LegendOptions); DemoHeader("Legend Popups", Demo_LegendPopups); DemoHeader("Colormap Widgets", Demo_ColormapWidgets); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Custom")) { DemoHeader("Custom Styles", Demo_CustomStyles); DemoHeader("Custom Data and Getters", Demo_CustomDataAndGetters); DemoHeader("Custom Rendering", Demo_CustomRendering); DemoHeader("Custom Plotters and Tooltips", Demo_CustomPlottersAndTooltips); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Config")) { Demo_Config(); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Help")) { Demo_Help(); ImGui::EndTabItem(); } ImGui::EndTabBar(); } ImGui::End(); } } // namespace ImPlot namespace MyImPlot { ImPlotPoint SineWave(int idx, void* data) { WaveData* wd = (WaveData*)data; double x = idx * wd->X; return ImPlotPoint(x, wd->Offset + wd->Amp * sin(2 * 3.14 * wd->Freq * x)); } ImPlotPoint SawWave(int idx, void* data) { WaveData* wd = (WaveData*)data; double x = idx * wd->X; return ImPlotPoint(x, wd->Offset + wd->Amp * (-2 / 3.14 * atan(cos(3.14 * wd->Freq * x) / sin(3.14 * wd->Freq * x)))); } ImPlotPoint Spiral(int idx, void*) { float r = 0.9f; // outer radius float a = 0; // inner radius float b = 0.05f; // increment per rev float n = (r - a) / b; // number of revolutions double th = 2 * n * 3.14; // angle float Th = float(th * idx / (1000 - 1)); return ImPlotPoint(0.5f+(a + b*Th / (2.0f * (float) 3.14))*cos(Th), 0.5f + (a + b*Th / (2.0f * (float)3.14))*sin(Th)); } void Sparkline(const char* id, const float* values, int count, float min_v, float max_v, int offset, const ImVec4& col, const ImVec2& size) { ImPlot::PushStyleVar(ImPlotStyleVar_PlotPadding, ImVec2(0,0)); if (ImPlot::BeginPlot(id,size,ImPlotFlags_CanvasOnly|ImPlotFlags_NoChild)) { ImPlot::SetupAxes(nullptr,nullptr,ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_NoDecorations); ImPlot::SetupAxesLimits(0, count - 1, min_v, max_v, ImGuiCond_Always); ImPlot::SetNextLineStyle(col); ImPlot::SetNextFillStyle(col, 0.25); ImPlot::PlotLine(id, values, count, 1, 0, ImPlotLineFlags_Shaded, offset); ImPlot::EndPlot(); } ImPlot::PopStyleVar(); } void StyleSeaborn() { ImPlotStyle& style = ImPlot::GetStyle(); ImVec4* colors = style.Colors; colors[ImPlotCol_Line] = IMPLOT_AUTO_COL; colors[ImPlotCol_Fill] = IMPLOT_AUTO_COL; colors[ImPlotCol_MarkerOutline] = IMPLOT_AUTO_COL; colors[ImPlotCol_MarkerFill] = IMPLOT_AUTO_COL; colors[ImPlotCol_ErrorBar] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); colors[ImPlotCol_FrameBg] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); colors[ImPlotCol_PlotBg] = ImVec4(0.92f, 0.92f, 0.95f, 1.00f); colors[ImPlotCol_PlotBorder] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); colors[ImPlotCol_LegendBg] = ImVec4(0.92f, 0.92f, 0.95f, 1.00f); colors[ImPlotCol_LegendBorder] = ImVec4(0.80f, 0.81f, 0.85f, 1.00f); colors[ImPlotCol_LegendText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); colors[ImPlotCol_TitleText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); colors[ImPlotCol_InlayText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); colors[ImPlotCol_AxisText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); colors[ImPlotCol_AxisGrid] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); colors[ImPlotCol_AxisBgHovered] = ImVec4(0.92f, 0.92f, 0.95f, 1.00f); colors[ImPlotCol_AxisBgActive] = ImVec4(0.92f, 0.92f, 0.95f, 0.75f); colors[ImPlotCol_Selection] = ImVec4(1.00f, 0.65f, 0.00f, 1.00f); colors[ImPlotCol_Crosshairs] = ImVec4(0.23f, 0.10f, 0.64f, 0.50f); style.LineWeight = 1.5; style.Marker = ImPlotMarker_None; style.MarkerSize = 4; style.MarkerWeight = 1; style.FillAlpha = 1.0f; style.ErrorBarSize = 5; style.ErrorBarWeight = 1.5f; style.DigitalBitHeight = 8; style.DigitalBitGap = 4; style.PlotBorderSize = 0; style.MinorAlpha = 1.0f; style.MajorTickLen = ImVec2(0,0); style.MinorTickLen = ImVec2(0,0); style.MajorTickSize = ImVec2(0,0); style.MinorTickSize = ImVec2(0,0); style.MajorGridSize = ImVec2(1.2f,1.2f); style.MinorGridSize = ImVec2(1.2f,1.2f); style.PlotPadding = ImVec2(12,12); style.LabelPadding = ImVec2(5,5); style.LegendPadding = ImVec2(5,5); style.MousePosPadding = ImVec2(5,5); style.PlotMinSize = ImVec2(300,225); } } // namespaece MyImPlot // WARNING: // // You can use "implot_internal.h" to build custom plotting fuctions or extend ImPlot. // However, note that forward compatibility of this file is not guaranteed and the // internal API is subject to change. At some point we hope to bring more of this // into the public API and expose the necessary building blocks to fully support // custom plotters. For now, proceed at your own risk! #include "implot_internal.h" namespace MyImPlot { template int BinarySearch(const T* arr, int l, int r, T x) { if (r >= l) { int mid = l + (r - l) / 2; if (arr[mid] == x) return mid; if (arr[mid] > x) return BinarySearch(arr, l, mid - 1, x); return BinarySearch(arr, mid + 1, r, x); } return -1; } void PlotCandlestick(const char* label_id, const double* xs, const double* opens, const double* closes, const double* lows, const double* highs, int count, bool tooltip, float width_percent, ImVec4 bullCol, ImVec4 bearCol) { // get ImGui window DrawList ImDrawList* draw_list = ImPlot::GetPlotDrawList(); // calc real value width double half_width = count > 1 ? (xs[1] - xs[0]) * width_percent : width_percent; // custom tool if (ImPlot::IsPlotHovered() && tooltip) { ImPlotPoint mouse = ImPlot::GetPlotMousePos(); mouse.x = ImPlot::RoundTime(ImPlotTime::FromDouble(mouse.x), ImPlotTimeUnit_Day).ToDouble(); float tool_l = ImPlot::PlotToPixels(mouse.x - half_width * 1.5, mouse.y).x; float tool_r = ImPlot::PlotToPixels(mouse.x + half_width * 1.5, mouse.y).x; float tool_t = ImPlot::GetPlotPos().y; float tool_b = tool_t + ImPlot::GetPlotSize().y; ImPlot::PushPlotClipRect(); draw_list->AddRectFilled(ImVec2(tool_l, tool_t), ImVec2(tool_r, tool_b), IM_COL32(128,128,128,64)); ImPlot::PopPlotClipRect(); // find mouse location index int idx = BinarySearch(xs, 0, count - 1, mouse.x); // render tool tip (won't be affected by plot clip rect) if (idx != -1) { ImGui::BeginTooltip(); char buff[32]; ImPlot::FormatDate(ImPlotTime::FromDouble(xs[idx]),buff,32,ImPlotDateFmt_DayMoYr,ImPlot::GetStyle().UseISO8601); ImGui::Text("Day: %s", buff); ImGui::Text("Open: $%.2f", opens[idx]); ImGui::Text("Close: $%.2f", closes[idx]); ImGui::Text("Low: $%.2f", lows[idx]); ImGui::Text("High: $%.2f", highs[idx]); ImGui::EndTooltip(); } } // begin plot item if (ImPlot::BeginItem(label_id)) { // override legend icon color ImPlot::GetCurrentItem()->Color = IM_COL32(64,64,64,255); // fit data if requested if (ImPlot::FitThisFrame()) { for (int i = 0; i < count; ++i) { ImPlot::FitPoint(ImPlotPoint(xs[i], lows[i])); ImPlot::FitPoint(ImPlotPoint(xs[i], highs[i])); } } // render data for (int i = 0; i < count; ++i) { ImVec2 open_pos = ImPlot::PlotToPixels(xs[i] - half_width, opens[i]); ImVec2 close_pos = ImPlot::PlotToPixels(xs[i] + half_width, closes[i]); ImVec2 low_pos = ImPlot::PlotToPixels(xs[i], lows[i]); ImVec2 high_pos = ImPlot::PlotToPixels(xs[i], highs[i]); ImU32 color = ImGui::GetColorU32(opens[i] > closes[i] ? bearCol : bullCol); draw_list->AddLine(low_pos, high_pos, color); draw_list->AddRectFilled(open_pos, close_pos, color); } // end plot item ImPlot::EndItem(); } } } // namespace MyImplot ================================================ FILE: Source/External/imgui_tools/implot/implot_internal.h ================================================ // MIT License // Copyright (c) 2022 Evan Pezent // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // ImPlot v0.14 // You may use this file to debug, understand or extend ImPlot features but we // don't provide any guarantee of forward compatibility! //----------------------------------------------------------------------------- // [SECTION] Header Mess //----------------------------------------------------------------------------- #pragma once #include #include "imgui_internal.h" #ifndef IMPLOT_VERSION #error Must include implot.h before implot_internal.h #endif // Support for pre-1.84 versions. ImPool's GetSize() -> GetBufSize() #if (IMGUI_VERSION_NUM < 18303) #define GetBufSize GetSize #endif //----------------------------------------------------------------------------- // [SECTION] Constants //----------------------------------------------------------------------------- // Constants can be changed unless stated otherwise. We may move some of these // to ImPlotStyleVar_ over time. // Mimimum allowable timestamp value 01/01/1970 @ 12:00am (UTC) (DO NOT DECREASE THIS) #define IMPLOT_MIN_TIME 0 // Maximum allowable timestamp value 01/01/3000 @ 12:00am (UTC) (DO NOT INCREASE THIS) #define IMPLOT_MAX_TIME 32503680000 // Default label format for axis labels #define IMPLOT_LABEL_FORMAT "%g" // Max character size for tick labels #define IMPLOT_LABEL_MAX_SIZE 32 //----------------------------------------------------------------------------- // [SECTION] Macros //----------------------------------------------------------------------------- #define IMPLOT_NUM_X_AXES ImAxis_Y1 #define IMPLOT_NUM_Y_AXES (ImAxis_COUNT - IMPLOT_NUM_X_AXES) // Split ImU32 color into RGB components [0 255] #define IM_COL32_SPLIT_RGB(col,r,g,b) \ ImU32 r = ((col >> IM_COL32_R_SHIFT) & 0xFF); \ ImU32 g = ((col >> IM_COL32_G_SHIFT) & 0xFF); \ ImU32 b = ((col >> IM_COL32_B_SHIFT) & 0xFF); //----------------------------------------------------------------------------- // [SECTION] Forward Declarations //----------------------------------------------------------------------------- struct ImPlotTick; struct ImPlotAxis; struct ImPlotAxisColor; struct ImPlotItem; struct ImPlotLegend; struct ImPlotPlot; struct ImPlotNextPlotData; struct ImPlotTicker; //----------------------------------------------------------------------------- // [SECTION] Context Pointer //----------------------------------------------------------------------------- #ifndef GImPlot extern IMPLOT_API ImPlotContext* GImPlot; // Current implicit context pointer #endif //----------------------------------------------------------------------------- // [SECTION] Generic Helpers //----------------------------------------------------------------------------- // Computes the common (base-10) logarithm static inline float ImLog10(float x) { return log10f(x); } static inline double ImLog10(double x) { return log10(x); } static inline float ImSinh(float x) { return sinhf(x); } static inline double ImSinh(double x) { return sinh(x); } static inline float ImAsinh(float x) { return asinhf(x); } static inline double ImAsinh(double x) { return asinh(x); } // Returns true if a flag is set template static inline bool ImHasFlag(TSet set, TFlag flag) { return (set & flag) == flag; } // Flips a flag in a flagset template static inline void ImFlipFlag(TSet& set, TFlag flag) { ImHasFlag(set, flag) ? set &= ~flag : set |= flag; } // Linearly remaps x from [x0 x1] to [y0 y1]. template static inline T ImRemap(T x, T x0, T x1, T y0, T y1) { return y0 + (x - x0) * (y1 - y0) / (x1 - x0); } // Linear rempas x from [x0 x1] to [0 1] template static inline T ImRemap01(T x, T x0, T x1) { return (x - x0) / (x1 - x0); } // Returns always positive modulo (assumes r != 0) static inline int ImPosMod(int l, int r) { return (l % r + r) % r; } // Returns true if val is NAN static inline bool ImNan(double val) { return isnan(val); } // Returns true if val is NAN or INFINITY static inline bool ImNanOrInf(double val) { return !(val >= -DBL_MAX && val <= DBL_MAX) || ImNan(val); } // Turns NANs to 0s static inline double ImConstrainNan(double val) { return ImNan(val) ? 0 : val; } // Turns infinity to floating point maximums static inline double ImConstrainInf(double val) { return val >= DBL_MAX ? DBL_MAX : val <= -DBL_MAX ? - DBL_MAX : val; } // Turns numbers less than or equal to 0 to 0.001 (sort of arbitrary, is there a better way?) static inline double ImConstrainLog(double val) { return val <= 0 ? 0.001f : val; } // Turns numbers less than 0 to zero static inline double ImConstrainTime(double val) { return val < IMPLOT_MIN_TIME ? IMPLOT_MIN_TIME : (val > IMPLOT_MAX_TIME ? IMPLOT_MAX_TIME : val); } // True if two numbers are approximately equal using units in the last place. static inline bool ImAlmostEqual(double v1, double v2, int ulp = 2) { return ImAbs(v1-v2) < DBL_EPSILON * ImAbs(v1+v2) * ulp || ImAbs(v1-v2) < DBL_MIN; } // Finds min value in an unsorted array template static inline T ImMinArray(const T* values, int count) { T m = values[0]; for (int i = 1; i < count; ++i) { if (values[i] < m) { m = values[i]; } } return m; } // Finds the max value in an unsorted array template static inline T ImMaxArray(const T* values, int count) { T m = values[0]; for (int i = 1; i < count; ++i) { if (values[i] > m) { m = values[i]; } } return m; } // Finds the min and max value in an unsorted array template static inline void ImMinMaxArray(const T* values, int count, T* min_out, T* max_out) { T Min = values[0]; T Max = values[0]; for (int i = 1; i < count; ++i) { if (values[i] < Min) { Min = values[i]; } if (values[i] > Max) { Max = values[i]; } } *min_out = Min; *max_out = Max; } // Finds the sim of an array template static inline T ImSum(const T* values, int count) { T sum = 0; for (int i = 0; i < count; ++i) sum += values[i]; return sum; } // Finds the mean of an array template static inline double ImMean(const T* values, int count) { double den = 1.0 / count; double mu = 0; for (int i = 0; i < count; ++i) mu += (double)values[i] * den; return mu; } // Finds the sample standard deviation of an array template static inline double ImStdDev(const T* values, int count) { double den = 1.0 / (count - 1.0); double mu = ImMean(values, count); double x = 0; for (int i = 0; i < count; ++i) x += ((double)values[i] - mu) * ((double)values[i] - mu) * den; return sqrt(x); } // Mix color a and b by factor s in [0 256] static inline ImU32 ImMixU32(ImU32 a, ImU32 b, ImU32 s) { #ifdef IMPLOT_MIX64 const ImU32 af = 256-s; const ImU32 bf = s; const ImU64 al = (a & 0x00ff00ff) | (((ImU64)(a & 0xff00ff00)) << 24); const ImU64 bl = (b & 0x00ff00ff) | (((ImU64)(b & 0xff00ff00)) << 24); const ImU64 mix = (al * af + bl * bf); return ((mix >> 32) & 0xff00ff00) | ((mix & 0xff00ff00) >> 8); #else const ImU32 af = 256-s; const ImU32 bf = s; const ImU32 al = (a & 0x00ff00ff); const ImU32 ah = (a & 0xff00ff00) >> 8; const ImU32 bl = (b & 0x00ff00ff); const ImU32 bh = (b & 0xff00ff00) >> 8; const ImU32 ml = (al * af + bl * bf); const ImU32 mh = (ah * af + bh * bf); return (mh & 0xff00ff00) | ((ml & 0xff00ff00) >> 8); #endif } // Lerp across an array of 32-bit collors given t in [0.0 1.0] static inline ImU32 ImLerpU32(const ImU32* colors, int size, float t) { int i1 = (int)((size - 1 ) * t); int i2 = i1 + 1; if (i2 == size || size == 1) return colors[i1]; float den = 1.0f / (size - 1); float t1 = i1 * den; float t2 = i2 * den; float tr = ImRemap01(t, t1, t2); return ImMixU32(colors[i1], colors[i2], (ImU32)(tr*256)); } // Set alpha channel of 32-bit color from float in range [0.0 1.0] static inline ImU32 ImAlphaU32(ImU32 col, float alpha) { return col & ~((ImU32)((1.0f-alpha)*255)< static inline bool ImOverlaps(T min_a, T max_a, T min_b, T max_b) { return min_a <= max_b && min_b <= max_a; } //----------------------------------------------------------------------------- // [SECTION] ImPlot Enums //----------------------------------------------------------------------------- typedef int ImPlotTimeUnit; // -> enum ImPlotTimeUnit_ typedef int ImPlotDateFmt; // -> enum ImPlotDateFmt_ typedef int ImPlotTimeFmt; // -> enum ImPlotTimeFmt_ enum ImPlotTimeUnit_ { ImPlotTimeUnit_Us, // microsecond ImPlotTimeUnit_Ms, // millisecond ImPlotTimeUnit_S, // second ImPlotTimeUnit_Min, // minute ImPlotTimeUnit_Hr, // hour ImPlotTimeUnit_Day, // day ImPlotTimeUnit_Mo, // month ImPlotTimeUnit_Yr, // year ImPlotTimeUnit_COUNT }; enum ImPlotDateFmt_ { // default [ ISO 8601 ] ImPlotDateFmt_None = 0, ImPlotDateFmt_DayMo, // 10/3 [ --10-03 ] ImPlotDateFmt_DayMoYr, // 10/3/91 [ 1991-10-03 ] ImPlotDateFmt_MoYr, // Oct 1991 [ 1991-10 ] ImPlotDateFmt_Mo, // Oct [ --10 ] ImPlotDateFmt_Yr // 1991 [ 1991 ] }; enum ImPlotTimeFmt_ { // default [ 24 Hour Clock ] ImPlotTimeFmt_None = 0, ImPlotTimeFmt_Us, // .428 552 [ .428 552 ] ImPlotTimeFmt_SUs, // :29.428 552 [ :29.428 552 ] ImPlotTimeFmt_SMs, // :29.428 [ :29.428 ] ImPlotTimeFmt_S, // :29 [ :29 ] ImPlotTimeFmt_MinSMs, // 21:29.428 [ 21:29.428 ] ImPlotTimeFmt_HrMinSMs, // 7:21:29.428pm [ 19:21:29.428 ] ImPlotTimeFmt_HrMinS, // 7:21:29pm [ 19:21:29 ] ImPlotTimeFmt_HrMin, // 7:21pm [ 19:21 ] ImPlotTimeFmt_Hr // 7pm [ 19:00 ] }; //----------------------------------------------------------------------------- // [SECTION] Callbacks //----------------------------------------------------------------------------- typedef void (*ImPlotLocator)(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data); //----------------------------------------------------------------------------- // [SECTION] Structs //----------------------------------------------------------------------------- // Combined date/time format spec struct ImPlotDateTimeSpec { ImPlotDateTimeSpec() {} ImPlotDateTimeSpec(ImPlotDateFmt date_fmt, ImPlotTimeFmt time_fmt, bool use_24_hr_clk = false, bool use_iso_8601 = false) { Date = date_fmt; Time = time_fmt; UseISO8601 = use_iso_8601; Use24HourClock = use_24_hr_clk; } ImPlotDateFmt Date; ImPlotTimeFmt Time; bool UseISO8601; bool Use24HourClock; }; // Two part timestamp struct. struct ImPlotTime { time_t S; // second part int Us; // microsecond part ImPlotTime() { S = 0; Us = 0; } ImPlotTime(time_t s, int us = 0) { S = s + us / 1000000; Us = us % 1000000; } void RollOver() { S = S + Us / 1000000; Us = Us % 1000000; } double ToDouble() const { return (double)S + (double)Us / 1000000.0; } static ImPlotTime FromDouble(double t) { return ImPlotTime((time_t)t, (int)(t * 1000000 - floor(t) * 1000000)); } }; static inline ImPlotTime operator+(const ImPlotTime& lhs, const ImPlotTime& rhs) { return ImPlotTime(lhs.S + rhs.S, lhs.Us + rhs.Us); } static inline ImPlotTime operator-(const ImPlotTime& lhs, const ImPlotTime& rhs) { return ImPlotTime(lhs.S - rhs.S, lhs.Us - rhs.Us); } static inline bool operator==(const ImPlotTime& lhs, const ImPlotTime& rhs) { return lhs.S == rhs.S && lhs.Us == rhs.Us; } static inline bool operator<(const ImPlotTime& lhs, const ImPlotTime& rhs) { return lhs.S == rhs.S ? lhs.Us < rhs.Us : lhs.S < rhs.S; } static inline bool operator>(const ImPlotTime& lhs, const ImPlotTime& rhs) { return rhs < lhs; } static inline bool operator<=(const ImPlotTime& lhs, const ImPlotTime& rhs) { return lhs < rhs || lhs == rhs; } static inline bool operator>=(const ImPlotTime& lhs, const ImPlotTime& rhs) { return lhs > rhs || lhs == rhs; } // Colormap data storage struct ImPlotColormapData { ImVector Keys; ImVector KeyCounts; ImVector KeyOffsets; ImVector Tables; ImVector TableSizes; ImVector TableOffsets; ImGuiTextBuffer Text; ImVector TextOffsets; ImVector Quals; ImGuiStorage Map; int Count; ImPlotColormapData() { Count = 0; } int Append(const char* name, const ImU32* keys, int count, bool qual) { if (GetIndex(name) != -1) return -1; KeyOffsets.push_back(Keys.size()); KeyCounts.push_back(count); Keys.reserve(Keys.size()+count); for (int i = 0; i < count; ++i) Keys.push_back(keys[i]); TextOffsets.push_back(Text.size()); Text.append(name, name + strlen(name) + 1); Quals.push_back(qual); ImGuiID id = ImHashStr(name); int idx = Count++; Map.SetInt(id,idx); _AppendTable(idx); return idx; } void _AppendTable(ImPlotColormap cmap) { int key_count = GetKeyCount(cmap); const ImU32* keys = GetKeys(cmap); int off = Tables.size(); TableOffsets.push_back(off); if (IsQual(cmap)) { Tables.reserve(key_count); for (int i = 0; i < key_count; ++i) Tables.push_back(keys[i]); TableSizes.push_back(key_count); } else { int max_size = 255 * (key_count-1) + 1; Tables.reserve(off + max_size); // ImU32 last = keys[0]; // Tables.push_back(last); // int n = 1; for (int i = 0; i < key_count-1; ++i) { for (int s = 0; s < 255; ++s) { ImU32 a = keys[i]; ImU32 b = keys[i+1]; ImU32 c = ImMixU32(a,b,s); // if (c != last) { Tables.push_back(c); // last = c; // n++; // } } } ImU32 c = keys[key_count-1]; // if (c != last) { Tables.push_back(c); // n++; // } // TableSizes.push_back(n); TableSizes.push_back(max_size); } } void RebuildTables() { Tables.resize(0); TableSizes.resize(0); TableOffsets.resize(0); for (int i = 0; i < Count; ++i) _AppendTable(i); } inline bool IsQual(ImPlotColormap cmap) const { return Quals[cmap]; } inline const char* GetName(ImPlotColormap cmap) const { return cmap < Count ? Text.Buf.Data + TextOffsets[cmap] : nullptr; } inline ImPlotColormap GetIndex(const char* name) const { ImGuiID key = ImHashStr(name); return Map.GetInt(key,-1); } inline const ImU32* GetKeys(ImPlotColormap cmap) const { return &Keys[KeyOffsets[cmap]]; } inline int GetKeyCount(ImPlotColormap cmap) const { return KeyCounts[cmap]; } inline ImU32 GetKeyColor(ImPlotColormap cmap, int idx) const { return Keys[KeyOffsets[cmap]+idx]; } inline void SetKeyColor(ImPlotColormap cmap, int idx, ImU32 value) { Keys[KeyOffsets[cmap]+idx] = value; RebuildTables(); } inline const ImU32* GetTable(ImPlotColormap cmap) const { return &Tables[TableOffsets[cmap]]; } inline int GetTableSize(ImPlotColormap cmap) const { return TableSizes[cmap]; } inline ImU32 GetTableColor(ImPlotColormap cmap, int idx) const { return Tables[TableOffsets[cmap]+idx]; } inline ImU32 LerpTable(ImPlotColormap cmap, float t) const { int off = TableOffsets[cmap]; int siz = TableSizes[cmap]; int idx = Quals[cmap] ? ImClamp((int)(siz*t),0,siz-1) : (int)((siz - 1) * t + 0.5f); return Tables[off + idx]; } }; // ImPlotPoint with positive/negative error values struct ImPlotPointError { double X, Y, Neg, Pos; ImPlotPointError(double x, double y, double neg, double pos) { X = x; Y = y; Neg = neg; Pos = pos; } }; // Interior plot label/annotation struct ImPlotAnnotation { ImVec2 Pos; ImVec2 Offset; ImU32 ColorBg; ImU32 ColorFg; int TextOffset; bool Clamp; ImPlotAnnotation() { ColorBg = ColorFg = 0; TextOffset = 0; Clamp = false; } }; // Collection of plot labels struct ImPlotAnnotationCollection { ImVector Annotations; ImGuiTextBuffer TextBuffer; int Size; ImPlotAnnotationCollection() { Reset(); } void AppendV(const ImVec2& pos, const ImVec2& off, ImU32 bg, ImU32 fg, bool clamp, const char* fmt, va_list args) IM_FMTLIST(7) { ImPlotAnnotation an; an.Pos = pos; an.Offset = off; an.ColorBg = bg; an.ColorFg = fg; an.TextOffset = TextBuffer.size(); an.Clamp = clamp; Annotations.push_back(an); TextBuffer.appendfv(fmt, args); const char nul[] = ""; TextBuffer.append(nul,nul+1); Size++; } void Append(const ImVec2& pos, const ImVec2& off, ImU32 bg, ImU32 fg, bool clamp, const char* fmt, ...) IM_FMTARGS(7) { va_list args; va_start(args, fmt); AppendV(pos, off, bg, fg, clamp, fmt, args); va_end(args); } const char* GetText(int idx) { return TextBuffer.Buf.Data + Annotations[idx].TextOffset; } void Reset() { Annotations.shrink(0); TextBuffer.Buf.shrink(0); Size = 0; } }; struct ImPlotTag { ImAxis Axis; double Value; ImU32 ColorBg; ImU32 ColorFg; int TextOffset; }; struct ImPlotTagCollection { ImVector Tags; ImGuiTextBuffer TextBuffer; int Size; ImPlotTagCollection() { Reset(); } void AppendV(ImAxis axis, double value, ImU32 bg, ImU32 fg, const char* fmt, va_list args) IM_FMTLIST(6) { ImPlotTag tag; tag.Axis = axis; tag.Value = value; tag.ColorBg = bg; tag.ColorFg = fg; tag.TextOffset = TextBuffer.size(); Tags.push_back(tag); TextBuffer.appendfv(fmt, args); const char nul[] = ""; TextBuffer.append(nul,nul+1); Size++; } void Append(ImAxis axis, double value, ImU32 bg, ImU32 fg, const char* fmt, ...) IM_FMTARGS(6) { va_list args; va_start(args, fmt); AppendV(axis, value, bg, fg, fmt, args); va_end(args); } const char* GetText(int idx) { return TextBuffer.Buf.Data + Tags[idx].TextOffset; } void Reset() { Tags.shrink(0); TextBuffer.Buf.shrink(0); Size = 0; } }; // Tick mark info struct ImPlotTick { double PlotPos; float PixelPos; ImVec2 LabelSize; int TextOffset; bool Major; bool ShowLabel; int Level; int Idx; ImPlotTick(double value, bool major, int level, bool show_label) { PixelPos = 0; PlotPos = value; Major = major; ShowLabel = show_label; Level = level; TextOffset = -1; } }; // Collection of ticks struct ImPlotTicker { ImVector Ticks; ImGuiTextBuffer TextBuffer; ImVec2 MaxSize; ImVec2 LateSize; int Levels; ImPlotTicker() { Reset(); } ImPlotTick& AddTick(double value, bool major, int level, bool show_label, const char* label) { ImPlotTick tick(value, major, level, show_label); if (show_label && label != nullptr) { tick.TextOffset = TextBuffer.size(); TextBuffer.append(label, label + strlen(label) + 1); tick.LabelSize = ImGui::CalcTextSize(TextBuffer.Buf.Data + tick.TextOffset); } return AddTick(tick); } ImPlotTick& AddTick(double value, bool major, int level, bool show_label, ImPlotFormatter formatter, void* data) { ImPlotTick tick(value, major, level, show_label); if (show_label && formatter != nullptr) { char buff[IMPLOT_LABEL_MAX_SIZE]; tick.TextOffset = TextBuffer.size(); formatter(tick.PlotPos, buff, sizeof(buff), data); TextBuffer.append(buff, buff + strlen(buff) + 1); tick.LabelSize = ImGui::CalcTextSize(TextBuffer.Buf.Data + tick.TextOffset); } return AddTick(tick); } inline ImPlotTick& AddTick(ImPlotTick tick) { if (tick.ShowLabel) { MaxSize.x = tick.LabelSize.x > MaxSize.x ? tick.LabelSize.x : MaxSize.x; MaxSize.y = tick.LabelSize.y > MaxSize.y ? tick.LabelSize.y : MaxSize.y; } tick.Idx = Ticks.size(); Ticks.push_back(tick); return Ticks.back(); } const char* GetText(int idx) const { return TextBuffer.Buf.Data + Ticks[idx].TextOffset; } const char* GetText(const ImPlotTick& tick) { return GetText(tick.Idx); } void OverrideSizeLate(const ImVec2& size) { LateSize.x = size.x > LateSize.x ? size.x : LateSize.x; LateSize.y = size.y > LateSize.y ? size.y : LateSize.y; } void Reset() { Ticks.shrink(0); TextBuffer.Buf.shrink(0); MaxSize = LateSize; LateSize = ImVec2(0,0); Levels = 1; } int TickCount() const { return Ticks.Size; } }; // Axis state information that must persist after EndPlot struct ImPlotAxis { ImGuiID ID; ImPlotAxisFlags Flags; ImPlotAxisFlags PreviousFlags; ImPlotRange Range; ImPlotCond RangeCond; ImPlotScale Scale; ImPlotRange FitExtents; ImPlotAxis* OrthoAxis; ImPlotRange ConstraintRange; ImPlotRange ConstraintZoom; ImPlotTicker Ticker; ImPlotFormatter Formatter; void* FormatterData; char FormatSpec[16]; ImPlotLocator Locator; double* LinkedMin; double* LinkedMax; int PickerLevel; ImPlotTime PickerTimeMin, PickerTimeMax; ImPlotTransform TransformForward; ImPlotTransform TransformInverse; void* TransformData; float PixelMin, PixelMax; double ScaleMin, ScaleMax; double ScaleToPixel; float Datum1, Datum2; ImRect HoverRect; int LabelOffset; ImU32 ColorMaj, ColorMin, ColorTick, ColorTxt, ColorBg, ColorHov, ColorAct, ColorHiLi; bool Enabled; bool Vertical; bool FitThisFrame; bool HasRange; bool HasFormatSpec; bool ShowDefaultTicks; bool Hovered; bool Held; ImPlotAxis() { ID = 0; Flags = PreviousFlags = ImPlotAxisFlags_None; Range.Min = 0; Range.Max = 1; Scale = ImPlotScale_Linear; TransformForward = TransformInverse = nullptr; TransformData = nullptr; FitExtents.Min = HUGE_VAL; FitExtents.Max = -HUGE_VAL; OrthoAxis = nullptr; ConstraintRange = ImPlotRange(-INFINITY,INFINITY); ConstraintZoom = ImPlotRange(DBL_MIN,INFINITY); LinkedMin = LinkedMax = nullptr; PickerLevel = 0; Datum1 = Datum2 = 0; PixelMin = PixelMax = 0; LabelOffset = -1; ColorMaj = ColorMin = ColorTick = ColorTxt = ColorBg = ColorHov = ColorAct = 0; ColorHiLi = IM_COL32_BLACK_TRANS; Formatter = nullptr; FormatterData = nullptr; Locator = nullptr; Enabled = Hovered = Held = FitThisFrame = HasRange = HasFormatSpec = false; ShowDefaultTicks = true; } inline void Reset() { Enabled = false; Scale = ImPlotScale_Linear; TransformForward = TransformInverse = nullptr; TransformData = nullptr; LabelOffset = -1; HasFormatSpec = false; Formatter = nullptr; FormatterData = nullptr; Locator = nullptr; ShowDefaultTicks = true; FitThisFrame = false; FitExtents.Min = HUGE_VAL; FitExtents.Max = -HUGE_VAL; OrthoAxis = nullptr; ConstraintRange = ImPlotRange(-INFINITY,INFINITY); ConstraintZoom = ImPlotRange(DBL_MIN,INFINITY); Ticker.Reset(); } inline bool SetMin(double _min, bool force=false) { if (!force && IsLockedMin()) return false; _min = ImConstrainNan(ImConstrainInf(_min)); if (_min < ConstraintRange.Min) _min = ConstraintRange.Min; double z = Range.Max - _min; if (z < ConstraintZoom.Min) _min = Range.Max - ConstraintZoom.Min; if (z > ConstraintZoom.Max) _min = Range.Max - ConstraintZoom.Max; if (_min >= Range.Max) return false; Range.Min = _min; PickerTimeMin = ImPlotTime::FromDouble(Range.Min); UpdateTransformCache(); return true; }; inline bool SetMax(double _max, bool force=false) { if (!force && IsLockedMax()) return false; _max = ImConstrainNan(ImConstrainInf(_max)); if (_max > ConstraintRange.Max) _max = ConstraintRange.Max; double z = _max - Range.Min; if (z < ConstraintZoom.Min) _max = Range.Min + ConstraintZoom.Min; if (z > ConstraintZoom.Max) _max = Range.Min + ConstraintZoom.Max; if (_max <= Range.Min) return false; Range.Max = _max; PickerTimeMax = ImPlotTime::FromDouble(Range.Max); UpdateTransformCache(); return true; }; inline void SetRange(double v1, double v2) { Range.Min = ImMin(v1,v2); Range.Max = ImMax(v1,v2); Constrain(); PickerTimeMin = ImPlotTime::FromDouble(Range.Min); PickerTimeMax = ImPlotTime::FromDouble(Range.Max); UpdateTransformCache(); } inline void SetRange(const ImPlotRange& range) { SetRange(range.Min, range.Max); } inline void SetAspect(double unit_per_pix) { double new_size = unit_per_pix * PixelSize(); double delta = (new_size - Range.Size()) * 0.5; if (IsLocked()) return; else if (IsLockedMin() && !IsLockedMax()) SetRange(Range.Min, Range.Max + 2*delta); else if (!IsLockedMin() && IsLockedMax()) SetRange(Range.Min - 2*delta, Range.Max); else SetRange(Range.Min - delta, Range.Max + delta); } inline float PixelSize() const { return ImAbs(PixelMax - PixelMin); } inline double GetAspect() const { return Range.Size() / PixelSize(); } inline void Constrain() { Range.Min = ImConstrainNan(ImConstrainInf(Range.Min)); Range.Max = ImConstrainNan(ImConstrainInf(Range.Max)); if (Range.Min < ConstraintRange.Min) Range.Min = ConstraintRange.Min; if (Range.Max > ConstraintRange.Max) Range.Max = ConstraintRange.Max; double z = Range.Size(); if (z < ConstraintZoom.Min) { double delta = (ConstraintZoom.Min - z) * 0.5; Range.Min -= delta; Range.Max += delta; } if (z > ConstraintZoom.Max) { double delta = (z - ConstraintZoom.Max) * 0.5; Range.Min += delta; Range.Max -= delta; } if (Range.Max <= Range.Min) Range.Max = Range.Min + DBL_EPSILON; } inline void UpdateTransformCache() { ScaleToPixel = (PixelMax - PixelMin) / Range.Size(); if (TransformForward != nullptr) { ScaleMin = TransformForward(Range.Min, TransformData); ScaleMax = TransformForward(Range.Max, TransformData); } else { ScaleMin = Range.Min; ScaleMax = Range.Max; } } inline float PlotToPixels(double plt) const { if (TransformForward != nullptr) { double s = TransformForward(plt, TransformData); double t = (s - ScaleMin) / (ScaleMax - ScaleMin); plt = Range.Min + Range.Size() * t; } return (float)(PixelMin + ScaleToPixel * (plt - Range.Min)); } inline double PixelsToPlot(float pix) const { double plt = (pix - PixelMin) / ScaleToPixel + Range.Min; if (TransformInverse != nullptr) { double t = (plt - Range.Min) / Range.Size(); double s = t * (ScaleMax - ScaleMin) + ScaleMin; plt = TransformInverse(s, TransformData); } return plt; } inline void ExtendFit(double v) { if (!ImNanOrInf(v) && v >= ConstraintRange.Min && v <= ConstraintRange.Max) { FitExtents.Min = v < FitExtents.Min ? v : FitExtents.Min; FitExtents.Max = v > FitExtents.Max ? v : FitExtents.Max; } } inline void ExtendFitWith(ImPlotAxis& alt, double v, double v_alt) { if (ImHasFlag(Flags, ImPlotAxisFlags_RangeFit) && !alt.Range.Contains(v_alt)) return; if (!ImNanOrInf(v) && v >= ConstraintRange.Min && v <= ConstraintRange.Max) { FitExtents.Min = v < FitExtents.Min ? v : FitExtents.Min; FitExtents.Max = v > FitExtents.Max ? v : FitExtents.Max; } } inline void ApplyFit(float padding) { const double ext_size = FitExtents.Size() * 0.5; FitExtents.Min -= ext_size * padding; FitExtents.Max += ext_size * padding; if (!IsLockedMin() && !ImNanOrInf(FitExtents.Min)) Range.Min = FitExtents.Min; if (!IsLockedMax() && !ImNanOrInf(FitExtents.Max)) Range.Max = FitExtents.Max; if (ImAlmostEqual(Range.Min, Range.Max)) { Range.Max += 0.5; Range.Min -= 0.5; } Constrain(); UpdateTransformCache(); } inline bool HasLabel() const { return LabelOffset != -1 && !ImHasFlag(Flags, ImPlotAxisFlags_NoLabel); } inline bool HasGridLines() const { return !ImHasFlag(Flags, ImPlotAxisFlags_NoGridLines); } inline bool HasTickLabels() const { return !ImHasFlag(Flags, ImPlotAxisFlags_NoTickLabels); } inline bool HasTickMarks() const { return !ImHasFlag(Flags, ImPlotAxisFlags_NoTickMarks); } inline bool WillRender() const { return Enabled && (HasGridLines() || HasTickLabels() || HasTickMarks()); } inline bool IsOpposite() const { return ImHasFlag(Flags, ImPlotAxisFlags_Opposite); } inline bool IsInverted() const { return ImHasFlag(Flags, ImPlotAxisFlags_Invert); } inline bool IsForeground() const { return ImHasFlag(Flags, ImPlotAxisFlags_Foreground); } inline bool IsAutoFitting() const { return ImHasFlag(Flags, ImPlotAxisFlags_AutoFit); } inline bool CanInitFit() const { return !ImHasFlag(Flags, ImPlotAxisFlags_NoInitialFit) && !HasRange && !LinkedMin && !LinkedMax; } inline bool IsRangeLocked() const { return HasRange && RangeCond == ImPlotCond_Always; } inline bool IsLockedMin() const { return !Enabled || IsRangeLocked() || ImHasFlag(Flags, ImPlotAxisFlags_LockMin); } inline bool IsLockedMax() const { return !Enabled || IsRangeLocked() || ImHasFlag(Flags, ImPlotAxisFlags_LockMax); } inline bool IsLocked() const { return IsLockedMin() && IsLockedMax(); } inline bool IsInputLockedMin() const { return IsLockedMin() || IsAutoFitting(); } inline bool IsInputLockedMax() const { return IsLockedMax() || IsAutoFitting(); } inline bool IsInputLocked() const { return IsLocked() || IsAutoFitting(); } inline bool HasMenus() const { return !ImHasFlag(Flags, ImPlotAxisFlags_NoMenus); } inline bool IsPanLocked(bool increasing) { if (ImHasFlag(Flags, ImPlotAxisFlags_PanStretch)) { return IsInputLocked(); } else { if (IsLockedMin() || IsLockedMax() || IsAutoFitting()) return false; if (increasing) return Range.Max == ConstraintRange.Max; else return Range.Min == ConstraintRange.Min; } } void PushLinks() { if (LinkedMin) { *LinkedMin = Range.Min; } if (LinkedMax) { *LinkedMax = Range.Max; } } void PullLinks() { if (LinkedMin) { SetMin(*LinkedMin,true); } if (LinkedMax) { SetMax(*LinkedMax,true); } } }; // Align plots group data struct ImPlotAlignmentData { bool Vertical; float PadA; float PadB; float PadAMax; float PadBMax; ImPlotAlignmentData() { Vertical = true; PadA = PadB = PadAMax = PadBMax = 0; } void Begin() { PadAMax = PadBMax = 0; } void Update(float& pad_a, float& pad_b, float& delta_a, float& delta_b) { float bak_a = pad_a; float bak_b = pad_b; if (PadAMax < pad_a) { PadAMax = pad_a; } if (PadBMax < pad_b) { PadBMax = pad_b; } if (pad_a < PadA) { pad_a = PadA; delta_a = pad_a - bak_a; } else { delta_a = 0; } if (pad_b < PadB) { pad_b = PadB; delta_b = pad_b - bak_b; } else { delta_b = 0; } } void End() { PadA = PadAMax; PadB = PadBMax; } void Reset() { PadA = PadB = PadAMax = PadBMax = 0; } }; // State information for Plot items struct ImPlotItem { ImGuiID ID; ImU32 Color; ImRect LegendHoverRect; int NameOffset; bool Show; bool LegendHovered; bool SeenThisFrame; ImPlotItem() { ID = 0; Color = IM_COL32_WHITE; NameOffset = -1; Show = true; SeenThisFrame = false; LegendHovered = false; } ~ImPlotItem() { ID = 0; } }; // Holds Legend state struct ImPlotLegend { ImPlotLegendFlags Flags; ImPlotLegendFlags PreviousFlags; ImPlotLocation Location; ImPlotLocation PreviousLocation; ImVector Indices; ImGuiTextBuffer Labels; ImRect Rect; bool Hovered; bool Held; bool CanGoInside; ImPlotLegend() { Flags = PreviousFlags = ImPlotLegendFlags_None; CanGoInside = true; Hovered = Held = false; Location = PreviousLocation = ImPlotLocation_NorthWest; } void Reset() { Indices.shrink(0); Labels.Buf.shrink(0); } }; // Holds Items and Legend data struct ImPlotItemGroup { ImGuiID ID; ImPlotLegend Legend; ImPool ItemPool; int ColormapIdx; ImPlotItemGroup() { ID = 0; ColormapIdx = 0; } int GetItemCount() const { return ItemPool.GetBufSize(); } ImGuiID GetItemID(const char* label_id) { return ImGui::GetID(label_id); /* GetIDWithSeed */ } ImPlotItem* GetItem(ImGuiID id) { return ItemPool.GetByKey(id); } ImPlotItem* GetItem(const char* label_id) { return GetItem(GetItemID(label_id)); } ImPlotItem* GetOrAddItem(ImGuiID id) { return ItemPool.GetOrAddByKey(id); } ImPlotItem* GetItemByIndex(int i) { return ItemPool.GetByIndex(i); } int GetItemIndex(ImPlotItem* item) { return ItemPool.GetIndex(item); } int GetLegendCount() const { return Legend.Indices.size(); } ImPlotItem* GetLegendItem(int i) { return ItemPool.GetByIndex(Legend.Indices[i]); } const char* GetLegendLabel(int i) { return Legend.Labels.Buf.Data + GetLegendItem(i)->NameOffset; } void Reset() { ItemPool.Clear(); Legend.Reset(); ColormapIdx = 0; } }; // Holds Plot state information that must persist after EndPlot struct ImPlotPlot { ImGuiID ID; ImPlotFlags Flags; ImPlotFlags PreviousFlags; ImPlotLocation MouseTextLocation; ImPlotMouseTextFlags MouseTextFlags; ImPlotAxis Axes[ImAxis_COUNT]; ImGuiTextBuffer TextBuffer; ImPlotItemGroup Items; ImAxis CurrentX; ImAxis CurrentY; ImRect FrameRect; ImRect CanvasRect; ImRect PlotRect; ImRect AxesRect; ImRect SelectRect; ImVec2 SelectStart; int TitleOffset; bool JustCreated; bool Initialized; bool SetupLocked; bool FitThisFrame; bool Hovered; bool Held; bool Selecting; bool Selected; bool ContextLocked; ImPlotPlot() { Flags = PreviousFlags = ImPlotFlags_None; for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) XAxis(i).Vertical = false; for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) YAxis(i).Vertical = true; SelectStart = ImVec2(0,0); CurrentX = ImAxis_X1; CurrentY = ImAxis_Y1; MouseTextLocation = ImPlotLocation_South | ImPlotLocation_East; MouseTextFlags = ImPlotMouseTextFlags_None; TitleOffset = -1; JustCreated = true; Initialized = SetupLocked = FitThisFrame = false; Hovered = Held = Selected = Selecting = ContextLocked = false; } inline bool IsInputLocked() const { for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) { if (!XAxis(i).IsInputLocked()) return false; } for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) { if (!YAxis(i).IsInputLocked()) return false; } return true; } inline void ClearTextBuffer() { TextBuffer.Buf.shrink(0); } inline void SetTitle(const char* title) { if (title && ImGui::FindRenderedTextEnd(title, nullptr) != title) { TitleOffset = TextBuffer.size(); TextBuffer.append(title, title + strlen(title) + 1); } else { TitleOffset = -1; } } inline bool HasTitle() const { return TitleOffset != -1 && !ImHasFlag(Flags, ImPlotFlags_NoTitle); } inline const char* GetTitle() const { return TextBuffer.Buf.Data + TitleOffset; } inline ImPlotAxis& XAxis(int i) { return Axes[ImAxis_X1 + i]; } inline const ImPlotAxis& XAxis(int i) const { return Axes[ImAxis_X1 + i]; } inline ImPlotAxis& YAxis(int i) { return Axes[ImAxis_Y1 + i]; } inline const ImPlotAxis& YAxis(int i) const { return Axes[ImAxis_Y1 + i]; } inline int EnabledAxesX() { int cnt = 0; for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) cnt += XAxis(i).Enabled; return cnt; } inline int EnabledAxesY() { int cnt = 0; for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) cnt += YAxis(i).Enabled; return cnt; } inline void SetAxisLabel(ImPlotAxis& axis, const char* label) { if (label && ImGui::FindRenderedTextEnd(label, nullptr) != label) { axis.LabelOffset = TextBuffer.size(); TextBuffer.append(label, label + strlen(label) + 1); } else { axis.LabelOffset = -1; } } inline const char* GetAxisLabel(const ImPlotAxis& axis) const { return TextBuffer.Buf.Data + axis.LabelOffset; } }; // Holds subplot data that must persist after EndSubplot struct ImPlotSubplot { ImGuiID ID; ImPlotSubplotFlags Flags; ImPlotSubplotFlags PreviousFlags; ImPlotItemGroup Items; int Rows; int Cols; int CurrentIdx; ImRect FrameRect; ImRect GridRect; ImVec2 CellSize; ImVector RowAlignmentData; ImVector ColAlignmentData; ImVector RowRatios; ImVector ColRatios; ImVector RowLinkData; ImVector ColLinkData; float TempSizes[2]; bool FrameHovered; bool HasTitle; ImPlotSubplot() { ID = 0; Flags = PreviousFlags = ImPlotSubplotFlags_None; Rows = Cols = CurrentIdx = 0; FrameHovered = false; Items.Legend.Location = ImPlotLocation_North; Items.Legend.Flags = ImPlotLegendFlags_Horizontal|ImPlotLegendFlags_Outside; Items.Legend.CanGoInside = false; TempSizes[0] = TempSizes[1] = 0; FrameHovered = false; HasTitle = false; } }; // Temporary data storage for upcoming plot struct ImPlotNextPlotData { ImPlotCond RangeCond[ImAxis_COUNT]; ImPlotRange Range[ImAxis_COUNT]; bool HasRange[ImAxis_COUNT]; bool Fit[ImAxis_COUNT]; double* LinkedMin[ImAxis_COUNT]; double* LinkedMax[ImAxis_COUNT]; ImPlotNextPlotData() { Reset(); } void Reset() { for (int i = 0; i < ImAxis_COUNT; ++i) { HasRange[i] = false; Fit[i] = false; LinkedMin[i] = LinkedMax[i] = nullptr; } } }; // Temporary data storage for upcoming item struct ImPlotNextItemData { ImVec4 Colors[5]; // ImPlotCol_Line, ImPlotCol_Fill, ImPlotCol_MarkerOutline, ImPlotCol_MarkerFill, ImPlotCol_ErrorBar float LineWeight; ImPlotMarker Marker; float MarkerSize; float MarkerWeight; float FillAlpha; float ErrorBarSize; float ErrorBarWeight; float DigitalBitHeight; float DigitalBitGap; bool RenderLine; bool RenderFill; bool RenderMarkerLine; bool RenderMarkerFill; bool HasHidden; bool Hidden; ImPlotCond HiddenCond; ImPlotNextItemData() { Reset(); } void Reset() { for (int i = 0; i < 5; ++i) Colors[i] = IMPLOT_AUTO_COL; LineWeight = MarkerSize = MarkerWeight = FillAlpha = ErrorBarSize = ErrorBarWeight = DigitalBitHeight = DigitalBitGap = IMPLOT_AUTO; Marker = IMPLOT_AUTO; HasHidden = Hidden = false; } }; // Holds state information that must persist between calls to BeginPlot()/EndPlot() struct ImPlotContext { // Plot States ImPool Plots; ImPool Subplots; ImPlotPlot* CurrentPlot; ImPlotSubplot* CurrentSubplot; ImPlotItemGroup* CurrentItems; ImPlotItem* CurrentItem; ImPlotItem* PreviousItem; // Tick Marks and Labels ImPlotTicker CTicker; // Annotation and Tabs ImPlotAnnotationCollection Annotations; ImPlotTagCollection Tags; // Flags bool ChildWindowMade; // Style and Colormaps ImPlotStyle Style; ImVector ColorModifiers; ImVector StyleModifiers; ImPlotColormapData ColormapData; ImVector ColormapModifiers; // Time tm Tm; // Temp data for general use ImVector TempDouble1, TempDouble2; ImVector TempInt1; // Misc int DigitalPlotItemCnt; int DigitalPlotOffset; ImPlotNextPlotData NextPlotData; ImPlotNextItemData NextItemData; ImPlotInputMap InputMap; bool OpenContextThisFrame; ImGuiTextBuffer MousePosStringBuilder; ImPlotItemGroup* SortItems; // Align plots ImPool AlignmentData; ImPlotAlignmentData* CurrentAlignmentH; ImPlotAlignmentData* CurrentAlignmentV; }; //----------------------------------------------------------------------------- // [SECTION] Internal API // No guarantee of forward compatibility here! //----------------------------------------------------------------------------- namespace ImPlot { //----------------------------------------------------------------------------- // [SECTION] Context Utils //----------------------------------------------------------------------------- // Initializes an ImPlotContext IMPLOT_API void Initialize(ImPlotContext* ctx); // Resets an ImPlot context for the next call to BeginPlot IMPLOT_API void ResetCtxForNextPlot(ImPlotContext* ctx); // Resets an ImPlot context for the next call to BeginAlignedPlots IMPLOT_API void ResetCtxForNextAlignedPlots(ImPlotContext* ctx); // Resets an ImPlot context for the next call to BeginSubplot IMPLOT_API void ResetCtxForNextSubplot(ImPlotContext* ctx); //----------------------------------------------------------------------------- // [SECTION] Plot Utils //----------------------------------------------------------------------------- // Gets a plot from the current ImPlotContext IMPLOT_API ImPlotPlot* GetPlot(const char* title); // Gets the current plot from the current ImPlotContext IMPLOT_API ImPlotPlot* GetCurrentPlot(); // Busts the cache for every plot in the current context IMPLOT_API void BustPlotCache(); // Shows a plot's context menu. IMPLOT_API void ShowPlotContextMenu(ImPlotPlot& plot); //----------------------------------------------------------------------------- // [SECTION] Setup Utils //----------------------------------------------------------------------------- // Lock Setup and call SetupFinish if necessary. static inline void SetupLock() { ImPlotContext& gp = *GImPlot; if (!gp.CurrentPlot->SetupLocked) SetupFinish(); gp.CurrentPlot->SetupLocked = true; } //----------------------------------------------------------------------------- // [SECTION] Subplot Utils //----------------------------------------------------------------------------- // Advances to next subplot IMPLOT_API void SubplotNextCell(); // Shows a subplot's context menu. IMPLOT_API void ShowSubplotsContextMenu(ImPlotSubplot& subplot); //----------------------------------------------------------------------------- // [SECTION] Item Utils //----------------------------------------------------------------------------- // Begins a new item. Returns false if the item should not be plotted. Pushes PlotClipRect. IMPLOT_API bool BeginItem(const char* label_id, ImPlotItemFlags flags=0, ImPlotCol recolor_from=IMPLOT_AUTO); // Same as above but with fitting functionality. template bool BeginItemEx(const char* label_id, const _Fitter& fitter, ImPlotItemFlags flags=0, ImPlotCol recolor_from=IMPLOT_AUTO) { if (BeginItem(label_id, flags, recolor_from)) { ImPlotPlot& plot = *GetCurrentPlot(); if (plot.FitThisFrame && !ImHasFlag(flags, ImPlotItemFlags_NoFit)) fitter.Fit(plot.Axes[plot.CurrentX], plot.Axes[plot.CurrentY]); return true; } return false; } // Ends an item (call only if BeginItem returns true). Pops PlotClipRect. IMPLOT_API void EndItem(); // Register or get an existing item from the current plot. IMPLOT_API ImPlotItem* RegisterOrGetItem(const char* label_id, ImPlotItemFlags flags, bool* just_created = nullptr); // Get a plot item from the current plot. IMPLOT_API ImPlotItem* GetItem(const char* label_id); // Gets the current item. IMPLOT_API ImPlotItem* GetCurrentItem(); // Busts the cache for every item for every plot in the current context. IMPLOT_API void BustItemCache(); //----------------------------------------------------------------------------- // [SECTION] Axis Utils //----------------------------------------------------------------------------- // Returns true if any enabled axis is locked from user input. static inline bool AnyAxesInputLocked(ImPlotAxis* axes, int count) { for (int i = 0; i < count; ++i) { if (axes[i].Enabled && axes[i].IsInputLocked()) return true; } return false; } // Returns true if all enabled axes are locked from user input. static inline bool AllAxesInputLocked(ImPlotAxis* axes, int count) { for (int i = 0; i < count; ++i) { if (axes[i].Enabled && !axes[i].IsInputLocked()) return false; } return true; } static inline bool AnyAxesHeld(ImPlotAxis* axes, int count) { for (int i = 0; i < count; ++i) { if (axes[i].Enabled && axes[i].Held) return true; } return false; } static inline bool AnyAxesHovered(ImPlotAxis* axes, int count) { for (int i = 0; i < count; ++i) { if (axes[i].Enabled && axes[i].Hovered) return true; } return false; } // Returns true if the user has requested data to be fit. static inline bool FitThisFrame() { return GImPlot->CurrentPlot->FitThisFrame; } // Extends the current plot's axes so that it encompasses a vertical line at x static inline void FitPointX(double x) { ImPlotPlot& plot = *GetCurrentPlot(); ImPlotAxis& x_axis = plot.Axes[plot.CurrentX]; x_axis.ExtendFit(x); } // Extends the current plot's axes so that it encompasses a horizontal line at y static inline void FitPointY(double y) { ImPlotPlot& plot = *GetCurrentPlot(); ImPlotAxis& y_axis = plot.Axes[plot.CurrentY]; y_axis.ExtendFit(y); } // Extends the current plot's axes so that it encompasses point p static inline void FitPoint(const ImPlotPoint& p) { ImPlotPlot& plot = *GetCurrentPlot(); ImPlotAxis& x_axis = plot.Axes[plot.CurrentX]; ImPlotAxis& y_axis = plot.Axes[plot.CurrentY]; x_axis.ExtendFitWith(y_axis, p.x, p.y); y_axis.ExtendFitWith(x_axis, p.y, p.x); } // Returns true if two ranges overlap static inline bool RangesOverlap(const ImPlotRange& r1, const ImPlotRange& r2) { return r1.Min <= r2.Max && r2.Min <= r1.Max; } // Shows an axis's context menu. IMPLOT_API void ShowAxisContextMenu(ImPlotAxis& axis, ImPlotAxis* equal_axis, bool time_allowed = false); //----------------------------------------------------------------------------- // [SECTION] Legend Utils //----------------------------------------------------------------------------- // Gets the position of an inner rect that is located inside of an outer rect according to an ImPlotLocation and padding amount. IMPLOT_API ImVec2 GetLocationPos(const ImRect& outer_rect, const ImVec2& inner_size, ImPlotLocation location, const ImVec2& pad = ImVec2(0,0)); // Calculates the bounding box size of a legend IMPLOT_API ImVec2 CalcLegendSize(ImPlotItemGroup& items, const ImVec2& pad, const ImVec2& spacing, bool vertical); // Renders legend entries into a bounding box IMPLOT_API bool ShowLegendEntries(ImPlotItemGroup& items, const ImRect& legend_bb, bool interactable, const ImVec2& pad, const ImVec2& spacing, bool vertical, ImDrawList& DrawList); // Shows an alternate legend for the plot identified by #title_id, outside of the plot frame (can be called before or after of Begin/EndPlot but must occur in the same ImGui window!). IMPLOT_API void ShowAltLegend(const char* title_id, bool vertical = true, const ImVec2 size = ImVec2(0,0), bool interactable = true); // Shows an legends's context menu. IMPLOT_API bool ShowLegendContextMenu(ImPlotLegend& legend, bool visible); //----------------------------------------------------------------------------- // [SECTION] Label Utils //----------------------------------------------------------------------------- // Create a a string label for a an axis value IMPLOT_API void LabelAxisValue(const ImPlotAxis& axis, double value, char* buff, int size, bool round = false); //----------------------------------------------------------------------------- // [SECTION] Styling Utils //----------------------------------------------------------------------------- // Get styling data for next item (call between Begin/EndItem) static inline const ImPlotNextItemData& GetItemData() { return GImPlot->NextItemData; } // Returns true if a color is set to be automatically determined static inline bool IsColorAuto(const ImVec4& col) { return col.w == -1; } // Returns true if a style color is set to be automatically determined static inline bool IsColorAuto(ImPlotCol idx) { return IsColorAuto(GImPlot->Style.Colors[idx]); } // Returns the automatically deduced style color IMPLOT_API ImVec4 GetAutoColor(ImPlotCol idx); // Returns the style color whether it is automatic or custom set static inline ImVec4 GetStyleColorVec4(ImPlotCol idx) { return IsColorAuto(idx) ? GetAutoColor(idx) : GImPlot->Style.Colors[idx]; } static inline ImU32 GetStyleColorU32(ImPlotCol idx) { return ImGui::ColorConvertFloat4ToU32(GetStyleColorVec4(idx)); } // Draws vertical text. The position is the bottom left of the text rect. IMPLOT_API void AddTextVertical(ImDrawList *DrawList, ImVec2 pos, ImU32 col, const char* text_begin, const char* text_end = nullptr); // Draws multiline horizontal text centered. IMPLOT_API void AddTextCentered(ImDrawList* DrawList, ImVec2 top_center, ImU32 col, const char* text_begin, const char* text_end = nullptr); // Calculates the size of vertical text static inline ImVec2 CalcTextSizeVertical(const char *text) { ImVec2 sz = ImGui::CalcTextSize(text); return ImVec2(sz.y, sz.x); } // Returns white or black text given background color static inline ImU32 CalcTextColor(const ImVec4& bg) { return (bg.x * 0.299f + bg.y * 0.587f + bg.z * 0.114f) > 0.5f ? IM_COL32_BLACK : IM_COL32_WHITE; } static inline ImU32 CalcTextColor(ImU32 bg) { return CalcTextColor(ImGui::ColorConvertU32ToFloat4(bg)); } // Lightens or darkens a color for hover static inline ImU32 CalcHoverColor(ImU32 col) { return ImMixU32(col, CalcTextColor(col), 32); } // Clamps a label position so that it fits a rect defined by Min/Max static inline ImVec2 ClampLabelPos(ImVec2 pos, const ImVec2& size, const ImVec2& Min, const ImVec2& Max) { if (pos.x < Min.x) pos.x = Min.x; if (pos.y < Min.y) pos.y = Min.y; if ((pos.x + size.x) > Max.x) pos.x = Max.x - size.x; if ((pos.y + size.y) > Max.y) pos.y = Max.y - size.y; return pos; } // Returns a color from the Color map given an index >= 0 (modulo will be performed). IMPLOT_API ImU32 GetColormapColorU32(int idx, ImPlotColormap cmap); // Returns the next unused colormap color and advances the colormap. Can be used to skip colors if desired. IMPLOT_API ImU32 NextColormapColorU32(); // Linearly interpolates a color from the current colormap given t between 0 and 1. IMPLOT_API ImU32 SampleColormapU32(float t, ImPlotColormap cmap); // Render a colormap bar IMPLOT_API void RenderColorBar(const ImU32* colors, int size, ImDrawList& DrawList, const ImRect& bounds, bool vert, bool reversed, bool continuous); //----------------------------------------------------------------------------- // [SECTION] Math and Misc Utils //----------------------------------------------------------------------------- // Rounds x to powers of 2,5 and 10 for generating axis labels (from Graphics Gems 1 Chapter 11.2) IMPLOT_API double NiceNum(double x, bool round); // Computes order of magnitude of double. static inline int OrderOfMagnitude(double val) { return val == 0 ? 0 : (int)(floor(log10(fabs(val)))); } // Returns the precision required for a order of magnitude. static inline int OrderToPrecision(int order) { return order > 0 ? 0 : 1 - order; } // Returns a floating point precision to use given a value static inline int Precision(double val) { return OrderToPrecision(OrderOfMagnitude(val)); } // Round a value to a given precision static inline double RoundTo(double val, int prec) { double p = pow(10,(double)prec); return floor(val*p+0.5)/p; } // Returns the intersection point of two lines A and B (assumes they are not parallel!) static inline ImVec2 Intersection(const ImVec2& a1, const ImVec2& a2, const ImVec2& b1, const ImVec2& b2) { float v1 = (a1.x * a2.y - a1.y * a2.x); float v2 = (b1.x * b2.y - b1.y * b2.x); float v3 = ((a1.x - a2.x) * (b1.y - b2.y) - (a1.y - a2.y) * (b1.x - b2.x)); return ImVec2((v1 * (b1.x - b2.x) - v2 * (a1.x - a2.x)) / v3, (v1 * (b1.y - b2.y) - v2 * (a1.y - a2.y)) / v3); } // Fills a buffer with n samples linear interpolated from vmin to vmax template void FillRange(ImVector& buffer, int n, T vmin, T vmax) { buffer.resize(n); T step = (vmax - vmin) / (n - 1); for (int i = 0; i < n; ++i) { buffer[i] = vmin + i * step; } } // Calculate histogram bin counts and widths template static inline void CalculateBins(const T* values, int count, ImPlotBin meth, const ImPlotRange& range, int& bins_out, double& width_out) { switch (meth) { case ImPlotBin_Sqrt: bins_out = (int)ceil(sqrt(count)); break; case ImPlotBin_Sturges: bins_out = (int)ceil(1.0 + log2(count)); break; case ImPlotBin_Rice: bins_out = (int)ceil(2 * cbrt(count)); break; case ImPlotBin_Scott: width_out = 3.49 * ImStdDev(values, count) / cbrt(count); bins_out = (int)round(range.Size() / width_out); break; } width_out = range.Size() / bins_out; } //----------------------------------------------------------------------------- // Time Utils //----------------------------------------------------------------------------- // Returns true if year is leap year (366 days long) static inline bool IsLeapYear(int year) { return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); } // Returns the number of days in a month, accounting for Feb. leap years. #month is zero indexed. static inline int GetDaysInMonth(int year, int month) { static const int days[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; return days[month] + (int)(month == 1 && IsLeapYear(year)); } // Make a UNIX timestamp from a tm struct expressed in UTC time (i.e. GMT timezone). IMPLOT_API ImPlotTime MkGmtTime(struct tm *ptm); // Make a tm struct expressed in UTC time (i.e. GMT timezone) from a UNIX timestamp. IMPLOT_API tm* GetGmtTime(const ImPlotTime& t, tm* ptm); // Make a UNIX timestamp from a tm struct expressed in local time. IMPLOT_API ImPlotTime MkLocTime(struct tm *ptm); // Make a tm struct expressed in local time from a UNIX timestamp. IMPLOT_API tm* GetLocTime(const ImPlotTime& t, tm* ptm); // NB: The following functions only work if there is a current ImPlotContext because the // internal tm struct is owned by the context! They are aware of ImPlotStyle.UseLocalTime. // Make a timestamp from time components. // year[1970-3000], month[0-11], day[1-31], hour[0-23], min[0-59], sec[0-59], us[0,999999] IMPLOT_API ImPlotTime MakeTime(int year, int month = 0, int day = 1, int hour = 0, int min = 0, int sec = 0, int us = 0); // Get year component from timestamp [1970-3000] IMPLOT_API int GetYear(const ImPlotTime& t); // Adds or subtracts time from a timestamp. #count > 0 to add, < 0 to subtract. IMPLOT_API ImPlotTime AddTime(const ImPlotTime& t, ImPlotTimeUnit unit, int count); // Rounds a timestamp down to nearest unit. IMPLOT_API ImPlotTime FloorTime(const ImPlotTime& t, ImPlotTimeUnit unit); // Rounds a timestamp up to the nearest unit. IMPLOT_API ImPlotTime CeilTime(const ImPlotTime& t, ImPlotTimeUnit unit); // Rounds a timestamp up or down to the nearest unit. IMPLOT_API ImPlotTime RoundTime(const ImPlotTime& t, ImPlotTimeUnit unit); // Combines the date of one timestamp with the time-of-day of another timestamp. IMPLOT_API ImPlotTime CombineDateTime(const ImPlotTime& date_part, const ImPlotTime& time_part); // Formats the time part of timestamp t into a buffer according to #fmt IMPLOT_API int FormatTime(const ImPlotTime& t, char* buffer, int size, ImPlotTimeFmt fmt, bool use_24_hr_clk); // Formats the date part of timestamp t into a buffer according to #fmt IMPLOT_API int FormatDate(const ImPlotTime& t, char* buffer, int size, ImPlotDateFmt fmt, bool use_iso_8601); // Formats the time and/or date parts of a timestamp t into a buffer according to #fmt IMPLOT_API int FormatDateTime(const ImPlotTime& t, char* buffer, int size, ImPlotDateTimeSpec fmt); // Shows a date picker widget block (year/month/day). // #level = 0 for day, 1 for month, 2 for year. Modified by user interaction. // #t will be set when a day is clicked and the function will return true. // #t1 and #t2 are optional dates to highlight. IMPLOT_API bool ShowDatePicker(const char* id, int* level, ImPlotTime* t, const ImPlotTime* t1 = nullptr, const ImPlotTime* t2 = nullptr); // Shows a time picker widget block (hour/min/sec). // #t will be set when a new hour, minute, or sec is selected or am/pm is toggled, and the function will return true. IMPLOT_API bool ShowTimePicker(const char* id, ImPlotTime* t); //----------------------------------------------------------------------------- // [SECTION] Transforms //----------------------------------------------------------------------------- static inline double TransformForward_Log10(double v, void*) { v = v <= 0.0 ? DBL_MIN : v; return ImLog10(v); } static inline double TransformInverse_Log10(double v, void*) { return ImPow(10, v); } static inline double TransformForward_SymLog(double v, void*) { return 2.0 * ImAsinh(v / 2.0); } static inline double TransformInverse_SymLog(double v, void*) { return 2.0 * ImSinh(v / 2.0); } static inline double TransformForward_Logit(double v, void*) { v = ImClamp(v, DBL_MIN, 1.0 - DBL_EPSILON); return ImLog10(v / (1 - v)); } static inline double TransformInverse_Logit(double v, void*) { return 1.0 / (1.0 + ImPow(10,-v)); } //----------------------------------------------------------------------------- // [SECTION] Formatters //----------------------------------------------------------------------------- static inline int Formatter_Default(double value, char* buff, int size, void* data) { char* fmt = (char*)data; return ImFormatString(buff, size, fmt, value); } static inline int Formatter_Logit(double value, char* buff, int size, void*) { if (value == 0.5) return ImFormatString(buff,size,"1/2"); else if (value < 0.5) return ImFormatString(buff,size,"%g", value); else return ImFormatString(buff,size,"1 - %g", 1 - value); } struct Formatter_Time_Data { ImPlotTime Time; ImPlotDateTimeSpec Spec; ImPlotFormatter UserFormatter; void* UserFormatterData; }; static inline int Formatter_Time(double, char* buff, int size, void* data) { Formatter_Time_Data* ftd = (Formatter_Time_Data*)data; return FormatDateTime(ftd->Time, buff, size, ftd->Spec); } //------------------------------------------------------------------------------ // [SECTION] Locator //------------------------------------------------------------------------------ void Locator_Default(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data); void Locator_Time(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data); void Locator_Log10(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data); void Locator_SymLog(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data); } // namespace ImPlot ================================================ FILE: Source/External/imgui_tools/implot/implot_items.cpp ================================================ // MIT License // Copyright (c) 2020 Evan Pezent // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // ImPlot v0.14 #define IMGUI_DEFINE_MATH_OPERATORS #include "implot.h" #include "implot_internal.h" //----------------------------------------------------------------------------- // [SECTION] Macros and Defines //----------------------------------------------------------------------------- #define SQRT_1_2 0.70710678118f #define SQRT_3_2 0.86602540378f #ifndef IMPLOT_NO_FORCE_INLINE #ifdef _MSC_VER #define IMPLOT_INLINE __forceinline #elif defined(__GNUC__) #define IMPLOT_INLINE inline __attribute__((__always_inline__)) #elif defined(__CLANG__) #if __has_attribute(__always_inline__) #define IMPLOT_INLINE inline __attribute__((__always_inline__)) #else #define IMPLOT_INLINE inline #endif #else #define IMPLOT_INLINE inline #endif #else #define IMPLOT_INLINE inline #endif #if defined __SSE__ || defined __x86_64__ || defined _M_X64 #ifndef IMGUI_ENABLE_SSE #include #endif static IMPLOT_INLINE float ImInvSqrt(float x) { return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ss(x))); } #else static IMPLOT_INLINE float ImInvSqrt(float x) { return 1.0f / sqrtf(x); } #endif #define IMPLOT_NORMALIZE2F_OVER_ZERO(VX,VY) do { float d2 = VX*VX + VY*VY; if (d2 > 0.0f) { float inv_len = ImInvSqrt(d2); VX *= inv_len; VY *= inv_len; } } while (0) // Support for pre-1.82 versions. Users on 1.82+ can use 0 (default) flags to mean "all corners" but in order to support older versions we are more explicit. #if (IMGUI_VERSION_NUM < 18102) && !defined(ImDrawFlags_RoundCornersAll) #define ImDrawFlags_RoundCornersAll ImDrawCornerFlags_All #endif //----------------------------------------------------------------------------- // [SECTION] Template instantiation utility //----------------------------------------------------------------------------- // By default, templates are instantiated for `float`, `double`, and for the following integer types, which are defined in imgui.h: // signed char ImS8; // 8-bit signed integer // unsigned char ImU8; // 8-bit unsigned integer // signed short ImS16; // 16-bit signed integer // unsigned short ImU16; // 16-bit unsigned integer // signed int ImS32; // 32-bit signed integer == int // unsigned int ImU32; // 32-bit unsigned integer // signed long long ImS64; // 64-bit signed integer // unsigned long long ImU64; // 64-bit unsigned integer // (note: this list does *not* include `long`, `unsigned long` and `long double`) // // You can customize the supported types by defining IMPLOT_CUSTOM_NUMERIC_TYPES at compile time to define your own type list. // As an example, you could use the compile time define given by the line below in order to support only float and double. // -DIMPLOT_CUSTOM_NUMERIC_TYPES="(float)(double)" // In order to support all known C++ types, use: // -DIMPLOT_CUSTOM_NUMERIC_TYPES="(signed char)(unsigned char)(signed short)(unsigned short)(signed int)(unsigned int)(signed long)(unsigned long)(signed long long)(unsigned long long)(float)(double)(long double)" #ifdef IMPLOT_CUSTOM_NUMERIC_TYPES #define IMPLOT_NUMERIC_TYPES IMPLOT_CUSTOM_NUMERIC_TYPES #else #define IMPLOT_NUMERIC_TYPES (ImS8)(ImU8)(ImS16)(ImU16)(ImS32)(ImU32)(ImS64)(ImU64)(float)(double) #endif // CALL_INSTANTIATE_FOR_NUMERIC_TYPES will duplicate the template instantion code `INSTANTIATE_MACRO(T)` on supported types. #define _CAT(x, y) _CAT_(x, y) #define _CAT_(x,y) x ## y #define _INSTANTIATE_FOR_NUMERIC_TYPES(chain) _CAT(_INSTANTIATE_FOR_NUMERIC_TYPES_1 chain, _END) #define _INSTANTIATE_FOR_NUMERIC_TYPES_1(T) INSTANTIATE_MACRO(T); _INSTANTIATE_FOR_NUMERIC_TYPES_2 #define _INSTANTIATE_FOR_NUMERIC_TYPES_2(T) INSTANTIATE_MACRO(T); _INSTANTIATE_FOR_NUMERIC_TYPES_1 #define _INSTANTIATE_FOR_NUMERIC_TYPES_1_END #define _INSTANTIATE_FOR_NUMERIC_TYPES_2_END #define CALL_INSTANTIATE_FOR_NUMERIC_TYPES() _INSTANTIATE_FOR_NUMERIC_TYPES(IMPLOT_NUMERIC_TYPES); namespace ImPlot { //----------------------------------------------------------------------------- // [SECTION] Utils //----------------------------------------------------------------------------- // Calc maximum index size of ImDrawIdx template struct MaxIdx { static const unsigned int Value; }; template <> const unsigned int MaxIdx::Value = 65535; template <> const unsigned int MaxIdx::Value = 4294967295; IMPLOT_INLINE void GetLineRenderProps(const ImDrawList& draw_list, float& half_weight, ImVec2& tex_uv0, ImVec2& tex_uv1) { const bool aa = ImHasFlag(draw_list.Flags, ImDrawListFlags_AntiAliasedLines) && ImHasFlag(draw_list.Flags, ImDrawListFlags_AntiAliasedLinesUseTex); if (aa) { ImVec4 tex_uvs = draw_list._Data->TexUvLines[(int)(half_weight*2)]; tex_uv0 = ImVec2(tex_uvs.x, tex_uvs.y); tex_uv1 = ImVec2(tex_uvs.z, tex_uvs.w); half_weight += 1; } else { tex_uv0 = tex_uv1 = draw_list._Data->TexUvWhitePixel; } } IMPLOT_INLINE void PrimLine(ImDrawList& draw_list, const ImVec2& P1, const ImVec2& P2, float half_weight, ImU32 col, const ImVec2& tex_uv0, const ImVec2 tex_uv1) { float dx = P2.x - P1.x; float dy = P2.y - P1.y; IMPLOT_NORMALIZE2F_OVER_ZERO(dx, dy); dx *= half_weight; dy *= half_weight; draw_list._VtxWritePtr[0].pos.x = P1.x + dy; draw_list._VtxWritePtr[0].pos.y = P1.y - dx; draw_list._VtxWritePtr[0].uv = tex_uv0; draw_list._VtxWritePtr[0].col = col; draw_list._VtxWritePtr[1].pos.x = P2.x + dy; draw_list._VtxWritePtr[1].pos.y = P2.y - dx; draw_list._VtxWritePtr[1].uv = tex_uv0; draw_list._VtxWritePtr[1].col = col; draw_list._VtxWritePtr[2].pos.x = P2.x - dy; draw_list._VtxWritePtr[2].pos.y = P2.y + dx; draw_list._VtxWritePtr[2].uv = tex_uv1; draw_list._VtxWritePtr[2].col = col; draw_list._VtxWritePtr[3].pos.x = P1.x - dy; draw_list._VtxWritePtr[3].pos.y = P1.y + dx; draw_list._VtxWritePtr[3].uv = tex_uv1; draw_list._VtxWritePtr[3].col = col; draw_list._VtxWritePtr += 4; draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx); draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1); draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 2); draw_list._IdxWritePtr[3] = (ImDrawIdx)(draw_list._VtxCurrentIdx); draw_list._IdxWritePtr[4] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 2); draw_list._IdxWritePtr[5] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 3); draw_list._IdxWritePtr += 6; draw_list._VtxCurrentIdx += 4; } IMPLOT_INLINE void PrimRectFill(ImDrawList& draw_list, const ImVec2& Pmin, const ImVec2& Pmax, ImU32 col, const ImVec2& uv) { draw_list._VtxWritePtr[0].pos = Pmin; draw_list._VtxWritePtr[0].uv = uv; draw_list._VtxWritePtr[0].col = col; draw_list._VtxWritePtr[1].pos = Pmax; draw_list._VtxWritePtr[1].uv = uv; draw_list._VtxWritePtr[1].col = col; draw_list._VtxWritePtr[2].pos.x = Pmin.x; draw_list._VtxWritePtr[2].pos.y = Pmax.y; draw_list._VtxWritePtr[2].uv = uv; draw_list._VtxWritePtr[2].col = col; draw_list._VtxWritePtr[3].pos.x = Pmax.x; draw_list._VtxWritePtr[3].pos.y = Pmin.y; draw_list._VtxWritePtr[3].uv = uv; draw_list._VtxWritePtr[3].col = col; draw_list._VtxWritePtr += 4; draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx); draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1); draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 2); draw_list._IdxWritePtr[3] = (ImDrawIdx)(draw_list._VtxCurrentIdx); draw_list._IdxWritePtr[4] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1); draw_list._IdxWritePtr[5] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 3); draw_list._IdxWritePtr += 6; draw_list._VtxCurrentIdx += 4; } IMPLOT_INLINE void PrimRectLine(ImDrawList& draw_list, const ImVec2& Pmin, const ImVec2& Pmax, float weight, ImU32 col, const ImVec2& uv) { draw_list._VtxWritePtr[0].pos.x = Pmin.x; draw_list._VtxWritePtr[0].pos.y = Pmin.y; draw_list._VtxWritePtr[0].uv = uv; draw_list._VtxWritePtr[0].col = col; draw_list._VtxWritePtr[1].pos.x = Pmin.x; draw_list._VtxWritePtr[1].pos.y = Pmax.y; draw_list._VtxWritePtr[1].uv = uv; draw_list._VtxWritePtr[1].col = col; draw_list._VtxWritePtr[2].pos.x = Pmax.x; draw_list._VtxWritePtr[2].pos.y = Pmax.y; draw_list._VtxWritePtr[2].uv = uv; draw_list._VtxWritePtr[2].col = col; draw_list._VtxWritePtr[3].pos.x = Pmax.x; draw_list._VtxWritePtr[3].pos.y = Pmin.y; draw_list._VtxWritePtr[3].uv = uv; draw_list._VtxWritePtr[3].col = col; draw_list._VtxWritePtr[4].pos.x = Pmin.x + weight; draw_list._VtxWritePtr[4].pos.y = Pmin.y + weight; draw_list._VtxWritePtr[4].uv = uv; draw_list._VtxWritePtr[4].col = col; draw_list._VtxWritePtr[5].pos.x = Pmin.x + weight; draw_list._VtxWritePtr[5].pos.y = Pmax.y - weight; draw_list._VtxWritePtr[5].uv = uv; draw_list._VtxWritePtr[5].col = col; draw_list._VtxWritePtr[6].pos.x = Pmax.x - weight; draw_list._VtxWritePtr[6].pos.y = Pmax.y - weight; draw_list._VtxWritePtr[6].uv = uv; draw_list._VtxWritePtr[6].col = col; draw_list._VtxWritePtr[7].pos.x = Pmax.x - weight; draw_list._VtxWritePtr[7].pos.y = Pmin.y + weight; draw_list._VtxWritePtr[7].uv = uv; draw_list._VtxWritePtr[7].col = col; draw_list._VtxWritePtr += 8; draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 0); draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1); draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 5); draw_list._IdxWritePtr += 3; draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 0); draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 5); draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 4); draw_list._IdxWritePtr += 3; draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1); draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 2); draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 6); draw_list._IdxWritePtr += 3; draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1); draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 6); draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 5); draw_list._IdxWritePtr += 3; draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 2); draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 3); draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 7); draw_list._IdxWritePtr += 3; draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 2); draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 7); draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 6); draw_list._IdxWritePtr += 3; draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 3); draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 0); draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 4); draw_list._IdxWritePtr += 3; draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 3); draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 4); draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 7); draw_list._IdxWritePtr += 3; draw_list._VtxCurrentIdx += 8; } //----------------------------------------------------------------------------- // [SECTION] Item Utils //----------------------------------------------------------------------------- ImPlotItem* RegisterOrGetItem(const char* label_id, ImPlotItemFlags flags, bool* just_created) { ImPlotContext& gp = *GImPlot; ImPlotItemGroup& Items = *gp.CurrentItems; ImGuiID id = Items.GetItemID(label_id); if (just_created != nullptr) *just_created = Items.GetItem(id) == nullptr; ImPlotItem* item = Items.GetOrAddItem(id); if (item->SeenThisFrame) return item; item->SeenThisFrame = true; int idx = Items.GetItemIndex(item); item->ID = id; if (!ImHasFlag(flags, ImPlotItemFlags_NoLegend) && ImGui::FindRenderedTextEnd(label_id, nullptr) != label_id) { Items.Legend.Indices.push_back(idx); item->NameOffset = Items.Legend.Labels.size(); Items.Legend.Labels.append(label_id, label_id + strlen(label_id) + 1); } else { item->Show = true; } return item; } ImPlotItem* GetItem(const char* label_id) { ImPlotContext& gp = *GImPlot; return gp.CurrentItems->GetItem(label_id); } bool IsItemHidden(const char* label_id) { ImPlotItem* item = GetItem(label_id); return item != nullptr && !item->Show; } ImPlotItem* GetCurrentItem() { ImPlotContext& gp = *GImPlot; return gp.CurrentItem; } void SetNextLineStyle(const ImVec4& col, float weight) { ImPlotContext& gp = *GImPlot; gp.NextItemData.Colors[ImPlotCol_Line] = col; gp.NextItemData.LineWeight = weight; } void SetNextFillStyle(const ImVec4& col, float alpha) { ImPlotContext& gp = *GImPlot; gp.NextItemData.Colors[ImPlotCol_Fill] = col; gp.NextItemData.FillAlpha = alpha; } void SetNextMarkerStyle(ImPlotMarker marker, float size, const ImVec4& fill, float weight, const ImVec4& outline) { ImPlotContext& gp = *GImPlot; gp.NextItemData.Marker = marker; gp.NextItemData.Colors[ImPlotCol_MarkerFill] = fill; gp.NextItemData.MarkerSize = size; gp.NextItemData.Colors[ImPlotCol_MarkerOutline] = outline; gp.NextItemData.MarkerWeight = weight; } void SetNextErrorBarStyle(const ImVec4& col, float size, float weight) { ImPlotContext& gp = *GImPlot; gp.NextItemData.Colors[ImPlotCol_ErrorBar] = col; gp.NextItemData.ErrorBarSize = size; gp.NextItemData.ErrorBarWeight = weight; } ImVec4 GetLastItemColor() { ImPlotContext& gp = *GImPlot; if (gp.PreviousItem) return ImGui::ColorConvertU32ToFloat4(gp.PreviousItem->Color); return ImVec4(); } void BustItemCache() { ImPlotContext& gp = *GImPlot; for (int p = 0; p < gp.Plots.GetBufSize(); ++p) { ImPlotPlot& plot = *gp.Plots.GetByIndex(p); plot.Items.Reset(); } for (int p = 0; p < gp.Subplots.GetBufSize(); ++p) { ImPlotSubplot& subplot = *gp.Subplots.GetByIndex(p); subplot.Items.Reset(); } } void BustColorCache(const char* plot_title_id) { ImPlotContext& gp = *GImPlot; if (plot_title_id == nullptr) { BustItemCache(); } else { ImGuiID id = ImGui::GetCurrentWindow()->GetID(plot_title_id); ImPlotPlot* plot = gp.Plots.GetByKey(id); if (plot != nullptr) plot->Items.Reset(); else { ImPlotSubplot* subplot = gp.Subplots.GetByKey(id); if (subplot != nullptr) subplot->Items.Reset(); } } } //----------------------------------------------------------------------------- // [SECTION] BeginItem / EndItem //----------------------------------------------------------------------------- static const float ITEM_HIGHLIGHT_LINE_SCALE = 2.0f; static const float ITEM_HIGHLIGHT_MARK_SCALE = 1.25f; // Begins a new item. Returns false if the item should not be plotted. bool BeginItem(const char* label_id, ImPlotItemFlags flags, ImPlotCol recolor_from) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "PlotX() needs to be called between BeginPlot() and EndPlot()!"); SetupLock(); bool just_created; ImPlotItem* item = RegisterOrGetItem(label_id, flags, &just_created); // set current item gp.CurrentItem = item; ImPlotNextItemData& s = gp.NextItemData; // set/override item color if (recolor_from != -1) { if (!IsColorAuto(s.Colors[recolor_from])) item->Color = ImGui::ColorConvertFloat4ToU32(s.Colors[recolor_from]); else if (!IsColorAuto(gp.Style.Colors[recolor_from])) item->Color = ImGui::ColorConvertFloat4ToU32(gp.Style.Colors[recolor_from]); else if (just_created) item->Color = NextColormapColorU32(); } else if (just_created) { item->Color = NextColormapColorU32(); } // hide/show item if (gp.NextItemData.HasHidden) { if (just_created || gp.NextItemData.HiddenCond == ImGuiCond_Always) item->Show = !gp.NextItemData.Hidden; } if (!item->Show) { // reset next item data gp.NextItemData.Reset(); gp.PreviousItem = item; gp.CurrentItem = nullptr; return false; } else { ImVec4 item_color = ImGui::ColorConvertU32ToFloat4(item->Color); // stage next item colors s.Colors[ImPlotCol_Line] = IsColorAuto(s.Colors[ImPlotCol_Line]) ? ( IsColorAuto(ImPlotCol_Line) ? item_color : gp.Style.Colors[ImPlotCol_Line] ) : s.Colors[ImPlotCol_Line]; s.Colors[ImPlotCol_Fill] = IsColorAuto(s.Colors[ImPlotCol_Fill]) ? ( IsColorAuto(ImPlotCol_Fill) ? item_color : gp.Style.Colors[ImPlotCol_Fill] ) : s.Colors[ImPlotCol_Fill]; s.Colors[ImPlotCol_MarkerOutline] = IsColorAuto(s.Colors[ImPlotCol_MarkerOutline]) ? ( IsColorAuto(ImPlotCol_MarkerOutline) ? s.Colors[ImPlotCol_Line] : gp.Style.Colors[ImPlotCol_MarkerOutline] ) : s.Colors[ImPlotCol_MarkerOutline]; s.Colors[ImPlotCol_MarkerFill] = IsColorAuto(s.Colors[ImPlotCol_MarkerFill]) ? ( IsColorAuto(ImPlotCol_MarkerFill) ? s.Colors[ImPlotCol_Line] : gp.Style.Colors[ImPlotCol_MarkerFill] ) : s.Colors[ImPlotCol_MarkerFill]; s.Colors[ImPlotCol_ErrorBar] = IsColorAuto(s.Colors[ImPlotCol_ErrorBar]) ? ( GetStyleColorVec4(ImPlotCol_ErrorBar) ) : s.Colors[ImPlotCol_ErrorBar]; // stage next item style vars s.LineWeight = s.LineWeight < 0 ? gp.Style.LineWeight : s.LineWeight; s.Marker = s.Marker < 0 ? gp.Style.Marker : s.Marker; s.MarkerSize = s.MarkerSize < 0 ? gp.Style.MarkerSize : s.MarkerSize; s.MarkerWeight = s.MarkerWeight < 0 ? gp.Style.MarkerWeight : s.MarkerWeight; s.FillAlpha = s.FillAlpha < 0 ? gp.Style.FillAlpha : s.FillAlpha; s.ErrorBarSize = s.ErrorBarSize < 0 ? gp.Style.ErrorBarSize : s.ErrorBarSize; s.ErrorBarWeight = s.ErrorBarWeight < 0 ? gp.Style.ErrorBarWeight : s.ErrorBarWeight; s.DigitalBitHeight = s.DigitalBitHeight < 0 ? gp.Style.DigitalBitHeight : s.DigitalBitHeight; s.DigitalBitGap = s.DigitalBitGap < 0 ? gp.Style.DigitalBitGap : s.DigitalBitGap; // apply alpha modifier(s) s.Colors[ImPlotCol_Fill].w *= s.FillAlpha; s.Colors[ImPlotCol_MarkerFill].w *= s.FillAlpha; // TODO: this should be separate, if it at all // apply highlight mods if (item->LegendHovered) { if (!ImHasFlag(gp.CurrentItems->Legend.Flags, ImPlotLegendFlags_NoHighlightItem)) { s.LineWeight *= ITEM_HIGHLIGHT_LINE_SCALE; s.MarkerSize *= ITEM_HIGHLIGHT_MARK_SCALE; s.MarkerWeight *= ITEM_HIGHLIGHT_LINE_SCALE; // TODO: how to highlight fills? } if (!ImHasFlag(gp.CurrentItems->Legend.Flags, ImPlotLegendFlags_NoHighlightAxis)) { if (gp.CurrentPlot->EnabledAxesX() > 1) gp.CurrentPlot->Axes[gp.CurrentPlot->CurrentX].ColorHiLi = item->Color; if (gp.CurrentPlot->EnabledAxesY() > 1) gp.CurrentPlot->Axes[gp.CurrentPlot->CurrentY].ColorHiLi = item->Color; } } // set render flags s.RenderLine = s.Colors[ImPlotCol_Line].w > 0 && s.LineWeight > 0; s.RenderFill = s.Colors[ImPlotCol_Fill].w > 0; s.RenderMarkerFill = s.Colors[ImPlotCol_MarkerFill].w > 0; s.RenderMarkerLine = s.Colors[ImPlotCol_MarkerOutline].w > 0 && s.MarkerWeight > 0; // push rendering clip rect PushPlotClipRect(); return true; } } // Ends an item (call only if BeginItem returns true) void EndItem() { ImPlotContext& gp = *GImPlot; // pop rendering clip rect PopPlotClipRect(); // reset next item data gp.NextItemData.Reset(); // set current item gp.PreviousItem = gp.CurrentItem; gp.CurrentItem = nullptr; } //----------------------------------------------------------------------------- // [SECTION] Indexers //----------------------------------------------------------------------------- template IMPLOT_INLINE T IndexData(const T* data, int idx, int count, int offset, int stride) { const int s = ((offset == 0) << 0) | ((stride == sizeof(T)) << 1); switch (s) { case 3 : return data[idx]; case 2 : return data[(offset + idx) % count]; case 1 : return *(const T*)(const void*)((const unsigned char*)data + (size_t)((idx) ) * stride); case 0 : return *(const T*)(const void*)((const unsigned char*)data + (size_t)((offset + idx) % count) * stride); default: return T(0); } } template struct IndexerIdx { IndexerIdx(const T* data, int count, int offset = 0, int stride = sizeof(T)) : Data(data), Count(count), Offset(count ? ImPosMod(offset, count) : 0), Stride(stride) { } template IMPLOT_INLINE double operator()(I idx) const { return (double)IndexData(Data, idx, Count, Offset, Stride); } const T* Data; int Count; int Offset; int Stride; }; template struct IndexerAdd { IndexerAdd(const _Indexer1& indexer1, const _Indexer2& indexer2, double scale1 = 1, double scale2 = 1) : Indexer1(indexer1), Indexer2(indexer2), Scale1(scale1), Scale2(scale2), Count(ImMin(Indexer1.Count, Indexer2.Count)) { } template IMPLOT_INLINE double operator()(I idx) const { return Scale1 * Indexer1(idx) + Scale2 * Indexer2(idx); } const _Indexer1& Indexer1; const _Indexer2& Indexer2; double Scale1; double Scale2; int Count; }; struct IndexerLin { IndexerLin(double m, double b) : M(m), B(b) { } template IMPLOT_INLINE double operator()(I idx) const { return M * idx + B; } const double M; const double B; }; struct IndexerConst { IndexerConst(double ref) : Ref(ref) { } template IMPLOT_INLINE double operator()(I) const { return Ref; } const double Ref; }; //----------------------------------------------------------------------------- // [SECTION] Getters //----------------------------------------------------------------------------- template struct GetterXY { GetterXY(_IndexerX x, _IndexerY y, int count) : IndxerX(x), IndxerY(y), Count(count) { } template IMPLOT_INLINE ImPlotPoint operator()(I idx) const { return ImPlotPoint(IndxerX(idx),IndxerY(idx)); } const _IndexerX IndxerX; const _IndexerY IndxerY; const int Count; }; /// Interprets a user's function pointer as ImPlotPoints struct GetterFuncPtr { GetterFuncPtr(ImPlotGetter getter, void* data, int count) : Getter(getter), Data(data), Count(count) { } template IMPLOT_INLINE ImPlotPoint operator()(I idx) const { return Getter(idx, Data); } ImPlotGetter Getter; void* const Data; const int Count; }; template struct GetterOverrideX { GetterOverrideX(_Getter getter, double x) : Getter(getter), X(x), Count(getter.Count) { } template IMPLOT_INLINE ImPlotPoint operator()(I idx) const { ImPlotPoint p = Getter(idx); p.x = X; return p; } const _Getter Getter; const double X; const int Count; }; template struct GetterOverrideY { GetterOverrideY(_Getter getter, double y) : Getter(getter), Y(y), Count(getter.Count) { } template IMPLOT_INLINE ImPlotPoint operator()(I idx) const { ImPlotPoint p = Getter(idx); p.y = Y; return p; } const _Getter Getter; const double Y; const int Count; }; template struct GetterLoop { GetterLoop(_Getter getter) : Getter(getter), Count(getter.Count + 1) { } template IMPLOT_INLINE ImPlotPoint operator()(I idx) const { idx = idx % (Count - 1); return Getter(idx); } const _Getter Getter; const int Count; }; template struct GetterError { GetterError(const T* xs, const T* ys, const T* neg, const T* pos, int count, int offset, int stride) : Xs(xs), Ys(ys), Neg(neg), Pos(pos), Count(count), Offset(count ? ImPosMod(offset, count) : 0), Stride(stride) { } template IMPLOT_INLINE ImPlotPointError operator()(I idx) const { return ImPlotPointError((double)IndexData(Xs, idx, Count, Offset, Stride), (double)IndexData(Ys, idx, Count, Offset, Stride), (double)IndexData(Neg, idx, Count, Offset, Stride), (double)IndexData(Pos, idx, Count, Offset, Stride)); } const T* const Xs; const T* const Ys; const T* const Neg; const T* const Pos; const int Count; const int Offset; const int Stride; }; //----------------------------------------------------------------------------- // [SECTION] Fitters //----------------------------------------------------------------------------- template struct Fitter1 { Fitter1(const _Getter1& getter) : Getter(getter) { } void Fit(ImPlotAxis& x_axis, ImPlotAxis& y_axis) const { for (int i = 0; i < Getter.Count; ++i) { ImPlotPoint p = Getter(i); x_axis.ExtendFitWith(y_axis, p.x, p.y); y_axis.ExtendFitWith(x_axis, p.y, p.x); } } const _Getter1& Getter; }; template struct FitterX { FitterX(const _Getter1& getter) : Getter(getter) { } void Fit(ImPlotAxis& x_axis, ImPlotAxis&) const { for (int i = 0; i < Getter.Count; ++i) { ImPlotPoint p = Getter(i); x_axis.ExtendFit(p.x); } } const _Getter1& Getter; }; template struct FitterY { FitterY(const _Getter1& getter) : Getter(getter) { } void Fit(ImPlotAxis&, ImPlotAxis& y_axis) const { for (int i = 0; i < Getter.Count; ++i) { ImPlotPoint p = Getter(i); y_axis.ExtendFit(p.y); } } const _Getter1& Getter; }; template struct Fitter2 { Fitter2(const _Getter1& getter1, const _Getter2& getter2) : Getter1(getter1), Getter2(getter2) { } void Fit(ImPlotAxis& x_axis, ImPlotAxis& y_axis) const { for (int i = 0; i < Getter1.Count; ++i) { ImPlotPoint p = Getter1(i); x_axis.ExtendFitWith(y_axis, p.x, p.y); y_axis.ExtendFitWith(x_axis, p.y, p.x); } for (int i = 0; i < Getter2.Count; ++i) { ImPlotPoint p = Getter2(i); x_axis.ExtendFitWith(y_axis, p.x, p.y); y_axis.ExtendFitWith(x_axis, p.y, p.x); } } const _Getter1& Getter1; const _Getter2& Getter2; }; template struct FitterBarV { FitterBarV(const _Getter1& getter1, const _Getter2& getter2, double width) : Getter1(getter1), Getter2(getter2), HalfWidth(width*0.5) { } void Fit(ImPlotAxis& x_axis, ImPlotAxis& y_axis) const { int count = ImMin(Getter1.Count, Getter2.Count); for (int i = 0; i < count; ++i) { ImPlotPoint p1 = Getter1(i); p1.x -= HalfWidth; ImPlotPoint p2 = Getter2(i); p2.x += HalfWidth; x_axis.ExtendFitWith(y_axis, p1.x, p1.y); y_axis.ExtendFitWith(x_axis, p1.y, p1.x); x_axis.ExtendFitWith(y_axis, p2.x, p2.y); y_axis.ExtendFitWith(x_axis, p2.y, p2.x); } } const _Getter1& Getter1; const _Getter2& Getter2; const double HalfWidth; }; template struct FitterBarH { FitterBarH(const _Getter1& getter1, const _Getter2& getter2, double height) : Getter1(getter1), Getter2(getter2), HalfHeight(height*0.5) { } void Fit(ImPlotAxis& x_axis, ImPlotAxis& y_axis) const { int count = ImMin(Getter1.Count, Getter2.Count); for (int i = 0; i < count; ++i) { ImPlotPoint p1 = Getter1(i); p1.y -= HalfHeight; ImPlotPoint p2 = Getter2(i); p2.y += HalfHeight; x_axis.ExtendFitWith(y_axis, p1.x, p1.y); y_axis.ExtendFitWith(x_axis, p1.y, p1.x); x_axis.ExtendFitWith(y_axis, p2.x, p2.y); y_axis.ExtendFitWith(x_axis, p2.y, p2.x); } } const _Getter1& Getter1; const _Getter2& Getter2; const double HalfHeight; }; struct FitterRect { FitterRect(const ImPlotPoint& pmin, const ImPlotPoint& pmax) : Pmin(pmin), Pmax(pmax) { } FitterRect(const ImPlotRect& rect) : FitterRect(rect.Min(), rect.Max()) { } void Fit(ImPlotAxis& x_axis, ImPlotAxis& y_axis) const { x_axis.ExtendFitWith(y_axis, Pmin.x, Pmin.y); y_axis.ExtendFitWith(x_axis, Pmin.y, Pmin.x); x_axis.ExtendFitWith(y_axis, Pmax.x, Pmax.y); y_axis.ExtendFitWith(x_axis, Pmax.y, Pmax.x); } const ImPlotPoint Pmin; const ImPlotPoint Pmax; }; //----------------------------------------------------------------------------- // [SECTION] Transformers //----------------------------------------------------------------------------- struct Transformer1 { Transformer1(double pixMin, double pltMin, double pltMax, double m, double scaMin, double scaMax, ImPlotTransform fwd, void* data) : ScaMin(scaMin), ScaMax(scaMax), PltMin(pltMin), PltMax(pltMax), PixMin(pixMin), M(m), TransformFwd(fwd), TransformData(data) { } template IMPLOT_INLINE float operator()(T p) const { if (TransformFwd != nullptr) { double s = TransformFwd(p, TransformData); double t = (s - ScaMin) / (ScaMax - ScaMin); p = PltMin + (PltMax - PltMin) * t; } return (float)(PixMin + M * (p - PltMin)); } double ScaMin, ScaMax, PltMin, PltMax, PixMin, M; ImPlotTransform TransformFwd; void* TransformData; }; struct Transformer2 { Transformer2(const ImPlotAxis& x_axis, const ImPlotAxis& y_axis) : Tx(x_axis.PixelMin, x_axis.Range.Min, x_axis.Range.Max, x_axis.ScaleToPixel, x_axis.ScaleMin, x_axis.ScaleMax, x_axis.TransformForward, x_axis.TransformData), Ty(y_axis.PixelMin, y_axis.Range.Min, y_axis.Range.Max, y_axis.ScaleToPixel, y_axis.ScaleMin, y_axis.ScaleMax, y_axis.TransformForward, y_axis.TransformData) { } Transformer2(const ImPlotPlot& plot) : Transformer2(plot.Axes[plot.CurrentX], plot.Axes[plot.CurrentY]) { } Transformer2() : Transformer2(*GImPlot->CurrentPlot) { } template IMPLOT_INLINE ImVec2 operator()(const P& plt) const { ImVec2 out; out.x = Tx(plt.x); out.y = Ty(plt.y); return out; } template IMPLOT_INLINE ImVec2 operator()(T x, T y) const { ImVec2 out; out.x = Tx(x); out.y = Ty(y); return out; } Transformer1 Tx; Transformer1 Ty; }; //----------------------------------------------------------------------------- // [SECTION] Renderers //----------------------------------------------------------------------------- struct RendererBase { RendererBase(int prims, int idx_consumed, int vtx_consumed) : Prims(prims), IdxConsumed(idx_consumed), VtxConsumed(vtx_consumed) { } const int Prims; Transformer2 Transformer; const int IdxConsumed; const int VtxConsumed; }; template struct RendererLineStrip : RendererBase { RendererLineStrip(const _Getter& getter, ImU32 col, float weight) : RendererBase(getter.Count - 1, 6, 4), Getter(getter), Col(col), HalfWeight(ImMax(1.0f,weight)*0.5f) { P1 = this->Transformer(Getter(0)); } void Init(ImDrawList& draw_list) const { GetLineRenderProps(draw_list, HalfWeight, UV0, UV1); } IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { ImVec2 P2 = this->Transformer(Getter(prim + 1)); if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) { P1 = P2; return false; } PrimLine(draw_list,P1,P2,HalfWeight,Col,UV0,UV1); P1 = P2; return true; } const _Getter& Getter; const ImU32 Col; mutable float HalfWeight; mutable ImVec2 P1; mutable ImVec2 UV0; mutable ImVec2 UV1; }; template struct RendererLineStripSkip : RendererBase { RendererLineStripSkip(const _Getter& getter, ImU32 col, float weight) : RendererBase(getter.Count - 1, 6, 4), Getter(getter), Col(col), HalfWeight(ImMax(1.0f,weight)*0.5f) { P1 = this->Transformer(Getter(0)); } void Init(ImDrawList& draw_list) const { GetLineRenderProps(draw_list, HalfWeight, UV0, UV1); } IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { ImVec2 P2 = this->Transformer(Getter(prim + 1)); if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) { if (!ImNan(P2.x) && !ImNan(P2.y)) P1 = P2; return false; } PrimLine(draw_list,P1,P2,HalfWeight,Col,UV0,UV1); if (!ImNan(P2.x) && !ImNan(P2.y)) P1 = P2; return true; } const _Getter& Getter; const ImU32 Col; mutable float HalfWeight; mutable ImVec2 P1; mutable ImVec2 UV0; mutable ImVec2 UV1; }; template struct RendererLineSegments1 : RendererBase { RendererLineSegments1(const _Getter& getter, ImU32 col, float weight) : RendererBase(getter.Count / 2, 6, 4), Getter(getter), Col(col), HalfWeight(ImMax(1.0f,weight)*0.5f) { } void Init(ImDrawList& draw_list) const { GetLineRenderProps(draw_list, HalfWeight, UV0, UV1); } IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { ImVec2 P1 = this->Transformer(Getter(prim*2+0)); ImVec2 P2 = this->Transformer(Getter(prim*2+1)); if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) return false; PrimLine(draw_list,P1,P2,HalfWeight,Col,UV0,UV1); return true; } const _Getter& Getter; const ImU32 Col; mutable float HalfWeight; mutable ImVec2 UV0; mutable ImVec2 UV1; }; template struct RendererLineSegments2 : RendererBase { RendererLineSegments2(const _Getter1& getter1, const _Getter2& getter2, ImU32 col, float weight) : RendererBase(ImMin(getter1.Count, getter1.Count), 6, 4), Getter1(getter1), Getter2(getter2), Col(col), HalfWeight(ImMax(1.0f,weight)*0.5f) {} void Init(ImDrawList& draw_list) const { GetLineRenderProps(draw_list, HalfWeight, UV0, UV1); } IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { ImVec2 P1 = this->Transformer(Getter1(prim)); ImVec2 P2 = this->Transformer(Getter2(prim)); if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) return false; PrimLine(draw_list,P1,P2,HalfWeight,Col,UV0,UV1); return true; } const _Getter1& Getter1; const _Getter2& Getter2; const ImU32 Col; mutable float HalfWeight; mutable ImVec2 UV0; mutable ImVec2 UV1; }; template struct RendererBarsFillV : RendererBase { RendererBarsFillV(const _Getter1& getter1, const _Getter2& getter2, ImU32 col, double width) : RendererBase(ImMin(getter1.Count, getter1.Count), 6, 4), Getter1(getter1), Getter2(getter2), Col(col), HalfWidth(width/2) {} void Init(ImDrawList& draw_list) const { UV = draw_list._Data->TexUvWhitePixel; } IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { ImPlotPoint p1 = Getter1(prim); ImPlotPoint p2 = Getter2(prim); p1.x += HalfWidth; p2.x -= HalfWidth; ImVec2 P1 = this->Transformer(p1); ImVec2 P2 = this->Transformer(p2); float width_px = ImAbs(P1.x-P2.x); if (width_px < 1.0f) { P1.x += P1.x > P2.x ? (1-width_px) / 2 : (width_px-1) / 2; P2.x += P2.x > P1.x ? (1-width_px) / 2 : (width_px-1) / 2; } ImVec2 PMin = ImMin(P1, P2); ImVec2 PMax = ImMax(P1, P2); if (!cull_rect.Overlaps(ImRect(PMin, PMax))) return false; PrimRectFill(draw_list,PMin,PMax,Col,UV); return true; } const _Getter1& Getter1; const _Getter2& Getter2; const ImU32 Col; const double HalfWidth; mutable ImVec2 UV; }; template struct RendererBarsFillH : RendererBase { RendererBarsFillH(const _Getter1& getter1, const _Getter2& getter2, ImU32 col, double height) : RendererBase(ImMin(getter1.Count, getter1.Count), 6, 4), Getter1(getter1), Getter2(getter2), Col(col), HalfHeight(height/2) {} void Init(ImDrawList& draw_list) const { UV = draw_list._Data->TexUvWhitePixel; } IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { ImPlotPoint p1 = Getter1(prim); ImPlotPoint p2 = Getter2(prim); p1.y += HalfHeight; p2.y -= HalfHeight; ImVec2 P1 = this->Transformer(p1); ImVec2 P2 = this->Transformer(p2); float height_px = ImAbs(P1.y-P2.y); if (height_px < 1.0f) { P1.y += P1.y > P2.y ? (1-height_px) / 2 : (height_px-1) / 2; P2.y += P2.y > P1.y ? (1-height_px) / 2 : (height_px-1) / 2; } ImVec2 PMin = ImMin(P1, P2); ImVec2 PMax = ImMax(P1, P2); if (!cull_rect.Overlaps(ImRect(PMin, PMax))) return false; PrimRectFill(draw_list,PMin,PMax,Col,UV); return true; } const _Getter1& Getter1; const _Getter2& Getter2; const ImU32 Col; const double HalfHeight; mutable ImVec2 UV; }; template struct RendererBarsLineV : RendererBase { RendererBarsLineV(const _Getter1& getter1, const _Getter2& getter2, ImU32 col, double width, float weight) : RendererBase(ImMin(getter1.Count, getter1.Count), 24, 8), Getter1(getter1), Getter2(getter2), Col(col), HalfWidth(width/2), Weight(weight) {} void Init(ImDrawList& draw_list) const { UV = draw_list._Data->TexUvWhitePixel; } IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { ImPlotPoint p1 = Getter1(prim); ImPlotPoint p2 = Getter2(prim); p1.x += HalfWidth; p2.x -= HalfWidth; ImVec2 P1 = this->Transformer(p1); ImVec2 P2 = this->Transformer(p2); float width_px = ImAbs(P1.x-P2.x); if (width_px < 1.0f) { P1.x += P1.x > P2.x ? (1-width_px) / 2 : (width_px-1) / 2; P2.x += P2.x > P1.x ? (1-width_px) / 2 : (width_px-1) / 2; } ImVec2 PMin = ImMin(P1, P2); ImVec2 PMax = ImMax(P1, P2); if (!cull_rect.Overlaps(ImRect(PMin, PMax))) return false; PrimRectLine(draw_list,PMin,PMax,Weight,Col,UV); return true; } const _Getter1& Getter1; const _Getter2& Getter2; const ImU32 Col; const double HalfWidth; const float Weight; mutable ImVec2 UV; }; template struct RendererBarsLineH : RendererBase { RendererBarsLineH(const _Getter1& getter1, const _Getter2& getter2, ImU32 col, double height, float weight) : RendererBase(ImMin(getter1.Count, getter1.Count), 24, 8), Getter1(getter1), Getter2(getter2), Col(col), HalfHeight(height/2), Weight(weight) {} void Init(ImDrawList& draw_list) const { UV = draw_list._Data->TexUvWhitePixel; } IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { ImPlotPoint p1 = Getter1(prim); ImPlotPoint p2 = Getter2(prim); p1.y += HalfHeight; p2.y -= HalfHeight; ImVec2 P1 = this->Transformer(p1); ImVec2 P2 = this->Transformer(p2); float height_px = ImAbs(P1.y-P2.y); if (height_px < 1.0f) { P1.y += P1.y > P2.y ? (1-height_px) / 2 : (height_px-1) / 2; P2.y += P2.y > P1.y ? (1-height_px) / 2 : (height_px-1) / 2; } ImVec2 PMin = ImMin(P1, P2); ImVec2 PMax = ImMax(P1, P2); if (!cull_rect.Overlaps(ImRect(PMin, PMax))) return false; PrimRectLine(draw_list,PMin,PMax,Weight,Col,UV); return true; } const _Getter1& Getter1; const _Getter2& Getter2; const ImU32 Col; const double HalfHeight; const float Weight; mutable ImVec2 UV; }; template struct RendererStairsPre : RendererBase { RendererStairsPre(const _Getter& getter, ImU32 col, float weight) : RendererBase(getter.Count - 1, 12, 8), Getter(getter), Col(col), HalfWeight(ImMax(1.0f,weight)*0.5f) { P1 = this->Transformer(Getter(0)); } void Init(ImDrawList& draw_list) const { UV = draw_list._Data->TexUvWhitePixel; } IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { ImVec2 P2 = this->Transformer(Getter(prim + 1)); if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) { P1 = P2; return false; } PrimRectFill(draw_list, ImVec2(P1.x - HalfWeight, P1.y), ImVec2(P1.x + HalfWeight, P2.y), Col, UV); PrimRectFill(draw_list, ImVec2(P1.x, P2.y + HalfWeight), ImVec2(P2.x, P2.y - HalfWeight), Col, UV); P1 = P2; return true; } const _Getter& Getter; const ImU32 Col; mutable float HalfWeight; mutable ImVec2 P1; mutable ImVec2 UV; }; template struct RendererStairsPost : RendererBase { RendererStairsPost(const _Getter& getter, ImU32 col, float weight) : RendererBase(getter.Count - 1, 12, 8), Getter(getter), Col(col), HalfWeight(ImMax(1.0f,weight) * 0.5f) { P1 = this->Transformer(Getter(0)); } void Init(ImDrawList& draw_list) const { UV = draw_list._Data->TexUvWhitePixel; } IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { ImVec2 P2 = this->Transformer(Getter(prim + 1)); if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) { P1 = P2; return false; } PrimRectFill(draw_list, ImVec2(P1.x, P1.y + HalfWeight), ImVec2(P2.x, P1.y - HalfWeight), Col, UV); PrimRectFill(draw_list, ImVec2(P2.x - HalfWeight, P2.y), ImVec2(P2.x + HalfWeight, P1.y), Col, UV); P1 = P2; return true; } const _Getter& Getter; const ImU32 Col; mutable float HalfWeight; mutable ImVec2 P1; mutable ImVec2 UV; }; template struct RendererStairsPreShaded : RendererBase { RendererStairsPreShaded(const _Getter& getter, ImU32 col) : RendererBase(getter.Count - 1, 6, 4), Getter(getter), Col(col) { P1 = this->Transformer(Getter(0)); Y0 = this->Transformer(ImPlotPoint(0,0)).y; } void Init(ImDrawList& draw_list) const { UV = draw_list._Data->TexUvWhitePixel; } IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { ImVec2 P2 = this->Transformer(Getter(prim + 1)); ImVec2 PMin(ImMin(P1.x, P2.x), ImMin(Y0, P2.y)); ImVec2 PMax(ImMax(P1.x, P2.x), ImMax(Y0, P2.y)); if (!cull_rect.Overlaps(ImRect(PMin, PMax))) { P1 = P2; return false; } PrimRectFill(draw_list, PMin, PMax, Col, UV); P1 = P2; return true; } const _Getter& Getter; const ImU32 Col; float Y0; mutable ImVec2 P1; mutable ImVec2 UV; }; template struct RendererStairsPostShaded : RendererBase { RendererStairsPostShaded(const _Getter& getter, ImU32 col) : RendererBase(getter.Count - 1, 6, 4), Getter(getter), Col(col) { P1 = this->Transformer(Getter(0)); Y0 = this->Transformer(ImPlotPoint(0,0)).y; } void Init(ImDrawList& draw_list) const { UV = draw_list._Data->TexUvWhitePixel; } IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { ImVec2 P2 = this->Transformer(Getter(prim + 1)); ImVec2 PMin(ImMin(P1.x, P2.x), ImMin(P1.y, Y0)); ImVec2 PMax(ImMax(P1.x, P2.x), ImMax(P1.y, Y0)); if (!cull_rect.Overlaps(ImRect(PMin, PMax))) { P1 = P2; return false; } PrimRectFill(draw_list, PMin, PMax, Col, UV); P1 = P2; return true; } const _Getter& Getter; const ImU32 Col; float Y0; mutable ImVec2 P1; mutable ImVec2 UV; }; template struct RendererShaded : RendererBase { RendererShaded(const _Getter1& getter1, const _Getter2& getter2, ImU32 col) : RendererBase(ImMin(getter1.Count, getter2.Count) - 1, 6, 5), Getter1(getter1), Getter2(getter2), Col(col) { P11 = this->Transformer(Getter1(0)); P12 = this->Transformer(Getter2(0)); } void Init(ImDrawList& draw_list) const { UV = draw_list._Data->TexUvWhitePixel; } IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { ImVec2 P21 = this->Transformer(Getter1(prim+1)); ImVec2 P22 = this->Transformer(Getter2(prim+1)); ImRect rect(ImMin(ImMin(ImMin(P11,P12),P21),P22), ImMax(ImMax(ImMax(P11,P12),P21),P22)); if (!cull_rect.Overlaps(rect)) { P11 = P21; P12 = P22; return false; } const int intersect = (P11.y > P12.y && P22.y > P21.y) || (P12.y > P11.y && P21.y > P22.y); ImVec2 intersection = Intersection(P11,P21,P12,P22); draw_list._VtxWritePtr[0].pos = P11; draw_list._VtxWritePtr[0].uv = UV; draw_list._VtxWritePtr[0].col = Col; draw_list._VtxWritePtr[1].pos = P21; draw_list._VtxWritePtr[1].uv = UV; draw_list._VtxWritePtr[1].col = Col; draw_list._VtxWritePtr[2].pos = intersection; draw_list._VtxWritePtr[2].uv = UV; draw_list._VtxWritePtr[2].col = Col; draw_list._VtxWritePtr[3].pos = P12; draw_list._VtxWritePtr[3].uv = UV; draw_list._VtxWritePtr[3].col = Col; draw_list._VtxWritePtr[4].pos = P22; draw_list._VtxWritePtr[4].uv = UV; draw_list._VtxWritePtr[4].col = Col; draw_list._VtxWritePtr += 5; draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx); draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1 + intersect); draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 3); draw_list._IdxWritePtr[3] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1); draw_list._IdxWritePtr[4] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 4); draw_list._IdxWritePtr[5] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 3 - intersect); draw_list._IdxWritePtr += 6; draw_list._VtxCurrentIdx += 5; P11 = P21; P12 = P22; return true; } const _Getter1& Getter1; const _Getter2& Getter2; const ImU32 Col; mutable ImVec2 P11; mutable ImVec2 P12; mutable ImVec2 UV; }; struct RectC { ImPlotPoint Pos; ImPlotPoint HalfSize; ImU32 Color; }; template struct RendererRectC : RendererBase { RendererRectC(const _Getter& getter) : RendererBase(getter.Count, 6, 4), Getter(getter) {} void Init(ImDrawList& draw_list) const { UV = draw_list._Data->TexUvWhitePixel; } IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { RectC rect = Getter(prim); ImVec2 P1 = this->Transformer(rect.Pos.x - rect.HalfSize.x , rect.Pos.y - rect.HalfSize.y); ImVec2 P2 = this->Transformer(rect.Pos.x + rect.HalfSize.x , rect.Pos.y + rect.HalfSize.y); if ((rect.Color & IM_COL32_A_MASK) == 0 || !cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) return false; PrimRectFill(draw_list,P1,P2,rect.Color,UV); return true; } const _Getter& Getter; mutable ImVec2 UV; }; //----------------------------------------------------------------------------- // [SECTION] RenderPrimitives //----------------------------------------------------------------------------- /// Renders primitive shapes in bulk as efficiently as possible. template void RenderPrimitivesEx(const _Renderer& renderer, ImDrawList& draw_list, const ImRect& cull_rect) { unsigned int prims = renderer.Prims; unsigned int prims_culled = 0; unsigned int idx = 0; renderer.Init(draw_list); while (prims) { // find how many can be reserved up to end of current draw command's limit unsigned int cnt = ImMin(prims, (MaxIdx::Value - draw_list._VtxCurrentIdx) / renderer.VtxConsumed); // make sure at least this many elements can be rendered to avoid situations where at the end of buffer this slow path is not taken all the time if (cnt >= ImMin(64u, prims)) { if (prims_culled >= cnt) prims_culled -= cnt; // reuse previous reservation else { // add more elements to previous reservation draw_list.PrimReserve((cnt - prims_culled) * renderer.IdxConsumed, (cnt - prims_culled) * renderer.VtxConsumed); prims_culled = 0; } } else { if (prims_culled > 0) { draw_list.PrimUnreserve(prims_culled * renderer.IdxConsumed, prims_culled * renderer.VtxConsumed); prims_culled = 0; } cnt = ImMin(prims, (MaxIdx::Value - 0/*draw_list._VtxCurrentIdx*/) / renderer.VtxConsumed); // reserve new draw command draw_list.PrimReserve(cnt * renderer.IdxConsumed, cnt * renderer.VtxConsumed); } prims -= cnt; for (unsigned int ie = idx + cnt; idx != ie; ++idx) { if (!renderer.Render(draw_list, cull_rect, idx)) prims_culled++; } } if (prims_culled > 0) draw_list.PrimUnreserve(prims_culled * renderer.IdxConsumed, prims_culled * renderer.VtxConsumed); } template