diff --git a/packages/canvas/canvas-graphics/src/CanvasGraphicsRenderer.js b/packages/canvas/canvas-graphics/src/CanvasGraphicsRenderer.js index 33c7bce..fde693f 100644 --- a/packages/canvas/canvas-graphics/src/CanvasGraphicsRenderer.js +++ b/packages/canvas/canvas-graphics/src/CanvasGraphicsRenderer.js @@ -78,11 +78,65 @@ { context.beginPath(); - this.renderPolygon(shape.points, shape.closed, context); + let points = shape.points; + const holes = data.holes; + let outerArea; + let innerArea; - for (let j = 0; j < data.holes.length; j++) + context.moveTo(points[0], points[1]); + + for (let j = 2; j < points.length; j += 2) { - // this.renderPolygon(data.holes[j].points, true, context); + context.lineTo(points[j], points[j + 1]); + } + + if (shape.closeStroke) + { + context.closePath(); + } + + if (holes.length > 0) + { + outerArea = 0; + for (let j = 0; j < points.length; j += 2) + { + outerArea += (points[j] * points[j + 3]) - (points[j + 1] * points[j + 2]); + } + + for (let k = 0; k < holes.length; k++) + { + points = holes[k].points; + + innerArea = 0; + for (let j = 0; j < points.length; j += 2) + { + innerArea += (points[j] * points[j + 3]) - (points[j + 1] * points[j + 2]); + } + + if (innerArea * outerArea < 0) + { + context.moveTo(points[0], points[1]); + + for (let j = 2; j < points.length; j += 2) + { + context.lineTo(points[j], points[j + 1]); + } + } + else + { + context.moveTo(points[points.length - 2], points[points.length - 1]); + + for (let j = points.length - 4; j >= 0; j -= 2) + { + context.lineTo(points[j], points[j + 1]); + } + } + + if (holes[k].shape.closeStroke) + { + context.closePath(); + } + } } if (fillStyle.visible) @@ -255,28 +309,6 @@ } /** - * Renders a polygon. - * - * @param {PIXI.Point[]} points - The points to render - * @param {boolean} close - Should the polygon be closed - * @param {CanvasRenderingContext2D} context - The rendering context to use - */ - renderPolygon(points, close, context) - { - context.moveTo(points[0], points[1]); - - for (let j = 1; j < points.length / 2; ++j) - { - context.lineTo(points[j * 2], points[(j * 2) + 1]); - } - - if (close) - { - context.closePath(); - } - } - - /** * destroy graphics object * */ diff --git a/packages/canvas/canvas-graphics/src/CanvasGraphicsRenderer.js b/packages/canvas/canvas-graphics/src/CanvasGraphicsRenderer.js index 33c7bce..fde693f 100644 --- a/packages/canvas/canvas-graphics/src/CanvasGraphicsRenderer.js +++ b/packages/canvas/canvas-graphics/src/CanvasGraphicsRenderer.js @@ -78,11 +78,65 @@ { context.beginPath(); - this.renderPolygon(shape.points, shape.closed, context); + let points = shape.points; + const holes = data.holes; + let outerArea; + let innerArea; - for (let j = 0; j < data.holes.length; j++) + context.moveTo(points[0], points[1]); + + for (let j = 2; j < points.length; j += 2) { - // this.renderPolygon(data.holes[j].points, true, context); + context.lineTo(points[j], points[j + 1]); + } + + if (shape.closeStroke) + { + context.closePath(); + } + + if (holes.length > 0) + { + outerArea = 0; + for (let j = 0; j < points.length; j += 2) + { + outerArea += (points[j] * points[j + 3]) - (points[j + 1] * points[j + 2]); + } + + for (let k = 0; k < holes.length; k++) + { + points = holes[k].points; + + innerArea = 0; + for (let j = 0; j < points.length; j += 2) + { + innerArea += (points[j] * points[j + 3]) - (points[j + 1] * points[j + 2]); + } + + if (innerArea * outerArea < 0) + { + context.moveTo(points[0], points[1]); + + for (let j = 2; j < points.length; j += 2) + { + context.lineTo(points[j], points[j + 1]); + } + } + else + { + context.moveTo(points[points.length - 2], points[points.length - 1]); + + for (let j = points.length - 4; j >= 0; j -= 2) + { + context.lineTo(points[j], points[j + 1]); + } + } + + if (holes[k].shape.closeStroke) + { + context.closePath(); + } + } } if (fillStyle.visible) @@ -255,28 +309,6 @@ } /** - * Renders a polygon. - * - * @param {PIXI.Point[]} points - The points to render - * @param {boolean} close - Should the polygon be closed - * @param {CanvasRenderingContext2D} context - The rendering context to use - */ - renderPolygon(points, close, context) - { - context.moveTo(points[0], points[1]); - - for (let j = 1; j < points.length / 2; ++j) - { - context.lineTo(points[j * 2], points[(j * 2) + 1]); - } - - if (close) - { - context.closePath(); - } - } - - /** * destroy graphics object * */ diff --git a/packages/graphics/src/Graphics.js b/packages/graphics/src/Graphics.js index c987b63..6e26b9f 100644 --- a/packages/graphics/src/Graphics.js +++ b/packages/graphics/src/Graphics.js @@ -309,14 +309,14 @@ { this.drawShape(this.currentPath); this.currentPath = new Polygon(); - this.currentPath.closed = false; + this.currentPath.closeStroke = false; this.currentPath.points.push(points[len - 2], points[len - 1]); } } else { this.currentPath = new Polygon(); - this.currentPath.closed = false; + this.currentPath.closeStroke = false; } } @@ -680,12 +680,12 @@ // see section 3.1: https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#3-managing-arguments let points = path; - let closed = true;// !!this._fillStyle; + let closeStroke = true;// !!this._fillStyle; // check if data has points.. if (points.points) { - closed = points.closed; + closeStroke = points.closeStroke; points = points.points; } @@ -703,7 +703,7 @@ const shape = new Polygon(points); - shape.closed = closed; + shape.closeStroke = closeStroke; this.drawShape(shape); @@ -1044,12 +1044,12 @@ */ closePath() { - // ok so close path assumes next one is a hole! const currentPath = this.currentPath; - if (currentPath && currentPath.close) + if (currentPath) { - currentPath.close(); + // we don't need to add extra point in the end because buildLine will take care of that + currentPath.closeStroke = true; } return this; @@ -1072,6 +1072,8 @@ * Begin adding holes to the last draw shape * IMPORTANT: holes must be fully inside a shape to work * Also weirdness ensues if holes overlap! + * Ellipses, Circles, Rectangles and Rounded Rectangles cannot be holes or host for holes in CanvasRenderer, + * please use `moveTo` `lineTo`, `quadraticCurveTo` if you rely on pixi-legacy bundle. * @return {PIXI.Graphics} Returns itself. */ beginHole() diff --git a/packages/canvas/canvas-graphics/src/CanvasGraphicsRenderer.js b/packages/canvas/canvas-graphics/src/CanvasGraphicsRenderer.js index 33c7bce..fde693f 100644 --- a/packages/canvas/canvas-graphics/src/CanvasGraphicsRenderer.js +++ b/packages/canvas/canvas-graphics/src/CanvasGraphicsRenderer.js @@ -78,11 +78,65 @@ { context.beginPath(); - this.renderPolygon(shape.points, shape.closed, context); + let points = shape.points; + const holes = data.holes; + let outerArea; + let innerArea; - for (let j = 0; j < data.holes.length; j++) + context.moveTo(points[0], points[1]); + + for (let j = 2; j < points.length; j += 2) { - // this.renderPolygon(data.holes[j].points, true, context); + context.lineTo(points[j], points[j + 1]); + } + + if (shape.closeStroke) + { + context.closePath(); + } + + if (holes.length > 0) + { + outerArea = 0; + for (let j = 0; j < points.length; j += 2) + { + outerArea += (points[j] * points[j + 3]) - (points[j + 1] * points[j + 2]); + } + + for (let k = 0; k < holes.length; k++) + { + points = holes[k].points; + + innerArea = 0; + for (let j = 0; j < points.length; j += 2) + { + innerArea += (points[j] * points[j + 3]) - (points[j + 1] * points[j + 2]); + } + + if (innerArea * outerArea < 0) + { + context.moveTo(points[0], points[1]); + + for (let j = 2; j < points.length; j += 2) + { + context.lineTo(points[j], points[j + 1]); + } + } + else + { + context.moveTo(points[points.length - 2], points[points.length - 1]); + + for (let j = points.length - 4; j >= 0; j -= 2) + { + context.lineTo(points[j], points[j + 1]); + } + } + + if (holes[k].shape.closeStroke) + { + context.closePath(); + } + } } if (fillStyle.visible) @@ -255,28 +309,6 @@ } /** - * Renders a polygon. - * - * @param {PIXI.Point[]} points - The points to render - * @param {boolean} close - Should the polygon be closed - * @param {CanvasRenderingContext2D} context - The rendering context to use - */ - renderPolygon(points, close, context) - { - context.moveTo(points[0], points[1]); - - for (let j = 1; j < points.length / 2; ++j) - { - context.lineTo(points[j * 2], points[(j * 2) + 1]); - } - - if (close) - { - context.closePath(); - } - } - - /** * destroy graphics object * */ diff --git a/packages/graphics/src/Graphics.js b/packages/graphics/src/Graphics.js index c987b63..6e26b9f 100644 --- a/packages/graphics/src/Graphics.js +++ b/packages/graphics/src/Graphics.js @@ -309,14 +309,14 @@ { this.drawShape(this.currentPath); this.currentPath = new Polygon(); - this.currentPath.closed = false; + this.currentPath.closeStroke = false; this.currentPath.points.push(points[len - 2], points[len - 1]); } } else { this.currentPath = new Polygon(); - this.currentPath.closed = false; + this.currentPath.closeStroke = false; } } @@ -680,12 +680,12 @@ // see section 3.1: https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#3-managing-arguments let points = path; - let closed = true;// !!this._fillStyle; + let closeStroke = true;// !!this._fillStyle; // check if data has points.. if (points.points) { - closed = points.closed; + closeStroke = points.closeStroke; points = points.points; } @@ -703,7 +703,7 @@ const shape = new Polygon(points); - shape.closed = closed; + shape.closeStroke = closeStroke; this.drawShape(shape); @@ -1044,12 +1044,12 @@ */ closePath() { - // ok so close path assumes next one is a hole! const currentPath = this.currentPath; - if (currentPath && currentPath.close) + if (currentPath) { - currentPath.close(); + // we don't need to add extra point in the end because buildLine will take care of that + currentPath.closeStroke = true; } return this; @@ -1072,6 +1072,8 @@ * Begin adding holes to the last draw shape * IMPORTANT: holes must be fully inside a shape to work * Also weirdness ensues if holes overlap! + * Ellipses, Circles, Rectangles and Rounded Rectangles cannot be holes or host for holes in CanvasRenderer, + * please use `moveTo` `lineTo`, `quadraticCurveTo` if you rely on pixi-legacy bundle. * @return {PIXI.Graphics} Returns itself. */ beginHole() diff --git a/packages/graphics/src/GraphicsGeometry.js b/packages/graphics/src/GraphicsGeometry.js index 04ed362..6f711ec 100644 --- a/packages/graphics/src/GraphicsGeometry.js +++ b/packages/graphics/src/GraphicsGeometry.js @@ -309,6 +309,8 @@ const lastShape = this.graphicsData[this.graphicsData.length - 1]; + data.lineStyle = lastShape.lineStyle; + lastShape.holes.push(data); this.dirty++; @@ -502,7 +504,7 @@ { if (data.holes.length) { - this.proccessHoles(data.holes); + this.processHoles(data.holes); buildPoly.triangulate(data, this); } @@ -514,6 +516,11 @@ else { buildLine(data, this); + + for (let i = 0; i < data.holes.length; i++) + { + buildLine(data.holes[i], this); + } } const size = (this.points.length / 2) - start; @@ -715,7 +722,7 @@ * @param {PIXI.GraphicsData[]} holes - Holes to render * @protected */ - proccessHoles(holes) + processHoles(holes) { for (let i = 0; i < holes.length; i++) { diff --git a/packages/canvas/canvas-graphics/src/CanvasGraphicsRenderer.js b/packages/canvas/canvas-graphics/src/CanvasGraphicsRenderer.js index 33c7bce..fde693f 100644 --- a/packages/canvas/canvas-graphics/src/CanvasGraphicsRenderer.js +++ b/packages/canvas/canvas-graphics/src/CanvasGraphicsRenderer.js @@ -78,11 +78,65 @@ { context.beginPath(); - this.renderPolygon(shape.points, shape.closed, context); + let points = shape.points; + const holes = data.holes; + let outerArea; + let innerArea; - for (let j = 0; j < data.holes.length; j++) + context.moveTo(points[0], points[1]); + + for (let j = 2; j < points.length; j += 2) { - // this.renderPolygon(data.holes[j].points, true, context); + context.lineTo(points[j], points[j + 1]); + } + + if (shape.closeStroke) + { + context.closePath(); + } + + if (holes.length > 0) + { + outerArea = 0; + for (let j = 0; j < points.length; j += 2) + { + outerArea += (points[j] * points[j + 3]) - (points[j + 1] * points[j + 2]); + } + + for (let k = 0; k < holes.length; k++) + { + points = holes[k].points; + + innerArea = 0; + for (let j = 0; j < points.length; j += 2) + { + innerArea += (points[j] * points[j + 3]) - (points[j + 1] * points[j + 2]); + } + + if (innerArea * outerArea < 0) + { + context.moveTo(points[0], points[1]); + + for (let j = 2; j < points.length; j += 2) + { + context.lineTo(points[j], points[j + 1]); + } + } + else + { + context.moveTo(points[points.length - 2], points[points.length - 1]); + + for (let j = points.length - 4; j >= 0; j -= 2) + { + context.lineTo(points[j], points[j + 1]); + } + } + + if (holes[k].shape.closeStroke) + { + context.closePath(); + } + } } if (fillStyle.visible) @@ -255,28 +309,6 @@ } /** - * Renders a polygon. - * - * @param {PIXI.Point[]} points - The points to render - * @param {boolean} close - Should the polygon be closed - * @param {CanvasRenderingContext2D} context - The rendering context to use - */ - renderPolygon(points, close, context) - { - context.moveTo(points[0], points[1]); - - for (let j = 1; j < points.length / 2; ++j) - { - context.lineTo(points[j * 2], points[(j * 2) + 1]); - } - - if (close) - { - context.closePath(); - } - } - - /** * destroy graphics object * */ diff --git a/packages/graphics/src/Graphics.js b/packages/graphics/src/Graphics.js index c987b63..6e26b9f 100644 --- a/packages/graphics/src/Graphics.js +++ b/packages/graphics/src/Graphics.js @@ -309,14 +309,14 @@ { this.drawShape(this.currentPath); this.currentPath = new Polygon(); - this.currentPath.closed = false; + this.currentPath.closeStroke = false; this.currentPath.points.push(points[len - 2], points[len - 1]); } } else { this.currentPath = new Polygon(); - this.currentPath.closed = false; + this.currentPath.closeStroke = false; } } @@ -680,12 +680,12 @@ // see section 3.1: https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#3-managing-arguments let points = path; - let closed = true;// !!this._fillStyle; + let closeStroke = true;// !!this._fillStyle; // check if data has points.. if (points.points) { - closed = points.closed; + closeStroke = points.closeStroke; points = points.points; } @@ -703,7 +703,7 @@ const shape = new Polygon(points); - shape.closed = closed; + shape.closeStroke = closeStroke; this.drawShape(shape); @@ -1044,12 +1044,12 @@ */ closePath() { - // ok so close path assumes next one is a hole! const currentPath = this.currentPath; - if (currentPath && currentPath.close) + if (currentPath) { - currentPath.close(); + // we don't need to add extra point in the end because buildLine will take care of that + currentPath.closeStroke = true; } return this; @@ -1072,6 +1072,8 @@ * Begin adding holes to the last draw shape * IMPORTANT: holes must be fully inside a shape to work * Also weirdness ensues if holes overlap! + * Ellipses, Circles, Rectangles and Rounded Rectangles cannot be holes or host for holes in CanvasRenderer, + * please use `moveTo` `lineTo`, `quadraticCurveTo` if you rely on pixi-legacy bundle. * @return {PIXI.Graphics} Returns itself. */ beginHole() diff --git a/packages/graphics/src/GraphicsGeometry.js b/packages/graphics/src/GraphicsGeometry.js index 04ed362..6f711ec 100644 --- a/packages/graphics/src/GraphicsGeometry.js +++ b/packages/graphics/src/GraphicsGeometry.js @@ -309,6 +309,8 @@ const lastShape = this.graphicsData[this.graphicsData.length - 1]; + data.lineStyle = lastShape.lineStyle; + lastShape.holes.push(data); this.dirty++; @@ -502,7 +504,7 @@ { if (data.holes.length) { - this.proccessHoles(data.holes); + this.processHoles(data.holes); buildPoly.triangulate(data, this); } @@ -514,6 +516,11 @@ else { buildLine(data, this); + + for (let i = 0; i < data.holes.length; i++) + { + buildLine(data.holes[i], this); + } } const size = (this.points.length / 2) - start; @@ -715,7 +722,7 @@ * @param {PIXI.GraphicsData[]} holes - Holes to render * @protected */ - proccessHoles(holes) + processHoles(holes) { for (let i = 0; i < holes.length; i++) { diff --git a/packages/graphics/src/utils/buildLine.js b/packages/graphics/src/utils/buildLine.js index 78023ff..ebddf05 100644 --- a/packages/graphics/src/utils/buildLine.js +++ b/packages/graphics/src/utils/buildLine.js @@ -7,9 +7,8 @@ * * @ignore * @private - * @param {PIXI.WebGLGraphicsData} graphicsData - The graphics object containing all the necessary properties - * @param {object} webGLData - an object containing all the WebGL-specific information to create this shape - * @param {object} webGLDataNativeLines - an object containing all the WebGL-specific information to create nativeLines + * @param {PIXI.GraphicsData} graphicsData - The graphics object containing all the necessary properties + * @param {PIXI.GraphicsGeometry} graphicsGeometry - Geometry where to append output */ export default function (graphicsData, graphicsGeometry) { @@ -57,11 +56,11 @@ // get first and last point.. figure out the middle! const firstPoint = new Point(points[0], points[1]); const lastPoint = new Point(points[points.length - 2], points[points.length - 1]); - const closedShape = shape.type !== SHAPES.POLY; + const closedShape = shape.type !== SHAPES.POLY || shape.closeStroke; const closedPath = firstPoint.x === lastPoint.x && firstPoint.y === lastPoint.y; // if the first point is the last point - gonna have issues :) - if (closedPath || closedShape) + if (closedShape) { // need to clone as we are going to slightly modify the shape.. points = points.slice(); @@ -244,8 +243,8 @@ * * @ignore * @private - * @param {PIXI.WebGLGraphicsData} graphicsData - The graphics object containing all the necessary properties - * @param {object} webGLData - an object containing all the WebGL-specific information to create this shape + * @param {PIXI.GraphicsData} graphicsData - The graphics object containing all the necessary properties + * @param {PIXI.GraphicsGeometry} graphicsGeometry - Geometry where to append output */ function buildNativeLine(graphicsData, graphicsGeometry) { diff --git a/packages/canvas/canvas-graphics/src/CanvasGraphicsRenderer.js b/packages/canvas/canvas-graphics/src/CanvasGraphicsRenderer.js index 33c7bce..fde693f 100644 --- a/packages/canvas/canvas-graphics/src/CanvasGraphicsRenderer.js +++ b/packages/canvas/canvas-graphics/src/CanvasGraphicsRenderer.js @@ -78,11 +78,65 @@ { context.beginPath(); - this.renderPolygon(shape.points, shape.closed, context); + let points = shape.points; + const holes = data.holes; + let outerArea; + let innerArea; - for (let j = 0; j < data.holes.length; j++) + context.moveTo(points[0], points[1]); + + for (let j = 2; j < points.length; j += 2) { - // this.renderPolygon(data.holes[j].points, true, context); + context.lineTo(points[j], points[j + 1]); + } + + if (shape.closeStroke) + { + context.closePath(); + } + + if (holes.length > 0) + { + outerArea = 0; + for (let j = 0; j < points.length; j += 2) + { + outerArea += (points[j] * points[j + 3]) - (points[j + 1] * points[j + 2]); + } + + for (let k = 0; k < holes.length; k++) + { + points = holes[k].points; + + innerArea = 0; + for (let j = 0; j < points.length; j += 2) + { + innerArea += (points[j] * points[j + 3]) - (points[j + 1] * points[j + 2]); + } + + if (innerArea * outerArea < 0) + { + context.moveTo(points[0], points[1]); + + for (let j = 2; j < points.length; j += 2) + { + context.lineTo(points[j], points[j + 1]); + } + } + else + { + context.moveTo(points[points.length - 2], points[points.length - 1]); + + for (let j = points.length - 4; j >= 0; j -= 2) + { + context.lineTo(points[j], points[j + 1]); + } + } + + if (holes[k].shape.closeStroke) + { + context.closePath(); + } + } } if (fillStyle.visible) @@ -255,28 +309,6 @@ } /** - * Renders a polygon. - * - * @param {PIXI.Point[]} points - The points to render - * @param {boolean} close - Should the polygon be closed - * @param {CanvasRenderingContext2D} context - The rendering context to use - */ - renderPolygon(points, close, context) - { - context.moveTo(points[0], points[1]); - - for (let j = 1; j < points.length / 2; ++j) - { - context.lineTo(points[j * 2], points[(j * 2) + 1]); - } - - if (close) - { - context.closePath(); - } - } - - /** * destroy graphics object * */ diff --git a/packages/graphics/src/Graphics.js b/packages/graphics/src/Graphics.js index c987b63..6e26b9f 100644 --- a/packages/graphics/src/Graphics.js +++ b/packages/graphics/src/Graphics.js @@ -309,14 +309,14 @@ { this.drawShape(this.currentPath); this.currentPath = new Polygon(); - this.currentPath.closed = false; + this.currentPath.closeStroke = false; this.currentPath.points.push(points[len - 2], points[len - 1]); } } else { this.currentPath = new Polygon(); - this.currentPath.closed = false; + this.currentPath.closeStroke = false; } } @@ -680,12 +680,12 @@ // see section 3.1: https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#3-managing-arguments let points = path; - let closed = true;// !!this._fillStyle; + let closeStroke = true;// !!this._fillStyle; // check if data has points.. if (points.points) { - closed = points.closed; + closeStroke = points.closeStroke; points = points.points; } @@ -703,7 +703,7 @@ const shape = new Polygon(points); - shape.closed = closed; + shape.closeStroke = closeStroke; this.drawShape(shape); @@ -1044,12 +1044,12 @@ */ closePath() { - // ok so close path assumes next one is a hole! const currentPath = this.currentPath; - if (currentPath && currentPath.close) + if (currentPath) { - currentPath.close(); + // we don't need to add extra point in the end because buildLine will take care of that + currentPath.closeStroke = true; } return this; @@ -1072,6 +1072,8 @@ * Begin adding holes to the last draw shape * IMPORTANT: holes must be fully inside a shape to work * Also weirdness ensues if holes overlap! + * Ellipses, Circles, Rectangles and Rounded Rectangles cannot be holes or host for holes in CanvasRenderer, + * please use `moveTo` `lineTo`, `quadraticCurveTo` if you rely on pixi-legacy bundle. * @return {PIXI.Graphics} Returns itself. */ beginHole() diff --git a/packages/graphics/src/GraphicsGeometry.js b/packages/graphics/src/GraphicsGeometry.js index 04ed362..6f711ec 100644 --- a/packages/graphics/src/GraphicsGeometry.js +++ b/packages/graphics/src/GraphicsGeometry.js @@ -309,6 +309,8 @@ const lastShape = this.graphicsData[this.graphicsData.length - 1]; + data.lineStyle = lastShape.lineStyle; + lastShape.holes.push(data); this.dirty++; @@ -502,7 +504,7 @@ { if (data.holes.length) { - this.proccessHoles(data.holes); + this.processHoles(data.holes); buildPoly.triangulate(data, this); } @@ -514,6 +516,11 @@ else { buildLine(data, this); + + for (let i = 0; i < data.holes.length; i++) + { + buildLine(data.holes[i], this); + } } const size = (this.points.length / 2) - start; @@ -715,7 +722,7 @@ * @param {PIXI.GraphicsData[]} holes - Holes to render * @protected */ - proccessHoles(holes) + processHoles(holes) { for (let i = 0; i < holes.length; i++) { diff --git a/packages/graphics/src/utils/buildLine.js b/packages/graphics/src/utils/buildLine.js index 78023ff..ebddf05 100644 --- a/packages/graphics/src/utils/buildLine.js +++ b/packages/graphics/src/utils/buildLine.js @@ -7,9 +7,8 @@ * * @ignore * @private - * @param {PIXI.WebGLGraphicsData} graphicsData - The graphics object containing all the necessary properties - * @param {object} webGLData - an object containing all the WebGL-specific information to create this shape - * @param {object} webGLDataNativeLines - an object containing all the WebGL-specific information to create nativeLines + * @param {PIXI.GraphicsData} graphicsData - The graphics object containing all the necessary properties + * @param {PIXI.GraphicsGeometry} graphicsGeometry - Geometry where to append output */ export default function (graphicsData, graphicsGeometry) { @@ -57,11 +56,11 @@ // get first and last point.. figure out the middle! const firstPoint = new Point(points[0], points[1]); const lastPoint = new Point(points[points.length - 2], points[points.length - 1]); - const closedShape = shape.type !== SHAPES.POLY; + const closedShape = shape.type !== SHAPES.POLY || shape.closeStroke; const closedPath = firstPoint.x === lastPoint.x && firstPoint.y === lastPoint.y; // if the first point is the last point - gonna have issues :) - if (closedPath || closedShape) + if (closedShape) { // need to clone as we are going to slightly modify the shape.. points = points.slice(); @@ -244,8 +243,8 @@ * * @ignore * @private - * @param {PIXI.WebGLGraphicsData} graphicsData - The graphics object containing all the necessary properties - * @param {object} webGLData - an object containing all the WebGL-specific information to create this shape + * @param {PIXI.GraphicsData} graphicsData - The graphics object containing all the necessary properties + * @param {PIXI.GraphicsGeometry} graphicsGeometry - Geometry where to append output */ function buildNativeLine(graphicsData, graphicsGeometry) { diff --git a/packages/math/src/shapes/Polygon.js b/packages/math/src/shapes/Polygon.js index ee38a3f..82f65db 100644 --- a/packages/math/src/shapes/Polygon.js +++ b/packages/math/src/shapes/Polygon.js @@ -36,8 +36,6 @@ points = p; } - this.closed = true; - /** * An array of the points of this polygon * @@ -54,6 +52,13 @@ * @see PIXI.SHAPES */ this.type = SHAPES.POLY; + + /** + * `false` after moveTo, `true` after `closePath`. In all other cases it is `true`. + * @member {boolean} + * @default true + */ + this.closeStroke = true; } /** @@ -63,22 +68,11 @@ */ clone() { - return new Polygon(this.points.slice()); - } + const polygon = new Polygon(this.points.slice()); - /** - * Closes the polygon, adding points if necessary. - * - */ - close() - { - const points = this.points; + polygon.closeStroke = this.closeStroke; - // close the poly if the value is true! - if (points[0] !== points[points.length - 2] || points[1] !== points[points.length - 1]) - { - points.push(points[0], points[1]); - } + return polygon; } /** diff --git a/packages/canvas/canvas-graphics/src/CanvasGraphicsRenderer.js b/packages/canvas/canvas-graphics/src/CanvasGraphicsRenderer.js index 33c7bce..fde693f 100644 --- a/packages/canvas/canvas-graphics/src/CanvasGraphicsRenderer.js +++ b/packages/canvas/canvas-graphics/src/CanvasGraphicsRenderer.js @@ -78,11 +78,65 @@ { context.beginPath(); - this.renderPolygon(shape.points, shape.closed, context); + let points = shape.points; + const holes = data.holes; + let outerArea; + let innerArea; - for (let j = 0; j < data.holes.length; j++) + context.moveTo(points[0], points[1]); + + for (let j = 2; j < points.length; j += 2) { - // this.renderPolygon(data.holes[j].points, true, context); + context.lineTo(points[j], points[j + 1]); + } + + if (shape.closeStroke) + { + context.closePath(); + } + + if (holes.length > 0) + { + outerArea = 0; + for (let j = 0; j < points.length; j += 2) + { + outerArea += (points[j] * points[j + 3]) - (points[j + 1] * points[j + 2]); + } + + for (let k = 0; k < holes.length; k++) + { + points = holes[k].points; + + innerArea = 0; + for (let j = 0; j < points.length; j += 2) + { + innerArea += (points[j] * points[j + 3]) - (points[j + 1] * points[j + 2]); + } + + if (innerArea * outerArea < 0) + { + context.moveTo(points[0], points[1]); + + for (let j = 2; j < points.length; j += 2) + { + context.lineTo(points[j], points[j + 1]); + } + } + else + { + context.moveTo(points[points.length - 2], points[points.length - 1]); + + for (let j = points.length - 4; j >= 0; j -= 2) + { + context.lineTo(points[j], points[j + 1]); + } + } + + if (holes[k].shape.closeStroke) + { + context.closePath(); + } + } } if (fillStyle.visible) @@ -255,28 +309,6 @@ } /** - * Renders a polygon. - * - * @param {PIXI.Point[]} points - The points to render - * @param {boolean} close - Should the polygon be closed - * @param {CanvasRenderingContext2D} context - The rendering context to use - */ - renderPolygon(points, close, context) - { - context.moveTo(points[0], points[1]); - - for (let j = 1; j < points.length / 2; ++j) - { - context.lineTo(points[j * 2], points[(j * 2) + 1]); - } - - if (close) - { - context.closePath(); - } - } - - /** * destroy graphics object * */ diff --git a/packages/graphics/src/Graphics.js b/packages/graphics/src/Graphics.js index c987b63..6e26b9f 100644 --- a/packages/graphics/src/Graphics.js +++ b/packages/graphics/src/Graphics.js @@ -309,14 +309,14 @@ { this.drawShape(this.currentPath); this.currentPath = new Polygon(); - this.currentPath.closed = false; + this.currentPath.closeStroke = false; this.currentPath.points.push(points[len - 2], points[len - 1]); } } else { this.currentPath = new Polygon(); - this.currentPath.closed = false; + this.currentPath.closeStroke = false; } } @@ -680,12 +680,12 @@ // see section 3.1: https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#3-managing-arguments let points = path; - let closed = true;// !!this._fillStyle; + let closeStroke = true;// !!this._fillStyle; // check if data has points.. if (points.points) { - closed = points.closed; + closeStroke = points.closeStroke; points = points.points; } @@ -703,7 +703,7 @@ const shape = new Polygon(points); - shape.closed = closed; + shape.closeStroke = closeStroke; this.drawShape(shape); @@ -1044,12 +1044,12 @@ */ closePath() { - // ok so close path assumes next one is a hole! const currentPath = this.currentPath; - if (currentPath && currentPath.close) + if (currentPath) { - currentPath.close(); + // we don't need to add extra point in the end because buildLine will take care of that + currentPath.closeStroke = true; } return this; @@ -1072,6 +1072,8 @@ * Begin adding holes to the last draw shape * IMPORTANT: holes must be fully inside a shape to work * Also weirdness ensues if holes overlap! + * Ellipses, Circles, Rectangles and Rounded Rectangles cannot be holes or host for holes in CanvasRenderer, + * please use `moveTo` `lineTo`, `quadraticCurveTo` if you rely on pixi-legacy bundle. * @return {PIXI.Graphics} Returns itself. */ beginHole() diff --git a/packages/graphics/src/GraphicsGeometry.js b/packages/graphics/src/GraphicsGeometry.js index 04ed362..6f711ec 100644 --- a/packages/graphics/src/GraphicsGeometry.js +++ b/packages/graphics/src/GraphicsGeometry.js @@ -309,6 +309,8 @@ const lastShape = this.graphicsData[this.graphicsData.length - 1]; + data.lineStyle = lastShape.lineStyle; + lastShape.holes.push(data); this.dirty++; @@ -502,7 +504,7 @@ { if (data.holes.length) { - this.proccessHoles(data.holes); + this.processHoles(data.holes); buildPoly.triangulate(data, this); } @@ -514,6 +516,11 @@ else { buildLine(data, this); + + for (let i = 0; i < data.holes.length; i++) + { + buildLine(data.holes[i], this); + } } const size = (this.points.length / 2) - start; @@ -715,7 +722,7 @@ * @param {PIXI.GraphicsData[]} holes - Holes to render * @protected */ - proccessHoles(holes) + processHoles(holes) { for (let i = 0; i < holes.length; i++) { diff --git a/packages/graphics/src/utils/buildLine.js b/packages/graphics/src/utils/buildLine.js index 78023ff..ebddf05 100644 --- a/packages/graphics/src/utils/buildLine.js +++ b/packages/graphics/src/utils/buildLine.js @@ -7,9 +7,8 @@ * * @ignore * @private - * @param {PIXI.WebGLGraphicsData} graphicsData - The graphics object containing all the necessary properties - * @param {object} webGLData - an object containing all the WebGL-specific information to create this shape - * @param {object} webGLDataNativeLines - an object containing all the WebGL-specific information to create nativeLines + * @param {PIXI.GraphicsData} graphicsData - The graphics object containing all the necessary properties + * @param {PIXI.GraphicsGeometry} graphicsGeometry - Geometry where to append output */ export default function (graphicsData, graphicsGeometry) { @@ -57,11 +56,11 @@ // get first and last point.. figure out the middle! const firstPoint = new Point(points[0], points[1]); const lastPoint = new Point(points[points.length - 2], points[points.length - 1]); - const closedShape = shape.type !== SHAPES.POLY; + const closedShape = shape.type !== SHAPES.POLY || shape.closeStroke; const closedPath = firstPoint.x === lastPoint.x && firstPoint.y === lastPoint.y; // if the first point is the last point - gonna have issues :) - if (closedPath || closedShape) + if (closedShape) { // need to clone as we are going to slightly modify the shape.. points = points.slice(); @@ -244,8 +243,8 @@ * * @ignore * @private - * @param {PIXI.WebGLGraphicsData} graphicsData - The graphics object containing all the necessary properties - * @param {object} webGLData - an object containing all the WebGL-specific information to create this shape + * @param {PIXI.GraphicsData} graphicsData - The graphics object containing all the necessary properties + * @param {PIXI.GraphicsGeometry} graphicsGeometry - Geometry where to append output */ function buildNativeLine(graphicsData, graphicsGeometry) { diff --git a/packages/math/src/shapes/Polygon.js b/packages/math/src/shapes/Polygon.js index ee38a3f..82f65db 100644 --- a/packages/math/src/shapes/Polygon.js +++ b/packages/math/src/shapes/Polygon.js @@ -36,8 +36,6 @@ points = p; } - this.closed = true; - /** * An array of the points of this polygon * @@ -54,6 +52,13 @@ * @see PIXI.SHAPES */ this.type = SHAPES.POLY; + + /** + * `false` after moveTo, `true` after `closePath`. In all other cases it is `true`. + * @member {boolean} + * @default true + */ + this.closeStroke = true; } /** @@ -63,22 +68,11 @@ */ clone() { - return new Polygon(this.points.slice()); - } + const polygon = new Polygon(this.points.slice()); - /** - * Closes the polygon, adding points if necessary. - * - */ - close() - { - const points = this.points; + polygon.closeStroke = this.closeStroke; - // close the poly if the value is true! - if (points[0] !== points[points.length - 2] || points[1] !== points[points.length - 1]) - { - points.push(points[0], points[1]); - } + return polygon; } /** diff --git a/packages/math/test/Polygon.js b/packages/math/test/Polygon.js index 92e1fc8..567bd36 100644 --- a/packages/math/test/Polygon.js +++ b/packages/math/test/Polygon.js @@ -46,6 +46,9 @@ it('should create a copy', function () { const polygon1 = new Polygon(0, 0, 10, 0, 0, 10); + + polygon1.closeStroke = !polygon1.closeStroke; + const polygon2 = polygon1.clone(); expect(polygon1.points.length).to.be.equals(6); @@ -56,40 +59,14 @@ expect(polygon1.points[i]).to.be.equals(polygon2.points[i]); } - polygon2.close(); + expect(polygon1.closeStroke).to.be.equals(polygon2.closeStroke); + polygon2.points.push(0, 0); expect(polygon1.points.length).to.be.equals(6); expect(polygon2.points.length).to.be.equals(8); }); }); - describe('close', function () - { - it('should close the polygon if open', function () - { - const polygon = new Polygon(0, 0, 10, 0, 0, 10); - - expect(polygon.points.length).to.be.equals(6); - - polygon.close(); - - expect(polygon.points.length).to.be.equals(8); - expect(polygon.points[6]).to.be.equals(0); - expect(polygon.points[7]).to.be.equals(0); - }); - - it('should do nothing if already closed', function () - { - const polygon = new Polygon(0, 0, 10, 0, 0, 10, 0, 0); - - expect(polygon.points.length).to.be.equals(8); - - polygon.close(); - - expect(polygon.points.length).to.be.equals(8); - }); - }); - describe('contains', function () { it('should include points inside', function ()