summaryrefslogtreecommitdiff
path: root/ShedCalc
diff options
context:
space:
mode:
authorArturs Artamonovs <arturs.artamonovs@protonmail.com>2026-08-07 02:01:40 +0100
committerArturs Artamonovs <arturs.artamonovs@protonmail.com>2026-08-07 02:01:40 +0100
commit91d0042017e6c46f2c9f5d42d438836efa6d53e4 (patch)
treeaf8c2dbad8ea148e882136843634ff53c42dd20b /ShedCalc
parentdabeb1603cf146aa671dddf949788b514b983b76 (diff)
downloadShedCalc-91d0042017e6c46f2c9f5d42d438836efa6d53e4.tar.gz
ShedCalc-91d0042017e6c46f2c9f5d42d438836efa6d53e4.zip
Added 3 views. Added basic beam drawing and basic calculations for width and length + beam drawing
Diffstat (limited to 'ShedCalc')
-rw-r--r--ShedCalc/BOMController.swift7
-rw-r--r--ShedCalc/BOMModel.swift7
-rw-r--r--ShedCalc/BOMView.swift63
-rw-r--r--ShedCalc/ContentView.swift34
-rw-r--r--ShedCalc/ParamController.swift7
-rw-r--r--ShedCalc/ParamModel.swift37
-rw-r--r--ShedCalc/ParamView.swift41
-rw-r--r--ShedCalc/RenderController.swift145
-rw-r--r--ShedCalc/RenderModel.swift19
-rw-r--r--ShedCalc/RenderView.swift70
10 files changed, 424 insertions, 6 deletions
diff --git a/ShedCalc/BOMController.swift b/ShedCalc/BOMController.swift
new file mode 100644
index 0000000..fa05eeb
--- /dev/null
+++ b/ShedCalc/BOMController.swift
@@ -0,0 +1,7 @@
+//
+// BOMController.swift
+// ShedCalc
+//
+// Created by Jacky Jack on 01/08/2026.
+//
+
diff --git a/ShedCalc/BOMModel.swift b/ShedCalc/BOMModel.swift
new file mode 100644
index 0000000..0605fed
--- /dev/null
+++ b/ShedCalc/BOMModel.swift
@@ -0,0 +1,7 @@
+//
+// BOMModel.swift
+// ShedCalc
+//
+// Created by Jacky Jack on 01/08/2026.
+//
+
diff --git a/ShedCalc/BOMView.swift b/ShedCalc/BOMView.swift
new file mode 100644
index 0000000..1bb6523
--- /dev/null
+++ b/ShedCalc/BOMView.swift
@@ -0,0 +1,63 @@
+//
+// BOMView.swift
+// ShedCalc
+//
+// Created by Jacky Jack on 28/07/2026.
+//
+
+import SwiftUI
+import Combine
+
+struct Materials: Identifiable {
+ let materialName: String
+ let pricePerMeter: Double
+ let totalMeters: Double
+ let totalPrice: Double
+ let metersPerPiece: Double
+ let totalPieces: Double
+
+ let id = UUID()
+}
+
+
+
+struct BOMView: View {
+ @Environment(ParamModel.self) private var paramModel
+
+ @State private var materials = [
+ Materials(materialName: "2x4", pricePerMeter: 1.2, totalMeters: 10, totalPrice: 12, metersPerPiece: 4.8, totalPieces: 23),
+ Materials(materialName: "OSB", pricePerMeter: 1.2, totalMeters: 10, totalPrice: 12, metersPerPiece: 4.8, totalPieces: 23),
+ Materials(materialName: "Roof Felt", pricePerMeter: 1.2, totalMeters: 10, totalPrice: 12, metersPerPiece: 4.8, totalPieces: 23),
+ Materials(materialName: "Nails", pricePerMeter: 1.2, totalMeters: 10, totalPrice: 12, metersPerPiece: 4.8, totalPieces: 23),
+ Materials(materialName: "Cladding", pricePerMeter: 1.2, totalMeters: 10, totalPrice: 12, metersPerPiece: 4.8, totalPieces: 23)
+ ]
+
+ var body: some View {
+ VStack {
+ Text("BOM")
+ }
+ VStack {
+ Table(materials) {
+ TableColumn("Material", value: \.materialName)
+ TableColumn("Price per m") { material in
+ Text(material.pricePerMeter, format: .number.precision(.fractionLength(2)))
+ }
+ TableColumn("Total m") { material in
+ Text(material.totalMeters, format: .number.precision(.fractionLength(2)))
+ }
+ TableColumn("Total price") { material in
+ Text(material.totalPrice, format: .number.precision(.fractionLength(2)))
+ }
+ TableColumn("m per Piece") { material in
+ Text(material.metersPerPiece, format: .number.precision(.fractionLength(2)))
+ }
+ TableColumn("Pieces") { material in
+ Text(material.totalPieces, format: .number)
+ }
+ }
+ }
+ HStack {
+ Text("Total price: 0.00")
+ }
+ }
+}
diff --git a/ShedCalc/ContentView.swift b/ShedCalc/ContentView.swift
index c19fdb3..dbf31a0 100644
--- a/ShedCalc/ContentView.swift
+++ b/ShedCalc/ContentView.swift
@@ -10,10 +10,12 @@ import SwiftData
struct ContentView: View {
@Environment(\.modelContext) private var modelContext
+
@Query private var items: [Item]
+ @State private var paramModel = ParamModel()
var body: some View {
- NavigationSplitView {
+ /*NavigationSplitView {
List {
ForEach(items) { item in
NavigationLink {
@@ -34,22 +36,33 @@ struct ContentView: View {
}
} detail: {
Text("Select an item")
- }
+ }*/
+ TabView {
+ Tab("Render", systemImage: "tray") {
+ RenderView()
+ }
+ Tab("Params", systemImage: "tray") {
+ ParamView()
+ }
+ Tab("BOM", systemImage: "keyboard") {
+ BOMView()
+ }
+ }.environment(paramModel)
}
private func addItem() {
- withAnimation {
+ /*withAnimation {
let newItem = Item(timestamp: Date())
modelContext.insert(newItem)
- }
+ }*/
}
private func deleteItems(offsets: IndexSet) {
- withAnimation {
+ /*withAnimation {
for index in offsets {
modelContext.delete(items[index])
}
- }
+ }*/
}
}
@@ -57,3 +70,12 @@ struct ContentView: View {
ContentView()
.modelContainer(for: Item.self, inMemory: true)
}
+
+#Preview {
+ NavigationSplitView {
+ ContentView()
+ } detail: {
+ Text("Select an item")
+ }
+}
+
diff --git a/ShedCalc/ParamController.swift b/ShedCalc/ParamController.swift
new file mode 100644
index 0000000..5678624
--- /dev/null
+++ b/ShedCalc/ParamController.swift
@@ -0,0 +1,7 @@
+//
+// ParamController.swift
+// ShedCalc
+//
+// Created by Jacky Jack on 01/08/2026.
+//
+
diff --git a/ShedCalc/ParamModel.swift b/ShedCalc/ParamModel.swift
new file mode 100644
index 0000000..4935bcd
--- /dev/null
+++ b/ShedCalc/ParamModel.swift
@@ -0,0 +1,37 @@
+//
+// ParamModel.swift
+// ShedCalc
+//
+// Created by Jacky Jack on 01/08/2026.
+//
+import Combine
+import SwiftUI
+import Observation
+
+@Observable
+class ParamModel {
+ var width: Float = 1.0
+ var height: Float = 1.0
+ var length: Float = 1.0
+ var beamSpace: Float = 0.6
+
+ func floorArea() -> Float {
+ return width*length
+ }
+
+ func roofArea() -> Float {
+ return width*length
+ }
+
+ func dimensions() -> SIMD3<Float> {
+ return [width,length,height]
+ }
+
+ func widthBeamNum() -> Int {
+ return Int(width/beamSpace) + 1
+ }
+
+ func lengthBeamNum() -> Int {
+ return Int(length/beamSpace) + 1
+ }
+}
diff --git a/ShedCalc/ParamView.swift b/ShedCalc/ParamView.swift
new file mode 100644
index 0000000..3e27978
--- /dev/null
+++ b/ShedCalc/ParamView.swift
@@ -0,0 +1,41 @@
+//
+// ParamView.swift
+// ShedCalc
+//
+// Created by Jacky Jack on 28/07/2026.
+//
+
+import SwiftUI
+
+
+struct ParamView: View {
+ //@StateObject private var viewModel = ParamModel()
+ @Environment(ParamModel.self) var paramModel
+
+ var body: some View {
+ @Bindable var paramModel = paramModel
+ VStack {
+ HStack {
+ Text("All in meters")
+ }
+ HStack {
+ Text("Width")
+ TextField("Width", value: $paramModel.width, format: .number)
+ }
+ HStack {
+ Text("Height")
+ TextField("Height", value: $paramModel.height, format: .number)
+ }
+ HStack {
+ Text("Length")
+ TextField("Length", value: $paramModel.length, format: .number)
+ }
+ HStack {
+ Text("Total floor area: \(paramModel.floorArea(), specifier: "%.2f") m^2")
+ }
+ HStack {
+ Text("Total roof area: \(paramModel.roofArea(), specifier: "%.2f") m^2")
+ }
+ }
+ }
+}
diff --git a/ShedCalc/RenderController.swift b/ShedCalc/RenderController.swift
new file mode 100644
index 0000000..1a57cb0
--- /dev/null
+++ b/ShedCalc/RenderController.swift
@@ -0,0 +1,145 @@
+//
+// RenderController.swift
+// ShedCalc
+//
+// Created by Jacky Jack on 28/07/2026.
+//
+
+import SwiftUI
+import RealityKit
+
+let BEAM_2x4_WIDTH: Float = 0.02
+let BEAM_2x4_LENGTH: Float = 0.04
+
+struct BeamData {
+ let id: String
+ let position: SIMD3<Float>
+ let height: Float
+ let rotation: SIMD3<Float>
+ }
+
+class RenderController {
+ func createScene(dimensions: SIMD3<Float>, rotation: SIMD2<Float>) -> Entity {
+ let scene = Entity()
+ scene.name = "scene"
+
+ let box = createBox(dimensions: [1,1,1])
+ scene.addChild(box)
+
+ let beam0 = createBeam2x4(position: [1,1,0], height: 2.4, rotate: [90,0,0], id: "beam0")
+ scene.addChild(beam0)
+
+ //let beam1 = createBeam2x4(position: [1,0,0], height: 2.4, rotate: [0,0,0], id: "beam1")
+ //scene.addChild(beam1)
+
+ //let beam2 = createBeam2x4(position: [1,1,0], height: 2.4, rotate: [0,0,90], id: "beam2") //Z-around beam
+ //scene.addChild(beam2)
+
+ //let beam3 = createBeam2x4(position: [1,1,0], height: 2.4, rotate: [0,90,0], id: "beam3")
+ //scene.addChild(beam3)
+
+ let ground = createGround()
+ scene.addChild(ground)
+
+ return scene
+ }
+
+ func createLights() -> (DirectionalLight, PointLight) {
+ let directional = DirectionalLight()
+ directional.light.intensity = 2000
+ directional.look(at: .zero, from: [2, 4, 3], relativeTo: nil)
+
+ let point = PointLight()
+ point.light.intensity = 5000
+ point.position = [-2, 2, -2]
+
+ return (directional, point)
+ }
+
+ func createCamera() -> PerspectiveCamera {
+ let camera = PerspectiveCamera()
+ camera.camera.fieldOfViewInDegrees = 60
+ camera.position = [0,1,5]
+ camera.look(at: .zero, from: camera.position, relativeTo: nil)
+ return camera
+ }
+
+ // MARK: - Gesture Handling
+
+ func handleDragGesture(
+ _ value: DragGesture.Value,
+ lastDrag: CGSize,
+ currentRotation: SIMD2<Float>
+ ) -> (rotation: SIMD2<Float>, drag: CGSize) {
+ let dx = Float(value.translation.width - lastDrag.width) * 0.01
+ let dy = Float(value.translation.height - lastDrag.height) * 0.01
+
+ let newRotation = SIMD2<Float>(
+ currentRotation.x + dy,
+ currentRotation.y + dx
+ )
+
+ return (rotation: newRotation, drag: value.translation)
+ }
+
+ // MARK: - Private Helpers
+
+ private func createBox(dimensions: SIMD3<Float>) -> ModelEntity {
+ let box = ModelEntity(
+ mesh: .generateBox(size: dimensions, cornerRadius: 0.02),
+ materials: [SimpleMaterial(color: .yellow, isMetallic: false)]
+ )
+ box.position = [0,0,0]
+ box.name = "box"
+ return box
+ }
+
+ private func createBeam2x4(position: SIMD3<Float>, height: Float, rotate: SIMD3<Float>, id: String) -> ModelEntity {
+ let box = ModelEntity(
+ mesh: .generateBox(size: SIMD3(BEAM_2x4_WIDTH,BEAM_2x4_LENGTH,height), cornerRadius: 0.02),
+ materials: [SimpleMaterial(color: .yellow, isMetallic: false)]
+ )
+ //box.look(at: position, from: lookFrom, relativeTo: nil)
+ //box.setOrientation(.init(eulerAngles: lookFrom), relativeTo: nil)
+ box.position = position
+ // Create individual axis rotations
+ let pitch = simd_quatf(angle: Float.pi*rotate.x/180.0, axis: [1, 0, 0]) // X-axis
+ let yaw = simd_quatf(angle: Float.pi*rotate.y/180.0, axis: [0, 1, 0]) // Y-axis
+ let roll = simd_quatf(angle: Float.pi*rotate.z/180.0, axis: [0, 0, 1]) // Z-axis
+
+ // Combine rotations (order matters: usually yaw * pitch * roll)
+ box.transform.rotation = yaw * pitch * roll
+ box.name = "beam-\(id)"
+ return box
+ }
+
+ func generateShedWall(position: SIMD3<Float>, height: Float, widthNum: Int, lengthNum: Int, spaceBeem: Float) -> [BeamData] {
+ var beams: [BeamData] = []
+ var id: Int = 0
+ //Wall 0
+ for i in 0...widthNum {
+ let x0 = Float(i)*spaceBeem
+ beams.append(BeamData(id: "beam\(id)", position: [x0,position.y, position.z], height: height, rotation: [90,0,0]))
+ id += 1
+ }
+ //Wall 1
+ //Wall 2
+ //Wall 3
+ return beams
+ }
+
+ func createBeams(_ beams: [BeamData]) -> [ModelEntity] {
+ return beams.map { beam in
+ createBeam2x4(position: beam.position, height: beam.height, rotate: beam.rotation, id: beam.id)
+ }
+ }
+
+ private func createGround() -> ModelEntity {
+ let ground = ModelEntity(
+ mesh: .generatePlane(width: 10, depth: 10),
+ materials: [UnlitMaterial(color: .green)]
+ )
+ ground.position.y = -0.5
+ return ground
+ }
+}
diff --git a/ShedCalc/RenderModel.swift b/ShedCalc/RenderModel.swift
new file mode 100644
index 0000000..86c5a1d
--- /dev/null
+++ b/ShedCalc/RenderModel.swift
@@ -0,0 +1,19 @@
+//
+// RenderModel.swift
+// ShedCalc
+//
+// Created by Jacky Jack on 01/08/2026.
+//
+import SwiftUI
+import Combine
+
+class RenderModel: ObservableObject {
+ // Scene rotation
+ @Published var rotationX: Float = 0.4
+ @Published var rotationY: Float = 0.6
+
+ func reset() {
+ rotationX = 0.4
+ rotationY = 0.6
+ }
+}
diff --git a/ShedCalc/RenderView.swift b/ShedCalc/RenderView.swift
new file mode 100644
index 0000000..a143e99
--- /dev/null
+++ b/ShedCalc/RenderView.swift
@@ -0,0 +1,70 @@
+//
+// RenderView.swift
+// ShedCalc
+//
+// Created by Jacky Jack on 28/07/2026.
+//
+import SwiftUI
+import RealityKit
+
+struct RenderView: View {
+ private let renderController = RenderController()
+ @Environment(ParamModel.self) var paramModel
+ @StateObject private var viewModel = RenderModel()
+ @State private var lastDrag: CGSize = .zero
+
+ var body: some View {
+ VStack(spacing: 0) {
+ RealityView { content in
+ //add objects to scence
+ let scene = renderController.createScene(dimensions: [0,0,0], rotation: [0,0])
+ content.add(scene)
+
+ //add lights to scene
+
+ //create camera view
+ let camera = renderController.createCamera()
+ content.add(camera)
+
+ //draw beams
+ //let beam = renderController.createBeam2x4(position: [0,0,0], height: 2.4, lookFrom: camera.position, id: "beam0")
+ //content.addChild(beam)
+
+ //draw all beams
+ let beamData = renderController.generateShedWall(position: [0,0,0], height: paramModel.height, widthNum: paramModel.widthBeamNum(), lengthNum: paramModel.lengthBeamNum(), spaceBeem: paramModel.beamSpace)
+ //content.add(beams)
+ let beams = renderController.createBeams(beamData)
+ beams.forEach { scene.addChild($0) }
+
+ } update: { content in
+ // Update box geometry directly in the scene
+ if let scene = content.entities.first(where: { $0.name == "scene" }),
+ let box = scene.findEntity(named: "box") as? ModelEntity {
+ box.model?.mesh = .generateBox(size: paramModel.dimensions(), cornerRadius: 0.02)
+ }
+ // Update scene rotation directly
+ if let scene = content.entities.first(where: { $0.name == "scene" }) {
+ let xRot = simd_quatf(angle: viewModel.rotationX, axis: [1, 0, 0])
+ let yRot = simd_quatf(angle: viewModel.rotationY, axis: [0, 1, 0])
+ scene.transform.rotation = yRot * xRot
+ }
+ }.gesture(
+ DragGesture()
+ .onChanged { value in
+ let result = renderController.handleDragGesture(
+ value,
+ lastDrag: lastDrag,
+ currentRotation: [viewModel.rotationX, viewModel.rotationY]
+ )
+ viewModel.rotationX = result.rotation.x
+ viewModel.rotationY = result.rotation.y
+ lastDrag = result.drag
+ }
+ .onEnded { _ in
+ lastDrag = .zero
+ }
+ )
+ }
+ .frame(minWidth: 500, minHeight: 500)
+ }
+}