问:
Draw on rotated CANVAS - Part 2
As a follow up to this question and answer...I have another issue to solve:

When I draw on a canvas and then apply some transformations like rotation, I would like to keep what was drawn and continue the drawing.

To test this, use the mouse to draw something and then click "rotate".

This is what I'm trying, but the canvas gets erased.

JS

```<code>//main variables
canvas = document.createElement("canvas");
canvas.width = 500;
canvas.height = 300;
canvas.ctx = canvas.getContext("2d");
ctx = canvas.ctx;

canvas_aux = document.createElement("canvas");
canvas_aux.width = 500;
canvas_aux.height = 300;
canvas_aux.ctx = canvas.getContext("2d");
ctx_aux = canvas_aux.ctx;

function rotate()
{
    ctx_aux.drawImage(canvas, 0, 0); //new line: save current drawing

    timer += timerStep;

    var cw = canvas.width / 2;
    var ch = canvas.height / 2;

    ctx.setTransform(1, 0, 0, 1, 0, 0);  // reset the transform so we can clear
    ctx.clearRect(0, 0, canvas.width, canvas.height);  // clear the canvas

    createMatrix(cw, ch -50, scale, timer);

    var m = matrix;
    ctx.setTransform(m[0], m[1], m[2], m[3], m[4], m[5]);

    //draw();
    ctx.drawImage(canvas_aux, 0, 0); //new line: repaint current drawing

    if(timer <= rotation )
    {
        requestAnimationFrame(rotate);
    }
}
</code>```

DEMO (updated version of original in linked question/answer)

https://jsfiddle.net/mgf8uz7s/1/

答:
Record all paths, use canvas buffer to keep interface smooth

You have several options which will depend on what the requirements are.

Offscreen buffer/s to hold the rendered lines. Render to the offscreen buffer then draw the buffer to the display canvas. This is the quickest method but you are working with pixels, thus if you zoom you will get pixel artifacts and it will limit the size of the drawing area (still large but not pseudo infinite) and severely restrict the number of undos your can provide due to memory limits

Buffer paths as they are draw, basicly recording mouse movements and clicks, then re-rendering all visible paths each time you update the display. This will let you zoom and rotate without pixel artifacts, give you a draw area as large as you like (within limit of 64bit doubles) and a bonus undo all the way back to the first line. The problem with this method is that it quickly becomes very slow (though you can improve rendering speed with webGL)

A combination of the above two methods. Record the paths as they are drawn, but also render them to an offscreen canvas/s. Use the offscreen canvas to update the display and keep the refresh rate high. You only re-render the offscreen canvas when you need to, ie when you undo or if you zoom, you will not need to re-render when you pan or rotate.

Demo

I am not going to do a full drawing package so this is just an example that uses an offscreen buffer to hold the visible paths. All paths that are drawn are recorded in a paths array. When the user changes the view, pan, zoom, rotate, the paths are redrawn to the offscreen canvas to match the new view.

There is some boilerplate to handle setup and mouse that can be ignored. As there is a lot of code and time is short you will have to pick out what you need from it as the comments are short.

There is a paths object for paths. view holds the transform and associated functions. Some functions for pan, zoom, rotate. And a display function that renders and handles all mouse and user IO. The pan,zoom and scale controls are accessed via holding the mouse modifiers ctrl, alt, shift

var drawing = createImage(100,100); // offscreen canvas for drawing paths

// the onResize is a callback used by the boilerplate code at the bottom of this snippet
// it is called whenever the display size has changed (including starting app). It is
// debounced by 100ms to prevent needless calls
var onResize = function(){
    drawing.width = canvas.width;
    drawing.height = canvas.height;
    redrawBuffers = true; // flag that drawing buffers need redrawing
    ctx.font = "18px arial";
    ctx.textAlign = "center";
    ctx.textBaseline = "middle";
    view.pos.x = cw;  // set origin at center of screen
    view.pos.y = ch;
    view.update();
}
const paths = [];  // array of all recorded paths
const path = {   // descriptor of a path object
    addPoint(x,y){   // adds a point to the path
        this.points.push({x,y});
    },
    draw(ctx){   // draws this path on context ctx
        var i = 0;
        ctx.beginPath();
        ctx.moveTo(this.points[i].x,this.points[i++].y);
        while(i < this.points.length){
            ctx.lineTo(this.points[i].x,this.points[i++].y);
        }
        ctx.stroke();
    }
}
// creates a new path and adds it to the array of paths.
// returns the new path
function addPath(){
    var newPath;
    newPath = Object.assign({points : []},path);
    paths.push(newPath)
    return newPath;
}
// draws all recorded paths onto context cts using the current view
function drawAll(ctx){
    ctx.setTransform(1,0,0,1,0,0);
    ctx.clearRect(0,0,w,h);
    var m = view.matrix;
    ctx.setTransform(m[0],m[1],m[2],m[3],m[4],m[5]);
    var i = 0;
    for(i = 0; i < paths.length; i ++){
        paths[i].draw(ctx);
    }
}

// this controls the view
const view = {
    matrix : [1,0,0,1,0,0],  // current view transform
    invMatrix : [1,0,0,1,0,0], // current inverse view transform
    rotate : 0,  // current x axis direction in radians
    scale : 1,   // current scale
    pos : {      // current position of origin
        x : 0,
        y : 0,
    },
    update(){ // call to update transforms
        var xdx = Math.cos(this.rotate) * this.scale;
        var xdy = Math.sin(this.rotate) * this.scale;
        var m = this.matrix;
        var im = this.invMatrix;
        m[0] = xdx;
        m[1] = xdy;
        m[2] = -xdy;
        m[3] = xdx;
        m[4] = this.pos.x;
        m[5] = this.pos.y;
        // calculate the inverse transformation
        cross = m[0] * m[3] - m[1] * m[2];
        im[0] =  m[3] / cross;
        im[1] = -m[1] / cross;
        im[2] = -m[2] / cross;
        im[3] =  m[0] / cross;
    },
    mouseToWorld(){  // conver screen to world coords
        var xx, yy, m;
        m = this.invMatrix;
        xx = mouse.x - this.matrix[4];
        yy = mouse.y - this.matrix[5];
        mouse.xr =  xx * m[0] + yy * m[2];
        mouse.yr =   xx * m[1] + yy * m[3];
    },
    toWorld(x,y,point = {}){  // convert screen to world coords
        var xx, yy, m;
        m = this.invMatrix;
        xx = x - this.matrix[4];
        yy = y - this.matrix[5];
        point.x =  xx * m[0] + yy * m[2];
        point.y = xx * m[1] + yy * m[3];
        return point;
    },
    toScreen(x,y,point = {}){  // convert world coords to  coords
        var m;
        m = this.matrix;
        point.x =  x * m[0] + y * m[2] + m[4];
        point.y = x * m[1] + y * m[3] + m[5];
        return point;
    },
    clickOrigin : {  // used to hold coords to deal with pan zoom and rotate
        x : 0,
        y : 0,
        scale : 1,
    },
   dragging : false, // true is dragging
   startDrag(){  // called to start a Orientation UI input such as rotate, pan and scale
        if(!view.dragging){
            view.dragging = true;
            view.clickOrigin.x = mouse.xr;
            view.clickOrigin.y = mouse.yr;
            view.clickOrigin.screenX = mouse.x;
            view.clickOrigin.screenY = mouse.y;
            view.clickOrigin.scale = view.scale;
        }
   }
}

// functions to do pan zoom and scale
function panView(){  // pans the view
    view.startDrag();  // set origins as referance point
    view.pos.x -= (view.clickOrigin.screenX - mouse.x);
    view.pos.y -= (view.clickOrigin.screenY - mouse.y);
    view.update();
    view.mouseToWorld(); // get the new mouse pos
    view.clickOrigin.screenX = mouse.x; // save the new mouse coords
    view.clickOrigin.screenY = mouse.y;
}
// scales the view
function scaleView(){
    view.startDrag();
    var y = view.clickOrigin.screenY - mouse.y;
    if(y !== 0){
        view.scale = view.clickOrigin.scale + (y/ch);
        view.update();
    }
}
// rotates the view by setting the x axis direction
function rotateView(){
    view.startDrag();
    workingCoord = view.toScreen(0,0,workingCoord); // get location of origin
    var x = workingCoord.x - mouse.x;
    var y = workingCoord.y - mouse.y;
    var dist = Math.sqrt(x * x + y * y);
    if(dist > 2 / view.scale){
        view.rotate = Math.atan2(-y,-x);
        view.update();
    }
}
var currentPath; // Holds the currently drawn path
var redrawBuffers = false; // if true this indicates that all paths need to be redrawn
var workingCoord; // var to use as a coordinate

// main loop function called from requestAnimationFrame callback in boilerplate code
function display() {
    var showTransform = false;  // flags that view is being changed
    // clear the canvas and set defaults
    ctx.setTransform(1, 0, 0, 1, 0, 0); // reset transform
    ctx.globalAlpha = 1; // reset alpha
    ctx.clearRect(0, 0, w, h);
    view.mouseToWorld();  // get the mouse world coords

    // get the transform matrix
    var m = view.matrix;
    // show feedback
    if(mouse.shift || mouse.alt || mouse.ctrl){
        if(mouse.shift){
            ctx.fillText("Click drag to pan",cw, 20);
        }else if(mouse.ctrl){
            ctx.fillText("Click drag to rotate",cw, 20);
        }else{
            ctx.fillText("Click drag to scale : " + view.scale.toFixed(4),cw, 20);
        }
    }else{
          ctx.fillText("Click drag to draw.",cw, 20);
          ctx.fillText("Hold [shift], [ctrl], or [alt] and use mouse to pan, rotate, scale",cw, 40);
    }
    if(mouse.buttonRaw === 1){ // when mouse is down
        if(mouse.shift || mouse.alt || mouse.ctrl){ // pan zoom rotate
            if(mouse.shift){
                panView();
            }else if(mouse.ctrl){
                rotateView();
            }else{
                scaleView();
            }
            m = view.matrix;
            showTransform = true;
            redrawBuffers = true;
        }else{ // or add a path
            if(currentPath === undefined){
                currentPath = addPath();
            }
            currentPath.addPoint(mouse.xr,mouse.yr)
        }
    }else{
        // if there is a path then draw it onto the offscreen canvas and
        // reset the path to undefined
        if(currentPath !== undefined){
            currentPath.draw(drawing.ctx);
            currentPath = undefined;
        }
        view.dragging = false; // incase there is a pan/zoom/scale happening turn it off
    }
    if(showTransform){  // redraw all paths when pan rotate or zoom
        redrawBuffers = false;
        drawAll(drawing.ctx);
        ctx.drawImage(drawing,0,0);
    }else{  // draws the sceen when normal drawing mode.
        if(redrawBuffers){
            redrawBuffers = false;
            drawAll(drawing.ctx);
        }
        ctx.drawImage(drawing,0,0);
        ctx.setTransform(m[0],m[1],m[2],m[3],m[4],m[5]);
        drawing.ctx.setTransform(m[0],m[1],m[2],m[3],m[4],m[5]);

        // draw a cross hair.
        if(mouse.buttonRaw === 0){
            var invScale = 1 / view.scale; // get inverted scale
            ctx.beginPath();
            ctx.moveTo(mouse.xr - 10 * invScale,mouse.yr);
            ctx.lineTo(mouse.xr + 10 * invScale,mouse.yr);
            ctx.moveTo(mouse.xr ,mouse.yr - 10 * invScale);
            ctx.lineTo(mouse.xr ,mouse.yr + 10 * invScale);
            ctx.lineWidth = invScale;
            ctx.stroke();
            ctx.lineWidth = 1;
        }
    }

    // draw a new path if being drawn
    if(currentPath){
        currentPath.draw(ctx);
    }
    // If rotating or about to rotate show feedback
    if(mouse.ctrl){
        ctx.setTransform(m[0],m[1],m[2],m[3],m[4],m[5]);
        view.mouseToWorld();  // get the mouse world coords
        ctx.strokeStyle = "black";
        ctx.lineWidth = 3;
        ctx.beginPath();
        ctx.arc(0,0,3,0,Math.PI * 2);
        ctx.moveTo(0,0);
        ctx.lineTo(mouse.xr,mouse.yr);
        ctx.stroke();
        ctx.lineWidth = 1.5;
        ctx.strokeStyle = "red";
        ctx.beginPath();
        ctx.arc(0,0,3,0,Math.PI * 2);
        ctx.moveTo(0,0);
        ctx.lineTo(mouse.xr,mouse.yr);
        ctx.stroke();
        ctx.strokeStyle = "black";
        ctx.beginPath();
        ctx.moveTo(0,0);
        ctx.lineTo(200000 / view.scale,0);
        ctx.stroke();
        ctx.scale(1/ view.scale,1 / view.scale);
        ctx.fillText("X axis",100 ,-10  );
    }
}

/******************************************************************************/
// end of answer code
/******************************************************************************/

//Boiler plate from here down and can be ignored.
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;
            }
            if (m.callbacks) {
                m.callbacks.forEach(c => c(e));
            }
            e.preventDefault();
        }
        m.addCallback = function (callback) {
            if (typeof callback === "function") {
                if (m.callbacks === undefined) {
                    m.callbacks = [callback];
                } else {
                    m.callbacks.push(callback);
                }
            }
        }
        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
        globalTime = timer;
        display(); // call demo code
        requestAnimationFrame(update);
    }
    setTimeout(function(){
        resizeCanvas();
        mouse.start(canvas, true);
        window.addEventListener("resize", resizeCanvas);
        requestAnimationFrame(update);
    },0);
})();
/** SimpleFullCanvasMouse.js end **/
// creates a blank image with 2d context
function createImage(w,h){var i=document.createElement("canvas");i.width=w;i.height=h;i.ctx=i.getContext("2d");return i;}```

UPDATE

Added many more comments.

Added toScreen(x,y) function to view object. Converts from world coordinates to screen coordinates.

Improved rotation method to set absolute x Axis direction.

Added rotation feed back with indicators to show rotation origin and the current x Axis direction and a red line to indicate new x Axis direction if mouse button down.

Showing scale in help text display.

Title: Draw on rotated CANVAS - Part 2

Body:
As a follow up to this question and answer...I have another issue to solve:

When I draw on a canvas and then apply some transformations like rotation, I would like to keep what was drawn and continue the drawing.

To test this, use the mouse to draw something and then click "rotate".

This is what I'm trying, but the canvas gets erased.

JS

//main variables
canvas = document.createElement("canvas");
canvas.width = 500;
canvas.height = 300;
canvas.ctx = canvas.getContext("2d");
ctx = canvas.ctx;

canvas_aux = document.createElement("canvas");
canvas_aux.width = 500;
canvas_aux.height = 300;
canvas_aux.ctx = canvas.getContext("2d");
ctx_aux = canvas_aux.ctx;

function rotate()
{
    ctx_aux.drawImage(canvas, 0, 0); //new line: save current drawing

    timer += timerStep;

    var cw = canvas.width / 2;
    var ch = canvas.height / 2;

    ctx.setTransform(1, 0, 0, 1, 0, 0);  // reset the transform so we can clear
    ctx.clearRect(0, 0, canvas.width, canvas.height);  // clear the canvas

    createMatrix(cw, ch -50, scale, timer);

    var m = matrix;
    ctx.setTransform(m[0], m[1], m[2], m[3], m[4], m[5]);

    //draw();
    ctx.drawImage(canvas_aux, 0, 0); //new line: repaint current drawing

    if(timer <= rotation )
    {
        requestAnimationFrame(rotate);
    }
}

DEMO (updated version of original in linked question/answer)

https://jsfiddle.net/mgf8uz7s/1/

Body:
Record all paths, use canvas buffer to keep interface smooth

You have several options which will depend on what the requirements are.

Offscreen buffer/s to hold the rendered lines. Render to the offscreen buffer then draw the buffer to the display canvas. This is the quickest method but you are working with pixels, thus if you zoom you will get pixel artifacts and it will limit the size of the drawing area (still large but not pseudo infinite) and severely restrict the number of undos your can provide due to memory limits

Buffer paths as they are draw, basicly recording mouse movements and clicks, then re-rendering all visible paths each time you update the display. This will let you zoom and rotate without pixel artifacts, give you a draw area as large as you like (within limit of 64bit doubles) and a bonus undo all the way back to the first line. The problem with this method is that it quickly becomes very slow (though you can improve rendering speed with webGL)

A combination of the above two methods. Record the paths as they are drawn, but also render them to an offscreen canvas/s. Use the offscreen canvas to update the display and keep the refresh rate high. You only re-render the offscreen canvas when you need to, ie when you undo or if you zoom, you will not need to re-render when you pan or rotate.

Demo

I am not going to do a full drawing package so this is just an example that uses an offscreen buffer to hold the visible paths. All paths that are drawn are recorded in a paths array. When the user changes the view, pan, zoom, rotate, the paths are redrawn to the offscreen canvas to match the new view.

There is some boilerplate to handle setup and mouse that can be ignored. As there is a lot of code and time is short you will have to pick out what you need from it as the comments are short.

There is a paths object for paths. view holds the transform and associated functions. Some functions for pan, zoom, rotate. And a display function that renders and handles all mouse and user IO. The pan,zoom and scale controls are accessed via holding the mouse modifiers ctrl, alt, shift

var drawing = createImage(100,100); // offscreen canvas for drawing paths

// the onResize is a callback used by the boilerplate code at the bottom of this snippet
// it is called whenever the display size has changed (including starting app). It is
// debounced by 100ms to prevent needless calls
var onResize = function(){
    drawing.width = canvas.width;
    drawing.height = canvas.height;
    redrawBuffers = true; // flag that drawing buffers need redrawing
    ctx.font = "18px arial";
    ctx.textAlign = "center";
    ctx.textBaseline = "middle";
    view.pos.x = cw;  // set origin at center of screen
    view.pos.y = ch;
    view.update();
}
const paths = [];  // array of all recorded paths
const path = {   // descriptor of a path object
    addPoint(x,y){   // adds a point to the path
        this.points.push({x,y});
    },
    draw(ctx){   // draws this path on context ctx
        var i = 0;
        ctx.beginPath();
        ctx.moveTo(this.points[i].x,this.points[i++].y);
        while(i < this.points.length){
            ctx.lineTo(this.points[i].x,this.points[i++].y);
        }
        ctx.stroke();
    }
}
// creates a new path and adds it to the array of paths.
// returns the new path
function addPath(){
    var newPath;
    newPath = Object.assign({points : []},path);
    paths.push(newPath)
    return newPath;
}
// draws all recorded paths onto context cts using the current view
function drawAll(ctx){
    ctx.setTransform(1,0,0,1,0,0);
    ctx.clearRect(0,0,w,h);
    var m = view.matrix;
    ctx.setTransform(m[0],m[1],m[2],m[3],m[4],m[5]);
    var i = 0;
    for(i = 0; i < paths.length; i ++){
        paths[i].draw(ctx);
    }
}

// this controls the view
const view = {
    matrix : [1,0,0,1,0,0],  // current view transform
    invMatrix : [1,0,0,1,0,0], // current inverse view transform
    rotate : 0,  // current x axis direction in radians
    scale : 1,   // current scale
    pos : {      // current position of origin
        x : 0,
        y : 0,
    },
    update(){ // call to update transforms
        var xdx = Math.cos(this.rotate) * this.scale;
        var xdy = Math.sin(this.rotate) * this.scale;
        var m = this.matrix;
        var im = this.invMatrix;
        m[0] = xdx;
        m[1] = xdy;
        m[2] = -xdy;
        m[3] = xdx;
        m[4] = this.pos.x;
        m[5] = this.pos.y;
        // calculate the inverse transformation
        cross = m[0] * m[3] - m[1] * m[2];
        im[0] =  m[3] / cross;
        im[1] = -m[1] / cross;
        im[2] = -m[2] / cross;
        im[3] =  m[0] / cross;
    },
    mouseToWorld(){  // conver screen to world coords
        var xx, yy, m;
        m = this.invMatrix;
        xx = mouse.x - this.matrix[4];
        yy = mouse.y - this.matrix[5];
        mouse.xr =  xx * m[0] + yy * m[2];
        mouse.yr =   xx * m[1] + yy * m[3];
    },
    toWorld(x,y,point = {}){  // convert screen to world coords
        var xx, yy, m;
        m = this.invMatrix;
        xx = x - this.matrix[4];
        yy = y - this.matrix[5];
        point.x =  xx * m[0] + yy * m[2];
        point.y = xx * m[1] + yy * m[3];
        return point;
    },
    toScreen(x,y,point = {}){  // convert world coords to  coords
        var m;
        m = this.matrix;
        point.x =  x * m[0] + y * m[2] + m[4];
        point.y = x * m[1] + y * m[3] + m[5];
        return point;
    },
    clickOrigin : {  // used to hold coords to deal with pan zoom and rotate
        x : 0,
        y : 0,
        scale : 1,
    },
   dragging : false, // true is dragging
   startDrag(){  // called to start a Orientation UI input such as rotate, pan and scale
        if(!view.dragging){
            view.dragging = true;
            view.clickOrigin.x = mouse.xr;
            view.clickOrigin.y = mouse.yr;
            view.clickOrigin.screenX = mouse.x;
            view.clickOrigin.screenY = mouse.y;
            view.clickOrigin.scale = view.scale;
        }
   }
}

// functions to do pan zoom and scale
function panView(){  // pans the view
    view.startDrag();  // set origins as referance point
    view.pos.x -= (view.clickOrigin.screenX - mouse.x);
    view.pos.y -= (view.clickOrigin.screenY - mouse.y);
    view.update();
    view.mouseToWorld(); // get the new mouse pos
    view.clickOrigin.screenX = mouse.x; // save the new mouse coords
    view.clickOrigin.screenY = mouse.y;
}
// scales the view
function scaleView(){
    view.startDrag();
    var y = view.clickOrigin.screenY - mouse.y;
    if(y !== 0){
        view.scale = view.clickOrigin.scale + (y/ch);
        view.update();
    }
}
// rotates the view by setting the x axis direction
function rotateView(){
    view.startDrag();
    workingCoord = view.toScreen(0,0,workingCoord); // get location of origin
    var x = workingCoord.x - mouse.x;
    var y = workingCoord.y - mouse.y;
    var dist = Math.sqrt(x * x + y * y);
    if(dist > 2 / view.scale){
        view.rotate = Math.atan2(-y,-x);
        view.update();
    }
}
var currentPath; // Holds the currently drawn path
var redrawBuffers = false; // if true this indicates that all paths need to be redrawn
var workingCoord; // var to use as a coordinate

// main loop function called from requestAnimationFrame callback in boilerplate code
function display() {
    var showTransform = false;  // flags that view is being changed
    // clear the canvas and set defaults
    ctx.setTransform(1, 0, 0, 1, 0, 0); // reset transform
    ctx.globalAlpha = 1; // reset alpha
    ctx.clearRect(0, 0, w, h);
    view.mouseToWorld();  // get the mouse world coords

    // get the transform matrix
    var m = view.matrix;
    // show feedback
    if(mouse.shift || mouse.alt || mouse.ctrl){
        if(mouse.shift){
            ctx.fillText("Click drag to pan",cw, 20);
        }else if(mouse.ctrl){
            ctx.fillText("Click drag to rotate",cw, 20);
        }else{
            ctx.fillText("Click drag to scale : " + view.scale.toFixed(4),cw, 20);
        }
    }else{
          ctx.fillText("Click drag to draw.",cw, 20);
          ctx.fillText("Hold [shift], [ctrl], or [alt] and use mouse to pan, rotate, scale",cw, 40);
    }
    if(mouse.buttonRaw === 1){ // when mouse is down
        if(mouse.shift || mouse.alt || mouse.ctrl){ // pan zoom rotate
            if(mouse.shift){
                panView();
            }else if(mouse.ctrl){
                rotateView();
            }else{
                scaleView();
            }
            m = view.matrix;
            showTransform = true;
            redrawBuffers = true;
        }else{ // or add a path
            if(currentPath === undefined){
                currentPath = addPath();
            }
            currentPath.addPoint(mouse.xr,mouse.yr)
        }
    }else{
        // if there is a path then draw it onto the offscreen canvas and
        // reset the path to undefined
        if(currentPath !== undefined){
            currentPath.draw(drawing.ctx);
            currentPath = undefined;
        }
        view.dragging = false; // incase there is a pan/zoom/scale happening turn it off
    }
    if(showTransform){  // redraw all paths when pan rotate or zoom
        redrawBuffers = false;
        drawAll(drawing.ctx);
        ctx.drawImage(drawing,0,0);
    }else{  // draws the sceen when normal drawing mode.
        if(redrawBuffers){
            redrawBuffers = false;
            drawAll(drawing.ctx);
        }
        ctx.drawImage(drawing,0,0);
        ctx.setTransform(m[0],m[1],m[2],m[3],m[4],m[5]);
        drawing.ctx.setTransform(m[0],m[1],m[2],m[3],m[4],m[5]);

        // draw a cross hair.
        if(mouse.buttonRaw === 0){
            var invScale = 1 / view.scale; // get inverted scale
            ctx.beginPath();
            ctx.moveTo(mouse.xr - 10 * invScale,mouse.yr);
            ctx.lineTo(mouse.xr + 10 * invScale,mouse.yr);
            ctx.moveTo(mouse.xr ,mouse.yr - 10 * invScale);
            ctx.lineTo(mouse.xr ,mouse.yr + 10 * invScale);
            ctx.lineWidth = invScale;
            ctx.stroke();
            ctx.lineWidth = 1;
        }
    }

    // draw a new path if being drawn
    if(currentPath){
        currentPath.draw(ctx);
    }
    // If rotating or about to rotate show feedback
    if(mouse.ctrl){
        ctx.setTransform(m[0],m[1],m[2],m[3],m[4],m[5]);
        view.mouseToWorld();  // get the mouse world coords
        ctx.strokeStyle = "black";
        ctx.lineWidth = 3;
        ctx.beginPath();
        ctx.arc(0,0,3,0,Math.PI * 2);
        ctx.moveTo(0,0);
        ctx.lineTo(mouse.xr,mouse.yr);
        ctx.stroke();
        ctx.lineWidth = 1.5;
        ctx.strokeStyle = "red";
        ctx.beginPath();
        ctx.arc(0,0,3,0,Math.PI * 2);
        ctx.moveTo(0,0);
        ctx.lineTo(mouse.xr,mouse.yr);
        ctx.stroke();
        ctx.strokeStyle = "black";
        ctx.beginPath();
        ctx.moveTo(0,0);
        ctx.lineTo(200000 / view.scale,0);
        ctx.stroke();
        ctx.scale(1/ view.scale,1 / view.scale);
        ctx.fillText("X axis",100 ,-10  );
    }
}

/******************************************************************************/
// end of answer code
/******************************************************************************/

//Boiler plate from here down and can be ignored.
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;
            }
            if (m.callbacks) {
                m.callbacks.forEach(c => c(e));
            }
            e.preventDefault();
        }
        m.addCallback = function (callback) {
            if (typeof callback === "function") {
                if (m.callbacks === undefined) {
                    m.callbacks = [callback];
                } else {
                    m.callbacks.push(callback);
                }
            }
        }
        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
        globalTime = timer;
        display(); // call demo code
        requestAnimationFrame(update);
    }
    setTimeout(function(){
        resizeCanvas();
        mouse.start(canvas, true);
        window.addEventListener("resize", resizeCanvas);
        requestAnimationFrame(update);
    },0);
})();
/** SimpleFullCanvasMouse.js end **/
// creates a blank image with 2d context
function createImage(w,h){var i=document.createElement("canvas");i.width=w;i.height=h;i.ctx=i.getContext("2d");return i;}

UPDATE

Added many more comments.

Added toScreen(x,y) function to view object. Converts from world coordinates to screen coordinates.

Improved rotation method to set absolute x Axis direction.

Added rotation feed back with indicators to show rotation origin and the current x Axis direction and a red line to indicate new x Axis direction if mouse button down.

Showing scale in help text display.


来源: stackexchange

问:
Universal Links - Attempting to load the view of a view controller while it is deallocating is not allowed for UIAlertController
I am pretty sure that it is a framework bug but just to make sure - did someone else have this problem and found a solution?

When I click on a universal link and my opens - I am getting the following error in the console:

Attempting to load the view of a view controller while it is deallocating is not allowed and may result in undefined behavior ()

There is no UIAlertController used at this point in the app - its just a view controller which has a web view - thats all.

Can someone help me or should I send a bug report?

Br Chris

答:
I think this is just a bug of iOS9. I'm having a similar issue, so I created a very simple project and tested w/iOS9 (iphone6+) and iOS10 (iphone7+). For my case, the error message appeared at UIDocumentInteractionController AND when I chose apple's apps (e.g., email). The error message came only w/ iOS9. So, I think this is just a bug of iOS9 and I'm simply ignoring. Here is the code.</p>

```<code>import UIKit
class ViewController: UIViewController {
  var documentInteractionController: UIDocumentInteractionController! = nil
  @IBAction func openDocInteraction(_ sender: Any) {
    if (documentInteractionController == nil) {
        documentInteractionController = UIDocumentInteractionController()
    }
    let pngfile = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] + "/testfile.png"
    documentInteractionController.url = URL(fileURLWithPath: pngfile)
    documentInteractionController.presentOptionsMenu(from: view.frame, in: view, animated: true)
  }
  override func viewDidLoad() {
    super.viewDidLoad()
  }
}
</code>```

ContentLicense: CC BY-SA 3.0

Body:
I think this is just a bug of iOS9. I'm having a similar issue, so I created a very simple project and tested w/iOS9 (iphone6+) and iOS10 (iphone7+). For my case, the error message appeared at UIDocumentInteractionController AND when I chose apple's apps (e.g., email). The error message came only w/ iOS9. So, I think this is just a bug of iOS9 and I'm simply ignoring. Here is the code.

import UIKit
class ViewController: UIViewController {
  var documentInteractionController: UIDocumentInteractionController! = nil
  @IBAction func openDocInteraction(_ sender: Any) {
    if (documentInteractionController == nil) {
        documentInteractionController = UIDocumentInteractionController()
    }
    let pngfile = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] + "/testfile.png"
    documentInteractionController.url = URL(fileURLWithPath: pngfile)
    documentInteractionController.presentOptionsMenu(from: view.frame, in: view, animated: true)
  }
  override func viewDidLoad() {
    super.viewDidLoad()
  }
}


问:
How do I fade the edges of an image in Java? (example given)
I am currently in the middle of a project in which I have to grayscale, blue filter, red filter, green filter, and fade the edges of a picture of a cat at the press of a JButton. grayscale and color filtering the picture was easy, but I can't seem to figure out how to fade the image's edges to black.

What I mean by this, is I have this picture of the cat I need to change:

and I need to change it into something like this one:

```<code>    import javax.imageio.ImageIO;
    import javax.swing.JButton;
    import javax.swing.JPanel;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.ImageIcon;
    import java.awt.image.BufferedImage;
    import java.io.File;
    import java.io.IOException;

    public class GUIKittenPicJC {
        public static class KittenChanger extends JPanel {
                BufferedImage img = null;
                File Kitten = null;
                ImageIcon imagetransformed = null;
                JButton grayscale = new JButton("Grayscale Image");
                JButton rgbB = new JButton("Blue filter this image");
                JButton rgbR = new JButton("Red filter this image");
                JButton rgbG = new JButton("Green filter this image");
                JButton fader = new JButton("Fade this image");
                {
                try
                {
                    Kitten = new File("C:\\Users\\Jarrod\\Desktop\\Lab 3\\Lab 3\\kitten.bmp");
                    img = ImageIO.read(Kitten);
                }
                catch(IOException e)
                {
                  System.out.println(e);
                }
            ImageIcon image = new ImageIcon(img);
            JLabel imageLabel = new JLabel(image);
            add(imageLabel);
            grayscale.addActionListener(e->{
                       imagetransformed = new ImageIcon(Grayscale(img));
                       imageLabel.setIcon(imagetransformed);
                       imgReset();
                    });
            rgbB.addActionListener(e->{
                   imagetransformed = new ImageIcon(Bluify(img));
                   imageLabel.setIcon(imagetransformed);
                   imgReset();
                });
            rgbG.addActionListener(e->{
                   imagetransformed = new ImageIcon(Greenify(img));
                   imageLabel.setIcon(imagetransformed);
                   imgReset();
                });
            rgbR.addActionListener(e->{
                   imagetransformed = new ImageIcon(Redify(img));
                   imageLabel.setIcon(imagetransformed);
                   imgReset();
                });
                        add(grayscale);
                        add(rgbB);
                        add(rgbG);
                        add(rgbR);
                }
                private void imgReset() {
                    try {
                            img = ImageIO.read(Kitten);
                        }
                    catch (IOException e1)
                        {
                            e1.printStackTrace();
                        }
                }

        }
        public static void main(String[] args)
        {
           createGUI();
          }
            private static void createGUI() {

                JFrame frame = new JFrame("Kitten Changer");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                KittenChanger newContentPane = new KittenChanger();
                newContentPane.setOpaque(true);
                frame.setContentPane(newContentPane);
                frame.setSize(400, 500);
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }

//Grayscale and rgb filter methods follow, not important to question
}
</code>```

How would I create a method for this sort of filter and properly apply it to a button like the others?

答:
You would have to edit each of the pixels individually. The main idea of this solution is that you have the center of an image, and each pixel is changed as a function of the distance to the center.

More simply put, pixels farther from the center will be made darker.

This is what your code would look like:

```<code>BufferedImage cat; //assuming it is assigned
for(int i = 0; i < cat.getWidth(); i++) { // i is the x coord
    for(int j = 0; j < cat.getHeight(); j++) { // j is the y coord
        int color = cat.getRGB(i, j);
        int r = (color >> 16) & 0xff; //extract red value
        int g = (color >> 8) & 0xff;
        int b = color & 0xff;
        double scale = 0.75; /**** Change this to change the resulting effect ****/
        //pixel's distance from center
        double dist = Math.sqrt( Math.pow(i - cat.getWidth()/2, 2) + Math.pow(j - cat.getHeight()/2, 2) );
        r = (int) Math.max(0, r - dist*scale); //r - dist*scale makes px darker
        g = (int) Math.max(0, g - dist*scale); //Math.max makes sure r is always >= 0
        b = (int) Math.max(0, b - dist*scale);
        int newRGB = (r << 16) + (g << 8) + b; //convert r,g,b to single int
        cat.setRGB(i, j, newRGB); //finally, update rgb value
    }

}
</code>```

And, when I ran this code:

Remember, you can always change the effect by changing the scale variable in the code above.

Body:
I am currently in the middle of a project in which I have to grayscale, blue filter, red filter, green filter, and fade the edges of a picture of a cat at the press of a JButton. grayscale and color filtering the picture was easy, but I can't seem to figure out how to fade the image's edges to black.

What I mean by this, is I have this picture of the cat I need to change:

and I need to change it into something like this one:

    import javax.imageio.ImageIO;
    import javax.swing.JButton;
    import javax.swing.JPanel;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.ImageIcon;
    import java.awt.image.BufferedImage;
    import java.io.File;
    import java.io.IOException;

    public class GUIKittenPicJC {
        public static class KittenChanger extends JPanel {
                BufferedImage img = null;
                File Kitten = null;
                ImageIcon imagetransformed = null;
                JButton grayscale = new JButton("Grayscale Image");
                JButton rgbB = new JButton("Blue filter this image");
                JButton rgbR = new JButton("Red filter this image");
                JButton rgbG = new JButton("Green filter this image");
                JButton fader = new JButton("Fade this image");
                {
                try
                {
                    Kitten = new File("C:\\Users\\Jarrod\\Desktop\\Lab 3\\Lab 3\\kitten.bmp");
                    img = ImageIO.read(Kitten);
                }
                catch(IOException e)
                {
                  System.out.println(e);
                }
            ImageIcon image = new ImageIcon(img);
            JLabel imageLabel = new JLabel(image);
            add(imageLabel);
            grayscale.addActionListener(e->{
                       imagetransformed = new ImageIcon(Grayscale(img));
                       imageLabel.setIcon(imagetransformed);
                       imgReset();
                    });
            rgbB.addActionListener(e->{
                   imagetransformed = new ImageIcon(Bluify(img));
                   imageLabel.setIcon(imagetransformed);
                   imgReset();
                });
            rgbG.addActionListener(e->{
                   imagetransformed = new ImageIcon(Greenify(img));
                   imageLabel.setIcon(imagetransformed);
                   imgReset();
                });
            rgbR.addActionListener(e->{
                   imagetransformed = new ImageIcon(Redify(img));
                   imageLabel.setIcon(imagetransformed);
                   imgReset();
                });
                        add(grayscale);
                        add(rgbB);
                        add(rgbG);
                        add(rgbR);
                }
                private void imgReset() {
                    try {
                            img = ImageIO.read(Kitten);
                        }
                    catch (IOException e1)
                        {
                            e1.printStackTrace();
                        }
                }

        }
        public static void main(String[] args)
        {
           createGUI();
          }
            private static void createGUI() {

                JFrame frame = new JFrame("Kitten Changer");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                KittenChanger newContentPane = new KittenChanger();
                newContentPane.setOpaque(true);
                frame.setContentPane(newContentPane);
                frame.setSize(400, 500);
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }

//Grayscale and rgb filter methods follow, not important to question
}

How would I create a method for this sort of filter and properly apply it to a button like the others?

Body:
You would have to edit each of the pixels individually. The main idea of this solution is that you have the center of an image, and each pixel is changed as a function of the distance to the center.

More simply put, pixels farther from the center will be made darker.

This is what your code would look like:

BufferedImage cat; //assuming it is assigned
for(int i = 0; i < cat.getWidth(); i++) { // i is the x coord
    for(int j = 0; j < cat.getHeight(); j++) { // j is the y coord
        int color = cat.getRGB(i, j);
        int r = (color >> 16) & 0xff; //extract red value
        int g = (color >> 8) & 0xff;
        int b = color & 0xff;
        double scale = 0.75; /**** Change this to change the resulting effect ****/
        //pixel's distance from center
        double dist = Math.sqrt( Math.pow(i - cat.getWidth()/2, 2) + Math.pow(j - cat.getHeight()/2, 2) );
        r = (int) Math.max(0, r - dist*scale); //r - dist*scale makes px darker
        g = (int) Math.max(0, g - dist*scale); //Math.max makes sure r is always >= 0
        b = (int) Math.max(0, b - dist*scale);
        int newRGB = (r << 16) + (g << 8) + b; //convert r,g,b to single int
        cat.setRGB(i, j, newRGB); //finally, update rgb value
    }

}

And, when I ran this code:

Remember, you can always change the effect by changing the scale variable in the code above.


问:
Changing unique constrained column in EF Core using a transaction
I'm using EF Core (inside ASP.NET Core, and my DbContext lives per request/scope).

My Employee entity has this property:

```<code>public bool? IsBoss { get; set; }
</code>```

and this config:

```<code>entityBuilder.Property(b => b.IsBoss).IsRequired(false);
entityBuilder.HasIndex(b => b.IsBoss).IsUnique();
</code>```

This creates a filtered index, so there can be only one true, one false, but many nulls.

My app requires that I always have exactly one employee with IsBoss==true.

Suppose I want to swap two employees around.

```<code>employee1.IsBoss = null;
employee2.IsBoss = true;
context.SaveChanges();
</code>```

This throws a unique constraint violation exception.

I can fix that by wrapping it in a transaction:

```<code>using (var transaction = context.BeginTransaction())
{
  try
  {
    employee1.IsBoss = null;
    context.SaveChanges();

    employee2.IsBoss = true;
    context.SaveChanges();

    transaction.Commit();
  }
  catch
  {
    transaction.Rollback();
  }
}
</code>```

My question is: why does the first approach fail? I thought EF Core automatically wraps everything in a transaction. Why must I use a transaction?

答:
The first approach fails due to reason different than transactions.
Tracking issue on EF repo which covers scenario same as yours.

When SaveChanges is called, EF processes the changes and computes commands to be sent to database. This set of commands can have dependency. Like in your case, you need to set value to null for employee1 before you can set value to true for employee2. EF does sorting of commands to find out order in which they need to be executed. EF did this sorting based on the foreign key constraints. But as that issue reported, we did not account for unique index so commands were being sent in wrong order causing unique constraint violation.

The issue is already fixed in current codebase. It will be available in next public release. Meanwhile as a work-around, you need to call SaveChanges twice as you are doing in your second code. Calling SaveChanges multiple times you can control ordering of commands being sent to database. You don't need to wrap it in a transaction unless you want both the changes to be one atomic operation. Each SaveChanges have own transaction unless user starts one.

Body:
I'm using EF Core (inside ASP.NET Core, and my DbContext lives per request/scope).

My Employee entity has this property:

public bool? IsBoss { get; set; }

and this config:

entityBuilder.Property(b => b.IsBoss).IsRequired(false);
entityBuilder.HasIndex(b => b.IsBoss).IsUnique();

This creates a filtered index, so there can be only one true, one false, but many nulls.

My app requires that I always have exactly one employee with IsBoss==true.

Suppose I want to swap two employees around.

employee1.IsBoss = null;
employee2.IsBoss = true;
context.SaveChanges();

This throws a unique constraint violation exception.

I can fix that by wrapping it in a transaction:

using (var transaction = context.BeginTransaction())
{
  try
  {
    employee1.IsBoss = null;
    context.SaveChanges();

    employee2.IsBoss = true;
    context.SaveChanges();

    transaction.Commit();
  }
  catch
  {
    transaction.Rollback();
  }
}

My question is: why does the first approach fail? I thought EF Core automatically wraps everything in a transaction. Why must I use a transaction?


问:
How to combine 2 int value columns to varchar with specified length
i would like to combine this two int columns into one:

```<code>Month   Year
1       2017
2       2016
12      2016
</code>```

i used SELECT CAST(Year AS nvarchar(20)) + '-' + CAST(Month AS nvarchar(20)) AS newcolumn from table_name

however, the result that i got is

```<code>newcolumn
2017-1
2016-2
2016-10
</code>```

my desired result is

```<code>newcolumn
2017-01
2016-02
2016-10
</code>```

im using ms sql server.

Thanks

答:
You can pad the month format so that it is forced to display 2 numbers.

```<code>SELECT CAST(Year AS nvarchar(20)) + '-'
+ RIGHT('00' + CAST(Month AS nvarchar(20)),2) AS newcolumn from table_name
</code>```

Sample with Getdate():

```<code>SELECT CAST(YEAR(GETDATE()) as nvarchar(20)) + '-' +
       RIGHT('00' + CAST(MONTH(GETDATE()) as NVARCHAR(2)),2)
</code>```

Body:
i would like to combine this two int columns into one:

Month   Year
1       2017
2       2016
12      2016

i used SELECT CAST(Year AS nvarchar(20)) + '-' + CAST(Month AS nvarchar(20)) AS newcolumn from table_name

however, the result that i got is

newcolumn
2017-1
2016-2
2016-10

my desired result is

newcolumn
2017-01
2016-02
2016-10

im using ms sql server.

Thanks

Body:
You can pad the month format so that it is forced to display 2 numbers.

SELECT CAST(Year AS nvarchar(20)) + '-'
+ RIGHT('00' + CAST(Month AS nvarchar(20)),2) AS newcolumn from table_name

Sample with Getdate():

SELECT CAST(YEAR(GETDATE()) as nvarchar(20)) + '-' +
       RIGHT('00' + CAST(MONTH(GETDATE()) as NVARCHAR(2)),2)


问:
basic_client which is built with Makefile BusAttachment::Connect failed
I just built standard core 15.09a on linux and run sample apps basic_client.

This one built by scons.

```<code>bin/samples/basic_client works fine.
</code>```

However, I built cpp/samples/basic/basic_client with Makefile.

It does not work. It shows an error

```<code>"0.232 ****** ERROR ALLJOYN external          ...e/src/BusAttachment.cc:560 | BusAttachment::Connect failed: ER_OS_ERROR
BusAttachment::Connect('') failed.".
</code>```

So, I launched the bin/allhoyn-daemon, then no error occurred.

What is difference between scon and Makefile version?

Additionally, where can I get the information of application under the bin directory?

I searched the web-site of AllSeen and couldn't find any info.

答:
As i know, the error that you got when build with Makefile happens because basic_client hasn't alljoyn router inside, with scons it runs fine because it compiles with BR=on and some flags:

```<code>-lajrouter -lBundledRouter.o -lssl -lcrypto
</code>```

For more infomation, visit: here

Body:
I just built standard core 15.09a on linux and run sample apps basic_client.

This one built by scons.

bin/samples/basic_client works fine.

However, I built cpp/samples/basic/basic_client with Makefile.

It does not work. It shows an error

"0.232 ****** ERROR ALLJOYN external          ...e/src/BusAttachment.cc:560 | BusAttachment::Connect failed: ER_OS_ERROR
BusAttachment::Connect('') failed.".

So, I launched the bin/allhoyn-daemon, then no error occurred.

What is difference between scon and Makefile version?

Additionally, where can I get the information of application under the bin directory?

I searched the web-site of AllSeen and couldn't find any info.

Body:
As i know, the error that you got when build with Makefile happens because basic_client hasn't alljoyn router inside, with scons it runs fine because it compiles with BR=on and some flags:

-lajrouter -lBundledRouter.o -lssl -lcrypto

For more infomation, visit: here


问:
Associative arrays in C
I am implementing a way to transfer a set of data to a programmable dongle. The dongle is based on a smart card technology and can execute an arbitrary code inside. The input and output data is passed as a binary blocks that can be accessed via input and output pointers.

I would like to use an associative array to simplify the data processing code. Everything should work this way:

First the host application:

```<code>// Host application in C++
in_data["method"] = "calc_r";
in_data["id"] = 12;
in_data["loc_a"] = 56.19;
in_data["loc_l"] = 44.02;
processor->send(in_data);
</code>```

Next the code inside the dongle:

```<code>// Some dongle function in C
char* method_name = assoc_get_string(in_data, "method");
int id = assoc_get_int(in_data, "id");
float loc_a = assoc_get_float(in_data, "loc_a");
float loc_l = assoc_get_float(in_data, "loc_l");
</code>```

So my question is about the dongle part functionality. Is there C code or library to implement such an associative array behavior like the above?

答:
This is an old thread, but I thought this might still be useful for anyone out there looking for an implementation. It doesn't take too much code; I did mine in ~100 lines of without any extra library. I called it a dictionary since it parallels (sort of) the python datatype. Here is my code:

```<code>#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>

typedef struct hollow_list hollow_list;

struct hollow_list{
    unsigned int size;
    void *value;
    bool *written;
    hollow_list *children;
};

//Creates a hollow list and allocates all of the needed memory
hollow_list hollow_list_create(unsigned int size){
    hollow_list output;
    output = (hollow_list) {.size = size, .value = (void *) 0, .written = calloc(size, sizeof(bool)), .children = calloc(size, sizeof(hollow_list))};
    return output;
}

//Frees all memory of associated with a hollow list and its children
void hollow_list_free(hollow_list *l, bool free_values){
    int i;
    for(i = 0; i < l->size; i++){
        hollow_list_free(l->children + i, free_values);
    }
    if(free_values){
        free(l->value);
    }
    free(l);
}

//Reads from the hollow list and returns a pointer to the item's data
void *hollow_list_read(hollow_list *l, unsigned int index){
    if(index == 0){
        return l->value;
    }
    unsigned int bit_checker;
    bit_checker = 1<<(l->size - 1);
    int i;
    for(i = 0; i < l->size; i++){
        if(bit_checker & index){
            if(l->written[i] == true){
                return hollow_list_read(l->children + i, bit_checker ^ index);
            } else {
                return (void *) 0;
            }
        }
        bit_checker >>= 1;
    }
}

//Writes to the hollow list, allocating memory only as it needs
void hollow_list_write(hollow_list *l, unsigned int index, void *value){
    if(index == 0){
        l->value = value;
    } else {
        unsigned int bit_checker;
        bit_checker = 1<<(l->size - 1);
        int i;
        for(i = 0; i < l->size; i++){
            if(bit_checker & index){
                if(!l->written[i]){
                    l->children[i] = hollow_list_create(l->size - i - 1);
                    l->written[i] = true;
                }
                hollow_list_write(l->children + i, bit_checker ^ index, value);
                break;
            }
            bit_checker >>= 1;
        }
    }
}

typedef struct dictionary dictionary;

struct dictionary{
    void *value;
    hollow_list *child;
};

dictionary dictionary_create(){
    dictionary output;
    output.child = malloc(sizeof(hollow_list));
    *output.child = hollow_list_create(8);
    output.value = (void *) 0;
    return output;
}

void dictionary_write(dictionary *dict, char *index, unsigned int strlen, void *value){
    void *hollow_list_value;
    dictionary *new_dict;
    int i;
    for(i = 0; i < strlen; i++){
        hollow_list_value = hollow_list_read(dict->child, (int) index[i]);
        if(hollow_list_value == (void *) 0){
            new_dict = malloc(sizeof(dictionary));
            *new_dict = dictionary_create();
            hollow_list_write(dict->child, (int) index[i], new_dict);
            dict = new_dict;
        } else {
            dict = (dictionary *) hollow_list_value;
        }
    }
    dict->value = value;
}

void *dictionary_read(dictionary *dict, char *index, unsigned int strlen){
    void *hollow_list_value;
    dictionary *new_dict;
    int i;
    for(i = 0; i < strlen; i++){
        hollow_list_value = hollow_list_read(dict->child, (int) index[i]);
        if(hollow_list_value == (void *) 0){
            return hollow_list_value;
        } else {
            dict = (dictionary *) hollow_list_value;
        }
    }
    return dict->value;
}

int main(){
    char index0[] = "hello, this is a test";
    char index1[] = "hello, this is also a test";
    char index2[] = "hello world";
    char index3[] = "hi there!";
    char index4[] = "this is something";
    char index5[] = "hi there";

    int item0 = 0;
    int item1 = 1;
    int item2 = 2;
    int item3 = 3;
    int item4 = 4;

    dictionary d;
    d = dictionary_create();
    dictionary_write(&d, index0, 21, &item0);
    dictionary_write(&d, index1, 26, &item1);
    dictionary_write(&d, index2, 11, &item2);
    dictionary_write(&d, index3, 13, &item3);
    dictionary_write(&d, index4, 17, &item4);

    printf("%d\n", *((int *) dictionary_read(&d, index0, 21)));
    printf("%d\n", *((int *) dictionary_read(&d, index1, 26)));
    printf("%d\n", *((int *) dictionary_read(&d, index2, 11)));
    printf("%d\n", *((int *) dictionary_read(&d, index3, 13)));
    printf("%d\n", *((int *) dictionary_read(&d, index4, 17)));
    printf("%d\n", ((int) dictionary_read(&d, index5, 8)));
}
</code>```

Unfortunately you can't replicate the list[x] syntax, but this is the best alternative I have come up with.

ContentLicense: CC BY-SA 2.5

Title: Associative arrays in C

Body:
I am implementing a way to transfer a set of data to a programmable dongle. The dongle is based on a smart card technology and can execute an arbitrary code inside. The input and output data is passed as a binary blocks that can be accessed via input and output pointers.

I would like to use an associative array to simplify the data processing code. Everything should work this way:

First the host application:

// Host application in C++
in_data["method"] = "calc_r";
in_data["id"] = 12;
in_data["loc_a"] = 56.19;
in_data["loc_l"] = 44.02;
processor->send(in_data);

Next the code inside the dongle:

// Some dongle function in C
char* method_name = assoc_get_string(in_data, "method");
int id = assoc_get_int(in_data, "id");
float loc_a = assoc_get_float(in_data, "loc_a");
float loc_l = assoc_get_float(in_data, "loc_l");

So my question is about the dongle part functionality. Is there C code or library to implement such an associative array behavior like the above?

Body:
This is an old thread, but I thought this might still be useful for anyone out there looking for an implementation. It doesn't take too much code; I did mine in ~100 lines of without any extra library. I called it a dictionary since it parallels (sort of) the python datatype. Here is my code:

#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>

typedef struct hollow_list hollow_list;

struct hollow_list{
    unsigned int size;
    void *value;
    bool *written;
    hollow_list *children;
};

//Creates a hollow list and allocates all of the needed memory
hollow_list hollow_list_create(unsigned int size){
    hollow_list output;
    output = (hollow_list) {.size = size, .value = (void *) 0, .written = calloc(size, sizeof(bool)), .children = calloc(size, sizeof(hollow_list))};
    return output;
}

//Frees all memory of associated with a hollow list and its children
void hollow_list_free(hollow_list *l, bool free_values){
    int i;
    for(i = 0; i < l->size; i++){
        hollow_list_free(l->children + i, free_values);
    }
    if(free_values){
        free(l->value);
    }
    free(l);
}

//Reads from the hollow list and returns a pointer to the item's data
void *hollow_list_read(hollow_list *l, unsigned int index){
    if(index == 0){
        return l->value;
    }
    unsigned int bit_checker;
    bit_checker = 1<<(l->size - 1);
    int i;
    for(i = 0; i < l->size; i++){
        if(bit_checker & index){
            if(l->written[i] == true){
                return hollow_list_read(l->children + i, bit_checker ^ index);
            } else {
                return (void *) 0;
            }
        }
        bit_checker >>= 1;
    }
}

//Writes to the hollow list, allocating memory only as it needs
void hollow_list_write(hollow_list *l, unsigned int index, void *value){
    if(index == 0){
        l->value = value;
    } else {
        unsigned int bit_checker;
        bit_checker = 1<<(l->size - 1);
        int i;
        for(i = 0; i < l->size; i++){
            if(bit_checker & index){
                if(!l->written[i]){
                    l->children[i] = hollow_list_create(l->size - i - 1);
                    l->written[i] = true;
                }
                hollow_list_write(l->children + i, bit_checker ^ index, value);
                break;
            }
            bit_checker >>= 1;
        }
    }
}

typedef struct dictionary dictionary;

struct dictionary{
    void *value;
    hollow_list *child;
};

dictionary dictionary_create(){
    dictionary output;
    output.child = malloc(sizeof(hollow_list));
    *output.child = hollow_list_create(8);
    output.value = (void *) 0;
    return output;
}

void dictionary_write(dictionary *dict, char *index, unsigned int strlen, void *value){
    void *hollow_list_value;
    dictionary *new_dict;
    int i;
    for(i = 0; i < strlen; i++){
        hollow_list_value = hollow_list_read(dict->child, (int) index[i]);
        if(hollow_list_value == (void *) 0){
            new_dict = malloc(sizeof(dictionary));
            *new_dict = dictionary_create();
            hollow_list_write(dict->child, (int) index[i], new_dict);
            dict = new_dict;
        } else {
            dict = (dictionary *) hollow_list_value;
        }
    }
    dict->value = value;
}

void *dictionary_read(dictionary *dict, char *index, unsigned int strlen){
    void *hollow_list_value;
    dictionary *new_dict;
    int i;
    for(i = 0; i < strlen; i++){
        hollow_list_value = hollow_list_read(dict->child, (int) index[i]);
        if(hollow_list_value == (void *) 0){
            return hollow_list_value;
        } else {
            dict = (dictionary *) hollow_list_value;
        }
    }
    return dict->value;
}

int main(){
    char index0[] = "hello, this is a test";
    char index1[] = "hello, this is also a test";
    char index2[] = "hello world";
    char index3[] = "hi there!";
    char index4[] = "this is something";
    char index5[] = "hi there";

    int item0 = 0;
    int item1 = 1;
    int item2 = 2;
    int item3 = 3;
    int item4 = 4;

    dictionary d;
    d = dictionary_create();
    dictionary_write(&d, index0, 21, &item0);
    dictionary_write(&d, index1, 26, &item1);
    dictionary_write(&d, index2, 11, &item2);
    dictionary_write(&d, index3, 13, &item3);
    dictionary_write(&d, index4, 17, &item4);

    printf("%d\n", *((int *) dictionary_read(&d, index0, 21)));
    printf("%d\n", *((int *) dictionary_read(&d, index1, 26)));
    printf("%d\n", *((int *) dictionary_read(&d, index2, 11)));
    printf("%d\n", *((int *) dictionary_read(&d, index3, 13)));
    printf("%d\n", *((int *) dictionary_read(&d, index4, 17)));
    printf("%d\n", ((int) dictionary_read(&d, index5, 8)));
}

Unfortunately you can't replicate the list[x] syntax, but this is the best alternative I have come up with.


问:
Kivy PyInstaller on Appveyor
I would like to build the executables for my Kivy application using PyInstaller on Appveyor. I have the build working on my own computer, but on Appveyor build I get the following message:

```<code>OpenGL version detected: 1.1
Version: b'1.1.0'
Vendor: b'Microsoft Corporation'
Renderer: b'GDI Generic'
Try upgrading your graphics drivers and/or your graphics hardware in case of problems.
The application will leave now.
</code>```

I am using the default PyInstaller configuration as described in https://kivy.org/docs/guide/packaging-windows.html.

The appveyor.yml is as follows:

```<code>image: Visual Studio 2015

environment:
  matrix:
    - PYTHON: "C:\\Python34"
      PYTHON_VERSION: "3.4.x" # currently 3.4.3
      PYTHON_ARCH: "32"

    - PYTHON: "C:\\Python34-x64"
      PYTHON_VERSION: "3.4.x" # currently 3.4.3
      PYTHON_ARCH: "64"

install:
  # Install Python (from the official .msi of http://python.org) and pip when
  # not already installed.
  - ps: if (-not(Test-Path($env:PYTHON))) { & appveyor\install.ps1 }

  - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"

  # Check that we have the expected version and architecture for Python
  - "python --version"
  - "python -c \"import struct; print(struct.calcsize('P') * 8)\""

  - "pip install --disable-pip-version-check --user --upgrade pip"

  # Kivy and pyinstaller included
  - "%CMD_IN_ENV% pip install -r requirements.txt"

  # Saw somewhere on the internet, doesn't work with it nor without it.
  - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-desktop.ps1'))

build_script:
  - "%CMD_IN_ENV% python -m PyInstaller myapp.spec"

artifacts:
  - path: dist\*
</code>```

As far as I see it, it can go two ways:
* somehow prevent kivy pyinstaller hook from using OpenGL
* configure Appveyor to use OpenGL

Thanks for all your suggestions!

答: I afraid it is not possible as build happens in Hyper-V VMs which do not have physical GPU...</p>

Title: Kivy PyInstaller on Appveyor

Body:
I would like to build the executables for my Kivy application using PyInstaller on Appveyor. I have the build working on my own computer, but on Appveyor build I get the following message:

OpenGL version detected: 1.1
Version: b'1.1.0'
Vendor: b'Microsoft Corporation'
Renderer: b'GDI Generic'
Try upgrading your graphics drivers and/or your graphics hardware in case of problems.
The application will leave now.

I am using the default PyInstaller configuration as described in https://kivy.org/docs/guide/packaging-windows.html.

The appveyor.yml is as follows:

image: Visual Studio 2015

environment:
  matrix:
    - PYTHON: "C:\\Python34"
      PYTHON_VERSION: "3.4.x" # currently 3.4.3
      PYTHON_ARCH: "32"

    - PYTHON: "C:\\Python34-x64"
      PYTHON_VERSION: "3.4.x" # currently 3.4.3
      PYTHON_ARCH: "64"

install:
  # Install Python (from the official .msi of http://python.org) and pip when
  # not already installed.
  - ps: if (-not(Test-Path($env:PYTHON))) { & appveyor\install.ps1 }

  - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"

  # Check that we have the expected version and architecture for Python
  - "python --version"
  - "python -c \"import struct; print(struct.calcsize('P') * 8)\""

  - "pip install --disable-pip-version-check --user --upgrade pip"

  # Kivy and pyinstaller included
  - "%CMD_IN_ENV% pip install -r requirements.txt"

  # Saw somewhere on the internet, doesn't work with it nor without it.
  - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-desktop.ps1'))

build_script:
  - "%CMD_IN_ENV% python -m PyInstaller myapp.spec"

artifacts:
  - path: dist\*

As far as I see it, it can go two ways:
* somehow prevent kivy pyinstaller hook from using OpenGL
* configure Appveyor to use OpenGL

Thanks for all your suggestions!


问:
Set a key if it does not exist, otherwise return its value
I don't seem to find a command in Redis to achieve the following:

Set a key if it does not exist, otherwise return its value

Is there any way of achieving this in one transaction?

答:
(disclaimer: I'm a Lua kind of guy, the accepted answer is perfectly valid) One server-side Lua script? sure. One command? Don't think so ;)

Here's one way of doing it with EVAL:

```<code>local v = redis.call('GET', KEYS[1])
if v then
  return v
end
return redis.call('SET', KEYS[1], ARGV[1])
</code>```

Body:
(disclaimer: I'm a Lua kind of guy, the accepted answer is perfectly valid) One server-side Lua script? sure. One command? Don't think so ;)

Here's one way of doing it with EVAL:

local v = redis.call('GET', KEYS[1])
if v then
  return v
end
return redis.call('SET', KEYS[1], ARGV[1])


问:
Running ionic apps in an Android Emulator.
I am new in ionic framework,and want to run the app I made into an Android emulator. I type this command,

```<code>ionic emulate android
</code>```

and get this error.

```<code>Error: cmd: Command failed with exit code 1
</code>```

I have all the path variables set up correctly I think.

PATH

```<code>C:\Program Files\Git\usr\local;C:\Users\W\AppData\Roaming\npm\node_modules\bower\bin;C:\Users\Theodosios\AppData\Roaming\npm;C:\Program Files (x86)\Java\jre1.8.0_111\bin
</code>```

JAVA_OPTIONS

```<code>-Xmx512M
</code>```

Any ideas what is wrong and how can it be fixed?

Thanks,

Theo.

答:
Use this commands on console

npm install -g npm

npm install -g ionic cordova

npm install -g angular-cli

and setting the "Environment Variables"

JAVA_HOME

ANDROID_HOME

AVD

Body:
I am new in ionic framework,and want to run the app I made into an Android emulator. I type this command,

ionic emulate android

and get this error.

Error: cmd: Command failed with exit code 1

I have all the path variables set up correctly I think.

PATH

C:\Program Files\Git\usr\local;C:\Users\W\AppData\Roaming\npm\node_modules\bower\bin;C:\Users\Theodosios\AppData\Roaming\npm;C:\Program Files (x86)\Java\jre1.8.0_111\bin

JAVA_OPTIONS

-Xmx512M

Any ideas what is wrong and how can it be fixed?

Thanks,

Theo.


问:
Android Share the text link use intent to LINE APP
I have a question.

I need to use intent share the link text to LINE App in Android.

But I don't know how to share hyperlink  text to Line.

I know share the text to LINE message. below:

```<code>intent.setAction(Intent.ACTION_SEND);
intent = mContext.getPackageManager().getLaunchIntentForPackage(
                                            AppConfig.LINE_PACKAGE_NAME);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_TEXT, "this is share text but I want to set hyperlink");
mContext.startActivity(intent);
</code>```

Have anyone know how to set text link(ex:link to www.google.com) use intent and share to LINE App in android?

Thank you very much.

答:
Just add the hyper link to the end of the text: </p>

```<code>intent.putExtra(Intent.EXTRA_TEXT, "this is share text but I want to set hyperlink" + url);
</code>```

Body:
I have a question.

I need to use intent share the link text to LINE App in Android.

But I don't know how to share hyperlink  text to Line.

I know share the text to LINE message. below:

intent.setAction(Intent.ACTION_SEND);
intent = mContext.getPackageManager().getLaunchIntentForPackage(
                                            AppConfig.LINE_PACKAGE_NAME);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_TEXT, "this is share text but I want to set hyperlink");
mContext.startActivity(intent);

Have anyone know how to set text link(ex:link to www.google.com) use intent and share to LINE App in android?

Thank you very much.

Body:
Just add the hyper link to the end of the text:

intent.putExtra(Intent.EXTRA_TEXT, "this is share text but I want to set hyperlink" + url);


问:
multithreading and strtok
I will be continually splitting strings in a multithreaded application, I've read that strtok is not suitable for this, but why?

Should I consider using a semaphore around the portion of my code that calls strtok?

答: Use strtok_r() for thread safety.</p>

Title: multithreading and strtok

Body: Use strtok_r() for thread safety.


问:
How to connect two wireless networks to share internet?
I have 2 devices:

Device A: Huawei E8231 emits wifi signal

Device B: Ravpower WD-02 device emits wifi signal

Device A has access to internet and is running stock firmware.

Device B doesn't have access to internet and is running Openwrt Attitude Adjustment

I want device B to connect to A, wirelessly. So that when I connect to device B's wifi through computer or phone, I can access internet seamlessly. Just the way chromecast device's wifinetwork can easily join another wifi network for internet given SSID and password.

I have already looked up the option of WDS, which I think my device A doesn't support even if my device B supports it (as client).

Any other way I can go about this?

Thanks.

答:
For anyone having a similar situation: the answer is to make device A a Client of Device B.

Step 0: Open Openwrt login page on device B.

Step 1: Click on "join" and join the wifi network which which has internet. Make sure the network you check is 'lan'.

Step 2: Add new wifi interface. This is the signal you will connect to from your devices. Make sure the network checked is 'wan'.

Step 3: Go to Network>Interfaces> click on edit for 'wan' and under Physical settings and uncheck "Bridge" option.

and you should have your device B connecting to device A for internet wirelessly! :) Make sure router pages of either devices are not set to same location. That is to say, if one device is using 192.168.1.1 then make the other use something else.


问:
How to display item from server once a link is clicked
student here! So I created a function that runs an $.ajax request, and for each item, I output html to the DOM. Well, check it out:

```<code>  function getLinks (){
  let blogLinks;
  $.ajax(settings).then(function(data, status, xhr){
    data.forEach(function(item, i, arr){
      let id = item._id;
      if (item.title){
        blogLinks = `<li><a class="link" href=#${item.title}>${item.title}</a></li>`
        $($findUl).append(blogLinks);
      }
    })
  })
}
</code>```

this var might be useful info as well:

```<code>let $blogViewAll =
    $(`<div class="blog-view">
        <h3>Check Out These Blogs</h3>
        <div class="column-1">
          <ul class="link-list"></ul>
        </div>
        <div class="column-2"></div>
      </div>`);

let $findUl = $($blogViewAll).find('.link-list');
</code>```

It's doing exactly what I want it to do and appending the links to the page. Now I want to click on the link and have the message body display on the page. So I'm trying this:

```<code>$findUl.on('click', function(e){
  $.ajax(settings).then(function(data, status, xhr){
    data.forEach(function(item, i, arr){
      //since you clicked on that link
      //I should look at what you clicked
      //match it to the corresponding obj{item.body}
      //and display it, but I don't know how to match and grab :(
    })
  })
});
</code>```

And that's where I'm stuck! Any suggestions? Thanks!!!
(i'm using the tiny-za server btw)

答:
You can access the current link the user clicked on in your initial argument e, via the property e.currentTarget:

```<code>$findUl.on('click', function(e){
  // this gives you the DOM node that was clicked:
  console.log(e.currentTarget);
}
</code>```

A little shortcut to get the title using jQuery:

```<code>$findUl.on('click', function(e){
  var title = $(e.currentTarget).text();
}
</code>```

Another note, you should really only be loading the data from the server in that ajax call once, not every time something is clicked (which is sloooow).

I'd recommend loading the data into an object when the page loads, then using the obejct properties to append to the DOM. You'll be able to access that same object in future click handlers

Body:
student here! So I created a function that runs an $.ajax request, and for each item, I output html to the DOM. Well, check it out:

  function getLinks (){
  let blogLinks;
  $.ajax(settings).then(function(data, status, xhr){
    data.forEach(function(item, i, arr){
      let id = item._id;
      if (item.title){
        blogLinks = `<li><a class="link" href=#${item.title}>${item.title}</a></li>`
        $($findUl).append(blogLinks);
      }
    })
  })
}

this var might be useful info as well:

let $blogViewAll =
    $(`

Check Out These Blogs

`);

let $findUl = $($blogViewAll).find('.link-list');

It's doing exactly what I want it to do and appending the links to the page. Now I want to click on the link and have the message body display on the page. So I'm trying this:

$findUl.on('click', function(e){
  $.ajax(settings).then(function(data, status, xhr){
    data.forEach(function(item, i, arr){
      //since you clicked on that link
      //I should look at what you clicked
      //match it to the corresponding obj{item.body}
      //and display it, but I don't know how to match and grab :(
    })
  })
});

And that's where I'm stuck! Any suggestions? Thanks!!!
(i'm using the tiny-za server btw)

Body:
You can access the current link the user clicked on in your initial argument e, via the property e.currentTarget:

$findUl.on('click', function(e){
  // this gives you the DOM node that was clicked:
  console.log(e.currentTarget);
}

A little shortcut to get the title using jQuery:

$findUl.on('click', function(e){
  var title = $(e.currentTarget).text();
}

Another note, you should really only be loading the data from the server in that ajax call once, not every time something is clicked (which is sloooow).

I'd recommend loading the data into an object when the page loads, then using the obejct properties to append to the DOM. You'll be able to access that same object in future click handlers


问:
Create line breaks in output based on array index
I want to display output like this:

```<code>1 2 3 4
5 6 7 8
9 1 2
</code>```

I've tried this code:

```<code>$num = ['1','2','3','4','....'];
$size = sizeof($num) / 4;
foreach ($num as $key => $value) {
  echo $value;
  if($key >= round($size){
    echo "<br>"
  }
}
</code>```

But the output is like this:

```<code>1 2 3 4
5
6
7
8
...
</code>```

Can anyone suggest how to write the loop?

答:
```<code>$num= ['1','2','3','4','5','6','7','8','9'];
$size = sizeof($num) / 4;
foreach ($num as $key => $value){
   echo $value;
   if(($key+1) % 4 == 0){
      echo "<br>";
    }
   }
</code>```

You can use modulus instead of round. Cool I didn't know about sizeOf! Good to know. Mark this as the right answer pwease if this works!

Another way to do this if you didn't want to write out all the numbers that are in the Num Array is to just push them into an array with a while loop.

```<code>$num= [];
$i = 1;

//Set the Num Variable to have as many numbers as you want without having to manually enter them in
while ($i < 100) {
     array_push($num, $i);
     $i++;
}

//Run the actual code that adds breaks ever 4 lines
$size = sizeof($num) / 4;
foreach ($num as $key => $value){
   echo $value;
   if(($key+1) % 4 == 0){
      echo "<br>";
    }
   }
</code>```

Body:
I want to display output like this:

1 2 3 4
5 6 7 8
9 1 2

I've tried this code:

$num = ['1','2','3','4','....'];
$size = sizeof($num) / 4;
foreach ($num as $key => $value) {
  echo $value;
  if($key >= round($size){
    echo "
"
  }
}

But the output is like this:

1 2 3 4
5
6
7
8
...

Can anyone suggest how to write the loop?

Body:
$num= ['1','2','3','4','5','6','7','8','9'];
$size = sizeof($num) / 4;
foreach ($num as $key => $value){
   echo $value;
   if(($key+1) % 4 == 0){
      echo "<br>";
    }
   }

You can use modulus instead of round. Cool I didn't know about sizeOf! Good to know. Mark this as the right answer pwease if this works!

Another way to do this if you didn't want to write out all the numbers that are in the Num Array is to just push them into an array with a while loop.

$num= [];
$i = 1;

//Set the Num Variable to have as many numbers as you want without having to manually enter them in
while ($i < 100) {
     array_push($num, $i);
     $i++;
}

//Run the actual code that adds breaks ever 4 lines
$size = sizeof($num) / 4;
foreach ($num as $key => $value){
   echo $value;
   if(($key+1) % 4 == 0){
      echo "<br>";
    }
   }


问:
Show image in Tkinter for python 3.2
How to display an image in Python 3.2 using Tkinter? The PIL doesn't work so something that doesn't use it.</p>

答:
try this (for python 3)

```<code>from tkinter import PhotoImage

photo = PhotoImage(file="/home/pi/pile.gif")
lb=tkinter.Label(root,image = photo)
lb.grid(row=4, column=1)
</code>```

Title: Show image in Tkinter for python 3.2

Body:
try this (for python 3)

from tkinter import PhotoImage

photo = PhotoImage(file="/home/pi/pile.gif")
lb=tkinter.Label(root,image = photo)
lb.grid(row=4, column=1)


问:
Bootstrap columns not same height in iPad portrait mode
So portrait mode for this site I'm making is all messed up. Currently looks like this:

All I want is for the watch to be vertically centered, which it is in normal desktop view.

HTML:

```<code><header>
    <div class="container">
        <div class="row">
          <div class="col-sm-7 col-md-7  col-xl-7 col-lg-7">
              <div class="header-content">
                  <div class="header-content-inner">
                      <h1>One for all. All for you.</h1>
                      <!-- <a href="#download" class="btn btn-outline btn-xl page-scroll">Start Now for Free!</a> -->
                  </div>
              </div>
          </div>
          <div class="col-sm-5 col-md-5  col-xl-5 col-lg-5" style="height: 100%">
            <div class="device-container vertical-align">
              <div class="cycle-slideshow" data-cycle-timeout="1500" data-cycle-transition="1500" data-cycle-fx="fadeout" data-cycle-pause-on-hover="#er-pauser">
                <img class="img-responsive device-img center-block" src="img/landing2.png" alt="">
                <img class="img-responsive device-img center-block" src="img/landing1.png" alt="">
                <img class="img-responsive device-img center-block" src="img/landing3.png" alt="">
                <img class="img-responsive device-img center-block" src="img/landing5.png" alt="">
                <img class="img-responsive device-img center-block" src="img/landing4.png" alt="">
                <img class="img-responsive device-img center-block" src="img/landing6.png" alt="">
              </div>
            </div>
          </div>
        </div>
    </div>
    </header>
</code>```

CSS:

```<code>header {
        position: relative;
        width: 100%;
        min-height: auto;
        overflow-y: hidden;
        background: #47c0d7; /* Old browsers */
        background: -moz-linear-gradient(top, #47c0d7 0%, #e6f9ff 55%, #e6f9ff 100%); /* FF3.6-15 */
        background: -webkit-linear-gradient(top, #47c0d7 0%,#e6f9ff 55%,#e6f9ff 100%); /* Chrome10-25,Safari5.1-6 */
        background: linear-gradient(to bottom, #47c0d7 0%,#e6f9ff 55%,#e6f9ff 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
        filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#47c0d7', endColorstr='#e6f9ff',GradientType=0 ); /* IE6-9 */

    }
    header .header-content {
      text-align: center;
      padding: 100px 0px 0px;
      position: relative;

    }
    header .header-content .header-content-inner {
      width:100%;
      text-align: center;
      position: relative;
    /*  max-width: 500px;
    */  margin: 0 auto;
    }

    header .device-container {
    /*  max-width: 400px;*/
      height: 100%;
    /*  margin: 100px auto;
    */}

    header .device-img {
      left: 0;
      right: 0;
      width: 100%;
      margin: 0 auto;
      width: 300px;
    }

    @media (min-width: 768px) {
        header {
        min-height: 100%;
      }

      header .header-content {
        text-align: center;
        padding: 0;
        height: 100vh;
      }
      header .header-content .header-content-inner {
        text-align: center;
        max-width: none;
        margin: 0 auto;
        position: absolute;
        top: 50%;
        transform: translateY(-50%);
        width: 100%;
      }

    .about-content .about-content-inner {
        text-align: justify;
        max-width: none;
        position: relative;
        top: 50%;
        transform: translateY(-50%);
      }
      header .header-content .header-content-inner h1 {
        margin-top: 0;
        margin-bottom: 30px;
        font-size: 55px;
        color: #363939;
        text-align: center;
      }
    .about-content .about-content-inner h3 {
        font-size: 20px;
        font-weight: 400;
      }
      header .device-container {
     /*   max-width: none;
        max-height: calc(0vh);
        margin: 100px auto 0;*/
        padding: 0;
        height: 100vh;
      }
</code>```

The height of the column it's in isn't 100% and the container it's in (device-container) also doesn't appear to hold its contents.

Thank you so much for your help. Sorry there's so many commented it out things. Also if anyone has an idea why the navbar extends past the page making that light blue column to the right happen please let me know too.

答:
Setting 100% on a floating box will not make it 100% of parent height, as you'd expect. Your best solution, from where I see it, would be to use

```<code>header .row {
  display: flex;
  align-items: center;
  min-height: 100vh;
}
</code>```

Of course, this means you'll need to lose the huge top padding on the
, so it actually centers. If you're happy with how it looks on desktop, just wrap my suggestion in a @media query.

There's one more thing I'd note: you seem to use col-xl-* classes, which don't exist in Bootstrap, but you seem to miss the col-xs-* ones. Is this intentional? If not, do replace them.

Here's a fiddle with what I could make from the code you posted.

Don't forget to autoprefix.

Body:
So portrait mode for this site I'm making is all messed up. Currently looks like this:

All I want is for the watch to be vertically centered, which it is in normal desktop view.

HTML:

One for all. All for you.

CSS:

header {
        position: relative;
        width: 100%;
        min-height: auto;
        overflow-y: hidden;
        background: #47c0d7; /* Old browsers */
        background: -moz-linear-gradient(top, #47c0d7 0%, #e6f9ff 55%, #e6f9ff 100%); /* FF3.6-15 */
        background: -webkit-linear-gradient(top, #47c0d7 0%,#e6f9ff 55%,#e6f9ff 100%); /* Chrome10-25,Safari5.1-6 */
        background: linear-gradient(to bottom, #47c0d7 0%,#e6f9ff 55%,#e6f9ff 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
        filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#47c0d7', endColorstr='#e6f9ff',GradientType=0 ); /* IE6-9 */

    }
    header .header-content {
      text-align: center;
      padding: 100px 0px 0px;
      position: relative;

    }
    header .header-content .header-content-inner {
      width:100%;
      text-align: center;
      position: relative;
    /*  max-width: 500px;
    */  margin: 0 auto;
    }

    header .device-container {
    /*  max-width: 400px;*/
      height: 100%;
    /*  margin: 100px auto;
    */}

    header .device-img {
      left: 0;
      right: 0;
      width: 100%;
      margin: 0 auto;
      width: 300px;
    }

    @media (min-width: 768px) {
        header {
        min-height: 100%;
      }

      header .header-content {
        text-align: center;
        padding: 0;
        height: 100vh;
      }
      header .header-content .header-content-inner {
        text-align: center;
        max-width: none;
        margin: 0 auto;
        position: absolute;
        top: 50%;
        transform: translateY(-50%);
        width: 100%;
      }

    .about-content .about-content-inner {
        text-align: justify;
        max-width: none;
        position: relative;
        top: 50%;
        transform: translateY(-50%);
      }
      header .header-content .header-content-inner h1 {
        margin-top: 0;
        margin-bottom: 30px;
        font-size: 55px;
        color: #363939;
        text-align: center;
      }
    .about-content .about-content-inner h3 {
        font-size: 20px;
        font-weight: 400;
      }
      header .device-container {
     /*   max-width: none;
        max-height: calc(0vh);
        margin: 100px auto 0;*/
        padding: 0;
        height: 100vh;
      }

The height of the column it's in isn't 100% and the container it's in (device-container) also doesn't appear to hold its contents.

Thank you so much for your help. Sorry there's so many commented it out things. Also if anyone has an idea why the navbar extends past the page making that light blue column to the right happen please let me know too.

Body:
Setting 100% on a floating box will not make it 100% of parent height, as you'd expect. Your best solution, from where I see it, would be to use

header .row {
  display: flex;
  align-items: center;
  min-height: 100vh;
}

Of course, this means you'll need to lose the huge top padding on the <h1>, so it actually centers. If you're happy with how it looks on desktop, just wrap my suggestion in a @media query.

There's one more thing I'd note: you seem to use col-xl-* classes, which don't exist in Bootstrap, but you seem to miss the col-xs-* ones. Is this intentional? If not, do replace them.

Here's a fiddle with what I could make from the code you posted.

Don't forget to autoprefix.