From ceb727e8da5185635095c80e03f0b977bc4cf199 Mon Sep 17 00:00:00 2001 From: Mike Pilgrem Date: Sat, 16 Oct 2021 09:20:34 +0100 Subject: [PATCH] Enable use of POV-Ray for Windows (#225) This pull request reflects code changes made to be able to use POV-Ray for Windows, Version 3.7.0.msvc10.win64. The changes have been tested on Windows 10 but not on unix-like operating systems. However, I don't think the code for unix-like operating systems has been affected. The reason for the changes is explained below. 1. The POV-Ray for Windows executable is named `pvengine64.exe` on 64-bit Windows, not `povray I've created two modules `POVRay`, specified separately for `windows` and `unix`, that export `povrayApp :: String`, yielding the correct name of the executable. The function is used in place of `"povray"` (eg in `Reanimate.Driver.Check.hasPovray`). 2. Generated file names containing `$` cause problems under PowerShell, due to automatic variables, and file names on Windows are not case sensitive I've changed the 64-character `alphabet` in `Reanimate.Cache.encodeInt` for a 32-character one which includes only capital letters and digits. This affects both Windows and unix-like operating systems. (An alternative would be versions of `encodeInt` specified differently for `windows` and `unix`.) 3. POV-Ray for Windows requires the special command line option `/EXIT` to return I've moved `Reanimate.Povray.mkPovrayImage'` to the two modules `POVRay`. In the `windows` version, `/EXIT` is now added to the list of switches passed to the POV-Ray executable. 4. POV-Ray for Windows cannot parse switches enclosed in `""` as switches The cause of the problem is described in more detail in the comments in the source code of module `POVRay`. As a work around, in the `windows` version of `mkPovrayImage'`, the POV-Ray command is written to a temporary batch file and it is that batch file which is passed to `runCmd`. Other matters In addition to the above, POV-Ray 3.7 includes 'Script I\O Restrictions` which can prevent the writing of files. These need to be disabled in the POV-Ray for Windows GUI (under the Options menu). --- reanimate.cabal | 1 + src/Reanimate/Cache.hs | 6 ++-- src/Reanimate/Driver/Check.hs | 3 +- src/Reanimate/Povray.hs | 18 +--------- unix/POVRay.hs | 27 ++++++++++++++ windows/POVRay.hs | 66 +++++++++++++++++++++++++++++++++++ 6 files changed, 100 insertions(+), 21 deletions(-) create mode 100644 unix/POVRay.hs create mode 100644 windows/POVRay.hs diff --git a/reanimate.cabal b/reanimate.cabal index 79eabfe..d1ff354 100644 --- a/reanimate.cabal +++ b/reanimate.cabal @@ -111,6 +111,7 @@ library Reanimate.Scene.Sprite Reanimate.Scene.Object Detach + POVRay autogen-modules: Paths_reanimate if flag(no-hgeometry) cpp-options: -DNO_HGEOMETRY diff --git a/src/Reanimate/Cache.hs b/src/Reanimate/Cache.hs index e39800e..0144ce6 100644 --- a/src/Reanimate/Cache.hs +++ b/src/Reanimate/Cache.hs @@ -104,6 +104,6 @@ encodeInt i = worker (fromIntegral i) 60 worker key sh | sh < 0 = [] | otherwise = - case (key `shiftR` sh) `mod` 64 of - idx -> alphabet !! fromIntegral idx : worker key (sh-6) - alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+$" + case (key `shiftR` sh) `mod` 32 of + idx -> alphabet !! fromIntegral idx : worker key (sh-5) + alphabet = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789" diff --git a/src/Reanimate/Driver/Check.hs b/src/Reanimate/Driver/Check.hs index 33a2d61..66ae5c4 100644 --- a/src/Reanimate/Driver/Check.hs +++ b/src/Reanimate/Driver/Check.hs @@ -11,6 +11,7 @@ import Control.Exception (SomeException, handle) import Control.Monad (forM_) import Data.Maybe (fromMaybe, listToMaybe) import Data.Version (Version (Version), parseVersion, showVersion) +import POVRay (povrayApp) import Reanimate.Driver.Magick (magickCmd) import Reanimate.Misc (runCmd_) import System.Console.ANSI.Codes @@ -88,7 +89,7 @@ hasDvisvgm :: IO (Either String String) hasDvisvgm = hasProgram "dvisvgm" hasPovray :: IO (Either String String) -hasPovray = hasProgram "povray" +hasPovray = hasProgram povrayApp hasFFmpeg :: IO (Either String String) hasFFmpeg = checkMinVersion minVersion <$> ffmpegVersion diff --git a/src/Reanimate/Povray.hs b/src/Reanimate/Povray.hs index 14b76eb..76cbb46 100644 --- a/src/Reanimate/Povray.hs +++ b/src/Reanimate/Povray.hs @@ -14,18 +14,13 @@ module Reanimate.Povray , povrayExtreme' ) where -import Data.Hashable (Hashable (hash)) import Data.Text (Text) -import qualified Data.Text as T -import qualified Data.Text.IO as T import Graphics.SvgTree (Tree) -import Reanimate.Cache (cacheFile, encodeInt) +import POVRay (mkPovrayImage') import Reanimate.Constants (screenHeight, screenWidth) -import Reanimate.Misc (requireExecutable, runCmd) import Reanimate.Parameters (pNoExternals) import Reanimate.Raster (mkImage) import Reanimate.Svg.Constructors (mkText) -import System.FilePath (replaceExtension, (<.>)) import System.IO.Unsafe (unsafePerformIO) povrayRaw :: [String] -> Text -> Tree @@ -88,14 +83,3 @@ mkPovrayImage :: [String] -> Text -> IO Tree mkPovrayImage _ script | pNoExternals = pure $ mkText script mkPovrayImage args script = mkImage screenWidth screenHeight <$> mkPovrayImage' args script - -mkPovrayImage' :: [String] -> Text -> IO FilePath -mkPovrayImage' _ _ | pNoExternals = pure "/povray/has/been/disabled" -mkPovrayImage' args script = cacheFile template $ \target -> do - exec <- requireExecutable "povray" - let pov_file = replaceExtension target "pov" - T.writeFile pov_file script - runCmd exec (args ++ ["-D","+UA", pov_file, "+o"++target]) - where - template = encodeInt (hash key) <.> "png" - key = T.concat (script:map T.pack args) diff --git a/unix/POVRay.hs b/unix/POVRay.hs new file mode 100644 index 0000000..ae6d1e8 --- /dev/null +++ b/unix/POVRay.hs @@ -0,0 +1,27 @@ +module POVRay + ( povrayApp + , mkPovrayImage' + ) where + +import Data.Hashable (Hashable (hash)) +import Data.Text (Text) +import qualified Data.Text as T (concat, pack) +import qualified Data.Text.IO as T (writeFile) +import Reanimate.Cache (cacheFile, encodeInt) +import Reanimate.Misc (requireExecutable, runCmd) +import Reanimate.Parameters (pNoExternals) +import System.FilePath (replaceExtension, (<.>)) + +povrayApp :: String +povrayApp = "povray" + +mkPovrayImage' :: [String] -> Text -> IO FilePath +mkPovrayImage' _ _ | pNoExternals = pure "/povray/has/been/disabled" +mkPovrayImage' args script = cacheFile template $ \target -> do + exec <- requireExecutable povrayApp + let pov_file = replaceExtension target "pov" + T.writeFile pov_file script + runCmd exec (args ++ ["-D", "+UA", "+I" ++ pov_file, "+o" ++ target]) + where + template = encodeInt (hash key) <.> "png" + key = T.concat (script:map T.pack args) diff --git a/windows/POVRay.hs b/windows/POVRay.hs new file mode 100644 index 0000000..1262a42 --- /dev/null +++ b/windows/POVRay.hs @@ -0,0 +1,66 @@ +module POVRay + ( povrayApp + , mkPovrayImage' + ) where + +import Data.Hashable (Hashable (hash)) +import Data.Text (Text) +import qualified Data.Text as T (concat, pack) +import qualified Data.Text.IO as T (writeFile) +import Reanimate.Cache (cacheFile, encodeInt) +import Reanimate.Misc (requireExecutable, runCmd) +import Reanimate.Parameters (pNoExternals) +import System.FilePath (replaceExtension, (<.>)) +import System.IO (hClose, hPutStrLn) +import System.IO.Temp (withSystemTempFile) + +povrayApp :: String +povrayApp = "pvengine64" -- Assumes 64 bit Windows + +mkPovrayImage' :: [String] -> Text -> IO FilePath +mkPovrayImage' _ _ | pNoExternals = pure "/povray/has/been/disabled" +mkPovrayImage' args script = cacheFile template $ \target -> do + exec <- requireExecutable povrayApp + let pov_file = replaceExtension target "pov" + exec' = '"' : exec ++ "\"" -- Wrap exec in "" because the path is likely + -- to includes spaces + otherArgs = [ "-D" + , "+UA" + , "+I" ++ pov_file + , "+O" ++ target + , "/EXIT" -- Note [/EXIT special command-line option] + ] + command = concatMap (' ':) (exec' : args ++ otherArgs) + T.writeFile pov_file script + -- Note [Use of a batch file] + withSystemTempFile "pvcommand.bat" $ \path h -> do + hPutStrLn h command + hClose h + runCmd path [] + where + template = encodeInt (hash key) <.> "png" + key = T.concat (script:map T.pack args) + +{- +Note [/EXIT special command-line option] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +"The /EXIT command tells POV-Ray to perform the render given by the other +command-line options, combined with previously-set options such as internal +command-line settings, INI file, source file, and so forth, and then to exit. By +default, if this switch is not present, POV-Ray for Windows will remain running +after the render is complete. This switch only applies to renders started by +other options on the command-line. It will not affect renders started manually +from within POV-Ray for Windows itself." +source: https://www.povray.org/documentation/view/3.6.1/603/ + +Note [Use of batch file] +~~~~~~~~~~~~~~~~~~~~~~~~ + +POV-Ray for Windows assumes that anything on the command-line that is not a +switch is the name of an INI file. Switches are preceded by a plus or minus. +However, System.Process.createProcess_ wraps all arguments in "". POV_Ray for +Windows appears to interpret a switch wrapped in "" as the name of an INI file. + +The work around used here is to write the POV-Ray command line to a batch file. +-}