Skip to content

Commit 0da5676

Browse files
committed
changes and update for v0.6
1 parent d76319c commit 0da5676

16 files changed

+357
-328
lines changed

CHANGELOG.md

+14
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,19 @@
11
# Changelog
22

3+
## [v0.6.0] - 2020-02-21
4+
5+
### Added
6+
7+
### Changed
8+
9+
- cartesiantospherical returns points in correct "math" order `(ρ, θ, ϕ)`
10+
rather than scrambled "physics"-style
11+
- pin(object) changed to accept a simpler rendering function
12+
13+
### Removed
14+
15+
### Deprecated
16+
317
## [v0.5.0] - 2020-01-31
418

519
### Added

Project.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "Thebes"
22
uuid = "8b424ff8-82f5-59a4-86a6-de3761897198"
33
authors = ["cormullion <[email protected]>"]
4-
version = "0.5.0"
4+
version = "0.6.0"
55

66
[deps]
77
Luxor = "ae8d54c2-7ccd-5906-9d76-62fc9837b5bc"

data/sphere.jl

+1
Large diffs are not rendered by default.

docs/src/objects.md

+98-73
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ Object(
4747
"cube")
4848
```
4949

50-
The default rendering applied by `pin()` uses less than fifty shades of grey to draw it.
50+
The default rendering applied by `pin()` is an attempt at a simple hidden-surface display. In real 3D software, this process has to be far more sophisticated.
5151

5252
```@example
5353
using Thebes, Luxor # hide
@@ -84,19 +84,15 @@ end
8484
sethue("darkorange")
8585
knot = make([a, []], "knot")
8686
87-
pin(knot, gfunction = (args...) -> poly(args[1], :stroke))
87+
pin(knot, gfunction = (o) -> poly(objecttopoly(o)[1], :stroke))
8888
8989
finish() # hide
9090
nothing # hide
9191
```
9292

9393
![point example](assets/figures/object1.svg)
9494

95-
The gfunction here receives the `vertices`, `faces`, and `labels` in `args`, but the faces and labels are empty, so this simple use of `pin` only needs to draw a polygon through the vertices.
96-
97-
!!! warning
98-
99-
The gfunction used by `pin()` for objects doesn't give you access to the 3D points, unlike the versions used for points.
95+
The `objecttopoly()` function returns a tuple, containing the 2D vertices, and the polygons that define the faces.
10096

10197
## OFF the shelf objects
10298

@@ -120,11 +116,12 @@ sethue("blue") # hide
120116
helloworld() # hide
121117
122118
t = Tiler(600, 300, 2, 2)
119+
setline(0.5)
123120
for (n, o) in enumerate([Cube, Tetrahedron, Pyramid, Teapot])
124121
@layer begin
125122
translate(first.(t)[n])
126123
object = make(o, string(o))
127-
scaleby!(object, 50, 50, 50)
124+
scaleby!(object, 80, 80, 80)
128125
pin(object)
129126
end
130127
end
@@ -135,17 +132,20 @@ nothing # hide
135132

136133
![more objects](assets/figures/moreobjects.svg)
137134

138-
```
139-
@svg begin
140-
helloworld()
141-
perspective(1600)
142-
axes3D()
143-
teapot = make(Teapot)
144-
sortfaces!(teapot)
145-
scaleby!(teapot, 15, 15, 15)
146-
setopacity(0.5)
147-
pin(teapot)
148-
end
135+
```@example
136+
using Thebes, Luxor # hide
137+
Drawing(800, 300, "assets/figures/teapot.svg") # hide
138+
background("white") # hide
139+
origin() # hide
140+
helloworld()
141+
axes3D(200)
142+
teapot = make(Teapot)
143+
setline(0.5)
144+
scaleby!(teapot, 100, 100, 100)
145+
pin(teapot, gfunction=wireframe)
146+
finish() # hide
147+
nothing # hide
148+
149149
```
150150

151151
![teapot](assets/figures/teapot.svg)
@@ -162,7 +162,13 @@ which brings these objects into play:
162162

163163
## Rendering objects
164164

165-
To render objects, there are many choices you can make about how to draw the faces and the vertices. You do this with a gfunction. For objects, the gfunction is more complex than for points and lines. It takes lists of vertices, faces, and labels. Here's a simple example:
165+
To render objects, there are many choices you can make about how to draw the faces and the vertices.
166+
167+
### Using gfunctions
168+
169+
You do this with a gfunction.
170+
171+
Here's a simple example:
166172

167173
```@example
168174
using Thebes, Luxor # hide
@@ -177,32 +183,37 @@ helloworld() # hide
177183
setlinejoin("bevel")
178184
eyepoint(150, 150, 150)
179185
180-
function mygfunction(vertices, faces, labels; action=:fill)
186+
function mygfunction(o::Object)
181187
cols = [Luxor.julia_green, Luxor.julia_red, Luxor.julia_purple, Luxor.julia_blue]
182-
183-
if !isempty(faces)
188+
sortfaces!(o)
189+
if !isempty(o.faces)
184190
@layer begin
185-
for (n, p) in enumerate(faces)
186-
187-
@layer begin
188-
sethue(cols[mod1(n, end)])
189-
poly(p, close = true, action)
190-
end
191-
192-
sethue("white")
193-
setline(0.5)
194-
poly(p, :stroke, close=true)
195-
191+
for (n, face) in enumerate(o.faces)
192+
@layer begin
193+
vertices = o.vertices[face]
194+
sn = surfacenormal(vertices)
195+
ang = anglebetweenvectors(sn, eyepoint())
196+
sethue(cols[mod1(n, end)])
197+
pin(vertices, gfunction = (p3, p2) ->
198+
begin
199+
poly(p2, :fill)
200+
sethue("gold")
201+
poly(p2, :stroke, close=true)
202+
end)
203+
end
196204
end
197205
end
198206
end
199-
setcolor("gold")
200-
circle.(vertices, 2, :fill)
207+
setcolor("gold3")
208+
pin.(o.vertices, gfunction = (p3, p2) -> begin
209+
setopacity(1)
210+
circle(p2, 2, :fill)
211+
end)
201212
end
202213
203-
setopacity(0.7)
204214
object = make(geodesic, "geodesic")
205-
sortfaces!(object)
215+
setopacity(0.9)
216+
setline(0.5)
206217
pin(scaleby!(object, 200, 200, 200), gfunction = mygfunction)
207218
208219
finish() # hide
@@ -219,53 +230,67 @@ The faces are drawn in the order in which they were defined. But to be a more re
219230

220231
This is why Thebes is more of a wireframe tool than any kind of genuine 3D application. Use Makie.jl. Or program Blender with Julia.
221232

222-
In theory it's possible to do some quick calculations on an object to sort the faces into the correct order for a particular viewpoint. The `sortfaces!()` function can do this for simple objects - it may be sufficient.
233+
In theory it's possible to do some quick calculations on an object to sort the faces into the correct order for a particular viewpoint. The `sortfaces!()` function used above can do this for simple objects - it may be sufficient.
223234

224-
```@example
225-
using Thebes, Luxor # hide
226-
Drawing(600, 500, "assets/figures/sortsurfaces.svg") # hide
235+
## Using custom code
227236

228-
function mygfunction(vertices, faces, labels; action=:fill)
229-
cols = [Luxor.julia_green, Luxor.julia_red, Luxor.julia_purple, Luxor.julia_blue]
230-
if !isempty(faces)
231-
@layer begin
232-
for (n, p) in enumerate(faces)
237+
Thebes.jl is a work in progress, and a good general-purpose rendering function that draws everything with lots of optional parameters is not yet provided. However, you can avoid using the built-in `pin(o::Object)` function, and experiment with code such as the following:
233238

234-
@layer begin
235-
sethue(cols[mod1(n, end)])
236-
poly(p, close = true, action)
237-
end
239+
```@example
240+
using Luxor, Thebes, Colors, ColorSchemes
238241
239-
sethue("white")
240-
setline(0.5)
241-
poly(p, :stroke, close=true)
242+
include(dirname(pathof(Thebes)) * "/../data/moreobjects.jl")
242243
244+
function lighten(col::Colorant, f)
245+
c = convert(RGB, col)
246+
return RGB(f * c.r, f* c.g, f * c.b)
247+
end
248+
249+
function drawobject(o;
250+
color=colorant"red")
251+
setlinejoin("bevel")
252+
if !isempty(o.faces)
253+
@layer begin
254+
for (n, f) in enumerate(o.faces)
255+
vs = o.vertices[f]
256+
sn = surfacenormal(vs)
257+
ang = anglebetweenvectors(sn, eyepoint())
258+
sl = slope(O, vs[1])
259+
sethue(lighten(color, rescale(ang, 0, π, -2, 2)))
260+
pin(vs, gfunction = (p3, p2) -> begin
261+
poly(p2, :fill)
262+
sethue("grey30")
263+
poly(p2, :stroke)
264+
end)
243265
end
244266
end
245267
end
246268
end
247269
248-
background("black")
249-
origin()
250-
setlinejoin("bevel")
251-
eyepoint(Point3D(150, 150, 150))
252-
perspective(0)
253-
axes3D(20)
254-
255-
object = make(Cube, "cube")
256-
scaleby!(object, 100, 100, 100)
257-
258-
# draw as is
259-
moveby!(object, Point3D(0, -200, 0))
260-
pin(object, gfunction = mygfunction)
270+
function sphere(size, origin, color)
271+
s1 = make(sphere2)
272+
scaleby!(s1, size, size, size)
273+
moveby!(s1, origin)
274+
sortfaces!(s1)
275+
drawobject(s1, color=color)
276+
end
261277
262-
# draw with sorted faces
263-
moveby!(object, Point3D(0, 400, 0))
264-
sortfaces!(object, eyepoint=eyepoint())
265-
pin(object, gfunction = mygfunction)
278+
function main()
279+
Drawing(500, 500, "assets/figures/juliaspheres.svg")
280+
background("grey20")
281+
origin()
282+
helloworld()
283+
eyepoint(300, 300, 300)
284+
perspective(450)
285+
setline(.5)
286+
sphere(90, Point3D(150, 0, 0), RGB(Luxor.julia_red...))
287+
sphere(90, Point3D(0, 150, 0), RGB(Luxor.julia_purple...))
288+
sphere(90, Point3D(0, 0, 150), RGB(Luxor.julia_green...))
289+
finish()
290+
end
266291
267-
finish() # hide
292+
main()
268293
nothing # hide
269294
```
270295

271-
![object](assets/figures/sortsurfaces.svg)
296+
![custom object](assets/figures/juliaspheres.svg)

docs/src/tools.md

+2-21
Original file line numberDiff line numberDiff line change
@@ -51,33 +51,14 @@ function cullfrontfaces!(m::Object, angle;
5151
return m
5252
end
5353
54-
function drawobject(object)
55-
pin(object, gfunction = (args...) -> begin
56-
vertices, faces, labels = args
57-
setopacity(0.8)
58-
sethue("grey80")
59-
if !isempty(faces)
60-
@layer begin
61-
for (n, p) in enumerate(faces)
62-
poly(p, :fillpreserve, close=true)
63-
@layer begin
64-
sethue("grey20")
65-
strokepath()
66-
end
67-
end
68-
end
69-
end
70-
end)
71-
end
72-
7354
sortfaces!.((objectcut, objectfull))
7455
cullfrontfaces!(objectcut, π/3)
7556
7657
translate(-200, 0)
77-
drawobject(objectcut)
58+
pin(objectcut)
7859
7960
translate(400, 0)
80-
drawobject(objectfull)
61+
pin(objectfull)
8162
8263
@show length(objectcut.faces)
8364
@show length(objectfull.faces)

examples/convert-off-file.jl

+8-7
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ function convert_off_file(fname)
2626
return (vertices, faces)
2727
end
2828

29-
iobuffer = IOBuffer()
3029

3130
# cd(expanduser("/tmp/geometry"))
31+
# iobuffer = IOBuffer()
3232
#
3333
# for f in ["boxcube.off", "boxtorus.off", "concave.off", "cone.off", "cross.off", "cube.off",
3434
# "cuboctahedron.off", "dodecahedron.off", "geodesic.off", "helix2.off", "icosahedron.off",
@@ -43,12 +43,13 @@ iobuffer = IOBuffer()
4343
# println(iobuffer, fname, " ", object)
4444
# end
4545
#
46-
46+
#=open("/tmp/objects.txt", "w") do f
47+
write(f, String(take!(iobuffer)))
48+
end
4749
for f in readdir()
48-
object = convert_off_file(f)
49-
println(iobuffer, f, " ", object)
50+
object = convert_off_file(f)
51+
println(iobuffer, f, " ", object)
5052
end
53+
=#
5154

52-
open("/tmp/objects.txt", "w") do f
53-
write(f, String(take!(iobuffer)))
54-
end
55+
sphere1 = convert_off_file("/tmp/Untitled.obj.off")

0 commit comments

Comments
 (0)