@smartnet360/svelte-components 0.0.13 → 0.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/apps/antenna-pattern/components/AntennaControls.svelte +38 -48
- package/dist/apps/antenna-pattern/components/AntennaDiagrams.svelte +138 -96
- package/dist/apps/antenna-pattern/components/chart-engines/PolarAreaChart.svelte +125 -0
- package/dist/apps/antenna-pattern/components/chart-engines/PolarAreaChart.svelte.d.ts +16 -0
- package/dist/apps/antenna-pattern/components/chart-engines/PolarBarChart.svelte +121 -0
- package/dist/apps/antenna-pattern/components/chart-engines/PolarBarChart.svelte.d.ts +15 -0
- package/dist/apps/antenna-pattern/components/chart-engines/PolarLineChart.svelte +125 -0
- package/dist/apps/antenna-pattern/components/chart-engines/PolarLineChart.svelte.d.ts +16 -0
- package/dist/apps/antenna-pattern/components/chart-engines/index.d.ts +9 -0
- package/dist/apps/antenna-pattern/components/chart-engines/index.js +8 -0
- package/dist/apps/antenna-pattern/index.d.ts +1 -0
- package/dist/apps/antenna-pattern/index.js +2 -0
- package/dist/apps/antenna-pattern/utils/chart-engines/polar-area-utils.d.ts +89 -0
- package/dist/apps/antenna-pattern/utils/chart-engines/polar-area-utils.js +146 -0
- package/dist/apps/antenna-pattern/utils/chart-engines/polar-bar-utils.d.ts +88 -0
- package/dist/apps/antenna-pattern/utils/chart-engines/polar-bar-utils.js +131 -0
- package/dist/apps/antenna-pattern/utils/chart-engines/polar-line-utils.d.ts +88 -0
- package/dist/apps/antenna-pattern/utils/chart-engines/polar-line-utils.js +134 -0
- package/dist/apps/antenna-pattern/utils/recent-antennas.d.ts +20 -0
- package/dist/apps/antenna-pattern/utils/recent-antennas.js +60 -0
- package/package.json +1 -1
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
<script lang="ts">
|
4
4
|
import type { Antenna } from '../db';
|
5
|
+
import { sortAntennasWithRecent, addToRecentAntennas } from '../utils/recent-antennas';
|
5
6
|
|
6
7
|
interface Props {
|
7
8
|
antennas?: Antenna[];
|
@@ -47,9 +48,7 @@
|
|
47
48
|
$effect(() => {
|
48
49
|
// Only update internal state if the prop actually changed
|
49
50
|
// Avoid overwriting user selections
|
50
|
-
console.log(`[Antenna ${antennaNumber}] Effect triggered - selectedAntenna prop:`, selectedAntenna?.name || 'null', 'internal:', internalSelectedAntenna?.name || 'null');
|
51
51
|
if (selectedAntenna !== internalSelectedAntenna) {
|
52
|
-
console.log(`[Antenna ${antennaNumber}] Effect updating internal state from:`, internalSelectedAntenna?.name || 'null', 'to:', selectedAntenna?.name || 'null');
|
53
52
|
internalSelectedAntenna = selectedAntenna;
|
54
53
|
}
|
55
54
|
});
|
@@ -88,33 +87,17 @@
|
|
88
87
|
|
89
88
|
// Find the specific antenna that matches name, frequency, and electrical tilt
|
90
89
|
function findAntennaWithTilt(antennaName: string, tiltIndex: number): Antenna | null {
|
91
|
-
|
92
|
-
|
93
|
-
if (!antennaName) {
|
94
|
-
console.log(`[Antenna ${antennaNumber}] No antennaName provided, returning null`);
|
95
|
-
return null;
|
96
|
-
}
|
90
|
+
if (!antennaName) return null;
|
97
91
|
|
98
92
|
// Handle case where availableElectricalTilts might not be populated yet
|
99
93
|
const targetTilt = availableElectricalTilts[tiltIndex] || '0';
|
100
|
-
console.log(`[Antenna ${antennaNumber}] availableElectricalTilts:`, availableElectricalTilts);
|
101
|
-
console.log(`[Antenna ${antennaNumber}] targetTilt:`, targetTilt);
|
102
|
-
console.log(`[Antenna ${antennaNumber}] selectedFrequency:`, selectedFrequency);
|
103
94
|
|
104
95
|
// Find antenna with matching name, frequency, and electrical tilt
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
if (antenna.name !== antennaName) {
|
109
|
-
console.log(`[Antenna ${antennaNumber}] Name mismatch:`, antenna.name, '!==', antennaName);
|
110
|
-
return false;
|
111
|
-
}
|
96
|
+
return antennas.find(antenna => {
|
97
|
+
if (antenna.name !== antennaName) return false;
|
112
98
|
|
113
99
|
// If frequency is selected, must match
|
114
|
-
if (selectedFrequency && antenna.frequency !== selectedFrequency)
|
115
|
-
console.log(`[Antenna ${antennaNumber}] Frequency mismatch:`, antenna.frequency, '!==', selectedFrequency);
|
116
|
-
return false;
|
117
|
-
}
|
100
|
+
if (selectedFrequency && antenna.frequency !== selectedFrequency) return false;
|
118
101
|
|
119
102
|
if (antenna.tilt) {
|
120
103
|
const tiltString = antenna.tilt.toString();
|
@@ -122,19 +105,11 @@
|
|
122
105
|
? tiltString.split(',').map(t => t.trim())
|
123
106
|
: [tiltString];
|
124
107
|
|
125
|
-
|
126
|
-
const tiltMatch = antennasTilts.includes(targetTilt);
|
127
|
-
console.log(`[Antenna ${antennaNumber}] Tilt match:`, tiltMatch);
|
128
|
-
return tiltMatch;
|
108
|
+
return antennasTilts.includes(targetTilt);
|
129
109
|
}
|
130
110
|
|
131
|
-
|
132
|
-
console.log(`[Antenna ${antennaNumber}] Default tilt match:`, defaultMatch);
|
133
|
-
return defaultMatch; // Default tilt
|
111
|
+
return targetTilt === '0'; // Default tilt
|
134
112
|
}) || null;
|
135
|
-
|
136
|
-
console.log(`[Antenna ${antennaNumber}] findAntennaWithTilt result:`, result?.name || 'null');
|
137
|
-
return result;
|
138
113
|
}
|
139
114
|
|
140
115
|
// Handle frequency selection
|
@@ -160,9 +135,6 @@
|
|
160
135
|
const target = event.target as HTMLSelectElement;
|
161
136
|
const antennaName = target.value;
|
162
137
|
|
163
|
-
console.log(`[Antenna ${antennaNumber}] handleAntennaChange called with:`, antennaName);
|
164
|
-
console.log(`[Antenna ${antennaNumber}] Before change - internalSelectedAntenna:`, internalSelectedAntenna?.name || 'null');
|
165
|
-
|
166
138
|
// Reset tilt and frequency values when antenna changes
|
167
139
|
internalElectricalTiltIndex = 0;
|
168
140
|
internalMechanicalTilt = 0;
|
@@ -170,12 +142,10 @@
|
|
170
142
|
|
171
143
|
// Handle "Select Antenna" case (empty string)
|
172
144
|
if (!antennaName) {
|
173
|
-
console.log(`[Antenna ${antennaNumber}] Setting to null (Select Antenna case)`);
|
174
145
|
internalSelectedAntenna = null;
|
175
146
|
onAntennaChange?.(null);
|
176
147
|
onElectricalTiltChange?.(0);
|
177
148
|
onMechanicalTiltChange?.(0);
|
178
|
-
console.log(`[Antenna ${antennaNumber}] After null set - internalSelectedAntenna:`, internalSelectedAntenna?.name || 'null');
|
179
149
|
return;
|
180
150
|
}
|
181
151
|
|
@@ -191,9 +161,6 @@
|
|
191
161
|
let tiltToUse = tilts.includes('0') ? '0' : tilts[0];
|
192
162
|
let tiltIndexToUse = tilts.includes('0') ? tilts.indexOf('0') : 0;
|
193
163
|
|
194
|
-
console.log(`[Antenna ${antennaNumber}] Available tilts for ${antennaName}:`, tilts);
|
195
|
-
console.log(`[Antenna ${antennaNumber}] Using tilt:`, tiltToUse, 'at index:', tiltIndexToUse);
|
196
|
-
|
197
164
|
// Find the specific antenna with the chosen tilt
|
198
165
|
const newAntenna = antennas.find(antenna => {
|
199
166
|
if (antenna.name !== antennaName) return false;
|
@@ -207,21 +174,23 @@
|
|
207
174
|
return tiltToUse === '0';
|
208
175
|
});
|
209
176
|
|
210
|
-
console.log(`[Antenna ${antennaNumber}] Found antenna with tilt ${tiltToUse}:`, newAntenna?.name || 'null');
|
211
177
|
internalSelectedAntenna = newAntenna || null;
|
212
178
|
internalElectricalTiltIndex = tiltIndexToUse;
|
213
179
|
|
214
180
|
} else {
|
215
181
|
// Fallback: use findAntennaWithTilt with index 0
|
216
182
|
const newAntenna = findAntennaWithTilt(antennaName, 0);
|
217
|
-
console.log(`[Antenna ${antennaNumber}] Fallback - Found antenna:`, newAntenna?.name || 'null');
|
218
183
|
internalSelectedAntenna = newAntenna;
|
219
184
|
}
|
220
185
|
|
221
186
|
onAntennaChange?.(internalSelectedAntenna);
|
222
187
|
onElectricalTiltChange?.(internalElectricalTiltIndex);
|
223
188
|
onMechanicalTiltChange?.(0);
|
224
|
-
|
189
|
+
|
190
|
+
// Track antenna selection in recent list
|
191
|
+
if (internalSelectedAntenna) {
|
192
|
+
addToRecentAntennas(internalSelectedAntenna);
|
193
|
+
}
|
225
194
|
}
|
226
195
|
|
227
196
|
// Handle electrical tilt changes
|
@@ -300,6 +269,9 @@
|
|
300
269
|
|
301
270
|
// Get unique antennas (by name, not by individual DB entries with different tilts)
|
302
271
|
let uniqueAntennas = $state<Antenna[]>([]);
|
272
|
+
let recentAntennas = $state<Antenna[]>([]);
|
273
|
+
let otherAntennas = $state<Antenna[]>([]);
|
274
|
+
|
303
275
|
$effect(() => {
|
304
276
|
if (antennas.length > 0) {
|
305
277
|
// Group antennas by name and take the first one from each group
|
@@ -309,7 +281,13 @@
|
|
309
281
|
antennaMap.set(antenna.name, antenna);
|
310
282
|
}
|
311
283
|
});
|
312
|
-
|
284
|
+
const allUniqueAntennas = Array.from(antennaMap.values()).sort((a, b) => a.name.localeCompare(b.name));
|
285
|
+
|
286
|
+
// Sort with recent antennas first
|
287
|
+
const { recent, others } = sortAntennasWithRecent(allUniqueAntennas);
|
288
|
+
recentAntennas = recent;
|
289
|
+
otherAntennas = others;
|
290
|
+
uniqueAntennas = [...recent, ...others];
|
313
291
|
}
|
314
292
|
});
|
315
293
|
</script>
|
@@ -330,12 +308,24 @@
|
|
330
308
|
class="form-select form-select-sm"
|
331
309
|
value={internalSelectedAntenna?.name || ''}
|
332
310
|
onchange={handleAntennaChange}
|
333
|
-
onfocus={() => console.log(`[Antenna ${antennaNumber}] Dropdown focused - current value:`, internalSelectedAntenna?.name || 'null')}
|
334
311
|
>
|
335
312
|
<option value="">-- Select Antenna --</option>
|
336
|
-
|
337
|
-
|
338
|
-
{
|
313
|
+
|
314
|
+
<!-- Recent Antennas -->
|
315
|
+
{#if recentAntennas.length > 0}
|
316
|
+
<optgroup label="Recent">
|
317
|
+
{#each recentAntennas as antenna}
|
318
|
+
<option value={antenna.name}>▸ {antenna.name}</option>
|
319
|
+
{/each}
|
320
|
+
</optgroup>
|
321
|
+
{/if}
|
322
|
+
|
323
|
+
<!-- All Other Antennas -->
|
324
|
+
<optgroup label="All Antennas">
|
325
|
+
{#each otherAntennas as antenna}
|
326
|
+
<option value={antenna.name}>{antenna.name}</option>
|
327
|
+
{/each}
|
328
|
+
</optgroup>
|
339
329
|
</select>
|
340
330
|
{#if internalSelectedAntenna}
|
341
331
|
<small class="text-muted mt-1 d-block">
|
@@ -2,25 +2,18 @@
|
|
2
2
|
|
3
3
|
<script lang="ts">
|
4
4
|
import { onMount } from 'svelte';
|
5
|
-
import { browser } from '$app/environment';
|
6
5
|
import { loadAntennas } from '../utils/db-utils';
|
7
6
|
import type { Antenna } from '../db';
|
8
7
|
import AntennaControls from './AntennaControls.svelte';
|
9
8
|
import AntennaSettingsModal from './AntennaSettingsModal.svelte';
|
10
|
-
import {
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
type PlotlyTraceData
|
16
|
-
} from '../utils/plotly-chart-utils';
|
17
|
-
|
18
|
-
let Plotly: any;
|
19
|
-
let chartDiv = $state<HTMLDivElement>();
|
9
|
+
import { PolarLineChart, PolarBarChart, PolarAreaChart } from './chart-engines/index';
|
10
|
+
|
11
|
+
// Chart engine types
|
12
|
+
type ChartEngineType = 'polar-line' | 'polar-bar' | 'polar-area';
|
13
|
+
|
20
14
|
let antennas = $state<Antenna[]>([]);
|
21
15
|
let selectedAntenna = $state<Antenna | null>(null);
|
22
16
|
let selectedAntenna2 = $state<Antenna | null>(null);
|
23
|
-
let chartInitialized = $state(false);
|
24
17
|
|
25
18
|
// External Bootstrap slider values
|
26
19
|
let ant1ElectricalTilt = $state(0);
|
@@ -31,6 +24,12 @@
|
|
31
24
|
// Viewing mode and pattern visibility
|
32
25
|
let viewMode = $state<'single' | 'compare'>('single');
|
33
26
|
let patternType = $state<'horizontal' | 'vertical'>('vertical');
|
27
|
+
|
28
|
+
// Chart engine selection
|
29
|
+
let chartEngine = $state<ChartEngineType>('polar-line');
|
30
|
+
|
31
|
+
// Pattern display mode - normalized vs gain-adjusted
|
32
|
+
let patternDisplayMode = $state<'normalized' | 'gain-adjusted'>('normalized');
|
34
33
|
|
35
34
|
// Settings modal state
|
36
35
|
let showSettingsModal = $state(false);
|
@@ -118,89 +117,38 @@
|
|
118
117
|
}
|
119
118
|
}
|
120
119
|
|
121
|
-
// Watch for slider changes and update chart
|
122
|
-
$effect(() => {
|
123
|
-
if (chartInitialized) {
|
124
|
-
updateChart();
|
125
|
-
}
|
126
|
-
});
|
127
|
-
|
128
120
|
onMount(async () => {
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
Plotly = await import('plotly.js-dist-min');
|
133
|
-
antennas = await loadAntennas();
|
121
|
+
try {
|
122
|
+
// Load antenna data
|
123
|
+
antennas = await loadAntennas();
|
134
124
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
}
|
148
|
-
|
149
|
-
await initializeChart();
|
125
|
+
if (antennas.length > 0) {
|
126
|
+
// Set default antenna selection for antenna 1
|
127
|
+
selectedAntenna = antennas[0];
|
128
|
+
if (selectedAntenna.tilt) {
|
129
|
+
availableElectricalTilts = selectedAntenna.tilt.split(',').map(t => t.trim());
|
130
|
+
}
|
131
|
+
|
132
|
+
// Set default antenna selection for antenna 2 (for compare mode)
|
133
|
+
if (antennas.length > 1) {
|
134
|
+
selectedAntenna2 = antennas[1];
|
135
|
+
} else {
|
136
|
+
selectedAntenna2 = antennas[0]; // Use same antenna if only one available
|
150
137
|
}
|
151
|
-
} catch (error) {
|
152
|
-
console.error('Failed to initialize:', error);
|
153
138
|
}
|
154
|
-
}
|
155
|
-
});
|
156
|
-
|
157
|
-
async function initializeChart() {
|
158
|
-
if (!Plotly || !chartDiv) return;
|
159
|
-
|
160
|
-
const data = createAntennaChartData(
|
161
|
-
selectedAntenna,
|
162
|
-
selectedAntenna2,
|
163
|
-
viewMode,
|
164
|
-
patternType === 'horizontal',
|
165
|
-
patternType === 'vertical',
|
166
|
-
ant1ElectricalTilt,
|
167
|
-
ant1MechanicalTilt,
|
168
|
-
ant2ElectricalTilt,
|
169
|
-
ant2MechanicalTilt
|
170
|
-
);
|
171
|
-
const title = generateChartTitle(viewMode, selectedAntenna, selectedAntenna2);
|
172
|
-
const layout = createChartLayout(title);
|
173
|
-
const config = createChartConfig();
|
174
|
-
|
175
|
-
try {
|
176
|
-
await Plotly.newPlot(chartDiv, data, layout, config);
|
177
|
-
chartInitialized = true;
|
178
|
-
console.log('Chart initialized with antenna controls');
|
179
139
|
} catch (error) {
|
180
|
-
console.error('Failed to
|
140
|
+
console.error('Failed to initialize:', error);
|
181
141
|
}
|
182
|
-
}
|
183
|
-
|
184
|
-
async function updateChart() {
|
185
|
-
if (!Plotly || !chartDiv || !chartInitialized) return;
|
142
|
+
});
|
186
143
|
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
patternType === 'vertical',
|
194
|
-
ant1ElectricalTilt,
|
195
|
-
ant1MechanicalTilt,
|
196
|
-
ant2ElectricalTilt,
|
197
|
-
ant2MechanicalTilt
|
198
|
-
);
|
199
|
-
const title = generateChartTitle(viewMode, selectedAntenna, selectedAntenna2);
|
200
|
-
await Plotly.react(chartDiv, data, createChartLayout(title));
|
201
|
-
} catch (error) {
|
202
|
-
console.error('Failed to update chart:', error);
|
144
|
+
// Generate chart title based on current selection
|
145
|
+
function generateChartTitle(): string {
|
146
|
+
if (viewMode === 'compare' && selectedAntenna && selectedAntenna2) {
|
147
|
+
return `${selectedAntenna.name} vs ${selectedAntenna2.name}`;
|
148
|
+
} else if (selectedAntenna) {
|
149
|
+
return `${selectedAntenna.name} - Pattern Analysis`;
|
203
150
|
}
|
151
|
+
return 'Antenna Pattern Analysis';
|
204
152
|
}
|
205
153
|
</script>
|
206
154
|
|
@@ -208,10 +156,10 @@
|
|
208
156
|
|
209
157
|
|
210
158
|
<!-- Viewing Mode and Pattern Visibility Controls -->
|
211
|
-
<div class="row mb-
|
212
|
-
<div class="col-md-
|
213
|
-
<div class="form-label">
|
214
|
-
<div class="btn-group w-100" role="group" aria-label="
|
159
|
+
<div class="row mb-3">
|
160
|
+
<div class="col-md-2">
|
161
|
+
<div class="form-label">View Mode:</div>
|
162
|
+
<div class="btn-group w-100" role="group" aria-label="View mode selection">
|
215
163
|
<input
|
216
164
|
type="radio"
|
217
165
|
class="btn-check"
|
@@ -219,7 +167,7 @@
|
|
219
167
|
bind:group={viewMode}
|
220
168
|
value="single"
|
221
169
|
>
|
222
|
-
<label class="btn btn-outline-primary" for="singleMode">Single
|
170
|
+
<label class="btn btn-outline-primary" for="singleMode">Single</label>
|
223
171
|
|
224
172
|
<input
|
225
173
|
type="radio"
|
@@ -232,7 +180,62 @@
|
|
232
180
|
</div>
|
233
181
|
</div>
|
234
182
|
|
235
|
-
<div class="col-md-
|
183
|
+
<div class="col-md-2">
|
184
|
+
<div class="form-label">Chart Type:</div>
|
185
|
+
<div class="btn-group w-100" role="group" aria-label="Chart type selection">
|
186
|
+
<input
|
187
|
+
type="radio"
|
188
|
+
class="btn-check"
|
189
|
+
id="polarLineChart"
|
190
|
+
bind:group={chartEngine}
|
191
|
+
value="polar-line"
|
192
|
+
>
|
193
|
+
<label class="btn btn-outline-primary" for="polarLineChart">Line</label>
|
194
|
+
|
195
|
+
<!-- <input
|
196
|
+
type="radio"
|
197
|
+
class="btn-check"
|
198
|
+
id="polarBarChart"
|
199
|
+
bind:group={chartEngine}
|
200
|
+
value="polar-bar"
|
201
|
+
>
|
202
|
+
<label class="btn btn-outline-success" for="polarBarChart">Bar</label> -->
|
203
|
+
|
204
|
+
<input
|
205
|
+
type="radio"
|
206
|
+
class="btn-check"
|
207
|
+
id="polarAreaChart"
|
208
|
+
bind:group={chartEngine}
|
209
|
+
value="polar-area"
|
210
|
+
>
|
211
|
+
<label class="btn btn-outline-primary" for="polarAreaChart">Area</label>
|
212
|
+
</div>
|
213
|
+
</div>
|
214
|
+
|
215
|
+
<div class="col-md-3">
|
216
|
+
<div class="form-label">Display Mode:</div>
|
217
|
+
<div class="btn-group w-100" role="group" aria-label="Pattern display mode selection">
|
218
|
+
<input
|
219
|
+
type="radio"
|
220
|
+
class="btn-check"
|
221
|
+
id="normalizedMode"
|
222
|
+
bind:group={patternDisplayMode}
|
223
|
+
value="normalized"
|
224
|
+
>
|
225
|
+
<label class="btn btn-outline-primary" for="normalizedMode">Pattern</label>
|
226
|
+
|
227
|
+
<input
|
228
|
+
type="radio"
|
229
|
+
class="btn-check"
|
230
|
+
id="gainAdjustedMode"
|
231
|
+
bind:group={patternDisplayMode}
|
232
|
+
value="gain-adjusted"
|
233
|
+
>
|
234
|
+
<label class="btn btn-outline-primary" for="gainAdjustedMode">+Gain</label>
|
235
|
+
</div>
|
236
|
+
</div>
|
237
|
+
|
238
|
+
<div class="col-md-2">
|
236
239
|
<div class="form-label">Pattern Type:</div>
|
237
240
|
<div class="btn-group w-100" role="group" aria-label="Pattern type selection">
|
238
241
|
<input
|
@@ -255,7 +258,7 @@
|
|
255
258
|
</div>
|
256
259
|
</div>
|
257
260
|
|
258
|
-
<div class="col-md-
|
261
|
+
<div class="col-md-3">
|
259
262
|
<div class="form-label">Actions:</div>
|
260
263
|
<button
|
261
264
|
type="button"
|
@@ -284,11 +287,50 @@
|
|
284
287
|
/>
|
285
288
|
</div>
|
286
289
|
|
287
|
-
<!-- Center Column - Chart (always centered) -->
|
290
|
+
<!-- Center Column - Chart Engine (always centered) -->
|
288
291
|
<div class="col-md-6">
|
289
292
|
<div class="card">
|
290
293
|
<div class="card-body p-1">
|
291
|
-
|
294
|
+
{#if chartEngine === 'polar-line'}
|
295
|
+
<PolarLineChart
|
296
|
+
{selectedAntenna}
|
297
|
+
{selectedAntenna2}
|
298
|
+
{viewMode}
|
299
|
+
{patternType}
|
300
|
+
{patternDisplayMode}
|
301
|
+
{ant1ElectricalTilt}
|
302
|
+
{ant1MechanicalTilt}
|
303
|
+
{ant2ElectricalTilt}
|
304
|
+
{ant2MechanicalTilt}
|
305
|
+
title={generateChartTitle()}
|
306
|
+
/>
|
307
|
+
{:else if chartEngine === 'polar-bar'}
|
308
|
+
<PolarBarChart
|
309
|
+
{selectedAntenna}
|
310
|
+
{selectedAntenna2}
|
311
|
+
{viewMode}
|
312
|
+
{patternType}
|
313
|
+
{patternDisplayMode}
|
314
|
+
{ant1ElectricalTilt}
|
315
|
+
{ant1MechanicalTilt}
|
316
|
+
{ant2ElectricalTilt}
|
317
|
+
{ant2MechanicalTilt}
|
318
|
+
title={generateChartTitle()}
|
319
|
+
/>
|
320
|
+
{:else if chartEngine === 'polar-area'}
|
321
|
+
<PolarAreaChart
|
322
|
+
{selectedAntenna}
|
323
|
+
{selectedAntenna2}
|
324
|
+
{viewMode}
|
325
|
+
{patternType}
|
326
|
+
{patternDisplayMode}
|
327
|
+
{ant1ElectricalTilt}
|
328
|
+
{ant1MechanicalTilt}
|
329
|
+
{ant2ElectricalTilt}
|
330
|
+
{ant2MechanicalTilt}
|
331
|
+
title={generateChartTitle()}
|
332
|
+
/>
|
333
|
+
{/if}
|
292
334
|
</div>
|
293
335
|
</div>
|
294
336
|
</div>
|
@@ -0,0 +1,125 @@
|
|
1
|
+
<svelte:options runes={true} />
|
2
|
+
|
3
|
+
<script lang="ts">
|
4
|
+
import { onMount } from 'svelte';
|
5
|
+
import { browser } from '$app/environment';
|
6
|
+
import type { Antenna } from '../../db';
|
7
|
+
import {
|
8
|
+
createPolarAreaChartData,
|
9
|
+
createPolarAreaLayout,
|
10
|
+
type PolarAreaTraceData
|
11
|
+
} from '../../utils/chart-engines/polar-area-utils';
|
12
|
+
|
13
|
+
interface Props {
|
14
|
+
selectedAntenna?: Antenna | null;
|
15
|
+
selectedAntenna2?: Antenna | null;
|
16
|
+
viewMode?: 'single' | 'compare';
|
17
|
+
patternType?: 'horizontal' | 'vertical';
|
18
|
+
patternDisplayMode?: 'normalized' | 'gain-adjusted';
|
19
|
+
ant1ElectricalTilt?: number;
|
20
|
+
ant1MechanicalTilt?: number;
|
21
|
+
ant2ElectricalTilt?: number;
|
22
|
+
ant2MechanicalTilt?: number;
|
23
|
+
title?: string;
|
24
|
+
}
|
25
|
+
|
26
|
+
let {
|
27
|
+
selectedAntenna = null,
|
28
|
+
selectedAntenna2 = null,
|
29
|
+
viewMode = 'single',
|
30
|
+
patternType = 'vertical',
|
31
|
+
patternDisplayMode = 'normalized',
|
32
|
+
ant1ElectricalTilt = 0,
|
33
|
+
ant1MechanicalTilt = 0,
|
34
|
+
ant2ElectricalTilt = 0,
|
35
|
+
ant2MechanicalTilt = 0,
|
36
|
+
title = 'Antenna Pattern - Polar Area Chart'
|
37
|
+
}: Props = $props();
|
38
|
+
|
39
|
+
let Plotly: any;
|
40
|
+
let chartDiv = $state<HTMLDivElement>();
|
41
|
+
let chartInitialized = $state(false);
|
42
|
+
|
43
|
+
// Initialize Plotly on mount
|
44
|
+
onMount(async () => {
|
45
|
+
if (browser) {
|
46
|
+
try {
|
47
|
+
Plotly = await import('plotly.js-dist-min');
|
48
|
+
console.log('Plotly loaded for polar area chart');
|
49
|
+
await initializeChart();
|
50
|
+
} catch (error) {
|
51
|
+
console.error('Failed to load Plotly:', error);
|
52
|
+
}
|
53
|
+
}
|
54
|
+
});
|
55
|
+
|
56
|
+
// Update chart when data changes
|
57
|
+
$effect(() => {
|
58
|
+
if (chartInitialized) {
|
59
|
+
updateChart();
|
60
|
+
}
|
61
|
+
});
|
62
|
+
|
63
|
+
async function initializeChart() {
|
64
|
+
if (!Plotly || !chartDiv) return;
|
65
|
+
|
66
|
+
const data = createPolarAreaChartData(
|
67
|
+
selectedAntenna,
|
68
|
+
selectedAntenna2,
|
69
|
+
viewMode,
|
70
|
+
patternType === 'horizontal',
|
71
|
+
patternType === 'vertical',
|
72
|
+
ant1ElectricalTilt,
|
73
|
+
ant1MechanicalTilt,
|
74
|
+
ant2ElectricalTilt,
|
75
|
+
ant2MechanicalTilt,
|
76
|
+
patternDisplayMode
|
77
|
+
);
|
78
|
+
|
79
|
+
const layout = createPolarAreaLayout(title);
|
80
|
+
const config = {
|
81
|
+
responsive: true,
|
82
|
+
displayModeBar: true,
|
83
|
+
displaylogo: false,
|
84
|
+
modeBarButtonsToRemove: [
|
85
|
+
'zoom2d', 'pan2d', 'select2d', 'lasso2d',
|
86
|
+
'zoomIn2d', 'zoomOut2d', 'autoScale2d',
|
87
|
+
'resetScale2d', 'toggleSpikelines', 'toImage'
|
88
|
+
]
|
89
|
+
};
|
90
|
+
|
91
|
+
try {
|
92
|
+
await Plotly.newPlot(chartDiv, data, layout, config);
|
93
|
+
chartInitialized = true;
|
94
|
+
console.log('Polar area chart initialized');
|
95
|
+
} catch (error) {
|
96
|
+
console.error('Failed to create polar area chart:', error);
|
97
|
+
}
|
98
|
+
}
|
99
|
+
|
100
|
+
async function updateChart() {
|
101
|
+
if (!Plotly || !chartDiv || !chartInitialized) return;
|
102
|
+
|
103
|
+
try {
|
104
|
+
const data = createPolarAreaChartData(
|
105
|
+
selectedAntenna,
|
106
|
+
selectedAntenna2,
|
107
|
+
viewMode,
|
108
|
+
patternType === 'horizontal',
|
109
|
+
patternType === 'vertical',
|
110
|
+
ant1ElectricalTilt,
|
111
|
+
ant1MechanicalTilt,
|
112
|
+
ant2ElectricalTilt,
|
113
|
+
ant2MechanicalTilt,
|
114
|
+
patternDisplayMode
|
115
|
+
);
|
116
|
+
|
117
|
+
const layout = createPolarAreaLayout(title);
|
118
|
+
await Plotly.react(chartDiv, data, layout);
|
119
|
+
} catch (error) {
|
120
|
+
console.error('Failed to update polar area chart:', error);
|
121
|
+
}
|
122
|
+
}
|
123
|
+
</script>
|
124
|
+
|
125
|
+
<div bind:this={chartDiv} style="height: 700px;"></div>
|
@@ -0,0 +1,16 @@
|
|
1
|
+
import type { Antenna } from '../../db';
|
2
|
+
interface Props {
|
3
|
+
selectedAntenna?: Antenna | null;
|
4
|
+
selectedAntenna2?: Antenna | null;
|
5
|
+
viewMode?: 'single' | 'compare';
|
6
|
+
patternType?: 'horizontal' | 'vertical';
|
7
|
+
patternDisplayMode?: 'normalized' | 'gain-adjusted';
|
8
|
+
ant1ElectricalTilt?: number;
|
9
|
+
ant1MechanicalTilt?: number;
|
10
|
+
ant2ElectricalTilt?: number;
|
11
|
+
ant2MechanicalTilt?: number;
|
12
|
+
title?: string;
|
13
|
+
}
|
14
|
+
declare const PolarAreaChart: import("svelte").Component<Props, {}, "">;
|
15
|
+
type PolarAreaChart = ReturnType<typeof PolarAreaChart>;
|
16
|
+
export default PolarAreaChart;
|