dy3dmap
Version:
1,419 lines (1,300 loc) • 390 kB
JavaScript
// 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