From 17bc761bc41f9ad802d8d405943a6c417813071d Mon Sep 17 00:00:00 2001 From: Severin Ibarluzea Date: Tue, 9 Jul 2024 16:45:55 -0700 Subject: [PATCH] extrudeLinear implementation (#10) * get extrudeLinear working * fix formatting --- biome.json | 3 +- examples/extrude-linear.fixture.tsx | 21 +++ lib/components/jscad-fixture.tsx | 4 - lib/convert-csg-to-three-geom.ts | 2 - lib/create-host-config.ts | 230 +++++++++++++++++++--------- lib/intrinsic-jsx.d.ts | 20 +++ lib/jscad-fns/extrude-linear.tsx | 23 +++ lib/jscad-fns/index.ts | 2 + lib/jscad-fns/polygon.tsx | 2 +- lib/jscad-primitives.ts | 44 +++--- tests/fixtures/test-render.ts | 10 ++ tests/smoke/extrude-linear.test.tsx | 20 +++ 12 files changed, 275 insertions(+), 106 deletions(-) create mode 100644 examples/extrude-linear.fixture.tsx create mode 100644 lib/intrinsic-jsx.d.ts create mode 100644 lib/jscad-fns/extrude-linear.tsx create mode 100644 tests/fixtures/test-render.ts create mode 100644 tests/smoke/extrude-linear.test.tsx diff --git a/biome.json b/biome.json index 8a8479f..03c497a 100644 --- a/biome.json +++ b/biome.json @@ -29,7 +29,8 @@ "noExplicitAny": "off" }, "complexity": { - "noForEach": "off" + "noForEach": "warn", + "noBannedTypes": "warn" }, "style": { "noNonNullAssertion": "off", diff --git a/examples/extrude-linear.fixture.tsx b/examples/extrude-linear.fixture.tsx new file mode 100644 index 0000000..6b2b6a9 --- /dev/null +++ b/examples/extrude-linear.fixture.tsx @@ -0,0 +1,21 @@ +import { JsCadFixture } from "../lib/components/jscad-fixture" +import { ExtrudeLinear } from "../lib/jscad-fns" +import { Polygon } from "../lib/jscad-fns/polygon" + +export default () => ( + + + + + +) diff --git a/lib/components/jscad-fixture.tsx b/lib/components/jscad-fixture.tsx index eb4e554..797f709 100644 --- a/lib/components/jscad-fixture.tsx +++ b/lib/components/jscad-fixture.tsx @@ -23,10 +23,6 @@ export function JsCadFixture({ const root = createJSCADRoot(jscadGeoms) root.render(children) - // Here, you would typically use the container to render the 3D shape - // For this example, we'll just log the result - console.log("JSCad objects:", jscadGeoms) - const scene = new THREE.Scene() const camera = new THREE.PerspectiveCamera( 75, diff --git a/lib/convert-csg-to-three-geom.ts b/lib/convert-csg-to-three-geom.ts index 1497eb4..8a5c73b 100644 --- a/lib/convert-csg-to-three-geom.ts +++ b/lib/convert-csg-to-three-geom.ts @@ -9,8 +9,6 @@ import { } from "three" export default function convertCSGToThreeGeom(csg): BufferGeometry { - console.log("csg", csg) - if (csg.polygons) { // 3D shape const vertices = [] diff --git a/lib/create-host-config.ts b/lib/create-host-config.ts index 391c497..c3c5e40 100644 --- a/lib/create-host-config.ts +++ b/lib/create-host-config.ts @@ -9,11 +9,162 @@ import type { RoundedCylinderProps, SphereProps, TorusProps, + PolygonProps, + ExtrudeLinearProps, } from "./jscad-fns" import type { JSCADModule, JSCADPrimitive } from "./jscad-primitives" -import type { PolygonProps } from "./jscad-fns/polygon" + +const ex = { + sides: [ + [ + [-2, 2], + [-2, -1], + ], + [ + [-2, -1], + [2, -1], + ], + [ + [2, -1], + [2.5, 2], + ], + [ + [2.5, 2], + [1, 1], + ], + [ + [1, 1], + [0, 2], + ], + [ + [0, 2], + [-1, 1], + ], + [ + [-1, 1], + [-2, 2], + ], + ], + transforms: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], +} export function createHostConfig(jscad: JSCADModule) { + const createInstance = ( + type: string | Function, + props: any, + rootContainerInstance: any, + hostContext: any, + internalInstanceHandle: any, + ) => { + const renderChildren = (children: any) => { + if (Array.isArray(children)) { + // TODO union all children together + throw new Error("Unioning multiple children is not yet supported") + } + if (children) { + // Single child + return createInstance( + children.type, + children.props, + [], + hostContext, + internalInstanceHandle, + ) + } + return null + } + + if (typeof type === "function") { + const element = type(props) + return createInstance( + element.type, + element.props, + rootContainerInstance, + hostContext, + internalInstanceHandle, + ) + } + + switch (type) { + case "cube": + return jscad.primitives.cube({ size: (props as CubeProps).size }) + case "sphere": + return jscad.primitives.sphere({ + radius: (props as SphereProps).radius, + segments: (props as SphereProps).segments, + }) + case "cuboid": + return jscad.primitives.cuboid({ + size: (props as CuboidProps).size, + }) + case "roundedCuboid": + return jscad.primitives.roundedCuboid({ + size: (props as RoundedCuboidProps).size, + roundRadius: (props as RoundedCuboidProps).roundRadius, + }) + case "geodesicSphere": + return jscad.primitives.geodesicSphere({ + radius: (props as GeodesicSphereProps).radius, + frequency: (props as GeodesicSphereProps).frequency, + }) + case "ellipsoid": + return jscad.primitives.ellipsoid({ + radius: (props as EllipsoidProps).radius, + }) + case "cylinder": + return jscad.primitives.cylinder({ + radius: (props as CylinderProps).radius, + height: (props as CylinderProps).height, + startRadius: (props as CylinderProps).startRadius, + endRadius: (props as CylinderProps).endRadius, + }) + case "roundedCylinder": + return jscad.primitives.roundedCylinder({ + radius: (props as RoundedCylinderProps).radius, + height: (props as RoundedCylinderProps).height, + roundRadius: (props as RoundedCylinderProps).roundRadius, + }) + case "cylinderElliptic": + return jscad.primitives.cylinderElliptic({ + radius: (props as CylinderEllipticProps).radius, + height: (props as CylinderEllipticProps).height, + startRadius: (props as CylinderEllipticProps).startRadius, + endRadius: (props as CylinderEllipticProps).endRadius, + startAngle: (props as CylinderEllipticProps).startAngle, + endAngle: (props as CylinderEllipticProps).endAngle, + }) + case "torus": + return jscad.primitives.torus({ + radius: (props as TorusProps).radius, + tube: (props as TorusProps).tube, + }) + case "jscadPolygon": + return jscad.primitives.polygon({ + points: (props as PolygonProps).points, + }) + // biome-ignore lint/suspicious/noFallthroughSwitchClause: + case "extrudeLinear": { + const { children, ...extrudeProps } = props as ExtrudeLinearProps + + const childrenGeometry = renderChildren(children) + + const extrudedGeometry = jscad.extrusions.extrudeLinear( + { + height: extrudeProps.height, + // twistAngle: extrudeProps.twistAngle, + // twistSteps: extrudeProps.twistSteps, + }, + childrenGeometry, + ) + + return extrudedGeometry + } + + default: + throw new Error(`Unknown element type: ${type}`) + } + } + const hostConfig: ReactReconciler.HostConfig< string, // Type Props, // Props @@ -34,89 +185,22 @@ export function createHostConfig(jscad: JSCADModule) { supportsPersistence: false, supportsHydration: false, - createInstance( - type: string, - props: Props, - rootContainerInstance: any, - hostContext: any, - internalInstanceHandle: any, - ) { - switch (type) { - case "cube": - return jscad.primitives.cube({ size: (props as CubeProps).size }) - case "sphere": - return jscad.primitives.sphere({ - radius: (props as SphereProps).radius, - segments: (props as SphereProps).segments, - }) - case "cuboid": - return jscad.primitives.cuboid({ - size: (props as CuboidProps).size, - }) - case "roundedCuboid": - return jscad.primitives.roundedCuboid({ - size: (props as RoundedCuboidProps).size, - roundRadius: (props as RoundedCuboidProps).roundRadius, - }) - case "geodesicSphere": - return jscad.primitives.geodesicSphere({ - radius: (props as GeodesicSphereProps).radius, - frequency: (props as GeodesicSphereProps).frequency, - }) - case "ellipsoid": - return jscad.primitives.ellipsoid({ - radius: (props as EllipsoidProps).radius, - }) - case "cylinder": - return jscad.primitives.cylinder({ - radius: (props as CylinderProps).radius, - height: (props as CylinderProps).height, - startRadius: (props as CylinderProps).startRadius, - endRadius: (props as CylinderProps).endRadius, - }) - case "roundedCylinder": - return jscad.primitives.roundedCylinder({ - radius: (props as RoundedCylinderProps).radius, - height: (props as RoundedCylinderProps).height, - roundRadius: (props as RoundedCylinderProps).roundRadius, - }) - case "cylinderElliptic": - return jscad.primitives.cylinderElliptic({ - radius: (props as CylinderEllipticProps).radius, - height: (props as CylinderEllipticProps).height, - startRadius: (props as CylinderEllipticProps).startRadius, - endRadius: (props as CylinderEllipticProps).endRadius, - startAngle: (props as CylinderEllipticProps).startAngle, - endAngle: (props as CylinderEllipticProps).endAngle, - }) - case "torus": - return jscad.primitives.torus({ - radius: (props as TorusProps).radius, - tube: (props as TorusProps).tube, - }) - case "jscadpolygon": - return jscad.primitives.polygon({ - points: (props as PolygonProps).points, - }) - default: - throw new Error(`Unknown element type: ${type}`) - } - }, + createInstance: createInstance, createTextInstance() { throw new Error("Text elements are not supported in JSCAD") }, appendInitialChild(parentInstance: JSCADPrimitive, child: JSCADPrimitive) { - return jscad.booleans.union(parentInstance, child) as JSCADPrimitive + return parentInstance }, appendChild(parentInstance: JSCADPrimitive, child: JSCADPrimitive) { - return jscad.booleans.union(parentInstance, child) as JSCADPrimitive + return parentInstance }, removeChild(parentInstance: JSCADPrimitive, child: JSCADPrimitive) { - return jscad.booleans.subtract(parentInstance, child) as JSCADPrimitive + return parentInstance }, appendChildToContainer(container: JSCADPrimitive[], child: JSCADPrimitive) { @@ -143,7 +227,7 @@ export function createHostConfig(jscad: JSCADModule) { newProps: Props, ) { // Re-create the instance with new props - return this.createInstance(type, newProps, instance, {}, null) + return createInstance(type, newProps, instance, {}, null) }, finalizeInitialChildren() { diff --git a/lib/intrinsic-jsx.d.ts b/lib/intrinsic-jsx.d.ts new file mode 100644 index 0000000..cc62b0f --- /dev/null +++ b/lib/intrinsic-jsx.d.ts @@ -0,0 +1,20 @@ +import type * as FN from "./jscad-fns" + +declare global { + namespace JSX { + interface IntrinsicElements { + extrudeLinear: FN.ExtrudeLinearProps + cube: FN.CubeProps + sphere: FN.SphereProps + cuboid: FN.CuboidProps + roundedCuboid: FN.RoundedCuboidProps + geodesicSphere: FN.GeodesicSphereProps + ellipsoid: FN.EllipsoidProps + cylinder: FN.CylinderProps + roundedCylinder: FN.RoundedCylinderProps + cylinderElliptic: FN.CylinderEllipticProps + torus: FN.TorusProps + jscadPolygon: FN.PolygonProps + } + } +} diff --git a/lib/jscad-fns/extrude-linear.tsx b/lib/jscad-fns/extrude-linear.tsx new file mode 100644 index 0000000..64fdc2e --- /dev/null +++ b/lib/jscad-fns/extrude-linear.tsx @@ -0,0 +1,23 @@ +export type ExtrudeLinearProps = { + height: number + twistAngle?: number + twistSteps?: number + children: any +} + +export function ExtrudeLinear({ + height, + twistAngle, + twistSteps, + children, +}: ExtrudeLinearProps) { + return ( + + {children} + + ) +} diff --git a/lib/jscad-fns/index.ts b/lib/jscad-fns/index.ts index e68a18f..020ae00 100644 --- a/lib/jscad-fns/index.ts +++ b/lib/jscad-fns/index.ts @@ -8,3 +8,5 @@ export * from "./cylinder" export * from "./rounded-cylinder" export * from "./cylinder-elliptic" export * from "./torus" +export * from "./polygon" +export * from "./extrude-linear" diff --git a/lib/jscad-fns/polygon.tsx b/lib/jscad-fns/polygon.tsx index cd3cfb0..69cdb7d 100644 --- a/lib/jscad-fns/polygon.tsx +++ b/lib/jscad-fns/polygon.tsx @@ -3,5 +3,5 @@ export type PolygonProps = { } export function Polygon({ points }: PolygonProps) { - return + return } diff --git a/lib/jscad-primitives.ts b/lib/jscad-primitives.ts index 0bf8838..94ecfee 100644 --- a/lib/jscad-primitives.ts +++ b/lib/jscad-primitives.ts @@ -1,30 +1,16 @@ // Define a type for the JSCAD module structure we expect export interface JSCADModule { primitives: { - polygon: (options: { - points: [number, number][] - }) => any - cube: (options: { - size: number | [number, number, number] - }) => any - sphere: (options: { - radius: number - segments?: number - }) => any - geodesicSphere: (options: { - radius: number - frequency: number - }) => any - cuboid: (options: { - size: number | [number, number, number] - }) => any + polygon: (options: { points: [number, number][] }) => any + cube: (options: { size: number | [number, number, number] }) => any + sphere: (options: { radius: number; segments?: number }) => any + geodesicSphere: (options: { radius: number; frequency: number }) => any + cuboid: (options: { size: number | [number, number, number] }) => any roundedCuboid: (options: { size: number | [number, number, number] roundRadius: number }) => any - ellipsoid: (options: { - radius: [number, number, number] - }) => any + ellipsoid: (options: { radius: [number, number, number] }) => any cylinder: (options: { radius: number height: number @@ -44,16 +30,22 @@ export interface JSCADModule { startAngle?: number endAngle?: number }) => any - torus: (options: { - radius: number - tube: number - segments?: number - }) => any + torus: (options: { radius: number; tube: number; segments?: number }) => any } booleans: { union: (a: any, b: any) => any subtract: (a: any, b: any) => any } + extrusions: { + extrudeLinear: ( + options: { + height: number + twistAngle?: number + twistSteps?: number + }, + geometry: any, + ) => any + } } // Define types for JSCAD objects and operations @@ -69,6 +61,8 @@ export type JSCADPrimitive = | ReturnType | ReturnType | ReturnType + | ReturnType + export type JSCADOperation = | JSCADModule["booleans"]["union"] | JSCADModule["booleans"]["subtract"] diff --git a/tests/fixtures/test-render.ts b/tests/fixtures/test-render.ts new file mode 100644 index 0000000..e7b09bd --- /dev/null +++ b/tests/fixtures/test-render.ts @@ -0,0 +1,10 @@ +import * as jscad from "@jscad/modeling" +import { createJSCADRenderer } from "../../lib" + +export const testRender = (reactNode: any) => { + const container = [] + const renderer = createJSCADRenderer(jscad as any) + const root = renderer.createJSCADRoot(container) + root.render(reactNode) + return container +} diff --git a/tests/smoke/extrude-linear.test.tsx b/tests/smoke/extrude-linear.test.tsx new file mode 100644 index 0000000..622f02d --- /dev/null +++ b/tests/smoke/extrude-linear.test.tsx @@ -0,0 +1,20 @@ +import { it } from "bun:test" +import { testRender } from "../fixtures/test-render" + +it("should not error when rendering an extrudeLinear", () => { + testRender( + + + , + ) +})