英文:
Complex shape in JavaScript by combining intersecting shapes
问题
如何通过组合相交的形状在JavaScript中创建新的复杂形状?我目前正在使用FabricJS,但我发现很难实现期望的结果。
基本上,我有多个简单的形状相交,当我选择它们全部时,我想创建一个新的复杂形状,其外边线与相交形状的外边线相对应。
是否有可能实现这一点?
英文:
How can I create a new complex shape in JavaScript by combining intersecting shapes? I am currently using FabricJS, but I am finding it difficult to achieve the desired result.
Essentially, I have multiple simple shapes that intersect, and when I select them all I want to create a new complex shape with outer lines that correspond to the outer lines of the intersecting shapes.
Is it possible to achieve this?
答案1
得分: 1
我不确定FabricJS是否支持这个功能,但你描述的问题通常被称为“构造实体几何”(Constructive Solid Geometry)。通常在3D环境下讨论它,但在2D中的概念与原理是相同的。
这里有一个jsfiddle示例,其中我:
- 创建矩形、三角形和圆形
- 绘制普通的形状
- 将这些形状转换成“CSG对象”以供csg2d.js使用
- 执行矩形和三角形的CSG联合操作
- 绘制联合结果
- 将矩形和三角形的联合结果转换成“CSG对象”
- 执行矩形、三角形和圆形的CSG联合操作
- 执行矩形、三角形和圆形的CSG减法操作
- 绘制最终结果
我使用csg2d.js库执行CSG操作。
所以要记住的是,除非Fabric.js支持CSG操作,否则你可能需要手动从Fabric.js对象中进行构建和转换,自己实现CSG操作,或者借助诸如csg2d.js之类的库来实现,同时还要实现转换回Fabric.js格式。
虽然这不是什么大问题,但这是我处理这个问题的方式,因为这些CSG操作不是Fabric.js特定的,而是一个更一般的问题。
通过将CSG操作视为与Fabric.js“分开的、不同的事物”,你还可以享受到更多有用的教程和文档,而不是专门搜索“Fabric.js CSG”帮助。
JSFiddle代码:
// 这里是你的JSFiddle代码,用于创建和操作形状以进行CSG操作。
希望这些信息对你有所帮助。如果你有其他问题,请随时提出。
<details>
<summary>英文:</summary>
I'm not entirely sure about FabricJS specifically, but the problem you are describing has a more general name - [Constructive Solid Geometry][1]. It is usually talked about in 3D context, but the idea and the concept is the same in 2D as well.
Here is a [jsfiddle example][2] where I:
- Construct rectangle, triangle and circle
- Draw the ordinary shapes
- Turn the shapes into a "CSG object" for csg2d.js
- Perform a CSG union between rectangle and triangle
- Draw the union
- Turn the union of the rectangle and triangle into a "CSG object"
- Perform a CSG union between rectangle-triangle union and circle
- Perform a CSG subtraction between rectangle-triangle union and circle
- Draw the end results
I use [csg2d.js][3] library to perform the CSG operations.
So the takeaway here is that unless Fabric.js supports CSG operations, you will most likely have to implement the constructions and conversions by hand from the Fabric.js objects, implement the CSG operations yourself or with the help of a library such as csg2d.js, and implement the conversions back to Fabric.js format as well.
Not too big of a deal, but that is how I would approach this because these CSG operations are not Fabric.js specific, but instead a more general problem to solve.
By treating the CSG operations as a "separate, different beast" from Fabric.js, you will also enjoy the benefits of finding a lot of helpful tutorials and documentation than if you'd search for "Fabric.js CSG" help specifically.
JSFiddle code:
const canvas = document.querySelector('#canvas');
const ctx = canvas.getContext('2d');
// Helper function to display polygons
function drawPolygon(polygon, color, x, y) {
const offX = x > 0 ? x : 0;
const offY = y > 0 ? y : 0;
const firstPoint = polygon.shift();
// Skip last point
polygon.pop();
ctx.fillStyle = color;
ctx.beginPath();
ctx.moveTo(offX + firstPoint[0], offY + firstPoint[1]);
for(let point of polygon) {
ctx.lineTo(offX + point[0], offY + point[1]);
}
ctx.closePath();
ctx.fill();
}
// Helper function to create polygons
// Most importantly this closes the polygon for CSG2D.js by appending the first point to the end of the point list
function makePolygon(points) {
const closed = points;
closed.push(points[0]);
return closed;
}
// Helper function to convert the CSG2D.js polygon to our array format
function xyPolygonToArrayPolygon(xyPolygon) {
return xyPolygon[0].map(p => {
return [p.x, p.y]
});
}
// Define rectangle by its points
const rectangle = makePolygon([[10, 10], [100, 10], [100, 100], [10, 100], [10, 10]]);
// Define triangle by its points
const triangle = makePolygon([[60, 60], [85, 125], [35, 125], [60, 60]]);
// Define circle
let circle = [];
const circleX = 100;
const circleY = 50;
const circleRad = 25;
// Generate points for the circle
for(let d = 0; d < 360; d += 36){
var rad = d * Math.PI / 180;
var x = circleX + circleRad * Math.cos(rad);
var y = circleY + circleRad * Math.sin(rad);
circle.push([x, y]);
}
circle = makePolygon(circle);
// Draw the simple polygons
drawPolygon(rectangle, '#FF0000');
drawPolygon(triangle, '#0000FF');
drawPolygon(circle, '#00FF00');
// Construct a more complex polygons by using CSG2D.js
const rectangleCSG = CSG.fromPolygons([rectangle]);
const triangleCSG = CSG.fromPolygons([triangle]);
const circleCSG = CSG.fromPolygons([circle]);
const rectangleAndTriangle = xyPolygonToArrayPolygon(rectangleCSG.union(triangleCSG).toPolygons());
const rectangleAndTriangleCSG = CSG.fromPolygons([rectangleAndTriangle]);
const rectangleAndTriangleAndCircle = xyPolygonToArrayPolygon(rectangleAndTriangleCSG.union(circleCSG).toPolygons());
const rectangleAndTriangleAndSubtractCircle = xyPolygonToArrayPolygon(rectangleAndTriangleCSG.subtract(circleCSG).toPolygons());
drawPolygon(rectangleAndTriangle, '#FFFF00', 100, 100);
drawPolygon(rectangleAndTriangleAndCircle, '#00FFFF', 200, 200);
drawPolygon(rectangleAndTriangleAndSubtractCircle, '#505000', 300, 300);
Note: The subtraction didn't originally work properly until I defined the circle with 10 points, instead of the original 360 points. So it seems like there is some kind of vertex limit with the csg2d.js library, or it could be that I just did something wrong.
[1]: https://en.wikipedia.org/wiki/Constructive_solid_geometry
[2]: https://jsfiddle.net/ahvonenj/gtoryu9p/
[3]: https://github.com/come/csg2d.js/
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论