triangular 0.0.2 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/workflows/build.yml +30 -0
- data/.gitignore +3 -0
- data/.rspec +2 -0
- data/.rubocop.yml +31 -0
- data/Gemfile +3 -1
- data/Gemfile.lock +51 -12
- data/MIT-LICENSE +1 -1
- data/README.md +98 -0
- data/examples/slice_example.rb +6 -4
- data/lib/triangular/facet.rb +32 -42
- data/lib/triangular/line.rb +42 -16
- data/lib/triangular/point.rb +15 -15
- data/lib/triangular/polyline.rb +13 -12
- data/lib/triangular/ray.rb +41 -0
- data/lib/triangular/solid.rb +34 -36
- data/lib/triangular/units.rb +18 -14
- data/lib/triangular/vector.rb +8 -1
- data/lib/triangular/version.rb +3 -1
- data/lib/triangular/vertex.rb +17 -16
- data/lib/triangular.rb +4 -1
- data/spec/benchmark/benchmark_spec.rb +23 -0
- data/spec/profile/profile_spec.rb +20 -0
- data/spec/spec_helper.rb +17 -3
- data/spec/triangular/facet_spec.rb +235 -0
- data/spec/triangular/line_spec.rb +285 -0
- data/spec/triangular/point_spec.rb +108 -0
- data/spec/triangular/polyline_spec.rb +22 -0
- data/spec/triangular/ray_spec.rb +63 -0
- data/spec/{solid_spec.rb → triangular/solid_spec.rb} +71 -70
- data/spec/triangular/triangular_spec.rb +24 -0
- data/spec/triangular/units_spec.rb +77 -0
- data/spec/triangular/vector_spec.rb +23 -0
- data/spec/triangular/vertex_spec.rb +46 -0
- data/triangular.gemspec +22 -18
- metadata +114 -65
- data/README.rdoc +0 -64
- data/Rakefile +0 -11
- data/spec/facet_spec.rb +0 -233
- data/spec/line_spec.rb +0 -108
- data/spec/point_spec.rb +0 -88
- data/spec/polyline_spec.rb +0 -20
- data/spec/triangular_spec.rb +0 -22
- data/spec/units_spec.rb +0 -75
- data/spec/vertex_spec.rb +0 -44
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 8ad12df593fb18caf82b87ed4f58c789750f1aa0d17b902a3720032a6e3fb344
|
4
|
+
data.tar.gz: cadec3a31d3af63f89af1e51f398d4af3a4ca0bb78a045ba2426a8d6d13846e4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9980d58a82095bc137e524c1778cb519d22578e28e785b07e6c1207e6d336aa5cf6eddcfba2fe57d3a788555614d5634e5ab29c699ee7a5851451b0d62eb7f19
|
7
|
+
data.tar.gz: fc3b770e3a01cc5fb4f4a4c055896a7c9fb6f4129741bc1c34bd7bc2d75fdca200bab16f091af2ed751d52a285c6590695f907c2fa277f8f113ba53257e8d4fe
|
@@ -0,0 +1,30 @@
|
|
1
|
+
name: build
|
2
|
+
|
3
|
+
on: [push, pull_request]
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
build:
|
7
|
+
runs-on: ubuntu-latest
|
8
|
+
|
9
|
+
steps:
|
10
|
+
- uses: actions/checkout@v1
|
11
|
+
|
12
|
+
- name: Install Ruby (2.6)
|
13
|
+
uses: actions/setup-ruby@v1
|
14
|
+
with:
|
15
|
+
ruby-version: 2.6.x
|
16
|
+
|
17
|
+
- name: Setup Code Climate test-reporter
|
18
|
+
run: |
|
19
|
+
curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
|
20
|
+
chmod +x ./cc-test-reporter
|
21
|
+
./cc-test-reporter before-build
|
22
|
+
- name: Build and test with RSpec
|
23
|
+
run: |
|
24
|
+
gem install bundler
|
25
|
+
bundle install --jobs 4 --retry 3
|
26
|
+
bundle exec rspec
|
27
|
+
- name: Publish code coverage
|
28
|
+
run: |
|
29
|
+
export GIT_BRANCH="${GITHUB_REF/refs\/heads\//}"
|
30
|
+
./cc-test-reporter after-build --id ${{secrets.CC_TEST_REPORTER_ID}} --coverage-input-type simplecov
|
data/.gitignore
CHANGED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
Metrics/BlockLength:
|
2
|
+
IgnoredMethods: ['describe', 'context']
|
3
|
+
|
4
|
+
Style/SymbolProc:
|
5
|
+
IgnoredMethods: ['configure']
|
6
|
+
|
7
|
+
AllCops:
|
8
|
+
SuggestExtensions: false
|
9
|
+
NewCops: enable
|
10
|
+
|
11
|
+
Style/Documentation:
|
12
|
+
Enabled: false
|
13
|
+
|
14
|
+
Naming/MethodParameterName:
|
15
|
+
AllowedNames: ['x', 'y', 'z']
|
16
|
+
|
17
|
+
Lint/BinaryOperatorWithIdenticalOperands:
|
18
|
+
Enabled: false
|
19
|
+
|
20
|
+
Layout/EmptyLineAfterGuardClause:
|
21
|
+
Enabled: false
|
22
|
+
|
23
|
+
Metrics/AbcSize:
|
24
|
+
Max: 20
|
25
|
+
CountRepeatedAttributes: false
|
26
|
+
|
27
|
+
Metrics/MethodLength:
|
28
|
+
Max: 15
|
29
|
+
|
30
|
+
Metrics/CyclomaticComplexity:
|
31
|
+
Max: 10
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,25 +1,64 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
triangular (0.0
|
4
|
+
triangular (0.1.0)
|
5
5
|
|
6
6
|
GEM
|
7
|
-
remote:
|
7
|
+
remote: https://rubygems.org/
|
8
8
|
specs:
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
9
|
+
ast (2.4.2)
|
10
|
+
diff-lcs (1.3)
|
11
|
+
docile (1.3.5)
|
12
|
+
json (2.6.1)
|
13
|
+
parallel (1.20.1)
|
14
|
+
parser (3.1.0.0)
|
15
|
+
ast (~> 2.4.1)
|
16
|
+
rainbow (3.0.0)
|
17
|
+
regexp_parser (2.2.0)
|
18
|
+
rexml (3.2.5)
|
19
|
+
rspec (3.7.0)
|
20
|
+
rspec-core (~> 3.7.0)
|
21
|
+
rspec-expectations (~> 3.7.0)
|
22
|
+
rspec-mocks (~> 3.7.0)
|
23
|
+
rspec-core (3.7.0)
|
24
|
+
rspec-support (~> 3.7.0)
|
25
|
+
rspec-expectations (3.7.0)
|
26
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
27
|
+
rspec-support (~> 3.7.0)
|
28
|
+
rspec-mocks (3.7.0)
|
29
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
30
|
+
rspec-support (~> 3.7.0)
|
31
|
+
rspec-support (3.7.0)
|
32
|
+
rubocop (1.12.1)
|
33
|
+
parallel (~> 1.10)
|
34
|
+
parser (>= 3.0.0.0)
|
35
|
+
rainbow (>= 2.2.2, < 4.0)
|
36
|
+
regexp_parser (>= 1.8, < 3.0)
|
37
|
+
rexml
|
38
|
+
rubocop-ast (>= 1.2.0, < 2.0)
|
39
|
+
ruby-progressbar (~> 1.7)
|
40
|
+
unicode-display_width (>= 1.4.0, < 3.0)
|
41
|
+
rubocop-ast (1.4.1)
|
42
|
+
parser (>= 2.7.1.5)
|
43
|
+
ruby-prof (1.4.2)
|
44
|
+
ruby-progressbar (1.11.0)
|
45
|
+
simplecov (0.17.1)
|
46
|
+
docile (~> 1.1)
|
47
|
+
json (>= 1.8, < 3)
|
48
|
+
simplecov-html (~> 0.10.0)
|
49
|
+
simplecov-html (0.10.2)
|
50
|
+
unicode-display_width (2.1.0)
|
18
51
|
|
19
52
|
PLATFORMS
|
20
53
|
ruby
|
21
54
|
|
22
55
|
DEPENDENCIES
|
23
56
|
bundler (>= 1.0.0)
|
24
|
-
rspec (~>
|
57
|
+
rspec (~> 3)
|
58
|
+
rubocop
|
59
|
+
ruby-prof
|
60
|
+
simplecov (= 0.17.1)
|
25
61
|
triangular!
|
62
|
+
|
63
|
+
BUNDLED WITH
|
64
|
+
1.17.3
|
data/MIT-LICENSE
CHANGED
data/README.md
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
[![Ruby Style Guide](https://img.shields.io/badge/code_style-rubocop-brightgreen.svg)](https://github.com/rubocop/rubocop)
|
2
|
+
[![Actions Status](https://github.com/aarongough/triangular/actions/workflows/build.yml/badge.svg)](https://github.com/aarongough/triangular/actions/workflows/build.yml)
|
3
|
+
[![Maintainability](https://api.codeclimate.com/v1/badges/e64ecb8e1c703a010077/maintainability)](https://codeclimate.com/github/aarongough/triangular/maintainability)
|
4
|
+
[![Test Coverage](https://api.codeclimate.com/v1/badges/e64ecb8e1c703a010077/test_coverage)](https://codeclimate.com/github/aarongough/triangular/test_coverage)
|
5
|
+
[![Gem Version](https://badge.fury.io/rb/triangular.svg)](https://badge.fury.io/rb/triangular)
|
6
|
+
|
7
|
+
# Triangular:
|
8
|
+
|
9
|
+
Triangular is an easy-to-use Ruby library for reading, writing and manipulating Stereolithography (STL) files.
|
10
|
+
|
11
|
+
The main purpose of Triangular is to enable its users to quickly create new software for Rapid Prototyping and Personal Manufacturing applications. Triangular has many of the core functions needed in order to generate toolpaths for 3D printers and CNC Mills/Routers.
|
12
|
+
|
13
|
+
### Usage:
|
14
|
+
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
require "rubygems"
|
18
|
+
require "triangular"
|
19
|
+
|
20
|
+
# Open and parse an STL file
|
21
|
+
solid = Triangular.parse_file("test.stl")
|
22
|
+
|
23
|
+
# Set the units of measurement for the resulting solid to inches
|
24
|
+
solid.units = :inches
|
25
|
+
|
26
|
+
# Move the solid so that all of it's coordinates are in positive space (ie: greater than 0)
|
27
|
+
solid.align_to_origin!
|
28
|
+
|
29
|
+
# Get the bounding box of the solid
|
30
|
+
bounds = solid.bounds
|
31
|
+
|
32
|
+
# Create a section plane ('slice') through the solid on the XY plane at a Z height of 0.7
|
33
|
+
slice = solid.slice_at_z(0.7)
|
34
|
+
|
35
|
+
# Open a file for SVG output
|
36
|
+
File.open("slice.svg", "w+") do |file|
|
37
|
+
|
38
|
+
# Output the slice as an SVG document (correctly scaled according to the solid's units)
|
39
|
+
file.puts slice.to_svg(bounds[1].x, bounds[1].y, solid.units)
|
40
|
+
end
|
41
|
+
```
|
42
|
+
|
43
|
+
### Installation:
|
44
|
+
|
45
|
+
Add Triangular to your Gemfile:
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
gem 'triangular', '~>0.1.0'
|
49
|
+
```
|
50
|
+
And then execute:
|
51
|
+
```
|
52
|
+
bundle install
|
53
|
+
```
|
54
|
+
Or install it manually by entering the following on your command line:
|
55
|
+
```
|
56
|
+
gem install triangular
|
57
|
+
```
|
58
|
+
|
59
|
+
### Performance:
|
60
|
+
|
61
|
+
At the moment Triangular has not been optimized at all. The parser is a relatively naive one that was designed to be easy to read rather than performant. Once the feature-set of Triangular has stabilized I will be doing a pass over it in order to make it fast enough for production use. Right now it could definitely be improved.
|
62
|
+
|
63
|
+
For example here is some information about run-times when processing a 51Mb STL file:
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
solid = Triangular.parse("big_file.stl")
|
67
|
+
# 65 seconds
|
68
|
+
|
69
|
+
solid.align_to_origin!
|
70
|
+
# 8 seconds
|
71
|
+
|
72
|
+
solid.slice_at_z(1.0)
|
73
|
+
# 2 seconds
|
74
|
+
```
|
75
|
+
|
76
|
+
### Development:
|
77
|
+
|
78
|
+
To get setup for local development of Triangular please run the following steps:
|
79
|
+
|
80
|
+
```
|
81
|
+
git clone git@github.com:aarongough/triangular.git
|
82
|
+
cd triangular
|
83
|
+
bundle install
|
84
|
+
```
|
85
|
+
|
86
|
+
Then run the specs to make sure everything is working!
|
87
|
+
```
|
88
|
+
bundle exec rspec
|
89
|
+
```
|
90
|
+
|
91
|
+
### Author & Credits:
|
92
|
+
|
93
|
+
Author: [Aaron Gough](mailto:aaron@aarongough.com)
|
94
|
+
|
95
|
+
Special thanks go out to [Alkas Baybas](https://github.com/abaybas) for lending me his massive brain!
|
96
|
+
|
97
|
+
Copyright © 2022 [Aaron Gough](http://aarongough.com/) ([http://aarongough.com](http://aarongough.com/)), released under the MIT license
|
98
|
+
|
data/examples/slice_example.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'triangular'
|
3
5
|
|
4
6
|
solid = Triangular.parse_file("#{File.dirname(__FILE__)}/example_files/y-axis-spacer.stl")
|
5
7
|
solid.units = :inches
|
@@ -9,6 +11,6 @@ bounds = solid.get_bounds
|
|
9
11
|
|
10
12
|
polyline = solid.slice_at_z(0.7)
|
11
13
|
|
12
|
-
File.open(File.expand_path(
|
14
|
+
File.open(File.expand_path('~/Desktop/slice.svg'), 'w+') do |file|
|
13
15
|
file.puts polyline.to_svg(bounds[1].x, bounds[1].y, solid.units)
|
14
|
-
end
|
16
|
+
end
|
data/lib/triangular/facet.rb
CHANGED
@@ -1,25 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Triangular
|
2
4
|
class Facet
|
3
|
-
|
4
5
|
attr_accessor :normal, :vertices
|
5
|
-
|
6
|
+
|
6
7
|
def initialize(normal = nil, *args)
|
7
8
|
@normal = normal
|
8
9
|
@vertices = args
|
9
10
|
end
|
10
|
-
|
11
|
+
|
11
12
|
def to_s
|
12
|
-
output = "facet normal #{@normal
|
13
|
+
output = "facet normal #{@normal}\n"
|
13
14
|
output += "outer loop\n"
|
14
15
|
@vertices.each do |vertex|
|
15
|
-
output += vertex
|
16
|
+
output += "#{vertex}\n"
|
16
17
|
end
|
17
18
|
output += "endloop\n"
|
18
19
|
output += "endfacet\n"
|
19
|
-
|
20
|
+
|
20
21
|
output
|
21
22
|
end
|
22
|
-
|
23
|
+
|
23
24
|
def lines
|
24
25
|
[
|
25
26
|
Line.new(@vertices[0], @vertices[1]),
|
@@ -27,51 +28,40 @@ module Triangular
|
|
27
28
|
Line.new(@vertices[2], @vertices[0])
|
28
29
|
]
|
29
30
|
end
|
30
|
-
|
31
|
+
|
31
32
|
def intersection_at_z(z_plane)
|
32
|
-
return nil if @vertices.count{|vertex| vertex.z == z_plane} > 2
|
33
|
-
|
34
|
-
intersection_points =
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
nil
|
42
|
-
elsif intersection_points.count == 2
|
43
|
-
Line.new(intersection_points[0], intersection_points[1])
|
44
|
-
end
|
33
|
+
return nil if @vertices.count { |vertex| vertex.z == z_plane } > 2
|
34
|
+
|
35
|
+
intersection_points = lines.map do |line|
|
36
|
+
line.intersection_at_z(z_plane)
|
37
|
+
end.compact
|
38
|
+
|
39
|
+
return Line.new(intersection_points[0], intersection_points[1]) if intersection_points.count == 2
|
40
|
+
|
41
|
+
nil
|
45
42
|
end
|
46
|
-
|
43
|
+
|
47
44
|
def translate!(x, y, z)
|
48
45
|
@vertices.each do |vertex|
|
49
46
|
vertex.translate!(x, y, z)
|
50
47
|
end
|
51
|
-
end
|
52
|
-
|
48
|
+
end
|
49
|
+
|
53
50
|
def self.parse(string)
|
54
51
|
facets = []
|
55
|
-
|
56
|
-
string.scan(
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
facet.normal = Vector.parse(match_data[0])
|
64
|
-
|
65
|
-
facets << facet
|
66
|
-
end
|
67
|
-
|
68
|
-
if facets.length == 1
|
69
|
-
facets.first
|
70
|
-
else
|
71
|
-
facets
|
52
|
+
|
53
|
+
string.scan(pattern) do |match_data|
|
54
|
+
facets << Facet.new(
|
55
|
+
Vector.parse(match_data[0]), # Normal
|
56
|
+
Vertex.parse(match_data[4]), # Vertex 1
|
57
|
+
Vertex.parse(match_data[9]), # Vertex 2
|
58
|
+
Vertex.parse(match_data[14]) # Vertex 3
|
59
|
+
)
|
72
60
|
end
|
61
|
+
|
62
|
+
facets.length == 1 ? facets.first : facets
|
73
63
|
end
|
74
|
-
|
64
|
+
|
75
65
|
def self.pattern
|
76
66
|
/
|
77
67
|
\s* facet\snormal\s (?<normal> #{Point.pattern})\s
|
data/lib/triangular/line.rb
CHANGED
@@ -1,36 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Triangular
|
2
4
|
class Line
|
3
|
-
|
4
5
|
attr_accessor :start, :end
|
5
|
-
|
6
|
+
|
6
7
|
def initialize(line_start, line_end)
|
7
8
|
@start = line_start
|
8
9
|
@end = line_end
|
9
10
|
end
|
10
|
-
|
11
|
+
|
11
12
|
def ==(other)
|
12
13
|
return false unless other.is_a?(Line)
|
13
|
-
|
14
|
+
|
15
|
+
start == other.start && self.end == other.end
|
14
16
|
end
|
15
|
-
|
17
|
+
|
18
|
+
def intersects_x?(x_plane)
|
19
|
+
(@start.x >= x_plane && @end.x <= x_plane) || (@start.x <= x_plane && @end.x >= x_plane)
|
20
|
+
end
|
21
|
+
|
22
|
+
def intersection_at_x(x_plane)
|
23
|
+
return nil unless intersects_x?(x_plane)
|
24
|
+
return nil if @start.x == x_plane && @end.x == x_plane
|
25
|
+
|
26
|
+
y_intersect = (@end.y - @start.y) / (@end.x - @start.x) * (x_plane - @start.x) + @start.y
|
27
|
+
z_intersect = (@end.z - @start.z) / (@end.x - @start.x) * (x_plane - @start.x) + @start.z
|
28
|
+
|
29
|
+
Point.new(x_plane, y_intersect, z_intersect)
|
30
|
+
end
|
31
|
+
|
32
|
+
def intersects_y?(y_plane)
|
33
|
+
(@start.y >= y_plane && @end.y <= y_plane) || (@start.y <= y_plane && @end.y >= y_plane)
|
34
|
+
end
|
35
|
+
|
36
|
+
def intersection_at_y(y_plane)
|
37
|
+
return nil unless intersects_y?(y_plane)
|
38
|
+
return nil if @start.y == y_plane && @end.y == y_plane
|
39
|
+
|
40
|
+
x_intersect = (@end.x - @start.x) / (@end.y - @start.y) * (y_plane - @start.y) + @start.x
|
41
|
+
z_intersect = (@end.z - @start.z) / (@end.y - @start.y) * (y_plane - @start.y) + @start.z
|
42
|
+
|
43
|
+
Point.new(x_intersect, y_plane, z_intersect)
|
44
|
+
end
|
45
|
+
|
16
46
|
def intersects_z?(z_plane)
|
17
|
-
|
18
|
-
true
|
19
|
-
else
|
20
|
-
false
|
21
|
-
end
|
47
|
+
(@start.z >= z_plane && @end.z <= z_plane) || (@start.z <= z_plane && @end.z >= z_plane)
|
22
48
|
end
|
23
|
-
|
49
|
+
|
24
50
|
def intersection_at_z(z_plane)
|
25
|
-
return nil
|
26
|
-
|
27
|
-
|
51
|
+
return nil unless intersects_z?(z_plane)
|
52
|
+
return nil if @start.z == z_plane && @end.z == z_plane
|
53
|
+
|
28
54
|
x_intersect = (@end.x - @start.x) / (@end.z - @start.z) * (z_plane - @start.z) + @start.x
|
29
55
|
y_intersect = (@end.y - @start.y) / (@end.z - @start.z) * (z_plane - @start.z) + @start.y
|
30
|
-
|
56
|
+
|
31
57
|
Point.new(x_intersect, y_intersect, z_plane)
|
32
58
|
end
|
33
|
-
|
59
|
+
|
34
60
|
def to_svg_path(units)
|
35
61
|
"<path d=\"M #{@start.x} #{@start.y} L #{@end.x} #{@end.y}\" fill=\"none\" stroke=\"black\" stroke-width=\"#{Units.stroke_width(units)}\" />"
|
36
62
|
end
|
data/lib/triangular/point.rb
CHANGED
@@ -1,39 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Triangular
|
2
4
|
class Point
|
3
|
-
|
4
5
|
attr_accessor :x, :y, :z
|
5
|
-
|
6
|
+
|
6
7
|
def initialize(x, y, z)
|
7
8
|
@x = x
|
8
9
|
@y = y
|
9
|
-
@z = z
|
10
|
+
@z = z
|
10
11
|
end
|
11
|
-
|
12
|
+
|
12
13
|
def to_s
|
13
14
|
"#{@x.to_f} #{@y.to_f} #{@z.to_f}"
|
14
15
|
end
|
15
|
-
|
16
|
+
|
16
17
|
def translate!(x, y, z)
|
17
18
|
@x += x
|
18
19
|
@y += y
|
19
20
|
@z += z
|
20
21
|
end
|
21
|
-
|
22
|
+
|
22
23
|
def ==(other)
|
23
24
|
return false unless other.is_a?(Point)
|
24
|
-
|
25
|
+
|
26
|
+
x == other.x && y == other.y && z == other.z
|
25
27
|
end
|
26
|
-
|
28
|
+
|
27
29
|
def self.parse(string)
|
28
|
-
string.strip
|
29
|
-
|
30
|
-
|
31
|
-
self.new(match_data[:x].to_f, match_data[:y].to_f, match_data[:z].to_f)
|
30
|
+
match_data = string.strip.match(pattern)
|
31
|
+
|
32
|
+
new(match_data[:x].to_f, match_data[:y].to_f, match_data[:z].to_f)
|
32
33
|
end
|
33
|
-
|
34
|
+
|
34
35
|
def self.pattern
|
35
|
-
/(?<x>-?\d
|
36
|
+
/(?<x>-?\d+(.\d+(e-?\d+)?)?)\s(?<y>-?\d+(.\d+(e-?\d+)?)?)\s(?<z>-?\d+(.\d+(e-?\d+)?)?)/
|
36
37
|
end
|
37
|
-
|
38
38
|
end
|
39
39
|
end
|
data/lib/triangular/polyline.rb
CHANGED
@@ -1,24 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Triangular
|
2
4
|
class Polyline
|
3
|
-
|
4
5
|
attr_accessor :lines
|
5
|
-
|
6
|
+
|
6
7
|
def initialize(lines)
|
7
8
|
@lines = lines
|
8
9
|
end
|
9
|
-
|
10
|
+
|
10
11
|
def to_svg(width, height, units, x_offset = 0, y_offset = 0)
|
11
|
-
output
|
12
|
-
output
|
13
|
-
output
|
14
|
-
output
|
15
|
-
|
12
|
+
output = "<?xml version=\"1.0\" standalone=\"no\"?>\n"
|
13
|
+
output += "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"
|
14
|
+
output += "<svg x=\"0\" y=\"0\" width=\"#{width}#{Units.svg_name(units)}\" height=\"#{height}#{Units.svg_name(units)}\" viewBox=\"0 0 #{width} #{height}\" xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">\n"
|
15
|
+
output += " <g transform=\"translate(#{x_offset}#{Units.svg_name(units)},#{y_offset}#{Units.svg_name(units)})\">\n"
|
16
|
+
|
16
17
|
@lines.each do |line|
|
17
|
-
output
|
18
|
+
output += " #{line.to_svg_path(units)}\n"
|
18
19
|
end
|
19
|
-
|
20
|
-
output
|
21
|
-
output
|
20
|
+
|
21
|
+
output += " </g>\n"
|
22
|
+
output += '</svg>'
|
22
23
|
end
|
23
24
|
end
|
24
25
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Triangular
|
4
|
+
class Ray
|
5
|
+
attr_accessor :x, :y
|
6
|
+
|
7
|
+
def initialize(x, y)
|
8
|
+
@x = x
|
9
|
+
@y = y
|
10
|
+
end
|
11
|
+
|
12
|
+
def intersection(facet)
|
13
|
+
lines = facet.lines
|
14
|
+
|
15
|
+
x_intersection = false
|
16
|
+
y_intersection = false
|
17
|
+
|
18
|
+
lines.each do |line|
|
19
|
+
x_intersection = true if line.start.x <= @x && line.end.x >= @x
|
20
|
+
x_intersection = true if line.end.x <= @x && line.start.x >= @x
|
21
|
+
|
22
|
+
y_intersection = true if line.start.y <= @y && line.end.y >= @y
|
23
|
+
y_intersection = true if line.end.y <= @y && line.start.y >= @y
|
24
|
+
end
|
25
|
+
|
26
|
+
return nil unless x_intersection && y_intersection
|
27
|
+
|
28
|
+
x_intersections = lines.map do |line|
|
29
|
+
line.intersection_at_x(@x)
|
30
|
+
end
|
31
|
+
|
32
|
+
x_intersections.compact!
|
33
|
+
|
34
|
+
if x_intersections[0] == x_intersections[1]
|
35
|
+
x_intersections[0]
|
36
|
+
else
|
37
|
+
Line.new(x_intersections[0], x_intersections[1]).intersection_at_y(@y)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|