mirror of
https://github.com/Doodle3D/Doodle3D-Core.git
synced 2024-09-29 01:08:38 +02:00
131 lines
3.8 KiB
JavaScript
131 lines
3.8 KiB
JavaScript
import { shapeToPoints } from '../shape/shapeToPoints.js';
|
|
import { shapeChanged } from '../shape/shapeDataUtils.js';
|
|
import { Matrix } from 'cal';
|
|
import { LINE_WIDTH } from '../constants/d2Constants.js';
|
|
import { hexToStyle } from '../utils/colorUtils.js';
|
|
import { DESELECT_TRANSPARENCY, FILL_TRANSPARENCY, LINE_TRANSPARENCY } from '../constants/d2Constants.js';
|
|
import { PIXEL_RATIO } from '../constants/general.js';
|
|
import holePaternUrl from '../../img/holepatern.png';
|
|
import { loadImage } from '../utils/imageUtils.js';
|
|
|
|
let holePatern;
|
|
export const load = loadImage(holePaternUrl).then(image => {
|
|
holePatern = document.createElement('canvas').getContext('2d').createPattern(image, 'repeat');
|
|
});
|
|
|
|
export default class Shape extends Matrix {
|
|
constructor(shapeData) {
|
|
super();
|
|
|
|
this.visible = true;
|
|
this.active = false;
|
|
this.depth = shapeData.height + shapeData.z;
|
|
|
|
this.color = '';
|
|
|
|
this.UID = shapeData.UID;
|
|
|
|
this.update(shapeData);
|
|
this.setOpaque(true);
|
|
}
|
|
update(shapeData) {
|
|
let changed = false;
|
|
|
|
this.depth = shapeData.height + shapeData.z;
|
|
|
|
if (!this._shapeData || shapeChanged(shapeData, this._shapeData)) {
|
|
this.shapes = shapeToPoints(shapeData);
|
|
|
|
changed = true;
|
|
}
|
|
|
|
if (!this._shapeData || this._shapeData.transform !== shapeData.transform) {
|
|
if (shapeData.transform.matrix.some(value => typeof value !== 'number' || isNaN(value))) {
|
|
throw new Error(`Cannot update object ${this.UID}: transform contains invalid values.`);
|
|
}
|
|
|
|
this.copyMatrix(shapeData.transform);
|
|
|
|
changed = true;
|
|
}
|
|
|
|
if (!this._shapeData || this._shapeData.color !== shapeData.color) {
|
|
if (typeof shapeData.color !== 'number' || isNaN(shapeData.color)) {
|
|
throw new Error(`Cannot update object ${this.UID}: color is an invalid value.`);
|
|
}
|
|
this.color = hexToStyle(shapeData.color);
|
|
changed = true;
|
|
}
|
|
|
|
if (!this._shapeData || this._shapeData.solid !== shapeData.solid) {
|
|
changed = true;
|
|
}
|
|
|
|
this._shapeData = shapeData;
|
|
return changed;
|
|
}
|
|
setOpaque(opaque) {
|
|
const selectTransparency = this._shapeData.fill ? FILL_TRANSPARENCY : LINE_TRANSPARENCY;
|
|
this.alpha = opaque ? selectTransparency : DESELECT_TRANSPARENCY;
|
|
}
|
|
draw(context, matrix) {
|
|
context.beginPath();
|
|
|
|
for (let i = 0; i < this.shapes.length; i ++) {
|
|
const { points, holes } = this.shapes[i];
|
|
|
|
for (let j = 0; j < points.length; j ++) {
|
|
const point = points[j].applyMatrix(matrix);
|
|
|
|
if (j === 0) {
|
|
context.moveTo(point.x, point.y);
|
|
} else {
|
|
context.lineTo(point.x, point.y);
|
|
}
|
|
}
|
|
|
|
for (let j = 0; j < holes.length; j ++) {
|
|
const hole = holes[j];
|
|
|
|
for (let k = 0; k < hole.length; k ++) {
|
|
const point = hole[k].applyMatrix(matrix);
|
|
|
|
if (k === 0) {
|
|
context.moveTo(point.x, point.y);
|
|
} else {
|
|
context.lineTo(point.x, point.y);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
context.lineCap = 'round';
|
|
context.lineJoin = 'round';
|
|
|
|
const lineWidth = PIXEL_RATIO * LINE_WIDTH;
|
|
|
|
context.globalAlpha = this.alpha;
|
|
if (this._shapeData.fill) {
|
|
context.fillStyle = this._shapeData.solid ? this.color : holePatern;
|
|
context.fill();
|
|
|
|
context.strokeStyle = 'black';
|
|
context.lineWidth = lineWidth / 2.0;
|
|
context.stroke();
|
|
} else {
|
|
const outerLineWidth = lineWidth * this.parent.sx;
|
|
const innerLineWidth = outerLineWidth - lineWidth;
|
|
|
|
context.strokeStyle = 'black';
|
|
context.lineWidth = outerLineWidth;
|
|
context.stroke();
|
|
|
|
if (innerLineWidth > 0) {
|
|
context.strokeStyle = this._shapeData.solid ? this.color : holePatern;
|
|
context.lineWidth = innerLineWidth;
|
|
context.stroke();
|
|
}
|
|
}
|
|
}
|
|
}
|