Skip to content

Commit

Permalink
Merge branch 'dev' into testing
Browse files Browse the repository at this point in the history
  • Loading branch information
masfaraud authored Oct 17, 2023
2 parents f5d777c + b6fecf8 commit 9e16fbb
Show file tree
Hide file tree
Showing 16 changed files with 547 additions and 107 deletions.
2 changes: 2 additions & 0 deletions .pyenchant_dessia.dict
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ BSpline's
bsplinesurface
cartesian
classmethod
cls
cosinus
ctrlpts
ctrlptsw
Expand All @@ -43,6 +44,7 @@ discretizing
distutils
dof
EdgeStyle
eps
euler
facecolor
filepath
Expand Down
9 changes: 6 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
-

### Fixed
-
- ToroidalSurface3D: line_intersections, linesegment_intersections, plane_intersections
- ToroidalFace3D: PlaneFace3D intersectios.
- PlaneFace3D: circle_intersections.

### Refactor
-
Expand All @@ -20,9 +22,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
-

### Unittests
-
- ToroidalSurface3D: line_intersections, plane_intersections
- ToroidalFace3D: PlaneFace3D intersectios.

## v0.14.0
## v0.14.0 [Unreleased]

### New Features
- DisplayTriangleShell3D: a TriangleShell3D optimized for performance of display / saving / loading.
Expand Down
4 changes: 2 additions & 2 deletions code_pylint.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
'too-many-statements': 13,
'super-init-not-called': 5,
'no-name-in-module': 14,
'abstract-method': 41,
'abstract-method': 45,
'duplicate-code': 10,
'arguments-renamed': 56,
'too-many-ancestors': 25,
Expand All @@ -43,7 +43,7 @@
'unspecified-encoding': 1,
'too-many-function-args': 4,
'too-many-nested-blocks': 7,
'too-many-return-statements': 4,
'too-many-return-statements': 5,
'cyclic-import': 1,
'undefined-variable': 8, # 2 when gmsh is fixed
'broad-except': 1,
Expand Down
4 changes: 2 additions & 2 deletions scripts/showcases/casing.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@

bbox = casing.bounding_box

assert bbox.zmin == -0.005
assert math.isclose(bbox.xmax, 0.340676, abs_tol=1e-5)
assert bbox.zmin == -0.00509003002
assert math.isclose(bbox.xmax, 0.3417427, abs_tol=1e-5)

box = Block.from_bounding_box(bbox)
box.alpha = 0.3
Expand Down
2 changes: 1 addition & 1 deletion tests/core/test_assembly.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def test_bounding_box(self):
components = [box1, box1]
positions = [volmdlr.OXYZ, volmdlr.Frame3D(volmdlr.Point3D(0, 0, 1), volmdlr.X3D, volmdlr.Y3D, volmdlr.Z3D)]
assembly = volmdlr.core.Assembly(components, positions)
self.assertEqual(assembly.bounding_box.volume(), 2) # add assertion here
self.assertAlmostEqual(assembly.bounding_box.volume(), 2.0322289545807095) # add assertion here


if __name__ == '__main__':
Expand Down
2 changes: 1 addition & 1 deletion tests/core/test_bounding_box.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def test_points(self):
def test_from_bounding_boxes(self):
self.assertEqual(
BoundingBox.from_bounding_boxes([self.bbox1, self.bbox2, self.bbox3]),
BoundingBox(0.0, 5.0, 0.0, 5.0, -1.0, 5.0),
BoundingBox(-0.005, 5.005, -0.005, 5.005, -1.006, 5.006),
)

def test_from_points(self):
Expand Down
7 changes: 5 additions & 2 deletions tests/faces/test_extrusionface3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@
from volmdlr.core import VolumeModel
from volmdlr.step import Step
from volmdlr import faces
import os

folder = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'objects_extrusion_tests')


class TestExtrusionFace3D(unittest.TestCase):
face = faces.ExtrusionFace3D.load_from_file(
"faces/objects_extrusion_tests/extrusionface_with_ellipse_test_boundingbox.json")
os.path.join(folder, "extrusionface_with_ellipse_test_boundingbox.json"))

def test_bounding_box(self):
bbox = self.face.bounding_box
self.assertAlmostEqual(bbox.volume(), 3.54136919512989e-08)
self.assertAlmostEqual(bbox.volume(), 4.078418559129855e-08)

def test_to_step(self):
model = VolumeModel.load_from_file("faces/objects_extrusion_tests/extrusionface_export_test.json")
Expand Down
21 changes: 21 additions & 0 deletions tests/faces/test_toroidalface3d.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import math
import numpy as npy
import unittest
import volmdlr
from volmdlr import faces, surfaces, wires


Expand All @@ -18,6 +20,25 @@ def test_from_contours3d(self):
self.assertAlmostEqual(face.surface2d.area(), math.pi**2, 4)
self.assertTrue(face.surface2d.outer_contour.is_ordered())

def test_planeface_intersections(self):
expected_results = [[14.700000000000001], [9.388571325840358], [9.282044389126776], [9.107655247892733],
[8.870824266395537], [8.582455249249456], [5.000000000001248, 5.00000000000125],
[3.717515834742253, 3.7176228274330256], [3.325526484035388, 3.426440202827559],
[3.081959775756376, 3.081960004770588]]
ts = surfaces.ToroidalSurface3D(volmdlr.OXYZ, 2, 1)
tf = faces.ToroidalFace3D.from_surface_rectangular_cut(ts, -1.4, 3.5, 0., 2.5)

list_expected_lenghts1 = []
plane1 = surfaces.Plane3D(volmdlr.OXYZ)
plane1 = plane1.rotation(volmdlr.O3D, volmdlr.Z3D, math.pi / 4)
for i, n in enumerate(npy.linspace(0, math.pi / 4, 10)):
plane = plane1.rotation(plane1.frame.origin, volmdlr.X3D, n)
plane_face = faces.PlaneFace3D.from_surface_rectangular_cut(plane, 4, -4, 4, -4)
plane_intersections = tf.face_intersections(plane_face)
list_expected_lenghts1.append([i.length() for i in plane_intersections])
for result, expected_result in zip(plane_intersections, expected_results[i]):
self.assertAlmostEqual(result.length(), expected_result)


if __name__ == '__main__':
unittest.main()
2 changes: 1 addition & 1 deletion tests/shells/test_closedshell3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ def test_cut_by_plane(self):
plane = surfaces.Plane3D.from_normal(center, volmdlr.Y3D)
cut_by_plane = union.cut_by_plane(plane)
self.assertEqual(len(cut_by_plane), 1)
self.assertEqual(cut_by_plane[0].area(), 0.254)
self.assertAlmostEqual(cut_by_plane[0].area(), 0.254)

def test_intersection(self):
box1 = primitives3d.Block(
Expand Down
64 changes: 62 additions & 2 deletions tests/surfaces/test_toroidal_surface3d.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import math
import unittest

import numpy as np
import volmdlr
from volmdlr import edges, surfaces, wires

Expand All @@ -12,7 +12,10 @@ class TestToroidalSurface3D(unittest.TestCase):
volmdlr.Vector3D(-0.7505709694705869, -0.4949144228333324, 0.43783893597935386),
volmdlr.Vector3D(-0.0, 0.6625993710787045, 0.748974013865705))
toroidal_surface2 = surfaces.ToroidalSurface3D(frame, 0.000725, 0.000125)

linesegs = [
edges.LineSegment3D(volmdlr.Point3D(4, 0, 0), volmdlr.Point3D(-4, 0.25, 0.25)),
edges.LineSegment3D(volmdlr.Point3D(4, 0, 0), volmdlr.Point3D(-4, 0.25, 4)),
]
def test_arc3d_to_2d(self):
arc1 = edges.Arc3D.from_3_points(volmdlr.Point3D(1-0.1/math.sqrt(2), 0, 0.1/math.sqrt(2)),
volmdlr.Point3D(0.9, 0, 0), volmdlr.Point3D(1-0.1/math.sqrt(2), 0, -0.1/math.sqrt(2)))
Expand Down Expand Up @@ -96,6 +99,63 @@ def test_contour3d_to_2d(self):
self.assertTrue(contour2d.is_ordered())
self.assertAlmostEqual(contour2d.area(), 1.0990644259885822, 2)

def test_line_intersections(self):
expected_results = [[volmdlr.Point3D(2.9993479584651066, 0.031270376297965426, 0.031270376297965426),
volmdlr.Point3D(1.0000193965498871, 0.09374939385781603, 0.09374939385781603),
volmdlr.Point3D(-1.0001508657814862, 0.15625471455567144, 0.15625471455567144),
volmdlr.Point3D(-2.968027405412837, 0.21775085641915115, 0.21775085641915115)],
[volmdlr.Point3D(2.799597842042955, 0.03751256743615766, 0.6002010789785226),
volmdlr.Point3D(2.000000955072264, 0.06249997015399175, 0.999999522463868)]]

toroidal_surface = surfaces.ToroidalSurface3D(volmdlr.OXYZ, 2, 1)
for i, lineseg in enumerate(self.linesegs):
inters = toroidal_surface.line_intersections(lineseg.line)
for expected_result, inter in zip(expected_results[i], inters):
self.assertTrue(expected_result.is_close(inter))

def test_plane_intersections(self):
expected_results1 = [[18.84955592153876, 6.283185307179586], [18.774778972399815, 6.306324629324003],
[18.561749480682863, 6.382385910045063], [18.213003549947153, 6.522535326954354],
[17.73936325618824, 6.755617023729998], [17.162568850138605, 7.158697717832439],
[12.566370614359176, 12.566370614359176], [9.548698487359694, 9.548754306669936],
[8.513195638064289, 8.627615279189477], [7.859510527335956, 7.859510528088488]]
expected_results2 = [18.007768707061835, 7.124972521656521]
expected_results3 = [[6.283185307179586, 6.283185307179586], [6.287527793065231, 6.2875277930653075],
[6.304007940714406, 6.304008313559747], [6.402392698169013, 6.385653138168592],
[6.374756663452417, 6.3742078689160975], [6.432102115248554, 6.435832133353287],
[6.51052494933726, 6.510526578371843], [6.617587948267936, 6.6174395158125385],
[6.770799162158078, 6.770798442215525], [7.02765259262962, 7.027622387212875],
[14.078057920670332], [13.573542097891794], [13.223883969710258], [12.919853160635844],
[12.627496331426345], [12.329955764747575], [12.016630378969065], [11.679649471224833],
[11.312293366198755], [10.908108182904483]]
toroidal_surface = surfaces.ToroidalSurface3D(volmdlr.OXYZ, 2, 1)
plane1 = surfaces.Plane3D(volmdlr.OXYZ)
plane1 = plane1.rotation(volmdlr.O3D, volmdlr.Z3D, math.pi / 4)
for i, n in enumerate(np.linspace(0, math.pi / 4, 10)):
plane = plane1.rotation(plane1.frame.origin, volmdlr.X3D, n)
plane_intersections = toroidal_surface.plane_intersections(plane)
for intersection, expected_result in zip(plane_intersections, expected_results1[i]):
self.assertAlmostEqual(intersection.length(), expected_result)

plane2 = surfaces.Plane3D(volmdlr.Frame3D(volmdlr.Point3D(0, 0, 0.5), volmdlr.X3D,
volmdlr.Y3D, volmdlr.Z3D))
plane_intersections = toroidal_surface.plane_intersections(plane2)
self.assertAlmostEqual(plane_intersections[0].length(), expected_results2[0])
self.assertAlmostEqual(plane_intersections[1].length(), expected_results2[1])

plane3 = surfaces.Plane3D(volmdlr.OYZX)
for i, n in enumerate(np.linspace(0, 2, 20)):
plane = plane3.translation(n * volmdlr.X3D)
plane_intersections = toroidal_surface.plane_intersections(plane)
for intersection, expected_result in zip(plane_intersections, expected_results3[i]):
self.assertAlmostEqual(intersection.length(), expected_result)

plane4 = surfaces.Plane3D(volmdlr.OYZX)
plane4 = plane4.translation(volmdlr.X3D)
plane_intersections = toroidal_surface.plane_intersections(plane4)
for intersection, expected_result in zip(plane_intersections, [7.4155670073846425, 7.4149870554616255]):
self.assertAlmostEqual(intersection.length(), expected_result)


if __name__ == '__main__':
unittest.main()
11 changes: 11 additions & 0 deletions volmdlr/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -601,10 +601,21 @@ def from_bounding_boxes(cls, bounding_boxes: List["BoundingBox"], name: str = ''
"""
xmin = min(bb.xmin for bb in bounding_boxes)
xmax = max(bb.xmax for bb in bounding_boxes)
x_length = xmax - xmin
xmin -= x_length * 0.001
xmax += x_length * 0.001

ymin = min(bb.ymin for bb in bounding_boxes)
ymax = max(bb.ymax for bb in bounding_boxes)
y_length = ymax - ymin
ymin -= y_length * 0.001
ymax += y_length * 0.001

zmin = min(bb.zmin for bb in bounding_boxes)
zmax = max(bb.zmax for bb in bounding_boxes)
z_length = zmax - zmin
zmin -= z_length * 0.001
zmax += z_length * 0.001
return cls(xmin, xmax, ymin, ymax, zmin, zmax, name=name)

@classmethod
Expand Down
8 changes: 5 additions & 3 deletions volmdlr/curves.py
Original file line number Diff line number Diff line change
Expand Up @@ -1240,6 +1240,8 @@ def line_intersections(self, line2d: Line2D, tol=1e-9):
:param tol: tolerance to consider in calculations.
:return: circle and line intersections.
"""
if line2d.point_distance(self.center) > self.radius + tol:
return []
if line2d.point_belongs(self.center):
direction_vector = line2d.unit_direction_vector()
return [self.center + self.radius * direction_vector, self.center - self.radius * direction_vector]
Expand Down Expand Up @@ -2158,9 +2160,9 @@ def point_at_abscissa(self, abscissa):
angle_start = 0
abscissa_angle = vm_common_operations.ellipse_abscissa_angle_integration(
self, abscissa, angle_start, initial_angle)
x = self.major_axis * math.cos(abscissa_angle)
y = self.minor_axis * math.sin(abscissa_angle)
return self.frame.local_to_global_coordinates(volmdlr.Point2D(x, y))
return self.frame.local_to_global_coordinates(
volmdlr.Point2D(self.major_axis * math.cos(abscissa_angle),
self.minor_axis * math.sin(abscissa_angle)))

def point_distance(self, point):
"""
Expand Down
61 changes: 58 additions & 3 deletions volmdlr/edges.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import sys
import warnings
from itertools import product
from typing import List, Union, Dict, Any
from typing import List, Union

import dessia_common.core as dc
import matplotlib.patches
Expand Down Expand Up @@ -84,6 +84,7 @@ def split(self, split_point, tol: float = 1e-6):
raise NotImplementedError(f'split method not implemented by {self.__class__.__name__}')

def reverse(self):
"""Gets the edge in the reverse direction."""
if self._reverse is None:
self._reverse = self.get_reverse()
return self._reverse
Expand Down Expand Up @@ -3316,6 +3317,20 @@ def copy(self, *args, **kwargs):

@classmethod
def dict_to_object(cls, dict_, *args, **kwargs):
"""
Create a FullArc2D object from a dictionary representation.
This class method takes a dictionary containing the necessary data for
creating a FullArc2D object and returns an instance of the FullArc2D class.
It expects the dictionary to have the following keys:
:param cls: The FullArc2D class itself (automatically passed).
:param dict_: A dictionary containing the required data for object creation.
:param args: Additional positional arguments (if any).
:param kwargs: Additional keyword arguments (if any).
:return: FullArc2D: An instance of the FullArc2D class created from the provided dictionary.
"""
circle = volmdlr_curves.Circle2D.dict_to_object(dict_['circle'])
start_end = volmdlr.Point2D.dict_to_object(dict_['start_end'])

Expand Down Expand Up @@ -6021,6 +6036,16 @@ def __init__(self, ellipse: volmdlr_curves.Ellipse3D, start: volmdlr.Point3D, en
self._bbox = None

def get_start_end_angles(self):
"""
Calculate the start and end angles of the ArcEllipse3D in radians.
This method computes the start and end angles of the ArcEllipse3D, which represent
the angles, in radians, between the major axis of the ellipse and the start and end points
on the ellipse's boundary.
:return: tuple of floats
A tuple containing the start and end angles in radians.
"""
local_start_point = self.ellipse.frame.global_to_local_coordinates(self.start)
u1, u2 = local_start_point.x / self.ellipse.major_axis, local_start_point.y / self.ellipse.minor_axis
start_angle = volmdlr.geometry.sin_cos_angle(u1, u2)
Expand Down Expand Up @@ -6091,7 +6116,23 @@ def normal_vector(self, abscissa):
return self.direction_vector(abscissa).deterministic_normal_vector()

def direction_vector(self, abscissa):
"""Returns the tangent vector at a given abscissa."""
"""
Returns the tangent vector at a given abscissa along the ArcEllipse3D.
This method calculates and returns the tangent vector at a specific abscissa
along the ArcEllipse3D, which represents the direction of the curve at that point.
:param abscissa: The parameter value (abscissa) along the curve.
:type abscissa: float
:return: Vector3D
A Vector3D object representing the tangent vector at the given abscissa.
:raises:
- ValueError: If the abscissa is out of the valid range of the curve.
"""
if abscissa > self.length():
raise ValueError('The abscissa is out of the valid range of the curve.')
direction_vector_2d = self.self_2d.direction_vector(abscissa)
direction_vector_3d = direction_vector_2d.to_3d(
self.ellipse.center, self.ellipse.frame.u, self.ellipse.frame.v)
Expand Down Expand Up @@ -6355,7 +6396,21 @@ def to_dict(self, use_pointers: bool = False, memo=None, path: str = '#'):
return dict_

@classmethod
def dict_to_object(cls, dict_, global_dict=None, pointers_memo: Dict[str, Any] = None, path: str = '#'):
def dict_to_object(cls, dict_, *args, **kwargs):
"""
Create a FullArcEllipse3D object from a dictionary representation.
This class method takes a dictionary containing the necessary data for
creating a FullArcEllipse3D object and returns an instance of the FullArcEllipse3D class.
It expects the dictionary to have the following keys:
:param cls: The FullArcEllipse3D class itself (automatically passed).
:param dict_: A dictionary containing the required data for object creation.
:param args: Additional positional arguments (if any).
:param kwargs: Additional keyword arguments (if any).
:return: FullArcEllipse3D: An instance of the FullArcEllipse3D class created from the provided dictionary.
"""
ellipse = volmdlr_curves.Ellipse3D.dict_to_object(dict_['ellipse'])
start_end = volmdlr.Point3D.dict_to_object(dict_['start_end'])

Expand Down
Loading

0 comments on commit 9e16fbb

Please sign in to comment.