-1

I'm using Konva library to draw some stuff on HTML5 canvas.

I have given 2 points from user interaction by mouse click:

var A={x:'',y:''};
var B={x:'',y:''};

1) How to draw line line this? enter image description here

My question is:

1) How to get perpendicular lines on each interval?

2) How to get distance from A to B point?

3) How to get all points on line from A to B?

4) How to get red points?

duffymo
  • 305,152
  • 44
  • 369
  • 561
Norbert Pisz
  • 3,392
  • 3
  • 27
  • 42
  • You have to define - what kind of curve is at the picture? Sine wave? Perpendicular to what? Distance - length of straight segment or curve length? – MBo Apr 24 '17 at 07:49
  • It's custom. All distances can be flexible depends of screen. I have only point A and B. Red points are perpendicular from line (A,B) so I need to find these points. – Norbert Pisz Apr 24 '17 at 08:45
  • Are there always the same number of red dots? Or can that also change? – MrApnea Apr 24 '17 at 08:51
  • 1
    The singular "My question is" indicates that you have just one question, but you then ask four (in addition to the one that you already asked), which makes this too broad. – John Coleman Apr 24 '17 at 11:30

2 Answers2

3

You have not explained what your line is so I am assuming it is a sin wave (though the image looks like circles stuck together???)

As MBo has given the basics this is just applying it to the wavy line.

// normalize a vector
function normalize(vec){
    var length = Math.sqrt(vec.x * vec.x + vec.y * vec.y);
    vec.x /= length;
    vec.y /= length;
    return vec;
        
}
// creates a wavy line
function wavyLine(start, end, waves, amplitude){
    return ({ 
        start, 
        end, 
        waves, 
        amplitude, 
        update(){
            if(this.vec === undefined){
                this.vec = {};
                this.norm = {};
            }
            this.vec.x = this.end.x - this.start.x; 
            this.vec.y = this.end.y - this.start.y;
            this.length = Math.sqrt(this.vec.x * this.vec.x + this.vec.y * this.vec.y);
            this.norm.x = this.vec.x / this.length;  
            this.norm.y = this.vec.y / this.length;
            return this;
        }
    }).update();
}


// draws a wavy line
function drawWavyLine(line) {
    var x, stepSize, i, y, phase, dist;
    ctx.beginPath();
    stepSize = ctx.lineWidth;
    ctx.moveTo(line.start.x, line.start.y);
    for (i = stepSize; i < line.length; i+= stepSize) {
        x = line.start.x + line.norm.x * i; // get point i pixels from start
        y = line.start.y + line.norm.y * i; // get point i pixels from start
        phase = (i / (line.length / line.waves)) * Math.PI * 2; // get the wave phase at this point
        dist = Math.sin(phase) * line.amplitude; // get the distance from the line to the point on the wavy curve
        x -= line.norm.y * dist;
        y += line.norm.x * dist;
        ctx.lineTo(x, y);
    }
    phase = line.waves * Math.PI * 2; // get the wave phase at this point
    dist = Math.sin(phase) * line.amplitude; // get the distance from the line to the point on the wavy curve
    ctx.lineTo(line.end.x - line.norm.y * dist, line.end.y + line.norm.x * dist);    
    ctx.stroke();
}

// find the closest point on a wavy line to a point returns the pos on the wave, tangent and point on the linear line
function closestPointOnLine(point,line){
    var x = point.x - line.start.x;
    var y = point.y - line.start.y;
    // get the amount the line vec needs to be scaled so tat point is perpendicular to the line
    var l = (line.vec.x * x + line.vec.y * y) / (line.length * line.length);
    x = line.vec.x * l;  // scale the vec
    y = line.vec.y * l;
    return pointAtDistance(Math.sqrt(x * x + y * y), line);
}


// find the point at (linear) distance along wavy line and return coordinate, coordinate on wave, and tangent
function pointAtDistance(distance,line){
    var lenScale = line.length / line.waves; // scales the length into radians
    var phase = distance * Math.PI * 2 / lenScale; // get the wave phase at this point
    var dist = Math.sin(phase) * line.amplitude; // get the distance from the line to the point on the wavy curve
    var slope = Math.cos(phase ) * Math.PI * 2 * line.amplitude / lenScale; // derivitive of sin(a*x) is -a*cos(a*x)
    // transform tangent (slope) into a vector along the line. This vector is not a unit vector so normalize it
    var tangent = normalize({
        x : line.norm.x  - line.norm.y * slope, 
        y : line.norm.y  + line.norm.x * slope
    });
    // move from the line start to the point on the linear line at distance
    var linear  = {
        x : line.start.x + line.norm.x * distance,
        y : line.start.y + line.norm.y * distance
    }
    // move out perpendicular  to the wavy part
    return {
        x : linear.x - line.norm.y * dist,
        y : linear.y + line.norm.x * dist,
        tangent,linear
    };
}

// create a wavy line
var wLine = wavyLine({x:10,y:100},{x:300,y:100},3,50);



// draw the wavy line and show some points on it
function display(timer){
    globalTime = timer;
    ctx.setTransform(1,0,0,1,0,0); // reset transform
    ctx.globalAlpha = 1;           // reset alpha
    ctx.clearRect(0,0,w,h);
    var radius = Math.max(ch,cw);
    // set up the wavy line
    wLine.waves = Math.sin(timer / 10000) * 6;
    wLine.start.x = Math.cos(timer / 50000) * radius + cw;
    wLine.start.y = Math.sin(timer / 50000) * radius + ch;
    wLine.end.x = -Math.cos(timer / 50000) * radius + cw;
    wLine.end.y = -Math.sin(timer / 50000) * radius + ch ;
    wLine.update();
    
    // draw the linear line
    ctx.lineWidth = 0.5;
    ctx.strokeStyle = "blue";
    ctx.beginPath();
    ctx.moveTo(wLine.start.x, wLine.start.y);
    ctx.lineTo(wLine.end.x, wLine.end.y);
    ctx.stroke();

    // draw the wavy line
    ctx.lineWidth = 2;
    ctx.strokeStyle = "black";
    drawWavyLine(wLine);
    
    
    // find point nearest mouse
    var p = closestPointOnLine(mouse,wLine);
    ctx.lineWidth = 1;
    ctx.strokeStyle = "red";
    ctx.beginPath();
    ctx.arc(p.x,p.y,5,0,Math.PI * 2);
    ctx.moveTo(p.x + p.tangent.x * 20,p.y + p.tangent.y * 20);
    ctx.lineTo(p.x - p.tangent.y * 10,p.y + p.tangent.x * 10);
    ctx.lineTo(p.x + p.tangent.y * 10,p.y - p.tangent.x * 10);
    ctx.closePath();
    ctx.stroke();
    
    // find points at equal distance along line
    
    ctx.lineWidth = 1;
    ctx.strokeStyle = "blue";
    ctx.beginPath();
    for(var i = 0; i < w; i += w / 10){
        var p = pointAtDistance(i,wLine);
        ctx.moveTo(p.x + 5,p.y);
        ctx.arc(p.x,p.y,5,0,Math.PI * 2);
        ctx.moveTo(p.x,p.y);
        ctx.lineTo(p.linear.x,p.linear.y);
        ctx.moveTo(p.x + p.tangent.x * 40, p.y + p.tangent.y * 40);        
        ctx.lineTo(p.x - p.tangent.x * 40, p.y - p.tangent.y * 40);        
        
    }
    ctx.stroke();
    
}
























/******************************************************************************
 The code from here down is generic full page mouse and canvas boiler plate 
 code. As I do many examples which all require the same mouse and canvas 
 functionality I have created this code to keep a consistent interface. The
 Code may or may not be part of the answer.
 This code may or may not have ES6 only sections so will require a transpiler
 such as babel.js to run on legacy browsers.
 *****************************************************************************/
// V2.0 ES6 version for Stackoverflow and Groover QuickRun 
var w, h, cw, ch, canvas, ctx, mouse, globalTime = 0;
// You can declare onResize (Note the capital R) as a callback that is also
// called once at start up. Warning on first call canvas may not be at full
// size. 
;(function(){
    const RESIZE_DEBOUNCE_TIME = 100;
    var resizeTimeoutHandle;
    var firstRun = true;
    function createCanvas () {
        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;
    }
    function resizeCanvas () {
        if (canvas === undefined) { canvas = createCanvas() }
        canvas.width = innerWidth;
        canvas.height = innerHeight;
        ctx = canvas.getContext("2d");
        if (typeof setGlobals === "function") { setGlobals() }
        if (typeof onResize === "function") {
            clearTimeout(resizeTimeoutHandle);
            if (firstRun) { onResize() }
            else { resizeTimeoutHandle = setTimeout(onResize, RESIZE_DEBOUNCE_TIME) }
            firstRun = false;
        }
    }
    function setGlobals () {
        cw = (w = canvas.width) / 2;
        ch = (h = canvas.height) / 2;
    }
    mouse = (function () {
        var m; // alias for mouse
        var mouse = {
            x : 0, y : 0, // mouse position and wheel
            buttonRaw : 0,
            buttonOnMasks : [0b1, 0b10, 0b100],  // mouse button on masks
            buttonOffMasks : [0b110, 0b101, 0b011], // mouse button off masks
            bounds : null,
            eventNames : "mousemove,mousedown,mouseup".split(","),
            event(e) {
                var t = e.type;
                m.bounds = m.element.getBoundingClientRect();
                m.x = e.pageX - m.bounds.left - scrollX;
                m.y = e.pageY - m.bounds.top - scrollY;
                if (t === "mousedown") { m.buttonRaw |= m.buttonOnMasks[e.which - 1] }
                else if (t === "mouseup") { m.buttonRaw &= m.buttonOffMasks[e.which - 1] }
            },
            start(element) {
                m.element = element === undefined ? document : element;
                m.eventNames.forEach(name =>  document.addEventListener(name, mouse.event) );
            },
        }
        m = mouse;
        return mouse;
    })();

    function update(timer) { // Main update loop
        globalTime = timer;
        display(timer);           // call demo code
        requestAnimationFrame(update);
    }
    setTimeout(function(){
        canvas = createCanvas(); 
        mouse.start(canvas);
        resizeCanvas();
        window.addEventListener("resize", resizeCanvas);
        requestAnimationFrame(update);
    },0);
})();
Blindman67
  • 51,134
  • 11
  • 73
  • 136
1

We have points A and B. Difference vector

D.X = B.X - A.X
D.Y = B.Y - A.Y
Length = Sqrt(D.X * D.X + D.Y * D.Y)

normalized (unit) vector
uD.X = D.X / Length
uD.Y = D.Y / Length

perpendicular unit vector
P.X = - uD.Y
P.Y = uD.X

some red point:
R.X = A.X + uD.X * Dist + P.X * SideDist * SideSign    
R.Y = A.Y + uD.Y * Dist + P.Y * SideDist * SideSign        

where Dist is in range 0..Length
Dist = i / N * Length for N equidistant points
SideSign is +/- 1   for left and right side 
MBo
  • 77,366
  • 5
  • 53
  • 86