Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Drawing scalloped polygon between multiple points

I am trying to draw a scalloped path using SVG between multiple points like it is drawn for rectangle here but between multiple points. Expecting two or more two or more selected points to be connected by scalloped line.

But the problems I am facing are,

  1. Scallops are not symmetric or random in sizes. - I solved this
  2. After clicking multiple points scallops directions and up down. Like in below image.

    enter image description here

I am completely ok even if the answer is given in html5 canvas context. I will make adjustments. I am missing some extra calculation but could not figure out what.

Please click multiple times in result page to see the scallops drawn currently

var strokeWidth = 3;

function distance(x1, y1, x2, y2) {
  return Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}

function findNewPoint(x, y, angle, distance) {
  var result = {};
  result.x = Math.round(Math.cos(angle) * distance + x);
  result.y = Math.round(Math.sin(angle) * distance + y);
  return result;
}

function getAngle(x1, y1, x2, y2) {
  return Math.atan2(y2 - y1, x2 - x1);
}

function scapolledLine(points, strokeWidth) {
  var that = this;
  var scallopSize = strokeWidth * 8;
  var path = [],
    newP = null;
  path.push("M", points[0].x, points[0].y);
  points.forEach(function(s, i) {
    var stepW = scallopSize,
      lsw = 0;
    var e = points[i + 1];
    if (!e) {
      path.push('A', stepW / 2, stepW / 2, "0 0 1", s.x, s.y);
      return;
    }
    var args = [s.x, s.y, e.x, e.y];
    var dist = that.distance.apply(that, args);
    if (dist === 0) return;
    var angle = that.getAngle.apply(that, args);
    newP = s;
    // Number of possible scallops between current points
    var n = dist / stepW,
      crumb;

    if (dist < (stepW * 2)) {
      stepW = (dist - stepW) > (stepW * 0.38) ? (dist / 2) : dist;
    } else {
      n = (n - (n % 1));
      crumb = dist - (n * stepW);
      /*if((stepW - crumb) > (stepW * 0.7)) {
          lsw = crumb;
      } else {
          stepW += (crumb / n);
      }*/
      stepW += (crumb / n);
    }

    // Recalculate possible scallops.
    n = dist / stepW;
    var aw = stepW / 2;
    for (var i = 0; i < n; i++) {
      newP = that.findNewPoint(newP.x, newP.y, angle, stepW);
      if (i === (n - 1)) {
        aw = (lsw > 0 ? lsw : stepW) / 2;
      }
      path.push('A', aw, aw, "0 0 1", newP.x, newP.y);
    }
    // scallopSize = stepW;
  });
  return path.join(' ');
  // return path.join(' ') + (points.length > 3 ? 'z' : '');
}

var points = [];
var mouse = null;
var dblclick = null,
  doneEnding = false;

window.test.setAttribute('stroke-width', strokeWidth);

function feed() {
  if (dblclick && doneEnding) return;
  if (!dblclick && (points.length > 0 && mouse)) {
    var arr = points.slice(0);
    arr.push(mouse);
    var str = scapolledLine(arr, strokeWidth);
    window.test.setAttribute('d', str);
  } else if (dblclick) {
    points.push(points[0]);
    doneEnding = true;
    var str = scapolledLine(points, strokeWidth);
    window.test.setAttribute('d', str);
  }
}

document.addEventListener('mousedown', function(event) {
  points.push({
    x: event.clientX,
    y: event.clientY
  });
  feed();
});

document.addEventListener('dblclick', function(event) {
  dblclick = true;
  feed();
});

document.addEventListener('mousemove', function(event) {
  if (points.length > 0) {
    mouse = {
      x: event.clientX,
      y: event.clientY
    }
    feed();
  }
});
body,
html {
  height: 100%;
  width: 100%;
  margin: 0;
  padding: 0
}
svg {
  height: 100%;
  width: 100%
}
<svg id="svgP">
  <path id="test" style="stroke: RGBA(212, 50, 105, 1.00); fill: none" />
</svg>
like image 582
Exception Avatar asked Dec 02 '16 22:12

Exception


1 Answers

Finding circle to fit 3Points

This method uses a function that finds a circle that fits 3 points. Two of the points are the set of points you have. The 3rd point is taken perpendicular to the line between the points and moved out by a factor of the line seg length.

When the circle is found then the start and end angle from the circle center point is found to make the arc segment and that is all done. Just draw the arcs with ctx.arc(

I am not sure exactly what you want. I have it so the arcs all bend up, But it is easy to make the go around.

If you want them all the same size you have to separate the points be equal distance, which is very simple, but means its hard to fit a given area.

Demo

The demo lets you add and drag point. Mouse wheel changes arc depth.

The const at the top arcDepth determines how deep each arc is compared to line segment length. It is a fraction.

You can make it a constant in pixels see, calcArc for how to change.

Each arc has an independent depth so if you don't like the overlapping arcs reduce the depth for that arc (in the code of course).

Hope that helps.

const pointSize = 4;
const pointCol = "#4AF";
var arcDepth = -0.5; // depth of arc as a factor of line seg length
                       // Note to have arc go the other (positive) way you have
                       // to change the ctx.arc draw call by adding anticlockwise flag 
                       // see drawArc for more
                       
const arcCol = "#4FA";
const arcWidth = 3;


// Find a circle that fits 3 points.
function fitCircleTo3P(p1x, p1y, p2x, p2y, p3x, p3y, arc) {
    var vx,
    vy,
    c,
    c1,
    u;

    c = (p2x - p1x) / (p1y - p2y); // slope of vector from vec 1 to vec 2
    c1 = (p3x - p2x) / (p2y - p3y); // slope of vector from vec 2 to vec 3
    // This will not happen in this example
    if (c === c1) { // if slope is the same they must be on the same line
        return null; // points are in a line
    }
    // locate the center
    if (p1y === p2y) { // special case with p1 and p2 have same y
        vx = (p1x + p2x) / 2;
        vy = c1 * vx + (((p2y + p3y) / 2) - c1 * ((p2x + p3x) / 2));
    } else
        if (p2y === p3y) { // special case with p2 and p3 have same y
            vx = (p2x + p3x) / 2;
            vy = c * vx + (((p1y + p2y) / 2) - c * ((p1x + p2x) / 2));
        } else {
            vx = ((((p2y + p3y) / 2) - c1 * ((p2x + p3x) / 2)) - (u = ((p1y + p2y) / 2) - c * ((p1x + p2x) / 2))) / (c - c1);
            vy = c * vx + u;
        }
    arc.x = vx;
    arc.y = vy;
    vx = p1x - vx;
    vy = p1y - vy;
    arc.rad = Math.sqrt(vx * vx + vy * vy);
    return arc;
}

var points = [];
var arcs = [];
function addArc(p1, p2, depth) {

    // remove next 5 line if you dont want all arcs to face the same way.
    if(points[p1][0] > points[p2][0]){
        var temp = p1;
        p1 = p2;
        p2 = temp;
    }
    var arc = {
        p1 : p1,
        p2 : p2,
        depth : depth,
        rad : null, // radius
        a1 : null, // angle from
        a2 : null, // angle to
        x : null,
        y : null,
    }
    arcs.push(arc);
    return arc;
}
function calcArc(arc, depth) {
    var p = points[arc.p1]; // get points
    var pp = points[arc.p2];
    // change depth if needed
    depth = arc.depth = depth !== undefined ? depth : arc.depth;
    var vx = pp[0] - p[0]; // vector from p to pp
    var vy = pp[1] - p[1];
    var cx = (pp[0] + p[0]) / 2; // center point
    var cy = (pp[1] + p[1]) / 2; // center point
    var len = Math.sqrt(vx * vx + vy * vy); // get length
    cx -= vy * depth; // find 3 point at 90 deg to line and dist depth
    cy += vx * depth;

    // To have depth as a fixed length uncomment 4 lines below and comment out 2 lines above.
    //var nx = vx / len;  // normalise vector
    //var ny = vy / len;
    //cx -= ny * depth; // find 3 point at 90 deg to line and dist depth
    //cy += nx * depth;


    fitCircleTo3P(p[0], p[1], cx, cy, pp[0], pp[1], arc); // get the circle that fits
    arc.a1 = Math.atan2(p[1] - arc.y, p[0] - arc.x); // get angle from circle center to first point
    arc.a2 = Math.atan2(pp[1] - arc.y, pp[0] - arc.x); // get angle from circle center to second point

}
function addPoint(x, y) {
    points.push([x, y]);
}
function drawPoint(x, y, size, col) {
    ctx.fillStyle = col;
    ctx.beginPath();
    ctx.arc(x, y, size, 0, Math.PI * 2);
    ctx.fill();
}

function drawArc(arc, width, col) {
    ctx.lineCap = "round";
    ctx.strokeStyle = col;
    ctx.lineWidth = width;
    ctx.beginPath();
    ctx.arc(arc.x, arc.y, arc.rad, arc.a1, arc.a2,false);  // true for anti clock wise
    ctx.stroke();
}
function findClosestPoint(x, y, dist) {
    var index = -1;
    for (var i = 0; i < points.length; i++) {
        var p = points[i];
        var vx = x - p[0];
        var vy = y - p[1];
        var d = Math.sqrt(vx * vx + vy * vy);
        if (d < dist) {
            dist = d;
            index = i;
        }
    }
    return index;
}

var dragging = false;
var drag = -1;
var dragX, dragY;
var recalcArcs = false;
function display() {
    ctx.setTransform(1, 0, 0, 1, 0, 0); // reset transform
    ctx.globalAlpha = 1; // reset alpha
    ctx.clearRect(0, 0, w, h);
    if(mouse.w > 0){
        arcDepth *= 1.05;
        mouse.w = 0;
        recalcArcs = true;
    }
    if(mouse.w < 0){
        arcDepth *= 1/1.05;
        mouse.w = 0;
        recalcArcs = true;
    }
       
    if (mouse.buttonRaw & 1) {

        if (!dragging) {
            var i = findClosestPoint(mouse.x, mouse.y, pointSize * 3);
            if (i > -1) {
                drag = i;
                dragging = true;
                dragX = mouse.x - points[drag][0];
                dragY = mouse.y - points[drag][1];
            }
        }
        if (dragging) {
            points[drag][0] = mouse.x - dragX
                points[drag][1] = mouse.y - dragY
                recalcArcs = true;

        } else {
            addPoint(mouse.x, mouse.y);
            if (points.length > 1) {
                calcArc(addArc(points.length - 2, points.length - 1, arcDepth));
            }
            mouse.buttonRaw = 0;
        }

    } else {
        if (dragging) {
            dragging = false;
            drag = -1;
            recalcArcs = true;
        }
        var i = findClosestPoint(mouse.x, mouse.y, pointSize * 3);
        if (i > -1) {
            canvas.style.cursor = "move";
        } else {
            canvas.style.cursor = "default";

        }
    }
    for (var i = 0; i < arcs.length; i++) {
        if (recalcArcs) {
            calcArc(arcs[i],arcDepth);
        }
        drawArc(arcs[i], arcWidth, arcCol);

    }
    recalcArcs = false;

    for (var i = 0; i < points.length; i++) {
        var p = points[i];
        drawPoint(p[0], p[1], pointSize, pointCol);
    }

}


//===========================================================================================
// END OF ANSWER

// Boiler plate code from here down. Does mouse,canvas,resize and what not
var w, h, cw, ch, canvas, ctx, mouse, globalTime = 0, firstRun = true; ;
(function () {
    const RESIZE_DEBOUNCE_TIME = 100;
    var createCanvas,
    resizeCanvas,
    setGlobals,
    resizeCount = 0;
    createCanvas = function () {
        var c,
        cs;
        cs = (c = document.createElement("canvas")).style;
        cs.position = "absolute";
        cs.top = cs.left = "0px";
        cs.zIndex = 1000;
        document.body.appendChild(c);
        return c;
    }
    resizeCanvas = function () {
        if (canvas === undefined) {
            canvas = createCanvas();
        }
        canvas.width = innerWidth;
        canvas.height = innerHeight;
        ctx = canvas.getContext("2d");
        if (typeof setGlobals === "function") {
            setGlobals();
        }
        if (typeof onResize === "function") {
            if (firstRun) {
                onResize();
                firstRun = false;
            } else {
                resizeCount += 1;
                setTimeout(debounceResize, RESIZE_DEBOUNCE_TIME);
            }
        }
    }
    function debounceResize() {
        resizeCount -= 1;
        if (resizeCount <= 0) {
            onResize();
        }
    }
    setGlobals = function () {
        cw = (w = canvas.width) / 2;
        ch = (h = canvas.height) / 2;
    }
    mouse = (function () {
        function preventDefault(e) {
            e.preventDefault();
        }
        var mouse = {
            x : 0,
            y : 0,
            w : 0,
            alt : false,
            shift : false,
            ctrl : false,
            buttonRaw : 0,
            over : false,
            bm : [1, 2, 4, 6, 5, 3],
            active : false,
            bounds : null,
            crashRecover : null,
            mouseEvents : "mousemove,mousedown,mouseup,mouseout,mouseover,mousewheel,DOMMouseScroll".split(",")
        };
        var m = mouse;
        function mouseMove(e) {
            var t = e.type;
            m.bounds = m.element.getBoundingClientRect();
            m.x = e.pageX - m.bounds.left;
            m.y = e.pageY - m.bounds.top;
            m.alt = e.altKey;
            m.shift = e.shiftKey;
            m.ctrl = e.ctrlKey;
            if (t === "mousedown") {
                m.buttonRaw |= m.bm[e.which - 1];
            } else if (t === "mouseup") {
                m.buttonRaw &= m.bm[e.which + 2];
            } else if (t === "mouseout") {
                m.buttonRaw = 0;
                m.over = false;
            } else if (t === "mouseover") {
                m.over = true;
            } else if (t === "mousewheel") {
                m.w = e.wheelDelta;
            } else if (t === "DOMMouseScroll") {
                m.w = -e.detail;
            }
            e.preventDefault();
        }
        m.start = function (element) {
            if (m.element !== undefined) {
                m.removeMouse();
            }
            m.element = element === undefined ? document : element;
            m.mouseEvents.forEach(n => {
                m.element.addEventListener(n, mouseMove);
            });
            m.element.addEventListener("contextmenu", preventDefault, false);
            m.active = true;
        }
        m.remove = function () {
            if (m.element !== undefined) {
                m.mouseEvents.forEach(n => {
                    m.element.removeEventListener(n, mouseMove);
                });
                m.element.removeEventListener("contextmenu", preventDefault);
                m.element = m.callbacks = undefined;
                m.active = false;
            }
        }
        return mouse;
    })();


    function update(timer) { // Main update loop
        if (ctx === undefined) {
            return;
        }
        globalTime = timer;
        display(); // call demo code
        requestAnimationFrame(update);
    }
    setTimeout(function () {
        resizeCanvas();
        mouse.start(canvas, true);
        window.addEventListener("resize", resizeCanvas);
        requestAnimationFrame(update);
    }, 0);
})();
Left click to add point. Left click drag to move points.<br>
Mouse wheel changes arc depth.

Take two...

Maybe this is what you are after.. Sorry its a bit of a mess as I am short on time at the moment.

Same code as before just add the points to the outside of the box, making sure that the width and height steps are equally spaced from the edge.

const pointSize = 4;
const pointCol = "#4AF";
var arcDepth = -0.5; // depth of arc as a factor of line seg length
                       // Note to have arc go the other (positive) way you have
                       // to change the ctx.arc draw call by adding anticlockwise flag 
                       // see drawArc for more
                       
const arcCol = "#F92";
const arcWidth = 8;


// Find a circle that fits 3 points.
function fitCircleTo3P(p1x, p1y, p2x, p2y, p3x, p3y, arc) {
    var vx,
    vy,
    c,
    c1,
    u;

    c = (p2x - p1x) / (p1y - p2y); // slope of vector from vec 1 to vec 2
    c1 = (p3x - p2x) / (p2y - p3y); // slope of vector from vec 2 to vec 3
    // This will not happen in this example
    if (c === c1) { // if slope is the same they must be on the same line
        return null; // points are in a line
    }
    // locate the center
    if (p1y === p2y) { // special case with p1 and p2 have same y
        vx = (p1x + p2x) / 2;
        vy = c1 * vx + (((p2y + p3y) / 2) - c1 * ((p2x + p3x) / 2));
    } else
        if (p2y === p3y) { // special case with p2 and p3 have same y
            vx = (p2x + p3x) / 2;
            vy = c * vx + (((p1y + p2y) / 2) - c * ((p1x + p2x) / 2));
        } else {
            vx = ((((p2y + p3y) / 2) - c1 * ((p2x + p3x) / 2)) - (u = ((p1y + p2y) / 2) - c * ((p1x + p2x) / 2))) / (c - c1);
            vy = c * vx + u;
        }
    arc.x = vx;
    arc.y = vy;
    vx = p1x - vx;
    vy = p1y - vy;
    arc.rad = Math.sqrt(vx * vx + vy * vy);
    return arc;
}

var points = [];
var arcs = [];
function addArc(p1, p2, depth) {
    var arc = {
        p1 : p1,
        p2 : p2,
        depth : depth,
        rad : null, // radius
        a1 : null, // angle from
        a2 : null, // angle to
        x : null,
        y : null,
    }
    arcs.push(arc);
    return arc;
}
function calcArc(arc, depth) {
    var p = points[arc.p1]; // get points
    var pp = points[arc.p2];
    // change depth if needed
    depth = arc.depth = depth !== undefined ? depth : arc.depth;
    var vx = pp[0] - p[0]; // vector from p to pp
    var vy = pp[1] - p[1];
    var cx = (pp[0] + p[0]) / 2; // center point
    var cy = (pp[1] + p[1]) / 2; // center point
    var len = Math.sqrt(vx * vx + vy * vy); // get length
    cx -= vy * depth; // find 3 point at 90 deg to line and dist depth
    cy += vx * depth;

    // To have depth as a fixed length uncomment 4 lines below and comment out 2 lines above.
    //var nx = vx / len;  // normalise vector
    //var ny = vy / len;
    //cx -= ny * depth; // find 3 point at 90 deg to line and dist depth
    //cy += nx * depth;


    fitCircleTo3P(p[0], p[1], cx, cy, pp[0], pp[1], arc); // get the circle that fits
    arc.a1 = Math.atan2(p[1] - arc.y, p[0] - arc.x); // get angle from circle center to first point
    arc.a2 = Math.atan2(pp[1] - arc.y, pp[0] - arc.x); // get angle from circle center to second point

}
function addPoint(x, y) {
    points.push([x, y]);
}
function drawPoint(x, y, size, col) {
    ctx.fillStyle = col;
    ctx.beginPath();
    ctx.arc(x, y, size, 0, Math.PI * 2);
    ctx.fill();
}
function drawArcStart(width,col){
    ctx.lineCap = "round";
    ctx.strokeStyle = col;
    ctx.lineJoin = "round";
    ctx.lineWidth = width;
    ctx.beginPath();
    
}
function drawArc(arc){
    ctx.arc(arc.x,arc.y,arc.rad,arc.a1,arc.a2);
}    
function drawArcDone(){
    ctx.closePath();
    ctx.stroke();
}
function findClosestPoint(x, y, dist) {
    var index = -1;
    for (var i = 0; i < points.length; i++) {
        var p = points[i];
        var vx = x - p[0];
        var vy = y - p[1];
        var d = Math.sqrt(vx * vx + vy * vy);
        if (d < dist) {
            dist = d;
            index = i;
        }
    }
    return index;
}

var dragging = false;
var drag = -1;
var dragX, dragY;
var recalcArcs = false;
var box;
//========================================================================
// New box code from her down

// creates the box when canvas is ready
var onResize = function(){
    box = {
        x : canvas.width * (1/8),
        y : canvas.height * (1/8),
        w : canvas.width * (6/8),
        h : canvas.height * (6/8),
        recalculate : true,
        arcCount : 20, // number of arcs to try and fit. Does not mean that it will happen
    }
}

function display() {
    ctx.setTransform(1, 0, 0, 1, 0, 0); // reset transform
    ctx.globalAlpha = 1; // reset alpha
    ctx.clearRect(0, 0, w, h);

if(mouse.w !== 0){
    if(mouse.buttonRaw & 4){ // change arc depth
        if(mouse.w < 0){
            arcDepth *= 1/1.05;
        }else{
            arcDepth *= 1.05;
        }
        recalcArcs = true;
        
    }else{  // change arc count
        box.arcCount += Math.sign(mouse.w);
        box.arcCount = Math.max(4,box.arcCount);
        box.recalculate = true;
    }
    mouse.w = 0;
}
// drag out box;
if(mouse.buttonRaw & 1){
    if(!dragging){
        box.x = mouse.x;
        box.y = mouse.y;
        dragging = true;
    }
    box.w = mouse.x - box.x;
    box.h = mouse.y - box.y;
    box.recalculate = true;
    if(box.w <0){
        box.x = box.x + box.w;
        box.w = - box.w;
    }
    if(box.h <0){
        box.y = box.y + box.h;
        box.h = - box.h;
    }
}else{
    dragging = false;
}
// stop error
if(box.w === 0 || box.h === 0){
    box.recaculate = false;
}

// caculate box arcs
if(box.recalculate){
    // reset arrays
    points.length = 0;
    arcs.length = 0;
    // get perimiter length
    var perimLen = (box.w + box.h)* 2;
    // get estimated step size
    var step = perimLen / box.arcCount;
    // get inset size for width and hight
    var wInStep = (box.w - (Math.floor(box.w/step)-1)*step) / 2;
    var hInStep = (box.h - (Math.floor(box.h/step)-1)*step) / 2;
    // fix if box to narrow
    if(box.w < step){
        wInStep = 0;
        hInStep = 0;
        step = box.h / (Math.floor(box.h/step));
    }else if(box.h < step){
        wInStep = 0;
        hInStep = 0;
        step = box.w / (Math.floor(box.w/step));
        
    }
    
    // Add points clock wise
    var x = box.x + wInStep;
    while(x < box.x + box.w){ // across top
        addPoint(x,box.y);
        x += step;
    }
    var y = box.y + hInStep; 
    while(y < box.y + box.h){ // down right side
        addPoint(box.x + box.w,y);
        y += step;
    }
    x = box.x + box.w - wInStep;
    while(x > box.x){          // left along bottom
        addPoint(x,box.y + box.h);
        x -= step;
    }
    var y = box.y + box.h - hInStep;
    while(y > box.y){  // up along left side
        addPoint(box.x,y);
        y -= step;
    }
    // caculate arcs.
    for(var i =0; i <points.length; i++){
        calcArc(addArc(i,(i + 1) % points.length,arcDepth));
    }
    box.recalculate = false;
}
// recaculate arcs if needed
for(var i = 0; i < arcs.length; i ++){
    if(recalcArcs){
        calcArc(arcs[i],arcDepth);
    }
}
// draw arcs
drawArcStart(arcWidth,arcCol)
for(var i = 0; i < arcs.length; i ++){
    drawArc(arcs[i]);
}
drawArcDone();
recalcArcs = false;
}


//===========================================================================================
// END OF ANSWER

// Boiler plate code from here down. Does mouse,canvas,resize and what not
var w, h, cw, ch, canvas, ctx, mouse, globalTime = 0, firstRun = true; ;
(function () {
    const RESIZE_DEBOUNCE_TIME = 100;
    var createCanvas,
    resizeCanvas,
    setGlobals,
    resizeCount = 0;
    createCanvas = function () {
        var c,
        cs;
        cs = (c = document.createElement("canvas")).style;
        cs.position = "absolute";
        cs.top = cs.left = "0px";
        cs.zIndex = 1000;
        document.body.appendChild(c);
        return c;
    }
    resizeCanvas = function () {
        if (canvas === undefined) {
            canvas = createCanvas();
        }
        canvas.width = innerWidth;
        canvas.height = innerHeight;
        ctx = canvas.getContext("2d");
        if (typeof setGlobals === "function") {
            setGlobals();
        }
        if (typeof onResize === "function") {
            if (firstRun) {
                onResize();
                firstRun = false;
            } else {
                resizeCount += 1;
                setTimeout(debounceResize, RESIZE_DEBOUNCE_TIME);
            }
        }
    }
    function debounceResize() {
        resizeCount -= 1;
        if (resizeCount <= 0) {
            onResize();
        }
    }
    setGlobals = function () {
        cw = (w = canvas.width) / 2;
        ch = (h = canvas.height) / 2;
    }
    mouse = (function () {
        function preventDefault(e) {
            e.preventDefault();
        }
        var mouse = {
            x : 0,
            y : 0,
            w : 0,
            alt : false,
            shift : false,
            ctrl : false,
            buttonRaw : 0,
            over : false,
            bm : [1, 2, 4, 6, 5, 3],
            active : false,
            bounds : null,
            crashRecover : null,
            mouseEvents : "mousemove,mousedown,mouseup,mouseout,mouseover,mousewheel,DOMMouseScroll".split(",")
        };
        var m = mouse;
        function mouseMove(e) {
            var t = e.type;
            m.bounds = m.element.getBoundingClientRect();
            m.x = e.pageX - m.bounds.left;
            m.y = e.pageY - m.bounds.top;
            m.alt = e.altKey;
            m.shift = e.shiftKey;
            m.ctrl = e.ctrlKey;
            if (t === "mousedown") {
                m.buttonRaw |= m.bm[e.which - 1];
            } else if (t === "mouseup") {
                m.buttonRaw &= m.bm[e.which + 2];
            } else if (t === "mouseout") {
                m.buttonRaw = 0;
                m.over = false;
            } else if (t === "mouseover") {
                m.over = true;
            } else if (t === "mousewheel") {
                m.w = e.wheelDelta;
            } else if (t === "DOMMouseScroll") {
                m.w = -e.detail;
            }
            e.preventDefault();
        }
        m.start = function (element) {
            if (m.element !== undefined) {
                m.removeMouse();
            }
            m.element = element === undefined ? document : element;
            m.mouseEvents.forEach(n => {
                m.element.addEventListener(n, mouseMove);
            });
            m.element.addEventListener("contextmenu", preventDefault, false);
            m.active = true;
        }
        m.remove = function () {
            if (m.element !== undefined) {
                m.mouseEvents.forEach(n => {
                    m.element.removeEventListener(n, mouseMove);
                });
                m.element.removeEventListener("contextmenu", preventDefault);
                m.element = m.callbacks = undefined;
                m.active = false;
            }
        }
        return mouse;
    })();


    function update(timer) { // Main update loop
        if (ctx === undefined) {
            return;
        }
        globalTime = timer;
        display(); // call demo code
        requestAnimationFrame(update);
    }
    setTimeout(function () {
        resizeCanvas();
        mouse.start(canvas, true);
        window.addEventListener("resize", resizeCanvas);
        requestAnimationFrame(update);
    }, 0);
})();
Left click drag to create a box<br>Mouse wheel to change arc count<br>Hold right button down and wheel to change arc depth.<br>
like image 133
Blindman67 Avatar answered Sep 21 '22 14:09

Blindman67