diff --git a/example/config/webpack.config.js b/example/config/webpack.config.js index 7a76d05a46..9a2e4c989a 100644 --- a/example/config/webpack.config.js +++ b/example/config/webpack.config.js @@ -11,6 +11,7 @@ const gitRevisionPlugin = new GitRevisionPlugin({ commithashCommand: `rev-list v${config.version}..HEAD --count` }) const buildNumber = gitRevisionPlugin.commithash() + module.exports = override( addBundleVisualizer({}, true), addWebpackModuleRule({ diff --git a/example/yarn.lock b/example/yarn.lock index 6556559a25..772da76a87 100644 --- a/example/yarn.lock +++ b/example/yarn.lock @@ -4176,6 +4176,11 @@ electron-to-chromium@^1.3.378, electron-to-chromium@^1.3.585: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.588.tgz#c6515571737bfb42678115a5eaa818384593a9a5" integrity sha512-0zr+ZfytnLeJZxGgmEpPTcItu5Mm4A5zHPZXLfHcGp0mdsk95rmD7ePNewYtK1yIdLbk8Z1U2oTRRfOtR4gbYg== +element-closest-polyfill@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/element-closest-polyfill/-/element-closest-polyfill-1.0.2.tgz#272c217d60effe76a0509a911cde5fcdd78da5a6" + integrity sha512-+3cNhtv8YIyk/oDSlBv+zqUjQFcF9puLp4TFIk2w0Gqd6IL2uZnnu0jvDdOaI7dsK1bIA9gG69KIvkEyUFVGRg== + elliptic@^6.5.3: version "6.5.3" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.3.tgz#cb59eb2efdaf73a0bd78ccd7015a62ad6e0f93d6" diff --git a/src/icons/files/shape-line.svg b/src/icons/files/shape-line.svg new file mode 100644 index 0000000000..bdb5d677aa --- /dev/null +++ b/src/icons/files/shape-line.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/icons/files/shape-polyline.svg b/src/icons/files/shape-polyline.svg new file mode 100644 index 0000000000..858acdef95 --- /dev/null +++ b/src/icons/files/shape-polyline.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/icons/index.tsx b/src/icons/index.tsx index 769abaadc4..a701faf02e 100644 --- a/src/icons/index.tsx +++ b/src/icons/index.tsx @@ -84,6 +84,8 @@ import ZoomInIcon from './files/zoom-in.svg' import ZoomOutIcon from './files/zoom-out.svg' import ShapeCircleIcon from './files/shape-circle.svg' import ShapeRectangleIcon from './files/shape-rectangle.svg' +import ShapePolylineIcon from './files/shape-polyline.svg' +import ShapeLineIcon from './files/shape-line.svg' const icons = { about: AboutIcon, @@ -156,7 +158,9 @@ const icons = { 'zoom-in': ZoomInIcon, 'zoom-out': ZoomOutIcon, 'shape-circle': ShapeCircleIcon, - 'shape-rectangle': ShapeRectangleIcon + 'shape-rectangle': ShapeRectangleIcon, + 'shape-polyline': ShapePolylineIcon, + 'shape-line': ShapeLineIcon } function emptyIcon() { diff --git a/src/script/chem/struct/index.js b/src/script/chem/struct/index.js index 426c2d56cf..5d50ab7bbd 100644 --- a/src/script/chem/struct/index.js +++ b/src/script/chem/struct/index.js @@ -457,10 +457,9 @@ Struct.prototype.rxnArrowSetPos = function (id, pp) { item.pp = pp } -Struct.prototype.simpleObjectSetPos = function (id, p0, p1) { +Struct.prototype.simpleObjectSetPos = function (id, pos) { const item = this.simpleObjects.get(id) - item.p0 = p0 - item.p1 = p1 + item.pos = pos } /** @@ -781,14 +780,14 @@ Struct.prototype.findLoops = function () { const bondsToMark = new Pile() /* - Starting from each half-bond not known to be in a loop yet, - follow the 'next' links until the initial half-bond is reached or - the length of the sequence exceeds the number of half-bonds available. - In a planar graph, as long as every bond is a part of some "loop" - - either an outer or an inner one - every iteration either yields a loop - or doesn't start at all. Thus this has linear complexity in the number - of bonds for planar graphs. - */ + Starting from each half-bond not known to be in a loop yet, + follow the 'next' links until the initial half-bond is reached or + the length of the sequence exceeds the number of half-bonds available. + In a planar graph, as long as every bond is a part of some "loop" - + either an outer or an inner one - every iteration either yields a loop + or doesn't start at all. Thus this has linear complexity in the number + of bonds for planar graphs. + */ let hbIdNext, c, loop, loopId this.halfBonds.forEach((hb, hbId) => { @@ -1045,8 +1044,12 @@ RxnPlus.prototype.clone = function () { function SimpleObject(params) { params = params || {} - this.p0 = params.p0 ? new Vec2(params.p0) : new Vec2() - this.p1 = params.p1 ? new Vec2(params.p1) : new Vec2() + this.pos = [] + + if (params.pos) + for (let i = 0; i < params.pos.length; i++) + this.pos[i] = params.pos[i] ? new Vec2(params.pos[i]) : new Vec2() + this.mode = params.mode } @@ -1057,10 +1060,10 @@ SimpleObject.prototype.clone = function () { SimpleObject.prototype.center = function () { switch (this.mode) { case 'rectangle': { - return Vec2.centre(this.p0, this.p1) + return Vec2.centre(this.pos[0], this.pos[1]) } default: - return this.p0 + return this.pos[0] } } diff --git a/src/script/editor/actions/paste.js b/src/script/editor/actions/paste.js index b502198f1d..1a88ab4ce5 100644 --- a/src/script/editor/actions/paste.js +++ b/src/script/editor/actions/paste.js @@ -110,8 +110,7 @@ export function fromPaste(restruct, pstruct, point, angle = 0) { pstruct.simpleObjects.forEach(simpleObject => { action.addOp( new op.SimpleObjectAdd( - simpleObject.p0.add(offset), - simpleObject.p1.add(offset), + simpleObject.pos.map(p => p.add(offset)), simpleObject.mode ).perform(restruct) ) diff --git a/src/script/editor/actions/simpleobject.js b/src/script/editor/actions/simpleobject.js index eaf0f4b8dc..f6e6c6f54e 100644 --- a/src/script/editor/actions/simpleobject.js +++ b/src/script/editor/actions/simpleobject.js @@ -22,9 +22,9 @@ export function fromSimpleObjectDeletion(restruct, id) { return action.perform(restruct) } -export function fromSimpleObjectAddition(restruct, p0, p1, mode) { +export function fromSimpleObjectAddition(restruct, pos, mode) { var action = new Action() - action.addOp(new op.SimpleObjectAdd(p0, p1, mode)) + action.addOp(new op.SimpleObjectAdd(pos, mode)) return action.perform(restruct) } diff --git a/src/script/editor/operations/op.js b/src/script/editor/operations/op.js index 6105850db8..498ba93de0 100644 --- a/src/script/editor/operations/op.js +++ b/src/script/editor/operations/op.js @@ -934,8 +934,9 @@ function RestoreDescriptorsPosition(history) { } RestoreDescriptorsPosition.prototype = new Base() -function SimpleObjectAdd(p0, p1, mode) { - this.data = { id: null, p0, p1, mode } +function SimpleObjectAdd(pos, mode) { + this.data = { id: null, pos, mode } + let performed = false this.execute = function (restruct) { const struct = restruct.molecule @@ -953,8 +954,7 @@ function SimpleObjectAdd(p0, p1, mode) { struct.simpleObjectSetPos( this.data.id, - new Vec2(this.data.p0), - new Vec2(this.data.p1) + this.data.pos.map(p => new Vec2(p)) ) invalidateItem(restruct, 'simpleObjects', this.data.id, 1) @@ -969,14 +969,13 @@ function SimpleObjectAdd(p0, p1, mode) { SimpleObjectAdd.prototype = new Base() function SimpleObjectDelete(id) { - this.data = { id, p0: null, p1: null, item: null } + this.data = { id, pos: null, item: null } let performed = false this.execute = function (restruct) { const struct = restruct.molecule if (!performed) { const item = struct.simpleObjects.get(this.data.id) - this.data.p0 = item.p0 - this.data.p1 = item.p1 + this.data.pos = item.pos this.data.mode = item.mode } @@ -1004,8 +1003,7 @@ function SimpleObjectMove(id, d, noinvalidate) { const id = this.data.id const d = this.data.d const item = struct.simpleObjects.get(id) - item.p0.add_(d) - item.p1.add_(d) + item.pos.forEach(p => p.add_(d)) restruct.simpleObjects .get(id) .visel.translate(scale.obj2scaled(d, restruct.render.options)) @@ -1029,7 +1027,9 @@ function SimpleObjectResize(id, d, noinvalidate) { const id = this.data.id const d = this.data.d const item = struct.simpleObjects.get(id) - item.p1.add_(d) + + //JA: TODO + if (d) item.pos[1].add_(d) restruct.simpleObjects .get(id) .visel.translate(scale.obj2scaled(d, restruct.render.options)) diff --git a/src/script/editor/shared/closest.js b/src/script/editor/shared/closest.js index cad1798e3b..53628c4560 100644 --- a/src/script/editor/shared/closest.js +++ b/src/script/editor/shared/closest.js @@ -36,8 +36,7 @@ function findClosestSimpleObject(restruct, pos) { let ret = null restruct.simpleObjects.forEach((simpleObject, id) => { - const p = simpleObject.item.center() - const dist = Math.max(Math.abs(pos.x - p.x), Math.abs(pos.y - p.y)) + const dist = simpleObject.calcDistance(pos) if (dist < 0.3 && (!ret || dist < minDist)) { minDist = dist diff --git a/src/script/editor/tool/helper/locate.js b/src/script/editor/tool/helper/locate.js index d64b891b5d..4c4a16e76b 100644 --- a/src/script/editor/tool/helper/locate.js +++ b/src/script/editor/tool/helper/locate.js @@ -72,10 +72,10 @@ function getElementsInRectangle(restruct, p0, p1) { restruct.simpleObjects.forEach((item, id) => { if ( - item.item.p0.x > x0 && - item.item.p0.x < x1 && - item.item.p0.y > y0 && - item.item.p0.y < y1 + item.item.pos[0].x > x0 && + item.item.pos[0].x < x1 && + item.item.pos[0].y > y0 && + item.item.pos[0].y < y1 ) simpleObjectsList.push(id) }) @@ -144,7 +144,7 @@ function getElementsInPolygon(restruct, rr) { }) restruct.simpleObjects.forEach((item, id) => { - if (isPointInPolygon(r, item.item.p0)) simpleObjectsList.push(id) + if (isPointInPolygon(r, item.item.pos[0])) simpleObjectsList.push(id) }) const enhancedFlagList = [] diff --git a/src/script/editor/tool/simpleobject.js b/src/script/editor/tool/simpleobject.js index d49281d391..718c2e988a 100644 --- a/src/script/editor/tool/simpleobject.js +++ b/src/script/editor/tool/simpleobject.js @@ -33,6 +33,7 @@ SimpleObjectTool.prototype.mousedown = function (event) { var rnd = this.editor.render const p0 = rnd.page2obj(event) this.dragCtx = { p0 } + var ci = this.editor.findItem(event, ['simpleObjects']) if (ci && ci.map === 'simpleObjects') { this.editor.hover(null) @@ -61,8 +62,7 @@ SimpleObjectTool.prototype.mousemove = function (event) { if (!this.dragCtx.action) { const action = fromSimpleObjectAddition( rnd.ctab, - this.dragCtx.p0, - this.dragCtx.p0, + [this.dragCtx.p0, this.dragCtx.p0], this.mode ) //TODO: need to rework actions/operations logic @@ -74,6 +74,7 @@ SimpleObjectTool.prototype.mousemove = function (event) { } else { this.dragCtx.action.perform(rnd.ctab) } + this.dragCtx.action = fromSimpleObjectResizing( rnd.ctab, this.dragCtx.itemId, @@ -98,8 +99,7 @@ SimpleObjectTool.prototype.mouseup = function (event) { ) this.dragCtx.action = fromSimpleObjectAddition( rnd.ctab, - this.dragCtx.p0, - this.dragCtx.previous, + [this.dragCtx.p0, this.dragCtx.previous], this.mode ) } diff --git a/src/script/format/chemGraph/toGraph/prepare.js b/src/script/format/chemGraph/toGraph/prepare.js index c20b9ccb28..962dcdb898 100644 --- a/src/script/format/chemGraph/toGraph/prepare.js +++ b/src/script/format/chemGraph/toGraph/prepare.js @@ -71,11 +71,10 @@ export function prepareStructForGraph(struct) { struct.simpleObjects.forEach(item => { graphNodes.push({ type: 'simpleObject', - center: item.p0, + center: item.pos[0], data: { mode: item.mode, - p0: item.p0, - p1: item.p1 + pos: item.pos } }) }) diff --git a/src/script/format/schemes/simpleObjectSchema.js b/src/script/format/schemes/simpleObjectSchema.js index 062ef55210..8dd0c0fdd0 100644 --- a/src/script/format/schemes/simpleObjectSchema.js +++ b/src/script/format/schemes/simpleObjectSchema.js @@ -34,32 +34,25 @@ const schema = { mode: { type: 'string' }, - p0: { - type: 'object', - properties: { - x: { - type: 'integer' - }, - y: { - type: 'integer' + pos: { + type: 'array', + items: [ + { + type: 'object', + properties: { + x: { + type: 'integer' + }, + y: { + type: 'integer' + } + }, + required: ['x', 'y'] } - }, - required: ['x', 'y'] - }, - p1: { - type: 'object', - properties: { - x: { - type: 'integer' - }, - y: { - type: 'integer' - } - }, - required: ['x', 'y'] + ] } }, - required: ['type', 'mode', 'p0', 'p1'] + required: ['type', 'mode', 'pos'] } } } diff --git a/src/script/render/draw.js b/src/script/render/draw.js index 4fe1a0451e..f5033c6ad6 100644 --- a/src/script/render/draw.js +++ b/src/script/render/draw.js @@ -20,18 +20,30 @@ import Raphael from '../raphael-ext' const tfx = util.tfx -function rectangle(paper, p0, p1, options) { +function rectangle(paper, pos, options) { return paper.rect( - tfx(Math.min(p0.x, p1.x)), - tfx(Math.min(p0.y, p1.y)), - tfx(Math.abs(p1.x - p0.x)), - tfx(Math.abs(p1.y - p0.y)) + tfx(Math.min(pos[0].x, pos[1].x)), + tfx(Math.min(pos[0].y, pos[1].y)), + tfx(Math.abs(pos[1].x - pos[0].x)), + tfx(Math.abs(pos[1].y - pos[0].y)) ) } -function circle(paper, p0, p1, options) { - let rad = Math.sqrt(Math.pow(p0.x - p1.x, 2), Math.pow(p0.y - p1.y, 2)) - return paper.circle(p0.x, p0.y, rad) +function circle(paper, pos, options) { + const rad = Vec2.dist(pos[0], pos[1]) + return paper.circle(pos[0].x, pos[0].y, rad) +} + +function polyline(paper, pos, options) { + let path = ['M', pos[0].x, pos[0].y] + for (let i = 1; i < pos.length; i++) path.push('L', pos[i].x, pos[i].y) + return paper.path(path) +} + +function line(paper, pos, options) { + let path = ['M', pos[0].x, pos[0].y] + path.push('L', pos[1].x, pos[1].y) + return paper.path(path) } function arrow(paper, a, b, options) { @@ -394,5 +406,7 @@ export default { selectionPolygon, selectionLine, circle, - rectangle + rectangle, + polyline, + line } diff --git a/src/script/render/options.js b/src/script/render/options.js index fc8d2561ac..857b1f5d07 100644 --- a/src/script/render/options.js +++ b/src/script/render/options.js @@ -86,6 +86,12 @@ function defaultOptions(opt) { stroke: 'gray', 'stroke-width': '1px' }, + highlightStyleSimpleObject: { + stroke: '#0c0', + 'stroke-width': scaleFactor / 4, + 'stroke-linecap': 'round', + 'stroke-opacity': 0.6 + }, atomSelectionPlateRadius: labelFontSize * 1.2 } diff --git a/src/script/render/restruct/index.js b/src/script/render/restruct/index.js index faf277b238..c223ee2d7d 100644 --- a/src/script/render/restruct/index.js +++ b/src/script/render/restruct/index.js @@ -245,15 +245,19 @@ ReStruct.prototype.addReObjectPath = function ( // eslint-disable-line max-params if (!path || !this.layers[LAYER_MAP[group]].node.parentNode) return - const offset = this.render.options.offset - let bb = visible ? Box2Abs.fromRelBox(util.relBox(path.getBBox())) : null - const ext = pos && bb ? bb.translate(pos.negated()) : null - if (offset !== null) { - path.translateAbs(offset.x, offset.y) - bb = bb ? bb.translate(offset) : null - } - visel.add(path, bb, ext) - path.insertBefore(this.layers[LAYER_MAP[group]]) + const paths = Array.isArray(path) ? path : [path] + + paths.forEach(path => { + const offset = this.render.options.offset + let bb = visible ? Box2Abs.fromRelBox(util.relBox(path.getBBox())) : null + const ext = pos && bb ? bb.translate(pos.negated()) : null + if (offset !== null) { + path.translateAbs(offset.x, offset.y) + bb = bb ? bb.translate(offset) : null + } + visel.add(path, bb, ext) + path.insertBefore(this.layers[LAYER_MAP[group]]) + }) } ReStruct.prototype.clearMarks = function () { diff --git a/src/script/render/restruct/resimpleobject.js b/src/script/render/restruct/resimpleobject.js index 8e72ddf61d..9ef8a07e1f 100644 --- a/src/script/render/restruct/resimpleobject.js +++ b/src/script/render/restruct/resimpleobject.js @@ -21,6 +21,8 @@ import util from '../util' import scale from '../../util/scale' import Vec2 from 'src/script/util/vec2' +const tfx = util.tfx + function ReSimpleObject(simpleObject) { this.init('simpleObject') @@ -30,17 +32,179 @@ ReSimpleObject.prototype = new ReObject() ReSimpleObject.isSelectable = function () { return true } +ReSimpleObject.prototype.calcDistance = function (p) { + const point = new Vec2(p.x, p.y) + let dist = 0 + switch (this.item.mode) { + case 'circle': { + const dist1 = Vec2.dist(point, this.item.pos[0]) + const dist2 = Vec2.dist(this.item.pos[0], this.item.pos[1]) + dist = Math.max(dist1, dist2) - Math.min(dist1, dist2) + break + } + case 'rectangle': { + const diffX = Math.min( + Math.max(this.item.pos[0].x, point.x) - + Math.min(this.item.pos[0].x, point.x), + Math.max(this.item.pos[1].x, point.x) - + Math.min(this.item.pos[1].x, point.x) + ) + const diffY = Math.min( + Math.max(this.item.pos[0].y, point.y) - + Math.min(this.item.pos[0].y, point.y), + Math.max(this.item.pos[1].y, point.y) - + Math.min(this.item.pos[1].y, point.y) + ) + dist = Math.min(diffX, diffY) + break + } + case 'line': { + if ( + (point.x < Math.min(this.item.pos[0].x, this.item.pos[1].x) || + point.x > Math.max(this.item.pos[0].x, this.item.pos[1].x)) && + (point.y < Math.min(this.item.pos[0].y, this.item.pos[1].y) || + point.y > Math.max(this.item.pos[0].y, this.item.pos[1].y)) + ) + dist = Math.min( + Vec2.dist(this.item.pos[0], point), + Vec2.dist(this.item.pos[1], point) + ) + else { + const a = Vec2.dist(this.item.pos[0], this.item.pos[1]) + const b = Vec2.dist(this.item.pos[0], point) + const c = Vec2.dist(this.item.pos[1], point) + const per = (a + b + c) / 2 + dist = (2 / a) * Math.sqrt(per * (per - a) * (per - b) * (per - c)) + } + break + } + default: { + throw new Error('Unsupported shape type') + } + } + + return dist +} ReSimpleObject.prototype.highlightPath = function (render) { - var p = scale.obj2scaled(this.item.center(), render.options) - var s = render.options.scale - return render.paper.rect(p.x - s / 4, p.y - s / 4, s / 2, s / 2, s / 8) + const point = [] + + this.item.pos.forEach((p, index) => { + point[index] = scale.obj2scaled(p, render.options) + }) + const s = render.options.scale + + const path = [] + + //TODO: It seems that inheritance will be the better approach here + switch (this.item.mode) { + case 'circle': { + const rad = Vec2.dist(point[0], point[1]) + path.push( + render.paper.circle(point[0].x, point[0].y, rad + s / 8), + render.paper.circle(point[0].x, point[0].y, rad - s / 8) + ) + break + } + + case 'rectangle': { + path.push( + render.paper.rect( + tfx(Math.min(point[0].x, point[1].x) - s / 8), + tfx(Math.min(point[0].y, point[1].y) - s / 8), + tfx( + Math.max(point[0].x, point[1].x) - + Math.min(point[0].x, point[1].x) + + s / 4 + ), + tfx( + Math.max(point[0].y, point[1].y) - + Math.min(point[0].y, point[1].y) + + s / 4 + ) + ) + ) + + path.push( + render.paper.rect( + tfx(Math.min(point[0].x, point[1].x) + s / 8), + tfx(Math.min(point[0].y, point[1].y) + s / 8), + tfx( + Math.max(point[0].x, point[1].x) - + Math.min(point[0].x, point[1].x) - + s / 4 + ), + tfx( + Math.max(point[0].y, point[1].y) - + Math.min(point[0].y, point[1].y) - + s / 4 + ) + ) + ) + + break + } + case 'line': { + //TODO: reuse this code for polyline + const poly = [] + + let angle = Math.atan( + (point[1].y - point[0].y) / (point[1].x - point[0].x) + ) + + const p0 = { x: 0, y: 0 } + const p1 = { x: 0, y: 0 } + + const k = point[0].x > point[1].x ? -1 : 1 + + p0.x = point[0].x - k * ((s / 8) * Math.cos(angle)) + p0.y = point[0].y - k * ((s / 8) * Math.sin(angle)) + p1.x = point[1].x + k * ((s / 8) * Math.cos(angle)) + p1.y = point[1].y + k * ((s / 8) * Math.sin(angle)) + + poly.push( + 'M', + p0.x + ((k * s) / 8) * Math.sin(angle), + p0.y - ((k * s) / 8) * Math.cos(angle) + ) + poly.push( + 'L', + p1.x + ((k * s) / 8) * Math.sin(angle), + p1.y - ((k * s) / 8) * Math.cos(angle) + ) + poly.push( + 'L', + p1.x - ((k * s) / 8) * Math.sin(angle), + p1.y + ((k * s) / 8) * Math.cos(angle) + ) + poly.push( + 'L', + p0.x - ((k * s) / 8) * Math.sin(angle), + p0.y + ((k * s) / 8) * Math.cos(angle) + ) + poly.push( + 'L', + p0.x + ((k * s) / 8) * Math.sin(angle), + p0.y - ((k * s) / 8) * Math.cos(angle) + ) + + path.push(render.paper.path(poly)) + break + } + default: { + throw new Error('Unsupported shape type') + } + } + + return path } ReSimpleObject.prototype.drawHighlight = function (render) { - var ret = this.highlightPath(render).attr(render.options.highlightStyle) - render.ctab.addReObjectPath('highlighting', this.visel, ret) - return ret + const paths = this.highlightPath(render).map(path => + path.attr(render.options.highlightStyle) + ) + render.ctab.addReObjectPath('highlighting', this.visel, paths) + return paths } ReSimpleObject.prototype.makeSelectionPlate = function ( @@ -48,24 +212,59 @@ ReSimpleObject.prototype.makeSelectionPlate = function ( paper, styles ) { - // TODO [MK] review parameters - return this.highlightPath(restruct.render).attr(styles.selectionStyle) + const s = restruct.render.options.scale + const pos = [] + this.item.pos.forEach((p, index) => { + pos[index] = scale.obj2scaled(p, restruct.render.options) || new Vec2() + }) + + let path = null + switch (this.item.mode) { + case 'circle': { + path = draw.circle(paper, pos) + break + } + case 'rectangle': { + path = draw.rectangle(paper, pos) + break + } + case 'line': { + path = draw.line(paper, pos) + break + } + default: { + throw new Error('Unsupported shape type') + } + } + + return path.attr(styles.highlightStyleSimpleObject) } ReSimpleObject.prototype.show = function (restruct, id, options) { const render = restruct.render - const p0 = scale.obj2scaled(this.item.p0, options) - const p1 = scale.obj2scaled(this.item.p1, options) + const s = render.options.scale + const pos = [] + this.item.pos.forEach((p, index) => { + pos[index] = scale.obj2scaled(p, options) || new Vec2() + }) + let path = null switch (this.item.mode) { case 'circle': { - path = draw.circle(render.paper, p0, p1, options) + path = draw.circle(render.paper, pos, options) break } - default: { - path = draw.rectangle(render.paper, p0, p1, options) + case 'rectangle': { + path = draw.rectangle(render.paper, pos, options) break } + case 'line': { + path = draw.line(render.paper, pos, options) + break + } + default: { + throw new Error('Unsupported shape type') + } } var offset = options.offset if (offset != null) path.translateAbs(offset.x, offset.y) diff --git a/src/script/ui/action/tools.js b/src/script/ui/action/tools.js index d1483fb98d..6c3e1dd68c 100644 --- a/src/script/ui/action/tools.js +++ b/src/script/ui/action/tools.js @@ -120,6 +120,10 @@ const toolActions = { 'shape-rectangle': { title: 'Shape Rectangle', action: { tool: 'simpleobject', opts: 'rectangle' } + }, + 'shape-line': { + title: 'Shape Line', + action: { tool: 'simpleobject', opts: 'line' } } } diff --git a/src/script/ui/app/toolbar.jsx b/src/script/ui/app/toolbar.jsx index 44cfd800df..3cba9fa4eb 100644 --- a/src/script/ui/app/toolbar.jsx +++ b/src/script/ui/app/toolbar.jsx @@ -125,7 +125,7 @@ const toolbox = [ }, { id: 'shape', - menu: ['shape-circle', 'shape-rectangle'] + menu: ['shape-circle', 'shape-rectangle', 'shape-line'] } ] diff --git a/src/script/ui/dialog/template/template-attach.jsx b/src/script/ui/dialog/template/template-attach.jsx index b994129b6f..5dc0d837c0 100644 --- a/src/script/ui/dialog/template/template-attach.jsx +++ b/src/script/ui/dialog/template/template-attach.jsx @@ -26,7 +26,8 @@ import { initAttach, setAttachPoints, setTmplName } from '../../state/templates' const EDITOR_STYLES = { selectionStyle: { fill: '#47b3ec', stroke: 'none' }, - highlightStyle: { stroke: '#1a7090', 'stroke-width': 1.2 } + highlightStyle: { stroke: '#1a7090', 'stroke-width': 1.2 }, + highlightStyleSimpleObject: { 'stroke-opacity': 0.3 } } class Attach extends Component { diff --git a/src/script/util/scale.js b/src/script/util/scale.js index 5498e2e862..2bf464d6cd 100644 --- a/src/script/util/scale.js +++ b/src/script/util/scale.js @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. ***************************************************************************/ - function scaled2obj(v, options) { return v.scaled(1 / options.scale) }