diff --git a/lib/Slic3r/Extruder.pm b/lib/Slic3r/Extruder.pm index 2a8371ce..514aec7c 100644 --- a/lib/Slic3r/Extruder.pm +++ b/lib/Slic3r/Extruder.pm @@ -40,8 +40,7 @@ sub new_from_config { use_relative_e_distances => $config->use_relative_e_distances, ); foreach my $opt_key (@{&OPTIONS}) { - my $value = $config->get($opt_key); - $conf{$opt_key} = $value->[$extruder_id] // $value->[0]; + $conf{$opt_key} = $config->get_at($opt_key, $extruder_id); } return $class->new(%conf); } diff --git a/lib/Slic3r/Flow.pm b/lib/Slic3r/Flow.pm index 5b1502a7..f07be98f 100644 --- a/lib/Slic3r/Flow.pm +++ b/lib/Slic3r/Flow.pm @@ -72,7 +72,7 @@ sub mm3_per_mm { my $s = $self->spacing; if ($self->bridge) { - return ($s**2) * PI/4; + return ($w**2) * PI/4; } elsif ($w >= ($self->nozzle_diameter + $h)) { # rectangle with semicircles at the ends return $w * $h + ($h**2) / 4 * (PI - 4); @@ -140,7 +140,7 @@ sub _spacing { if ($bridge_flow_ratio > 0) { return $width + BRIDGE_EXTRA_SPACING; } - + use XXX; ZZZ "here" if !defined $nozzle_diameter; my $min_flow_spacing; if ($width >= ($nozzle_diameter + $layer_height)) { # rectangle with semicircles at the ends diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index b1c05d95..78324f3b 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -12,7 +12,7 @@ has 'print_config' => (is => 'ro', default => sub { Slic3r::Config::Print- has 'extra_variables' => (is => 'rw', default => sub {{}}); has 'standby_points' => (is => 'rw'); has 'enable_loop_clipping' => (is => 'rw', default => sub {1}); -has 'enable_wipe' => (is => 'lazy'); # at least one extruder has wipe enabled +has 'enable_wipe' => (is => 'rw', default => sub {0}); # at least one extruder has wipe enabled has 'layer_count' => (is => 'ro', required => 1 ); has 'layer' => (is => 'rw'); has 'region' => (is => 'rw'); @@ -25,14 +25,13 @@ has 'z' => (is => 'rw'); has 'speed' => (is => 'rw'); has '_extrusion_axis' => (is => 'rw'); has '_retract_lift' => (is => 'rw'); -has 'extruders' => (is => 'ro', default => sub {[]}); - +has 'extruders' => (is => 'ro', default => sub {{}}); +has 'extruder' => (is => 'rw'); has 'speeds' => (is => 'lazy'); # mm/min has 'external_mp' => (is => 'rw'); has 'layer_mp' => (is => 'rw'); has 'new_object' => (is => 'rw', default => sub {0}); has 'straight_once' => (is => 'rw', default => sub {1}); -has 'extruder' => (is => 'rw'); has 'elapsed_time' => (is => 'rw', default => sub {0} ); # seconds has 'lifted' => (is => 'rw', default => sub {0} ); has 'last_pos' => (is => 'rw', default => sub { Slic3r::Point->new(0,0) } ); @@ -52,7 +51,8 @@ sub set_extruders { my ($self, $extruder_ids) = @_; foreach my $i (@$extruder_ids) { - $self->extruders->[$i] = Slic3r::Extruder->new_from_config($self->print_config, $i); + $self->extruders->{$i} = my $e = Slic3r::Extruder->new_from_config($self->print_config, $i); + $self->enable_wipe(1) if $e->wipe; } } @@ -82,12 +82,7 @@ my %role_speeds = ( sub multiple_extruders { my $self = shift; - return @{$self->extruders} > 1; -} - -sub _build_enable_wipe { - my $self = shift; - return (first { $_->wipe } @{$self->extruders}) ? 1 : 0; + return (keys %{$self->extruders}) > 1; } sub set_shift { @@ -248,14 +243,12 @@ sub extrude_loop { @{$extrusion_path->subtract_expolygons($self->_layer_overhangs)}; # get overhang paths by intersecting overhangs with the loop - push @paths, - map { - $_->role(EXTR_ROLE_OVERHANG_PERIMETER); - $_->mm3_per_mm($self->region->flow(FLOW_ROLE_PERIMETER, undef, 1)->mm3_per_mm(undef)); - $_ - } - map $_->clone, - @{$extrusion_path->intersect_expolygons($self->_layer_overhangs)}; + foreach my $path (@{$extrusion_path->intersect_expolygons($self->_layer_overhangs)}) { + $path = $path->clone; + $path->role(EXTR_ROLE_OVERHANG_PERIMETER); + $path->mm3_per_mm($self->region->flow(FLOW_ROLE_PERIMETER, undef, 1)->mm3_per_mm(undef)); + push @paths, $path; + } # reapply the nearest point search for starting point # (clone because the collection gets DESTROY'ed) @@ -644,7 +637,7 @@ sub set_extruder { # if we are running a single-extruder setup, just set the extruder and return nothing if (!$self->multiple_extruders) { - $self->extruder($self->extruders->[$extruder_id]); + $self->extruder($self->extruders->{$extruder_id}); return ""; } @@ -673,7 +666,7 @@ sub set_extruder { } # set the new extruder - $self->extruder($self->extruders->[$extruder_id]); + $self->extruder($self->extruders->{$extruder_id}); $gcode .= sprintf "%s%d%s\n", ($self->print_config->gcode_flavor eq 'makerware' ? 'M135 T' diff --git a/lib/Slic3r/GCode/Layer.pm b/lib/Slic3r/GCode/Layer.pm index 96029eaa..08de360e 100644 --- a/lib/Slic3r/GCode/Layer.pm +++ b/lib/Slic3r/GCode/Layer.pm @@ -4,8 +4,8 @@ use Moo; use List::Util qw(first); use Slic3r::Geometry qw(X Y unscale); -has 'print' => (is => 'ro', required => 1, handles => [qw(extruders)]); -has 'gcodegen' => (is => 'ro', required => 1); +has 'print' => (is => 'ro', required => 1); +has 'gcodegen' => (is => 'ro', required => 1, handles => [qw(extruders)]); has 'shift' => (is => 'ro', default => sub { [0,0] }); has 'spiralvase' => (is => 'lazy'); @@ -57,9 +57,10 @@ sub process_layer { $self->gcodegen->enable_loop_clipping(!$spiralvase); if (!$self->second_layer_things_done && $layer->id == 1) { - for my $t (grep $self->extruders->[$_], 0 .. $#{$self->print->config->temperature}) { - $gcode .= $self->gcodegen->set_temperature($self->extruders->[$t]->temperature, 0, $t) - if $self->print->extruders->[$t]->temperature && $self->extruders->[$t]->temperature != $self->extruders->[$t]->first_layer_temperature; + for my $extruder_id (sort keys %{$self->extruders}) { + my $extruder = $self->extruders->{$extruder_id}; + $gcode .= $self->gcodegen->set_temperature($extruder->temperature, 0, $extruder->id) + if $extruder->temperature && $extruder->temperature != $extruder->first_layer_temperature; } $gcode .= $self->gcodegen->set_bed_temperature($self->print->config->bed_temperature) if $self->print->config->bed_temperature && $self->print->config->bed_temperature != $self->print->config->first_layer_bed_temperature; @@ -76,7 +77,8 @@ sub process_layer { if (((values %{$self->skirt_done}) < $self->print->config->skirt_height || $self->print->config->skirt_height == -1) && !$self->skirt_done->{$layer->print_z}) { $self->gcodegen->set_shift(@{$self->shift}); - $gcode .= $self->gcodegen->set_extruder($self->extruders->[0]); + my @extruder_ids = sort keys %{$self->extruders}; + $gcode .= $self->gcodegen->set_extruder($extruder_ids[0]); # skip skirt if we have a large brim if ($layer->id < $self->print->config->skirt_height || $self->print->config->skirt_height == -1) { # distribute skirt loops across all extruders @@ -85,7 +87,7 @@ sub process_layer { # when printing layers > 0 ignore 'min_skirt_length' and # just use the 'skirts' setting; also just use the current extruder last if ($layer->id > 0) && ($i >= $self->print->config->skirts); - $gcode .= $self->gcodegen->set_extruder(($i/@{$self->extruders}) % @{$self->extruders}) + $gcode .= $self->gcodegen->set_extruder(($i/@extruder_ids) % @extruder_ids) if $layer->id == 0; $gcode .= $self->gcodegen->extrude_loop($skirt_loops[$i], 'skirt'); } diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index f31e6665..e2407035 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -709,7 +709,7 @@ sub make_brim { my $flow = Slic3r::Flow->new_from_width( width => ($self->config->first_layer_extrusion_width || $self->regions->[0]->config->perimeter_extrusion_width), role => FLOW_ROLE_PERIMETER, - nozzle_diameter => $self->config->nozzle_diameter->[ $self->objects->[0]->config->support_material_extruder-1 ], + nozzle_diameter => $self->config->get_at('nozzle_diameter', $self->objects->[0]->config->support_material_extruder-1), layer_height => $first_layer_height, bridge_flow_ratio => 0, ); @@ -807,7 +807,6 @@ sub write_gcode { layer_count => $self->layer_count, ); $gcodegen->set_extruders($self->extruders); - $gcodegen->set_extruder($self->extruders->[0]); print $fh "G21 ; set units to millimeters\n" if $self->config->gcode_flavor ne 'makerware'; print $fh $gcodegen->set_fan(0, 1) if $self->config->cooling && $self->config->disable_fan_first_layers; @@ -822,8 +821,8 @@ sub write_gcode { my ($wait) = @_; return if $self->config->start_gcode =~ /M(?:109|104)/i; - for my $t (0 .. $#{$self->extruders}) { - my $temp = $self->config->first_layer_temperature->[$t] // $self->config->first_layer_temperature->[0]; + for my $t (@{$self->extruders}) { + my $temp = $self->config->get_at('first_layer_temperature', $t); $temp += $self->config->standby_temperature_delta if $self->config->ooze_prevention; printf $fh $gcodegen->set_temperature($temp, $wait, $t) if $temp > 0; } @@ -873,9 +872,9 @@ sub write_gcode { if (@skirt_points) { my $outer_skirt = convex_hull(\@skirt_points); my @skirts = (); - foreach my $extruder (@{$self->extruders}) { + foreach my $extruder_id (@{$self->extruders}) { push @skirts, my $s = $outer_skirt->clone; - $s->translate(map scale($_), @{$extruder->extruder_offset}); + $s->translate(map scale($_), @{$self->config->get_at('extruder_offset', $extruder_id)}); } my $convex_hull = convex_hull([ map @$_, @skirts ]); $gcodegen->standby_points([ map $_->clone, map @$_, map $_->subdivide(scale 10), @{offset([$convex_hull], scale 3)} ]); @@ -888,6 +887,9 @@ sub write_gcode { gcodegen => $gcodegen, ); + # set initial extruder only after custom start G-code + print $fh $gcodegen->set_extruder($self->extruders->[0]); + # do all objects for each layer if ($self->config->complete_objects) { # print objects from the smallest to the tallest to avoid collisions @@ -975,7 +977,7 @@ sub write_gcode { $self->total_used_filament(0); $self->total_extruded_volume(0); foreach my $extruder_id (@{$self->extruders}) { - my $extruder = $gcodegen->extruders->[$extruder_id]; + my $extruder = $gcodegen->extruders->{$extruder_id}; $self->total_used_filament($self->total_used_filament + $extruder->absolute_E); $self->total_extruded_volume($self->total_extruded_volume + $extruder->extruded_volume); diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 2d9e9b53..bf196c10 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -758,7 +758,7 @@ sub combine_infill { my $every = $region->config->infill_every_layers; # limit the number of combined layers to the maximum height allowed by this regions' nozzle - my $nozzle_diameter = $self->print->config->nozzle_diameter->[ $region->config->infill_extruder-1 ]; + my $nozzle_diameter = $self->print->config->get_at('nozzle_diameter', $region->config->infill_extruder-1); # define the combinations my @combine = (); # layer_id => thickness in layers @@ -810,12 +810,12 @@ sub combine_infill { # so let's remove those areas from all layers my @intersection_with_clearance = map @{$_->offset( - $layerms[-1]->solid_infill_flow->scaled_width / 2 - + $layerms[-1]->perimeter_flow->scaled_width / 2 + $layerms[-1]->flow(FLOW_ROLE_SOLID_INFILL)->scaled_width / 2 + + $layerms[-1]->flow(FLOW_ROLE_PERIMETER)->scaled_width / 2 # Because fill areas for rectilinear and honeycomb are grown # later to overlap perimeters, we need to counteract that too. + (($type == S_TYPE_INTERNALSOLID || $region->config->fill_pattern =~ /(rectilinear|honeycomb)/) - ? $layerms[-1]->solid_infill_flow->scaled_width * &Slic3r::INFILL_OVERLAP_OVER_SPACING + ? $layerms[-1]->flow(FLOW_ROLE_SOLID_INFILL)->scaled_width * &Slic3r::INFILL_OVERLAP_OVER_SPACING : 0) )}, @$intersection; @@ -866,7 +866,8 @@ sub generate_support_material { my $first_layer_flow = Slic3r::Flow->new_from_width( width => ($self->config->first_layer_extrusion_width || $self->config->support_material_extrusion_width), role => FLOW_ROLE_SUPPORT_MATERIAL, - nozzle_diameter => $self->print->config->nozzle_diameter->[ $self->config->support_material_extruder-1 ], + nozzle_diameter => $self->print->config->nozzle_diameter->[ $self->config->support_material_extruder-1 ] + // $self->print->config->nozzle_diameter->[0], layer_height => $self->config->get_abs_value('first_layer_height'), bridge_flow_ratio => 0, ); @@ -903,7 +904,7 @@ sub support_material_flow { return Slic3r::Flow->new_from_width( width => $self->config->support_material_extrusion_width, role => $role, - nozzle_diameter => $self->print->config->nozzle_diameter->[$extruder-1], + nozzle_diameter => $self->print->config->nozzle_diameter->[$extruder-1] // $self->print->config->nozzle_diameter->[0], layer_height => $self->config->layer_height, bridge_flow_ratio => 0, ); diff --git a/lib/Slic3r/Print/Region.pm b/lib/Slic3r/Print/Region.pm index 47296847..657aa34d 100644 --- a/lib/Slic3r/Print/Region.pm +++ b/lib/Slic3r/Print/Region.pm @@ -46,7 +46,7 @@ sub flow { } else { die "Unknown role $role"; } - my $nozzle_diameter = $self->print->config->nozzle_diameter->[$extruder-1]; + my $nozzle_diameter = $self->print->config->get_at('nozzle_diameter', $extruder-1); return Slic3r::Flow->new_from_width( width => $config_width, diff --git a/lib/Slic3r/Print/SupportMaterial.pm b/lib/Slic3r/Print/SupportMaterial.pm index a88728e6..2c533d8d 100644 --- a/lib/Slic3r/Print/SupportMaterial.pm +++ b/lib/Slic3r/Print/SupportMaterial.pm @@ -174,7 +174,7 @@ sub contact_area { # now apply the contact areas to the layer were they need to be made { # get the average nozzle diameter used on this layer - my @nozzle_diameters = map $self->print_config->nozzle_diameter->[$_], + my @nozzle_diameters = map $self->print_config->get_at('nozzle_diameter', $_), map { $_->config->perimeter_extruder-1, $_->config->infill_extruder-1 } @{$layer->regions}; my $nozzle_diameter = sum(@nozzle_diameters)/@nozzle_diameters; @@ -246,7 +246,7 @@ sub support_layers_z { # determine layer height for any non-contact layer # we use max() to prevent many ultra-thin layers to be inserted in case # layer_height > nozzle_diameter * 0.75 - my $nozzle_diameter = $self->print_config->nozzle_diameter->[$self->object_config->support_material_extruder-1]; + my $nozzle_diameter = $self->print_config->get_at('nozzle_diameter', $self->object_config->support_material_extruder-1); my $support_material_height = max($max_object_layer_height, $nozzle_diameter * 0.75); my @z = sort { $a <=> $b } @$contact_z, @$top_z, (map $_ + $nozzle_diameter, @$top_z); diff --git a/t/combineinfill.t b/t/combineinfill.t index bd80b0d3..62e22e6b 100644 --- a/t/combineinfill.t +++ b/t/combineinfill.t @@ -11,10 +11,41 @@ use List::Util qw(first); use Slic3r; use Slic3r::Test; -plan skip_all => 'this test is currently disabled'; # needs to be adapted to the new API -plan tests => 3; +plan tests => 2; { + my $config = Slic3r::Config->new_from_defaults; + $config->set('layer_height', 0.2); + $config->set('first_layer_height', 0.2); + $config->set('nozzle_diameter', [0.5]); + $config->set('infill_every_layers', 2); + $config->set('infill_extruder', 2); + $config->set('top_solid_layers', 0); + $config->set('bottom_solid_layers', 0); + my $print = Slic3r::Test::init_print('20mm_cube', config => $config); + ok my $gcode = Slic3r::Test::gcode($print), "infill_every_layers does not crash"; + + my $tool = undef; + my %layer_infill = (); # layer_z => has_infill + Slic3r::GCode::Reader->new->parse($gcode, sub { + my ($self, $cmd, $args, $info) = @_; + + if ($cmd =~ /^T(\d+)/) { + $tool = $1; + } elsif ($cmd eq 'G1' && $info->{extruding} && $info->{dist_XY} > 0) { + $layer_infill{$self->Z} //= 0; + if ($tool == $config->infill_extruder-1) { + $layer_infill{$self->Z} = 1; + } + } + }); + my $layers_with_infill = grep $_, values %layer_infill; + $layers_with_infill--; # first layer is never combined + is $layers_with_infill, scalar(keys %layer_infill)/2, 'infill is only present in correct number of layers'; +} + +# the following needs to be adapted to the new API +if (0) { my $config = Slic3r::Config->new_from_defaults; $config->set('skirts', 0); $config->set('solid_layers', 0); diff --git a/t/retraction.t b/t/retraction.t index 63635914..3737f63c 100644 --- a/t/retraction.t +++ b/t/retraction.t @@ -41,8 +41,8 @@ my $test = sub { if ($info->{dist_Z}) { # lift move or lift + change layer - if (_eq($info->{dist_Z}, $print->extruders->[$tool]->retract_lift) - || (_eq($info->{dist_Z}, $conf->layer_height + $print->extruders->[$tool]->retract_lift) && $print->extruders->[$tool]->retract_lift > 0)) { + if (_eq($info->{dist_Z}, $print->config->get_at('retract_lift', $tool)) + || (_eq($info->{dist_Z}, $conf->layer_height + $print->config->get_at('retract_lift', $tool)) && $print->config->get_at('retract_lift', $tool) > 0)) { fail 'only lifting while retracted' if !$retracted[$tool] && !($conf->g0 && $info->{retracting}); fail 'double lift' if $lifted; $lifted = 1; @@ -50,8 +50,8 @@ my $test = sub { if ($info->{dist_Z} < 0) { fail 'going down only after lifting' if !$lifted; fail 'going down by the same amount of the lift or by the amount needed to get to next layer' - if !_eq($info->{dist_Z}, -$print->extruders->[$tool]->retract_lift) - && !_eq($info->{dist_Z}, -$print->extruders->[$tool]->retract_lift + $conf->layer_height); + if !_eq($info->{dist_Z}, -$print->config->get_at('retract_lift', $tool)) + && !_eq($info->{dist_Z}, -$print->config->get_at('retract_lift', $tool) + $conf->layer_height); $lifted = 0; } fail 'move Z at travel speed' if ($args->{F} // $self->F) != $conf->travel_speed * 60; @@ -59,9 +59,9 @@ my $test = sub { if ($info->{retracting}) { $retracted[$tool] = 1; $retracted_length[$tool] += -$info->{dist_E}; - if (_eq($retracted_length[$tool], $print->extruders->[$tool]->retract_length)) { + if (_eq($retracted_length[$tool], $print->config->get_at('retract_length', $tool))) { # okay - } elsif (_eq($retracted_length[$tool], $print->extruders->[$tool]->retract_length_toolchange)) { + } elsif (_eq($retracted_length[$tool], $print->config->get_at('retract_length_toolchange', $tool))) { $wait_for_toolchange = 1; } else { fail 'retracted by the correct amount'; @@ -72,9 +72,9 @@ my $test = sub { if ($info->{extruding}) { fail 'only extruding while not lifted' if $lifted; if ($retracted[$tool]) { - my $expected_amount = $retracted_length[$tool] + $print->extruders->[$tool]->retract_restart_extra; + my $expected_amount = $retracted_length[$tool] + $print->config->get_at('retract_restart_extra', $tool); if ($changed_tool && $toolchange_count[$tool] > 1) { - $expected_amount = $print->extruders->[$tool]->retract_length_toolchange + $print->extruders->[$tool]->retract_restart_extra_toolchange; + $expected_amount = $print->config->get_at('retract_length_toolchange', $tool) + $print->config->get_at('retract_restart_extra_toolchange', $tool); $changed_tool = 0; } fail 'unretracted by the correct amount' @@ -83,7 +83,7 @@ my $test = sub { $retracted_length[$tool] = 0; } } - if ($info->{travel} && $info->{dist_XY} >= $print->extruders->[$tool]->retract_before_travel) { + if ($info->{travel} && $info->{dist_XY} >= $print->config->get_at('retract_before_travel', $tool)) { fail 'retracted before long travel move' if !$retracted[$tool]; } }); diff --git a/t/support.t b/t/support.t index bd82d0ce..a1e7173d 100644 --- a/t/support.t +++ b/t/support.t @@ -23,7 +23,13 @@ use Slic3r::Test; $print->init_extruders; my $flow = $print->objects->[0]->support_material_flow; my $support_z = Slic3r::Print::SupportMaterial - ->new(object_config => $print->objects->[0]->config, print_config => $print->config, flow => $flow) + ->new( + object_config => $print->objects->[0]->config, + print_config => $print->config, + flow => $flow, + interface_flow => $flow, + first_layer_flow => $flow, + ) ->support_layers_z(\@contact_z, \@top_z, $config->layer_height); is $support_z->[0], $config->first_layer_height, diff --git a/xs/src/Config.cpp b/xs/src/Config.cpp index 58c9343e..32c6d607 100644 --- a/xs/src/Config.cpp +++ b/xs/src/Config.cpp @@ -130,9 +130,9 @@ ConfigBase::get(t_config_option_key opt_key) { return optv->point.to_SV_pureperl(); } else if (ConfigOptionPoints* optv = dynamic_cast(opt)) { AV* av = newAV(); - av_fill(av, optv->points.size()-1); - for (Pointfs::iterator it = optv->points.begin(); it != optv->points.end(); ++it) - av_store(av, it - optv->points.begin(), it->to_SV_pureperl()); + av_fill(av, optv->values.size()-1); + for (Pointfs::iterator it = optv->values.begin(); it != optv->values.end(); ++it) + av_store(av, it - optv->values.begin(), it->to_SV_pureperl()); return newRV_noinc((SV*)av); } else if (ConfigOptionBool* optv = dynamic_cast(opt)) { return newSViv(optv->value ? 1 : 0); @@ -148,6 +148,28 @@ ConfigBase::get(t_config_option_key opt_key) { } } +SV* +ConfigBase::get_at(t_config_option_key opt_key, size_t i) { + ConfigOption* opt = this->option(opt_key); + if (opt == NULL) return &PL_sv_undef; + + if (ConfigOptionFloats* optv = dynamic_cast(opt)) { + return newSVnv(optv->get_at(i)); + } else if (ConfigOptionInts* optv = dynamic_cast(opt)) { + return newSViv(optv->get_at(i)); + } else if (ConfigOptionStrings* optv = dynamic_cast(opt)) { + // we don't serialize() because that would escape newlines + std::string val = optv->get_at(i); + return newSVpvn(val.c_str(), val.length()); + } else if (ConfigOptionPoints* optv = dynamic_cast(opt)) { + return optv->get_at(i).to_SV_pureperl(); + } else if (ConfigOptionBools* optv = dynamic_cast(opt)) { + return newSViv(optv->get_at(i) ? 1 : 0); + } else { + return &PL_sv_undef; + } +} + void ConfigBase::set(t_config_option_key opt_key, SV* value) { ConfigOption* opt = this->option(opt_key, true); @@ -193,14 +215,14 @@ ConfigBase::set(t_config_option_key opt_key, SV* value) { } else if (ConfigOptionPoint* optv = dynamic_cast(opt)) { optv->point.from_SV(value); } else if (ConfigOptionPoints* optv = dynamic_cast(opt)) { - optv->points.clear(); + optv->values.clear(); AV* av = (AV*)SvRV(value); const size_t len = av_len(av)+1; for (size_t i = 0; i < len; i++) { SV** elem = av_fetch(av, i, 0); Pointf point; point.from_SV(*elem); - optv->points.push_back(point); + optv->values.push_back(point); } } else if (ConfigOptionBool* optv = dynamic_cast(opt)) { optv->value = SvTRUE(value); diff --git a/xs/src/Config.hpp b/xs/src/Config.hpp index 9306a18b..64229cb2 100644 --- a/xs/src/Config.hpp +++ b/xs/src/Config.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include "Point.hpp" @@ -24,6 +25,22 @@ class ConfigOption { virtual void deserialize(std::string str) = 0; }; +template +class ConfigOptionVector +{ + public: + virtual ~ConfigOptionVector() {}; + std::vector values; + + T get_at(size_t i) { + try { + return this->values.at(i); + } catch (const std::out_of_range& oor) { + return this->values.front(); + } + }; +}; + class ConfigOptionFloat : public ConfigOption { public: @@ -43,10 +60,9 @@ class ConfigOptionFloat : public ConfigOption }; }; -class ConfigOptionFloats : public ConfigOption +class ConfigOptionFloats : public ConfigOption, public ConfigOptionVector { public: - std::vector values; std::string serialize() { std::ostringstream ss; @@ -86,10 +102,9 @@ class ConfigOptionInt : public ConfigOption }; }; -class ConfigOptionInts : public ConfigOption +class ConfigOptionInts : public ConfigOption, public ConfigOptionVector { public: - std::vector values; std::string serialize() { std::ostringstream ss; @@ -144,10 +159,9 @@ class ConfigOptionString : public ConfigOption }; // semicolon-separated strings -class ConfigOptionStrings : public ConfigOption +class ConfigOptionStrings : public ConfigOption, public ConfigOptionVector { public: - std::vector values; std::string serialize() { std::ostringstream ss; @@ -215,15 +229,14 @@ class ConfigOptionPoint : public ConfigOption }; }; -class ConfigOptionPoints : public ConfigOption +class ConfigOptionPoints : public ConfigOption, public ConfigOptionVector { public: - Pointfs points; std::string serialize() { std::ostringstream ss; - for (Pointfs::const_iterator it = this->points.begin(); it != this->points.end(); ++it) { - if (it - this->points.begin() != 0) ss << ","; + for (Pointfs::const_iterator it = this->values.begin(); it != this->values.end(); ++it) { + if (it - this->values.begin() != 0) ss << ","; ss << it->x; ss << "x"; ss << it->y; @@ -232,13 +245,13 @@ class ConfigOptionPoints : public ConfigOption }; void deserialize(std::string str) { - this->points.clear(); + this->values.clear(); std::istringstream is(str); std::string point_str; while (std::getline(is, point_str, ',')) { Pointf point; sscanf(point_str.c_str(), "%lfx%lf", &point.x, &point.y); - this->points.push_back(point); + this->values.push_back(point); } }; }; @@ -260,10 +273,9 @@ class ConfigOptionBool : public ConfigOption }; }; -class ConfigOptionBools : public ConfigOption +class ConfigOptionBools : public ConfigOption, public ConfigOptionVector { public: - std::vector values; std::string serialize() { std::ostringstream ss; @@ -398,6 +410,7 @@ class ConfigBase #ifdef SLIC3RXS SV* as_hash(); SV* get(t_config_option_key opt_key); + SV* get_at(t_config_option_key opt_key, size_t i); void set(t_config_option_key opt_key, SV* value); #endif }; diff --git a/xs/src/PrintConfig.hpp b/xs/src/PrintConfig.hpp index c5adff7c..cb44a007 100644 --- a/xs/src/PrintConfig.hpp +++ b/xs/src/PrintConfig.hpp @@ -1131,8 +1131,8 @@ class PrintConfig : public virtual StaticConfig this->external_perimeters_first.value = false; this->extruder_clearance_height.value = 20; this->extruder_clearance_radius.value = 20; - this->extruder_offset.points.resize(1); - this->extruder_offset.points[0] = Pointf(0,0); + this->extruder_offset.values.resize(1); + this->extruder_offset.values[0] = Pointf(0,0); this->extrusion_axis.value = "E"; this->extrusion_multiplier.values.resize(1); this->extrusion_multiplier.values[0] = 1; diff --git a/xs/t/15_config.t b/xs/t/15_config.t index 0d208060..c80f1440 100644 --- a/xs/t/15_config.t +++ b/xs/t/15_config.t @@ -4,7 +4,7 @@ use strict; use warnings; use Slic3r::XS; -use Test::More tests => 82; +use Test::More tests => 88; foreach my $config (Slic3r::Config->new, Slic3r::Config::Full->new) { $config->set('layer_height', 0.3); @@ -75,6 +75,9 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Full->new) { $config->set('wipe', [1,0]); is_deeply $config->get('wipe'), [1,0], 'set/get bools'; + is $config->get_at('wipe', 0), 1, 'get_at bools'; + is $config->get_at('wipe', 1), 0, 'get_at bools'; + is $config->get_at('wipe', 9), 1, 'get_at bools'; is $config->serialize('wipe'), '1,0', 'serialize bools'; $config->set_deserialize('wipe', '0,1,1'); is_deeply $config->get('wipe'), [0,1,1], 'deserialize bools'; diff --git a/xs/xsp/Config.xsp b/xs/xsp/Config.xsp index 743b38ca..79d9bd26 100644 --- a/xs/xsp/Config.xsp +++ b/xs/xsp/Config.xsp @@ -11,6 +11,7 @@ bool has(t_config_option_key opt_key); SV* as_hash(); SV* get(t_config_option_key opt_key); + SV* get_at(t_config_option_key opt_key, int i); void set(t_config_option_key opt_key, SV* value); void set_deserialize(t_config_option_key opt_key, std::string str); std::string serialize(t_config_option_key opt_key); @@ -31,6 +32,7 @@ bool has(t_config_option_key opt_key); SV* as_hash(); SV* get(t_config_option_key opt_key); + SV* get_at(t_config_option_key opt_key, int i); void set(t_config_option_key opt_key, SV* value); void set_deserialize(t_config_option_key opt_key, std::string str); std::string serialize(t_config_option_key opt_key); @@ -50,6 +52,7 @@ bool has(t_config_option_key opt_key); SV* as_hash(); SV* get(t_config_option_key opt_key); + SV* get_at(t_config_option_key opt_key, int i); void set(t_config_option_key opt_key, SV* value); void set_deserialize(t_config_option_key opt_key, std::string str); std::string serialize(t_config_option_key opt_key); @@ -70,6 +73,7 @@ bool has(t_config_option_key opt_key); SV* as_hash(); SV* get(t_config_option_key opt_key); + SV* get_at(t_config_option_key opt_key, int i); void set(t_config_option_key opt_key, SV* value); void set_deserialize(t_config_option_key opt_key, std::string str); std::string serialize(t_config_option_key opt_key); @@ -90,6 +94,7 @@ bool has(t_config_option_key opt_key); SV* as_hash(); SV* get(t_config_option_key opt_key); + SV* get_at(t_config_option_key opt_key, int i); void set(t_config_option_key opt_key, SV* value); void set_deserialize(t_config_option_key opt_key, std::string str); std::string serialize(t_config_option_key opt_key);