{-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE NoMonomorphismRestriction #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE ScopedTypeVariables #-} module Icons ( TransformParams(..), TransformableDia, getPortAngles, iconToDiagram, inputPort, resultPort, argumentPorts, caseRhsPorts, casePatternPorts, multiIfRhsPorts, multiIfBoolPorts, textBox, multilineComment, defaultLineWidth, ColorStyle(..), colorScheme, coloredTextBox, circleRadius, findIconFromName ) where import Diagrams.Prelude hiding ((&), (#), Name) import qualified Control.Arrow as Arrow import Data.Either(partitionEithers) import qualified Data.IntMap as IM import Data.List(find) import Data.Maybe(listToMaybe, isJust, fromJust, mapMaybe) import Data.Typeable(Typeable) import Constants(pattern InputPortConst, pattern ResultPortConst) import DrawingColors(colorScheme, ColorStyle(..)) import Types(Icon(..), SpecialQDiagram, SpecialBackend, SpecialNum , NodeName(..), Port(..), LikeApplyFlavor(..), SyntaxNode(..), NamedIcon, Labeled(..), IconInfo , Named(..)) {-# ANN module "HLint: ignore Use record patterns" #-} {-# ANN module "HLint: ignore Unnecessary hiding" #-} -- TYPES -- data TransformParams n = TransformParams { tpName :: NodeName -- The icon's name , tpNestingDepth :: Int -- The icon's nesting depth , tpIsReflected :: Bool -- If the icon will be reflected , tpAngle :: Angle n -- By what angle will the icon be rotated } -- | A TransformableDia is a function that returns a diagram for an icon when -- given the icon's name, its nesting depth, whether it will be reflected, and -- by what angle it will be rotated. type TransformableDia b n = TransformParams n -> SpecialQDiagram b n -- CONSTANTS -- defaultLineWidth :: (Fractional a) => a defaultLineWidth = 0.15 circleRadius :: (Fractional a) => a circleRadius = 0.5 defaultOpacity :: (Fractional a) => a defaultOpacity = 0.4 -- COLORS -- lineCol :: Colour Double lineCol = lineC colorScheme -- BEGIN Exported icon functions -- findIconFromName :: IconInfo -> NodeName -> NamedIcon findIconFromName icons name@(NodeName nameInt) = Named name $ IM.findWithDefault (error $ "findIconFromName: icon not found.\nicons=" <> show icons <> "\nname=" <> show name) nameInt icons -- TODO Detect if we are in a loop (have called iconToDiagram on the same node -- before) iconToDiagram :: SpecialBackend b n => IconInfo -> Icon -> TransformableDia b n iconToDiagram iconInfo icon = case icon of TextBoxIcon s -> textBox s BindTextBoxIcon s -> identDiaFunc $ bindTextBox s MultiIfIcon n -> nestedMultiIfDia iconInfo $ replicate (1 + (2 * n)) Nothing CaseIcon n -> nestedCaseDia iconInfo $ replicate (1 + (2 * n)) Nothing CaseResultIcon -> identDiaFunc caseResult LambdaIcon x bodyExp _ -> nestedLambda iconInfo x (findIconFromName iconInfo <$> bodyExp) NestedApply flavor headIcon args -> nestedApplyDia iconInfo flavor (fmap (findIconFromName iconInfo) headIcon) ((fmap . fmap) (findIconFromName iconInfo) args) NestedPApp constructor args -> nestedPAppDia iconInfo (repeat $ patternC colorScheme) constructor args NestedCaseIcon args -> nestedCaseDia iconInfo ((fmap . fmap) (findIconFromName iconInfo) args) NestedMultiIfIcon args -> nestedMultiIfDia iconInfo ((fmap . fmap) (findIconFromName iconInfo) args) -- BEGIN getPortAngles -- applyPortAngles :: Floating n => Port -> [Angle n] applyPortAngles (Port x) = fmap (@@ turn) $ case x of 0 -> [3/8, 1/2, 5/8] -- TODO Don't use angle of 1/2 for nested icons here --1 -> [1/8, 7/8, 0] 1 -> [0] _ -> [1/4, 3/4] lambdaPortAngles :: Floating n => Bool -> Port -> [Angle n] lambdaPortAngles embedded (Port x) = fmap (@@ turn) $ case x of -- 0 == lambda return value Icon 0 -> if embedded then [1/4, 3/4] else [3/8, 1/2, 5/8] -- 1 == value port --1 -> [1/8, 7/8, 0] 1 -> [0] _ -> [1/4, 3/4] pAppPortAngles :: Floating n => Port -> [Angle n] pAppPortAngles (Port x) = fmap (@@ turn) $ case x of 0 -> [1/4] 1 -> [0] _ -> [1/2] multiIfPortAngles :: Floating n => Port -> [Angle n] multiIfPortAngles (Port port) = case port of 0 -> [1/4 @@ turn] 1 -> [3/4 @@ turn] _ -> otherAngles where otherAngles | even port = [0 @@ turn] | otherwise = [1/2 @@ turn] findNestedIcon :: IconInfo -> NodeName -> Icon -> Maybe Icon findNestedIcon iconInfo name icon = case icon of NestedApply _ headIcon args -> snd <$> findIcon iconInfo name ((fmap . fmap) (findIconFromName iconInfo) (headIcon : args)) NestedPApp constructor args -> snd <$> findIcon iconInfo name (fmap laValue (constructor:args)) _ -> Nothing findIcon :: IconInfo -> NodeName -> [Maybe NamedIcon] -> Maybe (Int, Icon) findIcon iconInfo name args = icon where numberedArgs = zip ([0,1..] :: [Int]) args filteredArgs = Arrow.second fromJust <$> filter (isJust . snd) numberedArgs nameMatches (_, Named n _) = n == name icon = case find nameMatches filteredArgs of Nothing -> listToMaybe $ mapMaybe findSubSubIcon filteredArgs Just (argNum, Named _ finalIcon) -> Just (argNum, finalIcon) where findSubSubIcon (argNum, Named _ subIcon) = case findNestedIcon iconInfo name subIcon of Nothing -> Nothing Just x -> Just (argNum, x) generalNestedPortAngles :: SpecialNum n => IconInfo -> (Port -> [Angle n]) -> Maybe NamedIcon -> [Maybe NamedIcon] -> Port -> Maybe NodeName -> [Angle n] generalNestedPortAngles iconInfo defaultAngles headIcon args port maybeNodeName = case maybeNodeName of Nothing -> defaultAngles port Just name -> case findIcon iconInfo name (headIcon : args) of Nothing -> [] Just (_, icon) -> getPortAnglesHelper True iconInfo icon port Nothing reflectXAngle :: SpecialNum n => Angle n -> Angle n reflectXAngle x = reflectedAngle where normalizedAngle = normalizeAngle x reflectedAngle = (-) <$> halfTurn <*> normalizedAngle -- TODO reflect the angles for the right side sub-icons nestedMultiIfPortAngles :: SpecialNum n => IconInfo -> [Maybe NamedIcon] -> Port -> Maybe NodeName -> [Angle n] nestedMultiIfPortAngles iconInfo args port maybeNodeName = case maybeNodeName of Nothing -> multiIfPortAngles port Just name -> case findIcon iconInfo name args of Nothing -> [] -- TODO Don't use hardcoded numbers -- The arguments correspond to ports [0, 2, 3, 4 ...] Just (argNum, icon) -> if odd argNum && argNum >= 1 -- The icon will be reflected then fmap reflectXAngle subAngles else subAngles where subAngles = getPortAnglesHelper True iconInfo icon port Nothing getPortAngles :: SpecialNum n => IconInfo -> Icon -> Port -> Maybe NodeName -> [Angle n] getPortAngles = getPortAnglesHelper False getPortAnglesHelper :: SpecialNum n => Bool -> IconInfo -> Icon -> Port -> Maybe NodeName -> [Angle n] getPortAnglesHelper embedded iconInfo icon port maybeNodeName = case icon of TextBoxIcon _ -> [] BindTextBoxIcon _ -> [] MultiIfIcon _ -> multiIfPortAngles port CaseIcon _ -> multiIfPortAngles port CaseResultIcon -> [] LambdaIcon _ _ _ -> lambdaPortAngles embedded port NestedApply _ headIcon args -> generalNestedPortAngles iconInfo applyPortAngles -- TODO Refactor with iconToDiagram (fmap (findIconFromName iconInfo) headIcon) ((fmap . fmap) (findIconFromName iconInfo) args) port maybeNodeName NestedPApp headIcon args -> generalNestedPortAngles iconInfo pAppPortAngles (laValue headIcon) (fmap laValue args) port maybeNodeName NestedCaseIcon args -> nestedMultiIfPortAngles iconInfo ((fmap . fmap) (findIconFromName iconInfo) args) port maybeNodeName NestedMultiIfIcon args -> nestedMultiIfPortAngles iconInfo ((fmap . fmap) (findIconFromName iconInfo) args) port maybeNodeName -- END getPortAngles -- -- BEGIN Port numbers argPortsConst :: [Port] argPortsConst = fmap Port [2,3..] -- TODO It's a bit strange that the parameter is a SyntaxNode, not an Icon. inputPort :: SyntaxNode -> Port inputPort = const InputPortConst resultPort :: SyntaxNode -> Port resultPort = const ResultPortConst caseRhsPorts :: [Port] caseRhsPorts = fmap Port [3,5..] casePatternPorts :: [Port] casePatternPorts = fmap Port [2,4..] multiIfRhsPorts :: [Port] multiIfRhsPorts = casePatternPorts multiIfBoolPorts :: [Port] multiIfBoolPorts = caseRhsPorts argumentPorts :: SyntaxNode -> [Port] argumentPorts n = case n of (ApplyNode _ _) -> defaultPorts PatternApplyNode _ _-> defaultPorts (FunctionDefNode _ _) -> defaultPorts CaseOrMultiIfNode _ _ -> defaultPorts NameNode _ -> [] BindNameNode _ -> [] LiteralNode _ -> [] CaseResultNode -> [] where defaultPorts = argPortsConst -- END Port numbers -- END Exported icon functions -- -- BEGIN Diagram helper functions -- -- | Make an identity TransformableDia identDiaFunc :: SpecialNum n => SpecialQDiagram b n -> TransformableDia b n identDiaFunc dia transformParams = nameDiagram (tpName transformParams) dia -- | Names the diagram and puts all sub-names in the namespace of the top level -- name. nameDiagram :: SpecialNum n => NodeName -> SpecialQDiagram b n -> SpecialQDiagram b n nameDiagram name dia = named name (name .>> dia) -- | Make an port with an integer name. Always use <> to add a ports -- (not === or |||) since mempty has no size and will not be placed where you -- want it. makePort :: SpecialNum n => Port -> SpecialQDiagram b n makePort x = named x mempty --makePort x = circle 0.2 # fc green # named x -- Note, the version of makePort below seems to have a different type. --makePort x = textBox (show x) # fc green # named x makeQualifiedPort :: SpecialNum n => NodeName -> Port -> SpecialQDiagram b n makeQualifiedPort n x = n .>> makePort x makeLabelledPort :: SpecialBackend b n => NodeName -> Bool -> Angle n -> String -> Port -> SpecialQDiagram b n makeLabelledPort name reflect angle str portNum = case str of -- Don't display " tempvar" from Translate.hs/matchesToCase (' ':_) -> portAndCircle (_:_:_) -> portAndCircle ||| label _ -> portAndCircle where portAndCircle = makeQualifiedPort name portNum <> portCircle label = transformableBindTextBox str reflect angle -- END Diagram helper functions -- BEGIN Icons -- -- BEGIN Sub-diagrams -- apply0Triangle :: SpecialBackend b n => Colour Double -> SpecialQDiagram b n apply0Triangle col = fc col $ lw none $ rotateBy (-1/12) $ eqTriangle (2 * circleRadius) composeSemiCircle :: SpecialBackend b n => Colour Double -> SpecialQDiagram b n composeSemiCircle col = lc col $ lwG defaultLineWidth $ wedge circleRadius yDir halfTurn portCircle :: SpecialBackend b n => SpecialQDiagram b n portCircle = lw none $ fc lineCol $ circle (circleRadius * 0.5) resultIcon :: SpecialBackend b n => SpecialQDiagram b n resultIcon = lw none $ fc (lamArgResC colorScheme) unitSquare -- END Sub-diagrams -- BEGIN Main icons -- BEGIN Apply like icons makeAppInnerIcon :: SpecialBackend b n => IconInfo -> TransformParams n -> Bool -> -- If False then add one to the nesting level. Port -> -- Port number (if the NamedIcon is Nothing) Labeled (Maybe NamedIcon) -> -- The icon SpecialQDiagram b n makeAppInnerIcon _iconInfo (TransformParams name _ reflect angle) _ portNum (Labeled Nothing str) = centerX $ makeLabelledPort name reflect angle str portNum makeAppInnerIcon iconInfo (TransformParams _ nestingLevel reflect angle) func _ (Labeled (Just (Named iconNodeName icon)) _) = iconToDiagram iconInfo icon (TransformParams iconNodeName innerLevel reflect angle) where innerLevel = if func then nestingLevel else nestingLevel + 1 makeTransformedText :: SpecialBackend b n => IconInfo -> TransformParams n -> Labeled (Maybe NamedIcon) -> SpecialQDiagram b n makeTransformedText iconInfo tp maybeFunText = case laValue maybeFunText of Just _ -> makeAppInnerIcon iconInfo tp True InputPortConst maybeFunText Nothing -> mempty appArgBox :: (HasStyle a, Typeable (N a) , TrailLike a, RealFloat (N a), V a ~ V2) => Colour Double -> N a -> N a -> a appArgBox borderCol topAndBottomLineWidth portHeight = lwG defaultLineWidth $ lcA (withOpacity borderCol defaultOpacity) $ roundedRect topAndBottomLineWidth (portHeight + verticalSeparation) (circleRadius * 0.5) where verticalSeparation = circleRadius nestedPAppDia :: SpecialBackend b n => IconInfo -> [Colour Double] -> Labeled (Maybe NamedIcon) -> [Labeled (Maybe NamedIcon)] -> TransformableDia b n nestedPAppDia iconInfo borderCols maybeFunText args tp@(TransformParams name nestingLevel _ _) = named name $ centerXY $ centerY finalDia ||| beside' unitX transformedText resultCircleAndPort where borderCol = borderCols !! nestingLevel transformedText = makeTransformedText iconInfo tp maybeFunText separation = circleRadius * 1.5 resultCircleAndPort = makeQualifiedPort name ResultPortConst <> alignR (lc borderCol $ lwG defaultLineWidth $ fc borderCol $ circle circleRadius) triangleAndPorts = vsep separation $ rotate quarterTurn (apply0Triangle borderCol) : zipWith (makeAppInnerIcon iconInfo tp False) argPortsConst args allPorts = makeQualifiedPort name InputPortConst <> alignT triangleAndPorts argBox = alignT $ appArgBox borderCol (width allPorts) (height allPorts) finalDia = argBox <> allPorts -- | Like beside, but it puts the second dia atop the first dia beside' :: (Semigroup a, Juxtaposable a) => V a (N a) -> a -> a -> a beside' dir dia1 dia2 = juxtapose dir dia1 dia2 <> dia1 -- | apply port locations: -- InputPortConst: Function -- ResultPortConst: Result -- Ports 2,3..: Arguments generalNestedDia :: SpecialBackend b n => IconInfo -> (Colour Double -> SpecialQDiagram b n) -> [Colour Double] -> Maybe NamedIcon -> [Maybe NamedIcon] -> TransformableDia b n generalNestedDia iconInfo dia borderCols maybeFunText args tp@(TransformParams name nestingLevel _ _) = named name $ centerXY $ beside' unitX transformedText finalDia where borderCol = borderCols !! nestingLevel transformedText = makeTransformedText iconInfo tp (pure maybeFunText) separation = circleRadius * 1.5 trianglePortsCircle = hsep separation $ reflectX (dia borderCol) : zipWith (makeAppInnerIcon iconInfo tp False) argPortsConst (fmap pure args) ++ [makeQualifiedPort name ResultPortConst <> alignR (lc borderCol $ lwG defaultLineWidth $ fc borderCol $ circle circleRadius) ] allPorts = makeQualifiedPort name InputPortConst <> alignL trianglePortsCircle argBox = alignL $ appArgBox borderCol (width allPorts - circleRadius) (height allPorts) finalDia = argBox <> allPorts nestedApplyDia :: SpecialBackend b n => IconInfo -> LikeApplyFlavor -> Maybe NamedIcon -> [Maybe NamedIcon] -> TransformableDia b n nestedApplyDia iconInfo flavor = case flavor of ApplyNodeFlavor -> generalNestedDia iconInfo apply0Triangle (nestingC colorScheme) ComposeNodeFlavor -> generalNestedDia iconInfo composeSemiCircle (repeat $ apply1C colorScheme) -- END Apply like diagrams -- BEGIN Text boxes and icons -- -- Text constants -- textBoxFontSize :: (Num a) => a textBoxFontSize = 1 monoLetterWidthToHeightFraction :: (Fractional a) => a monoLetterWidthToHeightFraction = 0.61 textBoxHeightFactor :: (Fractional a) => a textBoxHeightFactor = 1.4 textFont :: String textFont = "monospace" -- BEGIN Text helper functions -- -- This may be a faster implementation of normalizeAngle --Get the decimal part of a float -- reduceAngleRange :: SpecialNum a => a -> a -- reduceAngleRange x = x - fromInteger (floor x) -- | Given the number of letters in a textbox string, make a rectangle that will -- enclose the text box. Since the normal SVG text has no size, some hackery is -- needed to determine the size of the text's bounding box. rectForText :: (InSpace V2 n t, TrailLike t) => Int -> t rectForText n = rect rectangleWidth (textBoxFontSize * textBoxHeightFactor) where rectangleWidth = (fromIntegral n * textBoxFontSize * monoLetterWidthToHeightFraction) + (textBoxFontSize * 0.3) -- END Text helper functions commentTextArea :: SpecialBackend b n => Colour Double -> String -> SpecialQDiagram b n commentTextArea textColor t = alignL $ fontSize (local textBoxFontSize) (font textFont $ fc textColor $ topLeftText t) <> alignTL (lw none $ rectForText (length t)) multilineComment :: SpecialBackend b n => Colour Double -> AlphaColour Double -> String -> SpecialQDiagram b n multilineComment textColor _boxColor t = lwG (0.6 * defaultLineWidth) textDia where textLines = lines t textAreas = map (commentTextArea textColor) textLines textDia = vcat textAreas coloredTextBox :: SpecialBackend b n => Colour Double -> AlphaColour Double -> String -> SpecialQDiagram b n coloredTextBox textColor boxColor t = fontSize (local textBoxFontSize) (bold $ font textFont $ fc textColor $ text t) <> lwG (0.6 * defaultLineWidth) (lcA boxColor $ fcA (withOpacity (backgroundC colorScheme) 0.5) $ rectForText (length t)) transformCorrectedTextBox :: SpecialBackend b n => String -> Colour Double -> Colour Double -> Bool -> Angle n -> SpecialQDiagram b n transformCorrectedTextBox str textCol borderCol reflect angle = rotateBy textBoxRotation (reflectIfTrue reflect (coloredTextBox textCol (opaque borderCol) str)) where -- If normalizeAngle is slow, the commented out function reduceAngleRange -- might be faster. reducedAngle = normalizeAngle angle ^. turn textBoxRotation = if (reducedAngle > (1/4)) && (reducedAngle < (3/4)) then 1 / 2 else 0 reflectIfTrue shouldReflect dia = if shouldReflect then reflectX dia else dia transformableBindTextBox :: SpecialBackend b n => String -> Bool -> Angle n -> SpecialQDiagram b n transformableBindTextBox str = transformCorrectedTextBox str (bindTextBoxTextC colorScheme) (bindTextBoxC colorScheme) bindTextBox :: SpecialBackend b n => String -> SpecialQDiagram b n bindTextBox = coloredTextBox (bindTextBoxTextC colorScheme) $ opaque (bindTextBoxC colorScheme) textBox :: SpecialBackend b n => String -> TransformableDia b n textBox t (TransformParams name _ reflect angle) = nameDiagram name $ transformCorrectedTextBox t (textBoxTextC colorScheme) (textBoxC colorScheme) reflect angle -- END Text boxes and icons -- BEGIN MultiIf and case icons -- multiIfSize :: (Fractional a) => a multiIfSize = 0.7 multiIfTriangle :: SpecialBackend b n => SpecialQDiagram b n -> SpecialQDiagram b n multiIfTriangle portDia = alignL $ alignR (triangleAndPort ||| lwG defaultLineWidth (hrule (multiIfSize * 0.8))) <> portDia where triangleAndPort = alignR $ alignT $ lwG defaultLineWidth $ rotateBy (1/8) $ polygon (polyType .~ PolySides [90 @@ deg, 45 @@ deg] [multiIfSize, multiIfSize] $ with) -- | generalNestedMultiIf port layout: -- 0 -> top -- 1 -> bottom -- odds -> left -- evens -> right generalNestedMultiIf :: SpecialBackend b n => IconInfo -> Colour Double -> (SpecialQDiagram b n -> SpecialQDiagram b n) -> SpecialQDiagram b n -> [Maybe NamedIcon] -> TransformableDia b n generalNestedMultiIf iconInfo triangleColor lBracket bottomDia inputAndArgs (TransformParams name nestingLevel reflect angle) = named name $ case inputAndArgs of [] -> mempty input : args -> centerXY finalDia where finalDia = alignT (bottomDia <> makeQualifiedPort name ResultPortConst) <> alignB (inputIcon === (bigVerticalLine <> multiIfDia <> makeQualifiedPort name InputPortConst)) iconMapper (Port portNum) arg | even portNum = Right $ multiIfTriangle port ||| makeInnerIcon True arg | otherwise = Left $ makeInnerIcon False arg ||| lBracket port where port = makeQualifiedPort name (Port portNum) (lBrackets, trianglesWithPorts) = partitionEithers $ zipWith iconMapper argPortsConst args trianglesAndBrackets = zipWith zipper trianglesWithPorts lBrackets zipper thisTriangle lBrack = verticalLine === (alignR (extrudeRight multiIfSize lBrack) <> lc triangleColor (alignL thisTriangle)) where verticalLine = strutY 0.4 inputIcon = makeInnerIcon False input multiIfDia = vcat (alignT trianglesAndBrackets) bigVerticalLine = alignT $ lwG defaultLineWidth $ lc triangleColor $ vrule (height multiIfDia) makeInnerIcon innerReflected mNameAndIcon = case mNameAndIcon of Nothing -> mempty Just (Named iconNodeName icon) -> if innerReflected then reflectX dia else dia where dia = iconToDiagram iconInfo icon (TransformParams iconNodeName nestingLevel (innerReflected /= reflect) angle) multiIfLBracket :: SpecialBackend b n => SpecialQDiagram b n -> SpecialQDiagram b n multiIfLBracket portDia = alignL (alignT ell) <> portDia where ellShape = fromOffsets $ map r2 [(0, multiIfSize), (-multiIfSize, 0)] ell = lineJoin LineJoinRound $ lwG defaultLineWidth $ lc (boolC colorScheme) (strokeLine ellShape) -- | The ports of the multiIf icon are as follows: -- InputPortConst: Top result port (not used) -- ResultPortConst: Bottom result port -- Ports 3,5...: The left ports for the booleans -- Ports 2,4...: The right ports for the values nestedMultiIfDia :: SpecialBackend b n => IconInfo -> [Maybe NamedIcon] -> TransformableDia b n nestedMultiIfDia iconInfo = generalNestedMultiIf iconInfo lineCol multiIfLBracket mempty -- TODO Improve design to be more than a circle. caseResult :: SpecialBackend b n => SpecialQDiagram b n caseResult = lw none $ lc caseCColor $ fc caseCColor $ circle circleRadius where caseCColor = caseRhsC colorScheme caseC :: SpecialBackend b n => SpecialQDiagram b n -> SpecialQDiagram b n caseC portDia = caseResult <> portDia -- | The ports of the case icon are as follows: -- InputPortConst: Top input port -- ResultPortConst: Bottom result port -- Ports 3,5...: The left ports for the results -- Ports 2,4...: The right ports for the patterns nestedCaseDia :: SpecialBackend b n => IconInfo -> [Maybe NamedIcon] -> TransformableDia b n nestedCaseDia iconInfo = generalNestedMultiIf iconInfo (patternC colorScheme) caseC caseResult -- END MultiIf and case icons -- | The ports of flatLambdaIcon are: -- 0: Result icon -- 1: The lambda function value -- 2,3.. : The parameters nestedLambda :: SpecialBackend b n => IconInfo -> [String] -> Maybe NamedIcon -> TransformableDia b n nestedLambda iconInfo paramNames mBodyExp (TransformParams name level reflect angle) = centerXY $ bodyExpIcon ||| centerY (named name finalDia) where lambdaCircle = lwG defaultLineWidth $ lc (regionPerimC colorScheme) $ fc (regionPerimC colorScheme) $ circle (1.85 * circleRadius) lambdaParts = (makeQualifiedPort name InputPortConst <> resultIcon) : (portIcons ++ [makeQualifiedPort name ResultPortConst <> alignR lambdaCircle]) bodyExpIcon = case mBodyExp of Nothing -> mempty Just (Named bodyNodeName bodyIcon) -> iconToDiagram iconInfo bodyIcon (TransformParams bodyNodeName level reflect angle) portIcons = zipWith (makeLabelledPort name reflect angle) paramNames argPortsConst middle = alignL (hsep 0.5 lambdaParts) topAndBottomLineWidth = width middle - (circleRadius + defaultLineWidth) topAndBottomLine = alignL $ lwG defaultLineWidth $ lcA (withOpacity (regionPerimC colorScheme) defaultOpacity) $ hrule topAndBottomLineWidth finalDia = vcat [topAndBottomLine, middle, topAndBottomLine] -- END Main icons -- END Icons