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(
+
+
+ ,
+ )
+})