From 0f65ea8507217e3a733117ff15d586394ac4bc8b Mon Sep 17 00:00:00 2001 From: davpapp Date: Thu, 15 Mar 2018 22:27:44 -0400 Subject: [PATCH] Added random transformations to mouse movement --- .classpath | 1 + src/Cursor.java | 20 +++++----- src/CursorPath.java | 12 +++--- src/CursorPoint.java | 10 +++-- src/PathTransformer.java | 53 +++++++++++++++++++++++++++ src/Randomizer.java | 12 +++--- target/classes/Cursor.class | Bin 9317 -> 9399 bytes target/classes/CursorPath.class | Bin 5770 -> 6249 bytes target/classes/CursorPoint.class | Bin 2067 -> 2287 bytes target/classes/PathTransformer.class | Bin 0 -> 2568 bytes target/classes/Randomizer.class | Bin 1013 -> 826 bytes 11 files changed, 79 insertions(+), 29 deletions(-) create mode 100644 src/PathTransformer.java create mode 100644 target/classes/PathTransformer.class diff --git a/.classpath b/.classpath index 0f640cf..509575b 100644 --- a/.classpath +++ b/.classpath @@ -14,5 +14,6 @@ + diff --git a/src/Cursor.java b/src/Cursor.java index aa58899..ee4b30b 100644 --- a/src/Cursor.java +++ b/src/Cursor.java @@ -26,7 +26,6 @@ public class Cursor { public static final int MAXIMUM_CLICK_LENGTH = 230; private Robot robot; - private Randomizer randomizer; private Random random; private ArrayList> cursorPathsByDistance; @@ -36,7 +35,6 @@ public class Cursor { initializeCursorPathsByDistanceFromFile(Paths.CURSOR_COORDINATES_FILE_PATH); robot = new Robot(); - randomizer = new Randomizer(); random = new Random(); } @@ -72,11 +70,11 @@ public class Cursor { // TODO: make sure these are reasonable private int getRandomClickLength() { - return randomizer.nextGaussianWithinRange(MINIMUM_CLICK_LENGTH, MAXIMUM_CLICK_LENGTH); + return Randomizer.nextGaussianWithinRange(MINIMUM_CLICK_LENGTH, MAXIMUM_CLICK_LENGTH); } private int getRandomClickReleaseLength() { - return randomizer.nextGaussianWithinRange(MINIMUM_CLICK_LENGTH + 5, MAXIMUM_CLICK_LENGTH + 10); + return Randomizer.nextGaussianWithinRange(MINIMUM_CLICK_LENGTH + 5, MAXIMUM_CLICK_LENGTH + 10); } // END @@ -125,8 +123,8 @@ public class Cursor { } private Point getRandomPointInBoundingRectangle(Rectangle boundingRectangle) { - int x = randomizer.nextGaussianWithinRange(boundingRectangle.x, boundingRectangle.x + boundingRectangle.width); - int y = randomizer.nextGaussianWithinRange(boundingRectangle.y, boundingRectangle.y + boundingRectangle.height); + int x = Randomizer.nextGaussianWithinRange(boundingRectangle.x, boundingRectangle.x + boundingRectangle.width); + int y = Randomizer.nextGaussianWithinRange(boundingRectangle.y, boundingRectangle.y + boundingRectangle.height); return new Point(x, y); } @@ -165,9 +163,9 @@ public class Cursor { CursorPath cursorPathWithDistanceSet = chooseCursorPathToFollowBasedOnDistance(distanceToMoveCursor); CursorPath cursorPathWithDistanceAndAngleSet = cursorPathWithDistanceSet.getRotatedCopyOfCursorPath(angleToRotateCursorPathTo); - // TODO: Add randomization by parabola or similar - // CursorPath randomizedCursorPath = cursorPathWithDistanceAndAngleSet.getCopyOfCursorPathTransformedByParabola(); - followCursorPath(cursorPathWithDistanceAndAngleSet, startingPoint); + CursorPath cursorPathTransformed = cursorPathWithDistanceAndAngleSet.getCopyOfCursorPathTransformedByParabola(); + + followCursorPath(cursorPathTransformed, startingPoint); } public int getDistanceBetweenPoints(Point startingPoint, Point goalPoint) { @@ -243,12 +241,12 @@ public class Cursor { private Point randomizePoint(Point goalPoint, int xTolerance, int yTolerance) { Randomizer randomizer = new Randomizer(); - return new Point(goalPoint.x + randomizer.nextGaussianWithinRange(-xTolerance, xTolerance), goalPoint.y + randomizer.nextGaussianWithinRange(-yTolerance, yTolerance)); + return new Point(goalPoint.x + Randomizer.nextGaussianWithinRange(-xTolerance, xTolerance), goalPoint.y + Randomizer.nextGaussianWithinRange(-yTolerance, yTolerance)); } private Point randomizePoint(Point goalPoint, int xToleranceLeft, int xToleranceRight, int yTolerance) { Randomizer randomizer = new Randomizer(); - return new Point(goalPoint.x + randomizer.nextGaussianWithinRange(-xToleranceLeft, xToleranceRight), goalPoint.y + randomizer.nextGaussianWithinRange(-yTolerance, yTolerance)); + return new Point(goalPoint.x + Randomizer.nextGaussianWithinRange(-xToleranceLeft, xToleranceRight), goalPoint.y + Randomizer.nextGaussianWithinRange(-yTolerance, yTolerance)); } public void displayCursorPaths() { diff --git a/src/CursorPath.java b/src/CursorPath.java index ba7a6ee..6ad93ca 100644 --- a/src/CursorPath.java +++ b/src/CursorPath.java @@ -22,7 +22,6 @@ public class CursorPath { this.randomizer = new Randomizer(); } - // TODO: refactor public CursorPath(ArrayList cursorPoints, boolean setInitializiationOff) { this.cursorPoints = cursorPoints; @@ -74,15 +73,14 @@ public class CursorPath { return new CursorPath(rotatedCursorPoints, true); } - /*public CursorPath getCopyOfCursorPathTransformedByParabola() { - double[] parabolaEquation = randomizer.generateParabolaEquation(this.getCursorPathDistance()); + public CursorPath getCopyOfCursorPathTransformedByParabola() { + double[] parabolaEquation = PathTransformer.generateParabolaEquation(this.getCursorPathDistance()); ArrayList transformedCursorPoints = new ArrayList(); for (CursorPoint cursorPoint : this.cursorPoints) { - transformedCursorPoints.add(cursorPoint.getCursorPointTransformedBy(parabolaEquation)); + transformedCursorPoints.add(cursorPoint.getCursorPointTransformedByParabola(parabolaEquation)); } - return new CursorPath(transformedCursorPoints, true); - - }*/ + return new CursorPath(transformedCursorPoints, true); + } private int calculateCursorPathTimespan() { int sumPathTimespanMilliseconds = 0; diff --git a/src/CursorPoint.java b/src/CursorPoint.java index 594eb66..9feddb9 100644 --- a/src/CursorPoint.java +++ b/src/CursorPoint.java @@ -34,10 +34,12 @@ public class CursorPoint { return (new CursorPoint(rotatedX, rotatedY, delay)); } - public CursorPoint getCursorPointTransformedBy(double[] parabolaEquation) { - int rotatedX = (int) 5; - int rotatedY = (int) 5; - return (new CursorPoint(rotatedX, rotatedY, delay)); + public CursorPoint getCursorPointTransformedByParabola(double[] parabolaEquation) { + double transformationFactor = PathTransformer.getParabolaHeightAtPoint(parabolaEquation, this.getDistanceFromOrigin()); + int transformedX = (int) (transformationFactor * this.x); + int transformedY = (int) (transformationFactor * this.y); + int transformedDelay = (int) (transformationFactor * this.delay); + return (new CursorPoint(transformedX, transformedY, transformedDelay)); } public CursorPoint getCursorPointWithNewDelay(int delay) { diff --git a/src/PathTransformer.java b/src/PathTransformer.java new file mode 100644 index 0000000..a61a18e --- /dev/null +++ b/src/PathTransformer.java @@ -0,0 +1,53 @@ +import java.awt.Point; +import java.awt.geom.Point2D; + +import Jama.Matrix; + +public class PathTransformer { + + private static Point2D generatePeakForTransformationParabola(int pathDistance) { + double maxTransformationScale = 0.20; + double peakX = Randomizer.nextGaussianDoubleWithinRange(pathDistance / 3, 2 * pathDistance / 3); + double peakY = Randomizer.nextGaussianDoubleWithinRange(1 - maxTransformationScale, 1 + maxTransformationScale); + return new Point2D.Double(peakX, peakY); + } + + public static double[] generateParabolaEquation(int pathDistance) { + Point2D p1 = new Point2D.Double(0, 1); + Point2D p2 = generatePeakForTransformationParabola(pathDistance); + Point2D p3 = new Point2D.Double(pathDistance, 1); + return generateEquationForTransformationParabola(p1, p2, p3); + } + + private static double[] generateEquationForTransformationParabola(Point2D p1, Point2D p2, Point2D p3) { + if (p1.getX() == p2.getX() || p1.getX() == p3.getX() || p2.getX() == p3.getX()) { + double[] equation = {0.0, 0.0, 1.0}; + return equation; + } + double[][] lhsArray = {{0, 0, 1}, {p2.getX() * p2.getX(), p2.getX(), 1}, {p3.getX() * p3.getX(), p3.getX(), 1}}; + double[] rhsArray = {p1.getY(), p2.getY(), p3.getY()}; + Matrix lhs = new Matrix(lhsArray); + Matrix rhs = new Matrix(rhsArray, 3); + + System.out.println("(" + p1.getX() + "," + p1.getY() + "), (" + p2.getX() + "," + p2.getY() + "), (" + p3.getX() + "," + p3.getY() + ")"); + + Matrix ans = lhs.solve(rhs); + /*System.out.println("x = " + ans.get(0, 0)); + System.out.println("y = " + ans.get(1, 0)); + System.out.println("z = " + ans.get(2, 0));*/ + + double[] equation = {ans.get(0, 0), ans.get(1, 0), ans.get(2, 0)}; + return equation; + } + + public static double getParabolaHeightAtPoint(double[] parabolaEquation, double x) { + return (parabolaEquation[0] * x * x + parabolaEquation[1] * x + parabolaEquation[2]); + } + + public static void main(String[] args) { + Point2D p1 = new Point2D.Double(0, 1); + Point2D p3 = new Point2D.Double(200, 1); + Point2D p2 = generatePeakForTransformationParabola(200); + generateEquationForTransformationParabola(p1, p2, p3); + } +} diff --git a/src/Randomizer.java b/src/Randomizer.java index e5c9b2f..259ab44 100644 --- a/src/Randomizer.java +++ b/src/Randomizer.java @@ -5,6 +5,10 @@ import java.util.Random; public class Randomizer { public static int nextGaussianWithinRange(double rangeBegin, double rangeEnd) { + return (int) nextGaussianDoubleWithinRange(rangeBegin, rangeEnd); + } + + public static double nextGaussianDoubleWithinRange(double rangeBegin, double rangeEnd) { Random random = new Random(); double rangeMean = (rangeEnd + rangeBegin) / 2.0; double rangeSTD = (rangeEnd - rangeMean) / 3.0; @@ -13,12 +17,6 @@ public class Randomizer { while (result > rangeEnd || result < rangeBegin) { result = random.nextGaussian() * rangeSTD + rangeMean; } - return (int) result; - } - public static Point generatePeakForTransformationParabola(int pathDistance) { - double maxTransformationScale = 0.2; - int peakX = nextGaussianWithinRange(0, pathDistance); - int peakY = nextGaussianWithinRange(0, pathDistance * maxTransformationScale); - return new Point(peakX, peakY); + return result; } } diff --git a/target/classes/Cursor.class b/target/classes/Cursor.class index 809b9bf76bfc9377702c3bb247479743d9c494b5..aad5c08f4afbedca5af6d301eaf2e1ea9985c115 100644 GIT binary patch literal 9399 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 literal 9317 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 NR@sp`0ZKWX{3YBs4lw`# diff --git a/target/classes/CursorPath.class b/target/classes/CursorPath.class index 140199be9826dcbb42cac4156413025072caa07c..1acd8a4da7731827676f25467b640a4979ecf12e 100644 GIT binary patch delta 1577 zcmZ`&YfzMB6n@^_g>Nt40()aw1^ir1mSuN9Bx69W5zw&#BqNBJar%e_mSA_=U7DC_ zT7NWCT9EK|!^|QrD@nz4Q7Oc37L`%iJ-gZun`ZjspZ;j)+rp7+`F z?(kkL(a&5yIR&5$e~jx-2{BivFIp80^>6N|>I+AL;RZR{)fkook&a-v*Vn$jzd;Vm zZNVOyA;VQzJKt2P2gBnc1|Tum6qXde+8>F^fqQ&IR%q@L z^Yp6LY6g4MF@M!L{R$)^V1%C$oauW+ynq+^--1{3l8C)r(&lSk7V!#SudUO(F5&>^ z+5*iRA`bE?Ivf@;&M#_Xe4EZ&a$Lj-WHSiu{z#}t?$==wZwq*fvUT<5zCrux3#HeXl@c1WRwqzXNz@xF*Gd_aElQN7jBq@+F+ z@eyWttKRMSgu&pE8vVV#NJtJO0B4ozXMB&owB&OJ(IZ{`Zz?1$zGTo*f0r*RS4axi zS4#KS47o0{kTbpAK>zYIz7_EuzUNNE^7J3^lYsNQ){tZQSrHU@>&ag$a3ZARkMygcllPMki&L(N&hijvM$f<7rbNv~W{b5nL2Y z;obX42FqJX*-{v>iu%e3aU;sR1a2>j=4)>bLOiSu?h3%GMD7 z24dSt$PHBA#Jf!X7{hk#pv|?=8<-`1X%uVRaXfY$gSC@b*D`_I>fA@MIF2n9n*GRe zXilJskqcGz9&b?`PsVX~vyyCQ*mdQ?Bv^|_Gl7Q=6Oo350*jDNzjY*`o+NBW0UEH9 zVkNdGyzP& za{}E?Kr>+Fv!;4e6E!tc%bjF+Gf~~ei_Lbnn{PDVoc(+pql06_ph`$hCuU$P-)%1P zQfc3z|5Vz@yX1UDXHqZ2etHs@%tfq&OBS12c_8JMRk7{`z1CFktLU6_yOE+Y&EcNF zYa_5w7#o2|;m`=uG-GN-mO3FTgCY<_212k=oEPDkBUVgMgOLw7ketB(w3MG6Hd`aFqXJb=tQr0H>ucGr&u2E9?(n zw|0yNsrKk~YjHkibH^q;b4&Br5uBXFbc^|&3A~rwwMZOi%pb?`DSdjB^KA0GR5^c< zJQu`qE{<>F_<``|Unq{J982!p0J$|t4{ZQW>_i)eXo-dsin5{NcVuU-_>Go|C059! UtdId}TA}3f80qkP!h#F`0(x_6fdBvi delta 1049 zcmZuvTTqN)7=FHOwWqJu*8ck|I=VQ)?8LnKoGvm^gG4lQ~iE;D2hv#{p@BO~_{hVEsY47`S^(KHxc-v*a zWuZ@2EqbTKF6<_&Ez?{lv4^JGlFaoIM8!70`JluhYPY4E8zq|PJv&Dvn#p9Zrr}l( zRok<&PfDDEN5E1NijCk=I!rlfv0lZhah7q>=8tfS z96pONmD*z8xu!E?1~km%su|3gMK!KIWe#WOvVH-p&Ex6<#tJDE7oyI1ziTl}C}PDW z+*J|Mv6P%{RqUWN_e@WXj@sIG9=(Y#0-_=KTt=JS8JP_2z3>lG!s37&U|IS4JdOnR z@t(YOXNc9*?^dFWtx=U+wR8yAJ;>cp0Y&j2Xh6KT7Yz-NYz{YIn7KX5Xd`N%4JTWm z0!~yyVf*LOgx^AI2e?Azz7NqoWs2B9-<2fML?bbC!4$7w!eHUGs<&`#A`-R8T35`o74FWoqO z4IM>soxSMx_F$xrh>lBfS9DzC&}aBJfBDIVfBTo8uH&wb2Rfc`ecTJSU4ws>$U_6_ z_@DOh&edTo>aiSzH9QvM-#jo)X0!J9Qxsm(SMc-Hj4yP-)DH)uEF4>~2)ug&uNg4m F%};%N;wk_D diff --git a/target/classes/CursorPoint.class b/target/classes/CursorPoint.class index 9493941d1b5a58d98e9c9402cfe6b45a817d728b..a88c33dfd5e6c4e0223713fcd47065b0f64cb08d 100644 GIT binary patch delta 765 zcmZ8eO-~b16g_YHF)&UnrB` z-JWV$P1DMmM#EmTc%xZ1t-4-Hw{zU$Yi5lz1Y^aFmfbg(Qot~w;s}l^xQ?4LZZHIL z+^+t0uvAPU%@Cm#dsef$QK{OKw!4hMUnb*>mSGrpw!3Wld5hbI*Qv61nUQ zU$;$*LH$GPrImlL1v1;$&FD4Wc3w#n$M52vjCtyA@E1H;-$n>9MXX9O@KH=J2EdCLJmz%Gzcm)ay=jzJ?7Cm!^@emci6ga}oErjViU??(dKG|rJl2q!T^V_p(QaEp+S zqAOI%0T*$}jjQ$?cjGeD-O0vplfbeI delta 565 zcmZ8dT}vBL5IuKy)$Mkv`M?^i+6dBo5Qr5)Nz`ggQme5|8?{ZN)*G{imAI>$O(9RE zzhVD~UnHR5lTZCKed}X!HtU1Pg}G;D&Y3yPU%%s1{`|kb1u%nyb7f9geO1@&m6p{s zt)i(L_NK*+=AmgFYqd<9!5^(;V}(ckG=uMiTfAyEcfMb7yxhPU1`3_t4HmTuHDExl3GEEyjdJ%Po@&yuZI|&;#U~VVx$|A&#;w5EqPlaKGC?h@6mJJ z6~A(p2KjPewTm@!HQ2#M;ByBh!hRQTdpJnAgKr(| aoRiA)tn{`_;VnJauj);F@XNq^%<4TDN_3t8 diff --git a/target/classes/PathTransformer.class b/target/classes/PathTransformer.class new file mode 100644 index 0000000000000000000000000000000000000000..d62fa82d405d2b446c718cf04d56287bfa5e8d9f GIT binary patch literal 2568 zcmb7GTXR!Y6#jN@Ck=tp&|5-Lij*e35Lyr<1t}0pi*2b57Gk~arib*<kX%^F07jEeVM<8^@Fb(H*0e>q$w+u*?~0%LS_>1ro`L)TEoByz8Wkx>ZikScd5gXGcQk{(Pu@k0RKIHVv%;ky&Xj zT4m$DZb#9M4uJ!vzT#Y#wQALnX4a}Px$6egGO1Z)-oa!xo62g43v}%m=CEfJK_~XB z+jli=Se9so)yx`IN16q?5)%ULWx2BTDmj)#3A41T@UAx+dtRV(y#|fzUbfzS*k z=jXGjImH@Ptn)4_JCT}e!bM!tz?3&ZJ9n?@=;bKJaG6o98d2Wn-NmrdGd3ZWb8KCf zM>MdH9UE_gY{M*Gs2Qb2-45do_A(j4IHpw4X?kgt*nL2r@`y@n95Ye8iCLy4D;3>b zB*K!}2ADS~H$0DoagDAAl;NB*ye=@flWQ;O9UTx;fz-gkWEk^wl~S(Wjp9ALFA#Pt zFE@eKsycE5{jSIY$x{gh%DMhbLB8!?H9?zUAcnbG2q?L!$;KDt}az zc9Eg??V!h5l(TAfK_4^J^KacY0gtL8bAn8moPrlivRc*YDCEr=bkn!rjS(WniOW^v zIn7;g5BAj&{*iRYCT+FheuKIjUSPxUFG+CAa4 zD`Wgqo(;DkrKe^yfoI6%2kSVKVQVt^cpy~AxlHIMTzH1@eC$dclPc|1T#e;c@s@I- z=o_R}>@_~=b-oI_##ccU{WkSi@y;qfhzH}LRp=SbO)0MV{i`sTRa^^rR4}duT`Cif zhgFAEWx*P&esr+Oo&0wrj+5Ar2k63M_U3y6q>h841&2fez2YpAuJ%o3DMsC%(U_F# z8Np0S^~^_4K9uRpPhUU7ghlExzC(q)0R4W0+pr0*I=)7gREYkMz#*kEir=*jqekZe z_zt(Q=XY?o0mCuEP`$EPpMR)#xp%oQz^%|194h=k?&ha8;AY!S9<{N;*y3^@O|OYj(psCe)M~57RK#|0FbYy3P$&-KB95_^8m-CDR^0jzgwjDl z9K}U&Q>ow&@F(~S+?*U-tX~vL@8Ise@4kEA=e}4!EWiEqUjeLQsVmala?6xuDfQK0KX|5Szs zDO@7!V1!GNe-V^t6wD&aK^YC;Aeec(r*S*I6yNM@AVd>jhWQNj;+`ZAIdCFEAc8Sk z6I7!X#4$yCpc15bNWllFk5TQNY=Yj(3$*+zTcW9p^*GYL#gd^%WOR0- zi&aaygPQD&7IewHycvAzl5urC_|T;XvJ?Jbas&b>6gW E0q3?v4gdfE delta 610 zcmY*Wzi-n}5dL2Lt8q;*PEy>`mQZMMLLm@Jr4*@D2vMvsL;@i)J|a^>TDx*w=$4TU zR5Bpg5eowoMXkgR{{jPl1uO8`u=Gyv?tJgNyYHP3^k0Vg{rACF083cjGv6x)PP`W7 zhK;&S9PlGzdsIM5C{iuyCg^_2LV>Rl4(i z2FvOpYMq&~x{t+A#N;yi4hI88+7fA&sS_F9T9&Vn$YtKEq;Q2tWSEA_q(bO8t};@Y JUdkF?`wJD(XcPbd