diff --git a/SmoulderingBeachBall.sln b/SmoulderingBeachBall.sln new file mode 100644 index 0000000..d7de6b3 --- /dev/null +++ b/SmoulderingBeachBall.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.28010.2016 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "SmoulderingBeachBall", "SmoulderingBeachBall\SmoulderingBeachBall.fsproj", "{DFC3CBCA-3DA7-4CF4-A8BC-BCCB740FA6CD}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {DFC3CBCA-3DA7-4CF4-A8BC-BCCB740FA6CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DFC3CBCA-3DA7-4CF4-A8BC-BCCB740FA6CD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DFC3CBCA-3DA7-4CF4-A8BC-BCCB740FA6CD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DFC3CBCA-3DA7-4CF4-A8BC-BCCB740FA6CD}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {78F0A77D-F78C-476F-A7C5-64E5579E9EB4} + EndGlobalSection +EndGlobal diff --git a/SmoulderingBeachBall/Domain.fs b/SmoulderingBeachBall/Domain.fs new file mode 100644 index 0000000..286c5d8 --- /dev/null +++ b/SmoulderingBeachBall/Domain.fs @@ -0,0 +1,21 @@ +namespace SmoulderingBeachBall.Domain + + [] + module DomainTypes = + + open System.Drawing + + type OverlayType = + | Border + | Full + + type OverlaySpec = + { colour: Brush; + overlayType: OverlayType } + + type ImageSpec = + { width: int; + height: int; + colour: Brush; + filePath: string; + overlay: OverlaySpec option } \ No newline at end of file diff --git a/SmoulderingBeachBall/InternalServices.fs b/SmoulderingBeachBall/InternalServices.fs new file mode 100644 index 0000000..ac0df8f --- /dev/null +++ b/SmoulderingBeachBall/InternalServices.fs @@ -0,0 +1,65 @@ +module internal InternalServices + + open System.IO + open System.Drawing + open SmoulderingBeachBall.Domain + + module Validation = + + let validateDimension dimension = + match dimension with + | dimension when dimension <= 0 -> + invalidArg "dimension" "The width and height must be greater than 0." + | _ -> () + + let validateDirectory filePath = + let path = Path.GetDirectoryName filePath + match (Directory.Exists path) with + | false -> invalidArg "filePath" "Unable to save to the specified location because it does not exist." + | true -> () + + module Drawing = + + let penOffset penWidth = int (penWidth / (float32 2)) + + let createBorderPath penWidth spec = + let offset = penOffset penWidth + [|Point (0, offset); // Essentially (0, 0) + Point ((spec.width - offset), offset); + Point ((spec.width - offset), (spec.height - offset)); + Point (offset, (spec.height - offset)); + Point (offset, 0)|] + + let drawBorder (graphics: Graphics) (pen: Pen) spec = + printfn "[INFO.] Adding border to image..." + let penPath = createBorderPath pen.Width spec + graphics.DrawLines (pen, penPath) + + + let drawFullOverlay (graphics: Graphics) (pen: Pen) spec = + drawBorder graphics pen spec + printfn "[INFO.] Adding full overlay to image..." + let offset = penOffset pen.Width + // Draws line from top-left to bottom-right of square. + graphics.DrawLine (pen, offset, offset, (spec.width - offset), (spec.height - offset)) + // Draws line from top-right to bottom-left of square. + graphics.DrawLine (pen, (spec.width - offset), offset, offset, (spec.height - offset)) + + let addOverlayToImage graphics spec = + let overlay = spec.overlay.Value + let pen = new Pen (overlay.colour, Width = 10.0f) + match overlay.overlayType with + | Border -> drawBorder graphics pen spec + | Full -> drawFullOverlay graphics pen spec + + let drawImage spec = + let bitmap = new Bitmap (spec.width, spec.height) + let graphics = Graphics.FromImage (bitmap) + let rectangle = Rectangle (0, 0, spec.width, spec.height) + graphics.FillRectangle (spec.colour, rectangle) + match spec.overlay.IsSome with + | true -> addOverlayToImage graphics spec + | false -> printfn "[INFO.] No overlay specified. Creating image without one." + bitmap.Save (spec.filePath) + bitmap.Dispose() + graphics.Dispose() \ No newline at end of file diff --git a/SmoulderingBeachBall/ScratchPad.fsx b/SmoulderingBeachBall/ScratchPad.fsx new file mode 100644 index 0000000..544dcb6 --- /dev/null +++ b/SmoulderingBeachBall/ScratchPad.fsx @@ -0,0 +1,41 @@ +#load "Domain.fs" +#load "InternalServices.fs" +#load "Services.fs" + +open System.Drawing +open System.Drawing.Imaging +open SmoulderingBeachBall.Domain.DomainTypes +open SmoulderingBeachBall.Services + +// INITIAL IDEA =============================================================== + +let width = 500 +let height = 500 +let colour = Brushes.BurlyWood +let testPath = "C:/users/craig/desktop/test.png" +let draw () = + use bmp = new Bitmap(width, height) + use gr = Graphics.FromImage(bmp) + gr.FillRectangle(colour, new Rectangle(0, 0, bmp.Width, bmp.Height)) + gr.DrawImage(bmp, 0, 0) + bmp.Save(testPath, ImageFormat.Png) + +// Smouldering Beach Ball Library Code ======================================== + +let borderOverlay = + { colour = Brushes.BlueViolet; + overlayType = Border } + +let fullOverlay = + { colour = Brushes.Fuchsia; + overlayType = Full } + +let imageSpec = + { width = 500; + height = 500; + colour = Brushes.Yellow; + filePath = "C:/users/craig/desktop/test.png"; + // Change this to flip between border/full overlay or None. + overlay = Some fullOverlay } + +makeImage imageSpec |> Async.RunSynchronously \ No newline at end of file diff --git a/SmoulderingBeachBall/Services.fs b/SmoulderingBeachBall/Services.fs new file mode 100644 index 0000000..f394b55 --- /dev/null +++ b/SmoulderingBeachBall/Services.fs @@ -0,0 +1,23 @@ +namespace SmoulderingBeachBall + +module Services = + + open System + open SmoulderingBeachBall.Domain.DomainTypes + open InternalServices.Validation + open InternalServices.Drawing + + let makeImage (spec: ImageSpec) = + async { + try + printfn "[INFO.] Attempting to make image..." + validateDimension spec.width + validateDimension spec.height + validateDirectory spec.filePath + drawImage spec + printfn "[SUCCESS] Image creation attempt complete." + return () + with + | :? ArgumentException as ex -> printfn "%s" ex.Message + | _ as ex -> printfn "%s" ex.Message + } \ No newline at end of file diff --git a/SmoulderingBeachBall/SmoulderingBeachBall.fsproj b/SmoulderingBeachBall/SmoulderingBeachBall.fsproj new file mode 100644 index 0000000..e6eb3b4 --- /dev/null +++ b/SmoulderingBeachBall/SmoulderingBeachBall.fsproj @@ -0,0 +1,18 @@ + + + + netstandard2.0 + + + + + + + + + + + + + +