API Docs for: 0.1.0
Show:

File: cangaja/box2d/b2d-terrain.js

                        /**
                         * @description
                         *
                         * B2DTerrain looks similar to B2DPolygon but has more features for polygon manipulation like clipping and triangulation.
                         *
                         ```
                        
                         var terrainShapes =
                         [{
                            outer: [{
                                x: 0,
                                y: 100.5
                            }, {
                                x: 1024,
                                y: 100.5
                            }, {
                                x: 1024,
                                y: 768
                            }, {
                                x: 0,
                                y: 768
                            }],
                            holes: []
                         }]
                        
                         b2world.createTerrain({
                             name: 'terrain',
                             image: false
                             terrainShape: terrainShapes,
                             x:0,
                             y:0,
                             world: b2world,
                             scale: 40
                         })
                        
                         ```
                         *
                         * @class CG.B2DTerrain
                         * @extends CG.B2DEntity
                         */
                        
                        
                        //@TODO code cleanup and description
                        //@TODO comment to polygon winding order for clipper (outer == CW; holes == CCW)
                        
                        /*@TODO known pol2tri exceptions ;o(:
                         'Cannot call method 'slice' of undefined',
                         'poly2tri Intersecting Constraints',
                         'poly2tri Invalid Triangle.index() call',
                         '"null" is not an object (evaluating 'pb.y')',
                         poly2tri Invalid Triangle.legalize() call
                         */
                        
                        CG.B2DEntity.extend('B2DTerrain', {
                            /**
                             * Options:
                             * name {string}
                             * image {mixed}
                             * points {array}
                             * x {number}
                             * y {number}
                             * world {object}
                             * scale {number}
                             *
                             *
                             *
                             * @method init
                             * @constructor
                             * @param options {Object}
                             * @return {*}
                             */
                            init: function (options) {
                        
                                CG._extend(this, {
                                    /**
                                     * @description bitmap for terrain
                                     * @property bitmap
                                     * @type {CG.Bitmap}
                                     */
                                    bitmap: new CG.Bitmap({width: Game.width, height: Game.height}),
                                    /**
                                     * @property image
                                     * @type {strng}
                                     */
                                    image: false,
                                    /**
                                     * @property polys
                                     * @type {Array}
                                     */
                                    polys: new Array(),
                                    /**
                                     * @property terrainShape
                                     * @type {*}
                                     */
                                    terrainShape: [],
                                    /**
                                     * @description the generated triangles generated thru clipper and poly2tri
                                     * @property terrainTriangles
                                     * @type {Array}
                                     */
                                    terrainTriangles: [],
                                    /**
                                     * @property holes
                                     * @type {Array}
                                     */
                                    holes: []
                                })
                        
                                this._super(options)
                                this.instanceOf = 'B2DTerrain'
                        
                                if (typeof this.image !== undefined) {
                                    this.bitmap.loadImage(this.image)
                                }
                        
                        
                                /**
                                 * @property bodyDef.type
                                 * @type {box2d.b2BodyType.b2_staticBody/box2d.b2BodyType.b2_dynamicBody/box2d.b2BodyType.b2_kinematicBody/box2d.b2BodyType.b2_bulletBody}
                                 */
                                this.bodyDef.type = box2d.b2BodyType.b2_staticBody //terrain is always static?
                                /**
                                 * @property bodyDef.position
                                 */
                                this.bodyDef.position.SetXY(this.x / this.scale, this.y / this.scale)
                                /**
                                 * @property bodyDef.userData
                                 * @type {*}
                                 */
                                this.bodyDef.userData = this.id
                        
                                this.createTerrain()
                        
                                return this
                        
                            },
                            /**
                             * @method createTerrain
                             */
                            createTerrain: function () {
                                this.body = this.world.CreateBody(this.bodyDef)
                        
                                try {
                                    for (var part = 0, len = this.terrainShape.length; part < len; part++) {
                        
                                        var outer = this.terrainShape[part].outer
                                        if (typeof outer === 'undefined')
                                            continue
                        
                                        var swctx = new poly2tri.SweepContext(outer, {cloneArrays: true})
                        
                                        if (this.terrainShape[part].holes.length > 0) {
                                            for (var i = 0, l = this.terrainShape[part].holes.length; i < l; i++) {
                                                swctx.addHole(this.terrainShape[part].holes[i])
                                            }
                                        }
                        
                                        swctx.triangulate();
                        
                                        this.terrainTriangles = this.terrainTriangles.concat(swctx.getTriangles() || [])
                        
                                    }
                        
                                    for (var i = 0, l = this.terrainTriangles.length; i < l; i++) {
                                        this.bodyShapePoly = new b2PolygonShape
                                        this.bodyShapePoly.bounce = 0.5
                                        this.bodyShapePoly.SetAsArray(this.getPolysFromTriangulation(this.terrainTriangles[i].points_), this.terrainTriangles[i].points_.length)
                        
                                        this.fixDef.density = this.density
                                        this.fixDef.restitution = this.restitution
                                        this.fixDef.friction = this.friction
                                        this.fixDef.shape = this.bodyShapePoly
                                        this.body.CreateFixture(this.fixDef)
                                    }
                                } catch (e) {
                        //            console.log('error: createTerrain()', e)
                        //            console.log(e.message)
                        //            console.log(e.stack)
                        //            console.log(this.terrainShape)
                        //            console.log(this.terrainTriangles)
                                }
                            },
                            /**
                             * @description deletes the terrain
                             * @method deleteTerrain
                             */
                            deleteTerrain: function () {
                                //remove triangles
                                this.terrainTriangles = []
                                //remove body from b2world
                                this.world.DestroyBody(this.body)
                            },
                            /**
                             * @description Using Clipper to clip a hole in a given polygonshape. Important: the outer polygon points have to be in CW orientation, the hole polygons must ordered in CCW
                             *
                             * Options:
                             * points - points for clipping,
                             * radius - radius for clipping,
                             * x - x pos for clipping,
                             * y - y pos for clipping
                             *
                             * @method clipTerrain
                             * @param {object} opt
                             */
                            clipTerrain: function (opt) {
                                var newhole = this.createCircle(opt)
                        
                                //add new hole to all contour terrainShapes
                                for (var part = 0, len = this.terrainShape.length; part < len; part++) {
                                    this.terrainShape[part].holes.push(newhole)
                                }
                        
                                //use clipper to calculate new terrainShapes
                                var tempPolys = []
                                var subj_polygons = []
                                var clip_polygons = []
                                for (var part = 0, len = this.terrainShape.length; part < len; part++) {
                                    subj_polygons = [this.terrainShape[part].outer]
                                    clip_polygons = []
                                    if (this.terrainShape[part].holes.length > 0) {
                                        for (var i = 0, l = this.terrainShape[part].holes.length; i < l; i++) {
                                            clip_polygons.push(this.terrainShape[part].holes[i])
                                        }
                                    }
                        
                        //            var cpr = new ClipperLib.Clipper()
                        //            cpr.AddPolygons(subj_polygons, ClipperLib.PolyType.ptSubject)
                        //            cpr.AddPolygons(clip_polygons, ClipperLib.PolyType.ptClip)
                        //
                        //            var solution_polygons = new ClipperLib.ExPolygons()
                        //            cpr.Execute(ClipperLib.ClipType.ctDifference, solution_polygons, ClipperLib.PolyFillType.pftNonZero, ClipperLib.PolyFillType.pftNonZero)
                        
                        
                                    var cpr = new ClipperLib.Clipper()
                                    cpr.PreserveCollinear = true
                                    cpr.StrictlySimple = true
                                    var polytree = new ClipperLib.PolyTree()
                                    cpr.AddPaths(subj_polygons, ClipperLib.PolyType.ptSubject, true)
                                    cpr.AddPaths(clip_polygons, ClipperLib.PolyType.ptClip, true)
                                    cpr.Execute(ClipperLib.ClipType.ctDifference, polytree, ClipperLib.PolyFillType.pftNonZero, ClipperLib.PolyFillType.pftNonZero)
                        
                                    var solution_polygons = ClipperLib.JS.PolyTreeToExPolygons(polytree);
                        
                        //            console.log('clipTerrain:', subj_polygons, clip_polygons)
                        //            console.log('solution_polygons:', solution_polygons)
                        
                                    if (solution_polygons.length > 0) {
                                        for (var spoly = 0, slen = solution_polygons.length; spoly < slen; spoly++) {
                                            tempPolys.push(solution_polygons[spoly])
                                        }
                                    }
                                }
                                this.terrainShape = tempPolys
                                this.lightenTerrain()
                        //        this.cleanTerrain()
                                this.deleteTerrain()
                                this.createTerrain()
                                this.bitmap.clearCircle(opt.x, opt.y, opt.radius)
                            },
                            /**
                             * @description this method uses the Clipper Lighten method to reduce vertices for better triangulation
                             * @method lightenTerrain
                             */
                            lightenTerrain: function () {
                                //use clipper to eliminate to much vertices
                                var tolerance = 0.02
                        
                                for (var part = 0, len = this.terrainShape.length; part < len; part++) {
                                    var temp = ClipperLib.JS.Lighten(this.terrainShape[part].outer, tolerance * this.scale)
                                    this.terrainShape[part].outer = temp[0]
                                    if (this.terrainShape[part].holes.length > 0) {
                                        for (var i = 0, l = this.terrainShape[part].holes.length; i < l; i++) {
                                            var temp = ClipperLib.JS.Lighten(this.terrainShape[part].holes[i], tolerance * this.scale)
                                            this.terrainShape[part].holes[i] = temp[0]
                                        }
                                    }
                                }
                            },
                            /**
                             * @description Experimental not working yet. Try to use the Clipperlib Clean method
                             * @method cleanTerrain
                             */
                            cleanTerrain: function () {
                                //use clipper to eliminate to much vertices
                                var cleandelta = 0.1
                        
                                for (var part = 0, len = this.terrainShape.length; part < len; part++) {
                                    var temp = ClipperLib.JS.Clean(this.terrainShape[part].outer, cleandelta * this.scale)
                                    this.terrainShape[part].outer = temp[0]
                                    if (this.terrainShape[part].holes.length > 0) {
                                        for (var i = 0, l = this.terrainShape[part].holes.length; i < l; i++) {
                                            var temp = ClipperLib.JS.Clean(this.terrainShape[part].holes[i], cleandelta * this.scale)
                                            this.terrainShape[part].holes[i] = temp[0]
                                        }
                                    }
                                }
                            },
                            /**
                             * @description extract the triangles out of poly2tri array
                             *
                             * @method getPolysFromJson
                             * @return {Array}
                             */
                            getPolysFromTriangulation: function (pointsArray) {
                                var vecs = []
                                for (var i = 0, l = pointsArray.length; i < l; i++) {
                                    var poly = pointsArray[i]
                                    vecs.push(new b2Vec2(poly.x / this.scale, poly.y / this.scale))
                                }
                                return vecs
                            },
                            /**
                             * @description creates a ccw wise circle vertices array for clipping
                             *
                             * Options:
                             * points - number of points of circle,
                             * radius - radius for circle,
                             * x - x position for circle,
                             * y - y position for circle
                             *
                             * @method createCircle
                             * @param {object} opts example {points: 16, radius: 30, x: 320, y: 240}
                             * @returns {Array}
                             */
                            createCircle: function (opts) {
                                var angle = 2 * Math.PI / opts.points
                                var circleArray = []
                                for (var i = 0; i < opts.points; i++) {
                                    circleArray.push({x: opts.x + opts.radius * Math.cos(angle * i), y: opts.y + opts.radius * Math.sin(angle * i)})
                                }
                                return circleArray.reverse()
                            },
                        
                            draw: function () {
                        
                                Game.renderer.draw(this.bitmap)
                        
                            },
                        
                            // converts polygons to SVG path string
                            polys2SvgImage: function (poly, scale) {
                                var path = "", i, j;
                                if (!scale)
                                    scale = 1;
                                for (i = 0; i < poly.length; i++) {
                                    for (j = 0; j < poly[i].length; j++) {
                                        if (!j)
                                            path += "M";
                                        else
                                            path += "L";
                                        path += (poly[i][j].X / scale) + ", " + (poly[i][j].Y / scale);
                                    }
                                    path += "Z";
                                }
                                return path;
                        
                                /*
                                 svg = '<svg style="" width="800" height="600">';
                                 svg += '<defs><pattern id="back" patternUnits="userSpaceOnUse" width="990" height="534"><image xlink:href="imagetest.png" x="0" y="0" width="990" height="534"/></pattern></defs>';
                                 svg += '<path stroke="" fill="url(#back)" stroke-width="" d="' + polys2path(solution_polygons, scale) + '"/>';
                                 svg += '</svg>';
                                 */
                            }
                        })