help@rskworld.in +91 93305 39277
RSK World
  • Home
  • Development
    • Web Development
    • Mobile Apps
    • Software
    • Games
    • Project
  • Technologies
    • Data Science
    • AI Development
    • Cloud Development
    • Blockchain
    • Cyber Security
    • Dev Tools
    • Testing Tools
  • About
  • Contact

Theme Settings

Color Scheme
Display Options
Font Size
100%
Back to Project
RSK World
swift-ios-calculator
RSK World
swift-ios-calculator
Swift iOS Calculator v1.0 - AI Math Solver + 3D Graphing + Apple Watch Integration + iOS Widgets + Siri Shortcuts + Currency Converter + Scientific Calculator + Matrix Operations + Platform Integration + Modern iOS Development
swift-ios-calculator
  • Assets.xcassets
  • Base.lproj
  • swift-ios-calculator.xcodeproj
  • AIMathSolverViewController.swift26.6 KB
  • AppDelegate.swift1.7 KB
  • CalculatorHistoryViewController.swift6.5 KB
  • CalculatorLogic.swift4.4 KB
  • CalculatorSettingsViewController.swift5.6 KB
  • CalculatorTheme.swift8.3 KB
  • CalculatorUtils.swift6.9 KB
  • CalculatorViewController.swift3.4 KB
  • CalculatorWidget.swift14.3 KB
  • CalculatorWidgetInfo.plist846 B
  • ChemistryCalculatorViewController.swift26.9 KB
  • CurrencyConverterViewController.swift13.3 KB
  • CustomFormulaBuilderViewController.swift22.1 KB
  • EngineeringCalculatorViewController.swift25.7 KB
  • EquationSolverViewController.swift22.8 KB
  • FinancialCalculatorViewController.swift27.5 KB
  • GeometryCalculatorViewController.swift29.7 KB
  • Graphing3DViewController.swift20.8 KB
  • GraphingCalculatorViewController.swift14.7 KB
  • Info.plist3.2 KB
  • LICENSE1.1 KB
  • MainTabBarController.swift22.3 KB
  • MatrixCalculatorViewController.swift26.6 KB
  • PRIVACY_POLICY.md1.6 KB
  • PhysicsCalculatorService.swift8.2 KB
  • ProgrammerCalculatorViewController.swift11 KB
  • README.md8.7 KB
  • RELEASE_NOTES.md6.5 KB
  • SceneDelegate.swift2.8 KB
  • ScientificCalculatorViewController.swift6.2 KB
  • SiriShortcutsManager.swift23.5 KB
  • Swift iOS Calculator.entitlements1.1 KB
  • UnitConverterViewController.swift14 KB
  • WatchCalculatorViewController.swift15.3 KB
  • index.html47.5 KB
Graphing3DViewController.swift
Graphing3DViewController.swift
Raw Download
Find: Go to:
//
//  Graphing3DViewController.swift
//  Swift iOS Calculator
//
//  Created by RSK World on 23/01/2026.
//  Copyright © 2026 RSK World. All rights reserved.
//
//  Developer: Molla Samser (Founder, RSK World)
//  Designer & Tester: Rima Khatun
//  Contact: info@rskworld.com, +91 93305 39277
//  Website: https://rskworld.in
//  Address: Nutanhat, Mongolkote, Purba Burdwan, West Bengal, India - 713147
//

import UIKit
import SceneKit
import CoreGraphics

class Graphing3DViewController: UIViewController {
    
    // MARK: - Outlets
    @IBOutlet weak var sceneView: SCNView!
    @IBOutlet weak var functionTextField: UITextField!
    @IBOutlet weak var plotButton: UIButton!
    @IBOutlet weak var clearButton: UIButton!
    @IBOutlet weak var resetViewButton: UIButton!
    @IBOutlet weak var rotateToggle: UISwitch!
    @IBOutlet weak var gridToggle: UISwitch!
    @IBOutlet weak var axesToggle: UISwitch!
    @IBOutlet weak var colorSegmentedControl: UISegmentedControl!
    @IBOutlet weak var resolutionSlider: UISlider!
    @IBOutlet weak var resolutionLabel: UILabel!
    
    // MARK: - Properties
    private var scene: SCNScene!
    private var cameraNode: SCNNode!
    private var currentFunctionNode: SCNNode?
    private var isRotating = false
    private var gridNode: SCNNode?
    private var axesNode: SCNNode?
    
    private var xRange: ClosedRange<Double> = -5...5
    private var yRange: ClosedRange<Double> = -5...5
    private var resolution: Int = 50
    
    enum ColorScheme: String, CaseIterable {
        case rainbow = "Rainbow"
        case gradient = "Gradient"
        case solid = "Solid"
        case height = "Height Map"
        
        var colors: [UIColor] {
            switch self {
            case .rainbow:
                return [.red, .orange, .yellow, .green, .blue, .purple]
            case .gradient:
                return [.blue, .cyan, .green, .yellow, .red]
            case .solid:
                return [.systemBlue]
            case .height:
                return [.blue, .green, .yellow, .orange, .red]
            }
        }
    }
    
    // MARK: - Lifecycle
    override func viewDidLoad() {
        super.viewDidLoad()
        setupUI()
        setupScene()
        setupControls()
        createGridAndAxes()
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        startRotation()
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        stopRotation()
    }
    
    // MARK: - Setup
    private func setupUI() {
        title = "3D Graphing Calculator"
        view.backgroundColor = CalculatorTheme.shared.backgroundColor
        
        // Setup text field
        functionTextField.backgroundColor = CalculatorTheme.shared.displayBackgroundColor
        functionTextField.textColor = CalculatorTheme.shared.textColor
        functionTextField.placeholder = "Enter function (e.g., sin(sqrt(x²+y²)))"
        functionTextField.layer.cornerRadius = 8
        functionTextField.layer.borderWidth = 1
        functionTextField.layer.borderColor = CalculatorTheme.shared.buttonBackgroundColor.cgColor
        
        // Setup buttons
        setupButton(plotButton, title: "Plot 3D", color: .systemGreen)
        setupButton(clearButton, title: "Clear", color: .systemRed)
        setupButton(resetViewButton, title: "Reset View", color: .systemBlue)
        
        // Setup switches
        setupSwitch(rotateToggle)
        setupSwitch(gridToggle)
        setupSwitch(axesToggle)
        
        // Setup segmented control
        setupSegmentedControl()
        
        // Setup slider
        setupSlider()
    }
    
    private func setupButton(_ button: UIButton, title: String, color: UIColor) {
        button.setTitle(title, for: .normal)
        button.backgroundColor = color
        button.setTitleColor(.white, for: .normal)
        button.layer.cornerRadius = 8
        button.titleLabel?.font = UIFont.systemFont(ofSize: 16, weight: .medium)
    }
    
    private func setupSwitch(_ switchControl: UISwitch) {
        switchControl.backgroundColor = CalculatorTheme.shared.buttonBackgroundColor
        switchControl.onTintColor = CalculatorTheme.shared.accentColor
        switchControl.layer.cornerRadius = 16
    }
    
    private func setupSegmentedControl() {
        colorSegmentedControl.removeAllSegments()
        for (index, scheme) in ColorScheme.allCases.enumerated() {
            colorSegmentedControl.insertSegment(withTitle: scheme.rawValue, at: index, animated: false)
        }
        colorSegmentedControl.selectedSegmentIndex = 0
        colorSegmentedControl.backgroundColor = CalculatorTheme.shared.buttonBackgroundColor
        colorSegmentedControl.selectedSegmentTintColor = CalculatorTheme.shared.accentColor
        colorSegmentedControl.setTitleTextAttributes([NSAttributedString.Key.foregroundColor: CalculatorTheme.shared.textColor], for: .normal)
        colorSegmentedControl.setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.white], for: .selected)
    }
    
    private func setupSlider() {
        resolutionSlider.minimumValue = 20
        resolutionSlider.maximumValue = 100
        resolutionSlider.value = 50
        resolutionSlider.tintColor = CalculatorTheme.shared.accentColor
        updateResolutionLabel()
    }
    
    private func setupScene() {
        scene = SCNScene()
        sceneView.scene = scene
        sceneView.backgroundColor = CalculatorTheme.shared.displayBackgroundColor
        
        // Setup camera
        cameraNode = SCNNode()
        cameraNode.camera = SCNCamera()
        cameraNode.position = SCNVector3(0, 0, 15)
        scene.rootNode.addChildNode(cameraNode)
        
        // Setup lighting
        let ambientLight = SCNNode()
        ambientLight.light = SCNLight()
        ambientLight.light!.type = .ambient
        ambientLight.light!.color = UIColor(white: 0.6, alpha: 1.0)
        scene.rootNode.addChildNode(ambientLight)
        
        let directionalLight = SCNNode()
        directionalLight.light = SCNLight()
        directionalLight.light!.type = .directional
        directionalLight.light!.color = UIColor(white: 0.8, alpha: 1.0)
        directionalLight.position = SCNVector3(0, 10, 10)
        scene.rootNode.addChildNode(directionalLight)
        
        // Setup gesture recognizers
        setupGestureRecognizers()
    }
    
    private func setupControls() {
        rotateToggle.isOn = true
        gridToggle.isOn = true
        axesToggle.isOn = true
    }
    
    private func setupGestureRecognizers() {
        // Pan gesture for rotation
        let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))
        sceneView.addGestureRecognizer(panGesture)
        
        // Pinch gesture for zoom
        let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(handlePinch(_:)))
        sceneView.addGestureRecognizer(pinchGesture)
    }
    
    // MARK: - Actions
    @IBAction func plotButtonPressed(_ sender: UIButton) {
        guard let function = functionTextField.text, !function.isEmpty else {
            showAlert(title: "Error", message: "Please enter a function to plot")
            return
        }
        
        plot3DFunction(function)
    }
    
    @IBAction func clearButtonPressed(_ sender: UIButton) {
        clearGraph()
    }
    
    @IBAction func resetViewButtonPressed(_ sender: UIButton) {
        resetCameraView()
    }
    
    @IBAction func rotateToggleChanged(_ sender: UISwitch) {
        if sender.isOn {
            startRotation()
        } else {
            stopRotation()
        }
    }
    
    @IBAction func gridToggleChanged(_ sender: UISwitch) {
        gridNode?.isHidden = !sender.isOn
    }
    
    @IBAction func axesToggleChanged(_ sender: UISwitch) {
        axesNode?.isHidden = !sender.isOn
    }
    
    @IBAction func colorSchemeChanged(_ sender: UISegmentedControl) {
        if let function = functionTextField.text, !function.isEmpty {
            plot3DFunction(function)
        }
    }
    
    @IBAction func resolutionSliderChanged(_ sender: UISlider) {
        resolution = Int(sender.value)
        updateResolutionLabel()
        
        // Replot if function exists
        if let function = functionTextField.text, !function.isEmpty {
            plot3DFunction(function)
        }
    }
    
    // MARK: - Methods
    private func plot3DFunction(_ function: String) {
        clearGraph()
        
        let geometry = createSurfaceGeometry(function: function)
        let material = createMaterial()
        geometry.materials = [material]
        
        let node = SCNNode(geometry: geometry)
        scene.rootNode.addChildNode(node)
        currentFunctionNode = node
        
        // Animate appearance
        node.opacity = 0
        SCNTransaction.begin()
        SCNTransaction.animationDuration = 1.0
        node.opacity = 1
        SCNTransaction.commit()
    }
    
    private func createSurfaceGeometry(function: String) -> SCNGeometry {
        let vertices = createVertices(function: function)
        let indices = createIndices(resolution: resolution)
        
        let vertexSource = SCNGeometrySource(vertices: vertices)
        let indexElement = SCNGeometryElement(indices: indices, primitiveType: .triangles)
        
        return SCNGeometry(sources: [vertexSource], elements: [indexElement])
    }
    
    private func createVertices(function: String) -> [SCNVector3] {
        var vertices: [SCNVector3] = []
        let xStep = (xRange.upperBound - xRange.lowerBound) / Double(resolution)
        let yStep = (yRange.upperBound - yRange.lowerBound) / Double(resolution)
        
        for i in 0...resolution {
            for j in 0...resolution {
                let x = xRange.lowerBound + Double(i) * xStep
                let y = yRange.lowerBound + Double(j) * yStep
                
                let z = evaluateFunction(function, x: x, y: y)
                
                // Scale and position
                let scaledX = Float(x * 0.5)
                let scaledY = Float(z * 0.5)
                let scaledZ = Float(y * 0.5)
                
                vertices.append(SCNVector3(scaledX, scaledY, scaledZ))
            }
        }
        
        return vertices
    }
    
    private func createIndices(resolution: Int) -> [Int32] {
        var indices: [Int32] = []
        
        for i in 0..<resolution {
            for j in 0..<resolution {
                let topLeft = Int32(i * (resolution + 1) + j)
                let topRight = Int32(topLeft + 1)
                let bottomLeft = Int32((i + 1) * (resolution + 1) + j)
                let bottomRight = Int32(bottomLeft + 1)
                
                // First triangle
                indices.append(contentsOf: [topLeft, bottomLeft, topRight])
                // Second triangle
                indices.append(contentsOf: [topRight, bottomLeft, bottomRight])
            }
        }
        
        return indices
    }
    
    private func createMaterial() -> SCNMaterial {
        let material = SCNMaterial()
        
        let colorScheme = ColorScheme.allCases[colorSegmentedControl.selectedSegmentIndex]
        
        switch colorScheme {
        case .rainbow:
            material.diffuse.contents = createRainbowTexture()
        case .gradient:
            material.diffuse.contents = createGradientTexture()
        case .solid:
            material.diffuse.contents = UIColor.systemBlue
        case .height:
            material.diffuse.contents = createHeightMapTexture()
        }
        
        material.isDoubleSided = true
        material.lightingModel = .phong
        
        return material
    }
    
    private func createRainbowTexture() -> UIImage {
        let size = CGSize(width: 256, height: 256)
        UIGraphicsBeginImageContextWithOptions(size, false, 0)
        let context = UIGraphicsGetCurrentContext()!
        
        let colors = ColorScheme.rainbow.colors
        let colorSpace = CGColorSpaceCreateDeviceRGB()
        let gradient = CGGradient(colorsSpace: colorSpace, colors: colors.map { $0.cgColor } as CFArray, locations: nil)!
        
        context.drawLinearGradient(gradient, start: CGPoint(x: 0, y: 0), end: CGPoint(x: size.width, y: size.height), options: [])
        
        let image = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()
        
        return image
    }
    
    private func createGradientTexture() -> UIImage {
        let size = CGSize(width: 256, height: 256)
        UIGraphicsBeginImageContextWithOptions(size, false, 0)
        let context = UIGraphicsGetCurrentContext()!
        
        let colors = ColorScheme.gradient.colors
        let colorSpace = CGColorSpaceCreateDeviceRGB()
        let gradient = CGGradient(colorsSpace: colorSpace, colors: colors.map { $0.cgColor } as CFArray, locations: nil)!
        
        context.drawLinearGradient(gradient, start: CGPoint(x: 0, y: size.height), end: CGPoint(x: 0, y: 0), options: [])
        
        let image = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()
        
        return image
    }
    
    private func createHeightMapTexture() -> UIImage {
        let size = CGSize(width: 256, height: 256)
        UIGraphicsBeginImageContextWithOptions(size, false, 0)
        let context = UIGraphicsGetCurrentContext()!
        
        let colors = ColorScheme.height.colors
        let colorSpace = CGColorSpaceCreateDeviceRGB()
        let gradient = CGGradient(colorsSpace: colorSpace, colors: colors.map { $0.cgColor } as CFArray, locations: nil)!
        
        context.drawLinearGradient(gradient, start: CGPoint(x: 0, y: 0), end: CGPoint(x: size.width, y: 0), options: [])
        
        let image = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()
        
        return image
    }
    
    private func evaluateFunction(_ function: String, x: Double, y: Double) -> Double {
        // Simple function evaluator - in a real app, use a more sophisticated parser
        let expression = function.lowercased()
            .replacingOccurrences(of: "x", with: "(\(x))")
            .replacingOccurrences(of: "y", with: "(\(y))")
            .replacingOccurrences(of: "^", with: "**")
            .replacingOccurrences(of: "sin", with: "Foundation.sin")
            .replacingOccurrences(of: "cos", with: "Foundation.cos")
            .replacingOccurrences(of: "tan", with: "Foundation.tan")
            .replacingOccurrences(of: "sqrt", with: "Foundation.sqrt")
            .replacingOccurrences(of: "exp", with: "Foundation.exp")
            .replacingOccurrences(of: "log", with: "Foundation.log")
            .replacingOccurrences(of: "abs", with: "Foundation.abs")
            .replacingOccurrences(of: "pi", with: "Double.pi")
        
        // Use NSExpression for evaluation
        guard let nsExpression = try? NSExpression(format: expression) else {
            return 0
        }
        
        return nsExpression.expressionValue(with: nil, context: nil) as? Double ?? 0
    }
    
    private func createGridAndAxes() {
        createGrid()
        createAxes()
    }
    
    private func createGrid() {
        let gridNode = SCNNode()
        
        // Create grid lines
        for i in -10...10 {
            // X-direction lines
            let xLine = SCNNode(geometry: SCNCylinder(radius: 0.02, height: 20))
            xLine.position = SCNVector3(Float(i) * 0.5, -5, 0)
            xLine.eulerAngles = SCNVector3(Float.pi/2, 0, 0)
            xLine.geometry?.firstMaterial?.diffuse.contents = UIColor.systemGray4
            gridNode.addChildNode(xLine)
            
            // Z-direction lines
            let zLine = SCNNode(geometry: SCNCylinder(radius: 0.02, height: 20))
            zLine.position = SCNVector3(0, -5, Float(i) * 0.5)
            zLine.geometry?.firstMaterial?.diffuse.contents = UIColor.systemGray4
            gridNode.addChildNode(zLine)
        }
        
        scene.rootNode.addChildNode(gridNode)
        self.gridNode = gridNode
    }
    
    private func createAxes() {
        let axesNode = SCNNode()
        
        // X-axis (red)
        let xAxis = SCNCylinder(radius: 0.05, height: 20)
        xAxis.firstMaterial?.diffuse.contents = UIColor.systemRed
        let xAxisNode = SCNNode(geometry: xAxis)
        xAxisNode.position = SCNVector3(0, -5, 0)
        xAxisNode.eulerAngles = SCNVector3(0, 0, Float.pi/2)
        axesNode.addChildNode(xAxisNode)
        
        // Y-axis (green)
        let yAxis = SCNCylinder(radius: 0.05, height: 20)
        yAxis.firstMaterial?.diffuse.contents = UIColor.systemGreen
        let yAxisNode = SCNNode(geometry: yAxis)
        yAxisNode.position = SCNVector3(0, 0, 0)
        axesNode.addChildNode(yAxisNode)
        
        // Z-axis (blue)
        let zAxis = SCNCylinder(radius: 0.05, height: 20)
        zAxis.firstMaterial?.diffuse.contents = UIColor.systemBlue
        let zAxisNode = SCNNode(geometry: zAxis)
        zAxisNode.position = SCNVector3(0, -5, 0)
        axesNode.addChildNode(zAxisNode)
        
        scene.rootNode.addChildNode(axesNode)
        self.axesNode = axesNode
    }
    
    private func clearGraph() {
        currentFunctionNode?.removeFromParentNode()
        currentFunctionNode = nil
    }
    
    private func resetCameraView() {
        SCNTransaction.begin()
        SCNTransaction.animationDuration = 1.0
        cameraNode.position = SCNVector3(0, 0, 15)
        cameraNode.eulerAngles = SCNVector3(0, 0, 0)
        SCNTransaction.commit()
    }
    
    private func startRotation() {
        isRotating = true
        rotateScene()
    }
    
    private func stopRotation() {
        isRotating = false
    }
    
    private func rotateScene() {
        guard isRotating else { return }
        
        SCNTransaction.begin()
        SCNTransaction.animationDuration = 0.016 // ~60 FPS
        currentFunctionNode?.eulerAngles.y += 0.01
        SCNTransaction.commit()
        
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.016) {
            self.rotateScene()
        }
    }
    
    private func updateResolutionLabel() {
        resolutionLabel.text = "Resolution: \(resolution)"
    }
    
    // MARK: - Gesture Handlers
    @objc private func handlePan(_ gesture: UIPanGestureRecognizer) {
        let translation = gesture.translation(in: sceneView)
        
        if gesture.state == .changed {
            let rotationAngle = Float(translation.x) * 0.01
            currentFunctionNode?.eulerAngles.y += rotationAngle
            cameraNode.eulerAngles.y += rotationAngle
        }
        
        gesture.setTranslation(.zero, in: sceneView)
    }
    
    @objc private func handlePinch(_ gesture: UIPinchGestureRecognizer) {
        if gesture.state == .changed {
            let scale = Float(gesture.scale)
            cameraNode.position.z = cameraNode.position.z / scale
            gesture.scale = 1.0
        }
    }
    
    private func showAlert(title: String, message: String) {
        let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "OK", style: .default))
        present(alert, animated: true)
    }
}

// MARK: - 3D Function Presets
extension Graphing3DViewController {
    
    func loadPresetFunction(_ preset: PresetFunction) {
        functionTextField.text = preset.function
        plot3DFunction(preset.function)
    }
    
    struct PresetFunction {
        let name: String
        let function: String
        let description: String
    }
    
    static let presetFunctions: [PresetFunction] = [
        PresetFunction(name: "Saddle", function: "x*x - y*y", description: "Classic saddle point"),
        PresetFunction(name: "Paraboloid", function: "x*x + y*y", description: "3D parabola"),
        PresetFunction(name: "Wave", function: "sin(sqrt(x*x + y*y))", description: "Circular wave pattern"),
        PresetFunction(name: "Gaussian", function: "exp(-(x*x + y*y))", description: "Gaussian distribution"),
        PresetFunction(name: "Ripple", function: "sin(x) * cos(y)", description: "Ripple effect"),
        PresetFunction(name: "Cone", function: "sqrt(x*x + y*y)", description: "Cical cone"),
        PresetFunction(name: "Complex", function: "sin(x*y) * exp(-(x*x + y*y)/10)", description: "Complex wave pattern"),
        PresetFunction(name: "Torus", function: "sqrt((sqrt(x*x + y*y) - 3)^2 + 1)", description: "Torus-like shape")
    ]
}
563 lines•20.8 KB
swift

About RSK World

Founded by Molla Samser, with Designer & Tester Rima Khatun, RSK World is your one-stop destination for free programming resources, source code, and development tools.

Founder: Molla Samser
Designer & Tester: Rima Khatun

Development

  • Game Development
  • Web Development
  • Mobile Development
  • AI Development
  • Development Tools

Legal

  • Terms & Conditions
  • Privacy Policy
  • Disclaimer

Contact Info

Nutanhat, Mongolkote
Purba Burdwan, West Bengal
India, 713147

+91 93305 39277

hello@rskworld.in
support@rskworld.in

© 2026 RSK World. All rights reserved.

Content used for educational purposes only. View Disclaimer