diff options
author | Joe Crayne <joe@jerkface.net> | 2019-07-20 21:03:55 -0400 |
---|---|---|
committer | Joe Crayne <joe@jerkface.net> | 2019-07-20 21:03:55 -0400 |
commit | f414c3f017a9da40440262bda8aac0486ef6e21b (patch) | |
tree | 566f6830c54ae048e0e3ad72de47238355037881 | |
parent | 89b10a78c24513b125fc505c78533a4ad6febd4c (diff) |
Support for renumbering vertices.
-rw-r--r-- | src/Text/UTF8.hs | 81 | ||||
-rw-r--r-- | src/Wavefront.hs | 45 | ||||
-rw-r--r-- | src/Wavefront/Lex.hs | 101 | ||||
-rw-r--r-- | wavefront-obj.cabal | 7 |
4 files changed, 226 insertions, 8 deletions
diff --git a/src/Text/UTF8.hs b/src/Text/UTF8.hs new file mode 100644 index 0000000..d793c8e --- /dev/null +++ b/src/Text/UTF8.hs | |||
@@ -0,0 +1,81 @@ | |||
1 | module Text.UTF8 (packUtf8,unpackUtf8) where | ||
2 | |||
3 | import Data.Word --(Word8,Word32) | ||
4 | import Data.Bits ((.|.),(.&.),shiftL,shiftR) | ||
5 | import Data.Char (chr,ord) | ||
6 | import qualified Data.ByteString as B | ||
7 | |||
8 | packUtf8 :: String -> B.ByteString | ||
9 | packUtf8 = B.pack . encode | ||
10 | |||
11 | unpackUtf8 :: B.ByteString -> String | ||
12 | unpackUtf8 = decode . B.unpack | ||
13 | |||
14 | |||
15 | replacement_character :: Char | ||
16 | replacement_character = '\xfffd' | ||
17 | |||
18 | -- | ||
19 | -- | Decode a UTF8 string packed into a list of Word8 values, directly to String | ||
20 | -- | ||
21 | decode :: [Word8] -> String | ||
22 | decode [ ] = "" | ||
23 | decode (c:cs) | ||
24 | | c < 0x80 = chr (fromEnum c) : decode cs | ||
25 | | c < 0xc0 = replacement_character : decode cs | ||
26 | | c < 0xe0 = multi1 | ||
27 | | c < 0xf0 = multi_byte 2 0xf 0x800 | ||
28 | | c < 0xf8 = multi_byte 3 0x7 0x10000 | ||
29 | | c < 0xfc = multi_byte 4 0x3 0x200000 | ||
30 | | c < 0xfe = multi_byte 5 0x1 0x4000000 | ||
31 | | otherwise = replacement_character : decode cs | ||
32 | where | ||
33 | multi1 = case cs of | ||
34 | c1 : ds | c1 .&. 0xc0 == 0x80 -> | ||
35 | let d = ((fromEnum c .&. 0x1f) `shiftL` 6) .|. fromEnum (c1 .&. 0x3f) | ||
36 | in if d >= 0x000080 then toEnum d : decode ds | ||
37 | else replacement_character : decode ds | ||
38 | _ -> replacement_character : decode cs | ||
39 | |||
40 | multi_byte :: Int -> Word8 -> Int -> [Char] | ||
41 | multi_byte i mask overlong = aux i cs (fromEnum (c .&. mask)) | ||
42 | where | ||
43 | aux 0 rs acc | ||
44 | | overlong <= acc && acc <= 0x10ffff && | ||
45 | (acc < 0xd800 || 0xdfff < acc) && | ||
46 | (acc < 0xfffe || 0xffff < acc) = chr acc : decode rs | ||
47 | | otherwise = replacement_character : decode rs | ||
48 | |||
49 | aux n (r:rs) acc | ||
50 | | r .&. 0xc0 == 0x80 = aux (n-1) rs | ||
51 | $ shiftL acc 6 .|. fromEnum (r .&. 0x3f) | ||
52 | |||
53 | aux _ rs _ = replacement_character : decode rs | ||
54 | |||
55 | |||
56 | -- | Encode a single Haskell Char to a list of Word8 values, in UTF8 format. | ||
57 | encodeChar :: Char -> [Word8] | ||
58 | encodeChar = map fromIntegral . go . ord | ||
59 | where | ||
60 | go oc | ||
61 | | oc <= 0x7f = [oc] | ||
62 | |||
63 | | oc <= 0x7ff = [ 0xc0 + (oc `shiftR` 6) | ||
64 | , 0x80 + oc .&. 0x3f | ||
65 | ] | ||
66 | |||
67 | | oc <= 0xffff = [ 0xe0 + (oc `shiftR` 12) | ||
68 | , 0x80 + ((oc `shiftR` 6) .&. 0x3f) | ||
69 | , 0x80 + oc .&. 0x3f | ||
70 | ] | ||
71 | | otherwise = [ 0xf0 + (oc `shiftR` 18) | ||
72 | , 0x80 + ((oc `shiftR` 12) .&. 0x3f) | ||
73 | , 0x80 + ((oc `shiftR` 6) .&. 0x3f) | ||
74 | , 0x80 + oc .&. 0x3f | ||
75 | ] | ||
76 | |||
77 | |||
78 | -- | Encode a Haskell String to a list of Word8 values, in UTF8 format. | ||
79 | encode :: String -> [Word8] | ||
80 | encode = concatMap encodeChar | ||
81 | |||
diff --git a/src/Wavefront.hs b/src/Wavefront.hs index 638b849..32959d4 100644 --- a/src/Wavefront.hs +++ b/src/Wavefront.hs | |||
@@ -11,6 +11,7 @@ import qualified Data.DList as DList | |||
11 | ;import Data.DList (DList) | 11 | ;import Data.DList (DList) |
12 | import Data.Functor.Identity | 12 | import Data.Functor.Identity |
13 | import qualified Data.IntMap as IntMap | 13 | import qualified Data.IntMap as IntMap |
14 | import qualified Data.Text as T | ||
14 | import Data.Text.Encoding (decodeUtf8) | 15 | import Data.Text.Encoding (decodeUtf8) |
15 | import qualified Data.Vector as Vector | 16 | import qualified Data.Vector as Vector |
16 | ;import Data.Vector (Vector) | 17 | ;import Data.Vector (Vector) |
@@ -104,7 +105,7 @@ buildOBJ = nullBuilder | |||
104 | , mtllib = \xs -> do | 105 | , mtllib = \xs -> do |
105 | let l = map decodeUtf8 xs | 106 | let l = map decodeUtf8 xs |
106 | libs <- gets (objMtlLibs . fst) | 107 | libs <- gets (objMtlLibs . fst) |
107 | modifyFirst $ \o -> o { objMtlLibs = libs `DList.append` DList.fromList l } | 108 | modifyFirst $ \o -> o { objMtlLibs = DList.fromList l `DList.append` libs } |
108 | , groups = \xs -> do | 109 | , groups = \xs -> do |
109 | let g = map decodeUtf8 xs | 110 | let g = map decodeUtf8 xs |
110 | modify' $ second $ \e -> e { elGroups = g } | 111 | modify' $ second $ \e -> e { elGroups = g } |
@@ -159,3 +160,45 @@ parseCustom :: Monad m => ObjBuilder m -> (OBJ Count -> m b) -> L.ByteString -> | |||
159 | parseCustom builder finish bs = do | 160 | parseCustom builder finish bs = do |
160 | counts <- execStateT (parseOBJ (objBookKeeping builder) (ObjConfig IntMap.empty) bs) emptyCounts | 161 | counts <- execStateT (parseOBJ (objBookKeeping builder) (ObjConfig IntMap.empty) bs) emptyCounts |
161 | finish counts | 162 | finish counts |
163 | |||
164 | data Renumbering = Renumbering | ||
165 | { renumV :: Int -> Int | ||
166 | , renumVT :: Int -> Int | ||
167 | , renumVN :: Int -> Int | ||
168 | , renumVP :: Int -> Int | ||
169 | } | ||
170 | |||
171 | renumFrom1 :: Renumbering | ||
172 | renumFrom1 = Renumbering | ||
173 | { renumV = succ | ||
174 | , renumVT = succ | ||
175 | , renumVN = succ | ||
176 | , renumVP = succ | ||
177 | } | ||
178 | |||
179 | addCounts :: OBJ Count -> Renumbering -> Renumbering | ||
180 | addCounts c r = Renumbering | ||
181 | { renumV = addc (objLocations c) . renumV r | ||
182 | , renumVT = addc (objTexCoords c) . renumVT r | ||
183 | , renumVN = addc (objNormals c) . renumVN r | ||
184 | , renumVP = renumVP r -- TODO | ||
185 | } | ||
186 | |||
187 | addc :: Count x -> Int -> Int | ||
188 | addc (Count c) x = c + x | ||
189 | |||
190 | renumTriple :: Renumbering -> RefTriple -> RefTriple | ||
191 | renumTriple r (RefTriple v t n) = RefTriple (renumV r v) (renumVT r <$> t) (renumVN r <$> n) | ||
192 | |||
193 | applyRenumbering :: MonadState Renumbering m => ObjBuilder m -> ObjBuilder m | ||
194 | applyRenumbering builder = builder | ||
195 | { face = \ts -> do | ||
196 | r <- get | ||
197 | face builder $ map (renumTriple r) ts | ||
198 | , line = \ts -> do | ||
199 | r <- get | ||
200 | line builder $ map (renumTriple r) ts | ||
201 | , surf = \u0 u1 v0 v1 ts -> do | ||
202 | r <- get | ||
203 | surf builder u0 u1 v0 v1 $ map (renumTriple r) ts | ||
204 | } | ||
diff --git a/src/Wavefront/Lex.hs b/src/Wavefront/Lex.hs index 05a6595..f4cb54a 100644 --- a/src/Wavefront/Lex.hs +++ b/src/Wavefront/Lex.hs | |||
@@ -2,15 +2,21 @@ | |||
2 | {-# LANGUAGE OverloadedStrings #-} | 2 | {-# LANGUAGE OverloadedStrings #-} |
3 | module Wavefront.Lex where | 3 | module Wavefront.Lex where |
4 | 4 | ||
5 | import Data.Bool | ||
5 | import qualified Data.ByteString.Lazy.Char8 as L | 6 | import qualified Data.ByteString.Lazy.Char8 as L |
6 | import qualified Data.ByteString.Char8 as S | 7 | import qualified Data.ByteString.Char8 as S |
7 | import Data.ByteString.Internal as BS | 8 | import Data.ByteString.Internal as BS |
9 | import Data.ByteString.Lex.Fractional as F | ||
10 | import Data.ByteString.Lex.Integral as I | ||
8 | import Data.Char | 11 | import Data.Char |
9 | import Data.IntMap (IntMap) | 12 | import Data.IntMap (IntMap) |
10 | import qualified Data.IntMap as IntMap | 13 | import qualified Data.IntMap as IntMap |
11 | import Data.ByteString.Lex.Fractional as F | 14 | import Data.List |
12 | import Data.ByteString.Lex.Integral as I | 15 | import Data.Maybe |
13 | import qualified Rank2 | 16 | import qualified Rank2 |
17 | import Control.Monad.Writer.Lazy | ||
18 | |||
19 | import Text.UTF8 | ||
14 | 20 | ||
15 | data ObjBuilder m = ObjBuilder | 21 | data ObjBuilder m = ObjBuilder |
16 | { vertex :: [Double] -> m () | 22 | { vertex :: [Double] -> m () |
@@ -18,7 +24,7 @@ data ObjBuilder m = ObjBuilder | |||
18 | , vertexN :: [Double] -> m () | 24 | , vertexN :: [Double] -> m () |
19 | , vertexP :: [Double] -> m () | 25 | , vertexP :: [Double] -> m () |
20 | , face :: [RefTriple] -> m () | 26 | , face :: [RefTriple] -> m () |
21 | , line :: [RefTriple] -> m () | 27 | , line :: [RefTriple] -> m () |
22 | , cstype :: Bool -> CSType -> m () | 28 | , cstype :: Bool -> CSType -> m () |
23 | , curv2 :: [Int] -> m () | 29 | , curv2 :: [Int] -> m () |
24 | , curv :: Double -> Double -> [Int] -> m () | 30 | , curv :: Double -> Double -> [Int] -> m () |
@@ -67,7 +73,7 @@ instance Rank2.Functor ObjBuilder where | |||
67 | , vertexN = \vs -> f $ vertexN b vs | 73 | , vertexN = \vs -> f $ vertexN b vs |
68 | , vertexP = \vs -> f $ vertexP b vs | 74 | , vertexP = \vs -> f $ vertexP b vs |
69 | , face = \is -> f $ face b is | 75 | , face = \is -> f $ face b is |
70 | , line = \is -> f $ line b is | 76 | , line = \is -> f $ line b is |
71 | , cstype = \isRat typ -> f $ cstype b isRat typ | 77 | , cstype = \isRat typ -> f $ cstype b isRat typ |
72 | , curv2 = \is -> f $ curv2 b is | 78 | , curv2 = \is -> f $ curv2 b is |
73 | , curv = \u0 v0 is -> f $ curv b u0 v0 is | 79 | , curv = \u0 v0 is -> f $ curv b u0 v0 is |
@@ -159,6 +165,59 @@ nullBuilder = ObjBuilder | |||
159 | , badToken = \bs -> pure () | 165 | , badToken = \bs -> pure () |
160 | } | 166 | } |
161 | 167 | ||
168 | echoBuilder :: (MonadWriter (f String) m, Applicative f) => ObjBuilder m | ||
169 | echoBuilder = ObjBuilder | ||
170 | { vertex = \vs -> echo $ unwords ("v": map show vs) | ||
171 | , vertexT = \vs -> echo $ unwords ("vt": map show vs) | ||
172 | , vertexN = \vs -> echo $ unwords ("vn": map show vs) | ||
173 | , vertexP = \vs -> echo $ unwords ("vp": map show vs) | ||
174 | , face = \ts -> echo $ unwords ("f":map showRefTriple ts) | ||
175 | , line = \ts -> echo $ unwords ("l":map showRefTriple ts) | ||
176 | , cstype = \isRat typ -> echo $ unwords [ if isRat then "cstype rat" else "cstype" | ||
177 | , map toLower (show typ) ] | ||
178 | , curv2 = \is -> echo $ unwords ("curv2":map show is) | ||
179 | , curv = \u0 v0 is -> echo $ unwords ("curv":show u0:show v0:map show is) | ||
180 | , parm = \isU ds -> echo $ unwords ("parm":showParamSpec isU:map show ds) | ||
181 | , specialPoints = \is -> echo $ unwords ("sp":map show is) | ||
182 | , endFreeForm = echo "end" | ||
183 | , ctech = \approx -> echo $ "ctech " ++ showCurveSamplingSpec approx | ||
184 | , stech = \approx -> echo $ "stech " ++ showSurfaceSamplingSpec approx | ||
185 | , deg = \is -> echo $ unwords ("deg":map show is) | ||
186 | , surf = \u0 u1 v0 v1 ts -> echo $ unwords $ "surf " : map show [u0,u1,v0,v1] | ||
187 | ++ map showRefTriple ts | ||
188 | , trim = \ss -> echo $ unwords ("trim":concatMap (\(CurveSpec s e r) -> [show s,show e,show r]) ss) | ||
189 | , hole = \ss -> echo $ unwords ("hole":concatMap (\(CurveSpec s e r) -> [show s,show e,show r]) ss) | ||
190 | , specialCurves = \ss -> echo $ unwords ("scrv":concatMap (\(CurveSpec s e r) -> [show s,show e,show r]) ss) | ||
191 | , equivalentCurves = \ccs -> echo $ unwords ("con":map showEmbeddedCurve ccs) | ||
192 | , groups = \gs -> echo $ unwords $ "g":map unpackUtf8 gs | ||
193 | , smoothingGroup = \sg -> echo ("s " ++ show sg) | ||
194 | , mergingGroup = \mg δ -> echo $ unwords ["mg",show mg,show δ] | ||
195 | , usemtl = \mtl -> echo ("usemtl " ++ unpackUtf8 mtl) | ||
196 | , deprecated_cdc = \is -> echo $ unwords ("cdc":map show is) | ||
197 | , deprecated_bzp = \is -> echo $ unwords ("bzp":map show is) | ||
198 | , mtllib = \fns -> echo $ unwords $ "mtllib " : map unpackUtf8 fns | ||
199 | , objectName = \obn -> echo ("o " ++ unpackUtf8 obn) | ||
200 | , bmat = \isU is -> echo $ unwords ("bmat":showParamSpec isU:map show is) | ||
201 | , step = \is -> echo $ unwords ("step":map show is) | ||
202 | , points = \is -> echo $ unwords ("p":map show is) | ||
203 | , usemap = \mp -> echo $ "usemap " ++ maybe "off" unpackUtf8 mp | ||
204 | , maplib = \fns -> echo ("maplib " ++ show fns) | ||
205 | , c_interp = \flag -> echo $ "c_interp " ++ bool "off" "on" flag | ||
206 | , d_interp = \flag -> echo $ "d_interp " ++ bool "off" "on" flag | ||
207 | , deprecated_cdp = \x -> echo $ "cdp " ++ show x | ||
208 | , deprecated_bsp = \x -> echo $ "bsp " ++ show x | ||
209 | , trace_obj = \x -> echo $ "trace_obj " ++ unpackUtf8 x | ||
210 | , shadow_obj = \x -> echo $ "shadow_obj " ++ unpackUtf8 x | ||
211 | , deprecated_res = \x -> echo $ "res " ++ show x | ||
212 | , bevel = \x -> echo $ "bevel " ++ show x | ||
213 | , lod = \x -> echo $ "lod " ++ show x | ||
214 | , call = \fn as -> echo $ unwords ("call":map unpackUtf8 (fn:as)) | ||
215 | , command = \e cmd -> do echo $ "csh " ++ (bool ('-':) id e $ unpackUtf8 $ L.toStrict cmd) | ||
216 | return L.empty | ||
217 | , badToken = \bs -> echo $ "bad token: " ++ show (L.take 20 bs) | ||
218 | } | ||
219 | where | ||
220 | echo = tell . pure | ||
162 | 221 | ||
163 | data CurveSamplingSpec | 222 | data CurveSamplingSpec |
164 | -- ctech cparm | 223 | -- ctech cparm |
@@ -175,6 +234,11 @@ data CurveSamplingSpec | |||
175 | | CurvatureBasedPolygon { maxDistanceToCurve :: Double, maximumDegreesPerSample :: Double } | 234 | | CurvatureBasedPolygon { maxDistanceToCurve :: Double, maximumDegreesPerSample :: Double } |
176 | deriving (Eq,Show) | 235 | deriving (Eq,Show) |
177 | 236 | ||
237 | showCurveSamplingSpec :: CurveSamplingSpec -> String | ||
238 | showCurveSamplingSpec (UniformSubdivision d) = "cparm " ++ show d | ||
239 | showCurveSamplingSpec (MaxLengthPolygonal d) = "cspace " ++ show d | ||
240 | showCurveSamplingSpec (CurvatureBasedPolygon d a) = unwords ["curv",show d,show a] | ||
241 | |||
178 | data SurfaceSamplingSpec | 242 | data SurfaceSamplingSpec |
179 | -- stech cparma ures vres | 243 | -- stech cparma ures vres |
180 | = UniformIsoparametric { uDivisionsPerDegree :: Double, vDivisionsPerDegree :: Double } | 244 | = UniformIsoparametric { uDivisionsPerDegree :: Double, vDivisionsPerDegree :: Double } |
@@ -186,6 +250,11 @@ data SurfaceSamplingSpec | |||
186 | | CurvatureBasedPolytope { maxDistanceToSurface :: Double, maxDegreesPerCorner :: Double } | 250 | | CurvatureBasedPolytope { maxDistanceToSurface :: Double, maxDegreesPerCorner :: Double } |
187 | deriving (Eq,Show) | 251 | deriving (Eq,Show) |
188 | 252 | ||
253 | showSurfaceSamplingSpec :: SurfaceSamplingSpec -> String | ||
254 | showSurfaceSamplingSpec (UniformIsoparametric ures vres) = unwords ["cparma",show ures,show vres] | ||
255 | showSurfaceSamplingSpec (UniformAfterTrimming uvres) = "cparmb " ++ show uvres | ||
256 | showSurfaceSamplingSpec (MaxLengthPolytopal maxlength) = "cspace " ++ show maxlength | ||
257 | showSurfaceSamplingSpec (CurvatureBasedPolytope maxd maxa) = unwords ["curv",show maxd,show maxa] | ||
189 | 258 | ||
190 | data ObjState = ObjState | 259 | data ObjState = ObjState |
191 | { | 260 | { |
@@ -291,13 +360,13 @@ findNewLine ps o@(ObjConfig args) bs = case L.break (\c -> c=='\n' || c=='\\' || | |||
291 | nextToken :: (L.ByteString -> L.ByteString) -> L.ByteString -> L.ByteString | 360 | nextToken :: (L.ByteString -> L.ByteString) -> L.ByteString -> L.ByteString |
292 | nextToken tok bs = tok $ L.dropWhile (not . isSpace) bs | 361 | nextToken tok bs = tok $ L.dropWhile (not . isSpace) bs |
293 | 362 | ||
294 | parseFloats tok bs cont = case L.splitAt 10 (tok bs) of | 363 | parseFloats tok bs cont = case L.splitAt 22 (tok bs) of |
295 | (ds,bs') -> case F.readSigned F.readExponential (L.toStrict ds) of | 364 | (ds,bs') -> case F.readSigned F.readExponential (L.toStrict ds) of |
296 | Just (x,b) -> parseFloats tok (reconsChunk b bs') (cont . (x :)) | 365 | Just (x,b) -> parseFloats tok (reconsChunk b bs') (cont . (x :)) |
297 | Nothing -> cont [] bs | 366 | Nothing -> cont [] bs |
298 | 367 | ||
299 | parseFloatsN 0 _ bs cont = cont [] bs | 368 | parseFloatsN 0 _ bs cont = cont [] bs |
300 | parseFloatsN n tok bs cont = case L.splitAt 10 (tok bs) of | 369 | parseFloatsN n tok bs cont = case L.splitAt 22 (tok bs) of |
301 | (ds,bs') -> case F.readSigned F.readExponential (L.toStrict ds) of | 370 | (ds,bs') -> case F.readSigned F.readExponential (L.toStrict ds) of |
302 | Just (x,b) -> parseFloatsN (n-1) tok (reconsChunk b bs') (cont . (x :)) | 371 | Just (x,b) -> parseFloatsN (n-1) tok (reconsChunk b bs') (cont . (x :)) |
303 | Nothing -> cont [] bs | 372 | Nothing -> cont [] bs |
@@ -376,6 +445,10 @@ data CSType = Bmatrix | Bezier | Bspline | Cardinal | Taylor | |||
376 | data ParamSpec = ParamU | ParamV | 445 | data ParamSpec = ParamU | ParamV |
377 | deriving (Eq,Ord,Show,Enum) | 446 | deriving (Eq,Ord,Show,Enum) |
378 | 447 | ||
448 | showParamSpec :: ParamSpec -> String | ||
449 | showParamSpec ParamU = "u" | ||
450 | showParamSpec ParamV = "v" | ||
451 | |||
379 | data RefTriple = RefTriple | 452 | data RefTriple = RefTriple |
380 | { refV :: {-# UNPACK #-} !Int | 453 | { refV :: {-# UNPACK #-} !Int |
381 | , refT :: !(Maybe Int) | 454 | , refT :: !(Maybe Int) |
@@ -384,6 +457,14 @@ data RefTriple = RefTriple | |||
384 | -- data RefTriple = RefTriple Int (Maybe Int) (Maybe Int) | 457 | -- data RefTriple = RefTriple Int (Maybe Int) (Maybe Int) |
385 | deriving (Eq,Ord,Show) | 458 | deriving (Eq,Ord,Show) |
386 | 459 | ||
460 | showRefTriple :: RefTriple -> String | ||
461 | showRefTriple (RefTriple v Nothing (Just n)) = show v ++ "//" ++ show n | ||
462 | showRefTriple (RefTriple v mt mn) = intercalate "/" $ map show | ||
463 | $ mappend [v] | ||
464 | $ maybe id (mappend . pure) mt | ||
465 | $ maybe id (mappend . pure) mn | ||
466 | $ [] | ||
467 | |||
387 | data CurveSpec = CurveSpec | 468 | data CurveSpec = CurveSpec |
388 | { curveStart :: Double | 469 | { curveStart :: Double |
389 | , curveEnd :: Double | 470 | , curveEnd :: Double |
@@ -397,6 +478,14 @@ data EmbeddedCurve = EmbeddedCurve | |||
397 | } | 478 | } |
398 | deriving (Eq,Ord,Show) | 479 | deriving (Eq,Ord,Show) |
399 | 480 | ||
481 | showEmbeddedCurve :: EmbeddedCurve -> String | ||
482 | showEmbeddedCurve (EmbeddedCurve s c) = unwords | ||
483 | [ show s | ||
484 | , show (curveStart c) | ||
485 | , show (curveEnd c) | ||
486 | , show (curveRef c) | ||
487 | ] | ||
488 | |||
400 | lengthLessThan :: Int -> L.ByteString -> Bool | 489 | lengthLessThan :: Int -> L.ByteString -> Bool |
401 | lengthLessThan n bs = | 490 | lengthLessThan n bs = |
402 | foldr (\c ret ac -> let m = S.length c in if ac <= m then False else ret $! ac - m) | 491 | foldr (\c ret ac -> let m = S.length c in if ac <= m then False else ret $! ac - m) |
diff --git a/wavefront-obj.cabal b/wavefront-obj.cabal index 5baa231..507c3c6 100644 --- a/wavefront-obj.cabal +++ b/wavefront-obj.cabal | |||
@@ -17,7 +17,7 @@ library | |||
17 | exposed-modules: Wavefront.Lex | 17 | exposed-modules: Wavefront.Lex |
18 | , Wavefront.Types | 18 | , Wavefront.Types |
19 | , Wavefront | 19 | , Wavefront |
20 | -- other-modules: | 20 | other-modules: Text.UTF8 |
21 | other-extensions: ConstraintKinds | 21 | other-extensions: ConstraintKinds |
22 | , DeriveFunctor | 22 | , DeriveFunctor |
23 | , FlexibleContexts | 23 | , FlexibleContexts |
@@ -44,3 +44,8 @@ library | |||
44 | 44 | ||
45 | hs-source-dirs: src | 45 | hs-source-dirs: src |
46 | default-language: Haskell2010 | 46 | default-language: Haskell2010 |
47 | ghc-options: -Wmissing-signatures | ||
48 | |||
49 | executable objcat | ||
50 | main-is: tools/objcat.hs | ||
51 | build-depends: base, bytestring, containers, dlist, mtl, wavefront-obj | ||