(function (global, factory) { if (typeof define === "function" && define.amd) { define(["exports", "three", "../renderers/Projector.js"], factory); } else if (typeof exports !== "undefined") { factory(exports, require("three"), require("../renderers/Projector.js")); } else { var mod = { exports: {} }; factory(mod.exports, global.three, global.Projector); global.SVGRenderer = mod.exports; } })(typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : this, function (_exports, _three, _Projector) { "use strict"; Object.defineProperty(_exports, "__esModule", { value: true }); _exports.SVGRenderer = _exports.SVGObject = void 0; function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } Object.defineProperty(subClass, "prototype", { value: Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }), writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _possibleConstructorReturn(self, call) { if (call && (typeof call === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); } function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } var SVGObject = /*#__PURE__*/function (_Object3D) { _inherits(SVGObject, _Object3D); var _super = _createSuper(SVGObject); function SVGObject(node) { var _this2; _classCallCheck(this, SVGObject); _this2 = _super.call(this); _this2.node = node; return _this2; } return _createClass(SVGObject); }(_three.Object3D); _exports.SVGObject = SVGObject; SVGObject.prototype.isSVGObject = true; var SVGRenderer = /*#__PURE__*/_createClass(function SVGRenderer() { _classCallCheck(this, SVGRenderer); var _renderData, _elements, _lights, _svgWidth, _svgHeight, _svgWidthHalf, _svgHeightHalf, _v1, _v2, _v3, _svgNode, _pathCount = 0, _precision = null, _quality = 1, _currentPath, _currentStyle; var _this = this, _clipBox = new _three.Box2(), _elemBox = new _three.Box2(), _color = new _three.Color(), _diffuseColor = new _three.Color(), _ambientLight = new _three.Color(), _directionalLights = new _three.Color(), _pointLights = new _three.Color(), _clearColor = new _three.Color(), _vector3 = new _three.Vector3(), // Needed for PointLight _centroid = new _three.Vector3(), _normal = new _three.Vector3(), _normalViewMatrix = new _three.Matrix3(), _viewMatrix = new _three.Matrix4(), _viewProjectionMatrix = new _three.Matrix4(), _svgPathPool = [], _projector = new _Projector.Projector(), _svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); this.domElement = _svg; this.autoClear = true; this.sortObjects = true; this.sortElements = true; this.overdraw = 0.5; this.info = { render: { vertices: 0, faces: 0 } }; this.setQuality = function (quality) { switch (quality) { case 'high': _quality = 1; break; case 'low': _quality = 0; break; } }; this.setClearColor = function (color) { _clearColor.set(color); }; this.setPixelRatio = function () {}; this.setSize = function (width, height) { _svgWidth = width; _svgHeight = height; _svgWidthHalf = _svgWidth / 2; _svgHeightHalf = _svgHeight / 2; _svg.setAttribute('viewBox', -_svgWidthHalf + ' ' + -_svgHeightHalf + ' ' + _svgWidth + ' ' + _svgHeight); _svg.setAttribute('width', _svgWidth); _svg.setAttribute('height', _svgHeight); _clipBox.min.set(-_svgWidthHalf, -_svgHeightHalf); _clipBox.max.set(_svgWidthHalf, _svgHeightHalf); }; this.getSize = function () { return { width: _svgWidth, height: _svgHeight }; }; this.setPrecision = function (precision) { _precision = precision; }; function removeChildNodes() { _pathCount = 0; while (_svg.childNodes.length > 0) { _svg.removeChild(_svg.childNodes[0]); } } function convert(c) { return _precision !== null ? c.toFixed(_precision) : c; } this.clear = function () { removeChildNodes(); _svg.style.backgroundColor = _clearColor.getStyle(); }; this.render = function (scene, camera) { if (camera instanceof _three.Camera === false) { console.error('THREE.SVGRenderer.render: camera is not an instance of Camera.'); return; } var background = scene.background; if (background && background.isColor) { removeChildNodes(); _svg.style.backgroundColor = background.getStyle(); } else if (this.autoClear === true) { this.clear(); } _this.info.render.vertices = 0; _this.info.render.faces = 0; _viewMatrix.copy(camera.matrixWorldInverse); _viewProjectionMatrix.multiplyMatrices(camera.projectionMatrix, _viewMatrix); _renderData = _projector.projectScene(scene, camera, this.sortObjects, this.sortElements); _elements = _renderData.elements; _lights = _renderData.lights; _normalViewMatrix.getNormalMatrix(camera.matrixWorldInverse); calculateLights(_lights); // reset accumulated path _currentPath = ''; _currentStyle = ''; for (var e = 0, el = _elements.length; e < el; e++) { var element = _elements[e]; var material = element.material; if (material === undefined || material.opacity === 0) continue; _elemBox.makeEmpty(); if (element instanceof _Projector.RenderableSprite) { _v1 = element; _v1.x *= _svgWidthHalf; _v1.y *= -_svgHeightHalf; renderSprite(_v1, element, material); } else if (element instanceof _Projector.RenderableLine) { _v1 = element.v1; _v2 = element.v2; _v1.positionScreen.x *= _svgWidthHalf; _v1.positionScreen.y *= -_svgHeightHalf; _v2.positionScreen.x *= _svgWidthHalf; _v2.positionScreen.y *= -_svgHeightHalf; _elemBox.setFromPoints([_v1.positionScreen, _v2.positionScreen]); if (_clipBox.intersectsBox(_elemBox) === true) { renderLine(_v1, _v2, element, material); } } else if (element instanceof _Projector.RenderableFace) { _v1 = element.v1; _v2 = element.v2; _v3 = element.v3; if (_v1.positionScreen.z < -1 || _v1.positionScreen.z > 1) continue; if (_v2.positionScreen.z < -1 || _v2.positionScreen.z > 1) continue; if (_v3.positionScreen.z < -1 || _v3.positionScreen.z > 1) continue; _v1.positionScreen.x *= _svgWidthHalf; _v1.positionScreen.y *= -_svgHeightHalf; _v2.positionScreen.x *= _svgWidthHalf; _v2.positionScreen.y *= -_svgHeightHalf; _v3.positionScreen.x *= _svgWidthHalf; _v3.positionScreen.y *= -_svgHeightHalf; if (this.overdraw > 0) { expand(_v1.positionScreen, _v2.positionScreen, this.overdraw); expand(_v2.positionScreen, _v3.positionScreen, this.overdraw); expand(_v3.positionScreen, _v1.positionScreen, this.overdraw); } _elemBox.setFromPoints([_v1.positionScreen, _v2.positionScreen, _v3.positionScreen]); if (_clipBox.intersectsBox(_elemBox) === true) { renderFace3(_v1, _v2, _v3, element, material); } } } flushPath(); // just to flush last svg:path scene.traverseVisible(function (object) { if (object.isSVGObject) { _vector3.setFromMatrixPosition(object.matrixWorld); _vector3.applyMatrix4(_viewProjectionMatrix); if (_vector3.z < -1 || _vector3.z > 1) return; var x = _vector3.x * _svgWidthHalf; var y = -_vector3.y * _svgHeightHalf; var node = object.node; node.setAttribute('transform', 'translate(' + x + ',' + y + ')'); _svg.appendChild(node); } }); }; function calculateLights(lights) { _ambientLight.setRGB(0, 0, 0); _directionalLights.setRGB(0, 0, 0); _pointLights.setRGB(0, 0, 0); for (var l = 0, ll = lights.length; l < ll; l++) { var light = lights[l]; var lightColor = light.color; if (light.isAmbientLight) { _ambientLight.r += lightColor.r; _ambientLight.g += lightColor.g; _ambientLight.b += lightColor.b; } else if (light.isDirectionalLight) { _directionalLights.r += lightColor.r; _directionalLights.g += lightColor.g; _directionalLights.b += lightColor.b; } else if (light.isPointLight) { _pointLights.r += lightColor.r; _pointLights.g += lightColor.g; _pointLights.b += lightColor.b; } } } function calculateLight(lights, position, normal, color) { for (var l = 0, ll = lights.length; l < ll; l++) { var light = lights[l]; var lightColor = light.color; if (light.isDirectionalLight) { var lightPosition = _vector3.setFromMatrixPosition(light.matrixWorld).normalize(); var amount = normal.dot(lightPosition); if (amount <= 0) continue; amount *= light.intensity; color.r += lightColor.r * amount; color.g += lightColor.g * amount; color.b += lightColor.b * amount; } else if (light.isPointLight) { var _lightPosition = _vector3.setFromMatrixPosition(light.matrixWorld); var _amount = normal.dot(_vector3.subVectors(_lightPosition, position).normalize()); if (_amount <= 0) continue; _amount *= light.distance == 0 ? 1 : 1 - Math.min(position.distanceTo(_lightPosition) / light.distance, 1); if (_amount == 0) continue; _amount *= light.intensity; color.r += lightColor.r * _amount; color.g += lightColor.g * _amount; color.b += lightColor.b * _amount; } } } function renderSprite(v1, element, material) { var scaleX = element.scale.x * _svgWidthHalf; var scaleY = element.scale.y * _svgHeightHalf; if (material.isPointsMaterial) { scaleX *= material.size; scaleY *= material.size; } var path = 'M' + convert(v1.x - scaleX * 0.5) + ',' + convert(v1.y - scaleY * 0.5) + 'h' + convert(scaleX) + 'v' + convert(scaleY) + 'h' + convert(-scaleX) + 'z'; var style = ''; if (material.isSpriteMaterial || material.isPointsMaterial) { style = 'fill:' + material.color.getStyle() + ';fill-opacity:' + material.opacity; } addPath(style, path); } function renderLine(v1, v2, element, material) { var path = 'M' + convert(v1.positionScreen.x) + ',' + convert(v1.positionScreen.y) + 'L' + convert(v2.positionScreen.x) + ',' + convert(v2.positionScreen.y); if (material.isLineBasicMaterial) { var style = 'fill:none;stroke:' + material.color.getStyle() + ';stroke-opacity:' + material.opacity + ';stroke-width:' + material.linewidth + ';stroke-linecap:' + material.linecap; if (material.isLineDashedMaterial) { style = style + ';stroke-dasharray:' + material.dashSize + ',' + material.gapSize; } addPath(style, path); } } function renderFace3(v1, v2, v3, element, material) { _this.info.render.vertices += 3; _this.info.render.faces++; var path = 'M' + convert(v1.positionScreen.x) + ',' + convert(v1.positionScreen.y) + 'L' + convert(v2.positionScreen.x) + ',' + convert(v2.positionScreen.y) + 'L' + convert(v3.positionScreen.x) + ',' + convert(v3.positionScreen.y) + 'z'; var style = ''; if (material.isMeshBasicMaterial) { _color.copy(material.color); if (material.vertexColors) { _color.multiply(element.color); } } else if (material.isMeshLambertMaterial || material.isMeshPhongMaterial || material.isMeshStandardMaterial) { _diffuseColor.copy(material.color); if (material.vertexColors) { _diffuseColor.multiply(element.color); } _color.copy(_ambientLight); _centroid.copy(v1.positionWorld).add(v2.positionWorld).add(v3.positionWorld).divideScalar(3); calculateLight(_lights, _centroid, element.normalModel, _color); _color.multiply(_diffuseColor).add(material.emissive); } else if (material.isMeshNormalMaterial) { _normal.copy(element.normalModel).applyMatrix3(_normalViewMatrix).normalize(); _color.setRGB(_normal.x, _normal.y, _normal.z).multiplyScalar(0.5).addScalar(0.5); } if (material.wireframe) { style = 'fill:none;stroke:' + _color.getStyle() + ';stroke-opacity:' + material.opacity + ';stroke-width:' + material.wireframeLinewidth + ';stroke-linecap:' + material.wireframeLinecap + ';stroke-linejoin:' + material.wireframeLinejoin; } else { style = 'fill:' + _color.getStyle() + ';fill-opacity:' + material.opacity; } addPath(style, path); } // Hide anti-alias gaps function expand(v1, v2, pixels) { var x = v2.x - v1.x, y = v2.y - v1.y; var det = x * x + y * y; if (det === 0) return; var idet = pixels / Math.sqrt(det); x *= idet; y *= idet; v2.x += x; v2.y += y; v1.x -= x; v1.y -= y; } function addPath(style, path) { if (_currentStyle === style) { _currentPath += path; } else { flushPath(); _currentStyle = style; _currentPath = path; } } function flushPath() { if (_currentPath) { _svgNode = getPathNode(_pathCount++); _svgNode.setAttribute('d', _currentPath); _svgNode.setAttribute('style', _currentStyle); _svg.appendChild(_svgNode); } _currentPath = ''; _currentStyle = ''; } function getPathNode(id) { if (_svgPathPool[id] == null) { _svgPathPool[id] = document.createElementNS('http://www.w3.org/2000/svg', 'path'); if (_quality == 0) { _svgPathPool[id].setAttribute('shape-rendering', 'crispEdges'); //optimizeSpeed } return _svgPathPool[id]; } return _svgPathPool[id]; } }); _exports.SVGRenderer = SVGRenderer; });