UNPKG

@awayjs/graphics

Version:
429 lines (428 loc) 20.9 kB
import { Point, MathConsts, Matrix } from '@awayjs/core'; import { ImageSampler, AttributesView, Float2Attributes } from '@awayjs/stage'; import { Style, TriangleElements } from '@awayjs/renderer'; import { Shape } from '../renderables/Shape'; import { CapsStyle } from '../draw/CapsStyle'; import { MaterialManager } from '../managers/MaterialManager'; import { Settings } from '../Settings'; var GraphicsFactoryHelper = /** @class */ (function () { function GraphicsFactoryHelper() { } GraphicsFactoryHelper.drawRectangles = function (inputRectangles, color, alpha) { if (inputRectangles.length % 4 > 0) { console.log('GraphicsFactoryHelper.drawRectangles: inputRectangles.length is not a multiple of 4', inputRectangles); return; } var final_vert_list = []; final_vert_list.length = (inputRectangles.length / 4) * 12; var i = 0; var outCnt = 0; var len = inputRectangles.length; var x = 0; var y = 0; var w = 0; var h = 0; for (i = 0; i < len; i += 4) { x = inputRectangles[i]; y = inputRectangles[i + 1]; w = inputRectangles[i + 2]; h = inputRectangles[i + 3]; final_vert_list[outCnt++] = x; final_vert_list[outCnt++] = y; final_vert_list[outCnt++] = x + w; final_vert_list[outCnt++] = y; final_vert_list[outCnt++] = x + w; final_vert_list[outCnt++] = y + h; final_vert_list[outCnt++] = x; final_vert_list[outCnt++] = y; final_vert_list[outCnt++] = x; final_vert_list[outCnt++] = y + h; final_vert_list[outCnt++] = x + w; final_vert_list[outCnt++] = y + h; } var obj = MaterialManager.getMaterialForColor(color, alpha); var material = obj.material; var attributesView = new AttributesView(Float32Array, material.curves ? 3 : 2); attributesView.set(final_vert_list); var attributesBuffer = attributesView.attributesBuffer.cloneBufferView(); attributesView.dispose(); var elements = new TriangleElements(attributesBuffer); elements.setPositions(new Float2Attributes(attributesBuffer)); var shape = Shape.getShape(elements, material); if (obj.colorPos) { shape.style = new Style(); var sampler = new ImageSampler(); material.animateUVs = true; shape.style.color = color; shape.style.addSamplerAt(sampler, material.getTextureAt(0)); shape.style.uvMatrix = new Matrix(0, 0, 0, 0, obj.colorPos.x, obj.colorPos.y); } return shape; }; GraphicsFactoryHelper.updateRectanglesShape = function (shape, inputRectangles) { var final_vert_list = []; final_vert_list.length = (inputRectangles.length / 4) * 12; var i = 0; var outCnt = 0; var len = inputRectangles.length; var x = 0; var y = 0; var w = 0; var h = 0; for (i = 0; i < len; i += 4) { x = inputRectangles[i]; y = inputRectangles[i + 1]; w = inputRectangles[i + 2]; h = inputRectangles[i + 3]; final_vert_list[outCnt++] = x; final_vert_list[outCnt++] = y; final_vert_list[outCnt++] = x + w; final_vert_list[outCnt++] = y; final_vert_list[outCnt++] = x + w; final_vert_list[outCnt++] = y + h; final_vert_list[outCnt++] = x; final_vert_list[outCnt++] = y; final_vert_list[outCnt++] = x; final_vert_list[outCnt++] = y + h; final_vert_list[outCnt++] = x + w; final_vert_list[outCnt++] = y + h; } var elements = shape.elements; elements.concatenatedBuffer.count = final_vert_list.length / 2; elements.setPositions(final_vert_list); elements.invalidate(); }; GraphicsFactoryHelper.isClockWiseXY = function (point1x, point1y, point2x, point2y, point3x, point3y) { var num = (point1x - point2x) * (point3y - point2y) - (point1y - point2y) * (point3x - point2x); if (num < 0) return false; return true; }; GraphicsFactoryHelper.getSign = function (ax, ay, cx, cy, bx, by) { return (ax - bx) * (cy - by) - (ay - by) * (cx - bx); }; GraphicsFactoryHelper.pointInTri = function (ax, ay, bx, by, cx, cy, xx, xy) { var b1 = this.getSign(ax, ay, xx, xy, bx, by) > 0; var b2 = this.getSign(bx, by, xx, xy, cx, cy) > 0; var b3 = this.getSign(cx, cy, xx, xy, ax, ay) > 0; return ((b1 == b2) && (b2 == b3)); }; GraphicsFactoryHelper.getControlXForCurveX = function (_a, c, _b) { return c; }; GraphicsFactoryHelper.getControlYForCurveY = function (_a, c, _b) { return c; }; GraphicsFactoryHelper.drawPoint = function (startX, startY, vertices, curves) { this.addTriangle(startX - 2, startY - 2, startX + 2, startY - 2, startX + 2, startY + 2, 0, vertices, curves); this.addTriangle(startX - 2, startY - 2, startX - 2, startY + 2, startX + 2, startY + 2, 0, vertices, curves); }; GraphicsFactoryHelper.drawElipse = function (x, y, width, height, vertices, startAngle, endAngle, stepAngle, curves) { // todo: validate input / check edge cases var degreeTotal = endAngle - startAngle; var steps = degreeTotal / stepAngle; var x_last = x + width * Math.cos(startAngle * (Math.PI / 180)); var y_last = y + height * Math.sin(startAngle * (Math.PI / 180)); for (var i = 1; i <= steps; i++) { var x_tmp = x + width * Math.cos((startAngle + i * stepAngle) * (Math.PI / 180)); var y_tmp = y + height * Math.sin((startAngle + i * stepAngle) * (Math.PI / 180)); this.addTriangle(x, y, x_tmp, y_tmp, x_last, y_last, 0, vertices, curves); x_last = x_tmp; y_last = y_tmp; } }; GraphicsFactoryHelper.drawElipseStrokes = function (x, y, width, height, strokePath, startAngle, endAngle, stepAngle) { // todo: validate input / check edge cases var degreeTotal = endAngle - startAngle; var steps = degreeTotal / stepAngle; var x_last = x + (width) * Math.cos(startAngle * (Math.PI / 180)); var y_last = y + (height) * Math.sin(startAngle * (Math.PI / 180)); strokePath.moveTo(x_last, y_last); for (var i = 1; i <= steps; i++) { var x_tmp = x + (width) * Math.cos((startAngle + i * stepAngle) * (Math.PI / 180)); var y_tmp = y + (height) * Math.sin((startAngle + i * stepAngle) * (Math.PI / 180)); strokePath.lineTo(x_tmp, y_tmp); } }; GraphicsFactoryHelper.addTriangle = function (startX, startY, controlX, controlY, endX, endY, tri_type, vertices, curves) { var x1 = startX; var y1 = startY; var x2 = controlX; var y2 = controlY; var x3 = endX; var y3 = endY; if (GraphicsFactoryHelper.isClockWiseXY(x1, y1, x2, y2, x3, y3)) { startX = x3; startY = y3; controlX = x2; controlY = y2; endX = x1; endY = y1; } var final_vert_cnt = vertices.length; if (tri_type == 0) { vertices[final_vert_cnt++] = startX; vertices[final_vert_cnt++] = startY; if (curves) vertices[final_vert_cnt++] = 4.5736980577097704e-41; // ((127<<24)+(127<<16)+0+0) vertices[final_vert_cnt++] = controlX; vertices[final_vert_cnt++] = controlY; if (curves) vertices[final_vert_cnt++] = 4.5736980577097704e-41; // ((127<<24)+(127<<16)+0+0) vertices[final_vert_cnt++] = endX; vertices[final_vert_cnt++] = endY; if (curves) vertices[final_vert_cnt++] = 4.5736980577097704e-41; // ((127<<24)+(127<<16)+0+0) } else if (tri_type < 0) { vertices[final_vert_cnt++] = startX; vertices[final_vert_cnt++] = startY; if (curves) vertices[final_vert_cnt++] = 1.1708844992641982e-38; // ((127<<24)+(127<<16)+0+0) vertices[final_vert_cnt++] = controlX; vertices[final_vert_cnt++] = controlY; if (curves) vertices[final_vert_cnt++] = 2.2778106537599901e-41; // ((127<<24)+(63<<16)+0+0) vertices[final_vert_cnt++] = endX; vertices[final_vert_cnt++] = endY; if (curves) vertices[final_vert_cnt++] = 1.7796490496925177e-43; // ((127<<24)+0+0+0) } else if (tri_type > 0) { vertices[final_vert_cnt++] = startX; vertices[final_vert_cnt++] = startY; if (curves) vertices[final_vert_cnt++] = 1.1708846393940446e-38; // ((-128<<24)+(127<<16)+0+0) vertices[final_vert_cnt++] = controlX; vertices[final_vert_cnt++] = controlY; if (curves) vertices[final_vert_cnt++] = 2.2779507836064226e-41; // ((-128<<24)+(63<<16)+0+0) vertices[final_vert_cnt++] = endX; vertices[final_vert_cnt++] = endY; if (curves) vertices[final_vert_cnt++] = 1.793662034335766e-43; // ((-128<<24)+0+0+0) } }; GraphicsFactoryHelper.createCap = function (startX, startY, start_le_x, start_le_y, start_ri_x, start_ri_y, direction_x, direction_y, capstyle, cap_position, thicknessX, thicknessY, vertices, curves) { direction_x *= cap_position; direction_y *= cap_position; if (capstyle == CapsStyle.ROUND) { //console.log("add round cap"); var end_x = startX + ((direction_x * thicknessX)); var end_y = startY + ((direction_y * thicknessY)); //end_x = end_x * 2 - start_le.x/2 - start_ri.x/2; //end_y = end_y * 2 - start_le.y/2 - start_ri.y/2; var tmp1_x = start_le_x + ((direction_x * thicknessX)); var tmp1_y = start_le_y + ((direction_y * thicknessY)); var tmp2_x = start_ri_x + ((direction_x * thicknessX)); var tmp2_y = start_ri_y + ((direction_y * thicknessY)); this.tesselateCurve(start_le_x, start_le_y, tmp1_x, tmp1_y, end_x, end_y, vertices, true); this.tesselateCurve(end_x, end_y, tmp2_x, tmp2_y, start_ri_x, start_ri_y, vertices, true); this.addTriangle(start_le_x, start_le_y, end_x, end_y, start_ri_x, start_ri_y, -1, vertices, curves); } else if (capstyle == CapsStyle.SQUARE) { //console.log("add square cap"); var tmp1_x = start_le_x + ((direction_x * thicknessX)); var tmp1_y = start_le_y + ((direction_y * thicknessY)); var tmp2_x = start_ri_x + ((direction_x * thicknessX)); var tmp2_y = start_ri_y + ((direction_y * thicknessY)); this.addTriangle(tmp2_x, tmp2_y, tmp1_x, tmp1_y, start_le_x, start_le_y, 0, vertices, curves); this.addTriangle(tmp2_x, tmp2_y, start_le_x, start_le_y, start_ri_x, start_ri_y, 0, vertices, curves); } }; GraphicsFactoryHelper.getLineFormularData = function (a, b) { var tmp_x = b.x - a.x; var tmp_y = b.y - a.y; var return_point = new Point(); if ((tmp_x != 0) && (tmp_y != 0)) return_point.x = tmp_y / tmp_x; return_point.y = -(return_point.x * a.x - a.y); return return_point; }; GraphicsFactoryHelper.getQuadricBezierPosition = function (t, start, control, end) { var xt = 1 - t; return xt * xt * start + 2 * xt * t * control + t * t * end; }; GraphicsFactoryHelper.subdivideCurve = function (startx, starty, cx, cy, endx, endy, startx2, starty2, cx2, cy2, endx2, endy2, array_out, array2_out) { var angle_1 = Math.atan2(cy - starty, cx - startx) * MathConsts.RADIANS_TO_DEGREES; var angle_2 = Math.atan2(endy - cy, endx - cx) * MathConsts.RADIANS_TO_DEGREES; var angle_delta = angle_2 - angle_1; //console.log("angle_delta "+angle_delta); if (angle_delta > 180) { angle_delta -= 360; } if (angle_delta < -180) { angle_delta += 360; } if (Math.abs(angle_delta) >= 175) { array_out.push(startx, starty, cx, cy, endx, endy); array2_out.push(startx2, starty2, cx2, cy2, endx2, endy2); return; } var b1 = false; //let b2: boolean = false; if (angle_delta < 0) { // curve is curved to right side. right side is convex b1 = GraphicsFactoryHelper.getSign(startx, starty, cx2, cy2, endx, endy) > 0; //b2 = GraphicsFactoryHelper.getSign(startx, starty, cx, cy, endx, endy) > 0; // eslint-disable-next-line max-len b1 = (((starty - endy) * (cx - startx) + (endx - startx) * (cy - starty)) * ((starty - endy) * (cx2 - startx) + (endx - startx) * (cy2 - starty))) < 0; } else { // curve is curved to left side. left side is convex b1 = GraphicsFactoryHelper.getSign(startx2, starty2, cx2, cy2, endx2, endy2) > 0; //b2 = GraphicsFactoryHelper.getSign(startx2, starty2, cx, cy, endx2, endy2) > 0; // eslint-disable-next-line max-len b1 = (((starty2 - endy) * (cx - startx2) + (endx2 - startx2) * (cy - starty2)) * ((starty2 - endy2) * (cx2 - startx2) + (endx2 - startx2) * (cy2 - starty2))) < 0; } if (b1) { array_out.push(startx, starty, cx, cy, endx, endy); array2_out.push(startx2, starty2, cx2, cy2, endx2, endy2); return; } // triangles overlap. we must subdivide: var c1x = startx + (cx - startx) * 0.5; // new controlpoint 1.1 var c1y = starty + (cy - starty) * 0.5; var c2x = cx + (endx - cx) * 0.5; // new controlpoint 1.2 var c2y = cy + (endy - cy) * 0.5; var ax = c1x + (c2x - c1x) * 0.5; // new middlepoint 1 var ay = c1y + (c2y - c1y) * 0.5; var c1x2 = startx2 + (cx2 - startx2) * 0.5; // new controlpoint 2.1 var c1y2 = starty2 + (cy2 - starty2) * 0.5; var c2x2 = cx2 + (endx2 - cx2) * 0.5; // new controlpoint 2.2 var c2y2 = cy2 + (endy2 - cy2) * 0.5; var ax2 = c1x2 + (c2x2 - c1x2) * 0.5; // new middlepoint 2 var ay2 = c1y2 + (c2y2 - c1y2) * 0.5; this.subdivideCurve(startx, starty, c1x, c1y, ax, ay, startx2, starty2, c1x2, c1y2, ax2, ay2, array_out, array2_out); this.subdivideCurve(ax, ay, c2x, c2y, endx, endy, ax2, ay2, c2x2, c2y2, endx2, endy2, array_out, array2_out); }; GraphicsFactoryHelper.tesselateCurve = function (startx, starty, cx, cy, endx, endy, array_out, filled, iterationCnt, qualityScale) { if (filled === void 0) { filled = false; } if (iterationCnt === void 0) { iterationCnt = 0; } if (qualityScale === void 0) { qualityScale = 1; } var maxIterations = Settings.CURVE_TESSELATION_COUNT; var minAngle = 1 / Math.sqrt(qualityScale); var minLengthSqr = 1 / qualityScale; // subdivide the curve var c1x = (startx + cx) * 0.5; // new controlpoint 1 var c1y = (starty + cy) * 0.5; var c2x = (cx + endx) * 0.5; // new controlpoint 2 var c2y = (cy + endy) * 0.5; var ax = (c1x + c2x) * 0.5; // new middlepoint 1 var ay = (c1y + c2y) * 0.5; // if "filled" is true, we are collecting final vert positions in the array, // ready to use for rendering. (6-position values for each tri) // if "filled" is false, we are collecting vert positions for a path (we do not need the start-position). // stop tesselation on maxIteration level. Set it to 0 for no tesselation at all. if (iterationCnt >= maxIterations) { if (filled) { array_out.push(startx, starty, ax, ay, endx, endy); return; } array_out.push(ax, ay, endx, endy); return; } // calculate length of segment // this does not include the crtl-point position var diff_x = endx - startx; var diff_y = endy - starty; var lenSq = diff_x * diff_x + diff_y * diff_y; // stop subdividing if the angle or the length is to small if (lenSq < minLengthSqr) { if (filled) { array_out.push(startx, starty, ax, ay, endx, endy); } else { array_out.push(endx, endy); } return; } // calculate angle between segments var angle_1 = Math.atan2(cy - starty, cx - startx) * MathConsts.RADIANS_TO_DEGREES; var angle_2 = Math.atan2(endy - cy, endx - cx) * MathConsts.RADIANS_TO_DEGREES; var angle_delta = angle_2 - angle_1; // make sure angle is in range -180 - 180 while (angle_delta > 180) { angle_delta -= 360; } while (angle_delta < -180) { angle_delta += 360; } angle_delta = angle_delta < 0 ? -angle_delta : angle_delta; // stop subdividing if the angle or the length is to small if (angle_delta <= minAngle) { if (filled) { array_out.push(startx, starty, ax, ay, endx, endy); } else { array_out.push(endx, endy); } return; } // if the output should be directly in valid tris, we always must create a tri, // even when we will keep on subdividing. if (filled) { array_out.push(startx, starty, ax, ay, endx, endy); } iterationCnt++; GraphicsFactoryHelper.tesselateCurve(startx, starty, c1x, c1y, ax, ay, array_out, filled, iterationCnt, qualityScale); GraphicsFactoryHelper.tesselateCurve(ax, ay, c2x, c2y, endx, endy, array_out, filled, iterationCnt, qualityScale); }; GraphicsFactoryHelper.tesselateCubicCurve = function (startx, starty, cx, cy, cx2, cy2, endx, endy, array_out, iterationCnt, qualityScale) { if (iterationCnt === void 0) { iterationCnt = 0; } if (qualityScale === void 0) { qualityScale = 1; } var maxIterations = Settings.CURVE_TESSELATION_COUNT; var minAngle = 1 / Math.sqrt(qualityScale); var minLengthSqr = 1 / qualityScale; // calculate length of segment // this does not include the crtl-point positions var diff_x = endx - startx; var diff_y = endy - starty; var lenSq = diff_x * diff_x + diff_y * diff_y; // stop subdividing if the angle or the length is to small if (lenSq < minLengthSqr) { array_out.push(endx, endy); return; } // subdivide the curve var c1x = (startx + cx) * 0.5; // new controlpoint 1 var c1y = (starty + cy) * 0.5; var c2x = (cx + cx2) * 0.5; // new controlpoint 2 var c2y = (cy + cy2) * 0.5; var c3x = (cx2 + endx) * 0.5; // new controlpoint 3 var c3y = (cy2 + endy) * 0.5; var d1x = (c1x + c2x) * 0.5; // new controlpoint 1 var d1y = (c1y + c2y) * 0.5; var d2x = (c2x + c3x) * 0.5; // new controlpoint 2 var d2y = (c2y + c3y) * 0.5; var ax = (d1x + d2x) * 0.5; // new middlepoint 1 var ay = (d1y + d2y) * 0.5; // stop tesselation on maxIteration level. Set it to 0 for no tesselation at all. if (iterationCnt >= maxIterations) { array_out.push(ax, ay, endx, endy); return; } // calculate angle between segments var angle_1 = Math.atan2(cy - starty, cx - startx) * MathConsts.RADIANS_TO_DEGREES; var angle_2 = Math.atan2(endy - cy, endx - cx) * MathConsts.RADIANS_TO_DEGREES; var angle_delta = angle_2 - angle_1; // make sure angle is in range -180 - 180 while (angle_delta > 180) { angle_delta -= 360; } while (angle_delta < -180) { angle_delta += 360; } angle_delta = angle_delta < 0 ? -angle_delta : angle_delta; // stop subdividing if the angle or the length is to small if (angle_delta <= minAngle) { array_out.push(endx, endy); return; } iterationCnt++; GraphicsFactoryHelper.tesselateCubicCurve(startx, starty, c1x, c1y, d1x, d1y, ax, ay, array_out, iterationCnt, qualityScale); GraphicsFactoryHelper.tesselateCubicCurve(ax, ay, d2x, d2y, c3x, c3y, endx, endy, array_out, iterationCnt, qualityScale); }; return GraphicsFactoryHelper; }()); export { GraphicsFactoryHelper };