diff --git a/debug/viewer.html b/debug/viewer.html index b1d951648..b9d9d166f 100644 --- a/debug/viewer.html +++ b/debug/viewer.html @@ -36,6 +36,7 @@ + @@ -62,7 +63,8 @@ model: polygonstackbox - stackbox + Rimbox + starbox combine logo smile diff --git a/debug/viewer.js b/debug/viewer.js index 43161279c..c6faa1760 100644 --- a/debug/viewer.js +++ b/debug/viewer.js @@ -10,7 +10,7 @@ var Viewer = { //apply slider parameters if (typeof index !== 'undefined') { - Viewer.Params[index] = makerjs.round(arg, .001); + Viewer.Params[index] = arg; } var model = makerjs.kit.construct(Viewer.Constructor, Viewer.Params); @@ -155,17 +155,35 @@ var Viewer = { var label = new makerjs.exporter.XmlTag('label', { "for": id, title: attrs.title }); label.innerText = attrs.title + ': '; - if (attrs.type == 'range') { - attrs.title = attrs.value; - var input = new makerjs.exporter.XmlTag('input', attrs); - input.attrs['onchange'] = 'this.title=this.value;Viewer.Refresh(' + i + ', this.valueAsNumber)'; - input.attrs['id'] = id; + var input = null; - var div = new makerjs.exporter.XmlTag('div'); - div.innerText = label.toString() + input.toString(); - div.innerTextEscaped = true; - paramsHtml += div.toString(); + switch (attrs.type) { + + case 'range': + attrs.title = attrs.value; + input = new makerjs.exporter.XmlTag('input', attrs); + input.attrs['onchange'] = 'this.title=this.value;Viewer.Refresh(' + i + ', makerjs.round(this.valueAsNumber, .001))'; + input.attrs['id'] = id; + + break; + + case 'bool': + input = new makerjs.exporter.XmlTag('input', { + id: id, + type: 'checkbox', + checked: attrs.value ? 'checked' : '', + onchange: 'Viewer.Refresh(' + i + ', this.checked)' + }); + + break; } + + if (!input) continue; + + var div = new makerjs.exporter.XmlTag('div'); + div.innerText = label.toString() + input.toString(); + div.innerTextEscaped = true; + paramsHtml += div.toString(); } } document.getElementById("params").innerHTML = paramsHtml; diff --git a/examples/Rimbox.js b/examples/Rimbox.js new file mode 100644 index 000000000..f97eca0eb --- /dev/null +++ b/examples/Rimbox.js @@ -0,0 +1,67 @@ +/// +var makerjs = require('makerjs'); +var RimboxCorner = (function () { + function RimboxCorner(holeRadius, rimThickness) { + var rim = Math.min(rimThickness, holeRadius); + var hr = holeRadius + rim; + this.paths = { + centerRound: new makerjs.paths.Arc([0, 0], hr, 0, 90), + hFillet: new makerjs.paths.Arc([0, hr + holeRadius], holeRadius, 180, 270), + wFillet: new makerjs.paths.Arc([hr + holeRadius, 0], holeRadius, 180, 270) + }; + } + return RimboxCorner; +})(); +var RimboxInner = (function () { + function RimboxInner(width, height, holeRadius, rimThickness) { + var mm = makerjs.model; + var corner = new RimboxCorner(holeRadius, rimThickness); + this.models = { + bottomLeft: corner, + bottomRight: mm.move(mm.mirror(corner, true, false), [width, 0]), + topLeft: mm.move(mm.mirror(corner, false, true), [0, height]), + topRight: mm.move(mm.mirror(corner, true, true), [width, height]) + }; + var line = makerjs.paths.Line; + var rim = Math.min(rimThickness, holeRadius); + var d = 2 * holeRadius + rim; + this.paths = { + bottom: new line([d, -holeRadius], [width - d, -holeRadius]), + top: new line([d, height + holeRadius], [width - d, height + holeRadius]), + left: new line([-holeRadius, d], [-holeRadius, height - d]), + right: new line([width + holeRadius, d], [width + holeRadius, height - d]) + }; + } + return RimboxInner; +})(); +var Rimbox = (function () { + function Rimbox(width, height, holeRadius, rimThickness, hollow) { + if (arguments.length == 0) { + var defaultValues = makerjs.kit.getParameterValues(Rimbox); + width = defaultValues.shift(); + height = defaultValues.shift(); + holeRadius = defaultValues.shift(); + rimThickness = defaultValues.shift(); + } + var mm = makerjs.models; + var cornerRadius = holeRadius + rimThickness; + var c2 = cornerRadius * 2; + this.models = { + bolts: new mm.BoltRectangle(width, height, holeRadius), + outer: new mm.RoundRectangle(width + c2, height + c2, cornerRadius) + }; + if (hollow) { + this.models['inner'] = new RimboxInner(width, height, holeRadius, rimThickness); + } + this.models['outer'].origin = [-cornerRadius, -cornerRadius]; + } + return Rimbox; +})(); +Rimbox.metaParameters = [ + { title: "width", type: "range", min: 10, max: 500, value: 120 }, + { title: "height", type: "range", min: 10, max: 500, value: 100 }, + { title: "holeRadius", type: "range", min: 1, max: 20, value: 3 }, + { title: "rimThickness", type: "range", min: 1, max: 20, value: 2 }, + { title: "hollow", type: "bool", value: true } +]; +module.exports = Rimbox; diff --git a/examples/combine.js b/examples/combine.js index 8b9f19eed..969b1ef66 100644 --- a/examples/combine.js +++ b/examples/combine.js @@ -1,5 +1,5 @@ -function combine(angle) { +function combine(angle, add) { var star1 = new makerjs.models.Oval(50, 100); @@ -31,11 +31,12 @@ function combine(angle) { star2: star2 }; - makerjs.model.combine(star1, star2, false, true, false, true); + makerjs.model.combine(star1, star2, false, true, !add, add, add); } combine.metaParameters = [ - { title: "angle", type: "range", min: -180, max: 180, step: 1, value: 40 } + { title: "angle", type: "range", min: -180, max: 180, step: 1, value: 40 }, + { title: "add", type: "bool", value: true } ]; diff --git a/examples/stackbox.js b/examples/starbox.js similarity index 82% rename from examples/stackbox.js rename to examples/starbox.js index 410bd03ee..672bbdeb2 100644 --- a/examples/stackbox.js +++ b/examples/starbox.js @@ -1,7 +1,7 @@ /// var makerjs = require('makerjs'); -var stackboxCorner = (function () { - function stackboxCorner(holeRadius, rimThickness) { +var starboxCorner = (function () { + function starboxCorner(holeRadius, rimThickness) { var rim = Math.min(rimThickness, holeRadius); var hr = holeRadius + rim; this.paths = { @@ -10,12 +10,12 @@ var stackboxCorner = (function () { wFillet: new makerjs.paths.Arc([hr + holeRadius, 0], holeRadius, 180, 270) }; } - return stackboxCorner; + return starboxCorner; })(); -var stackboxInner = (function () { - function stackboxInner(width, height, holeRadius, rimThickness) { +var starboxInner = (function () { + function starboxInner(width, height, holeRadius, rimThickness) { var mm = makerjs.model; - var corner = new stackboxCorner(holeRadius, rimThickness); + var corner = new starboxCorner(holeRadius, rimThickness); this.models = { bottomLeft: corner, bottomRight: mm.move(mm.mirror(corner, true, false), [width, 0]), @@ -32,12 +32,12 @@ var stackboxInner = (function () { right: new line([width + holeRadius, d], [width + holeRadius, height - d]) }; } - return stackboxInner; + return starboxInner; })(); -var stackbox = (function () { - function stackbox(width, height, holeRadius, rimThickness, angle) { +var starbox = (function () { + function starbox(width, height, holeRadius, rimThickness, angle) { if (arguments.length == 0) { - var defaultValues = makerjs.kit.getParameterValues(stackbox); + var defaultValues = makerjs.kit.getParameterValues(starbox); width = defaultValues.shift(); height = defaultValues.shift(); holeRadius = defaultValues.shift(); @@ -49,7 +49,7 @@ var stackbox = (function () { this.models = { bolts: new mm.BoltRectangle(width, height, holeRadius), outer: new mm.RoundRectangle(width + c2, height + c2, cornerRadius), - inner: new stackboxInner(width, height, holeRadius, rimThickness) + inner: new starboxInner(width, height, holeRadius, rimThickness) }; this.models['outer'].origin = [-cornerRadius, -cornerRadius]; @@ -64,13 +64,13 @@ var stackbox = (function () { makerjs.model.combine(this.models.inner, star, false, true, true, false); } - return stackbox; + return starbox; })(); -stackbox.metaParameters = [ +starbox.metaParameters = [ { title: "width", type: "range", min: 10, max: 500, value: 120 }, { title: "height", type: "range", min: 10, max: 500, value: 100 }, { title: "holeRadius", type: "range", min: 1, max: 20, value: 3 }, { title: "rimThickness", type: "range", min: 1, max: 20, value: 2 }, { title: "angle", type: "range", min: -180, max: 180, value: 45 } ]; -module.exports = stackbox; +module.exports = starbox; diff --git a/index.js b/index.js index 09efe4f27..c963ba93a 100644 --- a/index.js +++ b/index.js @@ -568,10 +568,10 @@ var MakerJs; */ var pathAreEqualMap = {}; pathAreEqualMap[MakerJs.pathType.Line] = function (line1, line2) { - return MakerJs.point.areEqual(line1.end, line2.end); + return (MakerJs.point.areEqual(line1.origin, line2.origin) && MakerJs.point.areEqual(line1.end, line2.end)) || (MakerJs.point.areEqual(line1.origin, line2.end) && MakerJs.point.areEqual(line1.end, line2.origin)); }; pathAreEqualMap[MakerJs.pathType.Circle] = function (circle1, circle2) { - return circle1.radius == circle2.radius; + return MakerJs.point.areEqual(circle1.origin, circle2.origin) && circle1.radius == circle2.radius; }; pathAreEqualMap[MakerJs.pathType.Arc] = function (arc1, arc2) { return pathAreEqualMap[MakerJs.pathType.Circle](arc1, arc2) && MakerJs.angle.areEqual(arc1.startAngle, arc2.startAngle) && MakerJs.angle.areEqual(arc1.endAngle, arc2.endAngle); @@ -585,7 +585,7 @@ var MakerJs; */ function areEqual(path1, path2) { var result = false; - if (path1.type == path2.type && MakerJs.point.areEqual(path1.origin, path2.origin)) { + if (path1.type == path2.type) { var fn = pathAreEqualMap[path1.type]; if (fn) { result = fn(path1, path2); @@ -1139,7 +1139,7 @@ var MakerJs; function breakAlongForeignPath(segments, overlappedSegments, foreignPath) { if (MakerJs.path.areEqual(segments[0].path, foreignPath)) { segments[0].overlapped = true; - segments[0].overlappedEqual = true; + segments[0].duplicate = true; overlappedSegments.push(segments[0]); return; } @@ -1283,7 +1283,7 @@ var MakerJs; function checkForEqualOverlaps(crossedPathsA, crossedPathsB) { function compareSegments(segment1, segment2) { if (MakerJs.path.areEqual(segment1.path, segment2.path)) { - segment1.overlappedEqual = segment2.overlappedEqual = true; + segment1.duplicate = segment2.duplicate = true; } } function compareAll(segment) { @@ -1298,7 +1298,7 @@ var MakerJs; /** * @private */ - function addOrDeleteSegments(crossedPath, includeInside, includeOutside, firstPass) { + function addOrDeleteSegments(crossedPath, includeInside, includeOutside, keepDuplicates) { function addSegment(model, pathIdBase, segment) { var id = model_1.getSimilarPathId(model, pathIdBase); model.paths[id] = segment.path; @@ -1311,8 +1311,8 @@ var MakerJs; //delete the original, its segments will be added delete crossedPath.modelContext.paths[crossedPath.pathId]; for (var i = 0; i < crossedPath.segments.length; i++) { - if (crossedPath.segments[i].overlappedEqual) { - if (firstPass) { + if (crossedPath.segments[i].duplicate) { + if (keepDuplicates) { addSegment(crossedPath.modelContext, crossedPath.pathId, crossedPath.segments[i]); } } @@ -1330,14 +1330,16 @@ var MakerJs; * @param includeAOutsideB Flag to include paths from modelA which are outside of modelB. * @param includeBInsideA Flag to include paths from modelB which are inside of modelA. * @param includeBOutsideA Flag to include paths from modelB which are outside of modelA. + * @param keepDuplicates Flag to include paths which are duplicate in both models. * @param farPoint Optional point of reference which is outside the bounds of both models. */ - function combine(modelA, modelB, includeAInsideB, includeAOutsideB, includeBInsideA, includeBOutsideA, farPoint) { + function combine(modelA, modelB, includeAInsideB, includeAOutsideB, includeBInsideA, includeBOutsideA, keepDuplicates, farPoint) { + if (keepDuplicates === void 0) { keepDuplicates = true; } var pathsA = breakAllPathsAtIntersections(modelA, modelB, farPoint); var pathsB = breakAllPathsAtIntersections(modelB, modelA, farPoint); checkForEqualOverlaps(pathsA.overlappedSegments, pathsB.overlappedSegments); for (var i = 0; i < pathsA.crossedPaths.length; i++) { - addOrDeleteSegments(pathsA.crossedPaths[i], includeAInsideB, includeAOutsideB, true); + addOrDeleteSegments(pathsA.crossedPaths[i], includeAInsideB, includeAOutsideB, keepDuplicates); } for (var i = 0; i < pathsB.crossedPaths.length; i++) { addOrDeleteSegments(pathsB.crossedPaths[i], includeBInsideA, includeBOutsideA); @@ -3383,6 +3385,35 @@ var MakerJs; })(models = MakerJs.models || (MakerJs.models = {})); })(MakerJs || (MakerJs = {})); var MakerJs; +(function (MakerJs) { + var models; + (function (models) { + var Dome = (function () { + function Dome(width, height, radius) { + if (radius === void 0) { radius = Math.min(width / 2, height); } + this.paths = {}; + var w2 = width / 2; + var wt = Math.max(w2 - radius, 0); + var hr = Math.max(height - radius, 0); + this.paths["Bottom"] = new MakerJs.paths.Line([-w2, 0], [w2, 0]); + if (hr) { + this.paths["Left"] = new MakerJs.paths.Line([-w2, 0], [-w2, hr]); + this.paths["Right"] = new MakerJs.paths.Line([w2, 0], [w2, hr]); + } + if (radius > 0) { + this.paths["TopLeft"] = new MakerJs.paths.Arc([-wt, hr], radius, 90, 180); + this.paths["TopRight"] = new MakerJs.paths.Arc([wt, hr], radius, 0, 90); + } + if (wt) { + this.paths["Top"] = new MakerJs.paths.Line([-wt, height], [wt, height]); + } + } + return Dome; + })(); + models.Dome = Dome; + })(models = MakerJs.models || (MakerJs.models = {})); +})(MakerJs || (MakerJs = {})); +var MakerJs; (function (MakerJs) { var models; (function (models) { diff --git a/package.json b/package.json index e451c25fa..e44857c8b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "makerjs", - "version": "0.5.0", + "version": "0.5.1", "description": "Maker.js, a Microsoft Garage project, is a JavaScript library for creating and sharing modular line drawings for CNC and laser cutters.", "main": "index.js", "scripts": { diff --git a/src/core/combine.ts b/src/core/combine.ts index 91757fd6c..d8ee127d7 100644 --- a/src/core/combine.ts +++ b/src/core/combine.ts @@ -28,7 +28,7 @@ module MakerJs.model { if (path.areEqual(segments[0].path, foreignPath)) { segments[0].overlapped = true; - segments[0].overlappedEqual = true; + segments[0].duplicate = true; overlappedSegments.push(segments[0]); return; @@ -172,7 +172,7 @@ module MakerJs.model { */ interface ICrossedPathSegment extends IPathInside { overlapped: boolean; - overlappedEqual?: boolean; + duplicate?: boolean; } /** @@ -242,7 +242,7 @@ module MakerJs.model { function compareSegments(segment1: ICrossedPathSegment, segment2: ICrossedPathSegment) { if (path.areEqual(segment1.path, segment2.path)) { - segment1.overlappedEqual = segment2.overlappedEqual = true; + segment1.duplicate = segment2.duplicate = true; } } @@ -261,7 +261,7 @@ module MakerJs.model { /** * @private */ - function addOrDeleteSegments(crossedPath: ICrossedPath, includeInside: boolean, includeOutside: boolean, firstPass?: boolean) { + function addOrDeleteSegments(crossedPath: ICrossedPath, includeInside: boolean, includeOutside: boolean, keepDuplicates?: boolean) { function addSegment(model: IModel, pathIdBase: string, segment: ICrossedPathSegment) { var id = getSimilarPathId(model, pathIdBase); @@ -278,8 +278,8 @@ module MakerJs.model { delete crossedPath.modelContext.paths[crossedPath.pathId]; for (var i = 0; i < crossedPath.segments.length; i++) { - if (crossedPath.segments[i].overlappedEqual) { - if (firstPass) { + if (crossedPath.segments[i].duplicate) { + if (keepDuplicates) { addSegment(crossedPath.modelContext, crossedPath.pathId, crossedPath.segments[i]); } } else { @@ -297,9 +297,10 @@ module MakerJs.model { * @param includeAOutsideB Flag to include paths from modelA which are outside of modelB. * @param includeBInsideA Flag to include paths from modelB which are inside of modelA. * @param includeBOutsideA Flag to include paths from modelB which are outside of modelA. + * @param keepDuplicates Flag to include paths which are duplicate in both models. * @param farPoint Optional point of reference which is outside the bounds of both models. */ - export function combine(modelA: IModel, modelB: IModel, includeAInsideB: boolean, includeAOutsideB: boolean, includeBInsideA: boolean, includeBOutsideA: boolean, farPoint?: IPoint) { + export function combine(modelA: IModel, modelB: IModel, includeAInsideB: boolean, includeAOutsideB: boolean, includeBInsideA: boolean, includeBOutsideA: boolean, keepDuplicates: boolean = true, farPoint?: IPoint) { var pathsA = breakAllPathsAtIntersections(modelA, modelB, farPoint); var pathsB = breakAllPathsAtIntersections(modelB, modelA, farPoint); @@ -307,7 +308,7 @@ module MakerJs.model { checkForEqualOverlaps(pathsA.overlappedSegments, pathsB.overlappedSegments); for (var i = 0; i < pathsA.crossedPaths.length; i++) { - addOrDeleteSegments(pathsA.crossedPaths[i], includeAInsideB, includeAOutsideB, true); + addOrDeleteSegments(pathsA.crossedPaths[i], includeAInsideB, includeAOutsideB, keepDuplicates); } for (var i = 0; i < pathsB.crossedPaths.length; i++) { diff --git a/src/core/path.ts b/src/core/path.ts index a09de1390..09d24e3de 100644 --- a/src/core/path.ts +++ b/src/core/path.ts @@ -15,11 +15,11 @@ module MakerJs.path { var pathAreEqualMap: IPathAreEqualMap = {}; pathAreEqualMap[pathType.Line] = function (line1: IPathLine, line2: IPathLine): boolean { - return point.areEqual(line1.end, line2.end); + return (point.areEqual(line1.origin, line2.origin) && point.areEqual(line1.end, line2.end)) || (point.areEqual(line1.origin, line2.end) && point.areEqual(line1.end, line2.origin)); }; pathAreEqualMap[pathType.Circle] = function (circle1: IPathCircle, circle2: IPathCircle): boolean { - return circle1.radius == circle2.radius; + return point.areEqual(circle1.origin, circle2.origin) && circle1.radius == circle2.radius; }; pathAreEqualMap[pathType.Arc] = function (arc1: IPathArc, arc2: IPathArc): boolean { @@ -37,13 +37,11 @@ module MakerJs.path { var result = false; - if (path1.type == path2.type && point.areEqual(path1.origin, path2.origin)) { - + if (path1.type == path2.type) { var fn = pathAreEqualMap[path1.type]; if (fn) { result = fn(path1, path2); } - } return result; diff --git a/src/models/Dome.ts b/src/models/Dome.ts new file mode 100644 index 000000000..3bfcaa512 --- /dev/null +++ b/src/models/Dome.ts @@ -0,0 +1,31 @@ +module MakerJs.models { + + export class Dome implements IModel { + + public paths: IPathMap = {}; + + constructor(width: number, height: number, radius: number = Math.min(width / 2, height)) { + + var w2 = width / 2; + var wt = Math.max(w2 - radius, 0); + var hr = Math.max(height - radius, 0); + + this.paths["Bottom"] = new paths.Line([-w2, 0], [w2, 0]); + + if (hr) { + this.paths["Left"] = new paths.Line([-w2, 0], [-w2, hr]); + this.paths["Right"] = new paths.Line([w2, 0], [w2, hr]); + } + + if (radius > 0) { + this.paths["TopLeft"] = new paths.Arc([-wt, hr], radius, 90, 180); + this.paths["TopRight"] = new paths.Arc([wt, hr], radius, 0, 90); + } + + if (wt) { + this.paths["Top"] = new paths.Line([-wt, height], [wt, height]); + } + + } + } +} diff --git a/target/js/browser.maker.js b/target/js/browser.maker.js index 2de6baa8a..732889937 100644 --- a/target/js/browser.maker.js +++ b/target/js/browser.maker.js @@ -569,10 +569,10 @@ var MakerJs; */ var pathAreEqualMap = {}; pathAreEqualMap[MakerJs.pathType.Line] = function (line1, line2) { - return MakerJs.point.areEqual(line1.end, line2.end); + return (MakerJs.point.areEqual(line1.origin, line2.origin) && MakerJs.point.areEqual(line1.end, line2.end)) || (MakerJs.point.areEqual(line1.origin, line2.end) && MakerJs.point.areEqual(line1.end, line2.origin)); }; pathAreEqualMap[MakerJs.pathType.Circle] = function (circle1, circle2) { - return circle1.radius == circle2.radius; + return MakerJs.point.areEqual(circle1.origin, circle2.origin) && circle1.radius == circle2.radius; }; pathAreEqualMap[MakerJs.pathType.Arc] = function (arc1, arc2) { return pathAreEqualMap[MakerJs.pathType.Circle](arc1, arc2) && MakerJs.angle.areEqual(arc1.startAngle, arc2.startAngle) && MakerJs.angle.areEqual(arc1.endAngle, arc2.endAngle); @@ -586,7 +586,7 @@ var MakerJs; */ function areEqual(path1, path2) { var result = false; - if (path1.type == path2.type && MakerJs.point.areEqual(path1.origin, path2.origin)) { + if (path1.type == path2.type) { var fn = pathAreEqualMap[path1.type]; if (fn) { result = fn(path1, path2); @@ -1140,7 +1140,7 @@ var MakerJs; function breakAlongForeignPath(segments, overlappedSegments, foreignPath) { if (MakerJs.path.areEqual(segments[0].path, foreignPath)) { segments[0].overlapped = true; - segments[0].overlappedEqual = true; + segments[0].duplicate = true; overlappedSegments.push(segments[0]); return; } @@ -1284,7 +1284,7 @@ var MakerJs; function checkForEqualOverlaps(crossedPathsA, crossedPathsB) { function compareSegments(segment1, segment2) { if (MakerJs.path.areEqual(segment1.path, segment2.path)) { - segment1.overlappedEqual = segment2.overlappedEqual = true; + segment1.duplicate = segment2.duplicate = true; } } function compareAll(segment) { @@ -1299,7 +1299,7 @@ var MakerJs; /** * @private */ - function addOrDeleteSegments(crossedPath, includeInside, includeOutside, firstPass) { + function addOrDeleteSegments(crossedPath, includeInside, includeOutside, keepDuplicates) { function addSegment(model, pathIdBase, segment) { var id = model_1.getSimilarPathId(model, pathIdBase); model.paths[id] = segment.path; @@ -1312,8 +1312,8 @@ var MakerJs; //delete the original, its segments will be added delete crossedPath.modelContext.paths[crossedPath.pathId]; for (var i = 0; i < crossedPath.segments.length; i++) { - if (crossedPath.segments[i].overlappedEqual) { - if (firstPass) { + if (crossedPath.segments[i].duplicate) { + if (keepDuplicates) { addSegment(crossedPath.modelContext, crossedPath.pathId, crossedPath.segments[i]); } } @@ -1331,14 +1331,16 @@ var MakerJs; * @param includeAOutsideB Flag to include paths from modelA which are outside of modelB. * @param includeBInsideA Flag to include paths from modelB which are inside of modelA. * @param includeBOutsideA Flag to include paths from modelB which are outside of modelA. + * @param keepDuplicates Flag to include paths which are duplicate in both models. * @param farPoint Optional point of reference which is outside the bounds of both models. */ - function combine(modelA, modelB, includeAInsideB, includeAOutsideB, includeBInsideA, includeBOutsideA, farPoint) { + function combine(modelA, modelB, includeAInsideB, includeAOutsideB, includeBInsideA, includeBOutsideA, keepDuplicates, farPoint) { + if (keepDuplicates === void 0) { keepDuplicates = true; } var pathsA = breakAllPathsAtIntersections(modelA, modelB, farPoint); var pathsB = breakAllPathsAtIntersections(modelB, modelA, farPoint); checkForEqualOverlaps(pathsA.overlappedSegments, pathsB.overlappedSegments); for (var i = 0; i < pathsA.crossedPaths.length; i++) { - addOrDeleteSegments(pathsA.crossedPaths[i], includeAInsideB, includeAOutsideB, true); + addOrDeleteSegments(pathsA.crossedPaths[i], includeAInsideB, includeAOutsideB, keepDuplicates); } for (var i = 0; i < pathsB.crossedPaths.length; i++) { addOrDeleteSegments(pathsB.crossedPaths[i], includeBInsideA, includeBOutsideA); @@ -3384,6 +3386,35 @@ var MakerJs; })(models = MakerJs.models || (MakerJs.models = {})); })(MakerJs || (MakerJs = {})); var MakerJs; +(function (MakerJs) { + var models; + (function (models) { + var Dome = (function () { + function Dome(width, height, radius) { + if (radius === void 0) { radius = Math.min(width / 2, height); } + this.paths = {}; + var w2 = width / 2; + var wt = Math.max(w2 - radius, 0); + var hr = Math.max(height - radius, 0); + this.paths["Bottom"] = new MakerJs.paths.Line([-w2, 0], [w2, 0]); + if (hr) { + this.paths["Left"] = new MakerJs.paths.Line([-w2, 0], [-w2, hr]); + this.paths["Right"] = new MakerJs.paths.Line([w2, 0], [w2, hr]); + } + if (radius > 0) { + this.paths["TopLeft"] = new MakerJs.paths.Arc([-wt, hr], radius, 90, 180); + this.paths["TopRight"] = new MakerJs.paths.Arc([wt, hr], radius, 0, 90); + } + if (wt) { + this.paths["Top"] = new MakerJs.paths.Line([-wt, height], [wt, height]); + } + } + return Dome; + })(); + models.Dome = Dome; + })(models = MakerJs.models || (MakerJs.models = {})); +})(MakerJs || (MakerJs = {})); +var MakerJs; (function (MakerJs) { var models; (function (models) { diff --git a/target/js/node.maker.js b/target/js/node.maker.js index 09efe4f27..c963ba93a 100644 --- a/target/js/node.maker.js +++ b/target/js/node.maker.js @@ -568,10 +568,10 @@ var MakerJs; */ var pathAreEqualMap = {}; pathAreEqualMap[MakerJs.pathType.Line] = function (line1, line2) { - return MakerJs.point.areEqual(line1.end, line2.end); + return (MakerJs.point.areEqual(line1.origin, line2.origin) && MakerJs.point.areEqual(line1.end, line2.end)) || (MakerJs.point.areEqual(line1.origin, line2.end) && MakerJs.point.areEqual(line1.end, line2.origin)); }; pathAreEqualMap[MakerJs.pathType.Circle] = function (circle1, circle2) { - return circle1.radius == circle2.radius; + return MakerJs.point.areEqual(circle1.origin, circle2.origin) && circle1.radius == circle2.radius; }; pathAreEqualMap[MakerJs.pathType.Arc] = function (arc1, arc2) { return pathAreEqualMap[MakerJs.pathType.Circle](arc1, arc2) && MakerJs.angle.areEqual(arc1.startAngle, arc2.startAngle) && MakerJs.angle.areEqual(arc1.endAngle, arc2.endAngle); @@ -585,7 +585,7 @@ var MakerJs; */ function areEqual(path1, path2) { var result = false; - if (path1.type == path2.type && MakerJs.point.areEqual(path1.origin, path2.origin)) { + if (path1.type == path2.type) { var fn = pathAreEqualMap[path1.type]; if (fn) { result = fn(path1, path2); @@ -1139,7 +1139,7 @@ var MakerJs; function breakAlongForeignPath(segments, overlappedSegments, foreignPath) { if (MakerJs.path.areEqual(segments[0].path, foreignPath)) { segments[0].overlapped = true; - segments[0].overlappedEqual = true; + segments[0].duplicate = true; overlappedSegments.push(segments[0]); return; } @@ -1283,7 +1283,7 @@ var MakerJs; function checkForEqualOverlaps(crossedPathsA, crossedPathsB) { function compareSegments(segment1, segment2) { if (MakerJs.path.areEqual(segment1.path, segment2.path)) { - segment1.overlappedEqual = segment2.overlappedEqual = true; + segment1.duplicate = segment2.duplicate = true; } } function compareAll(segment) { @@ -1298,7 +1298,7 @@ var MakerJs; /** * @private */ - function addOrDeleteSegments(crossedPath, includeInside, includeOutside, firstPass) { + function addOrDeleteSegments(crossedPath, includeInside, includeOutside, keepDuplicates) { function addSegment(model, pathIdBase, segment) { var id = model_1.getSimilarPathId(model, pathIdBase); model.paths[id] = segment.path; @@ -1311,8 +1311,8 @@ var MakerJs; //delete the original, its segments will be added delete crossedPath.modelContext.paths[crossedPath.pathId]; for (var i = 0; i < crossedPath.segments.length; i++) { - if (crossedPath.segments[i].overlappedEqual) { - if (firstPass) { + if (crossedPath.segments[i].duplicate) { + if (keepDuplicates) { addSegment(crossedPath.modelContext, crossedPath.pathId, crossedPath.segments[i]); } } @@ -1330,14 +1330,16 @@ var MakerJs; * @param includeAOutsideB Flag to include paths from modelA which are outside of modelB. * @param includeBInsideA Flag to include paths from modelB which are inside of modelA. * @param includeBOutsideA Flag to include paths from modelB which are outside of modelA. + * @param keepDuplicates Flag to include paths which are duplicate in both models. * @param farPoint Optional point of reference which is outside the bounds of both models. */ - function combine(modelA, modelB, includeAInsideB, includeAOutsideB, includeBInsideA, includeBOutsideA, farPoint) { + function combine(modelA, modelB, includeAInsideB, includeAOutsideB, includeBInsideA, includeBOutsideA, keepDuplicates, farPoint) { + if (keepDuplicates === void 0) { keepDuplicates = true; } var pathsA = breakAllPathsAtIntersections(modelA, modelB, farPoint); var pathsB = breakAllPathsAtIntersections(modelB, modelA, farPoint); checkForEqualOverlaps(pathsA.overlappedSegments, pathsB.overlappedSegments); for (var i = 0; i < pathsA.crossedPaths.length; i++) { - addOrDeleteSegments(pathsA.crossedPaths[i], includeAInsideB, includeAOutsideB, true); + addOrDeleteSegments(pathsA.crossedPaths[i], includeAInsideB, includeAOutsideB, keepDuplicates); } for (var i = 0; i < pathsB.crossedPaths.length; i++) { addOrDeleteSegments(pathsB.crossedPaths[i], includeBInsideA, includeBOutsideA); @@ -3383,6 +3385,35 @@ var MakerJs; })(models = MakerJs.models || (MakerJs.models = {})); })(MakerJs || (MakerJs = {})); var MakerJs; +(function (MakerJs) { + var models; + (function (models) { + var Dome = (function () { + function Dome(width, height, radius) { + if (radius === void 0) { radius = Math.min(width / 2, height); } + this.paths = {}; + var w2 = width / 2; + var wt = Math.max(w2 - radius, 0); + var hr = Math.max(height - radius, 0); + this.paths["Bottom"] = new MakerJs.paths.Line([-w2, 0], [w2, 0]); + if (hr) { + this.paths["Left"] = new MakerJs.paths.Line([-w2, 0], [-w2, hr]); + this.paths["Right"] = new MakerJs.paths.Line([w2, 0], [w2, hr]); + } + if (radius > 0) { + this.paths["TopLeft"] = new MakerJs.paths.Arc([-wt, hr], radius, 90, 180); + this.paths["TopRight"] = new MakerJs.paths.Arc([wt, hr], radius, 0, 90); + } + if (wt) { + this.paths["Top"] = new MakerJs.paths.Line([-wt, height], [wt, height]); + } + } + return Dome; + })(); + models.Dome = Dome; + })(models = MakerJs.models || (MakerJs.models = {})); +})(MakerJs || (MakerJs = {})); +var MakerJs; (function (MakerJs) { var models; (function (models) { diff --git a/target/ts/makerjs.d.ts b/target/ts/makerjs.d.ts index 8ed3d2ecf..3e2b16a8c 100644 --- a/target/ts/makerjs.d.ts +++ b/target/ts/makerjs.d.ts @@ -772,9 +772,10 @@ declare module MakerJs.model { * @param includeAOutsideB Flag to include paths from modelA which are outside of modelB. * @param includeBInsideA Flag to include paths from modelB which are inside of modelA. * @param includeBOutsideA Flag to include paths from modelB which are outside of modelA. + * @param keepDuplicates Flag to include paths which are duplicate in both models. * @param farPoint Optional point of reference which is outside the bounds of both models. */ - function combine(modelA: IModel, modelB: IModel, includeAInsideB: boolean, includeAOutsideB: boolean, includeBInsideA: boolean, includeBOutsideA: boolean, farPoint?: IPoint): void; + function combine(modelA: IModel, modelB: IModel, includeAInsideB: boolean, includeAOutsideB: boolean, includeBInsideA: boolean, includeBOutsideA: boolean, keepDuplicates?: boolean, farPoint?: IPoint): void; } declare module MakerJs.units { /** @@ -1202,6 +1203,12 @@ declare module MakerJs.models { constructor(width: number, height: number, holeRadius: number); } } +declare module MakerJs.models { + class Dome implements IModel { + paths: IPathMap; + constructor(width: number, height: number, radius?: number); + } +} declare module MakerJs.models { class RoundRectangle implements IModel { paths: IPathMap;