@design-core/core 0.0.6 → 0.0.7
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/.eslintrc.json +4 -0
- package/TODO.md +49 -6
- package/core/core/core.js +0 -3
- package/core/dimensions/alignedDimension.js +5 -4
- package/core/dimensions/angularDimension.js +6 -5
- package/core/dimensions/baseCircularDimension.js +3 -2
- package/core/dimensions/baseDimension.js +59 -93
- package/core/dimensions/baseLinearDimension.js +6 -5
- package/core/dimensions/diametricDimension.js +4 -3
- package/core/dimensions/dimension.js +21 -14
- package/core/dimensions/radialDimension.js +4 -3
- package/core/dimensions/rotatedDimension.js +12 -11
- package/core/entities/arc.js +112 -145
- package/core/entities/arctext.js +212 -174
- package/core/entities/basePolyline.js +30 -282
- package/core/entities/circle.js +77 -99
- package/core/entities/entity.js +114 -93
- package/core/entities/hatch.js +322 -249
- package/core/entities/insert.js +66 -81
- package/core/entities/line.js +21 -142
- package/core/entities/lwpolyline.js +16 -11
- package/core/entities/polyline.js +4 -4
- package/core/entities/rectangle.js +3 -1
- package/core/entities/solid.js +6 -15
- package/core/entities/text.js +188 -199
- package/core/lib/auxiliary/rubberBand.js +32 -0
- package/core/lib/auxiliary/selectionWindow.js +63 -0
- package/core/lib/auxiliary/snapPoint.js +113 -0
- package/core/lib/auxiliary/trackingLine.js +76 -0
- package/core/lib/canvas.js +228 -166
- package/core/lib/clipboard.js +7 -1
- package/core/lib/colour.js +16 -0
- package/core/lib/commandLine.js +1 -6
- package/core/lib/commandManager.js +18 -8
- package/core/lib/constants.js +28 -0
- package/core/lib/dxf/dxf.js +4 -3
- package/core/lib/entityManager.js +18 -11
- package/core/lib/inputManager.js +35 -11
- package/core/lib/intersect.js +25 -0
- package/core/lib/patterns.js +8 -1
- package/core/lib/plotOptions.js +67 -0
- package/core/lib/polylineUtils.js +75 -0
- package/core/lib/renderers/cairoRenderer.js +371 -0
- package/core/lib/renderers/canvasRenderer.js +315 -0
- package/core/lib/renderers/pdfRenderer.js +366 -0
- package/core/lib/renderers/rendererBase.js +284 -0
- package/core/lib/renderers/svgRenderer.js +343 -0
- package/core/lib/scene.js +27 -5
- package/core/lib/selectionManager.js +2 -2
- package/core/lib/settings.js +4 -2
- package/core/lib/snapping.js +4 -191
- package/core/lib/stateManager.js +30 -1
- package/core/lib/utils.js +21 -2
- package/core/properties/entityProperties.js +104 -0
- package/core/properties/property.js +106 -2
- package/core/properties/propertyManager.js +29 -13
- package/core/tables/block.js +4 -37
- package/core/tables/endblk.js +1 -1
- package/core/tables/layer.js +8 -0
- package/core/tables/styleManagerBase.js +3 -2
- package/core/tables/tableManagerBase.js +8 -3
- package/core/tools/chamfer.js +111 -137
- package/core/tools/chamferFilletBase.js +135 -34
- package/core/tools/copy.js +3 -3
- package/core/tools/explode.js +2 -2
- package/core/tools/extend.js +132 -19
- package/core/tools/fillet.js +86 -183
- package/core/tools/matchProp.js +2 -11
- package/core/tools/mirror.js +7 -6
- package/core/tools/move.js +3 -3
- package/core/tools/offset.js +3 -5
- package/core/tools/pasteclip.js +1 -1
- package/core/tools/rotate.js +6 -3
- package/core/tools/scale.js +1 -1
- package/core/tools/trim.js +265 -23
- package/core/tools/zoom.js +1 -1
- package/jest.config.js +6 -0
- package/package.json +5 -3
- package/test/dimensions/alignedDimension.test.js +14 -7
- package/test/dimensions/angularDimension.test.js +6 -6
- package/test/dimensions/baseDimension.test.js +69 -8
- package/test/dimensions/baseLinearDimension.test.js +62 -0
- package/test/dimensions/diametricDimension.test.js +6 -6
- package/test/dimensions/radialDimension.test.js +6 -6
- package/test/dimensions/rotatedDimension.test.js +14 -7
- package/test/entities/arc.test.js +80 -83
- package/test/entities/arctext.test.js +180 -53
- package/test/entities/basePolyline.test.js +68 -2
- package/test/entities/circle.test.js +57 -85
- package/test/entities/entity.test.js +42 -8
- package/test/entities/hatch.test.js +543 -70
- package/test/entities/insert.test.js +96 -1
- package/test/entities/line.test.js +15 -41
- package/test/entities/lwpolyline.test.js +104 -0
- package/test/entities/polyline.test.js +11 -0
- package/test/entities/rectangle.test.js +11 -0
- package/test/entities/solid.test.js +1 -1
- package/test/entities/text.test.js +294 -69
- package/test/integration/exportIntegration.reference.dxf +9 -9
- package/test/integration/exportIntegration.test.js +2 -2
- package/test/integration/intersectionIntegration.test.js +4 -5
- package/test/integration/pdfRendererIntegration.reference.pdf +17709 -0
- package/test/integration/pdfRendererIntegration.test.js +61 -0
- package/test/integration/svgRendererIntegration.reference.svg +3343 -0
- package/test/integration/svgRendererIntegration.test.js +61 -0
- package/test/integration/userInputIntegration.reference.dxf +1165 -105
- package/test/integration/userInputIntegration.test.js +239 -132
- package/test/lib/canvas.test.js +742 -123
- package/test/lib/constants.test.js +27 -0
- package/test/lib/entityManager.test.js +4 -4
- package/test/lib/inputManager.test.js +80 -19
- package/test/lib/intersect.test.js +83 -0
- package/test/lib/plotOptions.test.js +179 -0
- package/test/lib/rubberBand.test.js +84 -0
- package/test/lib/settings.test.js +5 -6
- package/test/lib/snapping.test.js +71 -129
- package/test/lib/stateManager.test.js +7 -7
- package/test/lib/utils.test.js +36 -2
- package/test/properties/entityProperties.test.js +132 -0
- package/test/properties/property.test.js +53 -0
- package/test/properties/propertyManager.test.js +3 -3
- package/test/renderers/canvasRenderer.test.js +317 -0
- package/test/renderers/pdfRenderer.test.js +271 -0
- package/test/renderers/rendererBase.test.js +213 -0
- package/test/renderers/svgRenderer.test.js +326 -0
- package/test/tables/block.test.js +31 -1
- package/test/tables/endblk.test.js +2 -2
- package/test/tables/layer.test.js +30 -0
- package/test/test-helpers/hatchGridRenderer.js +180 -0
- package/test/test-helpers/test-helpers.js +152 -0
- package/test/tools/chamfer.test.js +227 -5
- package/test/tools/chamferFilletBase.test.js +98 -9
- package/test/tools/copy.test.js +2 -2
- package/test/tools/explode.test.js +3 -3
- package/test/tools/extend.test.js +331 -0
- package/test/tools/fillet.test.js +181 -0
- package/test/tools/matchProp.test.js +5 -5
- package/test/tools/mirror.test.js +13 -13
- package/test/tools/offset.test.js +170 -3
- package/test/tools/pasteclip.test.js +5 -5
- package/test/tools/rotate.test.js +32 -2
- package/test/tools/scale.test.js +5 -5
- package/test/tools/trim.test.js +526 -0
- package/test/tools/zoom.test.js +7 -7
- package/core/lib/selectionWindow.js +0 -83
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Text } from '../../core/entities/text.js';
|
|
2
2
|
import { Point } from '../../core/entities/point.js';
|
|
3
3
|
import { DesignCore } from '../../core/designCore.js';
|
|
4
|
+
import { Input } from '../../core/lib/input.js';
|
|
4
5
|
|
|
5
6
|
import { File, withMockInput } from '../test-helpers/test-helpers.js';
|
|
6
7
|
import { Core } from '../../core/core/core.js';
|
|
@@ -45,10 +46,10 @@ test.each(textInputScenarios)('Text.execute handles $desc', async (scenario) =>
|
|
|
45
46
|
expect(text.points.length).toBeGreaterThanOrEqual(1);
|
|
46
47
|
expect(text.points[0].x).toBe(pt0.x);
|
|
47
48
|
expect(text.points[0].y).toBe(pt0.y);
|
|
48
|
-
expect(text.height).toBe(expectedHeight);
|
|
49
|
-
expect(text.rotation).toBe(expectedRotation);
|
|
50
|
-
expect(text.string).toBe(expectedString);
|
|
51
|
-
expect(text.styleName).toBe(styleName);
|
|
49
|
+
expect(text.getProperty('height')).toBe(expectedHeight);
|
|
50
|
+
expect(text.getProperty('rotation')).toBe(expectedRotation);
|
|
51
|
+
expect(text.getProperty('string')).toBe(expectedString);
|
|
52
|
+
expect(text.getProperty('styleName')).toBe(styleName);
|
|
52
53
|
});
|
|
53
54
|
});
|
|
54
55
|
|
|
@@ -96,8 +97,8 @@ test('Test Text.getTextFrameCorners', () => {
|
|
|
96
97
|
|
|
97
98
|
// backwards and upsideDown
|
|
98
99
|
text.setRotation(0);
|
|
99
|
-
text.backwards
|
|
100
|
-
text.upsideDown
|
|
100
|
+
text.setProperty('backwards', true);
|
|
101
|
+
text.setProperty('upsideDown', true);
|
|
101
102
|
corners = text.getTextFrameCorners();
|
|
102
103
|
|
|
103
104
|
expect(corners[0].x).toBeCloseTo(90);
|
|
@@ -115,7 +116,7 @@ test('Test Text.getTextFrameCorners', () => {
|
|
|
115
116
|
// Text frame calculation with text alignment
|
|
116
117
|
// bounding rect is 10x10, so with center/middle alignment the text frame should be offset by 5 in both x and y from the insertion point
|
|
117
118
|
const alignedText = new Text({ points: [new Point(100, 100)] });
|
|
118
|
-
alignedText.horizontalAlignment
|
|
119
|
+
alignedText.setProperty('horizontalAlignment', 1); // center
|
|
119
120
|
corners = alignedText.getTextFrameCorners();
|
|
120
121
|
|
|
121
122
|
expect(corners[0].x).toBeCloseTo(95);
|
|
@@ -132,7 +133,7 @@ test('Test Text.getTextFrameCorners', () => {
|
|
|
132
133
|
|
|
133
134
|
// set vertical alignment to middle as well
|
|
134
135
|
// offset the text frame up by another 5 in y so the corners should be at 95,95 - 105,105
|
|
135
|
-
alignedText.verticalAlignment
|
|
136
|
+
alignedText.setProperty('verticalAlignment', 2); // middle
|
|
136
137
|
corners = alignedText.getTextFrameCorners();
|
|
137
138
|
|
|
138
139
|
expect(corners[0].x).toBeCloseTo(95);
|
|
@@ -165,7 +166,7 @@ test('Test Text.getTextFrameCorners', () => {
|
|
|
165
166
|
expect(corners[3].y).toBeCloseTo(100);
|
|
166
167
|
|
|
167
168
|
// flip the text backwards and ensure the text frame corners are still correctly calculated with the flipped text
|
|
168
|
-
alignedText.backwards
|
|
169
|
+
alignedText.setProperty('backwards', true);
|
|
169
170
|
corners = alignedText.getTextFrameCorners();
|
|
170
171
|
|
|
171
172
|
expect(corners[0].x).toBeCloseTo(92.92893);
|
|
@@ -182,7 +183,7 @@ test('Test Text.getTextFrameCorners', () => {
|
|
|
182
183
|
|
|
183
184
|
// test right and top alignment
|
|
184
185
|
const rightAlignedtext = new Text({ points: [new Point(100, 100)] });
|
|
185
|
-
rightAlignedtext.horizontalAlignment
|
|
186
|
+
rightAlignedtext.setProperty('horizontalAlignment', 2); // right
|
|
186
187
|
corners = rightAlignedtext.getTextFrameCorners();
|
|
187
188
|
|
|
188
189
|
expect(corners[0].x).toBeCloseTo(90);
|
|
@@ -198,7 +199,7 @@ test('Test Text.getTextFrameCorners', () => {
|
|
|
198
199
|
expect(corners[3].y).toBeCloseTo(110);
|
|
199
200
|
|
|
200
201
|
// add vertical alignment top and ensure the text frame is still correctly calculated with the right and top alignment applied
|
|
201
|
-
rightAlignedtext.verticalAlignment
|
|
202
|
+
rightAlignedtext.setProperty('verticalAlignment', 3); // top
|
|
202
203
|
corners = rightAlignedtext.getTextFrameCorners();
|
|
203
204
|
|
|
204
205
|
expect(corners[0].x).toBeCloseTo(90);
|
|
@@ -219,47 +220,47 @@ test('Test Text.setRotation', () => {
|
|
|
219
220
|
|
|
220
221
|
// Zero
|
|
221
222
|
setRotText.setRotation(0);
|
|
222
|
-
expect(setRotText.rotation).toBeCloseTo(0);
|
|
223
|
+
expect(setRotText.getProperty('rotation')).toBeCloseTo(0);
|
|
223
224
|
|
|
224
225
|
// Positive
|
|
225
226
|
setRotText.setRotation(22.5);
|
|
226
|
-
expect(setRotText.rotation).toBe(22.5);
|
|
227
|
+
expect(setRotText.getProperty('rotation')).toBe(22.5);
|
|
227
228
|
|
|
228
229
|
setRotText.setRotation(23);
|
|
229
|
-
expect(setRotText.rotation).toBe(23);
|
|
230
|
+
expect(setRotText.getProperty('rotation')).toBe(23);
|
|
230
231
|
|
|
231
232
|
setRotText.setRotation(45);
|
|
232
|
-
expect(setRotText.rotation).toBe(45);
|
|
233
|
+
expect(setRotText.getProperty('rotation')).toBe(45);
|
|
233
234
|
|
|
234
235
|
setRotText.setRotation(90);
|
|
235
|
-
expect(setRotText.rotation).toBe(90);
|
|
236
|
+
expect(setRotText.getProperty('rotation')).toBe(90);
|
|
236
237
|
|
|
237
238
|
setRotText.setRotation(135);
|
|
238
|
-
expect(setRotText.rotation).toBe(135);
|
|
239
|
+
expect(setRotText.getProperty('rotation')).toBe(135);
|
|
239
240
|
|
|
240
241
|
setRotText.setRotation(180);
|
|
241
|
-
expect(setRotText.rotation).toBe(180);
|
|
242
|
+
expect(setRotText.getProperty('rotation')).toBe(180);
|
|
242
243
|
|
|
243
244
|
setRotText.setRotation(225);
|
|
244
|
-
expect(setRotText.rotation).toBe(225);
|
|
245
|
+
expect(setRotText.getProperty('rotation')).toBe(225);
|
|
245
246
|
|
|
246
247
|
setRotText.setRotation(270);
|
|
247
|
-
expect(setRotText.rotation).toBe(270);
|
|
248
|
+
expect(setRotText.getProperty('rotation')).toBe(270);
|
|
248
249
|
|
|
249
250
|
// Greater than 360
|
|
250
251
|
setRotText.setRotation((360 + 90));
|
|
251
|
-
expect(setRotText.rotation).toBe(90);
|
|
252
|
+
expect(setRotText.getProperty('rotation')).toBe(90);
|
|
252
253
|
|
|
253
254
|
// Negative
|
|
254
255
|
setRotText.setRotation(-22.5);
|
|
255
|
-
expect(setRotText.rotation).toBe(337.5);
|
|
256
|
+
expect(setRotText.getProperty('rotation')).toBe(337.5);
|
|
256
257
|
|
|
257
258
|
setRotText.setRotation(-90);
|
|
258
|
-
expect(setRotText.rotation).toBe(270);
|
|
259
|
+
expect(setRotText.getProperty('rotation')).toBe(270);
|
|
259
260
|
|
|
260
261
|
// precision - rounds to closest 5 dp
|
|
261
262
|
setRotText.setRotation(10.123456789);
|
|
262
|
-
expect(setRotText.rotation).toBe(10.12346);
|
|
263
|
+
expect(setRotText.getProperty('rotation')).toBe(10.12346);
|
|
263
264
|
});
|
|
264
265
|
|
|
265
266
|
|
|
@@ -307,13 +308,13 @@ test('Test Text.flags', () => {
|
|
|
307
308
|
const text = new Text();
|
|
308
309
|
expect(text.flags.getFlagValue()).toBe(0);
|
|
309
310
|
// set backwards - set 2 on flags
|
|
310
|
-
text.backwards
|
|
311
|
+
text.setProperty('backwards', true);
|
|
311
312
|
expect(text.flags.getFlagValue()).toBe(2);
|
|
312
313
|
// set upsideDown - set 4 on flags
|
|
313
|
-
text.upsideDown
|
|
314
|
+
text.setProperty('upsideDown', true);
|
|
314
315
|
expect(text.flags.getFlagValue()).toBe(6);
|
|
315
316
|
// unset backwards - remove 2 on flags
|
|
316
|
-
text.backwards
|
|
317
|
+
text.setProperty('backwards', false);
|
|
317
318
|
expect(text.flags.getFlagValue()).toBe(4);
|
|
318
319
|
});
|
|
319
320
|
|
|
@@ -322,10 +323,10 @@ test('Test Text.backwards', () => {
|
|
|
322
323
|
// 2 = Text is backward (mirrored in X).
|
|
323
324
|
// 4 = Text is upside down (mirrored in Y).
|
|
324
325
|
const text = new Text();
|
|
325
|
-
expect(text.backwards).toBe(false);
|
|
326
|
+
expect(text.getProperty('backwards')).toBe(false);
|
|
326
327
|
expect(text.flags.getFlagValue()).toBe(0);
|
|
327
|
-
text.backwards
|
|
328
|
-
expect(text.backwards).toBe(true);
|
|
328
|
+
text.setProperty('backwards', true);
|
|
329
|
+
expect(text.getProperty('backwards')).toBe(true);
|
|
329
330
|
expect(text.flags.getFlagValue()).toBe(2);
|
|
330
331
|
});
|
|
331
332
|
|
|
@@ -334,10 +335,10 @@ test('Test Text.upsideDown', () => {
|
|
|
334
335
|
// 2 = Text is backward (mirrored in X).
|
|
335
336
|
// 4 = Text is upside down (mirrored in Y).
|
|
336
337
|
const text = new Text();
|
|
337
|
-
expect(text.upsideDown).toBe(false);
|
|
338
|
+
expect(text.getProperty('upsideDown')).toBe(false);
|
|
338
339
|
expect(text.flags.getFlagValue()).toBe(0);
|
|
339
|
-
text.upsideDown
|
|
340
|
-
expect(text.upsideDown).toBe(true);
|
|
340
|
+
text.setProperty('upsideDown', true);
|
|
341
|
+
expect(text.getProperty('upsideDown')).toBe(true);
|
|
341
342
|
expect(text.flags.getFlagValue()).toBe(4);
|
|
342
343
|
});
|
|
343
344
|
|
|
@@ -388,7 +389,15 @@ AcDbText
|
|
|
388
389
|
expect(file.contents).toEqual(dxfString);
|
|
389
390
|
|
|
390
391
|
// create new entity from entity data to ensure all props are loaded
|
|
391
|
-
const newText = new Text(
|
|
392
|
+
const newText = new Text({
|
|
393
|
+
handle: text.getProperty('handle'),
|
|
394
|
+
points: text.points,
|
|
395
|
+
string: text.getProperty('string'),
|
|
396
|
+
height: text.getProperty('height'),
|
|
397
|
+
styleName: text.getProperty('styleName'),
|
|
398
|
+
horizontalAlignment: text.getProperty('horizontalAlignment'),
|
|
399
|
+
verticalAlignment: text.getProperty('verticalAlignment'),
|
|
400
|
+
});
|
|
392
401
|
file = new File();
|
|
393
402
|
newText.dxf(file);
|
|
394
403
|
expect(file.contents).toEqual(dxfString);
|
|
@@ -399,9 +408,17 @@ AcDbText
|
|
|
399
408
|
// and a second alignment point with groupcode 11,21,31 for the text insertion point
|
|
400
409
|
// bounding rect is 10x10, so second point should be at 100-(10*0.5), 200-(10*0.5) = 95, 195
|
|
401
410
|
|
|
402
|
-
const centeredText = new Text(
|
|
403
|
-
|
|
404
|
-
|
|
411
|
+
const centeredText = new Text({
|
|
412
|
+
handle: text.getProperty('handle'),
|
|
413
|
+
points: text.points,
|
|
414
|
+
string: text.getProperty('string'),
|
|
415
|
+
height: text.getProperty('height'),
|
|
416
|
+
styleName: text.getProperty('styleName'),
|
|
417
|
+
horizontalAlignment: text.getProperty('horizontalAlignment'),
|
|
418
|
+
verticalAlignment: text.getProperty('verticalAlignment'),
|
|
419
|
+
});
|
|
420
|
+
centeredText.setProperty('horizontalAlignment', 1); // center
|
|
421
|
+
centeredText.setProperty('verticalAlignment', 2); // middle
|
|
405
422
|
file = new File();
|
|
406
423
|
centeredText.dxf(file);
|
|
407
424
|
|
|
@@ -445,7 +462,15 @@ AcDbText
|
|
|
445
462
|
expect(file.contents).toEqual(dxfCenteredString);
|
|
446
463
|
|
|
447
464
|
// create new entity from entity data to ensure all props are loaded
|
|
448
|
-
const importCenteredText = new Text(
|
|
465
|
+
const importCenteredText = new Text({
|
|
466
|
+
handle: centeredText.getProperty('handle'),
|
|
467
|
+
points: centeredText.points,
|
|
468
|
+
string: centeredText.getProperty('string'),
|
|
469
|
+
height: centeredText.getProperty('height'),
|
|
470
|
+
styleName: centeredText.getProperty('styleName'),
|
|
471
|
+
horizontalAlignment: centeredText.getProperty('horizontalAlignment'),
|
|
472
|
+
verticalAlignment: centeredText.getProperty('verticalAlignment'),
|
|
473
|
+
});
|
|
449
474
|
file = new File();
|
|
450
475
|
importCenteredText.dxf(file);
|
|
451
476
|
expect(file.contents).toEqual(dxfCenteredString);
|
|
@@ -454,11 +479,11 @@ AcDbText
|
|
|
454
479
|
test('Text constructor covers all property branches', () => {
|
|
455
480
|
// Minimal data
|
|
456
481
|
let t = new Text({ points: [new Point(1, 2)] });
|
|
457
|
-
expect(t.string).toBe('');
|
|
458
|
-
expect(t.height).toBe(2.5);
|
|
459
|
-
expect(t.horizontalAlignment).toBe(0);
|
|
460
|
-
expect(t.verticalAlignment).toBe(0);
|
|
461
|
-
expect(t.styleName).toBe('STANDARD');
|
|
482
|
+
expect(t.getProperty('string')).toBe('');
|
|
483
|
+
expect(t.getProperty('height')).toBe(2.5);
|
|
484
|
+
expect(t.getProperty('horizontalAlignment')).toBe(0);
|
|
485
|
+
expect(t.getProperty('verticalAlignment')).toBe(0);
|
|
486
|
+
expect(t.getProperty('styleName')).toBe('STANDARD');
|
|
462
487
|
|
|
463
488
|
// All DXF groupcodes
|
|
464
489
|
t = new Text({
|
|
@@ -478,49 +503,49 @@ test('Text constructor covers all property branches', () => {
|
|
|
478
503
|
flags: 2,
|
|
479
504
|
71: 4,
|
|
480
505
|
});
|
|
481
|
-
expect(['abc', 'def']).toContain(t.string);
|
|
482
|
-
expect(['FOO', 'BAR']).toContain(t.styleName);
|
|
483
|
-
expect([5, 6]).toContain(t.height);
|
|
484
|
-
expect([45, 90]).toContain(t.rotation);
|
|
485
|
-
expect([2, 1]).toContain(t.horizontalAlignment);
|
|
486
|
-
expect([3, 2]).toContain(t.verticalAlignment);
|
|
506
|
+
expect(['abc', 'def']).toContain(t.getProperty('string'));
|
|
507
|
+
expect(['FOO', 'BAR']).toContain(t.getProperty('styleName'));
|
|
508
|
+
expect([5, 6]).toContain(t.getProperty('height'));
|
|
509
|
+
expect([45, 90]).toContain(t.getProperty('rotation'));
|
|
510
|
+
expect([2, 1]).toContain(t.getProperty('horizontalAlignment'));
|
|
511
|
+
expect([3, 2]).toContain(t.getProperty('verticalAlignment'));
|
|
487
512
|
expect([2, 4, 6, 0]).toContain(t.flags.getFlagValue());
|
|
488
513
|
});
|
|
489
514
|
|
|
490
515
|
test('Text static register and getApproximateWidth', () => {
|
|
491
516
|
expect(Text.register()).toEqual({ command: 'Text', shortcut: 'DT', type: 'Entity' });
|
|
492
|
-
expect(Text.getApproximateWidth('abc', 10)).toBeCloseTo(
|
|
517
|
+
expect(Text.getApproximateWidth('abc', 10)).toBeCloseTo(16.2);
|
|
493
518
|
});
|
|
494
519
|
|
|
495
520
|
test('Text getHorizontalAlignment covers all cases', () => {
|
|
496
521
|
const t = new Text({ points: [new Point()] });
|
|
497
|
-
t.horizontalAlignment
|
|
522
|
+
t.setProperty('horizontalAlignment', 0);
|
|
498
523
|
expect(t.getHorizontalAlignment()).toBe('left');
|
|
499
|
-
t.horizontalAlignment
|
|
524
|
+
t.setProperty('horizontalAlignment', 1);
|
|
500
525
|
expect(t.getHorizontalAlignment()).toBe('center');
|
|
501
|
-
t.horizontalAlignment
|
|
526
|
+
t.setProperty('horizontalAlignment', 2);
|
|
502
527
|
expect(t.getHorizontalAlignment()).toBe('right');
|
|
503
|
-
t.horizontalAlignment
|
|
528
|
+
t.setProperty('horizontalAlignment', 3); t.setProperty('verticalAlignment', 0);
|
|
504
529
|
expect(t.getHorizontalAlignment()).toBe('aligned');
|
|
505
|
-
t.horizontalAlignment
|
|
530
|
+
t.setProperty('horizontalAlignment', 4); t.setProperty('verticalAlignment', 0);
|
|
506
531
|
expect(t.getHorizontalAlignment()).toBe('center');
|
|
507
|
-
t.horizontalAlignment
|
|
532
|
+
t.setProperty('horizontalAlignment', 5); t.setProperty('verticalAlignment', 0);
|
|
508
533
|
expect(t.getHorizontalAlignment()).toBe('fit');
|
|
509
|
-
t.horizontalAlignment
|
|
534
|
+
t.setProperty('horizontalAlignment', 99);
|
|
510
535
|
expect(t.getHorizontalAlignment()).toBe('left');
|
|
511
536
|
});
|
|
512
537
|
|
|
513
538
|
test('Text getVerticalAlignment covers all cases', () => {
|
|
514
539
|
const t = new Text({ points: [new Point()] });
|
|
515
|
-
t.verticalAlignment
|
|
540
|
+
t.setProperty('verticalAlignment', 0);
|
|
516
541
|
expect(t.getVerticalAlignment()).toBe('alphabetic');
|
|
517
|
-
t.verticalAlignment
|
|
542
|
+
t.setProperty('verticalAlignment', 1);
|
|
518
543
|
expect(t.getVerticalAlignment()).toBe('bottom');
|
|
519
|
-
t.verticalAlignment
|
|
544
|
+
t.setProperty('verticalAlignment', 2);
|
|
520
545
|
expect(t.getVerticalAlignment()).toBe('middle');
|
|
521
|
-
t.verticalAlignment
|
|
546
|
+
t.setProperty('verticalAlignment', 3);
|
|
522
547
|
expect(t.getVerticalAlignment()).toBe('top');
|
|
523
|
-
t.verticalAlignment
|
|
548
|
+
t.setProperty('verticalAlignment', 99);
|
|
524
549
|
expect(t.getVerticalAlignment()).toBe('alphabetic');
|
|
525
550
|
});
|
|
526
551
|
|
|
@@ -578,13 +603,13 @@ test('Text toPolylinePoints returns correct array', () => {
|
|
|
578
603
|
test('Text setBackwards and setUpsideDown edge cases', () => {
|
|
579
604
|
const t = new Text({ points: [new Point()] });
|
|
580
605
|
t.setBackwards(true);
|
|
581
|
-
expect(t.backwards).toBe(true);
|
|
606
|
+
expect(t.getProperty('backwards')).toBe(true);
|
|
582
607
|
t.setBackwards(false);
|
|
583
|
-
expect(t.backwards).toBe(false);
|
|
608
|
+
expect(t.getProperty('backwards')).toBe(false);
|
|
584
609
|
t.setUpsideDown(true);
|
|
585
|
-
expect(t.upsideDown).toBe(true);
|
|
610
|
+
expect(t.getProperty('upsideDown')).toBe(true);
|
|
586
611
|
t.setUpsideDown(false);
|
|
587
|
-
expect(t.upsideDown).toBe(false);
|
|
612
|
+
expect(t.getProperty('upsideDown')).toBe(false);
|
|
588
613
|
});
|
|
589
614
|
|
|
590
615
|
test('Text setRotation handles undefined and height 0', () => {
|
|
@@ -611,8 +636,8 @@ test('Text.execute re-prompts on zero or negative height', async () => {
|
|
|
611
636
|
const text = new Text({});
|
|
612
637
|
await text.execute();
|
|
613
638
|
|
|
614
|
-
expect(text.height).toBe(5);
|
|
615
|
-
expect(text.string).toBe('Hello');
|
|
639
|
+
expect(text.getProperty('height')).toBe(5);
|
|
640
|
+
expect(text.getProperty('string')).toBe('Hello');
|
|
616
641
|
});
|
|
617
642
|
});
|
|
618
643
|
|
|
@@ -624,7 +649,7 @@ test('Text.execute does not create entity for empty string', async () => {
|
|
|
624
649
|
const text = new Text({});
|
|
625
650
|
await text.execute();
|
|
626
651
|
|
|
627
|
-
expect(text.string).toBe('');
|
|
652
|
+
expect(text.getProperty('string')).toBe('');
|
|
628
653
|
}, { extraMethods: { executeCommand: () => {
|
|
629
654
|
executeCommandCalled = true;
|
|
630
655
|
} } });
|
|
@@ -639,3 +664,203 @@ test('Text.snaps node snap position matches points[0]', () => {
|
|
|
639
664
|
expect(nodeSnaps[0].snapPoint.x).toBe(5);
|
|
640
665
|
expect(nodeSnaps[0].snapPoint.y).toBe(10);
|
|
641
666
|
});
|
|
667
|
+
|
|
668
|
+
test('Text.preview - uses rubber band when not waiting for string input', () => {
|
|
669
|
+
const text = new Text({ points: [new Point(5, 5)] });
|
|
670
|
+
DesignCore.Scene.auxiliaryEntities.clear();
|
|
671
|
+
DesignCore.Scene.previewEntities.clear();
|
|
672
|
+
// Simulate a pending POINT prompt (not STRING)
|
|
673
|
+
DesignCore.Scene.inputManager.promptOption = { types: [Input.Type.POINT] };
|
|
674
|
+
|
|
675
|
+
text.preview();
|
|
676
|
+
|
|
677
|
+
expect(DesignCore.Scene.auxiliaryEntities.count()).toBeGreaterThanOrEqual(1);
|
|
678
|
+
expect(DesignCore.Scene.previewEntities.count()).toBe(0);
|
|
679
|
+
|
|
680
|
+
DesignCore.Scene.inputManager.promptOption = undefined;
|
|
681
|
+
});
|
|
682
|
+
|
|
683
|
+
test('Text.preview - shows text entity when waiting for string input', () => {
|
|
684
|
+
const text = new Text({ points: [new Point(5, 5)], height: 5, rotation: 0, string: '' });
|
|
685
|
+
DesignCore.Scene.auxiliaryEntities.clear();
|
|
686
|
+
DesignCore.Scene.previewEntities.clear();
|
|
687
|
+
// Simulate a pending STRING prompt
|
|
688
|
+
DesignCore.Scene.inputManager.promptOption = { types: [Input.Type.STRING] };
|
|
689
|
+
|
|
690
|
+
text.preview();
|
|
691
|
+
|
|
692
|
+
expect(DesignCore.Scene.previewEntities.count()).toBeGreaterThanOrEqual(1);
|
|
693
|
+
expect(DesignCore.Scene.auxiliaryEntities.count()).toBe(0);
|
|
694
|
+
|
|
695
|
+
DesignCore.Scene.inputManager.promptOption = undefined;
|
|
696
|
+
});
|
|
697
|
+
|
|
698
|
+
test('Text.getApproximateWidth unknown character uses fallback width', () => {
|
|
699
|
+
// Unknown characters fall back to 0.56 em per character
|
|
700
|
+
const unknown = Text.getApproximateWidth('€', 10);
|
|
701
|
+
expect(unknown).toBeCloseTo(5.6);
|
|
702
|
+
});
|
|
703
|
+
|
|
704
|
+
test('Text.getApproximateWidth empty string returns 0', () => {
|
|
705
|
+
expect(Text.getApproximateWidth('', 10)).toBe(0);
|
|
706
|
+
});
|
|
707
|
+
|
|
708
|
+
test('Text setRotation with height 0 does not move points[1]', () => {
|
|
709
|
+
const t = new Text({ points: [new Point(0, 0)] });
|
|
710
|
+
t.setProperty('height', 0);
|
|
711
|
+
const originalPoint1 = t.points[1];
|
|
712
|
+
t.setRotation(45);
|
|
713
|
+
expect(t.points[1]).toBe(originalPoint1);
|
|
714
|
+
});
|
|
715
|
+
|
|
716
|
+
test('Text.fromDxf remaps insertion point from sequence-11 point', () => {
|
|
717
|
+
// When DXF points[1] has sequence == 11, fromDxf uses it as the insertion point (points[0])
|
|
718
|
+
const data = Text.fromDxf({
|
|
719
|
+
points: [
|
|
720
|
+
{ x: 0, y: 0 },
|
|
721
|
+
{ x: 50, y: 75, sequence: 11 },
|
|
722
|
+
],
|
|
723
|
+
});
|
|
724
|
+
const t = new Text(data);
|
|
725
|
+
expect(t.points[0].x).toBe(50);
|
|
726
|
+
expect(t.points[0].y).toBe(75);
|
|
727
|
+
});
|
|
728
|
+
|
|
729
|
+
test('Text getHorizontalAlignment unsupported cases return left when verticalAlignment !== 0', () => {
|
|
730
|
+
const t = new Text({ points: [new Point()] });
|
|
731
|
+
t.setProperty('verticalAlignment', 1); // non-zero
|
|
732
|
+
|
|
733
|
+
t.setProperty('horizontalAlignment', 3);
|
|
734
|
+
expect(t.getHorizontalAlignment()).toBe('left');
|
|
735
|
+
|
|
736
|
+
t.setProperty('horizontalAlignment', 4);
|
|
737
|
+
expect(t.getHorizontalAlignment()).toBe('left');
|
|
738
|
+
|
|
739
|
+
t.setProperty('horizontalAlignment', 5);
|
|
740
|
+
expect(t.getHorizontalAlignment()).toBe('left');
|
|
741
|
+
});
|
|
742
|
+
|
|
743
|
+
test('Text.dxf includes non-zero flags and rotation', () => {
|
|
744
|
+
const text = new Text({ handle: '1', points: [new Point(0, 0)] });
|
|
745
|
+
text.setProperty('backwards', true); // flag value 2
|
|
746
|
+
text.setProperty('upsideDown', true); // flag value 4 → combined = 6
|
|
747
|
+
text.setRotation(90);
|
|
748
|
+
text.setProperty('string', 'R');
|
|
749
|
+
|
|
750
|
+
const file = new File();
|
|
751
|
+
text.dxf(file);
|
|
752
|
+
|
|
753
|
+
expect(file.contents).toContain('71\n6\n');
|
|
754
|
+
expect(file.contents).toContain('50\n90\n');
|
|
755
|
+
});
|
|
756
|
+
|
|
757
|
+
test('Text.toCharacters returns one entry with scene coords, rotation and string', () => {
|
|
758
|
+
const text = new Text({ points: [new Point(5, 10), new Point(15, 10)], string: 'Hello', height: 2.5, rotation: 0 });
|
|
759
|
+
const chars = text.toCharacters();
|
|
760
|
+
expect(chars).toHaveLength(1);
|
|
761
|
+
expect(chars[0].char).toBe('Hello');
|
|
762
|
+
expect(typeof chars[0].x).toBe('number');
|
|
763
|
+
expect(typeof chars[0].y).toBe('number');
|
|
764
|
+
expect(chars[0].rotation).toBeCloseTo(0);
|
|
765
|
+
expect(chars[0].upsideDownOffset).toBe(0);
|
|
766
|
+
expect(chars[0].backwardsOffset).toBe(0);
|
|
767
|
+
});
|
|
768
|
+
|
|
769
|
+
test('Text.toCharacters returns empty array for empty string', () => {
|
|
770
|
+
const text = new Text({ points: [new Point(0, 0), new Point(10, 0)], string: '', height: 2.5 });
|
|
771
|
+
expect(text.toCharacters()).toEqual([]);
|
|
772
|
+
});
|
|
773
|
+
|
|
774
|
+
test('Text.draw calls renderer.drawText with correct font and string', () => {
|
|
775
|
+
const text = new Text({ points: [new Point(0, 0), new Point(10, 0)], string: 'Hello', height: 2.5 });
|
|
776
|
+
let calledChars; let calledHeight;
|
|
777
|
+
const mockRenderer = {
|
|
778
|
+
drawText(characters, fontName, height) {
|
|
779
|
+
calledChars = characters;
|
|
780
|
+
calledHeight = height;
|
|
781
|
+
},
|
|
782
|
+
measureText: () => ({ width: 20 }),
|
|
783
|
+
};
|
|
784
|
+
text.draw(mockRenderer);
|
|
785
|
+
expect(calledChars).toHaveLength(1);
|
|
786
|
+
expect(calledChars[0].char).toBe('Hello');
|
|
787
|
+
expect(calledHeight).toBe(2.5);
|
|
788
|
+
});
|
|
789
|
+
|
|
790
|
+
test('Text.draw skips call for empty string', () => {
|
|
791
|
+
const text = new Text({ points: [new Point(0, 0), new Point(10, 0)], string: '', height: 2.5 });
|
|
792
|
+
let drawTextCalled = false;
|
|
793
|
+
const mockRenderer = { drawText() {
|
|
794
|
+
drawTextCalled = true;
|
|
795
|
+
}, measureText() {} };
|
|
796
|
+
text.draw(mockRenderer);
|
|
797
|
+
expect(drawTextCalled).toBe(false);
|
|
798
|
+
});
|
|
799
|
+
|
|
800
|
+
test('Text.draw updates boundingRect from measureText result', () => {
|
|
801
|
+
const text = new Text({ points: [new Point(0, 0), new Point(10, 0)], string: 'Hi', height: 3 });
|
|
802
|
+
const mockRenderer = {
|
|
803
|
+
drawText() {},
|
|
804
|
+
measureText: () => ({ width: 42 }),
|
|
805
|
+
};
|
|
806
|
+
text.draw(mockRenderer);
|
|
807
|
+
expect(text.boundingRect.width).toBe(42);
|
|
808
|
+
expect(text.boundingRect.height).toBe(3);
|
|
809
|
+
});
|
|
810
|
+
|
|
811
|
+
describe('Text.fromDxf', () => {
|
|
812
|
+
test('projects points[1] from rotation angle (group code 50)', () => {
|
|
813
|
+
const data = Text.fromDxf({
|
|
814
|
+
points: [new Point(10, 20)],
|
|
815
|
+
50: 90,
|
|
816
|
+
40: 2.5,
|
|
817
|
+
});
|
|
818
|
+
const t = new Text(data);
|
|
819
|
+
expect(t.getProperty('rotation')).toBeCloseTo(90);
|
|
820
|
+
expect(t.points[1].x).toBeCloseTo(10);
|
|
821
|
+
expect(t.points[1].y).toBeCloseTo(22.5);
|
|
822
|
+
});
|
|
823
|
+
|
|
824
|
+
test('remaps sequence-11 alignment point to points[0]', () => {
|
|
825
|
+
const data = Text.fromDxf({
|
|
826
|
+
points: [
|
|
827
|
+
new Point(0, 0),
|
|
828
|
+
{ x: 30, y: 40, sequence: 11 },
|
|
829
|
+
],
|
|
830
|
+
});
|
|
831
|
+
const t = new Text(data);
|
|
832
|
+
expect(t.points[0].x).toBe(30);
|
|
833
|
+
expect(t.points[0].y).toBe(40);
|
|
834
|
+
});
|
|
835
|
+
|
|
836
|
+
test('preserves other properties unchanged', () => {
|
|
837
|
+
const data = Text.fromDxf({
|
|
838
|
+
points: [new Point(0, 0)],
|
|
839
|
+
1: 'hello',
|
|
840
|
+
40: 5,
|
|
841
|
+
layer: 'TEST',
|
|
842
|
+
});
|
|
843
|
+
expect(data[1]).toBe('hello');
|
|
844
|
+
expect(data[40]).toBe(5);
|
|
845
|
+
expect(data.layer).toBe('TEST');
|
|
846
|
+
});
|
|
847
|
+
|
|
848
|
+
test('returns data unchanged when no points present', () => {
|
|
849
|
+
const data = Text.fromDxf({ 1: 'abc' });
|
|
850
|
+
expect(data[1]).toBe('abc');
|
|
851
|
+
expect(data.points).toEqual([]);
|
|
852
|
+
});
|
|
853
|
+
|
|
854
|
+
test('result equivalent to direct construction with setRotation', () => {
|
|
855
|
+
const pt = new Point(5, 10);
|
|
856
|
+
const fromDxfData = Text.fromDxf({ points: [pt], 50: 45, 40: 2.5 });
|
|
857
|
+
const t1 = new Text(fromDxfData);
|
|
858
|
+
|
|
859
|
+
const t2 = new Text({ points: [pt] });
|
|
860
|
+
t2.setRotation(45);
|
|
861
|
+
|
|
862
|
+
expect(t1.points[0].x).toBeCloseTo(t2.points[0].x);
|
|
863
|
+
expect(t1.points[0].y).toBeCloseTo(t2.points[0].y);
|
|
864
|
+
expect(t1.getProperty('rotation')).toBeCloseTo(t2.getProperty('rotation'));
|
|
865
|
+
});
|
|
866
|
+
});
|
|
@@ -861,7 +861,7 @@ AcDbViewportTableRecord
|
|
|
861
861
|
21
|
|
862
862
|
1.0
|
|
863
863
|
12
|
|
864
|
-
|
|
864
|
+
145
|
|
865
865
|
22
|
|
866
866
|
142.5
|
|
867
867
|
13
|
|
@@ -891,7 +891,7 @@ AcDbViewportTableRecord
|
|
|
891
891
|
40
|
|
892
892
|
275
|
|
893
893
|
41
|
|
894
|
-
0.
|
|
894
|
+
0.9454545454545454
|
|
895
895
|
42
|
|
896
896
|
50.0
|
|
897
897
|
43
|
|
@@ -1133,7 +1133,7 @@ ByLayer
|
|
|
1133
1133
|
31
|
|
1134
1134
|
0.0
|
|
1135
1135
|
39
|
|
1136
|
-
|
|
1136
|
+
1
|
|
1137
1137
|
0
|
|
1138
1138
|
ENDBLK
|
|
1139
1139
|
5
|
|
@@ -1173,7 +1173,7 @@ ByLayer
|
|
|
1173
1173
|
31
|
|
1174
1174
|
0.0
|
|
1175
1175
|
39
|
|
1176
|
-
|
|
1176
|
+
1
|
|
1177
1177
|
0
|
|
1178
1178
|
TEXT
|
|
1179
1179
|
5
|
|
@@ -1221,7 +1221,7 @@ ByLayer
|
|
|
1221
1221
|
30
|
|
1222
1222
|
0.0
|
|
1223
1223
|
39
|
|
1224
|
-
|
|
1224
|
+
1
|
|
1225
1225
|
40
|
|
1226
1226
|
25
|
|
1227
1227
|
0
|
|
@@ -1271,9 +1271,9 @@ ByLayer
|
|
|
1271
1271
|
30
|
|
1272
1272
|
0.0
|
|
1273
1273
|
39
|
|
1274
|
-
2
|
|
1275
|
-
40
|
|
1276
1274
|
1
|
|
1275
|
+
40
|
|
1276
|
+
25
|
|
1277
1277
|
100
|
|
1278
1278
|
AcDbArc
|
|
1279
1279
|
50
|
|
@@ -1377,7 +1377,7 @@ POLYLINE_LAYER
|
|
|
1377
1377
|
6
|
|
1378
1378
|
ByLayer
|
|
1379
1379
|
39
|
|
1380
|
-
|
|
1380
|
+
1
|
|
1381
1381
|
90
|
|
1382
1382
|
4
|
|
1383
1383
|
70
|
|
@@ -1447,7 +1447,7 @@ LWPOLYLINE_LAYER
|
|
|
1447
1447
|
6
|
|
1448
1448
|
ByLayer
|
|
1449
1449
|
39
|
|
1450
|
-
|
|
1450
|
+
1
|
|
1451
1451
|
90
|
|
1452
1452
|
4
|
|
1453
1453
|
70
|
|
@@ -113,13 +113,13 @@ beforeAll(() => {
|
|
|
113
113
|
patternName: 'ANSI31',
|
|
114
114
|
layer: 'HATCH_LAYER',
|
|
115
115
|
});
|
|
116
|
-
hatch.childEntities
|
|
116
|
+
hatch.setProperty('childEntities', [boundary]);
|
|
117
117
|
core.scene.entities.add(hatch);
|
|
118
118
|
core.scene.entities.add(new Text({ points: [new Point(15, 205)], string: 'Hatch', height: 4 }));
|
|
119
119
|
|
|
120
120
|
// Row 2, Col 1: Insert
|
|
121
121
|
const blockLine = new Line({ points: [new Point(0, 0), new Point(40, 40)] });
|
|
122
|
-
blockLine.handle
|
|
122
|
+
blockLine.setProperty('handle', DesignCore.HandleManager.next());
|
|
123
123
|
const block = DesignCore.Scene.blockManager.addItem({
|
|
124
124
|
name: 'TEST_BLOCK',
|
|
125
125
|
items: [blockLine],
|
|
@@ -285,7 +285,7 @@ describe('Entity.touched - selection rectangle intersection', () => {
|
|
|
285
285
|
const charPos = pts[0];
|
|
286
286
|
const sE = selectionExtremes(
|
|
287
287
|
new Point(charPos.x - 5, charPos.y - 5),
|
|
288
|
-
new Point(charPos.x +
|
|
288
|
+
new Point(charPos.x + 1, charPos.y + 5),
|
|
289
289
|
);
|
|
290
290
|
expect(arcText.touched(sE)).toBe(true);
|
|
291
291
|
}
|
|
@@ -705,7 +705,7 @@ describe('Hatch touched tests', () => {
|
|
|
705
705
|
const boundary = new Polyline({
|
|
706
706
|
points: [new Point(0, 0), new Point(100, 0), new Point(100, 100), new Point(0, 100), new Point(0, 0)],
|
|
707
707
|
});
|
|
708
|
-
hatch.childEntities
|
|
708
|
+
hatch.setProperty('childEntities', [boundary]);
|
|
709
709
|
|
|
710
710
|
// Selection rectangle crossing the boundary
|
|
711
711
|
const sE = selectionExtremes(new Point(-5, 50), new Point(5, 60));
|
|
@@ -717,7 +717,7 @@ describe('Hatch touched tests', () => {
|
|
|
717
717
|
const boundary = new Polyline({
|
|
718
718
|
points: [new Point(0, 0), new Point(10, 0), new Point(10, 10), new Point(0, 10), new Point(0, 0)],
|
|
719
719
|
});
|
|
720
|
-
hatch.childEntities
|
|
720
|
+
hatch.setProperty('childEntities', [boundary]);
|
|
721
721
|
|
|
722
722
|
// Selection rectangle far from hatch
|
|
723
723
|
const sE = selectionExtremes(new Point(500, 500), new Point(600, 600));
|
|
@@ -732,8 +732,7 @@ describe('Hatch touched tests', () => {
|
|
|
732
732
|
const boundary2 = new Polyline({
|
|
733
733
|
points: [new Point(50, 50), new Point(60, 50), new Point(60, 60), new Point(50, 60), new Point(50, 50)],
|
|
734
734
|
});
|
|
735
|
-
hatch.childEntities
|
|
736
|
-
hatch.childEntities.push(boundary2);
|
|
735
|
+
hatch.setProperty('childEntities', [boundary1, boundary2]);
|
|
737
736
|
|
|
738
737
|
// Touch only the second boundary
|
|
739
738
|
const sE = selectionExtremes(new Point(48, 55), new Point(52, 58));
|