Home
Random example
Search
Applications
Chemistry
Economy
Famous theorems
Geography
Physics
Sports
Test
Assessment
Calculus
3D
Applied calculus
Basic calculus
Differential equations
Function plotting
Implicit plotting
Sequences and series
Charts and data
Charts
Statistics
Curves
Interpolation
Intersection, Union, Difference
Lindenmayer Systems
Splines
Geometry
3D
Analytic
Euclidean
Basic constructions
Mappings
Non-Euclidean
Projective
Symmetry
Technical
Accessibility
Animation
Roulettes
Board options
First steps
Images
JSXGraph objects
Arcs and angles
Axes
Circles
Glider
Groups
Lines and arrows
Point
Polygons
Slider
Turtle
Vectors
JessieCode
Texts
Transformations
Video
sketch
jsxgraph.org
JSXGraph logo
JSXGraph
JSXGraph share

Share

Soccer offside line
Show plain example
QR code
<iframe 
    src="https://jsxgraph.uni-bayreuth.de/share/iframe/soccer-offside-line" 
    style="border: 1px solid black; overflow: hidden; width: 550px; aspect-ratio: 55 / 65;" 
    name="JSXGraph example: Soccer offside line" 
    allowfullscreen
></iframe>
This code has to
<div id="board-0-wrapper" class="jxgbox-wrapper " style="width: 100%; ">
   <div id="board-0" class="jxgbox" style="aspect-ratio: 4 / 3; width: 30%;" data-ar="4 / 3"></div>
</div>
<div id="board-1-wrapper" class="jxgbox-wrapper " style="width: 100%; ">
   <div id="board-1" class="jxgbox" style="aspect-ratio: 4 / 3; width: 30%;" data-ar="4 / 3"></div>
</div>
<div id="board-2-wrapper" class="jxgbox-wrapper " style="width: 100%; ">
   <div id="board-2" class="jxgbox" style="aspect-ratio: 4 / 3; width: 30%;" data-ar="4 / 3"></div>
</div>

<script type = "text/javascript"> 
    /*
    This example is licensed under a 
    Creative Commons Attribution ShareAlike 4.0 International License.
    https://creativecommons.org/licenses/by-sa/4.0/
    
    Please note you have to mention 
    The Center of Mobile Learning with Digital Technology
    in the credits.
    */
    
    const BOARDID0 = 'board-0';
    const BOARDID1 = 'board-1';
    const BOARDID2 = 'board-2';
    const BOARDID = BOARDID0;

    const board1 = JXG.JSXGraph.initBoard(BOARDID0, { boundingbox: [-10, 10, 10, -10] });
    const board2 = JXG.JSXGraph.initBoard(BOARDID1, { boundingbox: [-10, 10, 10, -10] });
    const board3 = JXG.JSXGraph.initBoard(BOARDID2, { boundingbox: [-10, 10, 10, -10] });
    
    board1.addChild(board2);
    board1.addChild(board3);
    
    // Polygon in camera image
    var p1 = board1.create('polygon', [[-8, -6], [9, -6], [5, 6.5], [-6, 7]], { fillColor: 'green' });
    
    // Polygon in model view
    var p2 = board2.create('polygon', [[-5, -4], [5, -4], [5, 4], [-5, 4]], {
        fillColor: 'green',
        vertices: { visible: false, fixed: true }
    });
    
    // Polygon on TV screen (copy of p1)
    var p3 = board3.create('polygon', [
             [() => p1.vertices[0].X(), () => p1.vertices[0].Y()],
             [() => p1.vertices[1].X(), () => p1.vertices[1].Y()],
             [() => p1.vertices[2].X(), () => p1.vertices[2].Y()],
             [() => p1.vertices[3].X(), () => p1.vertices[3].Y()]
         ], { fillColor: 'green', vertices: { visible: false } });
    
    //
    // Compute a projective transformation which maps the polygon p1 to the polygon p2.
    //    
    
    // Two global variables containing the transformation matrix (in vector and in matrix form)
    var x_global = [],
        x2_global = [],
        mat_global = [[0, 0, 0], [0, 0, 0], [0, 0, 0]],
        mat2_global = [[0, 0, 0], [0, 0, 0], [0, 0, 0]];
    
    // This function computes the transformation matrix
    var updateTransformationMatrix = function() {
        var i, j, k, M = [];
    
        // Initialise a 13x13 matrix to zero.
        for (i = 0; i < 13; i++) {
            M.push([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
        }
    
        // Set up the system of linear equations:
        // 12 equations and 13 unknowns for the matrix 
        // mat_global such that
        // mat_global * p1 - p2 * (i, j, k, l)^T = 0
        for (i = 0; i < 3; i++) {
            for (j = 0; j < 3; j++) {
                for (k = 0; k < 4; k++) {
                    M[i * 4 + k][i * 3 + j] = p1.vertices[k].coords.usrCoords[j];
                }
            }
        }
        for (i = 0; i < 3; i++) {
            for (k = 0; k < 4; k++) {
                M[i * 4 + k][9 + k] = -p2.vertices[k].coords.usrCoords[i];
            }
        }
        // Equation 13: set mat_global[0][0] = 1.
        // Remember that in JSXGraph the coordinates are ordered by (z, x, y)
        M[12][0] = 1;
    
        // Right hand side vector
        var b = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1];
    
        // Solve the system
        x_global = JXG.Math.Numerics.Gauss(M, b);
    
        // Convert the solution vector into matrix form
        for (i = 0; i < 3; i++) {
            for (j = 0; j < 3; j++) {
                mat_global[i][j] = x_global[i * 3 + j];
            }
        }
    
        // Invert the matrix to get the inverse transform
        mat2_global = JXG.Math.inverse(mat_global);
    
        // Convert the matrix into vector form
        for (i = 0; i < 3; i++) {
            for (j = 0; j < 3; j++) {
                x2_global[i * 3 + j] = mat2_global[i][j];
            }
        }
    };
    
    // First computation of the transformation matrix
    updateTransformationMatrix();
    
    // Store the transformation vectors in functions
    // in order to make the JSXGraph transformation dynamic
    var x_fcts = [],
        x2_fcts = [];
    for (let i = 0; i < 9; i++) {
        x_fcts[i] = () => x_global[i];
        x2_fcts[i] = () => x2_global[i];
    }
    
    // Create the transform from p1 to p2 and its inverse.
    var transform = board1.create('transform', x_fcts, { type: 'generic' });
    var transform2 = board2.create('transform', x2_fcts, { type: 'generic' });
    
    // Whenever a point of p1 is dragged, the transformation matrix and its 
    // inverse will be updated.
    // Whenever a point of p1 is dragged, the transformation matrix and its 
    // inverse will be updated.
    for (let i = 0; i < 4; i++) {
        p1.vertices[i].on('drag', updateTransformationMatrix);
    }
    
    var player = { fillColor: 'white', strokeColor: 'black', size: 6 };
    
    // Set a point in the camera image
    var A = board1.create('point', [-2.5, 0], player);
    
    // Create its image and a vertical line through the point in the model
    var A2 = board2.create('point', [A, transform], player);
    var line1 = board2.create('segment', [
                [() => A2.X(), -4],
                [() => A2.X(), 4]], { strokeColor: 'yellow' });
    
    // Transform the point and the line into the TV screen
    var A3 = board3.create('point', [A2, transform2], player);
    
    var line2 = board3.create('segment', [line1, transform2], { strokeColor: 'yellow' });
    
 </script> 
/*
This example is licensed under a 
Creative Commons Attribution ShareAlike 4.0 International License.
https://creativecommons.org/licenses/by-sa/4.0/

Please note you have to mention 
The Center of Mobile Learning with Digital Technology
in the credits.
*/

const BOARDID0 = 'your_div_id_0'; // Insert your 1st board id here!
const BOARDID1 = 'your_div_id_1'; // Insert your 2nd board id here!
const BOARDID2 = 'your_div_id_2'; // Insert your 3rd board id here!

const board1 = JXG.JSXGraph.initBoard(BOARDID0, { boundingbox: [-10, 10, 10, -10] });
const board2 = JXG.JSXGraph.initBoard(BOARDID1, { boundingbox: [-10, 10, 10, -10] });
const board3 = JXG.JSXGraph.initBoard(BOARDID2, { boundingbox: [-10, 10, 10, -10] });

board1.addChild(board2);
board1.addChild(board3);

// Polygon in camera image
var p1 = board1.create('polygon', [[-8, -6], [9, -6], [5, 6.5], [-6, 7]], { fillColor: 'green' });

// Polygon in model view
var p2 = board2.create('polygon', [[-5, -4], [5, -4], [5, 4], [-5, 4]], {
    fillColor: 'green',
    vertices: { visible: false, fixed: true }
});

// Polygon on TV screen (copy of p1)
var p3 = board3.create('polygon', [
         [() => p1.vertices[0].X(), () => p1.vertices[0].Y()],
         [() => p1.vertices[1].X(), () => p1.vertices[1].Y()],
         [() => p1.vertices[2].X(), () => p1.vertices[2].Y()],
         [() => p1.vertices[3].X(), () => p1.vertices[3].Y()]
     ], { fillColor: 'green', vertices: { visible: false } });

//
// Compute a projective transformation which maps the polygon p1 to the polygon p2.
//    

// Two global variables containing the transformation matrix (in vector and in matrix form)
var x_global = [],
    x2_global = [],
    mat_global = [[0, 0, 0], [0, 0, 0], [0, 0, 0]],
    mat2_global = [[0, 0, 0], [0, 0, 0], [0, 0, 0]];

// This function computes the transformation matrix
var updateTransformationMatrix = function() {
    var i, j, k, M = [];

    // Initialise a 13x13 matrix to zero.
    for (i = 0; i < 13; i++) {
        M.push([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
    }

    // Set up the system of linear equations:
    // 12 equations and 13 unknowns for the matrix 
    // mat_global such that
    // mat_global * p1 - p2 * (i, j, k, l)^T = 0
    for (i = 0; i < 3; i++) {
        for (j = 0; j < 3; j++) {
            for (k = 0; k < 4; k++) {
                M[i * 4 + k][i * 3 + j] = p1.vertices[k].coords.usrCoords[j];
            }
        }
    }
    for (i = 0; i < 3; i++) {
        for (k = 0; k < 4; k++) {
            M[i * 4 + k][9 + k] = -p2.vertices[k].coords.usrCoords[i];
        }
    }
    // Equation 13: set mat_global[0][0] = 1.
    // Remember that in JSXGraph the coordinates are ordered by (z, x, y)
    M[12][0] = 1;

    // Right hand side vector
    var b = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1];

    // Solve the system
    x_global = JXG.Math.Numerics.Gauss(M, b);

    // Convert the solution vector into matrix form
    for (i = 0; i < 3; i++) {
        for (j = 0; j < 3; j++) {
            mat_global[i][j] = x_global[i * 3 + j];
        }
    }

    // Invert the matrix to get the inverse transform
    mat2_global = JXG.Math.inverse(mat_global);

    // Convert the matrix into vector form
    for (i = 0; i < 3; i++) {
        for (j = 0; j < 3; j++) {
            x2_global[i * 3 + j] = mat2_global[i][j];
        }
    }
};

// First computation of the transformation matrix
updateTransformationMatrix();

// Store the transformation vectors in functions
// in order to make the JSXGraph transformation dynamic
var x_fcts = [],
    x2_fcts = [];
for (let i = 0; i < 9; i++) {
    x_fcts[i] = () => x_global[i];
    x2_fcts[i] = () => x2_global[i];
}

// Create the transform from p1 to p2 and its inverse.
var transform = board1.create('transform', x_fcts, { type: 'generic' });
var transform2 = board2.create('transform', x2_fcts, { type: 'generic' });

// Whenever a point of p1 is dragged, the transformation matrix and its 
// inverse will be updated.
// Whenever a point of p1 is dragged, the transformation matrix and its 
// inverse will be updated.
for (let i = 0; i < 4; i++) {
    p1.vertices[i].on('drag', updateTransformationMatrix);
}

var player = { fillColor: 'white', strokeColor: 'black', size: 6 };

// Set a point in the camera image
var A = board1.create('point', [-2.5, 0], player);

// Create its image and a vertical line through the point in the model
var A2 = board2.create('point', [A, transform], player);
var line1 = board2.create('segment', [
            [() => A2.X(), -4],
            [() => A2.X(), 4]], { strokeColor: 'yellow' });

// Transform the point and the line into the TV screen
var A3 = board3.create('point', [A2, transform2], player);

var line2 = board3.create('segment', [line1, transform2], { strokeColor: 'yellow' });
<jsxgraph width="30%, 30%, 30%" aspect-ratio="4 / 3, 4 / 3, 4 / 3" numberOfBoards="3" title="Soccer offside line" description="This construction was copied from JSXGraph examples database: BTW HERE SHOULD BE A GENERATED LINKuseGlobalJS="false">
   /*
   This example is licensed under a 
   Creative Commons Attribution ShareAlike 4.0 International License.
   https://creativecommons.org/licenses/by-sa/4.0/
   
   Please note you have to mention 
   The Center of Mobile Learning with Digital Technology
   in the credits.
   */
   
   const board1 = JXG.JSXGraph.initBoard(BOARDID0, { boundingbox: [-10, 10, 10, -10] });
   const board2 = JXG.JSXGraph.initBoard(BOARDID1, { boundingbox: [-10, 10, 10, -10] });
   const board3 = JXG.JSXGraph.initBoard(BOARDID2, { boundingbox: [-10, 10, 10, -10] });
   
   board1.addChild(board2);
   board1.addChild(board3);
   
   // Polygon in camera image
   var p1 = board1.create('polygon', [[-8, -6], [9, -6], [5, 6.5], [-6, 7]], { fillColor: 'green' });
   
   // Polygon in model view
   var p2 = board2.create('polygon', [[-5, -4], [5, -4], [5, 4], [-5, 4]], {
       fillColor: 'green',
       vertices: { visible: false, fixed: true }
   });
   
   // Polygon on TV screen (copy of p1)
   var p3 = board3.create('polygon', [
            [() => p1.vertices[0].X(), () => p1.vertices[0].Y()],
            [() => p1.vertices[1].X(), () => p1.vertices[1].Y()],
            [() => p1.vertices[2].X(), () => p1.vertices[2].Y()],
            [() => p1.vertices[3].X(), () => p1.vertices[3].Y()]
        ], { fillColor: 'green', vertices: { visible: false } });
   
   //
   // Compute a projective transformation which maps the polygon p1 to the polygon p2.
   //    
   
   // Two global variables containing the transformation matrix (in vector and in matrix form)
   var x_global = [],
       x2_global = [],
       mat_global = [[0, 0, 0], [0, 0, 0], [0, 0, 0]],
       mat2_global = [[0, 0, 0], [0, 0, 0], [0, 0, 0]];
   
   // This function computes the transformation matrix
   var updateTransformationMatrix = function() {
       var i, j, k, M = [];
   
       // Initialise a 13x13 matrix to zero.
       for (i = 0; i < 13; i++) {
           M.push([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
       }
   
       // Set up the system of linear equations:
       // 12 equations and 13 unknowns for the matrix 
       // mat_global such that
       // mat_global * p1 - p2 * (i, j, k, l)^T = 0
       for (i = 0; i < 3; i++) {
           for (j = 0; j < 3; j++) {
               for (k = 0; k < 4; k++) {
                   M[i * 4 + k][i * 3 + j] = p1.vertices[k].coords.usrCoords[j];
               }
           }
       }
       for (i = 0; i < 3; i++) {
           for (k = 0; k < 4; k++) {
               M[i * 4 + k][9 + k] = -p2.vertices[k].coords.usrCoords[i];
           }
       }
       // Equation 13: set mat_global[0][0] = 1.
       // Remember that in JSXGraph the coordinates are ordered by (z, x, y)
       M[12][0] = 1;
   
       // Right hand side vector
       var b = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1];
   
       // Solve the system
       x_global = JXG.Math.Numerics.Gauss(M, b);
   
       // Convert the solution vector into matrix form
       for (i = 0; i < 3; i++) {
           for (j = 0; j < 3; j++) {
               mat_global[i][j] = x_global[i * 3 + j];
           }
       }
   
       // Invert the matrix to get the inverse transform
       mat2_global = JXG.Math.inverse(mat_global);
   
       // Convert the matrix into vector form
       for (i = 0; i < 3; i++) {
           for (j = 0; j < 3; j++) {
               x2_global[i * 3 + j] = mat2_global[i][j];
           }
       }
   };
   
   // First computation of the transformation matrix
   updateTransformationMatrix();
   
   // Store the transformation vectors in functions
   // in order to make the JSXGraph transformation dynamic
   var x_fcts = [],
       x2_fcts = [];
   for (let i = 0; i < 9; i++) {
       x_fcts[i] = () => x_global[i];
       x2_fcts[i] = () => x2_global[i];
   }
   
   // Create the transform from p1 to p2 and its inverse.
   var transform = board1.create('transform', x_fcts, { type: 'generic' });
   var transform2 = board2.create('transform', x2_fcts, { type: 'generic' });
   
   // Whenever a point of p1 is dragged, the transformation matrix and its 
   // inverse will be updated.
   // Whenever a point of p1 is dragged, the transformation matrix and its 
   // inverse will be updated.
   for (let i = 0; i < 4; i++) {
       p1.vertices[i].on('drag', updateTransformationMatrix);
   }
   
   var player = { fillColor: 'white', strokeColor: 'black', size: 6 };
   
   // Set a point in the camera image
   var A = board1.create('point', [-2.5, 0], player);
   
   // Create its image and a vertical line through the point in the model
   var A2 = board2.create('point', [A, transform], player);
   var line1 = board2.create('segment', [
               [() => A2.X(), -4],
               [() => A2.X(), 4]], { strokeColor: 'yellow' });
   
   // Transform the point and the line into the TV screen
   var A3 = board3.create('point', [A2, transform2], player);
   
   var line2 = board3.create('segment', [line1, transform2], { strokeColor: 'yellow' });
   
</jsxgraph>

Soccer offside line

Geometry
Sports
A TV camera broadcasting a soccer game "sees" a general quadrangle, not a rectangle. The question is how to add an offside line at the correct position? This is done with a little bit of Linear Algebra. __Left__ is the image the camera sees, the __middle__ is the model, __right__ is the image broadcasted to the TV screens
// Define the ids of your boards in BOARDID0, BOARDID1,...

const board1 = JXG.JSXGraph.initBoard(BOARDID0, { boundingbox: [-10, 10, 10, -10] });
const board2 = JXG.JSXGraph.initBoard(BOARDID1, { boundingbox: [-10, 10, 10, -10] });
const board3 = JXG.JSXGraph.initBoard(BOARDID2, { boundingbox: [-10, 10, 10, -10] });

board1.addChild(board2);
board1.addChild(board3);

// Polygon in camera image
var p1 = board1.create('polygon', [[-8, -6], [9, -6], [5, 6.5], [-6, 7]], { fillColor: 'green' });

// Polygon in model view
var p2 = board2.create('polygon', [[-5, -4], [5, -4], [5, 4], [-5, 4]], {
    fillColor: 'green',
    vertices: { visible: false, fixed: true }
});

// Polygon on TV screen (copy of p1)
var p3 = board3.create('polygon', [
         [() => p1.vertices[0].X(), () => p1.vertices[0].Y()],
         [() => p1.vertices[1].X(), () => p1.vertices[1].Y()],
         [() => p1.vertices[2].X(), () => p1.vertices[2].Y()],
         [() => p1.vertices[3].X(), () => p1.vertices[3].Y()]
     ], { fillColor: 'green', vertices: { visible: false } });

//
// Compute a projective transformation which maps the polygon p1 to the polygon p2.
//    

// Two global variables containing the transformation matrix (in vector and in matrix form)
var x_global = [],
    x2_global = [],
    mat_global = [[0, 0, 0], [0, 0, 0], [0, 0, 0]],
    mat2_global = [[0, 0, 0], [0, 0, 0], [0, 0, 0]];

// This function computes the transformation matrix
var updateTransformationMatrix = function() {
    var i, j, k, M = [];

    // Initialise a 13x13 matrix to zero.
    for (i = 0; i < 13; i++) {
        M.push([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
    }

    // Set up the system of linear equations:
    // 12 equations and 13 unknowns for the matrix 
    // mat_global such that
    // mat_global * p1 - p2 * (i, j, k, l)^T = 0
    for (i = 0; i < 3; i++) {
        for (j = 0; j < 3; j++) {
            for (k = 0; k < 4; k++) {
                M[i * 4 + k][i * 3 + j] = p1.vertices[k].coords.usrCoords[j];
            }
        }
    }
    for (i = 0; i < 3; i++) {
        for (k = 0; k < 4; k++) {
            M[i * 4 + k][9 + k] = -p2.vertices[k].coords.usrCoords[i];
        }
    }
    // Equation 13: set mat_global[0][0] = 1.
    // Remember that in JSXGraph the coordinates are ordered by (z, x, y)
    M[12][0] = 1;

    // Right hand side vector
    var b = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1];

    // Solve the system
    x_global = JXG.Math.Numerics.Gauss(M, b);

    // Convert the solution vector into matrix form
    for (i = 0; i < 3; i++) {
        for (j = 0; j < 3; j++) {
            mat_global[i][j] = x_global[i * 3 + j];
        }
    }

    // Invert the matrix to get the inverse transform
    mat2_global = JXG.Math.inverse(mat_global);

    // Convert the matrix into vector form
    for (i = 0; i < 3; i++) {
        for (j = 0; j < 3; j++) {
            x2_global[i * 3 + j] = mat2_global[i][j];
        }
    }
};

// First computation of the transformation matrix
updateTransformationMatrix();

// Store the transformation vectors in functions
// in order to make the JSXGraph transformation dynamic
var x_fcts = [],
    x2_fcts = [];
for (let i = 0; i < 9; i++) {
    x_fcts[i] = () => x_global[i];
    x2_fcts[i] = () => x2_global[i];
}

// Create the transform from p1 to p2 and its inverse.
var transform = board1.create('transform', x_fcts, { type: 'generic' });
var transform2 = board2.create('transform', x2_fcts, { type: 'generic' });

// Whenever a point of p1 is dragged, the transformation matrix and its 
// inverse will be updated.
// Whenever a point of p1 is dragged, the transformation matrix and its 
// inverse will be updated.
for (let i = 0; i < 4; i++) {
    p1.vertices[i].on('drag', updateTransformationMatrix);
}

var player = { fillColor: 'white', strokeColor: 'black', size: 6 };

// Set a point in the camera image
var A = board1.create('point', [-2.5, 0], player);

// Create its image and a vertical line through the point in the model
var A2 = board2.create('point', [A, transform], player);
var line1 = board2.create('segment', [
            [() => A2.X(), -4],
            [() => A2.X(), 4]], { strokeColor: 'yellow' });

// Transform the point and the line into the TV screen
var A3 = board3.create('point', [A2, transform2], player);

var line2 = board3.create('segment', [line1, transform2], { strokeColor: 'yellow' });

license

This example is licensed under a Creative Commons Attribution ShareAlike 4.0 International License.
Please note you have to mention The Center of Mobile Learning with Digital Technology in the credits.