zdog
Version:
Round, flat, designer-friendly pseudo-3D engine
164 lines (138 loc) • 4.35 kB
JavaScript
/**
* Cylinder composite shape
*/
( function( root, factory ) {
// module definition
if ( typeof module == 'object' && module.exports ) {
// CommonJS
module.exports = factory( require('./boilerplate'),
require('./path-command'), require('./shape'), require('./group'),
require('./ellipse') );
} else {
// browser global
var Zdog = root.Zdog;
Zdog.Cylinder = factory( Zdog, Zdog.PathCommand, Zdog.Shape,
Zdog.Group, Zdog.Ellipse );
}
}( this, function factory( utils, PathCommand, Shape, Group, Ellipse ) {
function noop() {}
// ----- CylinderGroup ----- //
var CylinderGroup = Group.subclass({
color: '#333',
updateSort: true,
});
CylinderGroup.prototype.create = function() {
Group.prototype.create.apply( this, arguments );
this.pathCommands = [
new PathCommand( 'move', [ {} ] ),
new PathCommand( 'line', [ {} ] ),
];
};
CylinderGroup.prototype.render = function( ctx, renderer ) {
this.renderCylinderSurface( ctx, renderer );
Group.prototype.render.apply( this, arguments );
};
CylinderGroup.prototype.renderCylinderSurface = function( ctx, renderer ) {
if ( !this.visible ) {
return;
}
// render cylinder surface
var elem = this.getRenderElement( ctx, renderer );
var frontBase = this.frontBase;
var rearBase = this.rearBase;
var scale = frontBase.renderNormal.magnitude();
var strokeWidth = frontBase.diameter * scale + frontBase.getLineWidth();
// set path command render points
this.pathCommands[0].renderPoints[0].set( frontBase.renderOrigin );
this.pathCommands[1].renderPoints[0].set( rearBase.renderOrigin );
if ( renderer.isCanvas ) {
ctx.lineCap = 'butt'; // nice
}
renderer.renderPath( ctx, elem, this.pathCommands );
renderer.stroke( ctx, elem, true, this.color, strokeWidth );
renderer.end( ctx, elem );
if ( renderer.isCanvas ) {
ctx.lineCap = 'round'; // reset
}
};
var svgURI = 'http://www.w3.org/2000/svg';
CylinderGroup.prototype.getRenderElement = function( ctx, renderer ) {
if ( !renderer.isSvg ) {
return;
}
if ( !this.svgElement ) {
// create svgElement
this.svgElement = document.createElementNS( svgURI, 'path' );
}
return this.svgElement;
};
// prevent double-creation in parent.copyGraph()
// only create in Cylinder.create()
CylinderGroup.prototype.copyGraph = noop;
// ----- CylinderEllipse ----- //
var CylinderEllipse = Ellipse.subclass();
CylinderEllipse.prototype.copyGraph = noop;
// ----- Cylinder ----- //
var Cylinder = Shape.subclass({
diameter: 1,
length: 1,
frontFace: undefined,
fill: true,
});
var TAU = utils.TAU;
Cylinder.prototype.create = function( /* options */) {
// call super
Shape.prototype.create.apply( this, arguments );
// composite shape, create child shapes
// CylinderGroup to render cylinder surface then bases
this.group = new CylinderGroup({
addTo: this,
color: this.color,
visible: this.visible,
});
var baseZ = this.length / 2;
var baseColor = this.backface || true;
// front outside base
this.frontBase = this.group.frontBase = new Ellipse({
addTo: this.group,
diameter: this.diameter,
translate: { z: baseZ },
rotate: { y: TAU/2 },
color: this.color,
stroke: this.stroke,
fill: this.fill,
backface: this.frontFace || baseColor,
visible: this.visible,
});
// back outside base
this.rearBase = this.group.rearBase = this.frontBase.copy({
translate: { z: -baseZ },
rotate: { y: 0 },
backface: baseColor,
});
};
// Cylinder shape does not render anything
Cylinder.prototype.render = function() {};
// ----- set child properties ----- //
var childProperties = [ 'stroke', 'fill', 'color', 'visible' ];
childProperties.forEach( function( property ) {
// use proxy property for custom getter & setter
var _prop = '_' + property;
Object.defineProperty( Cylinder.prototype, property, {
get: function() {
return this[ _prop ];
},
set: function( value ) {
this[ _prop ] = value;
// set property on children
if ( this.frontBase ) {
this.frontBase[ property ] = value;
this.rearBase[ property ] = value;
this.group[ property ] = value;
}
},
} );
} );
// TODO child property setter for backface, frontBaseColor, & rearBaseColor
return Cylinder;
} ) );