@obliczeniowo/elementary
Version:
Library made in Angular version 20
1,100 lines (1,091 loc) • 39.4 kB
JavaScript
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(/ /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