Skip to content

Commit f9e4b59

Browse files
committed
Add axes
1 parent 535134c commit f9e4b59

File tree

7 files changed

+151
-13
lines changed

7 files changed

+151
-13
lines changed

Examples/Train.shape

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ define wheel {
3030
}
3131

3232
// wheels
33-
for 1 to 3 {
34-
translate 0 0 0.7
33+
for i in -1 to 1 {
3534
group {
35+
translate 0 0 (0.7 * i)
3636
rotate 0.5 rnd
3737
translate 0 0.5 0
3838
wheel
@@ -47,7 +47,7 @@ for 1 to 3 {
4747
// base
4848
union {
4949
rotate 0 0.5 0
50-
translate -1.7 -0.06
50+
translate -1 -0.06
5151
color black
5252
extrude {
5353
size 1 1 0.7
@@ -80,7 +80,7 @@ union {
8080
// face
8181
lathe {
8282
color gray
83-
position 0 0.8 0.3
83+
position 0 0.8 1
8484
orientation 0 0 0.5
8585
path {
8686
point 0 0
@@ -93,7 +93,7 @@ lathe {
9393
// chimney
9494
union {
9595
color black
96-
position 0 0.6 0.1
96+
position 0 0.6 0.8
9797
cube { size 0.7 0.4 0.4 }
9898
cylinder {
9999
position 0 0.2 0
@@ -122,7 +122,7 @@ union {
122122
// body
123123
union {
124124
color lightBlue
125-
position 0 0.8 -0.5
125+
position 0 0.8 0.2
126126
orientation 0 0 0.5
127127
cylinder { size 0.7 0.8 }
128128
translate 0 0.425 -0.085
@@ -163,7 +163,7 @@ union {
163163
// coal
164164
union {
165165
color black
166-
position 0 0.73 -1.54
166+
position 0 0.73 -0.84
167167
orientation 0 0 0.5
168168
cube { size 0.8 0.325 0.4 }
169169
translate 0.45 -0.1 0.2

ShapeScript.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
01BFB3B52142726100E47A7C /* ParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01BFB3B42142726100E47A7C /* ParserTests.swift */; };
4343
01E3AA0526CB8E8200E0B287 /* Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01E3AA0426CB8E8200E0B287 /* Logging.swift */; };
4444
01E3AA0A26CB920000E0B287 /* LoggingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01E3AA0926CB920000E0B287 /* LoggingTests.swift */; };
45+
01E4E48826D377F000F390D7 /* Axes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01E4E48726D377F000F390D7 /* Axes.swift */; };
4546
01FDE3CE26BD73A000A46F66 /* LRUCache.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 01FDE3C026BD736700A46F66 /* LRUCache.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
4647
01FDE3CF26BD73AA00A46F66 /* LRUCache.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 01FDE3C026BD736700A46F66 /* LRUCache.framework */; };
4748
01FDE3D526BD766C00A46F66 /* GeometryCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01FDE3D426BD766C00A46F66 /* GeometryCache.swift */; };
@@ -182,6 +183,7 @@
182183
01BFB3B42142726100E47A7C /* ParserTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParserTests.swift; sourceTree = "<group>"; };
183184
01E3AA0426CB8E8200E0B287 /* Logging.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logging.swift; sourceTree = "<group>"; };
184185
01E3AA0926CB920000E0B287 /* LoggingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggingTests.swift; sourceTree = "<group>"; };
186+
01E4E48726D377F000F390D7 /* Axes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Axes.swift; sourceTree = "<group>"; };
185187
01FDE3B626BD736700A46F66 /* LRUCache.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; path = LRUCache.xcodeproj; sourceTree = "<group>"; };
186188
01FDE3D426BD766C00A46F66 /* GeometryCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeometryCache.swift; sourceTree = "<group>"; };
187189
/* End PBXFileReference section */
@@ -229,6 +231,7 @@
229231
01908DC726B7597F0068390E /* ProgramError.swift */,
230232
0152E5FB2145C0420082A5A3 /* Document.swift */,
231233
013312F421CD653B00626F1B /* Settings.swift */,
234+
01E4E48726D377F000F390D7 /* Axes.swift */,
232235
0152E5CD2145009A0082A5A3 /* Assets.xcassets */,
233236
0152E5CF2145009A0082A5A3 /* Main.storyboard */,
234237
0152E5D22145009A0082A5A3 /* Info.plist */,
@@ -605,6 +608,7 @@
605608
013312F521CD653B00626F1B /* Settings.swift in Sources */,
606609
0152E5FC2145C0420082A5A3 /* Document.swift in Sources */,
607610
0190CB6F21CE300F00CE4571 /* Utilities.swift in Sources */,
611+
01E4E48826D377F000F390D7 /* Axes.swift in Sources */,
608612
013312F221CD5A7000626F1B /* PreferencesViewController.swift in Sources */,
609613
01908DC826B7597F0068390E /* ProgramError.swift in Sources */,
610614
01746BE926C49597009DADEF /* MaterialProperty+Brightness.swift in Sources */,

Viewer/AppDelegate.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,19 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations {
134134
sender.state = showWireframe ? .on : .off
135135
}
136136

137+
public var showAxes = true {
138+
didSet {
139+
for case let document as Document in NSApp.orderedDocuments {
140+
document.updateViews()
141+
}
142+
}
143+
}
144+
145+
@IBAction func showAxes(_ sender: NSMenuItem) {
146+
showAxes = !showAxes
147+
sender.state = showAxes ? .on : .off
148+
}
149+
137150
// MARK: NSUserInterfaceValidations
138151

139152
func validateUserInterfaceItem(_ item: NSValidatedUserInterfaceItem) -> Bool {

Viewer/Axes.swift

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
//
2+
// Axis.swift
3+
// ShapeScript Viewer
4+
//
5+
// Created by Nick Lockwood on 23/08/2021.
6+
// Copyright © 2021 Nick Lockwood. All rights reserved.
7+
//
8+
9+
import Euclid
10+
import SceneKit
11+
import ShapeScript
12+
13+
struct Axes {
14+
let geometry: Geometry
15+
16+
init(scale: Double, background: MaterialProperty?) {
17+
let textScale = 0.1
18+
let color = Color(.underPageBackgroundColor)
19+
let brightness = background?.brightness(over: color) ?? color.brightness
20+
let lineColor = brightness > 0.5 ? Color.black : .white
21+
var material = Material.default
22+
material.color = lineColor
23+
geometry = Geometry(transform: Transform(scale: Vector(size: [scale])), children: [
24+
Geometry(type: .path(.line(-.x, .x))),
25+
Geometry(type: .fill(Path.text("+X")), transform: Transform(
26+
offset: .x - Vector(-0.5, 0.25, 0) * textScale,
27+
scale: Vector(size: [textScale])
28+
), material: material),
29+
Geometry(type: .fill(Path.text("-X")), transform: Transform(
30+
offset: -.x - Vector(1.5, 0.25, 0) * textScale,
31+
scale: Vector(size: [textScale])
32+
), material: material),
33+
Geometry(type: .path(.line(-.y, .y))),
34+
Geometry(type: .fill(Path.text("+Y")), transform: Transform(
35+
offset: .y - Vector(0.6, -0.5, 0) * textScale,
36+
scale: Vector(size: [textScale])
37+
), material: material),
38+
Geometry(type: .fill(Path.text("-Y")), transform: Transform(
39+
offset: -.y - Vector(0.5, 1.25, 0) * textScale,
40+
scale: Vector(size: [textScale])
41+
), material: material),
42+
Geometry(type: .path(.line(-.z, .z))),
43+
Geometry(type: .fill(Path.text("+Z")), transform: Transform(
44+
offset: .z - Vector(0.6, 0.25, -0.5) * textScale,
45+
scale: Vector(size: [textScale])
46+
), material: material),
47+
Geometry(type: .fill(Path.text("-Z")), transform: Transform(
48+
offset: -.z - Vector(0.5, 0.25, 0.5) * textScale,
49+
scale: Vector(size: [textScale])
50+
), material: material),
51+
])
52+
_ = geometry.build { true }
53+
var options = Scene.OutputOptions.default
54+
options.lineColor = lineColor
55+
geometry.scnBuild(with: options)
56+
}
57+
}
58+
59+
extension SCNNode {
60+
convenience init(_ axes: Axes) {
61+
self.init(axes.geometry)
62+
}
63+
}
64+
65+
private extension Vector {
66+
static let x = Vector(1, 0, 0)
67+
static let y = Vector(0, 1, 0)
68+
static let z = Vector(0, 0, 1)
69+
}
70+
71+
private extension Geometry {
72+
convenience init(
73+
type: GeometryType = .group,
74+
transform: Transform = .identity,
75+
material: Material = .default,
76+
children: [Geometry] = []
77+
) {
78+
self.init(
79+
type: type,
80+
name: nil,
81+
transform: transform,
82+
material: material,
83+
children: children,
84+
sourceLocation: nil
85+
)
86+
}
87+
}

Viewer/Base.lproj/Main.storyboard

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,12 @@
174174
<action selector="showWireframe:" target="Ady-hI-5gd" id="9uo-km-aSA"/>
175175
</connections>
176176
</menuItem>
177+
<menuItem title="Show Axes" state="on" id="7QN-OP-8q0">
178+
<modifierMask key="keyEquivalentModifierMask"/>
179+
<connections>
180+
<action selector="showAxes:" target="Ady-hI-5gd" id="iKT-GP-LR6"/>
181+
</connections>
182+
</menuItem>
177183
<menuItem title="Model Info" keyEquivalent="i" id="LsO-0m-RrC">
178184
<connections>
179185
<action selector="showModelInfo:" target="Ady-hI-5gd" id="GgQ-IA-hvt"/>

Viewer/Document.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ class Document: NSDocument, EvaluationDelegate {
6060
viewController.errorMessage = errorMessage
6161
viewController.showAccessButton = (errorMessage != nil && accessErrorURL != nil)
6262
viewController.showWireframe = (NSApp.delegate as! AppDelegate).showWireframe
63+
viewController.showAxes = (NSApp.delegate as! AppDelegate).showAxes
6364
}
6465
}
6566

Viewer/SceneViewController.swift

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,17 @@ class SceneViewController: NSViewController {
114114
}
115115
}
116116

117+
public var showAxes = false {
118+
didSet {
119+
guard showAxes != oldValue else {
120+
return
121+
}
122+
let geometry = self.geometry
123+
self.geometry = geometry
124+
resetCamera(nil)
125+
}
126+
}
127+
117128
var background: MaterialProperty? {
118129
get { MaterialProperty(scnMaterialProperty: scnScene.background) }
119130
set { newValue?.configureProperty(scnScene.background) }
@@ -124,11 +135,20 @@ class SceneViewController: NSViewController {
124135
// clear scene
125136
scnScene.rootNode.childNodes.forEach { $0.removeFromParentNode() }
126137

138+
// add axes
139+
let bounds = viewBounds
140+
let size = bounds.size
141+
let scale = max(size.x, size.y, size.z, 1)
142+
if showAxes {
143+
let axes = Axes(scale: scale / 2, background: background)
144+
scnScene.rootNode.addChildNode(SCNNode(axes))
145+
}
146+
127147
// restore selection
128148
selectGeometry(selectedGeometry?.scnGeometry)
129149

130150
guard let geometry = geometry, !geometry.bounds.isEmpty else {
131-
scnView.allowsCameraControl = false
151+
scnView.allowsCameraControl = showAxes
132152
return
133153
}
134154

@@ -138,10 +158,9 @@ class SceneViewController: NSViewController {
138158
}
139159

140160
// update camera
141-
let bounds = geometry.bounds
142161
let center = bounds.center
143-
let distance = max(bounds.size.x, bounds.size.y) + bounds.max.z + cameraNode.camera!.zNear
144-
cameraNode.position = SCNVector3(center.x, center.y, distance + cameraNode.camera!.zNear)
162+
let distance = scale * 1.2 + cameraNode.camera!.zNear
163+
cameraNode.position = SCNVector3(center.x, center.y, distance)
145164
scnView.allowsCameraControl = true
146165
scnView.defaultCameraController.target = SCNVector3(center)
147166
refreshView()
@@ -183,6 +202,15 @@ class SceneViewController: NSViewController {
183202
scnView.rendersContinuously = false
184203
}
185204

205+
private var viewBounds: Bounds {
206+
let bounds = geometry?.bounds ?? .empty
207+
guard showAxes else {
208+
return bounds
209+
}
210+
let m = max(-bounds.min, bounds.max) + Vector(size: [0.1])
211+
return Bounds(min: -m, max: m)
212+
}
213+
186214
private(set) weak var selectedGeometry: Geometry?
187215
func selectGeometry(_ scnGeometry: SCNGeometry?) {
188216
selectedGeometry = geometry?.select(with: scnGeometry)
@@ -224,8 +252,7 @@ class SceneViewController: NSViewController {
224252
}
225253

226254
@IBAction func resetCamera(_: Any?) {
227-
let center = geometry?.bounds.center ?? Vector.zero
228-
scnView.defaultCameraController.target = SCNVector3(center)
255+
scnView.defaultCameraController.target = SCNVector3(viewBounds.center)
229256
scnView.pointOfView = cameraNode
230257
refreshView()
231258
}

0 commit comments

Comments
 (0)