From 53a7843a41d91633796ec1df6af0fd3641011973 Mon Sep 17 00:00:00 2001 From: Ian-Woo Kim Date: Sun, 8 Feb 2026 15:21:15 -0500 Subject: [PATCH 1/9] implot3d C++ library nix package included --- flake.nix | 6 ++- imgui-gen/Gen.hs | 1 + nix/implot3d/default.nix | 82 ++++++++++++++++++++++++++++++++++++++++ workspace/cabal.project | 2 + 4 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 nix/implot3d/default.nix diff --git a/flake.nix b/flake.nix index e28c4c4..c9deeb0 100644 --- a/flake.nix +++ b/flake.nix @@ -30,6 +30,9 @@ implot = self.callPackage ./nix/implot/default.nix { frameworks = self.darwin.apple_sdk.frameworks; }; + implot3d = self.callPackage ./nix/implot3d/default.nix { + frameworks = self.darwin.apple_sdk.frameworks; + }; }; in flake-utils.lib.eachDefaultSystem (system: let @@ -58,7 +61,7 @@ ] ++ ( if isEnv - then [p.imgui p.implot] + then [p.imgui p.implot p.implot3d] else [] )); pyenv = @@ -76,6 +79,7 @@ pkgs.cabal-install pkgs.imgui pkgs.implot + pkgs.implot3d pkgs.glfw pkgs.alejandra pkgs.pkgconfig diff --git a/imgui-gen/Gen.hs b/imgui-gen/Gen.hs index 7739cf9..77b1624 100644 --- a/imgui-gen/Gen.hs +++ b/imgui-gen/Gen.hs @@ -126,6 +126,7 @@ cabal = cabal_extrafiles = [], cabal_pkg_config_depends = ["libimgui", "glfw3"], cabal_buildType = Simple + -- NOTE: on macos, we also need ot add "frameworks: OpenGL" } gLFWmonitor :: Class diff --git a/nix/implot3d/default.nix b/nix/implot3d/default.nix new file mode 100644 index 0000000..99565f3 --- /dev/null +++ b/nix/implot3d/default.nix @@ -0,0 +1,82 @@ +{ + stdenv, + lib, + fetchFromGitHub, + pkg-config, + frameworks, + glfw, + imgui, + implot, + libGL ? null, +}: +stdenv.mkDerivation rec { + pname = "implot3d"; + version = "0.3"; + + src = fetchFromGitHub { + owner = "wavewave"; + repo = "implot3d"; + #rev = "v${version}"; + rev = "7b0606a8d949d4997f28c2b376c35fbbecedda46"; + sha256 = "sha256-44BZhLGBheGPW1/m4Rrk9H7OJ1oKbP9IXivVgxuqUVE="; + }; + + nativeBuildInputs = [pkg-config]; + + buildInputs = + [ + glfw + imgui + implot + ] + ++ ( + if stdenv.isDarwin + then [ + frameworks.Cocoa + frameworks.Metal + frameworks.MetalKit + ] + else [libGL] + ); + + buildPhase = + if stdenv.isDarwin + then '' + $CXX -std=c++11 -I. -I./backends -c implot3d.cpp `pkg-config --cflags libimgui libimplot` + $CXX -std=c++11 -I. -I./backends -c implot3d_demo.cpp `pkg-config --cflags libimgui libimplot` + $CXX -std=c++11 -I. -I./backends -c implot3d_items.cpp `pkg-config --cflags libimgui libimplot` + $CXX -std=c++11 -I. -I./backends -c implot3d_meshes.cpp `pkg-config --cflags libimgui libimplot` + $CXX -dynamiclib -undefined suppress -flat_namespace -install_name $out/lib/libimplot3d.dylib -o libimplot3d.dylib implot3d.o implot3d_demo.o implot3d_items.o implot3d_meshes.o + '' + else '' + $CXX -std=c++11 -I. -I./backends -c implot3d.cpp `pkg-config --cflags libimgui libimplot` + $CXX -std=c++11 -I. -I./backends -c implot3d_demo.cpp `pkg-config --cflags libimgui libimplot` + $CXX -std=c++11 -I. -I./backends -c implot3d_items.cpp `pkg-config --cflags libimgui libimplot` + $CXX -std=c++11 -I. -I./backends -c implot3d_meshes.cpp `pkg-config --cflags libimgui libimplot` + $CXX -shared -o libimplot3d.so implot3d.o implot3d_demo.o implot3d_items.o implot3d_meshes.o + ''; + + installPhase = '' + mkdir -p $out/include/implot3d + mkdir -p $out/lib + cp *.h $out/include/implot3d + cp libimplot3d.* $out/lib + + mkdir -p $out/lib/pkgconfig + cat >> $out/lib/pkgconfig/libimplot3d.pc << EOF + Name: libimplot3d + Description: Dear ImPlot3d + Version: ${version} + Libs: -L$out/lib -limplot3d + Cflags: -I$out/include/implot3d + EOF + ''; + + meta = with lib; { + description = "Implot3d"; + homepage = "https://github.com/brenocq/implot3d"; + license = licenses.mit; + maintainers = with maintainers; [ianwookim]; + platforms = platforms.all; + }; +} diff --git a/workspace/cabal.project b/workspace/cabal.project index 863ce93..fc1cf22 100644 --- a/workspace/cabal.project +++ b/workspace/cabal.project @@ -1,3 +1,5 @@ packages: + ./imgui + ./implot ../examples/plot-demo ../examples/draw-demo From a43471e9c699c1202af7891557d797a4403ba38f Mon Sep 17 00:00:00 2001 From: Ian-Woo Kim Date: Sun, 8 Feb 2026 15:33:11 -0500 Subject: [PATCH 2/9] Start implot3d binding generation --- implot-gen/Gen.hs | 4 + implot3d-gen/Gen.hs | 270 ++++++++++++++++++++++++++++++++++++++++ workspace/build.sh | 25 ++-- workspace/cabal.project | 1 + 4 files changed, 289 insertions(+), 11 deletions(-) create mode 100644 implot3d-gen/Gen.hs diff --git a/implot-gen/Gen.hs b/implot-gen/Gen.hs index 62cebc1..8c1bd6b 100644 --- a/implot-gen/Gen.hs +++ b/implot-gen/Gen.hs @@ -335,6 +335,10 @@ extraLib = [] extraDep = [] +-- +-- +-- + data CLIMode = Gen (Maybe FilePath) | DepGraph (Maybe FilePath) diff --git a/implot3d-gen/Gen.hs b/implot3d-gen/Gen.hs new file mode 100644 index 0000000..74f96a0 --- /dev/null +++ b/implot3d-gen/Gen.hs @@ -0,0 +1,270 @@ +{-# LANGUAGE OverloadedStrings #-} + +module Main where + +import qualified Data.Graph as G +import qualified Data.HashMap.Strict as HM +import Data.Maybe (fromMaybe) +import Data.Tree (drawForest) +import FFICXX.Generate.Builder (simpleBuilder) +import FFICXX.Generate.Code.Primitive + ( bool, + bool_, + charpp, + cppclass, + cppclass_, + cppclasscopy_, + cppclassref, + cppclassref_, + cstring, + cstring_, + double, + double_, + float, + float_, + int, + int_, + star, + uint, + uint_, + void_, + voidp, + ) +import FFICXX.Generate.Config + ( FFICXXConfig (..), + SimpleBuilderConfig (..), + ) +import FFICXX.Generate.Dependency.Graph + ( constructDepGraph, + findDepCycles, + gatherHsBootSubmodules, + locateInDepCycles, + ) +import FFICXX.Generate.Type.Cabal (BuildType (..), Cabal (..), CabalName (..)) +import FFICXX.Generate.Type.Class + ( Arg (..), + CTypes (..), + Class (..), + EnumType (..), + Function (..), + ProtectedMethod (..), + TLOrdinary (..), + TLTemplate (..), + TopLevel (..), + Types (..), + Variable (..), + ) +import FFICXX.Generate.Type.Config + ( ModuleUnit (..), + ModuleUnitImports (..), + ModuleUnitMap (..), + modImports, + ) +import FFICXX.Generate.Type.Module (TemplateClassImportHeader (..)) +import FFICXX.Generate.Util.DepGraph (drawDepGraph) +import FFICXX.Runtime.Types (FFISafety (..)) +import qualified Options.Applicative as OA +import System.Directory (getCurrentDirectory) +import System.Environment (getArgs) +import System.FilePath (()) +import System.IO (IOMode (..), hPutStrLn, stdout, withFile) + +------------------------ +-- import from stdcxx -- +------------------------ + +stdcxx_cabal :: Cabal +stdcxx_cabal = + Cabal + { cabal_pkgname = CabalName "stdcxx", + cabal_version = "0.8.0.0", + cabal_cheaderprefix = "STD", + cabal_moduleprefix = "STD", + cabal_additional_c_incs = [], + cabal_additional_c_srcs = [], + cabal_additional_pkgdeps = [], + cabal_license = Nothing, + cabal_licensefile = Nothing, + cabal_extraincludedirs = [], + cabal_extralibdirs = [], + cabal_extrafiles = [], + cabal_pkg_config_depends = [], + cabal_buildType = Simple + } + +deletable :: Class +deletable = + AbstractClass + { class_cabal = stdcxx_cabal, + class_name = "Deletable", + class_parents = [], + class_protected = Protected [], + class_alias = Nothing, + class_funcs = [Destructor Nothing], + class_vars = [], + class_tmpl_funcs = [] + } + +-- +-- from imgui +-- + +imgui_cabal = + Cabal + { cabal_pkgname = CabalName "imgui", + cabal_version = "1.0.0.0", + cabal_cheaderprefix = "ImGui", + cabal_moduleprefix = "ImGui", + cabal_additional_c_incs = [], + cabal_additional_c_srcs = [], + cabal_additional_pkgdeps = [CabalName "stdcxx"], + cabal_license = Just "BSD-3-Clause", + cabal_licensefile = Just "LICENSE", + cabal_extraincludedirs = [], + cabal_extralibdirs = [], + cabal_extrafiles = [], + cabal_pkg_config_depends = ["libimgui", "glfw3"], + cabal_buildType = Simple + } + +imVec2 :: Class +imVec2 = + Class + imgui_cabal + "ImVec2" + [deletable] + mempty + Nothing + [] + [] + [] + False + +-- +-- from implot +-- + +implot_cabal = + Cabal + { cabal_pkgname = CabalName "implot", + cabal_version = "1.0.0.0", + cabal_cheaderprefix = "ImPlot", + cabal_moduleprefix = "ImPlot", + cabal_additional_c_incs = [], + cabal_additional_c_srcs = [], + cabal_additional_pkgdeps = [CabalName "stdcxx", CabalName "imgui"], + cabal_license = Just "BSD-3-Clause", + cabal_licensefile = Just "LICENSE", + cabal_extraincludedirs = [], + cabal_extralibdirs = [], + cabal_extrafiles = [], + cabal_pkg_config_depends = ["libimplot"], + cabal_buildType = Simple + } + +------------------ +-- start implot3d -- +------------------ + +cabal = + Cabal + { cabal_pkgname = CabalName "implot3d", + cabal_version = "1.0.0.0", + cabal_cheaderprefix = "ImPlot3D", + cabal_moduleprefix = "ImPlot3D", + cabal_additional_c_incs = [], + cabal_additional_c_srcs = [], + cabal_additional_pkgdeps = [CabalName "stdcxx", CabalName "imgui", CabalName "implot"], + cabal_license = Just "BSD-3-Clause", + cabal_licensefile = Just "LICENSE", + cabal_extraincludedirs = [], + cabal_extralibdirs = [], + cabal_extrafiles = [], + cabal_pkg_config_depends = ["libimplot3d"], + cabal_buildType = Simple + } + +classes = [] + +enums = [] + +toplevelfunctions = [] + +templates = [] + +headers = [] + +extraLib = [] + +extraDep = [] + +-- +-- +-- + +data CLIMode + = Gen (Maybe FilePath) + | DepGraph (Maybe FilePath) + +genMode :: OA.Mod OA.CommandFields CLIMode +genMode = + OA.command "gen" $ + OA.info + ( Gen + <$> OA.optional + (OA.strOption (OA.long "template" <> OA.short 't' <> OA.help "template directory")) + ) + (OA.progDesc "Generate source code") + +depGraphMode :: OA.Mod OA.CommandFields CLIMode +depGraphMode = + OA.command "depgraph" $ + OA.info + ( DepGraph + <$> OA.optional + (OA.strOption (OA.long "dotfile" <> OA.short 'f' <> OA.help "output dot file")) + ) + (OA.progDesc "Generate dependency graph") + +optsParser :: OA.ParserInfo CLIMode +optsParser = + OA.info + (OA.subparser (genMode <> depGraphMode) OA.<**> OA.helper) + OA.fullDesc + +main :: IO () +main = do + mode <- OA.execParser optsParser + case mode of + Gen mtmplDir -> do + let tmplDir = fromMaybe "../template" mtmplDir + cwd <- getCurrentDirectory + let fficfg = + FFICXXConfig + { fficxxconfig_workingDir = cwd "tmp" "working", + fficxxconfig_installBaseDir = cwd "implot3d", + fficxxconfig_staticFileDir = tmplDir + } + sbcfg = + SimpleBuilderConfig + { sbcTopModule = "ImPlot3D", + sbcModUnitMap = ModuleUnitMap (HM.fromList headers), + sbcCabal = cabal, + sbcClasses = classes, + sbcEnums = enums, + sbcTopLevels = toplevelfunctions, + sbcTemplates = templates, + sbcExtraLibs = extraLib, + sbcCxxOpts = ["-std=c++17"], + sbcExtraDeps = extraDep, + sbcStaticFiles = ["LICENSE"] + } + simpleBuilder fficfg sbcfg + DepGraph mdotFile -> do + let allclasses = fmap Right classes <> fmap (Left . tcihTClass) templates + drawAction h = do + hPutStrLn h $ + drawDepGraph allclasses toplevelfunctions + case mdotFile of + Nothing -> drawAction stdout + Just dotFile -> withFile dotFile WriteMode drawAction diff --git a/workspace/build.sh b/workspace/build.sh index bf049ad..c3d2eef 100755 --- a/workspace/build.sh +++ b/workspace/build.sh @@ -1,14 +1,17 @@ #!/bin/sh ghc ../imgui-gen/Gen.hs -package optparse-applicative && \ - ../imgui-gen/Gen gen --template=../imgui-gen/template && \ - cabal build imgui && \ - ghc ../implot-gen/Gen.hs -package optparse-applicative && \ - ../implot-gen/Gen gen --template=../implot-gen/template && \ - cabal build implot && \ - cabal exec -- ghc demo-imgui-builtin.hs -framework OpenGL -lglfw -lGL -package extra && \ - cabal exec -- ghc demo-implot-builtin.hs -framework OpenGL -lglfw -lGL -package extra && \ - ./demo-imgui-builtin && \ - ./demo-implot-builtin && \ - cabal run draw-demo && \ - cabal run plot-demo +../imgui-gen/Gen gen --template=../imgui-gen/template && \ +ghc ../implot-gen/Gen.hs -package optparse-applicative && \ +../implot-gen/Gen gen --template=../implot-gen/template && \ +ghc ../implot3d-gen/Gen.hs -package optparse-applicative && \ +../implot3d-gen/Gen gen --template=../implot-gen/template && \ +cabal build imgui && \ +cabal build implot && \ +cabal run draw-demo && \ +cabal run plot-demo + +#cabal exec -- ghc demo-imgui-builtin.hs -framework OpenGL -lglfw -lGL -package extra && \ +#cabal exec -- ghc demo-implot-builtin.hs -framework OpenGL -lglfw -lGL -package extra && \ +#./demo-imgui-builtin && \ +#./demo-implot-builtin && \ diff --git a/workspace/cabal.project b/workspace/cabal.project index fc1cf22..7051032 100644 --- a/workspace/cabal.project +++ b/workspace/cabal.project @@ -1,5 +1,6 @@ packages: ./imgui ./implot + ./implot3d ../examples/plot-demo ../examples/draw-demo From ff4abfc834b4b5111652167c40cfc63c9b7ff846 Mon Sep 17 00:00:00 2001 From: Ian-Woo Kim Date: Sun, 8 Feb 2026 15:37:43 -0500 Subject: [PATCH 3/9] start adding plot3d-demo --- examples/plot3d-demo/LICENSE | 0 examples/plot3d-demo/app/Main.hs | 4 ++++ examples/plot3d-demo/plot3d-demo.cabal | 33 ++++++++++++++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 examples/plot3d-demo/LICENSE create mode 100644 examples/plot3d-demo/app/Main.hs create mode 100644 examples/plot3d-demo/plot3d-demo.cabal diff --git a/examples/plot3d-demo/LICENSE b/examples/plot3d-demo/LICENSE new file mode 100644 index 0000000..e69de29 diff --git a/examples/plot3d-demo/app/Main.hs b/examples/plot3d-demo/app/Main.hs new file mode 100644 index 0000000..89ad4b3 --- /dev/null +++ b/examples/plot3d-demo/app/Main.hs @@ -0,0 +1,4 @@ +module Main where + +main :: IO () +main = pure () diff --git a/examples/plot3d-demo/plot3d-demo.cabal b/examples/plot3d-demo/plot3d-demo.cabal new file mode 100644 index 0000000..9eecdb1 --- /dev/null +++ b/examples/plot3d-demo/plot3d-demo.cabal @@ -0,0 +1,33 @@ +cabal-version: 3.4 +name: plot3d-demo +version: 0.1.0.0 +-- synopsis: +-- description: +license: MIT +license-file: LICENSE +author: Ian-Woo Kim +maintainer: ianwookim@gmail.com +-- copyright: +category: Graphics +build-type: Simple +extra-doc-files: CHANGELOG.md +-- extra-source-files: + +common warnings + ghc-options: -Wall + +executable plot3d-demo + import: warnings + main-is: Main.hs + -- other-modules: + -- other-extensions: + build-depends: base ^>=4.18.0.0, + extra, + fficxx-runtime, + imgui, + implot, + implot3d, + random, + stdcxx + hs-source-dirs: app + default-language: GHC2021 From ccb69126b9f56a0356dce7b46419024d69dca274 Mon Sep 17 00:00:00 2001 From: Ian-Woo Kim Date: Sun, 8 Feb 2026 16:09:17 -0500 Subject: [PATCH 4/9] Starting non-trivial GUI thing --- examples/plot3d-demo/app/Main.hs | 120 ++++++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 1 deletion(-) diff --git a/examples/plot3d-demo/app/Main.hs b/examples/plot3d-demo/app/Main.hs index 89ad4b3..6184d09 100644 --- a/examples/plot3d-demo/app/Main.hs +++ b/examples/plot3d-demo/app/Main.hs @@ -1,4 +1,122 @@ +{-# LANGUAGE ForeignFunctionInterface #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} + module Main where +import Control.Monad.Extra (whenM, whileM) +import Data.Bits ((.|.)) +import Data.Foldable (for_, traverse_) +import Data.IORef (IORef, modifyIORef', newIORef, readIORef) +import Data.String (IsString (..)) +import FFICXX.Runtime.Cast (FPtr (..)) +import FFICXX.Runtime.TH (IsCPrimitive (..), TemplateParamInfo (..)) +import Foreign.C.String (CString, newCString, withCString) +import Foreign.C.Types (CBool (..), CDouble (..), CFloat, CInt (..), CUInt (..)) +import Foreign.Marshal.Alloc (alloca) +import Foreign.Marshal.Array (allocaArray) +import Foreign.Marshal.Utils (fromBool, toBool) +import Foreign.Ptr (Ptr, castPtr, nullPtr) +import Foreign.Storable (Storable (..)) +import ImGui +import ImGui.Enum +import ImGui.ImGuiIO.Implementation + ( imGuiIO_ConfigFlags_get, + imGuiIO_ConfigFlags_set, + imGuiIO_DeltaTime_get, + imGuiIO_Framerate_get, + ) +import ImGui.ImVec2.Implementation (imVec2_x_get, imVec2_y_get) +import ImGui.ImVec4.Implementation (imVec4_w_get, imVec4_x_get, imVec4_y_get, imVec4_z_get) +import ImPlot qualified +import ImPlot.Enum +import ImPlot.TH qualified as TH +import ImPlot.Template +import ImPlot3D qualified +import STD.Deletable (delete) +import System.IO.Unsafe (unsafePerformIO) +import System.Random (randomRIO) +import Text.Printf (printf) + +instance IsString CString where + fromString s = unsafePerformIO $ newCString s + +showFramerate :: ImGuiIO -> IO () +showFramerate io = do + begin ("Framerate monitor" :: CString) nullPtr 0 + framerate :: Float <- realToFrac <$> imGuiIO_Framerate_get io + withCString (printf "Application average %.3f ms/frame (%.1f FPS)" (1000.0 / framerate) framerate) $ \c_str -> + textUnformatted c_str + end + main :: IO () -main = pure () +main = do + let glsl_version :: CString + glsl_version = "#version 150" + glfwInit + glfwWindowHint (0x22002 {- GLFW_CONTEXT_VERSION_MAJOR -}) 3 + glfwWindowHint (0x22003 {- GLFW_CONTEXT_VERSION_MINOR -}) 2 + -- 3.2+ only + glfwWindowHint (0x22008 {- GLFW_OPENGL_PROFILE -}) (0x32001 {- GLFW_OPENGL_CORE_PROFILE -}) + -- Required on Mac + glfwWindowHint (0x22006 {- GLFW_OPENGL_FORWARD_COMPAT -}) (1 {- GL_TRUE -}) + window :: GLFWwindow <- + glfwCreateWindow + 1280 + 720 + ("ImPlot3D Haskell Demo" :: CString) + (cast_fptr_to_obj nullPtr :: GLFWmonitor) + (cast_fptr_to_obj nullPtr :: GLFWwindow) + glfwMakeContextCurrent window + -- Enable vsync + glfwSwapInterval 1 + ctxt <- createContext + ImPlot.createImPlotContext + io <- getIO + + -- Setup Dear ImGui style + -- styleColorsDark + styleColorsLight + + -- Setup Platform/Renderer backends + imGui_ImplGlfw_InitForOpenGL window (fromBool True) + imGui_ImplOpenGL3_Init glsl_version + + flags <- imGuiIO_ConfigFlags_get io + -- Enable Keyboard Controls and Gamepad Controls + let flags' = + flags + .|. fromIntegral (fromEnum ImGuiConfigFlags_NavEnableKeyboard) + .|. fromIntegral (fromEnum ImGuiConfigFlags_NavEnableGamepad) + imGuiIO_ConfigFlags_set io flags' + + clear_color <- newImVec4 0.45 0.55 0.60 1.00 + + -- main loop + whileM $ do + glfwPollEvents + -- Start the Dear ImGui frame + imGui_ImplOpenGL3_NewFrame + imGui_ImplGlfw_NewFrame + newFrame + + showFramerate io + render + + alloca $ \p_dispW -> + alloca $ \p_dispH -> do + glfwGetFramebufferSize window p_dispW p_dispH + dispW <- peek p_dispW + dispH <- peek p_dispH + glViewport 0 0 dispW dispH + x <- imVec4_x_get clear_color + y <- imVec4_y_get clear_color + z <- imVec4_z_get clear_color + w <- imVec4_w_get clear_color + glClearColor (x * w) (y * w) (z * w) w + glClear 0x4000 {- GL_COLOR_BUFFER_BIT -} + + imGui_ImplOpenGL3_RenderDrawData =<< getDrawData + glfwSwapBuffers window + + not . toBool <$> glfwWindowShouldClose window From c6f86066b0d27f1f6fb678c0a6ff55f13849097a Mon Sep 17 00:00:00 2001 From: Ian-Woo Kim Date: Sun, 8 Feb 2026 23:15:00 -0500 Subject: [PATCH 5/9] First line plot example, no cheating! --- examples/plot3d-demo/app/Main.hs | 106 ++++++++++++++++++++++--------- implot3d-gen/Gen.hs | 61 +++++++++++++++++- workspace/build.sh | 6 +- workspace/cabal.project | 1 + 4 files changed, 138 insertions(+), 36 deletions(-) diff --git a/examples/plot3d-demo/app/Main.hs b/examples/plot3d-demo/app/Main.hs index 6184d09..851d83c 100644 --- a/examples/plot3d-demo/app/Main.hs +++ b/examples/plot3d-demo/app/Main.hs @@ -1,6 +1,7 @@ {-# LANGUAGE ForeignFunctionInterface #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} +{-# OPTIONS_GHC -ddump-splices #-} module Main where @@ -29,10 +30,10 @@ import ImGui.ImGuiIO.Implementation import ImGui.ImVec2.Implementation (imVec2_x_get, imVec2_y_get) import ImGui.ImVec4.Implementation (imVec4_w_get, imVec4_x_get, imVec4_y_get, imVec4_z_get) import ImPlot qualified -import ImPlot.Enum -import ImPlot.TH qualified as TH -import ImPlot.Template import ImPlot3D qualified +import ImPlot3D.Enum +import ImPlot3D.TH qualified +import ImPlot3D.Template import STD.Deletable (delete) import System.IO.Unsafe (unsafePerformIO) import System.Random (randomRIO) @@ -41,6 +42,18 @@ import Text.Printf (printf) instance IsString CString where fromString s = unsafePerformIO $ newCString s +-- this is a hack. but it's the best up to now. +ImPlot3D.TH.genPlotLine3DInstanceFor + CPrim + ( [t|Ptr CFloat|], + TPInfo + { tpinfoCxxType = "float", + tpinfoCxxHeaders = [], + tpinfoCxxNamespaces = [], + tpinfoSuffix = "float" + } + ) + showFramerate :: ImGuiIO -> IO () showFramerate io = do begin ("Framerate monitor" :: CString) nullPtr 0 @@ -49,6 +62,30 @@ showFramerate io = do textUnformatted c_str end +imPlot3DDemo :: (Ptr CFloat, Ptr CFloat, Ptr CFloat) -> IO () +imPlot3DDemo (px1, py1, pz1) = do + begin ("ImPlot3D Demo" :: CString) nullPtr 0 + whenM (toBool <$> beginMenuBar) $ do + whenM (toBool <$> beginMenu ("Tools" :: CString) (fromBool True)) $ do + menuItem_ ("Metrics" :: CString) ("" :: CString) (fromBool False) (fromBool True) + endMenu + endMenuBar + textUnformatted ("ImPlot3D says olá!" :: CString) + size <- newImVec2 (-1) 0 + whenM (toBool <$> ImPlot3D.beginPlot3D ("Line Plots" :: CString) size (fromIntegral (fromEnum ImPlot3DFlags_None))) $ do + ImPlot3D.setupAxes3D ("x" :: CString) ("y" :: CString) ("z" :: CString) + t <- getTime + for_ [0 .. 1000] $ \i -> do + let x = fromIntegral i * 0.001 + pokeElemOff px1 i x + pokeElemOff py1 i (0.5 + 0.5 * cos (50.0 * (x + realToFrac t / 10.0))) + pokeElemOff pz1 i (0.5 + 0.5 * sin (50.0 * (x + realToFrac t / 10.0))) + ImPlot3D.plotLine3D "f(x)" px1 py1 pz1 1001 + + ImPlot3D.endPlot3D + delete size + end + main :: IO () main = do let glsl_version :: CString @@ -72,6 +109,7 @@ main = do glfwSwapInterval 1 ctxt <- createContext ImPlot.createImPlotContext + ImPlot3D.createContext io <- getIO -- Setup Dear ImGui style @@ -92,31 +130,37 @@ main = do clear_color <- newImVec4 0.45 0.55 0.60 1.00 - -- main loop - whileM $ do - glfwPollEvents - -- Start the Dear ImGui frame - imGui_ImplOpenGL3_NewFrame - imGui_ImplGlfw_NewFrame - newFrame - - showFramerate io - render - - alloca $ \p_dispW -> - alloca $ \p_dispH -> do - glfwGetFramebufferSize window p_dispW p_dispH - dispW <- peek p_dispW - dispH <- peek p_dispH - glViewport 0 0 dispW dispH - x <- imVec4_x_get clear_color - y <- imVec4_y_get clear_color - z <- imVec4_z_get clear_color - w <- imVec4_w_get clear_color - glClearColor (x * w) (y * w) (z * w) w - glClear 0x4000 {- GL_COLOR_BUFFER_BIT -} - - imGui_ImplOpenGL3_RenderDrawData =<< getDrawData - glfwSwapBuffers window - - not . toBool <$> glfwWindowShouldClose window + allocaArray 1001 $ \(px1 :: Ptr CFloat) -> + allocaArray 1001 $ \(py1 :: Ptr CFloat) -> + allocaArray 1001 $ \(pz1 :: Ptr CFloat) -> do + + -- main loop + whileM $ do + glfwPollEvents + -- Start the Dear ImGui frame + imGui_ImplOpenGL3_NewFrame + imGui_ImplGlfw_NewFrame + newFrame + + showFramerate io + -- ImPlot3D.showDemoWindow nullPtr + imPlot3DDemo (px1, py1, pz1) + render + + alloca $ \p_dispW -> + alloca $ \p_dispH -> do + glfwGetFramebufferSize window p_dispW p_dispH + dispW <- peek p_dispW + dispH <- peek p_dispH + glViewport 0 0 dispW dispH + x <- imVec4_x_get clear_color + y <- imVec4_y_get clear_color + z <- imVec4_z_get clear_color + w <- imVec4_w_get clear_color + glClearColor (x * w) (y * w) (z * w) w + glClear 0x4000 {- GL_COLOR_BUFFER_BIT -} + + imGui_ImplOpenGL3_RenderDrawData =<< getDrawData + glfwSwapBuffers window + + not . toBool <$> glfwWindowShouldClose window diff --git a/implot3d-gen/Gen.hs b/implot3d-gen/Gen.hs index 74f96a0..858ffe5 100644 --- a/implot3d-gen/Gen.hs +++ b/implot3d-gen/Gen.hs @@ -184,15 +184,70 @@ cabal = cabal_buildType = Simple } +imPlot3DFlags_ :: EnumType +imPlot3DFlags_ = + EnumType + { enum_name = "ImPlot3DFlags_", + enum_cases = + [ "ImPlot3DFlags_None", + "ImPlot3DFlags_NoTitle", + "ImPlot3DFlags_NoLegend", + "ImPlot3DFlags_NoMouseText", + "ImPlot3DFlags_NoClip", + "ImPlot3DFlags_NoMenus", + "ImPlot3DFlags_Equal", + "ImPlot3DFlags_NoRotate", + "ImPlot3DFlags_NoPan", + "ImPlot3DFlags_NoZoom", + "ImPlot3DFlags_NoInputs", + "ImPlot3DFlags_CanvasOnly" + ], + enum_header = "implot3d.h" + } + classes = [] -enums = [] +enums = + [ imPlot3DFlags_ + ] -toplevelfunctions = [] +toplevelfunctions :: [TopLevel] +toplevelfunctions = + [ TLOrdinary (TopLevelFunction FFIUnsafe void_ "CreateContext" [] (Just "createContext")), + TLOrdinary (TopLevelFunction FFIUnsafe void_ "ShowDemoWindow" [star CTBool "p_open"] (Just "showDemoWindow")), + TLOrdinary (TopLevelFunction FFIUnsafe bool_ "BeginPlot" [cstring "title_id", cppclassref imVec2 "size", int "flags"] (Just "beginPlot3D")), + TLOrdinary (TopLevelFunction FFIUnsafe void_ "EndPlot" [] (Just "EndPlot3D")), + TLOrdinary (TopLevelFunction FFIUnsafe void_ "SetupAxes" [cstring "x_label", cstring "y_label", cstring "z_label"] (Just "setupAxes3D")), + TLTemplate + ( TopLevelTemplateFunction + { topleveltfunc_safety = FFIUnsafe, + topleveltfunc_params = ["t1"], + topleveltfunc_ret = void_, + topleveltfunc_name = "plotLine3D", + topleveltfunc_oname = "ImPlot3D::PlotLine", + topleveltfunc_args = + [ cstring "label_id", + Arg (TemplateParamPointer "t1") "xs", + Arg (TemplateParamPointer "t1") "ys", + Arg (TemplateParamPointer "t1") "zs", + int "count" + ] + } + ) + ] templates = [] -headers = [] +headers = + [ ( MU_TopLevel, + ModuleUnitImports + { muimports_namespaces = ["ImPlot3D"], + muimports_headers = + [ "implot3d.h" + ] + } + ) + ] extraLib = [] diff --git a/workspace/build.sh b/workspace/build.sh index c3d2eef..7f3a4a6 100755 --- a/workspace/build.sh +++ b/workspace/build.sh @@ -5,11 +5,13 @@ ghc ../imgui-gen/Gen.hs -package optparse-applicative && \ ghc ../implot-gen/Gen.hs -package optparse-applicative && \ ../implot-gen/Gen gen --template=../implot-gen/template && \ ghc ../implot3d-gen/Gen.hs -package optparse-applicative && \ -../implot3d-gen/Gen gen --template=../implot-gen/template && \ +../implot3d-gen/Gen gen --template=../implot3d-gen/template && \ cabal build imgui && \ cabal build implot && \ +cabal build implot3d && \ cabal run draw-demo && \ -cabal run plot-demo +cabal run plot-demo && \ +cabal run plot3d-demo #cabal exec -- ghc demo-imgui-builtin.hs -framework OpenGL -lglfw -lGL -package extra && \ #cabal exec -- ghc demo-implot-builtin.hs -framework OpenGL -lglfw -lGL -package extra && \ diff --git a/workspace/cabal.project b/workspace/cabal.project index 7051032..a0d568c 100644 --- a/workspace/cabal.project +++ b/workspace/cabal.project @@ -4,3 +4,4 @@ packages: ./implot3d ../examples/plot-demo ../examples/draw-demo + ../examples/plot3d-demo From 18b5ee916b81f871a1309cf73bfc23ae4cd3abb9 Mon Sep 17 00:00:00 2001 From: Ian-Woo Kim Date: Sun, 8 Feb 2026 23:30:37 -0500 Subject: [PATCH 6/9] wrap raw pointers into Resource --- examples/plot3d-demo/app/Main.hs | 78 ++++++++++++++++++-------------- 1 file changed, 44 insertions(+), 34 deletions(-) diff --git a/examples/plot3d-demo/app/Main.hs b/examples/plot3d-demo/app/Main.hs index 851d83c..9e6d0c4 100644 --- a/examples/plot3d-demo/app/Main.hs +++ b/examples/plot3d-demo/app/Main.hs @@ -86,6 +86,21 @@ imPlot3DDemo (px1, py1, pz1) = do delete size end +data Resource = Resource + { res_array1 :: (Ptr CFloat, Ptr CFloat, Ptr CFloat), + res_disp :: (Ptr CInt, Ptr CInt) + } + +withRes :: (Resource -> IO a) -> IO a +withRes f = + allocaArray 1001 $ \(px1 :: Ptr CFloat) -> + allocaArray 1001 $ \(py1 :: Ptr CFloat) -> + allocaArray 1001 $ \(pz1 :: Ptr CFloat) -> + alloca $ \(p_dispW :: Ptr CInt) -> + alloca $ \(p_dispH :: Ptr CInt) -> + let res = Resource (px1, py1, pz1) (p_dispW, p_dispH) + in f res + main :: IO () main = do let glsl_version :: CString @@ -130,37 +145,32 @@ main = do clear_color <- newImVec4 0.45 0.55 0.60 1.00 - allocaArray 1001 $ \(px1 :: Ptr CFloat) -> - allocaArray 1001 $ \(py1 :: Ptr CFloat) -> - allocaArray 1001 $ \(pz1 :: Ptr CFloat) -> do - - -- main loop - whileM $ do - glfwPollEvents - -- Start the Dear ImGui frame - imGui_ImplOpenGL3_NewFrame - imGui_ImplGlfw_NewFrame - newFrame - - showFramerate io - -- ImPlot3D.showDemoWindow nullPtr - imPlot3DDemo (px1, py1, pz1) - render - - alloca $ \p_dispW -> - alloca $ \p_dispH -> do - glfwGetFramebufferSize window p_dispW p_dispH - dispW <- peek p_dispW - dispH <- peek p_dispH - glViewport 0 0 dispW dispH - x <- imVec4_x_get clear_color - y <- imVec4_y_get clear_color - z <- imVec4_z_get clear_color - w <- imVec4_w_get clear_color - glClearColor (x * w) (y * w) (z * w) w - glClear 0x4000 {- GL_COLOR_BUFFER_BIT -} - - imGui_ImplOpenGL3_RenderDrawData =<< getDrawData - glfwSwapBuffers window - - not . toBool <$> glfwWindowShouldClose window + withRes $ \res -> do + -- main loop + whileM $ do + glfwPollEvents + -- Start the Dear ImGui frame + imGui_ImplOpenGL3_NewFrame + imGui_ImplGlfw_NewFrame + newFrame + + showFramerate io + imPlot3DDemo (res_array1 res) + render + + let (p_dispW, p_dispH) = res_disp res + glfwGetFramebufferSize window p_dispW p_dispH + dispW <- peek p_dispW + dispH <- peek p_dispH + glViewport 0 0 dispW dispH + x <- imVec4_x_get clear_color + y <- imVec4_y_get clear_color + z <- imVec4_z_get clear_color + w <- imVec4_w_get clear_color + glClearColor (x * w) (y * w) (z * w) w + glClear 0x4000 {- GL_COLOR_BUFFER_BIT -} + + imGui_ImplOpenGL3_RenderDrawData =<< getDrawData + glfwSwapBuffers window + + not . toBool <$> glfwWindowShouldClose window From 11f4b159c636cf82d1507e6ee0fa0b02de3217cb Mon Sep 17 00:00:00 2001 From: Ian-Woo Kim Date: Sun, 8 Feb 2026 23:40:01 -0500 Subject: [PATCH 7/9] organizing demos with tab --- examples/plot3d-demo/app/Main.hs | 34 +++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/examples/plot3d-demo/app/Main.hs b/examples/plot3d-demo/app/Main.hs index 9e6d0c4..d9969ae 100644 --- a/examples/plot3d-demo/app/Main.hs +++ b/examples/plot3d-demo/app/Main.hs @@ -62,15 +62,8 @@ showFramerate io = do textUnformatted c_str end -imPlot3DDemo :: (Ptr CFloat, Ptr CFloat, Ptr CFloat) -> IO () -imPlot3DDemo (px1, py1, pz1) = do - begin ("ImPlot3D Demo" :: CString) nullPtr 0 - whenM (toBool <$> beginMenuBar) $ do - whenM (toBool <$> beginMenu ("Tools" :: CString) (fromBool True)) $ do - menuItem_ ("Metrics" :: CString) ("" :: CString) (fromBool False) (fromBool True) - endMenu - endMenuBar - textUnformatted ("ImPlot3D says olá!" :: CString) +demoLinePlot3D :: (Ptr CFloat, Ptr CFloat, Ptr CFloat) -> IO () +demoLinePlot3D (px1, py1, pz1) = do size <- newImVec2 (-1) 0 whenM (toBool <$> ImPlot3D.beginPlot3D ("Line Plots" :: CString) size (fromIntegral (fromEnum ImPlot3DFlags_None))) $ do ImPlot3D.setupAxes3D ("x" :: CString) ("y" :: CString) ("z" :: CString) @@ -81,11 +74,27 @@ imPlot3DDemo (px1, py1, pz1) = do pokeElemOff py1 i (0.5 + 0.5 * cos (50.0 * (x + realToFrac t / 10.0))) pokeElemOff pz1 i (0.5 + 0.5 * sin (50.0 * (x + realToFrac t / 10.0))) ImPlot3D.plotLine3D "f(x)" px1 py1 pz1 1001 - ImPlot3D.endPlot3D delete size + +demoSurfacePlot :: IO () +demoSurfacePlot = do + pure () + +imPlot3DDemo :: Resource -> IO () +imPlot3DDemo res = do + begin ("ImPlot3D Demo" :: CString) nullPtr 0 + whenM (toBool <$> beginTabBar ("ImPlot3DDemoTabs" :: CString)) $ do + whenM (toBool <$> beginTabItem_ ("Line plot" :: CString)) $ do + demoLinePlot3D (res_array1 res) + endTabItem + whenM (toBool <$> beginTabItem_ ("Surface plot" :: CString)) $ do + demoSurfacePlot + endTabItem + endTabBar end + data Resource = Resource { res_array1 :: (Ptr CFloat, Ptr CFloat, Ptr CFloat), res_disp :: (Ptr CInt, Ptr CInt) @@ -155,7 +164,10 @@ main = do newFrame showFramerate io - imPlot3DDemo (res_array1 res) + + -- start demo + imPlot3DDemo res + render let (p_dispW, p_dispH) = res_disp res From 327e97aa0d950c950174a048633179db67a066e5 Mon Sep 17 00:00:00 2001 From: Ian-Woo Kim Date: Sun, 8 Feb 2026 23:55:04 -0500 Subject: [PATCH 8/9] add Line/AxisFlags enums. --- examples/plot3d-demo/app/Main.hs | 10 +++++-- implot3d-gen/Gen.hs | 47 ++++++++++++++++++++++++++++++-- 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/examples/plot3d-demo/app/Main.hs b/examples/plot3d-demo/app/Main.hs index d9969ae..a1a0822 100644 --- a/examples/plot3d-demo/app/Main.hs +++ b/examples/plot3d-demo/app/Main.hs @@ -66,14 +66,20 @@ demoLinePlot3D :: (Ptr CFloat, Ptr CFloat, Ptr CFloat) -> IO () demoLinePlot3D (px1, py1, pz1) = do size <- newImVec2 (-1) 0 whenM (toBool <$> ImPlot3D.beginPlot3D ("Line Plots" :: CString) size (fromIntegral (fromEnum ImPlot3DFlags_None))) $ do - ImPlot3D.setupAxes3D ("x" :: CString) ("y" :: CString) ("z" :: CString) + ImPlot3D.setupAxes3D + ("x" :: CString) + ("y" :: CString) + ("z" :: CString) + (fromIntegral (fromEnum ImPlot3DAxisFlags_None)) + (fromIntegral (fromEnum ImPlot3DAxisFlags_None)) + (fromIntegral (fromEnum ImPlot3DAxisFlags_None)) t <- getTime for_ [0 .. 1000] $ \i -> do let x = fromIntegral i * 0.001 pokeElemOff px1 i x pokeElemOff py1 i (0.5 + 0.5 * cos (50.0 * (x + realToFrac t / 10.0))) pokeElemOff pz1 i (0.5 + 0.5 * sin (50.0 * (x + realToFrac t / 10.0))) - ImPlot3D.plotLine3D "f(x)" px1 py1 pz1 1001 + ImPlot3D.plotLine3D "f(x)" px1 py1 pz1 1001 (fromIntegral (fromEnum ImPlot3DLineFlags_None)) 0 4 {- sizeof(float) -} ImPlot3D.endPlot3D delete size diff --git a/implot3d-gen/Gen.hs b/implot3d-gen/Gen.hs index 858ffe5..20b27b2 100644 --- a/implot3d-gen/Gen.hs +++ b/implot3d-gen/Gen.hs @@ -205,10 +205,48 @@ imPlot3DFlags_ = enum_header = "implot3d.h" } +imPlot3DAxisFlags_ :: EnumType +imPlot3DAxisFlags_ = + EnumType + { enum_name = "ImPlot3DAxisFlags_", + enum_cases = + [ "ImPlot3DAxisFlags_None", + "ImPlot3DAxisFlags_NoLabel", + "ImPlot3DAxisFlags_NoGridLines", + "ImPlot3DAxisFlags_NoTickMarks", + "ImPlot3DAxisFlags_NoTickLabels", + "ImPlot3DAxisFlags_LockMin", + "ImPlot3DAxisFlags_LockMax", + "ImPlot3DAxisFlags_AutoFit", + "ImPlot3DAxisFlags_Invert", + "ImPlot3DAxisFlags_PanStretch", + "ImPlot3DAxisFlags_Lock", + "ImPlot3DAxisFlags_NoDecorations" + ], + enum_header = "implot3d.h" + } + +imPlot3DLineFlags_ :: EnumType +imPlot3DLineFlags_ = + EnumType + { enum_name = "ImPlot3DLineFlags_", + enum_cases = + [ "ImPlot3DLineFlags_None", + "ImPlot3DLineFlags_NoLegend", + "ImPlot3DLineFlags_NoFit", + "ImPlot3DLineFlags_Segments", + "ImPlot3DLineFlags_Loop", + "ImPlot3DLineFlags_SkipNaN" + ], + enum_header = "implot3d.h" + } + classes = [] enums = - [ imPlot3DFlags_ + [ imPlot3DFlags_, + imPlot3DAxisFlags_, + imPlot3DLineFlags_ ] toplevelfunctions :: [TopLevel] @@ -217,7 +255,7 @@ toplevelfunctions = TLOrdinary (TopLevelFunction FFIUnsafe void_ "ShowDemoWindow" [star CTBool "p_open"] (Just "showDemoWindow")), TLOrdinary (TopLevelFunction FFIUnsafe bool_ "BeginPlot" [cstring "title_id", cppclassref imVec2 "size", int "flags"] (Just "beginPlot3D")), TLOrdinary (TopLevelFunction FFIUnsafe void_ "EndPlot" [] (Just "EndPlot3D")), - TLOrdinary (TopLevelFunction FFIUnsafe void_ "SetupAxes" [cstring "x_label", cstring "y_label", cstring "z_label"] (Just "setupAxes3D")), + TLOrdinary (TopLevelFunction FFIUnsafe void_ "SetupAxes" [cstring "x_label", cstring "y_label", cstring "z_label", int "x_flags", int "y_flags", int "z_flags"] (Just "setupAxes3D")), TLTemplate ( TopLevelTemplateFunction { topleveltfunc_safety = FFIUnsafe, @@ -230,7 +268,10 @@ toplevelfunctions = Arg (TemplateParamPointer "t1") "xs", Arg (TemplateParamPointer "t1") "ys", Arg (TemplateParamPointer "t1") "zs", - int "count" + int "count", + int "flags", + int "offset", + int "stride" ] } ) From 175d0c3669c733919970f35d31e17646e19d179a Mon Sep 17 00:00:00 2001 From: Ian-Woo Kim Date: Mon, 9 Feb 2026 00:27:54 -0500 Subject: [PATCH 9/9] First plotSurface example --- examples/plot3d-demo/app/Main.hs | 96 +++++++++++++++++++++++++------- implot3d-gen/Gen.hs | 40 ++++++++++++- 2 files changed, 115 insertions(+), 21 deletions(-) diff --git a/examples/plot3d-demo/app/Main.hs b/examples/plot3d-demo/app/Main.hs index a1a0822..2596939 100644 --- a/examples/plot3d-demo/app/Main.hs +++ b/examples/plot3d-demo/app/Main.hs @@ -8,7 +8,7 @@ module Main where import Control.Monad.Extra (whenM, whileM) import Data.Bits ((.|.)) import Data.Foldable (for_, traverse_) -import Data.IORef (IORef, modifyIORef', newIORef, readIORef) +import Data.IORef (IORef, modifyIORef', newIORef, readIORef, writeIORef) import Data.String (IsString (..)) import FFICXX.Runtime.Cast (FPtr (..)) import FFICXX.Runtime.TH (IsCPrimitive (..), TemplateParamInfo (..)) @@ -54,6 +54,17 @@ ImPlot3D.TH.genPlotLine3DInstanceFor } ) +ImPlot3D.TH.genPlotSurfaceInstanceFor + CPrim + ( [t|Ptr CFloat|], + TPInfo + { tpinfoCxxType = "float", + tpinfoCxxHeaders = [], + tpinfoCxxNamespaces = [], + tpinfoSuffix = "float" + } + ) + showFramerate :: ImGuiIO -> IO () showFramerate io = do begin ("Framerate monitor" :: CString) nullPtr 0 @@ -64,8 +75,15 @@ showFramerate io = do demoLinePlot3D :: (Ptr CFloat, Ptr CFloat, Ptr CFloat) -> IO () demoLinePlot3D (px1, py1, pz1) = do + t <- getTime + for_ [0 .. 1000] $ \i -> do + let x = fromIntegral i * 0.001 + pokeElemOff px1 i x + pokeElemOff py1 i (0.5 + 0.5 * cos (50.0 * (x + realToFrac t / 10.0))) + pokeElemOff pz1 i (0.5 + 0.5 * sin (50.0 * (x + realToFrac t / 10.0))) + size <- newImVec2 (-1) 0 - whenM (toBool <$> ImPlot3D.beginPlot3D ("Line Plots" :: CString) size (fromIntegral (fromEnum ImPlot3DFlags_None))) $ do + whenM (toBool <$> ImPlot3D.beginPlot3D ("Line Plot" :: CString) size (fromIntegral (fromEnum ImPlot3DFlags_None))) $ do ImPlot3D.setupAxes3D ("x" :: CString) ("y" :: CString) @@ -73,18 +91,46 @@ demoLinePlot3D (px1, py1, pz1) = do (fromIntegral (fromEnum ImPlot3DAxisFlags_None)) (fromIntegral (fromEnum ImPlot3DAxisFlags_None)) (fromIntegral (fromEnum ImPlot3DAxisFlags_None)) - t <- getTime - for_ [0 .. 1000] $ \i -> do - let x = fromIntegral i * 0.001 - pokeElemOff px1 i x - pokeElemOff py1 i (0.5 + 0.5 * cos (50.0 * (x + realToFrac t / 10.0))) - pokeElemOff pz1 i (0.5 + 0.5 * sin (50.0 * (x + realToFrac t / 10.0))) - ImPlot3D.plotLine3D "f(x)" px1 py1 pz1 1001 (fromIntegral (fromEnum ImPlot3DLineFlags_None)) 0 4 {- sizeof(float) -} + ImPlot3D.plotLine3D ("f(x)" :: CString) px1 py1 pz1 1001 (fromIntegral (fromEnum ImPlot3DLineFlags_None)) 0 4 {- sizeof(float) -} ImPlot3D.endPlot3D delete size -demoSurfacePlot :: IO () -demoSurfacePlot = do +demoSurfacePlot :: IORef CFloat -> (Ptr CFloat, Ptr CFloat, Ptr CFloat) -> IO () +demoSurfacePlot ref (px, py, pz) = do + t <- readIORef ref + io <- getIO + dt <- imGuiIO_DeltaTime_get io + let t' = t + dt + writeIORef ref t' + let n :: Int + n = 20 + min_val = -1.0 + max_val = 1.0 + step = (max_val - min_val) / (fromIntegral n - 1.0) + for_ [0 .. (n-1)] $ \i -> do + for_ [0 .. (n-1)] $ \j -> do + let idx = i * fromIntegral n + j + x, y, z :: CFloat + x = min_val + (fromIntegral j) * step + y = min_val + (fromIntegral i) * step + z = sin (2*t' + sqrt (x*x + y*y)) + pokeElemOff px idx x + pokeElemOff py idx y + pokeElemOff pz idx z + + size <- newImVec2 (-1) 0 + whenM (toBool <$> ImPlot3D.beginPlot3D ("Surface Plot" :: CString) size (fromIntegral (fromEnum ImPlot3DFlags_None))) $ do + ImPlot3D.setupAxes3D + ("x" :: CString) + ("y" :: CString) + ("z" :: CString) + (fromIntegral (fromEnum ImPlot3DAxisFlags_None)) + (fromIntegral (fromEnum ImPlot3DAxisFlags_None)) + (fromIntegral (fromEnum ImPlot3DAxisFlags_None)) + + ImPlot3D.plotSurface ("Wave Surface" :: CString) px py pz (fromIntegral n) (fromIntegral n) 0.0 0.0 (fromIntegral (fromEnum ImPlot3DSurfaceFlags_NoMarkers)) 0 4 {- sizeof(float) -} + ImPlot3D.endPlot3D + pure () imPlot3DDemo :: Resource -> IO () @@ -95,7 +141,7 @@ imPlot3DDemo res = do demoLinePlot3D (res_array1 res) endTabItem whenM (toBool <$> beginTabItem_ ("Surface plot" :: CString)) $ do - demoSurfacePlot + demoSurfacePlot (res_time res) (res_array2 res) endTabItem endTabBar end @@ -103,18 +149,28 @@ imPlot3DDemo res = do data Resource = Resource { res_array1 :: (Ptr CFloat, Ptr CFloat, Ptr CFloat), - res_disp :: (Ptr CInt, Ptr CInt) + res_array2 :: (Ptr CFloat, Ptr CFloat, Ptr CFloat), + res_disp :: (Ptr CInt, Ptr CInt), + res_time :: IORef CFloat } +alloca3DArray :: (Int, Int, Int) -> ((Ptr CFloat, Ptr CFloat, Ptr CFloat) -> IO a) -> IO a +alloca3DArray (nx, ny, nz) f = + allocaArray nx $ \(px :: Ptr CFloat) -> + allocaArray ny $ \(py :: Ptr CFloat) -> + allocaArray nz $ \(pz :: Ptr CFloat) -> + f (px, py, pz) + withRes :: (Resource -> IO a) -> IO a withRes f = - allocaArray 1001 $ \(px1 :: Ptr CFloat) -> - allocaArray 1001 $ \(py1 :: Ptr CFloat) -> - allocaArray 1001 $ \(pz1 :: Ptr CFloat) -> - alloca $ \(p_dispW :: Ptr CInt) -> - alloca $ \(p_dispH :: Ptr CInt) -> - let res = Resource (px1, py1, pz1) (p_dispW, p_dispH) - in f res + alloca3DArray (1001, 1001, 1001) $ \(px1, py1, pz1) -> + alloca3DArray (400, 400, 400) $ \(px2, py2, pz2) -> + alloca $ \(p_dispW :: Ptr CInt) -> + alloca $ \(p_dispH :: Ptr CInt) -> do + t0 <- realToFrac <$> getTime + ref <- newIORef t0 + let res = Resource (px1, py1, pz1) (px2, py2, pz2) (p_dispW, p_dispH) ref + f res main :: IO () main = do diff --git a/implot3d-gen/Gen.hs b/implot3d-gen/Gen.hs index 20b27b2..9386787 100644 --- a/implot3d-gen/Gen.hs +++ b/implot3d-gen/Gen.hs @@ -241,12 +241,28 @@ imPlot3DLineFlags_ = enum_header = "implot3d.h" } +imPlot3DSurfaceFlags_ :: EnumType +imPlot3DSurfaceFlags_ = + EnumType + { enum_name = "ImPlot3DSurfaceFlags_", + enum_cases = + [ "ImPlot3DSurfaceFlags_None", + "ImPlot3DSurfaceFlags_NoLegend", + "ImPlot3DSurfaceFlags_NoFit", + "ImPlot3DSurfaceFlags_NoLines", + "ImPlot3DSurfaceFlags_NoFill", + "ImPlot3DSurfaceFlags_NoMarkers" + ], + enum_header = "implot3d.h" + } + classes = [] enums = [ imPlot3DFlags_, imPlot3DAxisFlags_, - imPlot3DLineFlags_ + imPlot3DLineFlags_, + imPlot3DSurfaceFlags_ ] toplevelfunctions :: [TopLevel] @@ -274,6 +290,28 @@ toplevelfunctions = int "stride" ] } + ), + TLTemplate + ( TopLevelTemplateFunction + { topleveltfunc_safety = FFIUnsafe, + topleveltfunc_params = ["t1"], + topleveltfunc_ret = void_, + topleveltfunc_name = "plotSurface", + topleveltfunc_oname = "ImPlot3D::PlotSurface", + topleveltfunc_args = + [ cstring "label_id", + Arg (TemplateParamPointer "t1") "xs", + Arg (TemplateParamPointer "t1") "ys", + Arg (TemplateParamPointer "t1") "zs", + int "x_count", + int "y_count", + double "scale_min", + double "scale_max", + int "flags", + int "offset", + int "stride" + ] + } ) ]