Add support for holes in fog subtraction

This commit is contained in:
Mitchell McCaffrey 2020-06-14 12:27:05 +10:00
parent 9eaece24e5
commit f4d71cd6bf
3 changed files with 148 additions and 19 deletions

View File

@ -144,22 +144,33 @@ function Map({
]);
let subtractedShapes = {};
for (let shape of Object.values(shapesById)) {
let shapeGeom = [[shape.data.points.map(({ x, y }) => [x, y])]];
const shapePoints = shape.data.points.map(({ x, y }) => [x, y]);
const shapeHoles = shape.data.holes.map((hole) =>
hole.map(({ x, y }) => [x, y])
);
let shapeGeom = [[shapePoints, ...shapeHoles]];
const difference = polygonClipping.difference(
shapeGeom,
actionGeom
);
for (let i = 0; i < difference.length; i++) {
for (let j = 0; j < difference[i].length; j++) {
let newId = `${shape.id}-${i}_${j}`;
subtractedShapes[newId] = {
...shape,
id: newId,
data: {
points: difference[i][j].map(([x, y]) => ({ x, y })),
},
};
let newId = difference.length > 1 ? `${shape.id}-${i}` : shape.id;
// Holes detected
let holes = [];
if (difference[i].length > 1) {
for (let j = 1; j < difference[i].length; j++) {
holes.push(difference[i][j].map(([x, y]) => ({ x, y })));
}
}
subtractedShapes[newId] = {
...shape,
id: newId,
data: {
points: difference[i][0].map(([x, y]) => ({ x, y })),
holes,
},
};
}
}
shapesById = subtractedShapes;

View File

@ -1,6 +1,6 @@
import React, { useContext, useState, useCallback } from "react";
import shortid from "shortid";
import { Group, Line } from "react-konva";
import { Group } from "react-konva";
import useImage from "use-image";
import diagonalPattern from "../../images/DiagonalPattern.png";
@ -13,9 +13,9 @@ import {
simplifyPoints,
getStrokeWidth,
} from "../../helpers/drawing";
import colors from "../../helpers/colors";
import useMapBrush from "../../helpers/useMapBrush";
import { HoleyLine } from "../../helpers/konva";
function MapFog({
shapes,
@ -70,7 +70,7 @@ function MapFog({
) {
setDrawingShape({
type: "fog",
data: { points: [brushPosition] },
data: { points: [brushPosition], holes: [] },
strokeWidth: 0.5,
color: selectedToolSettings.type === "add" ? "black" : "red",
blend: false,
@ -106,7 +106,10 @@ function MapFog({
}
return {
...prevShape,
data: { points: [...prevPoints, brushPosition] },
data: {
...prevShape.data,
points: [...prevPoints, brushPosition],
},
};
});
}
@ -118,6 +121,7 @@ function MapFog({
const shape = {
...drawingShape,
data: {
...drawingShape.data,
points: simplifyPoints(
drawingShape.data.points,
gridSize,
@ -133,6 +137,7 @@ function MapFog({
if (drawingShape.data.points.length > 1) {
const shape = {
data: {
...drawingShape.data,
points: simplifyPoints(
drawingShape.data.points,
gridSize,
@ -187,18 +192,23 @@ function MapFog({
}
}
function reducePoints(acc, point) {
return [...acc, point.x * mapWidth, point.y * mapHeight];
}
function renderShape(shape) {
const points = shape.data.points.reduce(reducePoints, []);
const holes =
shape.data.holes &&
shape.data.holes.map((hole) => hole.reduce(reducePoints, []));
return (
<Line
<HoleyLine
key={shape.id}
onMouseMove={() => handleShapeOver(shape, isBrushDown)}
onTouchOver={() => handleShapeOver(shape, isBrushDown)}
onMouseDown={() => handleShapeOver(shape, true)}
onTouchStart={() => handleShapeOver(shape, true)}
points={shape.data.points.reduce(
(acc, point) => [...acc, point.x * mapWidth, point.y * mapHeight],
[]
)}
points={points}
stroke={colors[shape.color] || shape.color}
fill={colors[shape.color] || shape.color}
closed
@ -213,6 +223,7 @@ function MapFog({
opacity={isEditing ? 0.5 : 1}
fillPatternImage={patternImage}
fillPriority={isEditing && !shape.visible ? "pattern" : "color"}
holes={holes}
/>
);
}

107
src/helpers/konva.js Normal file
View File

@ -0,0 +1,107 @@
import React from "react";
import { Line } from "react-konva";
// Holes should be wound in the opposite direction as the containing points array
export function HoleyLine({ holes, ...props }) {
// Converted from https://github.com/rfestag/konva/blob/master/src/shapes/Line.ts
function drawLine(points, context, shape) {
const length = points.length;
const tension = shape.tension();
const closed = shape.closed();
const bezier = shape.bezier();
if (!length) {
return;
}
context.moveTo(points[0], points[1]);
if (tension !== 0 && length > 4) {
const tensionPoints = shape.getTensionPoints();
const tensionLength = tensionPoints.length;
let n = closed ? 0 : 4;
if (!closed) {
context.quadraticCurveTo(
tensionPoints[0],
tensionPoints[1],
tensionPoints[2],
tensionPoints[3]
);
}
while (n < tensionLength - 2) {
context.bezierCurveTo(
tensionPoints[n++],
tensionPoints[n++],
tensionPoints[n++],
tensionPoints[n++],
tensionPoints[n++],
tensionPoints[n++]
);
}
if (!closed) {
context.quadraticCurveTo(
tensionPoints[tensionLength - 2],
tensionPoints[tensionLength - 1],
points[length - 2],
points[length - 1]
);
}
} else if (bezier) {
// no tension but bezier
let n = 2;
while (n < length) {
context.bezierCurveTo(
points[n++],
points[n++],
points[n++],
points[n++],
points[n++],
points[n++]
);
}
} else {
// no tension
for (let n = 2; n < length; n += 2) {
context.lineTo(points[n], points[n + 1]);
}
}
}
// Draw points and holes
function sceneFunc(context, shape) {
const points = shape.points();
const closed = shape.closed();
if (!points.length) {
return;
}
context.beginPath();
console.log();
drawLine(points, context, shape);
context.beginPath();
drawLine(points, context, shape);
// closed e.g. polygons and blobs
if (closed) {
context.closePath();
if (holes && holes.length) {
for (let hole of holes) {
drawLine(hole, context, shape);
context.closePath();
}
}
context.fillStrokeShape(shape);
} else {
// open e.g. lines and splines
context.strokeShape(shape);
}
}
return <Line sceneFunc={sceneFunc} {...props} />;
}