Beispiel #1
0
    def create_settings(filename: str):
        """Generate initial settings file based on current working directory.

        :param filename:
        :return:
        """
        # default stl directory
        default_stl_dir = Util.path_conversion("assets/models/")
        # default part name
        default_part_name = "untitled.dat"
        # default part name directory
        default_part_dir = Util.path_conversion("assets/parts/")
        # default author
        default_author = "First Last"
        # default license
        default_license = "Redistributable under CCAL version 2.0 : see CAreadme.txt"
        # default Log directory
        default_log_dir = Util.path_conversion(str(Path.home()) + "/Documents")

        default_settings = {
            "stl_dir": default_stl_dir,
            "part_name": default_part_name,
            "part_dir": default_part_dir,
            "author": default_author,
            "license": default_license,
            "log_dir": default_log_dir
        }
        file_path = Util.path_conversion(f"assets/settings/{filename}")

        try:
            with open(file_path, "w") as file:
                json.dump(default_settings, file, indent=4)
        except FileNotFoundError as ferr:
            print(ferr)
Beispiel #2
0
    def testExportPlane(self):
        # Create an empty temp folder to use for temporary model files.
        try:
            os.mkdir(Util.path_conversion("tests/temp"))
        except OSError:
            pass

        pass
        # The file path we will use.
        file_path = Util.path_conversion("tests/temp/plane.dat")

        # Import the model.
        mesh = ModelShipper.load_stl_model(Util.path_conversion("tests/test_models/plane.stl"))
        self.assertNotEqual(mesh, False)

        model = LDrawModel(mesh)

        """# Export the model.
        ModelShipper.save_ldraw_file_model(file_path, model)

        # Read the file.
        with open(file_path, 'r') as file:
            file_data = file.read()

        # The final file data should look like this.
        self.assertEqual(
            file_data,
            "0 LScan auto generated part plane\n0 Name: plane.dat\n0 Author: Rando\n0 !LICENSE Redistributable under CCAL version 2.0 : see CAreadme.txt\n3 4 -0.5 0.0 0.5 -0.5 0.0 -0.5 0.5 0.0 -0.5\n3 4 0.5 0.0 -0.5 0.5 0.0 0.5 -0.5 0.0 0.5\n")
        """
        # Cleanup.
        if os.path.exists(file_path):
            os.remove(file_path)
        os.rmdir(Util.path_conversion("tests/temp/"))
Beispiel #3
0
 def test_rmdir(self):
     temp_test_dir = Util.path_conversion("tests/temp/temp_test")
     Util.mkdir(temp_test_dir)
     self.assertTrue(Util.is_dir(temp_test_dir))
     Util.rmdir(temp_test_dir)
     self.assertFalse(Util.is_dir(temp_test_dir))
     Util.rmdir(Util.path_conversion("tests/temp"))
    def build_mesh_triangulation_data(file_path):
        mesh = Mesh.from_file(Util.path_conversion(file_path))
        triangles = MeshTriangulation.get_mesh_triangles(mesh)
        normal_groups = MeshTriangulation.make_normal_groups(triangles)
        normal_groups_copy = copy.deepcopy(normal_groups)
        faces = MeshTriangulation.make_face_groups_loop(normal_groups_copy)
        face_boundaries, normals = MeshTriangulation.make_face_boundaries(
            faces)
        simple_boundaries = MeshTriangulation.make_simple_boundaries(
            face_boundaries)
        separate_boundaries = MeshTriangulation.split_boundaries(
            simple_boundaries)
        ordered_separate_boundaries = MeshTriangulation.find_outside_boundary(
            separate_boundaries)
        triangulated_faces = MeshTriangulation.buckets_to_dicts(
            ordered_separate_boundaries)
        mesh_dict = {
            "input_mesh": mesh,
            "triangles": triangles,
            "normal_groups": normal_groups,
            "faces": faces,
            "separate_boundaries": separate_boundaries,
            "ordered_separate_boundaries": ordered_separate_boundaries,
            "triangulated_faces": triangulated_faces
        }

        return mesh_dict
    def test_(self):
        test_message = "test input model message"

        input_model = ModelShipper.load_stl_model(
            Util.path_conversion("tests/test_models/plane.stl"))

        model_message = InputModelMessage(LogType.INFORMATION, test_message,
                                          input_model)

        self.assertEqual(model_message.get_message(), test_message)
        self.assertEqual(model_message.get_message_type(), LogType.INFORMATION)
        self.assertIsNotNone(model_message.get_timestamp())
        self.assertIsNotNone(model_message.get_model())
Beispiel #6
0
    def testImportPlane(self):
        # Load the model from the assets folder.
        mesh = ModelShipper.load_stl_model(Util.path_conversion("tests/test_models/plane.stl"))
        self.assertNotEqual(mesh, False)
        # First triangle facet.
        self.assertEqual(mesh.v0[0], Vector3([0.5, 0., -0.5]))
        self.assertEqual(mesh.v1[0], Vector3([-0.5, 0., -0.5]))
        self.assertEqual(mesh.v2[0], Vector3([-0.5, 0., 0.5]))

        # Second triangle facet.
        self.assertEqual(mesh.v0[1], Vector3([-0.5, 0., 0.5]))
        self.assertEqual(mesh.v1[1], Vector3([0.5, 0., 0.5]))
        self.assertEqual(mesh.v2[1], Vector3([0.5, 0., -0.5]))
    def test_(self):

        # Load the model from the assets folder.
        input_model = ModelShipper.load_stl_model(
            Util.path_conversion("tests/test_models/plane.stl"))
        output_model = LDrawModel(input_model  # Mesh
                                  )

        test_message = "test output model message"
        log_type = LogType.ERROR
        model_message = InputModelMessage(log_type, test_message,
                                          output_model.get_mesh())

        self.assertEqual(model_message.get_message(), test_message)
        self.assertEqual(model_message.get_message_type(), log_type)
        self.assertIsNotNone(model_message.get_timestamp())
Beispiel #8
0
 def setUp(self):
     mesh = Mesh.from_file(
         Util.path_conversion("tests/test_models/2_holes.stl"))
     mesh_triangles = []  # array of Triangles
     self.triangles_count = 0
     for data in mesh.data:
         normal = get_unit_normal(data[0])
         vertex_1 = data[1][0]
         vertex_2 = data[1][1]
         vertex_3 = data[1][2]
         edge_1 = Edge(vertex_1[0], vertex_1[1], vertex_1[2], vertex_2[0],
                       vertex_2[1], vertex_2[2])
         edge_2 = Edge(vertex_2[0], vertex_2[1], vertex_2[2], vertex_3[0],
                       vertex_3[1], vertex_3[2])
         edge_3 = Edge(vertex_3[0], vertex_3[1], vertex_3[2], vertex_1[0],
                       vertex_1[1], vertex_1[2])
         self.triangles_count += 1
         mesh_triangles.append(
             Triangle(edge_1, edge_2, edge_3, normal=normal))
     self.face = Face(mesh_triangles)
     self.normal = [0, 0, 1]
Beispiel #9
0
    def get_assets_file_text(file_name: str):
        """Return the contents of the file in the folder CWD/assets/info/

        :param file_name: The name of the file contained in the assets/info folder.
        :return: The text that was read from the file or None
        """
        enc = "utf-8"

        file_path = Util.path_conversion("assets/info/" + file_name)

        print(file_path)
        text = None
        # Try to open the complete file path and record the text.
        try:
            with open(str(file_path), "r", encoding=enc) as file:
                text = file.read()
        except PermissionError as perr:
            pass
        except FileNotFoundError as ferr:
            pass

        return text
    def test_mesh_triangulation(self):
        file_path = self.model_folder + "3001_dense.stl"
        mesh = Mesh.from_file(Util.path_conversion(file_path))

        # Step 1: Create list of triangle objects from mesh
        triangles = MeshTriangulation.get_mesh_triangles(mesh)

        # Step 2: Group triangles by their normals
        normal_groups = MeshTriangulation.make_normal_groups(triangles)

        # Group normal groups into faces (by connected parts)
        faces = MeshTriangulation.make_face_groups_loop(normal_groups)

        # Step 3: Get only outline edges for each face
        face_boundaries, face_normals = MeshTriangulation.make_face_boundaries(
            faces)

        # Simplify outline edges for each face (remove redundant vertices)
        simple_boundaries = MeshTriangulation.make_simple_boundaries(
            face_boundaries)

        # Split each outline by connected parts
        separate_boundaries = MeshTriangulation.split_boundaries(
            simple_boundaries)

        # Rearranges edges in each face so that outer
        ordered_separate_boundaries = MeshTriangulation.find_outside_boundary(
            separate_boundaries)

        triangulated_faces = MeshTriangulation.buckets_to_dicts(
            ordered_separate_boundaries)

        triangulations = []
        for face in triangulated_faces:
            triangulations.append(MeshTriangulation.triangulate(face))

        MeshTriangulation.triangulation_to_mesh(triangulations, face_normals)
Beispiel #11
0
    def save(self, event):
        """Save the finalized conversion of the input file. Hide main window options and replace them with metadata
        options. Once the user finalizes their metadata options (back or save), they return to the original options.

        :param event: The wx event that was recorded.
        :return: None
        """
        self.save_button.Disable()

        with open(SettingsManager.file_path, "r") as file:
            file_settings = json.load(file)
            part_dir = file_settings["part_dir"]
            part_name = file_settings["part_name"]

        file_path = Util.path_conversion(part_dir + "/" + part_name)
        with open(file_path, "w") as text_file:
            text_file.write(ModelShipper.get_metadata() +
                            ModelShipper.output_data_text)
        self.save_button.Enable()
        UIDriver.fire_event(
            UserEvent(
                UserEventType.LOG_INFO,
                LogMessage(LogType.INFORMATION,
                           "File was saved to '" + file_path + "'.")))
Beispiel #12
0
class SettingsManager:
    settings_path = Util.path_conversion("assets/settings")
    filename = "user_settings.json"
    file_path = settings_path + "/" + filename

    @staticmethod
    def create_settings(filename: str):
        """Generate initial settings file based on current working directory.

        :param filename:
        :return:
        """
        # default stl directory
        default_stl_dir = Util.path_conversion("assets/models/")
        # default part name
        default_part_name = "untitled.dat"
        # default part name directory
        default_part_dir = Util.path_conversion("assets/parts/")
        # default author
        default_author = "First Last"
        # default license
        default_license = "Redistributable under CCAL version 2.0 : see CAreadme.txt"
        # default Log directory
        default_log_dir = Util.path_conversion(str(Path.home()) + "/Documents")

        default_settings = {
            "stl_dir": default_stl_dir,
            "part_name": default_part_name,
            "part_dir": default_part_dir,
            "author": default_author,
            "license": default_license,
            "log_dir": default_log_dir
        }
        file_path = Util.path_conversion(f"assets/settings/{filename}")

        try:
            with open(file_path, "w") as file:
                json.dump(default_settings, file, indent=4)
        except FileNotFoundError as ferr:
            print(ferr)

    @staticmethod
    def save_settings(setting: str, val: str):
        """Save changes to user settings file.
        :param setting:
        :param val:
        :return:
        """
        # Write out settings changes
        # default_part_name is always "untitled.dat"

        try:
            with open(SettingsManager.file_path, "r") as file:
                file_settings = json.load(file)
                file_settings[setting] = val

            with open(SettingsManager.file_path, "w") as file:
                json.dump(file_settings, file, indent=4)

        except FileNotFoundError as ferr:
            print(ferr)

    @staticmethod
    def display_settings():
        """Display all settings and stl file path to standard out."""
        print("\n\nDisplay settings\n")
        try:
            with open(SettingsManager.file_path, "r") as file:
                all_settings = json.load(file)
                print(all_settings)
        except FileNotFoundError as ferr:
            print(ferr)

    @staticmethod
    def get_settings(settings: [str]):
        """
        Return a dictionary of settings and their values
        :param settings:
        :return requested settings:
        """

        if settings:
            try:
                with open(SettingsManager.file_path, "r") as file:
                    all_settings = json.load(file)
                    requested = []
                    for setting in settings:
                        if all_settings[setting]:
                            requested[setting] = all_settings[setting]
                    return requested
            except FileNotFoundError as ferr:
                print(ferr)
Beispiel #13
0
# "Brandon Goldbeck" <*****@*****.**>
# “Anthony Namba” <*****@*****.**>
# “Brandon Le” <*****@*****.**>
# “Ann Peake” <*****@*****.**>
# “Sohan Tamang” <*****@*****.**>
# “An Huynh” <*****@*****.**>
# “Theron Anderson” <*****@*****.**>
# This software is licensed under the MIT License. See LICENSE file for the full text.

import numpy
import unittest
from stl import Mesh
from src.util import Util
from src.model_conversion.ldraw_model import LDrawModel

path = Util.path_conversion("tests/test_models/cube.stl")


class LDrawModelTest(unittest.TestCase):
    """Class to verify the proper implementation of the ldraw_model

    :return: None
    """
    def setUp(self):
        self.mesh = Mesh.from_file(path)
        self.children = []
        self.ldraw_model = LDrawModel(self.mesh)

    def test_initialized(self):
        self.assertEqual(self.mesh.__dict__, self.ldraw_model.mesh.__dict__)
        self.assertEqual([], self.ldraw_model.children)
Beispiel #14
0
 def test_path_conversion(self):
     self.assertEqual(Util.path_conversion(self.relative_path), self.valid_file)
 def test_simple_plane_triangles(self):
     file_path = self.model_folder + "simple_plane_on_xy_180_tris.stl"
     mesh = Mesh.from_file(Util.path_conversion(file_path))
     triangles = MeshTriangulation.get_mesh_triangles(mesh)
     self.assertTrue(len(triangles) == 180)
Beispiel #16
0
    def __init__(self, triangle_data):
        """Constructor for the BasicMaterial.

        :param triangle_data: The triangle data to use in OpenGL Rendering context.
        """
        Material.__init__(self)

        self.vertex_shader = """
# version 420
in layout(location = 0) vec3 vertex_position;
in layout(location = 1) vec2 vertex_uv;
in layout(location = 2) vec3 vertex_normal;

out vec2 uv;
out vec3 position_worldspace;
out vec3 normal_cameraspace;
out vec3 eye_direction_cameraspace;
out vec3 light_direction_cameraspace;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
uniform vec3 light_position_worldspace;

void main() {
    // Output position of the vertex, in clip space
    gl_Position = projection * view * model *  vec4(vertex_position, 1.0);

    // Position of the vertex, in worldspace : M * position
    position_worldspace = (model * vec4(vertex_position,1)).xyz;


    // Vector that goes from the vertex to the camera, in camera space.
    // In camera space, the camera is at the origin (0,0,0).
    vec3 vertex_position_cameraspace = (view * model * vec4(vertex_position,1)).xyz;
    eye_direction_cameraspace = vec3(0,0,0) - vertex_position_cameraspace;

    // Vector that goes from the vertex to the light, in camera space. M is ommited because it's identity.
    vec3 light_position_cameraspace = (view * vec4(light_position_worldspace,1)).xyz;
    light_direction_cameraspace = light_position_cameraspace + eye_direction_cameraspace;

    // Normal of the the vertex, in camera space
    // Only correct if ModelMatrix does not scale the model ! Use its inverse transpose if not.
    normal_cameraspace = (view * model * vec4(vertex_normal,0)).xyz; 

    // UV of the vertex. No special space for this one.
    uv = vertex_uv;
}
"""

        self.fragment_shader = """
# version 420

// Interpolated values from the vertex shaders
in vec2 uv;
in vec3 position_worldspace;
in vec3 normal_cameraspace;
in vec3 eye_direction_cameraspace;
in vec3 light_direction_cameraspace;

// Ouput data
out vec3 color;


uniform sampler2D texture_sampler;
uniform vec3 light_position_worldspace = vec3(0,0,0);

// Light emission properties
uniform vec3 light_color = vec3(0,1,0);
uniform float light_power = 50.0f;
uniform vec3 ambient_color = vec3(0.3, 0.3, 0.3);
uniform vec3 specular_color = vec3(0.2, 0.2, 0.2);

void main() {
    // Material properties
    vec3 material_diffuse_color = texture(texture_sampler, uv ).rgb;
    vec3 material_ambient_color = ambient_color * material_diffuse_color;

    // Distance to the light
    float distance = length(light_position_worldspace - position_worldspace );

    // Normal of the computed fragment, in camera space
    vec3 n = normalize(normal_cameraspace );

    // Direction of the light (from the fragment to the light)
    vec3 l = normalize(light_direction_cameraspace );

    // Cosine of the angle between the normal and the light direction, 
    // clamped above 0
    //  - light is at the vertical of the triangle -> 1
    //  - light is perpendicular to the triangle -> 0
    //  - light is behind the triangle -> 0
    float cos_theta = clamp( dot( n,l ), 0,1 );

    // Eye vector (towards the camera)
    vec3 eye_vector = normalize(eye_direction_cameraspace);
    // Direction in which the triangle reflects the light
    vec3 reflection = reflect(-l,n);
    // Cosine of the angle between the Eye vector and the Reflect vector,
    // clamped to 0
    //  - Looking into the reflection -> 1
    //  - Looking elsewhere -> < 1
    float cos_alpha = clamp(dot(eye_vector, reflection), 0,1 );

    color = 
        // Ambiant : simulates indirect lighting
        material_ambient_color +
        // Diffuse : "color" of the object
        material_diffuse_color * light_color * light_power * cos_theta / (distance*distance) +
        // Specular : reflective highlight, like a mirror
        specular_color * light_color * light_power * pow(cos_alpha,5) / (distance*distance);
}
"""
        try:
            self.shader = OpenGL.GL.shaders.compileProgram(
                OpenGL.GL.shaders.compileShader(self.vertex_shader,
                                                GL_VERTEX_SHADER),
                OpenGL.GL.shaders.compileShader(self.fragment_shader,
                                                GL_FRAGMENT_SHADER))
        except Error:
            print("Failed to compile glsl shader.")
            return

        glUseProgram(self.shader)

        vbo = glGenBuffers(1)
        glBindBuffer(GL_ARRAY_BUFFER, vbo)
        glBufferData(GL_ARRAY_BUFFER,
                     len(triangle_data) * 4, triangle_data, GL_STATIC_DRAW)

        # Positions input to shader.
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 32, ctypes.c_void_p(0))

        # UV input to shader
        glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 32,
                              ctypes.c_void_p(12))

        # Normal input to shader
        glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 32,
                              ctypes.c_void_p(20))

        glEnableVertexAttribArray(0)
        glEnableVertexAttribArray(1)
        glEnableVertexAttribArray(2)
        texture = glGenTextures(1)

        glBindTexture(GL_TEXTURE_2D, texture)
        # Set the texture wrapping parameters
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
        # Set texture filtering parameters
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)

        # load image
        image = Image.open(
            Util.path_conversion("assets/images/default_brick_diffuse.jpg"))
        img_data = numpy.array(list(image.getdata()), numpy.uint8)
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image.width, image.height, 0,
                     GL_RGB, GL_UNSIGNED_BYTE, img_data)
        glEnable(GL_TEXTURE_2D)

        self.set_uniform_matrix4fv("view",
                                   RenderingEngine.camera.get_view_matrix())

        self.set_uniform_matrix4fv("projection", RenderingEngine.projection)

        self.set_uniform3f("light_position_worldspace",
                           Vector3([0.0, 10.0, 10.0]))
        self.set_uniform3f("light_color", Vector3([0.0, 0.0, 1.0]))
        self.set_uniform3f("ambient_color", Vector3([0.5, 0.5, 0.5]))
        self.set_uniform3f("specular_color", Vector3([0.3, 0.3, 0.3]))
        self.set_uniform3f("light_color", Vector3([0.2, 0.2, 0.2]))
        self.set_uniform1f("light_power", 100.0)