Ich halte mich von den Themen Artificial Intelligence und Machine Learning (Large Language Models) ja fern so gut es geht - vor allem, weil ich das mal studiert habe. Manchmal überkommt mich aber die Neugierde - das Resultat war bisher immer schlechte Laune.
Im Urlaub überkam mich wieder einmal diese Neugierde - Ich stellte nach der Fertigstellung des neuen Features für das Moduleworkspace Framework fest, dass es nun in seltenen Fällen sehr schwierig war (manchmal sogar unmöglich!) gezielt Verbindungen zu selektieren. Der Grund dafür lag darin, dass ich bisher lediglich die Bounding Boxes der Verbindungen für die Tests genutzt hatte - bei beliebigen Kurven wird das beliebig ungenau. Ich wollte also herausfinden, ob es nicht genauere Methoden gibt, herauszufinden, ob eine Kurve eine vorgegebene Region (ein Rechteck) schneidet oder nicht.
Für die (neu eingeführten) quadratischen Splines wurde ich sehr schnell fündig.
Für kubische - oder Bezier- - Kurven dauerte die Recherche etwas länger. Ich hatte auch bereits selber vage überlegt, ob man zu diesem Zweck die Kurve nicht einfach durch eine MEnge von Liniensegmenten approximieren könnte und anschließend die triviale Aufgabe lösen müsste nachzusehen, ob eines dieser Segmente das Rechteck schneidet. Wie aber diese Liniensegmente konstruieren? Es stellt sich heraus, dass mittels dieses Ansatzes in der Computergraphik tatsächlich Bezierkurven gezeichnet werden. Ich fand auch eine Implementierung eines Algorithmus für diesen Zweck - leider in Javascript.
Und hier setzte nun die Bequemlichkeit und mein Mangel an sozialen Kontakten, die als Korrektiv dienen könnten eine unheilvolle Entwicklung in Gang: Ich kam auf die Idee, diesen Javascript-Code automatisiert nach Java übersetzen zu lassen. Ich begab mich dazu im Internet auf die Suche und stieß auf Verschiedene Angebote - letztlich nutzte ich dieses hier.
Ich kopierte den Javascript-Code dort hinein, klickte die Fehlermeldung weg und wartete auf das Ergebnis der Konversion.
Was ich dann zu sehen bekam erstaunte mich so sehr, dass ich einige Minuten Zweifel hatte, ob ich bei meinen Einschätzungen und meiner Meinung zu aktuellen Ansätzen in KI und ML evetuell die ganze Zeit schon falsch gelegen hätte?
import java.util.ArrayList;
import java.util.List;
public class BezierBuilder {
private int RECURSION_LIMIT;
private double FLT_EPSILON;
private double PATH_DISTANCE_EPSILON;
private double curve_angle_tolerance_epsilon;
private double m_angle_tolerance;
private double m_cusp_limit;
public BezierBuilder(Options opt) {
if (opt == null) opt = new Options();
this.RECURSION_LIMIT = opt.recursion != null ? opt.recursion : 8;
this.FLT_EPSILON = opt.epsilon != null ? opt.epsilon : 1.19209290e-7;
this.PATH_DISTANCE_EPSILON = opt.pathEpsilon != null ? opt.pathEpsilon : 1.0;
this.curve_angle_tolerance_epsilon = opt.angleEpsilon != null ? opt.angleEpsilon : 0.01;
this.m_angle_tolerance = opt.angleTolerance != null ? opt.angleTolerance : 0;
this.m_cusp_limit = opt.cuspLimit != null ? opt.cuspLimit : 0;
}
public List<double[]> bezierCurve(double[] start, double[] c1, double[] c2, double[] end, Double scale, List<double[]> points) {
if (points == null) points = new ArrayList<>();
double s = (scale != null) ? scale : 1.0;
double distanceTolerance = PATH_DISTANCE_EPSILON / s;
distanceTolerance *= distanceTolerance;
begin(start, c1, c2, end, points, distanceTolerance);
return points;
}
private void begin(double[] start, double[] c1, double[] c2, double[] end, List<double[]> points, double distanceTolerance) {
points.add(clone(start));
double x1 = start[0], y1 = start[1];
double x2 = c1[0], y2 = c1[1];
double x3 = c2[0], y3 = c2[1];
double x4 = end[0], y4 = end[1];
recursive(x1, y1, x2, y2, x3, y3, x4, y4, points, distanceTolerance, 0);
points.add(clone(end));
}
private void recursive(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4,
List<double[]> points, double distanceTolerance, int level) {
if (level > RECURSION_LIMIT)
return;
double pi = Math.PI;
// Calculate all the mid-points of the line segments
double x12 = (x1 + x2) / 2;
double y12 = (y1 + y2) / 2;
double x23 = (x2 + x3) / 2;
double y23 = (y2 + y3) / 2;
double x34 = (x3 + x4) / 2;
double y34 = (y3 + y4) / 2;
double x123 = (x12 + x23) / 2;
double y123 = (y12 + y23) / 2;
double x234 = (x23 + x34) / 2;
double y234 = (y23 + y34) / 2;
double x1234 = (x123 + x234) / 2;
double y1234 = (y123 + y234) / 2;
if (level > 0) { // Enforce subdivision first time
// Try to approximate the full cubic curve by a single straight line
double dx = x4 - x1;
double dy = y4 - y1;
double d2 = Math.abs((x2 - x4) * dy - (y2 - y4) * dx);
double d3 = Math.abs((x3 - x4) * dy - (y3 - y4) * dx);
double da1, da2;
if (d2 > FLT_EPSILON && d3 > FLT_EPSILON) {
// Regular case
if ((d2 + d3) * (d2 + d3) <= distanceTolerance * (dx * dx + dy * dy)) {
// If the curvature doesn't exceed the distanceTolerance value
// we tend to finish subdivisions.
if (m_angle_tolerance < curve_angle_tolerance_epsilon) {
points.add(vec2(x1234, y1234));
return;
}
// Angle & Cusp Condition
double a23 = Math.atan2(y3 - y2, x3 - x2);
da1 = Math.abs(a23 - Math.atan2(y2 - y1, x2 - x1));
da2 = Math.abs(Math.atan2(y4 - y3, x4 - x3) - a23);
if (da1 >= pi) da1 = 2 * pi - da1;
if (da2 >= pi) da2 = 2 * pi - da2;
if (da1 + da2 < m_angle_tolerance) {
// Finally we can stop the recursion
points.add(vec2(x1234, y1234));
return;
}
if (m_cusp_limit != 0.0) {
if (da1 > m_cusp_limit) {
points.add(vec2(x2, y2));
return;
}
if (da2 > m_cusp_limit) {
points.add(vec2(x3, y3));
return;
}
}
}
} else if (d2 > FLT_EPSILON) {
if (d2 * d2 <= distanceTolerance * (dx * dx + dy * dy)) {
if (m_angle_tolerance < curve_angle_tolerance_epsilon) {
points.add(vec2(x1234, y1234));
return;
}
}
} else if (d3 > FLT_EPSILON) {
if (d3 * d3 <= distanceTolerance * (dx * dx + dy * dy)) {
if (m_angle_tolerance < curve_angle_tolerance_epsilon) {
points.add(vec2(x1234, y1234));
return;
}
}
} else {
// All control points are coincident
points.add(vec2(x1234, y1234));
return;
}
}
// Continue subdivision
recursive(x1, y1, x12, y12, x123, y123, x1234, y1234, points, distanceTolerance, level + 1);
recursive(x1234, y1234, x234, y234, x34, y34, x4, y4, points, distanceTolerance, level + 1);
}
private double[] clone(double[] point) {
return new double[]{point[0], point[1]};
}
private double[] vec2(double x, double y) {
return new double[]{x, y};
}
public static class Options {
public Integer recursion;
public Double epsilon;
public Double pathEpsilon;
public Double angleEpsilon;
public Double angleTolerance;
public Double cuspLimit;
public Options() {
}
}
}
Als ich das sah, war ich völlig konsterniert: Konnte es wirklich sein, dass diese Systeme inzwischen so gut waren, dass selbst so komplexe Aufgaben wie die Umwandlung eines Algorithmus aus einer Programmiersprache in eine auf völlig verschiedenen Konzepten beruhende (typlos vs. strongly typed) inzwischen funktionierte? Der generierte Quelltext war nicht nur syntaktisch korrekt - er lieferte ohne weitere Änderungen auch das korrekte Ergebnis wie in dieser Beispieldarstellung zu sehen ist (pink - Javas Implementierung, schwarz die Liniensegmente und deren Endpunkte)!
Eine Darstellung des Vergleichs des Verlaufs der Java2D-Implementierung in pink und der Approximation durch Liniensegmente in schwarz
Danach brauchte ich eine Pause und musste diese Erfahrung erst einmal sacken lassen. Ich nahm ein Buch zur Hnad, war aber nicht richtig bei der Sache. Und irgendwann kam der Gedanke: Der Ausgangspunkt war einfach nur Code in mehreren Funktionen - wie kam das System auf den Klassennamen? Und der Originalcode enthielt die Definition der Klasse für die Konfiguration des Algorithmus überhaupt nicht - wie kam diese Klasse Options dann aber ins Resultat? Mich beschlich eine Ahnung - dann erinnerte ich mich an die Fehlermeldung, die ich weggeklickt hatte. Ich startete die Operation erneut und dieses Mal beachtete ich sie:
Input Size Limit Reached
Only the first 4,000 characters will be converted. This might result in errors. Login to convert code with more than 4,000 characters.
Das würde ja bedeuten, dass für die Konversion gar nicht der komplette Code herangezogen wurde? Ich sah nach - und siehe da:
function clone(point) { //TODO: use gl-vec2 for this
return [point[0], point[1]]
}
function vec2(x, y) {
return [x, y]
}
module.exports = function createBezierBuilder(opt) {
opt = opt||{}
var RECURSION_LIMIT = typeof opt.recursion === 'number' ? opt.recursion : 8
var FLT_EPSILON = typeof opt.epsilon === 'number' ? opt.epsilon : 1.19209290e-7
var PATH_DISTANCE_EPSILON = typeof opt.pathEpsilon === 'number' ? opt.pathEpsilon : 1.0
var curve_angle_tolerance_epsilon = typeof opt.angleEpsilon === 'number' ? opt.angleEpsilon : 0.01
var m_angle_tolerance = opt.angleTolerance || 0
var m_cusp_limit = opt.cuspLimit || 0
return function bezierCurve(start, c1, c2, end, scale, points) {
if (!points)
points = []
scale = typeof scale === 'number' ? scale : 1.0
var distanceTolerance = PATH_DISTANCE_EPSILON / scale
distanceTolerance *= distanceTolerance
begin(start, c1, c2, end, points, distanceTolerance)
return points
}
////// Based on:
////// https://github.com/pelson/antigrain/blob/master/agg-2.4/src/agg_curves.cpp
function begin(start, c1, c2, end, points, distanceTolerance) {
points.push(clone(start))
var x1 = start[0],
y1 = start[1],
x2 = c1[0],
y2 = c1[1],
x3 = c2[0],
y3 = c2[1],
x4 = end[0],
y4 = end[1]
recursive(x1, y1, x2, y2, x3, y3, x4, y4, points, distanceTolerance, 0)
points.push(clone(end))
}
function recursive(x1, y1, x2, y2, x3, y3, x4, y4, points, distanceTolerance, level) {
if(level > RECURSION_LIMIT)
return
var pi = Math.PI
// Calculate all the mid-points of the line segments
//----------------------
var x12 = (x1 + x2) / 2
var y12 = (y1 + y2) / 2
var x23 = (x2 + x3) / 2
var y23 = (y2 + y3) / 2
var x34 = (x3 + x4) / 2
var y34 = (y3 + y4) / 2
var x123 = (x12 + x23) / 2
var y123 = (y12 + y23) / 2
var x234 = (x23 + x34) / 2
var y234 = (y23 + y34) / 2
var x1234 = (x123 + x234) / 2
var y1234 = (y123 + y234) / 2
if(level > 0) { // Enforce subdivision first time
// Try to approximate the full cubic curve by a single straight line
//------------------
var dx = x4-x1
var dy = y4-y1
var d2 = Math.abs((x2 - x4) * dy - (y2 - y4) * dx)
var d3 = Math.abs((x3 - x4) * dy - (y3 - y4) * dx)
var da1, da2
if(d2 > FLT_EPSILON && d3 > FLT_EPSILON) {
// Regular care
//-----------------
if((d2 + d3)*(d2 + d3) <= distanceTolerance * (dx*dx + dy*dy)) {
// If the curvature doesn't exceed the distanceTolerance value
// we tend to finish subdivisions.
//----------------------
if(m_angle_tolerance < curve_angle_tolerance_epsilon) {
points.push(vec2(x1234, y1234))
return
}
// Angle & Cusp Condition
//----------------------
var a23 = Math.atan2(y3 - y2, x3 - x2)
da1 = Math.abs(a23 - Math.atan2(y2 - y1, x2 - x1))
da2 = Math.abs(Math.atan2(y4 - y3, x4 - x3) - a23)
if(da1 >= pi) da1 = 2*pi - da1
if(da2 >= pi) da2 = 2*pi - da2
if(da1 + da2 < m_angle_tolerance) {
// Finally we can stop the recursion
//----------------------
points.push(vec2(x1234, y1234))
return
}
if(m_cusp_limit !== 0.0) {
if(da1 > m_cusp_limit) {
po
Der Code hörte tatsächlich nach 4000 Zeichen - und damit mittendrin - auf. Wie kann es dann also sein, dass er in seiner Gesamtheit - und noch ein wenig darüber hinaus - korrekt nach Java umgesetzt wurde? Dafür gibt es nur eine Lösung: Dieser Vorgang hat wie auch alle anderen, die man heute Artificial Intelligence und Machine Learning zuschreibt rein gar nichts mit Bewusstsein oder Intelligenz oder Problemlösung zu tun: die heutigen Modelle sind einfach eine riesige Volltextsuche über geraubtes Inputmaterial - irgendjemand hatte die Konversion des Algorithmus bereits vollzogen und dieses Stück des Quelltextes in den Trainingsdaten wurde durch den Input an die Oberfläche gespült! Leider ist es aber eine armselige Volltextsuche - sie zeigt zwar den Suchtreffer, jedoch nicht die Quelle und den Verfasser.
Damit stehe ich jetzt vor einem Dilemma: Wenn ich immer noch annehmen würde, dass das Ergebnis tatsächlich durch eine Konversion erreicht wurde, würde ich mir die Lizenz des Javascript-Projekts zu eigen machen und die Implementierung in mein Framework integrieren. Da es aber offensichtlich statt dessen die Kopie einer Implementierung ist, deren Quelle ich nicht kenne fällt es mir schwer, das zu tun, weil ich die Lizenz, unter der dieser geklaute Quelltext stand nicht kenne.
Ich habe anschließend noch überprüft, wie wenig Ausgangsmaterial dieses System braucht oder - mit anderen Worten - wie kurz der Suchbegriff sein kann, um trotzdem zu dem gezeigten Ergebnis zu kommen. da man nur zwei Konversionen pro Tag frei hat bin ich noch nicht auf die Stelle gestoßen, wo es nicht mehr fnktioniert - ich kann aber sagen, dass 55 Zeilen reichen, das Ergebnis zu erhalten:
function clone(point) { //TODO: use gl-vec2 for this
return [point[0], point[1]]
}
function vec2(x, y) {
return [x, y]
}
module.exports = function createBezierBuilder(opt) {
opt = opt||{}
var RECURSION_LIMIT = typeof opt.recursion === 'number' ? opt.recursion : 8
var FLT_EPSILON = typeof opt.epsilon === 'number' ? opt.epsilon : 1.19209290e-7
var PATH_DISTANCE_EPSILON = typeof opt.pathEpsilon === 'number' ? opt.pathEpsilon : 1.0
var curve_angle_tolerance_epsilon = typeof opt.angleEpsilon === 'number' ? opt.angleEpsilon : 0.01
var m_angle_tolerance = opt.angleTolerance || 0
var m_cusp_limit = opt.cuspLimit || 0
return function bezierCurve(start, c1, c2, end, scale, points) {
if (!points)
points = []
scale = typeof scale === 'number' ? scale : 1.0
var distanceTolerance = PATH_DISTANCE_EPSILON / scale
distanceTolerance *= distanceTolerance
begin(start, c1, c2, end, points, distanceTolerance)
return points
}
////// Based on:
////// https://github.com/pelson/antigrain/blob/master/agg-2.4/src/agg_curves.cpp
function begin(start, c1, c2, end, points, distanceTolerance) {
points.push(clone(start))
var x1 = start[0],
y1 = start[1],
x2 = c1[0],
y2 = c1[1],
x3 = c2[0],
y3 = c2[1],
x4 = end[0],
y4 = end[1]
recursive(x1, y1, x2, y2, x3, y3, x4, y4, points, distanceTolerance, 0)
points.push(clone(end))
}
function recursive(x1, y1, x2, y2, x3, y3, x4, y4, points, distanceTolerance, level) {
if(level > RECURSION_LIMIT)
return
var pi = Math.PI
// Calculate all the mid-points of the line segments
Nachtrag - vielleicht sollte man es auch nicht Violltextsuche nennen, sondern besser Assoziativspeicher...
Ich experimentierte noch etwas weiter nd fand heraus, dass der Assoziativspeicher überaus robust war - selbst diese kurze Eingabe
unction clone(point) { //TODO: use gl-vec2 for this
return [point[0], point[1]]
}
function vec2(x, y) {
return [x, y]
}
module.exports = function createBezierBuilder(opt) {
opt = opt||{}
var RECURSION_LIMIT = typeof opt.recursion === 'number' ? opt.recursion : 8
var FLT_EPSILON = typeof opt.epsilon === 'number' ? opt.epsilon : 1.19209290e-7
var PATH_DISTANCE_EPSILON = typeof opt.pathEpsilon === 'number' ? opt.pathEpsilon : 1.0
var curve_angle_tolerance_epsilon = typeof opt.angleEpsilon === 'number' ? opt.angleEpsilon : 0.01
var m_angle_tolerance = opt.angleTolerance || 0
var m_cusp_limit = opt.cuspLimit || 0
return function bezierCurve(start, c1, c2, end, scale, points) {
if (!points)
points = []
scale = typeof scale === 'number' ? scale : 1.0
var distanceTolerance = PATH_DISTANCE_EPSILON / scale
distanceTolerance *= distanceTolerance
begin(start, c1, c2, end, points, distanceTolerance)
zeitigte noch immer das bekannte Ergebnis. Auch nachdem ich die Zeilen dieses Ausschnitts umsortiert hatte, zeigte das System immer noch unbeirrt auf das bekannte Ergebnis:
var FLT_EPSILON = typeof opt.epsilon === 'number' ? opt.epsilon : 1.19209290e-7
var PATH_DISTANCE_EPSILON = typeof opt.pathEpsilon === 'number' ? opt.pathEpsilon : 1.0
var curve_angle_tolerance_epsilon = typeof opt.angleEpsilon === 'number' ? opt.angleEpsilon : 0.01
var m_angle_tolerance = opt.angleTolerance || 0
var m_cusp_limit = opt.cuspLimit || 0
return function bezierCurve(start, c1, c2, end, scale, points) {
if (!points)
points = []
scale = typeof scale === 'number' ? scale : 1.0
var distanceTolerance = PATH_DISTANCE_EPSILON / scale
distanceTolerance *= distanceTolerance
begin(start, c1, c2, end, points, distanceTolerance)
function clone(point) { //TODO: use gl-vec2 for this
return [point[0], point[1]]
}
function vec2(x, y) {
return [x, y]
}
module.exports = function createBezierBuilder(opt) {
opt = opt||{}
var RECURSION_LIMIT = typeof opt.recursion === 'number' ? opt.recursion : 8
Erst nach einer nochmaligen Reduktion der Anzahl vorgegebener Zeilen:
var FLT_EPSILON = typeof opt.epsilon === 'number' ? opt.epsilon : 1.19209290e-7
var PATH_DISTANCE_EPSILON = typeof opt.pathEpsilon === 'number' ? opt.pathEpsilon : 1.0
var curve_angle_tolerance_epsilon = typeof opt.angleEpsilon === 'number' ? opt.angleEpsilon : 0.01
var m_angle_tolerance = opt.angleTolerance || 0
var m_cusp_limit = opt.cuspLimit || 0
return function bezierCurve(start, c1, c2, end, scale, points) {
if (!points)
points = []
scale = typeof scale === 'number' ? scale : 1.0
var distanceTolerance = PATH_DISTANCE_EPSILON / scale
war das Ergebnis ein anderes. Auch hier wieder eine Java-Klasse, allerdings muss man dann doch wohl von Bug sprechen:
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class BezierUtil {
private double FLT_EPSILON;
private double PATH_DISTANCE_EPSILON;
private double curve_angle_tolerance_epsilon;
private double m_angle_tolerance;
private double m_cusp_limit;
public BezierUtil(Map opt) {
Object epsilonObj = opt.get("epsilon");
FLT_EPSILON = (epsilonObj instanceof Number) ? ((Number) epsilonObj).doubleValue() : 1.19209290e-7;
Object pathEpsilonObj = opt.get("pathEpsilon");
PATH_DISTANCE_EPSILON = (pathEpsilonObj instanceof Number) ? ((Number) pathEpsilonObj).doubleValue() : 1.0;
Object angleEpsilonObj = opt.get("angleEpsilon");
curve_angle_tolerance_epsilon = (angleEpsilonObj instanceof Number) ? ((Number) angleEpsilonObj).doubleValue() : 0.01;
Object angleToleranceObj = opt.get("angleTolerance");
m_angle_tolerance = (angleToleranceObj instanceof Number) ? ((Number) angleToleranceObj).doubleValue() : 0;
Object cuspLimitObj = opt.get("cuspLimit");
m_cusp_limit = (cuspLimitObj instanceof Number) ? ((Number) cuspLimitObj).doubleValue() : 0;
}
public List bezierCurve(Point start, Point c1, Point c2, Point end, Double scale, List points) {
if (points == null) {
points = new ArrayList<>();
}
double actualScale = (scale != null) ? scale : 1.0;
double distanceTolerance = PATH_DISTANCE_EPSILON / actualScale;
// The rest of the bezierCurve implementation would go here
return points;
}
public static class Point {
public double x;
public double y;
public Point(double x, double y) {
this.x = x;
this.y = y;
}
}
Und man sieht - jetzt ist das System komplett aus der Bahn geraten: Plötzlich erfindet es eine Klasse Point statt die in Java Standardbibliotheken verfügbare zu nutzen, es erfindet einen Rückgabewert für die unvollständig spezifizierte Funktion - und es bemerkt nicht, dass die Definition der Funktion gar nicht abgeschlossen wurde. Was hier entsteht ist einfach grober Unfug!
Abschließend ein weiteres Beispiel mit Belegen dafür, dass hier nichts super-intelligentes gelernt hat, wie man Code schreibt, sondern dass hier eine stochastische Maschine ohne Sinn und Verstand Versatzstücke klaut und neu zusammensetzt - zunächst der Input in die Konversion:
drawWallSliceRectangleTinted: function(x, y, width, height, xOffset, brighnessLevel)
{
//console.log("this.fWallTextureBuffer="+this.fWallTextureBuffer);
//var xOffset=x%this.fWallTexture.width; // wrap the image position
// wait until the texture loads
if (this.fWallTextureBuffer==undefined)
return;
var dy=height;
x=Math.floor(x);
y=Math.floor(y);
xOffset=Math.floor(xOffset);
var bytesPerPixel=4;
var sourceIndex=(bytesPerPixel*xOffset);
var lastSourceIndex=sourceIndex+(this.fWallTextureBuffer.width*this.fWallTextureBuffer.height*bytesPerPixel);
//var targetCanvasPixels=this.canvasContext.createImageData(0, 0, width, height);
var targetIndex=(this.offscreenCanvasPixels.width*bytesPerPixel)*y+(bytesPerPixel*x);
var heightToDraw = height;
// clip bottom
if (y+heightToDraw>this.offscreenCanvasPixels.height)
heightToDraw=this.offscreenCanvasPixels.height-y;
var yError=0;
// we need to check this, otherwise, program might crash when trying
// to fetch the shade if this condition is true (possible if height is 0)
if (heightToDraw<0)
return;
// we're going to draw the first row, then move down and draw the next row
// and so on we can use the original x destination to find out
// the x position of the next row
// Remeber that the source bitmap is rotated, so the width is actually the
// height
while (true)
{
// if error < actualHeight, this will cause row to be skipped until
// this addition sums to scaledHeight
// if error > actualHeight, this ill cause row to be drawn repeatedly until
// this addition becomes smaller than actualHeight
// 1) Think the image height as 100, if percent is >= 100, we'll need to
// copy the same pixel over and over while decrementing the percentage.
// 2) Similarly, if percent is <100, we skip a pixel while incrementing
// and do 1) when the percentage we're adding has reached >=100
yError += height;
// dereference for faster access (especially useful when the same bit
// will be copied more than once)
//BIT srcBit = shadedPal[*src];
var red=Math.floor(this.fWallTexturePixels[sourceIndex]*brighnessLevel);
var green=Math.floor(this.fWallTexturePixels[sourceIndex+1]*brighnessLevel);
var blue=Math.floor(this.fWallTexturePixels[sourceIndex+2]*brighnessLevel);
var alpha=Math.floor(this.fWallTexturePixels[sourceIndex+3]);
// while there's a row to draw & not end of drawing area
while (yError>=this.fWallTextureBuffer.width)
{
yError-=this.fWallTextureBuffer.width;
this.offscreenCanvasPixels.data[targetIndex]=red;
this.offscreenCanvasPixels.data[targetIndex+1]=green;
this.offscreenCanvasPixels.data[targetIndex+2]=blue;
this.offscreenCanvasPixels.data[targetIndex+3]=alpha;
targetIndex+=(bytesPerPixel*this.offscreenCanvasPixels.width);
// clip bottom (just return if we reach bottom)
heightToDraw--;
if (heightToDraw<1)
return;
}
sourceIndex+=(bytesPerPixel*this.fWallTextureBuffer.width);
if (sourceIndex>lastSourceIndex)
sourceIndex=lastSourceIndex;
}
},
clearOffscreenCanvas : function()
{
this.offscreenCanvasContext.clearRect(0, 0, this.width, this.height);
},
blitOffscreenCanvas : function()
{
this.canvasContext.putImageData(this.offscreenCanvasPixels,0,0);
},
drawFillRectangle: function(x, y, width, height, red, green, blue, alpha)
{
var bytesPerPixel=4;
//var targetCanvasPixels=this.canvasContext.createImageData(0, 0, width, height);
var targetIndex=(bytesPerPixel*this.offscreenCanvasPixels.width)*y+(bytesPerPixel*x);
for (var h=0; h<height; h++)
{
for (var w=0; w<width; w++)
{
this.offscreenCanvasPixels.data[targetIndex]=red;
this.offscreenCanvasPixels.data[targetIndex+1]=green;
this.offscreenCanvasPixels.data[targetIndex+2]=blue;
this.offscreenCanvasPixels.data[targetIndex+3]=
Man beachte im Speziellen, dass der Code sehr viel Kommentar enthält - außerdem hört er wieder mitten in einer Funktion auf. Die "AI" macht daraus folgenden Java-Code:
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
public class Renderer {
private BufferedImage fWallTextureBuffer;
private int[] fWallTexturePixels;
private int fWallTextureBufferWidth;
private int fWallTextureBufferHeight;
private BufferedImage offscreenCanvasPixels;
private int offscreenCanvasWidth;
private int offscreenCanvasHeight;
private Graphics2D offscreenCanvasContext;
private Graphics2D canvasContext;
private int width;
private int height;
public Renderer(BufferedImage wallTextureBuffer, BufferedImage offscreenCanvas, Graphics2D canvasContext, int width, int height) {
this.fWallTextureBuffer = wallTextureBuffer;
this.offscreenCanvasPixels = offscreenCanvas;
this.offscreenCanvasWidth = offscreenCanvas.getWidth();
this.offscreenCanvasHeight = offscreenCanvas.getHeight();
this.offscreenCanvasContext = offscreenCanvas.createGraphics();
this.fWallTextureBufferWidth = wallTextureBuffer.getWidth();
this.fWallTextureBufferHeight = wallTextureBuffer.getHeight();
this.fWallTexturePixels = wallTextureBuffer.getRGB(0, 0, fWallTextureBufferWidth, fWallTextureBufferHeight, null, 0, fWallTextureBufferWidth);
this.canvasContext = canvasContext;
this.width = width;
this.height = height;
}
public void drawWallSliceRectangleTinted(int x, int y, int width, int height, int xOffset, float brightnessLevel) {
if (fWallTextureBuffer == null)
return;
int dy = height;
x = (int) Math.floor(x);
y = (int) Math.floor(y);
xOffset = (int) Math.floor(xOffset);
int bytesPerPixel = 4;
// sourceIndex is the pixel index in the texture (not byte index)
int sourceX = xOffset;
int sourceY = 0;
int heightToDraw = height;
if (y + heightToDraw > offscreenCanvasHeight)
heightToDraw = offscreenCanvasHeight - y;
if (heightToDraw < 0)
return;
int yError = 0;
int targetX = x;
int targetY = y;
while (true) {
yError += height;
// Get pixel color from source texture at (sourceX, sourceY)
if (sourceY >= fWallTextureBufferHeight)
sourceY = fWallTextureBufferHeight - 1;
int srcPixel = fWallTextureBuffer.getRGB(sourceX, sourceY);
int alpha = (srcPixel >> 24) & 0xFF;
int red = (int) (((srcPixel >> 16) & 0xFF) * brightnessLevel);
int green = (int) (((srcPixel >> 8) & 0xFF) * brightnessLevel);
int blue = (int) ((srcPixel & 0xFF) * brightnessLevel);
// Clamp colors to 0-255
red = Math.min(255, Math.max(0, red));
green = Math.min(255, Math.max(0, green));
blue = Math.min(255, Math.max(0, blue));
while (yError >= fWallTextureBufferWidth) {
yError -= fWallTextureBufferWidth;
if (targetY >= offscreenCanvasHeight)
return;
int rgba = (alpha << 24) | (red << 16) | (green << 8) | blue;
offscreenCanvasPixels.setRGB(targetX, targetY, rgba);
targetY++;
heightToDraw--;
if (heightToDraw < 1)
return;
}
sourceY++;
if (sourceY >= fWallTextureBufferHeight)
sourceY = fWallTextureBufferHeight - 1;
}
}
public void clearOffscreenCanvas() {
offscreenCanvasContext.clearRect(0, 0, width, height);
}
public void blitOffscreenCanvas() {
canvasContext.drawImage(offscreenCanvasPixels, 0, 0, null);
}
public void drawFillRectangle(int x, int y, int width, int height, int red, int green, int blue, int alpha) {
for (int h = 0; h < height; h++) {
for (int w = 0; w < width; w++) {
int targetX = x + w;
int targetY = y + h;
if (targetX < 0 || targetX >= offscreenCanvasWidth || targetY < 0 || targetY >= offscreenCanvasHeight)
continue;
int rgba = (alpha << 24) | (red << 16) | (green << 8) | blue;
offscreenCanvasPixels.setRGB(targetX, targetY, rgba);
}
}
}
}
Das System bemerkt den Fakt wieder nicht, dass der Input syntaktisch nicht korrekt ist - die letzte Methode wird einfach beendet. Dazu kommt, dass kein Kommentar der ursprünglichen Form mehr übrig bleibt - statt dessen wird einfach neuer erzeugt, der wenig Bezug zum Code hat (speziell der erste, der sich auf Byte- vs. Pixel-Indices bezieht: Im gesamten Codeschnipsel wird nicht mit vytes operiert). Daher liegt auch hier der Gedanke nahe, dass der Assoziativspeicher einen Abschnitt der Trainingsdaten widerkäut, der zufällig dem Input sehr ähnelt.
GeoJSON in EBMap4D
31.07.2021
Ich habe die Anwendung EBMap4D einer Generalüberholung unterzogen und neue Features hinzugefügt...
WeiterlesenAI und ML Android Basteln C und C++ Chaos Datenbanken Docker dWb+ ESP Wifi Garten Geo Go GUI Hardware Java Jupyter JupyterBinder Komponenten Links Linux Markdown Markup Music Numerik OpenSource PKI-X.509-CA Präsentationen Python QBrowser Rants Raspi Revisited Security Software-Test sQLshell TeleGrafana Verschiedenes Video Virtualisierung Windows Upcoming...
Wie bereits in einem früheren Artikel beschrieben habe verfügt die sQLshell über eine Integration zur Erfassung von Daten aus Dokumentendirekt per Optical Character Recognition. Ich habe diese Integration weiter verfeinert und möchte hier einen exemplarischen Workflow für diese Möglichkeit zeigen
WeiterlesenDiese Seite wird mit allen derzeit in meinem Self-Hosting Homelab betriebenen Services auf Docker-Basis befüllt (Eigenentwicklungen werden nicht genannt, sofern sie keine öffentlich verfügbaren Open-Source-Lösungen darstellen).
WeiterlesenEine neue Version der sQLshell ist verfügbar!
WeiterlesenManche nennen es Blog, manche Web-Seite - ich schreibe hier hin und wieder über meine Erlebnisse, Rückschläge und Erleuchtungen bei meinen Hobbies.
Wer daran teilhaben und eventuell sogar davon profitieren möchte, muss damit leben, daß ich hin und wieder kleine Ausflüge in Bereiche mache, die nichts mit IT, Administration oder Softwareentwicklung zu tun haben.
Ich wünsche allen Lesern viel Spaß und hin und wieder einen kleinen AHA!-Effekt...
PS: Meine öffentlichen Codeberg-Repositories findet man hier.