From f7fa74f4e3ea315f15726ca1b665ad390b512668 Mon Sep 17 00:00:00 2001 From: Nikos M Date: Fri, 24 Feb 2023 15:49:13 +0200 Subject: [PATCH] v.1.0.0 contd * line.parallelTo, line.perpendicularTo * some more utils for 3D * update manual --- build/Geometrize.js | 91 +++++++++++++++++++++++++++++++++++++++-- build/Geometrize.min.js | 4 +- manual.md | 57 ++++++++++++++++++++++++++ src/Line.js | 27 +++++++++++- src/Point2D.js | 29 +++++++++++++ src/utils.js | 31 ++++++++++++++ 6 files changed, 231 insertions(+), 8 deletions(-) diff --git a/build/Geometrize.js b/build/Geometrize.js index e75afa9..df4c0e6 100644 --- a/build/Geometrize.js +++ b/build/Geometrize.js @@ -2,14 +2,14 @@ * Geometrize * computational geometry and rendering library for JavaScript * -* @version 1.0.0 (2023-02-23 11:55:45) +* @version 1.0.0 (2023-02-24 15:46:51) * https://github.com/foo123/Geometrize * **//** * Geometrize * computational geometry and rendering library for JavaScript * -* @version 1.0.0 (2023-02-23 11:55:45) +* @version 1.0.0 (2023-02-24 15:46:51) * https://github.com/foo123/Geometrize * **/ @@ -915,6 +915,10 @@ var Point2D = makeClass(Object2D, { self.$super('dispose'); }; }, +/**[DOC_MD] + * **Methods:** + * +[/DOC_MD]**/ name: 'Point2D', clone: function() { return new Point2D(this.x, this.y); @@ -931,6 +935,9 @@ var Point2D = makeClass(Object2D, { xmax: self.x }; }, +/**[DOC_MD] + * * `eq(point: Point2D|Object{x,y}|[x,y]): Bool` determine if equals another point-like +[/DOC_MD]**/ eq: function(other) { var self = this; if (other instanceof Point2D) @@ -947,26 +954,48 @@ var Point2D = makeClass(Object2D, { } return false; }, +/**[DOC_MD] + * * `add(other: Point2D): Point2D` add points coordinate-wise + * * `add(other: Number): Point2D` add number to point coordinates +[/DOC_MD]**/ add: function(other) { var self = this; return other instanceof Point2D ? new Point2D(self.x+other.x, self.y+other.y) : new Point2D(self.x+Num(other), self.y+Num(other)); }, +/**[DOC_MD] + * * `mul(other: Number): Point2D` multiply number to point coordinates +[/DOC_MD]**/ mul: function(other) { other = Num(other); return new Point2D(this.x*other, this.y*other); }, +/**[DOC_MD] + * * `dot(other: Point2D): Number` dot product of points +[/DOC_MD]**/ dot: function(other) { return dotp(this.x, this.y, other.x, other.y); }, +/**[DOC_MD] + * * `cross(other: Point2D): Number` cross product of points +[/DOC_MD]**/ cross: function(other) { return crossp(this.x, this.y, other.x, other.y); }, +/**[DOC_MD] + * * `angle(other: Point2D): Number` angle between points +[/DOC_MD]**/ angle: function(other) { return angle(this.x, this.y, other.x, other.y); }, +/**[DOC_MD] + * * `between(p1: Point2D, p1: Point2D): Bool` check if point is on line segment defined by points p1,p2 +[/DOC_MD]**/ between: function(p1, p2) { return point_on_line_segment(this, p1, p2); }, +/**[DOC_MD] + * * `distanceToLine(p1: Point2D, p1: Point2D): Number` distance of point to line defined by points p1,p2 +[/DOC_MD]**/ distanceToLine: function(p1, p2) { return point_line_distance(this, p1, p2); }, @@ -2368,6 +2397,10 @@ var Line = makeClass(Bezier2D, { return self.$super('isChanged', arguments); }; }, +/**[DOC_MD] + * **Methods:** + * +[/DOC_MD]**/ name: 'Line', clone: function() { var self = this; @@ -2434,8 +2467,27 @@ var Line = makeClass(Bezier2D, { } return false; }, - distanceToPoint: function(point) { - return point_line_segment_distance(point, this._points[0], this._points[1]); +/**[DOC_MD] + * * `distanceToPoint(p: Point2D): Number` distance of point to this line segment +[/DOC_MD]**/ + distanceToPoint: function(p) { + return point_line_segment_distance(p, this._points[0], this._points[1]); + }, +/**[DOC_MD] + * * `isParallelTo(l: Line): Bool` determine if line is parallel to line l + * * `isParallelTo(p: Point2D, q: Point2D): Bool` determine if line is parallel to line defined by points p,q +[/DOC_MD]**/ + isParallelTo: function(p, q) { + var _p = this._points; + return p instanceof Line ? lines_parallel(_p[0], _p[1], p._points[0], p._points[1]) : lines_parallel(_p[0], _p[1], p, q); + }, +/**[DOC_MD] + * * `isPerpendicularTo(l: Line): Bool` determine if line is perpendicular to line l + * * `isPerpendicularTo(p: Point2D, q: Point2D): Bool` determine if line is perpendicular to line defined by points p,q +[/DOC_MD]**/ + isPerpendicularTo: function(p, q) { + var _p = this._points; + return p instanceof Line ? lines_perpendicular(_p[0], _p[1], p._points[0], p._points[1]) : lines_perpendicular(_p[0], _p[1], p, q); }, bezierPoints: function(t) { if (arguments.length) t = clamp(t, 0, 1); @@ -4949,6 +5001,14 @@ function point_line_segment_distance(p0, p1, p2) t = stdMath.max(0, stdMath.min(1, ((x - x1)*dx + (y - y1)*dy) / d)); return hypot(x - x1 - t*dx, y - y1 - t*dy); } +function lines_parallel(p1, p2, q1, q2) +{ + return is_almost_equal((p2.y - p1.y)*(q2.x - q1.x), (q2.y - q1.y)*(p2.x - p1.x)); +} +function lines_perpendicular(p1, p2, q1, q2) +{ + return is_almost_equal((p2.y - p1.y)*(q2.y - q1.y), -(p2.x - p1.x)*(q2.x - q1.x)); +} function point_on_line_segment(p, p1, p2) { var t = 0, @@ -5478,6 +5538,29 @@ function polyline_area(polyline_points) } return area; } +function plane_plane_intersection(a, b, c, d, k, l, m, n) +{ + var D = a*l - b*k; + // none, line or single point + if (is_strictly_equal(D, 0)) return false; // none + // one or two points (a line) + // x:(b*(m*t + n) - l*(c*t+d))/D, y:(k*(c*t+d) - a*(m*t + n))/D, z:t + return [ + {x:(b*n - l*d)/D, y:(k*d - a*n)/D, z:0}, + {x:(b*(m+n) - l*(c+d))/D, y:(k*(c+d) - a*(m+n))/D, z:1} + ]; +} +function normal_to_plane(a, b, c, d) +{ + return {x:a, y:b, z:c}; +} +function normal_to_points(p1, p2, p3) +{ + return crossp3( + p2.x - p1.x, p2.y - p1.y, p2.z - p1.z, + p3.x - p1.x, p3.y - p1.y, p3.z - p1.z + ); +} function convex_hull(points) { var pc = points.length, p0, i0, i, p, ps, convexHull, hullSize; diff --git a/build/Geometrize.min.js b/build/Geometrize.min.js index 192b7d6..0fdc376 100644 --- a/build/Geometrize.min.js +++ b/build/Geometrize.min.js @@ -2,7 +2,7 @@ * Geometrize * computational geometry and rendering library for JavaScript * -* @version 1.0.0 (2023-02-23 11:55:45) +* @version 1.0.0 (2023-02-24 15:46:51) * https://github.com/foo123/Geometrize * -**/!function(t,n,e){"use strict";"object"==typeof module&&module.exports?(module.$deps=module.$deps||{})&&(module.exports=module.$deps[n]=e.call(t)):"function"==typeof define&&define.amd&&"function"==typeof require&&"function"==typeof require.specified&&require.specified(n)?define(n,["module"],function(n){return e.moduleUri=n.uri,e.call(t)}):n in t||(t[n]=e.call(t)||1)&&"function"==typeof define&&define.amd&&define(function(){return t[n]})}("undefined"!=typeof self?self:this,"Geometrize",function(){"use strict";var x=Object.prototype.hasOwnProperty,t=Object.prototype.toString,C=Object.defineProperty,_=Math,w=_.abs,$=_.sqrt,P=_.pow,S=_.PI,A=2*S,l=1e-6,G=S/4,r=$(2),c=$(3),f=20,h=.01,g={},y=function(){},n="undefined"!=typeof global&&"[object global]"===t.call(global),p="undefined"!=typeof window&&"[object Window]"===t.call(window),d=(n?global:p&&window,{VERSION:"1.0.0",Math:{},Geometry:{}});function e(n,t,e){arguments.length<2&&(t=n,n=null);var r,i,o,u=x.call(t,"constructor")?t.constructor:function(){};for(r in n?(u.prototype=Object.create(n.prototype),u.prototype.$super=(i=n,o={},function(n,t){var e=":"+n;return 1===o[e]?(i.prototype.$super||y).call(this,n,t):(o[e]=1,t=("constructor"===n?i:i.prototype[n]||y).apply(this,t||[]),o[e]=0,t)})):u.prototype.$super=y,u.prototype.constructor=u,t)x.call(t,r)&&"constructor"!==r&&(u.prototype[r]=t[r]);if(e)for(r in e)x.call(e,r)&&(u[r]=e[r]);return u}var i={$changed:!1,$cb:null,dispose:function(){this.$cb=null},isChanged:function(n){return arguments.length?(this.$changed=!!n,this):this.$changed},onChange:function(n,t){var e,r=this;return!1===t&&(_t(n)||vt(n))?r.$cb&&-1!==(e=(vt(n)?r.$cb.map(function(n){return xt(n.id)}):r.$cb).indexOf(n))&&r.$cb.splice(e,1):_t(n)&&(r.$cb||(r.$cb=[]),-1===(e=r.$cb.indexOf(n))&&r.$cb.push(n)),r},triggerChange:function(){var t=this;return t.$cb&&t.$cb.forEach(function(n){n(t)}),t}},o=e(null,ot(null,{constructor:function n(t){var e=this,r=null,i=null;return t instanceof n?t:e instanceof n?(i=ot(r=n.Properties,{},n.Defaults),Ct(t)&&(i=ot(r,i,t)),r.forEach(function(t){C(e,t,{get:function(){return i[t]},set:function(n){i[t]!==n&&(i[t]=n,e.isChanged(!0),e.triggerChange())},enumerable:!0,configurable:!1})}),e.toObj=function(){return r.reduce(function(n,t){return n[t]=i[t],n},{})},void e.isChanged(!0)):new n(t)},clone:function(){return new o(this.toObj())},toSVG:function(){var e=this.toObj();return Object.keys(e).reduce(function(n,t){return n+t+":"+xt(e[t])+";"},"")},toCanvas:function(n){var t=this;return n.lineCap=t["stroke-linecap"],n.lineJoin=t["stroke-linejoin"],n.lineWidth=t["stroke-width"],n.fillStyle="none"===t.fill?"transparent":t.fill,n.strokeStyle=t.stroke,n}},i),{Properties:["stroke-width","stroke","stroke-opacity","stroke-linecap","stroke-linejoin","fill","fill-opacity","fill-rule"],Defaults:{"stroke-width":1,stroke:"#000000","stroke-opacity":1,"stroke-linecap":"butt","stroke-linejoin":"miter",fill:"none","fill-opacity":1,"fill-rule":"evenodd"}});d.Style=o;var j=e(null,ot(null,{constructor:function e(r){var i=this;return r instanceof e?r:i instanceof e?(r=pt(r),i.dispose=function(){r=null,e.prototype.dispose.call(i)},i.clone=function(){return new e(r)},i.val=function(n){if(arguments.length){n=n instanceof e?n.val():pt(n);var t=!et(r,n);return r=n,t&&(i.isChanged(!0),i.triggerChange()),i}return r},i.valueOf=function(){return r.valueOf()},i.toString=function(){return xt(r)},void i.isChanged(!0)):new e(r)},clone:null,val:null,valueOf:null,toString:null},i)),m=e(null,{constructor:function n(t,e,r,i,o,u){var a=this;return t instanceof n?t:a instanceof n?void(bt(t)&&6<=t.length?(a.$00=pt(t[0]),a.$01=pt(t[1]),a.$02=pt(t[2]),a.$10=pt(t[3]),a.$11=pt(t[4]),a.$12=pt(t[5])):(a.$00=t,a.$01=e,a.$02=r,a.$10=i,a.$11=o,a.$12=u)):new n(t,e,r,i,o,u)},$00:1,$01:0,$02:0,$10:0,$11:1,$12:0,clone:function(){var n=this;return new m(n.$00,n.$01,n.$02,n.$10,n.$11,n.$12)},eq:function(n){if(n instanceof m){var t=this;return et(t.$00,n.$00)&&et(t.$01,n.$01)&&et(t.$02,n.$02)&&et(t.$10,n.$10)&&et(t.$11,n.$11)&&et(t.$12,n.$12)}return!1},add:function(n){var t=this;return n instanceof m?new m(t.$00+n.$00,t.$01+n.$01,t.$02+n.$02,t.$10+n.$10,t.$11+n.$11,t.$12+n.$12):(n=pt(n),new m(t.$00+n,t.$01+n,t.$02+n,t.$10+n,t.$11+n,t.$12+n))},mul:function(n){var t=this,e=t.$00,r=t.$01,i=t.$02,o=t.$10,u=t.$11,a=t.$12;if(n instanceof m){var s=n.$00,l=n.$01,c=n.$02,f=n.$10,h=n.$11,t=n.$12;return new m(e*s+r*f,e*l+r*h,e*c+r*t+i,o*s+u*f,o*l+u*h,o*c+u*t+a)}return n=pt(n),new m(e*n,r*n,i*n,o*n,u*n,a*n)},det:function(){var n=this,t=n.$00,e=n.$01,r=(n.$02,n.$10),i=n.$11;n.$12;return t*i-e*r},inv:function(){var n,t,e,r=this,i=r.$00,o=r.$01,u=r.$02,a=r.$10,s=r.$11,l=r.$12,c=i*s-o*a;return tt(c,0)?null:new m(n=s/c,t=-o/c,-n*u-t*l,e=-a/c,r=i/c,-e*u-r*l)},transform:function(n,t){var e=this,r=n.x,i=n.y,n=e.$00*r+e.$01*i+e.$02,e=e.$10*r+e.$11*i+e.$12;return t?(t.x=n,t.y=e):t=new V(n,e),t},getTranslation:function(){return{x:this.$02,y:this.$12}},getRotationAngle:function(){return _.atan2(this.$10,this.$00)},getScale:function(){var n=this.$00,t=-this.$01,e=this.$10,r=this.$11;return{x:nt(n)*Qn(n,e),y:nt(r)*Qn(t,r)}},toArray:function(){var n=this;return[n.$00,n.$01,n.$02,n.$10,n.$11,n.$12,0,0,1]},toSVG:function(){var n=this;return"matrix("+xt(n.$00)+" "+xt(n.$10)+" "+xt(n.$01)+" "+xt(n.$11)+" "+xt(n.$02)+" "+xt(n.$12)+")"},toCSS:function(){var n=this;return"matrix("+xt(n.$00)+","+xt(n.$10)+","+xt(n.$01)+","+xt(n.$11)+","+xt(n.$02)+","+xt(n.$12)+")"},toCanvas:function(n,t){var e=this;return n[!0===t?"setTransform":"transform"](e.$00,e.$10,e.$01,e.$11,e.$02,e.$12),n},toTex:function(){return m.arrayTex(this.toArray(),3,3)},toString:function(){return m.arrayString(this.toArray(),3,3)}},{eye:function(){return new m(1,0,0,0,1,0)},translate:function(n,t){return new m(1,0,pt(n),0,1,pt(t))},rotate:function(n,t,e){e=pt(e||0),t=pt(t||0),n=pt(n||0);var r=_.cos(n),n=_.sin(n);return new m(r,-n,t-r*t+n*e,n,r,e-r*e-n*t)},scale:function(n,t,e,r){return r=pt(r||0),e=pt(e||0),n=pt(n),t=pt(t),new m(n,0,-n*e+e,0,t,-t*r+r)},reflectX:function(){return new m(-1,0,0,0,1,0)},reflectY:function(){return new m(1,0,0,0,-1,0)},shearX:function(n){return new m(1,pt(n),0,0,1,0)},shearY:function(n){return new m(1,0,0,pt(n),1,0)},arrayTex:function(n,t,e){for(var r="\\begin{pmatrix}",i=0;iS,fs:0t.x?n:t),(!l&&(dt.y?n:t),{ymin:y.y,xmin:g.x,ymax:h.y,xmax:a.x}}),configurable:!1}),C(l,"_hull",{get:function(){var t,n,e,r,i,o,u,a;return null==b&&(t=l.center,n=l.rX,e=l.rY,r=l.theta,i=l.dtheta,o=l.start,u=l.end,a=_.abs(i)+G=A&&n.closePath()},toTex:function(){var n=this;return"\\text{Arc: }\\left("+[dt(n.start),dt(n.end),xt(n.radiusX),xt(n.radiusY),xt(n.angle)+"\\text{°}",xt(n.largeArc?1:0),xt(n.sweep?1:0)].join(",")+"\\right)"},toString:function(){var n=this;return"Arc("+[xt(n.start),xt(n.end),xt(n.radiusX),xt(n.radiusY),xt(n.angle)+"°",xt(n.largeArc),xt(n.sweep)].join(",")+")"}});d.Arc=D;var Q=e(i,{constructor:function n(t,e){var r=this,i=null,o=null,u=null,a=null;return t instanceof n?t:r instanceof n?(i=new j(_.abs(pt(e))),r.$super("constructor",[[t],{radius:i}]),C(r,"center",{get:function(){return r.points[0]},set:function(n){r.points[0]=n},enumerable:!0,configurable:!1}),C(r,"radius",{get:function(){return i.val()},set:function(n){i.val(_.abs(pt(n))),i.isChanged()&&(r.isChanged(!0),r.triggerChange())},enumerable:!0,configurable:!1}),C(r,"radiusX",{get:function(){return r.radius},set:function(n){r.radius=n},enumerable:!0,configurable:!1}),C(r,"radiusY",{get:function(){return r.radius},set:function(n){r.radius=n},enumerable:!0,configurable:!1}),C(r,"rX",{get:function(){return r.radius},enumerable:!0,configurable:!1}),C(r,"rY",{get:function(){return r.radius},enumerable:!0,configurable:!1}),C(r,"angle",{get:function(){return 0},set:function(n){},enumerable:!0,configurable:!1}),C(r,"theta",{get:function(){return 0},enumerable:!1,configurable:!1}),C(r,"dtheta",{get:function(){return A},enumerable:!1,configurable:!1}),C(r,"cs",{get:function(){return[1,0]},enumerable:!1,configurable:!1}),C(r,"length",{get:function(){return null==o&&(o=A*i.val()),o},enumerable:!0,configurable:!1}),C(r,"area",{get:function(){return null==u&&(u=S*i.val()*i.val()),u},enumerable:!0,configurable:!1}),C(r,"_bbox",{get:function(){var n,t;return null==a&&(n=r.center,t=i.val(),a={ymin:n.y-t,xmin:n.x-t,ymax:n.y+t,xmax:n.x+t}),a},enumerable:!1,configurable:!1}),void(r.isChanged=function(n){return!0===n&&(a=u=o=null),r.$super("isChanged",arguments)})):new n(t,e)},name:"Circle",clone:function(){return new Q(this.center.clone(),this.radius)},transform:function(n){var t=this.center,e=this.radius,r=t.transform(n),n=new V(t.x+e,t.y+e).transform(n);return new Q(r,qn(r,n))},isClosed:function(){return!0},intersects:function(n){if(n instanceof V)return!!this.hasPoint(n)&&[n];if(n instanceof Q){var t=function(n,t,e,r){{t"+xt(r)+"":"/>");return t}function at(u,a,s,i){if(_t(u.onChange))return u;function l(t){u.$cb.forEach(function(n){n(t)})}var c=!0;i=i||lt;var f=function(n,t){for(var e=n;eS,fs:0t.x?n:t),(!l&&(dt.y?n:t),{ymin:y.y,xmin:g.x,ymax:h.y,xmax:a.x}}),configurable:!1}),C(l,"_hull",{get:function(){var t,n,e,r,i,o,u,a;return null==b&&(t=l.center,n=l.rX,e=l.rY,r=l.theta,i=l.dtheta,o=l.start,u=l.end,a=_.abs(i)+G=A&&n.closePath()},toTex:function(){var n=this;return"\\text{Arc: }\\left("+[vt(n.start),vt(n.end),dt(n.radiusX),dt(n.radiusY),dt(n.angle)+"\\text{°}",dt(n.largeArc?1:0),dt(n.sweep?1:0)].join(",")+"\\right)"},toString:function(){var n=this;return"Arc("+[dt(n.start),dt(n.end),dt(n.radiusX),dt(n.radiusY),dt(n.angle)+"°",dt(n.largeArc),dt(n.sweep)].join(",")+")"}});d.Arc=D;var Q=e(i,{constructor:function n(t,e){var r=this,i=null,o=null,u=null,a=null;return t instanceof n?t:r instanceof n?(i=new j(_.abs(mt(e))),r.$super("constructor",[[t],{radius:i}]),C(r,"center",{get:function(){return r.points[0]},set:function(n){r.points[0]=n},enumerable:!0,configurable:!1}),C(r,"radius",{get:function(){return i.val()},set:function(n){i.val(_.abs(mt(n))),i.isChanged()&&(r.isChanged(!0),r.triggerChange())},enumerable:!0,configurable:!1}),C(r,"radiusX",{get:function(){return r.radius},set:function(n){r.radius=n},enumerable:!0,configurable:!1}),C(r,"radiusY",{get:function(){return r.radius},set:function(n){r.radius=n},enumerable:!0,configurable:!1}),C(r,"rX",{get:function(){return r.radius},enumerable:!0,configurable:!1}),C(r,"rY",{get:function(){return r.radius},enumerable:!0,configurable:!1}),C(r,"angle",{get:function(){return 0},set:function(n){},enumerable:!0,configurable:!1}),C(r,"theta",{get:function(){return 0},enumerable:!1,configurable:!1}),C(r,"dtheta",{get:function(){return A},enumerable:!1,configurable:!1}),C(r,"cs",{get:function(){return[1,0]},enumerable:!1,configurable:!1}),C(r,"length",{get:function(){return null==o&&(o=A*i.val()),o},enumerable:!0,configurable:!1}),C(r,"area",{get:function(){return null==u&&(u=S*i.val()*i.val()),u},enumerable:!0,configurable:!1}),C(r,"_bbox",{get:function(){var n,t;return null==a&&(n=r.center,t=i.val(),a={ymin:n.y-t,xmin:n.x-t,ymax:n.y+t,xmax:n.x+t}),a},enumerable:!1,configurable:!1}),void(r.isChanged=function(n){return!0===n&&(a=u=o=null),r.$super("isChanged",arguments)})):new n(t,e)},name:"Circle",clone:function(){return new Q(this.center.clone(),this.radius)},transform:function(n){var t=this.center,e=this.radius,r=t.transform(n),n=new V(t.x+e,t.y+e).transform(n);return new Q(r,Nn(r,n))},isClosed:function(){return!0},intersects:function(n){if(n instanceof V)return!!this.hasPoint(n)&&[n];if(n instanceof Q){var t=function(n,t,e,r){{t"+dt(r)+"":"/>");return t}function lt(u,a,s,i){if($t(u.onChange))return u;function l(t){u.$cb.forEach(function(n){n(t)})}var c=!0;i=i||ft;var f=function(n,t){for(var e=n;e