Created
July 31, 2012 17:04
-
-
Save markusl/3218518 to your computer and use it in GitHub Desktop.
FSharp translation of the Cohen–Sutherland 2D line clipping algorithm
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// FSharp translation of the Cohen–Sutherland 2D line clipping algorithm | |
/// http://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm | |
/// | |
/// Implemented in purely functional form which means there are no mutable variables. | |
/// No guarantees are given on the performance or reliability, it's not | |
/// thoroughly tested so feel free to submit unit tests. :-) | |
/// | |
/// Markus Lindqvist 07/2012 | |
module CohenSutherland | |
// Enumeration which defines if the point lies inside or outside the given area | |
type OutCode = Inside = 0 | Left = 1 | Right = 2 | Bottom = 4 | Top = 8 | |
/// Defines the clipping functionality | |
type CohenSutherland() = | |
/// Clips the given line (p1 - p2) with the given rectangle (from min to max) | |
/// Returns None if line is not within the given rectangle or Some(p1, p2) for clipped line | |
static member clip (min:vector, max:vector) (p1:vector) (p2:vector) = | |
// Get the status code for given point (check if point is inside or not) | |
let getOutcode (min:vector) (max:vector) (point:vector) = | |
let getXAxisCode = | |
if point.[0] < min.[0] then OutCode.Left | |
else if point.[0] > max.[0] then OutCode.Right | |
else OutCode.Inside | |
let getYAxisCode = | |
if point.[1] < min.[1] then OutCode.Bottom | |
else if point.[1] > max.[1] then OutCode.Top | |
else OutCode.Inside | |
getXAxisCode ||| getYAxisCode | |
// Recursive function to clip next part of line | |
let rec clipNext outcode1 outcode2 (p1:vector) (p2:vector) = | |
// Completely inside, return points as such | |
if outcode1 ||| outcode2 = OutCode.Inside then | |
Some(p1, p2) | |
// Completely outside, stop here | |
else if outcode1 &&& outcode2 <> OutCode.Inside then | |
None | |
// Process next point, from outcode1 first | |
else | |
let currentCode = if outcode1 <> OutCode.Inside then outcode1 | |
else outcode2 | |
// Extract points to be more readable in equations | |
let x0, y0, x1, y1 = p1.[0], p1.[1], p2.[0], p2.[1] | |
// Clip next point based on outcode1 | |
let getNextPoint = | |
if (currentCode &&& OutCode.Top) <> OutCode.Inside then | |
let x = x0 + (x1 - x0) * (max.[1] - y0) / (y1 - y0) | |
vector [x; max.[1]] | |
else if (currentCode &&& OutCode.Bottom) <> OutCode.Inside then | |
let x = x0 + (x1 - x0) * (min.[1] - y0) / (y1 - y0) | |
vector [x; min.[1]] | |
else if (currentCode &&& OutCode.Right) <> OutCode.Inside then | |
let y = y0 + (y1 - y0) * (max.[0] - x0) / (x1 - x0) | |
vector [max.[0]; y] | |
else if (currentCode &&& OutCode.Left) <> OutCode.Inside then | |
let y = y0 + (y1 - y0) * (min.[0] - x0) / (x1 - x0) | |
vector [min.[0]; y] | |
else | |
vector [System.Double.NaN; System.Double.NaN] | |
// If we processed outcode1, check it again and continue recursively | |
if currentCode = outcode1 then | |
clipNext (getOutcode min max getNextPoint) outcode2 getNextPoint p2 | |
else | |
clipNext outcode1 (getOutcode min max getNextPoint) p1 getNextPoint | |
// Do the first round here and continue recursively | |
let outcode1 = getOutcode min max p1 | |
let outcode2 = getOutcode min max p2 | |
clipNext outcode1 outcode2 p1 p2 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uses Microsoft.FSharp.Math.Vector (add reference to FSharp.PowerPack)