PageSourceSearch

https://www.saint-malo-tourisme.com/app/dist/saint-malo-tourisme/a…/code/es-modules/Extensions/BorderRadius.js

js saint-malo-tourisme.com collected 2026-10-02 10:38:38 UTC 13,969 bytes, 380 lines download raw bytes

vendor: 13,969 bytes, lines 1-380
1/* *
2 *
3 *  Highcharts Border Radius module
4 *
5 *  Author: Torstein Honsi
6 *
7 *  License: www.highcharts.com/license
8 *
9 *  !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
10 *
11 * */
12'use strict';
13import D from '../Core/Defaults.js';
14const { defaultOptions } = D;
15import H from '../Core/Globals.js';
16const { noop } = H;
17import U from '../Core/Utilities.js';
18const { addEvent, extend, isObject, merge, relativeLength } = U;
19/* *
20 *
21 *  Constants
22 *
23 * */
24const defaultBorderRadiusOptions = {
25    radius: 0,
26    scope: 'stack',
27    where: void 0
28};
29/* *
30 *
31 *  Variables
32 *
33 * */
34let oldArc = noop;
35let oldRoundedRect = noop;
36/* *
37 *
38 *  Functions
39 *
40 * */
41/**
42 * @private
43 */
44function applyBorderRadius(path, i, r) {
45    const a = path[i];
46    let b = path[i + 1];
47    if (b[0] === 'Z') {
48        b = path[0];
49    }
50    let line, arc, fromLineToArc;
51    // From straight line to arc
52    if ((a[0] === 'M' || a[0] === 'L') && b[0] === 'A') {
53        line = a;
54        arc = b;
55        fromLineToArc = true;
56        // From arc to straight line
57    }
58    else if (a[0] === 'A' && (b[0] === 'M' || b[0] === 'L')) {
59        line = b;
60        arc = a;
61    }
62    if (line && arc && arc.params) {
63        const bigR = arc[1], 
64        // In our use cases, outer pie slice arcs are clockwise and inner
65        // arcs (donut/sunburst etc) are anti-clockwise
66        clockwise = arc[5], params = arc.params, { start, end, cx, cy } = params;
67        // Some geometric constants
68        const relativeR = clockwise ? (bigR - r) : (bigR + r), 
69        // The angle, on the big arc, that the border radius arc takes up
70        angleOfBorderRadius = relativeR ? Math.asin(r / relativeR) : 0, angleOffset = clockwise ?
71            angleOfBorderRadius :
72            -angleOfBorderRadius, 
73        // The distance along the radius of the big arc to the starting
74        // point of the small border radius arc
75        distanceBigCenterToStartArc = (Math.cos(angleOfBorderRadius) *
76            relativeR);
77        // From line to arc
78        if (fromLineToArc) {
79            // Update the cache
80            params.start = start + angleOffset;
81            // First move to the start position at the radial line. We want to
82            // start one borderRadius closer to the center.
83            line[1] = cx + distanceBigCenterToStartArc * Math.cos(start);
84            line[2] = cy + distanceBigCenterToStartArc * Math.sin(start);
85            // Now draw an arc towards the point where the small circle touches
86            // the great circle.
87            path.splice(i + 1, 0, [
88                'A',
89                r,
90                r,
91                0, // Slanting,
92                0, // Long arc
93                1, // Clockwise
94                cx + bigR * Math.cos(params.start),
95                cy + bigR * Math.sin(params.start)
96            ]);
97            // From arc to line
98        }
99        else {
100            // Update the cache
101            params.end = end - angleOffset;
102            // End the big arc a bit earlier
103            arc[6] = cx + bigR * Math.cos(params.end);
104            arc[7] = cy + bigR * Math.sin(params.end);
105            // Draw a small arc towards a point on the end angle, but one
106            // borderRadius closer to the center relative to the perimeter.
107            path.splice(i + 1, 0, [
108                'A',
109                r,
110                r,
111                0,
112                0,
113                1,
114                cx + distanceBigCenterToStartArc * Math.cos(end),
115                cy + distanceBigCenterToStartArc * Math.sin(end)
116            ]);
117        }
118        // Long or short arc must be reconsidered because we have modified the
119        // start and end points
120        arc[4] = Math.abs(params.end - params.start) < Math.PI ? 0 : 1;
121    }
122}
123/**
124 * Extend arc with borderRadius.
125 * @private
126 */
127function arc(x, y, w, h, options = {}) {
128    const path = oldArc(x, y, w, h, options), { innerR = 0, r = w, start = 0, end = 0 } = options;
129    if (options.open || !options.borderRadius) {
130        return path;
131    }
132    const alpha = end - start, sinHalfAlpha = Math.sin(alpha / 2), borderRadius = Math.max(Math.min(relativeLength(options.borderRadius || 0, r - innerR), 
133    // Cap to half the sector radius
134    (r - innerR) / 2, 
135    // For smaller pie slices, cap to the largest small circle that
136    // can be fitted within the sector
137    (r * sinHalfAlpha) / (1 + sinHalfAlpha)), 0), 
138    // For the inner radius, we need an extra cap because the inner arc
139    // is shorter than the outer arc
140    innerBorderRadius = Math.min(borderRadius, 2 * (alpha / Math.PI) * innerR);
141    // Apply turn-by-turn border radius. Start at the end since we're
142    // splicing in arc segments.
143    let i = path.length - 1;
144    while (i--) {
145        applyBorderRadius(path, i, i > 1 ? innerBorderRadius : borderRadius);
146    }
147    return path;
148}
149/** @private */
150function seriesOnAfterColumnTranslate() {
151    if (this.options.borderRadius &&
152        !(this.chart.is3d && this.chart.is3d())) {
153        const { options, yAxis } = this, percent = options.stacking === 'percent', seriesDefault = defaultOptions.plotOptions?.[this.type]
154            ?.borderRadius, borderRadius = optionsToObject(options.borderRadius, isObject(seriesDefault) ? seriesDefault : {}), reversed = yAxis.options.reversed;
155        for (const point of this.points) {
156            const { shapeArgs } = point;
157            if (point.shapeType === 'roundedRect' && shapeArgs) {
158                const { width = 0, height = 0, y = 0 } = shapeArgs;
159                let brBoxY = y, brBoxHeight = height;
160                // It would be nice to refactor StackItem.getStackBox/
161                // setOffset so that we could get a reliable box out of
162                // it. Currently it is close if we remove the label
163                // offset, but we still need to run crispCol and also
164                // flip it if inverted, so atm it is simpler to do it
165                // like the below.
166                if (borderRadius.scope === 'stack' &&
167                    point.stackTotal) {
168                    const stackEnd = yAxis.translate(percent ? 100 : point.stackTotal, false, true, false, true), stackThreshold = yAxis.translate(options.threshold || 0, false, true, false, true), box = this.crispCol(0, Math.min(stackEnd, stackThreshold), 0, Math.abs(stackEnd - stackThreshold));
169                    brBoxY = box.y;
170                    brBoxHeight = box.height;
171                }
172                const flip = (point.negative ? -1 : 1) *
173                    (reversed ? -1 : 1) === -1;
174                // Handle the where option
175                let where = borderRadius.where;
176                // Waterfall, hanging columns should have rounding on
177                // all sides
178                if (!where &&
179                    this.is('waterfall') &&
180                    Math.abs((point.yBottom || 0) -
181                        (this.translatedThreshold || 0)) > this.borderWidth) {
182                    where = 'all';
183                }
184                if (!where) {
185                    where = 'end';
186                }
187                // Get the radius
188                const r = Math.min(relativeLength(borderRadius.radius, width), width / 2, 
189                // Cap to the height, but not if where is `end`
190                where === 'all' ? height / 2 : Infinity) || 0;
191                // If the `where` option is 'end', cut off the
192                // rectangles by making the border-radius box one r
193                // greater, so that the imaginary radius falls outside
194                // the rectangle.
195                if (where === 'end') {
196                    if (flip) {
197                        brBoxY -= r;
198                        brBoxHeight += r;
199                    }
200                    else {
201                        brBoxHeight += r;
202                    }
203                }
204                extend(shapeArgs, { brBoxHeight, brBoxY, r });
205            }
206        }
207    }
208}
209/** @private */
210function compose(SeriesClass, SVGElementClass, SVGRendererClass) {
211    const PieSeriesClass = SeriesClass.types.pie;
212    if (!SVGElementClass.symbolCustomAttribs.includes('borderRadius')) {
213        const symbols = SVGRendererClass.prototype.symbols;
214        addEvent(SeriesClass, 'afterColumnTranslate', seriesOnAfterColumnTranslate, {
215            // After columnrange and polar column modifications
216            order: 9
217        });
218        addEvent(PieSeriesClass, 'afterTranslate', pieSeriesOnAfterTranslate);
219        SVGElementClass.symbolCustomAttribs.push('borderRadius', 'brBoxHeight', 'brBoxY');
220        oldArc = symbols.arc;
221        oldRoundedRect = symbols.roundedRect;
222        symbols.arc = arc;
223        symbols.roundedRect = roundedRect;
224    }
225}
226/** @private */
227function optionsToObject(options, seriesBROptions) {
228    if (!isObject(options)) {
229        options = { radius: options || 0 };
230    }
231    return merge(defaultBorderRadiusOptions, seriesBROptions, options);
232}
233/** @private */
234function pieSeriesOnAfterTranslate() {
235    const borderRadius = optionsToObject(this.options.borderRadius);
236    for (const point of this.points) {
237        const shapeArgs = point.shapeArgs;
238        if (shapeArgs) {
239            shapeArgs.borderRadius = relativeLength(borderRadius.radius, (shapeArgs.r || 0) - ((shapeArgs.innerR) || 0));
240        }
241    }
242}
243/**
244 * Extend roundedRect with individual cutting through rOffset.
245 * @private
246 */
247function roundedRect(x, y, width, height, options = {}) {
248    const path = oldRoundedRect(x, y, width, height, options), { r = 0, brBoxHeight = height, brBoxY = y } = options, brOffsetTop = y - brBoxY, brOffsetBtm = (brBoxY + brBoxHeight) - (y + height), 
249    // When the distance to the border-radius box is greater than the r
250    // itself, it means no border radius. The -0.1 accounts for float
251    // rounding errors.
252    rTop = (brOffsetTop - r) > -0.1 ? 0 : r, rBtm = (brOffsetBtm - r) > -0.1 ? 0 : r, cutTop = Math.max(rTop && brOffsetTop, 0), cutBtm = Math.max(rBtm && brOffsetBtm, 0);
253    /*
254
255    The naming of control points:
256
257      / a -------- b \
258     /                \
259    h                  c
260    |                  |
261    |                  |
262    |                  |
263    g                  d
264     \                /
265      \ f -------- e /
266
267    */
268    const a = [x + rTop, y], b = [x + width - rTop, y], c = [x + width, y + rTop], d = [
269        x + width, y + height - rBtm
270    ], e = [
271        x + width - rBtm,
272        y + height
273    ], f = [x + rBtm, y + height], g = [x, y + height - rBtm], h = [x, y + rTop];
274    const applyPythagoras = (r, altitude) => Math.sqrt(Math.pow(r, 2) - Math.pow(altitude, 2));
275    // Inside stacks, cut off part of the top
276    if (cutTop) {
277        const base = applyPythagoras(rTop, rTop - cutTop);
278        a[0] -= base;
279        b[0] += base;
280        c[1] = h[1] = y + rTop - cutTop;
281    }
282    // Column is lower than the radius. Cut off bottom inside the top
283    // radius.
284    if (height < rTop - cutTop) {
285        const base = applyPythagoras(rTop, rTop - cutTop - height);
286        c[0] = d[0] = x + width - rTop + base;
287        e[0] = Math.min(c[0], e[0]);
288        f[0] = Math.max(d[0], f[0]);
289        g[0] = h[0] = x + rTop - base;
290        c[1] = h[1] = y + height;
291    }
292    // Inside stacks, cut off part of the bottom
293    if (cutBtm) {
294        const base = applyPythagoras(rBtm, rBtm - cutBtm);
295        e[0] += base;
296        f[0] -= base;
297        d[1] = g[1] = y + height - rBtm + cutBtm;
298    }
299    // Cut off top inside the bottom radius
300    if (height < rBtm - cutBtm) {
301        const base = applyPythagoras(rBtm, rBtm - cutBtm - height);
302        c[0] = d[0] = x + width - rBtm + base;
303        b[0] = Math.min(c[0], b[0]);
304        a[0] = Math.max(d[0], a[0]);
305        g[0] = h[0] = x + rBtm - base;
306        d[1] = g[1] = y;
307    }
308    // Preserve the box for data labels
309    path.length = 0;
310    path.push(['M', ...a], 
311    // Top side
312    ['L', ...b], 
313    // Top right corner
314    ['A', rTop, rTop, 0, 0, 1, ...c], 
315    // Right side
316    ['L', ...d], 
317    // Bottom right corner
318    ['A', rBtm, rBtm, 0, 0, 1, ...e], 
319    // Bottom side
320    ['L', ...f], 
321    // Bottom left corner
322    ['A', rBtm, rBtm, 0, 0, 1, ...g], 
323    // Left side
324    ['L', ...h], 
325    // Top left corner
326    ['A', rTop, rTop, 0, 0, 1, ...a], ['Z']);
327    return path;
328}
329/* *
330 *
331 *  Default Export
332 *
333 * */
334const BorderRadius = {
335    compose,
336    optionsToObject
337};
338export default BorderRadius;
339/* *
340 *
341 *  API Declarations
342 *
343 * */
344/**
345 * Detailed options for border radius.
346 *
347 * @sample  {highcharts} highcharts/plotoptions/column-borderradius/
348 *          Rounded columns
349 * @sample  highcharts/plotoptions/series-border-radius
350 *          Column and pie with rounded border
351 *
352 * @interface Highcharts.BorderRadiusOptionsObject
353 */ /**
354* The border radius. A number signifies pixels. A percentage string, like for
355* example `50%`, signifies a relative size. For columns this is relative to the
356* column width, for pies it is relative to the radius and the inner radius.
357*
358* @name Highcharts.BorderRadiusOptionsObject#radius
359* @type {string|number}
360*/ /**
361* The scope of the rounding for column charts. In a stacked column chart, the
362* value `point` means each single point will get rounded corners. The value
363* `stack` means the rounding will apply to the full stack, so that only points
364* close to the top or bottom will receive rounding.
365*
366* @name Highcharts.BorderRadiusOptionsObject#scope
367* @validvalue ["point", "stack"]
368* @type {string}
369*/ /**
370* For column charts, where in the point or stack to apply rounding. The `end`
371* value means only those corners at the point value will be rounded, leaving
372* the corners at the base or threshold unrounded. This is the most intuitive
373* behaviour. The `all` value means also the base will be rounded.
374*
375* @name Highcharts.BorderRadiusOptionsObject#where
376* @validvalue ["all", "end"]
377* @type {string}
378* @default end
379*/
380(''); // Keeps doclets above in JS file

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.