SitePoint Sponsor

User Tag List

Results 1 to 7 of 7

Hybrid View

  1. #1
    SitePoint Guru
    Join Date
    Jan 2007
    Posts
    971
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)

    Canvas drawing experiment. How to add a loading graphic to canvas.

    Here is an experiment I've been working on. It draws shapes inspired by Islamic geometric principles.

    http://www.webventions.com/applicati...bdivision.html

    - I'm trying to figure out how to detect when the canvas is loaded so I can add a loading spinner.
    - Any thoughts on how to make this run faster would also be appreciated.
    - Any other thoughts...

    Here are some parameters I like:
    21/22/1
    50/20/1
    12/9/1
    40/5/2

    If you find any other cool ones let me know.

    Thank you E
    Last edited by eruna; May 16, 2012 at 21:24. Reason: Submitted by accident without finishing

  2. #2
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,729
    Mentioned
    104 Post(s)
    Tagged
    4 Thread(s)
    Quote Originally Posted by eruna View Post
    - I'm trying to figure out how to detect when the canvas is loaded so I can add a loading spinner.
    You can place the code within a jQuery callback to get started, but the spinner isn't required when good coding is used.

    With 25 I've turned this from taking 1.5 seconds to taking only 4 milliseconds, which is nearly well over 350 times faster.
    The main thing that I did to improve on this is to remove the coords to string conversion, and remove as many global variables as possible.

    I've also used objects to contain the x/y coords, which while they don't provide much speed benefit, result in easier to read code.

    Here's the line_exists function from before:

    Code:
    function line_exists(coord1,coord2,subdivided_output) {
        
        coord1=coord1[0]+' '+coord1[1];
        coord2=coord2[0]+' '+coord2[1];
        
        new_cords=coord1+' '+coord2;
        new_cords_alt=coord2+' '+coord1;
        counter=1;
        for (iii = 0; iii < subdivided_output.length; iii++) {
        
            sub_coord1=subdivided_output[iii][0][0]+' '+subdivided_output[iii][0][1];
            sub_coord2=subdivided_output[iii][1][0]+' '+subdivided_output[iii][1][1];
            
            old_coord=sub_coord1+' '+sub_coord2;
            
            if(old_coord==new_cords || old_coord==new_cords_alt ) {
                //$('#data').append('<span style="color:red">'+old_coord+" :: "+new_cords+"</span><br/>");
                return true;
            }
            else {
                //$('#data').append(counter+' ::: '+old_coord+" :: "+new_cords+'<br />' );
                counter++;
            }
            
            //$('#data').append(old_coord+" :: "+new_cords+'<br />' );
        }
        
        return false;
        
    }
    and the line_exists function after the changes

    Code javascript:
    function line_exists(lineFrom, lineTo, subdivided_output) {
        var i,
            from,
            to,
            sameStart,
            sameEnd,
            switchedStart,
            switchedEnd;
     
        for (i = 0; i < subdivided_output.length; i += 1) {
            from = subdivided_output[i].from;
            to = subdivided_output[i].to;
     
            sameStart = (from.x === lineFrom.x && from.y === lineFrom.y);
            sameEnd = (to.x === lineTo.x && to.y === lineTo.y);
            switchedStart = (from.x === lineTo.x && from.y === lineTo.y);
            switchedEnd = (to.x === lineFrom.x && to.y === lineFrom.y);
     
            if (sameStart && sameEnd || switchedStart && switchedEnd) {
                return true;
            }
        }
        return false;
    }

    Another example of improvement in terms of performance is in the subdivide function. Before it was looping through all lines and then using modulus with skip:

    Code:
    for (ii = 0; ii < output.length; ii++) {
        if(ii%skip==0 && output[i] != output[ii] && line_exists(output[i],output[ii], subdivided_output)==false){
            ...
    So with a skip of 4, that is similar to this:
    0. ii % skip = 0 so use this
    1. ii % skip = 1 so don't use this
    2. ii % skip = 2 so don't use this
    3. ii % skip = 3 so don't use this
    4. ii % skip = 0 so use this
    5. ii % skip = 1 so don't use this
    6. ii % skip = 2 so don't use this
    7. ii % skip = 3 so don't use this
    8. ii % skip = 0 so use this

    The updated code doesn't need that check, because it just loops by the skip amount:

    Code javascript:
    for (to = 0; to < output.length; to += skip) {
        if (output[from] !== output[to] && !line_exists(output[from], output[to], subdivided_output)) {
            ...

    So with a skip of 4, that is similar to this:
    0. use this
    4. use this
    8. use this

    The improved speed also means that most of your size constraints shouldn't be required now.

    The working code is up at http://jsfiddle.net/pmw57/kJVjR/2/
    Put in sides of 25, or 40, and you'll find that there is now an instantaneous response time.
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  3. #3
    SitePoint Guru
    Join Date
    Jan 2007
    Posts
    971
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    Very Awesome! You've given me a lot to chew on. I'll go through and review it until I understand all of it. I've been wanting to add sliders it sounds like with this information it will be practical.

    E

  4. #4
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,729
    Mentioned
    104 Post(s)
    Tagged
    4 Thread(s)
    Quote Originally Posted by eruna View Post
    Very Awesome! You've given me a lot to chew on. I'll go through and review it until I understand all of it. I've been wanting to add sliders it sounds like with this information it will be practical.
    The steps that I took to achieve that are:

    1. www.jsbeautifier.com to provide a consistent presentation
    2. www.jslint.com to find (and fix) as many obvious problems as possible
    3. remove that string conversion for the coords
    4. change some variables and/or data schemes to make the code easier to understand
    5. rework the step situation
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  5. #5
    SitePoint Guru
    Join Date
    Jan 2007
    Posts
    971
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    Thanks for these tips. I don't understand some of this syntax. Could you explain a few things?

    In the section below, what does the '.from' do?
    Code:
      from = subdivided_output[i].from;
    In this section, how is "===" diferent from "==" and how are x and y defined?
    Code:
            sameStart = (from.x === lineFrom.x && from.y === lineFrom.y);
    Many thanks

  6. #6
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,729
    Mentioned
    104 Post(s)
    Tagged
    4 Thread(s)
    Quote Originally Posted by eruna View Post
    Thanks for these tips. I don't understand some of this syntax. Could you explain a few things?

    In the section below, what does the '.from' do?
    Code:
      from = subdivided_output[i].from;
    To clarify, the "from =" is a completely different "from" than the one used in ".from" at the end.
    One is a named variable, while the other is an object property.

    Previously, push was being used to add items to the subdivided_output array:

    Code:
    subdivided_output.push(Array(output[i],output[ii]);
    Using the push method with only one item tends to be slower than other techniques, so to squeeze more performance out of this section, we can use the array length to add the item to the end of that array.
    It's also preferable to use [arrayItem] instead of new Array(arrayItem) too.
    I also renamed the for loops to make it clear what they are referring to.

    Code:
    subdivided_output[subdivided_output.length] = [output[from],output[to]];
    Later on, we had lots of different array index being used:

    Code:
    sub_coord1=subdivided_output[iii][0][0] + ...
    So make things nice and clear, we can store the array items as named objects instead.

    Code javascript:
    subdivided_output[subdivided_output.length] = {'from': output[from], 'to': output[to]};

    Those output items are also objects, which we'll get in to below, so code to access the subdivided_output values can now be:

    Code:
    sub_coord1=subdivided_output[iii]['from']['x'] + ...
    Which can also be done without the array notation, as:

    Code:
    sub_coord1=subdivided_output[iii].from.x + ...
    Using ['from'] or .from, those are not array variables. They are just ways to access object properties.

    That line of code might be easier to understand if the variables are adjusted slightly, so that instead of using from/to to store the coord, we use start/end instead.
    That way we would have code like this:

    Code javascript:
    start = subdivided_output[i].from;

    Quote Originally Posted by eruna View Post
    In this section, how is "===" diferent from "=="
    The triple equals is used to ensure that both side really are equal.
    The double equal allows far too many strange cases to slip through, and programmers are almost certainly not aware of all variations, so it's best advised to stay away from using the double equals.
    Some of this is explained in articles such as The pleasures and perils of JavaScript’s promiscuous comparison operator or in slides like The Good Parts Part Two (slides 31 & 32) which you can also see explained in good detail in JavaScript: The Good Parts [from 14:59] (video)

    Quote Originally Posted by eruna View Post
    and how are x and y defined?
    x and y are defined in the shape function. They used to be array items, such as:

    Code:
    points=new Array(x,y);
    output.push(points);
    which is the same as doing:

    Code:
    output[i] = [x, y];
    however, storing them as array items results in needing to use [0] and [1] to access their values, and they are actually different types of values, so it makes better sense and is easier to read when we store and access them as x and y properties instead.

    Code:
    output[i] = {
        x: Math.round(size * (1 + Math.cos(angle))),
        y: Math.round(size * (1 + Math.sin(angle)))
    };
    It also means that what used to be this:

    Code:
    function shape(sides, size){
        output=new Array;
        x=0;
        y=0;
        for (i = 0; i < (sides+1); i++) {
            x= size * Math.cos(2 * Math.PI * i / sides);
            y=size * Math.sin(2 * Math.PI * i / sides);
            x= x+size;
            y= y+size;
            x=Math.round(x);
            y=Math.round(y);
            points=new Array(x,y);
            output.push(points);
        }
        return output;
    }
    is now easier to understand as:

    Code javascript:
    function shape(sides, size) {
        Math.TAU = Math.PI * 2;
     
        var output = [],
            i,
            slices = Math.TAU / sides,
            angle;
        for (i = 0; i <= sides; i += 1) {
            angle = slices * i;
            output[i] = {
                x: Math.round(size * (1 + Math.cos(angle))),
                y: Math.round(size * (1 + Math.sin(angle)))
            };
        }
        return output;
    }
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  7. #7
    SitePoint Guru
    Join Date
    Jan 2007
    Posts
    971
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    Paul,
    Thanks for all this information.
    You've given me a lot to work with.
    I really appreciate this.
    E


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •