Cohen Sutherland Line Clipping
The HTML file:
<head>
<script src="https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js"></script>
<script type="text/javascript" async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-MML-AM_CHTML"> </script>
<script type="text/x-mathjax-config">
MathJax.Hub.Config({tex2jax: {inlineMath: [['$','$'], ['\\(','\\)']]}});
</script>
<link rel="stylesheet" href="http://mirkwood.cs.edinboro.edu/~bennett/style/hw.css">
</head>
<body>
<h1>Cohen Sutherland Line Clipping</h1>
<canvas id="canvas" width="400" height="400" style="border:1px solid #000000;"></canvas>
<div id="output">
</div>
<script type="text/javascript" src="clipper.js"></script>
<script type="text/javascript" src="ui.js"></script>
<script type="text/javascript">
"use strict"
let canvas = document.getElementById("canvas");
let ctx = canvas.getContext('2d');
canvas.tabIndex = 0;
let width = canvas.width;
let height = canvas.height;
let x0=0,y0=0;
let x1=0, y1=0;
canvas.addEventListener("click", MyHandler);
DrawScene();
</script>
clipper.js
"use strict"
let clipXMin = 25;
let clipYMin = 25;
let clipXMax = 75;
let clipYMax = 75;
let o0, o1;
let text;
function DrawScene() {
ctx.clearRect(0,0, width, height);
ctx.save();
ctx.setTransform(1, 0, 0, -1, 0, height);
ctx.scale(width/100, height/100);
ctx.lineWidth = 100/width;
ctx.setLineDash([2,3]);
ctx.strokeStyle ="blue";
ctx.beginPath();
ctx.moveTo(0,clipYMin);
ctx.lineTo(100,clipYMin);
ctx.moveTo(0,clipYMax);
ctx.lineTo(100,clipYMax);
ctx.moveTo(clipXMin,0);
ctx.lineTo(clipXMin,100);
ctx.moveTo(clipXMax,0);
ctx.lineTo(clipXMax,100);
ctx.stroke();
ctx.setLineDash([]);
ctx.strokeStyle ="black";
ctx.lineWidth = ctx.lineWidth * 2;
ctx.strokeRect(clipXMin, clipYMin, clipXMax-clipXMin, clipYMax-clipYMin);
ctx.lineWidth = ctx.lineWidth * .5;
if (state == 1) {
DrawFirstPoint();
} else if (state == 2) {
DrawTheLine();
}
ctx.restore();
}
function TextPair(x,y) {
x = Math.round(x);
y = Math.round(y);
return "(" + x.toString() + "," + y.toString() + ") ";
}
function MakeCode(x,y) {
let code;
if (y> clipYMax) {
code ="1";
} else {
code = "0";
}
if (y < clipYMin) {
code = code + "1";
} else {
code = code + "0";
}
if (x > clipXMax) {
code = code + "1";
} else {
code = code + "0";
}
if (x < clipXMin) {
code = code + "1";
} else {
code = code + "0";
}
return code;
}
function DrawPoint(x,y, code) {
ctx.beginPath();
ctx.arc(x,y, 1, 0, 2*Math.PI);
ctx.fill();
ctx.save();
ctx.translate(x,y);
ctx.font="2pt sans";
ctx.scale(1,-1);
ctx.strokeText(code,-4,-4);
ctx.restore();
}
function DrawLine(x0,y0, x1, y1){
ctx.beginPath();
ctx.moveTo(x0,y0);
ctx.lineTo(x1,y1);
ctx.stroke();
}
function DrawFirstPoint() {
o0 = MakeCode(x0,y0);
text = "<pre>"
text += "pt1 " + TextPair(x0,y0) + o0;
DrawPoint(x0,y0, o0);
}
function DrawTheLine() {
o1 = MakeCode(x1, y1);
DrawPoint(x0,y0,o0);
text += "\npt2 "+ TextPair(x1,y1) + o1;
DrawPoint(x1,y1, o1);
DrawLine(x0,y0, x1, y1);
DoClip();
text += "</pre>";
document.getElementById("output").innerHTML = text ;
}
function Test1() {
if (o0 == "0000" && o1 == "0000") {
ctx.strokeStyle = "green";
DrawLine(x0,y0, x1,y1);
ctx.strokeStyle = "black";
text += "\nAccept";
return true;
}
return false;
}
function LERP(s,f,alpha) {
return s*(1-alpha)+f*alpha;
}
function ClipUD(a1, a2, b1, b2, newy) {
let alpha = (b2-newy)/(b2-a2);
let x = LERP(b1,a1, alpha);
return([x,newy]);
}
function ClipLR(a1, a2, b1, b2, newx) {
let alpha = (b1-newx)/(b1-a1);
let y = LERP(b2,a2, alpha);
return([newx,y]);
}
function RealClip(a1,a2,b1,b2, pos) {
let value = [0,0];
switch(pos) {
case 0:
text += "\nClip against y="+clipYMax.toString();
value = ClipUD(a1,a2,b1,b2, clipYMax);
break;
case 1:
text += "\nClip against y="+clipYMin.toString();
value = ClipUD(a1,a2,b1,b2, clipYMin);
break;
case 2:
text += "\nClip against x="+clipXMax.toString();
value = ClipLR(a1,a2,b1,b2, clipXMax);
break
case 3:
value = ClipLR(a1,a2,b1,b2, clipXMin);
text += "\nClip against x="+clipXMin.toString();
break
}
return value;
}
function Clip(a1,a2, b1, b2, code) {
for(let i=0;i<4;i++) {
if (code.substring(i,i+1) == "1"){
let value = RealClip(a1,a2,b1,b2, i);
b1 = value[0];
b2 = value[1];
code = MakeCode(b1,b2);
break;
}
}
ctx.fillStyle = "red";
ctx.strokeStyle = "red";
DrawPoint(b1,b2,code);
ctx.fillStyle = "black";
ctx.strokeStyle = "black";
return ([b1,b2,code]);
}
function Test3() {
for(let i = 0; i < 4; i++) {
if (o0.substring(i,i+1) == "1" && o1.substring(i,i+1)== "1") {
return true;
}
}
return false;
}
function DoClip() {
if (Test1() ) {
return true;
}
if (o0=="0000") {
text += "\nTest 2 passed, keep p1";
let values = Clip(x0,y0,x1,y1,o1);
x1 = values[0];
y1 = values[1];
o1 = values[2];
text += "\n\tNew p2 "+ TextPair(x1,y1) + o1;
DoClip();
}
if (o1== "0000") {
text += "\nTest 2 passed, keep p2";
let values = Clip(x1,y1,x0,y0,o0);
x0 = values[0];
y0 = values[1];
o0 = values[2];
text += "\n\tNew p1 "+ TextPair(x0,y0) + o0;
DoClip();
}
if (Test3()) {
text += "\n\nTest3 passed ";
text += "\n\tReject the line, nothing left";
} else {
text += "\n\nTest3 Failed ";
text += "\n\tClip Anything";
let values = Clip(x0,y0,x1,y1,o1);
x1 = values[0];
y1 = values[1];
o1 = values[2];
text += "\n\tNew p2 "+ TextPair(x1,y1) + o1;
DoClip();
}
}
ui.js
"use strict"
let state = 0;
function MyHandler(evnt) {
switch(state) {
case 0:
x0= evnt.offsetX;
y0 = evnt.offsetY;
x0 = x0 * 100/width;
y0 = (height-y0)* 100/width;
state = 1;
DrawScene();
break;
case 1:
x1= evnt.offsetX;
y1 = evnt.offsetY;
x1 = x1 * 100/width;
y1 = (height-y1)* 100/width;
state = 2;
DrawScene();
state = 0;
}
return;
}