Please bookmark this page to avoid losing your image tool!

Image Tire Track Filter Effect Application

(Free & Supports Bulk Upload)

Drag & drop your images here or

The result will appear here...
You can edit the below JavaScript code to customize the image tool.
async function processImage(originalImg, trackColor = "rgba(0,0,0,0.6)", trackWidth = 10, trackSpacing = 15, angleDegrees = 25, useDash = true, dashOnLength = 30, dashOffLength = 20) {
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');

    const w = originalImg.width;
    const h = originalImg.height;

    if (w === 0 || h === 0) {
        canvas.width = 0;
        canvas.height = 0;
        // console.warn("Image has zero width or height.");
        return canvas;
    }

    canvas.width = w;
    canvas.height = h;

    // Draw the original image
    ctx.drawImage(originalImg, 0, 0, w, h);

    // Save context state to isolate filter changes
    ctx.save();

    // Tire track settings
    ctx.strokeStyle = trackColor;
    ctx.lineWidth = trackWidth;
    ctx.lineCap = "square"; // "butt", "round", "square" (square looks more like treads)

    if (useDash) {
        if (dashOnLength > 0 && dashOffLength > 0) {
            ctx.setLineDash([dashOnLength, dashOffLength]);
        } else {
            // If dash lengths are invalid or zero, draw solid line
            ctx.setLineDash([]);
        }
    } else {
        ctx.setLineDash([]); // Solid line
    }

    const angleRadians = angleDegrees * Math.PI / 180;
    const sR = Math.sin(angleRadians); // sin of track angle
    const cR = Math.cos(angleRadians); // cos of track angle

    // P is the period of the pattern: sum of track width and the gap between tracks.
    // This represents distance from the start-edge-of-track to start-edge-of-next-track,
    // or centerline-to-centerline.
    const P = trackWidth + trackSpacing; 
    
    if (P <= 0 || trackWidth <= 0) { 
        // console.warn("Invalid track parameters (P or trackWidth non-positive).");
        ctx.restore();
        return canvas;
    }

    // Calculate the projection range of the canvas corners onto the normal vector of the lines.
    // The normal vector to the track lines is (-sR, cR) if tracks are defined by direction (cR, sR).
    // Line equation used: -sR * x + cR * y = d_val (where d_val is the signed distance from origin along normal)
    const projections = [
        -sR * 0 + cR * 0,   // Projection of (0,0)
        -sR * w + cR * 0,   // Projection of (w,0)
        -sR * 0 + cR * h,   // Projection of (0,h)
        -sR * w + cR * h    // Projection of (w,h)
    ];
    const minProj = Math.min(...projections);
    const maxProj = Math.max(...projections);

    // Iterate d_val (centerline parameter for tracks) to cover the canvas.
    // Loop from slightly before minProj to slightly after maxProj to ensure full coverage.
    // The actual lines drawn will have `lineWidth = trackWidth`.
    // The loop iterates over the center positions `d_val` of these lines.
    for (let d_val = minProj - P; d_val < maxProj + P; d_val += P) {
        ctx.beginPath();
        
        const intersectionPoints = [];
        const tolerance = 1e-5; // For floating point comparisons (e.g., cR near zero)

        // Calculate intersections with vertical canvas edges (x=0 and x=w)
        if (Math.abs(cR) > tolerance) { // If track line is not perfectly vertical (angle != 90 or 270 deg)
            let y_at_x0 = d_val / cR;
            if (y_at_x0 >= -tolerance && y_at_x0 <= h + tolerance) intersectionPoints.push({ x: 0, y: y_at_x0 });
            
            let y_at_xw = (d_val + sR * w) / cR;
            if (y_at_xw >= -tolerance && y_at_xw <= h + tolerance) intersectionPoints.push({ x: w, y: y_at_xw });
        } else { // Track line IS vertical (angle is 90 or 270 deg, so cR is ~0, sR is ~+/-1)
            let x_val = -d_val / sR; // from equation -sR*x = d_val (since cR=0)
            if (x_val >= -tolerance && x_val <= w + tolerance) {
                intersectionPoints.push({ x: x_val, y: 0 });
                intersectionPoints.push({ x: x_val, y: h });
            }
        }

        // Calculate intersections with horizontal canvas edges (y=0 and y=h)
        if (Math.abs(sR) > tolerance) { // If track line is not perfectly horizontal (angle != 0 or 180 deg)
            let x_at_y0 = -d_val / sR; // from -sR*x = d_val (for y=0)
            if (x_at_y0 >= -tolerance && x_at_y0 <= w + tolerance) intersectionPoints.push({ x: x_at_y0, y: 0 });
            
            let x_at_yh = (cR * h - d_val) / sR; // from -sR*x + cR*h = d_val  => x = (cR*h - d_val)/sR
            if (x_at_yh >= -tolerance && x_at_yh <= w + tolerance) intersectionPoints.push({ x: x_at_yh, y: h });
        } else { // Track line IS horizontal (angle is 0 or 180 deg, so sR is ~0, cR is ~+/-1)
            let y_val = d_val / cR; // from cR*y = d_val (since sR=0)
            if (y_val >= -tolerance && y_val <= h + tolerance) {
                intersectionPoints.push({ x: 0, y: y_val });
                intersectionPoints.push({ x: w, y: y_val });
            }
        }
        
        // Filter for unique points and clamp them to be strictly within canvas boundaries.
        // This handles cases where intersection points are calculated multiple times (e.g., at corners)
        // or are slightly outside due to floating point inaccuracies.
        const uniqueClampedPoints = [];
        if (intersectionPoints.length >= 2) {
            const seen = new Set();
            for (const p of intersectionPoints) {
                // Clamp coordinates to be strictly within or on canvas boundaries
                const clamped_x = Math.max(0, Math.min(w, p.x));
                const clamped_y = Math.max(0, Math.min(h, p.y));
                
                // Use a string key with fixed precision for robust uniqueness check
                const key = `${clamped_x.toFixed(4)},${clamped_y.toFixed(4)}`; 
                if (!seen.has(key)) {
                    uniqueClampedPoints.push({ x: clamped_x, y: clamped_y });
                    seen.add(key);
                }
            }
        }

        // If we have at least two unique points, they define the line segment across the canvas.
        if (uniqueClampedPoints.length >= 2) {
            ctx.moveTo(uniqueClampedPoints[0].x, uniqueClampedPoints[0].y);
            // A line segment crossing a rectangle typically has 2 unique intersection points.
            // If more are found (e.g. numerical issues or collinearity with an edge),
            // using only the first two found unique clamped points is a common simplification.
            ctx.lineTo(uniqueClampedPoints[1].x, uniqueClampedPoints[1].y);
            ctx.stroke();
        }
    }

    // Restore context state to remove line dash, stroke style etc.
    ctx.restore();

    return canvas;
}

Free Image Tool Creator

Can't find the image tool you're looking for?
Create one based on your own needs now!

Description

The Image Tire Track Filter Effect Application allows users to apply a customizable tire track effect to their images. This tool enables users to modify parameters such as track color, width, spacing, angle, and line style (dashed or solid) to achieve the desired visual effect. Real-world use cases for this tool include enhancing photos for automotive-themed content, creating unique artwork, or adding stylized effects to images for promotional materials or social media posts.

Leave a Reply

Your email address will not be published. Required fields are marked *