Flat, round, designer-friendly pseudo-3D engine for canvas & SVG
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 134 lines 4.0 kB view raw
1/** 2 * Cone composite shape 3 */ 4 5( function( root, factory ) { 6 // module definition 7 if ( typeof module == 'object' && module.exports ) { 8 // CommonJS 9 module.exports = factory( require('./boilerplate'), require('./vector'), 10 require('./path-command'), require('./anchor'), require('./ellipse') ); 11 } else { 12 // browser global 13 var Zdog = root.Zdog; 14 Zdog.Cone = factory( Zdog, Zdog.Vector, Zdog.PathCommand, 15 Zdog.Anchor, Zdog.Ellipse ); 16 } 17}( this, function factory( utils, Vector, PathCommand, Anchor, Ellipse ) { 18 19var Cone = Ellipse.subclass({ 20 length: 1, 21 fill: true, 22}); 23Cone.type = 'Cone'; 24 25var TAU = utils.TAU; 26 27Cone.prototype.create = function( /* options */) { 28 // call super 29 Ellipse.prototype.create.apply( this, arguments ); 30 // composite shape, create child shapes 31 this.apex = new Anchor({ 32 addTo: this, 33 translate: { z: this.length }, 34 }); 35 36 // vectors used for calculation 37 this.renderApex = new Vector(); 38 this.renderCentroid = new Vector(); 39 this.tangentA = new Vector(); 40 this.tangentB = new Vector(); 41 42 this.surfacePathCommands = [ 43 new PathCommand( 'move', [ {} ] ), // points set in renderConeSurface 44 new PathCommand( 'line', [ {} ] ), 45 new PathCommand( 'line', [ {} ] ), 46 ]; 47}; 48 49Cone.prototype.updateSortValue = function() { 50 // center of cone is one third of its length 51 this.renderCentroid.set( this.renderOrigin ) 52 .lerp( this.apex.renderOrigin, 1/3 ); 53 this.sortValue = this.renderCentroid.z; 54}; 55 56Cone.prototype.render = function( ctx, renderer ) { 57 this.renderConeSurface( ctx, renderer ); 58 Ellipse.prototype.render.apply( this, arguments ); 59}; 60 61Cone.prototype.renderConeSurface = function( ctx, renderer ) { 62 if ( !this.visible ) { 63 return; 64 } 65 this.renderApex.set( this.apex.renderOrigin ) 66 .subtract( this.renderOrigin ); 67 68 var scale = this.renderNormal.magnitude(); 69 var apexDistance = this.renderApex.magnitude2d(); 70 var normalDistance = this.renderNormal.magnitude2d(); 71 // eccentricity 72 var eccenAngle = Math.acos( normalDistance/scale ); 73 var eccen = Math.sin( eccenAngle ); 74 var radius = this.diameter / 2 * scale; 75 // does apex extend beyond eclipse of face 76 var isApexVisible = radius * eccen < apexDistance; 77 if ( !isApexVisible ) { 78 return; 79 } 80 // update tangents 81 var apexAngle = Math.atan2( this.renderNormal.y, this.renderNormal.x ) + 82 TAU/2; 83 var projectLength = apexDistance/eccen; 84 var projectAngle = Math.acos( radius/projectLength ); 85 // set tangent points 86 var tangentA = this.tangentA; 87 var tangentB = this.tangentB; 88 89 tangentA.x = Math.cos( projectAngle ) * radius * eccen; 90 tangentA.y = Math.sin( projectAngle ) * radius; 91 92 tangentB.set( this.tangentA ); 93 tangentB.y *= -1; 94 95 tangentA.rotateZ( apexAngle ); 96 tangentB.rotateZ( apexAngle ); 97 tangentA.add( this.renderOrigin ); 98 tangentB.add( this.renderOrigin ); 99 100 this.setSurfaceRenderPoint( 0, tangentA ); 101 this.setSurfaceRenderPoint( 1, this.apex.renderOrigin ); 102 this.setSurfaceRenderPoint( 2, tangentB ); 103 104 // render 105 var elem = this.getSurfaceRenderElement( ctx, renderer ); 106 renderer.renderPath( ctx, elem, this.surfacePathCommands ); 107 renderer.stroke( ctx, elem, this.stroke, this.color, this.getLineWidth() ); 108 renderer.fill( ctx, elem, this.fill, this.color ); 109 renderer.end( ctx, elem ); 110}; 111 112var svgURI = 'http://www.w3.org/2000/svg'; 113 114Cone.prototype.getSurfaceRenderElement = function( ctx, renderer ) { 115 if ( !renderer.isSvg ) { 116 return; 117 } 118 if ( !this.surfaceSvgElement ) { 119 // create svgElement 120 this.surfaceSvgElement = document.createElementNS( svgURI, 'path' ); 121 this.surfaceSvgElement.setAttribute( 'stroke-linecap', 'round' ); 122 this.surfaceSvgElement.setAttribute( 'stroke-linejoin', 'round' ); 123 } 124 return this.surfaceSvgElement; 125}; 126 127Cone.prototype.setSurfaceRenderPoint = function( index, point ) { 128 var renderPoint = this.surfacePathCommands[ index ].renderPoints[0]; 129 renderPoint.set( point ); 130}; 131 132return Cone; 133 134} ) );