// Graphic primitives are not broken down into simpler shapes.
// They have have their own draw method, a getNearestZ method for
// depth sorting and applying atmospheric perpective,
// and a printSVG method for static images of the stage.
var graphic_objects_3d = window.graphic_objects_3d || {};
// augmented method to Point3d for drawing screen radii.
window.Point3d.prototype.getScale = function () {
'use strict';
return this.fl / (this.fl + this.z + this.cZ);
};
/* An object containing a method for
* assigning common properties to primitives
*
* @namespace graphic_objects_3d
*/
graphic_objects_3d.Primitive = {
getProperties : function (colour, alpha) {
'use strict';
this.primitives = [this];
this.colour = (colour === undefined) ? '#000000' : window.utils.parseColor(colour);
this.alpha = alpha || 0.5;
}
};
// PRIMITVE OBJECTS (HAVE THEIR OWN DRAW METHOD) ////////////////////////////////////////////////////////
/* Creates a Fill
*
* @namespace graphic_objects_3d
* @constructor
* @this {Fill}
* @parameter {object} pointA
* @parameter {object} pointB
* @parameter {object} pointC
* @parameter {object} pointD
* @parameter {number} colour
* @parameter {number} alpha
*/
graphic_objects_3d.Fill = function (pointA, pointB, pointC, pointD, colour, alpha) {
'use strict';
this.pointA = pointA;
this.pointB = pointB;
this.pointC = pointC;
this.pointD = pointD;
this.colour = (colour === undefined) ? '#ffffff' : window.utils.parseColor(colour);
this.alpha = alpha || 0.5;
};
graphic_objects_3d.Fill.prototype.draw = function (context) {
'use strict';
context.save();
context.fillStyle = window.utils.colorToRGB(this.colour, this.alpha);
context.beginPath();
context.moveTo(this.pointA.getScreenX(), this.pointA.getScreenY());
context.lineTo(this.pointB.getScreenX(), this.pointB.getScreenY());
context.lineTo(this.pointC.getScreenX(), this.pointC.getScreenY());
context.lineTo(this.pointD.getScreenX(), this.pointD.getScreenY());
context.closePath();
context.fill();
context.restore();
};
graphic_objects_3d.Fill.prototype.getSVG = function () {
'use strict';
return '';
};
graphic_objects_3d.Fill.prototype.getNearestPoint = function () {
'use strict';
return Math.min(this.pointA.z, this.pointB.z, this.pointC.z, this.pointD.z);
};
/* Creates a CircularFill
*
* @namespace graphic_objects_3d
* @constructor
* @this {CircularFill}
* @parameter {object} centre
* @parameter {number} r the radius
* @parameter {number} colour
* @parameter {number} alpha
*/
graphic_objects_3d.CircularFill = function (centre, r, colour, alpha) {
'use strict';
this.centre = centre || new window.Point3d();
this.r = r || 100;
this.points = [this.centre];
this.arc = 2 * Math.PI;
this.colour = (colour === undefined) ? '#ffffff' : window.utils.parseColor(colour);
this.alpha = alpha || 0.5;
};
graphic_objects_3d.CircularFill.prototype.draw = function (context) {
'use strict';
context.save();
context.fillStyle = window.utils.colorToRGB(this.colour, this.alpha);
context.beginPath();
context.arc(
this.centre.getScreenX(),
this.centre.getScreenY(),
this.r * this.centre.getScale(),
0,
this.arc
);
context.closePath();
context.fill();
context.restore();
};
graphic_objects_3d.CircularFill.prototype.getSVG = function () {
'use strict';
return '';
};
graphic_objects_3d.CircularFill.prototype.getNearestPoint = function () {
'use strict';
return this.centre.z - this.r;
};
/* Creates a SinglePoint
*
* @namespace graphic_objects_3d
* @constructor
* @this {SinglePoint}
* @parameter {object} centre
* @parameter {number} colour
* @parameter {number} alpha
*/
graphic_objects_3d.SinglePoint = function (centre, colour, alpha) {
'use strict';
this.centre = centre || new window.Point3d();
this.points = [this.centre];
this.r = 40;
graphic_objects_3d.Primitive.getProperties.call(this, colour, alpha);
};
// draw a cross hair at the point of radius r
graphic_objects_3d.SinglePoint.prototype.draw = function (context) {
'use strict';
var cx = this.centre.getScreenX(),
cy = this.centre.getScreenY(),
screenR = this.r * this.centre.getScale();
context.save();
context.beginPath();
context.strokeStyle = window.utils.colorToRGB(this.colour, this.alpha);
context.moveTo(cx - screenR, cy);
context.lineTo(cx + screenR, cy);
context.moveTo(cx, cy - screenR);
context.lineTo(cx, cy + screenR);
context.stroke();
context.restore();
context.closePath();
};
graphic_objects_3d.SinglePoint.prototype.getNearestPoint = function () {
'use strict';
return this.centre.z;
};
graphic_objects_3d.SinglePoint.prototype.getSVG = function () {
'use strict';
var screenR = this.r * this.centre.getScale();
return ''
+ '';
};
/* Creates a Line
*
* @namespace graphic_objects_3d
* @constructor
* @this {Line}
* @parameter {array} points
* @parameter {object} pointA
* @parameter {object} pointB
* @parameter {number} colour
* @parameter {number} alpha
*/
graphic_objects_3d.Line = function (pointA, pointB, colour, alpha) {
'use strict';
this.pointA = pointA;
this.pointB = pointB;
this.points = [this.pointA, this.pointB];
graphic_objects_3d.Primitive.getProperties.call(this, colour, alpha);
};
graphic_objects_3d.Line.prototype.draw = function (context) {
'use strict';
context.save();
context.beginPath();
context.strokeStyle = window.utils.colorToRGB(this.colour, this.alpha);
context.moveTo(this.pointA.getScreenX(), this.pointA.getScreenY());
context.lineTo(this.pointB.getScreenX(), this.pointB.getScreenY());
context.closePath();
context.stroke();
context.restore();
};
graphic_objects_3d.Line.prototype.getSVG = function () {
'use strict';
return '';
};
graphic_objects_3d.Line.prototype.getNearestPoint = function () {
'use strict';
return Math.min(this.pointA.z, this.pointB.z);
};
graphic_objects_3d.ImageLabel = function (image, centre, alpha) {
'use strict';
this.centre = centre || new window.Point3d();
this.points = [this.centre];
this.fixedSize = false;
this.alpha = alpha || 0.5;
};
graphic_objects_3d.ImageLabel.prototype.draw = function (context) {
'use strict';
context.save();
context.globalAlpha = this.alpha;
if (this.fixedSize === false) {
context.drawImage(
this.image,
this.centre.getScreenX(),
this.centre.getScreenY(),
this.height * this.centre.getScale(),
this.width * this.centre.getScale());
} else {
context.drawImage(
this.image,
this.centre.getScreenX(),
this.centre.getScreenY());
}
context.restore();
};
graphic_objects_3d.ImageLabel.prototype.getNearestPoint = function () {
'use strict';
return this.centre.z;
};
/* Creates a Label
*
* @namespace graphic_objects_3d
* @constructor
* @this {Label}
* @parameter {object} centre
* @parameter {number} colour
* @parameter {number} alpha
*/
graphic_objects_3d.Label = function (text, centre, colour, alpha) {
'use strict';
this.text = text;
this.centre = centre || new window.Point3d();
this.points = [this.centre];
this.fixedSize = false;
this.fontSize = 20;
graphic_objects_3d.Primitive.getProperties.call(this, colour, alpha);
};
graphic_objects_3d.Label.prototype.draw = function (context) {
'use strict';
context.save();
context.fillStyle = window.utils.colorToRGB(this.colour, this.alpha);
if (this.fixedSize === false) {
var fontSize = this.fontSize * this.centre.getScale();
context.font = fontSize + 'px sans-serif';
}
context.fillText(this.text, this.centre.getScreenX(), this.centre.getScreenY());
context.restore();
};
graphic_objects_3d.Label.prototype.getNearestPoint = function () {
'use strict';
return this.centre.z;
};
graphic_objects_3d.Label.prototype.getSVG = function () {
'use strict';
var fontSize = 10;
if (this.fixedSize === false) {
fontSize = 20 * this.centre.getScale();
}
return ''
+ this.text + '';
};
/* Creates a WireSphere with no fill
*
* @namespace graphic_objects_3d
* @constructor
* @this {WireSphere}
* @parameter {object} centre
* @parameter {number} r the radius
* @parameter {number} colour
* @parameter {number} alpha
*/
graphic_objects_3d.WireSphere = function (centre, r, colour, alpha) {
'use strict';
this.centre = centre || new window.Point3d();
this.r = r || 100;
this.points = [this.centre];
this.arc = 2 * Math.PI;
graphic_objects_3d.Primitive.getProperties.call(this, colour, alpha);
};
graphic_objects_3d.WireSphere.prototype.draw = function (context) {
'use strict';
context.save();
context.strokeStyle = window.utils.colorToRGB(this.colour, this.alpha);
context.beginPath();
context.arc(
this.centre.getScreenX(),
this.centre.getScreenY(),
this.r * this.centre.getScale(),
0,
this.arc
);
context.closePath();
context.stroke();
context.restore();
};
graphic_objects_3d.WireSphere.prototype.getSVG = function () {
'use strict';
return '';
};
graphic_objects_3d.WireSphere.prototype.getNearestPoint = function () {
'use strict';
return this.centre.z;
};
// GRAPHIC OBJECTS CONSTRUCTED FROM PROTOTYPES //////////////////////////////////////////////////////////
/* Creates a Sphere
*
* @namespace graphic_objects_3d
* @constructor
* @this {Sphere}
* @parameter {object} centre
* @parameter {number} r the radius
* @parameter {number} colour
* @parameter {number} alpha
*/
graphic_objects_3d.Sphere = function (centre, r, colour, alpha) {
'use strict';
this.centre = centre || new window.Point3d();
this.r = r || 100;
this.points = [this.centre];
this.arc = 2 * Math.PI;
this.colour = (colour === undefined) ? '#000000' : window.utils.parseColor(colour);
this.alpha = alpha || 0.5;
this.fillColour = '#ffffff';
this.primitives = [
new graphic_objects_3d.WireSphere(this.centre, this.r, this.colour, this.alpha),
new graphic_objects_3d.CircularFill(this.centre, this.r, this.fillColour, this.alpha)
];
};
/* Creates a Hexahedron - defaults to a cube
*
* @namespace graphic_objects_3d
* @constructor
* @this {Hexahedron}
* @parameter {array} points
* @parameter {number} colour
* @parameter {number} alpha
*/
graphic_objects_3d.Hexahedron = function (points, colour, alpha) {
'use strict';
var defaultPoints = [
new window.Point3d(-100, -100, -100), // 0 left top front
new window.Point3d(100, -100, -100), // 1 right top front
new window.Point3d(100, 100, -100), // 2 right bottom front
new window.Point3d(-100, 100, -100), // 3 left bottom front
new window.Point3d(-100, -100, 100), // 4 left top back
new window.Point3d(100, -100, 100), // 5 right top back
new window.Point3d(100, 100, 100), // 6 right bottom back
new window.Point3d(-100, 100, 100) // 7 left bottom back
];
this.points = (points === undefined || points.length < 8) ? defaultPoints : points;
this.colour = (colour === undefined) ? '#000000' : window.utils.parseColor(colour);
this.alpha = alpha || 0.5;
this.fillColour = '#ffffff';
this.primitives = [
new graphic_objects_3d.Line(this.points[0], this.points[1], this.colour, this.alpha),
new graphic_objects_3d.Line(this.points[1], this.points[2], this.colour, this.alpha),
new graphic_objects_3d.Line(this.points[2], this.points[3], this.colour, this.alpha),
new graphic_objects_3d.Line(this.points[3], this.points[0], this.colour, this.alpha),
new graphic_objects_3d.Line(this.points[5], this.points[4], this.colour, this.alpha),
new graphic_objects_3d.Line(this.points[4], this.points[7], this.colour, this.alpha),
new graphic_objects_3d.Line(this.points[7], this.points[6], this.colour, this.alpha),
new graphic_objects_3d.Line(this.points[6], this.points[5], this.colour, this.alpha),
new graphic_objects_3d.Line(this.points[0], this.points[4], this.colour, this.alpha),
new graphic_objects_3d.Line(this.points[1], this.points[5], this.colour, this.alpha),
new graphic_objects_3d.Line(this.points[2], this.points[6], this.colour, this.alpha),
new graphic_objects_3d.Line(this.points[3], this.points[7], this.colour, this.alpha),
// fill points are defined clockwise
new graphic_objects_3d.Fill(this.points[0], this.points[1], this.points[2], this.points[3], this.fillColour, this.alpha),
new graphic_objects_3d.Fill(this.points[1], this.points[5], this.points[6], this.points[2], this.fillColour, this.alpha),
new graphic_objects_3d.Fill(this.points[5], this.points[4], this.points[7], this.points[6], this.fillColour, this.alpha),
new graphic_objects_3d.Fill(this.points[4], this.points[0], this.points[3], this.points[7], this.fillColour, this.alpha),
new graphic_objects_3d.Fill(this.points[3], this.points[2], this.points[6], this.points[7], this.fillColour, this.alpha),
new graphic_objects_3d.Fill(this.points[4], this.points[5], this.points[1], this.points[0], this.fillColour, this.alpha)
];
};
/* Creates a Quadrilateral - defaults to a cube
*
* @namespace graphic_objects_3d
* @constructor
* @this {Quadrilateral}
* @parameter {object} pointA
* @parameter {object} pointB
* @parameter {object} pointC
* @parameter {object} pointD
* @parameter {number} colour
* @parameter {number} alpha
*/
graphic_objects_3d.Quadrilateral = function (pointA, pointB, pointC, pointD, colour, alpha) {
'use strict';
var incorrectNumberOfPointsForQuadrilateralError = new Error(
"A Quadrialteral needs to be passed exactly four points."
);
this.pointA = pointA;
this.pointB = pointB;
this.pointC = pointC;
this.pointD = pointD;
this.points = [this.pointA, this.pointB, this.pointC, this.pointD];
if (this.points.length !== 4) {
throw incorrectNumberOfPointsForQuadrilateralError;
}
this.colour = (colour === undefined) ? "#000000" : window.utils.parseColor(colour);
this.alpha = alpha || 0.5;
this.fillColour = '#ffffff';
this.primitives = [
new graphic_objects_3d.Line(this.points[0], this.points[1], this.colour, this.alpha),
new graphic_objects_3d.Line(this.points[1], this.points[2], this.colour, this.alpha),
new graphic_objects_3d.Line(this.points[2], this.points[3], this.colour, this.alpha),
new graphic_objects_3d.Line(this.points[3], this.points[0], this.colour, this.alpha),
new graphic_objects_3d.Fill(this.points[0], this.points[1], this.points[2],
this.points[3], this.fillColour, this.alpha)
];
};