Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions 3D_rotating_GIF.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import pyvista as pv
from pyvista import examples
import imageio
import numpy as np

# Load a sample mesh or your own 3D object file
mesh = pv.read("dinosaur_simp_10_1.obj")
# mesh = examples.download_st_helens().warp_by_scalar()

# Set up the plotter
plotter = pv.Plotter(off_screen=True)
plotter.add_mesh(mesh)

# Set the radius of the sphere (distance from the object)
radius = 216

# Set the focal point (center of the object)
focal_point = (0, 0, 0)

# Create a list to store frames
frames = []

# Number of frames for a full rotation
n_frames = 72

# Rotate the object and capture frames
for i in range(n_frames):
angle = 2 * np.pi * i / n_frames # Current angle in radians
# Calculate camera position on the sphere
x = radius * np.cos(angle)
y = radius * np.sin(angle)
z = radius * 0.1 # Slight elevation to view from above
plotter.camera_position = [(x, y, z), focal_point, (0, 0, 1)] # (position, focal point, up direction)
plotter.render() # Render the current frame
# Capture the current frame
image = plotter.screenshot()
frames.append(image)

# Close the plotter
plotter.close()

# Save frames as a GIF
imageio.mimsave('rotating_object.gif', frames, fps=10)
63 changes: 54 additions & 9 deletions class_3d_model.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
# -*- coding: utf-8 -*-
"""
@author: Anton Wang
"""

# 3D model class

import numpy as np
import tqdm

class a_3d_model:
def __init__(self, filepath):
Expand All @@ -14,7 +9,8 @@ def __init__(self, filepath):
self.calculate_plane_equations()
self.calculate_Q_matrices()

def load_obj_file(self):
def _load_obj_file(self):

with open(self.model_filepath) as file:
self.points = []
self.faces = []
Expand All @@ -38,9 +34,57 @@ def load_obj_file(self):
unique_edges_trans, unique_edges_locs=np.unique(self.edges[:,0]*(10**10)+self.edges[:,1], return_index=True)
self.edges=self.edges[unique_edges_locs,:]

def load_obj_file(self):
with open(self.model_filepath) as file:
self.points = []
self.faces = []
while True:
line = file.readline().strip()
if not line:
break
strs = line.split()
if len(strs) == 0:
continue
if strs[0] == "v":
try:
self.points.append((float(strs[1]), float(strs[2]), float(strs[3])))
except ValueError as e:
print(f"Error parsing vertex line: {line} -> {e}")
elif strs[0] == "f":
face = []
for f in strs[1:]:
try:
face.append(int(f.split('/')[0])) # Only take the vertex index, ignoring texture and normal indices
except ValueError as e:
print(f"Error parsing face line: {line} -> {e}")
if len(face) == 3:
self.faces.append(tuple(face))
else:
print(f"Skipping non-triangular face: {face}")
elif strs[0] != "v":
pass

self.points = np.array(self.points)
self.faces = np.array(self.faces, dtype=int)
print(f"Points: {self.points.shape}, Faces: {self.faces.shape}")

if self.faces.shape[0] == 0:
print("No faces were loaded. Please check the OBJ file format and face definitions.")

self.number_of_points = self.points.shape[0]
self.number_of_faces = self.faces.shape[0]
edge_1 = self.faces[:, 0:2]
edge_2 = self.faces[:, 1:]
edge_3 = np.concatenate([self.faces[:, :1], self.faces[:, -1:]], axis=1)
self.edges = np.concatenate([edge_1, edge_2, edge_3], axis=0)
unique_edges_trans, unique_edges_locs = np.unique(self.edges[:, 0] * (10**10) + self.edges[:, 1], return_index=True)
self.edges = self.edges[unique_edges_locs, :]


def calculate_plane_equations(self):
self.plane_equ_para = []
for i in range(0, self.number_of_faces):
#for i in range(0, self.number_of_faces):
for i in tqdm(range(self.number_of_faces), desc="Calculating Plane Equations"):
# solving equation ax+by+cz+d=0, a^2+b^2+c^2=1
# set d=-1, give three points (x1, y1 ,z1), (x2, y2, z2), (x3, y3, z3)
point_1=self.points[self.faces[i,0]-1, :]
Expand All @@ -54,7 +98,8 @@ def calculate_plane_equations(self):

def calculate_Q_matrices(self):
self.Q_matrices = []
for i in range(0, self.number_of_points):
# for i in range(0, self.number_of_points):
for i in tqdm(range(self.number_of_points), desc="Calculating Q Matrices"):
point_index=i+1
# each point is the solution of the intersection of a set of planes
# find the planes for point_index
Expand Down
25 changes: 19 additions & 6 deletions class_mesh_simplify.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
# -*- coding: utf-8 -*-
"""
@author: Anton Wang
"""

import numpy as np
import sys
from mayavi import mlab
import tqdm
# import trimesh

from class_3d_model import a_3d_model

Expand All @@ -20,10 +18,23 @@ def __init__(self, input_filepath, threshold, simplify_ratio):
self.t=threshold
self.ratio=simplify_ratio


def render_3d_model(self):
fig = mlab.figure(size=(800, 600))
mlab.points3d(self.points[:, 0], self.points[:, 1], self.points[:, 2], scale_factor=0.1)
for face in self.faces:
triangle = [self.points[vertex_index - 1] for vertex_index in face]
triangle.append(triangle[0]) # Ensure closed loop
xs, ys, zs = zip(*triangle)
mlab.plot3d(xs, ys, zs, tube_radius=None, line_width=0.2)

mlab.show()

# Select all valid pairs.
def generate_valid_pairs(self):
self.dist_pairs = []
for i in range(0, self.number_of_points):
# for i in range(0, self.number_of_points):
for i in tqdm(range(self.number_of_points), desc="Generate Valid Pairs"):
current_point_location=i+1
current_point=self.points[i,:]
current_point_to_others_dist=(np.sum((self.points-current_point)**2,axis=1))**0.5
Expand Down Expand Up @@ -162,12 +173,14 @@ def iteratively_remove_least_cost_valid_pairs(self):
print('Simplification: '+str(100*(self.number_of_points-self.new_point_count)/(self.number_of_points))+'%')
print('Remaining: '+str(self.number_of_points-self.new_point_count)+' points')
print('\n')


self.new_point_count=self.new_point_count+1

print('Simplification: '+str(100*(self.number_of_points-self.new_point_count)/(self.number_of_points+self.new_point_count))+'%')
print('Remaining: '+str(self.number_of_points-self.new_point_count)+' points')
print('End\n')


def calculate_plane_equation_for_one_face(self, p1, p2, p3):
# input: p1, p2, p3 numpy.array, shape: (3, 1) or (1,3) or (3, )
Expand Down
Loading