raycasting edge detection, first pass

pull/95/head
jmoenig 2020-12-03 14:36:55 +01:00
rodzic 35a3faa6a0
commit 0fdcca4680
3 zmienionych plików z 129 dodań i 60 usunięć

Wyświetl plik

@ -9,8 +9,8 @@
<script src="src/symbols.js?version=2020-10-07"></script>
<script src="src/widgets.js?version=2020-10-06"></script>
<script src="src/blocks.js?version=2020-12-02"></script>
<script src="src/threads.js?version=2020-12-02"></script>
<script src="src/objects.js?version=2020-12-02"></script>
<script src="src/threads.js?version=2020-12-03"></script>
<script src="src/objects.js?version=2020-12-03"></script>
<script src="src/gui.js?version=2020-12-01"></script>
<script src="src/paint.js?version=2020-05-17"></script>
<script src="src/lists.js?version=2020-12-01"></script>

Wyświetl plik

@ -84,7 +84,7 @@ BlockEditorMorph, BlockDialogMorph, PrototypeHatBlockMorph, BooleanSlotMorph,
localize, TableMorph, TableFrameMorph, normalizeCanvas, VectorPaintEditorMorph,
AlignmentMorph, Process, WorldMap, copyCanvas, useBlurredShadows*/
modules.objects = '2020-December-02';
modules.objects = '2020-December-03';
var SpriteMorph;
var StageMorph;
@ -979,12 +979,12 @@ SpriteMorph.prototype.initBlocks = function () {
spec: '%rel to %dst',
defaults: [['distance'], ['mouse-pointer']]
},
reportDistanceFacing: { // +++ experimental, under construction
reportDistanceFacing: { // experimental, under construction
only: SpriteMorph,
type: 'reporter',
category: 'sensing',
spec: 'distance facing %dst',
defaults: [['mouse-pointer']]
spec: 'distance facing %cln',
defaults: [['myself']]
},
doResetTimer: {
type: 'command',
@ -2547,6 +2547,8 @@ SpriteMorph.prototype.blockTemplates = function (category) {
blocks.push(block('reportThreadCount'));
blocks.push(block('reportStackSize'));
blocks.push(block('reportFrameCount'));
blocks.push('-');
blocks.push(block('reportDistanceFacing'));
}
/////////////////////////////////

Wyświetl plik

@ -4791,7 +4791,14 @@ Process.prototype.reportDistanceTo = function (name) {
};
Process.prototype.reportDistanceFacing = function (name) {
// +++ experimental - under construction
// raycasting edge detection, highly experimental - under construction
if (this.enableHyperOps) {
if (name instanceof List) {
return name.map(each => this.reportDistanceFacing(each));
}
}
var thisObj = this.blockReceiver(),
thatObj,
stage,
@ -4802,8 +4809,9 @@ Process.prototype.reportDistanceFacing = function (name) {
a, b, x, y,
top, bottom, left, right,
hSect, vSect,
point,
temp;
point, hit,
temp,
canvas, width, imageData;
hSect = (yLevel) => {
var theta = radians(dir);
@ -4843,64 +4851,123 @@ Process.prototype.reportDistanceFacing = function (name) {
}
};
if (thisObj) {
rc = thisObj.rotationCenter();
point = rc;
if (this.inputOption(name) === 'mouse-pointer') {
point = thisObj.world().hand.position();
} else if (this.inputOption(name) === 'center') {
return new Point(thisObj.xPosition(), thisObj.yPosition())
.distanceTo(ZERO);
} else if (name instanceof List) {
return new Point(thisObj.xPosition(), thisObj.yPosition())
.distanceTo(new Point(name.at(1), name.at(2)));
if (!thisObj) {return -1; }
rc = thisObj.rotationCenter();
point = rc;
stage = thisObj.parentThatIsA(StageMorph);
thatObj = this.getOtherObject(name, thisObj, stage);
if (!thatObj) {return -1; }
// determine intersections with the target's bounding box
dir = thisObj.heading;
targetBounds = thatObj.bounds;
top = targetBounds.top();
bottom = targetBounds.bottom();
left = targetBounds.left();
right = targetBounds.right();
// test if already inside the target
if (targetBounds.containsPoint(rc)) {
intersections.push(rc);
hSect(top);
hSect(bottom);
vSect(left);
vSect(right);
if (intersections.length < 2) {
return -1;
}
stage = thisObj.parentThatIsA(StageMorph);
thatObj = this.getOtherObject(name, thisObj, stage);
if (thatObj) {
// determine intersections with the target's bounding box
dir = thisObj.heading;
targetBounds = thatObj.bounds;
top = targetBounds.top();
bottom = targetBounds.bottom();
left = targetBounds.left();
right = targetBounds.right();
// test if already inside the target
if (targetBounds.containsPoint(rc)) {
intersections.push(rc);
hSect(top);
hSect(bottom);
vSect(left);
vSect(right);
} else {
hSect(top);
hSect(bottom);
vSect(left);
vSect(right);
// sort
if (intersections.length > 1) {
if (Math.sign(rc.x - intersections[0].x) !==
Math.sign(intersections[0].x - intersections[1].x) ||
Math.sign(rc.y - intersections[0].y) !==
Math.sign(intersections[0].y - intersections[1].y)
) {
temp = intersections[0];
intersections[0] = intersections[1];
intersections[1] = temp;
}
}
} else {
hSect(top);
hSect(bottom);
vSect(left);
vSect(right);
if (intersections.length < 2) {
return -1;
}
// sort
if (dir !== 90) {
if (Math.sign(rc.x - intersections[0].x) !==
Math.sign(intersections[0].x - intersections[1].x) ||
Math.sign(rc.y - intersections[0].y) !==
Math.sign(intersections[0].y - intersections[1].y)
) {
temp = intersections[0];
intersections[0] = intersections[1];
intersections[1] = temp;
}
// point = thatObj.rotationCenter();
}
// return rc.distanceTo(point) / stage.scale;
}
// return 0;
// for debugging:
/*
return new List(intersections)
.map(point => thisObj.snapPoint(point))
.map(point => new List([point.x, point.y]));
*/
// convert intersections to local bitmap coordinates of the target
intersections = intersections.map(point =>
point.subtract(targetBounds.origin).floor()
);
// get image data
canvas = thatObj.getImage();
width = canvas.width;
imageData = canvas.getContext('2d').getImageData(
0,
0,
width,
canvas.height
).data;
// scan the ray along the coordinates of a Bresenham line
// for the first opaque pixel
function alphaAt(imageData, width, x, y) {
var idx = (y * width * 4) + x * 4;
return imageData[idx + 3];
}
function isOpaque(x, y) {
return alphaAt(imageData, width, x, y) > 0;
}
function scan(testFunc, x0, y0, x1, y1) {
// Bresenham's algorithm
var dx = Math.abs(x1 - x0),
sx = x0 < x1 ? 1 : -1,
dy = -Math.abs(y1 - y0),
sy = y0 < y1 ? 1 : -1,
err = dx + dy,
e2;
while (true) {
if (testFunc(x0, y0)) {
return new Point(x0, y0);
}
if (x0 === x1 && y0 === y1) {
return -1; // not found
}
e2 = 2 * err;
if (e2 > dy) {
err += dy;
x0 += sx;
}
if (e2 < dx) {
err += dx;
y0 += sy;
}
}
}
hit = scan(
isOpaque,
intersections[0].x,
intersections[0].y,
intersections[1].x,
intersections[1].y
);
if (hit === -1) {return hit; }
return rc.distanceTo(hit.add(targetBounds.origin)) / stage.scale;
};
Process.prototype.reportDirectionTo = function (name) {