Skip to content

Commit 6dcd62b

Browse files
authored
Implement Curve feature: add Curve, CurvePath, CatmullRomCurve3, CubicBezierCurve3, QuadraticBezierCurve3, LineCurve3, EllipseCurve classes with sample and routing (#88)
1 parent afa4439 commit 6dcd62b

11 files changed

Lines changed: 738 additions & 9 deletions

File tree

example/client/src/main/scala/dev/cheleb/scalajswebgl/app/HomePage.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ object HomePage:
2121
h2("Three.js Basics"),
2222
ul(
2323
demo("ThreeJS Scene", Router.uiRoute("demo", "three", "scene")),
24-
demo("Math Utils", Router.uiRoute("demo", "three", "math"))
24+
demo("Math Utils", Router.uiRoute("demo", "three", "math")),
25+
demo("Curves", Router.uiRoute("demo", "three", "curve"))
2526
)
2627
),
2728
div(

example/client/src/main/scala/dev/cheleb/scalajswebgl/app/Router.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,9 @@ object Router:
267267
},
268268
path("trackballcontrols") {
269269
dev.cheleb.scalajswebgl.samples.three.TrackballControlsSample()
270+
},
271+
path("curve") {
272+
dev.cheleb.scalajswebgl.samples.three.CurveSample()
270273
}
271274
)
272275
}
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
package dev.cheleb.scalajswebgl.samples.three
2+
3+
import com.raquo.laminar.api.L.*
4+
5+
import THREE.*
6+
import THREE.extras.curves.*
7+
8+
import org.scalajs.dom
9+
import org.scalajs.dom.window
10+
import scala.scalajs.js
11+
import scala.math.Pi
12+
13+
object CurveSample {
14+
15+
def apply() =
16+
17+
val curveDiv = div(
18+
h1("Curve Demo"),
19+
p("Demonstrating different curve types: CatmullRom, Bezier, Line, and Ellipse curves."),
20+
div(
21+
cls := "canvas-container"
22+
)
23+
)
24+
25+
// Create scene
26+
val scene = Scene()
27+
28+
// Create camera
29+
val camera = new PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
30+
camera.position.set(0, 5, 10)
31+
camera.lookAt(0, 0, 0)
32+
33+
// Create renderer
34+
val renderer = WebGLRenderer(antialias = true)
35+
renderer.setSize(window.innerWidth * 0.8, window.innerHeight * 0.8)
36+
renderer.setClearColor("#1a1a2e", 1)
37+
38+
// Create different curves
39+
40+
// 1. CatmullRom curve
41+
val catmullPoints = js.Array(
42+
new Vector3(-5, 0, 0),
43+
new Vector3(-2, 2, 0),
44+
new Vector3(0, 0, 0),
45+
new Vector3(2, -2, 0),
46+
new Vector3(5, 0, 0)
47+
)
48+
val catmullCurve = new CatmullRomCurve3(catmullPoints, closed = true)
49+
val catmullPointsArray = catmullCurve.getPoints(50)
50+
val catmullGeometry = BufferGeometry().setFromPoints(catmullPointsArray.asInstanceOf[js.Array[Vector3]])
51+
val catmullMaterial = LineBasicMaterial(js.Dynamic.literal(color = 0xff0000))
52+
val catmullLine = Line(catmullGeometry, catmullMaterial)
53+
scene.add(catmullLine)
54+
55+
// 2. Cubic Bezier curve
56+
val bezierCurve = new CubicBezierCurve3(
57+
new Vector3(-4, -3, 0),
58+
new Vector3(-2, -1, 0),
59+
new Vector3(2, -1, 0),
60+
new Vector3(4, -3, 0)
61+
)
62+
val bezierPointsArray = bezierCurve.getPoints(50)
63+
val bezierGeometry = BufferGeometry().setFromPoints(bezierPointsArray.asInstanceOf[js.Array[Vector3]])
64+
val bezierMaterial = LineBasicMaterial(js.Dynamic.literal(color = 0x00ff00))
65+
val bezierLine = Line(bezierGeometry, bezierMaterial)
66+
scene.add(bezierLine)
67+
68+
// 3. Quadratic Bezier curve
69+
val quadBezierCurve = new QuadraticBezierCurve3(
70+
new Vector3(-4, -5, 0),
71+
new Vector3(0, -3, 0),
72+
new Vector3(4, -5, 0)
73+
)
74+
val quadBezierPointsArray = quadBezierCurve.getPoints(50)
75+
val quadBezierGeometry = BufferGeometry().setFromPoints(quadBezierPointsArray.asInstanceOf[js.Array[Vector3]])
76+
val quadBezierMaterial = LineBasicMaterial(js.Dynamic.literal(color = 0x0000ff))
77+
val quadBezierLine = Line(quadBezierGeometry, quadBezierMaterial)
78+
scene.add(quadBezierLine)
79+
80+
// 4. Line curve
81+
val lineCurve = new LineCurve3(
82+
new Vector3(-6, 2, 0),
83+
new Vector3(6, 2, 0)
84+
)
85+
val linePointsArray = lineCurve.getPoints(2)
86+
val lineGeometry = BufferGeometry().setFromPoints(linePointsArray.asInstanceOf[js.Array[Vector3]])
87+
val lineMaterial = LineBasicMaterial(js.Dynamic.literal(color = 0xffff00))
88+
val lineLine = Line(lineGeometry, lineMaterial)
89+
scene.add(lineLine)
90+
91+
// 5. Ellipse curve (2D, but we'll display it in 3D space)
92+
val ellipseCurve = new EllipseCurve(0, 3, 3, 2, 0, 2 * Pi, false, 0)
93+
val ellipsePointsArray = ellipseCurve.getPoints(50)
94+
val ellipseGeometry = BufferGeometry().setFromPoints(ellipsePointsArray.asInstanceOf[js.Array[Vector3]])
95+
val ellipseMaterial = LineBasicMaterial(js.Dynamic.literal(color = 0xff00ff))
96+
val ellipseLine = Line(ellipseGeometry, ellipseMaterial)
97+
scene.add(ellipseLine)
98+
99+
// Add lighting
100+
val directionalLight = DirectionalLight(0xffffff, 1)
101+
directionalLight.position.set(5, 5, 5)
102+
scene.add(directionalLight)
103+
104+
val ambientLight = AmbientLight(0x404040, 0.6)
105+
scene.add(ambientLight)
106+
107+
// Animation loop
108+
val animate: () => Unit = () => {
109+
val time = js.Date.now() * 0.001
110+
111+
// Rotate the entire scene slowly
112+
scene.rotation.y = time * 0.1
113+
114+
renderer.render(scene, camera)
115+
}
116+
renderer.setAnimationLoop(animate)
117+
118+
// Handle window resize
119+
val onWindowResize: dom.Event => Unit = { _ =>
120+
camera.aspect = window.innerWidth / window.innerHeight
121+
camera.updateProjectionMatrix()
122+
renderer.setSize(window.innerWidth * 0.8, window.innerHeight * 0.8)
123+
}
124+
window.addEventListener("resize", onWindowResize)
125+
126+
// Append renderer to the canvas container
127+
curveDiv.ref.querySelector(".canvas-container").appendChild(renderer.domElement)
128+
129+
curveDiv
130+
}

0 commit comments

Comments
 (0)