forked from hlorus/CAD_Sketcher
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbevel.py
206 lines (156 loc) · 5.88 KB
/
bevel.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
import logging
from enum import Enum
from bpy.types import Operator, Context
from bpy.props import FloatProperty
from mathutils import Vector
from mathutils.geometry import (
intersect_line_line_2d,
intersect_line_sphere_2d,
intersect_sphere_sphere_2d,
)
from ..model.types import SlvsPoint2D
from ..utilities.view import refresh
from ..solver import solve_system
from ..utilities.data_handling import to_list, is_entity_referenced
from ..declarations import Operators
from ..stateful_operator.utilities.register import register_stateops_factory
from ..stateful_operator.state import state_from_args
from .base_2d import Operator2d
logger = logging.getLogger(__name__)
class ElementTypes(str, Enum):
Line = "LINE"
Sphere = "Sphere"
def _get_intersection_func(type_a, type_b):
if all([t == ElementTypes.Line for t in (type_a, type_b)]):
return intersect_line_line_2d
if all([t == ElementTypes.Sphere for t in (type_a, type_b)]):
return intersect_sphere_sphere_2d
return intersect_line_sphere_2d
def _order_intersection_args(arg1, arg2):
if arg1[0] == ElementTypes.Sphere and arg1[0] == ElementTypes.Line:
return arg2, arg1
return arg1, arg2
def _get_offset_line(line, offset):
normal = line.normal()
offset_vec = normal * offset
return (line.p1.co + offset_vec, line.p2.co + offset_vec)
def _get_offset_sphere(arc, offset):
"""Return sphere_co and sphere_radius of offset sphere"""
return arc.ct.co, arc.radius + offset
def _get_offset_elements(entity, offset):
t = ElementTypes.Line if entity.type == "SlvsLine2D" else ElementTypes.Sphere
func = _get_offset_sphere if t == ElementTypes.Sphere else _get_offset_line
return (
(t, func(entity, offset)),
(t, func(entity, -offset)),
)
def _get_intersections(*element_list):
"""Find all intersections between all combinations of elements, (type, element)"""
intersections = []
lenght = len(element_list)
for i, elem_a in enumerate(element_list):
if i + 1 == lenght:
break
for elem_b in element_list[i + 1 :]:
a, b = _order_intersection_args(elem_a, elem_b)
func = _get_intersection_func(a[0], b[0])
retval = to_list(func(*a[1], *b[1]))
for intr in retval:
if not intr:
continue
intersections.append(intr)
return intersections
class View3D_OT_slvs_bevel(Operator, Operator2d):
"""Add a tangential arc between the two segments of a selected point"""
bl_idname = Operators.Bevel
bl_label = "Sketch Bevel"
bl_options = {"REGISTER", "UNDO"}
radius: FloatProperty(name="Radius")
states = (
state_from_args(
"Point",
description="Point to bevel",
pointer="p1",
types=(SlvsPoint2D,),
),
state_from_args(
"Radius",
description="Radius of the bevel",
property="radius",
interactive=True,
),
)
def main(self, context):
sketch = self.sketch
sse = context.scene.sketcher.entities
point = self.p1
radius = self.radius
# Get connected entities from point
connected = []
for e in (*sse.lines2D, *sse.arcs):
# TODO: Priorize non_construction entities
if point in e.connection_points():
connected.append(e)
if len(connected) != 2:
self.report({"WARNING"}, "Point should have two connected segments")
return False
l1, l2 = connected
self.connected = connected
# If more than 1 intersection point, then sort them so we prioritise
# the closest ones to the selected point.
# (Can happen with intersecting arcs)
intersections = sorted(
_get_intersections(
*_get_offset_elements(l1, radius),
*_get_offset_elements(l2, radius),
),
key=lambda i: (i - self.p1.co).length,
)
coords = None
for intersection in intersections:
if hasattr(l1, "is_inside") and not l1.is_inside(intersection):
continue
if hasattr(l2, "is_inside") and not l2.is_inside(intersection):
continue
coords = intersection
break
if not coords:
return False
self.ct = sse.add_point_2d(coords, sketch)
# Get tangent points
p1_co, p2_co = l1.project_point(coords), l2.project_point(coords)
if not all([co is not None for co in (p1_co, p2_co)]):
return False
self.points = (
sse.add_point_2d(p1_co, sketch),
sse.add_point_2d(p2_co, sketch),
)
# Get direction of arc
connection_angle = l1.connection_angle(l2, connection_point=self.p1)
invert = connection_angle < 0
# Add Arc
self.arc = sse.add_arc(sketch.wp.nm, self.ct, *self.points, sketch)
self.arc.invert_direction = invert
refresh(context)
return True
def fini(self, context, succeede):
if not succeede:
return
sketch = self.sketch
# Replace endpoints of existing segments
point = self.p1
p1, p2 = self.points
seg1, seg2 = self.connected
seg1.replace_point(point, p1)
seg2.replace_point(point, p2)
context.view_layer.update()
# Add tangent constraints
ssc = context.scene.sketcher.constraints
ssc.add_tangent(self.arc, seg1, sketch)
ssc.add_tangent(self.arc, seg2, sketch)
# Remove original point if not referenced
if not is_entity_referenced(point, context):
context.scene.sketcher.entities.remove(point.slvs_index)
refresh(context)
solve_system(context)
register, unregister = register_stateops_factory((View3D_OT_slvs_bevel,))