diff options
| author | OsCoder <oscoder@lczimmermann.eu> | 2026-07-22 23:21:40 +0200 |
|---|---|---|
| committer | OsCoder <oscoder@lczimmermann.eu> | 2026-07-22 23:21:40 +0200 |
| commit | 400b321f8799f369cf26cf611462598117004ced (patch) | |
| tree | 85bc676bd110edc57c2faa7461f23b5eb1099cc1 | |
| download | solar_map-400b321f8799f369cf26cf611462598117004ced.tar.xz solar_map-400b321f8799f369cf26cf611462598117004ced.zip | |
initial commit
| -rw-r--r-- | .editorconfig | 8 | ||||
| -rw-r--r-- | .gitattributes | 8 | ||||
| -rw-r--r-- | .gitignore | 27 | ||||
| -rw-r--r-- | LICENSE.txt | 287 | ||||
| -rw-r--r-- | README.md | 20 | ||||
| -rw-r--r-- | galaxy.bin | bin | 0 -> 1536 bytes | |||
| -rw-r--r-- | galaxy.bmp | bin | 0 -> 3456054 bytes | |||
| -rw-r--r-- | galaxy.csv | 25 | ||||
| -rw-r--r-- | render/render.v | 157 | ||||
| -rw-r--r-- | solar_map.v | 168 | ||||
| -rw-r--r-- | solar_map_test.v | 28 | ||||
| -rw-r--r-- | v.mod | 7 |
12 files changed, 735 insertions, 0 deletions
diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..01072ca --- /dev/null +++ b/.editorconfig @@ -0,0 +1,8 @@ +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.v] +indent_style = tab diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..9a98968 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,8 @@ +* text=auto eol=lf +*.bat eol=crlf + +*.v linguist-language=V +*.vv linguist-language=V +*.vsh linguist-language=V +v.mod linguist-language=V +.vdocignore linguist-language=ignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f0d06f8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,27 @@ +# Binaries for programs and plugins +main +solar_map +*.exe +*.exe~ +*.so +*.dylib +*.dll + +# Ignore binary output folders +bin/ + +# Ignore common editor/system specific metadata +.DS_Store +.idea/ +.vscode/ +*.iml + +# ENV +.env + +# Web assets and local databases +*.db +*.js + +# Ignore installed modules through `v install --local`: +modules/ diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..4153cd3 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,287 @@ + EUROPEAN UNION PUBLIC LICENCE v. 1.2 + EUPL © the European Union 2007, 2016 + +This European Union Public Licence (the ‘EUPL’) applies to the Work (as defined +below) which is provided under the terms of this Licence. Any use of the Work, +other than as authorised under this Licence is prohibited (to the extent such +use is covered by a right of the copyright holder of the Work). + +The Work is provided under the terms of this Licence when the Licensor (as +defined below) has placed the following notice immediately following the +copyright notice for the Work: + + Licensed under the EUPL + +or has expressed by any other means his willingness to license under the EUPL. + +1. Definitions + +In this Licence, the following terms have the following meaning: + +- ‘The Licence’: this Licence. + +- ‘The Original Work’: the work or software distributed or communicated by the + Licensor under this Licence, available as Source Code and also as Executable + Code as the case may be. + +- ‘Derivative Works’: the works or software that could be created by the + Licensee, based upon the Original Work or modifications thereof. This Licence + does not define the extent of modification or dependence on the Original Work + required in order to classify a work as a Derivative Work; this extent is + determined by copyright law applicable in the country mentioned in Article 15. + +- ‘The Work’: the Original Work or its Derivative Works. + +- ‘The Source Code’: the human-readable form of the Work which is the most + convenient for people to study and modify. + +- ‘The Executable Code’: any code which has generally been compiled and which is + meant to be interpreted by a computer as a program. + +- ‘The Licensor’: the natural or legal person that distributes or communicates + the Work under the Licence. + +- ‘Contributor(s)’: any natural or legal person who modifies the Work under the + Licence, or otherwise contributes to the creation of a Derivative Work. + +- ‘The Licensee’ or ‘You’: any natural or legal person who makes any usage of + the Work under the terms of the Licence. + +- ‘Distribution’ or ‘Communication’: any act of selling, giving, lending, + renting, distributing, communicating, transmitting, or otherwise making + available, online or offline, copies of the Work or providing access to its + essential functionalities at the disposal of any other natural or legal + person. + +2. Scope of the rights granted by the Licence + +The Licensor hereby grants You a worldwide, royalty-free, non-exclusive, +sublicensable licence to do the following, for the duration of copyright vested +in the Original Work: + +- use the Work in any circumstance and for all usage, +- reproduce the Work, +- modify the Work, and make Derivative Works based upon the Work, +- communicate to the public, including the right to make available or display + the Work or copies thereof to the public and perform publicly, as the case may + be, the Work, +- distribute the Work or copies thereof, +- lend and rent the Work or copies thereof, +- sublicense rights in the Work or copies thereof. + +Those rights can be exercised on any media, supports and formats, whether now +known or later invented, as far as the applicable law permits so. + +In the countries where moral rights apply, the Licensor waives his right to +exercise his moral right to the extent allowed by law in order to make effective +the licence of the economic rights here above listed. + +The Licensor grants to the Licensee royalty-free, non-exclusive usage rights to +any patents held by the Licensor, to the extent necessary to make use of the +rights granted on the Work under this Licence. + +3. Communication of the Source Code + +The Licensor may provide the Work either in its Source Code form, or as +Executable Code. If the Work is provided as Executable Code, the Licensor +provides in addition a machine-readable copy of the Source Code of the Work +along with each copy of the Work that the Licensor distributes or indicates, in +a notice following the copyright notice attached to the Work, a repository where +the Source Code is easily and freely accessible for as long as the Licensor +continues to distribute or communicate the Work. + +4. Limitations on copyright + +Nothing in this Licence is intended to deprive the Licensee of the benefits from +any exception or limitation to the exclusive rights of the rights owners in the +Work, of the exhaustion of those rights or of other applicable limitations +thereto. + +5. Obligations of the Licensee + +The grant of the rights mentioned above is subject to some restrictions and +obligations imposed on the Licensee. Those obligations are the following: + +Attribution right: The Licensee shall keep intact all copyright, patent or +trademarks notices and all notices that refer to the Licence and to the +disclaimer of warranties. The Licensee must include a copy of such notices and a +copy of the Licence with every copy of the Work he/she distributes or +communicates. The Licensee must cause any Derivative Work to carry prominent +notices stating that the Work has been modified and the date of modification. + +Copyleft clause: If the Licensee distributes or communicates copies of the +Original Works or Derivative Works, this Distribution or Communication will be +done under the terms of this Licence or of a later version of this Licence +unless the Original Work is expressly distributed only under this version of the +Licence — for example by communicating ‘EUPL v. 1.2 only’. The Licensee +(becoming Licensor) cannot offer or impose any additional terms or conditions on +the Work or Derivative Work that alter or restrict the terms of the Licence. + +Compatibility clause: If the Licensee Distributes or Communicates Derivative +Works or copies thereof based upon both the Work and another work licensed under +a Compatible Licence, this Distribution or Communication can be done under the +terms of this Compatible Licence. For the sake of this clause, ‘Compatible +Licence’ refers to the licences listed in the appendix attached to this Licence. +Should the Licensee's obligations under the Compatible Licence conflict with +his/her obligations under this Licence, the obligations of the Compatible +Licence shall prevail. + +Provision of Source Code: When distributing or communicating copies of the Work, +the Licensee will provide a machine-readable copy of the Source Code or indicate +a repository where this Source will be easily and freely available for as long +as the Licensee continues to distribute or communicate the Work. + +Legal Protection: This Licence does not grant permission to use the trade names, +trademarks, service marks, or names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the copyright notice. + +6. Chain of Authorship + +The original Licensor warrants that the copyright in the Original Work granted +hereunder is owned by him/her or licensed to him/her and that he/she has the +power and authority to grant the Licence. + +Each Contributor warrants that the copyright in the modifications he/she brings +to the Work are owned by him/her or licensed to him/her and that he/she has the +power and authority to grant the Licence. + +Each time You accept the Licence, the original Licensor and subsequent +Contributors grant You a licence to their contributions to the Work, under the +terms of this Licence. + +7. Disclaimer of Warranty + +The Work is a work in progress, which is continuously improved by numerous +Contributors. It is not a finished work and may therefore contain defects or +‘bugs’ inherent to this type of development. + +For the above reason, the Work is provided under the Licence on an ‘as is’ basis +and without warranties of any kind concerning the Work, including without +limitation merchantability, fitness for a particular purpose, absence of defects +or errors, accuracy, non-infringement of intellectual property rights other than +copyright as stated in Article 6 of this Licence. + +This disclaimer of warranty is an essential part of the Licence and a condition +for the grant of any rights to the Work. + +8. Disclaimer of Liability + +Except in the cases of wilful misconduct or damages directly caused to natural +persons, the Licensor will in no event be liable for any direct or indirect, +material or moral, damages of any kind, arising out of the Licence or of the use +of the Work, including without limitation, damages for loss of goodwill, work +stoppage, computer failure or malfunction, loss of data or any commercial +damage, even if the Licensor has been advised of the possibility of such damage. +However, the Licensor will be liable under statutory product liability laws as +far such laws apply to the Work. + +9. Additional agreements + +While distributing the Work, You may choose to conclude an additional agreement, +defining obligations or services consistent with this Licence. However, if +accepting obligations, You may act only on your own behalf and on your sole +responsibility, not on behalf of the original Licensor or any other Contributor, +and only if You agree to indemnify, defend, and hold each Contributor harmless +for any liability incurred by, or claims asserted against such Contributor by +the fact You have accepted any warranty or additional liability. + +10. Acceptance of the Licence + +The provisions of this Licence can be accepted by clicking on an icon ‘I agree’ +placed under the bottom of a window displaying the text of this Licence or by +affirming consent in any other similar way, in accordance with the rules of +applicable law. Clicking on that icon indicates your clear and irrevocable +acceptance of this Licence and all of its terms and conditions. + +Similarly, you irrevocably accept this Licence and all of its terms and +conditions by exercising any rights granted to You by Article 2 of this Licence, +such as the use of the Work, the creation by You of a Derivative Work or the +Distribution or Communication by You of the Work or copies thereof. + +11. Information to the public + +In case of any Distribution or Communication of the Work by means of electronic +communication by You (for example, by offering to download the Work from a +remote location) the distribution channel or media (for example, a website) must +at least provide to the public the information requested by the applicable law +regarding the Licensor, the Licence and the way it may be accessible, concluded, +stored and reproduced by the Licensee. + +12. Termination of the Licence + +The Licence and the rights granted hereunder will terminate automatically upon +any breach by the Licensee of the terms of the Licence. + +Such a termination will not terminate the licences of any person who has +received the Work from the Licensee under the Licence, provided such persons +remain in full compliance with the Licence. + +13. Miscellaneous + +Without prejudice of Article 9 above, the Licence represents the complete +agreement between the Parties as to the Work. + +If any provision of the Licence is invalid or unenforceable under applicable +law, this will not affect the validity or enforceability of the Licence as a +whole. Such provision will be construed or reformed so as necessary to make it +valid and enforceable. + +The European Commission may publish other linguistic versions or new versions of +this Licence or updated versions of the Appendix, so far this is required and +reasonable, without reducing the scope of the rights granted by the Licence. New +versions of the Licence will be published with a unique version number. + +All linguistic versions of this Licence, approved by the European Commission, +have identical value. Parties can take advantage of the linguistic version of +their choice. + +14. Jurisdiction + +Without prejudice to specific agreement between parties, + +- any litigation resulting from the interpretation of this License, arising + between the European Union institutions, bodies, offices or agencies, as a + Licensor, and any Licensee, will be subject to the jurisdiction of the Court + of Justice of the European Union, as laid down in article 272 of the Treaty on + the Functioning of the European Union, + +- any litigation arising between other parties and resulting from the + interpretation of this License, will be subject to the exclusive jurisdiction + of the competent court where the Licensor resides or conducts its primary + business. + +15. Applicable Law + +Without prejudice to specific agreement between parties, + +- this Licence shall be governed by the law of the European Union Member State + where the Licensor has his seat, resides or has his registered office, + +- this licence shall be governed by Belgian law if the Licensor has no seat, + residence or registered office inside a European Union Member State. + +Appendix + +‘Compatible Licences’ according to Article 5 EUPL are: + +- GNU General Public License (GPL) v. 2, v. 3 +- GNU Affero General Public License (AGPL) v. 3 +- Open Software License (OSL) v. 2.1, v. 3.0 +- Eclipse Public License (EPL) v. 1.0 +- CeCILL v. 2.0, v. 2.1 +- Mozilla Public Licence (MPL) v. 2 +- GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3 +- Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for + works other than software +- European Union Public Licence (EUPL) v. 1.1, v. 1.2 +- Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong + Reciprocity (LiLiQ-R+). + +The European Commission may update this Appendix to later versions of the above +licences without producing a new version of the EUPL, as long as they provide +the rights granted in Article 2 of this Licence and protect the covered Source +Code from exclusive appropriation. + +All other changes or additions to this Appendix require the production of a new +EUPL version. diff --git a/README.md b/README.md new file mode 100644 index 0000000..b477add --- /dev/null +++ b/README.md @@ -0,0 +1,20 @@ +# Solar Map + +A fast and lightweight V module that reads astronomical dataset records from CSV, encodes them into a portable fixed-size binary format, and renders the spatial distribution to a 24-bit BMP image. + +## Features + +- **Fast Memory Dump**: Serializes and deserializes spatial objects (`SpaceBody`) using a custom 64-byte binary layout. +- **Cross-Platform & Safe**: Uses explicit Little-Endian byte-shifting (no `unsafe` blocks), making the binary output completely safe and compatible across different architectures (x86_64, ARM64, RISC-V). +- **Non-Linear Visual Rendering**: Renders positional data into a 24-bit BMP bitmap with square-root distance scaling to clearly separate both inner and outer space bodies. + +## Project Structure + +```text +. +├── main.v # Application entry point & binary file IO +├── galaxy.csv # Source CSV dataset +├── galaxy.bin # Generated binary dataset (created on run) +├── galaxy.bmp # Output image (created on run) +└── render/ + └── render.v # Module for BMP rendering and distance scaling diff --git a/galaxy.bin b/galaxy.bin Binary files differnew file mode 100644 index 0000000..7242b58 --- /dev/null +++ b/galaxy.bin diff --git a/galaxy.bmp b/galaxy.bmp Binary files differnew file mode 100644 index 0000000..d8070ac --- /dev/null +++ b/galaxy.bmp diff --git a/galaxy.csv b/galaxy.csv new file mode 100644 index 0000000..6bee13e --- /dev/null +++ b/galaxy.csv @@ -0,0 +1,25 @@ +id,parent_id,rel_x,rel_y,rel_z,radius,type,flags,comment +1,0,0,0,0,696340000,1,0,Sonne (Zentrum) +2,1,57909050000,0,0,2439700,2,0,Merkur +3,1,108208000000,0,0,6051800,2,0,Venus +4,1,149597870700,0,0,6371000,2,0,Erde +5,4,384400000,0,0,1737400,3,0,Mond +6,1,227939200000,0,0,3389500,2,0,Mars +7,6,9376000,0,0,11267,3,0,Phobos +8,6,23463200,0,0,6200,3,0,Deimos +9,1,413700000000,0,0,469730,4,0,Ceres (Zwergplanet) +10,1,778570000000,0,0,69911000,2,0,Jupiter +11,10,421700000,0,0,1821600,3,0,Io +12,10,670900000,0,0,1560800,3,0,Europa +13,10,1070400000,0,0,2634100,3,0,Ganymed +14,10,1882700000,0,0,2410300,3,0,Kallisto +15,1,1433530000000,0,0,58232000,2,0,Saturn +16,15,1221870000,0,0,2574700,3,0,Titan +17,15,238000000,0,0,252100,3,0,Enceladus +18,1,2870972000000,0,0,25362000,2,0,Uranus +19,18,435910000,0,0,788400,3,0,Titania +20,1,4500000000000,0,0,24622000,2,0,Neptun +21,20,354759000,0,0,1353400,3,0,Triton +22,1,5906380000000,0,0,1188300,4,0,Pluto (Zwergplanet) +23,22,19571000,0,0,606000,3,0,Charon +24,1,10120000000000,0,0,1163000,4,0,Eris (Zwergplanet) diff --git a/render/render.v b/render/render.v new file mode 100644 index 0000000..910e524 --- /dev/null +++ b/render/render.v @@ -0,0 +1,157 @@ +module render + +import os +import math + +@[packed] +pub struct SpaceBody { +pub mut: + id u32 + parent_id u32 + rel_x i64 + rel_y i64 + rel_z i64 + radius u32 + body_type u8 + flags [3]u8 + comment [24]u8 +} + +struct Color { + r u8 + g u8 + b u8 +} + +fn get_abs_x(id u32, bodies map[u32]SpaceBody) i64 { + if id == 0 || id !in bodies { + return 0 + } + body := bodies[id] + if body.parent_id == 0 { + return body.rel_x + } + return body.rel_x + get_abs_x(body.parent_id, bodies) +} + +pub fn draw_bodies_to_bmp(bodies []SpaceBody, output_path string, width int, height int) ! { + center_y := height / 2 + + mut body_map := map[u32]SpaceBody{} + for body in bodies { + body_map[body.id] = body + } + + mut max_abs_x := f64(1) + for body in bodies { + abs_x := math.abs(f64(get_abs_x(body.id, body_map))) + if abs_x > max_abs_x { + max_abs_x = abs_x + } + } + + mut pixels := []u8{len: width * height * 3, init: 10} + + set_pixel := fn (mut px []u8, w int, h int, x int, y int, color Color) { + if x >= 0 && x < w && y >= 0 && y < h { + idx := (y * w + x) * 3 + px[idx] = color.r + px[idx + 1] = color.g + px[idx + 2] = color.b + } + } + + for body in bodies { + abs_x := f64(get_abs_x(body.id, body_map)) + + norm_x := math.sqrt(abs_x / max_abs_x) + px_x := int(50 + norm_x * f64(width - 100)) + + mut col := Color{r: 255, g: 255, b: 255} + mut draw_radius := 3 + + match body.body_type { + 1 { + col = Color{r: 255, g: 220, b: 50} + draw_radius = 12 + } + 2 { + col = Color{r: 100, g: 180, b: 255} + draw_radius = 5 + } + 3 { + col = Color{r: 180, g: 180, b: 180} + draw_radius = 2 + } + 4 { + col = Color{r: 200, g: 130, b: 100} + draw_radius = 3 + } + else {} + } + + for dy := -draw_radius; dy <= draw_radius; dy++ { + for dx := -draw_radius; dx <= draw_radius; dx++ { + if dx * dx + dy * dy <= draw_radius * draw_radius { + set_pixel(mut pixels, width, height, px_x + dx, center_y + dy, col) + } + } + } + } + + save_bmp(output_path, width, height, pixels)! +} + +fn save_bmp(path string, width int, height int, rgb_pixels []u8) ! { + file_header_size := 14 + info_header_size := 40 + row_padding := (4 - (width * 3) % 4) % 4 + pixel_array_size := (width * 3 + row_padding) * height + file_size := file_header_size + info_header_size + pixel_array_size + + mut buf := []u8{cap: file_size} + + // BMP Header + buf << `B` + buf << `M` + buf << u32_to_bytes(u32(file_size)) + buf << [u8(0), 0, 0, 0] + buf << u32_to_bytes(u32(file_header_size + info_header_size)) + + // DIB Header + buf << u32_to_bytes(u32(info_header_size)) + buf << i32_to_bytes(i32(width)) + buf << i32_to_bytes(i32(-height)) + buf << [u8(1), 0] + buf << [u8(24), 0] + buf << u32_to_bytes(0) + buf << u32_to_bytes(u32(pixel_array_size)) + buf << u32_to_bytes(2835) + buf << u32_to_bytes(2835) + buf << u32_to_bytes(0) + buf << u32_to_bytes(0) + + // Pixeldaten (BGR-Reihenfolge) + for y := 0; y < height; y++ { + for x := 0; x < width; x++ { + idx := (y * width + x) * 3 + buf << rgb_pixels[idx + 2] // B + buf << rgb_pixels[idx + 1] // G + buf << rgb_pixels[idx] // R + } + for p := 0; p < row_padding; p++ { + buf << u8(0) + } + } + + os.write_file(path, buf.bytestr())! +} + +fn u32_to_bytes(val u32) []u8 { + return [u8(val & 0xFF), u8((val >> 8) & 0xFF), u8((val >> 16) & 0xFF), u8((val >> 24) & 0xFF)] +} + +fn i32_to_bytes(val i32) []u8 { + u := u32(val) + return [u8(u & 0xFF), u8((u >> 8) & 0xFF), u8((u >> 16) & 0xFF), u8((u >> 24) & 0xFF)] +} diff --git a/solar_map.v b/solar_map.v new file mode 100644 index 0000000..eea0faf --- /dev/null +++ b/solar_map.v @@ -0,0 +1,168 @@ +module solar_map + +import os +import time +import render + +pub type SpaceBody = render.SpaceBody + +// Convert a SpaceBody into a 64-byte Little-Endian array +fn body_to_bytes(b SpaceBody) []u8 { + mut bytes := []u8{cap: 64} + + bytes << u32_to_bytes(b.id) + bytes << u32_to_bytes(b.parent_id) + bytes << i64_to_bytes(b.rel_x) + bytes << i64_to_bytes(b.rel_y) + bytes << i64_to_bytes(b.rel_z) + bytes << u32_to_bytes(b.radius) + bytes << b.body_type + bytes << b.flags[0] + bytes << b.flags[1] + bytes << b.flags[2] + + for i in 0 .. 24 { + bytes << b.comment[i] + } + + return bytes +} + +// Reconstruct a SpaceBody from a 64-byte slice +fn body_from_bytes(data []u8) SpaceBody { + mut flags_arr := [3]u8{} + flags_arr[0] = data[37] + flags_arr[1] = data[38] + flags_arr[2] = data[39] + + mut comment_arr := [24]u8{} + for i in 0 .. 24 { + comment_arr[i] = data[40 + i] + } + + return SpaceBody{ + id: bytes_to_u32(data[0..4]) + parent_id: bytes_to_u32(data[4..8]) + rel_x: bytes_to_i64(data[8..16]) + rel_y: bytes_to_i64(data[16..24]) + rel_z: bytes_to_i64(data[24..32]) + radius: bytes_to_u32(data[32..36]) + body_type: data[36] + flags: flags_arr + comment: comment_arr + } +} + +// Public API function to process a CSV file, output binary data, and optionally render a BMP image +pub fn process_galaxy(csv_path string, generate_image bool) ! { + struct_size := int(sizeof(SpaceBody)) + + if !os.exists(csv_path) { + return error('File not found: ${csv_path}') + } + + // 1. Parse CSV + sw_parse := time.new_stopwatch() + lines := os.read_lines(csv_path)! + + mut bodies := []SpaceBody{cap: lines.len - 1} + + for i := 1; i < lines.len; i++ { + line := lines[i].trim_space() + if line == '' { + continue + } + + parts := line.split(',') + if parts.len < 7 { + continue + } + + mut flag_val := u8(0) + if parts.len >= 8 { + flag_val = parts[7].u8() + } + + mut body := SpaceBody{ + id: parts[0].u32() + parent_id: parts[1].u32() + rel_x: parts[2].i64() + rel_y: parts[3].i64() + rel_z: parts[4].i64() + radius: parts[5].u32() + body_type: parts[6].u8() + flags: [flag_val, u8(0), u8(0)]! + } + + if parts.len >= 9 { + comment_str := parts[8].trim_space() + mut bytes_to_copy := comment_str.len + if bytes_to_copy > 24 { + bytes_to_copy = 24 + } + for j := 0; j < bytes_to_copy; j++ { + body.comment[j] = comment_str[j] + } + } + + bodies << body + } + println('CSV parsed in: ${sw_parse.elapsed().microseconds()} µs') + + // 2. Write binary data + bin_path := csv_path.replace('.csv', '.bin') + mut bytes_to_write := []u8{cap: bodies.len * struct_size} + for body in bodies { + bytes_to_write << body_to_bytes(body) + } + os.write_file(bin_path, bytes_to_write.bytestr())! + + // 3. Load binary data back (Fast Load validation) + sw_load := time.new_stopwatch() + bin_bytes := os.read_bytes(bin_path)! + body_count := bin_bytes.len / struct_size + + mut loaded_bodies := []SpaceBody{cap: body_count} + for i in 0 .. body_count { + offset := i * struct_size + loaded_bodies << body_from_bytes(bin_bytes[offset .. offset + struct_size]) + } + println('Binary loaded in: ${sw_load.elapsed().microseconds()} µs') + + // 4. Render BMP image if requested + if generate_image { + bmp_path := csv_path.replace('.csv', '.bmp') + sw_render := time.new_stopwatch() + render.draw_bodies_to_bmp(loaded_bodies, bmp_path, 1920, 600)! + println('Image successfully rendered to "${bmp_path}" in ${sw_render.elapsed().microseconds()} µs') + } +} + +// Helper functions for Little-Endian conversions +fn u32_to_bytes(v u32) []u8 { + return [u8(v), u8(v >> 8), u8(v >> 16), u8(v >> 24)] +} + +fn i64_to_bytes(v i64) []u8 { + u := u64(v) + return [ + u8(u), + u8(u >> 8), + u8(u >> 16), + u8(u >> 24), + u8(u >> 32), + u8(u >> 40), + u8(u >> 48), + u8(u >> 56), + ] +} + +fn bytes_to_u32(b []u8) u32 { + return u32(b[0]) | (u32(b[1]) << 8) | (u32(b[2]) << 16) | (u32(b[3]) << 24) +} + +fn bytes_to_i64(b []u8) i64 { + u := u64(b[0]) | (u64(b[1]) << 8) | (u64(b[2]) << 16) | (u64(b[3]) << 24) | + (u64(b[4]) << 32) | (u64(b[5]) << 40) | (u64(b[6]) << 48) | (u64(b[7]) << 56) + return i64(u) +} diff --git a/solar_map_test.v b/solar_map_test.v new file mode 100644 index 0000000..6d2d8d9 --- /dev/null +++ b/solar_map_test.v @@ -0,0 +1,28 @@ +module solar_map + +import os + +fn test_process_galaxy() { + // Erstelle eine kleine temporäre Test-CSV + test_csv := 'test_galaxy.csv' + test_bin := 'test_galaxy.bin' + test_bmp := 'test_galaxy.bmp' + + csv_content := 'id,parent_id,rel_x,rel_y,rel_z,radius,type,flags,comment +1,0,0,0,0,696340000,1,0,Sun +2,1,57909050000,0,0,2439700,2,0,Merkur' + + os.write_file(test_csv, csv_content) or { assert false } + + // Ausführen der öffentlichen Funktion + process_galaxy(test_csv, true) or { assert false } + + // Überprüfen, ob die Output-Dateien erzeugt wurden + assert os.exists(test_bin) + assert os.exists(test_bmp) + + // Aufräumen + os.rm(test_csv) or {} + os.rm(test_bin) or {} + os.rm(test_bmp) or {} +} @@ -0,0 +1,7 @@ +Module { + name: 'solar_map' + description: 'A high-performance V utility to convert spatial body CSV data into a binary format and render it to a BMP image.' + version: '0.1.0' + license: 'EUPL-1.2' + dependencies: [] +} |
