From 74f4873c30d31852767d59bcdcbdcf9692a070bf Mon Sep 17 00:00:00 2001 From: Chris Musselle Date: Thu, 14 Mar 2013 18:02:19 +0000 Subject: [PATCH 01/31] First Commit with minor Comments added --- wikihouse.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/wikihouse.rb b/wikihouse.rb index 0d6f966..3c9cf07 100755 --- a/wikihouse.rb +++ b/wikihouse.rb @@ -156,6 +156,8 @@ def show_wikihouse_error(msg) # Centroid Calculation # ------------------------------------------------------------------------------ +# (Chris) Dont think this function is currently being used + def get_face_center(face) # First, triangulate the polygon. @@ -622,7 +624,7 @@ def initialize(panels, root, dimensions) if not p0 break end - transform = Geom::Transformation.translation ([origin.x - p0[0], origin.y - p0[1], 0]) + transform = Geom::Transformation.translation([origin.x - p0[0], origin.y - p0[1], 0]) if angle transform = transform * Geom::Transformation.rotation(origin, Z_AXIS, angle) end @@ -1900,7 +1902,7 @@ def load_wikihouse_make show_wikihouse_error "You need to open a SketchUp model before it can be fabricated" return end - + # Initialise an attribute dictionary for custom metadata. attr = model.attribute_dictionary WIKIHOUSE_TITLE, true if attr.size == 0 From 3a8b3219d9882e0d936a7e756dd52f5e358fbc62 Mon Sep 17 00:00:00 2001 From: Chris Musselle Date: Fri, 15 Mar 2013 00:02:00 +0000 Subject: [PATCH 02/31] Restructured folders to hold all plugin code in a single folder, and a short script to add the plugin as a SketchUp Extension. Also updated README install instructions. --- README.md | 15 +++++++++++++-- load_wikihouse_extension.rb | 12 ++++++++++++ .../wikihouse-assets}/download-16.png | Bin .../wikihouse-assets}/download.png | Bin .../wikihouse-assets}/guide-16.png | Bin .../wikihouse-assets}/guide.png | Bin .../wikihouse-assets}/make-16.png | Bin .../wikihouse-assets}/make.png | Bin .../wikihouse-assets}/upload-16.png | Bin .../wikihouse-assets}/upload.png | Bin wikihouse.rb => wikihouse-plugin/wikihouse.rb | 0 11 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 load_wikihouse_extension.rb rename {wikihouse-assets => wikihouse-plugin/wikihouse-assets}/download-16.png (100%) rename {wikihouse-assets => wikihouse-plugin/wikihouse-assets}/download.png (100%) rename {wikihouse-assets => wikihouse-plugin/wikihouse-assets}/guide-16.png (100%) rename {wikihouse-assets => wikihouse-plugin/wikihouse-assets}/guide.png (100%) rename {wikihouse-assets => wikihouse-plugin/wikihouse-assets}/make-16.png (100%) rename {wikihouse-assets => wikihouse-plugin/wikihouse-assets}/make.png (100%) rename {wikihouse-assets => wikihouse-plugin/wikihouse-assets}/upload-16.png (100%) rename {wikihouse-assets => wikihouse-plugin/wikihouse-assets}/upload.png (100%) rename wikihouse.rb => wikihouse-plugin/wikihouse.rb (100%) diff --git a/README.md b/README.md index b5d94e3..3f87fb7 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,19 @@ you can: **Installation** -To install, simply download the zip file and copy over the files into -your SketchUp `plugins` directory. +The Wikihouse plugin is now available as a SketchUp 8 extension, which allow easier installing and managing of SketchUp plugins. + +To install: + +1. Download the file `wikihouseExtension.rbz` +2. Open SketchUp and navigate to **Window** > **Preferences** (Microsoft Windows) or **SketchUp** > **Preferences** (Mac OS X). +3. Click on Extensions, which will display the Extensions panel showing all current extensions in your plugin folder. +4. Click on the *Install Extension* button and locate the downloaded `wikihouseExtension.rbz` file to install it. + +The plugin will then appear in the list of other extensions, and all necessary files will be copied to your plugins folder. If you ever wish to disable it, simply untick it in the list and restart SketchUp. + +On previous versions of SketchUp to install, simply download the zip file and copy over the files into +your SketchUp `plugins` directory. **License** diff --git a/load_wikihouse_extension.rb b/load_wikihouse_extension.rb new file mode 100644 index 0000000..d347cd5 --- /dev/null +++ b/load_wikihouse_extension.rb @@ -0,0 +1,12 @@ +# Create an entry in the Extension list that loads the wikihouse.rb scripts + +require 'sketchup.rb' +require 'extensions.rb' + +wikihouse_extension = SketchupExtension.new "Wikihouse Plugin", "wikihouse-plugin/wikihouse.rb" +wikihouse_extension.version = '0.1' +wikihouse_extension.description = "Allows for the sharing and downloading of wikihouse models at http://www.wikihouse.cc/, as well as the trasformation of models to cutting templates." +wikihouse_extension.creator = "Wikihouse Development Team" +wikihouse_extension.copyright = "Public Domain - 2013" + +Sketchup.register_extension wikihouse_extension, true \ No newline at end of file diff --git a/wikihouse-assets/download-16.png b/wikihouse-plugin/wikihouse-assets/download-16.png similarity index 100% rename from wikihouse-assets/download-16.png rename to wikihouse-plugin/wikihouse-assets/download-16.png diff --git a/wikihouse-assets/download.png b/wikihouse-plugin/wikihouse-assets/download.png similarity index 100% rename from wikihouse-assets/download.png rename to wikihouse-plugin/wikihouse-assets/download.png diff --git a/wikihouse-assets/guide-16.png b/wikihouse-plugin/wikihouse-assets/guide-16.png similarity index 100% rename from wikihouse-assets/guide-16.png rename to wikihouse-plugin/wikihouse-assets/guide-16.png diff --git a/wikihouse-assets/guide.png b/wikihouse-plugin/wikihouse-assets/guide.png similarity index 100% rename from wikihouse-assets/guide.png rename to wikihouse-plugin/wikihouse-assets/guide.png diff --git a/wikihouse-assets/make-16.png b/wikihouse-plugin/wikihouse-assets/make-16.png similarity index 100% rename from wikihouse-assets/make-16.png rename to wikihouse-plugin/wikihouse-assets/make-16.png diff --git a/wikihouse-assets/make.png b/wikihouse-plugin/wikihouse-assets/make.png similarity index 100% rename from wikihouse-assets/make.png rename to wikihouse-plugin/wikihouse-assets/make.png diff --git a/wikihouse-assets/upload-16.png b/wikihouse-plugin/wikihouse-assets/upload-16.png similarity index 100% rename from wikihouse-assets/upload-16.png rename to wikihouse-plugin/wikihouse-assets/upload-16.png diff --git a/wikihouse-assets/upload.png b/wikihouse-plugin/wikihouse-assets/upload.png similarity index 100% rename from wikihouse-assets/upload.png rename to wikihouse-plugin/wikihouse-assets/upload.png diff --git a/wikihouse.rb b/wikihouse-plugin/wikihouse.rb similarity index 100% rename from wikihouse.rb rename to wikihouse-plugin/wikihouse.rb From ecc56a6c0b724b21e5cc753a5f0268a72a668e60 Mon Sep 17 00:00:00 2001 From: Chris Musselle Date: Fri, 15 Mar 2013 00:08:54 +0000 Subject: [PATCH 03/31] Added .rbz file for extension install --- wikihouse_extension.rbz | Bin 0 -> 28195 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 wikihouse_extension.rbz diff --git a/wikihouse_extension.rbz b/wikihouse_extension.rbz new file mode 100644 index 0000000000000000000000000000000000000000..8b571ffb6a1f7b8907b145631529d78aff7497a6 GIT binary patch literal 28195 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 literal 0 HcmV?d00001 From d6efc62c3a6842fbe222d2dc8c2f9ec4b2024e87 Mon Sep 17 00:00:00 2001 From: Chris Musselle Date: Fri, 15 Mar 2013 00:14:14 +0000 Subject: [PATCH 04/31] Minor changes to README --- README.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 3f87fb7..03c9333 100644 --- a/README.md +++ b/README.md @@ -13,15 +13,14 @@ The Wikihouse plugin is now available as a SketchUp 8 extension, which allow eas To install: -1. Download the file `wikihouseExtension.rbz` +1. Download the file `wikihouse_extension.rbz` 2. Open SketchUp and navigate to **Window** > **Preferences** (Microsoft Windows) or **SketchUp** > **Preferences** (Mac OS X). 3. Click on Extensions, which will display the Extensions panel showing all current extensions in your plugin folder. -4. Click on the *Install Extension* button and locate the downloaded `wikihouseExtension.rbz` file to install it. +4. Click on the *Install Extension* button and locate the downloaded `wikihouse_extension.rbz` file to install it. -The plugin will then appear in the list of other extensions, and all necessary files will be copied to your plugins folder. If you ever wish to disable it, simply untick it in the list and restart SketchUp. +The plugin will then appear in the list of other extensions, and all necessary files will be copied to your plugins directory. If you ever wish to disable it, simply untick it in the list and restart SketchUp. -On previous versions of SketchUp to install, simply download the zip file and copy over the files into -your SketchUp `plugins` directory. +On previous versions of SketchUp to install, simply download the `wikihouse-plugin` directory and copy all contents over into your SketchUp `plugins` directory. **License** From 67f3b27a157f435ae456c990cfb09329df0b3be5 Mon Sep 17 00:00:00 2001 From: Chris Musselle Date: Sat, 16 Mar 2013 02:07:45 +0000 Subject: [PATCH 05/31] Changed folder name to wikihouse-extension --- .../wikihouse-assets/download-16.png | Bin .../wikihouse-assets/download.png | Bin .../wikihouse-assets/guide-16.png | Bin .../wikihouse-assets/guide.png | Bin .../wikihouse-assets/make-16.png | Bin .../wikihouse-assets/make.png | Bin .../wikihouse-assets/upload-16.png | Bin .../wikihouse-assets/upload.png | Bin .../wikihouse.rb | 0 ...se_extension.rb => wikihouse_extension_loader.rb | 0 10 files changed, 0 insertions(+), 0 deletions(-) rename {wikihouse-plugin => wikihouse-extension}/wikihouse-assets/download-16.png (100%) rename {wikihouse-plugin => wikihouse-extension}/wikihouse-assets/download.png (100%) rename {wikihouse-plugin => wikihouse-extension}/wikihouse-assets/guide-16.png (100%) rename {wikihouse-plugin => wikihouse-extension}/wikihouse-assets/guide.png (100%) rename {wikihouse-plugin => wikihouse-extension}/wikihouse-assets/make-16.png (100%) rename {wikihouse-plugin => wikihouse-extension}/wikihouse-assets/make.png (100%) rename {wikihouse-plugin => wikihouse-extension}/wikihouse-assets/upload-16.png (100%) rename {wikihouse-plugin => wikihouse-extension}/wikihouse-assets/upload.png (100%) rename {wikihouse-plugin => wikihouse-extension}/wikihouse.rb (100%) rename load_wikihouse_extension.rb => wikihouse_extension_loader.rb (100%) diff --git a/wikihouse-plugin/wikihouse-assets/download-16.png b/wikihouse-extension/wikihouse-assets/download-16.png similarity index 100% rename from wikihouse-plugin/wikihouse-assets/download-16.png rename to wikihouse-extension/wikihouse-assets/download-16.png diff --git a/wikihouse-plugin/wikihouse-assets/download.png b/wikihouse-extension/wikihouse-assets/download.png similarity index 100% rename from wikihouse-plugin/wikihouse-assets/download.png rename to wikihouse-extension/wikihouse-assets/download.png diff --git a/wikihouse-plugin/wikihouse-assets/guide-16.png b/wikihouse-extension/wikihouse-assets/guide-16.png similarity index 100% rename from wikihouse-plugin/wikihouse-assets/guide-16.png rename to wikihouse-extension/wikihouse-assets/guide-16.png diff --git a/wikihouse-plugin/wikihouse-assets/guide.png b/wikihouse-extension/wikihouse-assets/guide.png similarity index 100% rename from wikihouse-plugin/wikihouse-assets/guide.png rename to wikihouse-extension/wikihouse-assets/guide.png diff --git a/wikihouse-plugin/wikihouse-assets/make-16.png b/wikihouse-extension/wikihouse-assets/make-16.png similarity index 100% rename from wikihouse-plugin/wikihouse-assets/make-16.png rename to wikihouse-extension/wikihouse-assets/make-16.png diff --git a/wikihouse-plugin/wikihouse-assets/make.png b/wikihouse-extension/wikihouse-assets/make.png similarity index 100% rename from wikihouse-plugin/wikihouse-assets/make.png rename to wikihouse-extension/wikihouse-assets/make.png diff --git a/wikihouse-plugin/wikihouse-assets/upload-16.png b/wikihouse-extension/wikihouse-assets/upload-16.png similarity index 100% rename from wikihouse-plugin/wikihouse-assets/upload-16.png rename to wikihouse-extension/wikihouse-assets/upload-16.png diff --git a/wikihouse-plugin/wikihouse-assets/upload.png b/wikihouse-extension/wikihouse-assets/upload.png similarity index 100% rename from wikihouse-plugin/wikihouse-assets/upload.png rename to wikihouse-extension/wikihouse-assets/upload.png diff --git a/wikihouse-plugin/wikihouse.rb b/wikihouse-extension/wikihouse.rb similarity index 100% rename from wikihouse-plugin/wikihouse.rb rename to wikihouse-extension/wikihouse.rb diff --git a/load_wikihouse_extension.rb b/wikihouse_extension_loader.rb similarity index 100% rename from load_wikihouse_extension.rb rename to wikihouse_extension_loader.rb From 941a93521370abd23f2383ffdc07fffd408932f4 Mon Sep 17 00:00:00 2001 From: Chris Musselle Date: Mon, 18 Mar 2013 21:04:14 +0000 Subject: [PATCH 06/31] make or make build now generates wikihouse_extension.rbz make release should upload following to Amazon S3 bucket: wikihouse_extension.rbz and wikihouse_extension-{git-rev}.rbz Included .gitignore for .rbz files. Renamed all instances of plugin to extension for clarity. Updated install instruction in README --- .gitignore | 3 +++ Makefile | 17 +++++++++++++++++ README.md | 16 ++++++---------- wikihouse_extension.rbz | Bin 28195 -> 0 bytes wikihouse_extension_loader.rb | 10 +++++----- 5 files changed, 31 insertions(+), 15 deletions(-) create mode 100644 .gitignore create mode 100644 Makefile delete mode 100644 wikihouse_extension.rbz diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a9a5482 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +# Ruby Extension Files # +######################## +*.rbz \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..349caea --- /dev/null +++ b/Makefile @@ -0,0 +1,17 @@ +# Make file for creating wikihouse_extention.rbz + +assets=wikihouse-extension/wikihouse-assets/* +scripts=wikihouse-extension/* + +git-rev=$(git rev-parse --short=8 HEAD) + +# May need changing - not sure if full url is used for amazon buckets +wikihouse_bucket="https://wikihouse.s3.amazonaws.com/sketchup/" + +build: wikihouse_extension_loader.rb $(assets) $(scripts) + zip wikihouse_extension.rbz wikihouse_extension_loader.rb $(assets) $(scripts) + +release: wikihouse_extension.rbz + # s3put BUCKET/[OBJECT] [FILE] - OBJECT is the name FILE is saved as in BUCKET + s3put ${wikihouse_bucket}wikihouse_extension-$git-rev.rbz wikihouse_extension.rbz + s3put ${wikihouse_bucket}wikihouse_extension.rbz wikihouse_extension.rbz diff --git a/README.md b/README.md index 03c9333..acdcf0f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -This is the WikiHouse plugin for use with Google SketchUp. Using it, +This is the WikiHouse extension for use with Google SketchUp. Using it, you can: * Import designs from the WikiHouse library. @@ -8,19 +8,15 @@ you can: * Generate cutting sheets for your house designs. **Installation** - -The Wikihouse plugin is now available as a SketchUp 8 extension, which allow easier installing and managing of SketchUp plugins. - -To install: - -1. Download the file `wikihouse_extension.rbz` + +1. Download or clone the repository, then run `make build` to generate the `wikihouse_extension.rbz` file. 2. Open SketchUp and navigate to **Window** > **Preferences** (Microsoft Windows) or **SketchUp** > **Preferences** (Mac OS X). -3. Click on Extensions, which will display the Extensions panel showing all current extensions in your plugin folder. -4. Click on the *Install Extension* button and locate the downloaded `wikihouse_extension.rbz` file to install it. +3. Click on Extensions, which will display all current extensions installed. +4. Click on the *Install Extension* button and locate the built `wikihouse_extension.rbz` file to install it. The plugin will then appear in the list of other extensions, and all necessary files will be copied to your plugins directory. If you ever wish to disable it, simply untick it in the list and restart SketchUp. -On previous versions of SketchUp to install, simply download the `wikihouse-plugin` directory and copy all contents over into your SketchUp `plugins` directory. +On previous versions of SketchUp (version 7 and bellow) to install, simply download the `wikihouse-extension` directory and copy all contents over into your SketchUp `plugins` directory. **License** diff --git a/wikihouse_extension.rbz b/wikihouse_extension.rbz deleted file mode 100644 index 8b571ffb6a1f7b8907b145631529d78aff7497a6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28195 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 diff --git a/wikihouse_extension_loader.rb b/wikihouse_extension_loader.rb index d347cd5..754ee74 100644 --- a/wikihouse_extension_loader.rb +++ b/wikihouse_extension_loader.rb @@ -3,10 +3,10 @@ require 'sketchup.rb' require 'extensions.rb' -wikihouse_extension = SketchupExtension.new "Wikihouse Plugin", "wikihouse-plugin/wikihouse.rb" -wikihouse_extension.version = '0.1' -wikihouse_extension.description = "Allows for the sharing and downloading of wikihouse models at http://www.wikihouse.cc/, as well as the trasformation of models to cutting templates." -wikihouse_extension.creator = "Wikihouse Development Team" -wikihouse_extension.copyright = "Public Domain - 2013" +wikihouse_extension = SketchupExtension.new "Wikihouse Plugin", "wikihouse-extension/wikihouse.rb" +wikihouse_extension.version = ' 0.1' +wikihouse_extension.description = "Allows for the sharing and downloading of wikihouse models at http://www.wikihouse.cc/, as well as the traslation of models to cutting templates." +wikihouse_extension.creator = " Wikihouse Development Team" +wikihouse_extension.copyright = " Public Domain - 2013" Sketchup.register_extension wikihouse_extension, true \ No newline at end of file From 29689b1ebbcae41c9f9934ae38fdce77c2bbfc6d Mon Sep 17 00:00:00 2001 From: Chris Musselle Date: Mon, 1 Apr 2013 01:10:00 +0100 Subject: [PATCH 07/31] Introduced an encapsulating Module 'WikihouseExtension' to allow for namespace recalling between different files of ruby code. Beginning to split up wikihouse.rb into smaller files of related code. Run flags and config constants now in wikihouse_extension_loader before extension is loaded. Extra constants now in constants.rb Utility functions and classes now in utils.rb Output writters now in writers.rb SVG output currently has an error. Investigating. --- Makefile | 6 +- wikihouse-extension/lib/constants.rb | 57 + wikihouse-extension/lib/other.rb | 89 + wikihouse-extension/lib/utils.rb | 146 ++ wikihouse-extension/lib/writers.rb | 123 + wikihouse-extension/wikihouse.rb | 3494 ++++++++++++-------------- 6 files changed, 2011 insertions(+), 1904 deletions(-) create mode 100644 wikihouse-extension/lib/constants.rb create mode 100644 wikihouse-extension/lib/other.rb create mode 100644 wikihouse-extension/lib/utils.rb create mode 100644 wikihouse-extension/lib/writers.rb diff --git a/Makefile b/Makefile index 349caea..a889b81 100644 --- a/Makefile +++ b/Makefile @@ -1,15 +1,13 @@ # Make file for creating wikihouse_extention.rbz -assets=wikihouse-extension/wikihouse-assets/* -scripts=wikihouse-extension/* - git-rev=$(git rev-parse --short=8 HEAD) # May need changing - not sure if full url is used for amazon buckets wikihouse_bucket="https://wikihouse.s3.amazonaws.com/sketchup/" build: wikihouse_extension_loader.rb $(assets) $(scripts) - zip wikihouse_extension.rbz wikihouse_extension_loader.rb $(assets) $(scripts) + # Zip all files and subfolders recursively + zip -r wikihouse_extension.rbz wikihouse_extension_loader.rb wikihouse-extension/* release: wikihouse_extension.rbz # s3put BUCKET/[OBJECT] [FILE] - OBJECT is the name FILE is saved as in BUCKET diff --git a/wikihouse-extension/lib/constants.rb b/wikihouse-extension/lib/constants.rb new file mode 100644 index 0000000..49c5b49 --- /dev/null +++ b/wikihouse-extension/lib/constants.rb @@ -0,0 +1,57 @@ +# ------------------------------------------------------------------------------ +# Wikihouse Constants +# ------------------------------------------------------------------------------ + +module WikihouseExtension + + # Pannel stuff + PANEL_ID_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + PANEL_ID_ALPHABET_LENGTH = PANEL_ID_ALPHABET.length + + WIKIHOUSE_FONT_HEIGHT = 30.mm + WIKIHOUSE_PANEL_PADDING = 25.mm / 2 + WIKIHOUSE_SHEET_HEIGHT = 1200.mm + WIKIHOUSE_SHEET_MARGIN = 15.mm - WIKIHOUSE_PANEL_PADDING + WIKIHOUSE_SHEET_WIDTH = 2400.mm + + + # Set Wikihouse Pannel Dimentions + WIKIHOUSE_SHEET_INNER_HEIGHT = WIKIHOUSE_SHEET_HEIGHT - (2 * WIKIHOUSE_SHEET_MARGIN) + WIKIHOUSE_SHEET_INNER_WIDTH = WIKIHOUSE_SHEET_WIDTH - (2 * WIKIHOUSE_SHEET_MARGIN) + + WIKIHOUSE_DIMENSIONS = [ + WIKIHOUSE_SHEET_HEIGHT, + WIKIHOUSE_SHEET_WIDTH, + WIKIHOUSE_SHEET_INNER_HEIGHT, + WIKIHOUSE_SHEET_INNER_WIDTH, + WIKIHOUSE_SHEET_MARGIN, + WIKIHOUSE_PANEL_PADDING, + WIKIHOUSE_FONT_HEIGHT + ] + + # Status Messages + WIKIHOUSE_DETECTION_STATUS = gen_status_msg "Detecting matching faces" + WIKIHOUSE_DXF_STATUS = gen_status_msg "Generating DXF output" + WIKIHOUSE_LAYOUT_STATUS = gen_status_msg "Nesting panels for layout" + WIKIHOUSE_PANEL_STATUS = gen_status_msg "Generating panel data" + WIKIHOUSE_SVG_STATUS = gen_status_msg "Generating SVG output" + + # UI Message Box codes + REPLY_ABORT = 3 + REPLY_CANCEL = 2 + REPLY_NO = 7 + REPLY_OK = 1 + REPLY_RETRY = 4 + REPLY_YES = 6 + + # Dummy Group + class WikiHouseDummyGroup + attr_reader :name + + def initialize + @name = "Ungrouped Objects" + end + end + WIKIHOUSE_DUMMY_GROUP = WikiHouseDummyGroup.new + +end diff --git a/wikihouse-extension/lib/other.rb b/wikihouse-extension/lib/other.rb new file mode 100644 index 0000000..eed75a7 --- /dev/null +++ b/wikihouse-extension/lib/other.rb @@ -0,0 +1,89 @@ + +# ------------------------------------------------------------------------------ +# Centroid Calculation +# ------------------------------------------------------------------------------ + +# (Chris) Dont think this function is currently being used + +def get_face_center(face) + + # First, triangulate the polygon. + mesh = face.mesh + + # Initialise aggregation variables. + idx = 0 + xs = [] + ys = [] + areas = [] + + # For each triangle, calculate the surface area and center of mass. + for i in 0...mesh.count_polygons + + a, b, c = mesh.polygon_points_at i+1 + + ax, ay, _ = a.to_a + bx, by, _ = b.to_a + cx, cy, _ = c.to_a + + dax = ax - bx + dbx = bx - cx + dcx = cx - ax + day = ay - by + dby = by - cy + dcy = cy - ay + + la = Math.sqrt((dax * dax) + (day * day)) + lb = Math.sqrt((dbx * dbx) + (dby * dby)) + lc = Math.sqrt((dcx * dcx) + (dcy * dcy)) + + max = (ax + bx) / 2 + mbx = (bx + cx) / 2 + mcx = (cx + ax) / 2 + may = (ay + by) / 2 + mby = (by + cy) / 2 + mcy = (cy + ay) / 2 + + px = ((max * la) + (mbx * lb) + (mcx * lc)) / (la + lb + lc) + py = ((may * la) + (mby * lb) + (mcy * lc)) / (la + lb + lc) + + # angle = (Math.acos((la * la) + (lb * lb) - (lc * lc)) * Math::PI) / (360 * la * lb) + # area = (la * lb * Math.sin(angle)) / 2 + + s1, s2, s3 = [la, lb, lc].sort.reverse + top = (s1 + (s2 + s3)) * (s3 - (s1 - s2)) * (s3 + (s1 - s2)) * (s1 + (s2 - s3)) + + # TODO(tav): Read http://www.eecs.berkeley.edu/~wkahan/Triangle.pdf and + # figure out why this fails on triangles with small angles. + if top < 0 + puts "Failed surface area calculation" + next + end + + area = Math.sqrt(top) / 4 + + xs[idx] = px + ys[idx] = py + areas[idx] = area + + idx += 1 + + end + + # Calculate the total surface area. + total = areas.inject(0) { |t, a| a + t } + + # Calculate the weighted center points. + px, py = 0, 0 + for i in 0...xs.length + x, y, a = xs[i], ys[i], areas[i] + px += x * a + py += y * a + end + + # Calculate the center of mass. + px = px / total + py = py / total + + [px, py] + +end diff --git a/wikihouse-extension/lib/utils.rb b/wikihouse-extension/lib/utils.rb new file mode 100644 index 0000000..c387184 --- /dev/null +++ b/wikihouse-extension/lib/utils.rb @@ -0,0 +1,146 @@ + +module WikihouseExtension + + # ------------------------------------------------------------------------------ + # Utility Functions + # ------------------------------------------------------------------------------ + + # Path Utilities + def get_documents_directory(home, docs) + dir = File.join home, docs + if not (File.directory?(dir) and File.writable?(dir)) + home + else + dir + end + end + + def get_temp_directory + temp = '.' + for dir in [ENV['TMPDIR'], ENV['TMP'], ENV['TEMP'], ENV['USERPROFILE'], '/tmp'] + if dir and File.directory?(dir) and File.writable?(dir) + temp = dir + break + end + end + File.expand_path temp + end + + + # Status Messages + def gen_status_msg(msg) + return [ + msg + " .", + msg + " ..", + msg + " ...", + msg + " ....", + msg + " .....", + ] + end + + + def get_wikihouse_thumbnail(model, view, suffix) + filename = File.join WIKIHOUSE_TEMP, "#{model.guid}-#{suffix}.png" + opts = { + :antialias => true, + :compression => 0.8, + :filename => filename, + :height => [view.vpheight, 1600].min, + :transparent => true, + :width => [view.vpwidth, 1600].min + } + view.write_image opts + data = File.open(filename, 'rb') do |io| + io.read + end + File.delete filename + data + end + + def get_dom_value(dialog, id, value) + if value.length > 2097152 + dialog.execute_script "WIKIHOUSE_DATA = [#{value[0...2097152].inspect}];" + start, stop = 2097152, (2097152+2097152) + idx = 1 + while 1 + segment = value[start...stop] + if not segment + break + end + dialog.execute_script "WIKIHOUSE_DATA[#{idx}] = #{segment.inspect};" + idx += 1 + start = stop + stop = stop + 2097152 + end + dialog.execute_script "document.getElementById('#{id}').value = WIKIHOUSE_DATA.join('');" + else + dialog.execute_script "document.getElementById('#{id}').value = #{value.inspect};" + end + end + + def show_wikihouse_error(msg) + UI.messagebox "!! ERROR !!\n\n#{msg}" + end + + extend self + # Adds all instance methods previously defined here in a 'WikihouseExtension' namespace to + # the module itself, therfore allowing access to instance methods without the need to make a class first. + + # ------------------------------------------------------------------------------ + # Utility Classes + # ------------------------------------------------------------------------------ + + # App Observer + # ------------------------------------------------------------------------------ + class WikiHouseAppObserver < Sketchup::AppObserver + + def onNewModel(model) + end + + # TODO(tav): This doesn't seem to be getting called. + # (Chris) Should do now I think. Still need to test. + def onQuit + if WIKIHOUSE_DOWNLOADS.length > 0 + show_wikihouse_error "Aborting downloads from #{WIKIHOUSE_TITLE}" + end + if WIKIHOUSE_UPLOADS.length > 0 + show_wikihouse_error "Aborting uploads to #{WIKIHOUSE_TITLE}" + end + end + end + + # Load Handler + # ------------------------------------------------------------------------------ + # (Chris) For loading Wikihouse models via web? + + class WikiHouseLoader + + attr_accessor :cancel, :error + + def initialize(name) + @cancel = false + @error = nil + @name = name + end + + def cancelled? + @cancel + end + + def onFailure(error) + @error = error + Sketchup.set_status_text '' + end + + def onPercentChange(p) + Sketchup.set_status_text "LOADING #{name}: #{p.to_i}%" + end + + def onSuccess + Sketchup.set_status_text '' + end + + end + + +end diff --git a/wikihouse-extension/lib/writers.rb b/wikihouse-extension/lib/writers.rb new file mode 100644 index 0000000..89bda6d --- /dev/null +++ b/wikihouse-extension/lib/writers.rb @@ -0,0 +1,123 @@ + +module WikihouseExtension + + # ------------------------------------------------------------------------------ + # DXF Writer + # ------------------------------------------------------------------------------ + + class WikiHouseDXF + + def initialize(layout) + end + + def generate + "" + end + + end + + # ------------------------------------------------------------------------------ + # SVG Writer + # ------------------------------------------------------------------------------ + + class WikiHouseSVG + + def initialize(layout, scale) + @layout = layout + @scale = scale + end + + def generate + + layout = @layout + scale = @scale + + sheet_height, sheet_width, inner_height, inner_width, margin = layout.dimensions + sheets = layout.sheets + count = sheets.length + + scaled_height = scale * sheet_height + scaled_width = scale * sheet_width + total_height = scale * ((count * (sheet_height + (12 * margin))) + (margin * 10)) + total_width = scale * (sheet_width + (margin * 2)) + + svg = [] + svg << <<-HEADER.gsub(/^ {6}/, '') + + + + #{WIKIHOUSE_TITLE} Cutting Sheets" + + + + + HEADER + + loop_count = 0 + + for s in 0...count + + sheet = sheets[s] + base_x = scale * margin + base_y = scale * ((s * (sheet_height + (12 * margin))) + (margin * 9)) + + svg << "" + + base_x += scale * margin + base_y += scale * margin + + sheet.each do |loops, circles, outer_mapped, centroid, label| + + Sketchup.set_status_text WIKIHOUSE_SVG_STATUS[(loop_count/5) % 5] + loop_count += 1 + + svg << '' + + for i in 0...loops.length + circle = circles[i] + if circle + center, radius = circle + x = (scale * center.x) + base_x + y = (scale * center.y) + base_y + radius = scale * radius + svg << <<-CIRCLE.gsub(/^ {14}/, '') + + CIRCLE + else + loop = loops[i] + first = loop.shift + path = [] + path << "M #{(scale * first.x) + base_x} #{(scale * first.y) + base_y}" + loop.each do |point| + path << "L #{(scale * point.x) + base_x} #{(scale * point.y) + base_y}" + end + path << "Z" + svg << <<-PATH.gsub(/^ {14}/, '') + + PATH + end + end + + if label and label != "" + svg << <<-LABEL.gsub(/^ {12}/, '') + #{label} + LABEL + end + + svg << '' + + end + end + + svg << '' + svg << '' + svg.join "\n" + + end + + end + +end \ No newline at end of file diff --git a/wikihouse-extension/wikihouse.rb b/wikihouse-extension/wikihouse.rb index 3c9cf07..fc7cbfc 100755 --- a/wikihouse-extension/wikihouse.rb +++ b/wikihouse-extension/wikihouse.rb @@ -7,696 +7,385 @@ require 'sketchup.rb' -# ------------------------------------------------------------------------------ -# Path Utilities -# ------------------------------------------------------------------------------ - -def get_documents_directory(home, docs) - dir = File.join home, docs - if not (File.directory?(dir) and File.writable?(dir)) - home - else - dir - end -end - -def get_temp_directory - temp = '.' - for dir in [ENV['TMPDIR'], ENV['TMP'], ENV['TEMP'], ENV['USERPROFILE'], '/tmp'] - if dir and File.directory?(dir) and File.writable?(dir) - temp = dir - break - end - end - File.expand_path temp -end - -# ------------------------------------------------------------------------------ -# Some Constants -# ------------------------------------------------------------------------------ - -PANEL_ID_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" -PANEL_ID_ALPHABET_LENGTH = PANEL_ID_ALPHABET.length - -REPLY_ABORT = 3 -REPLY_CANCEL = 2 -REPLY_NO = 7 -REPLY_OK = 1 -REPLY_RETRY = 4 -REPLY_YES = 6 - -if RUBY_PLATFORM =~ /mswin/ - WIKIHOUSE_CONF_FILE = File.join ENV['APPDATA'], 'WikiHouse.conf' - WIKIHOUSE_SAVE = get_documents_directory ENV['USERPROFILE'], 'Documents' - WIKIHOUSE_MAC = false -else - WIKIHOUSE_CONF_FILE = File.join ENV['HOME'], '.wikihouse.conf' - WIKIHOUSE_SAVE = get_documents_directory ENV['HOME'], 'Documents' - WIKIHOUSE_MAC = true -end - -WIKIHOUSE_DEV = false -WIKIHOUSE_HIDE = false -WIKIHOUSE_LOCAL = false -WIKIHOUSE_SHORT_CIRCUIT = false - -if WIKIHOUSE_LOCAL - WIKIHOUSE_SERVER = "http://localhost:8080" -else - WIKIHOUSE_SERVER = "http://wikihouse-cc.appspot.com" -end - -WIKIHOUSE_DOWNLOAD_PATH = "/library/sketchup" -WIKIHOUSE_UPLOAD_PATH = "/library/designs/add/sketchup" -WIKIHOUSE_DOWNLOAD_URL = WIKIHOUSE_SERVER + WIKIHOUSE_DOWNLOAD_PATH -WIKIHOUSE_UPLOAD_URL = WIKIHOUSE_SERVER + WIKIHOUSE_UPLOAD_PATH - -WIKIHOUSE_PLUGIN_VERSION = "0.1" -WIKIHOUSE_SPEC = "0.1" -WIKIHOUSE_TEMP = get_temp_directory -WIKIHOUSE_TITLE = "WikiHouse" - -WIKIHOUSE_FONT_HEIGHT = 30.mm -WIKIHOUSE_PANEL_PADDING = 25.mm / 2 -WIKIHOUSE_SHEET_HEIGHT = 1200.mm -WIKIHOUSE_SHEET_MARGIN = 15.mm - WIKIHOUSE_PANEL_PADDING -WIKIHOUSE_SHEET_WIDTH = 2400.mm - -WIKIHOUSE_SHEET_INNER_HEIGHT = WIKIHOUSE_SHEET_HEIGHT - (2 * WIKIHOUSE_SHEET_MARGIN) -WIKIHOUSE_SHEET_INNER_WIDTH = WIKIHOUSE_SHEET_WIDTH - (2 * WIKIHOUSE_SHEET_MARGIN) - -WIKIHOUSE_DIMENSIONS = [ - WIKIHOUSE_SHEET_HEIGHT, - WIKIHOUSE_SHEET_WIDTH, - WIKIHOUSE_SHEET_INNER_HEIGHT, - WIKIHOUSE_SHEET_INNER_WIDTH, - WIKIHOUSE_SHEET_MARGIN, - WIKIHOUSE_PANEL_PADDING, - WIKIHOUSE_FONT_HEIGHT - ] - -# ------------------------------------------------------------------------------ -# Utility Functions -# ------------------------------------------------------------------------------ - -def gen_status_msg(msg) - return [ - msg + " .", - msg + " ..", - msg + " ...", - msg + " ....", - msg + " .....", - ] -end - -def get_wikihouse_thumbnail(model, view, suffix) - filename = File.join WIKIHOUSE_TEMP, "#{model.guid}-#{suffix}.png" - opts = { - :antialias => true, - :compression => 0.8, - :filename => filename, - :height => [view.vpheight, 1600].min, - :transparent => true, - :width => [view.vpwidth, 1600].min - } - view.write_image opts - data = File.open(filename, 'rb') do |io| - io.read - end - File.delete filename - data -end +# Using regular require statments don't seem to work with the embedded Ruby in SKetchup +# unless the full path of files is included in the $LOAD_PATH array -def set_dom_value(dialog, id, value) - if value.length > 2097152 - dialog.execute_script "WIKIHOUSE_DATA = [#{value[0...2097152].inspect}];" - start, stop = 2097152, (2097152+2097152) - idx = 1 - while 1 - segment = value[start...stop] - if not segment - break - end - dialog.execute_script "WIKIHOUSE_DATA[#{idx}] = #{segment.inspect};" - idx += 1 - start = stop - stop = stop + 2097152 - end - dialog.execute_script "document.getElementById('#{id}').value = WIKIHOUSE_DATA.join('');" - else - dialog.execute_script "document.getElementById('#{id}').value = #{value.inspect};" - end -end - -def show_wikihouse_error(msg) - UI.messagebox "!! ERROR !!\n\n#{msg}" -end - -# ------------------------------------------------------------------------------ -# Centroid Calculation -# ------------------------------------------------------------------------------ - -# (Chris) Dont think this function is currently being used - -def get_face_center(face) - - # First, triangulate the polygon. - mesh = face.mesh - - # Initialise aggregation variables. - idx = 0 - xs = [] - ys = [] - areas = [] - - # For each triangle, calculate the surface area and center of mass. - for i in 0...mesh.count_polygons - - a, b, c = mesh.polygon_points_at i+1 - - ax, ay, _ = a.to_a - bx, by, _ = b.to_a - cx, cy, _ = c.to_a - - dax = ax - bx - dbx = bx - cx - dcx = cx - ax - day = ay - by - dby = by - cy - dcy = cy - ay - - la = Math.sqrt((dax * dax) + (day * day)) - lb = Math.sqrt((dbx * dbx) + (dby * dby)) - lc = Math.sqrt((dcx * dcx) + (dcy * dcy)) - - max = (ax + bx) / 2 - mbx = (bx + cx) / 2 - mcx = (cx + ax) / 2 - may = (ay + by) / 2 - mby = (by + cy) / 2 - mcy = (cy + ay) / 2 - - px = ((max * la) + (mbx * lb) + (mcx * lc)) / (la + lb + lc) - py = ((may * la) + (mby * lb) + (mcy * lc)) / (la + lb + lc) - - # angle = (Math.acos((la * la) + (lb * lb) - (lc * lc)) * Math::PI) / (360 * la * lb) - # area = (la * lb * Math.sin(angle)) / 2 - - s1, s2, s3 = [la, lb, lc].sort.reverse - top = (s1 + (s2 + s3)) * (s3 - (s1 - s2)) * (s3 + (s1 - s2)) * (s1 + (s2 - s3)) - - # TODO(tav): Read http://www.eecs.berkeley.edu/~wkahan/Triangle.pdf and - # figure out why this fails on triangles with small angles. - if top < 0 - puts "Failed surface area calculation" - next - end - - area = Math.sqrt(top) / 4 - - xs[idx] = px - ys[idx] = py - areas[idx] = area - - idx += 1 - - end - - # Calculate the total surface area. - total = areas.inject(0) { |t, a| a + t } - - # Calculate the weighted center points. - px, py = 0, 0 - for i in 0...xs.length - x, y, a = xs[i], ys[i], areas[i] - px += x * a - py += y * a - end +# Add current working directory to $LOAD_PATH array +cwd = File.expand_path(File.dirname(__FILE__)) +$LOAD_PATH.unshift(cwd) unless $LOAD_PATH.include?(cwd) - # Calculate the center of mass. - px = px / total - py = py / total +# Add all files in the lib directory to the $LOAD_PATH array +abs_lib_path = File.join(File.expand_path(File.dirname(__FILE__)), "lib") +$LOAD_PATH.unshift(abs_lib_path) unless $LOAD_PATH.include?(abs_lib_path) +require_all(abs_lib_path) - [px, py] - -end - -# ------------------------------------------------------------------------------ -# Status Messages -# ------------------------------------------------------------------------------ - -WIKIHOUSE_DETECTION_STATUS = gen_status_msg "Detecting matching faces" -WIKIHOUSE_DXF_STATUS = gen_status_msg "Generating DXF output" -WIKIHOUSE_LAYOUT_STATUS = gen_status_msg "Nesting panels for layout" -WIKIHOUSE_PANEL_STATUS = gen_status_msg "Generating panel data" -WIKIHOUSE_SVG_STATUS = gen_status_msg "Generating SVG output" - -# ------------------------------------------------------------------------------ -# Load Handler -# ------------------------------------------------------------------------------ - -class WikiHouseLoader - - attr_accessor :cancel, :error - - def initialize(name) - @cancel = false - @error = nil - @name = name - end - - def cancelled? - @cancel - end - - def onFailure(error) - @error = error - Sketchup.set_status_text '' - end - - def onPercentChange(p) - Sketchup.set_status_text "LOADING #{name}: #{p.to_i}%" - end - - def onSuccess - Sketchup.set_status_text '' - end - -end - -# ------------------------------------------------------------------------------ -# App Observer -# ------------------------------------------------------------------------------ - -class WikiHouseAppObserver < Sketchup::AppObserver - - def onNewModel(model) - end - - # TODO(tav): This doesn't seem to be getting called. - def onQuit - if WIKIHOUSE_DOWNLOADS.length > 0 - show_wikihouse_error "Aborting downloads from #{WIKIHOUSE_TITLE}" - end - if WIKIHOUSE_UPLOADS.length > 0 - show_wikihouse_error "Aborting uploads to #{WIKIHOUSE_TITLE}" +module WikihouseExtension # Top Level Namespace + + # ------------------------------------------------------------------------------ + # SVG Writer + # ------------------------------------------------------------------------------ + + class WikiHouseSVG + + def initialize(layout, scale) + @layout = layout + @scale = scale end - end - -end - -# ------------------------------------------------------------------------------ -# Dummy Group -# ------------------------------------------------------------------------------ - -class WikiHouseDummyGroup - - attr_reader :name - - def initialize - @name = "Ungrouped Objects" - end - -end - -WIKIHOUSE_DUMMY_GROUP = WikiHouseDummyGroup.new - -# ------------------------------------------------------------------------------ -# DXF Writer -# ------------------------------------------------------------------------------ - -class WikiHouseDXF - - def initialize(layout) - end - - def generate - "" - end - -end - -# ------------------------------------------------------------------------------ -# SVG Writer -# ------------------------------------------------------------------------------ - -class WikiHouseSVG - - def initialize(layout, scale) - @layout = layout - @scale = scale - end - - def generate - - layout = @layout - scale = @scale - - sheet_height, sheet_width, inner_height, inner_width, margin = layout.dimensions - sheets = layout.sheets - count = sheets.length - - scaled_height = scale * sheet_height - scaled_width = scale * sheet_width - total_height = scale * ((count * (sheet_height + (12 * margin))) + (margin * 10)) - total_width = scale * (sheet_width + (margin * 2)) - - svg = [] - svg << <<-HEADER.gsub(/^ {6}/, '') - - - - #{WIKIHOUSE_TITLE} Cutting Sheets" - - - - - HEADER - - loop_count = 0 - - for s in 0...count - - sheet = sheets[s] - base_x = scale * margin - base_y = scale * ((s * (sheet_height + (12 * margin))) + (margin * 9)) - - svg << "" - - base_x += scale * margin - base_y += scale * margin - - sheet.each do |loops, circles, outer_mapped, centroid, label| - - Sketchup.set_status_text WIKIHOUSE_SVG_STATUS[(loop_count/5) % 5] - loop_count += 1 - - svg << '' - - for i in 0...loops.length - circle = circles[i] - if circle - center, radius = circle - x = (scale * center.x) + base_x - y = (scale * center.y) + base_y - radius = scale * radius - svg << <<-CIRCLE.gsub(/^ {14}/, '') - - CIRCLE - else - loop = loops[i] - first = loop.shift - path = [] - path << "M #{(scale * first.x) + base_x} #{(scale * first.y) + base_y}" - loop.each do |point| - path << "L #{(scale * point.x) + base_x} #{(scale * point.y) + base_y}" + + def generate + + layout = @layout + scale = @scale + + sheet_height, sheet_width, inner_height, inner_width, margin = layout.dimensions + sheets = layout.sheets + count = sheets.length + + scaled_height = scale * sheet_height + scaled_width = scale * sheet_width + total_height = scale * ((count * (sheet_height + (12 * margin))) + (margin * 10)) + total_width = scale * (sheet_width + (margin * 2)) + + svg = [] + svg << <<-HEADER.gsub(/^ {6}/, '') + + + + #{WIKIHOUSE_TITLE} Cutting Sheets" + + + + + HEADER + + loop_count = 0 + + for s in 0...count + + sheet = sheets[s] + base_x = scale * margin + base_y = scale * ((s * (sheet_height + (12 * margin))) + (margin * 9)) + + svg << "" + + base_x += scale * margin + base_y += scale * margin + + sheet.each do |loops, circles, outer_mapped, centroid, label| + + Sketchup.set_status_text WIKIHOUSE_SVG_STATUS[(loop_count/5) % 5] + loop_count += 1 + + svg << '' + + for i in 0...loops.length + circle = circles[i] + if circle + center, radius = circle + x = (scale * center.x) + base_x + y = (scale * center.y) + base_y + radius = scale * radius + svg << <<-CIRCLE.gsub(/^ {14}/, '') + + CIRCLE + else + loop = loops[i] + first = loop.shift + path = [] + path << "M #{(scale * first.x) + base_x} #{(scale * first.y) + base_y}" + loop.each do |point| + path << "L #{(scale * point.x) + base_x} #{(scale * point.y) + base_y}" + end + path << "Z" + svg << <<-PATH.gsub(/^ {14}/, '') + + PATH end - path << "Z" - svg << <<-PATH.gsub(/^ {14}/, '') - - PATH end + + if label and label != "" + svg << <<-LABEL.gsub(/^ {12}/, '') + #{label} + LABEL + end + + svg << '' + end - - if label and label != "" - svg << <<-LABEL.gsub(/^ {12}/, '') - #{label} - LABEL - end - - svg << '' - end + + svg << '' + svg << '' + svg.join "\n" + end - - svg << '' - svg << '' - svg.join "\n" - + end - -end - -# ------------------------------------------------------------------------------ -# Layout Engine -# ------------------------------------------------------------------------------ - -class WikiHouseLayoutEngine - - attr_accessor :sheets - attr_reader :dimensions - - def initialize(panels, root, dimensions) - - @dimensions = dimensions - @sheets = sheets = [] - - # Set local variables to save repeated lookups. - sheet_height, sheet_width, inner_height, inner_width, - sheet_margin, panel_padding, font_height = dimensions - - # Filter out the singletons from the other panels. - singletons = panels.select { |panel| panel.singleton } - panels = panels.select { |panel| !panel.singleton } - - # Loop through the panels. - panels.map! do |panel| - - # Get padding related info. - no_padding = panel.no_padding - - # Get the bounding box. - min = panel.min - max = panel.max - min_x, min_y = min.x, min.y - max_x, max_y = max.x, max.y - - # Set a flag to indicate clipped panels. - clipped = false - - # Determine if the potential savings exceeds the hard-coded threshold. If - # so, see if we can generate an outline with rectangular areas clipped - # from each corner. - if (panel.bounds_area - panel.shell_area) > 50 - # puts (panel.bounds_area - panel.shell_area) - end - - # Otherwise, treat the bounding box as the outline. - if not clipped - - # Define the inner outline. - inner = [[min_x, min_y, 0], [max_x, min_y, 0], [max_x, max_y, 0], [min_x, max_y, 0]] - - # Add padding around each side. - if not no_padding - min_x -= panel_padding - min_y -= panel_padding - max_x += panel_padding - max_y += panel_padding - elsif no_padding == "w" - min_y -= panel_padding - max_y += panel_padding - elsif no_padding == "h" - min_x -= panel_padding - max_x += panel_padding + + # ------------------------------------------------------------------------------ + # Layout Engine + # ------------------------------------------------------------------------------ + + class WikiHouseLayoutEngine + + attr_accessor :sheets + attr_reader :dimensions + + def initialize(panels, root, dimensions) + + @dimensions = dimensions + @sheets = sheets = [] + + # Set local variables to save repeated lookups. + sheet_height, sheet_width, inner_height, inner_width, + sheet_margin, panel_padding, font_height = dimensions + + # Filter out the singletons from the other panels. + singletons = panels.select { |panel| panel.singleton } + panels = panels.select { |panel| !panel.singleton } + + # Loop through the panels. + panels.map! do |panel| + + # Get padding related info. + no_padding = panel.no_padding + + # Get the bounding box. + min = panel.min + max = panel.max + min_x, min_y = min.x, min.y + max_x, max_y = max.x, max.y + + # Set a flag to indicate clipped panels. + clipped = false + + # Determine if the potential savings exceeds the hard-coded threshold. If + # so, see if we can generate an outline with rectangular areas clipped + # from each corner. + if (panel.bounds_area - panel.shell_area) > 50 + # puts (panel.bounds_area - panel.shell_area) end - - # Calculate the surface area that will be occupied by this panel. - width = max_x - min_x - height = max_y - min_y - area = width * height - - # Define the padded outer outline. - # outline = [[min_x, max_y, 0], [max_x, max_y, 0], [max_x, min_y, 0], [min_x, min_y, 0]] - outer = [[min_x, min_y, 0], [max_x, min_y, 0], [max_x, max_y, 0], [min_x, max_y, 0]] - outlines = [[nil, inner, outer]] - - # See if the panel can be rotated, if so add the transformation. - if not no_padding - if (inner_width > height) and (inner_height > width) - # inner = [inner[3], inner[0], inner[1], inner[2]] - # outer = [outer[3], outer[0], outer[1], outer[2]] - outlines << [90.degrees, inner, outer] - outlines << [270.degrees, inner, outer] + + # Otherwise, treat the bounding box as the outline. + if not clipped + + # Define the inner outline. + inner = [[min_x, min_y, 0], [max_x, min_y, 0], [max_x, max_y, 0], [min_x, max_y, 0]] + + # Add padding around each side. + if not no_padding + min_x -= panel_padding + min_y -= panel_padding + max_x += panel_padding + max_y += panel_padding + elsif no_padding == "w" + min_y -= panel_padding + max_y += panel_padding + elsif no_padding == "h" + min_x -= panel_padding + max_x += panel_padding + end + + # Calculate the surface area that will be occupied by this panel. + width = max_x - min_x + height = max_y - min_y + area = width * height + + # Define the padded outer outline. + # outline = [[min_x, max_y, 0], [max_x, max_y, 0], [max_x, min_y, 0], [min_x, min_y, 0]] + outer = [[min_x, min_y, 0], [max_x, min_y, 0], [max_x, max_y, 0], [min_x, max_y, 0]] + outlines = [[nil, inner, outer]] + + # See if the panel can be rotated, if so add the transformation. + if not no_padding + if (inner_width > height) and (inner_height > width) + # inner = [inner[3], inner[0], inner[1], inner[2]] + # outer = [outer[3], outer[0], outer[1], outer[2]] + outlines << [90.degrees, inner, outer] + outlines << [270.degrees, inner, outer] + end + outlines << [180.degrees, inner, outer] end - outlines << [180.degrees, inner, outer] + end - + + # Save the generated data. + [panel, outlines, area, panel.labels.dup] + end - - # Save the generated data. - [panel, outlines, area, panel.labels.dup] - - end - - # Sort the panels by surface area. - panels = panels.sort_by { |data| data[2] }.reverse - - # Generate new groups to hold sheet faces. - inner_group = root.add_group - inner_faces = inner_group.entities - outer_group = root.add_group - outer_faces = outer_group.entities - temp_group = root.add_group - temp_faces = temp_group.entities - total_area = inner_width * inner_height - - # Initialise the loop counter. - loop_count = 0 - - # Make local certain global constants. - outside = Sketchup::Face::PointOutside - - # panels = panels[-10...-1] - # panels = panels[-5...-1] - c = 0 - - # Do the optimising layout. - while 1 - - # Create a fresh sheet. - sheet = [] - available_area = total_area - idx = 0 - placed_i = [] - placed_o = [] - - while available_area > 0 - - Sketchup.set_status_text WIKIHOUSE_LAYOUT_STATUS[(loop_count/20) % 5] - loop_count += 1 - - panel_data = panels[idx] - if not panel_data - break - end - - panel, outlines, panel_area, labels = panel_data - if panel_area > available_area - idx += 1 - next - end - - match = true - t = nil - used = nil - - # If this is the first item, do the cheap placement check. - if sheet.length == 0 - transform, inner, outer = outlines[0] - point = outer[0] - translate = Geom::Transformation.translation [-point[0], -point[1], 0] - inner.each do |point| - point = translate * point - if (point.x > inner_width) or (-point.y > inner_height) - p (point.x - inner_width) - p (point.y - inner_height) - match = false - break - end + + # Sort the panels by surface area. + panels = panels.sort_by { |data| data[2] }.reverse + + # Generate new groups to hold sheet faces. + inner_group = root.add_group + inner_faces = inner_group.entities + outer_group = root.add_group + outer_faces = outer_group.entities + temp_group = root.add_group + temp_faces = temp_group.entities + total_area = inner_width * inner_height + + # Initialise the loop counter. + loop_count = 0 + + # Make local certain global constants. + outside = Sketchup::Face::PointOutside + + # panels = panels[-10...-1] + # panels = panels[-5...-1] + c = 0 + + # Do the optimising layout. + while 1 + + # Create a fresh sheet. + sheet = [] + available_area = total_area + idx = 0 + placed_i = [] + placed_o = [] + + while available_area > 0 + + Sketchup.set_status_text WIKIHOUSE_LAYOUT_STATUS[(loop_count/20) % 5] + loop_count += 1 + + panel_data = panels[idx] + if not panel_data + break end - if not match - puts "Error: couldn't place panel onto an empty sheet" - panels.delete_at idx + + panel, outlines, panel_area, labels = panel_data + if panel_area > available_area + idx += 1 next end - t = translate - used = [inner, outer] - else - # Otherwise, loop around the already placed panel regions and see if - # the outline can be placed next to it. - match = false - placed_o.each do |face| - # Loop through the vertices of the available region. - face.outer_loop.vertices.each do |vertex| - origin = vertex.position - # Loop through each outline. - outlines.each do |angle, inner, outer| - # Loop through every vertex of the outline, starting from the - # top left. - p_idx = -1 - all_match = true - while 1 - p0 = outer[p_idx] - if not p0 - break - end - transform = Geom::Transformation.translation([origin.x - p0[0], origin.y - p0[1], 0]) - if angle - transform = transform * Geom::Transformation.rotation(origin, Z_AXIS, angle) - end - # Check every point to see if it's within the available region. + + match = true + t = nil + used = nil + + # If this is the first item, do the cheap placement check. + if sheet.length == 0 + transform, inner, outer = outlines[0] + point = outer[0] + translate = Geom::Transformation.translation [-point[0], -point[1], 0] + inner.each do |point| + point = translate * point + if (point.x > inner_width) or (-point.y > inner_height) + p (point.x - inner_width) + p (point.y - inner_height) + match = false + break + end + end + if not match + puts "Error: couldn't place panel onto an empty sheet" + panels.delete_at idx + next + end + t = translate + used = [inner, outer] + else + # Otherwise, loop around the already placed panel regions and see if + # the outline can be placed next to it. + match = false + placed_o.each do |face| + # Loop through the vertices of the available region. + face.outer_loop.vertices.each do |vertex| + origin = vertex.position + # Loop through each outline. + outlines.each do |angle, inner, outer| + # Loop through every vertex of the outline, starting from the + # top left. + p_idx = -1 all_match = true - inner.each do |point| - point = transform * point - px, py = point.x, point.y - if (px < 0) or (py < 0) or (px > inner_width) or (py > inner_height) - all_match = false + while 1 + p0 = outer[p_idx] + if not p0 break end - placed_o.each do |placement| - if placement.classify_point(point) != outside + transform = Geom::Transformation.translation([origin.x - p0[0], origin.y - p0[1], 0]) + if angle + transform = transform * Geom::Transformation.rotation(origin, Z_AXIS, angle) + end + # Check every point to see if it's within the available region. + all_match = true + inner.each do |point| + point = transform * point + px, py = point.x, point.y + if (px < 0) or (py < 0) or (px > inner_width) or (py > inner_height) all_match = false break end - end - if not all_match - break - end - end - # If the vertices don't overlap, check that the edges don't - # intersect. - if all_match - # TODO(tav): Optimise with a sweep line algorithm variant: - # http://en.wikipedia.org/wiki/Sweep_line_algorithm - outer_mapped = outer.map { |point| transform * point } - for i in 0...outer.length - p1 = outer_mapped[i] - p2 = outer_mapped[i+1] - if not p2 - p2 = outer_mapped[0] + placed_o.each do |placement| + if placement.classify_point(point) != outside + all_match = false + break + end end - p1x, p1y = p1.x, p1.y - p2x, p2y = p2.x, p2.y - s1 = p2x - p1x - s2 = p2y - p1y - edge = [p1, [s1, s2, 0]] - edge_length = Math.sqrt((s1 * s1) + (s2 * s2)) - placed_i.each do |placement| - placement.edges.each do |other_edge| - intersection = Geom.intersect_line_line edge, other_edge.line - if intersection - p3x, p3y = intersection.x, intersection.y - s1 = p3x - p1x - s2 = p3y - p1y - length = Math.sqrt((s1 * s1) + (s2 * s2)) - if length > edge_length - next - end - s1 = p3x - p2x - s2 = p3y - p2y - length = Math.sqrt((s1 * s1) + (s2 * s2)) - if length > edge_length - next - end - other_edge_length = other_edge.length - p4, p5 = other_edge.start.position, other_edge.end.position - s1 = p3x - p4.x - s2 = p3y - p4.y - length = Math.sqrt((s1 * s1) + (s2 * s2)) - if length > other_edge_length - next - end - s1 = p3x - p5.x - s2 = p3y - p5.y - length = Math.sqrt((s1 * s1) + (s2 * s2)) - if length > other_edge_length - next + if not all_match + break + end + end + # If the vertices don't overlap, check that the edges don't + # intersect. + if all_match + # TODO(tav): Optimise with a sweep line algorithm variant: + # http://en.wikipedia.org/wiki/Sweep_line_algorithm + outer_mapped = outer.map { |point| transform * point } + for i in 0...outer.length + p1 = outer_mapped[i] + p2 = outer_mapped[i+1] + if not p2 + p2 = outer_mapped[0] + end + p1x, p1y = p1.x, p1.y + p2x, p2y = p2.x, p2.y + s1 = p2x - p1x + s2 = p2y - p1y + edge = [p1, [s1, s2, 0]] + edge_length = Math.sqrt((s1 * s1) + (s2 * s2)) + placed_i.each do |placement| + placement.edges.each do |other_edge| + intersection = Geom.intersect_line_line edge, other_edge.line + if intersection + p3x, p3y = intersection.x, intersection.y + s1 = p3x - p1x + s2 = p3y - p1y + length = Math.sqrt((s1 * s1) + (s2 * s2)) + if length > edge_length + next + end + s1 = p3x - p2x + s2 = p3y - p2y + length = Math.sqrt((s1 * s1) + (s2 * s2)) + if length > edge_length + next + end + other_edge_length = other_edge.length + p4, p5 = other_edge.start.position, other_edge.end.position + s1 = p3x - p4.x + s2 = p3y - p4.y + length = Math.sqrt((s1 * s1) + (s2 * s2)) + if length > other_edge_length + next + end + s1 = p3x - p5.x + s2 = p3y - p5.y + length = Math.sqrt((s1 * s1) + (s2 * s2)) + if length > other_edge_length + next + end + all_match = false + break end - all_match = false + end + if not all_match break end end @@ -704,17 +393,17 @@ def initialize(panels, root, dimensions) break end end - if not all_match - break - end + end + if all_match + match = true + t = transform + used = [inner, outer] + end + p_idx -= 1 + if match + break end end - if all_match - match = true - t = transform - used = [inner, outer] - end - p_idx -= 1 if match break end @@ -727,334 +416,304 @@ def initialize(panels, root, dimensions) break end end - if match - break - end end - end - - if match - - available_area -= panel_area - inner_faces.add_face(used[0].map { |p| t * p }) - outer_faces.add_face(used[1].map { |p| t * p }) - placed_i = inner_faces.select { |e| e.typename == "Face" } - placed_o = outer_faces.select { |e| e.typename == "Face" } - - # Generate the new loop vertices. - loops = panel.loops.map do |loop| - loop.map do |point| - t * point + + if match + + available_area -= panel_area + inner_faces.add_face(used[0].map { |p| t * p }) + outer_faces.add_face(used[1].map { |p| t * p }) + placed_i = inner_faces.select { |e| e.typename == "Face" } + placed_o = outer_faces.select { |e| e.typename == "Face" } + + # Generate the new loop vertices. + loops = panel.loops.map do |loop| + loop.map do |point| + t * point + end end - end - - # Generate the new circle data. - circles = panel.circles.map do |circle| - if circle - center = t * circle[0] - [center, circle[1]] - else - nil + + # Generate the new circle data. + circles = panel.circles.map do |circle| + if circle + center = t * circle[0] + [center, circle[1]] + else + nil + end end + + # Generate the new centroid. + centroid = t * panel.centroid + + # Get the label. + label = labels.pop + + # If this was the last label, remove the panel. + if labels.length == 0 + panels.delete_at idx + end + + outer_mapped = outer.map { |p| t * p } + + # Append the generated data to the current sheet. + sheet << [loops, circles, outer_mapped, centroid, label] + c += 1 + + else + + # We do not have a match, try the next panel. + idx += 1 + end - - # Generate the new centroid. - centroid = t * panel.centroid - - # Get the label. - label = labels.pop - - # If this was the last label, remove the panel. - if labels.length == 0 - panels.delete_at idx - end - - outer_mapped = outer.map { |p| t * p } - - # Append the generated data to the current sheet. - sheet << [loops, circles, outer_mapped, centroid, label] - c += 1 - - else - - # We do not have a match, try the next panel. - idx += 1 - + end - - end - - # If no panels could be fitted, break so as to avoid an infinite loop. - if sheet.length == 0 - break - end - - # Add the sheet to the collection. - sheets << sheet - - # If there are no more panels remaining, exit the loop. - if panels.length == 0 - break + + # If no panels could be fitted, break so as to avoid an infinite loop. + if sheet.length == 0 + break + end + + # Add the sheet to the collection. + sheets << sheet + + # If there are no more panels remaining, exit the loop. + if panels.length == 0 + break + end + + # Wipe the generated entities. + inner_faces.clear! + outer_faces.clear! + end - - # Wipe the generated entities. - inner_faces.clear! - outer_faces.clear! - + + # Delete the generated sheet group. + root.erase_entities [inner_group, outer_group] + end - - # Delete the generated sheet group. - root.erase_entities [inner_group, outer_group] - + end - -end - -# ------------------------------------------------------------------------------ -# Panel -# ------------------------------------------------------------------------------ - -class WikiHousePanel - - attr_accessor :area, :centroid, :circles, :labels, :loops, :max, :min - attr_reader :bounds_area, :error, :no_padding, :shell_area, :singleton - - def initialize(root, face, transform, labels, limits) - - # Initalise some of the object attributes. - @error = nil - @labels = labels - @no_padding = false - @singleton = false - - # Initialise a variable to hold temporarily generated entities. - to_delete = [] - - # Create a new face with the vertices transformed if the transformed areas - # do not match. - if (face.area - face.area(transform)).abs > 0.1 - group_entity = root.add_group - to_delete << group_entity - group = group_entity.entities - tface = group.add_face(face.outer_loop.vertices.map {|v| transform * v.position }) - face.loops.each do |loop| - if not loop.outer? - hole = group.add_face(loop.vertices.map {|v| transform * v.position }) - hole.erase! if hole.valid? + + # ------------------------------------------------------------------------------ + # Panel + # ------------------------------------------------------------------------------ + + class WikiHousePanel + + attr_accessor :area, :centroid, :circles, :labels, :loops, :max, :min + attr_reader :bounds_area, :error, :no_padding, :shell_area, :singleton + + def initialize(root, face, transform, labels, limits) + + # Initalise some of the object attributes. + @error = nil + @labels = labels + @no_padding = false + @singleton = false + + # Initialise a variable to hold temporarily generated entities. + to_delete = [] + + # Create a new face with the vertices transformed if the transformed areas + # do not match. + if (face.area - face.area(transform)).abs > 0.1 + group_entity = root.add_group + to_delete << group_entity + group = group_entity.entities + tface = group.add_face(face.outer_loop.vertices.map {|v| transform * v.position }) + face.loops.each do |loop| + if not loop.outer? + hole = group.add_face(loop.vertices.map {|v| transform * v.position }) + hole.erase! if hole.valid? + end end + face = tface end - face = tface - end - - # Save the total surface area of the face. - total_area = face.area - - # Find the normal to the face. - normal = face.normal - y_axis = normal.axes[1] - - # See if the face is parallel to any of the base axes. - if normal.parallel? X_AXIS - x, y = 1, 2 - elsif normal.parallel? Y_AXIS - x, y = 0, 2 - elsif normal.parallel? Z_AXIS - x, y = 0, 1 - else - x, y = nil, nil - end - - # Initialise the ``loops`` variable. - loops = [] - - # Initialise a reference point for transforming slanted faces. - base = face.outer_loop.vertices[0].position - - # Loop through the edges and convert the face into a 2D polygon -- ensuring - # that we are traversing the edges in the right order. - face.loops.each do |loop| - newloop = [] - if loop.outer? - loops.insert 0, newloop + + # Save the total surface area of the face. + total_area = face.area + + # Find the normal to the face. + normal = face.normal + y_axis = normal.axes[1] + + # See if the face is parallel to any of the base axes. + if normal.parallel? X_AXIS + x, y = 1, 2 + elsif normal.parallel? Y_AXIS + x, y = 0, 2 + elsif normal.parallel? Z_AXIS + x, y = 0, 1 else - loops << newloop + x, y = nil, nil end - edgeuse = first = loop.edgeuses[0] - virgin = true - prev = nil - while 1 - edge = edgeuse.edge - if virgin - start = edge.start - stop = edge.end - next_edge = edgeuse.next.edge - next_start = next_edge.start - next_stop = next_edge.end - if (start == next_start) or (start == next_stop) - stop, start = start, stop - elsif not ((stop == next_start) or (stop == next_stop)) - @error = "Unexpected edge connection" - return - end - virgin = nil + + # Initialise the ``loops`` variable. + loops = [] + + # Initialise a reference point for transforming slanted faces. + base = face.outer_loop.vertices[0].position + + # Loop through the edges and convert the face into a 2D polygon -- ensuring + # that we are traversing the edges in the right order. + face.loops.each do |loop| + newloop = [] + if loop.outer? + loops.insert 0, newloop else - start = edge.start - stop = edge.end - if stop == prev - stop, start = start, stop - elsif not start == prev - @error = "Unexpected edge connection" - return - end + loops << newloop end - if x - # If the face is parallel to a base axis, use the cheap conversion - # route. - point = start.position.to_a - newloop << [point[x], point[y], 0] - else - # Otherwise, handle the case where the face is angled at a slope by - # realigning edges relative to the origin and rotating them according - # to their angle to the y-axis. - point = start.position - edge = Geom::Vector3d.new(point.x - base.x, point.y - base.y, point.z - base.z) - if not edge.valid? - newloop << [base.x, base.y, 0] + edgeuse = first = loop.edgeuses[0] + virgin = true + prev = nil + while 1 + edge = edgeuse.edge + if virgin + start = edge.start + stop = edge.end + next_edge = edgeuse.next.edge + next_start = next_edge.start + next_stop = next_edge.end + if (start == next_start) or (start == next_stop) + stop, start = start, stop + elsif not ((stop == next_start) or (stop == next_stop)) + @error = "Unexpected edge connection" + return + end + virgin = nil + else + start = edge.start + stop = edge.end + if stop == prev + stop, start = start, stop + elsif not start == prev + @error = "Unexpected edge connection" + return + end + end + if x + # If the face is parallel to a base axis, use the cheap conversion + # route. + point = start.position.to_a + newloop << [point[x], point[y], 0] else - if edge.samedirection? y_axis - angle = 0 - elsif edge.parallel? y_axis - angle = Math::PI + # Otherwise, handle the case where the face is angled at a slope by + # realigning edges relative to the origin and rotating them according + # to their angle to the y-axis. + point = start.position + edge = Geom::Vector3d.new(point.x - base.x, point.y - base.y, point.z - base.z) + if not edge.valid? + newloop << [base.x, base.y, 0] else - angle = edge.angle_between y_axis - if not edge.cross(y_axis).samedirection? normal - angle = -angle + if edge.samedirection? y_axis + angle = 0 + elsif edge.parallel? y_axis + angle = Math::PI + else + angle = edge.angle_between y_axis + if not edge.cross(y_axis).samedirection? normal + angle = -angle + end end + rotate = Geom::Transformation.rotation ORIGIN, Z_AXIS, angle + newedge = rotate * Geom::Vector3d.new(edge.length, 0, 0) + newloop << [base.x + newedge.x, base.y + newedge.y, 0] end - rotate = Geom::Transformation.rotation ORIGIN, Z_AXIS, angle - newedge = rotate * Geom::Vector3d.new(edge.length, 0, 0) - newloop << [base.x + newedge.x, base.y + newedge.y, 0] end - end - edgeuse = edgeuse.next - if edgeuse == first - break - end - prev = stop - end - end - - # Initialise some more meta variables. - areas = [] - circles = [] - cxs, cys = [], [] - intersections = [] - outer_loop = true - - # Go through the various loops calculating centroids and intersection points - # of potential curves. - loops.each do |loop| - idx = 0 - intersect_points = [] - area = 0 - cx, cy = 0, 0 - while 1 - # Get the next three points on the loop. - p1, p2, p3 = loop[idx...idx+3] - if not p3 - if not p1 + edgeuse = edgeuse.next + if edgeuse == first break end - if not p2 - # Loop around to the first edge. - p2 = loop[0] - p3 = loop[1] - else - # Loop around to the first point. - p3 = loop[0] - end + prev = stop end - # Construct the edge vectors. - edge1 = Geom::Vector3d.new(p2.x - p1.x, p2.y - p1.y, p2.z - p1.z) - edge2 = Geom::Vector3d.new(p3.x - p2.x, p3.y - p2.y, p3.z - p2.z) - intersect = nil - if not edge1.parallel? edge2 - # Find the perpendicular vectors. - cross = edge1.cross edge2 - vec1 = edge1.cross cross - vec2 = edge2.cross cross - # Find the midpoints. - mid1 = Geom.linear_combination 0.5, p1, 0.5, p2 - mid2 = Geom.linear_combination 0.5, p2, 0.5, p3 - # Try finding an intersection. - line1 = [mid1, vec1] - line2 = [mid2, vec2] - intersect = Geom.intersect_line_line line1, line2 - # If no intersection, try finding one in the other direction. - if not intersect - vec1.reverse! - vec2.reverse! + end + + # Initialise some more meta variables. + areas = [] + circles = [] + cxs, cys = [], [] + intersections = [] + outer_loop = true + + # Go through the various loops calculating centroids and intersection points + # of potential curves. + loops.each do |loop| + idx = 0 + intersect_points = [] + area = 0 + cx, cy = 0, 0 + while 1 + # Get the next three points on the loop. + p1, p2, p3 = loop[idx...idx+3] + if not p3 + if not p1 + break + end + if not p2 + # Loop around to the first edge. + p2 = loop[0] + p3 = loop[1] + else + # Loop around to the first point. + p3 = loop[0] + end + end + # Construct the edge vectors. + edge1 = Geom::Vector3d.new(p2.x - p1.x, p2.y - p1.y, p2.z - p1.z) + edge2 = Geom::Vector3d.new(p3.x - p2.x, p3.y - p2.y, p3.z - p2.z) + intersect = nil + if not edge1.parallel? edge2 + # Find the perpendicular vectors. + cross = edge1.cross edge2 + vec1 = edge1.cross cross + vec2 = edge2.cross cross + # Find the midpoints. + mid1 = Geom.linear_combination 0.5, p1, 0.5, p2 + mid2 = Geom.linear_combination 0.5, p2, 0.5, p3 + # Try finding an intersection. + line1 = [mid1, vec1] + line2 = [mid2, vec2] intersect = Geom.intersect_line_line line1, line2 + # If no intersection, try finding one in the other direction. + if not intersect + vec1.reverse! + vec2.reverse! + intersect = Geom.intersect_line_line line1, line2 + end end + intersect_points << intersect + if p3 + x1, y1 = p1.x, p1.y + x2, y2 = p2.x, p2.y + cross = (x1 * y2) - (x2 * y1) + area += cross + cx += (x1 + x2) * cross + cy += (y1 + y2) * cross + end + idx += 1 end - intersect_points << intersect - if p3 - x1, y1 = p1.x, p1.y - x2, y2 = p2.x, p2.y - cross = (x1 * y2) - (x2 * y1) - area += cross - cx += (x1 + x2) * cross - cy += (y1 + y2) * cross - end - idx += 1 - end - intersections << intersect_points - area = area * 0.5 - areas << area.abs - cxs << (cx / (6 * area)) - cys << (cy / (6 * area)) - outer_loop = false - end - - # Allocate variables relating to the minimal alignment. - bounds_area = nil - bounds_min = nil - bounds_max = nil - transform = nil - outer = loops[0] - - # Unpack panel dimension limits. - panel_height, panel_width, panel_max_height, panel_max_width, padding = limits - - # Try rotating at half degree intervals and find the transformation which - # occupies the most minimal bounding rectangle. - (0...180.0).step(0.5) do |angle| - t = Geom::Transformation.rotation ORIGIN, Z_AXIS, angle.degrees - bounds = Geom::BoundingBox.new - outer.each do |point| - point = t * point - bounds.add point - end - min, max = bounds.min, bounds.max - height = max.y - min.y - width = max.x - min.x - if (height - panel_height) > 0.1 - next - end - if (width - panel_width) > 0.1 - next - end - area = width * height - if (not bounds_area) or ((bounds_area - area) > 0.1) - bounds_area = area - bounds_min, bounds_max = min, max - transform = t + intersections << intersect_points + area = area * 0.5 + areas << area.abs + cxs << (cx / (6 * area)) + cys << (cy / (6 * area)) + outer_loop = false end - end - - # If we couldn't find a fitting angle, try again at 0.1 degree intervals. - if not transform - (0...180.0).step(0.1) do |angle| + + # Allocate variables relating to the minimal alignment. + bounds_area = nil + bounds_min = nil + bounds_max = nil + transform = nil + outer = loops[0] + + # Unpack panel dimension limits. + panel_height, panel_width, panel_max_height, panel_max_width, padding = limits + + # Try rotating at half degree intervals and find the transformation which + # occupies the most minimal bounding rectangle. + (0...180.0).step(0.5) do |angle| t = Geom::Transformation.rotation ORIGIN, Z_AXIS, angle.degrees bounds = Geom::BoundingBox.new outer.each do |point| @@ -1064,10 +723,10 @@ def initialize(root, face, transform, labels, limits) min, max = bounds.min, bounds.max height = max.y - min.y width = max.x - min.x - if (width - panel_max_width) > 0.1 + if (height - panel_height) > 0.1 next end - if (height - panel_max_height) > 0.1 + if (width - panel_width) > 0.1 next end area = width * height @@ -1077,1001 +736,1035 @@ def initialize(root, face, transform, labels, limits) transform = t end end - end - - # If we still couldn't find a fitting, abort. - if not transform - @error = "Couldn't fit panel within cutting sheet" - puts @error - return - end - - # Set the panel to a singleton panel (i.e. without any padding) if it is - # larger than the height and width, otherwise set the no_padding flag. - width = bounds_max.x - bounds_min.x - height = bounds_max.y - bounds_min.y - if (width + padding) > panel_width - @no_padding = 'w' - end - if (height + padding) > panel_height - if @no_padding - @singleton = true - @no_padding = nil - else - @no_padding = 'h' - end - end - - # Transform all points on every loop. - loops.map! do |loop| - loop.map! do |point| - transform * point - end - end - - # Find the centroid. - @shell_area = surface_area = areas.shift - topx = surface_area * cxs.shift - topy = surface_area * cys.shift - for i in 0...areas.length - area = areas[i] - topx -= area * cxs[i] - topy -= area * cys[i] - surface_area -= area - end - cx = topx / surface_area - cy = topy / surface_area - centroid = transform * [cx, cy, 0] - - # Sanity check the surface area calculation. - if (total_area - surface_area).abs > 0.1 - @error = "Surface area calculation differs" - return - end - - # TODO(tav): We could also detect arcs once we figure out how to create - # polylined shapes with arcs in the DXF output. This may not be ideal as - # polyarcs may also cause issues with certain CNC routers. - - # Detect all circular loops. - for i in 0...loops.length - points = intersections[i] - length = points.length - last = length - 1 - circle = true - for j in 0...length - c1 = points[j] - c2 = points[j+1] - if j == last - c2 = points[0] - end - if not (c1 and c2) - circle = false - break - end - if ((c2.x - c1.x).abs > 0.1) or ((c2.y - c1.y).abs > 0.1) - circle = false - break + + # If we couldn't find a fitting angle, try again at 0.1 degree intervals. + if not transform + (0...180.0).step(0.1) do |angle| + t = Geom::Transformation.rotation ORIGIN, Z_AXIS, angle.degrees + bounds = Geom::BoundingBox.new + outer.each do |point| + point = t * point + bounds.add point + end + min, max = bounds.min, bounds.max + height = max.y - min.y + width = max.x - min.x + if (width - panel_max_width) > 0.1 + next + end + if (height - panel_max_height) > 0.1 + next + end + area = width * height + if (not bounds_area) or ((bounds_area - area) > 0.1) + bounds_area = area + bounds_min, bounds_max = min, max + transform = t + end end end - if circle and length >= 24 - center = transform * points[0] - p1 = loops[i][0] - x = center.x - p1.x - y = center.y - p1.y - radius = Math.sqrt((x * x) + (y * y)) - circles[i] = [center, radius] + + # If we still couldn't find a fitting, abort. + if not transform + @error = "Couldn't fit panel within cutting sheet" + puts @error + return end - end - - # Save the generated data. - @area = total_area - @bounds_area = bounds_area - @centroid = centroid - @circles = circles - @loops = loops - @max = bounds_max - @min = bounds_min - - # Delete any temporarily generated groups. - if to_delete.length > 0 - root.erase_entities to_delete - end - - end - -end - -# ------------------------------------------------------------------------------ -# Entities Loader -# ------------------------------------------------------------------------------ - -class WikiHouseEntities - - attr_accessor :orphans, :panels - - def initialize(entities, root, dimensions) - - $count_s1 = 0 - $count_s2 = 0 - $count_s3 = 0 - $count_s4 = 0 - - # Initialise the default attribute values. - @faces = Hash.new - @groups = groups = Hash.new - @orphans = orphans = Hash.new - @root = root - @to_delete = [] - @todo = todo = [] - - # Set a loop counter variable and the default identity transformation. - loop = 0 - transform = Geom::Transformation.new - - # Aggregate all the entities into the ``todo`` array. - entities.each { |entity| todo << [entity, transform] } - - # Visit all component and group entities defined within the model and count - # up all orphaned face entities. - while todo.length != 0 - Sketchup.set_status_text WIKIHOUSE_DETECTION_STATUS[(loop/10) % 5] - loop += 1 - entity, transform = todo.pop - case entity.typename - when "Group", "ComponentInstance" - visit entity, transform - when "Face" - if orphans[WIKIHOUSE_DUMMY_GROUP] - orphans[WIKIHOUSE_DUMMY_GROUP] += 1 + + # Set the panel to a singleton panel (i.e. without any padding) if it is + # larger than the height and width, otherwise set the no_padding flag. + width = bounds_max.x - bounds_min.x + height = bounds_max.y - bounds_min.y + if (width + padding) > panel_width + @no_padding = 'w' + end + if (height + padding) > panel_height + if @no_padding + @singleton = true + @no_padding = nil else - orphans[WIKIHOUSE_DUMMY_GROUP] = 1 + @no_padding = 'h' end end - end - - # If there were no orphans, unset the ``@orphans`` attribute. - if not orphans.length > 0 - @orphans = nil - end - - # Reset the loop counter. - loop = 0 - - # Construct the panel limit dimensions. - height, width, padding = [dimensions[2], dimensions[3], dimensions[5]] - padding = 2 * padding - limits = [height - padding, width - padding, height, width, padding] - - # Loop through each group and aggregate parsed data for the faces. - @panels = items = [] - @faces.each_pair do |group, faces| - meta = groups[group] - sample = faces[0] - if meta.length == 1 - f_data = { meta[0][0] => [meta[0][1]] } - else - f_data = Hash.new - meta = meta.map { |t, l| [t, l, sample.area(t)] }.sort_by { |t| t[2] } - while meta.length != 0 - t1, l1, a1 = meta.pop - idx = -1 - f_data[t1] = [l1] - while 1 - f2_data = meta[idx] - if not f2_data - break - end - t2, l2, a2 = f2_data - if (a2 - a1).abs > 0.1 - break - end - f_data[t1] << l2 - meta.delete_at idx + + # Transform all points on every loop. + loops.map! do |loop| + loop.map! do |point| + transform * point + end + end + + # Find the centroid. + @shell_area = surface_area = areas.shift + topx = surface_area * cxs.shift + topy = surface_area * cys.shift + for i in 0...areas.length + area = areas[i] + topx -= area * cxs[i] + topy -= area * cys[i] + surface_area -= area + end + cx = topx / surface_area + cy = topy / surface_area + centroid = transform * [cx, cy, 0] + + # Sanity check the surface area calculation. + if (total_area - surface_area).abs > 0.1 + @error = "Surface area calculation differs" + return + end + + # TODO(tav): We could also detect arcs once we figure out how to create + # polylined shapes with arcs in the DXF output. This may not be ideal as + # polyarcs may also cause issues with certain CNC routers. + + # Detect all circular loops. + for i in 0...loops.length + points = intersections[i] + length = points.length + last = length - 1 + circle = true + for j in 0...length + c1 = points[j] + c2 = points[j+1] + if j == last + c2 = points[0] + end + if not (c1 and c2) + circle = false + break + end + if ((c2.x - c1.x).abs > 0.1) or ((c2.y - c1.y).abs > 0.1) + circle = false + break end end - end - f_data.each_pair do |transform, labels| - panels = faces.map do |face| - Sketchup.set_status_text WIKIHOUSE_PANEL_STATUS[(loop/3) % 5] - loop += 1 - WikiHousePanel.new root, face, transform, labels, limits + if circle and length >= 24 + center = transform * points[0] + p1 = loops[i][0] + x = center.x - p1.x + y = center.y - p1.y + radius = Math.sqrt((x * x) + (y * y)) + circles[i] = [center, radius] end - items.concat panels end + + # Save the generated data. + @area = total_area + @bounds_area = bounds_area + @centroid = centroid + @circles = circles + @loops = loops + @max = bounds_max + @min = bounds_min + + # Delete any temporarily generated groups. + if to_delete.length > 0 + root.erase_entities to_delete + end + end - - total = 0 - items.each { |item| total += item.labels.length } - - if @orphans - puts "Orphans: #{@orphans.length} Groups" - end - - puts "Items: #{total}" - puts "S1: #{$count_s1}" - puts "S2: #{$count_s2}" - puts "S3: #{$count_s3}" - puts "S4: #{$count_s4}" - + end - - def visit(group, transform) - - # Setup some local variables. - exists = false - faces = [] - groups = @groups - - # Setup the min/max heights for the depth edge/faces. - min_height = 17.mm - max_height = 19.mm - - # Apply the transformation if one has been set for this group. - if group.transformation - transform = transform * group.transformation - end - - # Get the label. - label = group.name - if label == "" - label = nil - end - - # Get the entities set. - case group.typename - when "Group" - entities = group.entities - else - group = group.definition - entities = group.entities - # Check if we've seen this component before, and if so, reuse previous - # data. - if groups[group] - groups[group] << [transform, label] - entities.each do |entity| - case entity.typename - when "Group", "ComponentInstance" - @todo << [entity, transform] + + # ------------------------------------------------------------------------------ + # Entities Loader + # ------------------------------------------------------------------------------ + + class WikiHouseEntities + + attr_accessor :orphans, :panels + + def initialize(entities, root, dimensions) + + $count_s1 = 0 + $count_s2 = 0 + $count_s3 = 0 + $count_s4 = 0 + + # Initialise the default attribute values. + @faces = Hash.new + @groups = groups = Hash.new + @orphans = orphans = Hash.new + @root = root + @to_delete = [] + @todo = todo = [] + + # Set a loop counter variable and the default identity transformation. + loop = 0 + transform = Geom::Transformation.new + + # Aggregate all the entities into the ``todo`` array. + entities.each { |entity| todo << [entity, transform] } + + # Visit all component and group entities defined within the model and count + # up all orphaned face entities. + while todo.length != 0 + Sketchup.set_status_text WIKIHOUSE_DETECTION_STATUS[(loop/10) % 5] # Loop through status msg + loop += 1 + entity, transform = todo.pop + case entity.typename + when "Group", "ComponentInstance" + visit entity, transform + when "Face" + if orphans[WIKIHOUSE_DUMMY_GROUP] + orphans[WIKIHOUSE_DUMMY_GROUP] += 1 + else + orphans[WIKIHOUSE_DUMMY_GROUP] = 1 end end - return end - end - - # Add the new group/component definition. - groups[group] = [[transform, label]] - - # Loop through the entities. - entities.each do |entity| - case entity.typename - when "Face" - edges = entity.edges - ignore = 0 - # Ignore all faces which match the specification for the depth side. - if edges.length == 4 - for i in 0...4 - edge = edges[i] - length = edge.length - if length < max_height and length > min_height - ignore += 1 - if ignore == 2 + + # If there were no orphans, unset the ``@orphans`` attribute. + if not orphans.length > 0 + @orphans = nil + end + + # Reset the loop counter. + loop = 0 + + # Construct the panel limit dimensions. + height, width, padding = [dimensions[2], dimensions[3], dimensions[5]] + padding = 2 * padding + limits = [height - padding, width - padding, height, width, padding] + + # Loop through each group and aggregate parsed data for the faces. + @panels = items = [] + @faces.each_pair do |group, faces| + meta = groups[group] + sample = faces[0] + if meta.length == 1 + f_data = { meta[0][0] => [meta[0][1]] } + else + f_data = Hash.new + meta = meta.map { |t, l| [t, l, sample.area(t)] }.sort_by { |t| t[2] } + while meta.length != 0 + t1, l1, a1 = meta.pop + idx = -1 + f_data[t1] = [l1] + while 1 + f2_data = meta[idx] + if not f2_data + break + end + t2, l2, a2 = f2_data + if (a2 - a1).abs > 0.1 break end + f_data[t1] << l2 + meta.delete_at idx end end end - if WIKIHOUSE_HIDE and ignore == 2 - entity.hidden = false - end - if ignore != 2 # TODO(tav): and entity.visible? - faces << entity + f_data.each_pair do |transform, labels| + panels = faces.map do |face| + Sketchup.set_status_text WIKIHOUSE_PANEL_STATUS[(loop/3) % 5] + loop += 1 + WikiHousePanel.new root, face, transform, labels, limits + end + items.concat panels end - when "Group", "ComponentInstance" - # Append the entity to the todo attribute instead of recursively calling - # ``visit`` so as to avoid blowing the stack. - @todo << [entity, transform] end - end - - faces, orphans = visit_faces faces, transform - - if orphans and orphans.length > 0 - @orphans[group] = orphans.length - end - - if faces and faces.length > 0 - @faces[group] = faces - end - - end - - def visit_faces(faces, transform) - - # Handle the case where no faces have been found or just a single orphaned - # face exists. - if faces.length <= 1 - if faces.length == 0 - return [], nil - else - return [], faces + + total = 0 + items.each { |item| total += item.labels.length } + + if @orphans + puts "Orphans: #{@orphans.length} Groups" end + + puts "Items: #{total}" + puts "S1: #{$count_s1}" + puts "S2: #{$count_s2}" + puts "S3: #{$count_s3}" + puts "S4: #{$count_s4}" + end - - # Define some local variables. - found = [] - orphans = [] - - # Sort the faces by their respective surface areas in order to minimise - # lookups. - faces = faces.sort_by { |face| face.area transform } - - # Iterate through the faces and see if we can find matching pairs. - while faces.length != 0 - face1 = faces.pop - area1 = face1.area transform - # Ignore small faces. - if area1 < 5 - next + + def visit(group, transform) + + # Setup some local variables. + exists = false + faces = [] + groups = @groups + + # Setup the min/max heights for the depth edge/faces. + min_height = 17.mm + max_height = 19.mm + + # Apply the transformation if one has been set for this group. + if group.transformation + transform = transform * group.transformation end - idx = -1 - match = false - # Check against all remaining faces. - while 1 - face2 = faces[idx] - if not face2 - break - end - if face1 == face2 - faces.delete_at idx - next - end - # Check that the area of both faces are close enough -- accounting for - # any discrepancies caused by floating point rounding errors. - area2 = face2.area transform - diff = (area2 - area1).abs - if diff < 0.5 # TODO(tav): Ideally, this tolerance will be 0.1 or less. - $count_s1 += 1 - # Ensure that the faces don't intersect, i.e. are parallel to each - # other. - intersect = Geom.intersect_plane_plane face1.plane, face2.plane - if intersect - # Calculate the angle between the two planes and accomodate for - # rounding errors. - angle = face1.normal.angle_between face2.normal - if angle < 0.01 - intersect = nil - elsif (Math::PI - angle).abs < 0.01 - intersect = nil + + # Get the label. + label = group.name + if label == "" + label = nil + end + + # Get the entities set. + case group.typename + when "Group" + entities = group.entities + else # is component + group = group.definition + entities = group.entities + # Check if we've seen this component before, and if so, reuse previous + # data. + if groups[group] + groups[group] << [transform, label] + entities.each do |entity| + case entity.typename + when "Group", "ComponentInstance" + @todo << [entity, transform] end end - if not intersect - $count_s2 += 1 - vertices1 = face1.vertices - vertices2 = face2.vertices - vertices_length = vertices1.length - # Check if both faces have matching number of outer vertices and - # that they each share a common edge. - vertices1 = face1.outer_loop.vertices - vertices2 = face2.outer_loop.vertices - for i in 0...vertices1.length - vertex1 = vertices1[i] - connected = false - for j in 0...vertices2.length - vertex2 = vertices2[j] - if vertex1.common_edge vertex2 - connected = true - vertices2.delete_at j + return + end + end + + # Add the new group/component definition. + groups[group] = [[transform, label]] + + # Loop through the entities. + entities.each do |entity| + case entity.typename + when "Face" + edges = entity.edges + ignore = 0 + # Ignore all faces which match the specification for the depth side. + if edges.length == 4 + for i in 0...4 + edge = edges[i] + length = edge.length + if length < max_height and length > min_height + ignore += 1 + if ignore == 2 break end end - if not connected - break + end + end + if WIKIHOUSE_HIDE and ignore == 2 + entity.hidden = false + end + if ignore != 2 # TODO(tav): and entity.visible? + faces << entity + end + when "Group", "ComponentInstance" + # Append the entity to the todo attribute instead of recursively calling + # ``visit`` so as to avoid blowing the stack. + @todo << [entity, transform] + end + end + + faces, orphans = visit_faces faces, transform + + if orphans and orphans.length > 0 + @orphans[group] = orphans.length + end + + if faces and faces.length > 0 + @faces[group] = faces + end + + end + + def visit_faces(faces, transform) + + # Handle the case where no faces have been found or just a single orphaned + # face exists. + if faces.length <= 1 + if faces.length == 0 + return [], nil + else + return [], faces + end + end + + # Define some local variables. + found = [] + orphans = [] + + # Sort the faces by their respective surface areas in order to minimise + # lookups. + faces = faces.sort_by { |face| face.area transform } + + # Iterate through the faces and see if we can find matching pairs. + while faces.length != 0 + face1 = faces.pop + area1 = face1.area transform + # Ignore small faces. + if area1 < 5 # (Chris) This may be why the small C shaped parts in Joins are being ignored. + next + end + idx = -1 + match = false + # Check against all remaining faces. + while 1 + face2 = faces[idx] + if not face2 + break + end + if face1 == face2 + faces.delete_at idx + next + end + # Check that the area of both faces are close enough -- accounting for + # any discrepancies caused by floating point rounding errors. + area2 = face2.area transform + diff = (area2 - area1).abs + if diff < 0.5 # TODO(tav): Ideally, this tolerance will be 0.1 or less. + $count_s1 += 1 + # Ensure that the faces don't intersect, i.e. are parallel to each + # other. + intersect = Geom.intersect_plane_plane face1.plane, face2.plane + if intersect + # Calculate the angle between the two planes and accomodate for + # rounding errors. + angle = face1.normal.angle_between face2.normal + if angle < 0.01 + intersect = nil + elsif (Math::PI - angle).abs < 0.01 + intersect = nil end end - if connected - $count_s3 += 1 - # Go through the various loops of edges and find ones that have - # shared edges to the other face. - loops1 = [] - loops2 = [] - loops2_lengths = [] - face2.loops.each do |loop| - if not loop.outer? - loops2 << loop - loops2_lengths << loop.vertices.length + if not intersect + $count_s2 += 1 + vertices1 = face1.vertices + vertices2 = face2.vertices + vertices_length = vertices1.length + # Check if both faces have matching number of outer vertices and + # that they each share a common edge. + vertices1 = face1.outer_loop.vertices + vertices2 = face2.outer_loop.vertices + for i in 0...vertices1.length + vertex1 = vertices1[i] + connected = false + for j in 0...vertices2.length + vertex2 = vertices2[j] + if vertex1.common_edge vertex2 + connected = true + vertices2.delete_at j + break + end + end + if not connected + break end end - face1_loops = face1.loops - face1_loops.each do |loop1| - if not loop1.outer? - loop1_vertices = loop1.vertices - loop1_length = loop1_vertices.length - for l in 0...loops2.length - if loops2_lengths[l] == loop1_length - loop2_vertices = loops2[l].vertices - for i in 0...loop1_length - v1 = loop1_vertices[i] - connected = false - for j in 0...loop2_vertices.length - v2 = loop2_vertices[j] - if v1.common_edge v2 - connected = true - loop2_vertices.delete_at j + if connected + $count_s3 += 1 + # Go through the various loops of edges and find ones that have + # shared edges to the other face. + loops1 = [] + loops2 = [] + loops2_lengths = [] + face2.loops.each do |loop| + if not loop.outer? + loops2 << loop + loops2_lengths << loop.vertices.length + end + end + face1_loops = face1.loops + face1_loops.each do |loop1| + if not loop1.outer? + loop1_vertices = loop1.vertices + loop1_length = loop1_vertices.length + for l in 0...loops2.length + if loops2_lengths[l] == loop1_length + loop2_vertices = loops2[l].vertices + for i in 0...loop1_length + v1 = loop1_vertices[i] + connected = false + for j in 0...loop2_vertices.length + v2 = loop2_vertices[j] + if v1.common_edge v2 + connected = true + loop2_vertices.delete_at j + break + end + end + if not connected break end end - if not connected + if connected + loops1 << loops2[l].vertices + loops2.delete_at l + loops2_lengths.delete_at l break end end - if connected - loops1 << loops2[l].vertices - loops2.delete_at l - loops2_lengths.delete_at l - break - end end end end - end - # If the number of loops with shared edges don't match up with the - # original state, create a new face. - if loops1.length != (face1.loops.length - 1) - group = @root.add_group - group_ents = group.entities - face = group_ents.add_face vertices1 - loops1.each do |v| - hole = group_ents.add_face v - hole.erase! if hole.valid? + # If the number of loops with shared edges don't match up with the + # original state, create a new face. + if loops1.length != (face1.loops.length - 1) + group = @root.add_group + group_ents = group.entities + face = group_ents.add_face vertices1 + loops1.each do |v| + hole = group_ents.add_face v + hole.erase! if hole.valid? + end + @to_delete << group + else + face = face1 end - @to_delete << group - else - face = face1 - end - # We have matching and connected faces! - match = true - found << face - faces.delete_at idx - if WIKIHOUSE_HIDE - face1.hidden = true - face2.hidden = true + # We have matching and connected faces! + match = true + found << face + faces.delete_at idx + if WIKIHOUSE_HIDE + face1.hidden = true + face2.hidden = true + end + break end - break end end + idx -= 1 + end + if match + next end - idx -= 1 + orphans << face1 end - if match - next + + # Return all the found and orphaned faces. + return found, orphans + + end + + def purge + + # Delete any custom generated entity groups. + if @to_delete and @to_delete.length != 0 + @root.erase_entities @to_delete end - orphans << face1 + + # Nullify all container attributes. + @faces = nil + @groups = nil + @orphans = nil + @root = nil + @to_delete = nil + @todo = nil + end - - # Return all the found and orphaned faces. - return found, orphans - + end - - def purge - - # Delete any custom generated entity groups. - if @to_delete and @to_delete.length != 0 - @root.erase_entities @to_delete + + # ------------------------------------------------------------------------------ + # Make This House + # These methods are instance methods of the module 'WikihouseExtension' + # ------------------------------------------------------------------------------ + + def make_wikihouse(model, interactive) + + # Isolate the entities to export. + entities = root = model.active_entities + selection = model.selection + if selection.empty? + if interactive + reply = UI.messagebox "No objects selected. Export the entire model?", MB_OKCANCEL + if reply != REPLY_OK + return + end + end + else + entities = selection end - - # Nullify all container attributes. - @faces = nil - @groups = nil - @orphans = nil - @root = nil - @to_delete = nil - @todo = nil - - end - -end - - -# ------------------------------------------------------------------------------ -# Make This House -# ------------------------------------------------------------------------------ - -def make_wikihouse(model, interactive) - - # Isolate the entities to export. - entities = root = model.active_entities - selection = model.selection - if selection.empty? - if interactive - reply = UI.messagebox "No objects selected. Export the entire model?", MB_OKCANCEL - if reply != REPLY_OK - return + + dimensions = WIKIHOUSE_DIMENSIONS + + # Load and parse the entities. + if WIKIHOUSE_SHORT_CIRCUIT and $wikloader + loader = $wikloader + else + loader = WikiHouseEntities.new entities, root, dimensions + if WIKIHOUSE_SHORT_CIRCUIT + $wikloader = loader end end - else - entities = selection - end - - dimensions = WIKIHOUSE_DIMENSIONS - - # Load and parse the entities. - if WIKIHOUSE_SHORT_CIRCUIT and $wikloader - loader = $wikloader - else - loader = WikiHouseEntities.new entities, root, dimensions - if WIKIHOUSE_SHORT_CIRCUIT - $wikloader = loader + + if interactive and loader.orphans + msg = "The cutting sheets may be incomplete. The following number of faces could not be matched appropriately:\n\n" + loader.orphans.each_pair do |group, count| + msg += " #{count} in #{group.name.length > 0 and group.name or 'Group#???'}\n" + end + UI.messagebox msg end + + # Filter out any panels which raised an error. + panels = loader.panels.select { |panel| !panel.error } + + # Run the detected panels through the layout engine. + layout = WikiHouseLayoutEngine.new panels, root, dimensions + + # Generate the SVG file. + svg = WikiHouseSVG.new layout, 8 + svg_data = svg.generate + + # Generate the DXF file. + dxf = WikiHouseDXF.new layout + dxf_data = dxf.generate + + # Cleanup. + Sketchup.set_status_text "" + loader.purge + + # Return the generated data. + [svg_data, dxf_data] + end - - if interactive and loader.orphans - msg = "The cutting sheets may be incomplete. The following number of faces could not be matched appropriately:\n\n" - loader.orphans.each_pair do |group, count| - msg += " #{count} in #{group.name.length > 0 and group.name or 'Group#???'}\n" + + # ------------------------------------------------------------------------------ + # WebDialog Callbacks + # ------------------------------------------------------------------------------ + + def wikihouse_download_callback(dialog, params) + + # Exit if the download parameters weren't set. + if params == "" + show_wikihouse_error "Couldn't find the #{WIKIHOUSE_TITLE} model name and url" + return end - UI.messagebox msg - end - - # Filter out any panels which raised an error. - panels = loader.panels.select { |panel| !panel.error } - - # Run the detected panels through the layout engine. - layout = WikiHouseLayoutEngine.new panels, root, dimensions - - # Generate the SVG file. - svg = WikiHouseSVG.new layout, 8 - svg_data = svg.generate - - # Generate the DXF file. - dxf = WikiHouseDXF.new layout - dxf_data = dxf.generate - - # Cleanup. - Sketchup.set_status_text "" - loader.purge - - # Return the generated data. - [svg_data, dxf_data] - -end - -# ------------------------------------------------------------------------------ -# WebDialog Callbacks -# ------------------------------------------------------------------------------ - -def wikihouse_download_callback(dialog, params) - # Exit if the download parameters weren't set. - if params == "" - show_wikihouse_error "Couldn't find the #{WIKIHOUSE_TITLE} model name and url" - return - end - - is_comp, base64_url, blob_url, name = params.split ",", 4 - model = Sketchup.active_model - - # Try and save the model/component directly into the current model. - if model and is_comp == '1' - reply = UI.messagebox "Load this directly into your Google SketchUp model?", MB_YESNOCANCEL - if reply == REPLY_YES - loader = WikiHouseLoader.new name - blob_url = WIKIHOUSE_SERVER + blob_url - model.definitions.load_from_url blob_url, loader - if not loader.error - dialog.close - UI.messagebox "Successfully downloaded #{name}" - component = model.definitions[-1] - if component - model.place_component component - end - return - else - UI.messagebox loader.error - reply = UI.messagebox "Would you like to save the model file instead?", MB_YESNO - if reply == REPLY_NO + is_comp, base64_url, blob_url, name = params.split ",", 4 + model = Sketchup.active_model + + # Try and save the model/component directly into the current model. + if model and is_comp == '1' + reply = UI.messagebox "Load this directly into your Google SketchUp model?", MB_YESNOCANCEL + if reply == REPLY_YES + loader = WikiHouseLoader.new name + blob_url = WIKIHOUSE_SERVER + blob_url + model.definitions.load_from_url blob_url, loader + if not loader.error + dialog.close + UI.messagebox "Successfully downloaded #{name}" + component = model.definitions[-1] + if component + model.place_component component + end return + else + UI.messagebox loader.error + reply = UI.messagebox "Would you like to save the model file instead?", MB_YESNO + if reply == REPLY_NO + return + end end + elsif reply == REPLY_NO + # Skip through to saving the file directly. + else + return end - elsif reply == REPLY_NO - # Skip through to saving the file directly. - else + end + + # Otherwise, get the filename to save into. + filename = UI.savepanel "Save Model", WIKIHOUSE_SAVE, "#{name}.skp" + if not filename + show_wikihouse_error "No filename specified to save the #{WIKIHOUSE_TITLE} model. Please try again." return end + + # TODO(tav): Ensure that this is atomic and free of thread-related + # concurrency issues. + $WIKIHOUSE_DOWNLOADS_ID += 1 + download_id = $WIKIHOUSE_DOWNLOADS_ID.to_s + + WIKIHOUSE_DOWNLOADS[download_id] = filename + + # Initiate the download. + dialog.execute_script "wikihouse.download('#{download_id}', '#{base64_url}');" + end - - # Otherwise, get the filename to save into. - filename = UI.savepanel "Save Model", WIKIHOUSE_SAVE, "#{name}.skp" - if not filename - show_wikihouse_error "No filename specified to save the #{WIKIHOUSE_TITLE} model. Please try again." - return - end - - # TODO(tav): Ensure that this is atomic and free of thread-related - # concurrency issues. - $WIKIHOUSE_DOWNLOADS_ID += 1 - download_id = $WIKIHOUSE_DOWNLOADS_ID.to_s - - WIKIHOUSE_DOWNLOADS[download_id] = filename - - # Initiate the download. - dialog.execute_script "wikihouse.download('#{download_id}', '#{base64_url}');" - -end - -def wikihouse_save_callback(dialog, download_id) - - errmsg = "Couldn't find the #{WIKIHOUSE_TITLE} model data to save" - - # Exit if the save parameters weren't set. - if download_id == "" - show_wikihouse_error errmsg - return - end - - if not WIKIHOUSE_DOWNLOADS.key? download_id - show_wikihouse_error errmsg - return - end - - filename = WIKIHOUSE_DOWNLOADS[download_id] - WIKIHOUSE_DOWNLOADS.delete download_id - - segment_count = dialog.get_element_value "design-download-data" - dialog.close - - if segment_count == "" - show_wikihouse_error errmsg - return - end - - data = [] - for i in 0...segment_count.to_i - segment = dialog.get_element_value "design-download-data-#{i}" - if segment == "" + + def wikihouse_save_callback(dialog, download_id) + + errmsg = "Couldn't find the #{WIKIHOUSE_TITLE} model data to save" + + # Exit if the save parameters weren't set. + if download_id == "" + show_wikihouse_error errmsg + return + end + + if not WIKIHOUSE_DOWNLOADS.key? download_id + show_wikihouse_error errmsg + return + end + + filename = WIKIHOUSE_DOWNLOADS[download_id] + WIKIHOUSE_DOWNLOADS.delete download_id + + segment_count = dialog.get_element_value "design-download-data" + dialog.close + + if segment_count == "" + show_wikihouse_error errmsg + return + end + + data = [] + for i in 0...segment_count.to_i + segment = dialog.get_element_value "design-download-data-#{i}" + if segment == "" + show_wikihouse_error errmsg + return + end + data << segment + end + + # Decode the base64-encoded data. + data = data.join('').unpack("m")[0] + if data == "" show_wikihouse_error errmsg return end - data << segment - end - - # Decode the base64-encoded data. - data = data.join('').unpack("m")[0] - if data == "" - show_wikihouse_error errmsg - return - end - - # Save the data to the local file. - File.open(filename, 'wb') do |io| - io.write data - end - - reply = UI.messagebox "Successfully saved #{WIKIHOUSE_TITLE} model. Would you like to open it?", MB_YESNO - if reply == REPLY_YES - if not Sketchup.open_file filename - show_wikihouse_error "Couldn't open #{filename}" + + # Save the data to the local file. + File.open(filename, 'wb') do |io| + io.write data + end + + reply = UI.messagebox "Successfully saved #{WIKIHOUSE_TITLE} model. Would you like to open it?", MB_YESNO + if reply == REPLY_YES + if not Sketchup.open_file filename + show_wikihouse_error "Couldn't open #{filename}" + end end + end - -end - -def wikihouse_error_callback(dialog, download_id) - - if not WIKIHOUSE_DOWNLOADS.key? download_id - return + + def wikihouse_error_callback(dialog, download_id) + + if not WIKIHOUSE_DOWNLOADS.key? download_id + return + end + + filename = WIKIHOUSE_DOWNLOADS[download_id] + WIKIHOUSE_DOWNLOADS.delete download_id + + show_wikihouse_error "Couldn't download #{filename} from #{WIKIHOUSE_TITLE}. Please try again." + end - - filename = WIKIHOUSE_DOWNLOADS[download_id] - WIKIHOUSE_DOWNLOADS.delete download_id - - show_wikihouse_error "Couldn't download #{filename} from #{WIKIHOUSE_TITLE}. Please try again." - -end - -def wikihouse_process_upload(dialog, model, model_path) - - if File.size(model_path) > 12582912 - reply = UI.messagebox "The model file is larger than 12MB. Would you like to purge unused objects, materials and styles?", MB_OKCANCEL - if reply == REPLY_OK - model.layers.purge_unused - model.styles.purge_unused - model.materials.purge_unused - model.definitions.purge_unused - if not model.save model_path - show_wikihouse_error "Couldn't save the purged model to #{model_path}" - dialog.close - return - end - if File.size(model_path) > 12582912 - UI.messagebox "The model file is still larger than 12MB after purging. Please break up the file into smaller components." + + def wikihouse_process_upload(dialog, model, model_path) + + if File.size(model_path) > 12582912 + reply = UI.messagebox "The model file is larger than 12MB. Would you like to purge unused objects, materials and styles?", MB_OKCANCEL + if reply == REPLY_OK + model.layers.purge_unused + model.styles.purge_unused + model.materials.purge_unused + model.definitions.purge_unused + if not model.save model_path + show_wikihouse_error "Couldn't save the purged model to #{model_path}" + dialog.close + return + end + if File.size(model_path) > 12582912 + UI.messagebox "The model file is still larger than 12MB after purging. Please break up the file into smaller components." + dialog.close + return + end + else dialog.close - return end - else + end + + # Get the model file data. + model_data = File.open(model_path, 'rb') do |io| + io.read + end + + model_data = [model_data].pack('m') + set_dom_value dialog, "design-model", model_data + + # Capture the current view info. + view = model.active_view + camera = view.camera + eye, target, up = camera.eye, camera.target, camera.up + center = model.bounds.center + + # Get the data for the model's front image. + front_thumbnail = get_wikihouse_thumbnail model, view, "front" + if not front_thumbnail + show_wikihouse_error "Couldn't generate thumbnails for the model: #{model_name}" dialog.close + return end - end - - # Get the model file data. - model_data = File.open(model_path, 'rb') do |io| - io.read - end - - model_data = [model_data].pack('m') - set_dom_value dialog, "design-model", model_data - - # Capture the current view info. - view = model.active_view - camera = view.camera - eye, target, up = camera.eye, camera.target, camera.up - center = model.bounds.center - - # Get the data for the model's front image. - front_thumbnail = get_wikihouse_thumbnail model, view, "front" - if not front_thumbnail - show_wikihouse_error "Couldn't generate thumbnails for the model: #{model_name}" - dialog.close - return - end - - front_thumbnail = [front_thumbnail].pack('m') - set_dom_value dialog, "design-model-preview", front_thumbnail - - # Rotate the camera and zoom all the way out. - rotate = Geom::Transformation.rotation center, Z_AXIS, 180.degrees - camera.set eye.transform(rotate), center, Z_AXIS - view.zoom_extents - - # Get the data for the model's back image. - back_thumbnail = get_wikihouse_thumbnail model, view, "back" - if not back_thumbnail + + front_thumbnail = [front_thumbnail].pack('m') + set_dom_value dialog, "design-model-preview", front_thumbnail + + # Rotate the camera and zoom all the way out. + rotate = Geom::Transformation.rotation center, Z_AXIS, 180.degrees + camera.set eye.transform(rotate), center, Z_AXIS + view.zoom_extents + + # Get the data for the model's back image. + back_thumbnail = get_wikihouse_thumbnail model, view, "back" + if not back_thumbnail + camera.set eye, target, up + show_wikihouse_error "Couldn't generate thumbnails for the model: #{model_name}" + dialog.close + return + end + + back_thumbnail = [back_thumbnail].pack('m') + set_dom_value dialog, "design-model-preview-reverse", back_thumbnail + + # Set the camera view back to the original setup. camera.set eye, target, up - show_wikihouse_error "Couldn't generate thumbnails for the model: #{model_name}" - dialog.close - return - end - - back_thumbnail = [back_thumbnail].pack('m') - set_dom_value dialog, "design-model-preview-reverse", back_thumbnail - - # Set the camera view back to the original setup. - camera.set eye, target, up - - # Get the generated sheets data. - sheets_data = make_wikihouse model, false - if not sheets_data - svg_data, dxf_data = "", "" - else - svg_data = [sheets_data[0]].pack('m') - dxf_data = [sheets_data[1]].pack('m') - end - - set_dom_value dialog, "design-sheets", dxf_data - set_dom_value dialog, "design-sheets-preview", svg_data - - WIKIHOUSE_UPLOADS[dialog] = 1 - dialog.execute_script "wikihouse.upload();" - -end - -# ------------------------------------------------------------------------------ -# Download Dialog -# ------------------------------------------------------------------------------ - -def load_wikihouse_download - - # Exit if the computer is not online. - if not Sketchup.is_online - UI.messagebox "You need to be connected to the internet to download #{WIKIHOUSE_TITLE} models." - return - end - - dialog = UI::WebDialog.new WIKIHOUSE_TITLE, true, "#{WIKIHOUSE_TITLE}-Download", 480, 640, 150, 150, true - - dialog.add_action_callback "download" do |dialog, params| - wikihouse_download_callback dialog, params - end - - dialog.add_action_callback "save" do |dialog, download_id| - wikihouse_save_callback dialog, download_id - end - - dialog.add_action_callback "error" do |dialog, download_id| - wikihouse_error_callback dialog, download_id - end - - # Set the dialog's url and display it. - dialog.set_url WIKIHOUSE_DOWNLOAD_URL - dialog.show - dialog.show_modal - -end - -# ------------------------------------------------------------------------------ -# Make Dialog -# ------------------------------------------------------------------------------ - -def load_wikihouse_make - - model = Sketchup.active_model - - # Exit if a model wasn't available. - if not model - show_wikihouse_error "You need to open a SketchUp model before it can be fabricated" - return - end - # Initialise an attribute dictionary for custom metadata. - attr = model.attribute_dictionary WIKIHOUSE_TITLE, true - if attr.size == 0 - attr["spec"] = WIKIHOUSE_SPEC - end - - # Exit if it's an unsaved model. - model_path = model.path - if model_path == "" - UI.messagebox "You need to save the model before the cutting sheets can be generated" - return - end - - # Try and infer the model's filename. - filename = model.title - if filename == "" - filename = "Untitled" - end - - # Get the model's parent directory and generate the new filenames to save to. - directory = File.dirname(model_path) - svg_filename = File.join(directory, filename + ".svg") - dxf_filename = File.join(directory, filename + ".dxf") - - # Make the cutting sheets for the house! - data = make_wikihouse model, true - if not data - return - end - - svg_data, dxf_data = data - - # Save the SVG data to the file. - File.open(svg_filename, "wb") do |io| - io.write svg_data - end - - # Save the DXF data to the file. - File.open(dxf_filename, "wb") do |io| - io.write dxf_data + # Get the generated sheets data. + sheets_data = make_wikihouse model, false + if not sheets_data + svg_data, dxf_data = "", "" + else + svg_data = [sheets_data[0]].pack('m') + dxf_data = [sheets_data[1]].pack('m') + end + + set_dom_value dialog, "design-sheets", dxf_data + set_dom_value dialog, "design-sheets-preview", svg_data + + WIKIHOUSE_UPLOADS[dialog] = 1 + dialog.execute_script "wikihouse.upload();" + end - - UI.messagebox "Cutting sheets successfully saved to #{directory}", MB_OK - - if WIKIHOUSE_MAC - dialog = UI::WebDialog.new "Cutting Sheets Preview", true, "#{WIKIHOUSE_TITLE}-Preview", 800, 800, 150, 150, true - dialog.set_file svg_filename + + # ------------------------------------------------------------------------------ + # Download Dialog + # ------------------------------------------------------------------------------ + + def load_wikihouse_download + + # Exit if the computer is not online. + if not Sketchup.is_online + UI.messagebox "You need to be connected to the internet to download #{WIKIHOUSE_TITLE} models." + return + end + + dialog = UI::WebDialog.new WIKIHOUSE_TITLE, true, "#{WIKIHOUSE_TITLE}-Download", 480, 640, 150, 150, true + + dialog.add_action_callback "download" do |dialog, params| + wikihouse_download_callback dialog, params + end + + dialog.add_action_callback "save" do |dialog, download_id| + wikihouse_save_callback dialog, download_id + end + + dialog.add_action_callback "error" do |dialog, download_id| + wikihouse_error_callback dialog, download_id + end + + # Set the dialog's url and display it. + dialog.set_url WIKIHOUSE_DOWNLOAD_URL dialog.show dialog.show_modal + end - -end - -# ------------------------------------------------------------------------------ -# Upload Dialog -# ------------------------------------------------------------------------------ - -def load_wikihouse_upload - - # Exit if the computer is not online. - if not Sketchup.is_online - UI.messagebox "You need to be connected to the internet to upload models to #{WIKIHOUSE_TITLE}." - return - end - - model = Sketchup.active_model - - # Exit if a model wasn't available. - if not model - show_wikihouse_error "You need to open a SketchUp model to share" - return - end - - # Initialise an attribute dictionary for custom metadata. - attr = model.attribute_dictionary WIKIHOUSE_TITLE, true - if attr.size == 0 - attr["spec"] = "0.1" - end - - # Exit if it's an unsaved model. - model_path = model.path - if model_path == "" - UI.messagebox "You need to save the model before it can be shared at #{WIKIHOUSE_TITLE}" - return - end - - # Auto-save the model if it has been modified. - if model.modified? - if not model.save model_path - show_wikihouse_error "Couldn't auto-save the model to #{model_path}" + + # ------------------------------------------------------------------------------ + # Make Dialog + # ------------------------------------------------------------------------------ + + def load_wikihouse_make + + model = Sketchup.active_model + + # Exit if a model wasn't available. + if not model + show_wikihouse_error "You need to open a SketchUp model before it can be fabricated" return end + + # Initialise an attribute dictionary for custom metadata. + attr = model.attribute_dictionary WIKIHOUSE_TITLE, true + if attr.size == 0 + attr["spec"] = WIKIHOUSE_EXTENSION.version + end + + # Exit if it's an unsaved model. + model_path = model.path + if model_path == "" + UI.messagebox "You need to save the model before the cutting sheets can be generated" + return + end + + # Try and infer the model's filename. + filename = model.title + if filename == "" + filename = "Untitled" + end + + # Get the model's parent directory and generate the new filenames to save to. + directory = File.dirname(model_path) + svg_filename = File.join(directory, filename + ".svg") + dxf_filename = File.join(directory, filename + ".dxf") + + # Make the cutting sheets for the house! + data = make_wikihouse model, true + if not data + return + end + + svg_data, dxf_data = data + + # Save the SVG data to the file. + File.open(svg_filename, "wb") do |io| + io.write svg_data + end + + # Save the DXF data to the file. + File.open(dxf_filename, "wb") do |io| + io.write dxf_data + end + + UI.messagebox "Cutting sheets successfully saved to #{directory}", MB_OK + + if WIKIHOUSE_MAC + dialog = UI::WebDialog.new "Cutting Sheets Preview", true, "#{WIKIHOUSE_TITLE}-Preview", 800, 800, 150, 150, true + dialog.set_file svg_filename + dialog.show + dialog.show_modal + end + end - - # Try and infer the model's name. - model_name = model.name - if model_name == "" - model_name = model.title - end - - # Instantiate an upload web dialog. - dialog = UI::WebDialog.new WIKIHOUSE_TITLE, true, "#{WIKIHOUSE_TITLE}-Upload", 480, 640, 150, 150, true - - # Load default values into the upload form. - dialog.add_action_callback "load" do |dialog, params| - if model_name != "" - if dialog.get_element_value("design-title") == "" - set_dom_value dialog, "design-title", model_name + + # ------------------------------------------------------------------------------ + # Upload Dialog + # ------------------------------------------------------------------------------ + + def load_wikihouse_upload + + # Exit if the computer is not online. + if not Sketchup.is_online + UI.messagebox "You need to be connected to the internet to upload models to #{WIKIHOUSE_TITLE}." + return + end + + model = Sketchup.active_model + + # Exit if a model wasn't available. + if not model + show_wikihouse_error "You need to open a SketchUp model to share" + return + end + + # Initialise an attribute dictionary for custom metadata. + attr = model.attribute_dictionary WIKIHOUSE_TITLE, true + if attr.size == 0 + attr["spec"] = "0.1" + end + + # Exit if it's an unsaved model. + model_path = model.path + if model_path == "" + UI.messagebox "You need to save the model before it can be shared at #{WIKIHOUSE_TITLE}" + return + end + + # Auto-save the model if it has been modified. + if model.modified? + if not model.save model_path + show_wikihouse_error "Couldn't auto-save the model to #{model_path}" + return end end - if model.description != "" - if dialog.get_element_value("design-description") == "" - set_dom_value dialog, "design-description", model.description + + # Try and infer the model's name. + model_name = model.name + if model_name == "" + model_name = model.title + end + + # Instantiate an upload web dialog. + dialog = UI::WebDialog.new WIKIHOUSE_TITLE, true, "#{WIKIHOUSE_TITLE}-Upload", 480, 640, 150, 150, true + + # Load default values into the upload form. + dialog.add_action_callback "load" do |dialog, params| + if model_name != "" + if dialog.get_element_value("design-title") == "" + set_dom_value dialog, "design-title", model_name + end + end + if model.description != "" + if dialog.get_element_value("design-description") == "" + set_dom_value dialog, "design-description", model.description + end end + if Sketchup.version + set_dom_value dialog, "design-sketchup-version", Sketchup.version + end + set_dom_value dialog, "design-plugin-version", WIKIHOUSE_PLUGIN_VERSION end - if Sketchup.version - set_dom_value dialog, "design-sketchup-version", Sketchup.version + + # Process and prepare the model related data for upload. + dialog.add_action_callback "process" do |dialog, params| + wikihouse_process_upload dialog, model, model_path end - set_dom_value dialog, "design-plugin-version", WIKIHOUSE_PLUGIN_VERSION - end - - # Process and prepare the model related data for upload. - dialog.add_action_callback "process" do |dialog, params| - wikihouse_process_upload dialog, model, model_path - end - - dialog.add_action_callback "uploaded" do |dialog, params| - if WIKIHOUSE_UPLOADS.key? dialog - WIKIHOUSE_UPLOADS.delete dialog + + dialog.add_action_callback "uploaded" do |dialog, params| + if WIKIHOUSE_UPLOADS.key? dialog + WIKIHOUSE_UPLOADS.delete dialog + end + if params == "success" + UI.messagebox "Successfully uploaded #{model_name}" + else + UI.messagebox "Upload to #{WIKIHOUSE_TITLE} failed. Please try again." + end end - if params == "success" - UI.messagebox "Successfully uploaded #{model_name}" - else - UI.messagebox "Upload to #{WIKIHOUSE_TITLE} failed. Please try again." + + dialog.add_action_callback "download" do |dialog, params| + wikihouse_download_callback dialog, params end - end - - dialog.add_action_callback "download" do |dialog, params| - wikihouse_download_callback dialog, params - end - - dialog.add_action_callback "save" do |dialog, download_id| - wikihouse_save_callback dialog, download_id - end - - dialog.add_action_callback "error" do |dialog, download_id| - wikihouse_error_callback dialog, download_id - end - - # TODO(tav): There can be a situation where the dialog has been closed, but - # the upload succeeds and the dialog gets called with "uploaded" and brought - # to front. - dialog.set_on_close do - dialog.set_url "about:blank" - if WIKIHOUSE_UPLOADS.key? dialog - show_wikihouse_error "Upload to #{WIKIHOUSE_TITLE} has been aborted" - WIKIHOUSE_UPLOADS.delete dialog + + dialog.add_action_callback "save" do |dialog, download_id| + wikihouse_save_callback dialog, download_id + end + + dialog.add_action_callback "error" do |dialog, download_id| + wikihouse_error_callback dialog, download_id + end + + # TODO(tav): There can be a situation where the dialog has been closed, but + # the upload succeeds and the dialog gets called with "uploaded" and brought + # to front. + dialog.set_on_close do + dialog.set_url "about:blank" + if WIKIHOUSE_UPLOADS.key? dialog + show_wikihouse_error "Upload to #{WIKIHOUSE_TITLE} has been aborted" + WIKIHOUSE_UPLOADS.delete dialog + end end + + dialog.set_url WIKIHOUSE_UPLOAD_URL + dialog.show + dialog.show_modal + end - dialog.set_url WIKIHOUSE_UPLOAD_URL - dialog.show - dialog.show_modal - + extend self # This makes each instance method defined above callable via the module + # instead of needing to instatiate a class first. + end # ------------------------------------------------------------------------------ # Set Globals # ------------------------------------------------------------------------------ +# This section is run only once and sets up the Extension menue items and tool buttons. +# It in not part of module named WIkihouse Extension, so methods etc. must be included/extended in to it + if not file_loaded? __FILE__ @@ -2086,10 +1779,10 @@ def load_wikihouse_upload # Initialise the core commands. WIKIHOUSE_DOWNLOAD = UI::Command.new "Get Models..." do - load_wikihouse_download + WikihouseExtension::load_wikihouse_download end - - WIKIHOUSE_DOWNLOAD.tooltip = "Find new models to use at #{WIKIHOUSE_TITLE}" + + WIKIHOUSE_DOWNLOAD.tooltip = "Find new models to use at #{WikihouseExtension::WIKIHOUSE_TITLE}" WIKIHOUSE_DOWNLOAD.small_icon = File.join WIKIHOUSE_DIR, "download-16.png" WIKIHOUSE_DOWNLOAD.large_icon = File.join WIKIHOUSE_DIR, "download.png" @@ -2100,7 +1793,7 @@ def load_wikihouse_upload } WIKIHOUSE_MAKE = UI::Command.new "Make This House..." do - load_wikihouse_make + WikihouseExtension::load_wikihouse_make end WIKIHOUSE_MAKE.tooltip = "Convert a model of a House into printable components" @@ -2113,12 +1806,12 @@ def load_wikihouse_upload MF_DISABLED|MF_GRAYED end } - + WIKIHOUSE_UPLOAD = UI::Command.new "Share Model..." do - load_wikihouse_upload + WikihouseExtension::load_wikihouse_upload end - WIKIHOUSE_UPLOAD.tooltip = "Upload and share your model at #{WIKIHOUSE_TITLE}" + WIKIHOUSE_UPLOAD.tooltip = "Upload and share your model at #{WikihouseExtension::WIKIHOUSE_TITLE}" WIKIHOUSE_UPLOAD.small_icon = File.join WIKIHOUSE_DIR, "upload-16.png" WIKIHOUSE_UPLOAD.large_icon = File.join WIKIHOUSE_DIR, "upload.png" WIKIHOUSE_UPLOAD.set_validation_proc { @@ -2130,29 +1823,30 @@ def load_wikihouse_upload } # Register a new toolbar with the commands. - WIKIHOUSE_TOOLBAR = UI::Toolbar.new WIKIHOUSE_TITLE + WIKIHOUSE_TOOLBAR = UI::Toolbar.new WikihouseExtension::WIKIHOUSE_TITLE WIKIHOUSE_TOOLBAR.add_item WIKIHOUSE_DOWNLOAD WIKIHOUSE_TOOLBAR.add_item WIKIHOUSE_UPLOAD WIKIHOUSE_TOOLBAR.add_item WIKIHOUSE_MAKE WIKIHOUSE_TOOLBAR.show # Register a new submenu of the standard Plugins menu with the commands. - WIKIHOUSE_MENU = UI.menu("Plugins").add_submenu WIKIHOUSE_TITLE + WIKIHOUSE_MENU = UI.menu("Plugins").add_submenu WikihouseExtension::WIKIHOUSE_TITLE WIKIHOUSE_MENU.add_item WIKIHOUSE_DOWNLOAD WIKIHOUSE_MENU.add_item WIKIHOUSE_UPLOAD WIKIHOUSE_MENU.add_item WIKIHOUSE_MAKE # Add our custom AppObserver. - Sketchup.add_observer WikiHouseAppObserver.new + Sketchup.add_observer WikihouseExtension::WikiHouseAppObserver.new # Display the Ruby Console in dev mode. - if WIKIHOUSE_DEV + if WikihouseExtension::WIKIHOUSE_DEV Sketchup.send_action "showRubyPanel:" + def w load "wikihouse.rb" end puts "" - puts "#{WIKIHOUSE_TITLE} Plugin Successfully Loaded." + puts "#{WikihouseExtension::WIKIHOUSE_TITLE} Extension Successfully Loaded." puts "" end @@ -2160,17 +1854,17 @@ def w end -def w - load "wikihouse.rb" - puts - data = make_wikihouse Sketchup.active_model, false - if data - filename = "/Users/tav/Documents/sketchup/Wikhouse10_tester3.svg" - svg_data, dxf_data = data - # Save the SVG data to the file. - File.open(filename, "wb") do |io| - io.write svg_data - end - "Sheets generated!" - end -end +#def test +# load "wikihouse.rb" +# puts +# data = make_wikihouse Sketchup.active_model, false +# if data +# filename = "/Users/tav/Documents/sketchup/Wikhouse10_tester3.svg" +# svg_data, dxf_data = data +# # Save the SVG data to the file. +# File.open(filename, "wb") do |io| +# io.write svg_data +# end +# "Sheets generated!" +# end +#end From 30721ec3ad1ee34e712d1e424cf27353aae406f1 Mon Sep 17 00:00:00 2001 From: Chris Musselle Date: Mon, 1 Apr 2013 01:49:33 +0100 Subject: [PATCH 08/31] Fixed the error. Origin was extra white space in generated svg file. --- wikihouse-extension/lib/writers.rb | 10 +-- wikihouse-extension/wikihouse.rb | 104 ----------------------------- wikihouse_extension_loader.rb | 59 ++++++++++++++-- 3 files changed, 57 insertions(+), 116 deletions(-) diff --git a/wikihouse-extension/lib/writers.rb b/wikihouse-extension/lib/writers.rb index 89bda6d..6195e56 100644 --- a/wikihouse-extension/lib/writers.rb +++ b/wikihouse-extension/lib/writers.rb @@ -42,13 +42,13 @@ def generate total_width = scale * (sheet_width + (margin * 2)) svg = [] - svg << <<-HEADER.gsub(/^ {6}/, '') + svg << <<-HEADER.gsub(/^\s+/, '') - #{WIKIHOUSE_TITLE} Cutting Sheets" + #{WIKIHOUSE_TITLE} Cutting Sheets" @@ -82,7 +82,7 @@ def generate x = (scale * center.x) + base_x y = (scale * center.y) + base_y radius = scale * radius - svg << <<-CIRCLE.gsub(/^ {14}/, '') + svg << <<-CIRCLE.gsub(/^\s+/, '') CIRCLE @@ -95,14 +95,14 @@ def generate path << "L #{(scale * point.x) + base_x} #{(scale * point.y) + base_y}" end path << "Z" - svg << <<-PATH.gsub(/^ {14}/, '') + svg << <<-PATH.gsub(/^\s+/, '') PATH end end if label and label != "" - svg << <<-LABEL.gsub(/^ {12}/, '') + svg << <<-LABEL.gsub(/^\s+/, '') #{label} LABEL end diff --git a/wikihouse-extension/wikihouse.rb b/wikihouse-extension/wikihouse.rb index fc7cbfc..1be4d92 100755 --- a/wikihouse-extension/wikihouse.rb +++ b/wikihouse-extension/wikihouse.rb @@ -21,110 +21,6 @@ module WikihouseExtension # Top Level Namespace - # ------------------------------------------------------------------------------ - # SVG Writer - # ------------------------------------------------------------------------------ - - class WikiHouseSVG - - def initialize(layout, scale) - @layout = layout - @scale = scale - end - - def generate - - layout = @layout - scale = @scale - - sheet_height, sheet_width, inner_height, inner_width, margin = layout.dimensions - sheets = layout.sheets - count = sheets.length - - scaled_height = scale * sheet_height - scaled_width = scale * sheet_width - total_height = scale * ((count * (sheet_height + (12 * margin))) + (margin * 10)) - total_width = scale * (sheet_width + (margin * 2)) - - svg = [] - svg << <<-HEADER.gsub(/^ {6}/, '') - - - - #{WIKIHOUSE_TITLE} Cutting Sheets" - - - - - HEADER - - loop_count = 0 - - for s in 0...count - - sheet = sheets[s] - base_x = scale * margin - base_y = scale * ((s * (sheet_height + (12 * margin))) + (margin * 9)) - - svg << "" - - base_x += scale * margin - base_y += scale * margin - - sheet.each do |loops, circles, outer_mapped, centroid, label| - - Sketchup.set_status_text WIKIHOUSE_SVG_STATUS[(loop_count/5) % 5] - loop_count += 1 - - svg << '' - - for i in 0...loops.length - circle = circles[i] - if circle - center, radius = circle - x = (scale * center.x) + base_x - y = (scale * center.y) + base_y - radius = scale * radius - svg << <<-CIRCLE.gsub(/^ {14}/, '') - - CIRCLE - else - loop = loops[i] - first = loop.shift - path = [] - path << "M #{(scale * first.x) + base_x} #{(scale * first.y) + base_y}" - loop.each do |point| - path << "L #{(scale * point.x) + base_x} #{(scale * point.y) + base_y}" - end - path << "Z" - svg << <<-PATH.gsub(/^ {14}/, '') - - PATH - end - end - - if label and label != "" - svg << <<-LABEL.gsub(/^ {12}/, '') - #{label} - LABEL - end - - svg << '' - - end - end - - svg << '' - svg << '' - svg.join "\n" - - end - - end - # ------------------------------------------------------------------------------ # Layout Engine # ------------------------------------------------------------------------------ diff --git a/wikihouse_extension_loader.rb b/wikihouse_extension_loader.rb index 754ee74..210ed68 100644 --- a/wikihouse_extension_loader.rb +++ b/wikihouse_extension_loader.rb @@ -1,12 +1,57 @@ # Create an entry in the Extension list that loads the wikihouse.rb scripts -require 'sketchup.rb' require 'extensions.rb' -wikihouse_extension = SketchupExtension.new "Wikihouse Plugin", "wikihouse-extension/wikihouse.rb" -wikihouse_extension.version = ' 0.1' -wikihouse_extension.description = "Allows for the sharing and downloading of wikihouse models at http://www.wikihouse.cc/, as well as the traslation of models to cutting templates." -wikihouse_extension.creator = " Wikihouse Development Team" -wikihouse_extension.copyright = " Public Domain - 2013" +# Add all files in the lib directory to the $LOAD_PATH array +abs_lib_path = File.join(File.expand_path(File.dirname(__FILE__)), "/wikihouse-extension/lib") +$LOAD_PATH.unshift(abs_lib_path) unless $LOAD_PATH.include?(abs_lib_path) -Sketchup.register_extension wikihouse_extension, true \ No newline at end of file +require 'utils.rb' + +module WikihouseExtension + + # Run Flags + WIKIHOUSE_DEV = true + WIKIHOUSE_LOCAL = false + WIKIHOUSE_HIDE = false + WIKIHOUSE_SHORT_CIRCUIT = false + + # Some Global Constants + WIKIHOUSE_TITLE = 'Wikihouse' # name of Wikihouse project, incase it changes. + + # Path setup + if WIKIHOUSE_LOCAL + WIKIHOUSE_SERVER = "http://localhost:8080" + else + WIKIHOUSE_SERVER = "http://wikihouse-cc.appspot.com" + end + + WIKIHOUSE_DOWNLOAD_PATH = "/library/sketchup" + WIKIHOUSE_UPLOAD_PATH = "/library/designs/add/sketchup" + WIKIHOUSE_DOWNLOAD_URL = WIKIHOUSE_SERVER + WIKIHOUSE_DOWNLOAD_PATH + WIKIHOUSE_UPLOAD_URL = WIKIHOUSE_SERVER + WIKIHOUSE_UPLOAD_PATH + + WIKIHOUSE_TEMP = get_temp_directory + + # Get Platform + if RUBY_PLATFORM =~ /mswin/ + WIKIHOUSE_CONF_FILE = File.join ENV['APPDATA'], 'WikiHouse.conf' + WIKIHOUSE_SAVE = get_documents_directory ENV['USERPROFILE'], 'Documents' + WIKIHOUSE_MAC = false + else + WIKIHOUSE_CONF_FILE = File.join ENV['HOME'], '.wikihouse.conf' + WIKIHOUSE_SAVE = get_documents_directory ENV['HOME'], 'Documents' + WIKIHOUSE_MAC = true + end + + # Define and Load the wikihouse Extension + WIKIHOUSE_EXTENSION = SketchupExtension.new "Wikihouse Plugin Development Version", "wikihouse-extension/wikihouse.rb" + WIKIHOUSE_EXTENSION.version = ' 0.1' + WIKIHOUSE_EXTENSION.description = "Allows for the sharing and downloading of wikihouse models at http://www.wikihouse.cc/, as well as the traslation of models to cutting templates." + WIKIHOUSE_EXTENSION.creator = " Wikihouse Development Team" + WIKIHOUSE_EXTENSION.copyright = " Public Domain - 2013" + + # All constants should be defined before loading extension to avoid error. + Sketchup.register_extension WIKIHOUSE_EXTENSION, true + +end From 1a2c7640493bb7b635b184c640302085b1962268 Mon Sep 17 00:00:00 2001 From: Chris Musselle Date: Tue, 9 Apr 2013 16:40:32 +0100 Subject: [PATCH 09/31] Minor comment edits + experimenting with SVG writer code. --- wikihouse-extension/lib/writers.rb | 17 +++++++++++++++++ wikihouse-extension/wikihouse.rb | 17 ++++++++++++++--- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/wikihouse-extension/lib/writers.rb b/wikihouse-extension/lib/writers.rb index 6195e56..dd63ce6 100644 --- a/wikihouse-extension/lib/writers.rb +++ b/wikihouse-extension/lib/writers.rb @@ -21,6 +21,11 @@ def generate # ------------------------------------------------------------------------------ class WikiHouseSVG + + # May reformat with multiline string in this format: + # string = "line #1"\ + # "line #2"\ + # "line #3" def initialize(layout, scale) @layout = layout @@ -55,6 +60,18 @@ def generate HEADER +# svg << %[\n] \ +# %[\n] \ +# %["\n] +# #{WIKIHOUSE_TITLE} Cutting Sheets" +# +# +# +# +# HEADER + loop_count = 0 for s in 0...count diff --git a/wikihouse-extension/wikihouse.rb b/wikihouse-extension/wikihouse.rb index 1be4d92..e763890 100755 --- a/wikihouse-extension/wikihouse.rb +++ b/wikihouse-extension/wikihouse.rb @@ -1658,9 +1658,9 @@ def load_wikihouse_upload # ------------------------------------------------------------------------------ # Set Globals # ------------------------------------------------------------------------------ -# This section is run only once and sets up the Extension menue items and tool buttons. -# It in not part of module named WIkihouse Extension, so methods etc. must be included/extended in to it - +# This section is run only once and sets up the Extension menu items and tool buttons. +# It in not part of module named WIkihouseExtension, so methods etc. must be referenced with +# WikihouseExtension::name, where name is the name of the method, constant or class. if not file_loaded? __FILE__ @@ -1744,6 +1744,17 @@ def w puts "" puts "#{WikihouseExtension::WIKIHOUSE_TITLE} Extension Successfully Loaded." puts "" + + # Interactive utilities + def mod + return Sketchup.active_model # Open model + end + def ent + return Sketchup.active_model.entities # All entities in model + end + def sel + return Sketchup.active_model.selection # Current selection + end end file_loaded __FILE__ From 4808846a9a83c5b8b6ea8544ea6866b73b73f456 Mon Sep 17 00:00:00 2001 From: Chris Musselle Date: Sun, 28 Apr 2013 01:27:55 +0100 Subject: [PATCH 10/31] Updated Make file to also install files into default location on Mac with "install_mac" --- Makefile | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index a889b81..4183317 100644 --- a/Makefile +++ b/Makefile @@ -1,15 +1,31 @@ # Make file for creating wikihouse_extention.rbz +assets=wikihouse-extension/wikihouse-assets/* +libs=wikihouse-extension/lib/* + +# Store current git revision git-rev=$(git rev-parse --short=8 HEAD) # May need changing - not sure if full url is used for amazon buckets wikihouse_bucket="https://wikihouse.s3.amazonaws.com/sketchup/" -build: wikihouse_extension_loader.rb $(assets) $(scripts) - # Zip all files and subfolders recursively - zip -r wikihouse_extension.rbz wikihouse_extension_loader.rb wikihouse-extension/* + +build: wikihouse_extension_loader.rb $(assets) $(libs) + # Creating .rbz file + zip wikihouse_extension.rbz \ + wikihouse_extension_loader.rb \ + wikihouse-extension/wikihouse.rb \ + $(assets) \ + $(libs) release: wikihouse_extension.rbz - # s3put BUCKET/[OBJECT] [FILE] - OBJECT is the name FILE is saved as in BUCKET + # s3put BUCKET/[OBJECT] [FILE] - OBJECT is the name FILE is saved as in BUCKET s3put ${wikihouse_bucket}wikihouse_extension-$git-rev.rbz wikihouse_extension.rbz - s3put ${wikihouse_bucket}wikihouse_extension.rbz wikihouse_extension.rbz + s3put ${wikihouse_bucket}wikihouse_extension.rbz wikihouse_extension.rbz + +install_mac: wikihouse_extension_loader.rb wikihouse-extension/wikihouse.rb $(assets) $(libs) + # Copying files to their locations + cp -v wikihouse_extension_loader.rb /Library/Application\ Support/Google\ SketchUp\ 8/SketchUp/plugins/wikihouse_extension_loader.rb + cp -v wikihouse-extension/wikihouse.rb /Library/Application\ Support/Google\ SketchUp\ 8/SketchUp/plugins/wikihouse-extension/wikihouse.rb + cp -v $(assets) /Library/Application\ Support/Google\ SketchUp\ 8/SketchUp/plugins/wikihouse-extension/wikihouse-assets/ + cp -v $(libs) /Library/Application\ Support/Google\ SketchUp\ 8/SketchUp/plugins/wikihouse-extension/lib/ \ No newline at end of file From 020f1a4038010edbb40816b6811fc25e162db608 Mon Sep 17 00:00:00 2001 From: Chris Musselle Date: Sun, 28 Apr 2013 01:28:54 +0100 Subject: [PATCH 11/31] All Web dialogue will soon be moved here fore easier reference. --- wikihouse-extension/lib/WebDialog.rb | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 wikihouse-extension/lib/WebDialog.rb diff --git a/wikihouse-extension/lib/WebDialog.rb b/wikihouse-extension/lib/WebDialog.rb new file mode 100644 index 0000000..1f59114 --- /dev/null +++ b/wikihouse-extension/lib/WebDialog.rb @@ -0,0 +1,9 @@ +# Web Dialogue for + +module WikihouseExtension + + def f + end + + +end \ No newline at end of file From 5faf0c83569164f04f038f5aa68f99bc3f478125 Mon Sep 17 00:00:00 2001 From: Chris Musselle Date: Sun, 28 Apr 2013 01:30:34 +0100 Subject: [PATCH 12/31] New JSON module for parsing of Ruby Hashes and Arrays to and from json object strings. Used in parsing data to web dialogues. --- wikihouse-extension/lib/JSON.rb | 55 +++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 wikihouse-extension/lib/JSON.rb diff --git a/wikihouse-extension/lib/JSON.rb b/wikihouse-extension/lib/JSON.rb new file mode 100644 index 0000000..4cd2a58 --- /dev/null +++ b/wikihouse-extension/lib/JSON.rb @@ -0,0 +1,55 @@ +# JSON formatter for parsing Ruby hases and Arrays to JavaScript as +# json objects. +# +# Original Author: Aerilius +# Source: http://sketchucation.com/forums/viewtopic.php?f=180&t=35969 +# + +module WikihouseExtension + + module JSON + + module_function() # Allows Methods to be callable from module + + def from_json(json_string) + # split at every even number of unescaped quotes; if it's not a string then replace : and null + ruby_string = json_string.split(/(\"(?:.*?[^\\])*?\")/). + collect{|s| + (s[0..0] != '"')? s.gsub(/\:/, "=>").gsub(/null/, "nil") : s + }. + join() + result = eval(ruby_string) + return result + rescue Exception => e + {} + end + + def to_json(obj) + json_classes = [String, Symbol, Fixnum, Float, Length, Array, Hash, TrueClass, FalseClass, NilClass] + # remove non-JSON objects + check_value = nil + check_array = Proc.new{|o| o.reject!{|k| !check_value.call(k) } } + check_hash = Proc.new{|o| o.reject!{|k,v| !k.is_a?(String) && !k.is_a?(Symbol) || !check_value.call(v) } } + check_value = Proc.new{|v| + if v.is_a?(Array) + check_array.call(v) + elsif v.is_a?(Hash) + check_hash.call(v) + end + json_classes.include?(v.class) + } + return "null" unless check_value.call(obj) + # split at every even number of unescaped quotes; if it's not a string then turn Symbols into String and replace => and nil + json_string = obj.inspect.split(/(\"(?:.*?[^\\])*?\")/).collect{ |s| + (s[0..0] != '"')? # If we are not inside a string + s.gsub(/\:(\S+?(?=\=>|\s))/, "\"\\1\""). # Symbols to String + gsub(/=>/, ":"). # Arrow to colon + gsub(/\bnil\b/, "null") : # nil to null + s + }.join() + return json_string + end + + end #JSON + +end # Wikihouse Module \ No newline at end of file From 0018ed96528f6c03ca28fc02329a4953e6b756a5 Mon Sep 17 00:00:00 2001 From: Chris Musselle Date: Sun, 28 Apr 2013 01:31:42 +0100 Subject: [PATCH 13/31] File no longer needed. Constants moved into utils.rb or wikihouse_extension_loader.rb --- wikihouse-extension/lib/constants.rb | 57 ---------------------------- 1 file changed, 57 deletions(-) delete mode 100644 wikihouse-extension/lib/constants.rb diff --git a/wikihouse-extension/lib/constants.rb b/wikihouse-extension/lib/constants.rb deleted file mode 100644 index 49c5b49..0000000 --- a/wikihouse-extension/lib/constants.rb +++ /dev/null @@ -1,57 +0,0 @@ -# ------------------------------------------------------------------------------ -# Wikihouse Constants -# ------------------------------------------------------------------------------ - -module WikihouseExtension - - # Pannel stuff - PANEL_ID_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - PANEL_ID_ALPHABET_LENGTH = PANEL_ID_ALPHABET.length - - WIKIHOUSE_FONT_HEIGHT = 30.mm - WIKIHOUSE_PANEL_PADDING = 25.mm / 2 - WIKIHOUSE_SHEET_HEIGHT = 1200.mm - WIKIHOUSE_SHEET_MARGIN = 15.mm - WIKIHOUSE_PANEL_PADDING - WIKIHOUSE_SHEET_WIDTH = 2400.mm - - - # Set Wikihouse Pannel Dimentions - WIKIHOUSE_SHEET_INNER_HEIGHT = WIKIHOUSE_SHEET_HEIGHT - (2 * WIKIHOUSE_SHEET_MARGIN) - WIKIHOUSE_SHEET_INNER_WIDTH = WIKIHOUSE_SHEET_WIDTH - (2 * WIKIHOUSE_SHEET_MARGIN) - - WIKIHOUSE_DIMENSIONS = [ - WIKIHOUSE_SHEET_HEIGHT, - WIKIHOUSE_SHEET_WIDTH, - WIKIHOUSE_SHEET_INNER_HEIGHT, - WIKIHOUSE_SHEET_INNER_WIDTH, - WIKIHOUSE_SHEET_MARGIN, - WIKIHOUSE_PANEL_PADDING, - WIKIHOUSE_FONT_HEIGHT - ] - - # Status Messages - WIKIHOUSE_DETECTION_STATUS = gen_status_msg "Detecting matching faces" - WIKIHOUSE_DXF_STATUS = gen_status_msg "Generating DXF output" - WIKIHOUSE_LAYOUT_STATUS = gen_status_msg "Nesting panels for layout" - WIKIHOUSE_PANEL_STATUS = gen_status_msg "Generating panel data" - WIKIHOUSE_SVG_STATUS = gen_status_msg "Generating SVG output" - - # UI Message Box codes - REPLY_ABORT = 3 - REPLY_CANCEL = 2 - REPLY_NO = 7 - REPLY_OK = 1 - REPLY_RETRY = 4 - REPLY_YES = 6 - - # Dummy Group - class WikiHouseDummyGroup - attr_reader :name - - def initialize - @name = "Ungrouped Objects" - end - end - WIKIHOUSE_DUMMY_GROUP = WikiHouseDummyGroup.new - -end From 5ee3cacd00b7152ddd8c228fc4fa5c59bc0aa635 Mon Sep 17 00:00:00 2001 From: Chris Musselle Date: Sun, 28 Apr 2013 01:32:19 +0100 Subject: [PATCH 14/31] Added some more constants --- wikihouse-extension/lib/utils.rb | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/wikihouse-extension/lib/utils.rb b/wikihouse-extension/lib/utils.rb index c387184..3a10f89 100644 --- a/wikihouse-extension/lib/utils.rb +++ b/wikihouse-extension/lib/utils.rb @@ -2,7 +2,7 @@ module WikihouseExtension # ------------------------------------------------------------------------------ - # Utility Functions + # Utility Functions and Constants # ------------------------------------------------------------------------------ # Path Utilities @@ -26,9 +26,9 @@ def get_temp_directory File.expand_path temp end - # Status Messages - def gen_status_msg(msg) + # Use self methods as they need to be called next + def self.gen_status_msg(msg) return [ msg + " .", msg + " ..", @@ -38,6 +38,29 @@ def gen_status_msg(msg) ] end + WIKIHOUSE_DETECTION_STATUS = self.gen_status_msg "Detecting matching faces" + WIKIHOUSE_DXF_STATUS = self.gen_status_msg "Generating DXF output" + WIKIHOUSE_LAYOUT_STATUS = self.gen_status_msg "Nesting panels for layout" + WIKIHOUSE_PANEL_STATUS = self.gen_status_msg "Generating panel data" + WIKIHOUSE_SVG_STATUS = self.gen_status_msg "Generating SVG output" + + # Dummy Group + class WikiHouseDummyGroup + attr_reader :name + + def initialize + @name = "Ungrouped Objects" + end + end + WIKIHOUSE_DUMMY_GROUP = WikiHouseDummyGroup.new + + # UI Message Box codes + REPLY_ABORT = 3 + REPLY_CANCEL = 2 + REPLY_NO = 7 + REPLY_OK = 1 + REPLY_RETRY = 4 + REPLY_YES = 6 def get_wikihouse_thumbnail(model, view, suffix) filename = File.join WIKIHOUSE_TEMP, "#{model.guid}-#{suffix}.png" From 8eeae1393044fce64f7419210e532ec40d893aa4 Mon Sep 17 00:00:00 2001 From: Chris Musselle Date: Sun, 28 Apr 2013 01:33:53 +0100 Subject: [PATCH 15/31] Changed dimensions to a module vaiable. Added a Hash '@@wikihouse_settings' to store variable info. --- wikihouse_extension_loader.rb | 59 ++++++++++++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 4 deletions(-) diff --git a/wikihouse_extension_loader.rb b/wikihouse_extension_loader.rb index 210ed68..5accd69 100644 --- a/wikihouse_extension_loader.rb +++ b/wikihouse_extension_loader.rb @@ -7,17 +7,21 @@ $LOAD_PATH.unshift(abs_lib_path) unless $LOAD_PATH.include?(abs_lib_path) require 'utils.rb' +require 'JSON.rb' module WikihouseExtension # Run Flags - WIKIHOUSE_DEV = true - WIKIHOUSE_LOCAL = false - WIKIHOUSE_HIDE = false + WIKIHOUSE_DEV = true # If true brings up Ruby Console and loads some utility functions on startup + WIKIHOUSE_LOCAL = false + WIKIHOUSE_HIDE = false WIKIHOUSE_SHORT_CIRCUIT = false # Some Global Constants WIKIHOUSE_TITLE = 'Wikihouse' # name of Wikihouse project, incase it changes. + # Pannel stuff + PANEL_ID_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + PANEL_ID_ALPHABET_LENGTH = PANEL_ID_ALPHABET.length # Path setup if WIKIHOUSE_LOCAL @@ -44,9 +48,56 @@ module WikihouseExtension WIKIHOUSE_MAC = true end + # Set defaults for Global Variables + + # Set Wikihouse Pannel Dimentions + wikihouse_sheet_height = 1200.mm + wikihouse_sheet_width = 2400.mm + wihihouse_panel_padding = 25.mm / 2 + wikihouse_sheet_margin = 15.mm - wihihouse_panel_padding + wikihouse_font_height = 30.mm + wikihouse_sheet_inner_height = wikihouse_sheet_height - (2 * wikihouse_sheet_margin) + wikihouse_sheet_inner_width = wikihouse_sheet_width - (2 * wikihouse_sheet_margin) + + @@wikihouse_dimensions = [ + wikihouse_sheet_height, + wikihouse_sheet_width, + wikihouse_sheet_inner_height, + wikihouse_sheet_inner_width, + wikihouse_sheet_margin, + wihihouse_panel_padding, + wikihouse_font_height + ] + + #(Chris) Plan to eventually store all setting as a hash. + # Duplication exists for now to ensure code compatability. + + # Store the actual values as length objects (in inches) + @@wikihouse_settings = { + "sheet_height" => wikihouse_sheet_height, + "sheet_inner_height" => wikihouse_sheet_inner_height, + "sheet_width" => wikihouse_sheet_width, + "sheet_inner_width" => wikihouse_sheet_inner_width, + "padding" => wihihouse_panel_padding, + "margin" => wikihouse_sheet_margin, + "font_height" => wikihouse_font_height, + } + + # Store default values for recall + DEFAULT_SETTINGS = Hash[@@wikihouse_settings] + + # Get and set methods for wikihouse_settings (so they can be returned via + # referencing the module e.g. WikihouseExtension.settings ) + def self.settings + @@wikihouse_settings + end + def self.settings=(settings) + @@wikihouse_settings = settings + end + # Define and Load the wikihouse Extension WIKIHOUSE_EXTENSION = SketchupExtension.new "Wikihouse Plugin Development Version", "wikihouse-extension/wikihouse.rb" - WIKIHOUSE_EXTENSION.version = ' 0.1' + WIKIHOUSE_EXTENSION.version = ' 0.2 Dev' WIKIHOUSE_EXTENSION.description = "Allows for the sharing and downloading of wikihouse models at http://www.wikihouse.cc/, as well as the traslation of models to cutting templates." WIKIHOUSE_EXTENSION.creator = " Wikihouse Development Team" WIKIHOUSE_EXTENSION.copyright = " Public Domain - 2013" From 049f861151b5e9220b6349c61670a86c79b9319f Mon Sep 17 00:00:00 2001 From: Chris Musselle Date: Sun, 28 Apr 2013 01:34:57 +0100 Subject: [PATCH 16/31] Added settings Web Dialogue. Testing in progress. --- wikihouse-extension/lib/settings.html | 40 +++++ wikihouse-extension/lib/settings.js | 95 +++++++++++ .../wikihouse-assets/cog-16.png | Bin 0 -> 825 bytes wikihouse-extension/wikihouse-assets/cog.png | Bin 0 -> 1525 bytes wikihouse-extension/wikihouse.rb | 159 ++++++++++++++++-- 5 files changed, 284 insertions(+), 10 deletions(-) create mode 100644 wikihouse-extension/lib/settings.html create mode 100644 wikihouse-extension/lib/settings.js create mode 100644 wikihouse-extension/wikihouse-assets/cog-16.png create mode 100644 wikihouse-extension/wikihouse-assets/cog.png diff --git a/wikihouse-extension/lib/settings.html b/wikihouse-extension/lib/settings.html new file mode 100644 index 0000000..92aafb5 --- /dev/null +++ b/wikihouse-extension/lib/settings.html @@ -0,0 +1,40 @@ + + + + + + + +

Wikihouse Options


+ +

Sheet Settings


+ +Sheet Height: mm
+Sheet Width: mm
+ +
+Sheet Margin: mm
+ +
+Sheet Padding: mm
+ +
+Font Height: mm
+ +
+
+ + + + + +
+

+ +
+ + + + + diff --git a/wikihouse-extension/lib/settings.js b/wikihouse-extension/lib/settings.js new file mode 100644 index 0000000..14fd600 --- /dev/null +++ b/wikihouse-extension/lib/settings.js @@ -0,0 +1,95 @@ +// JavaScript File for settings.html +// @author: C. Musselle + +// Debugging +window.onerror = function (msg, url, line) { + alert("Message : " + msg + "\n\nurl : " + url + "\n\nLine No: " + line); +} + +window.onload = function() { fetch_settings('current') } +// For some reason the page is loaded blank untill the window is refocused onto. +window.blur() +window.focus() + +// Define Globals +var settings; + +// Parse Wikihouse Settings for the input JSON string +function recieve_wikihouse_settings(args) { + + /* + @@wikihouse_settings = { + "sheet_height" => wikihouse_sheet_height, + "sheet_inner_height" => wikihouse_sheet_inner_height, + "sheet_width" => wikihouse_sheet_width, + "sheet_inner_width" => wikihouse_sheet_inner_width, + "padding" => wihihouse_panel_padding, + "margin" => wikihouse_sheet_margin, + "font_height" => wikihouse_font_height, + } + */ + + settings = JSON.parse(args) + + document.getElementById("sheet_height").value = settings.sheet_height; + document.getElementById("sheet_width").value = settings.sheet_width; + document.getElementById("margin").value = settings.margin; + document.getElementById("padding").value = settings.padding; + document.getElementById("font_height").value = settings.font_height; + } + +// Update Settings +function send_wikihouse_settings(mode) { + + var ids = new Array("sheet_height", "sheet_width", "sheet_margin", + "sheet_padding", "font_height"); + + var value, args; + + for (field in ids) { + + value = document.getElementById(field).value + alert(typeof value) + + if (typeof value == "number") { + // Only update those that are genuine numbers + settings[field] = value + } + } + + //Convert to String + args = JSON.stringify(settings) + + // Possibly add close flag for dialogue + if (mode == 1) { + args = args + "--close"; + } + + //Send argument to SketchUp script for processing + window.location.href = "skp:update_settings@" + args; +} + +function cancel() { + window.location.href = 'skp:cancel_settings@'; +} + +function display_status(msg) { + document.getElementById("status_out").innerHTML = msg; +} + +function fetch_settings(arg) { + window.location.href = 'skp:fetch_settings@' + arg; +} + +function do_stuff() { + alert(settings["sheet_height"] = eval('12')) + alert(typeof settings.sheet_height) + alert(settings["sheet_height"] = eval(234566)) + alert(typeof settings.sheet_height) +} + + + + + + diff --git a/wikihouse-extension/wikihouse-assets/cog-16.png b/wikihouse-extension/wikihouse-assets/cog-16.png new file mode 100644 index 0000000000000000000000000000000000000000..f70a6fac60b3b3be7c25158c4ffe83c52b8c3633 GIT binary patch literal 825 zcmV-91IGM`P)(*1 zK~y-)t&>kkR8bVhzx&=B=iWE-3L0!s&>R=iYgv*cW{#l{mJKCT5|)!zo1zxE%4$kA2#}&-b{rvw}ll&v(a{Zdn z_0@U5))N45xm>Vq8vw9vYlYs>(C}7~WiUp?GHn@&&P9=#nF-sr#p|IEsXurqI~t9K z0YCx(+qO~a_n#;)EB6!>6tpR_EEuC;loDB%kd>7M+qS{ENrVt$YemJY-zKQ8uHI3* ze_y9FEe#HbLoh}Oqf{`)h?7z*SPS6X#Ei)?GBSq7Q;pf3ot@!8AOHmba(y|qiY&w7 zaDXvN7^8wZDQ>j3zKTpuzpJX+x#wdvDsgTi_9=#BGC3Ov1ONas0Bo^P;=jP_dB>-r?+FaY+)AK^p zG(w#elu~^C`X#lrB*9`b8IQ*kV2r`(aEh|>vMs@2@CpDBNs^FCr2xP)92%PScvir& zEHTYZbadQ&^kHIRyr`%s=ytm$m8oDV17i#WF9+Jro;zEuq*5tFCf;LH>1GEZz`SK) zhMRf=!Ixlg()aPFKP(jE8@m z^52w^k)hVq)O?a8sV@;vTyDD5bXQRnEG{nozYqQfdZi?(Ty2a_00000NkvXXu0mjf DdOC4= literal 0 HcmV?d00001 diff --git a/wikihouse-extension/wikihouse-assets/cog.png b/wikihouse-extension/wikihouse-assets/cog.png new file mode 100644 index 0000000000000000000000000000000000000000..d6e59020846fa77776228d2879d40a6df4699a0c GIT binary patch literal 1525 zcmVmgD6tTYfdv0(gM_8lU}BpTi%n|EBa!Z6d?DRZA!WOB_toz1Y{wtl7Aggae7`35 z&YW|;@18jqxI=Nyr(8Ep6L(w6oi^j~H~_$E&2&Eb#1pfLBo+Am{_hDPm+pyRZB-Ro zPo9LsZhvv}=DJts%%1%qV=UK5r7}7@J74eVIrGsByWQ;T?+1Wf0D&1C%k7Qc5yZ6}`Q^v#j@7<-Y!Y13><#2b#`Y_V>jb_kOr49F7e9 z8%w@jYa{V^s;aVbOKwgM6iNxB6r6L&5}9eY+aH-bch2;kJGO7HUR(X>vL#D$N@mTf z`Th4lz7GTffhh?xGc%D$Bme;W_U%PmTYG;|QPH-tvPBkImcbZ>qENGV#td!w@@39N z3*8kZB_$8Zl7zre5SdnMuA<20KX!F}0l*l?l>lJNmO4Zt5dc^ZK;E_M4XmwMyXTpw z*XI#Jz!(Lk42&@%D>5V^;r_flaLy3ag{cc+hQnbjSWsHJa^=deZ#jcQhmJtgGO7e7Z@BIGz`NK zHxo$X4_mj^H)LmLW6{Ee>MJjAe%5NW62>TDjDd{-TPzl685uB=27KF408@(P$LYrcHyYs@0X1m0x+i-c(;-Uut4RIkueXl+ycDRfVc@P|8fk z)9NjnhSNWG!E>Tzw`rOvEGPhg%WZA#M?@%uXehTqQFfB(kDR9O!7uxN&NP005GP5syY=P{!X=sOfMxDC2DD z@bGY1AQ1S@>+}Ap0KoZk=P#s^$(PE?$`rrf-{x{YTuvkjj8a6SQMl&K!_({6y-5gZ z9HYr|xm+(xl9V1H<6w*t%F>+}Nkb0?gV!fT#m^UdQ$JtmO>L{M_e5i{@*JBD@k9ba zT|k7w=B7<`AK2}V=L185za5SlYYGbsp(u*U8BaTnGcd-$88;*0h~ClBacL4kch_kw zT~Q8)!!C`Ej$$Mp$IUj3L?UpyobzL`;rWszO#3&+)d$S3kb0XnGOf zuwg?DfYzzf^kG?+>GiGCc>6x6S9j8tmD=jU3igTVH3WbOef?U0J4S~QAtX69pm@$)4MmXotEE-zAX*t*4 z-d?kD<3{*=z7zl-01ONaOqr$$Awq4Mrai_OD+mU4^!D~1xp?uHeOInr8Pqf_KR+)& zi!lbu((!Gx*$P`*TMzquzR*;$Pi}J^SZZr)J(46jnwy);0K{xIn^eEG{%yD0{fl*l?5S0^ytx}YsS~Iv$GKl21nzG_^*S5gRo>;Vv3>+ zUjFOyg{A{d?*sTrQ4|=4an~J8S#Gzxz-F^mq*AG&&dyFBfIk4h>2$*9^WmOq_3G9C b>$`sd$lx(ak-%m;00000NkvXXu0mjfp(DV$ literal 0 HcmV?d00001 diff --git a/wikihouse-extension/wikihouse.rb b/wikihouse-extension/wikihouse.rb index e763890..b3f2321 100755 --- a/wikihouse-extension/wikihouse.rb +++ b/wikihouse-extension/wikihouse.rb @@ -1175,7 +1175,7 @@ def make_wikihouse(model, interactive) entities = selection end - dimensions = WIKIHOUSE_DIMENSIONS + dimensions = @@wikihouse_dimensions # Load and parse the entities. if WIKIHOUSE_SHORT_CIRCUIT and $wikloader @@ -1437,6 +1437,128 @@ def wikihouse_process_upload(dialog, model, model_path) end + + # ------------------------------------------------------------------------------ + # Configure Dialog + # ------------------------------------------------------------------------------ +#def load_wikihouse_settings +# my_dialog = UI::WebDialog.new("Selection Info", false, "Selection Info", 200, 200, 200, 200, true) +# +# # Attach an action callback +# my_dialog.add_action_callback("get_data") do |web_dialog,action_name| +# UI.messagebox("Ruby says: Your javascript has asked for " + action_name.to_s) +# end +# +# # Find and show our html file +# html_path = Sketchup.find_support_file "selectionInfo.html" ,"Plugins/cm" +# my_dialog.set_file(html_path) +# my_dialog.show() +#end +# + + +def load_wikihouse_settings + + # Create WebDialog + dialog = UI::WebDialog.new WIKIHOUSE_TITLE, true, "#{WIKIHOUSE_TITLE}-Settings", 480, 640, 150, 150, true + + # Get Current Wikihouse Settings + dialog.add_action_callback("fetch_settings") do |d, args| + + if args == "default" + + # Convert Dimenstions to mm + dims = {} + for k, v in DEFAULT_SETTINGS + dims[k] = v.to_mm + end + script = "recieve_wikihouse_settings('" + JSON.to_json(dims) + "');" + d.execute_script(script) + + elsif args == "current" + + # Convert Dimenstions to mm + dims = {} + for k, v in @@wikihouse_settings + dims[k] = v.to_mm + end + script = "recieve_wikihouse_settings('" + JSON.to_json(dims) + "');" + d.execute_script(script) + + end + end + + + # Set Web Dialog's Callbacks + dialog.add_action_callback("update_settings") do |d, args| + + if args == nil + UI.messagebox("Some arguments are empty!") + else + args = args.split(", ") + if args[0] != "" + @@wikihouse_dimensions[0] = eval(args[0] + ".mm") + end + if args[1] != "" + @@wikihouse_dimensions[1] = eval(args[1] + ".mm") + end + if args[4] != "" + @@wikihouse_dimensions[4] = eval(args[2] + ".mm") + end + if args[5] != "" + @@wikihouse_dimensions[5] = eval(args[3] + ".mm") + end + if args[6] != "" + @@wikihouse_dimensions[6] = eval(args[4] + ".mm") + end + + # Recalculate inner hieghts and widths + @@wikihouse_dimensions[2] = @@wikihouse_dimensions[0] - (2 * @@wikihouse_dimensions[4]) + @@wikihouse_dimensions[3] = @@wikihouse_dimensions[1] - (2 * @@wikihouse_dimensions[4]) + + puts "Dimensions Updated" + + if args[-1] == 'close' + d.close + else + d.execute_script("display_status(" + "Settings Updated!" + ");") + end + end + end + + + # Cancel and close dialog + dialog.add_action_callback("cancel_settings") { |d, args| + d.close } + + # Set HTML + html_path = Sketchup.find_support_file "settings.html", "Plugins/wikihouse-extension/lib/" + dialog.set_file html_path + dialog.show_modal +# dialog.bring_to_front +# dialog.show + + puts dialog.visible? + + puts "Dialog Loaded" + +end + +#Code: Select all +#myWebDialog.add_action_callback('act_on_form_data') { |wd, params| +# field1 = wd.get_element_value('my_form_field_1') +# # Now do something with it... +#} +# +# +#And in JS: +# +# +#Code: Select all +#$('#mySubmitButton').click(function() { window.location.href = 'skp:act_on_form_data@'; }); +# +#Also make sure to do this after DOM has loaded using $(document).ready(function() {}); + # ------------------------------------------------------------------------------ # Download Dialog # ------------------------------------------------------------------------------ @@ -1664,7 +1786,7 @@ def load_wikihouse_upload if not file_loaded? __FILE__ - WIKIHOUSE_DIR = File.join File.dirname(__FILE__), "wikihouse-assets" + WIKIHOUSE_ASSETS = File.join File.dirname(__FILE__), "wikihouse-assets" # Initialise the data containers. WIKIHOUSE_DOWNLOADS = Hash.new @@ -1679,8 +1801,8 @@ def load_wikihouse_upload end WIKIHOUSE_DOWNLOAD.tooltip = "Find new models to use at #{WikihouseExtension::WIKIHOUSE_TITLE}" - WIKIHOUSE_DOWNLOAD.small_icon = File.join WIKIHOUSE_DIR, "download-16.png" - WIKIHOUSE_DOWNLOAD.large_icon = File.join WIKIHOUSE_DIR, "download.png" + WIKIHOUSE_DOWNLOAD.small_icon = File.join WIKIHOUSE_ASSETS, "download-16.png" + WIKIHOUSE_DOWNLOAD.large_icon = File.join WIKIHOUSE_ASSETS, "download.png" # TODO(tav): Irregardless of these procs, all commands seem to get greyed out # when no models are open -- at least, on OS X. @@ -1693,8 +1815,8 @@ def load_wikihouse_upload end WIKIHOUSE_MAKE.tooltip = "Convert a model of a House into printable components" - WIKIHOUSE_MAKE.small_icon = File.join WIKIHOUSE_DIR, "make-16.png" - WIKIHOUSE_MAKE.large_icon = File.join WIKIHOUSE_DIR, "make.png" + WIKIHOUSE_MAKE.small_icon = File.join WIKIHOUSE_ASSETS, "make-16.png" + WIKIHOUSE_MAKE.large_icon = File.join WIKIHOUSE_ASSETS, "make.png" WIKIHOUSE_MAKE.set_validation_proc { if Sketchup.active_model MF_ENABLED @@ -1708,8 +1830,8 @@ def load_wikihouse_upload end WIKIHOUSE_UPLOAD.tooltip = "Upload and share your model at #{WikihouseExtension::WIKIHOUSE_TITLE}" - WIKIHOUSE_UPLOAD.small_icon = File.join WIKIHOUSE_DIR, "upload-16.png" - WIKIHOUSE_UPLOAD.large_icon = File.join WIKIHOUSE_DIR, "upload.png" + WIKIHOUSE_UPLOAD.small_icon = File.join WIKIHOUSE_ASSETS, "upload-16.png" + WIKIHOUSE_UPLOAD.large_icon = File.join WIKIHOUSE_ASSETS, "upload.png" WIKIHOUSE_UPLOAD.set_validation_proc { if Sketchup.active_model MF_ENABLED @@ -1717,12 +1839,25 @@ def load_wikihouse_upload MF_DISABLED|MF_GRAYED end } + + WIKIHOUSE_SETTINGS = UI::Command.new "Settings..." do + WikihouseExtension::load_wikihouse_settings + end + + WIKIHOUSE_SETTINGS.tooltip = "Change #{WikihouseExtension::WIKIHOUSE_TITLE} settings" + WIKIHOUSE_SETTINGS.small_icon = File.join WIKIHOUSE_ASSETS, "cog-16.png" + WIKIHOUSE_SETTINGS.large_icon = File.join WIKIHOUSE_ASSETS, "cog.png" + WIKIHOUSE_SETTINGS.set_validation_proc { + MF_ENABLED + } + # Register a new toolbar with the commands. WIKIHOUSE_TOOLBAR = UI::Toolbar.new WikihouseExtension::WIKIHOUSE_TITLE WIKIHOUSE_TOOLBAR.add_item WIKIHOUSE_DOWNLOAD WIKIHOUSE_TOOLBAR.add_item WIKIHOUSE_UPLOAD WIKIHOUSE_TOOLBAR.add_item WIKIHOUSE_MAKE + WIKIHOUSE_TOOLBAR.add_item WIKIHOUSE_SETTINGS WIKIHOUSE_TOOLBAR.show # Register a new submenu of the standard Plugins menu with the commands. @@ -1730,6 +1865,7 @@ def load_wikihouse_upload WIKIHOUSE_MENU.add_item WIKIHOUSE_DOWNLOAD WIKIHOUSE_MENU.add_item WIKIHOUSE_UPLOAD WIKIHOUSE_MENU.add_item WIKIHOUSE_MAKE + WIKIHOUSE_MENU.add_item WIKIHOUSE_SETTINGS # Add our custom AppObserver. Sketchup.add_observer WikihouseExtension::WikiHouseAppObserver.new @@ -1738,6 +1874,9 @@ def load_wikihouse_upload if WikihouseExtension::WIKIHOUSE_DEV Sketchup.send_action "showRubyPanel:" + WE = WikihouseExtension + S = WE::settings + def w load "wikihouse.rb" end @@ -1761,6 +1900,7 @@ def sel end + #def test # load "wikihouse.rb" # puts @@ -1773,5 +1913,4 @@ def sel # io.write svg_data # end # "Sheets generated!" -# end -#end +# end \ No newline at end of file From 29d19781d9bcc8c2ef471c02e89a57394d5c4fa5 Mon Sep 17 00:00:00 2001 From: Chris Musselle Date: Sun, 28 Apr 2013 19:45:49 +0100 Subject: [PATCH 17/31] Finished Testing Settings Web Dialogue. --- wikihouse-extension/lib/settings.html | 63 ++++++++-------- wikihouse-extension/lib/settings.js | 23 +++--- wikihouse-extension/wikihouse.rb | 102 +++++++++++--------------- wikihouse_extension_loader.rb | 11 --- 4 files changed, 86 insertions(+), 113 deletions(-) diff --git a/wikihouse-extension/lib/settings.html b/wikihouse-extension/lib/settings.html index 92aafb5..47a769d 100644 --- a/wikihouse-extension/lib/settings.html +++ b/wikihouse-extension/lib/settings.html @@ -1,39 +1,40 @@ - - - + + + -

Wikihouse Options


- -

Sheet Settings


- -Sheet Height: mm
-Sheet Width: mm
- -
-Sheet Margin: mm
- -
-Sheet Padding: mm
- -
-Font Height: mm
- -
-
- - - - - -
-

- -
+

Wikihouse Options

+

Note: All measurements must be specified in milimeters.

+

Sheet Settings

+ Sheet Height: + mm +
Sheet Width: + mm +

Outer dimensions of the plywood sheet used.

+
Sheet Margin: + mm +

This is the outer margin left clear of any parts for the cutting templates generated.

+
Sheet Padding: + mm +

This is the minimum amount of distance between two parts.
+ (Usage still in development)

+
Font Height: + mm +

This is the height of the font used to mark the templates.
+ (Usage still in development)

+ + + + + +

+ diff --git a/wikihouse-extension/lib/settings.js b/wikihouse-extension/lib/settings.js index 14fd600..ecebb31 100644 --- a/wikihouse-extension/lib/settings.js +++ b/wikihouse-extension/lib/settings.js @@ -41,22 +41,23 @@ function recieve_wikihouse_settings(args) { // Update Settings function send_wikihouse_settings(mode) { - var ids = new Array("sheet_height", "sheet_width", "sheet_margin", - "sheet_padding", "font_height"); + + var fields = new Array("sheet_height", "sheet_width", "margin", + "padding", "font_height"); - var value, args; + var idx, value, args; - for (field in ids) { + for (idx in fields) { - value = document.getElementById(field).value - alert(typeof value) + value = eval(document.getElementById(fields[idx]).value) if (typeof value == "number") { // Only update those that are genuine numbers - settings[field] = value + settings[fields[idx]] = value } } + //Convert to String args = JSON.stringify(settings) @@ -81,11 +82,11 @@ function fetch_settings(arg) { window.location.href = 'skp:fetch_settings@' + arg; } +// For debugging function do_stuff() { - alert(settings["sheet_height"] = eval('12')) - alert(typeof settings.sheet_height) - alert(settings["sheet_height"] = eval(234566)) - alert(typeof settings.sheet_height) + alert(typeof settings.padding) +// alert(settings["sheet_height"] = eval('12')) +// alert(settings["sheet_height"] = eval(234566)) } diff --git a/wikihouse-extension/wikihouse.rb b/wikihouse-extension/wikihouse.rb index b3f2321..797614f 100755 --- a/wikihouse-extension/wikihouse.rb +++ b/wikihouse-extension/wikihouse.rb @@ -1175,7 +1175,15 @@ def make_wikihouse(model, interactive) entities = selection end - dimensions = @@wikihouse_dimensions + dimensions = [ + @@wikihouse_settings["sheet_height"], + @@wikihouse_settings["sheet_width"], + @@wikihouse_settings["sheet_inner_height"], + @@wikihouse_settings["sheet_inner_width"], + @@wikihouse_settings["margin"], + @@wikihouse_settings["padding"], + @@wikihouse_settings["font_height"] + ] # Load and parse the entities. if WIKIHOUSE_SHORT_CIRCUIT and $wikloader @@ -1441,35 +1449,20 @@ def wikihouse_process_upload(dialog, model, model_path) # ------------------------------------------------------------------------------ # Configure Dialog # ------------------------------------------------------------------------------ -#def load_wikihouse_settings -# my_dialog = UI::WebDialog.new("Selection Info", false, "Selection Info", 200, 200, 200, 200, true) -# -# # Attach an action callback -# my_dialog.add_action_callback("get_data") do |web_dialog,action_name| -# UI.messagebox("Ruby says: Your javascript has asked for " + action_name.to_s) -# end -# -# # Find and show our html file -# html_path = Sketchup.find_support_file "selectionInfo.html" ,"Plugins/cm" -# my_dialog.set_file(html_path) -# my_dialog.show() -#end -# - def load_wikihouse_settings # Create WebDialog - dialog = UI::WebDialog.new WIKIHOUSE_TITLE, true, "#{WIKIHOUSE_TITLE}-Settings", 480, 640, 150, 150, true + dialog = UI::WebDialog.new WIKIHOUSE_TITLE, true, "#{WIKIHOUSE_TITLE}-Settings", 480, 660, 150, 150, true # Get Current Wikihouse Settings - dialog.add_action_callback("fetch_settings") do |d, args| + dialog.add_action_callback("fetch_settings") { |d, args| if args == "default" # Convert Dimenstions to mm dims = {} - for k, v in DEFAULT_SETTINGS + for k, v in DEFAULT_SETTINGS do dims[k] = v.to_mm end script = "recieve_wikihouse_settings('" + JSON.to_json(dims) + "');" @@ -1479,54 +1472,45 @@ def load_wikihouse_settings # Convert Dimenstions to mm dims = {} - for k, v in @@wikihouse_settings + for k, v in @@wikihouse_settings do dims[k] = v.to_mm end script = "recieve_wikihouse_settings('" + JSON.to_json(dims) + "');" d.execute_script(script) - end - end - - + } + # Set Web Dialog's Callbacks - dialog.add_action_callback("update_settings") do |d, args| + dialog.add_action_callback("update_settings") { |d, args| + + close_flag = false + if args.include? "--close" + close_flag = true + args = args.gsub("--close", "") + end - if args == nil - UI.messagebox("Some arguments are empty!") - else - args = args.split(", ") - if args[0] != "" - @@wikihouse_dimensions[0] = eval(args[0] + ".mm") - end - if args[1] != "" - @@wikihouse_dimensions[1] = eval(args[1] + ".mm") - end - if args[4] != "" - @@wikihouse_dimensions[4] = eval(args[2] + ".mm") - end - if args[5] != "" - @@wikihouse_dimensions[5] = eval(args[3] + ".mm") - end - if args[6] != "" - @@wikihouse_dimensions[6] = eval(args[4] + ".mm") - end - - # Recalculate inner hieghts and widths - @@wikihouse_dimensions[2] = @@wikihouse_dimensions[0] - (2 * @@wikihouse_dimensions[4]) - @@wikihouse_dimensions[3] = @@wikihouse_dimensions[1] - (2 * @@wikihouse_dimensions[4]) +# UI.messagebox("Passed Arguments = #{args}") + + new_settings = JSON.from_json(args) + + for k,v in new_settings do + # Convert mm back to inches + @@wikihouse_settings[k] = v.mm + end + + # Recalculate inner heights and widths + @@wikihouse_settings["sheet_inner_height"] = @@wikihouse_settings["sheet_height"] - (2 * @@wikihouse_settings["margin"]) + @@wikihouse_settings["sheet_inner_width"] = @@wikihouse_settings["sheet_width"] - (2 * @@wikihouse_settings["margin"]) - puts "Dimensions Updated" + puts "Dimensions Updated!" - if args[-1] == 'close' - d.close - else - d.execute_script("display_status(" + "Settings Updated!" + ");") - end - end - end - - + if close_flag == true + d.close + else + d.execute_script("display_status('" + "Settings Updated!" + "');") + end + } + # Cancel and close dialog dialog.add_action_callback("cancel_settings") { |d, args| d.close } @@ -1538,8 +1522,6 @@ def load_wikihouse_settings # dialog.bring_to_front # dialog.show - puts dialog.visible? - puts "Dialog Loaded" end diff --git a/wikihouse_extension_loader.rb b/wikihouse_extension_loader.rb index 5accd69..2aa7f8f 100644 --- a/wikihouse_extension_loader.rb +++ b/wikihouse_extension_loader.rb @@ -59,18 +59,7 @@ module WikihouseExtension wikihouse_sheet_inner_height = wikihouse_sheet_height - (2 * wikihouse_sheet_margin) wikihouse_sheet_inner_width = wikihouse_sheet_width - (2 * wikihouse_sheet_margin) - @@wikihouse_dimensions = [ - wikihouse_sheet_height, - wikihouse_sheet_width, - wikihouse_sheet_inner_height, - wikihouse_sheet_inner_width, - wikihouse_sheet_margin, - wihihouse_panel_padding, - wikihouse_font_height - ] - #(Chris) Plan to eventually store all setting as a hash. - # Duplication exists for now to ensure code compatability. # Store the actual values as length objects (in inches) @@wikihouse_settings = { From 2e53890ca54903064eded096572dc21ced8aa617 Mon Sep 17 00:00:00 2001 From: Chris Musselle Date: Tue, 30 Apr 2013 00:25:53 +0100 Subject: [PATCH 18/31] Moved all Web Dialogue related code to one file. --- wikihouse-extension/lib/WebDialog.rb | 519 ++++++++++++++++++++++++++- 1 file changed, 515 insertions(+), 4 deletions(-) diff --git a/wikihouse-extension/lib/WebDialog.rb b/wikihouse-extension/lib/WebDialog.rb index 1f59114..2063e0f 100644 --- a/wikihouse-extension/lib/WebDialog.rb +++ b/wikihouse-extension/lib/WebDialog.rb @@ -1,9 +1,520 @@ -# Web Dialogue for +# Web Dialogues module WikihouseExtension + + module_function() # Makes all methods defined in the module callable via + #ModuleName.method. Else would have to define a class and mix them into it first. + + # ------------------------------------------------------------------------------ + # Common Callbacks + # ------------------------------------------------------------------------------ - def f - end - + # Download Callback + # ----------------- + def wikihouse_download_callback(dialog, params) + # Exit if the download parameters weren't set. + if params == "" + show_wikihouse_error "Couldn't find the #{WIKIHOUSE_TITLE} model name and url" + return + end + + is_comp, base64_url, blob_url, name = params.split ",", 4 + model = Sketchup.active_model + + # Try and save the model/component directly into the current model. + if model and is_comp == '1' + reply = UI.messagebox "Load this directly into your Google SketchUp model?", MB_YESNOCANCEL + if reply == REPLY_YES + loader = WikiHouseLoader.new name + blob_url = WIKIHOUSE_SERVER + blob_url + model.definitions.load_from_url blob_url, loader + if not loader.error + dialog.close + UI.messagebox "Successfully downloaded #{name}" + component = model.definitions[-1] + if component + model.place_component component + end + return + else + UI.messagebox loader.error + reply = UI.messagebox "Would you like to save the model file instead?", MB_YESNO + if reply == REPLY_NO + return + end + end + elsif reply == REPLY_NO + # Skip through to saving the file directly. + else + return + end + end + + # Otherwise, get the filename to save into. + filename = UI.savepanel "Save Model", WIKIHOUSE_SAVE, "#{name}.skp" + if not filename + show_wikihouse_error "No filename specified to save the #{WIKIHOUSE_TITLE} model. Please try again." + return + end + + # TODO(tav): Ensure that this is atomic and free of thread-related + # concurrency issues. + $WIKIHOUSE_DOWNLOADS_ID += 1 + download_id = $WIKIHOUSE_DOWNLOADS_ID.to_s + + WIKIHOUSE_DOWNLOADS[download_id] = filename + + # Initiate the download. + dialog.execute_script "wikihouse.download('#{download_id}', '#{base64_url}');" + end + + # Save Callback + # ------------- + def wikihouse_save_callback(dialog, params) + errmsg = "Couldn't find the #{WIKIHOUSE_TITLE} model data to save" + + # Exit if the save parameters weren't set. + if download_id == "" + show_wikihouse_error errmsg + return + end + + if not WIKIHOUSE_DOWNLOADS.key? download_id + show_wikihouse_error errmsg + return + end + + filename = WIKIHOUSE_DOWNLOADS[download_id] + WIKIHOUSE_DOWNLOADS.delete download_id + + segment_count = dialog.get_element_value "design-download-data" + dialog.close + + if segment_count == "" + show_wikihouse_error errmsg + return + end + + data = [] + for i in 0...segment_count.to_i + segment = dialog.get_element_value "design-download-data-#{i}" + if segment == "" + show_wikihouse_error errmsg + return + end + data << segment + end + + # Decode the base64-encoded data. + data = data.join('').unpack("m")[0] + if data == "" + show_wikihouse_error errmsg + return + end + + # Save the data to the local file. + File.open(filename, 'wb') do |io| + io.write data + end + + reply = UI.messagebox "Successfully saved #{WIKIHOUSE_TITLE} model. Would you like to open it?", MB_YESNO + if reply == REPLY_YES + if not Sketchup.open_file filename + show_wikihouse_error "Couldn't open #{filename}" + end + end + end + + # Error Callback + # -------------- + def wikihouse_error_callback(dialog, params) + if not WIKIHOUSE_DOWNLOADS.key? download_id + return + end + + filename = WIKIHOUSE_DOWNLOADS[download_id] + WIKIHOUSE_DOWNLOADS.delete download_id + + show_wikihouse_error "Couldn't download #{filename} from #{WIKIHOUSE_TITLE}. Please try again." + end + + # ------------------------------------------------------------------------------ + # Download Web Dialogue + # ------------------------------------------------------------------------------ + def load_wikihouse_download + + # Exit if the computer is not online. + if not Sketchup.is_online + UI.messagebox "You need to be connected to the internet to download #{WIKIHOUSE_TITLE} models." + return + end + + dialog = UI::WebDialog.new WIKIHOUSE_TITLE, true, "#{WIKIHOUSE_TITLE}-Download", 480, 640, 150, 150, true + + dialog.add_action_callback("download") { |dialog, params| + wikihouse_download_callback(dialog, params) + } + + dialog.add_action_callback("save") { |dialog, download_id| + wikihouse_save_callback(dialog, params) + } + + dialog.add_action_callback("error") { |dialog, download_id| + wikihouse_error_callback(dialog, params) + } + + # Set the dialog's url and display it. + dialog.set_url WIKIHOUSE_DOWNLOAD_URL + dialog.show + dialog.show_modal + + end + + # ------------------------------------------------------------------------------ + # Upload Web Dialogue + # ------------------------------------------------------------------------------ + + def load_wikihouse_upload + + # Exit if the computer is not online. + if not Sketchup.is_online + UI.messagebox "You need to be connected to the internet to upload models to #{WIKIHOUSE_TITLE}." + return + end + + model = Sketchup.active_model + + # Exit if a model wasn't available. + if not model + show_wikihouse_error "You need to open a SketchUp model to share" + return + end + + # Initialise an attribute dictionary for custom metadata. + attr = model.attribute_dictionary WIKIHOUSE_TITLE, true + if attr.size == 0 + attr["spec"] = "0.1" + end + + # Exit if it's an unsaved model. + model_path = model.path + if model_path == "" + UI.messagebox "You need to save the model before it can be shared at #{WIKIHOUSE_TITLE}" + return + end + + # Auto-save the model if it has been modified. + if model.modified? + if not model.save model_path + show_wikihouse_error "Couldn't auto-save the model to #{model_path}" + return + end + end + + # Try and infer the model's name. + model_name = model.name + if model_name == "" + model_name = model.title + end + + # Instantiate an upload web dialog. + dialog = UI::WebDialog.new WIKIHOUSE_TITLE, true, "#{WIKIHOUSE_TITLE}-Upload", 480, 640, 150, 150, true + + # Load Callback + # ------------- + # Load default values into the upload form. + dialog.add_action_callback("load") { |dialog, params| + if model_name != "" + if dialog.get_element_value("design-title") == "" + set_dom_value dialog, "design-title", model_name + end + end + if model.description != "" + if dialog.get_element_value("design-description") == "" + set_dom_value dialog, "design-description", model.description + end + end + if Sketchup.version + set_dom_value dialog, "design-sketchup-version", Sketchup.version + end + set_dom_value dialog, "design-plugin-version", WIKIHOUSE_PLUGIN_VERSION + } + + # Process Callback + # -------------- + # Process and prepare the model related data for upload. + dialog.add_action_callback("process") { |dialog, params| + + if File.size(model_path) > 12582912 + reply = UI.messagebox "The model file is larger than 12MB. Would you like to purge unused objects, materials and styles?", MB_OKCANCEL + if reply == REPLY_OK + model.layers.purge_unused + model.styles.purge_unused + model.materials.purge_unused + model.definitions.purge_unused + if not model.save model_path + show_wikihouse_error "Couldn't save the purged model to #{model_path}" + dialog.close + return + end + if File.size(model_path) > 12582912 + UI.messagebox "The model file is still larger than 12MB after purging. Please break up the file into smaller components." + dialog.close + return + end + else + dialog.close + end + end + + # Get the model file data. + model_data = File.open(model_path, 'rb') do |io| + io.read + end + + model_data = [model_data].pack('m') + set_dom_value dialog, "design-model", model_data + + # Capture the current view info. + view = model.active_view + camera = view.camera + eye, target, up = camera.eye, camera.target, camera.up + center = model.bounds.center + + # Get the data for the model's front image. + front_thumbnail = get_wikihouse_thumbnail model, view, "front" + if not front_thumbnail + show_wikihouse_error "Couldn't generate thumbnails for the model: #{model_name}" + dialog.close + return + end + + front_thumbnail = [front_thumbnail].pack('m') + set_dom_value dialog, "design-model-preview", front_thumbnail + + # Rotate the camera and zoom all the way out. + rotate = Geom::Transformation.rotation center, Z_AXIS, 180.degrees + camera.set eye.transform(rotate), center, Z_AXIS + view.zoom_extents + + # Get the data for the model's back image. + back_thumbnail = get_wikihouse_thumbnail model, view, "back" + if not back_thumbnail + camera.set eye, target, up + show_wikihouse_error "Couldn't generate thumbnails for the model: #{model_name}" + dialog.close + return + end + + back_thumbnail = [back_thumbnail].pack('m') + set_dom_value dialog, "design-model-preview-reverse", back_thumbnail + + # Set the camera view back to the original setup. + camera.set eye, target, up + + # Get the generated sheets data. + sheets_data = make_wikihouse model, false + if not sheets_data + svg_data, dxf_data = "", "" + else + svg_data = [sheets_data[0]].pack('m') + dxf_data = [sheets_data[1]].pack('m') + end + + set_dom_value dialog, "design-sheets", dxf_data + set_dom_value dialog, "design-sheets-preview", svg_data + + WIKIHOUSE_UPLOADS[dialog] = 1 + dialog.execute_script "wikihouse.upload();" + } + + # Uploaded Callback + # ----------------- + dialog.add_action_callback "uploaded" do |dialog, params| + if WIKIHOUSE_UPLOADS.key? dialog + WIKIHOUSE_UPLOADS.delete dialog + end + if params == "success" + UI.messagebox "Successfully uploaded #{model_name}" + else + UI.messagebox "Upload to #{WIKIHOUSE_TITLE} failed. Please try again." + end + end + + dialog.add_action_callback "download" do |dialog, params| + wikihouse_download_callback dialog, params + end + + dialog.add_action_callback "save" do |dialog, download_id| + wikihouse_save_callback dialog, download_id + end + + dialog.add_action_callback "error" do |dialog, download_id| + wikihouse_error_callback dialog, download_id + end + + # TODO(tav): There can be a situation where the dialog has been closed, but + # the upload succeeds and the dialog gets called with "uploaded" and brought + # to front. + dialog.set_on_close do + dialog.set_url "about:blank" + if WIKIHOUSE_UPLOADS.key? dialog + show_wikihouse_error "Upload to #{WIKIHOUSE_TITLE} has been aborted" + WIKIHOUSE_UPLOADS.delete dialog + end + end + + dialog.set_url WIKIHOUSE_UPLOAD_URL + dialog.show + dialog.show_modal + + end + + # ------------------------------------------------------------------------------ + # Make Web Dialog + # ------------------------------------------------------------------------------ + + def load_wikihouse_make + + model = Sketchup.active_model + + # Exit if a model wasn't available. + if not model + show_wikihouse_error "You need to open a SketchUp model before it can be fabricated" + return + end + + # Initialise an attribute dictionary for custom metadata. + attr = model.attribute_dictionary WIKIHOUSE_TITLE, true + if attr.size == 0 + attr["spec"] = WIKIHOUSE_EXTENSION.version + end + + # Exit if it's an unsaved model. + model_path = model.path + if model_path == "" + UI.messagebox "You need to save the model before the cutting sheets can be generated" + return + end + + # Try and infer the model's filename. + filename = model.title + if filename == "" + filename = "Untitled" + end + + # Get the model's parent directory and generate the new filenames to save to. + directory = File.dirname(model_path) + svg_filename = File.join(directory, filename + ".svg") + dxf_filename = File.join(directory, filename + ".dxf") + + # Make the cutting sheets for the house! + data = make_wikihouse model, true + if not data + return + end + + svg_data, dxf_data = data + + # Save the SVG data to the file. + File.open(svg_filename, "wb") do |io| + io.write svg_data + end + + # Save the DXF data to the file. + File.open(dxf_filename, "wb") do |io| + io.write dxf_data + end + UI.messagebox "Cutting sheets successfully saved to #{directory}", MB_OK + + if WIKIHOUSE_MAC + dialog = UI::WebDialog.new "Cutting Sheets Preview", true, "#{WIKIHOUSE_TITLE}-Preview", 800, 800, 150, 150, true + dialog.set_file svg_filename + dialog.show + dialog.show_modal + end + + end + + # ------------------------------------------------------------------------------ + # Settings Web Dialogue + # ------------------------------------------------------------------------------ + + def load_wikihouse_settings + + # Create WebDialog + dialog = UI::WebDialog.new WIKIHOUSE_TITLE, true, "#{WIKIHOUSE_TITLE}-Settings", 480, 660, 150, 150, true + + # Get Current Wikihouse Settings + dialog.add_action_callback("fetch_settings") { |d, args| + + if args == "default" + + # Convert Dimenstions to mm + dims = {} + for k, v in DEFAULT_SETTINGS do + dims[k] = v.to_mm + end + script = "recieve_wikihouse_settings('" + JSON.to_json(dims) + "');" + d.execute_script(script) + + elsif args == "current" + + # Convert Dimenstions to mm + dims = {} + for k, v in $wikihouse_settings do + dims[k] = v.to_mm + end + script = "recieve_wikihouse_settings('" + JSON.to_json(dims) + "');" + d.execute_script(script) + end + } + + # Set Web Dialog's Callbacks + dialog.add_action_callback("update_settings") { |d, args| + + close_flag = false + if args.include? "--close" + close_flag = true + args = args.gsub("--close", "") + end + + # UI.messagebox("Passed Arguments = #{args}") + + new_settings = JSON.from_json(args) + + for k,v in new_settings do + # Convert mm back to inches + $wikihouse_settings[k] = v.mm + end + + # Recalculate inner heights and widths + $wikihouse_settings["sheet_inner_height"] = $wikihouse_settings["sheet_height"] - (2 * $wikihouse_settings["margin"]) + $wikihouse_settings["sheet_inner_width"] = $wikihouse_settings["sheet_width"] - (2 * $wikihouse_settings["margin"]) + + puts "Dimensions Updated!" + + if close_flag == true + d.close + else + d.execute_script("display_status('" + "Settings Updated!" + "');") + end + } + + # Cancel and close dialog + dialog.add_action_callback("cancel_settings") { |d, args| + d.close } + + # Set HTML + html_path = Sketchup.find_support_file "settings.html", "Plugins/wikihouse-extension/lib/" + dialog.set_file html_path + dialog.show_modal + # dialog.bring_to_front + # dialog.show + + puts "Dialog Loaded" + + end + end \ No newline at end of file From fcc3edeb245c4f0b825b75290a357b4e6eb5d9c4 Mon Sep 17 00:00:00 2001 From: Chris Musselle Date: Tue, 30 Apr 2013 00:26:35 +0100 Subject: [PATCH 19/31] Make wikihouse_settings a global variable and added sheet_depth parameter. --- wikihouse-extension/lib/settings.html | 11 +- wikihouse-extension/lib/settings.js | 5 +- wikihouse-extension/wikihouse.rb | 569 +------------------------- wikihouse_extension_loader.rb | 30 +- 4 files changed, 49 insertions(+), 566 deletions(-) diff --git a/wikihouse-extension/lib/settings.html b/wikihouse-extension/lib/settings.html index 47a769d..f052f09 100644 --- a/wikihouse-extension/lib/settings.html +++ b/wikihouse-extension/lib/settings.html @@ -8,12 +8,11 @@

Wikihouse Options

Note: All measurements must be specified in milimeters.

Sheet Settings

- Sheet Height: - mm -
Sheet Width: - mm -

Outer dimensions of the plywood sheet used.

-
Sheet Margin: + Sheet Height: mm +          + Sheet Width: mm

+ Sheet Thickness: mm +

Sheet Margin: mm

This is the outer margin left clear of any parts for the cutting templates generated.


Sheet Padding: diff --git a/wikihouse-extension/lib/settings.js b/wikihouse-extension/lib/settings.js index ecebb31..a1aa5c1 100644 --- a/wikihouse-extension/lib/settings.js +++ b/wikihouse-extension/lib/settings.js @@ -33,6 +33,7 @@ function recieve_wikihouse_settings(args) { document.getElementById("sheet_height").value = settings.sheet_height; document.getElementById("sheet_width").value = settings.sheet_width; + document.getElementById("sheet_depth").value = settings.sheet_depth; document.getElementById("margin").value = settings.margin; document.getElementById("padding").value = settings.padding; document.getElementById("font_height").value = settings.font_height; @@ -42,8 +43,8 @@ function recieve_wikihouse_settings(args) { function send_wikihouse_settings(mode) { - var fields = new Array("sheet_height", "sheet_width", "margin", - "padding", "font_height"); + var fields = new Array("sheet_height", "sheet_width", "sheet_depth", + "margin", "padding", "font_height"); var idx, value, args; diff --git a/wikihouse-extension/wikihouse.rb b/wikihouse-extension/wikihouse.rb index 797614f..e4f914b 100755 --- a/wikihouse-extension/wikihouse.rb +++ b/wikihouse-extension/wikihouse.rb @@ -7,7 +7,11 @@ require 'sketchup.rb' -# Using regular require statments don't seem to work with the embedded Ruby in SKetchup +# ------------------ +# Update Path Arrays +# ------------------ + +# Using regular require statments don't seem to work with the embedded Ruby in SketchUp # unless the full path of files is included in the $LOAD_PATH array # Add current working directory to $LOAD_PATH array @@ -19,12 +23,15 @@ $LOAD_PATH.unshift(abs_lib_path) unless $LOAD_PATH.include?(abs_lib_path) require_all(abs_lib_path) + module WikihouseExtension # Top Level Namespace + module_function() # Makes all methods defined in the module callable via + #ModuleName.method. Else would have to define a class and mix them into it first. + # ------------------------------------------------------------------------------ # Layout Engine # ------------------------------------------------------------------------------ - class WikiHouseLayoutEngine attr_accessor :sheets @@ -874,7 +881,7 @@ def initialize(entities, root, dimensions) puts "S2: #{$count_s2}" puts "S3: #{$count_s3}" puts "S4: #{$count_s4}" - + end def visit(group, transform) @@ -885,8 +892,10 @@ def visit(group, transform) groups = @groups # Setup the min/max heights for the depth edge/faces. - min_height = 17.mm - max_height = 19.mm + min_height = $wikihouse_settings["sheet_depth"] - 1.mm + max_height = $wikihouse_settings["sheet_depth"] + 1.mm +# min_height = 17.mm +# max_height = 19.mm # Apply the transformation if one has been set for this group. if group.transformation @@ -1156,9 +1165,7 @@ def purge # ------------------------------------------------------------------------------ # Make This House - # These methods are instance methods of the module 'WikihouseExtension' # ------------------------------------------------------------------------------ - def make_wikihouse(model, interactive) # Isolate the entities to export. @@ -1176,14 +1183,14 @@ def make_wikihouse(model, interactive) end dimensions = [ - @@wikihouse_settings["sheet_height"], - @@wikihouse_settings["sheet_width"], - @@wikihouse_settings["sheet_inner_height"], - @@wikihouse_settings["sheet_inner_width"], - @@wikihouse_settings["margin"], - @@wikihouse_settings["padding"], - @@wikihouse_settings["font_height"] - ] + $wikihouse_settings["sheet_height"], + $wikihouse_settings["sheet_width"], + $wikihouse_settings["sheet_inner_height"], + $wikihouse_settings["sheet_inner_width"], + $wikihouse_settings["margin"], + $wikihouse_settings["padding"], + $wikihouse_settings["font_height"] + ] # Load and parse the entities. if WIKIHOUSE_SHORT_CIRCUIT and $wikloader @@ -1226,537 +1233,6 @@ def make_wikihouse(model, interactive) end - # ------------------------------------------------------------------------------ - # WebDialog Callbacks - # ------------------------------------------------------------------------------ - - def wikihouse_download_callback(dialog, params) - - # Exit if the download parameters weren't set. - if params == "" - show_wikihouse_error "Couldn't find the #{WIKIHOUSE_TITLE} model name and url" - return - end - - is_comp, base64_url, blob_url, name = params.split ",", 4 - model = Sketchup.active_model - - # Try and save the model/component directly into the current model. - if model and is_comp == '1' - reply = UI.messagebox "Load this directly into your Google SketchUp model?", MB_YESNOCANCEL - if reply == REPLY_YES - loader = WikiHouseLoader.new name - blob_url = WIKIHOUSE_SERVER + blob_url - model.definitions.load_from_url blob_url, loader - if not loader.error - dialog.close - UI.messagebox "Successfully downloaded #{name}" - component = model.definitions[-1] - if component - model.place_component component - end - return - else - UI.messagebox loader.error - reply = UI.messagebox "Would you like to save the model file instead?", MB_YESNO - if reply == REPLY_NO - return - end - end - elsif reply == REPLY_NO - # Skip through to saving the file directly. - else - return - end - end - - # Otherwise, get the filename to save into. - filename = UI.savepanel "Save Model", WIKIHOUSE_SAVE, "#{name}.skp" - if not filename - show_wikihouse_error "No filename specified to save the #{WIKIHOUSE_TITLE} model. Please try again." - return - end - - # TODO(tav): Ensure that this is atomic and free of thread-related - # concurrency issues. - $WIKIHOUSE_DOWNLOADS_ID += 1 - download_id = $WIKIHOUSE_DOWNLOADS_ID.to_s - - WIKIHOUSE_DOWNLOADS[download_id] = filename - - # Initiate the download. - dialog.execute_script "wikihouse.download('#{download_id}', '#{base64_url}');" - - end - - def wikihouse_save_callback(dialog, download_id) - - errmsg = "Couldn't find the #{WIKIHOUSE_TITLE} model data to save" - - # Exit if the save parameters weren't set. - if download_id == "" - show_wikihouse_error errmsg - return - end - - if not WIKIHOUSE_DOWNLOADS.key? download_id - show_wikihouse_error errmsg - return - end - - filename = WIKIHOUSE_DOWNLOADS[download_id] - WIKIHOUSE_DOWNLOADS.delete download_id - - segment_count = dialog.get_element_value "design-download-data" - dialog.close - - if segment_count == "" - show_wikihouse_error errmsg - return - end - - data = [] - for i in 0...segment_count.to_i - segment = dialog.get_element_value "design-download-data-#{i}" - if segment == "" - show_wikihouse_error errmsg - return - end - data << segment - end - - # Decode the base64-encoded data. - data = data.join('').unpack("m")[0] - if data == "" - show_wikihouse_error errmsg - return - end - - # Save the data to the local file. - File.open(filename, 'wb') do |io| - io.write data - end - - reply = UI.messagebox "Successfully saved #{WIKIHOUSE_TITLE} model. Would you like to open it?", MB_YESNO - if reply == REPLY_YES - if not Sketchup.open_file filename - show_wikihouse_error "Couldn't open #{filename}" - end - end - - end - - def wikihouse_error_callback(dialog, download_id) - - if not WIKIHOUSE_DOWNLOADS.key? download_id - return - end - - filename = WIKIHOUSE_DOWNLOADS[download_id] - WIKIHOUSE_DOWNLOADS.delete download_id - - show_wikihouse_error "Couldn't download #{filename} from #{WIKIHOUSE_TITLE}. Please try again." - - end - - def wikihouse_process_upload(dialog, model, model_path) - - if File.size(model_path) > 12582912 - reply = UI.messagebox "The model file is larger than 12MB. Would you like to purge unused objects, materials and styles?", MB_OKCANCEL - if reply == REPLY_OK - model.layers.purge_unused - model.styles.purge_unused - model.materials.purge_unused - model.definitions.purge_unused - if not model.save model_path - show_wikihouse_error "Couldn't save the purged model to #{model_path}" - dialog.close - return - end - if File.size(model_path) > 12582912 - UI.messagebox "The model file is still larger than 12MB after purging. Please break up the file into smaller components." - dialog.close - return - end - else - dialog.close - end - end - - # Get the model file data. - model_data = File.open(model_path, 'rb') do |io| - io.read - end - - model_data = [model_data].pack('m') - set_dom_value dialog, "design-model", model_data - - # Capture the current view info. - view = model.active_view - camera = view.camera - eye, target, up = camera.eye, camera.target, camera.up - center = model.bounds.center - - # Get the data for the model's front image. - front_thumbnail = get_wikihouse_thumbnail model, view, "front" - if not front_thumbnail - show_wikihouse_error "Couldn't generate thumbnails for the model: #{model_name}" - dialog.close - return - end - - front_thumbnail = [front_thumbnail].pack('m') - set_dom_value dialog, "design-model-preview", front_thumbnail - - # Rotate the camera and zoom all the way out. - rotate = Geom::Transformation.rotation center, Z_AXIS, 180.degrees - camera.set eye.transform(rotate), center, Z_AXIS - view.zoom_extents - - # Get the data for the model's back image. - back_thumbnail = get_wikihouse_thumbnail model, view, "back" - if not back_thumbnail - camera.set eye, target, up - show_wikihouse_error "Couldn't generate thumbnails for the model: #{model_name}" - dialog.close - return - end - - back_thumbnail = [back_thumbnail].pack('m') - set_dom_value dialog, "design-model-preview-reverse", back_thumbnail - - # Set the camera view back to the original setup. - camera.set eye, target, up - - # Get the generated sheets data. - sheets_data = make_wikihouse model, false - if not sheets_data - svg_data, dxf_data = "", "" - else - svg_data = [sheets_data[0]].pack('m') - dxf_data = [sheets_data[1]].pack('m') - end - - set_dom_value dialog, "design-sheets", dxf_data - set_dom_value dialog, "design-sheets-preview", svg_data - - WIKIHOUSE_UPLOADS[dialog] = 1 - dialog.execute_script "wikihouse.upload();" - - end - - - # ------------------------------------------------------------------------------ - # Configure Dialog - # ------------------------------------------------------------------------------ - -def load_wikihouse_settings - - # Create WebDialog - dialog = UI::WebDialog.new WIKIHOUSE_TITLE, true, "#{WIKIHOUSE_TITLE}-Settings", 480, 660, 150, 150, true - - # Get Current Wikihouse Settings - dialog.add_action_callback("fetch_settings") { |d, args| - - if args == "default" - - # Convert Dimenstions to mm - dims = {} - for k, v in DEFAULT_SETTINGS do - dims[k] = v.to_mm - end - script = "recieve_wikihouse_settings('" + JSON.to_json(dims) + "');" - d.execute_script(script) - - elsif args == "current" - - # Convert Dimenstions to mm - dims = {} - for k, v in @@wikihouse_settings do - dims[k] = v.to_mm - end - script = "recieve_wikihouse_settings('" + JSON.to_json(dims) + "');" - d.execute_script(script) - end - } - - # Set Web Dialog's Callbacks - dialog.add_action_callback("update_settings") { |d, args| - - close_flag = false - if args.include? "--close" - close_flag = true - args = args.gsub("--close", "") - end - -# UI.messagebox("Passed Arguments = #{args}") - - new_settings = JSON.from_json(args) - - for k,v in new_settings do - # Convert mm back to inches - @@wikihouse_settings[k] = v.mm - end - - # Recalculate inner heights and widths - @@wikihouse_settings["sheet_inner_height"] = @@wikihouse_settings["sheet_height"] - (2 * @@wikihouse_settings["margin"]) - @@wikihouse_settings["sheet_inner_width"] = @@wikihouse_settings["sheet_width"] - (2 * @@wikihouse_settings["margin"]) - - puts "Dimensions Updated!" - - if close_flag == true - d.close - else - d.execute_script("display_status('" + "Settings Updated!" + "');") - end - } - - # Cancel and close dialog - dialog.add_action_callback("cancel_settings") { |d, args| - d.close } - - # Set HTML - html_path = Sketchup.find_support_file "settings.html", "Plugins/wikihouse-extension/lib/" - dialog.set_file html_path - dialog.show_modal -# dialog.bring_to_front -# dialog.show - - puts "Dialog Loaded" - -end - -#Code: Select all -#myWebDialog.add_action_callback('act_on_form_data') { |wd, params| -# field1 = wd.get_element_value('my_form_field_1') -# # Now do something with it... -#} -# -# -#And in JS: -# -# -#Code: Select all -#$('#mySubmitButton').click(function() { window.location.href = 'skp:act_on_form_data@'; }); -# -#Also make sure to do this after DOM has loaded using $(document).ready(function() {}); - - # ------------------------------------------------------------------------------ - # Download Dialog - # ------------------------------------------------------------------------------ - - def load_wikihouse_download - - # Exit if the computer is not online. - if not Sketchup.is_online - UI.messagebox "You need to be connected to the internet to download #{WIKIHOUSE_TITLE} models." - return - end - - dialog = UI::WebDialog.new WIKIHOUSE_TITLE, true, "#{WIKIHOUSE_TITLE}-Download", 480, 640, 150, 150, true - - dialog.add_action_callback "download" do |dialog, params| - wikihouse_download_callback dialog, params - end - - dialog.add_action_callback "save" do |dialog, download_id| - wikihouse_save_callback dialog, download_id - end - - dialog.add_action_callback "error" do |dialog, download_id| - wikihouse_error_callback dialog, download_id - end - - # Set the dialog's url and display it. - dialog.set_url WIKIHOUSE_DOWNLOAD_URL - dialog.show - dialog.show_modal - - end - - # ------------------------------------------------------------------------------ - # Make Dialog - # ------------------------------------------------------------------------------ - - def load_wikihouse_make - - model = Sketchup.active_model - - # Exit if a model wasn't available. - if not model - show_wikihouse_error "You need to open a SketchUp model before it can be fabricated" - return - end - - # Initialise an attribute dictionary for custom metadata. - attr = model.attribute_dictionary WIKIHOUSE_TITLE, true - if attr.size == 0 - attr["spec"] = WIKIHOUSE_EXTENSION.version - end - - # Exit if it's an unsaved model. - model_path = model.path - if model_path == "" - UI.messagebox "You need to save the model before the cutting sheets can be generated" - return - end - - # Try and infer the model's filename. - filename = model.title - if filename == "" - filename = "Untitled" - end - - # Get the model's parent directory and generate the new filenames to save to. - directory = File.dirname(model_path) - svg_filename = File.join(directory, filename + ".svg") - dxf_filename = File.join(directory, filename + ".dxf") - - # Make the cutting sheets for the house! - data = make_wikihouse model, true - if not data - return - end - - svg_data, dxf_data = data - - # Save the SVG data to the file. - File.open(svg_filename, "wb") do |io| - io.write svg_data - end - - # Save the DXF data to the file. - File.open(dxf_filename, "wb") do |io| - io.write dxf_data - end - - UI.messagebox "Cutting sheets successfully saved to #{directory}", MB_OK - - if WIKIHOUSE_MAC - dialog = UI::WebDialog.new "Cutting Sheets Preview", true, "#{WIKIHOUSE_TITLE}-Preview", 800, 800, 150, 150, true - dialog.set_file svg_filename - dialog.show - dialog.show_modal - end - - end - - # ------------------------------------------------------------------------------ - # Upload Dialog - # ------------------------------------------------------------------------------ - - def load_wikihouse_upload - - # Exit if the computer is not online. - if not Sketchup.is_online - UI.messagebox "You need to be connected to the internet to upload models to #{WIKIHOUSE_TITLE}." - return - end - - model = Sketchup.active_model - - # Exit if a model wasn't available. - if not model - show_wikihouse_error "You need to open a SketchUp model to share" - return - end - - # Initialise an attribute dictionary for custom metadata. - attr = model.attribute_dictionary WIKIHOUSE_TITLE, true - if attr.size == 0 - attr["spec"] = "0.1" - end - - # Exit if it's an unsaved model. - model_path = model.path - if model_path == "" - UI.messagebox "You need to save the model before it can be shared at #{WIKIHOUSE_TITLE}" - return - end - - # Auto-save the model if it has been modified. - if model.modified? - if not model.save model_path - show_wikihouse_error "Couldn't auto-save the model to #{model_path}" - return - end - end - - # Try and infer the model's name. - model_name = model.name - if model_name == "" - model_name = model.title - end - - # Instantiate an upload web dialog. - dialog = UI::WebDialog.new WIKIHOUSE_TITLE, true, "#{WIKIHOUSE_TITLE}-Upload", 480, 640, 150, 150, true - - # Load default values into the upload form. - dialog.add_action_callback "load" do |dialog, params| - if model_name != "" - if dialog.get_element_value("design-title") == "" - set_dom_value dialog, "design-title", model_name - end - end - if model.description != "" - if dialog.get_element_value("design-description") == "" - set_dom_value dialog, "design-description", model.description - end - end - if Sketchup.version - set_dom_value dialog, "design-sketchup-version", Sketchup.version - end - set_dom_value dialog, "design-plugin-version", WIKIHOUSE_PLUGIN_VERSION - end - - # Process and prepare the model related data for upload. - dialog.add_action_callback "process" do |dialog, params| - wikihouse_process_upload dialog, model, model_path - end - - dialog.add_action_callback "uploaded" do |dialog, params| - if WIKIHOUSE_UPLOADS.key? dialog - WIKIHOUSE_UPLOADS.delete dialog - end - if params == "success" - UI.messagebox "Successfully uploaded #{model_name}" - else - UI.messagebox "Upload to #{WIKIHOUSE_TITLE} failed. Please try again." - end - end - - dialog.add_action_callback "download" do |dialog, params| - wikihouse_download_callback dialog, params - end - - dialog.add_action_callback "save" do |dialog, download_id| - wikihouse_save_callback dialog, download_id - end - - dialog.add_action_callback "error" do |dialog, download_id| - wikihouse_error_callback dialog, download_id - end - - # TODO(tav): There can be a situation where the dialog has been closed, but - # the upload succeeds and the dialog gets called with "uploaded" and brought - # to front. - dialog.set_on_close do - dialog.set_url "about:blank" - if WIKIHOUSE_UPLOADS.key? dialog - show_wikihouse_error "Upload to #{WIKIHOUSE_TITLE} has been aborted" - WIKIHOUSE_UPLOADS.delete dialog - end - end - - dialog.set_url WIKIHOUSE_UPLOAD_URL - dialog.show - dialog.show_modal - - end - - extend self # This makes each instance method defined above callable via the module - # instead of needing to instatiate a class first. - end # ------------------------------------------------------------------------------ @@ -1857,7 +1333,6 @@ def load_wikihouse_upload Sketchup.send_action "showRubyPanel:" WE = WikihouseExtension - S = WE::settings def w load "wikihouse.rb" diff --git a/wikihouse_extension_loader.rb b/wikihouse_extension_loader.rb index 2aa7f8f..6acd0ef 100644 --- a/wikihouse_extension_loader.rb +++ b/wikihouse_extension_loader.rb @@ -53,6 +53,7 @@ module WikihouseExtension # Set Wikihouse Pannel Dimentions wikihouse_sheet_height = 1200.mm wikihouse_sheet_width = 2400.mm + wikihouse_sheet_depth = 18.mm wihihouse_panel_padding = 25.mm / 2 wikihouse_sheet_margin = 15.mm - wihihouse_panel_padding wikihouse_font_height = 30.mm @@ -62,27 +63,34 @@ module WikihouseExtension #(Chris) Plan to eventually store all setting as a hash. # Store the actual values as length objects (in inches) - @@wikihouse_settings = { + $wikihouse_settings = { "sheet_height" => wikihouse_sheet_height, "sheet_inner_height" => wikihouse_sheet_inner_height, "sheet_width" => wikihouse_sheet_width, - "sheet_inner_width" => wikihouse_sheet_inner_width, + "sheet_inner_width" => wikihouse_sheet_inner_width, + "sheet_depth" => wikihouse_sheet_depth, "padding" => wihihouse_panel_padding, "margin" => wikihouse_sheet_margin, "font_height" => wikihouse_font_height, } # Store default values for recall - DEFAULT_SETTINGS = Hash[@@wikihouse_settings] + DEFAULT_SETTINGS = Hash[$wikihouse_settings] - # Get and set methods for wikihouse_settings (so they can be returned via - # referencing the module e.g. WikihouseExtension.settings ) - def self.settings - @@wikihouse_settings - end - def self.settings=(settings) - @@wikihouse_settings = settings - end +# NEEDED IF SETTINGS IS A MODULE/CLASS VARIABLE (currently made it global) +# Note: module variable @@wikihouse_settings is accessable in WikihouseExtension namespace +# e.g. X = @@wikihouse_settings["sheet_height"] but not in any subclasses. +# Therefore use get methods so they can be returned via referencing the module +# e.g. settings = WikihouseExtension.settings +# e.g. X = settings["sheet_height"] +# Or all at once: +# e.g. X = WikihouseExtension.settings["sheet_height"]. +# def self.settings +# $wikihouse_settings +# end +# def self.settings=(settings) +# $wikihouse_settings = settings +# end # Define and Load the wikihouse Extension WIKIHOUSE_EXTENSION = SketchupExtension.new "Wikihouse Plugin Development Version", "wikihouse-extension/wikihouse.rb" From 5a7ecd67c4826803fafac52ab0dc5050a312090a Mon Sep 17 00:00:00 2001 From: Chris Musselle Date: Tue, 30 Apr 2013 00:26:49 +0100 Subject: [PATCH 20/31] Added Changes log file --- changes.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 changes.md diff --git a/changes.md b/changes.md new file mode 100644 index 0000000..ecf5d31 --- /dev/null +++ b/changes.md @@ -0,0 +1,12 @@ +Wikihouse 0.2 Dev: + +List of Changes + ⁃ Wikihouse now loadable as a Sketchup Extension. + ⁃ Split the main Wikihouse.rb file into multiple files of related code located in the `wikihouse_extension/lib/` directory. + ⁃ Wrapped the whole code into a Ruby module called `WikihouseExtension`, thereby protecting against any namespace clashes in the future. + ⁃ `wikihouse_extension_loader.rb` script now contains all the configuration constants such as paths, platform, and run flags which are most likely to be changed between runs. + ⁃ All utility functions are now in the file 'utils.rb' along with any other perminant constants. + ⁃ All output writer classes moved to a file called 'writers.rb' + + +All WebDialoge moved to 'WebDialogue.rb' \ No newline at end of file From 605bc79dfc1ff6564651cb25147fe9034b3a7b37 Mon Sep 17 00:00:00 2001 From: Chris Musselle Date: Tue, 30 Apr 2013 00:27:24 +0100 Subject: [PATCH 21/31] Updated + added alternative manual install --- README.md | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index acdcf0f..53df517 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -This is the WikiHouse extension for use with Google SketchUp. Using it, +This is the WikiHouse extension for use Google SketchUp designed for the [Wikihouse](http://www.wikihouse.cc/) open source construction set. Using it, you can: * Import designs from the WikiHouse library. @@ -7,7 +7,13 @@ you can: * Generate cutting sheets for your house designs. -**Installation** +See the [changes log](changes.md) for a list of recent changes and new features. + +**Current Version: 0.2** + +##Installing + +###Using the Make File 1. Download or clone the repository, then run `make build` to generate the `wikihouse_extension.rbz` file. 2. Open SketchUp and navigate to **Window** > **Preferences** (Microsoft Windows) or **SketchUp** > **Preferences** (Mac OS X). @@ -16,15 +22,21 @@ you can: The plugin will then appear in the list of other extensions, and all necessary files will be copied to your plugins directory. If you ever wish to disable it, simply untick it in the list and restart SketchUp. -On previous versions of SketchUp (version 7 and bellow) to install, simply download the `wikihouse-extension` directory and copy all contents over into your SketchUp `plugins` directory. +###Manually -**License** +With the "Plugins" folder for SketchUp being by default: -All of the code has been released into the [Public Domain]. Do with it -as you please. +* `C:\Program Files\Google\Google SketchUp 8\Plugins` On Windows. +* `/Library/Application Support/Google SketchUp 8/SketchUp/Plugins` On Mac. --- -Enjoy, tav <> +Download or clone repository and manually move the `wikihouse_extension_loader.rb` file +as well as the `wikihouse-extension` folder plus all contents to the "Plugins" folder for SketchUp. +Installation on previous versions of SketchUp (version 7 and bellow) has to be done the manual way. + +##License + +All of the code has been released into the [Public Domain]. Do with it +as you please. [Public Domain]: https://github.com/tav/wikihouse-plugin/raw/master/UNLICENSE From dd4d24a928cc9cc79a9f2187d18a43532cec0954 Mon Sep 17 00:00:00 2001 From: Chris Musselle Date: Tue, 30 Apr 2013 00:43:58 +0100 Subject: [PATCH 22/31] Minor formating edits --- changes.md | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/changes.md b/changes.md index ecf5d31..097c741 100644 --- a/changes.md +++ b/changes.md @@ -1,12 +1,17 @@ -Wikihouse 0.2 Dev: +**Wikihouse 0.2 Dev** -List of Changes - ⁃ Wikihouse now loadable as a Sketchup Extension. - ⁃ Split the main Wikihouse.rb file into multiple files of related code located in the `wikihouse_extension/lib/` directory. - ⁃ Wrapped the whole code into a Ruby module called `WikihouseExtension`, thereby protecting against any namespace clashes in the future. - ⁃ `wikihouse_extension_loader.rb` script now contains all the configuration constants such as paths, platform, and run flags which are most likely to be changed between runs. - ⁃ All utility functions are now in the file 'utils.rb' along with any other perminant constants. - ⁃ All output writer classes moved to a file called 'writers.rb' +New Features: +* Wikihouse now loadable as a Sketchup Extension. +* Settings Menu added to custimise sheet dimentions. -All WebDialoge moved to 'WebDialogue.rb' \ No newline at end of file +List of Changes: + +* Split the main Wikihouse.rb file into multiple files of related code located in the `wikihouse_extension/lib/` directory. +* Wrapped the whole code into a Ruby module called `WikihouseExtension`, thereby protecting against any namespace clashes in the future. +* `wikihouse_extension_loader.rb` script now contains all the configuration constants such as paths, platform, and run flags which are most likely to be changed between runs. +* All utility functions are now in the file `utils.rb` along with any other perminant constants. +* All output writer classes moved to a file called `writers.rb`. +* All WebDialoge moved to `WebDialog.rb`. +* Added a hash **$wikihouse_settings** as a global variale to store all variable configureation/settup data. +* Any code that is not currently functional is in `other.rb`. \ No newline at end of file From f8684a4ef5bff7205b3a578380d459be4e22841c Mon Sep 17 00:00:00 2001 From: Chris Musselle Date: Tue, 30 Apr 2013 00:49:22 +0100 Subject: [PATCH 23/31] Minor markdown edits --- README.md | 4 +++- changes.md | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 53df517..b237ff4 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +# Wikihouse Extension 0.2 Dev + This is the WikiHouse extension for use Google SketchUp designed for the [Wikihouse](http://www.wikihouse.cc/) open source construction set. Using it, you can: @@ -9,7 +11,7 @@ you can: See the [changes log](changes.md) for a list of recent changes and new features. -**Current Version: 0.2** +**Current Version: 0.2 Dev** ##Installing diff --git a/changes.md b/changes.md index 097c741..85ff338 100644 --- a/changes.md +++ b/changes.md @@ -1,4 +1,4 @@ -**Wikihouse 0.2 Dev** +**Wikihouse Extension 0.2 Dev** New Features: From f2fb33f004c792141b6de83eafa41f29d2e0ab54 Mon Sep 17 00:00:00 2001 From: Chris Musselle Date: Tue, 30 Apr 2013 00:53:44 +0100 Subject: [PATCH 24/31] minor edits to markdown --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b237ff4..6b59213 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Wikihouse Extension 0.2 Dev +## Wikihouse Extension This is the WikiHouse extension for use Google SketchUp designed for the [Wikihouse](http://www.wikihouse.cc/) open source construction set. Using it, you can: From afc6090f22aec255d7cbf30f02346707ae022ee2 Mon Sep 17 00:00:00 2001 From: Chris Musselle Date: Sun, 26 May 2013 19:12:48 +0100 Subject: [PATCH 25/31] Fixed error in Web Dialogue save and error callbacks. --- wikihouse-extension/lib/WebDialog.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/wikihouse-extension/lib/WebDialog.rb b/wikihouse-extension/lib/WebDialog.rb index 2063e0f..8a7f75f 100644 --- a/wikihouse-extension/lib/WebDialog.rb +++ b/wikihouse-extension/lib/WebDialog.rb @@ -70,7 +70,7 @@ def wikihouse_download_callback(dialog, params) # Save Callback # ------------- - def wikihouse_save_callback(dialog, params) + def wikihouse_save_callback(dialog, download_id) errmsg = "Couldn't find the #{WIKIHOUSE_TITLE} model data to save" # Exit if the save parameters weren't set. @@ -127,7 +127,7 @@ def wikihouse_save_callback(dialog, params) # Error Callback # -------------- - def wikihouse_error_callback(dialog, params) + def wikihouse_error_callback(dialog, download_id) if not WIKIHOUSE_DOWNLOADS.key? download_id return end @@ -156,11 +156,11 @@ def load_wikihouse_download } dialog.add_action_callback("save") { |dialog, download_id| - wikihouse_save_callback(dialog, params) + wikihouse_save_callback(dialog, download_id) } dialog.add_action_callback("error") { |dialog, download_id| - wikihouse_error_callback(dialog, params) + wikihouse_error_callback(dialog, download_id) } # Set the dialog's url and display it. From 81556e868102bef8910e9ac569d1b30f2dc971b6 Mon Sep 17 00:00:00 2001 From: Chris Musselle Date: Sun, 26 May 2013 19:14:40 +0100 Subject: [PATCH 26/31] Added install_mac_SU8 and install_mac_SU2013 options for make file. These will update all files in necessary plugin folders. Note folders must already exist. --- Makefile | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 4183317..fa718b7 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,9 @@ assets=wikihouse-extension/wikihouse-assets/* libs=wikihouse-extension/lib/* +plugin_dir_2013=${HOME}/Library/Application\ Support/SketchUp\ 2013/SketchUp/Plugins/ +plugin_dir_SU8=/Library/Application\ Support/Google\ SketchUp\ 8/SketchUp/plugins/ + # Store current git revision git-rev=$(git rev-parse --short=8 HEAD) @@ -23,9 +26,16 @@ release: wikihouse_extension.rbz s3put ${wikihouse_bucket}wikihouse_extension-$git-rev.rbz wikihouse_extension.rbz s3put ${wikihouse_bucket}wikihouse_extension.rbz wikihouse_extension.rbz -install_mac: wikihouse_extension_loader.rb wikihouse-extension/wikihouse.rb $(assets) $(libs) +install_mac_SU8: wikihouse_extension_loader.rb wikihouse-extension/wikihouse.rb $(assets) $(libs) + # Copying files to their locations + cp -v wikihouse_extension_loader.rb $(plugin_dir_SU8) + cp -v wikihouse-extension/wikihouse.rb $(plugin_dir_SU8)wikihouse-extension/wikihouse.rb + cp -v $(assets) $(plugin_dir_SU8)wikihouse-extension/wikihouse-assets/ + cp -v $(libs) $(plugin_dir_SU8)wikihouse-extension/lib/ + +install_mac_SU2013: wikihouse_extension_loader.rb wikihouse-extension/wikihouse.rb $(assets) $(libs) # Copying files to their locations - cp -v wikihouse_extension_loader.rb /Library/Application\ Support/Google\ SketchUp\ 8/SketchUp/plugins/wikihouse_extension_loader.rb - cp -v wikihouse-extension/wikihouse.rb /Library/Application\ Support/Google\ SketchUp\ 8/SketchUp/plugins/wikihouse-extension/wikihouse.rb - cp -v $(assets) /Library/Application\ Support/Google\ SketchUp\ 8/SketchUp/plugins/wikihouse-extension/wikihouse-assets/ - cp -v $(libs) /Library/Application\ Support/Google\ SketchUp\ 8/SketchUp/plugins/wikihouse-extension/lib/ \ No newline at end of file + cp -v wikihouse_extension_loader.rb $(plugin_dir_2013) + cp -v wikihouse-extension/wikihouse.rb $(plugin_dir_2013)wikihouse-extension/ + cp -v $(assets) $(plugin_dir_2013)/wikihouse-extension/wikihouse-assets/ + cp -v $(libs) $(plugin_dir_2013)/wikihouse-extension/lib/ \ No newline at end of file From d41c0701f9dc5c806fb3cf845adf07fc537359b0 Mon Sep 17 00:00:00 2001 From: Chris Musselle Date: Sun, 26 May 2013 19:14:53 +0100 Subject: [PATCH 27/31] Minor edits --- changes.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/changes.md b/changes.md index 85ff338..e9c5767 100644 --- a/changes.md +++ b/changes.md @@ -14,4 +14,6 @@ List of Changes: * All output writer classes moved to a file called `writers.rb`. * All WebDialoge moved to `WebDialog.rb`. * Added a hash **$wikihouse_settings** as a global variale to store all variable configureation/settup data. -* Any code that is not currently functional is in `other.rb`. \ No newline at end of file +* Any code that is not currently functional is in `other.rb`. +* Added methods to convert Ruby Hash and Array classes to JSON strings and vice versa. This provides a more flexible bridge between Ruby and JavaScript in the Web Dialogues. +* Added this change log file. From d6d11f41c2e6d2279e076ab49e2461911606fdea Mon Sep 17 00:00:00 2001 From: Chris Musselle Date: Sun, 26 May 2013 19:15:50 +0100 Subject: [PATCH 28/31] Changed JSON to a class instead of module. Seems there is a validation check in SU 2013 that was throwing an error on loading plugin. --- wikihouse-extension/lib/JSON.rb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/wikihouse-extension/lib/JSON.rb b/wikihouse-extension/lib/JSON.rb index 4cd2a58..33d6194 100644 --- a/wikihouse-extension/lib/JSON.rb +++ b/wikihouse-extension/lib/JSON.rb @@ -7,11 +7,13 @@ module WikihouseExtension - module JSON + class JSON - module_function() # Allows Methods to be callable from module + # Redefined JSON to a class instead of a module as this seems to through + # a load error otherwise (things run fine though). +# module_function() # Allows Methods to be callable from module - def from_json(json_string) + def self.from_json(json_string) # split at every even number of unescaped quotes; if it's not a string then replace : and null ruby_string = json_string.split(/(\"(?:.*?[^\\])*?\")/). collect{|s| @@ -24,7 +26,7 @@ def from_json(json_string) {} end - def to_json(obj) + def self.to_json(obj) json_classes = [String, Symbol, Fixnum, Float, Length, Array, Hash, TrueClass, FalseClass, NilClass] # remove non-JSON objects check_value = nil From 37bf2023dc3c4e50da3e4a469c7b9adf7657fae3 Mon Sep 17 00:00:00 2001 From: Chris Musselle Date: Sun, 26 May 2013 22:18:25 +0100 Subject: [PATCH 29/31] Located the bug in downloading via web dialogues --- wikihouse-extension/lib/WebDialog.rb | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/wikihouse-extension/lib/WebDialog.rb b/wikihouse-extension/lib/WebDialog.rb index 8a7f75f..d1f32d8 100644 --- a/wikihouse-extension/lib/WebDialog.rb +++ b/wikihouse-extension/lib/WebDialog.rb @@ -63,7 +63,7 @@ def wikihouse_download_callback(dialog, params) download_id = $WIKIHOUSE_DOWNLOADS_ID.to_s WIKIHOUSE_DOWNLOADS[download_id] = filename - + # Initiate the download. dialog.execute_script "wikihouse.download('#{download_id}', '#{base64_url}');" end @@ -87,11 +87,16 @@ def wikihouse_save_callback(dialog, download_id) filename = WIKIHOUSE_DOWNLOADS[download_id] WIKIHOUSE_DOWNLOADS.delete download_id + + # TODO:(Chris) The Wikihouse Model Loading currently fails here as the + # segment_count value returned is the empty string "" segment_count = dialog.get_element_value "design-download-data" + dialog.close if segment_count == "" show_wikihouse_error errmsg + puts "Segment count variable was not parsed correctly" return end @@ -109,6 +114,7 @@ def wikihouse_save_callback(dialog, download_id) data = data.join('').unpack("m")[0] if data == "" show_wikihouse_error errmsg + puts "Triger 5" return end @@ -156,6 +162,9 @@ def load_wikihouse_download } dialog.add_action_callback("save") { |dialog, download_id| + + puts download_id + wikihouse_save_callback(dialog, download_id) } From 6a97356ea1b6149b1aa9db075c1d939291f4861f Mon Sep 17 00:00:00 2001 From: Chris Musselle Date: Mon, 27 May 2013 17:38:22 +0100 Subject: [PATCH 30/31] Minor comment additions to WikihouseLayoutEngine --- wikihouse-extension/wikihouse.rb | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/wikihouse-extension/wikihouse.rb b/wikihouse-extension/wikihouse.rb index e4f914b..c0f6a50 100755 --- a/wikihouse-extension/wikihouse.rb +++ b/wikihouse-extension/wikihouse.rb @@ -158,6 +158,7 @@ def initialize(panels, root, dimensions) loop_count += 1 panel_data = panels[idx] + if not panel_data break end @@ -539,7 +540,7 @@ def initialize(root, face, transform, labels, limits) intersections = [] outer_loop = true - # Go through the various loops calculating centroids and intersection points + # Go through the various loops calculating centroids, face area, and intersection points # of potential curves. loops.each do |loop| idx = 0 @@ -698,15 +699,18 @@ def initialize(root, face, transform, labels, limits) end # Find the centroid. + # uses the first area and centoid coordinates as these should be for the outer loop. + # Then subtracts those of any inner loops. @shell_area = surface_area = areas.shift topx = surface_area * cxs.shift topy = surface_area * cys.shift - for i in 0...areas.length + for i in 0...areas.length # Run through rest of areas, subtracting thier centroids * areas area = areas[i] topx -= area * cxs[i] topy -= area * cys[i] surface_area -= area end + # Final centorid cx = topx / surface_area cy = topy / surface_area centroid = transform * [cx, cy, 0] @@ -1197,6 +1201,7 @@ def make_wikihouse(model, interactive) loader = $wikloader else loader = WikiHouseEntities.new entities, root, dimensions + $wikloader = loader if WIKIHOUSE_SHORT_CIRCUIT $wikloader = loader end From cb485d8aa43393c559089a778de461c62ea68152 Mon Sep 17 00:00:00 2001 From: Chris Musselle Date: Mon, 27 May 2013 23:45:57 +0100 Subject: [PATCH 31/31] Lowered threshold on face area to include. Now able to detect C parts in joins. --- wikihouse-extension/wikihouse.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wikihouse-extension/wikihouse.rb b/wikihouse-extension/wikihouse.rb index c0f6a50..f9ad30d 100755 --- a/wikihouse-extension/wikihouse.rb +++ b/wikihouse-extension/wikihouse.rb @@ -307,7 +307,7 @@ def initialize(panels, root, dimensions) if match break end - end + end # While 1 if match break end @@ -319,7 +319,7 @@ def initialize(panels, root, dimensions) if match break end - end + end # placed_o.each end if match @@ -1005,7 +1005,7 @@ def visit_faces(faces, transform) face1 = faces.pop area1 = face1.area transform # Ignore small faces. - if area1 < 5 # (Chris) This may be why the small C shaped parts in Joins are being ignored. + if area1 < 2 # (Chris) This may be why the small C shaped parts in Joins are being ignored. next end idx = -1