@awayjs/graphics
Version:
AwayJS graphics classes
429 lines (428 loc) • 20.9 kB
JavaScript
import { Point, MathConsts } from '@awayjs/core';
import { 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';
import { SolidFillStyle } from './fills/SolidFillStyle';
import { TextureAtlas } from '../managers/TextureAtlas';
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 solid = new SolidFillStyle(color, alpha);
var material = MaterialManager.getMaterialForColor(solid);
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);
shape.material = material;
if (material.getNumTextures()) {
shape.style = new Style();
shape.style.image = TextureAtlas.getTextureForColor(solid);
shape.style.uvMatrix = solid.getUVMatrix();
}
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 };