0

I have this script I am working on that utilizes the oCanvas JS Library (http://ocanvas.org/) that creates an HTML5 canvas and displays multiple objects within the canvas. Currently, I have the script reading from an external XML document and loops through each project node and creates a circle object on the canvas.

I am having issues with trying to place this objects on the canvas evenly spaced from the middle circle (the logo variable in the code below).

// GLOBALS
    var xmlData = '<?xml version="1.0" encoding="UTF-8"?><root name="CompanyName"><projects><project name="Project1"></project><project name="Project2"></project></projects></root>'

    var xmlObj = []
    // var angle = (360 * Math.PI)/180
    var padding = 15
    var canvas = oCanvas.create({
        canvas: '#myCanvas'
    })
    var c_width = canvas.width
    var c_height = canvas.height

    var logo = canvas.display.ellipse({
        x: c_width / 2,
        y: c_height / 3,
        radius: 80,
        fill: '#d15851'
    })

    canvas.addChild(logo)

    // var getXML = function(file){
    //  $.ajax({
    //      url: file,
    //      type: 'GET',
    //      dataType: 'xml',
    //      async: false,
    //      success: parseXML
    //  })
    // }

    var parseXML = function() {
        var xmlDoc = $.parseXML(xmlData)
        var xml = $(xmlDoc)

        xml.find('project').each(function(i){
            xmlObj[i] = canvas.display.ellipse({
                fill: '#'+'0123456789abcdef'.split('').map(function(v,i,a){
                  return i>5 ? null : a[Math.floor(Math.random()*16)] }).join(''),
                radius: 40,
                opacity: 1
            })
        });

        var angleSingleton = {
            "currentAngle": 0,
            "currentOffset": 0,
            "incrementAngle": function() {
                this.currentAngle = this.currentAngle + this.currentOffset
            }
        }

        angleSingleton.currentOffset = Math.floor((360 * Math.PI)/xmlObj.length);

        for(h = 0; h < xmlObj.length; h++) {
            xmlObj[h].x = (logo.x + logo.radius * Math.cos(angleSingleton.currentAngle)) + xmlObj[h].radius + padding;
            xmlObj[h].y = (logo.y + logo.radius * Math.sin(angleSingleton.currentAngle)) + xmlObj[h].radius + padding;

            canvas.addChild(xmlObj[h])
            angleSingleton.incrementAngle()
        }
    }

//

$(document).ready(function(){
    parseXML()
})

enter image description here

dennismonsewicz
  • 25,132
  • 33
  • 116
  • 189
  • Without a lack of sample data there is no way to run the program, care to tell us the problem you are experiencing and providing some sample data? – Devin M Jan 20 '12 at 20:29
  • Yes, I am including a sample XML now... I have the centered circle in the middle of the canvas... its just getting the plot points (x/y coords) for the other circles... right now they are overlapping on each other. – dennismonsewicz Jan 20 '12 at 20:31
  • So your just wondering how to get points for the circles around the main circle? – Devin M Jan 20 '12 at 20:32
  • In short yes... I am really new to coming up with simple algorithms for plot points... I have updated the code base to read from an XML string vs a file – dennismonsewicz Jan 20 '12 at 20:36

2 Answers2

1

What you want to take a look at is the Parametric equation for circles. Basically it defines a point along a circles perimeter at a specific angle. This answer covers it in more detail.

To get your x and y values for the new circle you use the following equations:

x = logo.x + logo.radius * Math.cos(angle)
y = logo.y + logo.radius * Math.sin(angle)

However you need to account for the room the new circle is going to take up plus any room for padding if you want it.

x = (logo.x + logo.radius * Math.cos(angle)) + newCircle.radius + circlePadding
y = (logo.y + logo.radius * Math.sin(angle)) + newCircle.radius + circlePadding

For the angle function try something like this:

var angleSingleton = {
    "currentAngle": 0,
    "currentOffset": 0,
    "incrementAngle": function() {
        this.currentAngle = this.currentAngle + this.currentOffset
    }
}

angleSingleton.currentOffset = (360 * Math.PI)/xmlObj.length;

Then you can use this to keep track of the angle you need for the formula. To get the current angle use angleSingleton.currentAngle and replace angle++ with angleSingleton.incrementAngle

Community
  • 1
  • 1
Devin M
  • 9,636
  • 2
  • 33
  • 46
1

I ended up figuring it out!

// EXTENDING OBJECTS
    Array.prototype.min = function(array) {
        return Math.min.apply(Math, array);
    }

    Array.prototype.max = function(array) {
        return Math.max.apply(Math, array)
    }
//

// GLOBALS
    var xmlData = '<?xml version="1.0" encoding="UTF-8"?><root name="CompanyName"><projects><project name="Project1"></project><project name="Project2"></project><project name="Project3"></project></projects></root>'

    var xmlObj = []
        var xmlDoc, xml;
    var padding = 15
    var canvas = oCanvas.create({
        canvas: '#myCanvas'
    })
    var c_width = canvas.width
    var c_height = canvas.height

    var logo = canvas.display.ellipse({
        x: c_width / 2,
        y: c_height / 3,
        radius: 80,
        fill: '#d15851'
    })

        var rectObj = function(){
            this.x =  0;
            this.y =  0;
            this.width =  100;
            this.height = 100;
            this.size = this.width + this.height; //this would equate to a circles radius if dealing with circles
            this.fillerText =  null;
            this.fillRect = function(hexVal){
                if(!hexVal)
                    return '#'+'0123456789abcdef'.split('').map(function(v,i,a){
                return i>5 ? null : a[Math.floor(Math.random()*16)] }).join('')
                else
                    return hexVal

            };
            this.drawRect = function(){
                return canvas.display.rectangle({
                    width: this.width,
                    height: this.height,
                    fill: this.fillRect(),
                    x: this.x,
                    y: this.y
                })
            };
            this.checkCollisions = function(objToCheck) {
                var centerA = { x: this.x+(this.size/2), y: this.y+(this.size/2) };
                var centerB = { x:objToCheck.x+(objToCheck.size/2), y: objToCheck.y+(objToCheck.size/2) };
                var distance = Math.sqrt(((centerB.x-centerA.x)*(centerB.x-centerA.x) + (centerB.y-centerA.y)*(centerB.y-centerA.y)));

                if(distance < (this.size+objToCheck.size)) {
                    objToCheck.x = this.x - (canvas.width/4)
                    objToCheck.fillRect = function(){
                        return 'red'
                    }
                }
            }
        }

    canvas.addChild(logo)

    var parseXML = function() {
        xmlDoc = $.parseXML(xmlData)
        xml = $(xmlDoc)

        xml.find('project').each(function(i){
                    xmlObj[i] = new rectObj()
                    xmlObj[i].fillerText = $(this).attr('name')
                    xmlObj[i].x = (logo.x + logo.radius * Math.cos((360*Math.PI) / (i + 1)) + padding) + ((xmlObj[i].width / 2) + (i+1));
                    xmlObj[i].y = (logo.y + logo.radius * Math.sin((360*Math.PI) / (i + 1)) + padding);
        });

                for(i = 0; i < xmlObj.length; i++) {
                    for(a = i+1; a < xmlObj.length; a++) {
                        xmlObj[i].checkCollisions(xmlObj[a])
                    }
                    canvas.addChild(xmlObj[i].drawRect())
                }
    }

//

$(document).ready(function(){
    parseXML()
})

Screen shot: enter image description here

I obviously need to write in the Y coords for the rectangles so that they're not touching the main circle, but for now, they all "float" as they're supposed to :)

Thanks for all of your help Devin!

BTW, I was able to write my collision algorithm by studying this JS file: http://andersonferminiano.com/html5/studies/balls_collisions/collision.js

dennismonsewicz
  • 25,132
  • 33
  • 116
  • 189