UNPKG

dy3dmap

Version:

1,419 lines (1,300 loc) 390 kB
// import * as mars3d from "mars3d" // const Cesium = mars3d.Cesium (function () { var requirejs, require, define; (function (undef) { var main, req, makeMap, handlers, defined = {}, waiting = {}, config = {}, defining = {}, hasOwn = Object.prototype.hasOwnProperty, aps = [].slice, jsSuffixRegExp = /\.js$/; function hasProp(obj, prop) { return hasOwn.call(obj, prop); } /** * Given a relative module name, like ./something, normalize it to * a real name that can be mapped to a path. * @param {String} name the relative name * @param {String} baseName a real name that the name arg is relative * to. * @returns {String} normalized name */ function normalize(name, baseName) { var nameParts, nameSegment, mapValue, foundMap, lastIndex, foundI, foundStarMap, starI, i, j, part, normalizedBaseParts, baseParts = baseName && baseName.split("/"), map = config.map, starMap = (map && map['*']) || {}; //Adjust any relative paths. if (name) { name = name.split('/'); lastIndex = name.length - 1; // If wanting node ID compatibility, strip .js from end // of IDs. Have to do this here, and not in nameToUrl // because node allows either .js or non .js to map // to same file. if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) { name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, ''); } // Starts with a '.' so need the baseName if (name[0].charAt(0) === '.' && baseParts) { //Convert baseName to array, and lop off the last part, //so that . matches that 'directory' and not name of the baseName's //module. For instance, baseName of 'one/two/three', maps to //'one/two/three.js', but we want the directory, 'one/two' for //this normalization. normalizedBaseParts = baseParts.slice(0, baseParts.length - 1); name = normalizedBaseParts.concat(name); } //start trimDots for (i = 0; i < name.length; i++) { part = name[i]; if (part === '.') { name.splice(i, 1); i -= 1; } else if (part === '..') { // If at the start, or previous value is still .., // keep them so that when converted to a path it may // still work when converted to a path, even though // as an ID it is less than ideal. In larger point // releases, may be better to just kick out an error. if (i === 0 || (i === 1 && name[2] === '..') || name[i - 1] === '..') { continue; } else if (i > 0) { name.splice(i - 1, 2); i -= 2; } } } //end trimDots name = name.join('/'); } //Apply map config if available. if ((baseParts || starMap) && map) { nameParts = name.split('/'); for (i = nameParts.length; i > 0; i -= 1) { nameSegment = nameParts.slice(0, i).join("/"); if (baseParts) { //Find the longest baseName segment match in the config. //So, do joins on the biggest to smallest lengths of baseParts. for (j = baseParts.length; j > 0; j -= 1) { mapValue = map[baseParts.slice(0, j).join('/')]; //baseName segment has config, find if it has one for //this name. if (mapValue) { mapValue = mapValue[nameSegment]; if (mapValue) { //Match, update name to the new value. foundMap = mapValue; foundI = i; break; } } } } if (foundMap) { break; } //Check for a star map match, but just hold on to it, //if there is a shorter segment match later in a matching //config, then favor over this star map. if (!foundStarMap && starMap && starMap[nameSegment]) { foundStarMap = starMap[nameSegment]; starI = i; } } if (!foundMap && foundStarMap) { foundMap = foundStarMap; foundI = starI; } if (foundMap) { nameParts.splice(0, foundI, foundMap); name = nameParts.join('/'); } } return name; } function makeRequire(relName, forceSync) { return function () { //A version of a require function that passes a moduleName //value for items that may need to //look up paths relative to the moduleName var args = aps.call(arguments, 0); //If first arg is not require('string'), and there is only //one arg, it is the array form without a callback. Insert //a null so that the following concat is correct. if (typeof args[0] !== 'string' && args.length === 1) { args.push(null); } return req.apply(undef, args.concat([relName, forceSync])); }; } function makeNormalize(relName) { return function (name) { return normalize(name, relName); }; } function makeLoad(depName) { return function (value) { defined[depName] = value; }; } function callDep(name) { if (hasProp(waiting, name)) { var args = waiting[name]; delete waiting[name]; defining[name] = true; main.apply(undef, args); } if (!hasProp(defined, name) && !hasProp(defining, name)) { throw new Error('No ' + name); } return defined[name]; } //Turns a plugin!resource to [plugin, resource] //with the plugin being undefined if the name //did not have a plugin prefix. function splitPrefix(name) { var prefix, index = name ? name.indexOf('!') : -1; if (index > -1) { prefix = name.substring(0, index); name = name.substring(index + 1, name.length); } return [prefix, name]; } //Creates a parts array for a relName where first part is plugin ID, //second part is resource ID. Assumes relName has already been normalized. function makeRelParts(relName) { return relName ? splitPrefix(relName) : []; } /** * Makes a name map, normalizing the name, and using a plugin * for normalization if necessary. Grabs a ref to plugin * too, as an optimization. */ makeMap = function (name, relParts) { var plugin, parts = splitPrefix(name), prefix = parts[0], relResourceName = relParts[1]; name = parts[1]; if (prefix) { prefix = normalize(prefix, relResourceName); plugin = callDep(prefix); } //Normalize according if (prefix) { if (plugin && plugin.normalize) { name = plugin.normalize(name, makeNormalize(relResourceName)); } else { name = normalize(name, relResourceName); } } else { name = normalize(name, relResourceName); parts = splitPrefix(name); prefix = parts[0]; name = parts[1]; if (prefix) { plugin = callDep(prefix); } } //Using ridiculous property names for space reasons return { f: prefix ? prefix + '!' + name : name, //fullName n: name, pr: prefix, p: plugin }; }; function makeConfig(name) { return function () { return (config && config.config && config.config[name]) || {}; }; } handlers = { require: function (name) { return makeRequire(name); }, exports: function (name) { var e = defined[name]; if (typeof e !== 'undefined') { return e; } else { return (defined[name] = {}); } }, module: function (name) { return { id: name, uri: '', exports: defined[name], config: makeConfig(name) }; } }; main = function (name, deps, callback, relName) { var cjsModule, depName, ret, map, i, relParts, args = [], callbackType = typeof callback, usingExports; //Use name if no relName relName = relName || name; relParts = makeRelParts(relName); //Call the callback to define the module, if necessary. if (callbackType === 'undefined' || callbackType === 'function') { //Pull out the defined dependencies and pass the ordered //values to the callback. //Default to [require, exports, module] if no deps deps = !deps.length && callback.length ? ['require', 'exports', 'module'] : deps; for (i = 0; i < deps.length; i += 1) { map = makeMap(deps[i], relParts); depName = map.f; //Fast path CommonJS standard dependencies. if (depName === "require") { args[i] = handlers.require(name); } else if (depName === "exports") { //CommonJS module spec 1.1 args[i] = handlers.exports(name); usingExports = true; } else if (depName === "module") { //CommonJS module spec 1.1 cjsModule = args[i] = handlers.module(name); } else if (hasProp(defined, depName) || hasProp(waiting, depName) || hasProp(defining, depName)) { args[i] = callDep(depName); } else if (map.p) { map.p.load(map.n, makeRequire(relName, true), makeLoad(depName), {}); args[i] = defined[depName]; } else { throw new Error(name + ' missing ' + depName); } } ret = callback ? callback.apply(defined[name], args) : undefined; if (name) { //If setting exports via "module" is in play, //favor that over return value and exports. After that, //favor a non-undefined return value over exports use. if (cjsModule && cjsModule.exports !== undef && cjsModule.exports !== defined[name]) { defined[name] = cjsModule.exports; } else if (ret !== undef || !usingExports) { //Use the return value from the function. defined[name] = ret; } } } else if (name) { //May just be an object definition for the module. Only //worry about defining if have a module name. defined[name] = callback; } }; requirejs = require = req = function (deps, callback, relName, forceSync, alt) { if (typeof deps === "string") { if (handlers[deps]) { //callback in this case is really relName return handlers[deps](callback); } //Just return the module wanted. In this scenario, the //deps arg is the module name, and second arg (if passed) //is just the relName. //Normalize module name, if it contains . or .. return callDep(makeMap(deps, makeRelParts(callback)).f); } else if (!deps.splice) { //deps is a config object, not an array. config = deps; if (config.deps) { req(config.deps, config.callback); } if (!callback) { return; } if (callback.splice) { //callback is an array, which means it is a dependency list. //Adjust args if there are dependencies deps = callback; callback = relName; relName = null; } else { deps = undef; } } //Support require(['a']) callback = callback || function () { }; //If relName is a function, it is an errback handler, //so remove it. if (typeof relName === 'function') { relName = forceSync; forceSync = alt; } //Simulate async callback; if (forceSync) { main(undef, deps, callback, relName); } else { //Using a non-zero value because of concern for what old browsers //do, and latest browsers "upgrade" to 4 if lower value is used: //http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html#dom-windowtimers-settimeout: //If want a value immediately, use require('id') instead -- something //that works in almond on the global level, but not guaranteed and //unlikely to work in other AMD implementations. setTimeout(function () { main(undef, deps, callback, relName); }, 4); } return req; }; /** * Just drops the config on the floor, but returns req in case * the config return value is used. */ req.config = function (cfg) { return req(cfg); }; /** * Expose module registry for debugging and tooling */ requirejs._defined = defined; define = function (name, deps, callback) { if (typeof name !== 'string') { throw new Error('See almond README: incorrect module build, no module name'); } //This module may not have dependencies if (!deps.splice) { //deps is not an array, so probably means //an object literal or factory function for //the value. Adjust args. callback = deps; deps = []; } if (!hasProp(defined, name) && !hasProp(waiting, name)) { waiting[name] = [name, deps, callback]; } }; define.amd = { jQuery: true }; }()); /** * * @namespace Cesium */ //----CesiumMeshVisualizer---- define('Core/RendererUtils',[],function () { var Cartesian3 = Cesium.Cartesian3; var CesiumMath = Cesium.Math; var yUpToZUp = Cesium.Matrix4.fromRotationTranslation(Cesium.Matrix3.fromRotationX(CesiumMath.PI_OVER_TWO)); var scratchTranslation = new Cesium.Cartesian3(); var scratchQuaternion = new Cesium.Quaternion(); var scratchScale = new Cesium.Cartesian3(); var scratchTranslationQuaternionRotationScale = new Cesium.Matrix4(); var computeModelMatrix = new Cesium.Matrix4(); var scratchPosition = new Cesium.Cartesian3(); var clearCommandScratch = new Cesium.ClearCommand({ color: new Cesium.Color(0.0, 0.0, 0.0, 0.0) }); /** * *@constructor *@memberof Cesium */ function RendererUtils() { } /** *使用帧缓冲技术,执行渲染命令,渲染到纹理 *@param {Cesium.DrawCommand|Array<Cesium.DrawCommand>}drawCommand 渲染命令(集合) *@param {Cesium.FrameState}frameState 帧状态对象,可以从Cesium.Scene中获取 *@param {Cesium.Texture}outpuTexture 将渲染到的目标纹理对象 *@param {Cesium.Texture}[outputDepthTexture] 可选,输出的深度纹理 */ RendererUtils.renderToTexture = function (drawCommand, frameState, outputTexture, outputDepthTexture) { var drawCommands = Array.isArray(drawCommand) ? drawCommand : [drawCommand]; var context = frameState.context; var framebuffer = null, destroy = false; if (outputTexture instanceof Cesium.Framebuffer) { framebuffer = outputTexture; } if (!framebuffer) { framebuffer = new Cesium.Framebuffer({ context: context, colorTextures: [outputTexture], destroyAttachments: false, depthTexture: outputDepthTexture }); destroy = true; } var clearCommand = clearCommandScratch; clearCommand.framebuffer = framebuffer; clearCommand.renderState = frameState.renderState; clearCommand.execute(context); drawCommands.forEach(function (drawCommand) { drawCommand.framebuffer = framebuffer; drawCommand.execute(context); }); if (destroy) { framebuffer.destroy(); } } /** * *@param {Cesium.Matrix4}srcMatrix *@param {Cesium.Matrix4}dstMatrix *@param {Cesium.Matrix4} */ RendererUtils.yUp2Zup = function (srcMatrix, dstMatrix) { return Cesium.Matrix4.multiplyTransformation(srcMatrix, yUpToZUp, dstMatrix); } /** *平移、旋转或缩放,返回计算之后的模型转换矩阵 *@param {Cesium.Cartesian3}[translation=undefined] *@param {Object}[rotation=undefined] 旋转参数 *@param {Cesium.Cartesian3}[rotation.axis] 旋转轴 *@param {Number}[rotation.angle] 旋转角度 *@param {Cesium.Cartesian3}[rotation.scale] 缩放 *@param {Cesium.Matrix4}[outModelMatrix] 计算结果矩阵,和返回值一样,但是传递此参数时则返回值不是新创建的Cesium.Matrix4实例 *@return {Cesium.Matrix4} */ RendererUtils.computeModelMatrix = function (srcModelMatrix, translation, rotation, scale, outModelMatrix) { if (arguments.length == 0) { return srcModelMatrix; } var Matrix4 = Cesium.Matrix4; if (!outModelMatrix) { outModelMatrix = new Matrix4(); } Matrix4.clone(srcModelMatrix, outModelMatrix); if (!translation) { scratchTranslation.x = 0; scratchTranslation.y = 0; scratchTranslation.z = 0; } scratchTranslation.x = translation.x; scratchTranslation.y = translation.y; scratchTranslation.z = translation.z; if (!scale) { scratchScale.x = 0; scratchScale.y = 0; scratchScale.z = 0; } scratchScale.x = scale.x; scratchScale.y = scale.y; scratchScale.z = scale.z; if (rotation instanceof Cesium.Quaternion) { Cesium.Quaternion.clone(rotation, scratchQuaternion); } else { var axis = rotation.axis; var angle = rotation.angle; Cesium.Quaternion.fromAxisAngle( new Cartesian3(axis.x, axis.y, axis.z),//axis.y=1 y是旋转轴 CesiumMath.toRadians(angle), scratchQuaternion ); } //translate,rotate,scale Matrix4.fromTranslationQuaternionRotationScale( scratchTranslation, scratchQuaternion, scratchScale, scratchTranslationQuaternionRotationScale); Matrix4.multiplyTransformation( outModelMatrix, scratchTranslationQuaternionRotationScale, outModelMatrix); return outModelMatrix; } return RendererUtils; }); define('Core/Rotation',[],function () { /** * *@param {Cesium.Cartesian3}axis 旋转轴 *@param {Number}angle 旋转角度 * *@property {Cesium.Cartesian3}axis 旋转轴 *@property {Number}angle 旋转角度 *@property {Cesium.Event}paramChanged *@constructor *@memberof Cesium */ function Rotation(axis, angle) { this._axis = axis; this._angle = angle; this.paramChanged = new Cesium.Event(); } Object.defineProperties(Rotation.prototype, { axis: { set: function (val) { if (val.x != this._axis.x || val.y != this._axis.y || val.z != this._axis.z) { this._axis = val; this.paramChanged.raiseEvent(); } this._axis = val; }, get: function () { return this._axis; } }, angle: { set: function (val) { if (val != this._angle) { this._angle = val; this.paramChanged.raiseEvent(); } this._angle = val; }, get: function () { return this._angle; } } }) return Rotation; }); define('Util/CSG',[],function () { // Constructive Solid Geometry (CSG) is a modeling technique that uses Boolean // operations like union and intersection to combine 3D solids. This library // implements CSG operations on meshes elegantly and concisely using BSP trees, // and is meant to serve as an easily understandable implementation of the // algorithm. All edge cases involving overlapping coplanar polygons in both // solids are correctly handled. // // Example usage: // // var cube = CSG.cube(); // var sphere = CSG.sphere({ radius: 1.3 }); // var polygons = cube.subtract(sphere).toPolygons(); // // ## Implementation Details // // All CSG operations are implemented in terms of two functions, `clipTo()` and // `invert()`, which remove parts of a BSP tree inside another BSP tree and swap // solid and empty space, respectively. To find the union of `a` and `b`, we // want to remove everything in `a` inside `b` and everything in `b` inside `a`, // then combine polygons from `a` and `b` into one solid: // // a.clipTo(b); // b.clipTo(a); // a.build(b.allPolygons()); // // The only tricky part is handling overlapping coplanar polygons in both trees. // The code above keeps both copies, but we need to keep them in one tree and // remove them in the other tree. To remove them from `b` we can clip the // inverse of `b` against `a`. The code for union now looks like this: // // a.clipTo(b); // b.clipTo(a); // b.invert(); // b.clipTo(a); // b.invert(); // a.build(b.allPolygons()); // // Subtraction and intersection naturally follow from set operations. If // union is `A | B`, subtraction is `A - B = ~(~A | B)` and intersection is // `A & B = ~(~A | ~B)` where `~` is the complement operator. // // ## License // // Copyright (c) 2011 Evan Wallace (http://madebyevan.com/), under the MIT license. // # class CSG // Holds a binary space partition tree representing a 3D solid. Two solids can // be combined using the `union()`, `subtract()`, and `intersect()` methods. /** *源码参见{@link https://github.com/jscad/csg.js} <br/> *Constructive Solid Geometry (CSG) is a modeling technique that uses Boolean<br/> *operations like union and intersection to combine 3D solids. This library<br/> *implements CSG operations on meshes elegantly and concisely using BSP trees,<br/> *and is meant to serve as an easily understandable implementation of the<br/> *algorithm. All edge cases involving overlapping coplanar polygons in both<br/> *solids are correctly handled.<br/> * *@example MeshVisualizer = Cesium.MeshVisualizer; Mesh = Cesium.Mesh; MeshMaterial = Cesium.MeshMaterial; CSG = Cesium.CSG; GeometryUtils = Cesium.GeometryUtils; //示例1: var cube = CSG.cube(); var sphere = CSG.sphere({ radius: 1.3 }); var polygons = cube.subtract(sphere).toPolygons(); //示例2: var center = Cesium.Cartesian3.fromDegrees(homePosition[0], homePosition[1], 50000); var modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(center); var meshVisualizer = new MeshVisualizer({ modelMatrix: modelMatrix, up: { z: 1 } }); viewer.scene.primitives.add(meshVisualizer); var material = new MeshMaterial({ defaultColor: "rgba(0,0,255,1.0)", wireframe: true, side: MeshMaterial.Sides.DOUBLE }); //创建盒子 var dimensions = new Cesium.Cartesian3(100000, 50000, 50000); var boxGeometry = Cesium.BoxGeometry.createGeometry(Cesium.BoxGeometry.fromDimensions({ dimensions: dimensions, vertexFormat: Cesium.VertexFormat.POSITION_ONLY })); var box = GeometryUtils.toCSG(boxGeometry); var boxMesh = new Mesh(box, material); meshVisualizer.add(boxMesh); //创建球体 var sphere = new Cesium.SphereGeometry({ radius: 50000.0, vertexFormat: Cesium.VertexFormat.POSITION_ONLY }); sphere = Cesium.SphereGeometry.createGeometry(sphere); sphere = CSG.toCSG(sphere); var sphereMesh = new Mesh(sphere, material); sphereMesh.position = new Cesium.Cartesian3(100000, 0, 0) meshVisualizer.add(sphereMesh); //并 var unionResult = box.union(sphere); var unionResultMesh = new Mesh(unionResult, material); unionResultMesh.position = new Cesium.Cartesian3(300000, 0, 0) meshVisualizer.add(unionResultMesh); //交 var intersectResult = box.intersect(sphere); var intersectResultMesh = new Mesh(intersectResult, material); intersectResultMesh.position = new Cesium.Cartesian3(500000, 0, 0) meshVisualizer.add(intersectResultMesh); //球体减盒子 var subResult = sphere.subtract(box); var subResultMesh = new Mesh(subResult, material); subResultMesh.position = new Cesium.Cartesian3(700000, 0, 0) meshVisualizer.add(subResultMesh); //盒子减球体 var subResult2 = box.subtract(sphere); var subResultMesh2 = new Mesh(subResult2, material); subResultMesh2.position = new Cesium.Cartesian3(900000, 0, 0) meshVisualizer.add(subResultMesh2); //渲染CSG创建的几何体 var cube = CSG.cube({ center: [0, 0, 0], radius: 20000 }); var cubeMtl = new MeshMaterial({ defaultColor: "rgba(255,0,0,1)" }); meshVisualizer.add(new Mesh({ geometry: cube, material: cubeMtl, position: new Cesium.Cartesian3(-100000, 0, 0) })); *@memberof Cesium *@constructor */ function CSG() { this.polygons = []; }; /** *Construct a CSG solid from a list of `CSG.Polygon` instances. *@param {Array<Cesium.CSG.Polygon>} *@param {Array<Cesium.CSG>} */ CSG.fromPolygons = function (polygons) { var csg = new CSG(); csg.polygons = polygons; return csg; }; CSG.prototype = { /** *@return {Cesium.CSG} */ clone: function () { var csg = new CSG(); csg.polygons = this.polygons.map(function (p) { return p.clone(); }); return csg; }, /** * *@return {Array<Cesium.CSG.Polygon>} */ toPolygons: function () { return this.polygons; }, /** * Return a new CSG solid representing space in either this solid or in the<br/> * solid `csg`. Neither this solid nor the solid `csg` are modified.<br/> * <br/> * A.union(B)<br/> * <br/> *<pre><code> * +-------+ +-------+ * | | | | * | A | | | * | +--+----+ = | +----+ * +----+--+ | +----+ | * | B | | | * | | | | * +-------+ +-------+ *</code></pre> * @param {Cesium.CSG}csg * @return {Cesium.CSG} */ union: function (csg) { var a = new CSG.Node(this.clone().polygons); var b = new CSG.Node(csg.clone().polygons); a.clipTo(b); b.clipTo(a); b.invert(); b.clipTo(a); b.invert(); a.build(b.allPolygons()); return CSG.fromPolygons(a.allPolygons()); }, /** * Return a new CSG solid representing space in this solid but not in the<br/> * solid `csg`. Neither this solid nor the solid `csg` are modified.<br/> * <br/> * A.subtract(B)<br/> * <br/> *<pre><code> * +-------+ +-------+ * | | | | * | A | | | * | +--+----+ = | +--+ * +----+--+ | +----+ * | B | * | | * +-------+ * *</code></pre> * @param {Cesium.CSG}csg * @return {Cesium.CSG} */ subtract: function (csg) { var a = new CSG.Node(this.clone().polygons); var b = new CSG.Node(csg.clone().polygons); a.invert(); a.clipTo(b); b.clipTo(a); b.invert(); b.clipTo(a); b.invert(); a.build(b.allPolygons()); a.invert(); return CSG.fromPolygons(a.allPolygons()); }, /** * Return a new CSG solid representing space both this solid and in the<br/> * solid `csg`. Neither this solid nor the solid `csg` are modified.<br/> * <br/> * A.intersect(B)<br/> * <br/> *<pre><code> * +-------+ * | | * | A | * | +--+----+ = +--+ * +----+--+ | +--+ * | B | * | | * +-------+ * *</code></pre> * @param {Cesium.CSG}csg * @return {Cesium.CSG} */ intersect: function (csg) { var a = new CSG.Node(this.clone().polygons); var b = new CSG.Node(csg.clone().polygons); a.invert(); b.clipTo(a); b.invert(); a.clipTo(b); b.clipTo(a); a.build(b.allPolygons()); a.invert(); return CSG.fromPolygons(a.allPolygons()); }, /** * Return a new CSG solid with solid and empty space switched. This solid is * not modified. * @return {Cesium.CSG} */ inverse: function () { var csg = this.clone(); csg.polygons.map(function (p) { p.flip(); }); return csg; } }; /** * Construct an axis-aligned solid cuboid. Optional parameters are `center` and<br/> * `radius`, which default to `[0, 0, 0]` and `[1, 1, 1]`. The radius can be<br/> * specified using a single number or a list of three numbers, one for each axis.<br/> * *@example * * var cube = CSG.cube({ * center: [0, 0, 0], * radius: 1 * }); *@memberof Cesium.CSG *@param {Object}options *@param {Array<Number>|Cesium.CSG.Vector}[options.center=[0, 0, 0]] *@param {Number|Array<Number>|Cesium.CSG.Vector}[options.radius=1] *@return {Cesium.CSG} */ CSG.cube = function (options) { options = options || {}; var c = new CSG.Vector(options.center || [0, 0, 0]); var r = !options.radius ? [1, 1, 1] : options.radius.length ? options.radius : [options.radius, options.radius, options.radius]; return CSG.fromPolygons([ [[0, 4, 6, 2], [-1, 0, 0]], [[1, 3, 7, 5], [+1, 0, 0]], [[0, 1, 5, 4], [0, -1, 0]], [[2, 6, 7, 3], [0, +1, 0]], [[0, 2, 3, 1], [0, 0, -1]], [[4, 5, 7, 6], [0, 0, +1]] ].map(function (info) { return new CSG.Polygon(info[0].map(function (i) { var pos = new CSG.Vector( c.x + r[0] * (2 * !!(i & 1) - 1), c.y + r[1] * (2 * !!(i & 2) - 1), c.z + r[2] * (2 * !!(i & 4) - 1) ); return new CSG.Vertex(pos, new CSG.Vector(info[1])); })); })); }; /** * Construct a solid sphere. Optional parameters are `center`, `radius`,<br/> * `slices`, and `stacks`, which default to `[0, 0, 0]`, `1`, `16`, and `8`.<br/> * The `slices` and `stacks` parameters control the tessellation along the<br/> * longitude and latitude directions.<br/> * *@example * * var sphere = CSG.sphere({ * center: [0, 0, 0], * radius: 1, * slices: 16, * stacks: 8 * }); *@memberof Cesium.CSG *@param {Object}options *@param {Array<Number>|Cesium.CSG.Vector}[options.center=[0, 0, 0]] *@param {Number}[options.radius=1] *@param {Number}[options.slices=16] *@param {Number}[options.stacks=8] *@return {Cesium.CSG} */ CSG.sphere = function (options) { options = options || {}; var c = new CSG.Vector(options.center || [0, 0, 0]); var r = options.radius || 1; var slices = options.slices || 16; var stacks = options.stacks || 8; var polygons = [], vertices; function vertex(theta, phi) { theta *= Math.PI * 2; phi *= Math.PI; var dir = new CSG.Vector( Math.cos(theta) * Math.sin(phi), Math.cos(phi), Math.sin(theta) * Math.sin(phi) ); vertices.push(new CSG.Vertex(c.plus(dir.times(r)), dir)); } for (var i = 0; i < slices; i++) { for (var j = 0; j < stacks; j++) { vertices = []; vertex(i / slices, j / stacks); if (j > 0) vertex((i + 1) / slices, j / stacks); if (j < stacks - 1) vertex((i + 1) / slices, (j + 1) / stacks); vertex(i / slices, (j + 1) / stacks); polygons.push(new CSG.Polygon(vertices)); } } return CSG.fromPolygons(polygons); }; /** * Construct a solid cylinder. Optional parameters are `start`, `end`,<br/> * `radius`, and `slices`, which default to `[0, -1, 0]`, `[0, 1, 0]`, `1`, and<br/> * `16`. The `slices` parameter controls the tessellation.<br/> * *@example * * var cylinder = CSG.cylinder({ * start: [0, -1, 0], * end: [0, 1, 0], * radius: 1, * slices: 16 * }); *@memberof Cesium.CSG *@param {Object}options *@param {Array<Number>|Cesium.CSG.Vector}[options.start=[0, -1, 0]] *@param {Array<Number>|Cesium.CSG.Vector}[options.end=[0, -1, 0]] *@param {Number}[options.radius=1] *@param {Number}[options.slices=16] *@return {Cesium.CSG} */ CSG.cylinder = function (options) { options = options || {}; var s = new CSG.Vector(options.start || [0, -1, 0]); var e = new CSG.Vector(options.end || [0, 1, 0]); var ray = e.minus(s); var r = options.radius || 1; var slices = options.slices || 16; var axisZ = ray.unit(), isY = (Math.abs(axisZ.y) > 0.5); var axisX = new CSG.Vector(isY, !isY, 0).cross(axisZ).unit(); var axisY = axisX.cross(axisZ).unit(); var start = new CSG.Vertex(s, axisZ.negated()); var end = new CSG.Vertex(e, axisZ.unit()); var polygons = []; function point(stack, slice, normalBlend) { var angle = slice * Math.PI * 2; var out = axisX.times(Math.cos(angle)).plus(axisY.times(Math.sin(angle))); var pos = s.plus(ray.times(stack)).plus(out.times(r)); var normal = out.times(1 - Math.abs(normalBlend)).plus(axisZ.times(normalBlend)); return new CSG.Vertex(pos, normal); } for (var i = 0; i < slices; i++) { var t0 = i / slices, t1 = (i + 1) / slices; polygons.push(new CSG.Polygon([start, point(0, t0, -1), point(0, t1, -1)])); polygons.push(new CSG.Polygon([point(0, t1, 0), point(0, t0, 0), point(1, t0, 0), point(1, t1, 0)])); polygons.push(new CSG.Polygon([end, point(1, t1, 1), point(1, t0, 1)])); } return CSG.fromPolygons(polygons); }; /** * class Vector<br/> * Represents a 3D vector. *@example * * new CSG.Vector(1, 2, 3); * new CSG.Vector([1, 2, 3]); * new CSG.Vector({ x: 1, y: 2, z: 3 }); * *@memberof Cesium.CSG * *@param {Number|Array<Number>|Cesium.CSG.Vector}xOrArrayXYZOrVec *@param {Number}[y] *@param {Number}[z] * *@property {Number}x *@property {Number}y *@property {Number}z * *@constructor */ CSG.Vector = function (x, y, z) { if (arguments.length == 3) { this.x = x; this.y = y; this.z = z; } else if ('x' in x) { this.x = x.x; this.y = x.y; this.z = x.z; } else { this.x = x[0]; this.y = x[1]; this.z = x[2]; } }; CSG.Vector.prototype = { /** *@return {Cesium.CSG.Vector} */ clone: function () { return new CSG.Vector(this.x, this.y, this.z); }, /** *@return {Cesium.CSG.Vector} */ negated: function () { return new CSG.Vector(-this.x, -this.y, -this.z); }, /** *@param {Cesium.CSG.Vector}a *@return {Cesium.CSG.Vector} */ plus: function (a) { return new CSG.Vector(this.x + a.x, this.y + a.y, this.z + a.z); }, /** *@param {Cesium.CSG.Vector}a *@return {Cesium.CSG.Vector} */ minus: function (a) { return new CSG.Vector(this.x - a.x, this.y - a.y, this.z - a.z); }, /** *@param {Number}a *@return {Cesium.CSG.Vector} */ times: function (a) { return new CSG.Vector(this.x * a, this.y * a, this.z * a); }, /** *@param {Number}a *@return {Cesium.CSG.Vector} */ dividedBy: function (a) { return new CSG.Vector(this.x / a, this.y / a, this.z / a); }, /** *@param {Cesium.CSG.Vector}a *@return {Cesium.CSG.Vector} */ dot: function (a) { return this.x * a.x + this.y * a.y + this.z * a.z; }, /** *@param {Cesium.CSG.Vector}a *@param {Number}t *@return {Cesium.CSG.Vector} */ lerp: function (a, t) { return this.plus(a.minus(this).times(t)); }, /** *@return {Number} */ length: function () { return Math.sqrt(this.dot(this)); }, /** *@return {Cesium.CSG.Vector} */ unit: function () { return this.dividedBy(this.length()); }, /** *@param {Cesium.CSG.Vector}a *@return {Cesium.CSG.Vector} */ cross: function (a) { return new CSG.Vector( this.y * a.z - this.z * a.y, this.z * a.x - this.x * a.z, this.x * a.y - this.y * a.x ); } }; /** * class Vertex<br/> *<br/> * Represents a vertex of a polygon. Use your own vertex class instead of this<br/> * one to provide additional features like texture coordinates and vertex<br/> * colors. Custom vertex classes need to provide a `pos` property and `clone()`,<br/> * `flip()`, and `interpolate()` methods that behave analogous to the ones<br/> * defined by `CSG.Vertex`. This class provides `normal` so convenience<br/> * functions like `CSG.sphere()` can return a smooth vertex normal, but `normal`<br/> * is not used anywhere else.<br/> * *@memberof Cesium.CSG *@param {Array<Number>|Cesium.CSG.Vector}pos *@param {Array<Number>|Cesium.CSG.Vector}normal * *@property {Cesium.CSG.Vector}pos *@property {Cesium.CSG.Vector}normal * *@constructor */ CSG.Vertex = function (pos, normal) { this.pos = new CSG.Vector(pos); this.normal = new CSG.Vector(normal); }; CSG.Vertex.prototype = { /** *@return {Cesium.CSG.Vertex} */ clone: function () { return new CSG.Vertex(this.pos.clone(), this.normal.clone()); }, /** * Invert all orientation-specific data (e.g. vertex normal). Called when the<br/> * orientation of a polygon is flipped. * */ flip: function () { this.normal = this.normal.negated(); }, /** * Create a new vertex between this vertex and `other` by linearly<br/> * interpolating all properties using a parameter of `t`. Subclasses should<br/> * override this to interpolate additional properties. * *@param {Cesium.CSG.Vertex} *@param {Number} *@return {Cesium.CSG.Vertex} */ interpolate: function (other, t) { return new CSG.Vertex( this.pos.lerp(other.pos, t), this.normal.lerp(other.normal, t) ); } }; /** * class Plane</br/> * * Represents a plane in 3D space. * *@memberof Cesium.CSG *@param {Array<Number>|Cesium.CSG.Vector}normal *@param {Number}w * *@property {Cesium.CSG.Vector}normal *@property {Number}w * *@constructor */ CSG.Plane = function (normal, w) { this.normal = normal; this.w = w; }; /** * `CSG.Plane.EPSILON` is the tolerance used by `splitPolygon()` to decide if a</br/> * point is on the plane. */ CSG.Plane.EPSILON = 1e-5; /** * * *@param {Cesium.CSG.Vector}a *@param {Cesium.CSG.Vector}b *@param {Cesium.CSG.Vector}c */ CSG.Plane.fromPoints = function (a, b, c) { var n = b.minus(a).cross(c.minus(a)).unit(); return new CSG.Plane(n, n.dot(a)); }; CSG.Plane.prototype = { /** *@return {Cesium.CSG.Plane} */ clone: function () { return new CSG.Plane(this.normal.clone(), this.w); }, /** * */ flip: function () { this.normal = this.normal.negated(); this.w = -this.w; }, /** * Split `polygon` by this plane if needed, then put the polygon or polygon<br/> * fragments in the appropriate lists. Coplanar polygons go into either<br/> * `coplanarFront` or `coplanarBack` depending on their orientation with<br/> * respect to this plane. Polygons in front or in back of this plane go into<br/> * either `front` or `back`. * *@param {Cesium.CSG.Polygon}polygon *@param {Array<Cesium.CSG.Polygon>}coplanarFront *@param {Array<Cesium.CSG.Polygon>}coplanarBack *@param {Array<Cesium.CSG.Polygon>}front *@param {Array<Cesium.CSG.Polygon>}back */ splitPolygon: function (polygon, coplanarFront, coplanarBack, front, back) { var COPLANAR = 0; var FRONT = 1; var BACK = 2; var SPANNING = 3; // Classify each point as well as the entire polygon into one of the above // four classes. var polygonType = 0; var types = []; for (var i = 0; i < polygon.vertices.length; i++) { var t = this.normal.dot(polygon.vertices[i].pos) - this.w; var type = (t < -CSG.Plane.EPSILON) ? BACK : (t > CSG.Plane.EPSILON) ? FRONT : COPLANAR; polygonType |= type; types.push(type); } // Put the polygon in the correct list, splitting it when necessary. switch (polygonType) { case COPLANAR: (this.normal.dot(polygon.plane.normal) > 0 ? coplanarFront : coplanarBack).push(polygon); break; case FRONT: front.push(polygon); break; case BACK: back.push(polygon); break; case SPANNING: var f = [], b = []; for (var i = 0; i < polygon.vertices.length; i++) { var j = (i + 1) % polygon.vertices.length; var ti = types[i], tj = types[j]; var vi = polygon.vertices[i], vj = polygon.vertices[j]; if (ti != BACK) f.push(vi); if (ti != FRONT) b.push(ti != BACK ? vi.clone() : vi); if ((ti | tj) == SPANNING) { var t = (this.w - this.normal.dot(vi.pos)) / this.normal.dot(vj.pos.minus(vi.pos)); var v = vi.interpolate(vj, t); f.push(v); b.push(v.clone()); } } if (f.length >= 3) front.push(new CSG.Polygon(f, polygon.shared)); if (b.length >= 3) back.push(new CSG.Polygon(b, polygon.shared)); break; } } }; /** * class Polygon<br/> *<br/> * Represents a convex polygon. The vertices used to initialize a polygon must<br/> * be coplanar and form a convex loop. They do not have to be `CSG.Vertex`<br/> * instances but they must behave similarly (duck typing can be used for<br/> * customization).<br/> * <br/> * Each convex polygon has a `shared` property, which is shared between all<br/> * polygons that are clones of each other or were split from the same polygon.<br/> * This can be used to define per-polygon properties (such as surface color).<br/> * *@memberof Cesium.CSG *@param {Array<Cesium.CSG.Vertex>}vertices *@param {Boolean}shared * *@property {Array<Cesium.CSG.Vertex>}vertices *@property {Boolean}shared *@property {Cesium.CSG.Plane}plane *@constructor */ CSG.Polygon = function (vertices, shared) { this.vertices = vertices; this.shared = shared; this.plane = CSG.Plane.fromPoints(vertices[0].pos, vertices[1].pos, vertices[2].pos); }; CSG.Polygon.prototype = { /** *@return {Cesium.CSG.Polygon} */ clone: function () { var vertices = this.vertices.map(function (v) { return v.clone(); }); return new CSG.Polygon(vertices, this.shared); }, /** * */ flip: function () { this.vertices.reverse().map(function (v) { v.flip(); }); this.plane.flip(); } }; /** * * class Node<br/> *<br/> * Holds a node in a BSP tree. A BSP tree is built from a collection of polygons<br/> * by picking a polygon to split along. That polygon (and all other coplanar<br/> * polygons) are added directly to that node and the other polygons are added to<br/> * the front and/or back subtrees. This is not a leafy BSP tree since there is<br/> * no distinction between internal and leaf nodes.<br/> * *@memberof Cesium.CSG *@param {Array<Cesium.CSG.Polygon>}polygons * *@property {Array<Cesium.CSG.Polygon>}polygons *@property {Cesium.CSG.Plane}plane *@property {Cesium.CSG.Plan