import 'dart:math' as math; import 'package:flutter/material.dart'; /// Class utility untuk transformasi koordinat 3D World (meter) ke 2D Screen (piksel) /// dengan proyeksi Isometrik. class IsometricTransform { /// Skala piksel per meter (misalnya 1 meter = 40 piksel) final double scale; /// Titik origin (0,0,0) di layar final Offset origin; const IsometricTransform({ required this.scale, required this.origin, }); /// Mengonversi koordinat 3D World (x, y, z) dalam satuan meter ke Offset 2D Canvas (piksel). /// * [x] : Arah kedalaman kumbung (dari depan ke belakang) - e.g. 0 s.d 8 meter. /// * [y] : Arah lebar kumbung (dari kiri ke kanan) - e.g. 0 s.d 6 meter. /// * [z] : Arah tinggi kumbung (dari lantai ke atap) - e.g. 0 s.d 3 meter. Offset toScreen(double x, double y, double z) { const double angle = 30.0 * math.pi / 180.0; // Sudut proyeksi isometrik 30 derajat // dx = (x - y) * cos(30) * scale // dy = ((x + y) * sin(30) - z) * scale final double dx = (x - y) * math.cos(angle) * scale; final double dy = ((x + y) * math.sin(angle) - z) * scale; return Offset(origin.dx + dx, origin.dy + dy); } /// Mengonversi koordinat 2D Screen (tap) kembali ke koordinat 2D World Ground (x, y) pada z = 0. /// Berguna untuk mendeteksi penempatan/interaksi ketukan jari di lantai kumbung. Offset toWorldGround(Offset screenPt) { const double angle = 30.0 * math.pi / 180.0; final double cos30 = math.cos(angle); final double sin30 = math.sin(angle); final double rx = screenPt.dx - origin.dx; final double ry = screenPt.dy - origin.dy; // x - y = rx / (cos(30) * scale) // x + y = ry / (sin(30) * scale) final double diffVal = rx / (cos30 * scale); final double sumVal = ry / (sin30 * scale); final double x = (sumVal + diffVal) / 2; final double y = (sumVal - diffVal) / 2; return Offset(x, y); } }