1666830000
Provides 2D and 3D vector types for vector operations in Julia.
Installation
Run one of those commands in the Julia REPL:
Through the SISL registry:
] registry add https://github.com/sisl/Registry
add Vec
Through Pkg
import Pkg
Pkg.add(PackageSpec(url="https://github.com/sisl/Vec.jl.git"))
Usage
Vec.jl
provides several vector types, named after their groups. All types are immutable and are subtypes of 'StaticArrays'' FieldVector
, so they can be indexed and used as vectors in many contexts.
VecE2
provides an (x,y) type of the Euclidean-2 group.VecE3
provides an (x,y,z) type of the Euclidean-3 group.VecSE2
provides an (x,y,theta) type of the special-Euclidean 2 group.v = VecE2(0, 1)
v = VecSE2(0,1,0.5)
v = VecE3(0, 1, 2)
Additional geometry types include Quat
for quaternions, Line
, LineSegment
, and Projectile
.
The switch to StaticArrays brings several breaking changes. If you need a backwards-compatible version, please checkout the v0.1.0
tag with cd(Pkg.dir("Vec")); run(`git checkout v0.1.0`)
.
Author: sisl
Source Code: https://github.com/sisl/Vec.jl
License: View license
1666368600
R package, using vis.js library for 3D visualization
devtools::install_github("DataKnowledge/vis3D")
require(vis3D)
?vis3D
nb <- 10
don <- expand.grid(x = 1:nb, y = 1:nb)
don$z <-rnorm(nb*nb)
vis3D(don)
shiny::runApp(system.file("shiny", package = "vis3D"))
Author: Datastorm-open
Source Code: https://github.com/datastorm-open/vis3D
1666318140
Finite Difference Time Domain (FDTD) method for room acoustic simulation
From the Julia command line hit:
Pkg.clone("https://github.com/nantonel/AcFDTD.jl.git")
Once the package is installed you can update it along with the others issuing Pkg.update()
in the command line.
Import the package by typing using AcFDTD
. First you need to specify an acoustic environment and FDTD scheme:
using AcFDTD
X = 0.1 # spatial sampling
env = FDTDEnv(X,IISO()) # create new acoustic env with default values
where IISO()
returns the Interpolated Isotropic scheme. Alternatively one can choose a samplng frequency instead of a spatial sampling:
Fs = 2000. # sampling frequency in Hz
env = FDTDEnv(IISO(),Fs; c = 340) # create new acoustic env with default values
notice that in the latter line the speed of sound was chosen to be 340
m/s. By default this is set to 343
m/s. Set the acoustic impedance ξ
and room geometry room geometry:
ξ = [50.;50.;100.;30.;50.;50.]; # [ ξx1 ; ξx2 ; ξy1 ; ξy2 ; ξz1 ; ξz2 ]
# [front wall; rear wall; left wall; right wall; floor; ceiling]
geo = CuboidRoom(10, 11, 12, ξ, env)
The first three parameters indicate the number of spatial samples of the x
, y
and z
directions.
Alternatively one can specify the room dimensions in meters:
geo = CuboidRoom(4., 5., 3., ξ, env)
which are then approximated on the grid. Set the number of time steps Nt
Create a band-limited sound source with e.g. the DSP
package:
using DSP
Nt = round(Int,env.Fs) # number of time steps (1 sec)
s = zeros(Nt) # source signal
s[3] = 1
f2 = geo.env.Fs/2*0.175 # cut-off frequency of source
filt!(s,digitalfilter(Bandpass(10,f2;fs = geo.env.Fs),Butterworth(5)),s)
Define the position of microphone and sound sources:
xr = [(2, 2, 2), (2, geo.Ny-1, geo.Nz-1)] # mic positions
xs = (geo.Nx-1, geo.Ny-1, geo.Nz-1) # sound source position
#positions must be Tuple{Int,Int,Int} or Array{Tuple{Int,Int,Int},1}
Now type:
p = fdtd(xr,xs,Nt,geo,s)
to obtain the sound pressure of the microphones.
For more details on the methods type:
?fdtd
AcFDTD.jl is developed by Niccolò Antonello at KU Leuven, ESAT/Stadius.
Author: Nantonel
Source Code: https://github.com/nantonel/AcFDTD.jl
License: View license
1666024390
3D rotations made easy in Julia
This package implements various 3D rotation parameterizations and defines conversions between them. At their heart, each rotation parameterization is a 3×3 unitary (orthogonal) matrix (based on the StaticArrays.jl package), and acts to rotate a 3-vector about the origin through matrix-vector multiplication.
While the RotMatrix
type is a dense representation of a 3×3
matrix, we also have sparse (or computed, rather) representations such as quaternions, angle-axis parameterizations, and Euler angles.
For composing rotations about the origin with other transformations, see the CoordinateTransformations.jl package.
The following operations are supported by all of the implemented rotation parameterizations.
Any two rotations of the same type can be composed with simple multiplication:
q3 = q2*q1
Rotations can be composed with the opposite (or inverse) rotation with the appropriate division operation
q1 = q2\q3
q2 = q3/q1
Any rotation can operate on a 3D vector (represented as a SVector{3}
), again through simple multiplication:
r2 = q*r
which also supports multiplication by the opposite rotation
r = q\r2
The rotation angle and axis can be obtained for any rotation parameterization using
rotation_axis(r::Rotation)
rotation_angle(r::Rotation)
All rotation types support one(R)
to construct the identity rotation for the desired parameterization. A random rotation, uniformly sampled over the space of rotations, can be sampled using rand(R)
. For example:
r = one(QuatRotation) # equivalent to QuatRotation(1.0, 0.0, 0.0, 0.0)
q = rand(QuatRotation)
p = rand(MRP{Float32})
All rotatations can be converted to another parameterization by simply calling the constructor for the desired parameterization. For example:
q = rand(QuatRotation)
aa = AngleAxis(q)
r = RotMatrix(aa)
using Rotations, StaticArrays
# create the identity rotation (identity matrix)
id = one(RotMatrix{3, Float64})
# create a random rotation matrix (uniformly distributed over all 3D rotations)
r = rand(RotMatrix{3}) # uses Float64 by default
# create a point
p = SVector(1.0, 2.0, 3.0) # from StaticArrays.jl, but could use any AbstractVector...
# convert to a quaternion (QuatRotation) and rotate the point
q = QuatRotation(r)
p_rotated = q * p
# Compose rotations
q2 = rand(QuatRotation)
q3 = q * q2
# Take the inverse (equivalent to transpose)
q_inv = transpose(q)
q_inv == inv(q)
p ≈ q_inv * (q * p)
q4 = q3 / q2 # q4 = q3 * inv(q2)
q5 = q3 \ q2 # q5 = inv(q3) * q2
# convert to a Modified Rodrigues Parameter (aka Stereographic quaternion projection, recommended for applications with differentiation)
spq = MRP(r)
# convert to the Angle-axis parameterization, or related Rotation vector
aa = AngleAxis(r)
rv = RotationVec(r)
ϕ = rotation_angle(r)
v = rotation_axis(r)
# convert to Euler angles, composed of X/Y/Z axis rotations (Z applied first)
# (all combinations of "RotABC" are defined)
r_xyz = RotXYZ(r)
# Rotation about the X axis by 0.1 radians
r_x = RotX(0.1)
# Composing axis rotations together automatically results in Euler parameterization
RotX(0.1) * RotY(0.2) * RotZ(0.3) === RotXYZ(0.1, 0.2, 0.3)
# Can calculate Jacobian - derivatives of rotations with respect to parameters
j1 = Rotations.jacobian(RotMatrix, q) # How does the matrix change w.r.t the 4 Quat parameters?
j2 = Rotations.jacobian(q, p) # How does the rotated point q*p change w.r.t. the 4 Quat parameters?
# ... all Jacobian's involving RotMatrix, MRP and Quat are implemented
# (MRP is ideal for optimization purposes - no constaints/singularities)
Rotation Matrix RotMatrix{N, T}
An N×N rotation matrix storing the rotation. This is a simple wrapper for a StaticArrays SMatrix{N,N,T}
. A rotation matrix R
should have the property I = R * R'
, but this isn't enforced by the constructor. On the other hand, all the types below are guaranteed to be "proper" rotations for all input parameters (equivalently: parity conserving, in SO(3), det(r) = 1
, or a rotation without reflection).
Arbitrary Axis Rotation AngleAxis{T}
A 3D rotation with fields theta
, axis_x
, axis_y
, and axis_z
to store the rotation angle and axis of the rotation. Like all other types in this package, once it is constructed it acts and behaves as a 3×3 AbstractMatrix
. The axis will be automatically renormalized by the constructor to be a unit vector, so that theta
always represents the rotation angle in radians.
Quaternions QuatRotation{T}
A 3D rotation parameterized by a unit quaternion. Note that the constructor will renormalize the quaternion to be a unit quaternion, and that although they follow the same multiplicative algebra as quaternions, it is better to think of QuatRotation
as a 3×3 matrix rather than as a quaternion number.
Previously Quat
, UnitQuaternion
.
Rotation Vector RotationVec{T}
A 3D rotation encoded by an angle-axis representation as angle * axis
. This type is used in packages such as OpenCV.
Note: If you're differentiating a Rodrigues Vector check the result is what you expect at theta = 0. The first derivative of the rotation should behave, but higher-order derivatives of it (as well as parameterization conversions) should be tested. The Stereographic Quaternion Projection (MRP
) is the recommended three parameter format for differentiation.
Previously RodriguesVec
.
Rodrigues Parameters RodriguesParam{T}
A 3-parameter representation of 3D rotations that has a singularity at 180 degrees. They can be interpreted as a projection of the unit quaternion onto the plane tangent to the quaternion identity. They are computationally efficient and do not have a sign ambiguity.
Modified Rodrigues Parameter MRP{T}
A 3D rotation encoded by the stereographic projection of a unit quaternion. This projection can be visualized as a pin hole camera, with the pin hole matching the quaternion [-1,0,0,0]
and the image plane containing the origin and having normal direction [1,0,0,0]
. The "identity rotation" Quaternion(1.0,0,0,0)
then maps to the MRP(0,0,0)
These are similar to the Rodrigues vector in that the axis direction is stored in an unnormalized form, and the rotation angle is encoded in the length of the axis. This type has the nice property that the derivatives of the rotation matrix w.r.t. the MRP
parameters are rational functions, making the MRP
type a good choice for differentiation / optimization.
They are frequently used in aerospace applications since they are a 3-parameter representation whose singularity happens at 360 degrees. In practice, the singularity can be avoided with some switching logic between one of two equivalent MRPs (obtained by projecting the negated quaternion).
Previously SPQuat
.
Cardinal axis rotations RotX{T}
, RotY{T}
, RotZ{T}
Sparse representations of 3D rotations about the X, Y, or Z axis, respectively.
Two-axis rotations RotXY{T}
, etc
Conceptually, these are compositions of two of the cardinal axis rotations above, so that RotXY(x, y) == RotX(x) * RotY(y)
(note that the order of application to a vector is right-to-left, as-in matrix-matrix-vector multiplication: RotXY(x, y) * v == RotX(x) * (RotY(y) * v)
).
Euler Angles - Three-axis rotations RotXYZ{T}
, RotXYX{T}
, etc
A composition of 3 cardinal axis rotations is typically known as a Euler angle parameterization of a 3D rotation. The rotations with 3 unique axes, such as RotXYZ
, are said to follow the Tait Bryan angle ordering, while those which repeat (e.g. EulerXYX
) are said to use Proper Euler angle ordering.
Like the two-angle versions, the order of application to a vector is right-to-left, so that RotXYZ(x, y, z) * v == RotX(x) * (RotY(y) * (RotZ(z) * v))
. This may be interpreted as an "extrinsic" rotation about the Z, Y, and X axes or as an "intrinsic" rotation about the X, Y, and Z axes. Similarly, RotZYX(z, y, x)
may be interpreted as an "extrinsic" rotation about the X, Y, and Z axes or an "intrinsic" rotation about the Z, Y, and X axes.
It is often convenient to consider perturbations or errors about a particular 3D rotation, such as applications in state estimation or optimization-based control. Intuitively, we expect these errors to live in three-dimensional space. For globally non-singular parameterizations such as unit quaternions, we need a way to map between the four parameters of the quaternion to this three-dimensional plane tangent to the four-dimensional hypersphere on which quaternions live.
There are several of these maps provided by Rotations.jl:
ExponentialMap
: A very common mapping that uses the quaternion exponential and the quaternion logarithm. The quaternion logarithm converts a 3D rotation vector (i.e. axis-angle vector) to a unit quaternion. It tends to be the most computationally expensive mapping.
CayleyMap
: Represents the differential quaternion using Rodrigues parameters. This parameterization goes singular at 180° but does not inherit the sign ambiguities of the unit quaternion. It offers an excellent combination of cheap computation and good behavior.
MRPMap
: Uses Modified Rodrigues Parameters (MRPs) to represent the differential unit quaternion. This mapping goes singular at 360°.
QuatVecMap
: Uses the vector part of the unit quaternion as the differential unit quaternion. This mapping also goes singular at 180° but is the computationally cheapest map and often performs well.
Rotations.jl provides the RotationError
type for representing rotation errors, that act just like a SVector{3}
but carry the nonlinear map used to compute the error, which can also be used to convert the error back to a QuatRotation
(and, by extention, any other 3D rotation parameterization). The following methods are useful for computing RotationError
s and adding them back to the nominal rotation:
rotation_error(R1::Rotation, R2::Rotation, error_map::ErrorMap) # compute the error between `R1` and `R2` using `error_map`
add_error(R::Rotation, err::RotationError) # "adds" the error to `R` by converting back a `UnitQuaterion` and composing with `R`
or their aliases
R1 ⊖ R2 # caclulate the error using the default error map
R1 ⊕ err # alias for `add_error(R1, err)`
For a practical application of these ideas, see the quatenrion-multiplicative Extended Kalman Filter (MEKF). This article provides a good description.
When taking derivatives with respect to quaternions we need to account both for these mappings and the fact that local perturbations to a rotation act through composition instead of addition, as they do in vector space (e.g. q * dq
vs x + dx
). The following methods are useful for computing these Jacobians for QuatRotation
, RodriguesParam
or MRP
∇rotate(q,r)
: Jacobian of the q*r
with respect to the rotation∇composition1(q2,q1)
: Jacobian of q2*q1
with respect to q1∇composition2(q2,q1,b)
: Jacobian of q2*q1
with respect to q2∇²composition1(q2,q1)
: Jacobian of ∇composition1(q2,q2)'b
where b is an arbitrary vector∇differential(q)
: Jacobian of composing the rotation with an infinitesimal rotation, with respect to the infinitesimal rotation. For unit quaternions, this is a 4x3 matrix.∇²differential(q,b)
: Jacobian of ∇differential(q)'b
for some vector b.All parameterizations can be converted to and from (mutable or immutable) 3×3 matrices, e.g.
using StaticArrays, Rotations
# export
q = QuatRotation(1.0,0,0,0)
matrix_mutable = Array(q)
matrix_immutable = SMatrix{3,3}(q)
# import
q2 = Quaternion(matrix_mutable)
q3 = Quaternion(matrix_immutable)
This package assumes active (right handed) rotations where applicable.
They're faster (Julia's Array
and BLAS aren't great for 3×3 matrices) and don't need preallocating or garbage collection. For example, see this benchmark case where we get a 20× speedup:
julia> cd(Pkg.dir("Rotations") * "/test")
julia> include("benchmark.jl")
julia > BenchMarkRotations.benchmark_mutable()
Rotating using mutables (Base.Matrix and Base.Vector):
0.124035 seconds (2 allocations: 224 bytes)
Rotating using immutables (Rotations.RotMatrix and StaticArrays.SVector):
0.006006 seconds
Author: JuliaGeometry
Source Code: https://github.com/JuliaGeometry/Rotations.jl
License: View license
1665446580
According to the authorities at Urban Dictionary, ‘drip’ is synonymous with ‘immense swag.’ This package brings some D3 drip to R.
You can install d3rain
from GitHub via:
remotes::install_github("daranzolin/d3rain")
‘Rain’ visualizations are useful aids to observe the relationship between a ranked, numeric variable (e.g. percentile, rank, etc.) and any factored, categorical variable.
library(d3rain)
library(dplyr)
armed_levels <- c('No', 'Knife', 'Non-lethal firearm', 'Firearm')
pk <- fivethirtyeight::police_killings %>%
filter(armed %in% armed_levels,
!is.na(age)) %>%
mutate(armed = factor(armed, levels = armed_levels))
pk %>%
d3rain(age, armed, toolTip = raceethnicity, title = "2015 Police Killings by Age, Armed Status") %>%
drip_settings(dripSequence = 'iterate',
ease = 'bounce',
jitterWidth = 20,
dripSpeed = 1000,
dripFill = 'firebrick') %>%
chart_settings(fontFamily = 'times',
yAxisTickLocation = 'left')
drip_settings
adjusts the drip sequence, easing animation, jitter width, drip speed, and color. chart_settings
controls the axes displays, font size, font family, and background color.
You can adjust the drip iteration by reordering the data frame:
pk %>%
arrange(age) %>%
d3rain(age, armed, toolTip = raceethnicity, title = "2015 Police Killings by Age, Armed Status") %>%
drip_settings(dripSequence = 'iterate',
ease = 'linear',
jitterWidth = 25,
dripSpeed = 500,
dripFill = 'steelblue') %>%
chart_settings(fontFamily = 'times',
yAxisTickLocation = 'left')
Create rain histograms with d3rain_hist
and the accompanying functions:
d <- readr::read_csv("https://raw.githubusercontent.com/babeheim/citation-gates/master/citation-data-simulated.csv") %>%
rename(Downloaded = downloaded,
Contacted = contacted,
Replied = reply_received,
Received = data_received) %>%
mutate(Total = TRUE,
`Downloaded and Received` = case_when(
Downloaded ~ TRUE,
Received ~ TRUE,
TRUE ~ FALSE)
)
d %>%
d3rain_hist(x = year,
levels = c("Total", "Contacted", "Replied", "Downloaded and Received"),
title = "Citation Statuses, 1960-2019") %>%
hist_chart_settings(annotations = c("Total Sample: 560", "475 (95%)", "309 (65%)", "147 (26%)"),
levelLabelLocation = "right") %>%
hist_drip_settings(colors = c("black", "forestgreen", "orange", "firebrick"),
transitionIntervals = 2500,
dripSpeed = 300)
Author: Daranzolin
Source Code: https://github.com/daranzolin/d3rain
License: Unknown, MIT licenses found
1665138300
3D software rendering in pure Go. No OpenGL, no C extensions, no nothin'.
It's like OpenGL, but it's not. It's FauxGL.
It doesn't use your graphics card, only your CPU. So it's slow and unsuitable for realtime rendering. But it's still pretty fast. It works the same way OpenGL works - rasterizing.
FauxGL uses all of your CPU cores. But none of your GPU.
Rendering the Stanford Dragon shown above (871306 triangles) at 1920x1080px takes about 150 milliseconds on my machine. With 4x4=16x supersampling, it takes about 950 milliseconds. This is the time to render a frame and does not include loading the mesh from disk.
go get -u github.com/fogleman/fauxgl
cd go/src/github.com/fogleman/fauxgl
go run examples/hello.go
https://godoc.org/github.com/fogleman/fauxgl
package main
import (
. "github.com/fogleman/fauxgl"
"github.com/nfnt/resize"
)
const (
scale = 1 // optional supersampling
width = 1920 // output width in pixels
height = 1080 // output height in pixels
fovy = 30 // vertical field of view in degrees
near = 1 // near clipping plane
far = 10 // far clipping plane
)
var (
eye = V(-3, 1, -0.75) // camera position
center = V(0, -0.07, 0) // view center position
up = V(0, 1, 0) // up vector
light = V(-0.75, 1, 0.25).Normalize() // light direction
color = HexColor("#468966") // object color
)
func main() {
// load a mesh
mesh, err := LoadOBJ("examples/dragon.obj")
if err != nil {
panic(err)
}
// fit mesh in a bi-unit cube centered at the origin
mesh.BiUnitCube()
// smooth the normals
mesh.SmoothNormalsThreshold(Radians(30))
// create a rendering context
context := NewContext(width*scale, height*scale)
context.ClearColorBufferWith(HexColor("#FFF8E3"))
// create transformation matrix and light direction
aspect := float64(width) / float64(height)
matrix := LookAt(eye, center, up).Perspective(fovy, aspect, near, far)
// use builtin phong shader
shader := NewPhongShader(matrix, light, eye)
shader.ObjectColor = color
context.Shader = shader
// render
context.DrawMesh(mesh)
// downsample image for antialiasing
image := context.Image()
image = resize.Resize(width, height, image, resize.Bilinear)
// save image
SavePNG("out.png", image)
}
Author: fogleman
Source Code: https://github.com/fogleman/fauxgl
License: MIT license
1664905740
Compose3D is a Julia package written to try and extend Compose to 3-D. Currently, only WebGL backends using Three.JS are supported which allows you to draw 3D in a Jupyter notebook. The long term goal would be to integrate this package with the Compose package.
Please check the exp folder for some example IJulia notebooks.
Documentation
Contexts are the things that you are able to draw. Contexts are created by specifying an origin point and the width, height and depth of the required context.
You can use the Context constructor to create a Context.
Geometries look to provide the user with primitives for creation of 3-D shapes.
Current primitives implemented are :
Functions available for users to use to create such geometries are:
side
Materials are to add properties to the 3D objects like color and texture maps.
Functions available currently are:
color
can be a string or any Color in Color.jl that can be converted to RGB. All geometries that are children of this node and are part of the subtree at the same level will be coloured with this color.Drawing things is done by composing a root Context with other Contexts or Geometries.
Compositions work exactly like in Compose except for :
The function to be used by the user is the compose function.
Compose can also take inputs in S-tree formats to build a tree. This saves the user from having to call compose again and again.
Compose3D makes use of a similar measure system to Compose. The basic unit is of 'mm'.
Absolute measure units can be used like :
Relative measures can also be used where the units will be :
Absolute and relative measures can be combined.
cube1 = cube(0mm,0mm,0mm,1mm) # a cube of size 1 unit at the origin.
sphere1 = sphere(-5mm,0mm,0mm,0.1w) # a sphere with radius 1/10th of the width of the parent context and centered at (-5,0,0).
pyramid1 = pyramid(0mm,0mm,0.4d,2mm,1mm) # a pyramid with a corner at (0,0, 0.4*depth of parent context) of base 2mm and height 1mm.
context1 = Context(0mm,0mm,0mm,10mm,10mm,10mm) # context with origin at (0,0,0) and all dimensions of 10 units
context2 = compose(context1,cube1) # Returns context with the cube.
context3 = compose(context2, sphere1, pyramid1) # Returns a context with all the geometries. Notice how we added 2 children in one line.
The drawing of a Sierpenski Pyramid can be done with the following code:
function sierpinski(n)
if n == 0
compose(Context(0w,0h,0d,1w,1h,1d),pyramid(0w,0h,0d,1w,1h))
else
t = sierpinski(n - 1)
compose(Context(0w,0h,0d,1w,1h,1d),
(Context(0w,0h,0d,(1/2)w,(1/2)h,(1/2)d), t),
(Context(0.5w,0h,0d,(1/2)w,(1/2)h,(1/2)d), t),
(Context(0.5w,0.5h,0d,(1/2)w,(1/2)h,(1/2)d), t),
(Context(0w,0.5h,0d,(1/2)w,(1/2)h,(1/2)d), t),
(Context(0.25w,0.25h,0.5d,(1/2)w,(1/2)h,(1/2)d), t))
end
end
compose(Context(-5mm,-5mm,-5mm,10mm,10mm,10mm),sierpinski(0))
pranavtbhat for supplying the initial JavaScript code and getting this project started.
Author: Rohitvarkey
Source Code: https://github.com/rohitvarkey/Compose3D.jl
License: View license
1664858040
This package supports loading 3D model file formats: obj
, stl
, ply
, off
and 2DM
. More 3D model formats will be supported in the future.
Enter package mode in the Julia REPL and run the following command:
pkg> add FileIO MeshIO
Loading works over the FileIO interface. This means loading a mesh is as simple as this:
using FileIO
mesh = load("path/to/mesh.obj")
Displaying a mesh can be achieved with Makie.
Functions for mesh manipulation can be found in Meshes and JuliaGeometry
MeshIO now has the HomogenousMesh type. Name is still not settled, but it's supposed to be a dense mesh with all attributes either having the length of one (constant over the whole mesh) or the same length (per vertex). This meshtype holds a large variability for all the different attribute mixtures that I've encountered while trying to visualize things over at GLVisualize. This is the best type I've found so far to encode this large variability, without an explosion of functions.
The focus is on conversion between different mesh types and creation of different mesh types. This has led to some odd seeming design choices. First, you can get an attribute via decompose(::Type{AttributeType}, ::Mesh)
. This will try to get this attribute, and if it has the wrong type try to convert it, or if it is not available try to create it. So decompose(Point3{Float32}, mesh)
on a mesh with vertices of type Point3{Float64}
will return a vector of type Point3{Float32}
. Similarly, if you call decompose(Normal{3, Float32}, mesh)
but the mesh doesn't have normals, it will call the function normals(mesh.vertices, mesh.faces, Normal{3, Float32}
, which will create the normals for the mesh. As most attributes are independent, this enables us to easily create all kinds of conversions. Also, I can define decompose
for arbitrary geometric types. decompose{T}(Point3{T}, r::Rectangle)
can actually return the needed vertices for a rectangle. This together with convert
enables us to create mesh primitives like this:
MeshType(Cube(...))
MeshType(Sphere(...))
MeshType(Volume, 0.4f0) #0.4f0 => isovalue
Similarly, I can pass a meshtype to an IO function, which then parses only the attributes that I really need. So passing Mesh{Point3{Float32}, Face3{UInt32}}
to the obj importer will skip normals, uv coordinates etc, and automatically converts the given attributes to the right number type.
To put this one level further, the Face
type has the index offset relative to Julia's indexing as a parameter (e.g. Face3{T, 0}
is 1 indexed). Also, you can index into an array with this face type, and it will convert the indexes correctly while accessing the array. So something like this always works, independent of the underlying index offset:
v1, v2, v3 = vertices[face]
Also, the importer is sensitive to this, so if you always want to work with 0-indexed faces (like it makes sense for opengl based visualizations), you can parse the mesh already as an 0-indexed mesh, by just defining the mesh format to use Face3{T, -1}
. (only the OBJ importer yet)
Small example to demonstrate the advantage for IO:
#Export takes any mesh
function write{M <: Mesh}(msh::M, fn::File{:ply_binary})
# even if the native mesh format doesn't have an array of dense points or faces, the correct ones will
# now be created, or converted:
vts = decompose(Point3{Float32}, msh) # I know ply_binary needs Point3{Float32}
fcs = decompose(Face3{Int32, -1}, msh) # And 0 indexed Int32 faces.
#write code...
end
Author: JuliaIO
Source Code: https://github.com/JuliaIO/MeshIO.jl
License: View license
1664338560
This is an htmlwidgets-based visualization tool for hierarchical data. It is zoomable, meaning that you can interact with the hierarchy and zoom in/out accordingly.
Enjoy!
This package is currently not on CRAN, but you can install it from GitHub via devtools
:
library("devtools");
devtools::install_github("armish/coffeewheel");
library("coffeewheel");
coffeewheel(sampleWheelData, width=500, height=500, main="Sample Wheel Title", partitionAttribute="value");
sampleWheelData <- list(
list(
name="R",
children=list(
list(name="R_1", colour="#110000"),
list(name="R_3", colour="#330000"),
list(name="R_5", colour="#550000"),
list(name="R_7", colour="#770000"),
list(name="R_9", colour="#990000"),
list(name="R_b", colour="#bb0000"),
list(name="R_d", colour="#dd0000"),
list(name="R_f", colour="#ff0000")
)
),
list(
name="G",
children=list(
list(name="G_1", colour="#001100"),
list(name="G_3", colour="#003300"),
list(name="G_5", colour="#005500"),
list(name="G_7", colour="#007700"),
list(name="G_9", colour="#009900"),
list(name="G_b", colour="#00bb00"),
list(name="G_d", colour="#00dd00"),
list(name="G_f", colour="#00ff00")
)
),
list(
name="B",
children=list(
list(name="B_1", colour="#000011"),
list(name="B_3", colour="#000033"),
list(name="B_5", colour="#000055"),
list(name="B_7", colour="#000077"),
list(name="B_9", colour="#000099"),
list(name="B_b", colour="#0000bb"),
list(name="B_d", colour="#0000dd"),
list(name="B_f", colour="#0000ff")
)
)
);
This version of the wheel was adapted from Jason Davies's Coffee Wheel example. Built with D3.js. Sunburst zooming based on an example by Mike Bostock.
The visualization is D3-based and the actual wheel visualization code is adapted from:
https://www.jasondavies.com/coffee-wheel/
Author: Armish
Source Code: https://github.com/armish/coffeewheel
1662616037
In today's post we will learn about 5 Favorite Node.js 3D Libraries.
D3 (or D3.js) is a JavaScript library for visualizing data using web standards. D3 helps you bring data to life using SVG, Canvas and HTML. D3 combines powerful visualization and interaction techniques with a data-driven approach to DOM manipulation, giving you the full capabilities of modern browsers and the freedom to design the right visual interface for your data.
Vectors, Quaternions, Matrices and Transforms for 3D graphics in Node.js
With npm:
npm install math3d
All classes except Transform provide immutable objects.
As I used this project later on with Unity3D, I tried to keep everything as similar as possible. The coordinate system is the same as in Unity: y-Axis up, x-Axis right, z-Axis forward. The rotation order for Euler angles (used in Quaternion) is z then x then y.
A three-dimensional vector with x, y, z values; used for positions, directions or scales in 3D space.
var Vector3 = math3d.Vector3;
var v1 = new Vector3(42, 42, 42);
v1.add(Vector3.up); // Vector3(42, 43, 42);
Build BREP Solids with OpenCascade and NodeJS - 3D Modeling
$npm install node-occ
(use nodejs 12 or 14)
# installing nodejs and gyp utility to build extensions
sudo apt-get install nodejs npm
sudo npm install node-pre-gyp -g
sudo npm install mocha@7 -g
#installing cmake
sudo apt-get install cmake cmake-curses-gui g++ build-essential libtbb2
# ------------------------------------
git clone --recursive https://github.com/erossignon/node-occ.git
cd node-occ
# download prebuild OpenCascade Library and header files
bash ./prepare_node.sh
#
export OCCT_PACKAGE=occt-7.2.0
export LD_LIBRARY_PATH=`pwd`/${OCCT_PACKAGE}/lib:$LD_LIBRARY_PATH
npm install --build-from-source
# verify that everything is working OK
make test
Node client for shapeways api
npm install shapeways
git clone git://github.com/Shapeways/node-shapeways.git
cd ./node-shapeways
npm install
You can build documentation with.
git clone git://github.com/Shapeways/node-shapeways.git
cd ./node-shapeways
make docs
Then access documentation at /docs/index.html
Node.js SDK to communicate with Aspose.3D REST API. Create, Edit or Convert 3D files & objects in the Cloud.
Register an account at Aspose Cloud Dashboard to get you application information. Next, get the SDK package from NPM Distribution using the command npm install aspose3dcloud --save
.
// Get your ClientId and ClientSecret from https://dashboard.aspose.cloud (free registration required).
const threeDCloudApi = new ThreeDCloudApi("MY_CLIENT_ID", "MY_CLIENT_SECRET");
var req = new model.postConvertByFormatRequest()
req.name = "sample.ply";
req.newformat = "pdf";
req.newfilename = "saveasformat.pdf";
req.folder = "3DTest";
req.isOverwrite = true;
req.storage = "My_Storage_Name";
return threeDCloudApi.postConvertByFormat(req)
.then((result) => {
expect(result.response.statusCode).to.equal(200);
});
An interactive 3D animation using WebGL to depict a 2D predator prey ecology on a grid real-time mapped onto the surface of a 3D torus. Sound file is parsed then visualized both in time and frequency domains as well as rendered using Web Audio API - this is an exercise where I taught myself how to display data for an ongoing project on sound synthesis
Installation
Visit nodejs.org and install Node.js.
Clone this repository to your local machine:
git clone git@github.com:scottstensland/webgl-3d-animation.git
Change directory into the project folder:
cd webgl-3d-animation
Then install the dependent modules:
npm install
Launch the nodejs app:
npm start
Using a WebGL savvy browser, point it at URL :
http://localhost:8888
Thank you for following this article.
Top 5 JavaScript Libraries to Check Out Right Now!
1661876100
This module contains functions related to the representation of 3D rotations of reference frames. It is used on a daily basis on projects at the Brazilian National Institute for Space Research (INPE).
This package can be installed using:
julia> Pkg.update()
julia> Pkg.add("ReferenceFrameRotations")
See the package documentation.
Author: JuliaSpace
Source Code: https://github.com/JuliaSpace/ReferenceFrameRotations.jl
License: View license
1661084580
Fast, robust 2D and 3D geometrical predicates on generic point types. Implementation follows algorithms described in the Arepo paper and used (for e.g.) in the Illustris Simulation. License: MIT. Bug reports welcome!
Calculations are initially performed on Float64
while bounding max absolute errors. If unable to determine result, fall back to exact calculation using BigInt
s. This is a form of floating point filtering. Most calculations are cached for fast repeated testing of incircle/intriangle predicates.
Float64
. In addition all must reside in the range 1.0<=x<2.0
. In this range, according to IEEE754, Float64
s have a constant exponent, hence their mantissa can be used for a one to one mapping to integers, which in turn are used for exact calculations using BigInt
s.]add GeometricalPredicates
For Julia 0.6 and older
Pkg.add("GeometricalPredicates")
using GeometricalPredicates
# create a 2D point in (x, y) = (1.1, 1.9)
mypoint = Point(1.1, 1.9)
typeof(mypoint) # -> Point2D
# create a 3D point in (x, y, z) = (1.1, 1.9, 1.5)
mypoint = Point(1.1, 1.9, 1.5)
typeof(mypoint) # -> Point3D
# getting coordinates:
getx(mypoint) # -> 1.1
gety(mypoint) # -> 1.9
getz(mypoint) # -> 1.5
Point2D
is a subtype of AbstractPoint2D
, and Point3D
is a subtype of AbstractPoint3D
. You can implement custom point types by subtyping these abstract types. These custom point types can be used with the rest of the package:
type MyCustomPointType <: AbstractPoint2D
_x::FLoat64
_y::Float64
_mass::Float64
end
getx(p::MyCustomPointType) = p._x
gety(p::MyCustomPointType) = p._y
implementing getx
, gety
, and getz
for 3D points is necessary as this is the interface the package is expecting. These function should return Float64
. Points can be either immutables or types. Default Point2D
and Point3D
are immutables.
The point coordinates must reside in a region 1.0 <= x < 2.0
. Read above on why this limitation is necessary. For convenience there are 2 constants defined, min_coord
and max coord
representing the minimal and maximal feasible values of coordinates.
A triangle is the 2D primitive, and a tetrahedron is the 3D primitive.
# create a triangle using 3 points
a = Point(1.1, 1.1)
b = Point(1.9, 1.1)
c = Point(1.1, 1.9)
mytriangle = Primitive(a, b, c)
typeof(mytriangle) # -> UnOrientedTriangle{Point2D}
The triangle is unoriented in the sense that orientation is not-known in advance, it is not immutable and it could change if points in the triangle are updated. The orientation needs to be calculated when the triangle is created and when points within are updated. Read below for the definition of orientation. The triangle could be created using any points inheriting from AbstractPoint2D
which implement getx
and gety
, or using coordinates directly:
mytriangle = Primitive(1.1, 1.1, 1.9, 1.1, 1.1, 1.9)
# Getting point `a` in the triangle
geta(mytriangle) # -> Point2D(1.1, 1.1)
getb(mytriangle) # -> Point2D(1.9, 1.1)
getc(mytriangle) # -> Point2D(1.1, 1.9)
The same goes for tetrahedrons, except we now use 4 3D points instead of 3 2D ones:
# create a tetrahedron using 4 points
a = Point(1.1, 1.1, 1.1)
b = Point(1.9, 1.1, 1.1)
c = Point(1.1, 1.9, 1.1)
d = Point(1.1, 1.1, 1.9)
mytetraedron = Primitive(a, b, c, d)
typeof(mytetrahedron) # -> UnOrientedTetrahedron{Point3D}
For certain applications we use primitives with known orientation. In those cases there should be no need to calculate it. This is achieved by passing an orientation
flag to the Primitive
creation function:
mytetrahedron = Primitive(a, b, c, d, positivelyoriented)
typeof(mytetrahedron) # -> PositivelyOrientedTetrahedron{Point3D}
orientation(mytetrahedron) # -> constant 1, not calculated
mytetrahedron = Primitive(a, b, c, d, negativelyoriented)
typeof(mytetrahedron) # -> NegativelyOrientedTetrahedron{Point3D}
orientation(mytetrahedron) # -> constant -1, not calculated
Note that when the primitive is oriented the real orientation is never calculated. It is assumed that the user knows what he's doing. If in doubt, just use unoriented primitives at the cost of actual calculation.
Updating points in primitives can be done with seta
, setb
, etc. methods:
seta(mytriangle, Point(1.7, 1.7))
Updating a point in a primitive will fire all relevant pre-calculations. i.e. if the triangle is unoriented then orientation will be calculated. If it is oriented then still a few other pre-calculations will be done, but a few less. If there is need to update a number of points it is thus more efficient to do so in a group update:
setab(mytriangle, Point(1.7, 1.7), Point(1.3, 1.1))
setbcd(mytetrahedron, Point(1.1, 1.1, 1.2), Point(1.2,1.1,1.3), Point(1.4,1.1,1.2))
combinations for all points exist. The name always contains the point names in alphabetical order. As long as inner primitive data is not changed manually, it will keep giving correct results for all functions in this package.
incircle
is the popular name to test whether a point lies inside of the sphere defined by the primitive points:
a = Point(1.1, 1.1)
b = Point(1.5, 1.1)
c = Point(1.1, 1.5)
mytriangle = Primitive(a, b, c)
incircle(mytriangle, Point(1.9, 1.9)) # -> -1, i.e. outside
incircle(mytriangle, Point(1.2, 1.2)) # -> +1, i.e. inside
incircle(mytriangle, Point(1.5, 1.5)) # -> 0, i.e. point is exactly on circle
There is one more possibility. If the circle defined by our primitive has infinite radius then it is impossible to tell whether the point is inside or outside:
a = Point(1.1, 1.1)
b = Point(1.2, 1.2)
c = Point(1.3, 1.3)
mytriangle = Primitive(a, b, c)
incircle(mytriangle, Point(1.3, 1.4)) # -> +2, i.e. cannot tell
intriangle
is a popular name to test whether a point lies inside of the primitive:
a = Point(1.1, 1.1)
b = Point(1.5, 1.1)
c = Point(1.1, 1.5)
mytriangle = Primitive(a, b, c)
intriangle(mytriangle, Point(1.2, 1.2)) # -> +1, i.e. inside
intriangle(mytriangle, Point(1.6, 1.6)) # -> -1, i.e. outside
intriangle(mytriangle, Point(1.3, 1.1)) # -> 4, i.e. exactly on ab
intriangle(mytriangle, Point(1.1, 1.3)) # -> 3, i.e. exactly on ac
intriangle(mytriangle, Point(1.3, 1.3)) # -> 2, i.e. exactly on bc
Here any negative number means outside. The exact value gives some information regarding the direction in which the point lies outside:
-1
means the test point is in front of a, and outside of the triangle-2
means the test point is in front of b, and outside of the triangle-3
means the test point is in front of c, and outside of the triangle same goes for tetrahedrons. Note that the point could be both in front of a and b. In cases as this one is arbitrarily chosen, all in name of performance.1
means test point is inside. But there are other possible positive values:
1 + 1 = 2
means the test point is in front of a, exactly on the triangle1 + 2 = 3
means the test point is in front of b, exactly on the triangle1 + 3 = 4
means the test point is in front of c, exactly on the trianglesame extends for tetrahedrons.
orientation
tests for the primitive orientation. In 2D this means:
1
--> point c
is to the left of the line defined by ab
(with directionality from a
to b
)-1
--> point c
is to the right0
--> point c
is exactly on the linein 3D it means:
1
--> point d
is above the plane defined by abc
(note "above" here means the direction of the plane normal, which depends on its orientation)-1
--> point d
is below the plane0
--> point c
is exactly on the planeAnother convenience API to test for orientation in 2D is using a line. It has some performance advantages over creating a triangle:
a = Point(1.1, 1.1)
b = Point(1.5, 1.5)
l = Line(a, b)
println(orientation(l, Point(1.4, 1.6))) # --> 1
println(orientation(l, Point(1.4,1.05))) # --> -1
println(orientation(l, Point(1.4,1.40))) # --> 0
One can also create simple 2D polygons:
ll = Point(1.0,1.0)
lr = Point(1.2,1.0)
ur = Point(1.2,1.2)
ul = Point(1.0,1.2)
poly = Polygon(ll, lr, ur, ul)
inpolygon(poly, Point(1.1,1.1)) # assumes it is convex
orientation
gives the primitive orientation. area
, volume
, centroid
, circumcenter
, circumradius2
are all exported and I hope self descriptive.
Scale and scale-free Peano-Hilbert ordering is available. Look here for a nice explanation on Hilbert sorting and here for a nice explanation of multiscale sort. Both are implemented here:
p = Point(1.1, 1.2)
peanokey(p, 4) # -> 6, the peano key in a regular grid of 2^4 X 2^4 cells
p = Point(1.1, 1.2, 1.3)
peanokey(p, 4) # -> 94, the peano key in a regular grid of 2^4 X 2^4 X 2^4 cells
The number of cells doesn't need to be specified. The default for 2D is 2^31 X 2^31
and for 3D 2^21 X 2^21 X 2^21
. You can also do the inverse, and get a point from a key:
Point2D(6, 4) # -> Point2D(1.0625,1.1875)
in a finer grid we would get back something more accurate.
So that was scale-dependent Hilbert stuff, which is good to say balance workload between computing nodes. When you need to order particles spatially it is better to use a scale independent method, like the Hilbert ordering here:
a = [Point(1.0+rand(), 1.0+rand() for i in 1:1e6]
hilbertsort!(a)
Here keys are never calculated, and there is no grid, it uses a median policy, adjusting to the actual point distribution. There are a few parameters with good defaults, see links above to understand what they mean. For an algorithm such a Delaunay tesselation it is sometimes better to use a multi-scale sort with a Hilbert sort, like this:
mssort!(a)
of course this adds a few more default parameters, again with decent defaults, read above links to understand.
Author: JuliaGeometry
Source Code: https://github.com/JuliaGeometry/GeometricalPredicates.jl
License: View license
1659692340
MeshCat.jl: Julia bindings to the MeshCat WebGL viewer
MeshCat is a remotely-controllable 3D viewer, built on top of three.js. The viewer contains a tree of objects and transformations (i.e. a scene graph) and allows those objects and transformations to be added and manipulated with simple commands. This makes it easy to create 3D visualizations of geometries, mechanisms, and robots. MeshCat.jl runs on macOS, Linux, and Windows.
The MeshCat viewer runs entirely in the browser, with no external dependencies. All files are served locally, so no internet connection is required. Communication between the browser and your Julia code is managed by HTTP.jl. That means that MeshCat should work:
As much as possible, MeshCat.jl tries to use existing implementations of its fundamental types. In particular, we use:
That means that MeshCat should play well with other tools in the JuliaGeometry ecosystem like MeshIO.jl, Meshing.jl, etc.
Demos
For detailed examples of usage, check out demo.ipynb.
To learn about the animation system (introduced in MeshCat.jl v0.2.0), see animation.ipynb.
Related Projects
MeshCat.jl is a successor to DrakeVisualizer.jl, and the interface is quite similar (with the exception that we use setobject!
instead of setgeometry!
). The primary difference is that DrakeVisualizer required Director, LCM, and VTK, all of which could be difficult to install, while MeshCat just needs a web browser. MeshCat also has better support for materials, textures, point clouds, and complex meshes.
You may also want to check out:
Examples
using MeshCat
vis = Visualizer()
open(vis)
## In an IJulia/Jupyter notebook, you can also do:
# IJuliaCell(vis)
using GeometryBasics
using CoordinateTransformations
setobject!(vis, HyperRectangle(Vec(0., 0, 0), Vec(1., 1, 1)))
settransform!(vis, Translation(-0.5, -0.5, 0))
using ColorTypes
verts = rand(Point3f, 100_000)
colors = [RGB(p...) for p in verts]
setobject!(vis, PointCloud(verts, colors))
# Visualize a mesh from the level set of a function
using Meshing
f = x -> sum(sin, 5 * x)
sdf = SignedDistanceField(f, HyperRectangle(Vec(-1, -1, -1), Vec(2, 2, 2)))
mesh = HomogenousMesh(sdf, MarchingTetrahedra())
setobject!(vis, mesh,
MeshPhongMaterial(color=RGBA{Float32}(1, 0, 0, 0.5)))
See here for a notebook with the example.
# Visualize the permutahedron of order 4 using Polyhedra.jl
using Combinatorics, Polyhedra
v = vrep(collect(permutations([0, 1, 2, 3])))
using CDDLib
p4 = polyhedron(v, CDDLib.Library())
# Project that polyhedron down to 3 dimensions for visualization
v1 = [1, -1, 0, 0]
v2 = [1, 1, -2, 0]
v3 = [1, 1, 1, -3]
p3 = project(p4, [v1 v2 v3])
# Show the result
setobject!(vis, Polyhedra.Mesh(p3))
Using https://github.com/rdeits/MeshCatMechanisms.jl
Author: rdeits
Source Code: https://github.com/rdeits/MeshCat.jl
License: MIT license
1658306820
A three-dimensional static graph viewer.
(click the image to try it out)
<html>
<head>
<style>
#graph {
width: 500px;
height: 500px;
border: 1px solid grey;
}
</style>
</head>
<body>
<div id="graph"></div>
<script src="graphosaurus.min.js"></script>
<script>
// JavaScript will go here
</script>
</body>
</html>
If you open this up in your web browser, you'll see something that looks like this:
Look at that amazing square! Now let's create a graph, a couple nodes, and an edge between the nodes:
var graph = G.graph()
// Create a red node with cartesian coordinates x=0, y=0, z=0
var redNode = G.node([0, 0, 0], {color: "red"});
graph.addNode(redNode);
// You can also use the addTo method to add to the graph
var greenNode = G.node([1, 1, 1], {color: "green"}).addTo(graph);
var edge = G.edge([redNode, greenNode], {color: "blue"});
graph.addEdge(edge); // or edge.addTo(graph)
// Render the graph in the HTML element with id='graph'
graph.renderIn("graph");
After inserting this JavaScript in the <script>
block, you should see this:
While this is a very basic example, I hope I've demonstrated how simple it is to create graphs with Graphosaurus.
git clone https://github.com/frewsxcv/graphosaurus.git
to clone this repositorynpm install
to install all the build requirementsgrunt
to build Graphosaurus. The resulting compiled JavaScript will be in dist/
and the docs will be in doc/
JSDoc generated API documentation can be found here.
John Conway's illustration of our glorious leader, the gryposaurus graphosaurus.
All files in this repository are licensed under version two of the Mozilla Public License.
Graphosaurus has some third party dependencies listed in the package.json
file in the devDependencies
and dependencies
sections. Their licenses can be found on their respective project pages.
Author: frewsxcv
Source Code: https://github.com/frewsxcv/graphosaurus
License: MPL-2.0 license
1657902865
Что, если бы я сказал вам… что вы тоже можете научиться кодировать 3D-объекты в React, которые удивят клиентов? Изучите основы рендеринга трех волокон, и достаточно скоро вы сможете превратить статическую страницу в художественную галерею, интерактивную карту или любую другую метавселенную.
3D-графика теперь расширяет возможности пользователей приложений, позволяя компаниям привлекать новых клиентов с помощью запоминающихся визуальных эффектов. Они могут представлять почти реалистичные продукты, такие как кроссовки или рюкзаки, которые можно персонализировать.
Усовершенствованные рендеры помогут вам совершить виртуальную экскурсию по корпоративному офису или даже выполнить упражнения по вождению в виртуальной реальности. Галерея, о которой я упоминал ранее, дала мне 10-минутный опыт выхода из тела.
Испытайте мечтательность реактивного 3D-пространства. Источник: пульсация
Из проектов трех волокон, которые я видел в Интернете, я заметил, что они нацелены на то, чтобы сделать продукт/услугу запоминающимся или понятным. Но вы также можете использовать свои навыки интерфейса, чтобы просто создать экспериментальную страницу портфолио как произведение искусства.
Обычно вы используете WebGL — API браузера для рендеринга 3D, что довольно сложно. Список определений, необходимых для обычного куба, сумасшедший. См. этот пример WebGL от Mozilla.
Визуализация упрощается с помощью высокоуровневой библиотеки Three.js, которая позволяет создавать 3D-среду с помощью более простого API. Введите всего несколько строк кода, и вы сможете создать проект React с простой сценой, в которой размещается куб.
// Setting up a scene that will be used to place objects
const scene = new THREE.Scene();
// Camera is the camera, eyes of your character
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
// We can set some WebGL parameters like antialiasing (softening sharp edges)
const renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
// Boom, let's append our scene to the body
document.body.appendChild( renderer.domElement );
// Add light to the scene with Harry Potter's Lumos spell
const mainLight = new THREE.AmbientLight( '#FFFFFF' );
const pointLight = new THREE.PointLight( '#FFFFFF' );
scene.add( mainLight );
scene.add( pointLight );
Чтобы что-то увидеть, нужно объявить свой куб. Довольно просто.
// This is how we create simple cube
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial( { color: '#BADA55' } );
const cube = new THREE.Mesh( geometry, material );
// And add it to the existing scene
scene.add( cube );
Еще ничего? Хорошо, есть еще один шаг.
// recurring function that will render our scene and camera in WebGL renderer
// and request window to draw next frame of animation
const main = () => {
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
requestAnimationFrame(main);
renderer.render(scene, camera);
}
main();
Хотя это было довольно ясно, остальная часть кода имеет раздражающий стиль 2000-2010 годов.
const btn = document.createElement('button');
btn.innerText = 'Click Me';
btn.onclick = () => console.log('I am imperative');
document.body.appendChild(btn);
Смешивание современных приложений React с кодом, подобным jQuery, — не лучшая идея для улучшения качества кода. Но здесь нашим спасением является реактивное трехволокно (R3F). Рендерер является декларативным, как и React , позволяя вам создавать автономный компонент 3D-сцены без затуманивающей сложности.
Добавьте R3F
и Three.js
в качестве зависимостей к существующему приложению Create React . В качестве альтернативы используйте стартер TSH React с использованием yarn add three @react-three/fiber
или npm install three @react-three/fiber –save
.
Создайте функциональный компонент, который возвращает настройку сцены со светом.
import { Canvas, Camera } from 'react-three-fiber'
const MyAwesomeDeclarativeScene = () => {
return (
<Canvas camera={{ position: [0, 0, 1], fov: 45 }}>
{/* We declare three.js object lowerCase without importing */}
<ambientLight intensity={0.3} color="#FFFFFF" />
<pointLight intensity={1.0} position={[10, 10, 10]} />
</Canvas>
);
}
ReactDOM.render(MyAwesomeDeclarativeScene, document.getElementById('root'));
Ваша сцена почти готова. Теперь добавим куб, чтобы мы могли двигаться вперед и исследовать захватывающие возможности, которые предлагает R3F.
<mesh position={[0, 0, -1]}>
<boxGeometry args={[1, 1, 1]} />
<meshStandardMaterial color='#BADA55' />
</mesh>
Вы используете сетчатый компонент React, который является оболочкой для различных геометрий Three.js . Один из них boxGeometry
представляет собой прямоугольный параллелепипед (или, как говорится, — коробку ). Добавьте один из возможных материалов Three.js, имитирующих глубину, чтобы увидеть своего чудесного малыша.
import { Canvas, Camera } from 'react-three-fiber'
const MyAwesomeDeclarativeScene = () => {
return (
<Canvas camera={{ position: [0, 0, 5], fov: 45 }}>
<ambientLight intensity={0.3} color="#FFFFFF" />
<pointLight intensity={1.0} position={[10, 10, 10]} />
<mesh position={[0, 0, -1]}>
<boxGeometry args={[1, 1, 1]} />
<meshStandardMaterial color='#BADA55' />
</mesh>
</Canvas>
);
}
ReactDOM.render(MyAwesomeDeclarativeScene, document.getElementById('root'));
Что вам дает декларативный способ создания 3D в React? Прямой доступ ко всем наворотам React! Вы можете создавать собственные автономные компоненты, использовать реактивное состояние и делать все, что уже есть в React, простым и эффективным способом.
Клиент выходит из вашего шкафа и просит простое приложение с вращающимися и кликабельными 3D-боксами. Пользователь должен иметь возможность контролировать скорость вращения. Вам нужно доказательство концепции, сделанное на обед (за который вы платите). Как бы вы подошли к проекту?
Используйте компонент пользовательской Box
функции для указания size
, color
и speedFactor
. Код ниже присваивает значение по умолчанию некоторым дополнительным свойствам, которые можно изменить позже — не беспокойтесь.
Используйте обычные useState
операторы для отслеживания зависания и активного состояния. Добавьте boxRef
, чтобы сохранить ссылку на вашу сетку, так как это позволяет вам манипулировать объектом через useFrame
обратный вызов. Пожалуйста, анализируйте приведенный ниже код в своем темпе, пока не поймете его.
import React, { useMemo, useRef, useState } from 'react';
import { MeshProps, useFrame } from 'react-three-fiber';
type BoxProps = {
size?: [number, number, number];
color?: string;
boxSpeed?: number;
} & MeshProps;
export const Box = ({ size = [1, 1, 1], color = '#FFFFFF', boxSpeed = 0.01, ...meshProps }: BoxProps) => {
const boxRef = useRef<MeshProps | null>(null);
const [isHovered, setIsHovered] = useState(false);
const [isSelected, setIsSelected] = useState(false);
// It is react-three-fiber way to trigger animation
// each frame we are changing x, y, z rotation of our box
useFrame(() => {
if (!boxRef.current) {
return;
}
boxRef.current.rotation.x += boxSpeed;
boxRef.current.rotation.y += boxSpeed;
});
// it is color memo which indicates state of an item
const calculatedColor = useMemo(() => {
if (isSelected && isHovered) {
return 'orange';
}
if (isSelected) {
return 'yellow';
}
if (isHovered && !isSelected) {
return 'red';
}
return color;
}, [color, isHovered, isSelected]);
return (
<mesh
{...meshProps}
ref={boxRef}
onPointerOver={() => setIsHovered(true)}
onPointerLeave={() => setIsHovered(false)}
onClick={() => setIsSelected((prev) => !prev)}
scale={isSelected ? [1.2, 1.2, 1.2] : [1, 1, 1]}
>
<boxGeometry args={size} />
<meshStandardMaterial color={calculatedColor} />
</mesh>
);
};
Итак, ваша сцена теперь использует потрясающе настраиваемый Box
компонент. Для того, чтобы дать пользователю возможность управлять скоростью вращения, добавьте диапазон ввода в ваш JSX structure
и useState
, который будет содержать информацию о скорости.
export const Scene = () => {
const [speedFactor, setSpeedFactor] = useState(1);
return (
<div>
<label htmlFor="speedFactor">
Speed factor:
<input
name="speedFactor"
type="range"
value={speedFactor}
min={1}
max={10}
step={0.1}
onChange={(e) => setSpeedFactor(+e.currentTarget.value)}
/>
</label>
<Canvas camera={{ position: [0, 0, 5], fov: 45 }}>
<ambientLight intensity={0.3} color="#FFFFFF" />
<pointLight intensity={1.0} position={[10, 10, 10]} />
<Box position={[-2, 1, 0]} rotation={[3, 1, 0]} color="hotpink" boxSpeed={0.02 * speedFactor} />
<Box position={[2, 1, 0]} rotation={[1, 1, 0]} color="cyan" boxSpeed={0.01 * speedFactor} />
<Box position={[0, -1, 0]} size={[1, 2, 2]} boxSpeed={0.005 * speedFactor} />
</Canvas>
</div>
);
};
Небольшой пакет кода может содержать много чудес! Как только вы освоите react-three-fiber
, вы будете готовы играть с Three.js
его богатыми функциями. И результат?
Средство визуализации имеет собственный опыт разработки, выходящий за рамки декларативного стиля. R3F имеет выдающуюся поддержку Typescript , которая постоянно проверяет ваши типы.
Оптимизация производительности в R3F меняет правила игры, поскольку цикл рендеринга сцены three.js полностью отделен от виртуального DOM React.
Кроме того, у react-three-fiber есть отличная экосистема служебных библиотек. Существует @react-three/cannon для создания контента, основанного на физике, или @react-three/drei . Последний предлагает множество функций React с тремя волокнами, которые могут сэкономить часы на создании впечатляющей 3D-сцены, обогащенной потрясающими эффектами постобработки.
Ссылка: https://tsh.io/blog/react-three-fiber/
#react #3d