Add support for holes in fog subtraction
This commit is contained in:
parent
9eaece24e5
commit
f4d71cd6bf
@ -144,22 +144,33 @@ function Map({
|
|||||||
]);
|
]);
|
||||||
let subtractedShapes = {};
|
let subtractedShapes = {};
|
||||||
for (let shape of Object.values(shapesById)) {
|
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(
|
const difference = polygonClipping.difference(
|
||||||
shapeGeom,
|
shapeGeom,
|
||||||
actionGeom
|
actionGeom
|
||||||
);
|
);
|
||||||
for (let i = 0; i < difference.length; i++) {
|
for (let i = 0; i < difference.length; i++) {
|
||||||
for (let j = 0; j < difference[i].length; j++) {
|
let newId = difference.length > 1 ? `${shape.id}-${i}` : shape.id;
|
||||||
let newId = `${shape.id}-${i}_${j}`;
|
// Holes detected
|
||||||
subtractedShapes[newId] = {
|
let holes = [];
|
||||||
...shape,
|
if (difference[i].length > 1) {
|
||||||
id: newId,
|
for (let j = 1; j < difference[i].length; j++) {
|
||||||
data: {
|
holes.push(difference[i][j].map(([x, y]) => ({ x, y })));
|
||||||
points: 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;
|
shapesById = subtractedShapes;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React, { useContext, useState, useCallback } from "react";
|
import React, { useContext, useState, useCallback } from "react";
|
||||||
import shortid from "shortid";
|
import shortid from "shortid";
|
||||||
import { Group, Line } from "react-konva";
|
import { Group } from "react-konva";
|
||||||
import useImage from "use-image";
|
import useImage from "use-image";
|
||||||
|
|
||||||
import diagonalPattern from "../../images/DiagonalPattern.png";
|
import diagonalPattern from "../../images/DiagonalPattern.png";
|
||||||
@ -13,9 +13,9 @@ import {
|
|||||||
simplifyPoints,
|
simplifyPoints,
|
||||||
getStrokeWidth,
|
getStrokeWidth,
|
||||||
} from "../../helpers/drawing";
|
} from "../../helpers/drawing";
|
||||||
|
|
||||||
import colors from "../../helpers/colors";
|
import colors from "../../helpers/colors";
|
||||||
import useMapBrush from "../../helpers/useMapBrush";
|
import useMapBrush from "../../helpers/useMapBrush";
|
||||||
|
import { HoleyLine } from "../../helpers/konva";
|
||||||
|
|
||||||
function MapFog({
|
function MapFog({
|
||||||
shapes,
|
shapes,
|
||||||
@ -70,7 +70,7 @@ function MapFog({
|
|||||||
) {
|
) {
|
||||||
setDrawingShape({
|
setDrawingShape({
|
||||||
type: "fog",
|
type: "fog",
|
||||||
data: { points: [brushPosition] },
|
data: { points: [brushPosition], holes: [] },
|
||||||
strokeWidth: 0.5,
|
strokeWidth: 0.5,
|
||||||
color: selectedToolSettings.type === "add" ? "black" : "red",
|
color: selectedToolSettings.type === "add" ? "black" : "red",
|
||||||
blend: false,
|
blend: false,
|
||||||
@ -106,7 +106,10 @@ function MapFog({
|
|||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
...prevShape,
|
...prevShape,
|
||||||
data: { points: [...prevPoints, brushPosition] },
|
data: {
|
||||||
|
...prevShape.data,
|
||||||
|
points: [...prevPoints, brushPosition],
|
||||||
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -118,6 +121,7 @@ function MapFog({
|
|||||||
const shape = {
|
const shape = {
|
||||||
...drawingShape,
|
...drawingShape,
|
||||||
data: {
|
data: {
|
||||||
|
...drawingShape.data,
|
||||||
points: simplifyPoints(
|
points: simplifyPoints(
|
||||||
drawingShape.data.points,
|
drawingShape.data.points,
|
||||||
gridSize,
|
gridSize,
|
||||||
@ -133,6 +137,7 @@ function MapFog({
|
|||||||
if (drawingShape.data.points.length > 1) {
|
if (drawingShape.data.points.length > 1) {
|
||||||
const shape = {
|
const shape = {
|
||||||
data: {
|
data: {
|
||||||
|
...drawingShape.data,
|
||||||
points: simplifyPoints(
|
points: simplifyPoints(
|
||||||
drawingShape.data.points,
|
drawingShape.data.points,
|
||||||
gridSize,
|
gridSize,
|
||||||
@ -187,18 +192,23 @@ function MapFog({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function reducePoints(acc, point) {
|
||||||
|
return [...acc, point.x * mapWidth, point.y * mapHeight];
|
||||||
|
}
|
||||||
|
|
||||||
function renderShape(shape) {
|
function renderShape(shape) {
|
||||||
|
const points = shape.data.points.reduce(reducePoints, []);
|
||||||
|
const holes =
|
||||||
|
shape.data.holes &&
|
||||||
|
shape.data.holes.map((hole) => hole.reduce(reducePoints, []));
|
||||||
return (
|
return (
|
||||||
<Line
|
<HoleyLine
|
||||||
key={shape.id}
|
key={shape.id}
|
||||||
onMouseMove={() => handleShapeOver(shape, isBrushDown)}
|
onMouseMove={() => handleShapeOver(shape, isBrushDown)}
|
||||||
onTouchOver={() => handleShapeOver(shape, isBrushDown)}
|
onTouchOver={() => handleShapeOver(shape, isBrushDown)}
|
||||||
onMouseDown={() => handleShapeOver(shape, true)}
|
onMouseDown={() => handleShapeOver(shape, true)}
|
||||||
onTouchStart={() => handleShapeOver(shape, true)}
|
onTouchStart={() => handleShapeOver(shape, true)}
|
||||||
points={shape.data.points.reduce(
|
points={points}
|
||||||
(acc, point) => [...acc, point.x * mapWidth, point.y * mapHeight],
|
|
||||||
[]
|
|
||||||
)}
|
|
||||||
stroke={colors[shape.color] || shape.color}
|
stroke={colors[shape.color] || shape.color}
|
||||||
fill={colors[shape.color] || shape.color}
|
fill={colors[shape.color] || shape.color}
|
||||||
closed
|
closed
|
||||||
@ -213,6 +223,7 @@ function MapFog({
|
|||||||
opacity={isEditing ? 0.5 : 1}
|
opacity={isEditing ? 0.5 : 1}
|
||||||
fillPatternImage={patternImage}
|
fillPatternImage={patternImage}
|
||||||
fillPriority={isEditing && !shape.visible ? "pattern" : "color"}
|
fillPriority={isEditing && !shape.visible ? "pattern" : "color"}
|
||||||
|
holes={holes}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
107
src/helpers/konva.js
Normal file
107
src/helpers/konva.js
Normal 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} />;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user