Skip to content
11 changes: 4 additions & 7 deletions bot/control/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ class CameraController:
``cmd_zoom``, ``cmd_center`` and ``cmd_align_plane``.
"""

# TODO: check settings maybe a configuration error
def __init__(self, base, scene, settings):
"""
Set up the orthographic camera and its node hierarchy.
Expand Down Expand Up @@ -95,9 +94,8 @@ def refresh_scene(self):

# 6. Ajuster la profondeur de rendu (Near/Far)
# Très important pour ne pas que l'objet soit "tronçonné"
limit = max(100.0, self.model_radius * 5)
self.lens.setNear(-limit)
self.lens.setFar(limit)
self.lens.setNear(self.model_radius)
self.lens.setFar(self.model_radius * 3)

def create_marker(self):
"""
Expand Down Expand Up @@ -201,9 +199,8 @@ def handle_zoom(self, factor):

# NOTE: J'ai réduit le max pour résoudre un problème de précision mathématique des Float32.
# La taille de limit d'origine était beaucoup trop grande max(10000.0, self.model_radius * 100.0)
limit = max(100.0, self.model_radius * 5)
self.lens.setNear(-limit)
self.lens.setFar(limit)
self.lens.setNear(self.model_radius)
self.lens.setFar(self.model_radius * 3)

def recenter(self):
"""
Expand Down
2 changes: 1 addition & 1 deletion bot/control/shortcuts_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ def _format_binding(b) -> str:

@bind(Key("h"), scope="local")
def toggle_help(ctx):
"""Show or hide this help menu."""
"""Toggle help menu."""
if hasattr(ctx.base, "_help_ui") and ctx.base._help_ui is not None:
ctx.base._help_ui.destroy()
ctx.base._help_ui = None
Expand Down
56 changes: 48 additions & 8 deletions bot/core/cad.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ def __init__(self):
super().__init__()
self.initialize()
self.bounds = {"min": [0, 0, 0], "max": [0, 0, 0]}
self.scale_factor = 1.0

def initialize(self):
"""
Expand All @@ -31,8 +32,6 @@ def initialize(self):
gmsh.clear()
# to avoid messages on the console
gmsh.option.setNumber("General.Verbosity", 0)
gmsh.option.setNumber("Mesh.MeshSizeMin", 1)
gmsh.option.setNumber("Mesh.MeshSizeMax", 5)

def finalize(self):
"""
Expand All @@ -58,6 +57,25 @@ def open(self, filename):
else:
gmsh.model.occ.importShapes(filename)

self.__synchronize()
bbox = gmsh.model.getBoundingBox(-1, -1)
max_size = max(bbox[3] - bbox[0], bbox[4] - bbox[1], bbox[5] - bbox[2])

if max_size > 0:
# Set the maximum distance between points (e.g., 5% of the total model size)
gmsh.option.setNumber("Mesh.MeshSizeMax", max_size * 0.05)
# Set the minimum distance between points for tiny details (e.g., 0.1% of the size)
gmsh.option.setNumber("Mesh.MeshSizeMin", max_size * 0.001)

# NOTE: Higher value = smoother curves (20 is a good standard for CAD display)
gmsh.option.setNumber("Mesh.MeshSizeFromCurvature", 20)

# NOTE: normalize the maximum size of the model to exactly 10.0 units
if max_size > 0:
self.scale_factor = 10.0 / max_size
else:
self.scale_factor = 1.0

self._discretize_curves()
self._recompute_bounds()
self.__synchronize()
Expand All @@ -67,8 +85,14 @@ def _recompute_bounds(self):
# The map will be used later to store some geom info
# node_map = {tag: i for i, tag in enumerate(node_tags)}
self.points = [
(coords[i], coords[i + 1], coords[i + 2]) for i in range(0, len(coords), 3)
(
coords[i] * self.scale_factor,
coords[i + 1] * self.scale_factor,
coords[i + 2] * self.scale_factor,
)
for i in range(0, len(coords), 3)
]

if not self.points:
return
# Compute now the bounds for automatic rescaling
Expand All @@ -90,7 +114,10 @@ def add_point(self, coords: list, mesh_size: float = 1.0) -> int:
Returns the gmsh tag of the new point.
Notifies all connected viewers.
"""
tag = gmsh.model.occ.addPoint(coords[0], coords[1], coords[2], mesh_size)
local_coords = [c / self.scale_factor for c in coords]
tag = gmsh.model.occ.addPoint(
local_coords[0], local_coords[1], local_coords[2], mesh_size
)
gmsh.model.occ.synchronize()
gmsh.model.mesh.clear()
gmsh.model.mesh.generate(1)
Expand Down Expand Up @@ -194,7 +221,9 @@ def getClosestPoint(self, dim, tag, coord):
"the dim parameter must be an int comprised between 1 and 2."
)

return gmsh.model.getClosestPoint(dim, tag, coord)[0]
local_coord = [c / self.scale_factor for c in coord]
closest_local = gmsh.model.getClosestPoint(dim, tag, local_coord)[0]
return [c * self.scale_factor for c in closest_local]

def get_end_points(self, curve_tag):
"""
Expand Down Expand Up @@ -256,7 +285,7 @@ def get_point_coords(self, point_tag: int) -> list[float]:
# La dimension 0 correspond aux points dans l'API Gmsh.
# gmsh.model.getValue(dimension, tag, parametric_coords)
coords = gmsh.model.getValue(0, point_tag, [])
return list(coords)
return [c * self.scale_factor for c in coords]

def get_end_points_coords(self, point_tag) -> list[list[float]]:
"""
Expand Down Expand Up @@ -314,7 +343,11 @@ def get_curve_discretization(
] = {}
node_tags, coords, _ = gmsh.model.mesh.getNodes()
node_id_to_coords = {
tag: (coords[i * 3], coords[i * 3 + 1], coords[i * 3 + 2])
tag: (
coords[i * 3] * self.scale_factor,
coords[i * 3 + 1] * self.scale_factor,
coords[i * 3 + 2] * self.scale_factor,
)
for i, tag in enumerate(node_tags)
}

Expand Down Expand Up @@ -350,7 +383,14 @@ def __discretize_surfaces(self):
gmsh.model.mesh.generate(2)
# Get mesh nodes
node_tags, node_coords, _ = gmsh.model.mesh.getNodes()
node_coords_3d = list(zip(*(iter(node_coords),) * 3)) # (x, y, z) tuples
node_coords_3d = [
(
node_coords[i * 3] * self.scale_factor,
node_coords[i * 3 + 1] * self.scale_factor,
node_coords[i * 3 + 2] * self.scale_factor,
)
for i in range(len(node_tags))
] # (x, y, z) tuples

# NOTE: Build a node_id -> (x, y, z) map for later use
node_map = dict(zip(node_tags, node_coords_3d)) # noqa: F841
Expand Down
5 changes: 5 additions & 0 deletions bot/core/spline.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ def __init__(self):
super().__init__()
self._model = ferrispline.PyModel()
self.curves: [str] = []
self.scale_factor: float = 1.0

@staticmethod
def _default_control_points(coords_a, coords_b, degree=3) -> list[list[float]]:
Expand All @@ -42,6 +43,10 @@ def add_curve(
weights: list[float] = None,
knots: list[float] = None,
) -> str:
"""
Adds a new curve to the model.
Automatically scales the control points using the model's scale_factor.
"""
if type == BEZIER_TYP:
self._notify_observers()
tag = self._model.create_bezier(
Expand Down
6 changes: 2 additions & 4 deletions bot/math/constraints.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def mouse_to_constrained_axis(self, m_pos: Point2) -> Optional[List[float]]:
plane_normal = self._plane_normal_from_mask(mask)
if plane_normal is not None:
plane = Plane(plane_normal, Point3(*start))
ray_to = ray_origin + ray_dir * 100000.0
ray_to = ray_origin + ray_dir * 1e9
hit = Point3()
if plane.intersectsLine(hit, ray_origin, ray_to):
return [hit[0], hit[1], hit[2]]
Expand Down Expand Up @@ -89,9 +89,7 @@ def _mouse_to_plane(self, m_pos: Point2) -> Optional[List[float]]:
return None

hit = Point3()
if self.drag_plane.intersectsLine(
hit, ray_origin, ray_origin + ray_dir * 1000000.0
):
if self.drag_plane.intersectsLine(hit, ray_origin, ray_origin + ray_dir * 1e9):
return [hit[0], hit[1], hit[2]]
return None

Expand Down
5 changes: 4 additions & 1 deletion bot/view/curve_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,11 +184,14 @@ def __draw_curve(self):
"""Generates the main visual line of the curve."""
lines = LineSegs()
lines.setThickness(float(self.line_thickness))
if self.tag.split(':')[0] == "spline":
lines.setColor(0, 1, 1, 1)
else:
lines.setColor(1, 0, 1, 1)

for idxA, idxB in self.edges:
lines.moveTo(*self.points[idxA])
lines.drawTo(*self.points[idxB])

if self.curve_geom_node is not None:
self.curve_geom_node.removeNode()

Expand Down
Loading
Loading