UNPKG

@obliczeniowo/elementary

Version:
1,100 lines (1,091 loc) 39.4 kB
import { Point2D, ColorRGB } from '@obliczeniowo/elementary/classes'; import { ns } from '@obliczeniowo/elementary/svg'; import { keys } from '@obliczeniowo/elementary/objects'; import { TextSizePipe } from '@obliczeniowo/elementary/text-pipes'; import { ElementaryMath } from '@obliczeniowo/elementary/math'; /* eslint-disable @typescript-eslint/no-unsafe-argument */ var TextAlign; (function (TextAlign) { TextAlign["LEFT"] = "left"; TextAlign["CENTER"] = "center"; TextAlign["RIGHT"] = "right"; TextAlign["JUSTIFY"] = "justify"; })(TextAlign || (TextAlign = {})); var TextBaseline; (function (TextBaseline) { TextBaseline["ALPHABETIC"] = "alphabetic"; TextBaseline["IDEOGRAPHIC"] = "ideographic"; TextBaseline["BOTTOM"] = "bottom"; TextBaseline["TOP"] = "top"; TextBaseline["MIDDLE"] = "middle"; TextBaseline["HANGING"] = "hanging"; })(TextBaseline || (TextBaseline = {})); var LinePattern; (function (LinePattern) { LinePattern[LinePattern["NONE"] = 0] = "NONE"; LinePattern[LinePattern["DOTTED"] = 1] = "DOTTED"; LinePattern[LinePattern["DASHED"] = 2] = "DASHED"; LinePattern[LinePattern["DASH_DOT"] = 3] = "DASH_DOT"; LinePattern[LinePattern["DISABLED"] = 4] = "DISABLED"; })(LinePattern || (LinePattern = {})); /** * Abstract class to easy cooperate and switch drawing context logic */ class DrawingContextInterface { lastPoint = new Point2D(); arrowScale = 1; /** optional data that can be set after call draw method */ lastSettings = {}; group(options) { return this; } endGroup() { return this; } moveTo(point) { this.lastPoint = point; return this; } drawArrow(startPoint, endPoint, stroke, color, drawArrow = true, options) { this.group({ name: 'arrow' }); this.drawLine(startPoint, endPoint, stroke, color, options); if (drawArrow) { let arrowVector = startPoint.subtract(endPoint); arrowVector = arrowVector.multiply(10 / arrowVector.length() * this.arrowScale); const firstArrowPoint = arrowVector .rotate(15 * Math.PI / 180) .add(endPoint); const secondArrowPoint = arrowVector .rotate(-15 * Math.PI / 180) .add(endPoint); this.drawPolyline([firstArrowPoint, endPoint, secondArrowPoint], stroke, color, options); } this.endGroup(); return this; } /** * Draw all kind of object that implements Drawable interface * @param drawable table of object that implements Drawable interface */ draw(drawable) { drawable.forEach((drawable) => drawable.draw(this)); return this; } } class DrawingCanvasInterface extends DrawingContextInterface { ctx; _stroke = 0; get stroke() { return this._stroke; } set stroke(value) { this.ctx.lineWidth = value; this._stroke = value; } constructor(ctx) { super(); this.ctx = ctx; } getTextDimension(text) { const textMetrics = this.ctx.measureText(text); return { width: textMetrics.width, height: Math.abs(textMetrics.fontBoundingBoxAscent) + Math.abs(textMetrics.fontBoundingBoxDescent), }; } clear() { const rect = this.ctx.canvas.getBoundingClientRect(); const pt = this.transform(new Point2D()); this.ctx.clearRect(pt.x, pt.y, rect.width, rect.height); } drawLine(startPoint, endPoint, stroke, color, options) { this.stroke = stroke; this.ctx.strokeStyle = color.toString(); this.ctx.beginPath(); this.ctx.moveTo(startPoint.x, startPoint.y); this.ctx.lineTo(endPoint.x, endPoint.y); this.ctx.stroke(); return this; } drawText(text, handlePosition, color, angle, options) { this.ctx.fillStyle = color.toString(); this.ctx.save(); this.ctx.rotate(angle); this.ctx.translate(handlePosition.x, handlePosition.y); this.ctx.fillText(text, 0, 0); this.ctx.restore(); return this; } drawPolyline(points, stroke, color, options) { this.stroke = stroke; this.ctx.strokeStyle = color.toString(); this.ctx.beginPath(); if (points.length > 1) { this.ctx.moveTo(points[0].x, points[0].y); let point; for (let i = 1; i < points.length; i++) { point = points[i]; this.ctx.lineTo(point.x, point.y); } if (options?.close) { this.ctx.lineTo(points[0].x, points[0].y); } } this.ctx.stroke(); return this; } drawPolygon(points, stroke, color, fill, options) { this.stroke = stroke; this.ctx.strokeStyle = color.toString(); this.ctx.beginPath(); if (points.length > 1) { this.ctx.moveTo(points[0].x, points[0].y); let point; for (let i = 1; i < points.length; i++) { point = points[i]; this.ctx.lineTo(point.x, point.y); } if (options?.close) { this.ctx.lineTo(points[0].x, points[0].y); } } this.ctx.closePath(); if (color !== 'none') { this.ctx.stroke(); } if (fill !== 'none') { this.ctx.fill(); } return this; } drawCircle(point, ray, stroke, strokeColor, fillColor, options) { this.ctx.strokeStyle = strokeColor.toString(); this.stroke = stroke; this.ctx.fillStyle = fillColor.toString(); const draw = () => this.ctx.ellipse(point.x, point.y, ray, ray, 0, 0, Math.PI * 2); this.path(draw); this.path(draw, true); return this; } drawPie(center, rx, ry, start, end, stroke, strokeColor, fillColor, options) { if (rx !== ry) { console.warn(`Drawing pie with ${rx} !== ${ry} is imposable right now`); } this.stroke = stroke; this.ctx.strokeStyle = strokeColor.toString(); this.ctx.fillStyle = fillColor.toString(); const draw = () => { this.ctx.arc(center.x, center.y, rx, start, end); this.ctx.lineTo(center.x, center.y); }; this.path(draw); this.path(draw, true); return this; } drawEllipse(point, xRay, yRay, stroke, strokeColor, fillColor, options) { this.ctx.strokeStyle = strokeColor.toString(); this.stroke = stroke; this.ctx.fillStyle = fillColor.toString(); const draw = () => this.ctx.ellipse(point.x, point.y, xRay, yRay, 0, 0, Math.PI * 2); this.path(draw); this.path(draw, true); return this; } drawRect(x, y, width, height, stroke, strokeColor, fillColor, options) { this.ctx.strokeStyle = strokeColor.toString(); this.stroke = stroke; this.ctx.fillStyle = fillColor.toString(); this.ctx.fillRect(x, y, x + width, y + height); return this; } lineTo(point, stroke, color) { this.ctx.lineTo(point.x, point.y); return this; } setFontSize(fontSize) { this.ctx.font = fontSize + 'px'; return this; } getFontSize() { const metrics = this.ctx.measureText('Ty'); return metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent; } setTextAlign(align) { this.ctx.textAlign = align; return this; } setTextBaseline(textBaseline) { this.ctx.textBaseline = textBaseline; return this; } getTextWidth(text) { return this.ctx.measureText(text).width; } setLinePattern(linePattern, linePatternDef) { switch (linePattern) { case LinePattern.DASHED: this.ctx.setLineDash([4, 2]); break; case LinePattern.DOTTED: this.ctx.setLineDash([2, 1]); break; case LinePattern.DASH_DOT: this.ctx.setLineDash([10, 2, 1, 2]); break; default: if (linePatternDef) { this.ctx.setLineDash(linePatternDef.strokeDasharray); } } return this; } getLineStrokeSize(size) { if (size) { this.stroke = size; } return this._stroke; } transform(point) { const m = DOMMatrix.fromMatrix(this.ctx.getTransform()); m.invertSelf(); return new Point2D(m.a * point.x + m.b * point.y + m.e, m.c * point.x + m.d * point.y + m.f); } save(fileName) { const downloadLink = document.createElement('a'); downloadLink.setAttribute('download', `${fileName}.png`); const canvas = this.ctx.canvas; const dataURL = canvas.toDataURL('image/png'); const url = dataURL.replace(/^data:image\/png/, 'data:application/octet-stream'); downloadLink.setAttribute('href', url); downloadLink.click(); } path(cb, fillOrStroke) { this.ctx.beginPath(); cb(); if (fillOrStroke) { this.ctx.fill(); } else { this.ctx.stroke(); } } } /* eslint-disable @typescript-eslint/non-nullable-type-assertion-style */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ class DrawingSvgInterface extends DrawingContextInterface { getTextDimension(text) { return new TextSizePipe().transform(text, this.fontSize); } static archPath(center, rx, ry, start, end, reverse = false) { if (Math.abs(start - end) < Math.PI * 2) { let flag = start > end ? 1 : 0; if (Math.abs(start - end) - Math.floor(Math.abs(start - end) / (Math.PI * 2)) > Math.PI) { flag = start > end ? 0 : 1; } const fP = new Point2D(center.x + rx * Math.cos(start), center.y + ry * Math.sin(start)); const lP = new Point2D(center.x + rx * Math.cos(end), center.y + ry * Math.sin(end)); return ('M ' + ((reverse ? -lP.x : fP.x) + ',' + (reverse ? lP.y : fP.y) + ' A ' + rx + ',' + ry + ' 0 ' + flag + ' 1 ' + (reverse ? -fP.x : lP.x) + ',' + (reverse ? fP.y : lP.y))); } return undefined; } svg; root; xMinimum = null; xMaximum = null; yMinimum = null; yMaximum = null; textAnchor = 'start'; linePatternName = LinePattern.NONE; defs; linesDefs = new Map(); groups = []; renderer; fontSize = 10; constructor(svg, renderer) { super(); this.renderer = renderer; this.svg = svg; const root = svg.querySelector('g.root'); this.root = root || this.renderer.createElement('g', ns); if (!root) { this.svg.appendChild(this.root); } } setXMin(value) { if (this.xMinimum !== null) { if (this.xMinimum >= value) { this.xMinimum = value; } } else { this.xMinimum = value; } } setYMin(value) { if (this.yMinimum !== null) { if (this.yMinimum >= value) { this.yMinimum = value; } } else { this.yMinimum = value; } } setXMax(value) { if (this.xMaximum !== null) { if (this.xMaximum <= value) { this.xMaximum = value; } } else { this.xMaximum = value; } } setYMax(value) { if (this.yMaximum !== null) { if (this.yMaximum <= value) { this.yMaximum = value; } } else { this.yMaximum = value; } } setMinMax(point) { this.setXMin(point.x); this.setXMax(point.x); this.setYMin(point.y); this.setYMax(point.y); } setSvgOptions(element, options) { if (options) { const { opacity, style } = options; const { attributes } = options?.svgOnly || {}; if (opacity) { element.setAttribute('opacity', options.opacity.toString() || 1); } if (attributes) { keys(attributes).forEach((name) => { element.setAttribute(name, attributes[name]); }); } if (style) { keys(options.style).forEach((key) => (element.style[key] = options.style?.[key])); } } } createSvgPoint(point2d) { const point = this.svg.createSVGPoint(); point.x = point2d.x; point.y = point2d.y; return point; } setSvgElementLinePattern(element) { const pattern = this.linesDefs.get(this.linePatternName); if (pattern) { element.style.strokeMiterlimit = pattern.strokeMiterlimit.toString(); element.style.strokeDasharray = pattern.strokeDasharray.join(', '); element.style.strokeDashoffset = pattern.strokeDashoffset.toString(); } } lineTo(point, stroke, color) { this.drawLine(this.lastPoint, point, stroke, color); this.lastPoint = point; return this; } setFontSize(fontSize) { this.fontSize = fontSize; return this; } getFontSize() { return this.fontSize; } setTextAlign(align) { switch (align) { case TextAlign.LEFT: this.textAnchor = 'start'; break; case TextAlign.CENTER: this.textAnchor = 'middle'; break; case TextAlign.RIGHT: this.textAnchor = 'end'; break; default: this.textAnchor = 'start'; break; } return this; } setTextBaseline(textBaseline) { return this; } getTextWidth(text) { const lDiv = this.renderer.createElement('div'); document.body.appendChild(lDiv); lDiv.style.fontSize = this.fontSize + 'px'; lDiv.style.position = 'absolute'; lDiv.innerHTML = text; const lResult = { width: lDiv.clientWidth, height: lDiv.clientHeight, }; document.body.removeChild(lDiv); return lResult.width; } clear() { const container = this.svgContainer; if (this.defs) { while (this.defs.hasChildNodes()) { this.defs.removeChild(this.defs.firstChild); } } while (container.hasChildNodes()) { container.removeChild(container.firstChild); } return this; } setLinePattern(linePattern, linePatternDef) { if (linePattern && !this.linesDefs.has(linePattern)) { switch (linePattern) { case LinePattern.DASHED: this.linesDefs.set(linePattern, { strokeMiterlimit: 4, strokeDasharray: [4, 2], strokeDashoffset: 0, }); break; case LinePattern.DOTTED: this.linesDefs.set(linePattern, { strokeMiterlimit: 4, strokeDasharray: [2, 1], strokeDashoffset: 0, }); break; case LinePattern.DASH_DOT: this.linesDefs.set(linePattern, { strokeMiterlimit: 0, strokeDasharray: [10, 2, 1, 2], strokeDashoffset: 0, }); break; default: if (linePatternDef) { this.linesDefs.set(linePattern, linePatternDef); } } } this.linePatternName = linePattern || LinePattern.NONE; return this; } drawLine(startPoint, endPoint, stroke = 1, color = '#000000', options) { this.setMinMax(startPoint); this.setMinMax(endPoint); const svgPolyline = this.renderer.createElement('polyline', ns); if (!this.setClasses(svgPolyline, options)) { this.setSvgElementLinePattern(svgPolyline); svgPolyline.style.strokeWidth = stroke.toString(); svgPolyline.style.stroke = this.color(color); } svgPolyline.points.appendItem(this.createSvgPoint(startPoint)); svgPolyline.points.appendItem(this.createSvgPoint(endPoint)); this.setSvgOptions(svgPolyline, options); this.svgContainer.append(svgPolyline); return this; } drawCircle(point, ray, stroke = 1, strokeColor = '#000000', fillColor = 'none', options) { const svgCircle = this.renderer.createElement('circle', ns); svgCircle.setAttribute('cx', point.x.toString()); svgCircle.setAttribute('cy', point.y.toString()); svgCircle.setAttribute('r', ray.toString()); if (!this.setClasses(svgCircle, options)) { this.setSvgElementLinePattern(svgCircle); svgCircle.style.strokeWidth = stroke.toString(); svgCircle.style.stroke = this.color(strokeColor); svgCircle.style.fill = this.color(fillColor); } this.setSvgOptions(svgCircle, options); this.svgContainer.append(svgCircle); return this; } drawPie(center, rx, ry, start, end, stroke, strokeColor = '#000000', fillColor = 'none', options) { let element; if (Math.abs(start - end) < Math.PI * 2) { let flag = 0; if (Math.abs(start - end) - Math.floor(Math.abs(start - end) / (Math.PI * 2)) > Math.PI) { flag = 1; } element = this.renderer.createElement('path', ns); element.setAttribute('d', 'M ' + (center.x + rx * Math.cos(start) + ',' + (center.y + ry * Math.sin(start)) + ' A ' + rx + ',' + ry + ' 0 ' + flag + ' 1 ' + (center.x + rx * Math.cos(end)) + ',' + (center.y + ry * Math.sin(end)) + ' L ' + center.x + ',' + center.y + 'z')); } else { element = this.renderer.createElement('ellipse', ns); element.setAttribute('cx', center.x.toString()); element.setAttribute('cy', center.y.toString()); element.setAttribute('rx', rx.toString()); element.setAttribute('ry', ry.toString()); } this.setSvgElementLinePattern(element); if (!this.setClasses(element, options)) { element.style.strokeWidth = stroke.toString(); element.style.stroke = this.color(strokeColor); element.style.fill = this.color(fillColor); } this.svgContainer.append(element); return this; } drawArch(center, rx, ry, start, end, stroke, strokeColor = '#000000', options) { let element; const path = DrawingSvgInterface.archPath(center, rx, ry, start, end); if (path) { element = this.renderer.createElement('path', ns); element.setAttribute('d', path); } else { element = this.renderer.createElement('ellipse', ns); element.setAttribute('cx', center.x.toString()); element.setAttribute('cy', center.y.toString()); element.setAttribute('rx', rx.toString()); element.setAttribute('ry', ry.toString()); } if (!this.setClasses(element, options)) { this.setSvgElementLinePattern(element); element.style.strokeWidth = stroke.toString(); element.style.stroke = this.color(strokeColor); element.style.fill = 'none'; } this.svgContainer.append(element); return this; } drawEllipse(point, xRay, yRay, stroke, strokeColor, fillColor, options) { const svgEllipse = this.renderer.createElement('ellipse', ns); svgEllipse.setAttribute('cx', point.x.toString()); svgEllipse.setAttribute('cy', point.y.toString()); svgEllipse.setAttribute('rx', xRay.toString()); svgEllipse.setAttribute('ry', yRay.toString()); if (!this.setClasses(svgEllipse, options)) { this.setSvgElementLinePattern(svgEllipse); svgEllipse.style.strokeWidth = stroke.toString(); svgEllipse.style.stroke = this.color(strokeColor); svgEllipse.style.fill = this.color(fillColor); } this.setSvgOptions(svgEllipse, options); this.svgContainer.append(svgEllipse); return this; } drawRect(x, y, width, height, stroke, strokeColor, fillColor, options) { const svgRect = this.renderer.createElement('rect', ns); svgRect.setAttribute('x', x.toString()); svgRect.setAttribute('y', y.toString()); svgRect.setAttribute('width', width.toString()); svgRect.setAttribute('height', height.toString()); if (!this.setClasses(svgRect, options)) { this.setSvgElementLinePattern(svgRect); svgRect.style.strokeWidth = stroke.toString(); svgRect.style.stroke = this.color(strokeColor); svgRect.style.fill = this.color(fillColor); } this.setSvgOptions(svgRect, options); this.svgContainer.append(svgRect); return this; } get svgContainer() { return ((this.groups.length && this.groups[this.groups.length - 1]) || this.root); } drawText(text, handlePosition, color = '#000000', angle = 0, options) { text = text.replace(/&nbsp;/g, ' '); this.setMinMax(handlePosition); const svgText = this.renderer.createElement('text', ns); svgText.innerHTML = text; svgText.setAttribute('x', handlePosition.x.toString()); svgText.setAttribute('y', handlePosition.y.toString()); svgText.setAttribute('text-anchor', this.textAnchor); svgText.style.fontSize = `${this.fontSize}px`; if (angle) { svgText.style.transform = `translate(${handlePosition.x}px, ${handlePosition.y}px) rotate(${-angle}deg) translate(${-handlePosition.x}px, ${-handlePosition.y}px)`; } if (!this.setClasses(svgText, options)) { svgText.style.fill = this.color(color); } this.setSvgOptions(svgText, options); this.svgContainer.append(svgText); return this; } drawPolyline(points, stroke = 1, color = '#000000', options) { const svgPolyline = this.renderer.createElement('polyline', ns); if (!this.setClasses(svgPolyline, options)) { svgPolyline.style.strokeWidth = stroke.toString(); svgPolyline.style.stroke = this.color(color); svgPolyline.style.fill = 'none'; this.setSvgElementLinePattern(svgPolyline); } points.forEach((point) => { svgPolyline.points.appendItem(this.createSvgPoint(point)); this.setMinMax(point); }); if (points.length && options?.close) { svgPolyline.points.appendItem(this.createSvgPoint(points[0])); } this.setSvgOptions(svgPolyline, options); this.svgContainer.append(svgPolyline); return this; } drawPolygon(points, stroke, color, fill, options) { const polygon = this.renderer.createElement('polygon', ns); if (!this.setClasses(polygon, options)) { polygon.style.strokeWidth = stroke.toString(); polygon.style.stroke = this.color(color); polygon.style.fill = fill.toString(); this.setSvgElementLinePattern(polygon); } points.forEach((point) => { polygon.points.appendItem(this.createSvgPoint(point)); this.setMinMax(point); }); this.setSvgOptions(polygon, options); this.svgContainer.append(polygon); return this; } get width() { if (this.xMaximum !== null && this.xMinimum !== null) { return this.xMaximum - this.xMinimum; } return 1000; } get height() { if (this.yMaximum !== null && this.yMinimum !== null) { return this.yMaximum - this.yMinimum; } return 1000; } getSvg() { return this.svg.innerHTML; } group(options) { const g = this.renderer.createElement('g', ns); const { svgOnly } = options || {}; if (svgOnly) { const { events } = svgOnly; if (events) { keys(events).forEach((event) => { g[`on${event}`] = events[event]; }); } } if (g) { g.onmouseover = () => options?.onMouseOver && options?.onMouseOver(g); g.onmouseleave = () => options?.onMouseOut && options?.onMouseOut(); } if (options?.data) { keys(options.data).forEach((key) => (g.dataset[key] = options?.data?.[key] || '')); } if (options?.clipRect) { if (!this.defs) { this.defs = this.renderer.createElement('defs', ns); if (this.svgContainer.firstChild) { this.svgContainer.insertBefore(this.defs, this.svgContainer.firstChild); } else { this.svgContainer.appendChild(this.defs); } } else if (!this.svgContainer.querySelector('defs')) { this.svgContainer.appendChild(this.defs); } const clipPath = this.renderer.createElement('clipPath', ns); const rect = this.renderer.createElement('rect', ns); rect.setAttribute('x', options.clipRect.pos.x.toString()); rect.setAttribute('y', options.clipRect.pos.y.toString()); rect.setAttribute('width', options.clipRect.width.toString()); rect.setAttribute('height', options.clipRect.height.toString()); const id = 'ClipRect' + Math.floor(Math.random() * 10000000) + '-' + Math.floor(Math.random() * 10000000) + '-' + Math.floor(Math.random() * 10000000); clipPath.setAttribute('id', id); clipPath.appendChild(rect); this.defs.appendChild(clipPath); g.style.clipPath = 'url(#' + id + ')'; } if (options?.name) { g.classList.add(options.name); } this.setSvgOptions(g, options); this.groups.push(g); return this; } endGroup() { if (this.groups.length) { const gr = this.groups.pop(); if (gr) { this.svgContainer.appendChild(gr); } } return this; } getLineStrokeSize(size) { return size || 1; } save(fileName) { const file = new window.Blob([this.getSvg()], { type: 'image/svg+xml' }); const downloadAnchor = document.createElement('a'); const fileURL = URL.createObjectURL(file); downloadAnchor.href = fileURL; downloadAnchor.download = fileName + '.svg'; downloadAnchor.click(); } setClasses(element, options) { const { svgOnly } = options || {}; if (svgOnly) { const { events } = svgOnly; if (events) { keys(events).forEach((key) => { element['on' + key] = events[key]; }); } if (svgOnly?.classes?.length) { svgOnly.classes.forEach((cls) => element.classList.add(cls)); return true; } } return false; } color(color) { return typeof color === 'string' ? color : color.html; } } class DrawingOpenScadInterface extends DrawingContextInterface { openScad = ''; textAlign = TextAlign.LEFT; textBaseline = TextBaseline.ALPHABETIC; fontSize = 10; groups = []; modules = { line: { value: `module line(start, end, thickness = 1) { hull() { translate(start) circle(thickness); translate(end) circle(thickness); } }\n\n`, added: false }, pie: { value: `module pieSlice(r=1.0, a=20) { polygon(points=[ [0, 0], for(theta=0; theta<a; theta=theta+$fa) [r*cos(theta), r*sin(theta)], [r*cos(a), r*sin(a)] ]); }\n\n`, added: false } }; clear() { this.openScad = ''; keys(this.modules).forEach((key) => this.modules[key].added = false); } getTextDimension(text) { return { width: this.getTextWidth(text), height: this.getFontSize() }; } drawLine(startPoint, endPoint, stroke, color, options) { if (!this.modules.line.added) { this.openScad += this.modules.line.value; this.modules.line.added = true; } this.setColor(color); this.openScad += `\nline([${startPoint.x}, ${startPoint.y}], [${endPoint.x}, ${endPoint.y}], thickness=${stroke});\n`; return this; } setColor(color) { const converted = ColorRGB.fromHex(typeof color === 'string' ? color : color.hex); this.openScad += `color([${converted.red / 255}, ${converted.green / 255}, ${converted.blue / 255}])\n`; return this; } drawText(text, handlePosition, color, angle, options) { const mapping = { [TextAlign.CENTER]: 'center', [TextAlign.JUSTIFY]: 'left', [TextAlign.LEFT]: 'left', [TextAlign.RIGHT]: 'right' }; this.setColor(color); this.openScad += ` translate([${handlePosition.x}, ${handlePosition.y}]) scale([-1, 1, 1]) rotate(${angle + 180}) text("${text}", size=${this.getFontSize()}, halign="${mapping[this.textAlign]}"); \n`; return this; } drawPolyline(points, stroke, color, options) { this.setColor(color); this.openScad += '{\n'; points.forEach((point, index) => index > 0 ? this.drawLine(point, points[index - 1], stroke, color) : ''); this.openScad += '\n}'; return this; } drawPolygon(points, stroke, color, fill, options) { this.setColor(fill); this.openScad += `polygon(points=${points.map(point => `[${point.x}, ${point.y}}]`).join(',')};\n`; return this; } drawCircle(point, ray, stroke, strokeColor, fillColor, options) { this.setColor(fillColor); this.openScad += `translate([${point.x}, ${point.y})\n`; this.openScad += `circle(r=${ray});\n`; return this; } drawPie(center, rx, ry, start, end, stroke, strokeColor, fillColor, options) { if (!this.modules.pie.added) { this.openScad += this.modules.pie.value; this.modules.pie.added = true; } this.setColor(fillColor); this.openScad += `translate([${center.x}, ${center.y}]) \n`; this.openScad += `scale([1, ${ry / rx}, 0])\n`; this.openScad += `rotate([0, 0, ${ElementaryMath.radiansToDegrees(start)}]) \n`; this.openScad += `pieSlice(r=${rx}, a=${ElementaryMath.radiansToDegrees(end - start)});\n`; return this; } drawEllipse(point, xRay, yRay, stroke, strokeColor, fillColor, options) { this.setColor(fillColor); this.openScad += `scale([1, ${yRay / xRay}, 0])\n`; this.openScad += `translate([${point.x}, ${point.y}]) \n`; this.openScad += `circle(r=${xRay});\n`; return this; } drawRect(x, y, width, height, stroke, strokeColor, fillColor, options) { this.setColor(fillColor); this.openScad += `translate([${x}, ${y}]) \n`; this.openScad += `square([${width}, ${height}]);\n`; return this; } lineTo(point, stroke, color) { this.drawLine(this.lastPoint, point, stroke, color); this.moveTo(point); return this; } setFontSize(fontSize) { this.fontSize = fontSize; return this; } getFontSize() { return this.fontSize; } setTextAlign(align) { this.textAlign = align; return this; } setTextBaseline(textBaseline) { this.textBaseline = textBaseline; return this; } getTextWidth(text) { return text.length * this.fontSize * 0.8; } setLinePattern(linePattern, linePatternDef) { console.log('WARING: this method can not be implemented for OpenScad format'); return this; } getLineStrokeSize(size) { return 0; } save(fileName) { const file = new window.Blob([this.openScad], { type: 'text/plain' }); const downloadAnchor = document.createElement('a'); const fileURL = URL.createObjectURL(file); downloadAnchor.href = fileURL; downloadAnchor.download = fileName + '.scad'; downloadAnchor.click(); return this; } } class DrawingZwCadCommandInterface extends DrawingContextInterface { data = ''; textAlign = TextAlign.LEFT; fontSize = 10; clear() { this.data = ''; } getTextDimension(text) { return { width: this.getTextWidth(text), height: this.getFontSize() }; } drawLine(startPoint, endPoint, stroke, color, options) { this.data += '_line\r\n' + `${startPoint.x},${startPoint.y}\r\n` + `${endPoint.x},${endPoint.y}\r\n\r\n`; return this; } drawText(text, handlePosition, color, angle, options) { console.warn('cant implement ZwCad command for adding text'); return this; } drawPolyline(points, stroke, color, options) { this.data += 'polyline\r\n' + `${points.map(point => `${point.x},${point.y}`).join('\r\n')}\r\n\r\n`; return this; } drawPolygon(points, stroke, color, fill, options) { if (points.length) { this.data += 'polyline' + points.map(point => `${point.x},${point.y}`).join('\r\n') + `${points[0].x},${points[0].y}\r\n\r\n`; } return this; } drawCircle(point, ray, stroke, strokeColor, fillColor, options) { this.data += '_circle\r\n' + `${point.x},${point.y}\r\n` + `${ray}\r\n`; return this; } drawPie(center, rx, ry, start, end, stroke, strokeColor, fillColor, options) { const startCos = Math.cos(start); const startSin = Math.sin(start); const endCos = Math.cos(end); const endSin = Math.sin(end); const ellipseRay = (cos, sin) => { return Math.sqrt(rx * rx * ry * ry / (rx * rx * cos * cos + ry * ry * sin * sin)); }; const startRay = ellipseRay(startCos, startSin); const endRay = ellipseRay(endCos, endSin); this.data += '_ellipse\r\n' + '_a\r\n' + '_c\r\n' + `${center.x},${center.y}\r\n` + `${center.x + rx},${center.y}\r\n` + `${center.x},${center.y + ry}\r\n` + `${ElementaryMath.radiansToDegrees(start)}\r\n` + `${ElementaryMath.radiansToDegrees(end)}\r\n`; this.drawPolyline([ new Point2D(center.x + startRay * startCos, center.y + startRay * startSin), center, new Point2D(center.x + endRay * endCos, center.y + endRay * endSin), ], stroke, strokeColor); return this; } drawEllipse(point, xRay, yRay, stroke, strokeColor, fillColor, options) { this.data += '_ellipse\r\n' + '_c\r\n' + `${point.x},${point.y}\r\n` + `${point.x + xRay},${point.y}\r\n` + `${point.x},${point.y + yRay}\r\n`; return this; } drawRect(x, y, width, height, stroke, strokeColor, fillColor, options) { this.data += '_rectangle\r\n' + `${x}${y}\r\n` + `${x + width}${y + height}\r\n\r\n`; return this; } lineTo(point, stroke, color) { this.drawLine(this.lastPoint, point, stroke, color); this.lastPoint = point.copy(); return this; } setFontSize(fontSize) { this.fontSize = fontSize; return this; } getFontSize() { return this.fontSize; } setTextAlign(align) { this.textAlign = align; return this; } setTextBaseline(textBaseline) { return this; } getTextWidth(text) { return text.length * this.fontSize * 0.8; } setLinePattern(linePattern, linePatternDef) { console.log('WARING: this method can not be implemented for ZwCad command format'); return this; } getLineStrokeSize(size) { return 0; } save(fileName) { const file = new window.Blob([this.data], { type: 'text/plain' }); const downloadAnchor = document.createElement('a'); const fileURL = URL.createObjectURL(file); downloadAnchor.href = fileURL; downloadAnchor.download = fileName + '.txt'; downloadAnchor.click(); return this; } } class Rect { x; y; width; height; constructor(firstPoint, secondPoint, dx = 0, dy = 0) { this.x = Math.min(firstPoint.x, secondPoint.x) - dx; this.y = Math.min(firstPoint.y, secondPoint.y) - dy; this.width = Math.abs(firstPoint.x - secondPoint.x) + dx * 2; this.height = Math.abs(firstPoint.y - secondPoint.y) + dy * 2; } isPtInRect(point) { return this.x <= point.x && this.x + this.width >= point.x && this.y <= point.y && this.y + this.height >= point.y; } add(rect) { return new Rect(new Point2D(Math.min(this.x, rect.x), Math.min(this.y, rect.y)), new Point2D(Math.max(this.x + this.width, rect.x + rect.width), Math.max(this.y + this.height, rect.y + rect.height))); } addOffset(left, right, top, bottom) { this.x -= left; this.y -= top; this.width += left + right; this.height += top + bottom; } fillRect(ctx) { ctx.fillRect(this.x, this.y, this.width, this.height); } strokeRect(ctx) { ctx.strokeRect(this.x, this.y, this.width, this.height); } } /** * Generated bundle index. Do not edit. */ export { DrawingCanvasInterface, DrawingContextInterface, DrawingOpenScadInterface, DrawingSvgInterface, DrawingZwCadCommandInterface, LinePattern, Rect, TextAlign, TextBaseline }; //# sourceMappingURL=obliczeniowo-elementary-drawing.mjs.map