FajarHidaa commited on
Commit
a740478
·
verified ·
1 Parent(s): e9ca564

Create v7.2

Browse files
Files changed (1) hide show
  1. v7.2 +2635 -0
v7.2 ADDED
@@ -0,0 +1,2635 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Option Explicit
2
+
3
+ Private Const EPS As Double = 0.000001
4
+ Private Const ROLL_GAP As Double = 5#
5
+ Private Const MAX_ROLLS As Long = 100
6
+
7
+ Private Const MAX_RIGHT_WASTE As Double = 10#
8
+ Private Const ALIGN_TOLERANCE As Double = 0.5
9
+
10
+ ' Material area is the primary objective: roll width x actual used height.
11
+ ' Secondary terms are intentionally much smaller so they cannot beat a
12
+ ' meaningful material saving.
13
+ Private Const MATERIAL_SCORE_SCALE As Double = 1000000000#
14
+ Private Const RIGHT_WASTE_SCORE_SCALE As Double = 100000#
15
+ Private Const ALIGNMENT_SCORE_SCALE As Double = 0.01
16
+ Private Const ROLL_COUNT_SCORE_SCALE As Double = 0.0001
17
+
18
+ Private Const MAX_LOCAL_SEEDS As Long = 5
19
+ Private Const MAX_PAIR_MOVES As Long = 80
20
+ Private Const MAX_REINSERT_MOVES As Long = 100
21
+ Private Const MAX_TRIPLE_MOVES As Long = 40
22
+ Private Const MAX_LOCAL_PASSES As Long = 3
23
+
24
+ Private Const MAX_LNS_ITERATIONS As Long = 12
25
+ Private Const MAX_LNS_REPAIR_POSITIONS As Long = 30
26
+ Private Const MAX_ROTATION_MUTATIONS As Long = 24
27
+ Private Const LNS_ACCEPT_WORSE_PCT As Double = 0.003
28
+
29
+ Private Const CONTACT_TOLERANCE As Double = 0.05
30
+ Private Const CONTACT_PLACEMENT_WEIGHT As Double = 0.35
31
+ Private Const GUILLOTINE_PLACEMENT_WEIGHT As Double = 0.25
32
+ Private Const CONTACT_SCORE_WEIGHT As Double = 0.25
33
+ Private Const GUILLOTINE_SCORE_WEIGHT As Double = 0.35
34
+ Private Const MAX_CONTACT_MOVES As Long = 60
35
+ Private Const MAX_CUT_MOVES As Long = 50
36
+
37
+ ' Multi-width search tests pairs from at most the first 6 entered widths.
38
+ Private Const MAX_MULTI_WIDTH_CANDIDATES As Long = 6
39
+
40
+ Private Type TBanner
41
+ Label As String
42
+ OrigW As Double
43
+ OrigH As Double
44
+ margin As Double
45
+ End Type
46
+
47
+ Private Type TFreeRect
48
+ x As Double
49
+ y As Double
50
+ w As Double
51
+ h As Double
52
+ End Type
53
+
54
+ Private Type TPlaced
55
+ rid As Long
56
+ x As Double
57
+ y As Double
58
+ w As Double
59
+ h As Double
60
+ Rotated As Boolean
61
+ End Type
62
+
63
+ Private Type TRoll
64
+ placed() As TPlaced
65
+ placedCount As Long
66
+ UsedHeight As Double
67
+ End Type
68
+
69
+ Private Function MinD(ByVal a As Double, ByVal b As Double) As Double
70
+ If a < b Then
71
+ MinD = a
72
+ Else
73
+ MinD = b
74
+ End If
75
+ End Function
76
+
77
+ Private Function MaxD(ByVal a As Double, ByVal b As Double) As Double
78
+ If a > b Then
79
+ MaxD = a
80
+ Else
81
+ MaxD = b
82
+ End If
83
+ End Function
84
+
85
+ Private Function AbsD(ByVal a As Double) As Double
86
+ If a < 0# Then
87
+ AbsD = -a
88
+ Else
89
+ AbsD = a
90
+ End If
91
+ End Function
92
+
93
+ Private Function AreaOf(ByRef item As TBanner) As Double
94
+ AreaOf = (item.OrigW + 2# * item.margin) * (item.OrigH + 2# * item.margin)
95
+ End Function
96
+
97
+ Private Function SortValue(ByRef item As TBanner, ByVal sortMode As Long) As Double
98
+ Dim w As Double
99
+ Dim h As Double
100
+ Dim shortSide As Double
101
+ Dim longSide As Double
102
+
103
+ w = item.OrigW + 2# * item.margin
104
+ h = item.OrigH + 2# * item.margin
105
+ shortSide = MinD(w, h)
106
+ longSide = MaxD(w, h)
107
+
108
+ Select Case sortMode
109
+ Case 1
110
+ SortValue = w * h
111
+ Case 2
112
+ SortValue = w
113
+ Case 3
114
+ SortValue = h
115
+ Case 4
116
+ SortValue = shortSide
117
+ Case 5
118
+ SortValue = longSide
119
+ Case 6
120
+ If shortSide > EPS Then
121
+ SortValue = longSide / shortSide
122
+ Else
123
+ SortValue = 0#
124
+ End If
125
+ Case Else
126
+ SortValue = w * h
127
+ End Select
128
+ End Function
129
+
130
+ Private Sub SortIndex(ByRef items() As TBanner, ByRef sourceIdx() As Long, ByVal n As Long, ByVal sortMode As Long, ByRef sortedIdx() As Long)
131
+ Dim i As Long
132
+ Dim j As Long
133
+ Dim tmp As Long
134
+
135
+ ReDim sortedIdx(1 To n)
136
+ For i = 1 To n
137
+ sortedIdx(i) = sourceIdx(i)
138
+ Next i
139
+
140
+ For i = 1 To n - 1
141
+ For j = i + 1 To n
142
+ If SortValue(items(sortedIdx(j)), sortMode) > SortValue(items(sortedIdx(i)), sortMode) Then
143
+ tmp = sortedIdx(i)
144
+ sortedIdx(i) = sortedIdx(j)
145
+ sortedIdx(j) = tmp
146
+ End If
147
+ Next j
148
+ Next i
149
+ End Sub
150
+
151
+ Private Sub BuildInitialOrder(ByRef items() As TBanner, ByVal n As Long, ByVal sortMode As Long, ByRef order() As Long)
152
+ Dim sourceIdx() As Long
153
+ Dim i As Long
154
+
155
+ ReDim sourceIdx(1 To n)
156
+ ReDim order(1 To n)
157
+
158
+ For i = 1 To n
159
+ sourceIdx(i) = i
160
+ Next i
161
+
162
+ SortIndex items, sourceIdx, n, sortMode, order
163
+ End Sub
164
+
165
+ Private Sub CopyOrder(ByRef source() As Long, ByVal sourceCount As Long, ByRef target() As Long)
166
+ Dim i As Long
167
+
168
+ If sourceCount <= 0 Then
169
+ ReDim target(1 To 1)
170
+ Exit Sub
171
+ End If
172
+
173
+ ReDim target(1 To sourceCount)
174
+ For i = 1 To sourceCount
175
+ target(i) = source(i)
176
+ Next i
177
+ End Sub
178
+
179
+ Private Sub CopyRotationPrefs(ByRef source() As Long, ByVal n As Long, ByRef target() As Long)
180
+ Dim i As Long
181
+
182
+ ReDim target(1 To n)
183
+ For i = 1 To n
184
+ target(i) = source(i)
185
+ Next i
186
+ End Sub
187
+
188
+ Private Sub SwapLong(ByRef arr() As Long, ByVal i As Long, ByVal j As Long)
189
+ Dim tmp As Long
190
+
191
+ If i = j Then Exit Sub
192
+ tmp = arr(i)
193
+ arr(i) = arr(j)
194
+ arr(j) = tmp
195
+ End Sub
196
+
197
+ Private Sub MoveItemOrder(ByRef arr() As Long, ByVal fromPos As Long, ByVal toPos As Long, ByVal n As Long)
198
+ Dim value As Long
199
+ Dim i As Long
200
+
201
+ If fromPos = toPos Then Exit Sub
202
+ If fromPos < 1 Or fromPos > n Then Exit Sub
203
+ If toPos < 1 Or toPos > n Then Exit Sub
204
+
205
+ value = arr(fromPos)
206
+
207
+ If fromPos < toPos Then
208
+ For i = fromPos To toPos - 1
209
+ arr(i) = arr(i + 1)
210
+ Next i
211
+ Else
212
+ For i = fromPos To toPos + 1 Step -1
213
+ arr(i) = arr(i - 1)
214
+ Next i
215
+ End If
216
+
217
+ arr(toPos) = value
218
+ End Sub
219
+
220
+ Private Sub InsertLongAt(ByRef arr() As Long, ByRef currentCount As Long, ByVal position As Long, ByVal value As Long)
221
+ Dim i As Long
222
+
223
+ If position < 1 Then position = 1
224
+ If position > currentCount + 1 Then position = currentCount + 1
225
+
226
+ For i = currentCount To position Step -1
227
+ arr(i + 1) = arr(i)
228
+ Next i
229
+
230
+ arr(position) = value
231
+ currentCount = currentCount + 1
232
+ End Sub
233
+
234
+ Private Sub RemoveLongAt(ByRef arr() As Long, ByRef currentCount As Long, ByVal position As Long)
235
+ Dim i As Long
236
+
237
+ If currentCount <= 0 Then Exit Sub
238
+ If position < 1 Or position > currentCount Then Exit Sub
239
+
240
+ For i = position To currentCount - 1
241
+ arr(i) = arr(i + 1)
242
+ Next i
243
+
244
+ currentCount = currentCount - 1
245
+ End Sub
246
+
247
+ Private Function ContainsLong(ByRef arr() As Long, ByVal count As Long, ByVal value As Long) As Boolean
248
+ Dim i As Long
249
+
250
+ For i = 1 To count
251
+ If arr(i) = value Then
252
+ ContainsLong = True
253
+ Exit Function
254
+ End If
255
+ Next i
256
+
257
+ ContainsLong = False
258
+ End Function
259
+
260
+ Private Function RectsIntersect(ByRef a As TFreeRect, ByRef b As TFreeRect) As Boolean
261
+ If a.x >= b.x + b.w - EPS Then
262
+ RectsIntersect = False
263
+ Exit Function
264
+ End If
265
+ If b.x >= a.x + a.w - EPS Then
266
+ RectsIntersect = False
267
+ Exit Function
268
+ End If
269
+ If a.y >= b.y + b.h - EPS Then
270
+ RectsIntersect = False
271
+ Exit Function
272
+ End If
273
+ If b.y >= a.y + a.h - EPS Then
274
+ RectsIntersect = False
275
+ Exit Function
276
+ End If
277
+
278
+ RectsIntersect = True
279
+ End Function
280
+
281
+ Private Function IsContained(ByRef a As TFreeRect, ByRef b As TFreeRect) As Boolean
282
+ If a.x + EPS < b.x Then
283
+ IsContained = False
284
+ Exit Function
285
+ End If
286
+ If a.y + EPS < b.y Then
287
+ IsContained = False
288
+ Exit Function
289
+ End If
290
+ If a.x + a.w > b.x + b.w + EPS Then
291
+ IsContained = False
292
+ Exit Function
293
+ End If
294
+ If a.y + a.h > b.y + b.h + EPS Then
295
+ IsContained = False
296
+ Exit Function
297
+ End If
298
+
299
+ IsContained = True
300
+ End Function
301
+
302
+ Private Sub PruneFree(ByRef freeRects() As TFreeRect, ByRef freeCount As Long)
303
+ Dim i As Long
304
+ Dim j As Long
305
+ Dim k As Long
306
+ Dim contained As Boolean
307
+
308
+ If freeCount <= 1 Then Exit Sub
309
+
310
+ i = 1
311
+ Do While i <= freeCount
312
+ contained = False
313
+
314
+ For j = 1 To freeCount
315
+ If i <> j Then
316
+ If IsContained(freeRects(i), freeRects(j)) Then
317
+ contained = True
318
+ Exit For
319
+ End If
320
+ End If
321
+ Next j
322
+
323
+ If contained Then
324
+ For k = i To freeCount - 1
325
+ freeRects(k) = freeRects(k + 1)
326
+ Next k
327
+ freeCount = freeCount - 1
328
+ Else
329
+ i = i + 1
330
+ End If
331
+ Loop
332
+
333
+ If freeCount <= 0 Then
334
+ ReDim freeRects(1 To 1)
335
+ Else
336
+ ReDim Preserve freeRects(1 To freeCount)
337
+ End If
338
+ End Sub
339
+
340
+ Private Sub AddIfValid(ByRef arr() As TFreeRect, ByRef count As Long, ByVal x As Double, ByVal y As Double, ByVal w As Double, ByVal h As Double)
341
+ If w <= EPS Then Exit Sub
342
+ If h <= EPS Then Exit Sub
343
+
344
+ count = count + 1
345
+ arr(count).x = x
346
+ arr(count).y = y
347
+ arr(count).w = w
348
+ arr(count).h = h
349
+ End Sub
350
+
351
+ Private Sub PlaceAndSplit(ByRef freeRects() As TFreeRect, ByRef freeCount As Long, ByVal placedX As Double, ByVal placedY As Double, ByVal placedW As Double, ByVal placedH As Double)
352
+ Dim oldCount As Long
353
+ Dim newCount As Long
354
+ Dim maxNew As Long
355
+ Dim i As Long
356
+ Dim fr As TFreeRect
357
+ Dim placed As TFreeRect
358
+ Dim tmp() As TFreeRect
359
+
360
+ oldCount = freeCount
361
+ If oldCount <= 0 Then Exit Sub
362
+
363
+ maxNew = oldCount * 4 + 10
364
+ ReDim tmp(1 To maxNew)
365
+
366
+ placed.x = placedX
367
+ placed.y = placedY
368
+ placed.w = placedW
369
+ placed.h = placedH
370
+
371
+ For i = 1 To oldCount
372
+ fr = freeRects(i)
373
+
374
+ If Not RectsIntersect(fr, placed) Then
375
+ AddIfValid tmp, newCount, fr.x, fr.y, fr.w, fr.h
376
+ Else
377
+ AddIfValid tmp, newCount, fr.x, fr.y, placed.x - fr.x, fr.h
378
+ AddIfValid tmp, newCount, placed.x + placed.w, fr.y, fr.x + fr.w - placed.x - placed.w, fr.h
379
+ AddIfValid tmp, newCount, fr.x, fr.y, fr.w, placed.y - fr.y
380
+ AddIfValid tmp, newCount, fr.x, placed.y + placed.h, fr.w, fr.y + fr.h - placed.y - placed.h
381
+ End If
382
+ Next i
383
+
384
+ If newCount <= 0 Then
385
+ ReDim freeRects(1 To 1)
386
+ freeCount = 0
387
+ Exit Sub
388
+ End If
389
+
390
+ ReDim freeRects(1 To newCount)
391
+ For i = 1 To newCount
392
+ freeRects(i) = tmp(i)
393
+ Next i
394
+
395
+ freeCount = newCount
396
+ PruneFree freeRects, freeCount
397
+ End Sub
398
+
399
+ Private Function OverlapLength(ByVal a1 As Double, ByVal a2 As Double, ByVal b1 As Double, ByVal b2 As Double) As Double
400
+ Dim lo As Double
401
+ Dim hi As Double
402
+
403
+ lo = MaxD(a1, b1)
404
+ hi = MinD(a2, b2)
405
+
406
+ If hi <= lo + CONTACT_TOLERANCE Then
407
+ OverlapLength = 0#
408
+ Else
409
+ OverlapLength = hi - lo
410
+ End If
411
+ End Function
412
+
413
+ Private Function SharedEdgeLengthBetween(ByRef a As TPlaced, ByRef b As TPlaced) As Double
414
+ Dim total As Double
415
+ Dim ov As Double
416
+
417
+ total = 0#
418
+
419
+ If AbsD(a.x + a.w - b.x) <= CONTACT_TOLERANCE Then
420
+ ov = OverlapLength(a.y, a.y + a.h, b.y, b.y + b.h)
421
+ total = total + ov
422
+ ElseIf AbsD(b.x + b.w - a.x) <= CONTACT_TOLERANCE Then
423
+ ov = OverlapLength(a.y, a.y + a.h, b.y, b.y + b.h)
424
+ total = total + ov
425
+ End If
426
+
427
+ If AbsD(a.y + a.h - b.y) <= CONTACT_TOLERANCE Then
428
+ ov = OverlapLength(a.x, a.x + a.w, b.x, b.x + b.w)
429
+ total = total + ov
430
+ ElseIf AbsD(b.y + b.h - a.y) <= CONTACT_TOLERANCE Then
431
+ ov = OverlapLength(a.x, a.x + a.w, b.x, b.x + b.w)
432
+ total = total + ov
433
+ End If
434
+
435
+ SharedEdgeLengthBetween = total
436
+ End Function
437
+
438
+ Private Function StrongGuillotineEdgeBetween(ByRef a As TPlaced, ByRef b As TPlaced) As Double
439
+ Dim ov As Double
440
+ Dim minSpan As Double
441
+
442
+ If AbsD(a.x + a.w - b.x) <= CONTACT_TOLERANCE Or AbsD(b.x + b.w - a.x) <= CONTACT_TOLERANCE Then
443
+ ov = OverlapLength(a.y, a.y + a.h, b.y, b.y + b.h)
444
+ minSpan = MinD(a.h, b.h)
445
+ If minSpan > EPS And ov >= minSpan * 0.9 Then
446
+ StrongGuillotineEdgeBetween = ov
447
+ Exit Function
448
+ End If
449
+ End If
450
+
451
+ If AbsD(a.y + a.h - b.y) <= CONTACT_TOLERANCE Or AbsD(b.y + b.h - a.y) <= CONTACT_TOLERANCE Then
452
+ ov = OverlapLength(a.x, a.x + a.w, b.x, b.x + b.w)
453
+ minSpan = MinD(a.w, b.w)
454
+ If minSpan > EPS And ov >= minSpan * 0.9 Then
455
+ StrongGuillotineEdgeBetween = ov
456
+ Exit Function
457
+ End If
458
+ End If
459
+
460
+ StrongGuillotineEdgeBetween = 0#
461
+ End Function
462
+
463
+ Private Function PlacementContactBonus(ByRef placed() As TPlaced, ByVal placedCount As Long, ByVal x As Double, ByVal y As Double, ByVal w As Double, ByVal h As Double) As Double
464
+ Dim candidate As TPlaced
465
+ Dim i As Long
466
+ Dim edge As Double
467
+ Dim total As Double
468
+
469
+ If placedCount <= 0 Then Exit Function
470
+
471
+ candidate.x = x
472
+ candidate.y = y
473
+ candidate.w = w
474
+ candidate.h = h
475
+
476
+ For i = 1 To placedCount
477
+ edge = SharedEdgeLengthBetween(candidate, placed(i))
478
+ total = total + edge
479
+ Next i
480
+
481
+ PlacementContactBonus = total
482
+ End Function
483
+
484
+ Private Function PlacementGuillotineBonus(ByRef placed() As TPlaced, ByVal placedCount As Long, ByVal x As Double, ByVal y As Double, ByVal w As Double, ByVal h As Double) As Double
485
+ Dim candidate As TPlaced
486
+ Dim i As Long
487
+ Dim edge As Double
488
+ Dim total As Double
489
+
490
+ If placedCount <= 0 Then Exit Function
491
+
492
+ candidate.x = x
493
+ candidate.y = y
494
+ candidate.w = w
495
+ candidate.h = h
496
+
497
+ For i = 1 To placedCount
498
+ edge = StrongGuillotineEdgeBetween(candidate, placed(i))
499
+ total = total + edge
500
+ Next i
501
+
502
+ PlacementGuillotineBonus = total
503
+ End Function
504
+
505
+ Private Function PlacementScoreWithContact(ByRef fr As TFreeRect, ByVal rw As Double, ByVal rh As Double, ByVal fitMode As Long, ByRef placed() As TPlaced, ByVal placedCount As Long, ByRef outContact As Double, ByRef outGuillotine As Double) As Double
506
+ Dim baseScore As Double
507
+
508
+ baseScore = PlacementScore(fr, rw, rh, fitMode)
509
+ outContact = PlacementContactBonus(placed, placedCount, fr.x, fr.y, rw, rh)
510
+ outGuillotine = PlacementGuillotineBonus(placed, placedCount, fr.x, fr.y, rw, rh)
511
+
512
+ PlacementScoreWithContact = baseScore - outContact * CONTACT_PLACEMENT_WEIGHT - outGuillotine * GUILLOTINE_PLACEMENT_WEIGHT
513
+ End Function
514
+
515
+ Private Function PlacementScore(ByRef fr As TFreeRect, ByVal rw As Double, ByVal rh As Double, ByVal fitMode As Long) As Double
516
+ Dim leftoverW As Double
517
+ Dim leftoverH As Double
518
+ Dim shortSide As Double
519
+ Dim longSide As Double
520
+ Dim areaWaste As Double
521
+
522
+ leftoverW = fr.w - rw
523
+ leftoverH = fr.h - rh
524
+ shortSide = MinD(leftoverW, leftoverH)
525
+ longSide = MaxD(leftoverW, leftoverH)
526
+ areaWaste = fr.w * fr.h - rw * rh
527
+
528
+ Select Case fitMode
529
+ Case 1
530
+ PlacementScore = shortSide
531
+ Case 2
532
+ PlacementScore = areaWaste
533
+ Case 3
534
+ PlacementScore = longSide
535
+ Case Else
536
+ PlacementScore = shortSide
537
+ End Select
538
+ End Function
539
+
540
+ Private Function AlignmentPenalty(ByRef placed() As TPlaced, ByVal placedCount As Long, ByVal x As Double, ByVal y As Double) As Double
541
+ Dim i As Long
542
+ Dim leftDist As Double
543
+ Dim topDist As Double
544
+ Dim bestLeft As Double
545
+ Dim bestTop As Double
546
+
547
+ If placedCount <= 0 Then
548
+ AlignmentPenalty = 0#
549
+ Exit Function
550
+ End If
551
+
552
+ bestLeft = 1E+30
553
+ bestTop = 1E+30
554
+
555
+ For i = 1 To placedCount
556
+ leftDist = AbsD(x - placed(i).x)
557
+ topDist = AbsD(y - placed(i).y)
558
+
559
+ If leftDist < bestLeft Then bestLeft = leftDist
560
+ If topDist < bestTop Then bestTop = topDist
561
+ Next i
562
+
563
+ If bestLeft <= ALIGN_TOLERANCE Then bestLeft = 0#
564
+ If bestTop <= ALIGN_TOLERANCE Then bestTop = 0#
565
+
566
+ AlignmentPenalty = bestLeft + bestTop
567
+ End Function
568
+
569
+ Private Function IsBetterPlacement(ByVal score As Double, ByVal alignScore As Double, ByVal tieY As Double, ByVal tieX As Double, ByVal bestScore As Double, ByVal bestAlign As Double, ByVal bestY As Double, ByVal bestX As Double) As Boolean
570
+ If score < bestScore - EPS Then
571
+ IsBetterPlacement = True
572
+ Exit Function
573
+ End If
574
+
575
+ If AbsD(score - bestScore) <= EPS Then
576
+ If alignScore < bestAlign - EPS Then
577
+ IsBetterPlacement = True
578
+ Exit Function
579
+ End If
580
+
581
+ If AbsD(alignScore - bestAlign) <= EPS Then
582
+ If tieY < bestY - EPS Then
583
+ IsBetterPlacement = True
584
+ Exit Function
585
+ End If
586
+
587
+ If AbsD(tieY - bestY) <= EPS Then
588
+ If tieX < bestX - EPS Then
589
+ IsBetterPlacement = True
590
+ Exit Function
591
+ End If
592
+ End If
593
+ End If
594
+ End If
595
+
596
+ IsBetterPlacement = False
597
+ End Function
598
+
599
+ Private Function FindBestPlacement(ByRef freeRects() As TFreeRect, ByVal freeCount As Long, ByVal reqW As Double, ByVal reqH As Double, ByVal allowRotation As Boolean, ByVal rotationMode As Long, ByVal fitMode As Long, ByRef placed() As TPlaced, ByVal placedCount As Long, ByRef bestX As Double, ByRef bestY As Double, ByRef bestW As Double, ByRef bestH As Double, ByRef bestRot As Boolean) As Boolean
600
+ Dim i As Long
601
+ Dim rw As Double
602
+ Dim rh As Double
603
+ Dim score As Double
604
+ Dim alignScore As Double
605
+ Dim bestScore As Double
606
+ Dim bestAlign As Double
607
+ Dim tieY As Double
608
+ Dim tieX As Double
609
+ Dim bestTieY As Double
610
+ Dim bestTieX As Double
611
+ Dim contactBonus As Double
612
+ Dim guillotineBonus As Double
613
+ Dim found As Boolean
614
+ Dim tryNormal As Boolean
615
+ Dim tryRotated As Boolean
616
+
617
+ bestScore = 1E+30
618
+ bestAlign = 1E+30
619
+ bestTieY = 1E+30
620
+ bestTieX = 1E+30
621
+ found = False
622
+
623
+ tryNormal = True
624
+ tryRotated = allowRotation
625
+
626
+ If allowRotation Then
627
+ If rotationMode = 1 Then
628
+ tryRotated = False
629
+ ElseIf rotationMode = 2 Then
630
+ tryNormal = False
631
+ End If
632
+ Else
633
+ tryRotated = False
634
+ End If
635
+
636
+ For i = 1 To freeCount
637
+ If tryNormal Then
638
+ rw = reqW
639
+ rh = reqH
640
+
641
+ If rw <= freeRects(i).w + EPS And rh <= freeRects(i).h + EPS Then
642
+ score = PlacementScoreWithContact(freeRects(i), rw, rh, fitMode, placed, placedCount, contactBonus, guillotineBonus)
643
+ alignScore = AlignmentPenalty(placed, placedCount, freeRects(i).x, freeRects(i).y)
644
+ tieY = freeRects(i).y
645
+ tieX = freeRects(i).x
646
+
647
+ If IsBetterPlacement(score, alignScore, tieY, tieX, bestScore, bestAlign, bestTieY, bestTieX) Then
648
+ bestScore = score
649
+ bestAlign = alignScore
650
+ bestTieY = tieY
651
+ bestTieX = tieX
652
+ bestX = freeRects(i).x
653
+ bestY = freeRects(i).y
654
+ bestW = rw
655
+ bestH = rh
656
+ bestRot = False
657
+ found = True
658
+ End If
659
+ End If
660
+ End If
661
+
662
+ If tryRotated Then
663
+ rw = reqH
664
+ rh = reqW
665
+
666
+ If rw <= freeRects(i).w + EPS And rh <= freeRects(i).h + EPS Then
667
+ score = PlacementScoreWithContact(freeRects(i), rw, rh, fitMode, placed, placedCount, contactBonus, guillotineBonus)
668
+ alignScore = AlignmentPenalty(placed, placedCount, freeRects(i).x, freeRects(i).y)
669
+ tieY = freeRects(i).y
670
+ tieX = freeRects(i).x
671
+
672
+ If IsBetterPlacement(score, alignScore, tieY, tieX, bestScore, bestAlign, bestTieY, bestTieX) Then
673
+ bestScore = score
674
+ bestAlign = alignScore
675
+ bestTieY = tieY
676
+ bestTieX = tieX
677
+ bestX = freeRects(i).x
678
+ bestY = freeRects(i).y
679
+ bestW = rw
680
+ bestH = rh
681
+ bestRot = True
682
+ found = True
683
+ End If
684
+ End If
685
+ End If
686
+ Next i
687
+
688
+ FindBestPlacement = found
689
+ End Function
690
+
691
+ Private Sub PackOneBinOrdered(ByRef items() As TBanner, ByRef order() As Long, ByVal orderCount As Long, ByVal rollWidth As Double, ByVal maxHeight As Double, ByVal allowRotation As Boolean, ByVal fitMode As Long, ByRef rotationPrefs() As Long, ByRef placed() As TPlaced, ByRef placedCount As Long, ByRef outIdx() As Long, ByRef outCount As Long)
692
+ Dim freeRects() As TFreeRect
693
+ Dim freeCount As Long
694
+ Dim k As Long
695
+ Dim rid As Long
696
+ Dim reqW As Double
697
+ Dim reqH As Double
698
+ Dim bestX As Double
699
+ Dim bestY As Double
700
+ Dim bestW As Double
701
+ Dim bestH As Double
702
+ Dim bestRot As Boolean
703
+
704
+ If orderCount <= 0 Then
705
+ ReDim placed(1 To 1)
706
+ ReDim outIdx(1 To 1)
707
+ placedCount = 0
708
+ outCount = 0
709
+ Exit Sub
710
+ End If
711
+
712
+ ReDim placed(1 To orderCount)
713
+ ReDim outIdx(1 To orderCount)
714
+ placedCount = 0
715
+ outCount = 0
716
+
717
+ ReDim freeRects(1 To 1)
718
+ freeCount = 1
719
+ freeRects(1).x = 0#
720
+ freeRects(1).y = 0#
721
+ freeRects(1).w = rollWidth
722
+ freeRects(1).h = maxHeight
723
+
724
+ For k = 1 To orderCount
725
+ rid = order(k)
726
+ reqW = items(rid).OrigW + 2# * items(rid).margin
727
+ reqH = items(rid).OrigH + 2# * items(rid).margin
728
+
729
+ If FindBestPlacement(freeRects, freeCount, reqW, reqH, allowRotation, rotationPrefs(rid), fitMode, placed, placedCount, bestX, bestY, bestW, bestH, bestRot) Then
730
+ placedCount = placedCount + 1
731
+ placed(placedCount).rid = rid
732
+ placed(placedCount).x = bestX
733
+ placed(placedCount).y = bestY
734
+ placed(placedCount).w = bestW
735
+ placed(placedCount).h = bestH
736
+ placed(placedCount).Rotated = bestRot
737
+
738
+ PlaceAndSplit freeRects, freeCount, bestX, bestY, bestW, bestH
739
+ Else
740
+ outCount = outCount + 1
741
+ outIdx(outCount) = rid
742
+ End If
743
+ Next k
744
+ End Sub
745
+
746
+ Private Sub PackMultiRollWithOrder(ByRef items() As TBanner, ByVal orderCount As Long, ByVal rollWidth As Double, ByVal maxHeight As Double, ByVal allowRotation As Boolean, ByVal fitMode As Long, ByRef order() As Long, ByRef rotationPrefs() As Long, ByRef rolls() As TRoll, ByRef rollCount As Long, ByRef notPlaced As Long)
747
+ Dim remaining() As Long
748
+ Dim nextRemaining() As Long
749
+ Dim placed() As TPlaced
750
+ Dim outIdx() As Long
751
+ Dim remainingCount As Long
752
+ Dim outCount As Long
753
+ Dim placedCount As Long
754
+ Dim i As Long
755
+ Dim safety As Long
756
+
757
+ rollCount = 0
758
+ notPlaced = 0
759
+
760
+ If orderCount <= 0 Then Exit Sub
761
+
762
+ ReDim rolls(1 To MAX_ROLLS)
763
+ ReDim remaining(1 To orderCount)
764
+
765
+ For i = 1 To orderCount
766
+ remaining(i) = order(i)
767
+ Next i
768
+ remainingCount = orderCount
769
+
770
+ Do While remainingCount > 0
771
+ safety = safety + 1
772
+ If safety > MAX_ROLLS Then
773
+ notPlaced = remainingCount
774
+ Exit Do
775
+ End If
776
+
777
+ PackOneBinOrdered items, remaining, remainingCount, rollWidth, maxHeight, allowRotation, fitMode, rotationPrefs, placed, placedCount, outIdx, outCount
778
+
779
+ If placedCount <= 0 Then
780
+ notPlaced = remainingCount
781
+ Exit Do
782
+ End If
783
+
784
+ rollCount = rollCount + 1
785
+ rolls(rollCount).placedCount = placedCount
786
+ ReDim rolls(rollCount).placed(1 To placedCount)
787
+
788
+ For i = 1 To placedCount
789
+ rolls(rollCount).placed(i) = placed(i)
790
+ Next i
791
+
792
+ rolls(rollCount).UsedHeight = CalcPlacedHeight(placed, placedCount)
793
+
794
+ If outCount <= 0 Then
795
+ remainingCount = 0
796
+ Exit Do
797
+ End If
798
+
799
+ ReDim nextRemaining(1 To outCount)
800
+ For i = 1 To outCount
801
+ nextRemaining(i) = outIdx(i)
802
+ Next i
803
+
804
+ ReDim remaining(1 To outCount)
805
+ For i = 1 To outCount
806
+ remaining(i) = nextRemaining(i)
807
+ Next i
808
+ remainingCount = outCount
809
+ Loop
810
+ End Sub
811
+
812
+ Private Function CalcPlacedHeight(ByRef placed() As TPlaced, ByVal placedCount As Long) As Double
813
+ Dim i As Long
814
+ Dim h As Double
815
+
816
+ h = 0#
817
+ For i = 1 To placedCount
818
+ h = MaxD(h, placed(i).y + placed(i).h)
819
+ Next i
820
+
821
+ CalcPlacedHeight = h
822
+ End Function
823
+
824
+ Private Function CalcMaterialUsed(ByRef rolls() As TRoll, ByVal rollCount As Long, ByVal rollWidth As Double) As Double
825
+ Dim r As Long
826
+ Dim total As Double
827
+
828
+ total = 0#
829
+ For r = 1 To rollCount
830
+ total = total + rollWidth * rolls(r).UsedHeight
831
+ Next r
832
+
833
+ CalcMaterialUsed = total
834
+ End Function
835
+
836
+ Private Function CalcTrueArea(ByRef items() As TBanner, ByRef rolls() As TRoll, ByVal rollCount As Long) As Double
837
+ Dim r As Long
838
+ Dim i As Long
839
+ Dim rid As Long
840
+ Dim total As Double
841
+
842
+ total = 0#
843
+ For r = 1 To rollCount
844
+ For i = 1 To rolls(r).placedCount
845
+ rid = rolls(r).placed(i).rid
846
+ total = total + items(rid).OrigW * items(rid).OrigH
847
+ Next i
848
+ Next r
849
+
850
+ CalcTrueArea = total
851
+ End Function
852
+
853
+ Private Function CalcWaste(ByRef items() As TBanner, ByRef rolls() As TRoll, ByVal rollCount As Long, ByVal rollWidth As Double) As Double
854
+ Dim material As Double
855
+ Dim trueArea As Double
856
+
857
+ material = CalcMaterialUsed(rolls, rollCount, rollWidth)
858
+ trueArea = CalcTrueArea(items, rolls, rollCount)
859
+
860
+ If material <= EPS Then
861
+ CalcWaste = 1#
862
+ Else
863
+ CalcWaste = (material - trueArea) / material
864
+ End If
865
+ End Function
866
+
867
+ Private Function CalcRightWaste(ByRef rolls() As TRoll, ByVal rollCount As Long, ByVal rollWidth As Double) As Double
868
+ Dim r As Long
869
+ Dim i As Long
870
+ Dim rightEdge As Double
871
+ Dim maxRightEdge As Double
872
+ Dim waste As Double
873
+ Dim worstWaste As Double
874
+
875
+ worstWaste = 0#
876
+ For r = 1 To rollCount
877
+ maxRightEdge = 0#
878
+ For i = 1 To rolls(r).placedCount
879
+ rightEdge = rolls(r).placed(i).x + rolls(r).placed(i).w
880
+ maxRightEdge = MaxD(maxRightEdge, rightEdge)
881
+ Next i
882
+
883
+ waste = rollWidth - maxRightEdge
884
+ If waste < 0# Then waste = 0#
885
+ worstWaste = MaxD(worstWaste, waste)
886
+ Next r
887
+
888
+ CalcRightWaste = worstWaste
889
+ End Function
890
+
891
+ Private Function CalcAlignmentScore(ByRef rolls() As TRoll, ByVal rollCount As Long) As Double
892
+ Dim r As Long
893
+ Dim i As Long
894
+ Dim j As Long
895
+ Dim score As Double
896
+ Dim leftDist As Double
897
+ Dim topDist As Double
898
+ Dim candidateVisualTop As Double
899
+ Dim otherVisualTop As Double
900
+ Dim bestLeft As Double
901
+ Dim bestTop As Double
902
+
903
+ For r = 1 To rollCount
904
+ For i = 1 To rolls(r).placedCount
905
+ bestLeft = 1E+30
906
+ bestTop = 1E+30
907
+ candidateVisualTop = rolls(r).placed(i).y
908
+
909
+ For j = 1 To rolls(r).placedCount
910
+ If i <> j Then
911
+ leftDist = AbsD(rolls(r).placed(i).x - rolls(r).placed(j).x)
912
+ otherVisualTop = rolls(r).placed(j).y
913
+ topDist = AbsD(candidateVisualTop - otherVisualTop)
914
+ bestLeft = MinD(bestLeft, leftDist)
915
+ bestTop = MinD(bestTop, topDist)
916
+ End If
917
+ Next j
918
+
919
+ If bestLeft <= ALIGN_TOLERANCE Then bestLeft = 0#
920
+ If bestTop <= ALIGN_TOLERANCE Then bestTop = 0#
921
+
922
+ If bestLeft < 1E+20 Then score = score + bestLeft
923
+ If bestTop < 1E+20 Then score = score + bestTop
924
+ Next i
925
+ Next r
926
+
927
+ CalcAlignmentScore = score
928
+ End Function
929
+
930
+ Private Function CalcSharedEdgeLength(ByRef rolls() As TRoll, ByVal rollCount As Long) As Double
931
+ Dim r As Long
932
+ Dim i As Long
933
+ Dim j As Long
934
+ Dim total As Double
935
+
936
+ total = 0#
937
+ For r = 1 To rollCount
938
+ For i = 1 To rolls(r).placedCount - 1
939
+ For j = i + 1 To rolls(r).placedCount
940
+ total = total + SharedEdgeLengthBetween(rolls(r).placed(i), rolls(r).placed(j))
941
+ Next j
942
+ Next i
943
+ Next r
944
+
945
+ CalcSharedEdgeLength = total
946
+ End Function
947
+
948
+ Private Function CalcContactSegments(ByRef rolls() As TRoll, ByVal rollCount As Long) As Long
949
+ Dim r As Long
950
+ Dim i As Long
951
+ Dim j As Long
952
+ Dim edge As Double
953
+ Dim count As Long
954
+
955
+ count = 0
956
+ For r = 1 To rollCount
957
+ For i = 1 To rolls(r).placedCount - 1
958
+ For j = i + 1 To rolls(r).placedCount
959
+ edge = SharedEdgeLengthBetween(rolls(r).placed(i), rolls(r).placed(j))
960
+ If edge > CONTACT_TOLERANCE Then count = count + 1
961
+ Next j
962
+ Next i
963
+ Next r
964
+
965
+ CalcContactSegments = count
966
+ End Function
967
+
968
+ Private Function CalcGuillotineEdgeLength(ByRef rolls() As TRoll, ByVal rollCount As Long) As Double
969
+ Dim r As Long
970
+ Dim i As Long
971
+ Dim j As Long
972
+ Dim edge As Double
973
+ Dim total As Double
974
+
975
+ total = 0#
976
+ For r = 1 To rollCount
977
+ For i = 1 To rolls(r).placedCount - 1
978
+ For j = i + 1 To rolls(r).placedCount
979
+ edge = StrongGuillotineEdgeBetween(rolls(r).placed(i), rolls(r).placed(j))
980
+ total = total + edge
981
+ Next j
982
+ Next i
983
+ Next r
984
+
985
+ CalcGuillotineEdgeLength = total
986
+ End Function
987
+
988
+ Private Function CalcCutFriendlyPenalty(ByRef rolls() As TRoll, ByVal rollCount As Long, ByVal rollWidth As Double) As Double
989
+ Dim sharedEdge As Double
990
+ Dim strongEdge As Double
991
+ Dim segments As Long
992
+ Dim penalty As Double
993
+ Dim r As Long
994
+ Dim i As Long
995
+ Dim boundaryContact As Double
996
+
997
+ sharedEdge = CalcSharedEdgeLength(rolls, rollCount)
998
+ strongEdge = CalcGuillotineEdgeLength(rolls, rollCount)
999
+ segments = CalcContactSegments(rolls, rollCount)
1000
+
1001
+ boundaryContact = 0#
1002
+ For r = 1 To rollCount
1003
+ For i = 1 To rolls(r).placedCount
1004
+ If AbsD(rolls(r).placed(i).x) <= CONTACT_TOLERANCE Then boundaryContact = boundaryContact + rolls(r).placed(i).h
1005
+ If AbsD(rolls(r).placed(i).x + rolls(r).placed(i).w - rollWidth) <= CONTACT_TOLERANCE Then boundaryContact = boundaryContact + rolls(r).placed(i).h
1006
+ If AbsD(rolls(r).placed(i).y) <= CONTACT_TOLERANCE Then boundaryContact = boundaryContact + rolls(r).placed(i).w
1007
+ If AbsD(rolls(r).placed(i).y + rolls(r).placed(i).h - rolls(r).UsedHeight) <= CONTACT_TOLERANCE Then boundaryContact = boundaryContact + rolls(r).placed(i).w
1008
+ Next i
1009
+ Next r
1010
+
1011
+ penalty = CDbl(segments) * 2#
1012
+ penalty = penalty - sharedEdge * 0.08
1013
+ penalty = penalty - strongEdge * 0.18
1014
+ penalty = penalty - boundaryContact * 0.03
1015
+
1016
+ If penalty < 0# Then penalty = 0#
1017
+ CalcCutFriendlyPenalty = penalty
1018
+ End Function
1019
+
1020
+ Private Function CalcContactScore(ByRef rolls() As TRoll, ByVal rollCount As Long) As Double
1021
+ Dim sharedEdge As Double
1022
+ Dim segments As Long
1023
+ Dim score As Double
1024
+
1025
+ sharedEdge = CalcSharedEdgeLength(rolls, rollCount)
1026
+ segments = CalcContactSegments(rolls, rollCount)
1027
+ score = sharedEdge
1028
+ score = score - CDbl(segments) * 0.5
1029
+
1030
+ If score < 0# Then score = 0#
1031
+ CalcContactScore = score
1032
+ End Function
1033
+
1034
+ Private Function PackScore(ByRef items() As TBanner, ByRef rolls() As TRoll, ByVal rollCount As Long, ByVal notPlaced As Long, ByVal rollWidth As Double, ByRef rightWaste As Double, ByRef alignmentScore As Double, ByRef materialUsed As Double) As Double
1035
+ Dim excessRight As Double
1036
+ Dim contactScore As Double
1037
+ Dim cutPenalty As Double
1038
+
1039
+ rightWaste = CalcRightWaste(rolls, rollCount, rollWidth)
1040
+ alignmentScore = CalcAlignmentScore(rolls, rollCount)
1041
+
1042
+ ' IMPORTANT: materialUsed is already the real material area consumed:
1043
+ ' sum over rolls of (rollWidth x roll UsedHeight).
1044
+ ' Therefore roll HEIGHT is already considered together with WIDTH.
1045
+ materialUsed = CalcMaterialUsed(rolls, rollCount, rollWidth)
1046
+
1047
+ If notPlaced > 0 Then
1048
+ PackScore = 1E+30 + CDbl(notPlaced) * 1E+25
1049
+ Exit Function
1050
+ End If
1051
+
1052
+ excessRight = rightWaste - MAX_RIGHT_WASTE
1053
+ If excessRight < 0# Then excessRight = 0#
1054
+
1055
+ contactScore = CalcContactScore(rolls, rollCount)
1056
+ cutPenalty = CalcCutFriendlyPenalty(rolls, rollCount, rollWidth)
1057
+
1058
+ ' OBJECTIVE PRIORITY:
1059
+ ' 1) Feasible layout (handled above)
1060
+ ' 2) Minimum material area = width x used height
1061
+ ' 3) Minimum right waste above the 10 cm target
1062
+ ' 4) Alignment / contact / cut friendliness / roll count as tie-breakers
1063
+ '
1064
+ ' The material scale is deliberately dominant. A meaningful reduction
1065
+ ' in material area must beat improvements in the secondary metrics.
1066
+ PackScore = materialUsed * MATERIAL_SCORE_SCALE _
1067
+ + excessRight * RIGHT_WASTE_SCORE_SCALE _
1068
+ + alignmentScore * ALIGNMENT_SCORE_SCALE _
1069
+ - contactScore * CONTACT_SCORE_WEIGHT _
1070
+ + cutPenalty * GUILLOTINE_SCORE_WEIGHT _
1071
+ + CDbl(rollCount) * ROLL_COUNT_SCORE_SCALE
1072
+ End Function
1073
+
1074
+ Private Function EvaluateOrderScoreWithRotation(ByRef items() As TBanner, ByVal orderCount As Long, ByVal rollWidth As Double, ByVal maxHeight As Double, ByVal allowRotation As Boolean, ByVal fitMode As Long, ByRef order() As Long, ByRef rotationPrefs() As Long, ByRef outRollCount As Long, ByRef outNotPlaced As Long, ByRef outRightWaste As Double, ByRef outAlignment As Double, ByRef outMaterial As Double) As Double
1075
+ Dim testRolls() As TRoll
1076
+
1077
+ PackMultiRollWithOrder items, orderCount, rollWidth, maxHeight, allowRotation, fitMode, order, rotationPrefs, testRolls, outRollCount, outNotPlaced
1078
+ EvaluateOrderScoreWithRotation = PackScore(items, testRolls, outRollCount, outNotPlaced, rollWidth, outRightWaste, outAlignment, outMaterial)
1079
+ End Function
1080
+
1081
+ Private Sub AddSeed(ByRef seedOrders() As Long, ByRef seedScores() As Double, ByRef seedWidths() As Double, ByRef seedFits() As Long, ByRef seedSorts() As Long, ByRef seedCount As Long, ByVal maxSeeds As Long, ByRef candidateOrder() As Long, ByVal n As Long, ByVal candidateScore As Double, ByVal candidateWidth As Double, ByVal candidateFit As Long, ByVal candidateSort As Long)
1082
+ Dim pos As Long
1083
+ Dim i As Long
1084
+ Dim j As Long
1085
+
1086
+ If maxSeeds <= 0 Then Exit Sub
1087
+
1088
+ pos = 0
1089
+ For i = 1 To maxSeeds
1090
+ If i > seedCount Then
1091
+ pos = i
1092
+ Exit For
1093
+ End If
1094
+
1095
+ If candidateScore < seedScores(i) Then
1096
+ pos = i
1097
+ Exit For
1098
+ End If
1099
+ Next i
1100
+
1101
+ If pos = 0 Then Exit Sub
1102
+
1103
+ If seedCount < maxSeeds Then seedCount = seedCount + 1
1104
+
1105
+ For i = seedCount To pos + 1 Step -1
1106
+ seedScores(i) = seedScores(i - 1)
1107
+ seedWidths(i) = seedWidths(i - 1)
1108
+ seedFits(i) = seedFits(i - 1)
1109
+ seedSorts(i) = seedSorts(i - 1)
1110
+
1111
+ For j = 1 To n
1112
+ seedOrders(i, j) = seedOrders(i - 1, j)
1113
+ Next j
1114
+ Next i
1115
+
1116
+ seedScores(pos) = candidateScore
1117
+ seedWidths(pos) = candidateWidth
1118
+ seedFits(pos) = candidateFit
1119
+ seedSorts(pos) = candidateSort
1120
+
1121
+ For j = 1 To n
1122
+ seedOrders(pos, j) = candidateOrder(j)
1123
+ Next j
1124
+ End Sub
1125
+
1126
+ Private Sub CopyOrderFromSeed(ByRef seedOrders() As Long, ByVal seedNo As Long, ByVal n As Long, ByRef order() As Long)
1127
+ Dim i As Long
1128
+
1129
+ ReDim order(1 To n)
1130
+ For i = 1 To n
1131
+ order(i) = seedOrders(seedNo, i)
1132
+ Next i
1133
+ End Sub
1134
+
1135
+ Private Sub BuildDestroyScores(ByRef items() As TBanner, ByRef rolls() As TRoll, ByVal rollCount As Long, ByVal rollWidth As Double, ByVal n As Long, ByVal mode As Long, ByRef ids() As Long, ByRef scores() As Double)
1136
+ Dim i As Long
1137
+ Dim r As Long
1138
+ Dim j As Long
1139
+ Dim rid As Long
1140
+ Dim bestLeft As Double
1141
+ Dim bestTop As Double
1142
+ Dim leftDist As Double
1143
+ Dim topDist As Double
1144
+ Dim rightWaste As Double
1145
+ Dim alignment As Double
1146
+ Dim xPos As Double
1147
+ Dim area As Double
1148
+ Dim shortSide As Double
1149
+ Dim longSide As Double
1150
+ Dim contactEdge As Double
1151
+ Dim strongEdge As Double
1152
+
1153
+ ReDim ids(1 To n)
1154
+ ReDim scores(1 To n)
1155
+
1156
+ For i = 1 To n
1157
+ ids(i) = i
1158
+ scores(i) = 0#
1159
+ Next i
1160
+
1161
+ For r = 1 To rollCount
1162
+ For i = 1 To rolls(r).placedCount
1163
+ rid = rolls(r).placed(i).rid
1164
+ rightWaste = rollWidth - rolls(r).placed(i).x - rolls(r).placed(i).w
1165
+ If rightWaste < 0# Then rightWaste = 0#
1166
+
1167
+ bestLeft = 1E+30
1168
+ bestTop = 1E+30
1169
+
1170
+ For j = 1 To rolls(r).placedCount
1171
+ If j <> i Then
1172
+ leftDist = AbsD(rolls(r).placed(i).x - rolls(r).placed(j).x)
1173
+ topDist = AbsD(rolls(r).placed(i).y - rolls(r).placed(j).y)
1174
+ bestLeft = MinD(bestLeft, leftDist)
1175
+ bestTop = MinD(bestTop, topDist)
1176
+ End If
1177
+ Next j
1178
+
1179
+ If bestLeft >= 1E+20 Then bestLeft = 0#
1180
+ If bestTop >= 1E+20 Then bestTop = 0#
1181
+ alignment = bestLeft + bestTop
1182
+ xPos = rolls(r).placed(i).x
1183
+ contactEdge = 0#
1184
+ strongEdge = 0#
1185
+ For j = 1 To rolls(r).placedCount
1186
+ If j <> i Then
1187
+ contactEdge = contactEdge + SharedEdgeLengthBetween(rolls(r).placed(i), rolls(r).placed(j))
1188
+ strongEdge = strongEdge + StrongGuillotineEdgeBetween(rolls(r).placed(i), rolls(r).placed(j))
1189
+ End If
1190
+ Next j
1191
+ area = AreaOf(items(rid))
1192
+ shortSide = MinD(items(rid).OrigW + 2# * items(rid).margin, items(rid).OrigH + 2# * items(rid).margin)
1193
+ longSide = MaxD(items(rid).OrigW + 2# * items(rid).margin, items(rid).OrigH + 2# * items(rid).margin)
1194
+
1195
+ Select Case mode
1196
+ Case 1
1197
+ scores(rid) = rightWaste * 10# + alignment
1198
+ Case 2
1199
+ scores(rid) = area
1200
+ Case 3
1201
+ scores(rid) = alignment * 2# + rightWaste
1202
+ Case 4
1203
+ scores(rid) = longSide * 2# + rightWaste
1204
+ Case 5
1205
+ scores(rid) = xPos + rightWaste
1206
+ Case 6
1207
+ scores(rid) = -contactEdge + rightWaste * 0.2 + alignment * 0.1
1208
+ Case 7
1209
+ scores(rid) = -strongEdge + rightWaste * 0.1 + alignment * 0.1
1210
+ Case Else
1211
+ scores(rid) = rightWaste * 10# + alignment
1212
+ End Select
1213
+ Next i
1214
+ Next r
1215
+
1216
+ For i = 1 To n - 1
1217
+ For j = i + 1 To n
1218
+ If scores(ids(j)) > scores(ids(i)) Then
1219
+ SwapLong ids, i, j
1220
+ End If
1221
+ Next j
1222
+ Next i
1223
+ End Sub
1224
+
1225
+ Private Function GetDestroyCount(ByVal n As Long, ByVal iteration As Long) As Long
1226
+ Dim d As Long
1227
+
1228
+ If n <= 12 Then
1229
+ d = 3
1230
+ ElseIf n <= 25 Then
1231
+ d = 4
1232
+ ElseIf n <= 50 Then
1233
+ d = 5
1234
+ Else
1235
+ d = 6
1236
+ End If
1237
+
1238
+ If iteration Mod 4 = 0 Then d = d + 1
1239
+ If d > n - 1 Then d = n - 1
1240
+ If d < 1 Then d = 1
1241
+
1242
+ GetDestroyCount = d
1243
+ End Function
1244
+
1245
+ Private Sub BuildDestroyList(ByRef items() As TBanner, ByRef rolls() As TRoll, ByVal rollCount As Long, ByVal rollWidth As Double, ByVal n As Long, ByVal destroyCount As Long, ByVal mode As Long, ByRef selected() As Long, ByRef selectedCount As Long)
1246
+ Dim ids() As Long
1247
+ Dim scores() As Double
1248
+ Dim i As Long
1249
+
1250
+ BuildDestroyScores items, rolls, rollCount, rollWidth, n, mode, ids, scores
1251
+
1252
+ selectedCount = destroyCount
1253
+ If selectedCount > n - 1 Then selectedCount = n - 1
1254
+ If selectedCount < 1 Then
1255
+ ReDim selected(1 To 1)
1256
+ selectedCount = 0
1257
+ Exit Sub
1258
+ End If
1259
+
1260
+ ReDim selected(1 To selectedCount)
1261
+
1262
+ For i = 1 To selectedCount
1263
+ selected(i) = ids(i)
1264
+ Next i
1265
+ End Sub
1266
+
1267
+ Private Sub BuildOrderWithoutSelected(ByRef order() As Long, ByVal orderCount As Long, ByRef selected() As Long, ByVal selectedCount As Long, ByRef baseOrder() As Long, ByRef baseCount As Long, ByRef removed() As Long, ByRef removedCount As Long)
1268
+ Dim i As Long
1269
+ Dim rid As Long
1270
+
1271
+ ReDim baseOrder(1 To orderCount)
1272
+ ReDim removed(1 To selectedCount)
1273
+ baseCount = 0
1274
+ removedCount = 0
1275
+
1276
+ For i = 1 To orderCount
1277
+ rid = order(i)
1278
+ If ContainsLong(selected, selectedCount, rid) Then
1279
+ removedCount = removedCount + 1
1280
+ removed(removedCount) = rid
1281
+ Else
1282
+ baseCount = baseCount + 1
1283
+ baseOrder(baseCount) = rid
1284
+ End If
1285
+ Next i
1286
+ End Sub
1287
+
1288
+ Private Sub SortRemovedByArea(ByRef items() As TBanner, ByRef removed() As Long, ByVal removedCount As Long)
1289
+ Dim i As Long
1290
+ Dim j As Long
1291
+ Dim tmp As Long
1292
+
1293
+ For i = 1 To removedCount - 1
1294
+ For j = i + 1 To removedCount
1295
+ If AreaOf(items(removed(j))) > AreaOf(items(removed(i))) Then
1296
+ tmp = removed(i)
1297
+ removed(i) = removed(j)
1298
+ removed(j) = tmp
1299
+ End If
1300
+ Next j
1301
+ Next i
1302
+ End Sub
1303
+
1304
+ Private Sub AddRepairPosition(ByRef positions() As Long, ByRef count As Long, ByVal value As Long, ByVal maxValue As Long)
1305
+ Dim i As Long
1306
+
1307
+ If value < 1 Then value = 1
1308
+ If value > maxValue Then value = maxValue
1309
+
1310
+ For i = 1 To count
1311
+ If positions(i) = value Then Exit Sub
1312
+ Next i
1313
+
1314
+ count = count + 1
1315
+ positions(count) = value
1316
+ End Sub
1317
+
1318
+ Private Sub BuildRepairPositions(ByVal baseCount As Long, ByRef positions() As Long, ByRef positionCount As Long)
1319
+ Dim i As Long
1320
+ Dim stepValue As Double
1321
+ Dim value As Long
1322
+
1323
+ positionCount = 0
1324
+ ReDim positions(1 To MAX_LNS_REPAIR_POSITIONS)
1325
+
1326
+ If baseCount + 1 <= MAX_LNS_REPAIR_POSITIONS Then
1327
+ For i = 1 To baseCount + 1
1328
+ AddRepairPosition positions, positionCount, i, baseCount + 1
1329
+ Next i
1330
+ Exit Sub
1331
+ End If
1332
+
1333
+ AddRepairPosition positions, positionCount, 1, baseCount + 1
1334
+ AddRepairPosition positions, positionCount, baseCount + 1, baseCount + 1
1335
+
1336
+ stepValue = CDbl(baseCount) / CDbl(MAX_LNS_REPAIR_POSITIONS - 2)
1337
+ For i = 0 To MAX_LNS_REPAIR_POSITIONS - 3
1338
+ value = 1 + CLng(Int(CDbl(i) * stepValue))
1339
+ AddRepairPosition positions, positionCount, value, baseCount + 1
1340
+ Next i
1341
+ End Sub
1342
+
1343
+ Private Sub RepairDestroyedItems(ByRef items() As TBanner, ByVal n As Long, ByVal rollWidth As Double, ByVal maxHeight As Double, ByVal allowRotation As Boolean, ByVal fitMode As Long, ByRef baseOrder() As Long, ByVal baseCount As Long, ByRef removed() As Long, ByVal removedCount As Long, ByRef rotationPrefs() As Long, ByRef outOrder() As Long, ByRef outScore As Double)
1344
+ Dim currentCount As Long
1345
+ Dim positions() As Long
1346
+ Dim positionCount As Long
1347
+ Dim i As Long
1348
+ Dim p As Long
1349
+ Dim rid As Long
1350
+ Dim mode As Long
1351
+ Dim oldPref As Long
1352
+ Dim testScore As Double
1353
+ Dim bestLocalScore As Double
1354
+ Dim bestPosition As Long
1355
+ Dim bestMode As Long
1356
+ Dim testRollCount As Long
1357
+ Dim testNotPlaced As Long
1358
+ Dim testRight As Double
1359
+ Dim testAlign As Double
1360
+ Dim testMaterial As Double
1361
+
1362
+ ReDim outOrder(1 To n)
1363
+ currentCount = 0
1364
+ For i = 1 To baseCount
1365
+ outOrder(i) = baseOrder(i)
1366
+ Next i
1367
+ currentCount = baseCount
1368
+ SortRemovedByArea items, removed, removedCount
1369
+
1370
+ For i = 1 To removedCount
1371
+ rid = removed(i)
1372
+ BuildRepairPositions currentCount, positions, positionCount
1373
+ bestLocalScore = 1E+30
1374
+ bestPosition = 1
1375
+ bestMode = 0
1376
+ oldPref = rotationPrefs(rid)
1377
+
1378
+ For p = 1 To positionCount
1379
+ For mode = 0 To 2
1380
+ If Not allowRotation And mode <> 0 Then GoTo NextRepairMode
1381
+
1382
+ InsertLongAt outOrder, currentCount, positions(p), rid
1383
+ rotationPrefs(rid) = mode
1384
+
1385
+ testScore = EvaluateOrderScoreWithRotation(items, currentCount, rollWidth, maxHeight, allowRotation, fitMode, outOrder, rotationPrefs, testRollCount, testNotPlaced, testRight, testAlign, testMaterial)
1386
+
1387
+ If testScore < bestLocalScore - EPS Then
1388
+ bestLocalScore = testScore
1389
+ bestPosition = positions(p)
1390
+ bestMode = mode
1391
+ End If
1392
+
1393
+ RemoveLongAt outOrder, currentCount, positions(p)
1394
+ NextRepairMode:
1395
+ Next mode
1396
+ Next p
1397
+
1398
+ InsertLongAt outOrder, currentCount, bestPosition, rid
1399
+ rotationPrefs(rid) = bestMode
1400
+ Next i
1401
+
1402
+ outScore = EvaluateOrderScoreWithRotation(items, currentCount, rollWidth, maxHeight, allowRotation, fitMode, outOrder, rotationPrefs, testRollCount, testNotPlaced, testRight, testAlign, testMaterial)
1403
+ End Sub
1404
+
1405
+ Private Function GetCurrentRotation(ByRef rolls() As TRoll, ByVal rollCount As Long, ByVal rid As Long) As Boolean
1406
+ Dim r As Long
1407
+ Dim i As Long
1408
+
1409
+ For r = 1 To rollCount
1410
+ For i = 1 To rolls(r).placedCount
1411
+ If rolls(r).placed(i).rid = rid Then
1412
+ GetCurrentRotation = rolls(r).placed(i).Rotated
1413
+ Exit Function
1414
+ End If
1415
+ Next i
1416
+ Next r
1417
+
1418
+ GetCurrentRotation = False
1419
+ End Function
1420
+
1421
+ Private Sub TryRotationMutation(ByRef items() As TBanner, ByVal n As Long, ByVal rollWidth As Double, ByVal maxHeight As Double, ByVal allowRotation As Boolean, ByVal fitMode As Long, ByRef order() As Long, ByRef rotationPrefs() As Long, ByRef currentScore As Double)
1422
+ Dim currentRolls() As TRoll
1423
+ Dim currentRollCount As Long
1424
+ Dim currentNotPlaced As Long
1425
+ Dim currentRight As Double
1426
+ Dim currentAlign As Double
1427
+ Dim currentMaterial As Double
1428
+ Dim ids() As Long
1429
+ Dim scores() As Double
1430
+ Dim i As Long
1431
+ Dim rid As Long
1432
+ Dim oldPref As Long
1433
+ Dim newPref As Long
1434
+ Dim actualRotated As Boolean
1435
+ Dim testScore As Double
1436
+ Dim testRollCount As Long
1437
+ Dim testNotPlaced As Long
1438
+ Dim testRight As Double
1439
+ Dim testAlign As Double
1440
+ Dim testMaterial As Double
1441
+ Dim mutationCount As Long
1442
+
1443
+ If Not allowRotation Then Exit Sub
1444
+
1445
+ PackMultiRollWithOrder items, n, rollWidth, maxHeight, allowRotation, fitMode, order, rotationPrefs, currentRolls, currentRollCount, currentNotPlaced
1446
+ If currentNotPlaced > 0 Then Exit Sub
1447
+
1448
+ BuildDestroyScores items, currentRolls, currentRollCount, rollWidth, n, 1, ids, scores
1449
+
1450
+ mutationCount = MinD(MAX_ROTATION_MUTATIONS, n)
1451
+
1452
+ For i = 1 To mutationCount
1453
+ rid = ids(i)
1454
+ oldPref = rotationPrefs(rid)
1455
+ actualRotated = GetCurrentRotation(currentRolls, currentRollCount, rid)
1456
+
1457
+ If oldPref = 0 Then
1458
+ If actualRotated Then
1459
+ newPref = 1
1460
+ Else
1461
+ newPref = 2
1462
+ End If
1463
+ ElseIf oldPref = 1 Then
1464
+ newPref = 2
1465
+ Else
1466
+ newPref = 1
1467
+ End If
1468
+
1469
+ rotationPrefs(rid) = newPref
1470
+ testScore = EvaluateOrderScoreWithRotation(items, n, rollWidth, maxHeight, allowRotation, fitMode, order, rotationPrefs, testRollCount, testNotPlaced, testRight, testAlign, testMaterial)
1471
+
1472
+ If testNotPlaced = 0 And testScore < currentScore - EPS Then
1473
+ currentScore = testScore
1474
+ PackMultiRollWithOrder items, n, rollWidth, maxHeight, allowRotation, fitMode, order, rotationPrefs, currentRolls, currentRollCount, currentNotPlaced
1475
+ Else
1476
+ rotationPrefs(rid) = oldPref
1477
+ End If
1478
+ Next i
1479
+ End Sub
1480
+
1481
+ Private Sub OptimizeOrder(ByRef items() As TBanner, ByVal n As Long, ByVal rollWidth As Double, ByVal maxHeight As Double, ByVal allowRotation As Boolean, ByVal fitMode As Long, ByRef order() As Long, ByRef rotationPrefs() As Long, ByRef finalScore As Double)
1482
+ Dim currentScore As Double
1483
+ Dim testScore As Double
1484
+ Dim testRollCount As Long
1485
+ Dim testNotPlaced As Long
1486
+ Dim testRight As Double
1487
+ Dim testAlign As Double
1488
+ Dim testMaterial As Double
1489
+ Dim i As Long
1490
+ Dim j As Long
1491
+ Dim moveCount As Long
1492
+ Dim passNo As Long
1493
+ Dim improved As Boolean
1494
+
1495
+ currentScore = EvaluateOrderScoreWithRotation(items, n, rollWidth, maxHeight, allowRotation, fitMode, order, rotationPrefs, testRollCount, testNotPlaced, testRight, testAlign, testMaterial)
1496
+
1497
+ For passNo = 1 To MAX_LOCAL_PASSES
1498
+ improved = False
1499
+
1500
+ For i = 1 To n - 1
1501
+ SwapLong order, i, i + 1
1502
+ testScore = EvaluateOrderScoreWithRotation(items, n, rollWidth, maxHeight, allowRotation, fitMode, order, rotationPrefs, testRollCount, testNotPlaced, testRight, testAlign, testMaterial)
1503
+ If testScore < currentScore - EPS Then
1504
+ currentScore = testScore
1505
+ improved = True
1506
+ Else
1507
+ SwapLong order, i, i + 1
1508
+ End If
1509
+ Next i
1510
+
1511
+ moveCount = 0
1512
+ For i = 1 To n - 2
1513
+ For j = i + 2 To n
1514
+ moveCount = moveCount + 1
1515
+ If moveCount > MAX_PAIR_MOVES Then Exit For
1516
+
1517
+ SwapLong order, i, j
1518
+ testScore = EvaluateOrderScoreWithRotation(items, n, rollWidth, maxHeight, allowRotation, fitMode, order, rotationPrefs, testRollCount, testNotPlaced, testRight, testAlign, testMaterial)
1519
+ If testScore < currentScore - EPS Then
1520
+ currentScore = testScore
1521
+ improved = True
1522
+ Else
1523
+ SwapLong order, i, j
1524
+ End If
1525
+ Next j
1526
+ If moveCount >= MAX_PAIR_MOVES Then Exit For
1527
+ Next i
1528
+
1529
+ moveCount = 0
1530
+ For i = 1 To n
1531
+ For j = 1 To n
1532
+ If i <> j Then
1533
+ moveCount = moveCount + 1
1534
+ If moveCount > MAX_REINSERT_MOVES Then Exit For
1535
+
1536
+ MoveItemOrder order, i, j, n
1537
+ testScore = EvaluateOrderScoreWithRotation(items, n, rollWidth, maxHeight, allowRotation, fitMode, order, rotationPrefs, testRollCount, testNotPlaced, testRight, testAlign, testMaterial)
1538
+
1539
+ If testScore < currentScore - EPS Then
1540
+ currentScore = testScore
1541
+ improved = True
1542
+ Else
1543
+ MoveItemOrder order, j, i, n
1544
+ End If
1545
+ End If
1546
+ Next j
1547
+ If moveCount >= MAX_REINSERT_MOVES Then Exit For
1548
+ Next i
1549
+
1550
+ moveCount = 0
1551
+ For i = 1 To n - 2
1552
+ If moveCount >= MAX_TRIPLE_MOVES Then Exit For
1553
+
1554
+ SwapLong order, i + 1, i + 2
1555
+ moveCount = moveCount + 1
1556
+ testScore = EvaluateOrderScoreWithRotation(items, n, rollWidth, maxHeight, allowRotation, fitMode, order, rotationPrefs, testRollCount, testNotPlaced, testRight, testAlign, testMaterial)
1557
+ If testScore < currentScore - EPS Then
1558
+ currentScore = testScore
1559
+ improved = True
1560
+ Else
1561
+ SwapLong order, i + 1, i + 2
1562
+ End If
1563
+
1564
+ If moveCount >= MAX_TRIPLE_MOVES Then Exit For
1565
+
1566
+ SwapLong order, i, i + 1
1567
+ moveCount = moveCount + 1
1568
+ testScore = EvaluateOrderScoreWithRotation(items, n, rollWidth, maxHeight, allowRotation, fitMode, order, rotationPrefs, testRollCount, testNotPlaced, testRight, testAlign, testMaterial)
1569
+ If testScore < currentScore - EPS Then
1570
+ currentScore = testScore
1571
+ improved = True
1572
+ Else
1573
+ SwapLong order, i, i + 1
1574
+ End If
1575
+
1576
+ If moveCount >= MAX_TRIPLE_MOVES Then Exit For
1577
+
1578
+ SwapLong order, i, i + 2
1579
+ moveCount = moveCount + 1
1580
+ testScore = EvaluateOrderScoreWithRotation(items, n, rollWidth, maxHeight, allowRotation, fitMode, order, rotationPrefs, testRollCount, testNotPlaced, testRight, testAlign, testMaterial)
1581
+ If testScore < currentScore - EPS Then
1582
+ currentScore = testScore
1583
+ improved = True
1584
+ Else
1585
+ SwapLong order, i, i + 2
1586
+ End If
1587
+ Next i
1588
+
1589
+ If Not improved Then Exit For
1590
+ Next passNo
1591
+
1592
+ OptimizeContactAndCut items, n, rollWidth, maxHeight, allowRotation, fitMode, order, rotationPrefs, currentScore
1593
+
1594
+ finalScore = currentScore
1595
+ End Sub
1596
+
1597
+ Private Sub OptimizeContactAndCut(ByRef items() As TBanner, ByVal n As Long, ByVal rollWidth As Double, ByVal maxHeight As Double, ByVal allowRotation As Boolean, ByVal fitMode As Long, ByRef order() As Long, ByRef rotationPrefs() As Long, ByRef currentScore As Double)
1598
+ Dim i As Long
1599
+ Dim j As Long
1600
+ Dim moveCount As Long
1601
+ Dim testScore As Double
1602
+ Dim testRollCount As Long
1603
+ Dim testNotPlaced As Long
1604
+ Dim testRight As Double
1605
+ Dim testAlign As Double
1606
+ Dim testMaterial As Double
1607
+ Dim improved As Boolean
1608
+ Dim passNo As Long
1609
+
1610
+ For passNo = 1 To 2
1611
+ improved = False
1612
+ moveCount = 0
1613
+
1614
+ For i = 1 To n - 1
1615
+ SwapLong order, i, i + 1
1616
+ testScore = EvaluateOrderScoreWithRotation(items, n, rollWidth, maxHeight, allowRotation, fitMode, order, rotationPrefs, testRollCount, testNotPlaced, testRight, testAlign, testMaterial)
1617
+ moveCount = moveCount + 1
1618
+
1619
+ If testScore < currentScore - EPS Then
1620
+ currentScore = testScore
1621
+ improved = True
1622
+ Else
1623
+ SwapLong order, i, i + 1
1624
+ End If
1625
+
1626
+ If moveCount >= MAX_CONTACT_MOVES Then Exit For
1627
+ Next i
1628
+
1629
+ If moveCount < MAX_CONTACT_MOVES Then
1630
+ For i = 1 To n - 2
1631
+ For j = i + 2 To n
1632
+ SwapLong order, i, j
1633
+ testScore = EvaluateOrderScoreWithRotation(items, n, rollWidth, maxHeight, allowRotation, fitMode, order, rotationPrefs, testRollCount, testNotPlaced, testRight, testAlign, testMaterial)
1634
+ moveCount = moveCount + 1
1635
+
1636
+ If testScore < currentScore - EPS Then
1637
+ currentScore = testScore
1638
+ improved = True
1639
+ Else
1640
+ SwapLong order, i, j
1641
+ End If
1642
+
1643
+ If moveCount >= MAX_CUT_MOVES Then Exit For
1644
+ Next j
1645
+ If moveCount >= MAX_CUT_MOVES Then Exit For
1646
+ Next i
1647
+ End If
1648
+
1649
+ If Not improved Then Exit For
1650
+ Next passNo
1651
+ End Sub
1652
+
1653
+ Private Function GetLNSIterationLimit(ByVal n As Long) As Long
1654
+ If n <= 30 Then
1655
+ GetLNSIterationLimit = MAX_LNS_ITERATIONS
1656
+ ElseIf n <= 60 Then
1657
+ GetLNSIterationLimit = 8
1658
+ Else
1659
+ GetLNSIterationLimit = 6
1660
+ End If
1661
+ End Function
1662
+
1663
+ Private Sub OptimizeWithLNS(ByRef items() As TBanner, ByVal n As Long, ByVal rollWidth As Double, ByVal maxHeight As Double, ByVal allowRotation As Boolean, ByVal fitMode As Long, ByRef order() As Long, ByRef rotationPrefs() As Long, ByRef finalScore As Double, ByRef lnsIterationsUsed As Long, ByRef lnsImprovements As Long)
1664
+ Dim currentScore As Double
1665
+ Dim repairedScore As Double
1666
+ Dim optimizedScore As Double
1667
+ Dim iteration As Long
1668
+ Dim destroyCount As Long
1669
+ Dim selectionMode As Long
1670
+ Dim selected() As Long
1671
+ Dim selectedCount As Long
1672
+ Dim baseOrder() As Long
1673
+ Dim baseCount As Long
1674
+ Dim removed() As Long
1675
+ Dim removedCount As Long
1676
+ Dim trialOrder() As Long
1677
+ Dim trialPrefs() As Long
1678
+ Dim currentRolls() As TRoll
1679
+ Dim trialRolls() As TRoll
1680
+ Dim currentRollCount As Long
1681
+ Dim currentNotPlaced As Long
1682
+ Dim trialRollCount As Long
1683
+ Dim trialNotPlaced As Long
1684
+ Dim currentRight As Double
1685
+ Dim currentAlign As Double
1686
+ Dim currentMaterial As Double
1687
+ Dim trialRight As Double
1688
+ Dim trialAlign As Double
1689
+ Dim trialMaterial As Double
1690
+ Dim tempScore As Double
1691
+ Dim acceptWorse As Boolean
1692
+ Dim i As Long
1693
+ Dim iterationLimit As Long
1694
+
1695
+ lnsIterationsUsed = 0
1696
+ lnsImprovements = 0
1697
+
1698
+ currentScore = EvaluateOrderScoreWithRotation(items, n, rollWidth, maxHeight, allowRotation, fitMode, order, rotationPrefs, currentRollCount, currentNotPlaced, currentRight, currentAlign, currentMaterial)
1699
+ OptimizeOrder items, n, rollWidth, maxHeight, allowRotation, fitMode, order, rotationPrefs, optimizedScore
1700
+ currentScore = optimizedScore
1701
+
1702
+ If n < 3 Then
1703
+ finalScore = currentScore
1704
+ Exit Sub
1705
+ End If
1706
+
1707
+ iterationLimit = GetLNSIterationLimit(n)
1708
+
1709
+ For iteration = 1 To iterationLimit
1710
+ lnsIterationsUsed = iteration
1711
+ destroyCount = GetDestroyCount(n, iteration)
1712
+ selectionMode = ((iteration - 1) Mod 7) + 1
1713
+
1714
+ PackMultiRollWithOrder items, n, rollWidth, maxHeight, allowRotation, fitMode, order, rotationPrefs, currentRolls, currentRollCount, currentNotPlaced
1715
+ If currentNotPlaced > 0 Then Exit For
1716
+
1717
+ BuildDestroyList items, currentRolls, currentRollCount, rollWidth, n, destroyCount, selectionMode, selected, selectedCount
1718
+ BuildOrderWithoutSelected order, n, selected, selectedCount, baseOrder, baseCount, removed, removedCount
1719
+
1720
+ CopyRotationPrefs rotationPrefs, n, trialPrefs
1721
+ For i = 1 To selectedCount
1722
+ trialPrefs(selected(i)) = 0
1723
+ Next i
1724
+
1725
+ RepairDestroyedItems items, n, rollWidth, maxHeight, allowRotation, fitMode, baseOrder, baseCount, removed, removedCount, trialPrefs, trialOrder, repairedScore
1726
+
1727
+ acceptWorse = False
1728
+ If repairedScore < currentScore - EPS Then
1729
+ acceptWorse = True
1730
+ ElseIf iteration Mod 5 = 0 Then
1731
+ If repairedScore < currentScore * (1# + LNS_ACCEPT_WORSE_PCT) Then
1732
+ acceptWorse = True
1733
+ End If
1734
+ End If
1735
+
1736
+ If acceptWorse Then
1737
+ CopyOrder trialOrder, n, order
1738
+ CopyRotationPrefs trialPrefs, n, rotationPrefs
1739
+ currentScore = repairedScore
1740
+ lnsImprovements = lnsImprovements + 1
1741
+
1742
+ OptimizeOrder items, n, rollWidth, maxHeight, allowRotation, fitMode, order, rotationPrefs, optimizedScore
1743
+ If optimizedScore < currentScore - EPS Then
1744
+ currentScore = optimizedScore
1745
+ End If
1746
+ End If
1747
+
1748
+ TryRotationMutation items, n, rollWidth, maxHeight, allowRotation, fitMode, order, rotationPrefs, currentScore
1749
+
1750
+ tempScore = EvaluateOrderScoreWithRotation(items, n, rollWidth, maxHeight, allowRotation, fitMode, order, rotationPrefs, trialRollCount, trialNotPlaced, trialRight, trialAlign, trialMaterial)
1751
+ If tempScore < currentScore - EPS Then currentScore = tempScore
1752
+ Next iteration
1753
+
1754
+ finalScore = currentScore
1755
+ End Sub
1756
+
1757
+ Private Sub MoveShapeToBottomLeft(ByVal sh As Shape, ByVal targetX As Double, ByVal targetY As Double)
1758
+ Dim bx As Double
1759
+ Dim by As Double
1760
+ Dim bw As Double
1761
+ Dim bh As Double
1762
+
1763
+ sh.GetBoundingBox bx, by, bw, bh, False
1764
+ sh.Move targetX - bx, targetY - by
1765
+ End Sub
1766
+
1767
+ Private Sub ApplyNestingToObjects(ByRef items() As TBanner, ByRef sourceShapes() As Shape, ByRef rolls() As TRoll, ByVal rollCount As Long, ByVal rollWidth As Double)
1768
+ Dim totalHeight As Double
1769
+ Dim rollOffsetY As Double
1770
+ Dim r As Long
1771
+ Dim i As Long
1772
+ Dim rid As Long
1773
+ Dim margin As Double
1774
+ Dim targetX As Double
1775
+ Dim targetY As Double
1776
+ Dim pageLeft As Double
1777
+ Dim pageBottom As Double
1778
+ Dim pageTop As Double
1779
+ Dim rollTop As Double
1780
+ Dim sh As Shape
1781
+
1782
+ totalHeight = 0#
1783
+ For r = 1 To rollCount
1784
+ totalHeight = totalHeight + rolls(r).UsedHeight
1785
+ If r < rollCount Then totalHeight = totalHeight + ROLL_GAP
1786
+ Next r
1787
+
1788
+ If totalHeight <= EPS Then totalHeight = 10#
1789
+
1790
+ ActivePage.SizeWidth = rollWidth
1791
+ ActivePage.SizeHeight = totalHeight
1792
+
1793
+ pageLeft = ActivePage.LeftX
1794
+ pageBottom = ActivePage.BottomY
1795
+ pageTop = ActivePage.TopY
1796
+ rollOffsetY = 0#
1797
+
1798
+ For r = 1 To rollCount
1799
+ rollTop = pageTop - rollOffsetY
1800
+
1801
+ For i = 1 To rolls(r).placedCount
1802
+ rid = rolls(r).placed(i).rid
1803
+ Set sh = sourceShapes(rid)
1804
+ margin = items(rid).margin
1805
+
1806
+ If rolls(r).placed(i).Rotated Then sh.Rotate 90#
1807
+
1808
+ targetX = pageLeft + rolls(r).placed(i).x + margin
1809
+ targetY = rollTop - rolls(r).placed(i).y - rolls(r).placed(i).h + margin
1810
+ MoveShapeToBottomLeft sh, targetX, targetY
1811
+ Next i
1812
+
1813
+ rollOffsetY = rollOffsetY + rolls(r).UsedHeight
1814
+ If r < rollCount Then rollOffsetY = rollOffsetY + ROLL_GAP
1815
+ Next r
1816
+
1817
+ ActiveDocument.ClearSelection
1818
+ For r = 1 To rollCount
1819
+ For i = 1 To rolls(r).placedCount
1820
+ rid = rolls(r).placed(i).rid
1821
+ sourceShapes(rid).CreateSelection
1822
+ Next i
1823
+ Next r
1824
+ End Sub
1825
+
1826
+ Private Sub ParseWidthList(ByVal txt As String, ByRef widths() As Double, ByRef widthCount As Long)
1827
+ Dim parts() As String
1828
+ Dim i As Long
1829
+ Dim v As Double
1830
+
1831
+ widthCount = 0
1832
+ parts = Split(txt, ",")
1833
+ ReDim widths(1 To UBound(parts) + 1)
1834
+
1835
+ For i = LBound(parts) To UBound(parts)
1836
+ If Len(Trim$(parts(i))) > 0 Then
1837
+ v = Val(Trim$(parts(i)))
1838
+ If v > 0# Then
1839
+ widthCount = widthCount + 1
1840
+ widths(widthCount) = v
1841
+ End If
1842
+ End If
1843
+ Next i
1844
+
1845
+ If widthCount <= 0 Then
1846
+ ReDim widths(1 To 1)
1847
+ Else
1848
+ ReDim Preserve widths(1 To widthCount)
1849
+ End If
1850
+ End Sub
1851
+
1852
+ Private Function SortModeName(ByVal mode As Long) As String
1853
+ Select Case mode
1854
+ Case 1
1855
+ SortModeName = "Area Descending"
1856
+ Case 2
1857
+ SortModeName = "Width Descending"
1858
+ Case 3
1859
+ SortModeName = "Height Descending"
1860
+ Case 4
1861
+ SortModeName = "Short Side Descending"
1862
+ Case 5
1863
+ SortModeName = "Long Side Descending"
1864
+ Case 6
1865
+ SortModeName = "Aspect Ratio Descending"
1866
+ Case Else
1867
+ SortModeName = "Unknown"
1868
+ End Select
1869
+ End Function
1870
+
1871
+ Private Function FitModeName(ByVal mode As Long) As String
1872
+ Select Case mode
1873
+ Case 1
1874
+ FitModeName = "BSSF"
1875
+ Case 2
1876
+ FitModeName = "BAF"
1877
+ Case 3
1878
+ FitModeName = "BLSF"
1879
+ Case Else
1880
+ FitModeName = "Unknown"
1881
+ End Select
1882
+ End Function
1883
+
1884
+
1885
+
1886
+ ' -----------------------------------------------------------------------------
1887
+ ' MULTI-WIDTH HELPERS
1888
+ ' This version compares the existing best single-width result against layouts
1889
+ ' using two different roll widths. Each object is assigned to exactly one group.
1890
+ ' The partition candidates are generated from several sort strategies and
1891
+ ' split points, then both groups are packed and scored independently. This is a
1892
+ ' bounded heuristic: it compares one-width against two-width solutions, not all
1893
+ ' possible arbitrary partitions and not three-or-more-width combinations.
1894
+ ' -----------------------------------------------------------------------------
1895
+ Private Sub OptimizeSubsetOrder(ByRef items() As TBanner, ByVal subsetCount As Long, ByVal rollWidth As Double, ByVal maxHeight As Double, ByVal allowRotation As Boolean, ByVal fitMode As Long, ByRef order() As Long, ByRef rotationPrefs() As Long, ByRef finalScore As Double)
1896
+ Dim currentScore As Double
1897
+ Dim testScore As Double
1898
+ Dim testRollCount As Long
1899
+ Dim testNotPlaced As Long
1900
+ Dim testRight As Double
1901
+ Dim testAlign As Double
1902
+ Dim testMaterial As Double
1903
+ Dim i As Long
1904
+ Dim j As Long
1905
+ Dim passNo As Long
1906
+ Dim improved As Boolean
1907
+ Dim moves As Long
1908
+
1909
+ If subsetCount <= 1 Then
1910
+ finalScore = EvaluateOrderScoreWithRotation(items, subsetCount, rollWidth, maxHeight, allowRotation, fitMode, order, rotationPrefs, testRollCount, testNotPlaced, testRight, testAlign, testMaterial)
1911
+ Exit Sub
1912
+ End If
1913
+
1914
+ currentScore = EvaluateOrderScoreWithRotation(items, subsetCount, rollWidth, maxHeight, allowRotation, fitMode, order, rotationPrefs, testRollCount, testNotPlaced, testRight, testAlign, testMaterial)
1915
+
1916
+ For passNo = 1 To 2
1917
+ improved = False
1918
+
1919
+ For i = 1 To subsetCount - 1
1920
+ SwapLong order, i, i + 1
1921
+ testScore = EvaluateOrderScoreWithRotation(items, subsetCount, rollWidth, maxHeight, allowRotation, fitMode, order, rotationPrefs, testRollCount, testNotPlaced, testRight, testAlign, testMaterial)
1922
+ If testNotPlaced = 0 And testScore < currentScore - EPS Then
1923
+ currentScore = testScore
1924
+ improved = True
1925
+ Else
1926
+ SwapLong order, i, i + 1
1927
+ End If
1928
+ Next i
1929
+
1930
+ moves = 0
1931
+ For i = 1 To subsetCount - 1
1932
+ For j = i + 1 To subsetCount
1933
+ If j <> i + 1 Then
1934
+ SwapLong order, i, j
1935
+ testScore = EvaluateOrderScoreWithRotation(items, subsetCount, rollWidth, maxHeight, allowRotation, fitMode, order, rotationPrefs, testRollCount, testNotPlaced, testRight, testAlign, testMaterial)
1936
+ moves = moves + 1
1937
+
1938
+ If testNotPlaced = 0 And testScore < currentScore - EPS Then
1939
+ currentScore = testScore
1940
+ improved = True
1941
+ Else
1942
+ SwapLong order, i, j
1943
+ End If
1944
+
1945
+ If moves >= MAX_PAIR_MOVES Then Exit For
1946
+ End If
1947
+ Next j
1948
+ If moves >= MAX_PAIR_MOVES Then Exit For
1949
+ Next i
1950
+
1951
+ If Not improved Then Exit For
1952
+ Next passNo
1953
+
1954
+ finalScore = currentScore
1955
+ End Sub
1956
+
1957
+ Private Function TotalRollHeight(ByRef rolls() As TRoll, ByVal rollCount As Long) As Double
1958
+ Dim r As Long
1959
+ Dim totalHeight As Double
1960
+
1961
+ totalHeight = 0#
1962
+ For r = 1 To rollCount
1963
+ totalHeight = totalHeight + rolls(r).UsedHeight
1964
+ If r < rollCount Then totalHeight = totalHeight + ROLL_GAP
1965
+ Next r
1966
+
1967
+ If totalHeight <= EPS Then totalHeight = 10#
1968
+ TotalRollHeight = totalHeight
1969
+ End Function
1970
+
1971
+ Private Sub ApplyNestingToPage(ByRef items() As TBanner, ByRef sourceShapes() As Shape, ByRef rolls() As TRoll, ByVal rollCount As Long, ByVal rollWidth As Double, ByVal targetPage As Page)
1972
+ Dim rollOffsetY As Double
1973
+ Dim r As Long
1974
+ Dim i As Long
1975
+ Dim rid As Long
1976
+ Dim margin As Double
1977
+ Dim targetX As Double
1978
+ Dim targetY As Double
1979
+ Dim pageLeft As Double
1980
+ Dim pageTop As Double
1981
+ Dim rollTop As Double
1982
+ Dim sh As Shape
1983
+ Dim totalHeight As Double
1984
+
1985
+ totalHeight = TotalRollHeight(rolls, rollCount)
1986
+ targetPage.SetSize rollWidth, totalHeight
1987
+ targetPage.Activate
1988
+
1989
+ pageLeft = targetPage.LeftX
1990
+ pageTop = targetPage.TopY
1991
+ rollOffsetY = 0#
1992
+ ActiveDocument.ClearSelection
1993
+
1994
+ For r = 1 To rollCount
1995
+ rollTop = pageTop - rollOffsetY
1996
+
1997
+ For i = 1 To rolls(r).placedCount
1998
+ rid = rolls(r).placed(i).rid
1999
+ Set sh = sourceShapes(rid)
2000
+
2001
+ ' Move the original object to the output page's active layer.
2002
+ sh.MoveToLayer targetPage.ActiveLayer
2003
+ margin = items(rid).margin
2004
+
2005
+ If rolls(r).placed(i).Rotated Then sh.Rotate 90#
2006
+
2007
+ targetX = pageLeft + rolls(r).placed(i).x + margin
2008
+ targetY = rollTop - rolls(r).placed(i).y - rolls(r).placed(i).h + margin
2009
+ MoveShapeToBottomLeft sh, targetX, targetY
2010
+ sh.CreateSelection
2011
+ Next i
2012
+
2013
+ rollOffsetY = rollOffsetY + rolls(r).UsedHeight
2014
+ If r < rollCount Then rollOffsetY = rollOffsetY + ROLL_GAP
2015
+ Next r
2016
+ End Sub
2017
+
2018
+ Private Sub FindBestTwoWidthLayout(ByRef items() As TBanner, ByVal n As Long, ByRef widths() As Double, ByVal widthCount As Long, ByVal maxHeight As Double, ByVal allowRotation As Boolean, ByRef outOrderA() As Long, ByRef outCountA As Long, ByRef outWidthA As Double, ByRef outFitA As Long, ByRef outOrderB() As Long, ByRef outCountB As Long, ByRef outWidthB As Double, ByRef outFitB As Long, ByRef outMaterial As Double, ByRef outScore As Double)
2019
+ Dim sortedOrder() As Long
2020
+ Dim candidateOrderA() As Long
2021
+ Dim candidateOrderB() As Long
2022
+ Dim bestOrderA() As Long
2023
+ Dim bestOrderB() As Long
2024
+ Dim rotationPrefs() As Long
2025
+ Dim candidateRollsA() As TRoll
2026
+ Dim candidateRollsB() As TRoll
2027
+ Dim rollCountA As Long
2028
+ Dim rollCountB As Long
2029
+ Dim notPlacedA As Long
2030
+ Dim notPlacedB As Long
2031
+ Dim candidateCountA As Long
2032
+ Dim candidateCountB As Long
2033
+ Dim candidateWidthA As Double
2034
+ Dim candidateWidthB As Double
2035
+ Dim candidateMaterialA As Double
2036
+ Dim candidateMaterialB As Double
2037
+ Dim candidateMaterial As Double
2038
+ Dim candidateScoreA As Double
2039
+ Dim candidateScoreB As Double
2040
+ Dim candidateCombinedScore As Double
2041
+ Dim rightA As Double
2042
+ Dim rightB As Double
2043
+ Dim alignA As Double
2044
+ Dim alignB As Double
2045
+ Dim j As Long
2046
+ Dim k As Long
2047
+ Dim widthLimit As Long
2048
+ Dim sortMode As Long
2049
+ Dim splitStep As Long
2050
+ Dim splitPos As Long
2051
+ Dim previousSplit As Long
2052
+ Dim direction As Long
2053
+ Dim fitMode As Long
2054
+ Dim i As Long
2055
+
2056
+ outCountA = 0
2057
+ outCountB = 0
2058
+ outWidthA = 0#
2059
+ outWidthB = 0#
2060
+ outFitA = 1
2061
+ outFitB = 1
2062
+ outMaterial = 1E+30
2063
+ outScore = 1E+30
2064
+
2065
+ If n < 2 Or widthCount < 2 Then Exit Sub
2066
+
2067
+ widthLimit = widthCount
2068
+ If widthLimit > MAX_MULTI_WIDTH_CANDIDATES Then widthLimit = MAX_MULTI_WIDTH_CANDIDATES
2069
+
2070
+ ReDim rotationPrefs(1 To n)
2071
+ For i = 1 To n
2072
+ rotationPrefs(i) = 0
2073
+ Next i
2074
+
2075
+ For j = 1 To widthLimit - 1
2076
+ For k = j + 1 To widthLimit
2077
+ If AbsD(widths(j) - widths(k)) > EPS Then
2078
+ For sortMode = 1 To 6
2079
+ BuildInitialOrder items, n, sortMode, sortedOrder
2080
+ previousSplit = 0
2081
+
2082
+ ' Split points at approximately 1/5, 2/5, 3/5 and 4/5.
2083
+ For splitStep = 1 To 4
2084
+ splitPos = CLng(Int((CDbl(n) * CDbl(splitStep)) / 5#))
2085
+ If splitPos < 1 Then splitPos = 1
2086
+ If splitPos > n - 1 Then splitPos = n - 1
2087
+
2088
+ If splitPos <> previousSplit Then
2089
+ previousSplit = splitPos
2090
+ candidateCountA = splitPos
2091
+ candidateCountB = n - splitPos
2092
+ ReDim candidateOrderA(1 To candidateCountA)
2093
+ ReDim candidateOrderB(1 To candidateCountB)
2094
+
2095
+ For i = 1 To candidateCountA
2096
+ candidateOrderA(i) = sortedOrder(i)
2097
+ Next i
2098
+ For i = 1 To candidateCountB
2099
+ candidateOrderB(i) = sortedOrder(splitPos + i)
2100
+ Next i
2101
+
2102
+ For direction = 1 To 2
2103
+ If direction = 1 Then
2104
+ candidateWidthA = widths(j)
2105
+ candidateWidthB = widths(k)
2106
+ Else
2107
+ candidateWidthA = widths(k)
2108
+ candidateWidthB = widths(j)
2109
+ End If
2110
+
2111
+ For fitMode = 1 To 3
2112
+ PackMultiRollWithOrder items, candidateCountA, candidateWidthA, maxHeight, allowRotation, fitMode, candidateOrderA, rotationPrefs, candidateRollsA, rollCountA, notPlacedA
2113
+ candidateScoreA = PackScore(items, candidateRollsA, rollCountA, notPlacedA, candidateWidthA, rightA, alignA, candidateMaterialA)
2114
+
2115
+ If notPlacedA = 0 Then
2116
+ PackMultiRollWithOrder items, candidateCountB, candidateWidthB, maxHeight, allowRotation, fitMode, candidateOrderB, rotationPrefs, candidateRollsB, rollCountB, notPlacedB
2117
+ candidateScoreB = PackScore(items, candidateRollsB, rollCountB, notPlacedB, candidateWidthB, rightB, alignB, candidateMaterialB)
2118
+
2119
+ If notPlacedB = 0 Then
2120
+ candidateMaterial = candidateMaterialA + candidateMaterialB
2121
+ candidateCombinedScore = candidateMaterial * MATERIAL_SCORE_SCALE _
2122
+ + (candidateScoreA - candidateMaterialA * MATERIAL_SCORE_SCALE) _
2123
+ + (candidateScoreB - candidateMaterialB * MATERIAL_SCORE_SCALE)
2124
+
2125
+ If candidateMaterial < outMaterial - EPS _
2126
+ Or (AbsD(candidateMaterial - outMaterial) <= EPS And candidateCombinedScore < outScore) Then
2127
+ outMaterial = candidateMaterial
2128
+ outScore = candidateCombinedScore
2129
+ outWidthA = candidateWidthA
2130
+ outWidthB = candidateWidthB
2131
+ outFitA = fitMode
2132
+ outFitB = fitMode
2133
+ outCountA = candidateCountA
2134
+ outCountB = candidateCountB
2135
+ CopyOrder candidateOrderA, candidateCountA, bestOrderA
2136
+ CopyOrder candidateOrderB, candidateCountB, bestOrderB
2137
+ End If
2138
+ End If
2139
+ End If
2140
+ Next fitMode
2141
+ Next direction
2142
+ End If
2143
+ Next splitStep
2144
+ Next sortMode
2145
+ End If
2146
+ Next k
2147
+ Next j
2148
+
2149
+ If outCountA > 0 And outCountB > 0 Then
2150
+ CopyOrder bestOrderA, outCountA, outOrderA
2151
+ CopyOrder bestOrderB, outCountB, outOrderB
2152
+ End If
2153
+ End Sub
2154
+
2155
+ Public Sub RunNesting_BannerCorelDRAW()
2156
+ Dim doc As Document
2157
+ Dim sr As ShapeRange
2158
+ Dim sh As Shape
2159
+ Dim items() As TBanner
2160
+ Dim sourceShapes() As Shape
2161
+ Dim rolls() As TRoll
2162
+ Dim candidateRolls() As TRoll
2163
+ Dim candidateOrder() As Long
2164
+ Dim bestOrder() As Long
2165
+ Dim workOrder() As Long
2166
+ Dim workPrefs() As Long
2167
+ Dim bestPrefs() As Long
2168
+ Dim seedOrders() As Long
2169
+ Dim seedScores() As Double
2170
+ Dim seedWidths() As Double
2171
+ Dim seedFits() As Long
2172
+ Dim seedSorts() As Long
2173
+ Dim n As Long
2174
+ Dim i As Long
2175
+ Dim j As Long
2176
+ Dim oldUnit As Long
2177
+ Dim marginText As String
2178
+ Dim margin As Double
2179
+ Dim rollText As String
2180
+ Dim widths() As Double
2181
+ Dim widthCount As Long
2182
+ Dim rotateAnswer As VbMsgBoxResult
2183
+ Dim allowRotation As Boolean
2184
+ Dim maxHeight As Double
2185
+ Dim minNeeded As Double
2186
+ Dim candidateWidth As Double
2187
+ Dim bestWidth As Double
2188
+ Dim candidateSortMode As Long
2189
+ Dim candidateFitMode As Long
2190
+ Dim bestSortMode As Long
2191
+ Dim bestFitMode As Long
2192
+ Dim candidateRollCount As Long
2193
+ Dim candidateNotPlaced As Long
2194
+ Dim bestRollCount As Long
2195
+ Dim bestNotPlaced As Long
2196
+ Dim candidateScore As Double
2197
+ Dim bestScore As Double
2198
+ Dim candidateMaterial As Double
2199
+ Dim bestMaterial As Double
2200
+ Dim candidateWaste As Double
2201
+ Dim bestWaste As Double
2202
+ Dim candidateRightWaste As Double
2203
+ Dim bestRightWaste As Double
2204
+ Dim candidateAlignment As Double
2205
+ Dim bestAlignment As Double
2206
+ Dim optimizedScore As Double
2207
+ Dim seedCount As Long
2208
+ Dim lnsIterationsUsed As Long
2209
+ Dim lnsImprovements As Long
2210
+ Dim bx As Double
2211
+ Dim by As Double
2212
+ Dim bw As Double
2213
+ Dim bh As Double
2214
+ Dim sumSide As Double
2215
+ Dim maxMargin As Double
2216
+ Dim msg As String
2217
+ Dim errNumber As Long
2218
+ Dim errDescription As String
2219
+ Dim finalSharedEdge As Double
2220
+ Dim finalContactSegments As Long
2221
+ Dim finalGuillotineEdge As Double
2222
+ Dim finalCutPenalty As Double
2223
+ Dim multiOrderA() As Long
2224
+ Dim multiOrderB() As Long
2225
+ Dim multiPrefs() As Long
2226
+ Dim multiRollsA() As TRoll
2227
+ Dim multiRollsB() As TRoll
2228
+ Dim multiCountA As Long
2229
+ Dim multiCountB As Long
2230
+ Dim multiWidthA As Double
2231
+ Dim multiWidthB As Double
2232
+ Dim multiFitA As Long
2233
+ Dim multiFitB As Long
2234
+ Dim multiBestMaterial As Double
2235
+ Dim multiBestScore As Double
2236
+ Dim multiMaterialA As Double
2237
+ Dim multiMaterialB As Double
2238
+ Dim multiCombinedMaterial As Double
2239
+ Dim multiRollCountA As Long
2240
+ Dim multiRollCountB As Long
2241
+ Dim multiNotPlacedA As Long
2242
+ Dim multiNotPlacedB As Long
2243
+ Dim multiScoreA As Double
2244
+ Dim multiScoreB As Double
2245
+ Dim multiRightA As Double
2246
+ Dim multiRightB As Double
2247
+ Dim multiAlignA As Double
2248
+ Dim multiAlignB As Double
2249
+ Dim multiHeightA As Double
2250
+ Dim multiHeightB As Double
2251
+ Dim multiTrueArea As Double
2252
+ Dim multiWaste As Double
2253
+ Dim multiSavingPct As Double
2254
+ Dim useMultiWidth As Boolean
2255
+ Dim outputPageA As Page
2256
+ Dim outputPageB As Page
2257
+
2258
+ On Error GoTo ErrHandler
2259
+
2260
+ Set doc = ActiveDocument
2261
+ If doc Is Nothing Then
2262
+ MsgBox "Tidak ada document CorelDRAW aktif.", vbExclamation, "Auto Nesting"
2263
+ Exit Sub
2264
+ End If
2265
+
2266
+ oldUnit = doc.Unit
2267
+ Set sr = ActiveSelectionRange
2268
+
2269
+ If sr Is Nothing Then
2270
+ MsgBox "Pilih object/banner terlebih dahulu.", vbExclamation, "Auto Nesting"
2271
+ Exit Sub
2272
+ End If
2273
+
2274
+ If sr.count <= 0 Then
2275
+ MsgBox "Pilih minimal 1 object/banner terlebih dahulu.", vbExclamation, "Auto Nesting"
2276
+ Exit Sub
2277
+ End If
2278
+
2279
+ n = sr.count
2280
+ doc.Unit = cdrCentimeter
2281
+ ReDim items(1 To n)
2282
+ ReDim sourceShapes(1 To n)
2283
+
2284
+ i = 0
2285
+ For Each sh In sr
2286
+ i = i + 1
2287
+ Set sourceShapes(i) = sh
2288
+ sh.GetBoundingBox bx, by, bw, bh, False
2289
+
2290
+ If bw <= EPS Or bh <= EPS Then
2291
+ doc.Unit = oldUnit
2292
+ MsgBox "Object nomor " & CStr(i) & " memiliki bounding box tidak valid.", vbCritical, "Auto Nesting"
2293
+ Exit Sub
2294
+ End If
2295
+
2296
+ items(i).Label = "Object " & CStr(i)
2297
+ items(i).OrigW = bw
2298
+ items(i).OrigH = bh
2299
+ items(i).margin = 0#
2300
+ Next sh
2301
+
2302
+ marginText = InputBox("Margin / clearance per sisi (cm):", "Auto Nesting - Margin", "0")
2303
+ If Len(Trim$(marginText)) > 0 Then margin = Val(Trim$(marginText)) Else margin = 0#
2304
+ If margin < 0# Then margin = 0#
2305
+
2306
+ For i = 1 To n
2307
+ items(i).margin = margin
2308
+ Next i
2309
+
2310
+ rollText = InputBox("Masukkan daftar lebar roll dalam cm." & vbCrLf & "Contoh: 150,200,250,300", "Auto Nesting - Roll Width", "109,159,219,259,319")
2311
+ If Len(Trim$(rollText)) = 0 Then
2312
+ doc.Unit = oldUnit
2313
+ MsgBox "Lebar roll tidak diberikan.", vbExclamation, "Auto Nesting"
2314
+ Exit Sub
2315
+ End If
2316
+
2317
+ ParseWidthList rollText, widths, widthCount
2318
+ If widthCount <= 0 Then
2319
+ doc.Unit = oldUnit
2320
+ MsgBox "Daftar lebar roll tidak valid.", vbCritical, "Auto Nesting"
2321
+ Exit Sub
2322
+ End If
2323
+
2324
+ rotateAnswer = MsgBox("Izinkan object diputar 90°?", vbYesNo + vbQuestion, "Auto Nesting - Rotation")
2325
+ allowRotation = (rotateAnswer = vbYes)
2326
+
2327
+ sumSide = 0#
2328
+ maxMargin = 0#
2329
+ For i = 1 To n
2330
+ sumSide = sumSide + MaxD(items(i).OrigW, items(i).OrigH)
2331
+ maxMargin = MaxD(maxMargin, items(i).margin)
2332
+ Next i
2333
+
2334
+ maxHeight = sumSide + maxMargin * (n + 2) + 100#
2335
+ minNeeded = 0#
2336
+
2337
+ For i = 1 To n
2338
+ If allowRotation Then
2339
+ minNeeded = MaxD(minNeeded, MinD(items(i).OrigW, items(i).OrigH) + 2# * items(i).margin)
2340
+ Else
2341
+ minNeeded = MaxD(minNeeded, items(i).OrigW + 2# * items(i).margin)
2342
+ End If
2343
+ Next i
2344
+
2345
+ ReDim seedOrders(1 To MAX_LOCAL_SEEDS, 1 To n)
2346
+ ReDim seedScores(1 To MAX_LOCAL_SEEDS)
2347
+ ReDim seedWidths(1 To MAX_LOCAL_SEEDS)
2348
+ ReDim seedFits(1 To MAX_LOCAL_SEEDS)
2349
+ ReDim seedSorts(1 To MAX_LOCAL_SEEDS)
2350
+
2351
+ For i = 1 To MAX_LOCAL_SEEDS
2352
+ seedScores(i) = 1E+30
2353
+ Next i
2354
+
2355
+ seedCount = 0
2356
+ bestScore = 1E+30
2357
+ bestMaterial = 1E+30
2358
+ bestWaste = 1#
2359
+ bestRightWaste = 1E+30
2360
+ bestAlignment = 1E+30
2361
+ bestWidth = 0#
2362
+ bestRollCount = 0
2363
+ bestNotPlaced = n
2364
+ bestSortMode = 1
2365
+ bestFitMode = 1
2366
+
2367
+ ReDim bestOrder(1 To n)
2368
+ ReDim bestPrefs(1 To n)
2369
+ For i = 1 To n
2370
+ bestPrefs(i) = 0
2371
+ Next i
2372
+
2373
+ For j = 1 To widthCount
2374
+ candidateWidth = widths(j)
2375
+
2376
+ If candidateWidth + EPS >= minNeeded Then
2377
+ For candidateSortMode = 1 To 6
2378
+ BuildInitialOrder items, n, candidateSortMode, candidateOrder
2379
+
2380
+ For candidateFitMode = 1 To 3
2381
+ ReDim workPrefs(1 To n)
2382
+ For i = 1 To n
2383
+ workPrefs(i) = 0
2384
+ Next i
2385
+
2386
+ PackMultiRollWithOrder items, n, candidateWidth, maxHeight, allowRotation, candidateFitMode, candidateOrder, workPrefs, candidateRolls, candidateRollCount, candidateNotPlaced
2387
+ candidateScore = PackScore(items, candidateRolls, candidateRollCount, candidateNotPlaced, candidateWidth, candidateRightWaste, candidateAlignment, candidateMaterial)
2388
+
2389
+ If candidateNotPlaced = 0 Then
2390
+ candidateWaste = CalcWaste(items, candidateRolls, candidateRollCount, candidateWidth)
2391
+ Else
2392
+ candidateWaste = 1#
2393
+ End If
2394
+
2395
+ AddSeed seedOrders, seedScores, seedWidths, seedFits, seedSorts, seedCount, MAX_LOCAL_SEEDS, candidateOrder, n, candidateScore, candidateWidth, candidateFitMode, candidateSortMode
2396
+
2397
+ If candidateScore < bestScore - EPS Then
2398
+ bestScore = candidateScore
2399
+ bestWidth = candidateWidth
2400
+ bestRollCount = candidateRollCount
2401
+ bestNotPlaced = candidateNotPlaced
2402
+ bestSortMode = candidateSortMode
2403
+ bestFitMode = candidateFitMode
2404
+ bestMaterial = candidateMaterial
2405
+ bestWaste = candidateWaste
2406
+ bestRightWaste = candidateRightWaste
2407
+ bestAlignment = candidateAlignment
2408
+
2409
+ CopyOrder candidateOrder, n, bestOrder
2410
+ CopyRotationPrefs workPrefs, n, bestPrefs
2411
+ End If
2412
+ Next candidateFitMode
2413
+ Next candidateSortMode
2414
+ End If
2415
+ Next j
2416
+
2417
+ ' Even if no single width can hold all selected objects, continue: a
2418
+ ' feasible combination of two widths may still exist.
2419
+
2420
+ For i = 1 To seedCount
2421
+ CopyOrderFromSeed seedOrders, i, n, workOrder
2422
+ ReDim workPrefs(1 To n)
2423
+ For j = 1 To n
2424
+ workPrefs(j) = 0
2425
+ Next j
2426
+
2427
+ optimizedScore = 1E+30
2428
+ OptimizeOrder items, n, seedWidths(i), maxHeight, allowRotation, seedFits(i), workOrder, workPrefs, optimizedScore
2429
+
2430
+ PackMultiRollWithOrder items, n, seedWidths(i), maxHeight, allowRotation, seedFits(i), workOrder, workPrefs, candidateRolls, candidateRollCount, candidateNotPlaced
2431
+ candidateScore = PackScore(items, candidateRolls, candidateRollCount, candidateNotPlaced, seedWidths(i), candidateRightWaste, candidateAlignment, candidateMaterial)
2432
+
2433
+ If candidateNotPlaced = 0 Then
2434
+ candidateWaste = CalcWaste(items, candidateRolls, candidateRollCount, seedWidths(i))
2435
+ Else
2436
+ candidateWaste = 1#
2437
+ End If
2438
+
2439
+ If candidateScore < bestScore - EPS Then
2440
+ bestScore = candidateScore
2441
+ bestWidth = seedWidths(i)
2442
+ bestRollCount = candidateRollCount
2443
+ bestNotPlaced = candidateNotPlaced
2444
+ bestSortMode = seedSorts(i)
2445
+ bestFitMode = seedFits(i)
2446
+ bestMaterial = candidateMaterial
2447
+ bestWaste = candidateWaste
2448
+ bestRightWaste = candidateRightWaste
2449
+ bestAlignment = candidateAlignment
2450
+ CopyOrder workOrder, n, bestOrder
2451
+ CopyRotationPrefs workPrefs, n, bestPrefs
2452
+ End If
2453
+ Next i
2454
+
2455
+ lnsIterationsUsed = 0
2456
+ lnsImprovements = 0
2457
+
2458
+ If bestNotPlaced = 0 And bestWidth > EPS Then
2459
+ CopyOrder bestOrder, n, workOrder
2460
+ CopyRotationPrefs bestPrefs, n, workPrefs
2461
+ optimizedScore = bestScore
2462
+
2463
+ OptimizeWithLNS items, n, bestWidth, maxHeight, allowRotation, bestFitMode, workOrder, workPrefs, optimizedScore, lnsIterationsUsed, lnsImprovements
2464
+
2465
+ PackMultiRollWithOrder items, n, bestWidth, maxHeight, allowRotation, bestFitMode, workOrder, workPrefs, candidateRolls, candidateRollCount, candidateNotPlaced
2466
+ candidateScore = PackScore(items, candidateRolls, candidateRollCount, candidateNotPlaced, bestWidth, candidateRightWaste, candidateAlignment, candidateMaterial)
2467
+
2468
+ If candidateNotPlaced = 0 And candidateScore < bestScore - EPS Then
2469
+ bestScore = candidateScore
2470
+ bestRollCount = candidateRollCount
2471
+ bestNotPlaced = candidateNotPlaced
2472
+ bestMaterial = candidateMaterial
2473
+ bestWaste = CalcWaste(items, candidateRolls, candidateRollCount, bestWidth)
2474
+ bestRightWaste = candidateRightWaste
2475
+ bestAlignment = candidateAlignment
2476
+ CopyOrder workOrder, n, bestOrder
2477
+ CopyRotationPrefs workPrefs, n, bestPrefs
2478
+ End If
2479
+ End If
2480
+
2481
+ ' Search for a two-width partition after the strongest single-width layout
2482
+ ' has been found. A second width is accepted only if it reduces total
2483
+ ' material area compared with the best single-width result.
2484
+ If widthCount >= 2 And n >= 2 Then
2485
+ FindBestTwoWidthLayout items, n, widths, widthCount, maxHeight, allowRotation, _
2486
+ multiOrderA, multiCountA, multiWidthA, multiFitA, _
2487
+ multiOrderB, multiCountB, multiWidthB, multiFitB, multiBestMaterial, multiBestScore
2488
+
2489
+ If multiCountA > 0 And multiCountB > 0 Then
2490
+ ReDim multiPrefs(1 To n)
2491
+ For i = 1 To n
2492
+ multiPrefs(i) = 0
2493
+ Next i
2494
+
2495
+ OptimizeSubsetOrder items, multiCountA, multiWidthA, maxHeight, allowRotation, multiFitA, multiOrderA, multiPrefs, optimizedScore
2496
+ OptimizeSubsetOrder items, multiCountB, multiWidthB, maxHeight, allowRotation, multiFitB, multiOrderB, multiPrefs, optimizedScore
2497
+
2498
+ PackMultiRollWithOrder items, multiCountA, multiWidthA, maxHeight, allowRotation, multiFitA, multiOrderA, multiPrefs, multiRollsA, multiRollCountA, multiNotPlacedA
2499
+ multiScoreA = PackScore(items, multiRollsA, multiRollCountA, multiNotPlacedA, multiWidthA, multiRightA, multiAlignA, multiMaterialA)
2500
+
2501
+ PackMultiRollWithOrder items, multiCountB, multiWidthB, maxHeight, allowRotation, multiFitB, multiOrderB, multiPrefs, multiRollsB, multiRollCountB, multiNotPlacedB
2502
+ multiScoreB = PackScore(items, multiRollsB, multiRollCountB, multiNotPlacedB, multiWidthB, multiRightB, multiAlignB, multiMaterialB)
2503
+
2504
+ If multiNotPlacedA = 0 And multiNotPlacedB = 0 Then
2505
+ multiCombinedMaterial = multiMaterialA + multiMaterialB
2506
+
2507
+ If multiCombinedMaterial < bestMaterial - EPS Then
2508
+ useMultiWidth = True
2509
+ multiBestMaterial = multiCombinedMaterial
2510
+ End If
2511
+ End If
2512
+ End If
2513
+ End If
2514
+
2515
+ If useMultiWidth Then
2516
+ multiHeightA = TotalRollHeight(multiRollsA, multiRollCountA)
2517
+ multiHeightB = TotalRollHeight(multiRollsB, multiRollCountB)
2518
+
2519
+ ' Each chosen stock width receives its own accurately sized CorelDRAW page.
2520
+ Set outputPageA = doc.AddPagesEx(1, multiWidthA, multiHeightA)
2521
+ outputPageA.Name = "Nesting " & Format$(multiWidthA, "0.##") & " cm"
2522
+ ApplyNestingToPage items, sourceShapes, multiRollsA, multiRollCountA, multiWidthA, outputPageA
2523
+
2524
+ Set outputPageB = doc.AddPagesEx(1, multiWidthB, multiHeightB)
2525
+ outputPageB.Name = "Nesting " & Format$(multiWidthB, "0.##") & " cm"
2526
+ ApplyNestingToPage items, sourceShapes, multiRollsB, multiRollCountB, multiWidthB, outputPageB
2527
+
2528
+ doc.Unit = oldUnit
2529
+
2530
+ multiTrueArea = CalcTrueArea(items, multiRollsA, multiRollCountA) + CalcTrueArea(items, multiRollsB, multiRollCountB)
2531
+ If multiBestMaterial > EPS Then
2532
+ multiWaste = (multiBestMaterial - multiTrueArea) / multiBestMaterial
2533
+ Else
2534
+ multiWaste = 0#
2535
+ End If
2536
+ If bestWidth > EPS And bestRollCount > 0 And bestNotPlaced = 0 And bestMaterial < 1E+29 Then
2537
+ multiSavingPct = ((bestMaterial - multiBestMaterial) / bestMaterial) * 100#
2538
+ Else
2539
+ multiSavingPct = -1#
2540
+ End If
2541
+
2542
+ msg = "AUTO NESTING V6 - MULTI-WIDTH SELESAI" & vbCrLf & vbCrLf
2543
+ msg = msg & "Object total: " & CStr(n) & vbCrLf
2544
+ msg = msg & "Halaman output: 2 halaman, masing-masing ukuran roll berbeda" & vbCrLf & vbCrLf
2545
+ msg = msg & "Halaman " & CStr(outputPageA.Index) & ": lebar " & Format$(multiWidthA, "0.00") & " cm" & vbCrLf
2546
+ msg = msg & "Object: " & CStr(multiCountA) & " | Jumlah roll: " & CStr(multiRollCountA) & vbCrLf
2547
+ msg = msg & "Tinggi layout: " & Format$(multiHeightA, "0.00") & " cm" & vbCrLf & vbCrLf
2548
+ msg = msg & "Halaman " & CStr(outputPageB.Index) & ": lebar " & Format$(multiWidthB, "0.00") & " cm" & vbCrLf
2549
+ msg = msg & "Object: " & CStr(multiCountB) & " | Jumlah roll: " & CStr(multiRollCountB) & vbCrLf
2550
+ msg = msg & "Tinggi layout: " & Format$(multiHeightB, "0.00") & " cm" & vbCrLf & vbCrLf
2551
+ msg = msg & "Total material: " & Format$(multiBestMaterial, "0.00") & " cm2" & vbCrLf
2552
+ msg = msg & "Estimasi waste: " & Format$(multiWaste * 100#, "0.00") & "%" & vbCrLf
2553
+ If multiSavingPct >= 0# Then
2554
+ msg = msg & "Penghematan material vs 1 ukuran: " & Format$(multiSavingPct, "0.00") & "%" & vbCrLf
2555
+ Else
2556
+ msg = msg & "Pembanding 1 ukuran: tidak ada solusi yang feasible" & vbCrLf
2557
+ End If
2558
+ msg = msg & "Rotasi diizinkan: " & IIf(allowRotation, "Ya", "Tidak") & vbCrLf
2559
+ msg = msg & "Catatan: semua object dialokasikan tepat satu kali ke salah satu dari dua halaman output."
2560
+
2561
+ MsgBox msg, vbInformation, "Auto Nesting V6 - Multi-Width"
2562
+ Exit Sub
2563
+ End If
2564
+
2565
+ If bestWidth <= EPS Or bestRollCount <= 0 Or bestNotPlaced > 0 Then
2566
+ doc.Unit = oldUnit
2567
+ msg = "Tidak ditemukan konfigurasi roll yang feasible." & vbCrLf & vbCrLf
2568
+ msg = msg & "Minimum roll width: " & Format$(minNeeded, "0.00") & " cm"
2569
+ MsgBox msg, vbCritical, "Auto Nesting"
2570
+ Exit Sub
2571
+ End If
2572
+
2573
+ PackMultiRollWithOrder items, n, bestWidth, maxHeight, allowRotation, bestFitMode, bestOrder, bestPrefs, rolls, bestRollCount, bestNotPlaced
2574
+
2575
+ If bestNotPlaced > 0 Then
2576
+ doc.Unit = oldUnit
2577
+ MsgBox "Layout final menjadi tidak feasible. Object tidak akan dipindahkan.", vbCritical, "Auto Nesting V6"
2578
+ Exit Sub
2579
+ End If
2580
+
2581
+ bestMaterial = CalcMaterialUsed(rolls, bestRollCount, bestWidth)
2582
+ bestWaste = CalcWaste(items, rolls, bestRollCount, bestWidth)
2583
+ bestRightWaste = CalcRightWaste(rolls, bestRollCount, bestWidth)
2584
+ bestAlignment = CalcAlignmentScore(rolls, bestRollCount)
2585
+ finalSharedEdge = CalcSharedEdgeLength(rolls, bestRollCount)
2586
+ finalContactSegments = CalcContactSegments(rolls, bestRollCount)
2587
+ finalGuillotineEdge = CalcGuillotineEdgeLength(rolls, bestRollCount)
2588
+ finalCutPenalty = CalcCutFriendlyPenalty(rolls, bestRollCount, bestWidth)
2589
+
2590
+ ApplyNestingToObjects items, sourceShapes, rolls, bestRollCount, bestWidth
2591
+ doc.Unit = oldUnit
2592
+
2593
+ msg = "AUTO NESTING V6 SELESAI (SINGLE-WIDTH TERBAIK)" & vbCrLf & vbCrLf
2594
+ msg = msg & "Object: " & CStr(n) & vbCrLf
2595
+ msg = msg & "Roll width: " & Format$(bestWidth, "0.00") & " cm" & vbCrLf
2596
+ msg = msg & "Jumlah roll: " & CStr(bestRollCount) & vbCrLf
2597
+ msg = msg & "Tidak ditempatkan: " & CStr(bestNotPlaced) & vbCrLf
2598
+ msg = msg & "Material digunakan: " & Format$(bestMaterial, "0.00") & " cm2" & vbCrLf
2599
+ msg = msg & "Catatan: material = lebar roll x total height terpakai" & vbCrLf
2600
+ msg = msg & "Waste estimasi: " & Format$(bestWaste * 100#, "0.00") & "%" & vbCrLf
2601
+ msg = msg & "Waste kanan maksimum: " & Format$(bestRightWaste, "0.00") & " cm" & vbCrLf
2602
+
2603
+ If bestRightWaste <= MAX_RIGHT_WASTE + EPS Then
2604
+ msg = msg & "Right waste status: OK" & vbCrLf
2605
+ Else
2606
+ msg = msg & "Right waste status: > 10 cm" & vbCrLf
2607
+ End If
2608
+
2609
+ msg = msg & "Visual alignment score: " & Format$(bestAlignment, "0.00") & vbCrLf
2610
+ msg = msg & "Shared edge: " & Format$(finalSharedEdge, "0.00") & " cm" & vbCrLf
2611
+ msg = msg & "Contact segments: " & CStr(finalContactSegments) & vbCrLf
2612
+ msg = msg & "Strong guillotine edge: " & Format$(finalGuillotineEdge, "0.00") & " cm" & vbCrLf
2613
+ msg = msg & "Cut-friendly penalty: " & Format$(finalCutPenalty, "0.00") & vbCrLf
2614
+ msg = msg & "Seed sort: " & SortModeName(bestSortMode) & vbCrLf
2615
+ msg = msg & "Fitness: " & FitModeName(bestFitMode) & vbCrLf
2616
+ msg = msg & "LNS iterations: " & CStr(lnsIterationsUsed) & vbCrLf
2617
+ msg = msg & "LNS accepted moves: " & CStr(lnsImprovements) & vbCrLf
2618
+ msg = msg & "Optimization: SWAP + REINSERT + 3-OBJECT + CONTACT-POINT + GUILLOTINE + DESTROY/REPAIR + ROTATION MUTATION"
2619
+
2620
+ MsgBox msg, vbInformation, "Auto Nesting V6"
2621
+ Exit Sub
2622
+
2623
+ ErrHandler:
2624
+ errNumber = Err.Number
2625
+ errDescription = Err.Description
2626
+ On Error Resume Next
2627
+ If Not doc Is Nothing Then doc.Unit = oldUnit
2628
+ On Error GoTo 0
2629
+ MsgBox "Macro berhenti karena error." & vbCrLf & vbCrLf & "Error " & CStr(errNumber) & ": " & errDescription, vbCritical, "Auto Nesting V6"
2630
+ End Sub
2631
+
2632
+
2633
+
2634
+
2635
+