var grendCAD = (function grentWall() { var util = { /** * 获取一组坐标的XY坐标区间 * @param {*} points */ getSection (points) { var ret = { minx: points[0].x, maxx: points[0].x, miny: points[0].y, maxy: points[0].y } points.forEach(function (point) { if (ret.minx > point.x) { ret.minx = point.x } if (ret.maxx < point.x) { ret.maxx = point.x } if (ret.miny > point.y) { ret.miny = point.y } if (ret.maxy < point.y) { ret.maxy = point.y } }) return ret }, /** * 计算垂直向量 * @param {向量} arr */ verticalLine (arr) { if (arr.length !== 0) { var x, y; y = Math.sqrt(1 / ((arr[1] * arr[1]) / (arr[0] * arr[0]) + 1)); x = Math.sqrt(1 - y * y); return [x, y]; } return []; }, /** * 将所有坐标转化为指定范围之间的值,但是保持比例不变 * @param {*} points 要转化的坐标 * @param {*} section 指定范围 * @returns Array 转化后的值 */ tranProp (points, section) { var args = util.getSection(points) var xlen = args.maxx - args.minx var ylen = args.maxy - args.miny var prop = xlen > ylen ? xlen : ylen var offsetX = -(args.minx + xlen / 2) var offsetY = -(args.miny + ylen / 2) var range = section[1] - section[0] return { offset: { x: offsetX, y: offsetY }, prop, range: range } }, /** * 将屏幕转化为真实坐标 * @param {*} point * @param {*} section */ tranPoint (point, section) { return { x: point.x + section.minx, y: point.y + section.miny } }, /** * 判断点是否在线上 * @param {*} point 要判断的点 * @param {*} line 线段 * @param {*} width 线宽 */ isContainPoint (point, line, width) { var dis = 0 var s1 = line[1].x - line[0].x var s2 = point.x - line[0].x var s3 = point.x - line[1].x var k1 = line[1].y - line[0].y var k2 = point.y - line[0].y var k3 = point.y - line[1].y var max = width / 2 var min = 0 - max var cross = s1 * s2 + k1 * k2; var d2 = s1 * s1 + k1 * k1; if (cross <= 0) { dis = Math.sqrt(s2 * s2 + k2 * k2); } else if (cross >= d2) { dis = Math.sqrt(s3 * s3 + k3 * k3); } else { var r = cross / d2; var px = line[0].x + s1 * r; var py = line[0].y + k1 * r; dis = Math.sqrt((point.x - px) * (point.x - px) + (py - point.y) * (py - point.y)); } return dis >= min && dis <= max }, /** * 判断两条线段是否相交 * @param {*} line1 * @param {*} line2 */ isLineIntersect (line1, line2) { var a1 = line1[1].y - line1[0].y; var b1 = line1[0].x - line1[1].x; var c1 = a1 * line1[0].x + b1 * line1[0].y; //转换成一般式: Ax+By = C var a2 = line2[1].y - line2[0].y; var b2 = line2[0].x - line2[1].x; var c2 = a2 * line2[0].x + b2 * line2[0].y; // 计算交点 var d = a1 * b2 - a2 * b1; // 当d==0时,两线平行 if (d == 0) { return false; } else { var x = (b2 * c1 - b1 * c2) / d; var y = (a1 * c2 - a2 * c1) / d; // 检测交点是否在两条线段上 if ((isInBetween(line1[0].x, x, line1[1].x) || isInBetween(line1[0].y, y, line1[1].y)) && (isInBetween(line2[0].x, x, line2[1].x) || isInBetween(line2[0].y, y, line2[1].y))) { return true; } } function isInBetween(a, b, c) { // 如果b几乎等于a或c,返回false.为了避免浮点运行时两值几乎相等,但存在相差0.00000...0001的这种情况出现使用下面方式进行避免 if (Math.abs(a - b) < 0.000001 || Math.abs(b - c) < 0.000001) { return false; } return (a <= b && b <= c) || (c <= b && b <= a); } return false; }, /** * 判断两个点是否相同 * @param {*} point1 * @param {*} poin2 */ equalPoint (point1, poin2) { return point1.x === poin2.x && point1.y === poin2.y }, /** * 判断两个面是否相交, * @param {*} face1 * @param {*} face2 */ isFaceIntersect(face1, face2) { for (var i = 0; i < face1.length; i++) { var next = i + 1 === face1.length ? 0 : i + 1 var line1 = [face1[i], face1[next]] for (var j = 0; j < face2.length; j++) { var next = j + 1 === face2.length ? 0 : j + 1 var line2 = [face2[j], face2[next]] var isIntersect1 = util.isLineIntersect(line2, line1) var isIntersect2 = util.isLineIntersect(line1, line2) if (isIntersect1 && isIntersect2) { return true } } } }, /** * 判断一个点是否在面上 * @param {*} face1 * @param {*} face2 */ pointInside(point, face) { var inside = false; var x = point.x, y = point.y; for (var i = 0, j = face.length - 1; i < face.length; j = i++) { var xi = face[i].x, yi = face[i].y; var xj = face[j].x, yj = face[j].y; if(((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi)) { inside = !inside; } } return inside; } } var drawShape = { line (context, line, width) { context.strokeStyle = 'red'; context.lineWidth = width; context.beginPath(); context.moveTo(line[0].x, line[0].y); context.lineTo(line[1].x, line[1].y); context.closePath(); context.stroke(); }, camera (context, point, width) { context.fillStyle = "#00cc00"; context.fillRect(point.x - width / 2, point.y - width / 2, width, width); } } /** * 获取面的所有点 * @param {*} face */ function getFacePoints(face) { var start = face[0] var ret = [] var iteration = start do { ret.push(iteration.points[0]) iteration = face.find(function (item) { return item.id === iteration.linkedLineID[1] }) } while (start !== iteration) return ret } /** * 获取一个元素在指定父元素或文档中的位置 */ function getPosition(dom, parent) { var ret = { x: 0, y: 0 } while (dom === parent || dom === document.documentElement) { ret.x += dom.offsetLeft ret.y += dom.offsetTop dom = dom.offsetParent } return ret } function addMouseEvent (dom, event) { dom.addEventListener('mousedown', function downHandle(ev) { var prevPoint = {x: ev.offsetX, y: ev.offsetY} var con = event.down(prevPoint) if (!con) return; document.documentElement.addEventListener('mousemove', moveHandle, {passive: false}) document.documentElement.addEventListener('mouseup', endHandle, {passive: false}) function moveHandle (ev) { event.move({x: ev.offsetX, y: ev.offsetY}, prevPoint) prevPoint = { x: ev.offsetX, y: ev.offsetY } } function endHandle (ev) { document.documentElement.removeEventListener('mousemove', moveHandle, {passive: false}) document.documentElement.removeEventListener('mouseup', endHandle, {passive: false}) event.up() } }, false) } function Wall(canvas, data, cameras, cal) { this.width = canvas.width this.height = canvas.height this.canvas = canvas this.context = canvas.getContext('2d') this.origin = data this.context.translate(this.width / 2, this.height / 2); this.screenOffset = getPosition(this.canvas) this.lineWidth = 2 this.cameras = cameras this.cal = cal this.init() this.draw() } Wall.prototype.init = function () { var points = [] var faces = {} this.origin.forEach(function(line) { if (faces[line.roomIndex]) { faces[line.roomIndex].push(line) } else { faces[line.roomIndex] = [line] } points = points.concat(...line.points) }) var result = util.tranProp(points, [-0.99, 0.99]) this.status = { offset: result.offset, prop: result.prop, range: result.range } this.faces = faces this.regEvent() } Wall.prototype.transScreenPoint = function (point) { var spoint = {x: point.x, y: point.y} spoint.x += this.status.offset.x spoint.y += this.status.offset.y spoint.x = (spoint.x * this.status.range / this.status.prop) * (this.width / 2) spoint.y = (spoint.y * this.status.range / this.status.prop) * (this.height / 2) return spoint } Wall.prototype.transRealPoint = function (point) { return { x: (point.x * this.status.prop) / (this.status.range * this.width / 2) - this.status.offset.x, y: (point.y * this.status.prop) / (this.status.range * this.width / 2) - this.status.offset.y } } Wall.prototype.regEvent = function () { var sinfo; var section = { minx: -this.width / 2, maxx: this.width / 2, miny: -this.height / 2, maxy: this.height / 2 } var startPoint = null var dirct = null var event = { down (offset) { startPoint = this.transRealPoint(util.tranPoint(offset, section)) sinfo = this._down(startPoint) prevPoint = startPoint return !!sinfo }, move (move) { var point = this.transRealPoint(util.tranPoint(move, section)) if (!dirct) { dirct = sinfo.dire[0] - sinfo.dire[1] > 0 ? 'X' : 'Y' } this._move(point, prevPoint, sinfo, dirct) prevPoint = point }, up () { dirct = null startPoint = null } } event.down = event.down.bind(this) event.move = event.move.bind(this) event.up = event.up.bind(this) addMouseEvent(this.canvas, event) } Wall.prototype._down = function (point) { console.log(point) for (var key in this.faces) { var face = this.faces[key] for (var i = 0, line; line = face[i]; i++) { if (util.isContainPoint(point, line.points, 10 / this.status.prop)) { return { face: face, line: line, dire: util.verticalLine([ line.points[1].x - line.points[0].x, line.points[1].y - line.points[0].y, ]) } } } } } Wall.prototype.updateLine = function (line, x, y) { var face for (var key in this.faces) { var index = this.faces[key].findIndex(function (item) { return item === line }) if (~index) { face = this.faces[key] break } } if (!face) return var leftPoint = face.find(function (item) { return item.id === line.linkedLineID[0] }) var rightPoint = face.find(function (item) { return item.id === line.linkedLineID[1] }) line.points[0].x += x line.points[0].y += y line.points[1].x += x line.points[1].y += y leftPoint.points[1].x = line.points[0].x leftPoint.points[1].y = line.points[0].y rightPoint.points[0].x = line.points[1].x rightPoint.points[0].y = line.points[1].y } Wall.prototype._move = function (point, prevPoint, movePosition, dirct) { var xdis = point.x - prevPoint.x var ydis = point.y - prevPoint.y var revoke = false if (dirct === 'X') { this.updateLine(movePosition.line, xdis, 0) } else { this.updateLine(movePosition.line, 0, ydis) } var activeFacePoint = getFacePoints(movePosition.face) for (let key in this.faces) { if (this.faces[key] !== movePosition.face && util.isFaceIntersect(activeFacePoint, getFacePoints(this.faces[key]))) { revoke = true; break; } } var cameras; if (this.cameras && (cameras = this.cameras[movePosition.face[0].roomIndex])) { for (var i = 0, camera; camera = cameras[i]; i++) { if (!util.pointInside(camera, activeFacePoint)) { revoke = true break; } } } if (revoke) { if (dirct === 'X') { this.updateLine(movePosition.line, -xdis, 0) } else { this.updateLine(movePosition.line, 0, -ydis) } } else { this.cal && this.cal.changePoint && this.cal.changePoint(movePosition.line) } this.draw() } Wall.prototype.draw = function () { this.context.clearRect(-this.width / 2, -this.height / 2, this.width, this.height) for (var key in this.faces) { var face = this.faces[key] for (var i = 0, line; line = face[i]; i++) { drawShape.line( this.context, [ this.transScreenPoint(line.points[0]), this.transScreenPoint(line.points[1]) ], this.lineWidth ) } } if (this.cameras) { for (var key in this.cameras) { for (var i = 0; i < this.cameras[key].length; i++) { drawShape.camera(this.context, this.transScreenPoint(this.cameras[key][i]), this.lineWidth) } } } } return function (dom, line2Ds, cameras) { window. wall = new Wall(dom, line2Ds, cameras, { changePoint: function(line2D) { if (line2D.updateLine3D) { line2D.updateLine3D() } else { console.error('当前线条没有注册3D线条方法') } } }); line2Ds.forEach(function (line2D) { let fn = line2D.draw line2D.draw = function () { wall.draw() fn && fn.bind(this)(); } }) } })();